Fix Objective-C highlighter.

Added Objective-C++ highlighter (SpartanJ/ecode#616).
Replaced "Treat .h file as C++" in favor of "Treat .h files as..." with multiple options, including auto-detection (SpartanJ/ecode#614).
Refresh semantic-highlighting after syntax highlighter change.
This commit is contained in:
Martín Lucas Golini
2025-08-17 16:56:12 -03:00
parent cb9f6420ba
commit dbb36cdcf4
23 changed files with 713 additions and 94 deletions

View File

@@ -13,8 +13,8 @@
"url": "https://prettier.io"
},
{
"language": ["c", "cpp", "objective-c", "java"],
"file_patterns": ["%.inl$", "%.cpp$", "%.hpp$", "%.cc$", "%.cxx$", "%.c++$", "%.hh$", "%.hxx$", "%.h++$", "%.objcpp$", "%.m$", "%.java$"],
"language": ["c", "cpp", "objective-c", "objective-cpp", "java"],
"file_patterns": ["%.inl$", "%.cpp$", "%.hpp$", "%.cc$", "%.cxx$", "%.c++$", "%.hh$", "%.hxx$", "%.h++$", "%.objcpp$", "%.m$", "%.mm$", "%.java$"],
"command": "clang-format --style=file $FILENAME",
"url": "https://clang.llvm.org/docs/ClangFormat.html",
"prefer_lsp_formatter": true

View File

@@ -92,6 +92,12 @@
"share_process": true,
"file_patterns": ["%.m$"]
},
{
"language": "objective-cpp",
"use": "clangd",
"share_process": true,
"file_patterns": ["%.mm$"]
},
{
"language": "d",
"name": "serve-d",

View File

@@ -0,0 +1,16 @@
#pragma once
#include <string>
namespace EE { namespace UI { namespace Doc {
enum class HExtLanguageType { AutoDetect, C, CPP, ObjectiveC, ObjectiveCPP };
struct HExtLanguageTypeHelper {
static HExtLanguageType detectLanguage( const std::string& buffer );
static std::string toString( HExtLanguageType langType );
static HExtLanguageType fromString( const std::string& langType );
};
}}} // namespace EE::UI::Doc

View File

@@ -5,6 +5,7 @@
#include <eepp/system/iostream.hpp>
#include <eepp/system/pack.hpp>
#include <eepp/system/singleton.hpp>
#include <eepp/ui/doc/hextlanguagetype.hpp>
#include <eepp/ui/doc/syntaxdefinition.hpp>
#include <optional>
#include <vector>
@@ -33,13 +34,14 @@ class EE_API SyntaxDefinitionManager {
bool extensionCanRepresentManyLanguages( std::string extension ) const;
const SyntaxDefinition& getByExtension( const std::string& filePath,
bool hFileAsCPP = false ) const;
const SyntaxDefinition& getByExtension( const std::string& filePath ) const;
const SyntaxDefinition& getByHeader( const std::string& header, bool hFileAsCPP = false ) const;
const SyntaxDefinition&
getByHeader( const std::string& header, const std::string& filePath = "",
HExtLanguageType hLangType = HExtLanguageType::AutoDetect ) const;
const SyntaxDefinition& find( const std::string& filePath, const std::string& header,
bool hFileAsCPP = false );
HExtLanguageType hLangType = HExtLanguageType::AutoDetect );
const SyntaxDefinition& findFromString( const std::string_view& str ) const;
@@ -91,6 +93,10 @@ class EE_API SyntaxDefinitionManager {
mutable Mutex mMutex;
std::optional<size_t> getLanguageIndex( const std::string& langName );
const SyntaxDefinition* needsHFallback( HExtLanguageType langType, const std::string& lspName,
const std::string& ext,
const std::string& buffer ) const;
};
}}} // namespace EE::UI::Doc

View File

