Minor changes to the code editor and text document:

Add command to comment selected lines.
Option to trim trailing white spaces.
Option to ensure new line at end of file option.
Option to allow to select line endings type.
This commit is contained in:
Martín Lucas Golini
2020-06-21 22:43:21 -03:00
parent 2859ebb640
commit a9a866e5df
13 changed files with 184 additions and 35 deletions

View File

@@ -13,14 +13,6 @@
* Add XML tags auto-close.
* Add command to comment selected lines.
* On Save: trim white spaces.
* On Save: Ensure new line at end of file option.
* On Save: Allow to select line endings type.
## Code Editor
Keep improving it:

View File

@@ -119,7 +119,7 @@ class EE_API Console : protected LogReaderInterface {
void addCommand( const String& Command, ConsoleCallback CB );
/** Draw the Console ( allways call it, visible or not ) */
void draw();
void draw( const Time& elapsedTime = Time::Zero );
/** Set the line height ( distance between lines ) */
void setLineHeight( const Float& LineHeight );
@@ -194,7 +194,7 @@ class EE_API Console : protected LogReaderInterface {
void createDefaultCommands();
void fade();
void fade( const Time& elapsedTime );
/** Internal Callback for default command ( clear ) */
void cmdClear( const std::vector<String>& params );

View File

@@ -41,6 +41,8 @@ class EE_API SceneManager {
void setCurrentUISceneNode( UISceneNode* uiSceneNode );
Time getElapsed() const;
protected:
Clock mClock;
UISceneNode* mUISceneNode;

View File

@@ -9,12 +9,15 @@ namespace EE { namespace System {
class EE_API LuaPatternMatcher {
public:
struct Match {
int start;
int end;
int start{-1};
int end{-1};
bool isValid() { return -1 != start && -1 != end; }
};
static std::string match( const std::string& string, const std::string& pattern );
static Match find( const std::string& string, const std::string& pattern );
LuaPatternMatcher( const std::string& pattern );
bool matches( const char* stringSearch, int stringStartOffset,

View File

@@ -35,7 +35,9 @@ class EE_API TextDocument {
virtual void onDocumentLineChanged( const Int64& lineIndex ) = 0;
};
enum IndentType { IndentSpaces, IndentTabs };
enum class IndentType { IndentSpaces, IndentTabs };
enum class LineEnding { LF, CRLF };
TextDocument();
@@ -280,6 +282,8 @@ class EE_API TextDocument {
const String& getNonWordChars() const;
void toggleLineComments();
void setNonWordChars( const String& nonWordChars );
void resetSyntax();
@@ -288,6 +292,18 @@ class EE_API TextDocument {
void setAutoDetectIndentType( bool autodetect );
const LineEnding& getLineEnding() const;
void setLineEnding( const LineEnding& lineEnding );
bool getForceNewLineAtEndOfFile() const;
void setForceNewLineAtEndOfFile( bool forceNewLineAtEndOfFile );
bool getTrimTrailingWhitespaces() const;
void setTrimTrailingWhitespaces( bool trimTrailingWhitespaces );
protected:
friend class UndoStack;
UndoStack mUndoStack;
@@ -295,11 +311,13 @@ class EE_API TextDocument {
std::vector<TextDocumentLine> mLines;
TextRange mSelection;
std::unordered_set<Client*> mClients;
bool mIsCLRF{false};
LineEnding mLineEnding{LineEnding::LF};
bool mIsBOM{false};
bool mAutoDetectIndentType{true};
bool mForceNewLineAtEndOfFile{false};
bool mTrimTrailingWhitespaces{false};
Uint32 mIndentWidth{4};
IndentType mIndentType{IndentTabs};
IndentType mIndentType{IndentType::IndentTabs};
Clock mTimer;
SyntaxDefinition mSyntaxDefinition;
std::string mDefaultFileName;

View File

@@ -216,9 +216,9 @@ void Console::addCommand( const String& Command, ConsoleCallback CB ) {
mCallbacks[Command] = CB;
}
void Console::draw() {
void Console::draw( const Time& elapsedTime ) {
if ( mEnabled && NULL != mFontStyleConfig.Font ) {
fade();
fade( elapsedTime == Time::Zero ? mWindow->getElapsed() : elapsedTime );
if ( mY > 0.0f ) {
if ( mTexId == 0 ) {
@@ -473,15 +473,15 @@ void Console::toggle() {
fadeIn();
}
void Console::fade() {
void Console::fade( const Time& elapsedTime ) {
if ( mCurSide ) {
mCurAlpha -= 255.f * mWindow->getElapsed().asMilliseconds() / mFadeSpeed.asMilliseconds();
mCurAlpha -= 255.f * elapsedTime.asMilliseconds() / mFadeSpeed.asMilliseconds();
if ( mCurAlpha <= 0.0f ) {
mCurAlpha = 0.0f;
mCurSide = !mCurSide;
}
} else {
mCurAlpha += 255.f * mWindow->getElapsed().asMilliseconds() / mFadeSpeed.asMilliseconds();
mCurAlpha += 255.f * elapsedTime.asMilliseconds() / mFadeSpeed.asMilliseconds();
if ( mCurAlpha >= 255.f ) {
mCurAlpha = 255.f;
mCurSide = !mCurSide;
@@ -495,7 +495,7 @@ void Console::fade() {
if ( mFadeIn ) {
mFadeOut = false;
mY += mCurHeight * mWindow->getElapsed().asMilliseconds() / mFadeSpeed.asMilliseconds();
mY += mCurHeight * elapsedTime.asMilliseconds() / mFadeSpeed.asMilliseconds();
mA = ( mY * mMaxAlpha / mCurHeight );
if ( mY > mCurHeight ) {
@@ -507,7 +507,7 @@ void Console::fade() {
if ( mFadeOut ) {
mFadeIn = false;
mY -= mCurHeight * mWindow->getElapsed().asMilliseconds() / mFadeSpeed.asMilliseconds();
mY -= mCurHeight * elapsedTime.asMilliseconds() / mFadeSpeed.asMilliseconds();
mA = ( mY * mMaxAlpha / mCurHeight );
if ( mY <= 0.0f ) {

View File

@@ -75,4 +75,8 @@ void SceneManager::setCurrentUISceneNode( UISceneNode* uiSceneNode ) {
mUISceneNode = uiSceneNode;
}
Time SceneManager::getElapsed() const {
return mClock.getElapsedTime();
}
}} // namespace EE::Scene

View File

@@ -352,6 +352,7 @@ bool SceneNode::getDrawDebugData() const {
void SceneNode::setDrawBoxes( bool draw ) {
mDrawBoxes = draw;
invalidateDraw();
}
bool SceneNode::getDrawBoxes() const {
@@ -360,6 +361,7 @@ bool SceneNode::getDrawBoxes() const {
void SceneNode::setHighlightOver( bool Highlight ) {
mHighlightOver = Highlight;
invalidateDraw();
}
bool SceneNode::getHighlightOver() const {
@@ -368,6 +370,7 @@ bool SceneNode::getHighlightOver() const {
void SceneNode::setHighlightFocus( bool Highlight ) {
mHighlightFocus = Highlight;
invalidateDraw();
}
bool SceneNode::getHighlightFocus() const {
@@ -376,6 +379,7 @@ bool SceneNode::getHighlightFocus() const {
void SceneNode::setHighlightInvalidation( bool Highlight ) {
mHighlightInvalidation = Highlight;
invalidateDraw();
}
bool SceneNode::getHighlightInvalidation() const {
@@ -384,6 +388,7 @@ bool SceneNode::getHighlightInvalidation() const {
void SceneNode::setHighlightOverColor( const Color& color ) {
mHighlightOverColor = color;
invalidateDraw();
}
const Color& SceneNode::getHighlightOverColor() const {
@@ -392,6 +397,7 @@ const Color& SceneNode::getHighlightOverColor() const {
void SceneNode::setHighlightFocusColor( const Color& color ) {
mHighlightFocusColor = color;
invalidateDraw();
}
const Color& SceneNode::getHighlightFocusColor() const {
@@ -400,6 +406,7 @@ const Color& SceneNode::getHighlightFocusColor() const {
void SceneNode::setHighlightInvalidationColor( const Color& color ) {
mHighlightInvalidationColor = color;
invalidateDraw();
}
const Color& SceneNode::getHighlightInvalidationColor() const {

View File

@@ -21,6 +21,15 @@ std::string LuaPatternMatcher::match( const std::string& string, const std::stri
return "";
}
LuaPatternMatcher::Match LuaPatternMatcher::find( const std::string& string,
const std::string& pattern ) {
LuaPatternMatcher matcher( pattern );
int start = 0, end = 0;
if ( matcher.find( string, start, end ) )
return {start, end};
return {-1, -1};
}
LuaPatternMatcher::LuaPatternMatcher( const std::string& pattern ) : mPattern( pattern ) {
if ( !sFailHandlerInitialized ) {
sFailHandlerInitialized = true;

View File

@@ -1687,6 +1687,46 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() {
{{"^%+.-\n"}, "function"},
{{"^%-.-\n"}, "keyword2"},
}} );
// Add Java
add( {"Java",
{"%.java$"},
{
{{"//.-\n"}, "comment"},
{{"/%*", "%*/"}, "comment"},
{{"\"", "\"", "\\"}, "string"},
{{"'", "'", "\\"}, "string"},
{{"'\\x%x?%x?%x?%x'"}, "string"},
{{"'\\u%x%x%x%x'"}, "string"},
{{"'\\?.'"}, "string"},
{{"-?0x%x+"}, "number"},
{{"-?%d+[%d%.eE]*f?"}, "number"},
{{"-?%.?%d+f?"}, "number"},
{{"[%+%-=/%*%^%%<>!~|&]"}, "operator"},
{{"[%a_][%w_]*%f[(]"}, "function"},
{{"[%a_][%w_]*"}, "symbol"},
},
{
{"if", "keyword"}, {"then", "keyword"}, {"else", "keyword"},
{"elseif", "keyword"}, {"do", "keyword"}, {"while", "keyword"},
{"for", "keyword"}, {"new", "keyword"}, {"break", "keyword"},
{"continue", "keyword"}, {"return", "keyword"}, {"goto", "keyword"},
{"class", "keyword"}, {"implements", "keyword"}, {"extends", "keyword"},
{"private", "keyword"}, {"protected", "keyword"}, {"public", "keyword"},
{"abstract", "keyword"}, {"interface", "keyword"}, {"assert", "keyword"},
{"import", "keyword"}, {"native", "keyword"}, {"package", "keyword"},
{"super", "keyword"}, {"synchronized", "keyword"}, {"instanceof", "keyword"},
{"enum", "keyword"}, {"catch", "keyword"}, {"throw", "keyword"},
{"throws", "keyword"}, {"try", "keyword"}, {"transient", "keyword"},
{"finally", "keyword"}, {"static", "keyword"}, {"volatile", "keyword"},
{"final", "keyword"}, {"switch", "keyword"}, {"case", "keyword"},
{"default", "keyword"}, {"void", "keyword"}, {"int", "keyword2"},
{"short", "keyword2"}, {"byte", "keyword2"}, {"long", "keyword2"},
{"float", "keyword2"}, {"double", "keyword2"}, {"char", "keyword2"},
{"boolean", "keyword2"}, {"true", "literal"}, {"false", "literal"},
{"null", "literal"},
},
"//"} );
}
SyntaxDefinition& SyntaxDefinitionManager::add( SyntaxDefinition&& syntaxStyle ) {

View File

@@ -99,10 +99,10 @@ bool TextDocument::loadFromStream( IOStream& file ) {
if ( lineBuffer[lineBuffer.size() - 1] == '\n' || !consume ) {
if ( mLines.empty() && lineBuffer.size() > 1 &&
lineBuffer[lineBuffer.size() - 2] == '\r' ) {
mIsCLRF = true;
mLineEnding = LineEnding::CRLF;
}
if ( mIsCLRF && lineBuffer.size() > 1 ) {
if ( mLineEnding == LineEnding::CRLF && lineBuffer.size() > 1 ) {
lineBuffer[lineBuffer.size() - 2] = '\n';
lineBuffer.resize( lineBuffer.size() - 1 );
}
@@ -175,9 +175,9 @@ void TextDocument::guessIndentType() {
return;
}
if ( guessTabs > guessSpaces ) {
mIndentType = IndentTabs;
mIndentType = IndentType::IndentTabs;
} else {
mIndentType = IndentSpaces;
mIndentType = IndentType::IndentSpaces;
mIndentWidth = guessWidth.begin()->first;
}
}
@@ -195,6 +195,30 @@ void TextDocument::setAutoDetectIndentType( bool autodetect ) {
mAutoDetectIndentType = autodetect;
}
const TextDocument::LineEnding& TextDocument::getLineEnding() const {
return mLineEnding;
}
void TextDocument::setLineEnding( const LineEnding& lineEnding ) {
mLineEnding = lineEnding;
}
bool TextDocument::getForceNewLineAtEndOfFile() const {
return mForceNewLineAtEndOfFile;
}
void TextDocument::setForceNewLineAtEndOfFile( bool forceNewLineAtEndOfFile ) {
mForceNewLineAtEndOfFile = forceNewLineAtEndOfFile;
}
bool TextDocument::getTrimTrailingWhitespaces() const {
return mTrimTrailingWhitespaces;
}
void TextDocument::setTrimTrailingWhitespaces( bool trimTrailingWhitespaces ) {
mTrimTrailingWhitespaces = trimTrailingWhitespaces;
}
bool TextDocument::loadFromFile( const std::string& path ) {
if ( !FileSystem::fileExists( path ) && PackManager::instance()->isFallbackToPacksActive() ) {
std::string pathFix( path );
@@ -246,6 +270,7 @@ bool TextDocument::save( const std::string& path, const bool& utf8bom ) {
bool TextDocument::save( IOStream& stream, const bool& utf8bom ) {
if ( !stream.isOpen() || mLines.empty() )
return false;
const std::string whitespaces( " \t\f\v\n\r" );
char nl = '\n';
if ( utf8bom ) {
unsigned char bom[] = {0xEF, 0xBB, 0xBF};
@@ -254,13 +279,30 @@ bool TextDocument::save( IOStream& stream, const bool& utf8bom ) {
size_t lastLine = mLines.size() - 1;
for ( size_t i = 0; i <= lastLine; i++ ) {
std::string text( mLines[i].toUtf8() );
if ( i == lastLine && !text.empty() && text[text.size() - 1] == '\n' ) {
// Last \n is added by the document but it's not part of the document.
text.pop_back();
if ( text.empty() )
continue;
if ( mTrimTrailingWhitespaces && text.size() > 1 &&
whitespaces.find( text[text.size() - 2] ) != std::string::npos ) {
size_t pos = text.find_last_not_of( whitespaces );
if ( pos != std::string::npos ) {
text.erase( pos + 1 );
text += nl;
} else {
text = nl;
}
mLines[i].setText( text );
notifyLineChanged( i );
notifyTextChanged();
}
if ( mIsCLRF ) {
if ( i == lastLine ) {
if ( !text.empty() && text[text.size() - 1] == '\n' && !mForceNewLineAtEndOfFile ) {
// Last \n is added by the document but it's not part of the document.
text.pop_back();
if ( text.empty() )
continue;
} else if ( !text.empty() && text[text.size()] != '\n' && mForceNewLineAtEndOfFile ) {
text += mLineEnding == LineEnding::CRLF ? "\n\r" : "\n";
}
}
if ( mLineEnding == LineEnding::CRLF ) {
text[text.size() - 1] = '\r';
stream.write( text.c_str(), text.size() );
stream.write( &nl, 1 );
@@ -951,7 +993,7 @@ void TextDocument::appendLineIfLastLine( Int64 line ) {
}
String TextDocument::getIndentString() {
if ( IndentSpaces == mIndentType ) {
if ( IndentType::IndentSpaces == mIndentType ) {
return String( std::string( mIndentWidth, ' ' ) );
}
return String( "\t" );
@@ -1178,6 +1220,27 @@ const String& TextDocument::getNonWordChars() const {
return mNonWordChars;
}
void TextDocument::toggleLineComments() {
std::string comment = mSyntaxDefinition.getComment();
if ( comment.empty() )
return;
std::string commentText = comment + " ";
TextRange selection = getSelection( true );
bool uncomment = true;
for ( Int64 i = selection.start().line(); i < selection.end().line(); i++ ) {
const String& text = mLines[i].getText();
if ( text.find_first_not_of( " \t\n" ) != std::string::npos &&
text.find( commentText ) == std::string::npos ) {
uncomment = false;
}
}
if ( uncomment ) {
removeFromStartOfSelectedLines( commentText, true );
} else {
insertAtStartOfSelectedLines( commentText, true );
}
}
void TextDocument::setNonWordChars( const String& nonWordChars ) {
mNonWordChars = nonWordChars;
}
@@ -1257,6 +1320,7 @@ void TextDocument::initializeCommands() {
mCommands["unindent"] = [&] { unindent(); };
mCommands["undo"] = [&] { undo(); };
mCommands["redo"] = [&] { redo(); };
mCommands["toggle-line-comments"] = [&] { toggleLineComments(); };
}
TextDocument::Client::~Client() {}

View File

@@ -1653,6 +1653,7 @@ void UICodeEditor::registerKeybindings() {
{{KEY_MINUS, KEYMOD_CTRL}, "font-size-shrink"},
{{KEY_KP_MINUS, KEYMOD_CTRL}, "font-size-shrink"},
{{KEY_0, KEYMOD_CTRL}, "font-size-reset"},
{{KEY_KP_DIVIDE, KEYMOD_CTRL}, "toggle-line-comments" },
} );
}

View File

@@ -226,7 +226,15 @@ UICodeEditor* App::createCodeEditor() {
codeEditor->getDocument().setCommand( "fullscreen-toggle",
[&]() { mWindow->toggleFullscreen(); } );
codeEditor->getDocument().setCommand( "open-file", [&] { openFileDialog(); } );
codeEditor->getDocument().setCommand( "console-toggle", [&] { mConsole->toggle(); } );
codeEditor->getDocument().setCommand( "console-toggle", [&] {
mConsole->toggle();
bool lock = mConsole->isActive();
for ( auto tabW : mTabWidgets ) {
for ( size_t i = 0; i < tabW->getTabCount(); i++ ) {
tabW->getTab( i )->getOwnedWidget()->asType<UICodeEditor>()->setLocked( lock );
}
}
} );
codeEditor->getDocument().setCommand( "close-doc", [&] { tryTabClose( mCurEditor ); } );
codeEditor->getDocument().setCommand( "create-new", [&] {
auto d = createCodeEditorInTabWidget( tabWidgetFromEditor( mCurEditor ) );
@@ -761,13 +769,14 @@ void App::mainLoop() {
mUISceneNode->setDrawDebugData( !mUISceneNode->getDrawDebugData() );
}
Time elapsed = SceneManager::instance()->getElapsed();
SceneManager::instance()->update();
if ( SceneManager::instance()->getUISceneNode()->invalidated() || mConsole->isActive() ||
mConsole->isFading() ) {
mWindow->clear();
SceneManager::instance()->draw();
mConsole->draw();
mConsole->draw( elapsed );
mWindow->display();
} else {
Sys::sleep( Milliseconds( mWindow->hasFocus() ? 1 : 16 ) );