diff --git a/bin/assets/colorschemes/colorschemes.conf b/bin/assets/colorschemes/colorschemes.conf index 60ff85f25..a2609282d 100644 --- a/bin/assets/colorschemes/colorschemes.conf +++ b/bin/assets/colorschemes/colorschemes.conf @@ -6,7 +6,7 @@ selection = #394484 line_highlight = #2d303d line_number = #525259 line_number2 = #83838f -line_number_background = #282a36 +gutter_background = #282a36 whitespace = #54575b line_break_column = #54575b99 matching_bracket = #FFFFFF33 @@ -17,6 +17,7 @@ suggestion_selected = #ffffff,#2f3240 error = red warning = yellow notice = #8abdff +selection_region = #39448477 minimap_background = #282a36AA minimap_visible_area = #FFFFFF1A minimap_current_line = #FFFFFF0A @@ -45,7 +46,7 @@ selection = #48484f line_highlight = #343438 line_number = #525259 line_number2 = #83838f -line_number_background = #2e2e32 +gutter_background = #2e2e32 whitespace = #54575b line_break_column = #54575b99 matching_bracket = #FFFFFF33 diff --git a/bin/assets/linters/linters.json b/bin/assets/linters/linters.json index 6dc8524b9..7d58401b9 100644 --- a/bin/assets/linters/linters.json +++ b/bin/assets/linters/linters.json @@ -5,6 +5,14 @@ "warning_pattern_order": { "line": 2, "col": 0, "message": 1 }, "command": "php -l $FILENAME" }, + { + "file_patterns": ["%.json$"], + "warning_pattern": "parse%s(%w*):%s(.*)at%sline%s(%d*),%scolumn%s(%d*)", + "warning_pattern_order": { "line": 3, "col": 4, "message": 2, "type": 1 }, + "command": "jq -e . $FILENAME", + "expected_exitcodes": [1, 2, 3, 4], + "no_errors_exit_code": 0 + }, { "file_patterns": ["%.js$", "%.ts$"], "warning_pattern": "[^:]:(%d+):(%d+): ([^%[]+)%[([^\n]+)", diff --git a/include/eepp/scene/event.hpp b/include/eepp/scene/event.hpp index 06870b6a1..2d6320659 100644 --- a/include/eepp/scene/event.hpp +++ b/include/eepp/scene/event.hpp @@ -80,6 +80,7 @@ class EE_API Event { OnWindowReady, OnCreateContextMenu, OnDocumentMoved, + OnTextPasted, UserEvent, OnMenuShow, OnMenuHide, diff --git a/include/eepp/ui/doc/syntaxcolorscheme.hpp b/include/eepp/ui/doc/syntaxcolorscheme.hpp index cd27504ae..feea1955a 100644 --- a/include/eepp/ui/doc/syntaxcolorscheme.hpp +++ b/include/eepp/ui/doc/syntaxcolorscheme.hpp @@ -19,9 +19,9 @@ namespace EE { namespace UI { namespace Doc { * * Editor colors types accepted/used are: * "background", "text", "caret" - * "selection", "line_number_background", + * "selection", "gutter_background", * "line_number", "line_number2", "line_highlight", - * "line_number_background", "whitespace", "line_break_column", + * "gutter_background", "whitespace", "line_break_column", * "matching_bracket", "matching_selection", "suggestion", "suggestion_selected" * * Following the lite editor syntax colors (https://github.com/rxi/lite). diff --git a/include/eepp/ui/doc/textposition.hpp b/include/eepp/ui/doc/textposition.hpp index eeb456f09..80ff8c108 100644 --- a/include/eepp/ui/doc/textposition.hpp +++ b/include/eepp/ui/doc/textposition.hpp @@ -40,11 +40,11 @@ class EE_API TextPosition { } bool operator<=( const TextPosition& other ) const { - return mLine <= other.mLine || ( mLine == other.mLine && mColumn <= other.mColumn ); + return mLine < other.mLine || ( mLine == other.mLine && mColumn <= other.mColumn ); } bool operator>=( const TextPosition& other ) const { - return mLine >= other.mLine || ( mLine == other.mLine && mColumn >= other.mColumn ); + return mLine > other.mLine || ( mLine == other.mLine && mColumn >= other.mColumn ); } TextPosition operator+( const TextPosition& other ) const { diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index f7ac6b31d..99765caaf 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -650,7 +650,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { const Vector2f& startScroll, const Float& lineHeight ); virtual void drawWordMatch( const String& text, const std::pair& lineRange, - const Vector2f& startScroll, const Float& lineHeight ); + const Vector2f& startScroll, const Float& lineHeight, + bool ignoreSelectionMatch = false ); virtual void drawWhitespaces( const std::pair& lineRange, const Vector2f& startScroll, const Float& lineHeight ); diff --git a/include/eepp/ui/uitextinput.hpp b/include/eepp/ui/uitextinput.hpp index 75543f234..418ab74b9 100644 --- a/include/eepp/ui/uitextinput.hpp +++ b/include/eepp/ui/uitextinput.hpp @@ -106,6 +106,10 @@ class EE_API UITextInput : public UITextView, public TextDocument::Client { void setMenuIconSize( size_t menuIconSize ); + bool getEscapePastedText() const; + + void setEscapePastedText( bool escapePastedText ); + protected: TextDocument mDoc; Float mWaitCursorTime; @@ -119,6 +123,7 @@ class EE_API UITextInput : public UITextView, public TextDocument::Client { bool mAllowFloat; bool mMouseDown; bool mCreateDefaultContextMenuOptions{ true }; + bool mEscapePastedText{ false }; Uint32 mMaxLength{ 0 }; KeyBindings mKeyBindings; Clock mLastDoubleClick; diff --git a/src/eepp/ui/doc/syntaxcolorscheme.cpp b/src/eepp/ui/doc/syntaxcolorscheme.cpp index 01c5a7ee0..a0c806769 100644 --- a/src/eepp/ui/doc/syntaxcolorscheme.cpp +++ b/src/eepp/ui/doc/syntaxcolorscheme.cpp @@ -16,7 +16,7 @@ namespace EE { namespace UI { namespace Doc { // Color schemes are compatible with the lite (https://github.com/rxi/lite) color schemes. // But I also added: // "link" (link style) -// "line_number_background" (the gutter background color) +// "gutter_background" (the gutter background color) // "whitespace" (the whitespace color) // "line_break_column" (the right margin line column color) // "matching_bracket" (the background color drawn in the matching brackets) @@ -24,9 +24,16 @@ namespace EE { namespace UI { namespace Doc { // "matching_search" (the background color drawn in the text matching the current searched text) // "suggestion" (the auto-complete suggestion box text and background color // "suggestion_selected" (the auto-complete selected suggestion box text and background color +// "selection_region" (The background color of the region you select a region to find/replace text) // "error" (error underline color) // "warning" (warning underline color) // "notice" (notice underline color) +// "minimap_background" (Minimap background) +// "minimap_current_line" (Minimap current cursor line background) +// "minimap_hover" (Minimap mouse hover color) +// "minimap_selection" (Minimap text selection color) +// "minimap_highlight" (Minimap text highlight color) +// "minimap_visible_area" (Minimap visible area marker color) SyntaxColorScheme SyntaxColorScheme::getDefault() { return { "eepp", @@ -52,7 +59,7 @@ SyntaxColorScheme SyntaxColorScheme::getDefault() { { "line_number", Color( "#525259" ) }, { "line_number2", Color( "#83838f" ) }, // eepp colors - { "line_number_background", Color( "#282a36" ) }, + { "gutter_background", Color( "#282a36" ) }, { "whitespace", Color( "#54575b" ) }, { "line_break_column", Color( "#54575b99" ) }, { "matching_bracket", Color( "#FFFFFF33" ) }, @@ -63,6 +70,7 @@ SyntaxColorScheme SyntaxColorScheme::getDefault() { { "error", { Color::Red } }, { "warning", { Color::Yellow } }, { "notice", Color( "#8abdff" ) }, + { "selection_region", Color( "#39448477" ) }, // minimap colors { "minimap_background", Color( "#282a36AA" ) }, { "minimap_current_line", Color( "#93DDFA40" ) }, @@ -197,10 +205,10 @@ SyntaxColorScheme::getEditorSyntaxStyle( const std::string& type ) const { auto it = mEditorColors.find( type ); if ( it != mEditorColors.end() ) return it->second; - if ( type == "line_number_background" ) + if ( type == "gutter_background" || "minimap_background" ) return getEditorSyntaxStyle( "background" ); else if ( type == "guide" || type == "line_break_column" || type == "matching_bracket" || - type == "matching_selection" ) + type == "matching_selection" || type == "selection_region" ) return getEditorSyntaxStyle( "selection" ); else if ( type == "suggestion" ) return StyleDefault.getEditorSyntaxStyle( "suggestion" ); @@ -212,8 +220,6 @@ SyntaxColorScheme::getEditorSyntaxStyle( const std::string& type ) const { return StyleDefault.getEditorSyntaxStyle( "warning" ); else if ( type == "notice" ) return StyleDefault.getEditorSyntaxStyle( "notice" ); - else if ( type == "minimap_background" ) - return StyleEmpty; else if ( type == "minimap_current_line" ) return StyleDefault.getEditorSyntaxStyle( "minimap_current_line" ); else if ( type == "minimap_hover" ) diff --git a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp index 55638ac63..8c9a9f6bc 100644 --- a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp +++ b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp @@ -219,7 +219,7 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { // JavaScript add( { "JavaScript", - { "%.js$", "%.json$", "%.cson$" }, + { "%.js$" }, { { { "//.-\n" }, "comment" }, { { "/%*", "%*/" }, "comment" }, @@ -254,6 +254,20 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { { "enum", "keyword" } }, "//" } ); + // JSON + add( { "JSON", + { "%.json$", "%.cson$" }, + { + { { "\"", "\"", "\\" }, "string" }, + { { "'", "'", "\\" }, "string" }, + { { "`", "`", "\\" }, "string" }, + { { "0x[%da-fA-F]+" }, "number" }, + { { "-?%d+[%d%.eE]*" }, "number" }, + { { "-?%.?%d+" }, "number" }, + }, + {}, + "//" } ); + // TypeScript add( { "TypeScript", diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index aca113a78..6d7ab6050 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -243,6 +243,11 @@ void UICodeEditor::draw() { if ( mHighlightTextRange.isValid() && mHighlightTextRange.hasSelection() ) { drawTextRange( mHighlightTextRange, lineRange, startScroll, lineHeight, + mColorScheme.getEditorSyntaxStyle( "selection_region" ).color ); + } + + if ( mDoc->hasSelection() ) { + drawTextRange( mDoc->getSelection( true ), lineRange, startScroll, lineHeight, mFontStyleConfig.getFontSelectionBackColor() ); } @@ -254,11 +259,6 @@ void UICodeEditor::draw() { drawWordMatch( mHighlightWord, lineRange, startScroll, lineHeight ); } - if ( mDoc->hasSelection() ) { - drawTextRange( mDoc->getSelection( true ), lineRange, startScroll, lineHeight, - mFontStyleConfig.getFontSelectionBackColor() ); - } - // Draw tab marker if ( mShowWhitespaces ) { drawWhitespaces( lineRange, startScroll, lineHeight ); @@ -704,7 +704,7 @@ void UICodeEditor::updateColorScheme() { mFontStyleConfig.setFontSelectionBackColor( mColorScheme.getEditorColor( "selection" ) ); mLineNumberFontColor = mColorScheme.getEditorColor( "line_number" ); mLineNumberActiveFontColor = mColorScheme.getEditorColor( "line_number2" ); - mLineNumberBackgroundColor = mColorScheme.getEditorColor( "line_number_background" ); + mLineNumberBackgroundColor = mColorScheme.getEditorColor( "gutter_background" ); mCurrentLineBackgroundColor = mColorScheme.getEditorColor( "line_highlight" ); mCaretColor = mColorScheme.getEditorColor( "caret" ); mWhitespaceColor = mColorScheme.getEditorColor( "guide" ); @@ -712,8 +712,6 @@ void UICodeEditor::updateColorScheme() { mMatchingBracketColor = mColorScheme.getEditorColor( "matching_bracket" ); mSelectionMatchColor = mColorScheme.getEditorColor( "matching_selection" ); mMinimapBackgroundColor = mColorScheme.getEditorColor( "minimap_background" ); - if ( Color::Transparent == mMinimapBackgroundColor ) - mMinimapBackgroundColor = getBackgroundColor(); mMinimapVisibleAreaColor = mColorScheme.getEditorColor( "minimap_visible_area" ); mMinimapCurrentLineColor = mColorScheme.getEditorColor( "minimap_current_line" ); mMinimapHoverColor = mColorScheme.getEditorColor( "minimap_hover" ); @@ -2098,6 +2096,7 @@ void UICodeEditor::cut() { void UICodeEditor::paste() { mDoc->textInput( getUISceneNode()->getWindow()->getClipboard()->getText() ); + sendCommonEvent( Event::OnTextPasted ); } void UICodeEditor::fontSizeGrow() { @@ -2240,12 +2239,13 @@ void UICodeEditor::drawSelectionMatch( const std::pair& lineRange, String text( selectionLine.substr( selection.start().column(), selection.end().column() - selection.start().column() ) ); if ( !text.empty() ) - drawWordMatch( text, lineRange, startScroll, lineHeight ); + drawWordMatch( text, lineRange, startScroll, lineHeight, true ); } } void UICodeEditor::drawWordMatch( const String& text, const std::pair& lineRange, - const Vector2f& startScroll, const Float& lineHeight ) { + const Vector2f& startScroll, const Float& lineHeight, + bool ignoreSelectionMatch ) { if ( text.empty() ) return; Primitives primitives; @@ -2257,9 +2257,17 @@ void UICodeEditor::drawWordMatch( const String& text, const std::pair& // Skip ridiculously long lines. if ( line.size() > 300 ) continue; + do { pos = line.find( text, pos ); if ( pos != String::InvalidPos ) { + if ( ignoreSelectionMatch ) { + TextRange selection = mDoc->getSelection( true ); + if ( selection.inSameLine() && selection.start().line() == ln && + selection.start().column() == (Int64)pos ) + break; + } + Rectf selRect; Int64 startCol = pos; Int64 endCol = pos + text.size(); @@ -2878,6 +2886,11 @@ void UICodeEditor::drawMinimap( const Vector2f& start, } flushBatch( "normal" ); + if ( mHighlightTextRange.isValid() && mHighlightTextRange.hasSelection() ) { + drawTextRange( mHighlightTextRange, index, + Color( mMinimapSelectionColor ).blendAlpha( mAlpha ) ); + } + if ( mDoc->hasSelection() ) { drawTextRange( mDoc->getSelection( true ), index, Color( mMinimapSelectionColor ).blendAlpha( mAlpha ) ); diff --git a/src/eepp/ui/uitextinput.cpp b/src/eepp/ui/uitextinput.cpp index f4179dd70..06f025d91 100644 --- a/src/eepp/ui/uitextinput.cpp +++ b/src/eepp/ui/uitextinput.cpp @@ -678,8 +678,13 @@ void UITextInput::cut() { void UITextInput::paste() { String pasted( getUISceneNode()->getWindow()->getClipboard()->getText() ); - String::replaceAll( pasted, "\n", "" ); + if ( mEscapePastedText ) { + pasted.escape(); + } else { + String::replaceAll( pasted, "\n", "" ); + } mDoc.textInput( pasted ); + sendCommonEvent( Event::OnTextPasted ); } void UITextInput::registerCommands() { @@ -789,6 +794,14 @@ void UITextInput::setMenuIconSize( size_t menuIconSize ) { mMenuIconSize = menuIconSize; } +bool UITextInput::getEscapePastedText() const { + return mEscapePastedText; +} + +void UITextInput::setEscapePastedText( bool escapePastedText ) { + mEscapePastedText = escapePastedText; +} + Drawable* UITextInput::findIcon( const std::string& name ) { UIIcon* icon = getUISceneNode()->findIcon( name ); if ( icon ) diff --git a/src/tools/ecode/docsearchcontroller.cpp b/src/tools/ecode/docsearchcontroller.cpp index 987863671..275c87047 100644 --- a/src/tools/ecode/docsearchcontroller.cpp +++ b/src/tools/ecode/docsearchcontroller.cpp @@ -30,6 +30,7 @@ void DocSearchController::initSearchBar( kbind.addKeybindsStringUnordered( keybindings ); UITextInput* findInput = mSearchBarLayout->find( "search_find" ); + findInput->setEscapePastedText( true ); UITextInput* replaceInput = mSearchBarLayout->find( "search_replace" ); UICheckBox* caseSensitiveChk = mSearchBarLayout->find( "case_sensitive" ); UICheckBox* escapeSequenceChk = mSearchBarLayout->find( "escape_sequence" ); @@ -86,6 +87,14 @@ void DocSearchController::initSearchBar( } } } ); + findInput->addEventListener( + Event::OnTextPasted, [&, findInput, escapeSequenceChk]( const Event* ) { + if ( findInput->getUISceneNode()->getWindow()->getClipboard()->getText().find( '\n' ) != + String::InvalidPos ) { + if ( !escapeSequenceChk->isChecked() ) + escapeSequenceChk->setChecked( true ); + } + } ); mSearchBarLayout->addCommand( "close-searchbar", [&] { hideSearchBar(); if ( mEditorSplitter->getCurEditor() ) @@ -161,22 +170,16 @@ void DocSearchController::showFindView() { if ( doc.getSelection().hasSelection() ) { String text = doc.getSelectedText(); - if ( !doc.getSelection().inSameLine() ) { - text.escape(); - UICheckBox* escapeSequenceChk = mSearchBarLayout->find( "escape_sequence" ); - if ( !escapeSequenceChk->isChecked() ) - escapeSequenceChk->setChecked( true ); - } - if ( !text.empty() ) { + + if ( !doc.getSelection().inSameLine() ) + mSearchState.range = doc.getSelection( true ); + + if ( !text.empty() && doc.getSelection().inSameLine() ) { findInput->setText( text ); findInput->getDocument().selectAll(); } else if ( !findInput->getText().empty() ) { findInput->getDocument().selectAll(); } - } else if ( doc.getSelection().hasSelection() ) { - mSearchState.range = doc.getSelection( true ); - if ( !findInput->getText().empty() ) - findInput->getDocument().selectAll(); } mSearchState.text = findInput->getText(); editor->setHighlightTextRange( mSearchState.range ); diff --git a/src/tools/ecode/modules/linter/lintermodule.cpp b/src/tools/ecode/modules/linter/lintermodule.cpp index bc574c5df..07ad928b7 100644 --- a/src/tools/ecode/modules/linter/lintermodule.cpp +++ b/src/tools/ecode/modules/linter/lintermodule.cpp @@ -93,6 +93,12 @@ void LinterModule::load( const std::string& lintersPath ) { if ( obj.contains( "use_tmp_folder" ) ) linter.useTmpFolder = obj["use_tmp_folder"].get(); + if ( obj.contains( "no_errors_exit_code" ) && + obj["no_errors_exit_code"].is_number_integer() ) { + linter.hasNoErrorsExitCode = true; + linter.noErrorsExitCode = obj["no_errors_exit_code"].get(); + } + mLinters.emplace_back( std::move( linter ) ); } @@ -247,6 +253,12 @@ void LinterModule::runLinter( std::shared_ptr doc, const Linter& l subprocess_join( &subprocess, &returnCode ); subprocess_destroy( &subprocess ); + if ( linter.hasNoErrorsExitCode && linter.noErrorsExitCode == returnCode ) { + Lock matchesLock( mMatchesMutex ); + mMatches[doc.get()] = {}; + return; + } + if ( !linter.expectedExitCodes.empty() && std::find( linter.expectedExitCodes.begin(), linter.expectedExitCodes.end(), returnCode ) == linter.expectedExitCodes.end() ) @@ -313,25 +325,26 @@ void LinterModule::runLinter( std::shared_ptr doc, const Linter& l } } - { - Lock matchesLock( mMatchesMutex ); - totalMatches = matches.size(); - for ( const auto& matchLine : matches ) { - for ( const auto& match : matchLine.second ) { - switch ( match.type ) { - case LinterType::Warning: - ++totalWarns; - break; - case LinterType::Notice: - ++totalNotice; - break; - case LinterType::Error: - default: - ++totalErrors; - break; - } + totalMatches = matches.size(); + for ( const auto& matchLine : matches ) { + for ( const auto& match : matchLine.second ) { + switch ( match.type ) { + case LinterType::Warning: + ++totalWarns; + break; + case LinterType::Notice: + ++totalNotice; + break; + case LinterType::Error: + default: + ++totalErrors; + break; } } + } + + { + Lock matchesLock( mMatchesMutex ); mMatches[doc.get()] = std::move( matches ); } @@ -390,20 +403,25 @@ void LinterModule::drawAfterLineText( UICodeEditor* editor, const Int64& index, Int64 strSize = 0; TextPosition endPos; Vector2f pos; + if ( minCol < text.size() - 1 ) { - endPos = doc->nextWordBoundary( { index, (Int64)minCol } ); + endPos = doc->nextWordBoundary( { match.pos.line(), (Int64)minCol } ); strSize = eemax( (Int64)0, static_cast( endPos.column() - minCol ) ); pos = { position.x + editor->getXOffsetCol( { match.pos.line(), (Int64)minCol } ), position.y }; } else { - endPos = doc->previousWordBoundary( { index, (Int64)minCol } ); + endPos = doc->previousWordBoundary( { match.pos.line(), (Int64)minCol } ); strSize = eemax( (Int64)0, static_cast( minCol - endPos.column() ) ); pos = { position.x + editor->getXOffsetCol( { match.pos.line(), (Int64)endPos.column() } ), position.y }; } - if ( strSize <= 0 ) - return; + + if ( strSize == 0 ) { + strSize = 1; + pos = { position.x, position.y }; + } + std::string str( strSize, '~' ); String string( str ); line.setString( string ); diff --git a/src/tools/ecode/modules/linter/lintermodule.hpp b/src/tools/ecode/modules/linter/lintermodule.hpp index 8ad314216..998575f26 100644 --- a/src/tools/ecode/modules/linter/lintermodule.hpp +++ b/src/tools/ecode/modules/linter/lintermodule.hpp @@ -18,6 +18,7 @@ struct Linter { bool columnsStartAtZero{ false }; bool deduplicate{ false }; bool useTmpFolder{ false }; + bool hasNoErrorsExitCode{ false }; struct { int line{ 1 }; int col{ 2 }; @@ -26,6 +27,7 @@ struct Linter { } warningPatternOrder; std::string command; std::vector expectedExitCodes{}; + int noErrorsExitCode{ 0 }; }; struct LinterMatch {