Added a Copy Contents button in AI Chat UI globes (since copying directly won't resolve the links automatically).

Fix how `ECODE_SHAREDIR` is set.
Fix a very rare race condition in LSPClientPlugin.
Always resolve line number when locating files (do not enforce the need of the absolute path).
Add aliases for YAML and Markdown.
This commit is contained in:
Martín Lucas Golini
2025-12-28 23:28:59 -03:00
parent f9eb012f77
commit da5ca25297
13 changed files with 113 additions and 93 deletions

View File

@@ -597,7 +597,7 @@ function build_link_configuration( package_name, use_ee_icon )
end
if _OPTIONS["sharedir"] then
defines { "ECODE_SHAREDIR=\"" .. _OPTIONS["sharedir"] .. "\"" }
defines { "ECODE_SHAREDIR='\"" .. _OPTIONS["sharedir"] .. "\"'" }
end
set_ios_config()

View File

@@ -494,7 +494,7 @@ function build_link_configuration( package_name, use_ee_icon )
filter { "options:sharedir" }
if _OPTIONS["sharedir"] then
defines { "ECODE_SHAREDIR=\"" .. _OPTIONS["sharedir"] .. "\"" }
defines { "ECODE_SHAREDIR='\"" .. _OPTIONS["sharedir"] .. "\"'" }
end
filter {}
end

View File

@@ -9,7 +9,7 @@ void addConfigFile() {
{ "Config File",
{ "%.ini$", "%.conf$", "%.desktop$", "%.service$", "%.cfg$", "%.properties$", "%.wrap$",
"%.dev$", "Doxyfile", "%.timer$" },
"%.dev$", "Doxyfile", "%.timer$", "%.rules$" },
{
{ { "%s*#%x%x%x%x%x%x%x%x" }, "string" },
{ { "%s*#%x%x%x%x%x%x" }, "string" },

View File

@@ -192,6 +192,7 @@ void addMarkdown() {
} );
sd.setFoldRangeType( FoldRangeType::Markdown );
sd.setBlockComment( { "<!--", "-->" } );
sd.addAlternativeName( "md" );
}
}}}} // namespace EE::UI::Doc::Language

View File

@@ -94,7 +94,8 @@ SyntaxDefinition& addYAML() {
{ "^%%YAML %d+%.%d+" }
} )
.setFoldRangeType( FoldRangeType::Indentation );
.setFoldRangeType( FoldRangeType::Indentation )
.addAlternativeName( "yml" );
}
}}}} // namespace EE::UI::Doc::Language

View File