@@ -14,6 +14,7 @@
#include <eepp/system/time.hpp>
#include <eepp/system/uuid.hpp>
#include <eepp/ui/doc/foldrangeservice.hpp>
#include <eepp/ui/doc/hextlanguagetype.hpp>
#include <eepp/ui/doc/syntaxdefinition.hpp>
#include <eepp/ui/doc/textdocumentline.hpp>
#include <eepp/ui/doc/textformat.hpp>
@@ -650,9 +651,9 @@ class EE_API TextDocument {
TextRange getActiveClientVisibleRange() const;
bool hAsCpp() const;
HExtLanguageType hExtLanguageType() const;
void setHAsCpp( bool hAsCpp );
void setHExtLanguageType( HExtLanguageType hExtLanguageType );
const Uint64& getModificationId() const;
@@ -728,7 +729,7 @@ class EE_API TextDocument {
bool mSaving{ false };
bool mDeleteOnClose{ false };
bool mMightBeBinary{ false };
bool mHAsCpp{ false };
HExtLanguageType mHExtLanguageType{ false };
bool mLastCursorChangeWasInteresting{ false };
bool mDoingTextInput{ false };
bool mInsertingText{ false };

View File

@@ -975,6 +975,9 @@
../../src/eepp/ui/css/stylesheetvariable.cpp
../../src/eepp/ui/css/timingfunction.cpp
../../src/eepp/ui/css/transitiondefinition.cpp
../../src/eepp/ui/doc/documentview.cpp
../../src/eepp/ui/doc/foldrangeservice.cpp
../../src/eepp/ui/doc/hextlanguagetype.cpp
../../src/eepp/ui/doc/languages/cmake.cpp
../../src/eepp/ui/doc/languages/cmake.hpp
../../src/eepp/ui/doc/languages/configfile.cpp
@@ -1483,6 +1486,8 @@
../../src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/objeck.hpp
../../src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/objective-c.cpp
../../src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/objective-c.hpp
../../src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/objective-cpp.cpp
../../src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/objective-cpp.hpp
../../src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/ocaml.cpp
../../src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/ocaml.hpp
../../src/modules/languages-syntax-highlighting/src/eepp/ui/doc/languages/odin.cpp

View File

@@ -0,0 +1,174 @@
#include <array>
#include <sstream>
#include <eepp/ui/doc/hextlanguagetype.hpp>
namespace EE { namespace UI { namespace Doc {
// Checks if a character is a "word boundary".
inline bool isWordBoundary( char c ) {
return !std::isalnum( static_cast<unsigned char>( c ) ) && c != '_';
}
// Finds a keyword as a whole word (e.g., finds "class" but not "subclass").
bool findStandaloneWord( const std::string& line, std::string_view keyword ) {
size_t pos = 0;
while ( ( pos = line.find( keyword, pos ) ) != std::string::npos ) {
// Check character before the keyword
bool preBoundary = ( pos == 0 ) || isWordBoundary( line[pos - 1] );
// Check character after the keyword
size_t endPos = pos + keyword.length();
bool postBoundary = ( endPos == line.length() ) || isWordBoundary( line[endPos] );
if ( preBoundary && postBoundary ) {
return true; // Found a standalone occurrence
}
pos = endPos; // Continue search after this instance
}
return false;
}
/**
* @brief Analyzes the content of a header file to determine its programming language.
*
* This function scans a string buffer for language-specific keywords and syntax
* to distinguish between C, C++, Objective-C, and Objective-C++. It uses a
* fast, heuristic-based approach optimized with std::array and std::string_view.
*
* @param buffer A std::string containing the source code from the .h file.
* @return HExtLanguageType The detected language.
*/
HExtLanguageType HExtLanguageTypeHelper::detectLanguage( const std::string& buffer ) {
bool hasCppFeature = false;
bool hasObjcFeature = false;
// Objective-C features
static constexpr std::array<std::string_view, 6> objcKeywords = {
"@interface", "@protocol", "@property", "@implementation", "@end", "@class" };
static constexpr std::string_view objcDirective = "#import";
// C++ features
static constexpr std::array<std::string_view, 9> cppStandaloneKeywords = {
"class", "namespace", "template", "virtual", "override",
"final", "public", "private", "protected" };
static constexpr std::array<std::string_view, 2> cppTokens = { "::", "template<" };
std::stringstream ss( buffer );
std::string line;
bool inMultilineComment = false;
while ( std::getline( ss, line ) ) {
std::string processedLine = line;
if ( inMultilineComment ) {
size_t endCommentPos = processedLine.find( "*/" );
if ( endCommentPos != std::string::npos ) {
processedLine = processedLine.substr( endCommentPos + 2 );
inMultilineComment = false;
} else {
continue;
}
}
size_t startCommentPos = processedLine.find( "/*" );
if ( startCommentPos != std::string::npos ) {
size_t endCommentPos = processedLine.find( "*/", startCommentPos );
if ( endCommentPos != std::string::npos ) {
processedLine.erase( startCommentPos, endCommentPos - startCommentPos + 2 );
} else {
inMultilineComment = true;
processedLine = processedLine.substr( 0, startCommentPos );
}
}
size_t commentPos = processedLine.find( "//" );
if ( commentPos != std::string::npos ) {
processedLine = processedLine.substr( 0, commentPos );
}
if ( !hasObjcFeature ) {
std::string_view lineView = processedLine;
lineView.remove_prefix(
std::min( lineView.find_first_not_of( " \t" ), lineView.size() ) );
if ( lineView.rfind( objcDirective, 0 ) == 0 ) {
hasObjcFeature = true;
} else {
for ( const auto& keyword : objcKeywords ) {
if ( processedLine.find( keyword ) != std::string::npos ) {
hasObjcFeature = true;
break;
}
}
}
}
if ( !hasCppFeature ) {
for ( const auto& keyword : cppStandaloneKeywords ) {
if ( findStandaloneWord( processedLine, keyword ) ) {
hasCppFeature = true;
break;
}
}
if ( !hasCppFeature ) {
for ( const auto& token : cppTokens ) {
if ( processedLine.find( token ) != std::string::npos ) {
hasCppFeature = true;
break;
}
}
}
if ( !hasCppFeature ) {
size_t ampPos = processedLine.find( '&' );
if ( ampPos != std::string::npos && ampPos + 1 < processedLine.length() &&
processedLine[ampPos + 1] != '&' ) {
hasCppFeature = true;
}
}
}
if ( hasCppFeature && hasObjcFeature )
break;
}
if ( hasCppFeature && hasObjcFeature )
return HExtLanguageType::ObjectiveCPP;
if ( hasObjcFeature )
return HExtLanguageType::ObjectiveC;
if ( hasCppFeature )
return HExtLanguageType::CPP;
return HExtLanguageType::C;
}
std::string HExtLanguageTypeHelper::toString( HExtLanguageType langType ) {
switch ( langType ) {
case HExtLanguageType::AutoDetect:
return "autodetect";
case HExtLanguageType::C:
return "c";
case HExtLanguageType::CPP:
return "cpp";
case HExtLanguageType::ObjectiveC:
return "objective-c";
case HExtLanguageType::ObjectiveCPP:
return "objective-cpp";
}
return "autodetect";
}
HExtLanguageType HExtLanguageTypeHelper::fromString( const std::string& langType ) {
if ( langType == "c" )
return HExtLanguageType::C;
if ( langType == "cpp" )
return HExtLanguageType::CPP;
if ( langType == "objective-c" )
return HExtLanguageType::ObjectiveC;
if ( langType == "objective-cpp" )
return HExtLanguageType::ObjectiveCPP;
return HExtLanguageType::AutoDetect;
}
}}} // namespace EE::UI::Doc

View File

@@ -1231,8 +1231,31 @@ bool SyntaxDefinitionManager::extensionCanRepresentManyLanguages( std::string ex
return false;
}
const SyntaxDefinition& SyntaxDefinitionManager::getByExtension( const std::string& filePath,
bool hFileAsCPP ) const {
const SyntaxDefinition* SyntaxDefinitionManager::needsHFallback( HExtLanguageType langType,
const std::string& lspName,
const std::string& ext,
const std::string& buffer ) const {
if ( lspName != "c" || !( ext == "h" || ext == ".h" || ext == "%.h$" || ext == "%.h%.in$" ) )
return nullptr;
switch ( langType ) {
case HExtLanguageType::AutoDetect: {
return &getByLSPName( HExtLanguageTypeHelper::toString(
HExtLanguageTypeHelper::detectLanguage( buffer ) ) );
}
case HExtLanguageType::C:
return &getByLSPName( "c" );
case HExtLanguageType::CPP:
return &getByLSPName( "cpp" );
case HExtLanguageType::ObjectiveC:
return &getByLSPName( "objective-c" );
case HExtLanguageType::ObjectiveCPP:
return &getByLSPName( "objective-cpp" );
}
return nullptr;
}
const SyntaxDefinition&
SyntaxDefinitionManager::getByExtension( const std::string& filePath ) const {
std::string extension( FileSystem::fileExtension( filePath ) );
std::string fileName( FileSystem::fileNameFromPath( filePath ) );
@@ -1266,10 +1289,6 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByExtension( const std::stri
LuaPattern words( ext );
int start, end;
if ( words.find( fileName, start, end ) ) {
if ( hFileAsCPP && definition->getLSPName() == "c" &&
( ext == "%.h$" || ext == "%.h%.in$" ) )
return getByLSPName( "cpp" );
if ( extHasMultipleLangs && !definition->hasExtensionPriority() ) {
def = definition.get();
continue;
@@ -1278,9 +1297,6 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByExtension( const std::stri
return *definition.get();
}
} else if ( extension == ext ) {
if ( hFileAsCPP && definition->getLSPName() == "c" && ext == ".h" )
return getByLSPName( "cpp" );
if ( extHasMultipleLangs && !definition->hasExtensionPriority() ) {
def = definition.get();
continue;
@@ -1299,10 +1315,6 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByExtension( const std::stri
LuaPattern words( ext );
int start, end;
if ( words.find( fileName, start, end ) ) {
if ( hFileAsCPP && preDefinition.getLSPName() == "c" &&
( ext == "%.h$" || ext == "%.h%.in$" ) )
return getByLSPName( "cpp" );
if ( extHasMultipleLangs && !preDefinition.hasExtensionPriority() ) {
def = &preDefinition.load();
continue;
@@ -1311,9 +1323,6 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByExtension( const std::stri
return preDefinition.load();
}
} else if ( extension == ext ) {
if ( hFileAsCPP && preDefinition.getLSPName() == "c" && ext == ".h" )
return getByLSPName( "cpp" );
if ( extHasMultipleLangs && !preDefinition.hasExtensionPriority() ) {
def = &preDefinition.load();
continue;
@@ -1329,12 +1338,19 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByExtension( const std::stri
}
const SyntaxDefinition& SyntaxDefinitionManager::getByHeader( const std::string& header,
bool /*hFileAsCPP*/ ) const {
const std::string& filePath,
HExtLanguageType langType ) const {
if ( !header.empty() ) {
{
Lock l( mMutex );
for ( auto definition = mDefinitions.rbegin(); definition != mDefinitions.rend();
++definition ) {
auto needsHDef = needsHFallback( langType, definition->get()->getLSPName(),
FileSystem::fileExtension( filePath ), header );
if ( needsHDef )
return *needsHDef;
for ( const auto& hdr : definition->get()->getHeaders() ) {
LuaPattern words( hdr );
int start, end;
@@ -1347,6 +1363,11 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByHeader( const std::string&
for ( auto preDefinition = mPreDefinitions.rbegin();
preDefinition != mPreDefinitions.rend(); ++preDefinition ) {
auto needsHDef = needsHFallback( langType, preDefinition->getLSPName(),
FileSystem::fileExtension( filePath ), header );
if ( needsHDef )
return *needsHDef;
for ( const auto& hdr : preDefinition->getHeaders() ) {
LuaPattern words( hdr );
int start, end;
@@ -1361,10 +1382,10 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByHeader( const std::string&
const SyntaxDefinition& SyntaxDefinitionManager::find( const std::string& filePath,
const std::string& header,
bool hFileAsCPP ) {
const SyntaxDefinition& def = getByHeader( header );
HExtLanguageType langType ) {
const SyntaxDefinition& def = getByHeader( header, filePath, langType );
if ( def.getLanguageName() == mDefinitions[0]->getLanguageName() )
return getByExtension( filePath, hFileAsCPP );
return getByExtension( filePath );
return def;
}

View File

@@ -519,16 +519,23 @@ bool TextDocument::hasSyntaxDefinition() const {
}
const SyntaxDefinition& TextDocument::guessSyntax() const {
String header( getText( { { 0, 0 }, positionOffset( { 0, 0 }, 128 ) } ) );
return SyntaxDefinitionManager::instance()->find( mFilePath, header, mHAsCpp );
String header( getText(
{ { 0, 0 },
positionOffset( { 0, 0 },
FileSystem::fileExtension( mFilePath ) == "h" ? 5 * 1024 : 128 ) } ) );
return SyntaxDefinitionManager::instance()->find( mFilePath, header, mHExtLanguageType );
}
void TextDocument::resetSyntax() {
String header( getText( { { 0, 0 }, positionOffset( { 0, 0 }, 128 ) } ) );
String header( getText(
{ { 0, 0 },
positionOffset( { 0, 0 },
FileSystem::fileExtension( mFilePath ) == "h" ? 5 * 1024 : 128 ) } ) );
std::string oldDef = mSyntaxDefinition.getLSPName();
{
Lock l( mSyntaxDefinitionMutex );
mSyntaxDefinition = SyntaxDefinitionManager::instance()->find( mFilePath, header, mHAsCpp );
mSyntaxDefinition =
SyntaxDefinitionManager::instance()->find( mFilePath, header, mHExtLanguageType );
}
if ( mSyntaxDefinition.getLSPName() != oldDef )
notifySyntaxDefinitionChange();
@@ -2238,12 +2245,12 @@ TextRange TextDocument::getActiveClientVisibleRange() const {
return {};
}
bool TextDocument::hAsCpp() const {
return mHAsCpp;
HExtLanguageType TextDocument::hExtLanguageType() const {
return mHExtLanguageType;
}
void TextDocument::setHAsCpp( bool hAsCpp ) {
mHAsCpp = hAsCpp;
void TextDocument::setHExtLanguageType( HExtLanguageType hExtLanguageType ) {
mHExtLanguageType = hExtLanguageType;
}
const Uint64& TextDocument::getModificationId() const {

View File

@@ -12,34 +12,131 @@ SyntaxDefinition& addObjectiveC() {
{
{ { "//.-\n" }, "comment" },
{ { "/%*", "%*/" }, "comment" },
{ { "#", "\n" }, "comment" },
{ { "\"", "\"", "\\" }, "string" },
{ { "(#%s*include)%s+([<%\"][%w%d%.%\\%/%_%-]+[>%\"])" },
{ "keyword", "keyword", "literal" } },
{ { "(#%s*import)%s+([<%\"][%w%d%.%\\%/%_%-]+[>%\"])" },
{ "keyword", "keyword", "literal" } },
{ { "\"", "[\"\n]", "\\" }, "string" },
{ { "'", "'", "\\" }, "string" },
{ { "-?0x%x+" }, "number" },
{ { "-?%d+[%d%.eE]*f?" }, "number" },
{ { "-?%.?%d+f?" }, "number" },
{ { "c_number_parser" }, "number", "", SyntaxPatternMatchType::Parser },
{ { "[%+%-=/%*%^%%<>!~|&]" }, "operator" },
{ { "(if|for|while|switch|sizeof|_Alignof|defined)\\s*(?=\\()" },
{ "normal", "keyword", "keyword" },
"",
SyntaxPatternMatchType::RegEx },
{ { "[%a_][%w_]*%f[(]" }, "function" },
{ { "@[%a_][%w_]*" }, "type" },
{ { "[%a_][%w_]*" }, "symbol" },
{ { "[@#]?[%a_][%w_]*" }, "symbol" },
},
{
{ "elseif", "keyword" }, { "int", "type" }, { "then", "keyword" },
{ "unsigned", "type" }, { "continue", "keyword" }, { "default", "keyword" },
{ "volatile", "keyword" }, { "switch", "keyword" }, { "char", "type" },
{ "double", "type" }, { "auto", "keyword" }, { "bool", "type" },
{ "return", "keyword" }, { "extern", "keyword" }, { "NULL", "literal" },
{ "long", "type" }, { "void", "keyword" }, { "union", "keyword" },
{ "short", "type" }, { "static", "keyword" }, { "inline", "keyword" },
{ "false", "literal" }, { "for", "keyword" }, { "goto", "keyword" },
{ "while", "keyword" }, { "const", "keyword" }, { "typedef", "keyword" },
{ "true", "literal" }, { "else", "keyword" }, { "enum", "keyword" },
{ "nil", "literal" }, { "float", "type" }, { "struct", "keyword" },
{ "break", "keyword" }, { "case", "keyword" }, { "if", "keyword" },
// C
{ "elseif", "keyword" },
{ "int", "type" },
{ "then", "keyword" },
{ "unsigned", "type" },
{ "continue", "keyword" },
{ "default", "keyword" },
{ "volatile", "keyword" },
{ "switch", "keyword" },
{ "char", "type" },
{ "double", "type" },
{ "auto", "keyword" },
{ "bool", "type" },
{ "return", "keyword" },
{ "extern", "keyword" },
{ "NULL", "literal" },
{ "long", "type" },
{ "void", "keyword" },
{ "union", "keyword" },
{ "short", "type" },
{ "static", "keyword" },
{ "inline", "keyword" },
{ "false", "literal" },
{ "for", "keyword" },
{ "goto", "keyword" },
{ "while", "keyword" },
{ "const", "keyword" },
{ "int32_t", "type" },
{ "int16_t", "type" },
{ "uint16_t", "type" },
{ "typedef", "keyword" },
{ "true", "literal" },
{ "else", "keyword" },
{ "uint32_t", "type" },
{ "enum", "keyword" },
{ "int8_t", "type" },
{ "int64_t", "type" },
{ "float", "type" },
{ "struct", "keyword" },
{ "break", "keyword" },
{ "uint8_t", "type" },
{ "uint64_t", "type" },
{ "case", "keyword" },
{ "if", "keyword" },
{ "do", "keyword" },
},
{ "#if", "keyword" },
{ "#ifdef", "keyword" },
{ "#ifndef", "keyword" },
{ "#elif", "keyword" },
{ "#else", "keyword" },
{ "#endif", "keyword" },
{ "#define", "keyword" },
{ "#undef", "keyword" },
{ "#include", "keyword" },
{ "#line", "keyword" },
{ "#error", "keyword" },
{ "#pragma", "keyword" },
{ "#warning ", "keyword" },
{ "#elifdef ", "keyword" },
{ "#elifndef ", "keyword" },
// Objective-C
{ "retain", "keyword" },
{ "release", "keyword" },
{ "autorelease", "keyword" },
{ "@interface", "keyword" },
{ "@implementation", "keyword" },
{ "@protocol", "keyword" },
{ "@end", "keyword" },
{ "@class", "keyword" },
{ "@property", "keyword" },
{ "@synthesize", "keyword" },
{ "@dynamic", "keyword" },
{ "@autoreleasepool", "keyword" },
{ "@try", "keyword" },
{ "@catch", "keyword" },
{ "@finally", "keyword" },
{ "@throw", "keyword" },
{ "@public", "keyword" },
{ "@protected", "keyword" },
{ "@private", "keyword" },
{ "@package", "keyword" },
{ "@selector", "keyword" },
{ "@synchronized", "keyword" },
{ "@optional", "keyword" },
{ "@required", "keyword" },
{ "@encode", "keyword" },
{ "id", "type" },
{ "Class", "type" },
{ "SEL", "type" },
{ "nil", "literal" },
{ "Nil", "literal" },
{ "YES", "literal" },
{ "NO", "literal" },
{ "strong", "keyword" },
{ "weak", "keyword" },
{ "copy", "keyword" },
{ "nonatomic", "keyword" },
{ "assign", "keyword" },
{ "unsafe_unretained", "keyword" },
{ "bycopy", "keyword" },
{ "byref", "keyword" },
{ "in", "keyword" },
{ "out", "keyword" },
{ "inout", "keyword" } },
"//",
{}

View File

@@ -0,0 +1,234 @@
#include <eepp/ui/doc/languages/objective-c.hpp>
#include <eepp/ui/doc/syntaxdefinitionmanager.hpp>
namespace EE { namespace UI { namespace Doc { namespace Language {
SyntaxDefinition& addObjectiveCPP() {
auto& sd = SyntaxDefinitionManager::instance()->add(
{ "Objective-C++",
{ "%.mm$" },
{
{ { "R%\"(xml)%(", "%)(xml)%\"" }, { "string", "type", "type" }, "XML" },
{ { "R%\"(css)%(", "%)(css)%\"" }, { "string", "type", "type" }, "CSS" },
{ { "R%\"(html)%(", "%)(html)%\"" }, { "string", "type", "type" }, "HTML" },
{ { "R%\"(json)%(", "%)(json)%\"" }, { "string", "type", "type" }, "JSON" },
{ { "R\"[%a-\"]+%(", "%)[%a-\"]+%\"" }, "string" },
{ { "R\"%(", "%)\"" }, "string" },
{ { "//.-\n" }, "comment" },
{ { "/%*", "%*/" }, "comment" },
{ { "\"", "[\"\n]", "\\" }, "string" },
{ { "'", "'", "\\" }, "string" },
{ { "(#%s*include)%s+([<%\"][%w%d%.%\\%/%_%-]+[>%\"])" },
{ "keyword", "keyword", "literal" } },
{ { "(#%s*import)%s+([<%\"][%w%d%.%\\%/%_%-]+[>%\"])" },
{ "keyword", "keyword", "literal" } },
{ { "cpp_number_parser" }, "number", "", SyntaxPatternMatchType::Parser },
{ { "[%+%-=/%*%^%%<>!~|&]" }, "operator" },
{ { "(if|for|while|switch|catch|sizeof|typeid|defined|return|throw|alignof|noexcept|"
"new|delete)"
"\\s*(?=\\()" },
{ "normal", "keyword", "keyword" },
"",
SyntaxPatternMatchType::RegEx },
{ { "[%a_][%w_]*%f[(]" }, "function" },
{ { "std%:%:[%w_]*" }, "type" },
{ { "(%[)(%[)(%a[%w_]+)(%])(%])" },
{ "normal", "keyword", "parameter", "type", "parameter", "keyword" } },
{ { "[@#]?[%a_][%w_]*" }, "symbol" },
},
{
// C++
{ "int", "type" },
{ "then", "keyword" },
{ "new", "keyword" },
{ "reinterpret_cast", "keyword" },
{ "this", "keyword" },
{ "long", "type" },
{ "Uint64", "type" },
{ "Vector2i", "type" },
{ "String", "type" },
{ "Int8", "type" },
{ "short", "type" },
{ "nullptr", "keyword" },
{ "dynamic_cast", "keyword" },
{ "for", "keyword" },
{ "goto", "keyword" },
{ "while", "keyword" },
{ "const", "keyword" },
{ "enum", "keyword" },
{ "break", "keyword" },
{ "Uint32", "type" },
{ "Uint8", "type" },
{ "or", "keyword" },
{ "compl", "keyword" },
{ "char8_t", "type" },
{ "friend", "keyword" },
{ "alignof", "keyword" },
{ "volatile", "keyword" },
{ "noexcept", "keyword" },
{ "decltype", "keyword" },
{ "auto", "type" },
{ "constinit", "keyword" },
{ "class", "keyword" },
{ "constexpr", "keyword" },
{ "void", "keyword" },
{ "char16_t", "type" },
{ "static", "keyword" },
{ "inline", "keyword" },
{ "typedef", "keyword" },
{ "const_cast", "keyword" },
{ "co_return", "keyword" },
{ "throw", "keyword" },
{ "true", "type" },
{ "float", "type" },
{ "Vector2f", "type" },
{ "bitand", "keyword" },
{ "size_t", "type" },
{ "Uint16", "type" },
{ "case", "keyword" },
{ "mutable", "keyword" },
{ "protected", "keyword" },
{ "do", "keyword" },
{ "continue", "keyword" },
{ "asm", "keyword" },
{ "default", "keyword" },
{ "Recti", "type" },
{ "char", "type" },
{ "bool", "type" },
{ "Rectf", "type" },
{ "requires", "keyword" },
{ "extern", "keyword" },
{ "not_eq", "keyword" },
{ "static_cast", "keyword" },
{ "namespace", "keyword" },
{ "union", "keyword" },
{ "xor", "keyword" },
{ "Int64", "type" },
{ "false", "type" },
{ "int32_t", "type" },
{ "int16_t", "type" },
{ "uint16_t", "type" },
{ "concept", "keyword" },
{ "typename", "keyword" },
{ "else", "keyword" },
{ "co_yield", "keyword" },
{ "uint32_t", "type" },
{ "operator", "keyword" },
{ "Int32", "type" },
{ "struct", "keyword" },
{ "if", "keyword" },
{ "and_eq", "keyword" },
{ "Int16", "type" },
{ "xor_eq", "keyword" },
{ "elseif", "keyword" },
{ "public", "keyword" },
{ "virtual", "keyword" },
{ "unsigned", "type" },
{ "and", "keyword" },
{ "Float", "type" },
{ "private", "keyword" },
{ "or_eq", "keyword" },
{ "switch", "keyword" },
{ "using", "keyword" },
{ "double", "type" },
{ "typeid", "keyword" },
{ "delete", "keyword" },
{ "return", "keyword" },
{ "NULL", "literal" },
{ "static_assert", "keyword" },
{ "try", "keyword" },
{ "consteval", "keyword" },
{ "Color", "type" },
{ "register", "keyword" },
{ "explicit", "keyword" },
{ "catch", "keyword" },
{ "co_wait", "keyword" },
{ "override", "keyword" },
{ "not", "keyword" },
{ "template", "keyword" },
{ "int64_t", "type" },
{ "wchar_t", "type" },
{ "bitor", "keyword" },
{ "thread_local", "keyword" },
{ "uint64_t", "type" },
{ "char32_t", "type" },
{ "alignas", "keyword" },
{ "export", "keyword" },
{ "ssize_t", "type" },
{ "signed", "type" },
{ "#if", "keyword" },
{ "#ifdef", "keyword" },
{ "#ifndef", "keyword" },
{ "#else", "keyword" },
{ "#elif", "keyword" },
{ "#elifdef", "keyword" }, // C++23
{ "#elifndef", "keyword" }, // C++23
{ "#endif", "keyword" },
{ "#include", "keyword" },
{ "#define", "keyword" },
{ "#undef", "keyword" },
{ "#line", "keyword" },
{ "#error", "keyword" },
{ "#pragma", "keyword" },
{ "#warning", "keyword" },
// Objective-C
{ "retain", "keyword" },
{ "release", "keyword" },
{ "autorelease", "keyword" },
{ "@interface", "keyword" },
{ "@implementation", "keyword" },
{ "@protocol", "keyword" },
{ "@end", "keyword" },
{ "@class", "keyword" },
{ "@property", "keyword" },
{ "@synthesize", "keyword" },
{ "@dynamic", "keyword" },
{ "@autoreleasepool", "keyword" },
{ "@try", "keyword" },
{ "@catch", "keyword" },
{ "@finally", "keyword" },
{ "@throw", "keyword" },
{ "@public", "keyword" },
{ "@protected", "keyword" },
{ "@private", "keyword" },
{ "@package", "keyword" },
{ "@selector", "keyword" },
{ "@synchronized", "keyword" },
{ "@optional", "keyword" },
{ "@required", "keyword" },
{ "@encode", "keyword" },
{ "id", "type" },
{ "Class", "type" },
{ "SEL", "type" },
{ "nil", "literal" },
{ "Nil", "literal" },
{ "YES", "literal" },
{ "NO", "literal" },
{ "strong", "keyword" },
{ "weak", "keyword" },
{ "copy", "keyword" },
{ "nonatomic", "keyword" },
{ "assign", "keyword" },
{ "unsafe_unretained", "keyword" },
{ "bycopy", "keyword" },
{ "byref", "keyword" },
{ "in", "keyword" },
{ "out", "keyword" },
{ "inout", "keyword" } },
"//",
{},
"objective-cpp"
} );
sd.setFoldRangeType( FoldRangeType::Braces ).setFoldBraces( { { '{', '}' } } );
return sd;
}
}}}} // namespace EE::UI::Doc::Language

View File

@@ -0,0 +1,12 @@
#ifndef EE_UI_DOC_ObjectiveCPP
#define EE_UI_DOC_ObjectiveCPP
#include <eepp/ui/doc/syntaxdefinition.hpp>
namespace EE { namespace UI { namespace Doc { namespace Language {
extern SyntaxDefinition& addObjectiveCPP();
}}}} // namespace EE::UI::Doc::Language
#endif

View File

@@ -72,6 +72,7 @@
#include <eepp/ui/doc/languages/nix.hpp>
#include <eepp/ui/doc/languages/objeck.hpp>
#include <eepp/ui/doc/languages/objective-c.hpp>
#include <eepp/ui/doc/languages/objective-cpp.hpp>
#include <eepp/ui/doc/languages/ocaml.hpp>
#include <eepp/ui/doc/languages/odin.hpp>
#include <eepp/ui/doc/languages/openscad.hpp>
@@ -522,6 +523,12 @@ void LanguagesSyntaxHighlighting::load() {
{ "%.m$" },
} );
sdm->addPreDefinition( { "Objective-C++",
[]() -> SyntaxDefinition& { return addObjectiveCPP(); },
{ "%.mm$" },
{},
"objective-cpp" } );
sdm->addPreDefinition( {
"OCaml",
[]() -> SyntaxDefinition& { return addOCaml(); },

View File

@@ -497,7 +497,8 @@ void AppConfig::saveProject( std::string projectFolder, UICodeEditorSplitter* ed
IniFile cfg( projectCfgPath, false );
cfg.setValue( "path", "folder_path", projectFolder );
cfg.setValueB( "document", "use_global_settings", docConfig.useGlobalSettings );
cfg.setValueB( "document", "h_as_cpp", docConfig.hAsCPP );
cfg.setValue( "document", "h_ext_language_type",
HExtLanguageTypeHelper::toString( docConfig.hExtLanguageType ) );
cfg.setValueB( "document", "trim_trailing_whitespaces", docConfig.doc.trimTrailingWhitespaces );
cfg.setValueB( "document", "force_new_line_at_end_of_file",
docConfig.doc.forceNewLineAtEndOfFile );
@@ -761,7 +762,14 @@ void AppConfig::loadProject( std::string projectFolder, UICodeEditorSplitter* ed
IniFile cfg( projectCfgPath );
docConfig.useGlobalSettings = cfg.getValueB( "document", "use_global_settings", true );
docConfig.hAsCPP = cfg.getValueB( "document", "h_as_cpp", false );
if ( cfg.getValue( "document", "h_ext_language_type", "" ) == "" &&
cfg.getValueB( "document", "h_as_cpp", false ) == true ) {
docConfig.hExtLanguageType = HExtLanguageType::CPP;
} else {
docConfig.hExtLanguageType = HExtLanguageTypeHelper::fromString(
cfg.getValue( "document", "h_ext_language_type", "autodetect" ) );
}
docConfig.doc.trimTrailingWhitespaces =
cfg.getValueB( "document", "trim_trailing_whitespaces", false );
docConfig.doc.forceNewLineAtEndOfFile =

View File

@@ -129,7 +129,7 @@ struct GlobalSearchBarConfig {
struct ProjectDocumentConfig {
bool useGlobalSettings{ true };
bool hAsCPP{ false };
HExtLanguageType hExtLanguageType{ HExtLanguageType::AutoDetect };
DocumentConfig doc;
ProjectDocumentConfig() {}
ProjectDocumentConfig( const DocumentConfig& doc ) { this->doc = doc; }

View File

@@ -2577,7 +2577,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) {
UITab* tab = (UITab*)editor->getData();
tab->setIcon( icon->getSize( mMenuIconSize ) );
}
editor->getDocument().setHAsCpp( mProjectDocConfig.hAsCPP );
editor->getDocument().setHExtLanguageType( mProjectDocConfig.hExtLanguageType );
auto ext = editor->getDocument().getFileInfo().getExtension();
if ( SyntaxDefinitionManager::instance()->extensionCanRepresentManyLanguages( ext ) ) {

View File

@@ -695,7 +695,7 @@ void GlobalSearchController::updateGlobalSearchBarResults(
const std::string& search, std::shared_ptr<ProjectSearch::ResultModel> model,
bool searchReplace, bool isEscaped ) {
updateGlobalSearchBar();
mGlobalSearchTree->hAsCPP = mApp->getProjectDocConfig().hAsCPP;
mGlobalSearchTree->hExtLanguageType = mApp->getProjectDocConfig().hExtLanguageType;
mGlobalSearchTree->setSearchStr( search );
mGlobalSearchTree->setModel( model );
if ( mGlobalSearchTree->getModel()->rowCount() < 50 )

View File

@@ -103,6 +103,10 @@ void LSPDocumentClient::onDocumentClosed( TextDocument* ) {
void LSPDocumentClient::onDocumentDirtyOnFileSystem( TextDocument* ) {}
void LSPDocumentClient::onDocumentSyntaxDefinitionChange( const SyntaxDefinition& ) {
requestSemanticHighlightingDelayed( true );
}
void LSPDocumentClient::onDocumentMoved( TextDocument* ) {
refreshTag();
}

View File

@@ -39,6 +39,7 @@ class LSPDocumentClient : public TextDocument::Client, public FoldRangeProvider
virtual void onDocumentMoved( TextDocument* );
virtual void onDocumentReloaded( TextDocument* );
virtual void onDocumentReset( TextDocument* );
virtual void onDocumentSyntaxDefinitionChange( const SyntaxDefinition& );
Client::Type getTextDocumentClientType() { return TextDocument::Client::Auxiliary; }
void notifyOpen();

View File

@@ -1986,11 +1986,10 @@ void SettingsMenu::updateProjectSettingsMenu() {
mSettingsMenu->getItemId( "project_settings" )
->setEnabled( !mApp->getCurrentProject().empty() );
mProjectMenu->getItemId( "h_as_cpp" )
->asType<UIMenuCheckBox>()
->setActive( mApp->getProjectDocConfig().hAsCPP );
mDocMenu->getItemId( "project_doc_settings" )->setEnabled( !mApp->getCurrentProject().empty() );
auto item = mHExtLanguageTypeMenu->find(
HExtLanguageTypeHelper::toString( mApp->getProjectDocConfig().hExtLanguageType ) );
if ( item && item->isType( UI_TYPE_MENURADIOBUTTON ) )
item->asType<UIMenuRadioButton>()->setActive( true );
for ( size_t i = 0; i < mProjectDocMenu->getCount(); i++ ) {
mProjectDocMenu->getItem( i )->setEnabled( !mApp->getCurrentProject().empty() &&
@@ -2516,31 +2515,47 @@ void SettingsMenu::createProjectMenu() {
owner->on( Event::OnMenuShow,
[owner, this]( auto ) { mProjectDocMenu->setOwnerNode( owner ); } );
mProjectMenu
->addCheckBox( i18n( "h_as_cpp", "Treat .h files as C++ code." ),
mApp->getProjectDocConfig().hAsCPP )
->setId( "h_as_cpp" );
HExtLanguageType hExtLanguageType = mApp->getProjectDocConfig().hExtLanguageType;
mHExtLanguageTypeMenu = UIPopUpMenu::New();
mHExtLanguageTypeMenu->setId( "h_ext_files_submenu" );
mHExtLanguageTypeMenu
->addRadioButton( i18n( "auto-detect", "Auto-Detect" ),
hExtLanguageType == HExtLanguageType::AutoDetect )
->setId( "autodetect" );
mHExtLanguageTypeMenu->addRadioButton( "C", hExtLanguageType == HExtLanguageType::C )
->setId( "c" );
mHExtLanguageTypeMenu->addRadioButton( "C++", hExtLanguageType == HExtLanguageType::CPP )
->setId( "cpp" );
mHExtLanguageTypeMenu
->addRadioButton( "Objective-C", hExtLanguageType == HExtLanguageType::ObjectiveC )
->setId( "objective-c" );
mHExtLanguageTypeMenu
->addRadioButton( "Objective-C++", hExtLanguageType == HExtLanguageType::ObjectiveCPP )
->setId( "objective-cpp" );
mProjectMenu->on( Event::OnItemClicked, [this]( const Event* event ) {
mProjectMenu->addSubMenu( i18n( "treat_h_files_as_ellipsis", "Treat .h files as..." ), nullptr,
mHExtLanguageTypeMenu );
mHExtLanguageTypeMenu->on( Event::OnItemClicked, [this]( const Event* event ) {
if ( event->getNode()->isType( UI_TYPE_MENU_SEPARATOR ) ||
event->getNode()->isType( UI_TYPE_MENUSUBMENU ) )
return;
const String& id = event->getNode()->getId();
if ( event->getNode()->isType( UI_TYPE_MENUCHECKBOX ) ) {
if ( event->getNode()->isType( UI_TYPE_MENURADIOBUTTON ) ) {
UIMenuCheckBox* item = event->getNode()->asType<UIMenuCheckBox>();
if ( "h_as_cpp" == id ) {
mApp->getProjectDocConfig().hAsCPP = item->isActive();
mApp->getSplitter()->forEachEditor( [this]( UICodeEditor* editor ) {
editor->getDocument().setHAsCpp( mApp->getProjectDocConfig().hAsCPP );
if ( editor->getDocument().getFileInfo().getExtension() == "h" ) {
editor->resetSyntaxDefinition();
if ( mSplitter->isCurEditor( editor ) )
updateCurrentFileType();
}
} );
updateProjectSettingsMenu();
}
mApp->getProjectDocConfig().hExtLanguageType =
HExtLanguageTypeHelper::fromString( item->getId() );
mApp->getSplitter()->forEachEditor( [this]( UICodeEditor* editor ) {
editor->getDocument().setHExtLanguageType(
mApp->getProjectDocConfig().hExtLanguageType );
if ( editor->getDocument().getFileInfo().getExtension() == "h" ) {
editor->resetSyntaxDefinition();
if ( mSplitter->isCurEditor( editor ) )
updateCurrentFileType();
}
} );
updateProjectSettingsMenu();
}
} );
}

View File

@@ -113,6 +113,7 @@ class SettingsMenu {
UIPopUpMenu* mProjectTreeMenu{ nullptr };
UIPopUpMenu* mProjectDocMenu{ nullptr };
UIPopUpMenu* mProjectMenu{ nullptr };
UIPopUpMenu* mHExtLanguageTypeMenu{ nullptr };
UIPopUpMenu* mEditMenu{ nullptr };
UIPopUpMenu* mHelpMenu{ nullptr };
UIPopUpMenu* mLineWrapMenu{ nullptr };

View File

@@ -39,7 +39,7 @@ UITreeViewGlobalSearch::UITreeViewGlobalSearch( const SyntaxColorScheme& colorSc
UIWidget* UITreeViewGlobalSearch::createCell( UIWidget* rowWidget, const ModelIndex& index ) {
UITableCell* widget = index.column() == (Int64)getModel()->treeColumn()
? UITreeViewCellGlobalSearch::New( mSearchReplace, hAsCPP )
? UITreeViewCellGlobalSearch::New( mSearchReplace, hExtLanguageType )
: UITableCell::New();
return setupCell( widget, rowWidget, index );
}
@@ -128,8 +128,10 @@ ProjectSearch::ResultData* UITreeViewCellGlobalSearch::getResultDataPtr() {
#define CELL_GLOBAL_SEARCH_PADDING ( 12 )
UITreeViewCellGlobalSearch::UITreeViewCellGlobalSearch( bool selectionEnabled, bool hAsCPP ) :
UITreeViewCell( selectionEnabled ? getCheckBoxFn() : nullptr ), mHAsCpp( hAsCPP ) {}
UITreeViewCellGlobalSearch::UITreeViewCellGlobalSearch( bool selectionEnabled,
HExtLanguageType hExtLanguageType ) :
UITreeViewCell( selectionEnabled ? getCheckBoxFn() : nullptr ),
mHExtLanguageType( hExtLanguageType ) {}
UIPushButton* UITreeViewCellGlobalSearch::setText( const String& text ) {
auto* result = getResultPtr();
@@ -154,7 +156,7 @@ UIPushButton* UITreeViewCellGlobalSearch::updateText( const std::string& text )
(ProjectSearch::ResultData*)getCurIndex().parent().internalData();
const auto& styleDef =
SyntaxDefinitionManager::instance()->getByExtension( res->file, mHAsCpp );
SyntaxDefinitionManager::instance()->getByExtension( res->file );
Uint32 from = text.find_first_not_of( ' ' );
Uint32 to = from;

View File

@@ -12,11 +12,12 @@ namespace ecode {
class UITreeViewCellGlobalSearch : public UITreeViewCell {
public:
static UITreeViewCellGlobalSearch* New( bool selectionEnabled, bool hAsCPP ) {
return eeNew( UITreeViewCellGlobalSearch, ( selectionEnabled, hAsCPP ) );
static UITreeViewCellGlobalSearch* New( bool selectionEnabled,
HExtLanguageType hExtLanguageType ) {
return eeNew( UITreeViewCellGlobalSearch, ( selectionEnabled, hExtLanguageType ) );
}
explicit UITreeViewCellGlobalSearch( bool selectionEnabled, bool hAsCPP );
explicit UITreeViewCellGlobalSearch( bool selectionEnabled, HExtLanguageType hExtLanguageType );
UIPushButton* setText( const String& text );
@@ -29,7 +30,7 @@ class UITreeViewCellGlobalSearch : public UITreeViewCell {
protected:
std::pair<size_t, size_t> mSearchStrPos;
String mResultStr;
bool mHAsCpp{ false };
HExtLanguageType mHExtLanguageType{ HExtLanguageType::AutoDetect };
std::function<UITextView*( UIPushButton* )> getCheckBoxFn();
@@ -62,7 +63,8 @@ class UITreeViewGlobalSearch : public UITreeView {
const String& getSearchStr() const { return mSearchStr; }
bool hAsCPP = false;
HExtLanguageType hExtLanguageType{ HExtLanguageType::AutoDetect };
protected:
Color mLineNumColor;
SyntaxColorScheme mColorScheme;