Symbol chooser performance improvements

Cache LIB_SYMBOL properties used for chooser filtering
Reduce copying during creation of chooser widgets

Fixes https://gitlab.com/kicad/code/kicad/-/issues/23267
This commit is contained in:
Jon Evans
2026-03-02 23:10:11 -05:00
parent c8bc176086
commit f06a303384
15 changed files with 184 additions and 73 deletions
+10 -9
View File
@@ -22,22 +22,23 @@
#include <wx/tokenzr.h>
std::vector<SEARCH_TERM> DESIGN_BLOCK::GetSearchTerms()
std::vector<SEARCH_TERM>& DESIGN_BLOCK::GetSearchTerms()
{
std::vector<SEARCH_TERM> terms;
m_searchTerms.clear();
m_searchTerms.reserve( 6 );
terms.emplace_back( SEARCH_TERM( GetLibNickname(), 4 ) );
terms.emplace_back( SEARCH_TERM( GetName(), 8 ) );
terms.emplace_back( SEARCH_TERM( GetLIB_ID().Format(), 16 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetLibNickname(), 4 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetName(), 8 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetLIB_ID().Format(), 16 ) );
wxStringTokenizer keywordTokenizer( GetKeywords(), wxS( " " ), wxTOKEN_STRTOK );
while( keywordTokenizer.HasMoreTokens() )
terms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
m_searchTerms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
// Also include keywords as one long string, just in case
terms.emplace_back( SEARCH_TERM( GetKeywords(), 1 ) );
terms.emplace_back( SEARCH_TERM( GetDesc(), 1 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetKeywords(), 1 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetDesc(), 1 ) );
return terms;
return m_searchTerms;
}
+2 -1
View File
@@ -39,7 +39,7 @@ public:
wxString GetName() const override { return m_lib_id.GetLibItemName(); }
wxString GetLibNickname() const override { return m_lib_id.GetLibNickname(); }
wxString GetDesc() override { return GetLibDescription(); }
std::vector<SEARCH_TERM> GetSearchTerms() override;
std::vector<SEARCH_TERM>& GetSearchTerms() override;
void SetLibId( const LIB_ID& aName ) { m_lib_id = aName; }
const LIB_ID& GetLibId() const { return m_lib_id; }
@@ -73,6 +73,7 @@ private:
wxString m_keywords; ///< Search keywords to find design block in library.
nlohmann::ordered_map<wxString, wxString> m_fields;
std::vector<SEARCH_TERM> m_searchTerms;
};
#endif
+1 -3
View File
@@ -90,9 +90,7 @@ void FOOTPRINT_FILTER_IT::increment()
for( std::unique_ptr<EDA_COMBINED_MATCHER>& matcher : m_filter->m_pattern_filters )
{
std::vector<SEARCH_TERM> searchTerms = candidate.GetSearchTerms();
if( !matcher->ScoreTerms( searchTerms ) )
if( !matcher->ScoreTerms( candidate.GetSearchTerms() ) )
{
exclude = true;
break;
+10 -9
View File
@@ -64,24 +64,25 @@ FOOTPRINT_INFO* FOOTPRINT_LIST::GetFootprintInfo( const wxString& aFootprintName
}
std::vector<SEARCH_TERM> FOOTPRINT_INFO::GetSearchTerms()
std::vector<SEARCH_TERM>& FOOTPRINT_INFO::GetSearchTerms()
{
std::vector<SEARCH_TERM> terms;
m_searchTerms.clear();
m_searchTerms.reserve( 6 );
terms.emplace_back( SEARCH_TERM( GetLibNickname(), 4 ) );
terms.emplace_back( SEARCH_TERM( GetName(), 8 ) );
terms.emplace_back( SEARCH_TERM( GetLIB_ID().Format(), 16 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetLibNickname(), 4 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetName(), 8 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetLIB_ID().Format(), 16 ) );
wxStringTokenizer keywordTokenizer( GetKeywords(), " \t\r\n", wxTOKEN_STRTOK );
while( keywordTokenizer.HasMoreTokens() )
terms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
m_searchTerms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
// Also include keywords as one long string, just in case
terms.emplace_back( SEARCH_TERM( GetKeywords(), 1 ) );
terms.emplace_back( SEARCH_TERM( GetDesc(), 1 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetKeywords(), 1 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetDesc(), 1 ) );
return terms;
return m_searchTerms;
}
+1 -1
View File
@@ -33,7 +33,7 @@
void LIB_TREE_NODE::RebuildSearchTerms( const std::vector<wxString>& aShownColumns )
{
m_SearchTerms = m_sourceSearchTerms;
m_SearchTerms.assign( m_sourceSearchTerms.begin(), m_sourceSearchTerms.end() );
for( const auto& [name, value] : m_Fields )
{
+108 -21
View File
@@ -100,17 +100,35 @@ static std::shared_ptr<LIB_SYMBOL> GetSafeRootSymbol( const LIB_SYMBOL* aSymbol,
wxString LIB_SYMBOL::GetShownDescription( int aDepth ) const
{
wxString shownText = GetDescriptionField().GetShownText( false, aDepth );
return m_shownDescriptionCache;
}
void LIB_SYMBOL::cacheShownDescription()
{
wxString shownText = GetDescriptionField().GetShownText( false, 0 );
if( shownText.IsEmpty() && IsDerived() )
{
std::shared_ptr<LIB_SYMBOL> root = GetSafeRootSymbol( this, __FUNCTION__ );
if( root.get() != this )
shownText = root->GetDescriptionField().GetShownText( false, aDepth );
shownText = root->GetDescriptionField().GetShownText( false, 0 );
}
}
return shownText;
void LIB_SYMBOL::SetDescription( const wxString& aDescription )
{
GetDescriptionField().SetText( aDescription );
cacheSearchTerms();
cacheShownDescription();
}
void LIB_SYMBOL::SetKeyWords( const wxString& aKeyWords )
{
m_keyWords = aKeyWords;
cacheSearchTerms();
}
@@ -129,48 +147,64 @@ wxString LIB_SYMBOL::GetShownKeyWords( int aDepth ) const
}
std::vector<SEARCH_TERM> LIB_SYMBOL::GetSearchTerms()
enum SEARCH_TERM_CACHE_INDEX
{
std::vector<SEARCH_TERM> terms;
STCI_LIB_NICKNAME = 0,
STCI_LIB_SYMBOL_NAME,
STCI_LIB_ID
};
terms.emplace_back( SEARCH_TERM( GetLibNickname(), 4 ) );
terms.emplace_back( SEARCH_TERM( GetName(), 8 ) );
terms.emplace_back( SEARCH_TERM( GetLIB_ID().Format(), 16 ) );
void LIB_SYMBOL::cacheSearchTerms()
{
m_searchTermsCache.clear();
m_searchTermsCache.reserve( 6 );
// Order matters, see SEARCH_TERM_CACHE_INDEX
m_searchTermsCache.emplace_back( SEARCH_TERM( GetLibNickname(), 4 ) );
m_searchTermsCache.emplace_back( SEARCH_TERM( GetName(), 8 ) );
m_searchTermsCache.emplace_back( SEARCH_TERM( GetLIB_ID().Format(), 16 ) );
wxStringTokenizer keywordTokenizer( GetShownKeyWords(), " \t\r\n", wxTOKEN_STRTOK );
while( keywordTokenizer.HasMoreTokens() )
terms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
m_searchTermsCache.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
// Also include keywords as one long string, just in case
terms.emplace_back( SEARCH_TERM( GetShownKeyWords(), 1 ) );
terms.emplace_back( SEARCH_TERM( GetShownDescription(), 1 ) );
m_searchTermsCache.emplace_back( SEARCH_TERM( GetShownKeyWords(), 1 ) );
m_searchTermsCache.emplace_back( SEARCH_TERM( GetShownDescription(), 1 ) );
wxString footprint = GetFootprint();
if( !footprint.IsEmpty() )
terms.emplace_back( SEARCH_TERM( footprint, 1 ) );
return terms;
m_searchTermsCache.emplace_back( SEARCH_TERM( footprint, 1 ) );
}
void LIB_SYMBOL::GetChooserFields( std::map<wxString, wxString>& aColumnMap )
{
aColumnMap = m_chooserFieldsCache;
}
void LIB_SYMBOL::cacheChooserFields()
{
m_chooserFieldsCache.clear();
for( SCH_ITEM& item : m_drawings[SCH_FIELD_T] )
{
SCH_FIELD* field = static_cast<SCH_FIELD*>( &item );
if( field->ShowInChooser() )
aColumnMap[field->GetName()] = field->EDA_TEXT::GetShownText( false );
m_chooserFieldsCache[field->GetName()] = field->EDA_TEXT::GetShownText( false );
}
// If the user has a field named "Keywords", then prefer that. Otherwise add the KiCad
// keywords.
const wxString localizedKeywords = _( "Keywords" );
if( !aColumnMap.contains( localizedKeywords ) )
aColumnMap[localizedKeywords] = GetShownKeyWords();
if( !m_chooserFieldsCache.contains( localizedKeywords ) )
m_chooserFieldsCache[localizedKeywords] = GetShownKeyWords();
}
@@ -198,6 +232,7 @@ LIB_SYMBOL::LIB_SYMBOL( const wxString& aName, LIB_SYMBOL* aParent, LEGACY_SYMBO
m_options = ENTRY_NORMAL;
m_unitsLocked = false;
m_duplicatePinNumbersAreJumpers = false;
m_library = nullptr;
auto addField = [&]( FIELD_T id, bool visible )
{
@@ -216,6 +251,10 @@ LIB_SYMBOL::LIB_SYMBOL( const wxString& aName, LIB_SYMBOL* aParent, LEGACY_SYMBO
SetName( aName );
SetParent( aParent );
SetLib( aLibrary );
cacheSearchTerms();
cachePinCount();
cacheShownDescription();
cacheChooserFields();
}
@@ -263,6 +302,10 @@ LIB_SYMBOL::LIB_SYMBOL( const LIB_SYMBOL& aSymbol, LEGACY_SYMBOL_LIB* aLibrary,
}
SetParent( aSymbol.m_parent.lock().get() );
m_searchTermsCache = aSymbol.m_searchTermsCache;
m_pinCountCache = aSymbol.m_pinCountCache;
m_shownDescriptionCache = aSymbol.m_shownDescriptionCache;
m_chooserFieldsCache = aSymbol.m_chooserFieldsCache;
}
@@ -308,6 +351,11 @@ const LIB_SYMBOL& LIB_SYMBOL::operator=( const LIB_SYMBOL& aSymbol )
EMBEDDED_FILES::operator=( aSymbol );
m_searchTermsCache = aSymbol.m_searchTermsCache;
m_pinCountCache = aSymbol.m_pinCountCache;
m_shownDescriptionCache = aSymbol.m_shownDescriptionCache;
m_chooserFieldsCache = aSymbol.m_chooserFieldsCache;
return *this;
}
@@ -433,6 +481,23 @@ void LIB_SYMBOL::SetName( const wxString& aName )
{
m_name = aName;
m_libId.SetLibItemName( aName );
if( m_searchTermsCache.empty() )
cacheSearchTerms();
m_searchTermsCache[STCI_LIB_SYMBOL_NAME].Text = aName;
m_searchTermsCache[STCI_LIB_ID].Text = GetLIB_ID().Format();
}
void LIB_SYMBOL::SetLibId( const LIB_ID& aLibId )
{
m_libId = aLibId;
if( m_searchTermsCache.empty() )
cacheSearchTerms();
m_searchTermsCache[STCI_LIB_ID].Text = GetLIB_ID().Format();
}
@@ -627,6 +692,17 @@ std::unique_ptr<LIB_SYMBOL> LIB_SYMBOL::Flatten() const
}
void LIB_SYMBOL::SetLib( LEGACY_SYMBOL_LIB* aLibrary )
{
m_library = aLibrary;
if( m_searchTermsCache.empty() )
cacheSearchTerms();
m_searchTermsCache[STCI_LIB_NICKNAME].Text = GetLibraryName();
}
const wxString LIB_SYMBOL::GetLibraryName() const
{
if( m_library )
@@ -1012,6 +1088,9 @@ void LIB_SYMBOL::AddDrawItem( SCH_ITEM* aItem, bool aSort )
if( aSort )
m_drawings.sort();
cachePinCount();
cacheChooserFields();
}
}
@@ -1176,15 +1255,19 @@ std::vector<LIB_SYMBOL::LOGICAL_PIN> LIB_SYMBOL::GetLogicalPins( int aUnit, int
int LIB_SYMBOL::GetPinCount()
{
int count = 0;
return m_pinCountCache;
}
void LIB_SYMBOL::cachePinCount()
{
m_pinCountCache = 0;
for( SCH_PIN* pin : GetGraphicalPins( 0 /* all units */, 1 /* single body style */ ) )
{
int pinCount = pin->GetStackedPinCount();
count += pinCount;
m_pinCountCache += pinCount;
}
return count;
}
@@ -1370,6 +1453,8 @@ const BOX2I LIB_SYMBOL::GetBodyBoundingBox( int aUnit, int aBodyStyle, bool aInc
void LIB_SYMBOL::deleteAllFields()
{
m_drawings[SCH_FIELD_T].clear();
cacheSearchTerms();
cacheChooserFields();
}
@@ -1393,6 +1478,8 @@ void LIB_SYMBOL::SetFields( const std::vector<SCH_FIELD>& aFieldsList )
}
m_drawings.sort();
cacheSearchTerms();
cacheChooserFields();
}
+18 -8
View File
@@ -150,7 +150,7 @@ public:
int GetSubUnitCount() const override { return GetUnitCount(); }
const LIB_ID& GetLibId() const override { return m_libId; }
void SetLibId( const LIB_ID& aLibId ) { m_libId = aLibId; }
void SetLibId( const LIB_ID& aLibId );
LIB_ID GetSourceLibId() const { return m_sourceLibId; }
void SetSourceLibId( const LIB_ID& aLibId ) { m_sourceLibId = aLibId; }
@@ -158,10 +158,7 @@ public:
wxString GetLibNickname() const override { return GetLibraryName(); }
///< Sets the Description field text value
void SetDescription( const wxString& aDescription )
{
GetDescriptionField().SetText( aDescription );
}
void SetDescription( const wxString& aDescription );
///< Gets the Description field text value */
wxString GetDescription() const override
@@ -177,7 +174,7 @@ public:
wxString GetShownDescription( int aDepth = 0 ) const override;
void SetKeyWords( const wxString& aKeyWords ) { m_keyWords = aKeyWords; }
void SetKeyWords( const wxString& aKeyWords );
wxString GetKeyWords() const override
{
@@ -192,7 +189,7 @@ public:
wxString GetShownKeyWords( int aDepth = 0 ) const override;
std::vector<SEARCH_TERM> GetSearchTerms() override;
std::vector<SEARCH_TERM>& GetSearchTerms() override { return m_searchTermsCache; }
void GetChooserFields( std::map<wxString , wxString>& aColumnMap ) override;
@@ -205,7 +202,7 @@ public:
const wxString GetLibraryName() const;
LEGACY_SYMBOL_LIB* GetLib() const { return m_library; }
void SetLib( LEGACY_SYMBOL_LIB* aLibrary ) { m_library = aLibrary; }
void SetLib( LEGACY_SYMBOL_LIB* aLibrary );
timestamp_t GetLastModDate() const { return m_lastModDate; }
@@ -870,6 +867,11 @@ private:
void deleteAllFields();
void cacheSearchTerms();
void cachePinCount();
void cacheShownDescription();
void cacheChooserFields();
private:
std::shared_ptr<LIB_SYMBOL> m_me;
std::weak_ptr<LIB_SYMBOL> m_parent; ///< Use for inherited symbols.
@@ -908,4 +910,12 @@ private:
std::map<int, wxString> m_unitDisplayNames;
std::vector<wxString> m_bodyStyleNames;
// Caches for things that are expensive to compute but required every time
// the symbol chooser or other library list is created
std::vector<SEARCH_TERM> m_searchTermsCache;
int m_pinCountCache;
wxString m_shownDescriptionCache;
std::map<wxString, wxString> m_chooserFieldsCache;
};
+13 -4
View File
@@ -83,15 +83,24 @@ void SYMBOL_TREE_MODEL_ADAPTER::AddLibraries( SCH_BASE_FRAME* aFrame )
COMMON_SETTINGS* cfg = Pgm().GetCommonSettings();
PROJECT_FILE& project = aFrame->Prj().GetProjectFile();
std::unordered_set<wxString> pinned;
std::ranges::copy( cfg->m_Session.pinned_symbol_libs, std::inserter( pinned, pinned.begin() ) );
std::ranges::copy( project.m_PinnedSymbolLibs, std::inserter( pinned, pinned.begin() ) );
auto addFunc =
[&]( const wxString& aLibName, const std::vector<LIB_SYMBOL*>& aSymbolList,
const wxString& aDescription )
{
std::vector<LIB_TREE_ITEM*> treeItems( aSymbolList.begin(), aSymbolList.end() );
bool pinned = alg::contains( cfg->m_Session.pinned_symbol_libs, aLibName )
|| alg::contains( project.m_PinnedSymbolLibs, aLibName );
LIB_TREE_NODE_LIBRARY& lib_node =
DoAddLibraryNode( aLibName, aDescription, pinned.contains( aLibName ) );
DoAddLibrary( aLibName, aDescription, treeItems, pinned, false );
for( LIB_TREE_ITEM* item: aSymbolList )
{
if( item )
lib_node.AddItem( item );
}
lib_node.AssignIntrinsicRanks( m_shownColumns, false );
};
LIBRARY_MANAGER& manager = Pgm().GetLibraryManager();
+1 -2
View File
@@ -631,8 +631,7 @@ bool SYMBOL_VIEWER_FRAME::ReCreateSymbolList()
for( LIB_SYMBOL* symbol : symbols )
{
std::vector<SEARCH_TERM> searchTerms = symbol->GetSearchTerms();
int matched = matcher.ScoreTerms( searchTerms );
int matched = matcher.ScoreTerms( symbol->GetSearchTerms() );
if( filterTerm.IsNumber() && wxAtoi( filterTerm ) == (int)symbol->GetPinCount() )
matched++;
+3 -1
View File
@@ -83,7 +83,7 @@ public:
return m_keywords;
}
std::vector<SEARCH_TERM> GetSearchTerms() override;
std::vector<SEARCH_TERM>& GetSearchTerms() override;
unsigned GetPadCount()
{
@@ -139,6 +139,8 @@ protected:
unsigned m_unique_pad_count; ///< Number of unique pads
wxString m_doc; ///< Footprint description.
wxString m_keywords; ///< Footprint keywords.
std::vector<SEARCH_TERM> m_searchTerms;
};
+1 -1
View File
@@ -56,7 +56,7 @@ public:
*/
virtual void GetChooserFields( std::map<wxString , wxString>& aColumnMap ) {}
virtual std::vector<SEARCH_TERM> GetSearchTerms() { return std::vector<SEARCH_TERM>(); }
virtual std::vector<SEARCH_TERM>& GetSearchTerms() = 0;
/**
* For items having aliases, IsRoot() indicates the principal item.
+2 -1
View File
@@ -26,6 +26,7 @@
#include <vector>
#include <map>
#include <memory>
#include <span>
#include <wx/string.h>
#include <eda_pattern_match.h>
#include <lib_tree_item.h>
@@ -149,7 +150,7 @@ public:
bool m_IsAlreadyPlacedGroup;
protected:
std::vector<SEARCH_TERM> m_sourceSearchTerms;
std::span<SEARCH_TERM> m_sourceSearchTerms;
};
+10 -9
View File
@@ -1684,23 +1684,24 @@ wxString FOOTPRINT::GetTypeName() const
}
std::vector<SEARCH_TERM> FOOTPRINT::GetSearchTerms()
std::vector<SEARCH_TERM>& FOOTPRINT::GetSearchTerms()
{
std::vector<SEARCH_TERM> terms;
m_searchTerms.clear();
m_searchTerms.reserve( 6 );
terms.emplace_back( SEARCH_TERM( GetLibNickname(), 4 ) );
terms.emplace_back( SEARCH_TERM( GetName(), 8 ) );
terms.emplace_back( SEARCH_TERM( GetLIB_ID().Format(), 16 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetLibNickname(), 4 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetName(), 8 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetLIB_ID().Format(), 16 ) );
wxStringTokenizer keywordTokenizer( GetKeywords(), wxS( " \t\r\n" ), wxTOKEN_STRTOK );
while( keywordTokenizer.HasMoreTokens() )
terms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
m_searchTerms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
terms.emplace_back( SEARCH_TERM( GetKeywords(), 1 ) );
terms.emplace_back( SEARCH_TERM( GetLibDescription(), 1 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetKeywords(), 1 ) );
m_searchTerms.emplace_back( SEARCH_TERM( GetLibDescription(), 1 ) );
return terms;
return m_searchTerms;
}
+3 -1
View File
@@ -363,7 +363,7 @@ public:
wxString GetLibNickname() const override { return m_fpid.GetLibNickname(); }
wxString GetDesc() override { return GetLibDescription(); }
int GetPinCount() override { return static_cast<int>( GetUniquePadCount( DO_NOT_INCLUDE_NPTH ) ); }
std::vector<SEARCH_TERM> GetSearchTerms() override;
std::vector<SEARCH_TERM>& GetSearchTerms() override;
wxString GetLibDescription() const { return m_libDescription; }
void SetLibDescription( const wxString& aDesc ) { m_libDescription = aDesc; }
@@ -1388,6 +1388,8 @@ private:
// Optional unit mapping information for multi-unit symbols
std::vector<FP_UNIT_INFO> m_unitInfo;
std::vector<SEARCH_TERM> m_searchTerms;
};
#endif // FOOTPRINT_H
+1 -2
View File
@@ -511,8 +511,7 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
for( FOOTPRINT* footprint : footprints )
{
std::vector<SEARCH_TERM> searchTerms = footprint->GetSearchTerms();
int matched = matcher.ScoreTerms( searchTerms );
int matched = matcher.ScoreTerms( footprint->GetSearchTerms() );
if( filterTerm.IsNumber() && wxAtoi( filterTerm ) == (int)footprint->GetPadCount( DO_NOT_INCLUDE_NPTH ) )
matched++;