Try fix invalid memory access in SyntaxDefinitions.

Added attach in Chat UI.
Some minor improvements in Http pool implementation.
This commit is contained in:
Martín Lucas Golini
2025-08-12 00:49:13 -03:00
parent 1e95c07a1f
commit 0ec2a4dba9
34 changed files with 443 additions and 187 deletions

View File

@@ -23,6 +23,7 @@ static std::initializer_list<std::string> AIAssistantCommandList = {
"ai-prompt",
"ai-add-chat",
"ai-chat-history",
"ai-attach-file",
"ai-clone-chat",
"ai-settings",
"ai-toggle-private-chat",
@@ -336,6 +337,7 @@ void AIAssistantPlugin::loadAIAssistantConfig( const std::string& path, bool upd
mKeyBindings["ai-show-menu"] = "mod+m";
mKeyBindings["ai-chat-toggle-role"] = "mod+shift+r";
mKeyBindings["ai-refresh-local-models"] = "mod+shift+l";
mKeyBindings["ai-attach-file"] = "mod+shift+a";
}
auto& kb = j["keybindings"];

View File

@@ -46,7 +46,7 @@ class AIAssistantPlugin : public PluginBase {
void onSaveState( IniFile* state ) override;
void setConfig( AIAssistantConfig config ) { mConfig = std::move( config ); }
void setConfig( AIAssistantConfig&& config ) { mConfig = std::move( config ); }
protected:
LLMProviders mProviders;

View File

@@ -1,6 +1,7 @@
#include "chatui.hpp"
#include "../../appconfig.hpp"
#include "../../notificationcenter.hpp"
#include "../../projectdirectorytree.hpp"
#include "../../widgetcommandexecuter.hpp"
#include "aiassistantplugin.hpp"
#include "chathistory.hpp"
@@ -130,6 +131,17 @@ DropDownList.role_ui {
.llm_chatui .image {
tint: var(--font);
}
.llm_chat_attach {
padding: 8dp 8dp 38dp 8dp;
}
.llm_chat_locate_input {
margin-bottom: 2dp;
padding: 0 0 0 4dp;
}
.llm_chat_attach_locate {
border-radius: 8dp;
margin-bottom: 4dp;
}
</style>
<Splitter lw="mp" lh="mp" orientation="vertical" splitter-partition="75%" padding="4dp">
<RelativeLayout lw="mp">
@@ -143,9 +155,13 @@ DropDownList.role_ui {
</RelativeLayout>
<RelativeLayout lw="mp" class="llm_controls" clip="true">
<CodeEditor class="llm_chat_input" lw="mp" lh="mp" />
<vboxce class="llm_chat_attach" lw="mp" lh="mp" visible="false">
<TableView lw="mp" lh="0dp" lw8="1" class="llm_chat_attach_locate" />
<TextInput class="llm_chat_locate_input" lw="mp" lh="18dp" hint='@string(type_to_locate, "Type to Locate")' />
</vboxce>
<hbox lw="mp" lh="wc" layout_gravity="bottom|left" layout_margin="8dp" clip="false">
<PushButton id="llm_user" class="llm_button" text="@string(user, User)" tooltip="@string(change_role, Change Role)" min-width="60dp" margin-right="8dp" />
<!-- <PushButton class="llm_button" text="@string(attach, Attach)" tooltip="@string(attach, Attach)" icon="icon(attach, 14dp)" min-width="32dp" /> -->
<PushButton id="llm_attach" class="llm_button" text="@string(attach, Attach)" tooltip="@string(attach, Attach)" icon="icon(attach, 14dp)" min-width="32dp" />
<PushButton id="llm_chat_history" class="llm_button" text="@string(chat_history, Chat History)" tooltip="@string(chat_history, Chat History)" icon="icon(chat-history, 14dp)" min-width="32dp" />
<PushButton id="llm_settings_but" class="llm_button" text="@string(settings, Settings)" tooltip="@string(settings, Settings)" icon="icon(settings, 14dp)" min-width="32dp" />
<PushButton id="llm_more" class="llm_button" tooltip="@string(more_options, More Options)" icon="icon(more-fill, 14dp)" min-width="32dp" />
@@ -344,6 +360,8 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
setCmd( "ai-chat-history", [this] { showChatHistory(); } );
setCmd( "ai-attach-file", [this] { showAttachFile(); } );
setCmd( "ai-toggle-private-chat", [this] { mChatPrivate->toggleSelection(); } );
setCmd( "ai-toggle-lock-chat", [this] { renameChat( mSummary, true ); } );
@@ -427,9 +445,14 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
mChatHistory = find<UIPushButton>( "llm_chat_history" );
mChatHistory->onClick( [this]( auto ) { showChatHistory(); } );
mChatAttach = find<UIPushButton>( "llm_attach" );
mChatAttach->onClick( [this]( auto ) { showAttachFile(); } );
if ( getPlugin() == nullptr )
return;
initAttachFile();
auto providers = getPlugin()->getProviders();
setProviders( std::move( providers ) );
mCurModel = getDefaultModel();
@@ -462,6 +485,7 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
bindCmds( mChatInput, true );
appendShortcutToTooltip( mChatHistory, "ai-chat-history" );
appendShortcutToTooltip( mChatAttach, "ai-attach-file" );
appendShortcutToTooltip( mChatRun, "ai-prompt" );
appendShortcutToTooltip( mChatStop, "ai-prompt" );
appendShortcutToTooltip( mChatAdd, "ai-add-chat" );
@@ -479,8 +503,8 @@ LLMChatUI::~LLMChatUI() {
if ( getPlugin() ) {
AIAssistantPlugin::AIAssistantConfig config;
config.partition = getSplitter()->getSplitPartition();
config.modelProvider = getCurModel().provider;
config.modelName = getCurModel().name;
config.modelProvider = mCurModel.provider;
config.modelName = mCurModel.name;
getPlugin()->setConfig( std::move( config ) );
}
}
@@ -514,6 +538,7 @@ void LLMChatUI::bindCmds( UICodeEditor* editor, bool bindToChatUI ) {
addKb( editor, "mod+m", "ai-show-menu", bindToChatUI );
addKb( editor, "mod+shift+r", "ai-chat-toggle-role", bindToChatUI );
addKb( editor, "mod+shift+l", "ai-refresh-local-models", bindToChatUI );
addKb( editor, "mod+shift+a", "ai-attach-file", bindToChatUI );
if ( bindToChatUI )
addKb( editor, "mod+shift+return", "ai-add-chat", bindToChatUI );
@@ -539,6 +564,8 @@ void LLMChatUI::showChatHistory() {
if ( plugin == nullptr )
return;
hideAttachFile();
static const char* CHAT_HISTORY_LAYOUT = R"xml(
<window window-flags="shadow|modal|ephemeral"
window-title="@string(ai_conversations_history, AI Conversations History)">
@@ -1332,4 +1359,144 @@ void LLMChatUI::deleteOldConversations( int days ) {
FileSystem::fileRemove( chat.file.getFilepath() );
}
void LLMChatUI::updateLocateBarColumns() {
Float width = eeceil( mLocateTable->getPixelsSize().getWidth() );
width -= mLocateTable->getVerticalScrollBar()->getPixelsSize().getWidth();
mLocateTable->setColumnsVisible( { 0, 1 } );
mLocateTable->setColumnWidth( 0, eeceil( width * 0.5 ) );
mLocateTable->setColumnWidth( 1, width - mLocateTable->getColumnWidth( 0 ) );
}
void LLMChatUI::showAttachFile() {
auto text = mLocateInput->getText();
auto ctx = getPlugin()->getPluginContext();
if ( !ctx->isDirTreeReady() ) {
mLocateTable->setModel( ProjectDirectoryTree::emptyModel( {}, ctx->getCurrentProject() ) );
mLocateTable->getSelection().set( mLocateTable->getModel()->index( 0 ) );
} else if ( !mLocateInput->getText().empty() ) {
ctx->getDirTree()->asyncMatchTree(
ProjectDirectoryTree::MatchType::Fuzzy, text, 100,
[this, text]( auto res ) {
mUISceneNode->runOnMainThread( [this, res] {
mLocateTable->setModel( res );
mLocateTable->getSelection().set( mLocateTable->getModel()->index( 0 ) );
mLocateTable->scrollToTop();
updateLocateBarColumns();
} );
},
ctx->getCurrentProject() );
} else {
mLocateTable->setModel( ctx->getDirTree()->asModel(
100, {}, ctx->getCurrentProject(), Image::getImageExtensionsSupported() ) );
mLocateTable->getSelection().set( mLocateTable->getModel()->index( 0 ) );
}
mLocateBarLayout->setVisible( true );
mLocateInput->setFocus();
updateLocateBarColumns();
}
void LLMChatUI::hideAttachFile() {
mLocateBarLayout->setVisible( false );
}
void LLMChatUI::initAttachFile() {
mLocateBarLayout = findByClass<UIVLinearLayoutCommandExecuter>( "llm_chat_attach" );
mLocateInput = findByClass<UITextInput>( "llm_chat_locate_input" );
mLocateTable = findByClass<UITableView>( "llm_chat_attach_locate" );
mLocateTable->setHeadersVisible( false );
mLocateTable->on( Event::OnSizeChange, [this]( const Event* ) { updateLocateBarColumns(); } );
mLocateInput->on( Event::OnTextChanged, [this]( const Event* ) {
showAttachFile();
updateLocateBarColumns();
} );
mLocateInput->on( Event::OnPressEnter, [this]( const Event* ) {
KeyEvent keyEvent( mLocateTable, Event::KeyDown, KEY_RETURN, SCANCODE_UNKNOWN, 0, 0 );
mLocateTable->forceKeyDown( keyEvent );
} );
mLocateInput->on( Event::KeyDown, [this]( const Event* event ) {
const KeyEvent* keyEvent = static_cast<const KeyEvent*>( event );
mLocateTable->forceKeyDown( *keyEvent );
} );
mLocateBarLayout->setCommand( "close-locatebar", [this] {
hideAttachFile();
if ( mChatInput )
mChatInput->setFocus();
} );
mLocateBarLayout->getKeyBindings().addKeybindsString( {
{ "escape", "close-locatebar" },
} );
mLocateTable->on( Event::KeyDown, [this]( const Event* event ) {
const KeyEvent* keyEvent = static_cast<const KeyEvent*>( event );
if ( keyEvent->getKeyCode() == KEY_ESCAPE )
mLocateBarLayout->execute( "close-locatebar" );
} );
mLocateTable->on( Event::OnModelEvent, [this]( const Event* event ) {
const ModelEvent* modelEvent = static_cast<const ModelEvent*>( event );
if ( modelEvent->getModelEventType() == ModelEventType::Open ) {
Variant vName( modelEvent->getModel()->data(
modelEvent->getModel()->index( modelEvent->getModelIndex().row(), 0 ),
ModelRole::Display ) );
Variant vPath( modelEvent->getModel()->data(
modelEvent->getModel()->index( modelEvent->getModelIndex().row(), 1 ),
ModelRole::Display ) );
if ( !vPath.isValid() )
return;
std::string path( vPath.toString() );
if ( path.empty() )
return;
Variant rangeStr( modelEvent->getModel()->data(
modelEvent->getModel()->index( modelEvent->getModelIndex().row(), 1 ),
ModelRole::Custom ) );
std::string prjPath( getPlugin()->getPluginContext()->getCurrentProject() );
if ( FileSystem::isRelativePath( path ) )
path = prjPath + path;
TextDocument doc;
if ( doc.loadFromFile( path ) == TextDocument::LoadStatus::Loaded ) {
std::string nameToDisplay = doc.getFilename();
if ( !prjPath.empty() && String::startsWith( doc.getFilePath(), prjPath ) ) {
nameToDisplay = doc.getFilePath();
FileSystem::filePathRemoveBasePath( prjPath, nameToDisplay );
}
auto cdoc = mChatInput->getDocumentRef();
cdoc->resetSelection();
cdoc->moveToEndOfLine();
const auto& lineComment = doc.getSyntaxDefinition().getComment();
if ( lineComment.empty() ) {
cdoc->textInput( "\n" + nameToDisplay + ":\n" );
}
cdoc->textInput( "\n```" + doc.getSyntaxDefinition().getLSPName() );
if ( !lineComment.empty() ) {
cdoc->textInput( String::format( " %s %s", lineComment, nameToDisplay ) );
}
auto lineToFold = cdoc->getSelection().end().line();
if ( doc.linesCount() >= 1 &&
!String::startsWith( doc.line( 0 ).getText(), "\n" ) ) {
cdoc->textInput( "\n" );
}
cdoc->textInput( doc.getText() );
if ( doc.linesCount() >= 1 &&
doc.line( doc.linesCount() - 1 ).getText() != String( "\n" ) ) {
cdoc->textInput( "\n" );
}
cdoc->textInput( "```\n" );
cdoc->getFoldRangeService().findRegionsNative();
if ( doc.linesCount() > 30 &&
cdoc->getFoldRangeService().isFoldingRegionInLine( lineToFold ) ) {
mChatInput->toggleFoldUnfold( lineToFold );
}
}
mLocateBarLayout->execute( "close-locatebar" );
}
} );
}
} // namespace ecode

View File

@@ -16,6 +16,7 @@ class UIScrollView;
class UISceneNode;
class UIDropDownList;
class UIPushButton;
class UITableView;
}} // namespace EE::UI
namespace EE { namespace Graphics {
@@ -28,6 +29,7 @@ using namespace EE::Graphics;
namespace ecode {
class UIVLinearLayoutCommandExecuter;
class AIAssistantPlugin;
class LLMChat {
@@ -103,9 +105,14 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
UIPushButton* mChatHistory{ nullptr };
UIPushButton* mChatMore{ nullptr };
UIPushButton* mRefreshModels{ nullptr };
UIPushButton* mChatAttach{ nullptr };
UISelectButton* mChatPrivate{ nullptr };
UIScrollView* mChatScrollView{ nullptr };
UIDropDownList* mModelDDL{ nullptr };
UIVLinearLayoutCommandExecuter* mLocateBarLayout{ nullptr };
UITextInput* mLocateInput{ nullptr };
UITableView* mLocateTable{ nullptr };
std::unique_ptr<LLMChatCompletionRequest> mRequest;
std::unique_ptr<LLMChatCompletionRequest> mSummaryRequest;
LLMProviders mProviders;
@@ -175,6 +182,14 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
bool bindToChatUI = false, bool searchDefined = true );
void deleteOldConversations( int days );
void initAttachFile();
void updateLocateBarColumns();
void showAttachFile();
void hideAttachFile();
};
} // namespace ecode

View File

@@ -1407,8 +1407,12 @@ AutoCompletePlugin::SymbolsList AutoCompletePlugin::getDocumentSymbols( TextDocu
auto lineCount = doc->linesCount();
std::string string;
for ( Int64 i = 0; i < static_cast<Int64>( lineCount ); i++ ) {
if ( doc->getLineLength( i ) > MAX_LINE_LENGTH )
auto len = doc->getLineLength( i );
if ( len == 0 || len > MAX_LINE_LENGTH ) {
if ( len == 0 ) // Line count must have changed
lineCount = doc->linesCount();
continue;
}
doc->getLineTextToBufferUtf8( i, string );
for ( auto& match : pattern.gmatch( string ) ) {
std::string matchStr( match[0] );

View File

@@ -152,7 +152,7 @@ DebuggerPlugin::~DebuggerPlugin() {
}
if ( mSidePanel && mTab ) {
if ( Engine::isRunninMainThread() )
if ( Engine::isMainThread() )
mSidePanel->removeTab( mTab );
else {
auto sidePanel = mSidePanel;
@@ -2341,7 +2341,7 @@ void DebuggerPlugin::setUIDebuggingState( StatusDebuggerController::State state
}
if ( mPanelBoxButtons.box ) {
if ( Engine::isRunninMainThread() )
if ( Engine::isMainThread() )
updatePanelUIState( state );
else
mPanelBoxButtons.box->runOnMainThread( [this, state] { updatePanelUIState( state ); } );

View File

@@ -385,7 +385,7 @@ void FormatterPlugin::formatDoc( UICodeEditor* editor ) {
std::shared_ptr<TextDocument> doc = editor->getDocumentRef();
auto pos = doc->getSelection();
auto scroll = editor->getScroll();
doc->resetCursor();
doc->resetSelection();
doc->selectAll();
doc->setRunningTransaction( true );
doc->textInput( data, false );
@@ -425,7 +425,7 @@ void FormatterPlugin::runFormatter( UICodeEditor* editor, const Formatter& forma
std::shared_ptr<TextDocument> doc = editor->getDocumentRef();
TextPosition pos = doc->getSelection().start();
auto scroll = editor->getScroll();
doc->resetCursor();
doc->resetSelection();
doc->selectAll();
doc->setRunningTransaction( true );
doc->textInput( res.result, false );
@@ -471,7 +471,7 @@ void FormatterPlugin::runFormatter( UICodeEditor* editor, const Formatter& forma
std::shared_ptr<TextDocument> doc = editor->getDocumentRef();
TextPosition pos = doc->getSelection().start();
auto scroll = editor->getScroll();
doc->resetCursor();
doc->resetSelection();
doc->selectAll();
doc->setRunningTransaction( true );
doc->textInput( data, false );

View File

@@ -520,7 +520,7 @@ void processFormattingResponse( const std::shared_ptr<TextDocument>& doc,
std::vector<LSPTextEdit> edits ) {
TextRanges ranges = doc->getSelections();
doc->resetCursor();
doc->resetSelection();
doc->setRunningTransaction( true );
// Sort from bottom to top, this way we don't have to compute any position deltas

View File

@@ -1374,7 +1374,7 @@ void LSPClientServer::notifyServerInitialized() {
}
bool LSPClientServer::needsAsync() {
return Engine::isRunninMainThread();
return Engine::isMainThread();
}
bool LSPClientServer::isRunning() {

View File

@@ -132,6 +132,8 @@ class PluginContextProvider {
virtual std::string getDefaultFileDialogFolder() const = 0;
virtual AppConfig& getConfig() = 0;
virtual bool isDirTreeReady() const = 0;
};
} // namespace ecode