mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-29 17:46:29 +03:00
eepp:
Added Sys::execute and Sys::getProcessFilePath (not tested on macOS, could be broken). ecode: Optimized document search (now is async). Improved auto-reload of plugins, should solve some issues. Added "New Window" option on Settings Menu.
This commit is contained in:
@@ -35,9 +35,12 @@ class EE_API Sys {
|
||||
/** Wait the time defined before returning. */
|
||||
static void sleep( const Time& time );
|
||||
|
||||
/** @return The application path ( the executable path ) */
|
||||
/** @return The application path ( the executable path without the executable ) */
|
||||
static std::string getProcessPath();
|
||||
|
||||
/** @return The process path ( the executable file path ) */
|
||||
static std::string getProcessFilePath();
|
||||
|
||||
/** @return The System Time */
|
||||
static double getSystemTime();
|
||||
|
||||
@@ -94,6 +97,9 @@ class EE_API Sys {
|
||||
* Other platforms will do nothing.
|
||||
*/
|
||||
static bool windowAttachConsole();
|
||||
|
||||
/** Executes a command */
|
||||
static void execute( const std::string& cmd );
|
||||
};
|
||||
|
||||
}} // namespace EE::System
|
||||
|
||||
@@ -361,28 +361,26 @@ class EE_API TextDocument {
|
||||
bool removeCommand( const std::string& command );
|
||||
|
||||
TextRange find( const String& text, TextPosition from = { 0, 0 }, bool caseSensitive = true,
|
||||
bool wholeWord = false, const FindReplaceType& type = FindReplaceType::Normal,
|
||||
bool wholeWord = false, FindReplaceType type = FindReplaceType::Normal,
|
||||
TextRange restrictRange = TextRange() );
|
||||
|
||||
TextRange findLast( const String& text, TextPosition from = { 0, 0 }, bool caseSensitive = true,
|
||||
bool wholeWord = false,
|
||||
const FindReplaceType& type = FindReplaceType::Normal,
|
||||
bool wholeWord = false, FindReplaceType type = FindReplaceType::Normal,
|
||||
TextRange restrictRange = TextRange() );
|
||||
|
||||
TextRanges findAll( const String& text, bool caseSensitive = true, bool wholeWord = false,
|
||||
const FindReplaceType& type = FindReplaceType::Normal,
|
||||
FindReplaceType type = FindReplaceType::Normal,
|
||||
TextRange restrictRange = TextRange(), size_t maxResults = 0 );
|
||||
|
||||
int replaceAll( const String& text, const String& replace, const bool& caseSensitive = true,
|
||||
const bool& wholeWord = false,
|
||||
const FindReplaceType& type = FindReplaceType::Normal,
|
||||
const bool& wholeWord = false, FindReplaceType type = FindReplaceType::Normal,
|
||||
TextRange restrictRange = TextRange() );
|
||||
|
||||
TextPosition replaceSelection( const String& replace );
|
||||
|
||||
TextPosition replace( String search, const String& replace, TextPosition from = { 0, 0 },
|
||||
const bool& caseSensitive = true, const bool& wholeWord = false,
|
||||
const FindReplaceType& type = FindReplaceType::Normal,
|
||||
FindReplaceType type = FindReplaceType::Normal,
|
||||
TextRange restrictRange = TextRange() );
|
||||
|
||||
String getIndentString();
|
||||
@@ -699,13 +697,11 @@ class EE_API TextDocument {
|
||||
LoadStatus loadFromStream( IOStream& file, std::string path, bool callReset );
|
||||
|
||||
TextRange findText( String text, TextPosition from = { 0, 0 }, bool caseSensitive = true,
|
||||
bool wholeWord = false,
|
||||
const FindReplaceType& type = FindReplaceType::Normal,
|
||||
bool wholeWord = false, FindReplaceType type = FindReplaceType::Normal,
|
||||
TextRange restrictRange = TextRange() );
|
||||
|
||||
TextRange findTextLast( String text, TextPosition from = { 0, 0 }, bool caseSensitive = true,
|
||||
bool wholeWord = false,
|
||||
const FindReplaceType& type = FindReplaceType::Normal,
|
||||
bool wholeWord = false, FindReplaceType type = FindReplaceType::Normal,
|
||||
TextRange restrictRange = TextRange() );
|
||||
};
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
#endif
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <libproc.h>
|
||||
#include <unistd.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#elif EE_PLATFORM == EE_PLATFORM_WIN
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
@@ -48,6 +48,10 @@
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS || EE_PLATFORM == EE_PLATFORM_IOS
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS || EE_PLATFORM == EE_PLATFORM_BSD || \
|
||||
EE_PLATFORM == EE_PLATFORM_IOS
|
||||
#include <sys/mount.h>
|
||||
@@ -57,6 +61,7 @@
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
#include <direct.h>
|
||||
#include <sys/utime.h>
|
||||
#include <tchar.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
@@ -488,7 +493,7 @@ static std::string sGetProcessPath() {
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
|
||||
pid_t pid = getpid();
|
||||
int ret = proc_pidpath (pid, pathbuf, sizeof(pathbuf));
|
||||
int ret = proc_pidpath( pid, pathbuf, sizeof( pathbuf ) );
|
||||
if ( ret >= 0 )
|
||||
return FileSystem::fileRemoveFileName( std::string( pathbuf ) );
|
||||
|
||||
@@ -1143,4 +1148,61 @@ bool Sys::windowAttachConsole() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
static void windowsSystem( const std::string& programPath ) {
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory( &si, sizeof( si ) );
|
||||
si.cb = sizeof( si );
|
||||
ZeroMemory( &pi, sizeof( pi ) );
|
||||
|
||||
if ( CreateProcess( NULL, const_cast<char*>( programPath.c_str() ), NULL, NULL, FALSE, 0, NULL,
|
||||
NULL, &si, &pi ) ) {
|
||||
CloseHandle( pi.hProcess );
|
||||
CloseHandle( pi.hThread );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Sys::execute( const std::string& cmd ) {
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
windowsSystem( cmd );
|
||||
#else
|
||||
std::system( cmd.c_str() );
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Sys::getProcessFilePath() {
|
||||
char exename[PATH_MAX];
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
int len = GetModuleFileName( NULL, exename, PATH_MAX - 1 );
|
||||
exename[len] = '\0';
|
||||
#elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_ANDROID
|
||||
char path[] = "/proc/self/exe";
|
||||
ssize_t len = readlink( path, exename, PATH_MAX - 1 );
|
||||
if ( len > 0 )
|
||||
exename[len] = '\0';
|
||||
#elif EE_PLATFORM == EE_PLATFORM_MACOS || EE_PLATFORM == EE_PLATFORM_IOS
|
||||
/* use realpath to resolve a symlink if the process was launched from one.
|
||||
** This happens when Homebrew installs a cack and creates a symlink in
|
||||
** /usr/loca/bin for launching the executable from the command line. */
|
||||
unsigned size = PATH_MAX;
|
||||
char exepath[size];
|
||||
_NSGetExecutablePath( exepath, &size );
|
||||
realpath( exepath, exename );
|
||||
#elif EE_PLATFORM == EE_PLATFORM_BSD
|
||||
size_t len = PATH_MAX;
|
||||
const int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
|
||||
sysctl( mib, 4, exename, &len, NULL, 0 );
|
||||
#elif EE_PLATFORM == EE_PLATFORM_HAIKU
|
||||
image_info info;
|
||||
get_image_info( 0, &info );
|
||||
strncpy( exename, info.name, sizeof( exename ) );
|
||||
#else
|
||||
*exename = 0;
|
||||
#endif
|
||||
|
||||
return std::string( exename );
|
||||
}
|
||||
}} // namespace EE::System
|
||||
|
||||
@@ -2160,8 +2160,7 @@ static std::pair<size_t, size_t> findLastType( const String& str, const String&
|
||||
}
|
||||
|
||||
TextRange TextDocument::findText( String text, TextPosition from, bool caseSensitive,
|
||||
bool wholeWord, const FindReplaceType& type,
|
||||
TextRange restrictRange ) {
|
||||
bool wholeWord, FindReplaceType type, TextRange restrictRange ) {
|
||||
if ( text.empty() )
|
||||
return TextRange();
|
||||
from = sanitizePosition( from );
|
||||
@@ -2213,7 +2212,7 @@ TextRange TextDocument::findText( String text, TextPosition from, bool caseSensi
|
||||
}
|
||||
|
||||
TextRange TextDocument::findTextLast( String text, TextPosition from, bool caseSensitive,
|
||||
bool wholeWord, const FindReplaceType& type,
|
||||
bool wholeWord, FindReplaceType type,
|
||||
TextRange restrictRange ) {
|
||||
if ( text.empty() )
|
||||
return TextRange();
|
||||
@@ -2268,8 +2267,7 @@ TextRange TextDocument::findTextLast( String text, TextPosition from, bool caseS
|
||||
}
|
||||
|
||||
TextRange TextDocument::find( const String& text, TextPosition from, bool caseSensitive,
|
||||
bool wholeWord, const FindReplaceType& type,
|
||||
TextRange restrictRange ) {
|
||||
bool wholeWord, FindReplaceType type, TextRange restrictRange ) {
|
||||
std::vector<String> textLines = text.split( '\n', true, true );
|
||||
|
||||
if ( textLines.empty() || textLines.size() > mLines.size() )
|
||||
@@ -2349,8 +2347,7 @@ TextRange TextDocument::find( const String& text, TextPosition from, bool caseSe
|
||||
}
|
||||
|
||||
TextRange TextDocument::findLast( const String& text, TextPosition from, bool caseSensitive,
|
||||
bool wholeWord, const FindReplaceType& type,
|
||||
TextRange restrictRange ) {
|
||||
bool wholeWord, FindReplaceType type, TextRange restrictRange ) {
|
||||
std::vector<String> textLines = text.split( '\n', true, true );
|
||||
|
||||
if ( textLines.empty() || textLines.size() > mLines.size() )
|
||||
@@ -2439,7 +2436,7 @@ bool TextDocument::isInsertingText() const {
|
||||
}
|
||||
|
||||
TextRanges TextDocument::findAll( const String& text, bool caseSensitive, bool wholeWord,
|
||||
const FindReplaceType& type, TextRange restrictRange,
|
||||
FindReplaceType type, TextRange restrictRange,
|
||||
size_t maxResults ) {
|
||||
TextRanges all;
|
||||
TextRange found;
|
||||
@@ -2475,7 +2472,7 @@ TextRanges TextDocument::findAll( const String& text, bool caseSensitive, bool w
|
||||
}
|
||||
|
||||
int TextDocument::replaceAll( const String& text, const String& replace, const bool& caseSensitive,
|
||||
const bool& wholeWord, const FindReplaceType& type,
|
||||
const bool& wholeWord, FindReplaceType type,
|
||||
TextRange restrictRange ) {
|
||||
if ( text.empty() )
|
||||
return 0;
|
||||
@@ -2537,7 +2534,7 @@ void TextDocument::selectAllMatches() {
|
||||
|
||||
TextPosition TextDocument::replace( String search, const String& replace, TextPosition from,
|
||||
const bool& caseSensitive, const bool& wholeWord,
|
||||
const FindReplaceType& type, TextRange restrictRange ) {
|
||||
FindReplaceType type, TextRange restrictRange ) {
|
||||
TextRange found( findText( search, from, caseSensitive, wholeWord, type, restrictRange ) );
|
||||
if ( found.isValid() ) {
|
||||
setSelection( found );
|
||||
|
||||
@@ -6,17 +6,18 @@ namespace ecode {
|
||||
DocSearchController::DocSearchController( UICodeEditorSplitter* editorSplitter, App* app ) :
|
||||
mSplitter( editorSplitter ), mApp( app ) {}
|
||||
|
||||
DocSearchController::~DocSearchController() {
|
||||
Clock clock;
|
||||
while ( mSearchingCount && clock.getElapsedTime() < Seconds( 1 ) )
|
||||
Sys::sleep( Milliseconds( 10 ) );
|
||||
}
|
||||
|
||||
void DocSearchController::refreshHighlight() {
|
||||
if ( mSearchState.editor && mSplitter->editorExists( mSearchState.editor ) ) {
|
||||
mSearchState.text = mFindInput->getText();
|
||||
mSearchState.editor->setHighlightWord( mSearchState.toTextSearchParams() );
|
||||
if ( !mSearchState.text.empty() ) {
|
||||
mSearchState.editor->getDocument().setSelection( { 0, 0 } );
|
||||
if ( !findNextText( mSearchState ) ) {
|
||||
mFindInput->addClass( "error" );
|
||||
} else {
|
||||
mFindInput->removeClass( "error" );
|
||||
}
|
||||
findNextText( mSearchState, true );
|
||||
} else {
|
||||
mFindInput->removeClass( "error" );
|
||||
mSearchState.editor->getDocument().setSelection(
|
||||
@@ -201,86 +202,113 @@ void DocSearchController::showFindView() {
|
||||
editor->getDocument().setActiveClient( editor );
|
||||
}
|
||||
|
||||
bool DocSearchController::findPrevText( SearchState& search ) {
|
||||
void DocSearchController::findPrevText( SearchState& search ) {
|
||||
if ( search.text.empty() )
|
||||
search.text = mLastSearch;
|
||||
if ( !search.editor || !mSplitter->editorExists( search.editor ) || search.text.empty() )
|
||||
return false;
|
||||
return;
|
||||
|
||||
search.editor->getDocument().setActiveClient( search.editor );
|
||||
mLastSearch = search.text;
|
||||
TextDocument& doc = search.editor->getDocument();
|
||||
TextRange range = doc.getDocRange();
|
||||
TextPosition from = doc.getSelection( true ).start();
|
||||
if ( search.range.isValid() ) {
|
||||
range = doc.sanitizeRange( search.range ).normalized();
|
||||
from = from < range.start() ? range.start() : from;
|
||||
}
|
||||
|
||||
String txt( search.text );
|
||||
if ( search.escapeSequences )
|
||||
txt.unescape();
|
||||
Uint64 tag = String::hash( "DocSearchController::findPrevText-" +
|
||||
search.editor->getDocument().getHashHexString() );
|
||||
mApp->getThreadPool()->removeWithTag( tag );
|
||||
mApp->getThreadPool()->run( [this, search] {
|
||||
ScopedOp op( [this] { mSearchingCount++; }, [this] { mSearchingCount--; } );
|
||||
TextDocument& doc = search.editor->getDocument();
|
||||
TextRange range = doc.getDocRange();
|
||||
TextPosition from = doc.getSelection( true ).start();
|
||||
if ( search.range.isValid() ) {
|
||||
range = doc.sanitizeRange( search.range ).normalized();
|
||||
from = from < range.start() ? range.start() : from;
|
||||
}
|
||||
|
||||
TextRange found = doc.findLast( txt, from, search.caseSensitive, search.wholeWord, search.type,
|
||||
search.range );
|
||||
if ( found.isValid() ) {
|
||||
doc.setSelection( found );
|
||||
mSplitter->addEditorPositionToNavigationHistory( search.editor );
|
||||
mFindInput->removeClass( "error" );
|
||||
return true;
|
||||
} else {
|
||||
found = doc.findLast( txt, range.end(), search.caseSensitive, search.wholeWord, search.type,
|
||||
range );
|
||||
String txt( search.text );
|
||||
if ( search.escapeSequences )
|
||||
txt.unescape();
|
||||
|
||||
TextRange found = doc.findLast( txt, from, search.caseSensitive, search.wholeWord,
|
||||
search.type, search.range );
|
||||
if ( found.isValid() ) {
|
||||
doc.setSelection( found );
|
||||
mSplitter->addEditorPositionToNavigationHistory( search.editor );
|
||||
mFindInput->removeClass( "error" );
|
||||
return true;
|
||||
mFindInput->runOnMainThread( [this, search] {
|
||||
mSplitter->addEditorPositionToNavigationHistory( search.editor );
|
||||
mFindInput->removeClass( "error" );
|
||||
} );
|
||||
return;
|
||||
} else {
|
||||
found = doc.findLast( txt, range.end(), search.caseSensitive, search.wholeWord,
|
||||
search.type, range );
|
||||
if ( found.isValid() ) {
|
||||
doc.setSelection( found );
|
||||
mFindInput->runOnMainThread( [this, search] {
|
||||
mSplitter->addEditorPositionToNavigationHistory( search.editor );
|
||||
mFindInput->removeClass( "error" );
|
||||
} );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
mFindInput->addClass( "error" );
|
||||
return false;
|
||||
mFindInput->runOnMainThread( [this] { mFindInput->addClass( "error" ); } );
|
||||
} );
|
||||
}
|
||||
|
||||
bool DocSearchController::findNextText( SearchState& search ) {
|
||||
void DocSearchController::findNextText( SearchState& search, bool resetSelection ) {
|
||||
if ( search.text.empty() )
|
||||
search.text = mLastSearch;
|
||||
if ( !search.editor || !mSplitter->editorExists( search.editor ) || search.text.empty() )
|
||||
return false;
|
||||
return;
|
||||
|
||||
search.editor->getDocument().setActiveClient( search.editor );
|
||||
mLastSearch = search.text;
|
||||
TextDocument& doc = search.editor->getDocument();
|
||||
TextRange range = doc.getDocRange();
|
||||
TextPosition from = doc.getSelection( true ).end();
|
||||
if ( search.range.isValid() ) {
|
||||
range = doc.sanitizeRange( search.range ).normalized();
|
||||
from = from < range.start() ? range.start() : from;
|
||||
}
|
||||
|
||||
String txt( search.text );
|
||||
if ( search.escapeSequences )
|
||||
txt.unescape();
|
||||
Uint64 tag = String::hash( "DocSearchController::findNextText-" +
|
||||
search.editor->getDocument().getHashHexString() );
|
||||
|
||||
TextRange found =
|
||||
doc.find( txt, from, search.caseSensitive, search.wholeWord, search.type, range );
|
||||
if ( found.isValid() ) {
|
||||
doc.setSelection( found.reversed() );
|
||||
mSplitter->addEditorPositionToNavigationHistory( search.editor );
|
||||
mFindInput->removeClass( "error" );
|
||||
return true;
|
||||
} else {
|
||||
found = doc.find( txt, range.start(), search.caseSensitive, search.wholeWord, search.type,
|
||||
range );
|
||||
if ( found.isValid() ) {
|
||||
doc.setSelection( found.reversed() );
|
||||
mSplitter->addEditorPositionToNavigationHistory( search.editor );
|
||||
mFindInput->removeClass( "error" );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mFindInput->addClass( "error" );
|
||||
return false;
|
||||
mApp->getThreadPool()->removeWithTag( tag );
|
||||
mApp->getThreadPool()->run(
|
||||
[this, search, resetSelection] {
|
||||
ScopedOp op( [this] { mSearchingCount++; }, [this] { mSearchingCount--; } );
|
||||
TextDocument& doc = search.editor->getDocument();
|
||||
TextRange range = doc.getDocRange();
|
||||
TextPosition from =
|
||||
resetSelection ? TextPosition( 0, 0 ) : doc.getSelection( true ).end();
|
||||
|
||||
if ( search.range.isValid() ) {
|
||||
range = doc.sanitizeRange( search.range ).normalized();
|
||||
from = from < range.start() ? range.start() : from;
|
||||
}
|
||||
|
||||
String txt( search.text );
|
||||
if ( search.escapeSequences )
|
||||
txt.unescape();
|
||||
|
||||
TextRange found =
|
||||
doc.find( txt, from, search.caseSensitive, search.wholeWord, search.type, range );
|
||||
|
||||
if ( found.isValid() ) {
|
||||
doc.setSelection( found.reversed() );
|
||||
mFindInput->runOnMainThread( [this, search] {
|
||||
mSplitter->addEditorPositionToNavigationHistory( search.editor );
|
||||
mFindInput->removeClass( "error" );
|
||||
} );
|
||||
return;
|
||||
} else {
|
||||
found = doc.find( txt, range.start(), search.caseSensitive, search.wholeWord,
|
||||
search.type, range );
|
||||
if ( found.isValid() ) {
|
||||
doc.setSelection( found.reversed() );
|
||||
mFindInput->runOnMainThread( [this, search] {
|
||||
mSplitter->addEditorPositionToNavigationHistory( search.editor );
|
||||
mFindInput->removeClass( "error" );
|
||||
} );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mFindInput->runOnMainThread( [this] { mFindInput->addClass( "error" ); } );
|
||||
},
|
||||
{}, tag );
|
||||
}
|
||||
|
||||
bool DocSearchController::replaceSelection( SearchState& search, const String& replacement ) {
|
||||
@@ -333,13 +361,13 @@ int DocSearchController::replaceAll( SearchState& search, const String& replace
|
||||
return count;
|
||||
}
|
||||
|
||||
bool DocSearchController::findAndReplace( SearchState& search, const String& replace ) {
|
||||
void DocSearchController::findAndReplace( SearchState& search, const String& replace ) {
|
||||
if ( !search.editor || !mSplitter->editorExists( search.editor ) )
|
||||
return false;
|
||||
return;
|
||||
if ( search.text.empty() )
|
||||
search.text = mLastSearch;
|
||||
if ( search.text.empty() )
|
||||
return false;
|
||||
return;
|
||||
search.editor->getDocument().setActiveClient( search.editor );
|
||||
mLastSearch = search.text;
|
||||
TextDocument& doc = search.editor->getDocument();
|
||||
@@ -352,9 +380,9 @@ bool DocSearchController::findAndReplace( SearchState& search, const String& rep
|
||||
}
|
||||
|
||||
if ( doc.hasSelection() && doc.getSelectedText() == txt ) {
|
||||
return replaceSelection( search, repl );
|
||||
replaceSelection( search, repl );
|
||||
} else {
|
||||
return findNextText( search );
|
||||
findNextText( search );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,21 +46,23 @@ class DocSearchController {
|
||||
|
||||
DocSearchController( UICodeEditorSplitter*, App* app );
|
||||
|
||||
~DocSearchController();
|
||||
|
||||
void initSearchBar(
|
||||
UISearchBar* searchBar, const SearchBarConfig& searchBarConfig,
|
||||
std::unordered_map<std::string, std::string> keybindings = getDefaultKeybindings() );
|
||||
|
||||
void showFindView();
|
||||
|
||||
bool findPrevText( SearchState& search );
|
||||
void findPrevText( SearchState& search );
|
||||
|
||||
bool findNextText( SearchState& search );
|
||||
void findNextText( SearchState& search, bool resetSelection = false );
|
||||
|
||||
bool replaceSelection( SearchState& search, const String& replacement );
|
||||
|
||||
int replaceAll( SearchState& search, const String& replace );
|
||||
|
||||
bool findAndReplace( SearchState& search, const String& replace );
|
||||
void findAndReplace( SearchState& search, const String& replace );
|
||||
|
||||
void hideSearchBar();
|
||||
|
||||
@@ -73,6 +75,7 @@ class DocSearchController {
|
||||
void selectAll( SearchState& search );
|
||||
|
||||
void refreshHighlight();
|
||||
|
||||
protected:
|
||||
UICodeEditorSplitter* mSplitter{ nullptr };
|
||||
UITextInput* mFindInput{ nullptr };
|
||||
@@ -85,6 +88,7 @@ class DocSearchController {
|
||||
App* mApp{ nullptr };
|
||||
SearchState mSearchState;
|
||||
String mLastSearch;
|
||||
size_t mSearchingCount{ 0 };
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -552,6 +552,7 @@ void App::onReady() {
|
||||
// Plugin reload is only available right after we render the first frame and the editor is ready
|
||||
// to run.
|
||||
mPluginManager->setPluginReloadEnabled( true );
|
||||
Log::info( "App Ready" );
|
||||
}
|
||||
|
||||
void App::mainLoop() {
|
||||
@@ -1875,6 +1876,7 @@ std::map<KeyBindings::Shortcut, std::string> App::getLocalKeybindings() {
|
||||
"open-workspace-symbol-search" },
|
||||
{ { KEY_P, KeyMod::getDefaultModifier() | KEYMOD_SHIFT },
|
||||
"open-document-symbol-search" },
|
||||
{ { KEY_N, KEYMOD_SHIFT | KEYMOD_LALT }, "create-new-window" },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1950,7 +1952,8 @@ std::vector<std::string> App::getUnlockedCommands() {
|
||||
"open-workspace-symbol-search",
|
||||
"open-document-symbol-search",
|
||||
"show-folder-treeview-tab",
|
||||
"show-build-tab" };
|
||||
"show-build-tab",
|
||||
"create-new-window" };
|
||||
}
|
||||
|
||||
bool App::isUnlockedCommand( const std::string& command ) {
|
||||
|
||||
@@ -293,6 +293,13 @@ class App : public UICodeEditorSplitter::Client {
|
||||
mTerminalManager->configureTerminalScrollback();
|
||||
} );
|
||||
t.setCommand( "check-for-updates", [this] { checkForUpdates( false ); } );
|
||||
t.setCommand( "create-new-window", [this] {
|
||||
std::string processPath = Sys::getProcessFilePath();
|
||||
if ( !processPath.empty() ) {
|
||||
std::string cmd( processPath + " -x" );
|
||||
Sys::execute( cmd );
|
||||
}
|
||||
} );
|
||||
mSplitter->registerSplitterCommands( t );
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,8 @@ void IconManager::init( UISceneNode* sceneNode, FontTrueType* iconFont, FontTrue
|
||||
{ "add", 0xea12 },
|
||||
{ "hammer", 0xedee },
|
||||
{ "eraser", 0xec9e },
|
||||
{ "file-search", 0xed05 } };
|
||||
{ "file-search", 0xed05 },
|
||||
{ "window", 0xf2c4 } };
|
||||
|
||||
for ( const auto& icon : icons )
|
||||
iconTheme->add( UIGlyphIcon::New( icon.first, iconFont, icon.second ) );
|
||||
|
||||
@@ -203,9 +203,11 @@ void FormatterPlugin::loadFormatterConfig( const std::string& path, bool updateC
|
||||
j["keybindings"]["format-doc"] = mKeyBindings["format-doc"];
|
||||
|
||||
if ( updateConfigFile ) {
|
||||
data = j.dump( 2 );
|
||||
FileSystem::fileWrite( path, data );
|
||||
mConfigHash = String::hash( data );
|
||||
std::string newData = j.dump( 2 );
|
||||
if ( newData != data ) {
|
||||
FileSystem::fileWrite( path, newData );
|
||||
mConfigHash = String::hash( data );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !j.contains( "formatters" ) )
|
||||
|
||||
@@ -183,7 +183,11 @@ void LinterPlugin::loadLinterConfig( const std::string& path, bool updateConfigF
|
||||
}
|
||||
|
||||
if ( updateConfigFile ) {
|
||||
FileSystem::fileWrite( path, j.dump( 2 ) );
|
||||
std::string newData( j.dump( 2 ) );
|
||||
if ( newData != data ) {
|
||||
FileSystem::fileWrite( path, newData );
|
||||
mConfigHash = String::hash( newData );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !j.contains( "linters" ) )
|
||||
|
||||
@@ -908,7 +908,11 @@ void LSPClientPlugin::loadLSPConfig( std::vector<LSPDefinition>& lsps, const std
|
||||
}
|
||||
|
||||
if ( updateConfigFile ) {
|
||||
FileSystem::fileWrite( path, j.dump( 2 ) );
|
||||
std::string newData( j.dump( 2 ) );
|
||||
if ( newData != data ) {
|
||||
FileSystem::fileWrite( path, newData );
|
||||
mConfigHash = String::hash( newData );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !j.contains( "servers" ) )
|
||||
|
||||
@@ -22,6 +22,7 @@ PluginManager::~PluginManager() {
|
||||
Log::debug( "PluginManager: unloading plugin %s", plugin.second->getTitle().c_str() );
|
||||
eeDelete( plugin.second );
|
||||
}
|
||||
unsubscribeFileSystemListener();
|
||||
}
|
||||
|
||||
void PluginManager::registerPlugin( const PluginDefinition& def ) {
|
||||
@@ -65,12 +66,18 @@ bool PluginManager::isEnabled( const std::string& id ) const {
|
||||
}
|
||||
|
||||
bool PluginManager::reload( const std::string& id ) {
|
||||
if ( !isPluginReloadEnabled() ) {
|
||||
Log::warning( "PluginManager: tried to reload a plugin but plugin reload is not enabled." );
|
||||
return false;
|
||||
}
|
||||
if ( isEnabled( id ) ) {
|
||||
Log::warning( "PluginManager: reloading plugin %s", id.c_str() );
|
||||
Log::warning( "PluginManager: reloading plugin %s from process %u", id.c_str(),
|
||||
Sys::getProcessID() );
|
||||
setEnabled( id, false );
|
||||
setEnabled( id, true );
|
||||
return true;
|
||||
}
|
||||
Log::warning( "PluginManager: tried to reload a plugin but plugin is not enabled." );
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -254,6 +261,31 @@ void PluginManager::setFileSystemListener( FileSystemListener* listener ) {
|
||||
mFileSystemListener = listener;
|
||||
sendBroadcast( PluginMessageType::FileSystemListenerReady, PluginMessageFormat::Empty,
|
||||
nullptr );
|
||||
subscribeFileSystemListener();
|
||||
}
|
||||
|
||||
void PluginManager::subscribeFileSystemListener( Plugin* plugin ) {
|
||||
mPluginsFSSubs.insert( plugin );
|
||||
}
|
||||
|
||||
void PluginManager::unsubscribeFileSystemListener( Plugin* plugin ) {
|
||||
mPluginsFSSubs.erase( plugin );
|
||||
}
|
||||
|
||||
void PluginManager::subscribeFileSystemListener() {
|
||||
if ( mFileSystemListenerCb != 0 || mFileSystemListener == nullptr )
|
||||
return;
|
||||
|
||||
mFileSystemListenerCb =
|
||||
mFileSystemListener->addListener( [this]( const FileEvent& ev, const FileInfo& file ) {
|
||||
for ( Plugin* plugin : mPluginsFSSubs )
|
||||
plugin->onFileSystemEvent( ev, file );
|
||||
} );
|
||||
}
|
||||
|
||||
void PluginManager::unsubscribeFileSystemListener() {
|
||||
if ( mFileSystemListenerCb != 0 && mFileSystemListener )
|
||||
mFileSystemListener->removeListener( mFileSystemListenerCb );
|
||||
}
|
||||
|
||||
void PluginManager::sendBroadcast( const PluginMessageType& notification,
|
||||
@@ -456,45 +488,6 @@ Plugin::Plugin( PluginManager* manager ) :
|
||||
// avoid concurrency issues
|
||||
{}
|
||||
|
||||
void Plugin::subscribeFileSystemListener() {
|
||||
if ( mFileSystemListenerCb != 0 || mManager->getFileSystemListener() == nullptr )
|
||||
return;
|
||||
|
||||
mConfigFileInfo = FileInfo( mConfigPath );
|
||||
|
||||
mFileSystemListenerCb = mManager->getFileSystemListener()->addListener(
|
||||
[this]( const FileEvent& ev, const FileInfo& file ) {
|
||||
if ( ev.type != FileSystemEventType::Modified )
|
||||
return;
|
||||
if ( !mShuttingDown && !isLoading() && file.getFilepath() == mConfigPath &&
|
||||
file.getModificationTime() != mConfigFileInfo.getModificationTime() ) {
|
||||
std::string fileContents;
|
||||
FileSystem::fileGet( file.getFilepath(), fileContents );
|
||||
if ( getConfigFileHash() != String::hash( fileContents ) ) {
|
||||
if ( mManager->isPluginReloadEnabled() && !isLoading() && isReady() ) {
|
||||
mConfigFileInfo = file;
|
||||
mManager->getFileSystemListener()->removeListener( mFileSystemListenerCb );
|
||||
mFileSystemListenerCb = 0;
|
||||
mManager->reload( getId() );
|
||||
} else {
|
||||
Log::debug( "Plugin %s: Configuration file has been modified: %s. But "
|
||||
"plugin reload is not enabled.",
|
||||
getTitle().c_str(), mConfigPath.c_str() );
|
||||
}
|
||||
} else {
|
||||
Log::debug( "Plugin %s: Configuration file has been modified: %s. But contents "
|
||||
"are the same.",
|
||||
getTitle().c_str(), mConfigPath.c_str() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
void Plugin::unsubscribeFileSystemListener() {
|
||||
if ( mFileSystemListenerCb != 0 && mManager->getFileSystemListener() )
|
||||
mManager->getFileSystemListener()->removeListener( mFileSystemListenerCb );
|
||||
}
|
||||
|
||||
bool Plugin::isReady() const {
|
||||
return mReady;
|
||||
}
|
||||
@@ -511,6 +504,15 @@ bool Plugin::hasFileConfig() {
|
||||
return !mConfigPath.empty();
|
||||
}
|
||||
|
||||
void Plugin::subscribeFileSystemListener() {
|
||||
mConfigFileInfo = FileInfo( mConfigPath );
|
||||
mManager->subscribeFileSystemListener( this );
|
||||
}
|
||||
|
||||
void Plugin::unsubscribeFileSystemListener() {
|
||||
mManager->unsubscribeFileSystemListener( this );
|
||||
}
|
||||
|
||||
std::string Plugin::getFileConfigPath() {
|
||||
return mConfigPath;
|
||||
}
|
||||
@@ -519,9 +521,38 @@ PluginManager* Plugin::getManager() const {
|
||||
return mManager;
|
||||
}
|
||||
|
||||
void Plugin::onFileSystemEvent( const FileEvent& ev, const FileInfo& file ) {
|
||||
if ( ev.type != FileSystemEventType::Modified || mShuttingDown || isLoading() )
|
||||
return;
|
||||
|
||||
if ( file.getFilepath() != mConfigPath ||
|
||||
file.getModificationTime() == mConfigFileInfo.getModificationTime() )
|
||||
return;
|
||||
|
||||
std::string fileContents;
|
||||
FileSystem::fileGet( file.getFilepath(), fileContents );
|
||||
if ( getConfigFileHash() != String::hash( fileContents ) ) {
|
||||
if ( mManager->isPluginReloadEnabled() && !isLoading() && isReady() ) {
|
||||
mConfigFileInfo = file;
|
||||
unsubscribeFileSystemListener();
|
||||
mManager->reload( getId() );
|
||||
} else {
|
||||
Log::debug( "Plugin %s: Configuration file has been modified: %s. But "
|
||||
"plugin reload is not enabled.",
|
||||
getTitle().c_str(), mConfigPath.c_str() );
|
||||
}
|
||||
} else {
|
||||
Log::debug( "Plugin %s: Configuration file has been modified: %s. But contents "
|
||||
"are the same.",
|
||||
getTitle().c_str(), mConfigPath.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
void Plugin::setReady() {
|
||||
if ( mReady )
|
||||
Log::info( "Plugin: %s loaded and ready.", getTitle().c_str() );
|
||||
if ( mReady ) {
|
||||
Log::info( "Plugin: %s loaded and ready from process %u", getTitle().c_str(),
|
||||
Sys::getProcessID() );
|
||||
}
|
||||
}
|
||||
|
||||
PluginBase::~PluginBase() {
|
||||
|
||||
@@ -23,6 +23,7 @@ using namespace EE::UI::Tools;
|
||||
namespace ecode {
|
||||
|
||||
class PluginManager;
|
||||
class Plugin;
|
||||
class FileSystemListener;
|
||||
|
||||
typedef std::function<UICodeEditorPlugin*( PluginManager* pluginManager )> PluginCreatorFn;
|
||||
@@ -332,6 +333,10 @@ class PluginManager {
|
||||
|
||||
void setPluginReloadEnabled( bool pluginReloadEnabled );
|
||||
|
||||
void subscribeFileSystemListener( Plugin* plugin );
|
||||
|
||||
void unsubscribeFileSystemListener( Plugin* plugin );
|
||||
|
||||
protected:
|
||||
using SubscribedPlugins =
|
||||
std::map<std::string, std::function<PluginRequestHandle( const PluginMessage& )>>;
|
||||
@@ -348,6 +353,8 @@ class PluginManager {
|
||||
Mutex mSubscribedPluginsMutex;
|
||||
SubscribedPlugins mSubscribedPlugins;
|
||||
OnLoadFileCb mLoadFileFn;
|
||||
Uint64 mFileSystemListenerCb{ 0 };
|
||||
UnorderedSet<Plugin*> mPluginsFSSubs;
|
||||
bool mClosing{ false };
|
||||
bool mPluginReloadEnabled{ false };
|
||||
|
||||
@@ -356,6 +363,10 @@ class PluginManager {
|
||||
void setSplitter( UICodeEditorSplitter* splitter );
|
||||
|
||||
void setFileSystemListener( FileSystemListener* listener );
|
||||
|
||||
void subscribeFileSystemListener();
|
||||
|
||||
void unsubscribeFileSystemListener();
|
||||
};
|
||||
|
||||
class PluginsModel : public Model {
|
||||
@@ -418,10 +429,11 @@ class Plugin : public UICodeEditorPlugin {
|
||||
|
||||
virtual String::HashType getConfigFileHash() { return 0; }
|
||||
|
||||
virtual void onFileSystemEvent( const FileEvent& ev, const FileInfo& file );
|
||||
|
||||
protected:
|
||||
PluginManager* mManager{ nullptr };
|
||||
std::shared_ptr<ThreadPool> mThreadPool;
|
||||
Uint64 mFileSystemListenerCb{ 0 };
|
||||
std::string mConfigPath;
|
||||
FileInfo mConfigFileInfo;
|
||||
|
||||
|
||||
@@ -93,9 +93,11 @@ void XMLToolsPlugin::load( PluginManager* pluginManager ) {
|
||||
}
|
||||
|
||||
if ( updateConfigFile ) {
|
||||
data = j.dump( 2 );
|
||||
FileSystem::fileWrite( path, data );
|
||||
mConfigHash = String::hash( data );
|
||||
std::string newData = j.dump( 2 );
|
||||
if ( newData != data ) {
|
||||
FileSystem::fileWrite( path, newData );
|
||||
mConfigHash = String::hash( newData );
|
||||
}
|
||||
}
|
||||
|
||||
subscribeFileSystemListener();
|
||||
|
||||
@@ -36,6 +36,10 @@ void SettingsMenu::createSettingsMenu( App* app ) {
|
||||
->add( i18n( "new_terminal", "New Terminal" ), findIcon( "terminal" ),
|
||||
getKeybind( "create-new-terminal" ) )
|
||||
->setId( "create-new-terminal" );
|
||||
mSettingsMenu
|
||||
->add( i18n( "new_window", "New Window" ), findIcon( "window" ),
|
||||
getKeybind( "create-new-window" ) )
|
||||
->setId( "create-new-window" );
|
||||
mSettingsMenu
|
||||
->add( i18n( "open_file", "Open File..." ), findIcon( "document-open" ),
|
||||
getKeybind( "open-file" ) )
|
||||
|
||||
Reference in New Issue
Block a user