From fa5aa4d006423a736409b1a82fc009541c3dfdfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 11 Jul 2025 01:02:46 -0300 Subject: [PATCH] Add Qbs syntax highlighting (SpartanJ/ecode#553). Add the possibility from inherit patterns and repositories from other syntax definition. --- include/eepp/system/regex.hpp | 2 +- include/eepp/ui/doc/syntaxdefinition.hpp | 8 +- src/eepp/system/regex.cpp | 16 +- src/eepp/ui/doc/syntaxdefinition.cpp | 41 ++- src/eepp/ui/doc/syntaxdefinitionmanager.cpp | 4 +- src/eepp/ui/doc/syntaxtokenizer.cpp | 37 ++- .../src/eepp/ui/doc/languages/qbs.cpp | 311 ++++++++++++++++++ .../src/eepp/ui/doc/languages/qbs.hpp | 11 + .../ui/doc/languagessyntaxhighlighting.cpp | 7 + 9 files changed, 412 insertions(+), 25 deletions(-) create mode 100644 src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/qbs.cpp create mode 100644 src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/qbs.hpp diff --git a/include/eepp/system/regex.hpp b/include/eepp/system/regex.hpp index 9a352ea18..b6d85824a 100644 --- a/include/eepp/system/regex.hpp +++ b/include/eepp/system/regex.hpp @@ -98,7 +98,7 @@ class EE_API RegEx : public PatternMatcher { bool mCached : 1 { false }; bool mFilterOutCaptures : 1 { false }; - bool initWithOnigmo( std::string_view pattern, bool useCache ); + bool initWithOnigumura( std::string_view pattern, bool useCache ); }; }} // namespace EE::System diff --git a/include/eepp/ui/doc/syntaxdefinition.hpp b/include/eepp/ui/doc/syntaxdefinition.hpp index 95d45a929..010b761ce 100644 --- a/include/eepp/ui/doc/syntaxdefinition.hpp +++ b/include/eepp/ui/doc/syntaxdefinition.hpp @@ -113,8 +113,9 @@ struct EE_API SyntaxPattern { } std::string_view getRepositoryName() const { - eeASSERT( isRepositoryInclude() ); - return std::string_view{ patterns[1] }.substr( 1 ); + eeASSERT( isRepositoryInclude() || isSourceInclude() ); + return isSourceInclude() ? std::string_view{ patterns[1] } + : std::string_view{ patterns[1] }.substr( 1 ); } inline bool checkIsIncludePattern() const { @@ -144,13 +145,10 @@ struct EE_API SyntaxPattern { struct EE_API SyntaxRepository { std::vector patterns; - std::string syntax; SyntaxRepository() {} SyntaxRepository( std::vector&& patterns ) : patterns( std::move( patterns ) ) {} - - SyntaxRepository( const std::string& syntax ) : syntax( syntax ) {} }; struct EE_API SyntaxPreDefinition { diff --git a/src/eepp/system/regex.cpp b/src/eepp/system/regex.cpp index ce5c03163..87df47947 100644 --- a/src/eepp/system/regex.cpp +++ b/src/eepp/system/regex.cpp @@ -74,8 +74,8 @@ RegEx::RegEx( std::string_view pattern, Uint32 options, bool useCache ) : return; } - if ( useCache && ( mOptions & Options::AllowFallback ) && - !( mOptions & Options::UseOniguruma ) && RegExCache::instance()->isEnabled() && + if ( useCache && RegExCache::instance()->isEnabled() && ( mOptions & Options::AllowFallback ) && + !( mOptions & Options::UseOniguruma ) && ( mCompiledPattern = RegExCache::instance()->find( pattern, mOptions | Options::UseOniguruma ) ) ) { mValid = true; @@ -85,7 +85,7 @@ RegEx::RegEx( std::string_view pattern, Uint32 options, bool useCache ) : } if ( mOptions & Options::UseOniguruma ) { - initWithOnigmo( pattern, useCache ); + initWithOnigumura( pattern, useCache ); return; } @@ -111,7 +111,7 @@ RegEx::RegEx( std::string_view pattern, Uint32 options, bool useCache ) : pcre2_get_error_message( errornumber, buffer, sizeof( buffer ) ); mValid = false; if ( mOptions & Options::AllowFallback ) { - initWithOnigmo( pattern, useCache ); + initWithOnigumura( pattern, useCache ); } else { Log::debug( "PCRE2 compilation failed at offset " + std::to_string( erroroffset ) + ": " + reinterpret_cast( buffer ) ); @@ -150,7 +150,7 @@ bool RegEx::matches( const char* stringSearch, int stringStartOffset, if ( mOptions & Options::UseOniguruma ) { OnigRegion* region = onig_region_new(); if ( !region ) { - Log::error( "Onigmo: onig_region_new() failed." ); + Log::error( "Onigumura: onig_region_new() failed." ); mMatchNum = 0; return false; } @@ -206,7 +206,7 @@ bool RegEx::matches( const char* stringSearch, int stringStartOffset, } else { // Error UChar errBuf[ONIG_MAX_ERROR_MESSAGE_LEN]; onig_error_code_to_str( errBuf, ret ); - Log::debug( "Onigmo search error: %s", reinterpret_cast( errBuf ) ); + Log::debug( "Onigumura search error: %s", reinterpret_cast( errBuf ) ); onig_region_free( region, 1 ); mMatchNum = 0; return false; @@ -270,7 +270,7 @@ const size_t& RegEx::getNumMatches() const { return mMatchNum; } -bool RegEx::initWithOnigmo( std::string_view pattern, bool useCache ) { +bool RegEx::initWithOnigumura( std::string_view pattern, bool useCache ) { OnigOptionType opt = ONIG_OPTION_NONE; if ( mOptions & Options::Caseless ) @@ -290,7 +290,7 @@ bool RegEx::initWithOnigmo( std::string_view pattern, bool useCache ) { if ( ret != ONIG_NORMAL ) { UChar errBuf[ONIG_MAX_ERROR_MESSAGE_LEN]; onig_error_code_to_str( errBuf, ret, &err ); - Log::info( "Onigmo compilation failed: %s", reinterpret_cast( errBuf ) ); + Log::info( "Onigumura compilation failed: %s", reinterpret_cast( errBuf ) ); mValid = false; if ( mCompiledPattern ) { onig_free( regex ); diff --git a/src/eepp/ui/doc/syntaxdefinition.cpp b/src/eepp/ui/doc/syntaxdefinition.cpp index 20625d032..edfafc331 100644 --- a/src/eepp/ui/doc/syntaxdefinition.cpp +++ b/src/eepp/ui/doc/syntaxdefinition.cpp @@ -73,10 +73,38 @@ static void updatePatternState( SyntaxDefinition& def, SyntaxPattern& ptrn ) { ptrn.flags |= SyntaxPattern::IsRootSelfInclude; } else if ( ptrn.checkIsSourceInclude() && ptrn.patterns[1].size() > 7 ) { ptrn.flags |= SyntaxPattern::IsSourceInclude; - ptrn.syntax = SyntaxDefinitionManager::instance() - ->getByLanguageName( - std::string_view{ ptrn.patterns[1] }.substr( 7 /* "source." */ ) ) - .getLanguageName(); + + if ( def.getRepositoryName( String::hash( ptrn.patterns[1] ) ).empty() ) { + const auto& lang = SyntaxDefinitionManager::instance()->getByLanguageName( + std::string_view{ ptrn.patterns[1] }.substr( 7 /* "source." */ ) ); + + auto syntaxRepoName = ptrn.patterns[1]; + auto patterns = lang.getPatterns(); + for ( auto& iptrn : patterns ) { + if ( iptrn.isRepositoryInclude() ) { + iptrn.flags = SyntaxPattern::IsInclude | SyntaxPattern::IsSourceInclude; + iptrn.patterns[1] = String::format( "%s.%s", syntaxRepoName, + iptrn.patterns[1].substr( 1 ) ); + } + } + def.addRepository( std::move( syntaxRepoName ), { std::move( patterns ) } ); + const auto& repos = lang.getRepositories(); + std::vector>> nrepos; + nrepos.reserve( repos.size() ); + for ( const auto& repo : repos ) { + const auto& repoName = lang.getRepositoryName( repo.first ); + auto newRepoName = String::format( "%s.%s", ptrn.patterns[1], repoName ); + auto npatterns = repo.second.patterns; + for ( auto& iptrn : npatterns ) { + if ( iptrn.isRepositoryInclude() ) { + iptrn.flags = SyntaxPattern::IsInclude | SyntaxPattern::IsSourceInclude; + iptrn.patterns[1] = String::format( "%s.%s", ptrn.patterns[1], + iptrn.patterns[1].substr( 1 ) ); + } + } + nrepos.emplace_back( std::move( newRepoName ), npatterns ); + } + } } else { Log::warning( "updatePatternState unknown include directive: %s", ptrn.patterns[1] ); ptrn.flags &= ~SyntaxPattern::IsInclude; @@ -438,8 +466,7 @@ SyntaxPattern::SyntaxPattern( std::vector&& _patterns, SyntaxDefinition& SyntaxDefinition::addRepository( std::string&& name, SyntaxRepository&& repository ) { - eeASSERT( repository.patterns.size() < std::numeric_limits::max() - 1 && - repository.syntax.empty() ); + eeASSERT( repository.patterns.size() < std::numeric_limits::max() - 1 ); auto hash = String::hash( name ); mRepositoryIndex[hash] = ++mRepositoryIndexCounter; mRepositoryIndexInvert[mRepositoryIndexCounter] = hash; @@ -465,7 +492,7 @@ SyntaxDefinition& SyntaxDefinition::addRepositories( } const SyntaxRepository& SyntaxDefinition::getRepository( String::HashType hash ) const { - static SyntaxRepository EMPTY = SyntaxRepository( "" ); + static SyntaxRepository EMPTY = SyntaxRepository(); auto found = mRepository.find( hash ); return found != mRepository.end() ? found->second : EMPTY; } diff --git a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp index ad2e2f333..da420999c 100644 --- a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp +++ b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp @@ -306,7 +306,7 @@ static json toJson( const SyntaxDefinition& def ) { for ( const auto& [hash, patterns] : def.getRepositories() ) { std::string name = def.getRepositoryName( hash ); - if ( name.starts_with( "$CONTENT_" ) ) + if ( name.starts_with( "$CONTENT_" ) || name.starts_with( "source." ) ) continue; nlohmann::json repo; @@ -513,7 +513,7 @@ namespace EE { namespace UI { namespace Doc { namespace Language { buf += ".addRepositories( {\n"; for ( const auto& repo : def.getRepositories() ) { std::string name = def.getRepositoryName( repo.first ); - if ( name.starts_with( "$CONTENT_" ) ) + if ( name.starts_with( "$CONTENT_" ) || name.starts_with( "source." ) ) continue; buf += "\n{ \"" + name + "\", "; patternsToCPP( buf, repo.second.patterns ); diff --git a/src/eepp/ui/doc/syntaxtokenizer.cpp b/src/eepp/ui/doc/syntaxtokenizer.cpp index e6bd017a1..582d2bdcf 100644 --- a/src/eepp/ui/doc/syntaxtokenizer.cpp +++ b/src/eepp/ui/doc/syntaxtokenizer.cpp @@ -386,6 +386,13 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax int fullMatchEnd = matches[0].end; if ( pattern.matchType == SyntaxPatternMatchType::RegEx ) { + /* Should we do this? + for ( size_t i = 1; i < numMatches; i++ ) { + fullMatchStart = eemin( matches[i].start, fullMatchStart ); + fullMatchEnd = eemax( matches[i].end, fullMatchEnd ); + } + */ + priorityMap.clear(); priorityMap.resize( fullMatchEnd - fullMatchStart, 0 ); @@ -653,13 +660,26 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax #endif patternStack.push_back( { &targetRepo.patterns, 0, - static_cast( innerPtrn->repositoryIdx ) } ); + static_cast( innerPtrn->repositoryIdx ) } ); continue; } else if ( innerPtrn->isRootSelfInclude() ) { if ( patternStack.size() + 1 >= MAX_PATTERN_STACK_SIZE ) break; patternStack.push_back( { &curState.currentSyntax->getPatterns(), 0, 0 } ); continue; + } else if ( innerPtrn->isSourceInclude() ) { + if ( patternStack.size() + 1 >= MAX_PATTERN_STACK_SIZE ) + break; + const auto& targetRepo = + curState.currentSyntax->getRepository( innerPtrn->getRepositoryName() ); + + const auto repoIndex = curState.currentSyntax->getRepositoryIndex( + innerPtrn->getRepositoryName() ); + + patternStack.push_back( + { &targetRepo.patterns, 0, + static_cast( repoIndex ) } ); + continue; } if ( startIdx != 0 && @@ -762,12 +782,25 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax const auto& repo = curState.currentSyntax->getRepository( pattern->getRepositoryName() ); patternStack.push_back( - { &repo.patterns, 0, static_cast( pattern->repositoryIdx ) } ); + { &repo.patterns, 0, + static_cast( pattern->repositoryIdx ) } ); } else if ( pattern->isRootSelfInclude() ) { if ( patternStack.size() + 1 >= MAX_PATTERN_STACK_SIZE ) break; patternStack.push_back( { &curState.currentSyntax->getPatterns(), 0, 0 } ); + } else if ( pattern->isSourceInclude() ) { + if ( patternStack.size() + 1 >= MAX_PATTERN_STACK_SIZE ) + break; + + const auto& repo = + curState.currentSyntax->getRepository( pattern->getRepositoryName() ); + + auto repoIndex = + curState.currentSyntax->getRepositoryIndex( pattern->getRepositoryName() ); + + patternStack.push_back( + { &repo.patterns, 0, static_cast( repoIndex ) } ); } else { if ( startIdx != 0 && pattern->matchType == SyntaxPatternMatchType::LuaPattern && pattern->patterns[0][0] == '^' ) diff --git a/src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/qbs.cpp b/src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/qbs.cpp new file mode 100644 index 000000000..d83c174fe --- /dev/null +++ b/src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/qbs.cpp @@ -0,0 +1,311 @@ +#include +#include + +namespace EE { namespace UI { namespace Doc { namespace Language { + +SyntaxDefinition& addQbs() { + + return SyntaxDefinitionManager::instance() + ->add( + + { "Qbs", + { "%.qbs$" }, + { + { { "include", "#import" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "#object" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "#comment" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + }, + { + + }, + "", + {} + + } ) + .addRepositories( { + + { "obj-declaration", + { + { { "\\b([A-Z][a-z]*([A-Z][a-z]*)*)\\s*\\{", "\\}" }, + { "normal", "type" }, + {}, + "", + SyntaxPatternMatchType::RegEx, + { + { { "include", "obj-attributes" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "#comment" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + } }, + + } }, + { "attr-array", + { + { { "\\b([\\w\\.]*)\\s*:\\s*\\[\\s*", "\\]" }, + { "normal", "parameter" }, + {}, + "", + SyntaxPatternMatchType::RegEx, + { + { { "include", "#object" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "source.js" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + } }, + + } }, + { "comment-contents", + { + { { "\\b(NOTE|TODO|DEBUG|XXX)\\b" }, + "literal", + "", + SyntaxPatternMatchType::RegEx }, + { { "\\b(BUG|FIXME|WARNING)\\b" }, "error", "", SyntaxPatternMatchType::RegEx }, + + } }, + { "attr-block", + { + { { "\\b([\\w\\.]*)\\s*:\\s*\\{\\s*", "\\}" }, + { "normal", "parameter" }, + {}, + "", + SyntaxPatternMatchType::RegEx, + { + { { "include", "source.js" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + } }, + + } }, + { "attr-expr", + { + { { "\\b([\\w\\.]*)\\s*:\\s*(?=[^\\s]+)", ";|$" }, + { "normal", "parameter" }, + {}, + "", + SyntaxPatternMatchType::RegEx, + { + { { "include", "source.js" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + } }, + + } }, + { "comment", + { + { { "(\\/\\/)", "$" }, + { "comment" }, + {}, + "", + SyntaxPatternMatchType::RegEx, + { + { { "include", "#comment-contents" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + } }, + { { "(\\/\\*)", "(\\*\\/)" }, + { "comment" }, + {}, + "", + SyntaxPatternMatchType::RegEx, + { + { { "include", "#comment-contents" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + } }, + + } }, + { "string", + { + { { "'", "'" }, { "string" }, {}, "", SyntaxPatternMatchType::RegEx }, + { { "\"", "\"" }, { "string" }, {}, "", SyntaxPatternMatchType::RegEx }, + + } }, + { "attr-prop", + { + { { "\\b([\\w\\.]*)\\s*:\\s*(?=[A-Z]\\w*\\s*\\{)", "(?=\\})" }, + { "normal", "parameter" }, + {}, + "", + SyntaxPatternMatchType::RegEx, + { + { { "include", "#object" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + } }, + + } }, + { "import", + { + { { "\\b(import)\\b", "$" }, + { "normal", "keyword" }, + {}, + "", + SyntaxPatternMatchType::RegEx, + { + { { "\\b([\\w\\.]+)\\s+(\\d+\\.\\d+)?\\s" }, + { "normal", "normal", "number" }, + {}, + "", + SyntaxPatternMatchType::RegEx }, + { { "\\b(as)\\s+(\\w*)" }, + { "normal", "keyword", "type" }, + {}, + "", + SyntaxPatternMatchType::RegEx }, + { { "include", "#string" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "#comment" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + } }, + + } }, + { "obj-attributes", + { + { { "include", "#attr-prop" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "#attr-array" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "#attr-block" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "#attr-expr" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + } }, + { "obj-method", + { + { { "\\b(?=function)\\b", "(?<=\\})" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::RegEx, + { + { { "include", "source.js" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + } }, + + } }, + { "object", + { + { { "\\b([A-Z][\\w\\.]*)\\s*(\\{|$)", "\\}" }, + { "normal", "type" }, + {}, + "", + SyntaxPatternMatchType::RegEx, + { + { { "include", "$self" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "#obj-property" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "#obj-method" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "#obj-declaration" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + { { "include", "#obj-attributes" }, + { "normal" }, + {}, + "", + SyntaxPatternMatchType::LuaPattern }, + + } }, + + } }, + { "obj-property", + { + { { "\\b(readonly)\\s+(?=property)" }, + "keyword", + "", + SyntaxPatternMatchType::RegEx }, + { { "\\b(property)\\s+([\\w<>]+)(?=\\s+\\w+\\s*:)" }, + { "normal", "keyword", "keyword" }, + {}, + "", + SyntaxPatternMatchType::RegEx }, + { { "\\b(property)\\s+([\\w<>]+)\\s+(\\w+)\\s*$" }, + { "normal", "keyword", "keyword", "parameter" }, + {}, + "", + SyntaxPatternMatchType::RegEx }, + + } }, + } ); +} + +}}}} // namespace EE::UI::Doc::Language diff --git a/src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/qbs.hpp b/src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/qbs.hpp new file mode 100644 index 000000000..17d715515 --- /dev/null +++ b/src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/qbs.hpp @@ -0,0 +1,11 @@ +#ifndef EE_UI_DOC_Qbs +#define EE_UI_DOC_Qbs +#include + +namespace EE { namespace UI { namespace Doc { namespace Language { + +extern SyntaxDefinition& addQbs(); + +}}}} // namespace EE::UI::Doc::Language + +#endif diff --git a/src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languagessyntaxhighlighting.cpp b/src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languagessyntaxhighlighting.cpp index f7e9baac3..2825dff12 100644 --- a/src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languagessyntaxhighlighting.cpp +++ b/src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languagessyntaxhighlighting.cpp @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -555,6 +556,12 @@ void LanguagesSyntaxHighlighting::load() { { "%.ps1$", "%.psm1$", "%.psd1$", "%.ps1xml$", "%.pssc$", "%.psrc$", "%.cdxml$" }, } ); + sdm->addPreDefinition( { + "Qbs", + []() -> SyntaxDefinition& { return addQbs(); }, + { "%.qbs$" }, + } ); + sdm->addPreDefinition( { "QMake", []() -> SyntaxDefinition& { return addQmake(); },