@@ -138,9 +138,7 @@
namespace EE { namespace UI { namespace Doc { namespace Language {
void LanguagesSyntaxHighlighting::load() {
auto sdm = SyntaxDefinitionManager::instance();
static void preDefinitionLangsChunk1( SyntaxDefinitionManager* sdm ) {
sdm->addPreDefinition( {
"Ada",
[]() -> SyntaxDefinition& { return addAda(); },
@@ -285,21 +283,6 @@ void LanguagesSyntaxHighlighting::load() {
{ "%.ec$", "%.eh$" },
} );
sdm->addPreDefinition( {
"OpenSCAD",
[]() -> SyntaxDefinition& { return addOpenSCAD(); },
{ "%.scad$" },
} );
sdm->addPreDefinition( {
"Ring",
[]() -> SyntaxDefinition& { return addRing(); },
{ "%.ring$", "%.rh$", "%.rform$" },
} );
sdm->addPreDefinition(
{ "Tcl", []() -> SyntaxDefinition& { return addTcl(); }, { "%.tcl$" } } );
sdm->addPreDefinition(
{ "D", []() -> SyntaxDefinition& { return addD(); }, { "%.d$", "%.di$" } } );
@@ -429,7 +412,9 @@ void LanguagesSyntaxHighlighting::load() {
[]() -> SyntaxDefinition& { return addISPC(); },
{ "%.ispc$", "%.isph$", "%.ih$" },
} );
}
static void preDefinitionLangsChunk2( SyntaxDefinitionManager* sdm ) {
sdm->addPreDefinition(
{ "Jai", []() -> SyntaxDefinition& { return addJai(); }, { "%.jai$" } } );
@@ -571,6 +556,12 @@ void LanguagesSyntaxHighlighting::load() {
sdm->addPreDefinition(
{ "Odin", []() -> SyntaxDefinition& { return addOdin(); }, { "%.odin$" } } );
sdm->addPreDefinition( {
"OpenSCAD",
[]() -> SyntaxDefinition& { return addOpenSCAD(); },
{ "%.scad$" },
} );
sdm->addPreDefinition( {
"Pascal",
[]() -> SyntaxDefinition& { return addPascal(); },
@@ -639,6 +630,12 @@ void LanguagesSyntaxHighlighting::load() {
{ "%.r$", "%.rds$", "%.rda$", "%.rdata$", "%.R$" },
} );
sdm->addPreDefinition( {
"Ring",
[]() -> SyntaxDefinition& { return addRing(); },
{ "%.ring$", "%.rh$", "%.rform$" },
} );
sdm->addPreDefinition(
{ "Racket", []() -> SyntaxDefinition& { return addRacket(); }, { "%.rkt$" } } );
@@ -731,6 +728,9 @@ void LanguagesSyntaxHighlighting::load() {
} );
sdm->addPreDefinition(
{ "Tcl", []() -> SyntaxDefinition& { return addTcl(); }, { "%.tcl$" } } );
sdm->addPreDefinition(
{ "TOML", []() -> SyntaxDefinition& { return addToml(); }, { "%.toml$" } } );
@@ -812,13 +812,12 @@ void LanguagesSyntaxHighlighting::load() {
sdm->addPreDefinition(
{ "Xtend", []() -> SyntaxDefinition& { return addXtend(); }, { "%.xtend$" } } );
sdm->addPreDefinition( {
"YAML",
[]() -> SyntaxDefinition& { return addYAML(); },
{ "%.yml$", "%.yaml$", "^.clangd$" },
{ "^%%YAML %d+%.%d+" },
} );
sdm->addPreDefinition( { "YAML",
[]() -> SyntaxDefinition& { return addYAML(); },
{ "%.yml$", "%.yaml$", "^.clangd$" },
{ "^%%YAML %d+%.%d+" },
"yaml",
{ "yml" } } );
sdm->addPreDefinition( {
"YueScript",
@@ -833,4 +832,12 @@ void LanguagesSyntaxHighlighting::load() {
{ "Zig", []() -> SyntaxDefinition& { return addZig(); }, { "%.zig$" } } );
}
void LanguagesSyntaxHighlighting::load() {
auto sdm = SyntaxDefinitionManager::instance();
// This is to avoid reaching the VTA max-vartrack-size (var-tracking-assignments)
preDefinitionLangsChunk1( sdm );
preDefinitionLangsChunk2( sdm );
}
}}}} // namespace EE::UI::Doc::Language

View File

@@ -4000,6 +4000,8 @@ void App::init( InitParameters& params ) {
Log::info( "%s (codename: \"%s\") initializing", ecode::Version::getVersionFullName(),
ecode::Version::getCodename() );
Log::info( "ecode resources path: %s", mResPath );
if ( mWindow && mWindow->isOpen() ) {
// Only verify GPU driver availability on Windows.
// macOS will have at least a fallback renderer

View File

@@ -196,6 +196,7 @@ static const char* DEFAULT_CHAT_GLOBE = R"xml(
<item>@string(assistant, Assistant)</item>
<item>@string(system, System)</item>
</DropDownList>
<PushButton class="copy_contents" text="@string(copy_contents, Copy Contents)" icon="icon(copy, 12dp)" tooltip="@string(copy_contents, Copy Contents)" />
<PushButton class="move_up" text="@string(move_up, Move Up)" icon="icon(arrow-up-s, 12dp)" tooltip="@string(move_up, Move Up)" />
<PushButton class="move_down" text="@string(move_down, Move Down)" icon="icon(arrow-down-s, 12dp)" tooltip="@string(move_down, Move Down)" />
<PushButton class="erase_but" text="@string(remove_chat, Remove Chat)" icon="icon(chrome-close, 10dp)" tooltip="@string(remove_chat, Remove Chat)" />
@@ -952,6 +953,37 @@ void LLMChatUI::resizeToFit( UICodeEditor* editor ) {
editor->setPixelsSize( editor->getPixelsSize().getWidth(), height );
}
void LLMChatUI::replaceFileLinksToContents( std::string& text ) {
LuaPattern ptrn( "\n```file://([^`]*)```\n" );
PatternMatcher::Range matches[2];
while ( ptrn.matches( text, matches ) ) {
std::string path( text.substr( matches[1].start, matches[1].length() ) );
if ( FileSystem::isRelativePath( path ) ) {
std::string prjPath( getPlugin()->getPluginContext()->getCurrentProject() );
path = prjPath + path;
}
std::string fileBuffer;
TextDocument doc;
if ( FileSystem::fileExists( path ) &&
doc.loadFromFile( path ) == TextDocument::LoadStatus::Loaded ) {
fileBuffer += "\n`" + doc.getFilename() + "`:\n";
fileBuffer += "```" + doc.getSyntaxDefinition().getLSPName();
if ( doc.linesCount() >= 1 && !String::startsWith( doc.line( 0 ).getText(), "\n" ) ) {
fileBuffer += "\n";
}
fileBuffer += doc.getText().toUtf8();
if ( doc.linesCount() >= 1 &&
doc.line( doc.linesCount() - 1 ).getText() != String( "\n" ) ) {
fileBuffer += "\n";
}
fileBuffer += "```\n";
} else {
fileBuffer = path + "has been deleted from the file system.";
}
text.replace( matches[0].start, matches[0].length(), fileBuffer );
}
}
nlohmann::json LLMChatUI::chatToJson( bool forRequest ) {
auto j = nlohmann::json::array();
auto chats = findAllByClass( "llm_conversation" );
@@ -965,37 +997,8 @@ nlohmann::json LLMChatUI::chatToJson( bool forRequest ) {
if ( text.empty() )
continue;
if ( forRequest ) {
LuaPattern ptrn( "\n```file://([^`]*)```\n" );
PatternMatcher::Range matches[2];
while ( ptrn.matches( text, matches ) ) {
std::string path( text.substr( matches[1].start, matches[1].length() ) );
if ( FileSystem::isRelativePath( path ) ) {
std::string prjPath( getPlugin()->getPluginContext()->getCurrentProject() );
path = prjPath + path;
}
std::string fileBuffer;
TextDocument doc;
if ( FileSystem::fileExists( path ) &&
doc.loadFromFile( path ) == TextDocument::LoadStatus::Loaded ) {
fileBuffer += "\n`" + doc.getFilename() + "`:\n";
fileBuffer += "```" + doc.getSyntaxDefinition().getLSPName();
if ( doc.linesCount() >= 1 &&
!String::startsWith( doc.line( 0 ).getText(), "\n" ) ) {
fileBuffer += "\n";
}
fileBuffer += doc.getText().toUtf8();
if ( doc.linesCount() >= 1 &&
doc.line( doc.linesCount() - 1 ).getText() != String( "\n" ) ) {
fileBuffer += "\n";
}
fileBuffer += "```\n";
} else {
fileBuffer = path + "has been deleted from the file system.";
}
text.replace( matches[0].start, matches[0].length(), fileBuffer );
}
}
if ( forRequest )
replaceFileLinksToContents( text );
j.push_back( { { "role", role }, { "content", std::move( text ) } } );
}
@@ -1277,6 +1280,7 @@ void LLMChatUI::toggleEnableChat( UIWidget* chat, bool enabled ) {
chat->findByClass( "erase_but" )->setEnabled( enabled );
chat->findByClass( "move_up" )->setEnabled( enabled );
chat->findByClass( "move_down" )->setEnabled( enabled );
chat->findByClass( "copy_contents" )->setEnabled( enabled );
}
void LLMChatUI::toggleEnableChats( bool enabled ) {
@@ -1358,6 +1362,15 @@ UIWidget* LLMChatUI::addChatUI( LLMChat::Role role ) {
if ( chat->getNodeIndex() < chat->getParent()->getChildCount() - 1 )
chat->toPosition( chat->getNodeIndex() + 1 );
} );
chat->findByClass( "copy_contents" )->onClick( [this, editor]( auto ) {
auto text = editor->getDocument().getText().toUtf8();
if ( text.empty() )
return;
replaceFileLinksToContents( text );
getUISceneNode()->getWindow()->getClipboard()->setText( text );
getPlugin()->getPluginContext()->getNotificationCenter()->addNotification(
i18n( "chat_copied", "Chat Copied" ) );
} );
resizeToFit( editor );
return chat;
}

View File

@@ -193,6 +193,8 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
void hideAttachFile();
void insertFileToDocument( std::string path, std::shared_ptr<TextDocument> cdoc );
void replaceFileLinksToContents( std::string& text );
};
} // namespace ecode

View File

@@ -1960,44 +1960,38 @@ void LSPClientPlugin::drawTop( UICodeEditor* editor, const Vector2f& screenStart
}
void LSPClientPlugin::updateCurrentSymbol( TextDocument& doc ) {
if ( !mBreadcrumb )
if ( !mBreadcrumb || mShuttingDown )
return;
std::vector<DisplaySymbolInfo> symbolsInfo;
URI uri = doc.getURI();
{
mDocSymbolsMutex.lock();
Lock l( mDocSymbolsMutex );
auto symbolsIt = mDocSymbols.find( uri );
if ( symbolsIt == mDocSymbols.end() ) {
mDocSymbolsMutex.unlock();
Lock l( mDocCurrentSymbolsMutex );
mDocCurrentSymbols[uri] = {};
return;
}
if ( symbolsIt != mDocSymbols.end() ) {
LSPSymbolInformationList* list = &symbolsIt->second;
auto sel = doc.getSelection();
LSPSymbolInformationList::iterator foundIt;
LSPSymbolInformationList* list = &symbolsIt->second;
auto sel = doc.getSelection();
LSPSymbolInformationList::iterator foundIt;
bool found = false;
do {
foundIt = std::lower_bound( list->begin(), list->end(), sel,
[]( const LSPSymbolInformation& cur,
const TextRange& sel ) { return cur.range < sel; } );
found = foundIt != list->end() && foundIt->range.contains( sel );
if ( found ) {
symbolsInfo.push_back( { String::fromUtf8( foundIt->name ),
LSPSymbolKindHelper::toIconString( foundIt->kind ) } );
if ( foundIt->children.empty() )
bool found = false;
do {
foundIt =
std::lower_bound( list->begin(), list->end(), sel,
[]( const LSPSymbolInformation& cur, const TextRange& sel ) {
return cur.range < sel;
} );
found = foundIt != list->end() && foundIt->range.contains( sel );
if ( found ) {
symbolsInfo.push_back( { String::fromUtf8( foundIt->name ),
LSPSymbolKindHelper::toIconString( foundIt->kind ) } );
if ( foundIt->children.empty() )
break;
list = &foundIt->children;
} else
break;
list = &foundIt->children;
} else
break;
} while ( found );
mDocSymbolsMutex.unlock();
} while ( found );
}
}
Lock l( mDocCurrentSymbolsMutex );

View File

@@ -27,7 +27,7 @@ class LSPClientPlugin : public Plugin {
public:
static PluginDefinition Definition() {
return { "lspclient", "LSP Client", "Language Server Protocol Client.",
LSPClientPlugin::New, { 0, 2, 9 }, LSPClientPlugin::NewSync };
LSPClientPlugin::New, { 0, 3, 0 }, LSPClientPlugin::NewSync };
}
static Plugin* New( PluginManager* pluginManager );

View File

@@ -26,7 +26,8 @@ UITerminal* TerminalManager::createTerminalInSplitter(
auto splitter = mApp->getSplitter();
auto& config = mApp->getConfig();
if ( config.term.newTerminalOrientation == NewTerminalOrientation::StatusBarPanel ) {
if ( config.term.newTerminalOrientation == NewTerminalOrientation::StatusBarPanel &&
mApp->getStatusTerminalController() ) {
mApp->getStatusTerminalController()->show();
mApp->getStatusTerminalController()->createTerminal();
} else if ( splitter && splitter->hasSplit() ) {

View File

@@ -569,8 +569,7 @@ void UniversalLocator::initLocateBar( UILocateBar* locateBar, UITextInput* locat
path = mApp->getCurrentProject() + path;
if ( !range.isValid() && !FileSystem::isRelativePath( path ) &&
pathHasPosition( mLocateInput->getText() ) &&
String::startsWith( mLocateInput->getText().toUtf8(), path ) ) {
pathHasPosition( mLocateInput->getText() ) ) {
auto pathAndPos = getPathAndPosition( mLocateInput->getText() );
range = { pathAndPos.second, pathAndPos.second };
}