2018-08-17 12:31:33 +00:00
# include <algorithm>
# include <chrono>
# include <string>
# include <regex>
# include <cmath>
# include "TextEditor.h"
2019-01-19 13:05:54 +00:00
# define IMGUI_DEFINE_MATH_OPERATORS
# include "../imgui/imgui.h" // for imGui::GetCurrentWindow()
2018-08-17 12:38:57 +00:00
namespace tracy
{
2018-08-17 12:31:33 +00:00
// TODO
// - multiline comments vs single-line: latter is blocking start of a ML
// - handle unicode/utf
// - testing
template < class InputIt1 , class InputIt2 , class BinaryPredicate >
bool equals ( InputIt1 first1 , InputIt1 last1 ,
InputIt2 first2 , InputIt2 last2 , BinaryPredicate p )
{
2019-01-19 13:05:54 +00:00
for ( ; first1 ! = last1 & & first2 ! = last2 ; + + first1 , + + first2 )
2018-08-17 12:31:33 +00:00
{
if ( ! p ( * first1 , * first2 ) )
return false ;
}
return first1 = = last1 & & first2 = = last2 ;
}
TextEditor : : TextEditor ( )
2018-10-30 21:52:57 +00:00
: mLineSpacing ( 1.0f )
2018-08-17 12:31:33 +00:00
, mUndoIndex ( 0 )
, mTabSize ( 4 )
, mOverwrite ( false )
, mReadOnly ( false )
, mWithinRender ( false )
, mScrollToCursor ( false )
2019-01-19 13:05:54 +00:00
, mScrollToTop ( false )
2018-08-17 12:31:33 +00:00
, mTextChanged ( false )
2018-10-30 21:52:57 +00:00
, mTextStart ( 20.0f )
, mLeftMargin ( 10 )
2018-08-17 12:31:33 +00:00
, mColorRangeMin ( 0 )
, mColorRangeMax ( 0 )
, mSelectionMode ( SelectionMode : : Normal )
2019-01-19 13:05:54 +00:00
, mCheckComments ( true )
, mLastClick ( - 1.0f )
2018-08-17 12:31:33 +00:00
{
SetPalette ( GetDarkPalette ( ) ) ;
SetLanguageDefinition ( LanguageDefinition : : HLSL ( ) ) ;
mLines . push_back ( Line ( ) ) ;
}
TextEditor : : ~ TextEditor ( )
{
}
void TextEditor : : SetLanguageDefinition ( const LanguageDefinition & aLanguageDef )
{
mLanguageDefinition = aLanguageDef ;
mRegexList . clear ( ) ;
for ( auto & r : mLanguageDefinition . mTokenRegexStrings )
mRegexList . push_back ( std : : make_pair ( std : : regex ( r . first , std : : regex_constants : : optimize ) , r . second ) ) ;
}
void TextEditor : : SetPalette ( const Palette & aValue )
{
2019-01-19 13:05:54 +00:00
mPaletteBase = aValue ;
2018-08-17 12:31:33 +00:00
}
int TextEditor : : AppendBuffer ( std : : string & aBuffer , char chr , int aIndex )
{
if ( chr ! = ' \t ' )
{
aBuffer . push_back ( chr ) ;
return aIndex + 1 ;
}
else
{
2018-10-30 21:52:57 +00:00
//auto num = mTabSize - aIndex % mTabSize;
//for (int j = num; j > 0; --j)
// aBuffer.push_back(' ');
//return aIndex + num;
return aIndex ;
2018-08-17 12:31:33 +00:00
}
}
std : : string TextEditor : : GetText ( const Coordinates & aStart , const Coordinates & aEnd ) const
{
std : : string result ;
int prevLineNo = aStart . mLine ;
for ( auto it = aStart ; it < = aEnd ; Advance ( it ) )
{
2019-01-19 13:05:54 +00:00
if ( prevLineNo ! = it . mLine & & it . mLine < ( int ) mLines . size ( ) )
2018-08-17 12:31:33 +00:00
result . push_back ( ' \n ' ) ;
if ( it = = aEnd )
break ;
prevLineNo = it . mLine ;
const auto & line = mLines [ it . mLine ] ;
if ( ! line . empty ( ) & & it . mColumn < ( int ) line . size ( ) )
result . push_back ( line [ it . mColumn ] . mChar ) ;
}
return result ;
}
TextEditor : : Coordinates TextEditor : : GetActualCursorCoordinates ( ) const
{
return SanitizeCoordinates ( mState . mCursorPosition ) ;
}
TextEditor : : Coordinates TextEditor : : SanitizeCoordinates ( const Coordinates & aValue ) const
{
auto line = aValue . mLine ;
auto column = aValue . mColumn ;
if ( line > = ( int ) mLines . size ( ) )
{
2018-10-30 21:52:57 +00:00
if ( mLines . empty ( ) )
{
line = 0 ;
column = 0 ;
}
else
{
line = ( int ) mLines . size ( ) - 1 ;
column = ( int ) mLines [ line ] . size ( ) ;
}
2018-08-17 12:31:33 +00:00
}
else
{
column = mLines . empty ( ) ? 0 : std : : min ( ( int ) mLines [ line ] . size ( ) , aValue . mColumn ) ;
}
return Coordinates ( line , column ) ;
}
void TextEditor : : Advance ( Coordinates & aCoordinates ) const
{
if ( aCoordinates . mLine < ( int ) mLines . size ( ) )
{
auto & line = mLines [ aCoordinates . mLine ] ;
if ( aCoordinates . mColumn + 1 < ( int ) line . size ( ) )
+ + aCoordinates . mColumn ;
else
{
+ + aCoordinates . mLine ;
aCoordinates . mColumn = 0 ;
}
}
}
void TextEditor : : DeleteRange ( const Coordinates & aStart , const Coordinates & aEnd )
{
assert ( aEnd > = aStart ) ;
assert ( ! mReadOnly ) ;
if ( aEnd = = aStart )
return ;
if ( aStart . mLine = = aEnd . mLine )
{
auto & line = mLines [ aStart . mLine ] ;
if ( aEnd . mColumn > = ( int ) line . size ( ) )
line . erase ( line . begin ( ) + aStart . mColumn , line . end ( ) ) ;
else
line . erase ( line . begin ( ) + aStart . mColumn , line . begin ( ) + aEnd . mColumn ) ;
}
else
{
auto & firstLine = mLines [ aStart . mLine ] ;
auto & lastLine = mLines [ aEnd . mLine ] ;
firstLine . erase ( firstLine . begin ( ) + aStart . mColumn , firstLine . end ( ) ) ;
lastLine . erase ( lastLine . begin ( ) , lastLine . begin ( ) + aEnd . mColumn ) ;
if ( aStart . mLine < aEnd . mLine )
firstLine . insert ( firstLine . end ( ) , lastLine . begin ( ) , lastLine . end ( ) ) ;
if ( aStart . mLine < aEnd . mLine )
RemoveLine ( aStart . mLine + 1 , aEnd . mLine + 1 ) ;
}
mTextChanged = true ;
}
int TextEditor : : InsertTextAt ( Coordinates & /* inout */ aWhere , const char * aValue )
{
assert ( ! mReadOnly ) ;
int totalLines = 0 ;
auto chr = * aValue ;
while ( chr ! = ' \0 ' )
{
2018-10-30 21:52:57 +00:00
assert ( ! mLines . empty ( ) ) ;
2018-08-17 12:31:33 +00:00
if ( chr = = ' \r ' )
{
// skip
}
else if ( chr = = ' \n ' )
{
if ( aWhere . mColumn < ( int ) mLines [ aWhere . mLine ] . size ( ) )
{
auto & newLine = InsertLine ( aWhere . mLine + 1 ) ;
auto & line = mLines [ aWhere . mLine ] ;
newLine . insert ( newLine . begin ( ) , line . begin ( ) + aWhere . mColumn , line . end ( ) ) ;
line . erase ( line . begin ( ) + aWhere . mColumn , line . end ( ) ) ;
}
else
{
InsertLine ( aWhere . mLine + 1 ) ;
}
+ + aWhere . mLine ;
aWhere . mColumn = 0 ;
+ + totalLines ;
}
else
{
auto & line = mLines [ aWhere . mLine ] ;
line . insert ( line . begin ( ) + aWhere . mColumn , Glyph ( chr , PaletteIndex : : Default ) ) ;
+ + aWhere . mColumn ;
}
chr = * ( + + aValue ) ;
mTextChanged = true ;
}
return totalLines ;
}
void TextEditor : : AddUndo ( UndoRecord & aValue )
{
assert ( ! mReadOnly ) ;
mUndoBuffer . resize ( mUndoIndex + 1 ) ;
mUndoBuffer . back ( ) = aValue ;
+ + mUndoIndex ;
}
TextEditor : : Coordinates TextEditor : : ScreenPosToCoordinates ( const ImVec2 & aPosition ) const
{
ImVec2 origin = ImGui : : GetCursorScreenPos ( ) ;
ImVec2 local ( aPosition . x - origin . x , aPosition . y - origin . y ) ;
int lineNo = std : : max ( 0 , ( int ) floor ( local . y / mCharAdvance . y ) ) ;
2018-10-30 21:52:57 +00:00
/*
Compute columnCoord according to text size
*/
2019-01-19 13:05:54 +00:00
int columnCoord = 0 ;
float columnWidth = 0.0f ;
std : : string cumulatedString = " " ;
float cumulatedStringWidth [ 2 ] = { 0.0f , 0.0f } ; //( [0] is the lastest, [1] is the previous. I use that trick to check where cursor is exactly (important for tabs)
2018-10-30 21:52:57 +00:00
2018-08-17 12:31:33 +00:00
if ( lineNo > = 0 & & lineNo < ( int ) mLines . size ( ) )
{
2019-01-19 13:05:54 +00:00
auto & line = mLines . at ( lineNo ) ;
2018-10-30 21:52:57 +00:00
// First we find the hovered column coord.
2019-01-19 13:05:54 +00:00
while ( mTextStart + cumulatedStringWidth [ 0 ] < local . x & &
( size_t ) columnCoord < line . size ( ) )
{
cumulatedStringWidth [ 1 ] = cumulatedStringWidth [ 0 ] ;
2018-10-30 21:52:57 +00:00
cumulatedString + = line [ columnCoord ] . mChar ;
2019-03-15 00:06:27 +00:00
cumulatedStringWidth [ 0 ] = ImGui : : GetFont ( ) - > CalcTextSizeA ( ImGui : : GetFontSize ( ) , FLT_MAX , - 1.0f , cumulatedString . c_str ( ) , nullptr , nullptr ) . x ;
2018-10-30 21:52:57 +00:00
columnWidth = ( cumulatedStringWidth [ 0 ] - cumulatedStringWidth [ 1 ] ) ;
columnCoord + + ;
2018-08-17 12:31:33 +00:00
}
2018-10-30 21:52:57 +00:00
// Then we reduce by 1 column coord if cursor is on the left side of the hovered column.
2019-01-19 13:05:54 +00:00
if ( mTextStart + cumulatedStringWidth [ 0 ] - columnWidth / 2.0f > local . x )
columnCoord = std : : max ( 0 , columnCoord - 1 ) ;
2018-08-17 12:31:33 +00:00
}
2018-10-30 21:52:57 +00:00
return SanitizeCoordinates ( Coordinates ( lineNo , columnCoord ) ) ;
2018-08-17 12:31:33 +00:00
}
TextEditor : : Coordinates TextEditor : : FindWordStart ( const Coordinates & aFrom ) const
{
Coordinates at = aFrom ;
if ( at . mLine > = ( int ) mLines . size ( ) )
return at ;
auto & line = mLines [ at . mLine ] ;
if ( at . mColumn > = ( int ) line . size ( ) )
return at ;
auto cstart = ( PaletteIndex ) line [ at . mColumn ] . mColorIndex ;
while ( at . mColumn > 0 )
{
if ( cstart ! = ( PaletteIndex ) line [ at . mColumn - 1 ] . mColorIndex )
break ;
- - at . mColumn ;
}
return at ;
}
TextEditor : : Coordinates TextEditor : : FindWordEnd ( const Coordinates & aFrom ) const
{
Coordinates at = aFrom ;
if ( at . mLine > = ( int ) mLines . size ( ) )
return at ;
auto & line = mLines [ at . mLine ] ;
if ( at . mColumn > = ( int ) line . size ( ) )
return at ;
auto cstart = ( PaletteIndex ) line [ at . mColumn ] . mColorIndex ;
while ( at . mColumn < ( int ) line . size ( ) )
{
if ( cstart ! = ( PaletteIndex ) line [ at . mColumn ] . mColorIndex )
break ;
+ + at . mColumn ;
}
return at ;
}
bool TextEditor : : IsOnWordBoundary ( const Coordinates & aAt ) const
{
if ( aAt . mLine > = ( int ) mLines . size ( ) | | aAt . mColumn = = 0 )
return true ;
auto & line = mLines [ aAt . mLine ] ;
if ( aAt . mColumn > = ( int ) line . size ( ) )
return true ;
return line [ aAt . mColumn ] . mColorIndex ! = line [ aAt . mColumn - 1 ] . mColorIndex ;
}
void TextEditor : : RemoveLine ( int aStart , int aEnd )
{
assert ( ! mReadOnly ) ;
2018-10-30 21:52:57 +00:00
assert ( aEnd > = aStart ) ;
2019-01-19 13:05:54 +00:00
assert ( mLines . size ( ) > ( size_t ) ( aEnd - aStart ) ) ;
2018-08-17 12:31:33 +00:00
ErrorMarkers etmp ;
for ( auto & i : mErrorMarkers )
{
ErrorMarkers : : value_type e ( i . first > = aStart ? i . first - 1 : i . first , i . second ) ;
if ( e . first > = aStart & & e . first < = aEnd )
continue ;
etmp . insert ( e ) ;
}
mErrorMarkers = std : : move ( etmp ) ;
Breakpoints btmp ;
for ( auto i : mBreakpoints )
{
if ( i > = aStart & & i < = aEnd )
continue ;
btmp . insert ( i > = aStart ? i - 1 : i ) ;
}
mBreakpoints = std : : move ( btmp ) ;
mLines . erase ( mLines . begin ( ) + aStart , mLines . begin ( ) + aEnd ) ;
2018-10-30 21:52:57 +00:00
assert ( ! mLines . empty ( ) ) ;
2018-08-17 12:31:33 +00:00
mTextChanged = true ;
}
void TextEditor : : RemoveLine ( int aIndex )
{
assert ( ! mReadOnly ) ;
2018-10-30 21:52:57 +00:00
assert ( mLines . size ( ) > 1 ) ;
2018-08-17 12:31:33 +00:00
ErrorMarkers etmp ;
for ( auto & i : mErrorMarkers )
{
2018-10-30 21:52:57 +00:00
ErrorMarkers : : value_type e ( i . first > aIndex ? i . first - 1 : i . first , i . second ) ;
if ( e . first - 1 = = aIndex )
2018-08-17 12:31:33 +00:00
continue ;
etmp . insert ( e ) ;
}
mErrorMarkers = std : : move ( etmp ) ;
Breakpoints btmp ;
for ( auto i : mBreakpoints )
{
if ( i = = aIndex )
continue ;
btmp . insert ( i > = aIndex ? i - 1 : i ) ;
}
mBreakpoints = std : : move ( btmp ) ;
mLines . erase ( mLines . begin ( ) + aIndex ) ;
2018-10-30 21:52:57 +00:00
assert ( ! mLines . empty ( ) ) ;
2018-08-17 12:31:33 +00:00
mTextChanged = true ;
}
TextEditor : : Line & TextEditor : : InsertLine ( int aIndex )
{
assert ( ! mReadOnly ) ;
auto & result = * mLines . insert ( mLines . begin ( ) + aIndex , Line ( ) ) ;
ErrorMarkers etmp ;
for ( auto & i : mErrorMarkers )
etmp . insert ( ErrorMarkers : : value_type ( i . first > = aIndex ? i . first + 1 : i . first , i . second ) ) ;
mErrorMarkers = std : : move ( etmp ) ;
Breakpoints btmp ;
for ( auto i : mBreakpoints )
btmp . insert ( i > = aIndex ? i + 1 : i ) ;
mBreakpoints = std : : move ( btmp ) ;
return result ;
}
std : : string TextEditor : : GetWordUnderCursor ( ) const
{
auto c = GetCursorPosition ( ) ;
return GetWordAt ( c ) ;
}
std : : string TextEditor : : GetWordAt ( const Coordinates & aCoords ) const
{
auto start = FindWordStart ( aCoords ) ;
auto end = FindWordEnd ( aCoords ) ;
std : : string r ;
for ( auto it = start ; it < end ; Advance ( it ) )
r . push_back ( mLines [ it . mLine ] [ it . mColumn ] . mChar ) ;
return r ;
}
2019-01-19 13:05:54 +00:00
ImU32 TextEditor : : GetGlyphColor ( const Glyph & aGlyph ) const
2018-08-17 12:31:33 +00:00
{
2019-01-19 13:05:54 +00:00
if ( aGlyph . mComment )
return mPalette [ ( int ) PaletteIndex : : Comment ] ;
if ( aGlyph . mMultiLineComment )
return mPalette [ ( int ) PaletteIndex : : MultiLineComment ] ;
auto const color = mPalette [ ( int ) aGlyph . mColorIndex ] ;
if ( aGlyph . mPreprocessor )
{
const auto ppcolor = mPalette [ ( int ) PaletteIndex : : Preprocessor ] ;
const int c0 = ( ( ppcolor & 0xff ) + ( color & 0xff ) ) / 2 ;
const int c1 = ( ( ( ppcolor > > 8 ) & 0xff ) + ( ( color > > 8 ) & 0xff ) ) / 2 ;
const int c2 = ( ( ( ppcolor > > 16 ) & 0xff ) + ( ( color > > 16 ) & 0xff ) ) / 2 ;
const int c3 = ( ( ( ppcolor > > 24 ) & 0xff ) + ( ( color > > 24 ) & 0xff ) ) / 2 ;
return ImU32 ( c0 | ( c1 < < 8 ) | ( c2 < < 16 ) | ( c3 < < 24 ) ) ;
}
2018-08-17 12:31:33 +00:00
2019-01-19 13:05:54 +00:00
return color ;
}
2018-10-30 21:52:57 +00:00
2019-01-19 13:05:54 +00:00
void TextEditor : : HandleKeyboardInputs ( )
{
2018-10-30 21:52:57 +00:00
ImGuiIO & io = ImGui : : GetIO ( ) ;
2018-08-17 12:31:33 +00:00
auto shift = io . KeyShift ;
2018-10-30 21:52:57 +00:00
auto ctrl = io . ConfigMacOSXBehaviors ? io . KeySuper : io . KeyCtrl ;
auto alt = io . ConfigMacOSXBehaviors ? io . KeyCtrl : io . KeyAlt ;
2018-08-17 12:31:33 +00:00
if ( ImGui : : IsWindowFocused ( ) )
{
2018-10-30 21:52:57 +00:00
if ( ImGui : : IsWindowHovered ( ) )
ImGui : : SetMouseCursor ( ImGuiMouseCursor_TextInput ) ;
2018-08-17 12:31:33 +00:00
//ImGui::CaptureKeyboardFromApp(true);
io . WantCaptureKeyboard = true ;
io . WantTextInput = true ;
2019-01-19 13:05:54 +00:00
if ( ! IsReadOnly ( ) & & ctrl & & ! shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Z ) ) )
2018-10-30 21:52:57 +00:00
Undo ( ) ;
else if ( ! IsReadOnly ( ) & & ! ctrl & & ! shift & & alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Backspace ) ) )
Undo ( ) ;
2019-01-19 13:05:54 +00:00
else if ( ! IsReadOnly ( ) & & ctrl & & ! shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Y ) ) )
2018-08-17 12:31:33 +00:00
Redo ( ) ;
2018-10-30 21:52:57 +00:00
else if ( ! ctrl & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_UpArrow ) ) )
2018-08-17 12:31:33 +00:00
MoveUp ( 1 , shift ) ;
else if ( ! ctrl & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_DownArrow ) ) )
MoveDown ( 1 , shift ) ;
else if ( ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_LeftArrow ) ) )
MoveLeft ( 1 , shift , ctrl ) ;
else if ( ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_RightArrow ) ) )
MoveRight ( 1 , shift , ctrl ) ;
else if ( ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_PageUp ) ) )
MoveUp ( GetPageSize ( ) - 4 , shift ) ;
else if ( ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_PageDown ) ) )
MoveDown ( GetPageSize ( ) - 4 , shift ) ;
else if ( ! alt & & ctrl & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Home ) ) )
MoveTop ( shift ) ;
else if ( ctrl & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_End ) ) )
MoveBottom ( shift ) ;
else if ( ! ctrl & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Home ) ) )
MoveHome ( shift ) ;
else if ( ! ctrl & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_End ) ) )
MoveEnd ( shift ) ;
else if ( ! IsReadOnly ( ) & & ! ctrl & & ! shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Delete ) ) )
Delete ( ) ;
else if ( ! IsReadOnly ( ) & & ! ctrl & & ! shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Backspace ) ) )
BackSpace ( ) ;
2019-01-19 13:05:54 +00:00
else if ( ! ctrl & & ! shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Insert ) ) )
2018-08-17 12:31:33 +00:00
mOverwrite ^ = true ;
2019-01-19 13:05:54 +00:00
else if ( ctrl & & ! shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Insert ) ) )
2018-08-17 12:31:33 +00:00
Copy ( ) ;
2019-01-19 13:05:54 +00:00
else if ( ctrl & & ! shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_C ) ) )
2018-08-17 12:31:33 +00:00
Copy ( ) ;
2019-01-19 13:05:54 +00:00
else if ( ! IsReadOnly ( ) & & ! ctrl & & shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Insert ) ) )
2018-08-17 12:31:33 +00:00
Paste ( ) ;
2019-01-19 13:05:54 +00:00
else if ( ! IsReadOnly ( ) & & ctrl & & ! shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_V ) ) )
2018-08-17 12:31:33 +00:00
Paste ( ) ;
2019-01-19 13:05:54 +00:00
else if ( ctrl & & ! shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_X ) ) )
2018-08-17 12:31:33 +00:00
Cut ( ) ;
else if ( ! ctrl & & shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Delete ) ) )
Cut ( ) ;
else if ( ctrl & & ! shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_A ) ) )
SelectAll ( ) ;
2018-10-30 21:52:57 +00:00
else if ( ! IsReadOnly ( ) & & ! ctrl & & ! shift & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Enter ) ) )
EnterCharacter ( ' \n ' , false ) ;
2019-01-19 13:05:54 +00:00
else if ( ! IsReadOnly ( ) & & ! ctrl & & ! alt & & ImGui : : IsKeyPressed ( ImGui : : GetKeyIndex ( ImGuiKey_Tab ) ) )
EnterCharacter ( ' \t ' , shift ) ;
2018-10-30 21:52:57 +00:00
else if ( ! IsReadOnly ( ) & & ! ctrl & & ! alt )
2018-08-17 12:31:33 +00:00
{
2019-01-19 13:05:54 +00:00
for ( int i = 0 ; i < io . InputQueueCharacters . Size ; i + + )
2018-08-17 12:31:33 +00:00
{
2019-01-19 13:05:54 +00:00
auto c = ( unsigned char ) io . InputQueueCharacters [ i ] ;
2018-08-17 12:31:33 +00:00
if ( c ! = 0 )
{
if ( isprint ( c ) | | isspace ( c ) )
{
2018-10-30 21:52:57 +00:00
EnterCharacter ( ( char ) c , shift ) ;
2018-08-17 12:31:33 +00:00
}
}
}
2019-01-19 13:05:54 +00:00
io . InputQueueCharacters . resize ( 0 ) ;
2018-08-17 12:31:33 +00:00
}
}
2019-01-19 13:05:54 +00:00
}
2018-08-17 12:31:33 +00:00
2019-01-19 13:05:54 +00:00
void TextEditor : : HandleMouseInputs ( )
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
auto shift = io . KeyShift ;
auto ctrl = io . ConfigMacOSXBehaviors ? io . KeySuper : io . KeyCtrl ;
auto alt = io . ConfigMacOSXBehaviors ? io . KeyCtrl : io . KeyAlt ;
2018-10-30 21:52:57 +00:00
2018-08-17 12:31:33 +00:00
if ( ImGui : : IsWindowHovered ( ) )
{
if ( ! shift & & ! alt )
{
2019-01-19 13:05:54 +00:00
auto click = ImGui : : IsMouseClicked ( 0 ) ;
2018-08-17 12:31:33 +00:00
auto doubleClick = ImGui : : IsMouseDoubleClicked ( 0 ) ;
2019-01-19 13:05:54 +00:00
auto t = ImGui : : GetTime ( ) ;
auto tripleClick = click & & ! doubleClick & & ( mLastClick ! = - 1.0f & & ( t - mLastClick ) < io . MouseDoubleClickTime ) ;
2018-10-30 21:52:57 +00:00
/*
Left mouse button triple click
*/
2018-08-17 12:31:33 +00:00
if ( tripleClick )
{
if ( ! ctrl )
{
mState . mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates ( ScreenPosToCoordinates ( ImGui : : GetMousePos ( ) ) ) ;
mSelectionMode = SelectionMode : : Line ;
SetSelection ( mInteractiveStart , mInteractiveEnd , mSelectionMode ) ;
}
2019-01-19 13:05:54 +00:00
mLastClick = - 1.0f ;
2018-08-17 12:31:33 +00:00
}
2018-10-30 21:52:57 +00:00
/*
Left mouse button double click
*/
2018-08-17 12:31:33 +00:00
else if ( doubleClick )
{
if ( ! ctrl )
{
mState . mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates ( ScreenPosToCoordinates ( ImGui : : GetMousePos ( ) ) ) ;
if ( mSelectionMode = = SelectionMode : : Line )
mSelectionMode = SelectionMode : : Normal ;
else
mSelectionMode = SelectionMode : : Word ;
SetSelection ( mInteractiveStart , mInteractiveEnd , mSelectionMode ) ;
}
2019-01-19 13:05:54 +00:00
mLastClick = ( float ) ImGui : : GetTime ( ) ;
2018-08-17 12:31:33 +00:00
}
2018-10-30 21:52:57 +00:00
/*
Left mouse button click
*/
2018-08-17 12:31:33 +00:00
else if ( click )
{
mState . mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates ( ScreenPosToCoordinates ( ImGui : : GetMousePos ( ) ) ) ;
if ( ctrl )
mSelectionMode = SelectionMode : : Word ;
else
mSelectionMode = SelectionMode : : Normal ;
SetSelection ( mInteractiveStart , mInteractiveEnd , mSelectionMode ) ;
2019-01-19 13:05:54 +00:00
mLastClick = ( float ) ImGui : : GetTime ( ) ;
2018-08-17 12:31:33 +00:00
}
2018-10-30 21:52:57 +00:00
// Mouse left button dragging (=> update selection)
2018-08-17 12:31:33 +00:00
else if ( ImGui : : IsMouseDragging ( 0 ) & & ImGui : : IsMouseDown ( 0 ) )
{
io . WantCaptureMouse = true ;
mState . mCursorPosition = mInteractiveEnd = SanitizeCoordinates ( ScreenPosToCoordinates ( ImGui : : GetMousePos ( ) ) ) ;
SetSelection ( mInteractiveStart , mInteractiveEnd , mSelectionMode ) ;
}
}
}
2019-01-19 13:05:54 +00:00
}
2018-08-17 12:31:33 +00:00
2019-01-19 13:05:54 +00:00
void TextEditor : : Render ( )
{
/* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/
2019-03-15 00:06:27 +00:00
const float fontSize = ImGui : : GetFont ( ) - > CalcTextSizeA ( ImGui : : GetFontSize ( ) , FLT_MAX , - 1.0f , " # " , nullptr , nullptr ) . x ;
2019-01-19 13:05:54 +00:00
mCharAdvance = ImVec2 ( fontSize , ImGui : : GetTextLineHeightWithSpacing ( ) * mLineSpacing ) ;
2018-08-17 12:31:33 +00:00
2019-01-19 13:05:54 +00:00
/* Update palette with the current alpha from style */
for ( int i = 0 ; i < ( int ) PaletteIndex : : Max ; + + i )
{
auto color = ImGui : : ColorConvertU32ToFloat4 ( mPaletteBase [ i ] ) ;
color . w * = ImGui : : GetStyle ( ) . Alpha ;
mPalette [ i ] = ImGui : : ColorConvertFloat4ToU32 ( color ) ;
}
2018-08-17 12:31:33 +00:00
static std : : string buffer ;
2019-01-19 13:05:54 +00:00
assert ( buffer . empty ( ) ) ;
2018-08-17 12:31:33 +00:00
auto contentSize = ImGui : : GetWindowContentRegionMax ( ) ;
auto drawList = ImGui : : GetWindowDrawList ( ) ;
2018-10-30 21:52:57 +00:00
float longest ( mTextStart ) ;
2019-01-19 13:05:54 +00:00
if ( mScrollToTop )
{
mScrollToTop = false ;
ImGui : : SetScrollY ( 0.f ) ;
}
2018-08-17 12:31:33 +00:00
ImVec2 cursorScreenPos = ImGui : : GetCursorScreenPos ( ) ;
auto scrollX = ImGui : : GetScrollX ( ) ;
auto scrollY = ImGui : : GetScrollY ( ) ;
auto lineNo = ( int ) floor ( scrollY / mCharAdvance . y ) ;
2018-10-30 21:52:57 +00:00
auto globalLineMax = ( int ) mLines . size ( ) ;
2018-08-17 12:31:33 +00:00
auto lineMax = std : : max ( 0 , std : : min ( ( int ) mLines . size ( ) - 1 , lineNo + ( int ) floor ( ( scrollY + contentSize . y ) / mCharAdvance . y ) ) ) ;
2018-10-30 21:52:57 +00:00
// Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width
char buf [ 16 ] ;
snprintf ( buf , 16 , " %d " , globalLineMax ) ;
2019-03-15 00:06:27 +00:00
mTextStart = ImGui : : GetFont ( ) - > CalcTextSizeA ( ImGui : : GetFontSize ( ) , FLT_MAX , - 1.0f , buf , nullptr , nullptr ) . x + mLeftMargin ;
2018-10-30 21:52:57 +00:00
2018-08-17 12:31:33 +00:00
if ( ! mLines . empty ( ) )
{
2018-10-30 21:52:57 +00:00
auto fontScale = ImGui : : GetFontSize ( ) / ImGui : : GetFont ( ) - > FontSize ;
2019-03-15 00:06:27 +00:00
float spaceSize = ImGui : : GetFont ( ) - > CalcTextSizeA ( ImGui : : GetFontSize ( ) , FLT_MAX , - 1.0f , " " , nullptr , nullptr ) . x ;
2018-10-30 21:52:57 +00:00
2018-08-17 12:31:33 +00:00
while ( lineNo < = lineMax )
{
ImVec2 lineStartScreenPos = ImVec2 ( cursorScreenPos . x , cursorScreenPos . y + lineNo * mCharAdvance . y ) ;
2018-10-30 21:52:57 +00:00
ImVec2 textScreenPos = ImVec2 ( lineStartScreenPos . x + mTextStart , lineStartScreenPos . y ) ;
2018-08-17 12:31:33 +00:00
auto & line = mLines [ lineNo ] ;
2019-01-19 13:05:54 +00:00
longest = std : : max ( mTextStart + TextDistanceToLineStart ( Coordinates ( lineNo , ( int ) line . size ( ) ) ) , longest ) ;
2018-08-17 12:31:33 +00:00
auto columnNo = 0 ;
Coordinates lineStartCoord ( lineNo , 0 ) ;
Coordinates lineEndCoord ( lineNo , ( int ) line . size ( ) ) ;
2019-01-19 13:05:54 +00:00
// Draw selection for the current line
2018-10-30 21:52:57 +00:00
float sstart = - 1.0f ;
float ssend = - 1.0f ;
2018-08-17 12:31:33 +00:00
assert ( mState . mSelectionStart < = mState . mSelectionEnd ) ;
if ( mState . mSelectionStart < = lineEndCoord )
2018-10-30 21:52:57 +00:00
sstart = mState . mSelectionStart > lineStartCoord ? TextDistanceToLineStart ( mState . mSelectionStart ) : 0.0f ;
2018-08-17 12:31:33 +00:00
if ( mState . mSelectionEnd > lineStartCoord )
ssend = TextDistanceToLineStart ( mState . mSelectionEnd < lineEndCoord ? mState . mSelectionEnd : lineEndCoord ) ;
if ( mState . mSelectionEnd . mLine > lineNo )
2018-10-30 21:52:57 +00:00
ssend + = mCharAdvance . x ;
2018-08-17 12:31:33 +00:00
if ( sstart ! = - 1 & & ssend ! = - 1 & & sstart < ssend )
{
2018-10-30 21:52:57 +00:00
ImVec2 vstart ( lineStartScreenPos . x + mTextStart + sstart , lineStartScreenPos . y ) ;
ImVec2 vend ( lineStartScreenPos . x + mTextStart + ssend , lineStartScreenPos . y + mCharAdvance . y ) ;
2018-08-17 12:31:33 +00:00
drawList - > AddRectFilled ( vstart , vend , mPalette [ ( int ) PaletteIndex : : Selection ] ) ;
}
2019-01-19 13:05:54 +00:00
// Draw breakpoints
2018-08-17 12:31:33 +00:00
auto start = ImVec2 ( lineStartScreenPos . x + scrollX , lineStartScreenPos . y ) ;
2018-10-30 21:52:57 +00:00
if ( mBreakpoints . count ( lineNo + 1 ) ! = 0 )
2018-08-17 12:31:33 +00:00
{
auto end = ImVec2 ( lineStartScreenPos . x + contentSize . x + 2.0f * scrollX , lineStartScreenPos . y + mCharAdvance . y ) ;
drawList - > AddRectFilled ( start , end , mPalette [ ( int ) PaletteIndex : : Breakpoint ] ) ;
}
2019-01-19 13:05:54 +00:00
// Draw error markers
2018-08-17 12:31:33 +00:00
auto errorIt = mErrorMarkers . find ( lineNo + 1 ) ;
if ( errorIt ! = mErrorMarkers . end ( ) )
{
auto end = ImVec2 ( lineStartScreenPos . x + contentSize . x + 2.0f * scrollX , lineStartScreenPos . y + mCharAdvance . y ) ;
drawList - > AddRectFilled ( start , end , mPalette [ ( int ) PaletteIndex : : ErrorMarker ] ) ;
if ( ImGui : : IsMouseHoveringRect ( lineStartScreenPos , end ) )
{
ImGui : : BeginTooltip ( ) ;
ImGui : : PushStyleColor ( ImGuiCol_Text , ImVec4 ( 1.0f , 0.2f , 0.2f , 1.0f ) ) ;
ImGui : : Text ( " Error at line %d: " , errorIt - > first ) ;
ImGui : : PopStyleColor ( ) ;
ImGui : : Separator ( ) ;
ImGui : : PushStyleColor ( ImGuiCol_Text , ImVec4 ( 1.0f , 1.0f , 0.2f , 1.0f ) ) ;
ImGui : : Text ( " %s " , errorIt - > second . c_str ( ) ) ;
ImGui : : PopStyleColor ( ) ;
ImGui : : EndTooltip ( ) ;
}
}
2019-01-19 13:05:54 +00:00
// Draw line number (right aligned)
2018-10-30 21:52:57 +00:00
snprintf ( buf , 16 , " %d " , lineNo + 1 ) ;
2019-03-15 00:06:27 +00:00
auto lineNoWidth = ImGui : : GetFont ( ) - > CalcTextSizeA ( ImGui : : GetFontSize ( ) , FLT_MAX , - 1.0f , buf , nullptr , nullptr ) . x ;
2018-10-30 21:52:57 +00:00
drawList - > AddText ( ImVec2 ( lineStartScreenPos . x + mTextStart - lineNoWidth , lineStartScreenPos . y ) , mPalette [ ( int ) PaletteIndex : : LineNumber ] , buf ) ;
2019-01-19 13:05:54 +00:00
// Highlight the current line (where the cursor is)
2018-08-17 12:31:33 +00:00
if ( mState . mCursorPosition . mLine = = lineNo )
{
auto focused = ImGui : : IsWindowFocused ( ) ;
if ( ! HasSelection ( ) )
{
auto end = ImVec2 ( start . x + contentSize . x + scrollX , start . y + mCharAdvance . y ) ;
drawList - > AddRectFilled ( start , end , mPalette [ ( int ) ( focused ? PaletteIndex : : CurrentLineFill : PaletteIndex : : CurrentLineFillInactive ) ] ) ;
drawList - > AddRect ( start , end , mPalette [ ( int ) PaletteIndex : : CurrentLineEdge ] , 1.0f ) ;
}
2018-10-30 21:52:57 +00:00
float cx = TextDistanceToLineStart ( mState . mCursorPosition ) ;
2018-08-17 12:31:33 +00:00
if ( focused )
{
static auto timeStart = std : : chrono : : system_clock : : now ( ) ;
auto timeEnd = std : : chrono : : system_clock : : now ( ) ;
auto diff = timeEnd - timeStart ;
auto elapsed = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( diff ) . count ( ) ;
if ( elapsed > 400 )
{
2018-10-30 21:52:57 +00:00
ImVec2 cstart ( textScreenPos . x + cx , lineStartScreenPos . y ) ;
ImVec2 cend ( textScreenPos . x + cx + ( mOverwrite ? mCharAdvance . x : 1.0f ) , lineStartScreenPos . y + mCharAdvance . y ) ;
2018-08-17 12:31:33 +00:00
drawList - > AddRectFilled ( cstart , cend , mPalette [ ( int ) PaletteIndex : : Cursor ] ) ;
if ( elapsed > 800 )
timeStart = timeEnd ;
}
}
}
2019-01-19 13:05:54 +00:00
// Render colorized text
auto prevColor = line . empty ( ) ? mPalette [ ( int ) PaletteIndex : : Default ] : GetGlyphColor ( line [ 0 ] ) ;
ImVec2 bufferOffset ;
2018-08-17 12:31:33 +00:00
for ( auto & glyph : line )
{
2019-01-19 13:05:54 +00:00
auto color = GetGlyphColor ( glyph ) ;
2018-08-17 12:31:33 +00:00
2018-10-30 21:52:57 +00:00
if ( ( color ! = prevColor | | glyph . mChar = = ' \t ' ) & & ! buffer . empty ( ) )
2018-08-17 12:31:33 +00:00
{
2018-10-30 21:52:57 +00:00
const ImVec2 newOffset ( textScreenPos . x + bufferOffset . x , textScreenPos . y + bufferOffset . y ) ;
2019-01-19 13:05:54 +00:00
drawList - > AddText ( newOffset , prevColor , buffer . c_str ( ) ) ;
2019-03-15 00:06:27 +00:00
auto textSize = ImGui : : GetFont ( ) - > CalcTextSizeA ( ImGui : : GetFontSize ( ) , FLT_MAX , - 1.0f , buffer . c_str ( ) , nullptr , nullptr ) ;
bufferOffset . x + = textSize . x ;
2018-08-17 12:31:33 +00:00
buffer . clear ( ) ;
}
2018-10-30 21:52:57 +00:00
prevColor = color ;
if ( glyph . mChar = = ' \t ' )
bufferOffset . x = ( 1.0f * fontScale + std : : floor ( ( 1.0f + bufferOffset . x ) ) / ( float ( mTabSize ) * spaceSize ) ) * ( float ( mTabSize ) * spaceSize ) ;
else
AppendBuffer ( buffer , glyph . mChar , 0 ) ;
2018-08-17 12:31:33 +00:00
+ + columnNo ;
}
if ( ! buffer . empty ( ) )
{
2018-10-30 21:52:57 +00:00
const ImVec2 newOffset ( textScreenPos . x + bufferOffset . x , textScreenPos . y + bufferOffset . y ) ;
2019-01-19 13:05:54 +00:00
drawList - > AddText ( newOffset , prevColor , buffer . c_str ( ) ) ;
2018-08-17 12:31:33 +00:00
buffer . clear ( ) ;
}
2018-10-30 21:52:57 +00:00
2018-08-17 12:31:33 +00:00
+ + lineNo ;
}
2019-01-19 13:05:54 +00:00
// Draw a tooltip on known identifiers/preprocessor symbols
2018-10-30 21:52:57 +00:00
if ( ImGui : : IsMousePosValid ( ) )
2018-08-17 12:31:33 +00:00
{
2018-10-30 21:52:57 +00:00
auto id = GetWordAt ( ScreenPosToCoordinates ( ImGui : : GetMousePos ( ) ) ) ;
if ( ! id . empty ( ) )
2018-08-17 12:31:33 +00:00
{
2018-10-30 21:52:57 +00:00
auto it = mLanguageDefinition . mIdentifiers . find ( id ) ;
if ( it ! = mLanguageDefinition . mIdentifiers . end ( ) )
2018-08-17 12:31:33 +00:00
{
ImGui : : BeginTooltip ( ) ;
2018-10-30 21:52:57 +00:00
ImGui : : TextUnformatted ( it - > second . mDeclaration . c_str ( ) ) ;
2018-08-17 12:31:33 +00:00
ImGui : : EndTooltip ( ) ;
}
2018-10-30 21:52:57 +00:00
else
{
auto pi = mLanguageDefinition . mPreprocIdentifiers . find ( id ) ;
if ( pi ! = mLanguageDefinition . mPreprocIdentifiers . end ( ) )
{
ImGui : : BeginTooltip ( ) ;
ImGui : : TextUnformatted ( pi - > second . mDeclaration . c_str ( ) ) ;
ImGui : : EndTooltip ( ) ;
}
}
2018-08-17 12:31:33 +00:00
}
}
}
2018-10-30 21:52:57 +00:00
ImGui : : Dummy ( ImVec2 ( ( longest + 2 ) , mLines . size ( ) * mCharAdvance . y ) ) ;
2018-08-17 12:31:33 +00:00
if ( mScrollToCursor )
{
EnsureCursorVisible ( ) ;
ImGui : : SetWindowFocus ( ) ;
mScrollToCursor = false ;
}
2019-01-19 13:05:54 +00:00
}
void TextEditor : : Render ( const char * aTitle , const ImVec2 & aSize , bool aBorder )
{
mWithinRender = true ;
mTextChanged = false ;
mCursorPositionChanged = false ;
ImGui : : PushStyleColor ( ImGuiCol_ChildWindowBg , ImGui : : ColorConvertU32ToFloat4 ( mPalette [ ( int ) PaletteIndex : : Background ] ) ) ;
ImGui : : PushStyleVar ( ImGuiStyleVar_ItemSpacing , ImVec2 ( 0.0f , 0.0f ) ) ;
ImGui : : BeginChild ( aTitle , aSize , aBorder , ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove ) ;
ImGui : : PushAllowKeyboardFocus ( true ) ;
HandleKeyboardInputs ( ) ;
HandleMouseInputs ( ) ;
ColorizeInternal ( ) ;
Render ( ) ;
2018-08-17 12:31:33 +00:00
ImGui : : PopAllowKeyboardFocus ( ) ;
ImGui : : EndChild ( ) ;
ImGui : : PopStyleVar ( ) ;
ImGui : : PopStyleColor ( ) ;
mWithinRender = false ;
}
2018-10-30 21:52:57 +00:00
void TextEditor : : SetText ( const std : : string & aText )
2018-08-17 12:31:33 +00:00
{
mLines . clear ( ) ;
2019-01-19 13:05:54 +00:00
mLines . emplace_back ( Line ( ) ) ;
2018-10-30 21:52:57 +00:00
for ( auto chr : aText )
2018-08-17 12:31:33 +00:00
{
2018-10-30 21:52:57 +00:00
if ( chr = = ' \r ' )
{
// ignore the carriage return character
}
else if ( chr = = ' \n ' )
2019-01-19 13:05:54 +00:00
mLines . emplace_back ( Line ( ) ) ;
2018-08-17 12:31:33 +00:00
else
{
2019-01-19 13:05:54 +00:00
mLines . back ( ) . emplace_back ( Glyph ( chr , PaletteIndex : : Default ) ) ;
2018-08-17 12:31:33 +00:00
}
2019-01-19 13:05:54 +00:00
}
mTextChanged = true ;
mScrollToTop = true ;
2018-08-17 12:31:33 +00:00
2019-01-19 13:05:54 +00:00
mUndoBuffer . clear ( ) ;
mUndoIndex = 0 ;
Colorize ( ) ;
}
void TextEditor : : SetTextLines ( const std : : vector < std : : string > & aLines )
{
mLines . clear ( ) ;
if ( aLines . empty ( ) )
{
mLines . emplace_back ( Line ( ) ) ;
}
else
{
mLines . resize ( aLines . size ( ) ) ;
for ( size_t i = 0 ; i < aLines . size ( ) ; + + i )
{
const std : : string & aLine = aLines [ i ] ;
mLines [ i ] . reserve ( aLine . size ( ) ) ;
for ( size_t j = 0 ; j < aLine . size ( ) ; + + j )
mLines [ i ] . emplace_back ( Glyph ( aLine [ j ] , PaletteIndex : : Default ) ) ;
}
2018-08-17 12:31:33 +00:00
}
2019-01-19 13:05:54 +00:00
mTextChanged = true ;
mScrollToTop = true ;
2018-08-17 12:31:33 +00:00
mUndoBuffer . clear ( ) ;
2019-01-19 13:05:54 +00:00
mUndoIndex = 0 ;
2018-08-17 12:31:33 +00:00
Colorize ( ) ;
}
2018-10-30 21:52:57 +00:00
void TextEditor : : EnterCharacter ( Char aChar , bool aShift )
2018-08-17 12:31:33 +00:00
{
assert ( ! mReadOnly ) ;
UndoRecord u ;
u . mBefore = mState ;
if ( HasSelection ( ) )
{
2018-10-30 21:52:57 +00:00
if ( aChar = = ' \t ' )
{
auto start = mState . mSelectionStart ;
auto end = mState . mSelectionEnd ;
if ( start > end )
std : : swap ( start , end ) ;
start . mColumn = 0 ;
2019-01-19 13:05:54 +00:00
// end.mColumn = end.mLine < mLines.size() ? mLines[end.mLine].size() : 0;
2018-10-30 21:52:57 +00:00
if ( end . mColumn = = 0 & & end . mLine > 0 )
{
- - end . mLine ;
2019-01-19 13:05:54 +00:00
end . mColumn = ( int ) mLines [ end . mLine ] . size ( ) ;
2018-10-30 21:52:57 +00:00
}
2019-05-09 16:56:13 +00:00
if ( end . mColumn > = ( int ) mLines [ end . mLine ] . size ( ) )
end . mColumn = ( int ) mLines [ end . mLine ] . size ( ) - 1 ;
2018-10-30 21:52:57 +00:00
u . mRemovedStart = start ;
u . mRemovedEnd = end ;
u . mRemoved = GetText ( start , end ) ;
bool modified = false ;
for ( int i = start . mLine ; i < = end . mLine ; i + + )
{
auto & line = mLines [ i ] ;
if ( aShift )
{
2019-01-19 13:05:54 +00:00
if ( line . empty ( ) = = false )
2018-10-30 21:52:57 +00:00
{
2019-01-19 13:05:54 +00:00
if ( line . front ( ) . mChar = = ' \t ' )
{
line . erase ( line . begin ( ) ) ;
if ( i = = end . mLine & & end . mColumn > 0 )
end . mColumn - - ;
modified = true ;
}
2018-10-30 21:52:57 +00:00
}
2019-01-19 13:05:54 +00:00
else
2018-10-30 21:52:57 +00:00
{
2019-01-19 13:05:54 +00:00
for ( int j = 0 ; j < mTabSize & & line . empty ( ) = = false & & line . front ( ) . mChar = = ' ' ; j + + )
{
line . erase ( line . begin ( ) ) ;
if ( i = = end . mLine & & end . mColumn > 0 )
end . mColumn - - ;
modified = true ;
}
2018-10-30 21:52:57 +00:00
}
}
else
{
line . insert ( line . begin ( ) , Glyph ( ' \t ' , TextEditor : : PaletteIndex : : Background ) ) ;
if ( i = = end . mLine )
+ + end . mColumn ;
modified = true ;
}
}
if ( modified )
{
2019-05-09 16:56:13 +00:00
assert ( mLines . size ( ) > start . mLine & & mLines [ start . mLine ] . size ( ) > start . mColumn ) ;
assert ( mLines . size ( ) > end . mLine & & mLines [ end . mLine ] . size ( ) > end . mColumn ) ;
2018-10-30 21:52:57 +00:00
u . mAddedStart = start ;
u . mAddedEnd = end ;
u . mAdded = GetText ( start , end ) ;
mTextChanged = true ;
AddUndo ( u ) ;
EnsureCursorVisible ( ) ;
}
return ;
}
else
{
u . mRemoved = GetSelectedText ( ) ;
u . mRemovedStart = mState . mSelectionStart ;
u . mRemovedEnd = mState . mSelectionEnd ;
DeleteSelection ( ) ;
}
2018-08-17 12:31:33 +00:00
}
auto coord = GetActualCursorCoordinates ( ) ;
u . mAddedStart = coord ;
2018-10-30 21:52:57 +00:00
assert ( ! mLines . empty ( ) ) ;
2018-08-17 12:31:33 +00:00
if ( aChar = = ' \n ' )
{
InsertLine ( coord . mLine + 1 ) ;
auto & line = mLines [ coord . mLine ] ;
auto & newLine = mLines [ coord . mLine + 1 ] ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( mLanguageDefinition . mAutoIndentation )
{
for ( size_t it = 0 ; it < line . size ( ) & & isblank ( line [ it ] . mChar ) ; + + it )
newLine . push_back ( line [ it ] ) ;
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
const size_t whitespaceSize = newLine . size ( ) ;
newLine . insert ( newLine . end ( ) , line . begin ( ) + coord . mColumn , line . end ( ) ) ;
2018-08-17 12:31:33 +00:00
line . erase ( line . begin ( ) + coord . mColumn , line . begin ( ) + line . size ( ) ) ;
2018-10-30 21:52:57 +00:00
SetCursorPosition ( Coordinates ( coord . mLine + 1 , ( int ) whitespaceSize ) ) ;
2018-08-17 12:31:33 +00:00
}
else
{
auto & line = mLines [ coord . mLine ] ;
if ( mOverwrite & & ( int ) line . size ( ) > coord . mColumn )
line [ coord . mColumn ] = Glyph ( aChar , PaletteIndex : : Default ) ;
else
line . insert ( line . begin ( ) + coord . mColumn , Glyph ( aChar , PaletteIndex : : Default ) ) ;
2018-10-30 21:52:57 +00:00
SetCursorPosition ( Coordinates ( coord . mLine , coord . mColumn + 1 ) ) ;
2018-08-17 12:31:33 +00:00
}
mTextChanged = true ;
u . mAdded = aChar ;
u . mAddedEnd = GetActualCursorCoordinates ( ) ;
u . mAfter = mState ;
AddUndo ( u ) ;
Colorize ( coord . mLine - 1 , 3 ) ;
EnsureCursorVisible ( ) ;
}
void TextEditor : : SetReadOnly ( bool aValue )
{
mReadOnly = aValue ;
}
void TextEditor : : SetCursorPosition ( const Coordinates & aPosition )
{
2018-10-30 21:52:57 +00:00
if ( mState . mCursorPosition ! = aPosition )
2018-08-17 12:31:33 +00:00
{
mState . mCursorPosition = aPosition ;
2018-10-30 21:52:57 +00:00
mCursorPositionChanged = true ;
2018-08-17 12:31:33 +00:00
EnsureCursorVisible ( ) ;
}
}
void TextEditor : : SetSelectionStart ( const Coordinates & aPosition )
{
mState . mSelectionStart = SanitizeCoordinates ( aPosition ) ;
if ( mState . mSelectionStart > mState . mSelectionEnd )
std : : swap ( mState . mSelectionStart , mState . mSelectionEnd ) ;
}
void TextEditor : : SetSelectionEnd ( const Coordinates & aPosition )
{
mState . mSelectionEnd = SanitizeCoordinates ( aPosition ) ;
if ( mState . mSelectionStart > mState . mSelectionEnd )
std : : swap ( mState . mSelectionStart , mState . mSelectionEnd ) ;
}
void TextEditor : : SetSelection ( const Coordinates & aStart , const Coordinates & aEnd , SelectionMode aMode )
{
2018-10-30 21:52:57 +00:00
auto oldSelStart = mState . mSelectionStart ;
2019-01-19 13:05:54 +00:00
auto oldSelEnd = mState . mSelectionEnd ;
2018-10-30 21:52:57 +00:00
2018-08-17 12:31:33 +00:00
mState . mSelectionStart = SanitizeCoordinates ( aStart ) ;
mState . mSelectionEnd = SanitizeCoordinates ( aEnd ) ;
if ( aStart > aEnd )
std : : swap ( mState . mSelectionStart , mState . mSelectionEnd ) ;
switch ( aMode )
{
case TextEditor : : SelectionMode : : Normal :
break ;
case TextEditor : : SelectionMode : : Word :
{
mState . mSelectionStart = FindWordStart ( mState . mSelectionStart ) ;
if ( ! IsOnWordBoundary ( mState . mSelectionEnd ) )
mState . mSelectionEnd = FindWordEnd ( FindWordStart ( mState . mSelectionEnd ) ) ;
break ;
}
case TextEditor : : SelectionMode : : Line :
{
const auto lineNo = mState . mSelectionEnd . mLine ;
2019-01-19 13:05:54 +00:00
const auto lineSize = ( size_t ) lineNo < mLines . size ( ) ? mLines [ lineNo ] . size ( ) : 0 ;
2018-08-17 12:31:33 +00:00
mState . mSelectionStart = Coordinates ( mState . mSelectionStart . mLine , 0 ) ;
2019-01-19 13:05:54 +00:00
mState . mSelectionEnd = Coordinates ( lineNo , ( int ) lineSize ) ;
2018-08-17 12:31:33 +00:00
break ;
}
default :
break ;
}
2018-10-30 21:52:57 +00:00
if ( mState . mSelectionStart ! = oldSelStart | |
mState . mSelectionEnd ! = oldSelEnd )
mCursorPositionChanged = true ;
2018-08-17 12:31:33 +00:00
}
void TextEditor : : InsertText ( const std : : string & aValue )
{
InsertText ( aValue . c_str ( ) ) ;
}
void TextEditor : : InsertText ( const char * aValue )
{
if ( aValue = = nullptr )
return ;
auto pos = GetActualCursorCoordinates ( ) ;
auto start = std : : min ( pos , mState . mSelectionStart ) ;
int totalLines = pos . mLine - start . mLine ;
totalLines + = InsertTextAt ( pos , aValue ) ;
SetSelection ( pos , pos ) ;
SetCursorPosition ( pos ) ;
Colorize ( start . mLine - 1 , totalLines + 2 ) ;
}
void TextEditor : : DeleteSelection ( )
{
assert ( mState . mSelectionEnd > = mState . mSelectionStart ) ;
if ( mState . mSelectionEnd = = mState . mSelectionStart )
return ;
DeleteRange ( mState . mSelectionStart , mState . mSelectionEnd ) ;
SetSelection ( mState . mSelectionStart , mState . mSelectionStart ) ;
SetCursorPosition ( mState . mSelectionStart ) ;
Colorize ( mState . mSelectionStart . mLine , 1 ) ;
}
void TextEditor : : MoveUp ( int aAmount , bool aSelect )
{
auto oldPos = mState . mCursorPosition ;
mState . mCursorPosition . mLine = std : : max ( 0 , mState . mCursorPosition . mLine - aAmount ) ;
if ( oldPos ! = mState . mCursorPosition )
{
if ( aSelect )
{
if ( oldPos = = mInteractiveStart )
mInteractiveStart = mState . mCursorPosition ;
else if ( oldPos = = mInteractiveEnd )
mInteractiveEnd = mState . mCursorPosition ;
else
{
mInteractiveStart = mState . mCursorPosition ;
mInteractiveEnd = oldPos ;
}
}
else
mInteractiveStart = mInteractiveEnd = mState . mCursorPosition ;
SetSelection ( mInteractiveStart , mInteractiveEnd ) ;
EnsureCursorVisible ( ) ;
}
}
void TextEditor : : MoveDown ( int aAmount , bool aSelect )
{
assert ( mState . mCursorPosition . mColumn > = 0 ) ;
auto oldPos = mState . mCursorPosition ;
mState . mCursorPosition . mLine = std : : max ( 0 , std : : min ( ( int ) mLines . size ( ) - 1 , mState . mCursorPosition . mLine + aAmount ) ) ;
if ( mState . mCursorPosition ! = oldPos )
{
if ( aSelect )
{
if ( oldPos = = mInteractiveEnd )
mInteractiveEnd = mState . mCursorPosition ;
else if ( oldPos = = mInteractiveStart )
mInteractiveStart = mState . mCursorPosition ;
else
{
mInteractiveStart = oldPos ;
mInteractiveEnd = mState . mCursorPosition ;
}
}
else
mInteractiveStart = mInteractiveEnd = mState . mCursorPosition ;
SetSelection ( mInteractiveStart , mInteractiveEnd ) ;
EnsureCursorVisible ( ) ;
}
}
void TextEditor : : MoveLeft ( int aAmount , bool aSelect , bool aWordMode )
{
if ( mLines . empty ( ) )
return ;
auto oldPos = mState . mCursorPosition ;
mState . mCursorPosition = GetActualCursorCoordinates ( ) ;
while ( aAmount - - > 0 )
{
if ( mState . mCursorPosition . mColumn = = 0 )
{
if ( mState . mCursorPosition . mLine > 0 )
{
- - mState . mCursorPosition . mLine ;
mState . mCursorPosition . mColumn = ( int ) mLines [ mState . mCursorPosition . mLine ] . size ( ) ;
}
}
else
{
mState . mCursorPosition . mColumn = std : : max ( 0 , mState . mCursorPosition . mColumn - 1 ) ;
if ( aWordMode )
mState . mCursorPosition = FindWordStart ( mState . mCursorPosition ) ;
}
}
assert ( mState . mCursorPosition . mColumn > = 0 ) ;
if ( aSelect )
{
if ( oldPos = = mInteractiveStart )
mInteractiveStart = mState . mCursorPosition ;
else if ( oldPos = = mInteractiveEnd )
mInteractiveEnd = mState . mCursorPosition ;
else
{
mInteractiveStart = mState . mCursorPosition ;
mInteractiveEnd = oldPos ;
}
}
else
mInteractiveStart = mInteractiveEnd = mState . mCursorPosition ;
SetSelection ( mInteractiveStart , mInteractiveEnd , aSelect & & aWordMode ? SelectionMode : : Word : SelectionMode : : Normal ) ;
EnsureCursorVisible ( ) ;
}
void TextEditor : : MoveRight ( int aAmount , bool aSelect , bool aWordMode )
{
auto oldPos = mState . mCursorPosition ;
if ( mLines . empty ( ) )
return ;
while ( aAmount - - > 0 )
{
auto & line = mLines [ mState . mCursorPosition . mLine ] ;
if ( mState . mCursorPosition . mColumn > = ( int ) line . size ( ) )
{
if ( mState . mCursorPosition . mLine < ( int ) mLines . size ( ) - 1 )
{
mState . mCursorPosition . mLine = std : : max ( 0 , std : : min ( ( int ) mLines . size ( ) - 1 , mState . mCursorPosition . mLine + 1 ) ) ;
mState . mCursorPosition . mColumn = 0 ;
}
}
else
{
mState . mCursorPosition . mColumn = std : : max ( 0 , std : : min ( ( int ) line . size ( ) , mState . mCursorPosition . mColumn + 1 ) ) ;
if ( aWordMode )
mState . mCursorPosition = FindWordEnd ( mState . mCursorPosition ) ;
}
}
if ( aSelect )
{
if ( oldPos = = mInteractiveEnd )
mInteractiveEnd = SanitizeCoordinates ( mState . mCursorPosition ) ;
else if ( oldPos = = mInteractiveStart )
mInteractiveStart = mState . mCursorPosition ;
else
{
mInteractiveStart = oldPos ;
mInteractiveEnd = mState . mCursorPosition ;
}
}
else
mInteractiveStart = mInteractiveEnd = mState . mCursorPosition ;
SetSelection ( mInteractiveStart , mInteractiveEnd , aSelect & & aWordMode ? SelectionMode : : Word : SelectionMode : : Normal ) ;
EnsureCursorVisible ( ) ;
}
void TextEditor : : MoveTop ( bool aSelect )
{
auto oldPos = mState . mCursorPosition ;
SetCursorPosition ( Coordinates ( 0 , 0 ) ) ;
if ( mState . mCursorPosition ! = oldPos )
{
if ( aSelect )
{
mInteractiveEnd = oldPos ;
mInteractiveStart = mState . mCursorPosition ;
}
else
mInteractiveStart = mInteractiveEnd = mState . mCursorPosition ;
SetSelection ( mInteractiveStart , mInteractiveEnd ) ;
}
}
void TextEditor : : TextEditor : : MoveBottom ( bool aSelect )
{
auto oldPos = GetCursorPosition ( ) ;
auto newPos = Coordinates ( ( int ) mLines . size ( ) - 1 , 0 ) ;
SetCursorPosition ( newPos ) ;
if ( aSelect )
{
mInteractiveStart = oldPos ;
mInteractiveEnd = newPos ;
}
else
mInteractiveStart = mInteractiveEnd = newPos ;
SetSelection ( mInteractiveStart , mInteractiveEnd ) ;
}
void TextEditor : : MoveHome ( bool aSelect )
{
auto oldPos = mState . mCursorPosition ;
SetCursorPosition ( Coordinates ( mState . mCursorPosition . mLine , 0 ) ) ;
if ( mState . mCursorPosition ! = oldPos )
{
if ( aSelect )
{
if ( oldPos = = mInteractiveStart )
mInteractiveStart = mState . mCursorPosition ;
else if ( oldPos = = mInteractiveEnd )
mInteractiveEnd = mState . mCursorPosition ;
else
{
mInteractiveStart = mState . mCursorPosition ;
mInteractiveEnd = oldPos ;
}
}
else
mInteractiveStart = mInteractiveEnd = mState . mCursorPosition ;
SetSelection ( mInteractiveStart , mInteractiveEnd ) ;
}
}
void TextEditor : : MoveEnd ( bool aSelect )
{
auto oldPos = mState . mCursorPosition ;
SetCursorPosition ( Coordinates ( mState . mCursorPosition . mLine , ( int ) mLines [ oldPos . mLine ] . size ( ) ) ) ;
if ( mState . mCursorPosition ! = oldPos )
{
if ( aSelect )
{
if ( oldPos = = mInteractiveEnd )
mInteractiveEnd = mState . mCursorPosition ;
else if ( oldPos = = mInteractiveStart )
mInteractiveStart = mState . mCursorPosition ;
else
{
mInteractiveStart = oldPos ;
mInteractiveEnd = mState . mCursorPosition ;
}
}
else
mInteractiveStart = mInteractiveEnd = mState . mCursorPosition ;
SetSelection ( mInteractiveStart , mInteractiveEnd ) ;
}
}
void TextEditor : : Delete ( )
{
assert ( ! mReadOnly ) ;
if ( mLines . empty ( ) )
return ;
UndoRecord u ;
u . mBefore = mState ;
if ( HasSelection ( ) )
{
u . mRemoved = GetSelectedText ( ) ;
u . mRemovedStart = mState . mSelectionStart ;
u . mRemovedEnd = mState . mSelectionEnd ;
DeleteSelection ( ) ;
}
else
{
auto pos = GetActualCursorCoordinates ( ) ;
SetCursorPosition ( pos ) ;
auto & line = mLines [ pos . mLine ] ;
if ( pos . mColumn = = ( int ) line . size ( ) )
{
if ( pos . mLine = = ( int ) mLines . size ( ) - 1 )
return ;
u . mRemoved = ' \n ' ;
u . mRemovedStart = u . mRemovedEnd = GetActualCursorCoordinates ( ) ;
Advance ( u . mRemovedEnd ) ;
auto & nextLine = mLines [ pos . mLine + 1 ] ;
line . insert ( line . end ( ) , nextLine . begin ( ) , nextLine . end ( ) ) ;
RemoveLine ( pos . mLine + 1 ) ;
}
else
{
u . mRemoved = line [ pos . mColumn ] . mChar ;
u . mRemovedStart = u . mRemovedEnd = GetActualCursorCoordinates ( ) ;
u . mRemovedEnd . mColumn + + ;
line . erase ( line . begin ( ) + pos . mColumn ) ;
}
mTextChanged = true ;
Colorize ( pos . mLine , 1 ) ;
}
u . mAfter = mState ;
AddUndo ( u ) ;
}
void TextEditor : : BackSpace ( )
{
assert ( ! mReadOnly ) ;
if ( mLines . empty ( ) )
return ;
UndoRecord u ;
u . mBefore = mState ;
if ( HasSelection ( ) )
{
u . mRemoved = GetSelectedText ( ) ;
u . mRemovedStart = mState . mSelectionStart ;
u . mRemovedEnd = mState . mSelectionEnd ;
DeleteSelection ( ) ;
}
else
{
auto pos = GetActualCursorCoordinates ( ) ;
SetCursorPosition ( pos ) ;
if ( mState . mCursorPosition . mColumn = = 0 )
{
if ( mState . mCursorPosition . mLine = = 0 )
return ;
u . mRemoved = ' \n ' ;
2018-10-30 21:52:57 +00:00
u . mRemovedStart = u . mRemovedEnd = Coordinates ( pos . mLine - 1 , ( int ) mLines [ pos . mLine - 1 ] . size ( ) ) ;
2018-08-17 12:31:33 +00:00
Advance ( u . mRemovedEnd ) ;
auto & line = mLines [ mState . mCursorPosition . mLine ] ;
auto & prevLine = mLines [ mState . mCursorPosition . mLine - 1 ] ;
auto prevSize = ( int ) prevLine . size ( ) ;
prevLine . insert ( prevLine . end ( ) , line . begin ( ) , line . end ( ) ) ;
2018-10-30 21:52:57 +00:00
ErrorMarkers etmp ;
for ( auto & i : mErrorMarkers )
etmp . insert ( ErrorMarkers : : value_type ( i . first - 1 = = mState . mCursorPosition . mLine ? i . first - 1 : i . first , i . second ) ) ;
mErrorMarkers = std : : move ( etmp ) ;
2018-08-17 12:31:33 +00:00
RemoveLine ( mState . mCursorPosition . mLine ) ;
- - mState . mCursorPosition . mLine ;
mState . mCursorPosition . mColumn = prevSize ;
}
else
{
auto & line = mLines [ mState . mCursorPosition . mLine ] ;
u . mRemoved = line [ pos . mColumn - 1 ] . mChar ;
u . mRemovedStart = u . mRemovedEnd = GetActualCursorCoordinates ( ) ;
- - u . mRemovedStart . mColumn ;
- - mState . mCursorPosition . mColumn ;
if ( mState . mCursorPosition . mColumn < ( int ) line . size ( ) )
line . erase ( line . begin ( ) + mState . mCursorPosition . mColumn ) ;
}
mTextChanged = true ;
EnsureCursorVisible ( ) ;
Colorize ( mState . mCursorPosition . mLine , 1 ) ;
}
u . mAfter = mState ;
AddUndo ( u ) ;
}
void TextEditor : : SelectWordUnderCursor ( )
{
auto c = GetCursorPosition ( ) ;
SetSelection ( FindWordStart ( c ) , FindWordEnd ( c ) ) ;
}
void TextEditor : : SelectAll ( )
{
SetSelection ( Coordinates ( 0 , 0 ) , Coordinates ( ( int ) mLines . size ( ) , 0 ) ) ;
}
bool TextEditor : : HasSelection ( ) const
{
return mState . mSelectionEnd > mState . mSelectionStart ;
}
void TextEditor : : Copy ( )
{
if ( HasSelection ( ) )
{
ImGui : : SetClipboardText ( GetSelectedText ( ) . c_str ( ) ) ;
}
else
{
if ( ! mLines . empty ( ) )
{
std : : string str ;
auto & line = mLines [ GetActualCursorCoordinates ( ) . mLine ] ;
for ( auto & g : line )
str . push_back ( g . mChar ) ;
ImGui : : SetClipboardText ( str . c_str ( ) ) ;
}
}
}
void TextEditor : : Cut ( )
{
if ( IsReadOnly ( ) )
{
Copy ( ) ;
}
else
{
if ( HasSelection ( ) )
{
UndoRecord u ;
u . mBefore = mState ;
u . mRemoved = GetSelectedText ( ) ;
u . mRemovedStart = mState . mSelectionStart ;
u . mRemovedEnd = mState . mSelectionEnd ;
Copy ( ) ;
DeleteSelection ( ) ;
u . mAfter = mState ;
AddUndo ( u ) ;
}
}
}
void TextEditor : : Paste ( )
{
auto clipText = ImGui : : GetClipboardText ( ) ;
if ( clipText ! = nullptr & & strlen ( clipText ) > 0 )
{
UndoRecord u ;
u . mBefore = mState ;
if ( HasSelection ( ) )
{
u . mRemoved = GetSelectedText ( ) ;
u . mRemovedStart = mState . mSelectionStart ;
u . mRemovedEnd = mState . mSelectionEnd ;
DeleteSelection ( ) ;
}
u . mAdded = clipText ;
u . mAddedStart = GetActualCursorCoordinates ( ) ;
InsertText ( clipText ) ;
u . mAddedEnd = GetActualCursorCoordinates ( ) ;
u . mAfter = mState ;
AddUndo ( u ) ;
}
}
bool TextEditor : : CanUndo ( ) const
{
return mUndoIndex > 0 ;
}
bool TextEditor : : CanRedo ( ) const
{
return mUndoIndex < ( int ) mUndoBuffer . size ( ) ;
}
void TextEditor : : Undo ( int aSteps )
{
while ( CanUndo ( ) & & aSteps - - > 0 )
mUndoBuffer [ - - mUndoIndex ] . Undo ( this ) ;
}
void TextEditor : : Redo ( int aSteps )
{
while ( CanRedo ( ) & & aSteps - - > 0 )
mUndoBuffer [ mUndoIndex + + ] . Redo ( this ) ;
}
const TextEditor : : Palette & TextEditor : : GetDarkPalette ( )
{
2019-01-19 13:05:54 +00:00
const static Palette p = { {
0xff7f7f7f , // Default
2018-08-17 12:31:33 +00:00
0xffd69c56 , // Keyword
0xff00ff00 , // Number
0xff7070e0 , // String
0xff70a0e0 , // Char literal
0xffffffff , // Punctuation
2019-01-19 13:05:54 +00:00
0xff408080 , // Preprocessor
2018-08-17 12:31:33 +00:00
0xffaaaaaa , // Identifier
0xff9bc64d , // Known identifier
0xffc040a0 , // Preproc identifier
0xff206020 , // Comment (single line)
0xff406020 , // Comment (multi line)
0xff101010 , // Background
0xffe0e0e0 , // Cursor
0x80a06020 , // Selection
0x800020ff , // ErrorMarker
0x40f08000 , // Breakpoint
0xff707000 , // Line number
0x40000000 , // Current line fill
0x40808080 , // Current line fill (inactive)
0x40a0a0a0 , // Current line edge
} } ;
return p ;
}
const TextEditor : : Palette & TextEditor : : GetLightPalette ( )
{
2019-01-19 13:05:54 +00:00
const static Palette p = { {
0xff7f7f7f , // None
2018-08-17 12:31:33 +00:00
0xffff0c06 , // Keyword
0xff008000 , // Number
0xff2020a0 , // String
0xff304070 , // Char literal
0xff000000 , // Punctuation
2019-01-19 13:05:54 +00:00
0xff406060 , // Preprocessor
2018-08-17 12:31:33 +00:00
0xff404040 , // Identifier
0xff606010 , // Known identifier
0xffc040a0 , // Preproc identifier
0xff205020 , // Comment (single line)
0xff405020 , // Comment (multi line)
0xffffffff , // Background
0xff000000 , // Cursor
0x80600000 , // Selection
0xa00010ff , // ErrorMarker
0x80f08000 , // Breakpoint
0xff505000 , // Line number
0x40000000 , // Current line fill
0x40808080 , // Current line fill (inactive)
0x40000000 , // Current line edge
} } ;
return p ;
}
const TextEditor : : Palette & TextEditor : : GetRetroBluePalette ( )
{
2019-01-19 13:05:54 +00:00
const static Palette p = { {
2018-08-17 12:31:33 +00:00
0xff00ffff , // None
0xffffff00 , // Keyword
0xff00ff00 , // Number
0xff808000 , // String
0xff808000 , // Char literal
0xffffffff , // Punctuation
0xff008000 , // Preprocessor
0xff00ffff , // Identifier
0xffffffff , // Known identifier
0xffff00ff , // Preproc identifier
0xff808080 , // Comment (single line)
0xff404040 , // Comment (multi line)
0xff800000 , // Background
0xff0080ff , // Cursor
0x80ffff00 , // Selection
0xa00000ff , // ErrorMarker
0x80ff8000 , // Breakpoint
0xff808000 , // Line number
0x40000000 , // Current line fill
0x40808080 , // Current line fill (inactive)
0x40000000 , // Current line edge
} } ;
return p ;
}
std : : string TextEditor : : GetText ( ) const
{
return GetText ( Coordinates ( ) , Coordinates ( ( int ) mLines . size ( ) , 0 ) ) ;
}
2019-01-19 13:05:54 +00:00
std : : vector < std : : string > TextEditor : : GetTextLines ( ) const
{
std : : vector < std : : string > result ;
result . reserve ( mLines . size ( ) ) ;
for ( auto & line : mLines )
{
std : : string text ;
text . resize ( line . size ( ) ) ;
for ( size_t i = 0 ; i < line . size ( ) ; + + i )
text [ i ] = line [ i ] . mChar ;
result . emplace_back ( std : : move ( text ) ) ;
}
return result ;
}
2018-08-17 12:31:33 +00:00
std : : string TextEditor : : GetSelectedText ( ) const
{
return GetText ( mState . mSelectionStart , mState . mSelectionEnd ) ;
}
2018-10-30 21:52:57 +00:00
std : : string TextEditor : : GetCurrentLineText ( ) const
{
2019-01-19 13:05:54 +00:00
auto lineLength = ( int ) mLines [ mState . mCursorPosition . mLine ] . size ( ) ;
2018-10-30 21:52:57 +00:00
return GetText ( Coordinates ( mState . mCursorPosition . mLine , 0 ) , Coordinates ( mState . mCursorPosition . mLine , lineLength ) ) ;
}
2018-08-17 12:31:33 +00:00
void TextEditor : : ProcessInputs ( )
{
}
void TextEditor : : Colorize ( int aFromLine , int aLines )
{
int toLine = aLines = = - 1 ? ( int ) mLines . size ( ) : std : : min ( ( int ) mLines . size ( ) , aFromLine + aLines ) ;
mColorRangeMin = std : : min ( mColorRangeMin , aFromLine ) ;
mColorRangeMax = std : : max ( mColorRangeMax , toLine ) ;
mColorRangeMin = std : : max ( 0 , mColorRangeMin ) ;
mColorRangeMax = std : : max ( mColorRangeMin , mColorRangeMax ) ;
2019-01-19 13:05:54 +00:00
mCheckComments = true ;
2018-08-17 12:31:33 +00:00
}
void TextEditor : : ColorizeRange ( int aFromLine , int aToLine )
{
if ( mLines . empty ( ) | | aFromLine > = aToLine )
return ;
std : : string buffer ;
2018-10-30 21:52:57 +00:00
std : : cmatch results ;
std : : string id ;
2019-01-19 13:05:54 +00:00
2018-08-17 12:31:33 +00:00
int endLine = std : : max ( 0 , std : : min ( ( int ) mLines . size ( ) , aToLine ) ) ;
for ( int i = aFromLine ; i < endLine ; + + i )
{
auto & line = mLines [ i ] ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( line . empty ( ) )
continue ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
buffer . resize ( line . size ( ) ) ;
for ( size_t j = 0 ; j < line . size ( ) ; + + j )
2018-08-17 12:31:33 +00:00
{
2018-10-30 21:52:57 +00:00
auto & col = line [ j ] ;
buffer [ j ] = col . mChar ;
col . mColorIndex = PaletteIndex : : Default ;
2018-08-17 12:31:33 +00:00
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
const char * bufferBegin = & buffer . front ( ) ;
const char * bufferEnd = bufferBegin + buffer . size ( ) ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
auto last = bufferEnd ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
for ( auto first = bufferBegin ; first ! = last ; )
2018-08-17 12:31:33 +00:00
{
2018-10-30 21:52:57 +00:00
const char * token_begin = nullptr ;
const char * token_end = nullptr ;
PaletteIndex token_color = PaletteIndex : : Default ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
bool hasTokenizeResult = false ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( mLanguageDefinition . mTokenize ! = nullptr )
2018-08-17 12:31:33 +00:00
{
2018-10-30 21:52:57 +00:00
if ( mLanguageDefinition . mTokenize ( first , last , token_begin , token_end , token_color ) )
hasTokenizeResult = true ;
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( hasTokenizeResult = = false )
{
2019-01-19 13:05:54 +00:00
// todo : remove
//printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last - first), first);
2018-10-30 21:52:57 +00:00
for ( auto & p : mRegexList )
2018-08-17 12:31:33 +00:00
{
2018-10-30 21:52:57 +00:00
if ( std : : regex_search ( first , last , results , p . first , std : : regex_constants : : match_continuous ) )
2018-08-17 12:31:33 +00:00
{
2018-10-30 21:52:57 +00:00
hasTokenizeResult = true ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
auto & v = * results . begin ( ) ;
token_begin = v . first ;
token_end = v . second ;
token_color = p . second ;
break ;
}
}
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( hasTokenizeResult = = false )
{
first + + ;
}
else
{
const size_t token_length = token_end - token_begin ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( token_color = = PaletteIndex : : Identifier )
{
id . assign ( token_begin , token_end ) ;
2019-01-19 13:05:54 +00:00
// todo : allmost all language definitions use lower case to specify keywords, so shouldn't this use ::tolower ?
2018-10-30 21:52:57 +00:00
if ( ! mLanguageDefinition . mCaseSensitive )
std : : transform ( id . begin ( ) , id . end ( ) , id . begin ( ) , : : toupper ) ;
2018-08-17 12:31:33 +00:00
2019-01-19 13:05:54 +00:00
if ( ! line [ first - bufferBegin ] . mPreprocessor )
2018-10-30 21:52:57 +00:00
{
if ( mLanguageDefinition . mKeywords . count ( id ) ! = 0 )
token_color = PaletteIndex : : Keyword ;
else if ( mLanguageDefinition . mIdentifiers . count ( id ) ! = 0 )
token_color = PaletteIndex : : KnownIdentifier ;
else if ( mLanguageDefinition . mPreprocIdentifiers . count ( id ) ! = 0 )
token_color = PaletteIndex : : PreprocIdentifier ;
2018-08-17 12:31:33 +00:00
}
2018-10-30 21:52:57 +00:00
else
2018-08-17 12:31:33 +00:00
{
2018-10-30 21:52:57 +00:00
if ( mLanguageDefinition . mPreprocIdentifiers . count ( id ) ! = 0 )
token_color = PaletteIndex : : PreprocIdentifier ;
2018-08-17 12:31:33 +00:00
}
}
2019-01-19 13:05:54 +00:00
for ( size_t j = 0 ; j < token_length ; + + j )
2018-10-30 21:52:57 +00:00
line [ ( token_begin - bufferBegin ) + j ] . mColorIndex = token_color ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
first = token_end ;
2018-08-17 12:31:33 +00:00
}
}
}
}
void TextEditor : : ColorizeInternal ( )
{
if ( mLines . empty ( ) )
return ;
2019-01-19 13:05:54 +00:00
if ( mCheckComments )
2018-08-17 12:31:33 +00:00
{
auto end = Coordinates ( ( int ) mLines . size ( ) , 0 ) ;
auto commentStart = end ;
auto withinString = false ;
2019-01-19 13:05:54 +00:00
auto withinSingleLineComment = false ;
auto withinPreproc = false ;
auto firstChar = true ; // there is no other non-whitespace characters in the line before
auto concatenate = false ; // '\' on the very end of the line
for ( auto currentCoord = Coordinates ( 0 , 0 ) ; currentCoord < end ; Advance ( currentCoord ) )
2018-08-17 12:31:33 +00:00
{
2019-01-19 13:05:54 +00:00
auto & line = mLines [ currentCoord . mLine ] ;
if ( currentCoord . mColumn = = 0 & & ! concatenate )
{
withinSingleLineComment = false ;
withinPreproc = false ;
firstChar = true ;
}
concatenate = false ;
2018-08-17 12:31:33 +00:00
if ( ! line . empty ( ) )
{
2019-01-19 13:05:54 +00:00
auto & g = line [ currentCoord . mColumn ] ;
2018-08-17 12:31:33 +00:00
auto c = g . mChar ;
2019-01-19 13:05:54 +00:00
if ( c ! = mLanguageDefinition . mPreprocChar & & ! isspace ( c ) )
firstChar = false ;
if ( currentCoord . mColumn = = line . size ( ) - 1 & & line [ line . size ( ) - 1 ] . mChar = = ' \\ ' )
concatenate = true ;
bool inComment = commentStart < = currentCoord ;
2018-08-17 12:31:33 +00:00
if ( withinString )
{
2019-01-19 13:05:54 +00:00
line [ currentCoord . mColumn ] . mMultiLineComment = inComment ;
2018-08-17 12:31:33 +00:00
if ( c = = ' \" ' )
{
2019-01-19 13:05:54 +00:00
if ( currentCoord . mColumn + 1 < ( int ) line . size ( ) & & line [ currentCoord . mColumn + 1 ] . mChar = = ' \" ' )
2018-08-17 12:31:33 +00:00
{
2019-01-19 13:05:54 +00:00
Advance ( currentCoord ) ;
if ( currentCoord . mColumn < ( int ) line . size ( ) )
line [ currentCoord . mColumn ] . mMultiLineComment = inComment ;
2018-08-17 12:31:33 +00:00
}
else
withinString = false ;
}
else if ( c = = ' \\ ' )
{
2019-01-19 13:05:54 +00:00
Advance ( currentCoord ) ;
if ( currentCoord . mColumn < ( int ) line . size ( ) )
line [ currentCoord . mColumn ] . mMultiLineComment = inComment ;
2018-08-17 12:31:33 +00:00
}
}
else
{
2019-01-19 13:05:54 +00:00
if ( firstChar & & c = = mLanguageDefinition . mPreprocChar )
withinPreproc = true ;
2018-08-17 12:31:33 +00:00
if ( c = = ' \" ' )
{
withinString = true ;
2019-01-19 13:05:54 +00:00
line [ currentCoord . mColumn ] . mMultiLineComment = inComment ;
2018-08-17 12:31:33 +00:00
}
else
{
auto pred = [ ] ( const char & a , const Glyph & b ) { return a = = b . mChar ; } ;
2019-01-19 13:05:54 +00:00
auto from = line . begin ( ) + currentCoord . mColumn ;
2018-08-17 12:31:33 +00:00
auto & startStr = mLanguageDefinition . mCommentStart ;
2019-01-19 13:05:54 +00:00
auto & singleStartStr = mLanguageDefinition . mSingleLineComment ;
if ( singleStartStr . size ( ) > 0 & &
currentCoord . mColumn + singleStartStr . size ( ) < = line . size ( ) & &
equals ( singleStartStr . begin ( ) , singleStartStr . end ( ) , from , from + singleStartStr . size ( ) , pred ) )
withinSingleLineComment = true ;
else if ( ! withinSingleLineComment & & currentCoord . mColumn + startStr . size ( ) < = line . size ( ) & &
2018-08-17 12:31:33 +00:00
equals ( startStr . begin ( ) , startStr . end ( ) , from , from + startStr . size ( ) , pred ) )
2019-01-19 13:05:54 +00:00
commentStart = currentCoord ;
2018-08-17 12:31:33 +00:00
2019-01-19 13:05:54 +00:00
inComment = commentStart < = currentCoord ;
line [ currentCoord . mColumn ] . mMultiLineComment = inComment ;
line [ currentCoord . mColumn ] . mComment = withinSingleLineComment ;
2018-08-17 12:31:33 +00:00
auto & endStr = mLanguageDefinition . mCommentEnd ;
2019-01-19 13:05:54 +00:00
if ( currentCoord . mColumn + 1 > = ( int ) endStr . size ( ) & &
2018-08-17 12:31:33 +00:00
equals ( endStr . begin ( ) , endStr . end ( ) , from + 1 - endStr . size ( ) , from + 1 , pred ) )
commentStart = end ;
}
}
2019-01-19 13:05:54 +00:00
line [ currentCoord . mColumn ] . mPreprocessor = withinPreproc ;
2018-08-17 12:31:33 +00:00
}
}
2019-01-19 13:05:54 +00:00
mCheckComments = false ;
2018-08-17 12:31:33 +00:00
}
if ( mColorRangeMin < mColorRangeMax )
{
2018-10-30 21:52:57 +00:00
const int increment = ( mLanguageDefinition . mTokenize = = nullptr ) ? 10 : 10000 ;
const int to = std : : min ( mColorRangeMin + increment , mColorRangeMax ) ;
2018-08-17 12:31:33 +00:00
ColorizeRange ( mColorRangeMin , to ) ;
mColorRangeMin = to ;
if ( mColorRangeMax = = mColorRangeMin )
{
mColorRangeMin = std : : numeric_limits < int > : : max ( ) ;
mColorRangeMax = 0 ;
}
return ;
}
}
2018-10-30 21:52:57 +00:00
float TextEditor : : TextDistanceToLineStart ( const Coordinates & aFrom ) const
2018-08-17 12:31:33 +00:00
{
auto & line = mLines [ aFrom . mLine ] ;
2018-10-30 21:52:57 +00:00
float distance = 0.0f ;
auto fontScale = ImGui : : GetFontSize ( ) / ImGui : : GetFont ( ) - > FontSize ;
2019-03-15 00:06:27 +00:00
float spaceSize = ImGui : : GetFont ( ) - > CalcTextSizeA ( ImGui : : GetFontSize ( ) , FLT_MAX , - 1.0f , " " , nullptr , nullptr ) . x ;
2018-08-17 12:31:33 +00:00
for ( size_t it = 0u ; it < line . size ( ) & & it < ( unsigned ) aFrom . mColumn ; + + it )
2018-10-30 21:52:57 +00:00
{
if ( line [ it ] . mChar = = ' \t ' )
{
distance = ( 1.0f * fontScale + std : : floor ( ( 1.0f + distance ) ) / ( float ( mTabSize ) * spaceSize ) ) * ( float ( mTabSize ) * spaceSize ) ;
}
else
{
char tempCString [ 2 ] ;
2019-01-19 13:05:54 +00:00
tempCString [ 0 ] = line [ it ] . mChar ;
2018-10-30 21:52:57 +00:00
tempCString [ 1 ] = ' \0 ' ;
2019-03-15 00:06:27 +00:00
distance + = ImGui : : GetFont ( ) - > CalcTextSizeA ( ImGui : : GetFontSize ( ) , FLT_MAX , - 1.0f , tempCString , nullptr , nullptr ) . x ;
2018-10-30 21:52:57 +00:00
}
}
return distance ;
2018-08-17 12:31:33 +00:00
}
void TextEditor : : EnsureCursorVisible ( )
{
if ( ! mWithinRender )
{
mScrollToCursor = true ;
return ;
}
float scrollX = ImGui : : GetScrollX ( ) ;
float scrollY = ImGui : : GetScrollY ( ) ;
auto height = ImGui : : GetWindowHeight ( ) ;
auto width = ImGui : : GetWindowWidth ( ) ;
auto top = 1 + ( int ) ceil ( scrollY / mCharAdvance . y ) ;
auto bottom = ( int ) ceil ( ( scrollY + height ) / mCharAdvance . y ) ;
auto left = ( int ) ceil ( scrollX / mCharAdvance . x ) ;
auto right = ( int ) ceil ( ( scrollX + width ) / mCharAdvance . x ) ;
auto pos = GetActualCursorCoordinates ( ) ;
auto len = TextDistanceToLineStart ( pos ) ;
if ( pos . mLine < top )
ImGui : : SetScrollY ( std : : max ( 0.0f , ( pos . mLine - 1 ) * mCharAdvance . y ) ) ;
if ( pos . mLine > bottom - 4 )
ImGui : : SetScrollY ( std : : max ( 0.0f , ( pos . mLine + 4 ) * mCharAdvance . y - height ) ) ;
2018-10-30 21:52:57 +00:00
if ( len + mTextStart < left + 4 )
ImGui : : SetScrollX ( std : : max ( 0.0f , len + mTextStart - 4 ) ) ;
if ( len + mTextStart > right - 4 )
ImGui : : SetScrollX ( std : : max ( 0.0f , len + mTextStart + 4 - width ) ) ;
2018-08-17 12:31:33 +00:00
}
int TextEditor : : GetPageSize ( ) const
{
auto height = ImGui : : GetWindowHeight ( ) - 20.0f ;
return ( int ) floor ( height / mCharAdvance . y ) ;
}
TextEditor : : UndoRecord : : UndoRecord (
const std : : string & aAdded ,
const TextEditor : : Coordinates aAddedStart ,
const TextEditor : : Coordinates aAddedEnd ,
const std : : string & aRemoved ,
const TextEditor : : Coordinates aRemovedStart ,
const TextEditor : : Coordinates aRemovedEnd ,
TextEditor : : EditorState & aBefore ,
TextEditor : : EditorState & aAfter )
: mAdded ( aAdded )
, mAddedStart ( aAddedStart )
, mAddedEnd ( aAddedEnd )
, mRemoved ( aRemoved )
, mRemovedStart ( aRemovedStart )
, mRemovedEnd ( aRemovedEnd )
, mBefore ( aBefore )
, mAfter ( aAfter )
{
assert ( mAddedStart < = mAddedEnd ) ;
assert ( mRemovedStart < = mRemovedEnd ) ;
}
void TextEditor : : UndoRecord : : Undo ( TextEditor * aEditor )
{
if ( ! mAdded . empty ( ) )
{
aEditor - > DeleteRange ( mAddedStart , mAddedEnd ) ;
aEditor - > Colorize ( mAddedStart . mLine - 1 , mAddedEnd . mLine - mAddedStart . mLine + 2 ) ;
}
if ( ! mRemoved . empty ( ) )
{
auto start = mRemovedStart ;
aEditor - > InsertTextAt ( start , mRemoved . c_str ( ) ) ;
aEditor - > Colorize ( mRemovedStart . mLine - 1 , mRemovedEnd . mLine - mRemovedStart . mLine + 2 ) ;
}
aEditor - > mState = mBefore ;
aEditor - > EnsureCursorVisible ( ) ;
}
void TextEditor : : UndoRecord : : Redo ( TextEditor * aEditor )
{
if ( ! mRemoved . empty ( ) )
{
aEditor - > DeleteRange ( mRemovedStart , mRemovedEnd ) ;
aEditor - > Colorize ( mRemovedStart . mLine - 1 , mRemovedEnd . mLine - mRemovedStart . mLine + 1 ) ;
}
if ( ! mAdded . empty ( ) )
{
auto start = mAddedStart ;
aEditor - > InsertTextAt ( start , mAdded . c_str ( ) ) ;
aEditor - > Colorize ( mAddedStart . mLine - 1 , mAddedEnd . mLine - mAddedStart . mLine + 1 ) ;
}
aEditor - > mState = mAfter ;
aEditor - > EnsureCursorVisible ( ) ;
}
2018-10-30 21:52:57 +00:00
static bool TokenizeCStyleString ( const char * in_begin , const char * in_end , const char * & out_begin , const char * & out_end )
{
const char * p = in_begin ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( * p = = ' " ' )
{
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
while ( p < in_end )
{
// handle end of string
if ( * p = = ' " ' )
{
out_begin = in_begin ;
out_end = p + 1 ;
return true ;
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
// handle escape character for "
if ( * p = = ' \\ ' & & p + 1 < in_end & & p [ 1 ] = = ' " ' )
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
p + + ;
}
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
return false ;
}
static bool TokenizeCStyleCharacterLiteral ( const char * in_begin , const char * in_end , const char * & out_begin , const char * & out_end )
{
const char * p = in_begin ;
if ( * p = = ' \' ' )
{
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
// handle escape characters
if ( p < in_end & & * p = = ' \\ ' )
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( p < in_end )
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
// handle end of character literal
if ( p < in_end & & * p = = ' \' ' )
{
out_begin = in_begin ;
out_end = p + 1 ;
return true ;
}
}
return false ;
}
static bool TokenizeCStyleIdentifier ( const char * in_begin , const char * in_end , const char * & out_begin , const char * & out_end )
{
const char * p = in_begin ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( ( * p > = ' a ' & & * p < = ' z ' ) | | ( * p > = ' A ' & & * p < = ' Z ' ) | | * p = = ' _ ' )
{
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
while ( ( p < in_end ) & & ( ( * p > = ' a ' & & * p < = ' z ' ) | | ( * p > = ' A ' & & * p < = ' Z ' ) | | ( * p > = ' 0 ' & & * p < = ' 9 ' ) | | * p = = ' _ ' ) )
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
out_begin = in_begin ;
out_end = p ;
return true ;
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
return false ;
}
static bool TokenizeCStyleNumber ( const char * in_begin , const char * in_end , const char * & out_begin , const char * & out_end )
{
const char * p = in_begin ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
const bool startsWithNumber = * p > = ' 0 ' & & * p < = ' 9 ' ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( * p ! = ' + ' & & * p ! = ' - ' & & ! startsWithNumber )
return false ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
bool hasNumber = startsWithNumber ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
while ( p < in_end & & ( * p > = ' 0 ' & & * p < = ' 9 ' ) )
{
hasNumber = true ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
p + + ;
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( hasNumber = = false )
return false ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
bool isFloat = false ;
bool isHex = false ;
bool isBinary = false ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( p < in_end )
{
if ( * p = = ' . ' )
{
isFloat = true ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
while ( p < in_end & & ( * p > = ' 0 ' & & * p < = ' 9 ' ) )
p + + ;
}
else if ( * p = = ' x ' | | * p = = ' X ' )
{
// hex formatted integer of the type 0xef80
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
isHex = true ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
while ( p < in_end & & ( ( * p > = ' 0 ' & & * p < = ' 9 ' ) | | ( * p > = ' a ' & & * p < = ' f ' ) | | ( * p > = ' A ' & & * p < = ' F ' ) ) )
p + + ;
}
else if ( * p = = ' b ' | | * p = = ' B ' )
{
// binary formatted integer of the type 0b01011101
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
isBinary = true ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
while ( p < in_end & & ( * p > = ' 0 ' & & * p < = ' 1 ' ) )
p + + ;
}
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( isHex = = false & & isBinary = = false )
{
// floating point exponent
if ( p < in_end & & ( * p = = ' e ' | | * p = = ' E ' ) )
{
isFloat = true ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( p < in_end & & ( * p = = ' + ' | | * p = = ' - ' ) )
p + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
bool hasDigits = false ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
while ( p < in_end & & ( * p > = ' 0 ' & & * p < = ' 9 ' ) )
{
hasDigits = true ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
p + + ;
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( hasDigits = = false )
return false ;
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
// single precision floating point type
if ( p < in_end & & * p = = ' f ' )
p + + ;
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( isFloat = = false )
{
// integer size type
while ( p < in_end & & ( * p = = ' u ' | | * p = = ' U ' | | * p = = ' l ' | | * p = = ' L ' ) )
p + + ;
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
out_begin = in_begin ;
out_end = p ;
return true ;
}
static bool TokenizeCStylePunctuation ( const char * in_begin , const char * in_end , const char * & out_begin , const char * & out_end )
{
2019-01-19 13:05:54 +00:00
( void ) in_end ;
2018-10-30 21:52:57 +00:00
switch ( * in_begin )
{
2019-01-19 13:05:54 +00:00
case ' [ ' :
case ' ] ' :
case ' { ' :
case ' } ' :
case ' ! ' :
case ' % ' :
case ' ^ ' :
case ' & ' :
case ' * ' :
case ' ( ' :
case ' ) ' :
case ' - ' :
case ' + ' :
case ' = ' :
case ' ~ ' :
case ' | ' :
case ' < ' :
case ' > ' :
case ' ? ' :
case ' : ' :
case ' / ' :
case ' ; ' :
case ' , ' :
case ' . ' :
out_begin = in_begin ;
out_end = in_begin + 1 ;
return true ;
2018-10-30 21:52:57 +00:00
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
return false ;
}
const TextEditor : : LanguageDefinition & TextEditor : : LanguageDefinition : : CPlusPlus ( )
2018-08-17 12:31:33 +00:00
{
static bool inited = false ;
static LanguageDefinition langDef ;
if ( ! inited )
{
static const char * const cppKeywords [ ] = {
" alignas " , " alignof " , " and " , " and_eq " , " asm " , " atomic_cancel " , " atomic_commit " , " atomic_noexcept " , " auto " , " bitand " , " bitor " , " bool " , " break " , " case " , " catch " , " char " , " char16_t " , " char32_t " , " class " ,
" compl " , " concept " , " const " , " constexpr " , " const_cast " , " continue " , " decltype " , " default " , " delete " , " do " , " double " , " dynamic_cast " , " else " , " enum " , " explicit " , " export " , " extern " , " false " , " float " ,
" for " , " friend " , " goto " , " if " , " import " , " inline " , " int " , " long " , " module " , " mutable " , " namespace " , " new " , " noexcept " , " not " , " not_eq " , " nullptr " , " operator " , " or " , " or_eq " , " private " , " protected " , " public " ,
" register " , " reinterpret_cast " , " requires " , " return " , " short " , " signed " , " sizeof " , " static " , " static_assert " , " static_cast " , " struct " , " switch " , " synchronized " , " template " , " this " , " thread_local " ,
" throw " , " true " , " try " , " typedef " , " typeid " , " typename " , " union " , " unsigned " , " using " , " virtual " , " void " , " volatile " , " wchar_t " , " while " , " xor " , " xor_eq "
} ;
for ( auto & k : cppKeywords )
langDef . mKeywords . insert ( k ) ;
static const char * const identifiers [ ] = {
" abort " , " abs " , " acos " , " asin " , " atan " , " atexit " , " atof " , " atoi " , " atol " , " ceil " , " clock " , " cosh " , " ctime " , " div " , " exit " , " fabs " , " floor " , " fmod " , " getchar " , " getenv " , " isalnum " , " isalpha " , " isdigit " , " isgraph " ,
" ispunct " , " isspace " , " isupper " , " kbhit " , " log10 " , " log2 " , " log " , " memcmp " , " modf " , " pow " , " printf " , " sprintf " , " snprintf " , " putchar " , " putenv " , " puts " , " rand " , " remove " , " rename " , " sinh " , " sqrt " , " srand " , " strcat " , " strcmp " , " strerror " , " time " , " tolower " , " toupper " ,
" std " , " string " , " vector " , " map " , " unordered_map " , " set " , " unordered_set " , " min " , " max "
} ;
for ( auto & k : identifiers )
{
Identifier id ;
id . mDeclaration = " Built-in function " ;
langDef . mIdentifiers . insert ( std : : make_pair ( std : : string ( k ) , id ) ) ;
}
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
langDef . mTokenize = [ ] ( const char * in_begin , const char * in_end , const char * & out_begin , const char * & out_end , PaletteIndex & paletteIndex ) - > bool
{
paletteIndex = PaletteIndex : : Max ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
while ( in_begin < in_end & & isblank ( * in_begin ) )
in_begin + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( in_begin = = in_end )
{
out_begin = in_end ;
out_end = in_end ;
paletteIndex = PaletteIndex : : Default ;
}
else if ( TokenizeCStyleString ( in_begin , in_end , out_begin , out_end ) )
paletteIndex = PaletteIndex : : String ;
else if ( TokenizeCStyleCharacterLiteral ( in_begin , in_end , out_begin , out_end ) )
paletteIndex = PaletteIndex : : CharLiteral ;
else if ( TokenizeCStyleIdentifier ( in_begin , in_end , out_begin , out_end ) )
paletteIndex = PaletteIndex : : Identifier ;
else if ( TokenizeCStyleNumber ( in_begin , in_end , out_begin , out_end ) )
paletteIndex = PaletteIndex : : Number ;
else if ( TokenizeCStylePunctuation ( in_begin , in_end , out_begin , out_end ) )
paletteIndex = PaletteIndex : : Punctuation ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
return paletteIndex ! = PaletteIndex : : Max ;
} ;
2019-01-19 13:05:54 +00:00
2018-08-17 12:31:33 +00:00
langDef . mCommentStart = " /* " ;
langDef . mCommentEnd = " */ " ;
2019-01-19 13:05:54 +00:00
langDef . mSingleLineComment = " // " ;
2018-08-17 12:31:33 +00:00
langDef . mCaseSensitive = true ;
2018-10-30 21:52:57 +00:00
langDef . mAutoIndentation = true ;
2018-08-17 12:31:33 +00:00
langDef . mName = " C++ " ;
inited = true ;
}
return langDef ;
}
2018-10-30 21:52:57 +00:00
const TextEditor : : LanguageDefinition & TextEditor : : LanguageDefinition : : HLSL ( )
2018-08-17 12:31:33 +00:00
{
static bool inited = false ;
static LanguageDefinition langDef ;
if ( ! inited )
{
static const char * const keywords [ ] = {
" AppendStructuredBuffer " , " asm " , " asm_fragment " , " BlendState " , " bool " , " break " , " Buffer " , " ByteAddressBuffer " , " case " , " cbuffer " , " centroid " , " class " , " column_major " , " compile " , " compile_fragment " ,
" CompileShader " , " const " , " continue " , " ComputeShader " , " ConsumeStructuredBuffer " , " default " , " DepthStencilState " , " DepthStencilView " , " discard " , " do " , " double " , " DomainShader " , " dword " , " else " ,
" export " , " extern " , " false " , " float " , " for " , " fxgroup " , " GeometryShader " , " groupshared " , " half " , " Hullshader " , " if " , " in " , " inline " , " inout " , " InputPatch " , " int " , " interface " , " line " , " lineadj " ,
" linear " , " LineStream " , " matrix " , " min16float " , " min10float " , " min16int " , " min12int " , " min16uint " , " namespace " , " nointerpolation " , " noperspective " , " NULL " , " out " , " OutputPatch " , " packoffset " ,
" pass " , " pixelfragment " , " PixelShader " , " point " , " PointStream " , " precise " , " RasterizerState " , " RenderTargetView " , " return " , " register " , " row_major " , " RWBuffer " , " RWByteAddressBuffer " , " RWStructuredBuffer " ,
" RWTexture1D " , " RWTexture1DArray " , " RWTexture2D " , " RWTexture2DArray " , " RWTexture3D " , " sample " , " sampler " , " SamplerState " , " SamplerComparisonState " , " shared " , " snorm " , " stateblock " , " stateblock_state " ,
" static " , " string " , " struct " , " switch " , " StructuredBuffer " , " tbuffer " , " technique " , " technique10 " , " technique11 " , " texture " , " Texture1D " , " Texture1DArray " , " Texture2D " , " Texture2DArray " , " Texture2DMS " ,
" Texture2DMSArray " , " Texture3D " , " TextureCube " , " TextureCubeArray " , " true " , " typedef " , " triangle " , " triangleadj " , " TriangleStream " , " uint " , " uniform " , " unorm " , " unsigned " , " vector " , " vertexfragment " ,
" VertexShader " , " void " , " volatile " , " while " ,
" bool1 " , " bool2 " , " bool3 " , " bool4 " , " double1 " , " double2 " , " double3 " , " double4 " , " float1 " , " float2 " , " float3 " , " float4 " , " int1 " , " int2 " , " int3 " , " int4 " , " in " , " out " , " inout " ,
" uint1 " , " uint2 " , " uint3 " , " uint4 " , " dword1 " , " dword2 " , " dword3 " , " dword4 " , " half1 " , " half2 " , " half3 " , " half4 " ,
" float1x1 " , " float2x1 " , " float3x1 " , " float4x1 " , " float1x2 " , " float2x2 " , " float3x2 " , " float4x2 " ,
" float1x3 " , " float2x3 " , " float3x3 " , " float4x3 " , " float1x4 " , " float2x4 " , " float3x4 " , " float4x4 " ,
" half1x1 " , " half2x1 " , " half3x1 " , " half4x1 " , " half1x2 " , " half2x2 " , " half3x2 " , " half4x2 " ,
" half1x3 " , " half2x3 " , " half3x3 " , " half4x3 " , " half1x4 " , " half2x4 " , " half3x4 " , " half4x4 " ,
} ;
for ( auto & k : keywords )
langDef . mKeywords . insert ( k ) ;
static const char * const identifiers [ ] = {
" abort " , " abs " , " acos " , " all " , " AllMemoryBarrier " , " AllMemoryBarrierWithGroupSync " , " any " , " asdouble " , " asfloat " , " asin " , " asint " , " asint " , " asuint " ,
" asuint " , " atan " , " atan2 " , " ceil " , " CheckAccessFullyMapped " , " clamp " , " clip " , " cos " , " cosh " , " countbits " , " cross " , " D3DCOLORtoUBYTE4 " , " ddx " ,
" ddx_coarse " , " ddx_fine " , " ddy " , " ddy_coarse " , " ddy_fine " , " degrees " , " determinant " , " DeviceMemoryBarrier " , " DeviceMemoryBarrierWithGroupSync " ,
" distance " , " dot " , " dst " , " errorf " , " EvaluateAttributeAtCentroid " , " EvaluateAttributeAtSample " , " EvaluateAttributeSnapped " , " exp " , " exp2 " ,
" f16tof32 " , " f32tof16 " , " faceforward " , " firstbithigh " , " firstbitlow " , " floor " , " fma " , " fmod " , " frac " , " frexp " , " fwidth " , " GetRenderTargetSampleCount " ,
" GetRenderTargetSamplePosition " , " GroupMemoryBarrier " , " GroupMemoryBarrierWithGroupSync " , " InterlockedAdd " , " InterlockedAnd " , " InterlockedCompareExchange " ,
" InterlockedCompareStore " , " InterlockedExchange " , " InterlockedMax " , " InterlockedMin " , " InterlockedOr " , " InterlockedXor " , " isfinite " , " isinf " , " isnan " ,
" ldexp " , " length " , " lerp " , " lit " , " log " , " log10 " , " log2 " , " mad " , " max " , " min " , " modf " , " msad4 " , " mul " , " noise " , " normalize " , " pow " , " printf " ,
" Process2DQuadTessFactorsAvg " , " Process2DQuadTessFactorsMax " , " Process2DQuadTessFactorsMin " , " ProcessIsolineTessFactors " , " ProcessQuadTessFactorsAvg " ,
" ProcessQuadTessFactorsMax " , " ProcessQuadTessFactorsMin " , " ProcessTriTessFactorsAvg " , " ProcessTriTessFactorsMax " , " ProcessTriTessFactorsMin " ,
" radians " , " rcp " , " reflect " , " refract " , " reversebits " , " round " , " rsqrt " , " saturate " , " sign " , " sin " , " sincos " , " sinh " , " smoothstep " , " sqrt " , " step " ,
" tan " , " tanh " , " tex1D " , " tex1D " , " tex1Dbias " , " tex1Dgrad " , " tex1Dlod " , " tex1Dproj " , " tex2D " , " tex2D " , " tex2Dbias " , " tex2Dgrad " , " tex2Dlod " , " tex2Dproj " ,
" tex3D " , " tex3D " , " tex3Dbias " , " tex3Dgrad " , " tex3Dlod " , " tex3Dproj " , " texCUBE " , " texCUBE " , " texCUBEbias " , " texCUBEgrad " , " texCUBElod " , " texCUBEproj " , " transpose " , " trunc "
} ;
for ( auto & k : identifiers )
{
Identifier id ;
id . mDeclaration = " Built-in function " ;
langDef . mIdentifiers . insert ( std : : make_pair ( std : : string ( k ) , id ) ) ;
}
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [ \\ t]*#[ \\ t]*[a-zA-Z_]+ " , PaletteIndex : : Preprocessor ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " L? \\ \" ( \\ \\ .|[^ \\ \" ])* \\ \" " , PaletteIndex : : String ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " \\ ' \\ \\ ?[^ \\ '] \\ ' " , PaletteIndex : : CharLiteral ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [+-]?[0-9]+[Uu]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " 0[0-7]+[Uu]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " 0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [a-zA-Z_][a-zA-Z0-9_]* " , PaletteIndex : : Identifier ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [ \\ [ \\ ] \\ { \\ } \\ ! \\ % \\ ^ \\ & \\ * \\ ( \\ ) \\ - \\ + \\ = \\ ~ \\ | \\ < \\ > \\ ? \\ / \\ ; \\ , \\ .] " , PaletteIndex : : Punctuation ) ) ;
langDef . mCommentStart = " /* " ;
langDef . mCommentEnd = " */ " ;
2019-01-19 13:05:54 +00:00
langDef . mSingleLineComment = " // " ;
2018-08-17 12:31:33 +00:00
langDef . mCaseSensitive = true ;
2018-10-30 21:52:57 +00:00
langDef . mAutoIndentation = true ;
2018-08-17 12:31:33 +00:00
langDef . mName = " HLSL " ;
inited = true ;
}
return langDef ;
}
2018-10-30 21:52:57 +00:00
const TextEditor : : LanguageDefinition & TextEditor : : LanguageDefinition : : GLSL ( )
2018-08-17 12:31:33 +00:00
{
static bool inited = false ;
static LanguageDefinition langDef ;
if ( ! inited )
{
static const char * const keywords [ ] = {
" auto " , " break " , " case " , " char " , " const " , " continue " , " default " , " do " , " double " , " else " , " enum " , " extern " , " float " , " for " , " goto " , " if " , " inline " , " int " , " long " , " register " , " restrict " , " return " , " short " ,
" signed " , " sizeof " , " static " , " struct " , " switch " , " typedef " , " union " , " unsigned " , " void " , " volatile " , " while " , " _Alignas " , " _Alignof " , " _Atomic " , " _Bool " , " _Complex " , " _Generic " , " _Imaginary " ,
" _Noreturn " , " _Static_assert " , " _Thread_local "
} ;
for ( auto & k : keywords )
langDef . mKeywords . insert ( k ) ;
static const char * const identifiers [ ] = {
" abort " , " abs " , " acos " , " asin " , " atan " , " atexit " , " atof " , " atoi " , " atol " , " ceil " , " clock " , " cosh " , " ctime " , " div " , " exit " , " fabs " , " floor " , " fmod " , " getchar " , " getenv " , " isalnum " , " isalpha " , " isdigit " , " isgraph " ,
" ispunct " , " isspace " , " isupper " , " kbhit " , " log10 " , " log2 " , " log " , " memcmp " , " modf " , " pow " , " putchar " , " putenv " , " puts " , " rand " , " remove " , " rename " , " sinh " , " sqrt " , " srand " , " strcat " , " strcmp " , " strerror " , " time " , " tolower " , " toupper "
} ;
for ( auto & k : identifiers )
{
Identifier id ;
id . mDeclaration = " Built-in function " ;
langDef . mIdentifiers . insert ( std : : make_pair ( std : : string ( k ) , id ) ) ;
}
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [ \\ t]*#[ \\ t]*[a-zA-Z_]+ " , PaletteIndex : : Preprocessor ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " L? \\ \" ( \\ \\ .|[^ \\ \" ])* \\ \" " , PaletteIndex : : String ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " \\ ' \\ \\ ?[^ \\ '] \\ ' " , PaletteIndex : : CharLiteral ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [+-]?[0-9]+[Uu]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " 0[0-7]+[Uu]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " 0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [a-zA-Z_][a-zA-Z0-9_]* " , PaletteIndex : : Identifier ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [ \\ [ \\ ] \\ { \\ } \\ ! \\ % \\ ^ \\ & \\ * \\ ( \\ ) \\ - \\ + \\ = \\ ~ \\ | \\ < \\ > \\ ? \\ / \\ ; \\ , \\ .] " , PaletteIndex : : Punctuation ) ) ;
langDef . mCommentStart = " /* " ;
langDef . mCommentEnd = " */ " ;
2019-01-19 13:05:54 +00:00
langDef . mSingleLineComment = " // " ;
2018-08-17 12:31:33 +00:00
langDef . mCaseSensitive = true ;
2018-10-30 21:52:57 +00:00
langDef . mAutoIndentation = true ;
2018-08-17 12:31:33 +00:00
langDef . mName = " GLSL " ;
inited = true ;
}
return langDef ;
}
2018-10-30 21:52:57 +00:00
const TextEditor : : LanguageDefinition & TextEditor : : LanguageDefinition : : C ( )
2018-08-17 12:31:33 +00:00
{
static bool inited = false ;
static LanguageDefinition langDef ;
if ( ! inited )
{
static const char * const keywords [ ] = {
" auto " , " break " , " case " , " char " , " const " , " continue " , " default " , " do " , " double " , " else " , " enum " , " extern " , " float " , " for " , " goto " , " if " , " inline " , " int " , " long " , " register " , " restrict " , " return " , " short " ,
" signed " , " sizeof " , " static " , " struct " , " switch " , " typedef " , " union " , " unsigned " , " void " , " volatile " , " while " , " _Alignas " , " _Alignof " , " _Atomic " , " _Bool " , " _Complex " , " _Generic " , " _Imaginary " ,
" _Noreturn " , " _Static_assert " , " _Thread_local "
} ;
for ( auto & k : keywords )
langDef . mKeywords . insert ( k ) ;
static const char * const identifiers [ ] = {
" abort " , " abs " , " acos " , " asin " , " atan " , " atexit " , " atof " , " atoi " , " atol " , " ceil " , " clock " , " cosh " , " ctime " , " div " , " exit " , " fabs " , " floor " , " fmod " , " getchar " , " getenv " , " isalnum " , " isalpha " , " isdigit " , " isgraph " ,
" ispunct " , " isspace " , " isupper " , " kbhit " , " log10 " , " log2 " , " log " , " memcmp " , " modf " , " pow " , " putchar " , " putenv " , " puts " , " rand " , " remove " , " rename " , " sinh " , " sqrt " , " srand " , " strcat " , " strcmp " , " strerror " , " time " , " tolower " , " toupper "
} ;
for ( auto & k : identifiers )
{
Identifier id ;
id . mDeclaration = " Built-in function " ;
langDef . mIdentifiers . insert ( std : : make_pair ( std : : string ( k ) , id ) ) ;
}
2018-10-30 21:52:57 +00:00
langDef . mTokenize = [ ] ( const char * in_begin , const char * in_end , const char * & out_begin , const char * & out_end , PaletteIndex & paletteIndex ) - > bool
{
paletteIndex = PaletteIndex : : Max ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
while ( in_begin < in_end & & isblank ( * in_begin ) )
in_begin + + ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
if ( in_begin = = in_end )
{
out_begin = in_end ;
out_end = in_end ;
paletteIndex = PaletteIndex : : Default ;
}
else if ( TokenizeCStyleString ( in_begin , in_end , out_begin , out_end ) )
paletteIndex = PaletteIndex : : String ;
else if ( TokenizeCStyleCharacterLiteral ( in_begin , in_end , out_begin , out_end ) )
paletteIndex = PaletteIndex : : CharLiteral ;
else if ( TokenizeCStyleIdentifier ( in_begin , in_end , out_begin , out_end ) )
paletteIndex = PaletteIndex : : Identifier ;
else if ( TokenizeCStyleNumber ( in_begin , in_end , out_begin , out_end ) )
paletteIndex = PaletteIndex : : Number ;
else if ( TokenizeCStylePunctuation ( in_begin , in_end , out_begin , out_end ) )
paletteIndex = PaletteIndex : : Punctuation ;
2019-01-19 13:05:54 +00:00
2018-10-30 21:52:57 +00:00
return paletteIndex ! = PaletteIndex : : Max ;
} ;
2019-01-19 13:05:54 +00:00
2018-08-17 12:31:33 +00:00
langDef . mCommentStart = " /* " ;
langDef . mCommentEnd = " */ " ;
2019-01-19 13:05:54 +00:00
langDef . mSingleLineComment = " // " ;
2018-08-17 12:31:33 +00:00
langDef . mCaseSensitive = true ;
2018-10-30 21:52:57 +00:00
langDef . mAutoIndentation = true ;
2018-08-17 12:31:33 +00:00
langDef . mName = " C " ;
inited = true ;
}
return langDef ;
}
2018-10-30 21:52:57 +00:00
const TextEditor : : LanguageDefinition & TextEditor : : LanguageDefinition : : SQL ( )
2018-08-17 12:31:33 +00:00
{
static bool inited = false ;
static LanguageDefinition langDef ;
if ( ! inited )
{
static const char * const keywords [ ] = {
" ADD " , " EXCEPT " , " PERCENT " , " ALL " , " EXEC " , " PLAN " , " ALTER " , " EXECUTE " , " PRECISION " , " AND " , " EXISTS " , " PRIMARY " , " ANY " , " EXIT " , " PRINT " , " AS " , " FETCH " , " PROC " , " ASC " , " FILE " , " PROCEDURE " ,
" AUTHORIZATION " , " FILLFACTOR " , " PUBLIC " , " BACKUP " , " FOR " , " RAISERROR " , " BEGIN " , " FOREIGN " , " READ " , " BETWEEN " , " FREETEXT " , " READTEXT " , " BREAK " , " FREETEXTTABLE " , " RECONFIGURE " ,
" BROWSE " , " FROM " , " REFERENCES " , " BULK " , " FULL " , " REPLICATION " , " BY " , " FUNCTION " , " RESTORE " , " CASCADE " , " GOTO " , " RESTRICT " , " CASE " , " GRANT " , " RETURN " , " CHECK " , " GROUP " , " REVOKE " ,
" CHECKPOINT " , " HAVING " , " RIGHT " , " CLOSE " , " HOLDLOCK " , " ROLLBACK " , " CLUSTERED " , " IDENTITY " , " ROWCOUNT " , " COALESCE " , " IDENTITY_INSERT " , " ROWGUIDCOL " , " COLLATE " , " IDENTITYCOL " , " RULE " ,
" COLUMN " , " IF " , " SAVE " , " COMMIT " , " IN " , " SCHEMA " , " COMPUTE " , " INDEX " , " SELECT " , " CONSTRAINT " , " INNER " , " SESSION_USER " , " CONTAINS " , " INSERT " , " SET " , " CONTAINSTABLE " , " INTERSECT " , " SETUSER " ,
" CONTINUE " , " INTO " , " SHUTDOWN " , " CONVERT " , " IS " , " SOME " , " CREATE " , " JOIN " , " STATISTICS " , " CROSS " , " KEY " , " SYSTEM_USER " , " CURRENT " , " KILL " , " TABLE " , " CURRENT_DATE " , " LEFT " , " TEXTSIZE " ,
" CURRENT_TIME " , " LIKE " , " THEN " , " CURRENT_TIMESTAMP " , " LINENO " , " TO " , " CURRENT_USER " , " LOAD " , " TOP " , " CURSOR " , " NATIONAL " , " TRAN " , " DATABASE " , " NOCHECK " , " TRANSACTION " ,
" DBCC " , " NONCLUSTERED " , " TRIGGER " , " DEALLOCATE " , " NOT " , " TRUNCATE " , " DECLARE " , " NULL " , " TSEQUAL " , " DEFAULT " , " NULLIF " , " UNION " , " DELETE " , " OF " , " UNIQUE " , " DENY " , " OFF " , " UPDATE " ,
" DESC " , " OFFSETS " , " UPDATETEXT " , " DISK " , " ON " , " USE " , " DISTINCT " , " OPEN " , " USER " , " DISTRIBUTED " , " OPENDATASOURCE " , " VALUES " , " DOUBLE " , " OPENQUERY " , " VARYING " , " DROP " , " OPENROWSET " , " VIEW " ,
" DUMMY " , " OPENXML " , " WAITFOR " , " DUMP " , " OPTION " , " WHEN " , " ELSE " , " OR " , " WHERE " , " END " , " ORDER " , " WHILE " , " ERRLVL " , " OUTER " , " WITH " , " ESCAPE " , " OVER " , " WRITETEXT "
} ;
for ( auto & k : keywords )
langDef . mKeywords . insert ( k ) ;
static const char * const identifiers [ ] = {
" ABS " , " ACOS " , " ADD_MONTHS " , " ASCII " , " ASCIISTR " , " ASIN " , " ATAN " , " ATAN2 " , " AVG " , " BFILENAME " , " BIN_TO_NUM " , " BITAND " , " CARDINALITY " , " CASE " , " CAST " , " CEIL " ,
" CHARTOROWID " , " CHR " , " COALESCE " , " COMPOSE " , " CONCAT " , " CONVERT " , " CORR " , " COS " , " COSH " , " COUNT " , " COVAR_POP " , " COVAR_SAMP " , " CUME_DIST " , " CURRENT_DATE " ,
" CURRENT_TIMESTAMP " , " DBTIMEZONE " , " DECODE " , " DECOMPOSE " , " DENSE_RANK " , " DUMP " , " EMPTY_BLOB " , " EMPTY_CLOB " , " EXP " , " EXTRACT " , " FIRST_VALUE " , " FLOOR " , " FROM_TZ " , " GREATEST " ,
" GROUP_ID " , " HEXTORAW " , " INITCAP " , " INSTR " , " INSTR2 " , " INSTR4 " , " INSTRB " , " INSTRC " , " LAG " , " LAST_DAY " , " LAST_VALUE " , " LEAD " , " LEAST " , " LENGTH " , " LENGTH2 " , " LENGTH4 " ,
" LENGTHB " , " LENGTHC " , " LISTAGG " , " LN " , " LNNVL " , " LOCALTIMESTAMP " , " LOG " , " LOWER " , " LPAD " , " LTRIM " , " MAX " , " MEDIAN " , " MIN " , " MOD " , " MONTHS_BETWEEN " , " NANVL " , " NCHR " ,
" NEW_TIME " , " NEXT_DAY " , " NTH_VALUE " , " NULLIF " , " NUMTODSINTERVAL " , " NUMTOYMINTERVAL " , " NVL " , " NVL2 " , " POWER " , " RANK " , " RAWTOHEX " , " REGEXP_COUNT " , " REGEXP_INSTR " ,
" REGEXP_REPLACE " , " REGEXP_SUBSTR " , " REMAINDER " , " REPLACE " , " ROUND " , " ROWNUM " , " RPAD " , " RTRIM " , " SESSIONTIMEZONE " , " SIGN " , " SIN " , " SINH " ,
" SOUNDEX " , " SQRT " , " STDDEV " , " SUBSTR " , " SUM " , " SYS_CONTEXT " , " SYSDATE " , " SYSTIMESTAMP " , " TAN " , " TANH " , " TO_CHAR " , " TO_CLOB " , " TO_DATE " , " TO_DSINTERVAL " , " TO_LOB " ,
" TO_MULTI_BYTE " , " TO_NCLOB " , " TO_NUMBER " , " TO_SINGLE_BYTE " , " TO_TIMESTAMP " , " TO_TIMESTAMP_TZ " , " TO_YMINTERVAL " , " TRANSLATE " , " TRIM " , " TRUNC " , " TZ_OFFSET " , " UID " , " UPPER " ,
" USER " , " USERENV " , " VAR_POP " , " VAR_SAMP " , " VARIANCE " , " VSIZE "
} ;
for ( auto & k : identifiers )
{
Identifier id ;
id . mDeclaration = " Built-in function " ;
langDef . mIdentifiers . insert ( std : : make_pair ( std : : string ( k ) , id ) ) ;
}
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " L? \\ \" ( \\ \\ .|[^ \\ \" ])* \\ \" " , PaletteIndex : : String ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " \\ \' [^ \\ \' ]* \\ \' " , PaletteIndex : : String ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [+-]?[0-9]+[Uu]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " 0[0-7]+[Uu]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " 0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [a-zA-Z_][a-zA-Z0-9_]* " , PaletteIndex : : Identifier ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [ \\ [ \\ ] \\ { \\ } \\ ! \\ % \\ ^ \\ & \\ * \\ ( \\ ) \\ - \\ + \\ = \\ ~ \\ | \\ < \\ > \\ ? \\ / \\ ; \\ , \\ .] " , PaletteIndex : : Punctuation ) ) ;
langDef . mCommentStart = " /* " ;
langDef . mCommentEnd = " */ " ;
2019-01-19 13:05:54 +00:00
langDef . mSingleLineComment = " // " ;
2018-08-17 12:31:33 +00:00
langDef . mCaseSensitive = false ;
2018-10-30 21:52:57 +00:00
langDef . mAutoIndentation = false ;
2018-08-17 12:31:33 +00:00
langDef . mName = " SQL " ;
inited = true ;
}
return langDef ;
}
2018-10-30 21:52:57 +00:00
const TextEditor : : LanguageDefinition & TextEditor : : LanguageDefinition : : AngelScript ( )
2018-08-17 12:31:33 +00:00
{
static bool inited = false ;
static LanguageDefinition langDef ;
if ( ! inited )
{
static const char * const keywords [ ] = {
" and " , " abstract " , " auto " , " bool " , " break " , " case " , " cast " , " class " , " const " , " continue " , " default " , " do " , " double " , " else " , " enum " , " false " , " final " , " float " , " for " ,
" from " , " funcdef " , " function " , " get " , " if " , " import " , " in " , " inout " , " int " , " interface " , " int8 " , " int16 " , " int32 " , " int64 " , " is " , " mixin " , " namespace " , " not " ,
" null " , " or " , " out " , " override " , " private " , " protected " , " return " , " set " , " shared " , " super " , " switch " , " this " , " true " , " typedef " , " uint " , " uint8 " , " uint16 " , " uint32 " ,
" uint64 " , " void " , " while " , " xor "
} ;
for ( auto & k : keywords )
langDef . mKeywords . insert ( k ) ;
static const char * const identifiers [ ] = {
" cos " , " sin " , " tab " , " acos " , " asin " , " atan " , " atan2 " , " cosh " , " sinh " , " tanh " , " log " , " log10 " , " pow " , " sqrt " , " abs " , " ceil " , " floor " , " fraction " , " closeTo " , " fpFromIEEE " , " fpToIEEE " ,
" complex " , " opEquals " , " opAddAssign " , " opSubAssign " , " opMulAssign " , " opDivAssign " , " opAdd " , " opSub " , " opMul " , " opDiv "
} ;
for ( auto & k : identifiers )
{
Identifier id ;
id . mDeclaration = " Built-in function " ;
langDef . mIdentifiers . insert ( std : : make_pair ( std : : string ( k ) , id ) ) ;
}
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " L? \\ \" ( \\ \\ .|[^ \\ \" ])* \\ \" " , PaletteIndex : : String ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " \\ ' \\ \\ ?[^ \\ '] \\ ' " , PaletteIndex : : String ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [+-]?[0-9]+[Uu]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " 0[0-7]+[Uu]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " 0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [a-zA-Z_][a-zA-Z0-9_]* " , PaletteIndex : : Identifier ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [ \\ [ \\ ] \\ { \\ } \\ ! \\ % \\ ^ \\ & \\ * \\ ( \\ ) \\ - \\ + \\ = \\ ~ \\ | \\ < \\ > \\ ? \\ / \\ ; \\ , \\ .] " , PaletteIndex : : Punctuation ) ) ;
langDef . mCommentStart = " /* " ;
langDef . mCommentEnd = " */ " ;
2019-01-19 13:05:54 +00:00
langDef . mSingleLineComment = " // " ;
2018-08-17 12:31:33 +00:00
langDef . mCaseSensitive = true ;
2018-10-30 21:52:57 +00:00
langDef . mAutoIndentation = true ;
2018-08-17 12:31:33 +00:00
langDef . mName = " AngelScript " ;
inited = true ;
}
return langDef ;
}
2018-10-30 21:52:57 +00:00
const TextEditor : : LanguageDefinition & TextEditor : : LanguageDefinition : : Lua ( )
2018-08-17 12:31:33 +00:00
{
static bool inited = false ;
static LanguageDefinition langDef ;
if ( ! inited )
{
static const char * const keywords [ ] = {
" and " , " break " , " do " , " " , " else " , " elseif " , " end " , " false " , " for " , " function " , " if " , " in " , " " , " local " , " nil " , " not " , " or " , " repeat " , " return " , " then " , " true " , " until " , " while "
} ;
for ( auto & k : keywords )
langDef . mKeywords . insert ( k ) ;
static const char * const identifiers [ ] = {
" assert " , " collectgarbage " , " dofile " , " error " , " getmetatable " , " ipairs " , " loadfile " , " load " , " loadstring " , " next " , " pairs " , " pcall " , " print " , " rawequal " , " rawlen " , " rawget " , " rawset " ,
2019-01-19 13:05:54 +00:00
" select " , " setmetatable " , " tonumber " , " tostring " , " type " , " xpcall " , " _G " , " _VERSION " , " arshift " , " band " , " bnot " , " bor " , " bxor " , " btest " , " extract " , " lrotate " , " lshift " , " replace " ,
" rrotate " , " rshift " , " create " , " resume " , " running " , " status " , " wrap " , " yield " , " isyieldable " , " debug " , " getuservalue " , " gethook " , " getinfo " , " getlocal " , " getregistry " , " getmetatable " ,
" getupvalue " , " upvaluejoin " , " upvalueid " , " setuservalue " , " sethook " , " setlocal " , " setmetatable " , " setupvalue " , " traceback " , " close " , " flush " , " input " , " lines " , " open " , " output " , " popen " ,
2018-08-17 12:31:33 +00:00
" read " , " tmpfile " , " type " , " write " , " close " , " flush " , " lines " , " read " , " seek " , " setvbuf " , " write " , " __gc " , " __tostring " , " abs " , " acos " , " asin " , " atan " , " ceil " , " cos " , " deg " , " exp " , " tointeger " ,
" floor " , " fmod " , " ult " , " log " , " max " , " min " , " modf " , " rad " , " random " , " randomseed " , " sin " , " sqrt " , " string " , " tan " , " type " , " atan2 " , " cosh " , " sinh " , " tanh " ,
" pow " , " frexp " , " ldexp " , " log10 " , " pi " , " huge " , " maxinteger " , " mininteger " , " loadlib " , " searchpath " , " seeall " , " preload " , " cpath " , " path " , " searchers " , " loaded " , " module " , " require " , " clock " ,
" date " , " difftime " , " execute " , " exit " , " getenv " , " remove " , " rename " , " setlocale " , " time " , " tmpname " , " byte " , " char " , " dump " , " find " , " format " , " gmatch " , " gsub " , " len " , " lower " , " match " , " rep " ,
" reverse " , " sub " , " upper " , " pack " , " packsize " , " unpack " , " concat " , " maxn " , " insert " , " pack " , " unpack " , " remove " , " move " , " sort " , " offset " , " codepoint " , " char " , " len " , " codes " , " charpattern " ,
" coroutine " , " table " , " io " , " os " , " string " , " utf8 " , " bit32 " , " math " , " debug " , " package "
} ;
for ( auto & k : identifiers )
{
Identifier id ;
id . mDeclaration = " Built-in function " ;
langDef . mIdentifiers . insert ( std : : make_pair ( std : : string ( k ) , id ) ) ;
}
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " L? \\ \" ( \\ \\ .|[^ \\ \" ])* \\ \" " , PaletteIndex : : String ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " \\ \' [^ \\ \' ]* \\ \' " , PaletteIndex : : String ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " 0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [+-]?[0-9]+[Uu]?[lL]?[lL]? " , PaletteIndex : : Number ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [a-zA-Z_][a-zA-Z0-9_]* " , PaletteIndex : : Identifier ) ) ;
langDef . mTokenRegexStrings . push_back ( std : : make_pair < std : : string , PaletteIndex > ( " [ \\ [ \\ ] \\ { \\ } \\ ! \\ % \\ ^ \\ & \\ * \\ ( \\ ) \\ - \\ + \\ = \\ ~ \\ | \\ < \\ > \\ ? \\ / \\ ; \\ , \\ .] " , PaletteIndex : : Punctuation ) ) ;
2019-01-19 13:05:54 +00:00
langDef . mCommentStart = " --[[ " ;
langDef . mCommentEnd = " ]] " ;
langDef . mSingleLineComment = " -- " ;
2018-08-17 12:31:33 +00:00
langDef . mCaseSensitive = true ;
2018-10-30 21:52:57 +00:00
langDef . mAutoIndentation = false ;
2018-08-17 12:31:33 +00:00
langDef . mName = " Lua " ;
inited = true ;
}
return langDef ;
}
2018-08-17 12:38:57 +00:00
2019-01-19 13:05:54 +00:00
}