From 477b2a7a884d831125ca09873deadc48bd89835b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Tue, 21 Feb 2023 03:44:54 -0300 Subject: [PATCH] ecode: Closes SpartanJ/ecode#54. --- include/eepp/ui/uicodeeditor.hpp | 3 +- src/eepp/system/sys.cpp | 11 ++ src/eepp/ui/uicodeeditor.cpp | 5 +- src/tools/ecode/appconfig.cpp | 15 ++- src/tools/ecode/ecode.cpp | 119 ++++++++++++++++-- src/tools/ecode/ecode.hpp | 4 +- .../ecode/plugins/linter/linterplugin.cpp | 2 + .../ecode/plugins/linter/linterplugin.hpp | 1 + 8 files changed, 141 insertions(+), 19 deletions(-) diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index a80d8d17d..e8c343e64 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -460,7 +460,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void setColorPreview( bool colorPreview ); - void goToLine( const TextPosition& position, bool centered = true ); + void goToLine( const TextPosition& position, bool centered = true, + bool forceExactPosition = false, bool scrollX = true ); bool getAutoCloseBrackets() const; diff --git a/src/eepp/system/sys.cpp b/src/eepp/system/sys.cpp index 4ef22c066..9c8c18a52 100644 --- a/src/eepp/system/sys.cpp +++ b/src/eepp/system/sys.cpp @@ -1091,10 +1091,21 @@ static ULONG_PTR GetParentProcessId() { } return (ULONG_PTR)-1; } + +static bool isWineRunning() { + HMODULE hntdll = GetModuleHandle( "ntdll.dll" ); + if ( !hntdll ) + return false; + void* pwine_get_version = (void*)GetProcAddress( hntdll, "wine_get_version" ); + return pwine_get_version != NULL; +} #endif bool Sys::windowAttachConsole() { #if EE_PLATFORM == EE_PLATFORM_WIN + // WINE doesn't need to attach any console + if ( isWineRunning() ) + return true; ULONG_PTR ppid = GetParentProcessId(); if ( ppid == (ULONG_PTR)-1 ) { return false; diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 27b9c0a20..5ee522da0 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -1454,9 +1454,10 @@ void UICodeEditor::updateScrollBar() { setScrollY( mScroll.y ); } -void UICodeEditor::goToLine( const TextPosition& position, bool centered ) { +void UICodeEditor::goToLine( const TextPosition& position, bool centered, bool forceExactPosition, + bool scrollX ) { mDoc->setSelection( position ); - scrollTo( mDoc->getSelection().start(), centered ); + scrollTo( mDoc->getSelection().start(), centered, forceExactPosition, scrollX ); } bool UICodeEditor::getAutoCloseBrackets() const { diff --git a/src/tools/ecode/appconfig.cpp b/src/tools/ecode/appconfig.cpp index 2a3e07799..2a657ece4 100644 --- a/src/tools/ecode/appconfig.cpp +++ b/src/tools/ecode/appconfig.cpp @@ -386,8 +386,11 @@ static void loadDocuments( UICodeEditorSplitter* editorSplitter, std::shared_ptr editor->setDocument( tab->getOwnedWidget()->asType()->getDocumentRef() ); editorSplitter->removeUnusedTab( curTabWidget ); - editor->getDocument().setSelection( selection ); - editor->scrollToCursor(); + if ( !editor->getDocument().getSelection().isValid() || + editor->getDocument().getSelection() == TextRange( { 0, 0 }, { 0, 0 } ) ) { + editor->getDocument().setSelection( selection ); + editor->scrollToCursor(); + } if ( curTabWidget->getTabCount() == totalToLoad ) curTabWidget->setTabSelected( eeclamp( currentPage, 0, curTabWidget->getTabCount() - 1 ) ); @@ -396,8 +399,12 @@ static void loadDocuments( UICodeEditorSplitter* editorSplitter, std::shared_ptr path, pool, [curTabWidget, selection, totalToLoad, currentPage]( UICodeEditor* editor, const std::string& ) { - editor->getDocument().setSelection( selection ); - editor->scrollToCursor(); + if ( !editor->getDocument().getSelection().isValid() || + editor->getDocument().getSelection() == + TextRange( { 0, 0 }, { 0, 0 } ) ) { + editor->getDocument().setSelection( selection ); + editor->scrollToCursor(); + } if ( curTabWidget->getTabCount() == totalToLoad ) curTabWidget->setTabSelected( eeclamp( currentPage, 0, curTabWidget->getTabCount() - 1 ) ); diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 66c56a6aa..ef14f5c03 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -30,6 +30,43 @@ bool firstFrame = true; bool firstUpdate = true; App* appInstance = nullptr; +static bool pathHasPosition( const std::string& path ) { +#if EE_PLATFORM == EE_PLATFORM_WIN + return std::count( path.begin(), path.end(), ':' ) > 1; +#else + return std::count( path.begin(), path.end(), ':' ) > 0; +#endif +} + +static std::pair getPathAndPosition( const std::string& path ) { + if ( pathHasPosition( path ) ) { + auto parts = String::split( path, ':' ); + if ( parts.size() >= 2 ) { + Int64 line = 0; + Int64 col = 0; +#if EE_PLATFORM == EE_PLATFORM_WIN + size_t partCount = 4; +#else + size_t partCount = 3; +#endif + int linePos = parts.size() >= partCount ? parts.size() - 2 : parts.size() - 1; + int colPos = parts.size() >= partCount ? parts.size() - 1 : -1; + if ( String::fromString( line, parts[linePos] ) ) { + if ( colPos > 0 ) + String::fromString( col, parts[colPos] ); + } + std::string npath( parts[0] ); + if ( parts.size() >= 2 ) { + for ( Int64 i = 1; i < linePos; i++ ) { + npath += ":" + parts[i]; + } + } + return { npath, { eemax( (Int64)0, line - 1 ), col } }; + } + } + return { path, { 0, 0 } }; +} + void appLoop() { appInstance->mainLoop(); } @@ -2179,7 +2216,7 @@ void App::consoleToggle() { mSplitter->getCurWidget()->setFocus(); } -void App::initProjectTreeView( const std::string& path ) { +void App::initProjectTreeView( std::string path ) { mProjectTreeView = mUISceneNode->find( "project_view" ); mProjectTreeView->setColumnsHidden( { FileSystemModel::Icon, FileSystemModel::Size, FileSystemModel::Group, @@ -2257,6 +2294,14 @@ void App::initProjectTreeView( const std::string& path ) { return 0; } ); + bool hasPosition = pathHasPosition( path ); + TextPosition initialPosition; + if ( hasPosition ) { + auto pathAndPosition = getPathAndPosition( path ); + path = pathAndPosition.first; + initialPosition = pathAndPosition.second; + } + if ( !path.empty() ) { if ( FileSystem::isDirectory( path ) ) { loadFolder( path ); @@ -2282,10 +2327,19 @@ void App::initProjectTreeView( const std::string& path ) { if ( mFileSystemListener ) mFileSystemListener->setFileSystemModel( mFileSystemModel ); + std::function + forcePosition; + if ( initialPosition.isValid() ) { + forcePosition = [initialPosition]( UICodeEditor* editor, const auto& ) { + editor->runOnMainThread( + [initialPosition, editor] { editor->goToLine( initialPosition ); } ); + }; + } + if ( FileSystem::fileExists( rpath ) ) { - loadFileFromPath( rpath, false ); + loadFileFromPath( rpath, false, nullptr, forcePosition ); } else if ( FileSystem::fileCanWrite( folderPath ) ) { - loadFileFromPath( path, false ); + loadFileFromPath( path, false, nullptr, forcePosition ); } } } @@ -2465,7 +2519,7 @@ FontTrueType* App::loadFont( const std::string& name, std::string fontPath, void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDensity, const std::string& colorScheme, bool terminal, bool frameBuffer, bool benchmarkMode, const std::string& css, bool health, const std::string& healthLang, - FeaturesHealth::OutputFormat healthFormat ) { + FeaturesHealth::OutputFormat healthFormat, const std::string& fileToOpen ) { DisplayManager* displayManager = Engine::instance()->getDisplayManager(); Display* currentDisplay = displayManager->getDisplayIndex( 0 ); mDisplayDPI = currentDisplay->getDPI(); @@ -3117,13 +3171,50 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe file = ""; #endif - if ( terminal && file.empty() ) { + if ( terminal && file.empty() && fileToOpen.empty() ) { showSidePanel( false ); mTerminalManager->createNewTerminal(); } else { initProjectTreeView( file ); } + if ( !fileToOpen.empty() ) { + auto fileAndPos = getPathAndPosition( fileToOpen ); + auto tab = mSplitter->isDocumentOpen( fileAndPos.first, false, true ); + + if ( tab ) { + tab->getTabWidget()->setTabSelected( tab ); + if ( tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) { + UICodeEditor* editor = tab->getOwnedWidget()->asType(); + if ( editor->getDocument().isLoading() ) { + Uint32 cb = editor->on( + Event::OnDocumentLoaded, [fileAndPos]( const Event* event ) { + if ( event->getNode()->isType( UI_TYPE_CODEEDITOR ) ) { + UICodeEditor* editor = event->getNode()->asType(); + editor->runOnMainThread( [editor, fileAndPos] { + editor->goToLine( fileAndPos.second ); + } ); + } + event->getNode()->removeEventListener( event->getCallbackId() ); + } ); + // Don't listen forever if no event is received + editor->runOnMainThread( + [editor, cb]() { editor->removeEventListener( cb ); }, Seconds( 4 ) ); + } else { + editor->runOnMainThread( + [editor, fileAndPos] { editor->goToLine( fileAndPos.second ); } ); + } + } + } else { + loadFileFromPath( fileAndPos.first, true, nullptr, + [fileAndPos]( UICodeEditor* editor, const std::string& ) { + editor->runOnMainThread( [editor, fileAndPos] { + editor->goToLine( fileAndPos.second ); + } ); + } ); + } + } + Log::info( "Init ProjectTreeView took: %.2f ms", globalClock.getElapsedTime().asMilliseconds() ); @@ -3149,9 +3240,17 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { #endif args::ArgumentParser parser( "ecode" ); args::HelpFlag help( parser, "help", "Display this help menu", { 'h', '?', "help" } ); - args::Positional file( parser, "file", "The file or folder path" ); - args::ValueFlag filePos( parser, "file", "The file or folder path", - { 'f', "file", "folder" } ); + args::Positional fileOrFolderPos( parser, "file_or_folder", + "The file or folder path to open" ); + args::ValueFlag file( + parser, "file", + "The file path to open. A file path can also contain the line number and column to " + "position the cursor when the file is opened. The format is: " + "$FILEPATH:$LINE_NUMBER:$COLUMN. Both line number and column are optional (line number can " + "be provided without column too).", + { 'f', "file" } ); + args::ValueFlag folder( parser, "folder", "The folder path to open", + { "folder" } ); args::ValueFlag pixelDenstiyConf( parser, "pixel-density", "Set default application pixel density", { 'd', "pixel-density" } ); @@ -3219,11 +3318,11 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { Log::instance()->setConsoleOutput( true ); appInstance = eeNew( App, ( jobs ) ); - appInstance->init( logLevel.Get(), filePos ? filePos.Get() : file.Get(), + appInstance->init( logLevel.Get(), folder ? folder.Get() : fileOrFolderPos.Get(), pixelDenstiyConf ? pixelDenstiyConf.Get() : 0.f, prefersColorScheme ? prefersColorScheme.Get() : "", terminal.Get(), fb.Get(), benchmarkMode.Get(), css.Get(), health || healthLang, healthLang.Get(), - healthFormat.Get() ); + healthFormat.Get(), file.Get() ); eeSAFE_DELETE( appInstance ); Engine::destroySingleton(); diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index b663f2e4e..d5381d805 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -34,7 +34,7 @@ class App : public UICodeEditorSplitter::Client { void init( const LogLevel& logLevel, std::string file, const Float& pidelDensity, const std::string& colorScheme, bool terminal, bool frameBuffer, bool benchmarkMode, const std::string& css, bool health, const std::string& healthLang, - ecode::FeaturesHealth::OutputFormat healthFormat ); + ecode::FeaturesHealth::OutputFormat healthFormat, const std::string& fileToOpen ); void createWidgetInspector(); @@ -380,7 +380,7 @@ class App : public UICodeEditorSplitter::Client { void initLocateBar(); - void initProjectTreeView( const std::string& path ); + void initProjectTreeView( std::string path ); void initImageView(); diff --git a/src/tools/ecode/plugins/linter/linterplugin.cpp b/src/tools/ecode/plugins/linter/linterplugin.cpp index 4d860d45a..03bbdab3e 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.cpp +++ b/src/tools/ecode/plugins/linter/linterplugin.cpp @@ -327,6 +327,8 @@ PluginRequestHandle LinterPlugin::processMessage( const PluginMessage& notificat match.type = getLinterTypeFromSeverity( diag.severity ); match.lineCache = doc->line( match.range.start().line() ).getHash(); match.origin = MatchOrigin::Diagnostics; + if ( !diag.codeActions.empty() ) + match.codeActions = diag.codeActions; matches[match.range.start().line()].emplace_back( std::move( match ) ); } diff --git a/src/tools/ecode/plugins/linter/linterplugin.hpp b/src/tools/ecode/plugins/linter/linterplugin.hpp index 16d885d12..52896e992 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.hpp +++ b/src/tools/ecode/plugins/linter/linterplugin.hpp @@ -44,6 +44,7 @@ struct LinterMatch { String::HashType lineCache; MatchOrigin origin{ MatchOrigin::Linter }; std::map box; + std::vector codeActions; }; class LinterPlugin : public UICodeEditorPlugin {