TextEditor.cpp 94 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162
  1. //https://github.com/BalazsJako/ImGuiColorTextEdit
  2. #include <algorithm>
  3. #include <chrono>
  4. #include <string>
  5. #include <regex>
  6. #include <cmath>
  7. #include "TextEditor.h"
  8. #define IMGUI_DEFINE_MATH_OPERATORS
  9. #include "imgui.h" // for imGui::GetCurrentWindow()
  10. // TODO
  11. // - multiline comments vs single-line: latter is blocking start of a ML
  12. template<class InputIt1, class InputIt2, class BinaryPredicate>
  13. bool equals(InputIt1 first1, InputIt1 last1,
  14. InputIt2 first2, InputIt2 last2, BinaryPredicate p)
  15. {
  16. for (; first1 != last1 && first2 != last2; ++first1, ++first2)
  17. {
  18. if (!p(*first1, *first2))
  19. return false;
  20. }
  21. return first1 == last1 && first2 == last2;
  22. }
  23. TextEditor::TextEditor()
  24. : mLineSpacing(1.0f)
  25. , mUndoIndex(0)
  26. , mTabSize(4)
  27. , mOverwrite(false)
  28. , mReadOnly(false)
  29. , mWithinRender(false)
  30. , mScrollToCursor(false)
  31. , mScrollToTop(false)
  32. , mTextChanged(false)
  33. , mColorizerEnabled(true)
  34. , mTextStart(20.0f)
  35. , mLeftMargin(10)
  36. , mCursorPositionChanged(false)
  37. , mColorRangeMin(0)
  38. , mColorRangeMax(0)
  39. , mSelectionMode(SelectionMode::Normal)
  40. , mCheckComments(true)
  41. , mLastClick(-1.0f)
  42. , mHandleKeyboardInputs(true)
  43. , mHandleMouseInputs(true)
  44. , mIgnoreImGuiChild(false)
  45. , mShowWhitespaces(true)
  46. , mStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
  47. {
  48. SetPalette(GetDarkPalette());
  49. //SetLanguageDefinition(LanguageDefinition::HLSL());
  50. mLines.push_back(Line());
  51. }
  52. TextEditor::~TextEditor()
  53. {
  54. }
  55. void TextEditor::SetLanguageDefinition(const LanguageDefinition & aLanguageDef)
  56. {
  57. mLanguageDefinition = aLanguageDef;
  58. mRegexList.clear();
  59. for (auto& r : mLanguageDefinition.mTokenRegexStrings)
  60. mRegexList.push_back(std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second));
  61. Colorize();
  62. }
  63. void TextEditor::SetPalette(const Palette & aValue)
  64. {
  65. mPaletteBase = aValue;
  66. }
  67. std::string TextEditor::GetText(const Coordinates & aStart, const Coordinates & aEnd) const
  68. {
  69. std::string result;
  70. auto lstart = aStart.mLine;
  71. auto lend = aEnd.mLine;
  72. auto istart = GetCharacterIndex(aStart);
  73. auto iend = GetCharacterIndex(aEnd);
  74. size_t s = 0;
  75. for (size_t i = lstart; i < lend; i++)
  76. s += mLines[i].size();
  77. result.reserve(s + s / 8);
  78. while (istart < iend || lstart < lend)
  79. {
  80. if (lstart >= (int)mLines.size())
  81. break;
  82. auto& line = mLines[lstart];
  83. if (istart < (int)line.size())
  84. {
  85. result += line[istart].mChar;
  86. istart++;
  87. }
  88. else
  89. {
  90. istart = 0;
  91. ++lstart;
  92. result += '\n';
  93. }
  94. }
  95. return result;
  96. }
  97. TextEditor::Coordinates TextEditor::GetActualCursorCoordinates() const
  98. {
  99. return SanitizeCoordinates(mState.mCursorPosition);
  100. }
  101. TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates & aValue) const
  102. {
  103. auto line = aValue.mLine;
  104. auto column = aValue.mColumn;
  105. if (line >= (int)mLines.size())
  106. {
  107. if (mLines.empty())
  108. {
  109. line = 0;
  110. column = 0;
  111. }
  112. else
  113. {
  114. line = (int)mLines.size() - 1;
  115. column = GetLineMaxColumn(line);
  116. }
  117. return Coordinates(line, column);
  118. }
  119. else
  120. {
  121. column = mLines.empty() ? 0 : std::min(column, GetLineMaxColumn(line));
  122. return Coordinates(line, column);
  123. }
  124. }
  125. // https://en.wikipedia.org/wiki/UTF-8
  126. // We assume that the char is a standalone character (<128) or a leading byte of an UTF-8 code sequence (non-10xxxxxx code)
  127. static int UTF8CharLength(TextEditor::Char c)
  128. {
  129. if ((c & 0xFE) == 0xFC)
  130. return 6;
  131. if ((c & 0xFC) == 0xF8)
  132. return 5;
  133. if ((c & 0xF8) == 0xF0)
  134. return 4;
  135. else if ((c & 0xF0) == 0xE0)
  136. return 3;
  137. else if ((c & 0xE0) == 0xC0)
  138. return 2;
  139. return 1;
  140. }
  141. // "Borrowed" from ImGui source
  142. static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
  143. {
  144. if (c < 0x80)
  145. {
  146. buf[0] = (char)c;
  147. return 1;
  148. }
  149. if (c < 0x800)
  150. {
  151. if (buf_size < 2) return 0;
  152. buf[0] = (char)(0xc0 + (c >> 6));
  153. buf[1] = (char)(0x80 + (c & 0x3f));
  154. return 2;
  155. }
  156. if (c >= 0xdc00 && c < 0xe000)
  157. {
  158. return 0;
  159. }
  160. if (c >= 0xd800 && c < 0xdc00)
  161. {
  162. if (buf_size < 4) return 0;
  163. buf[0] = (char)(0xf0 + (c >> 18));
  164. buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
  165. buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
  166. buf[3] = (char)(0x80 + ((c) & 0x3f));
  167. return 4;
  168. }
  169. //else if (c < 0x10000)
  170. {
  171. if (buf_size < 3) return 0;
  172. buf[0] = (char)(0xe0 + (c >> 12));
  173. buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
  174. buf[2] = (char)(0x80 + ((c) & 0x3f));
  175. return 3;
  176. }
  177. }
  178. void TextEditor::Advance(Coordinates & aCoordinates) const
  179. {
  180. if (aCoordinates.mLine < (int)mLines.size())
  181. {
  182. auto& line = mLines[aCoordinates.mLine];
  183. auto cindex = GetCharacterIndex(aCoordinates);
  184. if (cindex + 1 < (int)line.size())
  185. {
  186. auto delta = UTF8CharLength(line[cindex].mChar);
  187. cindex = std::min(cindex + delta, (int)line.size() - 1);
  188. }
  189. else
  190. {
  191. ++aCoordinates.mLine;
  192. cindex = 0;
  193. }
  194. aCoordinates.mColumn = GetCharacterColumn(aCoordinates.mLine, cindex);
  195. }
  196. }
  197. void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEnd)
  198. {
  199. assert(aEnd >= aStart);
  200. assert(!mReadOnly);
  201. //printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn);
  202. if (aEnd == aStart)
  203. return;
  204. auto start = GetCharacterIndex(aStart);
  205. auto end = GetCharacterIndex(aEnd);
  206. if (aStart.mLine == aEnd.mLine)
  207. {
  208. auto& line = mLines[aStart.mLine];
  209. auto n = GetLineMaxColumn(aStart.mLine);
  210. if (aEnd.mColumn >= n)
  211. line.erase(line.begin() + start, line.end());
  212. else
  213. line.erase(line.begin() + start, line.begin() + end);
  214. }
  215. else
  216. {
  217. auto& firstLine = mLines[aStart.mLine];
  218. auto& lastLine = mLines[aEnd.mLine];
  219. firstLine.erase(firstLine.begin() + start, firstLine.end());
  220. lastLine.erase(lastLine.begin(), lastLine.begin() + end);
  221. if (aStart.mLine < aEnd.mLine)
  222. firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end());
  223. if (aStart.mLine < aEnd.mLine)
  224. RemoveLine(aStart.mLine + 1, aEnd.mLine + 1);
  225. }
  226. mTextChanged = true;
  227. }
  228. int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValue)
  229. {
  230. assert(!mReadOnly);
  231. int cindex = GetCharacterIndex(aWhere);
  232. int totalLines = 0;
  233. while (*aValue != '\0')
  234. {
  235. assert(!mLines.empty());
  236. if (*aValue == '\r')
  237. {
  238. // skip
  239. ++aValue;
  240. }
  241. else if (*aValue == '\n')
  242. {
  243. if (cindex < (int)mLines[aWhere.mLine].size())
  244. {
  245. auto& newLine = InsertLine(aWhere.mLine + 1);
  246. auto& line = mLines[aWhere.mLine];
  247. newLine.insert(newLine.begin(), line.begin() + cindex, line.end());
  248. line.erase(line.begin() + cindex, line.end());
  249. }
  250. else
  251. {
  252. InsertLine(aWhere.mLine + 1);
  253. }
  254. ++aWhere.mLine;
  255. aWhere.mColumn = 0;
  256. cindex = 0;
  257. ++totalLines;
  258. ++aValue;
  259. }
  260. else
  261. {
  262. auto& line = mLines[aWhere.mLine];
  263. auto d = UTF8CharLength(*aValue);
  264. while (d-- > 0 && *aValue != '\0')
  265. line.insert(line.begin() + cindex++, Glyph(*aValue++, PaletteIndex::Default));
  266. ++aWhere.mColumn;
  267. }
  268. mTextChanged = true;
  269. }
  270. return totalLines;
  271. }
  272. void TextEditor::AddUndo(UndoRecord& aValue)
  273. {
  274. assert(!mReadOnly);
  275. //printf("AddUndo: (@%d.%d) +\'%s' [%d.%d .. %d.%d], -\'%s', [%d.%d .. %d.%d] (@%d.%d)\n",
  276. // aValue.mBefore.mCursorPosition.mLine, aValue.mBefore.mCursorPosition.mColumn,
  277. // aValue.mAdded.c_str(), aValue.mAddedStart.mLine, aValue.mAddedStart.mColumn, aValue.mAddedEnd.mLine, aValue.mAddedEnd.mColumn,
  278. // aValue.mRemoved.c_str(), aValue.mRemovedStart.mLine, aValue.mRemovedStart.mColumn, aValue.mRemovedEnd.mLine, aValue.mRemovedEnd.mColumn,
  279. // aValue.mAfter.mCursorPosition.mLine, aValue.mAfter.mCursorPosition.mColumn
  280. // );
  281. mUndoBuffer.resize((size_t)(mUndoIndex + 1));
  282. mUndoBuffer.back() = aValue;
  283. ++mUndoIndex;
  284. }
  285. TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPosition) const
  286. {
  287. ImVec2 origin = ImGui::GetCursorScreenPos();
  288. ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y);
  289. int lineNo = std::max(0, (int)floor(local.y / mCharAdvance.y));
  290. int columnCoord = 0;
  291. if (lineNo >= 0 && lineNo < (int)mLines.size())
  292. {
  293. auto& line = mLines.at(lineNo);
  294. int columnIndex = 0;
  295. float columnX = 0.0f;
  296. while ((size_t)columnIndex < line.size())
  297. {
  298. float columnWidth = 0.0f;
  299. if (line[columnIndex].mChar == '\t')
  300. {
  301. float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x;
  302. float oldX = columnX;
  303. float newColumnX = (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);
  304. columnWidth = newColumnX - oldX;
  305. if (mTextStart + columnX + columnWidth * 0.5f > local.x)
  306. break;
  307. columnX = newColumnX;
  308. columnCoord = (columnCoord / mTabSize) * mTabSize + mTabSize;
  309. columnIndex++;
  310. }
  311. else
  312. {
  313. char buf[7];
  314. auto d = UTF8CharLength(line[columnIndex].mChar);
  315. int i = 0;
  316. while (i < 6 && d-- > 0)
  317. buf[i++] = line[columnIndex++].mChar;
  318. buf[i] = '\0';
  319. columnWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x;
  320. if (mTextStart + columnX + columnWidth * 0.5f > local.x)
  321. break;
  322. columnX += columnWidth;
  323. columnCoord++;
  324. }
  325. }
  326. }
  327. return SanitizeCoordinates(Coordinates(lineNo, columnCoord));
  328. }
  329. TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates & aFrom) const
  330. {
  331. Coordinates at = aFrom;
  332. if (at.mLine >= (int)mLines.size())
  333. return at;
  334. auto& line = mLines[at.mLine];
  335. auto cindex = GetCharacterIndex(at);
  336. if (cindex >= (int)line.size())
  337. return at;
  338. while (cindex > 0 && isspace(line[cindex].mChar))
  339. --cindex;
  340. auto cstart = (PaletteIndex)line[cindex].mColorIndex;
  341. while (cindex > 0)
  342. {
  343. auto c = line[cindex].mChar;
  344. if ((c & 0xC0) != 0x80) // not UTF code sequence 10xxxxxx
  345. {
  346. if (c <= 32 && isspace(c))
  347. {
  348. cindex++;
  349. break;
  350. }
  351. if (cstart != (PaletteIndex)line[size_t(cindex - 1)].mColorIndex)
  352. break;
  353. }
  354. --cindex;
  355. }
  356. return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex));
  357. }
  358. TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates & aFrom) const
  359. {
  360. Coordinates at = aFrom;
  361. if (at.mLine >= (int)mLines.size())
  362. return at;
  363. auto& line = mLines[at.mLine];
  364. auto cindex = GetCharacterIndex(at);
  365. if (cindex >= (int)line.size())
  366. return at;
  367. bool prevspace = (bool)isspace(line[cindex].mChar);
  368. auto cstart = (PaletteIndex)line[cindex].mColorIndex;
  369. while (cindex < (int)line.size())
  370. {
  371. auto c = line[cindex].mChar;
  372. auto d = UTF8CharLength(c);
  373. if (cstart != (PaletteIndex)line[cindex].mColorIndex)
  374. break;
  375. if (prevspace != !!isspace(c))
  376. {
  377. if (isspace(c))
  378. while (cindex < (int)line.size() && isspace(line[cindex].mChar))
  379. ++cindex;
  380. break;
  381. }
  382. cindex += d;
  383. }
  384. return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex));
  385. }
  386. TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates & aFrom) const
  387. {
  388. Coordinates at = aFrom;
  389. if (at.mLine >= (int)mLines.size())
  390. return at;
  391. // skip to the next non-word character
  392. auto cindex = GetCharacterIndex(aFrom);
  393. bool isword = false;
  394. bool skip = false;
  395. if (cindex < (int)mLines[at.mLine].size())
  396. {
  397. auto& line = mLines[at.mLine];
  398. isword = isalnum(line[cindex].mChar);
  399. skip = isword;
  400. }
  401. while (!isword || skip)
  402. {
  403. if (at.mLine >= mLines.size())
  404. {
  405. auto l = std::max(0, (int) mLines.size() - 1);
  406. return Coordinates(l, GetLineMaxColumn(l));
  407. }
  408. auto& line = mLines[at.mLine];
  409. if (cindex < (int)line.size())
  410. {
  411. isword = isalnum(line[cindex].mChar);
  412. if (isword && !skip)
  413. return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex));
  414. if (!isword)
  415. skip = false;
  416. cindex++;
  417. }
  418. else
  419. {
  420. cindex = 0;
  421. ++at.mLine;
  422. skip = false;
  423. isword = false;
  424. }
  425. }
  426. return at;
  427. }
  428. int TextEditor::GetCharacterIndex(const Coordinates& aCoordinates) const
  429. {
  430. if (aCoordinates.mLine >= mLines.size())
  431. return -1;
  432. auto& line = mLines[aCoordinates.mLine];
  433. int c = 0;
  434. int i = 0;
  435. for (; i < line.size() && c < aCoordinates.mColumn;)
  436. {
  437. if (line[i].mChar == '\t')
  438. c = (c / mTabSize) * mTabSize + mTabSize;
  439. else
  440. ++c;
  441. i += UTF8CharLength(line[i].mChar);
  442. }
  443. return i;
  444. }
  445. int TextEditor::GetCharacterColumn(int aLine, int aIndex) const
  446. {
  447. if (aLine >= mLines.size())
  448. return 0;
  449. auto& line = mLines[aLine];
  450. int col = 0;
  451. int i = 0;
  452. while (i < aIndex && i < (int)line.size())
  453. {
  454. auto c = line[i].mChar;
  455. i += UTF8CharLength(c);
  456. if (c == '\t')
  457. col = (col / mTabSize) * mTabSize + mTabSize;
  458. else
  459. col++;
  460. }
  461. return col;
  462. }
  463. int TextEditor::GetLineCharacterCount(int aLine) const
  464. {
  465. if (aLine >= mLines.size())
  466. return 0;
  467. auto& line = mLines[aLine];
  468. int c = 0;
  469. for (unsigned i = 0; i < line.size(); c++)
  470. i += UTF8CharLength(line[i].mChar);
  471. return c;
  472. }
  473. int TextEditor::GetLineMaxColumn(int aLine) const
  474. {
  475. if (aLine >= mLines.size())
  476. return 0;
  477. auto& line = mLines[aLine];
  478. int col = 0;
  479. for (unsigned i = 0; i < line.size(); )
  480. {
  481. auto c = line[i].mChar;
  482. if (c == '\t')
  483. col = (col / mTabSize) * mTabSize + mTabSize;
  484. else
  485. col++;
  486. i += UTF8CharLength(c);
  487. }
  488. return col;
  489. }
  490. bool TextEditor::IsOnWordBoundary(const Coordinates & aAt) const
  491. {
  492. if (aAt.mLine >= (int)mLines.size() || aAt.mColumn == 0)
  493. return true;
  494. auto& line = mLines[aAt.mLine];
  495. auto cindex = GetCharacterIndex(aAt);
  496. if (cindex >= (int)line.size())
  497. return true;
  498. if (mColorizerEnabled)
  499. return line[cindex].mColorIndex != line[size_t(cindex - 1)].mColorIndex;
  500. return isspace(line[cindex].mChar) != isspace(line[cindex - 1].mChar);
  501. }
  502. void TextEditor::RemoveLine(int aStart, int aEnd)
  503. {
  504. assert(!mReadOnly);
  505. assert(aEnd >= aStart);
  506. assert(mLines.size() > (size_t)(aEnd - aStart));
  507. ErrorMarkers etmp;
  508. for (auto& i : mErrorMarkers)
  509. {
  510. ErrorMarkers::value_type e(i.first >= aStart ? i.first - 1 : i.first, i.second);
  511. if (e.first >= aStart && e.first <= aEnd)
  512. continue;
  513. etmp.insert(e);
  514. }
  515. mErrorMarkers = std::move(etmp);
  516. Breakpoints btmp;
  517. for (auto i : mBreakpoints)
  518. {
  519. if (i >= aStart && i <= aEnd)
  520. continue;
  521. btmp.insert(i >= aStart ? i - 1 : i);
  522. }
  523. mBreakpoints = std::move(btmp);
  524. mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd);
  525. assert(!mLines.empty());
  526. mTextChanged = true;
  527. }
  528. void TextEditor::RemoveLine(int aIndex)
  529. {
  530. assert(!mReadOnly);
  531. assert(mLines.size() > 1);
  532. ErrorMarkers etmp;
  533. for (auto& i : mErrorMarkers)
  534. {
  535. ErrorMarkers::value_type e(i.first > aIndex ? i.first - 1 : i.first, i.second);
  536. if (e.first - 1 == aIndex)
  537. continue;
  538. etmp.insert(e);
  539. }
  540. mErrorMarkers = std::move(etmp);
  541. Breakpoints btmp;
  542. for (auto i : mBreakpoints)
  543. {
  544. if (i == aIndex)
  545. continue;
  546. btmp.insert(i >= aIndex ? i - 1 : i);
  547. }
  548. mBreakpoints = std::move(btmp);
  549. mLines.erase(mLines.begin() + aIndex);
  550. assert(!mLines.empty());
  551. mTextChanged = true;
  552. }
  553. TextEditor::Line& TextEditor::InsertLine(int aIndex)
  554. {
  555. assert(!mReadOnly);
  556. auto& result = *mLines.insert(mLines.begin() + aIndex, Line());
  557. ErrorMarkers etmp;
  558. for (auto& i : mErrorMarkers)
  559. etmp.insert(ErrorMarkers::value_type(i.first >= aIndex ? i.first + 1 : i.first, i.second));
  560. mErrorMarkers = std::move(etmp);
  561. Breakpoints btmp;
  562. for (auto i : mBreakpoints)
  563. btmp.insert(i >= aIndex ? i + 1 : i);
  564. mBreakpoints = std::move(btmp);
  565. return result;
  566. }
  567. std::string TextEditor::GetWordUnderCursor() const
  568. {
  569. auto c = GetCursorPosition();
  570. return GetWordAt(c);
  571. }
  572. std::string TextEditor::GetWordAt(const Coordinates & aCoords) const
  573. {
  574. auto start = FindWordStart(aCoords);
  575. auto end = FindWordEnd(aCoords);
  576. std::string r;
  577. auto istart = GetCharacterIndex(start);
  578. auto iend = GetCharacterIndex(end);
  579. for (auto it = istart; it < iend; ++it)
  580. r.push_back(mLines[aCoords.mLine][it].mChar);
  581. return r;
  582. }
  583. ImU32 TextEditor::GetGlyphColor(const Glyph & aGlyph) const
  584. {
  585. if (!mColorizerEnabled)
  586. return mPalette[(int)PaletteIndex::Default];
  587. if (aGlyph.mComment)
  588. return mPalette[(int)PaletteIndex::Comment];
  589. if (aGlyph.mMultiLineComment)
  590. return mPalette[(int)PaletteIndex::MultiLineComment];
  591. auto const color = mPalette[(int)aGlyph.mColorIndex];
  592. if (aGlyph.mPreprocessor)
  593. {
  594. const auto ppcolor = mPalette[(int)PaletteIndex::Preprocessor];
  595. const int c0 = ((ppcolor & 0xff) + (color & 0xff)) / 2;
  596. const int c1 = (((ppcolor >> 8) & 0xff) + ((color >> 8) & 0xff)) / 2;
  597. const int c2 = (((ppcolor >> 16) & 0xff) + ((color >> 16) & 0xff)) / 2;
  598. const int c3 = (((ppcolor >> 24) & 0xff) + ((color >> 24) & 0xff)) / 2;
  599. return ImU32(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24));
  600. }
  601. return color;
  602. }
  603. void TextEditor::HandleKeyboardInputs()
  604. {
  605. ImGuiIO& io = ImGui::GetIO();
  606. auto shift = io.KeyShift;
  607. auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
  608. auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
  609. if (ImGui::IsWindowFocused())
  610. {
  611. if (ImGui::IsWindowHovered())
  612. ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput);
  613. //ImGui::CaptureKeyboardFromApp(true);
  614. io.WantCaptureKeyboard = true;
  615. io.WantTextInput = true;
  616. if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Z)))
  617. Undo();
  618. else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace)))
  619. Undo();
  620. else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y)))
  621. Redo();
  622. else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
  623. MoveUp(1, shift);
  624. else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
  625. MoveDown(1, shift);
  626. else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)))
  627. MoveLeft(1, shift, ctrl);
  628. else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)))
  629. MoveRight(1, shift, ctrl);
  630. else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)))
  631. MoveUp(GetPageSize() - 4, shift);
  632. else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)))
  633. MoveDown(GetPageSize() - 4, shift);
  634. else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
  635. MoveTop(shift);
  636. else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
  637. MoveBottom(shift);
  638. else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
  639. MoveHome(shift);
  640. else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
  641. MoveEnd(shift);
  642. else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
  643. Delete();
  644. else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace)))
  645. Backspace();
  646. else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
  647. mOverwrite ^= true;
  648. else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
  649. Copy();
  650. else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
  651. Copy();
  652. else if (!IsReadOnly() && !ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
  653. Paste();
  654. else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V)))
  655. Paste();
  656. else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_X)))
  657. Cut();
  658. else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
  659. Cut();
  660. else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_A)))
  661. SelectAll();
  662. else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter)))
  663. EnterCharacter('\n', false);
  664. else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab)))
  665. EnterCharacter('\t', shift);
  666. if (!IsReadOnly() && !io.InputQueueCharacters.empty())
  667. {
  668. for (int i = 0; i < io.InputQueueCharacters.Size; i++)
  669. {
  670. auto c = io.InputQueueCharacters[i];
  671. if (c != 0 && (c == '\n' || c >= 32))
  672. EnterCharacter(c, shift);
  673. }
  674. io.InputQueueCharacters.resize(0);
  675. }
  676. }
  677. }
  678. void TextEditor::HandleMouseInputs()
  679. {
  680. ImGuiIO& io = ImGui::GetIO();
  681. auto shift = io.KeyShift;
  682. auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
  683. auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
  684. if (ImGui::IsWindowHovered())
  685. {
  686. if (!shift && !alt)
  687. {
  688. auto click = ImGui::IsMouseClicked(0);
  689. auto doubleClick = ImGui::IsMouseDoubleClicked(0);
  690. auto t = ImGui::GetTime();
  691. auto tripleClick = click && !doubleClick && (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime);
  692. /*
  693. Left mouse button triple click
  694. */
  695. if (tripleClick)
  696. {
  697. if (!ctrl)
  698. {
  699. mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());
  700. mSelectionMode = SelectionMode::Line;
  701. SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);
  702. }
  703. mLastClick = -1.0f;
  704. }
  705. /*
  706. Left mouse button double click
  707. */
  708. else if (doubleClick)
  709. {
  710. if (!ctrl)
  711. {
  712. mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());
  713. if (mSelectionMode == SelectionMode::Line)
  714. mSelectionMode = SelectionMode::Normal;
  715. else
  716. mSelectionMode = SelectionMode::Word;
  717. SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);
  718. }
  719. mLastClick = (float)ImGui::GetTime();
  720. }
  721. /*
  722. Left mouse button click
  723. */
  724. else if (click)
  725. {
  726. mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());
  727. if (ctrl)
  728. mSelectionMode = SelectionMode::Word;
  729. else
  730. mSelectionMode = SelectionMode::Normal;
  731. SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);
  732. mLastClick = (float)ImGui::GetTime();
  733. }
  734. // Mouse left button dragging (=> update selection)
  735. else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0))
  736. {
  737. io.WantCaptureMouse = true;
  738. mState.mCursorPosition = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());
  739. SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);
  740. }
  741. }
  742. }
  743. }
  744. void TextEditor::Render()
  745. {
  746. /* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/
  747. const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x;
  748. mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing);
  749. /* Update palette with the current alpha from style */
  750. for (int i = 0; i < (int)PaletteIndex::Max; ++i)
  751. {
  752. auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]);
  753. color.w *= ImGui::GetStyle().Alpha;
  754. mPalette[i] = ImGui::ColorConvertFloat4ToU32(color);
  755. }
  756. assert(mLineBuffer.empty());
  757. auto contentSize = ImGui::GetWindowContentRegionMax();
  758. auto drawList = ImGui::GetWindowDrawList();
  759. float longest(mTextStart);
  760. if (mScrollToTop)
  761. {
  762. mScrollToTop = false;
  763. ImGui::SetScrollY(0.f);
  764. }
  765. ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos();
  766. auto scrollX = ImGui::GetScrollX();
  767. auto scrollY = ImGui::GetScrollY();
  768. auto lineNo = (int)floor(scrollY / mCharAdvance.y);
  769. auto globalLineMax = (int)mLines.size();
  770. auto lineMax = std::max(0, std::min((int)mLines.size() - 1, lineNo + (int)floor((scrollY + contentSize.y) / mCharAdvance.y)));
  771. // Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width
  772. char buf[16];
  773. snprintf(buf, 16, " %d ", globalLineMax);
  774. mTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x + mLeftMargin;
  775. if (!mLines.empty())
  776. {
  777. float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x;
  778. while (lineNo <= lineMax)
  779. {
  780. ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * mCharAdvance.y);
  781. ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y);
  782. auto& line = mLines[lineNo];
  783. longest = std::max(mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest);
  784. auto columnNo = 0;
  785. Coordinates lineStartCoord(lineNo, 0);
  786. Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo));
  787. // Draw selection for the current line
  788. float sstart = -1.0f;
  789. float ssend = -1.0f;
  790. assert(mState.mSelectionStart <= mState.mSelectionEnd);
  791. if (mState.mSelectionStart <= lineEndCoord)
  792. sstart = mState.mSelectionStart > lineStartCoord ? TextDistanceToLineStart(mState.mSelectionStart) : 0.0f;
  793. if (mState.mSelectionEnd > lineStartCoord)
  794. ssend = TextDistanceToLineStart(mState.mSelectionEnd < lineEndCoord ? mState.mSelectionEnd : lineEndCoord);
  795. if (mState.mSelectionEnd.mLine > lineNo)
  796. ssend += mCharAdvance.x;
  797. if (sstart != -1 && ssend != -1 && sstart < ssend)
  798. {
  799. ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart, lineStartScreenPos.y);
  800. ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend, lineStartScreenPos.y + mCharAdvance.y);
  801. drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]);
  802. }
  803. // Draw breakpoints
  804. auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y);
  805. if (mBreakpoints.count(lineNo + 1) != 0)
  806. {
  807. auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y);
  808. drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]);
  809. }
  810. // Draw error markers
  811. auto errorIt = mErrorMarkers.find(lineNo + 1);
  812. if (errorIt != mErrorMarkers.end())
  813. {
  814. auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y);
  815. drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::ErrorMarker]);
  816. if (ImGui::IsMouseHoveringRect(lineStartScreenPos, end))
  817. {
  818. ImGui::BeginTooltip();
  819. ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f));
  820. ImGui::Text("Error at line %d:", errorIt->first);
  821. ImGui::PopStyleColor();
  822. ImGui::Separator();
  823. ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.2f, 1.0f));
  824. ImGui::Text("%s", errorIt->second.c_str());
  825. ImGui::PopStyleColor();
  826. ImGui::EndTooltip();
  827. }
  828. }
  829. // Draw line number (right aligned)
  830. snprintf(buf, 16, "%d ", lineNo + 1);
  831. auto lineNoWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x;
  832. drawList->AddText(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), mPalette[(int)PaletteIndex::LineNumber], buf);
  833. if (mState.mCursorPosition.mLine == lineNo)
  834. {
  835. auto focused = ImGui::IsWindowFocused();
  836. // Highlight the current line (where the cursor is)
  837. if (!HasSelection())
  838. {
  839. auto end = ImVec2(start.x + contentSize.x + scrollX, start.y + mCharAdvance.y);
  840. drawList->AddRectFilled(start, end, mPalette[(int)(focused ? PaletteIndex::CurrentLineFill : PaletteIndex::CurrentLineFillInactive)]);
  841. drawList->AddRect(start, end, mPalette[(int)PaletteIndex::CurrentLineEdge], 1.0f);
  842. }
  843. // Render the cursor
  844. if (focused)
  845. {
  846. auto timeEnd = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
  847. auto elapsed = timeEnd - mStartTime;
  848. if (elapsed > 400)
  849. {
  850. float width = 1.0f;
  851. auto cindex = GetCharacterIndex(mState.mCursorPosition);
  852. float cx = TextDistanceToLineStart(mState.mCursorPosition);
  853. if (mOverwrite && cindex < (int)line.size())
  854. {
  855. auto c = line[cindex].mChar;
  856. if (c == '\t')
  857. {
  858. auto x = (1.0f + std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);
  859. width = x - cx;
  860. }
  861. else
  862. {
  863. char buf2[2];
  864. buf2[0] = line[cindex].mChar;
  865. buf2[1] = '\0';
  866. width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf2).x;
  867. }
  868. }
  869. ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y);
  870. ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + mCharAdvance.y);
  871. drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]);
  872. if (elapsed > 800)
  873. mStartTime = timeEnd;
  874. }
  875. }
  876. }
  877. // Render colorized text
  878. auto prevColor = line.empty() ? mPalette[(int)PaletteIndex::Default] : GetGlyphColor(line[0]);
  879. ImVec2 bufferOffset;
  880. for (int i = 0; i < line.size();)
  881. {
  882. auto& glyph = line[i];
  883. auto color = GetGlyphColor(glyph);
  884. if ((color != prevColor || glyph.mChar == '\t' || glyph.mChar == ' ') && !mLineBuffer.empty())
  885. {
  886. const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y);
  887. drawList->AddText(newOffset, prevColor, mLineBuffer.c_str());
  888. auto textSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, mLineBuffer.c_str(), nullptr, nullptr);
  889. bufferOffset.x += textSize.x;
  890. mLineBuffer.clear();
  891. }
  892. prevColor = color;
  893. if (glyph.mChar == '\t')
  894. {
  895. auto oldX = bufferOffset.x;
  896. bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);
  897. ++i;
  898. if (mShowWhitespaces)
  899. {
  900. const auto s = ImGui::GetFontSize();
  901. const auto x1 = textScreenPos.x + oldX + 1.0f;
  902. const auto x2 = textScreenPos.x + bufferOffset.x - 1.0f;
  903. const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f;
  904. const ImVec2 p1(x1, y);
  905. const ImVec2 p2(x2, y);
  906. const ImVec2 p3(x2 - s * 0.2f, y - s * 0.2f);
  907. const ImVec2 p4(x2 - s * 0.2f, y + s * 0.2f);
  908. drawList->AddLine(p1, p2, 0x90909090);
  909. drawList->AddLine(p2, p3, 0x90909090);
  910. drawList->AddLine(p2, p4, 0x90909090);
  911. }
  912. }
  913. else if (glyph.mChar == ' ')
  914. {
  915. if (mShowWhitespaces)
  916. {
  917. const auto s = ImGui::GetFontSize();
  918. const auto x = textScreenPos.x + bufferOffset.x + spaceSize * 0.5f;
  919. const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f;
  920. drawList->AddCircleFilled(ImVec2(x, y), 1.5f, 0x80808080, 4);
  921. }
  922. bufferOffset.x += spaceSize;
  923. i++;
  924. }
  925. else
  926. {
  927. auto l = UTF8CharLength(glyph.mChar);
  928. while (l-- > 0)
  929. mLineBuffer.push_back(line[i++].mChar);
  930. }
  931. ++columnNo;
  932. }
  933. if (!mLineBuffer.empty())
  934. {
  935. const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y);
  936. drawList->AddText(newOffset, prevColor, mLineBuffer.c_str());
  937. mLineBuffer.clear();
  938. }
  939. ++lineNo;
  940. }
  941. // Draw a tooltip on known identifiers/preprocessor symbols
  942. if (ImGui::IsMousePosValid())
  943. {
  944. auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos()));
  945. if (!id.empty())
  946. {
  947. auto it = mLanguageDefinition.mIdentifiers.find(id);
  948. if (it != mLanguageDefinition.mIdentifiers.end())
  949. {
  950. ImGui::BeginTooltip();
  951. ImGui::TextUnformatted(it->second.mDeclaration.c_str());
  952. ImGui::EndTooltip();
  953. }
  954. else
  955. {
  956. auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id);
  957. if (pi != mLanguageDefinition.mPreprocIdentifiers.end())
  958. {
  959. ImGui::BeginTooltip();
  960. ImGui::TextUnformatted(pi->second.mDeclaration.c_str());
  961. ImGui::EndTooltip();
  962. }
  963. }
  964. }
  965. }
  966. }
  967. ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y));
  968. if (mScrollToCursor)
  969. {
  970. EnsureCursorVisible();
  971. ImGui::SetWindowFocus();
  972. mScrollToCursor = false;
  973. }
  974. }
  975. void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
  976. {
  977. mWithinRender = true;
  978. mTextChanged = false;
  979. mCursorPositionChanged = false;
  980. ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background]));
  981. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
  982. if (!mIgnoreImGuiChild)
  983. ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove);
  984. if (mHandleKeyboardInputs)
  985. {
  986. HandleKeyboardInputs();
  987. ImGui::PushAllowKeyboardFocus(true);
  988. }
  989. if (mHandleMouseInputs)
  990. HandleMouseInputs();
  991. ColorizeInternal();
  992. Render();
  993. if (mHandleKeyboardInputs)
  994. ImGui::PopAllowKeyboardFocus();
  995. if (!mIgnoreImGuiChild)
  996. ImGui::EndChild();
  997. ImGui::PopStyleVar();
  998. ImGui::PopStyleColor();
  999. mWithinRender = false;
  1000. }
  1001. void TextEditor::SetText(const std::string & aText)
  1002. {
  1003. mLines.clear();
  1004. mLines.emplace_back(Line());
  1005. for (auto chr : aText)
  1006. {
  1007. if (chr == '\r')
  1008. {
  1009. // ignore the carriage return character
  1010. }
  1011. else if (chr == '\n')
  1012. mLines.emplace_back(Line());
  1013. else
  1014. {
  1015. mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default));
  1016. }
  1017. }
  1018. mTextChanged = true;
  1019. mScrollToTop = true;
  1020. mUndoBuffer.clear();
  1021. mUndoIndex = 0;
  1022. Colorize();
  1023. }
  1024. void TextEditor::SetTextLines(const std::vector<std::string> & aLines)
  1025. {
  1026. mLines.clear();
  1027. if (aLines.empty())
  1028. {
  1029. mLines.emplace_back(Line());
  1030. }
  1031. else
  1032. {
  1033. mLines.resize(aLines.size());
  1034. for (size_t i = 0; i < aLines.size(); ++i)
  1035. {
  1036. const std::string & aLine = aLines[i];
  1037. mLines[i].reserve(aLine.size());
  1038. for (size_t j = 0; j < aLine.size(); ++j)
  1039. mLines[i].emplace_back(Glyph(aLine[j], PaletteIndex::Default));
  1040. }
  1041. }
  1042. mTextChanged = true;
  1043. mScrollToTop = true;
  1044. mUndoBuffer.clear();
  1045. mUndoIndex = 0;
  1046. Colorize();
  1047. }
  1048. void TextEditor::EnterCharacter(ImWchar aChar, bool aShift)
  1049. {
  1050. assert(!mReadOnly);
  1051. UndoRecord u;
  1052. u.mBefore = mState;
  1053. if (HasSelection())
  1054. {
  1055. if (aChar == '\t' && mState.mSelectionStart.mLine != mState.mSelectionEnd.mLine)
  1056. {
  1057. auto start = mState.mSelectionStart;
  1058. auto end = mState.mSelectionEnd;
  1059. auto originalEnd = end;
  1060. if (start > end)
  1061. std::swap(start, end);
  1062. start.mColumn = 0;
  1063. // end.mColumn = end.mLine < mLines.size() ? mLines[end.mLine].size() : 0;
  1064. if (end.mColumn == 0 && end.mLine > 0)
  1065. --end.mLine;
  1066. if (end.mLine >= (int)mLines.size())
  1067. end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1;
  1068. end.mColumn = GetLineMaxColumn(end.mLine);
  1069. //if (end.mColumn >= GetLineMaxColumn(end.mLine))
  1070. // end.mColumn = GetLineMaxColumn(end.mLine) - 1;
  1071. u.mRemovedStart = start;
  1072. u.mRemovedEnd = end;
  1073. u.mRemoved = GetText(start, end);
  1074. bool modified = false;
  1075. for (int i = start.mLine; i <= end.mLine; i++)
  1076. {
  1077. auto& line = mLines[i];
  1078. if (aShift)
  1079. {
  1080. if (!line.empty())
  1081. {
  1082. if (line.front().mChar == '\t')
  1083. {
  1084. line.erase(line.begin());
  1085. modified = true;
  1086. }
  1087. else
  1088. {
  1089. for (int j = 0; j < mTabSize && !line.empty() && line.front().mChar == ' '; j++)
  1090. {
  1091. line.erase(line.begin());
  1092. modified = true;
  1093. }
  1094. }
  1095. }
  1096. }
  1097. else
  1098. {
  1099. line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background));
  1100. modified = true;
  1101. }
  1102. }
  1103. if (modified)
  1104. {
  1105. start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0));
  1106. Coordinates rangeEnd;
  1107. if (originalEnd.mColumn != 0)
  1108. {
  1109. end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine));
  1110. rangeEnd = end;
  1111. u.mAdded = GetText(start, end);
  1112. }
  1113. else
  1114. {
  1115. end = Coordinates(originalEnd.mLine, 0);
  1116. rangeEnd = Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1));
  1117. u.mAdded = GetText(start, rangeEnd);
  1118. }
  1119. u.mAddedStart = start;
  1120. u.mAddedEnd = rangeEnd;
  1121. u.mAfter = mState;
  1122. mState.mSelectionStart = start;
  1123. mState.mSelectionEnd = end;
  1124. AddUndo(u);
  1125. mTextChanged = true;
  1126. EnsureCursorVisible();
  1127. }
  1128. return;
  1129. } // c == '\t'
  1130. else
  1131. {
  1132. u.mRemoved = GetSelectedText();
  1133. u.mRemovedStart = mState.mSelectionStart;
  1134. u.mRemovedEnd = mState.mSelectionEnd;
  1135. DeleteSelection();
  1136. }
  1137. } // HasSelection
  1138. auto coord = GetActualCursorCoordinates();
  1139. u.mAddedStart = coord;
  1140. assert(!mLines.empty());
  1141. if (aChar == '\n')
  1142. {
  1143. InsertLine(coord.mLine + 1);
  1144. auto& line = mLines[coord.mLine];
  1145. auto& newLine = mLines[coord.mLine + 1];
  1146. if (mLanguageDefinition.mAutoIndentation)
  1147. for (size_t it = 0; it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it)
  1148. newLine.push_back(line[it]);
  1149. const size_t whitespaceSize = newLine.size();
  1150. auto cindex = GetCharacterIndex(coord);
  1151. newLine.insert(newLine.end(), line.begin() + cindex, line.end());
  1152. line.erase(line.begin() + cindex, line.begin() + line.size());
  1153. SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize)));
  1154. u.mAdded = (char)aChar;
  1155. }
  1156. else
  1157. {
  1158. char buf[7];
  1159. int e = ImTextCharToUtf8(buf, 7, aChar);
  1160. if (e > 0)
  1161. {
  1162. buf[e] = '\0';
  1163. auto& line = mLines[coord.mLine];
  1164. auto cindex = GetCharacterIndex(coord);
  1165. if (mOverwrite && cindex < (int)line.size())
  1166. {
  1167. auto d = UTF8CharLength(line[cindex].mChar);
  1168. u.mRemovedStart = mState.mCursorPosition;
  1169. u.mRemovedEnd = Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + d));
  1170. while (d-- > 0 && cindex < (int)line.size())
  1171. {
  1172. u.mRemoved += line[cindex].mChar;
  1173. line.erase(line.begin() + cindex);
  1174. }
  1175. }
  1176. for (auto p = buf; *p != '\0'; p++, ++cindex)
  1177. line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default));
  1178. u.mAdded = buf;
  1179. SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex)));
  1180. }
  1181. else
  1182. return;
  1183. }
  1184. mTextChanged = true;
  1185. u.mAddedEnd = GetActualCursorCoordinates();
  1186. u.mAfter = mState;
  1187. AddUndo(u);
  1188. Colorize(coord.mLine - 1, 3);
  1189. EnsureCursorVisible();
  1190. }
  1191. void TextEditor::SetReadOnly(bool aValue)
  1192. {
  1193. mReadOnly = aValue;
  1194. }
  1195. void TextEditor::SetColorizerEnable(bool aValue)
  1196. {
  1197. mColorizerEnabled = aValue;
  1198. }
  1199. void TextEditor::SetCursorPosition(const Coordinates & aPosition)
  1200. {
  1201. if (mState.mCursorPosition != aPosition)
  1202. {
  1203. mState.mCursorPosition = aPosition;
  1204. mCursorPositionChanged = true;
  1205. EnsureCursorVisible();
  1206. }
  1207. }
  1208. void TextEditor::SetSelectionStart(const Coordinates & aPosition)
  1209. {
  1210. mState.mSelectionStart = SanitizeCoordinates(aPosition);
  1211. if (mState.mSelectionStart > mState.mSelectionEnd)
  1212. std::swap(mState.mSelectionStart, mState.mSelectionEnd);
  1213. }
  1214. void TextEditor::SetSelectionEnd(const Coordinates & aPosition)
  1215. {
  1216. mState.mSelectionEnd = SanitizeCoordinates(aPosition);
  1217. if (mState.mSelectionStart > mState.mSelectionEnd)
  1218. std::swap(mState.mSelectionStart, mState.mSelectionEnd);
  1219. }
  1220. void TextEditor::SetSelection(const Coordinates & aStart, const Coordinates & aEnd, SelectionMode aMode)
  1221. {
  1222. auto oldSelStart = mState.mSelectionStart;
  1223. auto oldSelEnd = mState.mSelectionEnd;
  1224. mState.mSelectionStart = SanitizeCoordinates(aStart);
  1225. mState.mSelectionEnd = SanitizeCoordinates(aEnd);
  1226. if (mState.mSelectionStart > mState.mSelectionEnd)
  1227. std::swap(mState.mSelectionStart, mState.mSelectionEnd);
  1228. switch (aMode)
  1229. {
  1230. case TextEditor::SelectionMode::Normal:
  1231. break;
  1232. case TextEditor::SelectionMode::Word:
  1233. {
  1234. mState.mSelectionStart = FindWordStart(mState.mSelectionStart);
  1235. if (!IsOnWordBoundary(mState.mSelectionEnd))
  1236. mState.mSelectionEnd = FindWordEnd(FindWordStart(mState.mSelectionEnd));
  1237. break;
  1238. }
  1239. case TextEditor::SelectionMode::Line:
  1240. {
  1241. const auto lineNo = mState.mSelectionEnd.mLine;
  1242. const auto lineSize = (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0;
  1243. mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0);
  1244. mState.mSelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo));
  1245. break;
  1246. }
  1247. default:
  1248. break;
  1249. }
  1250. if (mState.mSelectionStart != oldSelStart ||
  1251. mState.mSelectionEnd != oldSelEnd)
  1252. mCursorPositionChanged = true;
  1253. }
  1254. void TextEditor::SetTabSize(int aValue)
  1255. {
  1256. mTabSize = std::max(0, std::min(32, aValue));
  1257. }
  1258. void TextEditor::InsertText(const std::string & aValue)
  1259. {
  1260. InsertText(aValue.c_str());
  1261. }
  1262. void TextEditor::InsertText(const char * aValue)
  1263. {
  1264. if (aValue == nullptr)
  1265. return;
  1266. auto pos = GetActualCursorCoordinates();
  1267. auto start = std::min(pos, mState.mSelectionStart);
  1268. int totalLines = pos.mLine - start.mLine;
  1269. totalLines += InsertTextAt(pos, aValue);
  1270. SetSelection(pos, pos);
  1271. SetCursorPosition(pos);
  1272. Colorize(start.mLine - 1, totalLines + 2);
  1273. }
  1274. void TextEditor::DeleteSelection()
  1275. {
  1276. assert(mState.mSelectionEnd >= mState.mSelectionStart);
  1277. if (mState.mSelectionEnd == mState.mSelectionStart)
  1278. return;
  1279. DeleteRange(mState.mSelectionStart, mState.mSelectionEnd);
  1280. SetSelection(mState.mSelectionStart, mState.mSelectionStart);
  1281. SetCursorPosition(mState.mSelectionStart);
  1282. Colorize(mState.mSelectionStart.mLine, 1);
  1283. }
  1284. void TextEditor::MoveUp(int aAmount, bool aSelect)
  1285. {
  1286. auto oldPos = mState.mCursorPosition;
  1287. mState.mCursorPosition.mLine = std::max(0, mState.mCursorPosition.mLine - aAmount);
  1288. if (oldPos != mState.mCursorPosition)
  1289. {
  1290. if (aSelect)
  1291. {
  1292. if (oldPos == mInteractiveStart)
  1293. mInteractiveStart = mState.mCursorPosition;
  1294. else if (oldPos == mInteractiveEnd)
  1295. mInteractiveEnd = mState.mCursorPosition;
  1296. else
  1297. {
  1298. mInteractiveStart = mState.mCursorPosition;
  1299. mInteractiveEnd = oldPos;
  1300. }
  1301. }
  1302. else
  1303. mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
  1304. SetSelection(mInteractiveStart, mInteractiveEnd);
  1305. EnsureCursorVisible();
  1306. }
  1307. }
  1308. void TextEditor::MoveDown(int aAmount, bool aSelect)
  1309. {
  1310. assert(mState.mCursorPosition.mColumn >= 0);
  1311. auto oldPos = mState.mCursorPosition;
  1312. mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount));
  1313. if (mState.mCursorPosition != oldPos)
  1314. {
  1315. if (aSelect)
  1316. {
  1317. if (oldPos == mInteractiveEnd)
  1318. mInteractiveEnd = mState.mCursorPosition;
  1319. else if (oldPos == mInteractiveStart)
  1320. mInteractiveStart = mState.mCursorPosition;
  1321. else
  1322. {
  1323. mInteractiveStart = oldPos;
  1324. mInteractiveEnd = mState.mCursorPosition;
  1325. }
  1326. }
  1327. else
  1328. mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
  1329. SetSelection(mInteractiveStart, mInteractiveEnd);
  1330. EnsureCursorVisible();
  1331. }
  1332. }
  1333. static bool IsUTFSequence(char c)
  1334. {
  1335. return (c & 0xC0) == 0x80;
  1336. }
  1337. void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode)
  1338. {
  1339. if (mLines.empty())
  1340. return;
  1341. auto oldPos = mState.mCursorPosition;
  1342. mState.mCursorPosition = GetActualCursorCoordinates();
  1343. auto line = mState.mCursorPosition.mLine;
  1344. auto cindex = GetCharacterIndex(mState.mCursorPosition);
  1345. while (aAmount-- > 0)
  1346. {
  1347. if (cindex == 0)
  1348. {
  1349. if (line > 0)
  1350. {
  1351. --line;
  1352. if ((int)mLines.size() > line)
  1353. cindex = (int)mLines[line].size();
  1354. else
  1355. cindex = 0;
  1356. }
  1357. }
  1358. else
  1359. {
  1360. --cindex;
  1361. if (cindex > 0)
  1362. {
  1363. if ((int)mLines.size() > line)
  1364. {
  1365. while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar))
  1366. --cindex;
  1367. }
  1368. }
  1369. }
  1370. mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex));
  1371. if (aWordMode)
  1372. {
  1373. mState.mCursorPosition = FindWordStart(mState.mCursorPosition);
  1374. cindex = GetCharacterIndex(mState.mCursorPosition);
  1375. }
  1376. }
  1377. mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex));
  1378. assert(mState.mCursorPosition.mColumn >= 0);
  1379. if (aSelect)
  1380. {
  1381. if (oldPos == mInteractiveStart)
  1382. mInteractiveStart = mState.mCursorPosition;
  1383. else if (oldPos == mInteractiveEnd)
  1384. mInteractiveEnd = mState.mCursorPosition;
  1385. else
  1386. {
  1387. mInteractiveStart = mState.mCursorPosition;
  1388. mInteractiveEnd = oldPos;
  1389. }
  1390. }
  1391. else
  1392. mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
  1393. SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);
  1394. EnsureCursorVisible();
  1395. }
  1396. void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode)
  1397. {
  1398. auto oldPos = mState.mCursorPosition;
  1399. if (mLines.empty() || oldPos.mLine >= mLines.size())
  1400. return;
  1401. auto cindex = GetCharacterIndex(mState.mCursorPosition);
  1402. while (aAmount-- > 0)
  1403. {
  1404. auto lindex = mState.mCursorPosition.mLine;
  1405. auto& line = mLines[lindex];
  1406. if (cindex >= line.size())
  1407. {
  1408. if (mState.mCursorPosition.mLine < mLines.size() - 1)
  1409. {
  1410. mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + 1));
  1411. mState.mCursorPosition.mColumn = 0;
  1412. }
  1413. else
  1414. return;
  1415. }
  1416. else
  1417. {
  1418. cindex += UTF8CharLength(line[cindex].mChar);
  1419. mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex));
  1420. if (aWordMode)
  1421. mState.mCursorPosition = FindNextWord(mState.mCursorPosition);
  1422. }
  1423. }
  1424. if (aSelect)
  1425. {
  1426. if (oldPos == mInteractiveEnd)
  1427. mInteractiveEnd = SanitizeCoordinates(mState.mCursorPosition);
  1428. else if (oldPos == mInteractiveStart)
  1429. mInteractiveStart = mState.mCursorPosition;
  1430. else
  1431. {
  1432. mInteractiveStart = oldPos;
  1433. mInteractiveEnd = mState.mCursorPosition;
  1434. }
  1435. }
  1436. else
  1437. mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
  1438. SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);
  1439. EnsureCursorVisible();
  1440. }
  1441. void TextEditor::MoveTop(bool aSelect)
  1442. {
  1443. auto oldPos = mState.mCursorPosition;
  1444. SetCursorPosition(Coordinates(0, 0));
  1445. if (mState.mCursorPosition != oldPos)
  1446. {
  1447. if (aSelect)
  1448. {
  1449. mInteractiveEnd = oldPos;
  1450. mInteractiveStart = mState.mCursorPosition;
  1451. }
  1452. else
  1453. mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
  1454. SetSelection(mInteractiveStart, mInteractiveEnd);
  1455. }
  1456. }
  1457. void TextEditor::TextEditor::MoveBottom(bool aSelect)
  1458. {
  1459. auto oldPos = GetCursorPosition();
  1460. auto newPos = Coordinates((int)mLines.size() - 1, 0);
  1461. SetCursorPosition(newPos);
  1462. if (aSelect)
  1463. {
  1464. mInteractiveStart = oldPos;
  1465. mInteractiveEnd = newPos;
  1466. }
  1467. else
  1468. mInteractiveStart = mInteractiveEnd = newPos;
  1469. SetSelection(mInteractiveStart, mInteractiveEnd);
  1470. }
  1471. void TextEditor::MoveHome(bool aSelect)
  1472. {
  1473. auto oldPos = mState.mCursorPosition;
  1474. SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, 0));
  1475. if (mState.mCursorPosition != oldPos)
  1476. {
  1477. if (aSelect)
  1478. {
  1479. if (oldPos == mInteractiveStart)
  1480. mInteractiveStart = mState.mCursorPosition;
  1481. else if (oldPos == mInteractiveEnd)
  1482. mInteractiveEnd = mState.mCursorPosition;
  1483. else
  1484. {
  1485. mInteractiveStart = mState.mCursorPosition;
  1486. mInteractiveEnd = oldPos;
  1487. }
  1488. }
  1489. else
  1490. mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
  1491. SetSelection(mInteractiveStart, mInteractiveEnd);
  1492. }
  1493. }
  1494. void TextEditor::MoveEnd(bool aSelect)
  1495. {
  1496. auto oldPos = mState.mCursorPosition;
  1497. SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, GetLineMaxColumn(oldPos.mLine)));
  1498. if (mState.mCursorPosition != oldPos)
  1499. {
  1500. if (aSelect)
  1501. {
  1502. if (oldPos == mInteractiveEnd)
  1503. mInteractiveEnd = mState.mCursorPosition;
  1504. else if (oldPos == mInteractiveStart)
  1505. mInteractiveStart = mState.mCursorPosition;
  1506. else
  1507. {
  1508. mInteractiveStart = oldPos;
  1509. mInteractiveEnd = mState.mCursorPosition;
  1510. }
  1511. }
  1512. else
  1513. mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
  1514. SetSelection(mInteractiveStart, mInteractiveEnd);
  1515. }
  1516. }
  1517. void TextEditor::Delete()
  1518. {
  1519. assert(!mReadOnly);
  1520. if (mLines.empty())
  1521. return;
  1522. UndoRecord u;
  1523. u.mBefore = mState;
  1524. if (HasSelection())
  1525. {
  1526. u.mRemoved = GetSelectedText();
  1527. u.mRemovedStart = mState.mSelectionStart;
  1528. u.mRemovedEnd = mState.mSelectionEnd;
  1529. DeleteSelection();
  1530. }
  1531. else
  1532. {
  1533. auto pos = GetActualCursorCoordinates();
  1534. SetCursorPosition(pos);
  1535. auto& line = mLines[pos.mLine];
  1536. if (pos.mColumn == GetLineMaxColumn(pos.mLine))
  1537. {
  1538. if (pos.mLine == (int)mLines.size() - 1)
  1539. return;
  1540. u.mRemoved = '\n';
  1541. u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates();
  1542. Advance(u.mRemovedEnd);
  1543. auto& nextLine = mLines[pos.mLine + 1];
  1544. line.insert(line.end(), nextLine.begin(), nextLine.end());
  1545. RemoveLine(pos.mLine + 1);
  1546. }
  1547. else
  1548. {
  1549. auto cindex = GetCharacterIndex(pos);
  1550. u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates();
  1551. u.mRemovedEnd.mColumn++;
  1552. u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd);
  1553. auto d = UTF8CharLength(line[cindex].mChar);
  1554. while (d-- > 0 && cindex < (int)line.size())
  1555. line.erase(line.begin() + cindex);
  1556. }
  1557. mTextChanged = true;
  1558. Colorize(pos.mLine, 1);
  1559. }
  1560. u.mAfter = mState;
  1561. AddUndo(u);
  1562. }
  1563. void TextEditor::Backspace()
  1564. {
  1565. assert(!mReadOnly);
  1566. if (mLines.empty())
  1567. return;
  1568. UndoRecord u;
  1569. u.mBefore = mState;
  1570. if (HasSelection())
  1571. {
  1572. u.mRemoved = GetSelectedText();
  1573. u.mRemovedStart = mState.mSelectionStart;
  1574. u.mRemovedEnd = mState.mSelectionEnd;
  1575. DeleteSelection();
  1576. }
  1577. else
  1578. {
  1579. auto pos = GetActualCursorCoordinates();
  1580. SetCursorPosition(pos);
  1581. if (mState.mCursorPosition.mColumn == 0)
  1582. {
  1583. if (mState.mCursorPosition.mLine == 0)
  1584. return;
  1585. u.mRemoved = '\n';
  1586. u.mRemovedStart = u.mRemovedEnd = Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1));
  1587. Advance(u.mRemovedEnd);
  1588. auto& line = mLines[mState.mCursorPosition.mLine];
  1589. auto& prevLine = mLines[mState.mCursorPosition.mLine - 1];
  1590. auto prevSize = GetLineMaxColumn(mState.mCursorPosition.mLine - 1);
  1591. prevLine.insert(prevLine.end(), line.begin(), line.end());
  1592. ErrorMarkers etmp;
  1593. for (auto& i : mErrorMarkers)
  1594. etmp.insert(ErrorMarkers::value_type(i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first, i.second));
  1595. mErrorMarkers = std::move(etmp);
  1596. RemoveLine(mState.mCursorPosition.mLine);
  1597. --mState.mCursorPosition.mLine;
  1598. mState.mCursorPosition.mColumn = prevSize;
  1599. }
  1600. else
  1601. {
  1602. auto& line = mLines[mState.mCursorPosition.mLine];
  1603. auto cindex = GetCharacterIndex(pos) - 1;
  1604. auto cend = cindex + 1;
  1605. while (cindex > 0 && IsUTFSequence(line[cindex].mChar))
  1606. --cindex;
  1607. //if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1)
  1608. // --cindex;
  1609. u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates();
  1610. --u.mRemovedStart.mColumn;
  1611. --mState.mCursorPosition.mColumn;
  1612. while (cindex < line.size() && cend-- > cindex)
  1613. {
  1614. u.mRemoved += line[cindex].mChar;
  1615. line.erase(line.begin() + cindex);
  1616. }
  1617. }
  1618. mTextChanged = true;
  1619. EnsureCursorVisible();
  1620. Colorize(mState.mCursorPosition.mLine, 1);
  1621. }
  1622. u.mAfter = mState;
  1623. AddUndo(u);
  1624. }
  1625. void TextEditor::SelectWordUnderCursor()
  1626. {
  1627. auto c = GetCursorPosition();
  1628. SetSelection(FindWordStart(c), FindWordEnd(c));
  1629. }
  1630. void TextEditor::SelectAll()
  1631. {
  1632. SetSelection(Coordinates(0, 0), Coordinates((int)mLines.size(), 0));
  1633. }
  1634. bool TextEditor::HasSelection() const
  1635. {
  1636. return mState.mSelectionEnd > mState.mSelectionStart;
  1637. }
  1638. void TextEditor::Copy()
  1639. {
  1640. if (HasSelection())
  1641. {
  1642. ImGui::SetClipboardText(GetSelectedText().c_str());
  1643. }
  1644. else
  1645. {
  1646. if (!mLines.empty())
  1647. {
  1648. std::string str;
  1649. auto& line = mLines[GetActualCursorCoordinates().mLine];
  1650. for (auto& g : line)
  1651. str.push_back(g.mChar);
  1652. ImGui::SetClipboardText(str.c_str());
  1653. }
  1654. }
  1655. }
  1656. void TextEditor::Cut()
  1657. {
  1658. if (IsReadOnly())
  1659. {
  1660. Copy();
  1661. }
  1662. else
  1663. {
  1664. if (HasSelection())
  1665. {
  1666. UndoRecord u;
  1667. u.mBefore = mState;
  1668. u.mRemoved = GetSelectedText();
  1669. u.mRemovedStart = mState.mSelectionStart;
  1670. u.mRemovedEnd = mState.mSelectionEnd;
  1671. Copy();
  1672. DeleteSelection();
  1673. u.mAfter = mState;
  1674. AddUndo(u);
  1675. }
  1676. }
  1677. }
  1678. void TextEditor::Paste()
  1679. {
  1680. if (IsReadOnly())
  1681. return;
  1682. auto clipText = ImGui::GetClipboardText();
  1683. if (clipText != nullptr && strlen(clipText) > 0)
  1684. {
  1685. UndoRecord u;
  1686. u.mBefore = mState;
  1687. if (HasSelection())
  1688. {
  1689. u.mRemoved = GetSelectedText();
  1690. u.mRemovedStart = mState.mSelectionStart;
  1691. u.mRemovedEnd = mState.mSelectionEnd;
  1692. DeleteSelection();
  1693. }
  1694. u.mAdded = clipText;
  1695. u.mAddedStart = GetActualCursorCoordinates();
  1696. InsertText(clipText);
  1697. u.mAddedEnd = GetActualCursorCoordinates();
  1698. u.mAfter = mState;
  1699. AddUndo(u);
  1700. }
  1701. }
  1702. bool TextEditor::CanUndo() const
  1703. {
  1704. return !mReadOnly && mUndoIndex > 0;
  1705. }
  1706. bool TextEditor::CanRedo() const
  1707. {
  1708. return !mReadOnly && mUndoIndex < (int)mUndoBuffer.size();
  1709. }
  1710. void TextEditor::Undo(int aSteps)
  1711. {
  1712. while (CanUndo() && aSteps-- > 0)
  1713. mUndoBuffer[--mUndoIndex].Undo(this);
  1714. }
  1715. void TextEditor::Redo(int aSteps)
  1716. {
  1717. while (CanRedo() && aSteps-- > 0)
  1718. mUndoBuffer[mUndoIndex++].Redo(this);
  1719. }
  1720. const TextEditor::Palette & TextEditor::GetDarkPalette()
  1721. {
  1722. const static Palette p = { {
  1723. 0xff7f7f7f, // Default
  1724. 0xffd69c56, // Keyword
  1725. 0xff00ff00, // Number
  1726. 0xff7070e0, // String
  1727. 0xff70a0e0, // Char literal
  1728. 0xffffffff, // Punctuation
  1729. 0xff408080, // Preprocessor
  1730. 0xffaaaaaa, // Identifier
  1731. 0xff9bc64d, // Known identifier
  1732. 0xffc040a0, // Preproc identifier
  1733. 0xff206020, // Comment (single line)
  1734. 0xff406020, // Comment (multi line)
  1735. 0xff101010, // Background
  1736. 0xffe0e0e0, // Cursor
  1737. 0x80a06020, // Selection
  1738. 0x800020ff, // ErrorMarker
  1739. 0x40f08000, // Breakpoint
  1740. 0xff707000, // Line number
  1741. 0x40000000, // Current line fill
  1742. 0x40808080, // Current line fill (inactive)
  1743. 0x40a0a0a0, // Current line edge
  1744. } };
  1745. return p;
  1746. }
  1747. const TextEditor::Palette & TextEditor::GetLightPalette()
  1748. {
  1749. const static Palette p = { {
  1750. 0xff7f7f7f, // None
  1751. 0xffff0c06, // Keyword
  1752. 0xff008000, // Number
  1753. 0xff2020a0, // String
  1754. 0xff304070, // Char literal
  1755. 0xff000000, // Punctuation
  1756. 0xff406060, // Preprocessor
  1757. 0xff404040, // Identifier
  1758. 0xff606010, // Known identifier
  1759. 0xffc040a0, // Preproc identifier
  1760. 0xff205020, // Comment (single line)
  1761. 0xff405020, // Comment (multi line)
  1762. 0xffffffff, // Background
  1763. 0xff000000, // Cursor
  1764. 0x80600000, // Selection
  1765. 0xa00010ff, // ErrorMarker
  1766. 0x80f08000, // Breakpoint
  1767. 0xff505000, // Line number
  1768. 0x40000000, // Current line fill
  1769. 0x40808080, // Current line fill (inactive)
  1770. 0x40000000, // Current line edge
  1771. } };
  1772. return p;
  1773. }
  1774. const TextEditor::Palette & TextEditor::GetRetroBluePalette()
  1775. {
  1776. const static Palette p = { {
  1777. 0xff00ffff, // None
  1778. 0xffffff00, // Keyword
  1779. 0xff00ff00, // Number
  1780. 0xff808000, // String
  1781. 0xff808000, // Char literal
  1782. 0xffffffff, // Punctuation
  1783. 0xff008000, // Preprocessor
  1784. 0xff00ffff, // Identifier
  1785. 0xffffffff, // Known identifier
  1786. 0xffff00ff, // Preproc identifier
  1787. 0xff808080, // Comment (single line)
  1788. 0xff404040, // Comment (multi line)
  1789. 0xff800000, // Background
  1790. 0xff0080ff, // Cursor
  1791. 0x80ffff00, // Selection
  1792. 0xa00000ff, // ErrorMarker
  1793. 0x80ff8000, // Breakpoint
  1794. 0xff808000, // Line number
  1795. 0x40000000, // Current line fill
  1796. 0x40808080, // Current line fill (inactive)
  1797. 0x40000000, // Current line edge
  1798. } };
  1799. return p;
  1800. }
  1801. std::string TextEditor::GetText() const
  1802. {
  1803. return GetText(Coordinates(), Coordinates((int)mLines.size(), 0));
  1804. }
  1805. std::vector<std::string> TextEditor::GetTextLines() const
  1806. {
  1807. std::vector<std::string> result;
  1808. result.reserve(mLines.size());
  1809. for (auto & line : mLines)
  1810. {
  1811. std::string text;
  1812. text.resize(line.size());
  1813. for (size_t i = 0; i < line.size(); ++i)
  1814. text[i] = line[i].mChar;
  1815. result.emplace_back(std::move(text));
  1816. }
  1817. return result;
  1818. }
  1819. std::string TextEditor::GetSelectedText() const
  1820. {
  1821. return GetText(mState.mSelectionStart, mState.mSelectionEnd);
  1822. }
  1823. std::string TextEditor::GetCurrentLineText()const
  1824. {
  1825. auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine);
  1826. return GetText(
  1827. Coordinates(mState.mCursorPosition.mLine, 0),
  1828. Coordinates(mState.mCursorPosition.mLine, lineLength));
  1829. }
  1830. void TextEditor::ProcessInputs()
  1831. {
  1832. }
  1833. void TextEditor::Colorize(int aFromLine, int aLines)
  1834. {
  1835. int toLine = aLines == -1 ? (int)mLines.size() : std::min((int)mLines.size(), aFromLine + aLines);
  1836. mColorRangeMin = std::min(mColorRangeMin, aFromLine);
  1837. mColorRangeMax = std::max(mColorRangeMax, toLine);
  1838. mColorRangeMin = std::max(0, mColorRangeMin);
  1839. mColorRangeMax = std::max(mColorRangeMin, mColorRangeMax);
  1840. mCheckComments = true;
  1841. }
  1842. void TextEditor::ColorizeRange(int aFromLine, int aToLine)
  1843. {
  1844. if (mLines.empty() || aFromLine >= aToLine)
  1845. return;
  1846. std::string buffer;
  1847. std::cmatch results;
  1848. std::string id;
  1849. int endLine = std::max(0, std::min((int)mLines.size(), aToLine));
  1850. for (int i = aFromLine; i < endLine; ++i)
  1851. {
  1852. auto& line = mLines[i];
  1853. if (line.empty())
  1854. continue;
  1855. buffer.resize(line.size());
  1856. for (size_t j = 0; j < line.size(); ++j)
  1857. {
  1858. auto& col = line[j];
  1859. buffer[j] = col.mChar;
  1860. col.mColorIndex = PaletteIndex::Default;
  1861. }
  1862. const char * bufferBegin = &buffer.front();
  1863. const char * bufferEnd = bufferBegin + buffer.size();
  1864. auto last = bufferEnd;
  1865. for (auto first = bufferBegin; first != last; )
  1866. {
  1867. const char * token_begin = nullptr;
  1868. const char * token_end = nullptr;
  1869. PaletteIndex token_color = PaletteIndex::Default;
  1870. bool hasTokenizeResult = false;
  1871. if (mLanguageDefinition.mTokenize != nullptr)
  1872. {
  1873. if (mLanguageDefinition.mTokenize(first, last, token_begin, token_end, token_color))
  1874. hasTokenizeResult = true;
  1875. }
  1876. if (hasTokenizeResult == false)
  1877. {
  1878. // todo : remove
  1879. //printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last - first), first);
  1880. for (auto& p : mRegexList)
  1881. {
  1882. if (std::regex_search(first, last, results, p.first, std::regex_constants::match_continuous))
  1883. {
  1884. hasTokenizeResult = true;
  1885. auto& v = *results.begin();
  1886. token_begin = v.first;
  1887. token_end = v.second;
  1888. token_color = p.second;
  1889. break;
  1890. }
  1891. }
  1892. }
  1893. if (hasTokenizeResult == false)
  1894. {
  1895. first++;
  1896. }
  1897. else
  1898. {
  1899. const size_t token_length = token_end - token_begin;
  1900. if (token_color == PaletteIndex::Identifier)
  1901. {
  1902. id.assign(token_begin, token_end);
  1903. // todo : allmost all language definitions use lower case to specify keywords, so shouldn't this use ::tolower ?
  1904. if (!mLanguageDefinition.mCaseSensitive)
  1905. std::transform(id.begin(), id.end(), id.begin(), ::toupper);
  1906. if (!line[first - bufferBegin].mPreprocessor)
  1907. {
  1908. if (mLanguageDefinition.mKeywords.count(id) != 0)
  1909. token_color = PaletteIndex::Keyword;
  1910. else if (mLanguageDefinition.mIdentifiers.count(id) != 0)
  1911. token_color = PaletteIndex::KnownIdentifier;
  1912. else if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0)
  1913. token_color = PaletteIndex::PreprocIdentifier;
  1914. }
  1915. else
  1916. {
  1917. if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0)
  1918. token_color = PaletteIndex::PreprocIdentifier;
  1919. }
  1920. }
  1921. for (size_t j = 0; j < token_length; ++j)
  1922. line[(token_begin - bufferBegin) + j].mColorIndex = token_color;
  1923. first = token_end;
  1924. }
  1925. }
  1926. }
  1927. }
  1928. void TextEditor::ColorizeInternal()
  1929. {
  1930. if (mLines.empty() || !mColorizerEnabled)
  1931. return;
  1932. if (mCheckComments)
  1933. {
  1934. auto endLine = mLines.size();
  1935. auto endIndex = 0;
  1936. auto commentStartLine = endLine;
  1937. auto commentStartIndex = endIndex;
  1938. auto withinString = false;
  1939. auto withinSingleLineComment = false;
  1940. auto withinPreproc = false;
  1941. auto firstChar = true; // there is no other non-whitespace characters in the line before
  1942. auto concatenate = false; // '\' on the very end of the line
  1943. auto currentLine = 0;
  1944. auto currentIndex = 0;
  1945. while (currentLine < endLine || currentIndex < endIndex)
  1946. {
  1947. auto& line = mLines[currentLine];
  1948. if (currentIndex == 0 && !concatenate)
  1949. {
  1950. withinSingleLineComment = false;
  1951. withinPreproc = false;
  1952. firstChar = true;
  1953. }
  1954. concatenate = false;
  1955. if (!line.empty())
  1956. {
  1957. auto& g = line[currentIndex];
  1958. auto c = g.mChar;
  1959. if (c != mLanguageDefinition.mPreprocChar && !isspace(c))
  1960. firstChar = false;
  1961. if (currentIndex == (int)line.size() - 1 && line[line.size() - 1].mChar == '\\')
  1962. concatenate = true;
  1963. bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
  1964. if (withinString)
  1965. {
  1966. line[currentIndex].mMultiLineComment = inComment;
  1967. if (c == '\"')
  1968. {
  1969. if (currentIndex + 1 < (int)line.size() && line[currentIndex + 1].mChar == '\"')
  1970. {
  1971. currentIndex += 1;
  1972. if (currentIndex < (int)line.size())
  1973. line[currentIndex].mMultiLineComment = inComment;
  1974. }
  1975. else
  1976. withinString = false;
  1977. }
  1978. else if (c == '\\')
  1979. {
  1980. currentIndex += 1;
  1981. if (currentIndex < (int)line.size())
  1982. line[currentIndex].mMultiLineComment = inComment;
  1983. }
  1984. }
  1985. else
  1986. {
  1987. if (firstChar && c == mLanguageDefinition.mPreprocChar)
  1988. withinPreproc = true;
  1989. if (c == '\"')
  1990. {
  1991. withinString = true;
  1992. line[currentIndex].mMultiLineComment = inComment;
  1993. }
  1994. else
  1995. {
  1996. auto pred = [](const char& a, const Glyph& b) { return a == b.mChar; };
  1997. auto from = line.begin() + currentIndex;
  1998. auto& startStr = mLanguageDefinition.mCommentStart;
  1999. auto& singleStartStr = mLanguageDefinition.mSingleLineComment;
  2000. if (singleStartStr.size() > 0 &&
  2001. currentIndex + singleStartStr.size() <= line.size() &&
  2002. equals(singleStartStr.begin(), singleStartStr.end(), from, from + singleStartStr.size(), pred))
  2003. {
  2004. withinSingleLineComment = true;
  2005. }
  2006. else if (!withinSingleLineComment && currentIndex + startStr.size() <= line.size() &&
  2007. equals(startStr.begin(), startStr.end(), from, from + startStr.size(), pred))
  2008. {
  2009. commentStartLine = currentLine;
  2010. commentStartIndex = currentIndex;
  2011. }
  2012. inComment = inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
  2013. line[currentIndex].mMultiLineComment = inComment;
  2014. line[currentIndex].mComment = withinSingleLineComment;
  2015. auto& endStr = mLanguageDefinition.mCommentEnd;
  2016. if (currentIndex + 1 >= (int)endStr.size() &&
  2017. equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, pred))
  2018. {
  2019. commentStartIndex = endIndex;
  2020. commentStartLine = endLine;
  2021. }
  2022. }
  2023. }
  2024. line[currentIndex].mPreprocessor = withinPreproc;
  2025. currentIndex += UTF8CharLength(c);
  2026. if (currentIndex >= (int)line.size())
  2027. {
  2028. currentIndex = 0;
  2029. ++currentLine;
  2030. }
  2031. }
  2032. else
  2033. {
  2034. currentIndex = 0;
  2035. ++currentLine;
  2036. }
  2037. }
  2038. mCheckComments = false;
  2039. }
  2040. if (mColorRangeMin < mColorRangeMax)
  2041. {
  2042. const int increment = (mLanguageDefinition.mTokenize == nullptr) ? 10 : 10000;
  2043. const int to = std::min(mColorRangeMin + increment, mColorRangeMax);
  2044. ColorizeRange(mColorRangeMin, to);
  2045. mColorRangeMin = to;
  2046. if (mColorRangeMax == mColorRangeMin)
  2047. {
  2048. mColorRangeMin = std::numeric_limits<int>::max();
  2049. mColorRangeMax = 0;
  2050. }
  2051. return;
  2052. }
  2053. }
  2054. float TextEditor::TextDistanceToLineStart(const Coordinates& aFrom) const
  2055. {
  2056. auto& line = mLines[aFrom.mLine];
  2057. float distance = 0.0f;
  2058. float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x;
  2059. int colIndex = GetCharacterIndex(aFrom);
  2060. for (size_t it = 0u; it < line.size() && it < colIndex; )
  2061. {
  2062. if (line[it].mChar == '\t')
  2063. {
  2064. distance = (1.0f + std::floor((1.0f + distance) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);
  2065. ++it;
  2066. }
  2067. else
  2068. {
  2069. auto d = UTF8CharLength(line[it].mChar);
  2070. char tempCString[7];
  2071. int i = 0;
  2072. for (; i < 6 && d-- > 0 && it < (int)line.size(); i++, it++)
  2073. tempCString[i] = line[it].mChar;
  2074. tempCString[i] = '\0';
  2075. distance += ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, tempCString, nullptr, nullptr).x;
  2076. }
  2077. }
  2078. return distance;
  2079. }
  2080. void TextEditor::EnsureCursorVisible()
  2081. {
  2082. if (!mWithinRender)
  2083. {
  2084. mScrollToCursor = true;
  2085. return;
  2086. }
  2087. float scrollX = ImGui::GetScrollX();
  2088. float scrollY = ImGui::GetScrollY();
  2089. auto height = ImGui::GetWindowHeight();
  2090. auto width = ImGui::GetWindowWidth();
  2091. auto top = 1 + (int)ceil(scrollY / mCharAdvance.y);
  2092. auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y);
  2093. auto left = (int)ceil(scrollX / mCharAdvance.x);
  2094. auto right = (int)ceil((scrollX + width) / mCharAdvance.x);
  2095. auto pos = GetActualCursorCoordinates();
  2096. auto len = TextDistanceToLineStart(pos);
  2097. if (pos.mLine < top)
  2098. ImGui::SetScrollY(std::max(0.0f, (pos.mLine - 1) * mCharAdvance.y));
  2099. if (pos.mLine > bottom - 4)
  2100. ImGui::SetScrollY(std::max(0.0f, (pos.mLine + 4) * mCharAdvance.y - height));
  2101. if (len + mTextStart < left + 4)
  2102. ImGui::SetScrollX(std::max(0.0f, len + mTextStart - 4));
  2103. if (len + mTextStart > right - 4)
  2104. ImGui::SetScrollX(std::max(0.0f, len + mTextStart + 4 - width));
  2105. }
  2106. int TextEditor::GetPageSize() const
  2107. {
  2108. auto height = ImGui::GetWindowHeight() - 20.0f;
  2109. return (int)floor(height / mCharAdvance.y);
  2110. }
  2111. TextEditor::UndoRecord::UndoRecord(
  2112. const std::string& aAdded,
  2113. const TextEditor::Coordinates aAddedStart,
  2114. const TextEditor::Coordinates aAddedEnd,
  2115. const std::string& aRemoved,
  2116. const TextEditor::Coordinates aRemovedStart,
  2117. const TextEditor::Coordinates aRemovedEnd,
  2118. TextEditor::EditorState& aBefore,
  2119. TextEditor::EditorState& aAfter)
  2120. : mAdded(aAdded)
  2121. , mAddedStart(aAddedStart)
  2122. , mAddedEnd(aAddedEnd)
  2123. , mRemoved(aRemoved)
  2124. , mRemovedStart(aRemovedStart)
  2125. , mRemovedEnd(aRemovedEnd)
  2126. , mBefore(aBefore)
  2127. , mAfter(aAfter)
  2128. {
  2129. assert(mAddedStart <= mAddedEnd);
  2130. assert(mRemovedStart <= mRemovedEnd);
  2131. }
  2132. void TextEditor::UndoRecord::Undo(TextEditor * aEditor)
  2133. {
  2134. if (!mAdded.empty())
  2135. {
  2136. aEditor->DeleteRange(mAddedStart, mAddedEnd);
  2137. aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 2);
  2138. }
  2139. if (!mRemoved.empty())
  2140. {
  2141. auto start = mRemovedStart;
  2142. aEditor->InsertTextAt(start, mRemoved.c_str());
  2143. aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 2);
  2144. }
  2145. aEditor->mState = mBefore;
  2146. aEditor->EnsureCursorVisible();
  2147. }
  2148. void TextEditor::UndoRecord::Redo(TextEditor * aEditor)
  2149. {
  2150. if (!mRemoved.empty())
  2151. {
  2152. aEditor->DeleteRange(mRemovedStart, mRemovedEnd);
  2153. aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 1);
  2154. }
  2155. if (!mAdded.empty())
  2156. {
  2157. auto start = mAddedStart;
  2158. aEditor->InsertTextAt(start, mAdded.c_str());
  2159. aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 1);
  2160. }
  2161. aEditor->mState = mAfter;
  2162. aEditor->EnsureCursorVisible();
  2163. }
  2164. static bool TokenizeCStyleString(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
  2165. {
  2166. const char * p = in_begin;
  2167. if (*p == '"')
  2168. {
  2169. p++;
  2170. while (p < in_end)
  2171. {
  2172. // handle end of string
  2173. if (*p == '"')
  2174. {
  2175. out_begin = in_begin;
  2176. out_end = p + 1;
  2177. return true;
  2178. }
  2179. // handle escape character for "
  2180. if (*p == '\\' && p + 1 < in_end && p[1] == '"')
  2181. p++;
  2182. p++;
  2183. }
  2184. }
  2185. return false;
  2186. }
  2187. static bool TokenizeCStyleCharacterLiteral(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
  2188. {
  2189. const char * p = in_begin;
  2190. if (*p == '\'')
  2191. {
  2192. p++;
  2193. // handle escape characters
  2194. if (p < in_end && *p == '\\')
  2195. p++;
  2196. if (p < in_end)
  2197. p++;
  2198. // handle end of character literal
  2199. if (p < in_end && *p == '\'')
  2200. {
  2201. out_begin = in_begin;
  2202. out_end = p + 1;
  2203. return true;
  2204. }
  2205. }
  2206. return false;
  2207. }
  2208. static bool TokenizeCStyleIdentifier(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
  2209. {
  2210. const char * p = in_begin;
  2211. if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_')
  2212. {
  2213. p++;
  2214. while ((p < in_end) && ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_'))
  2215. p++;
  2216. out_begin = in_begin;
  2217. out_end = p;
  2218. return true;
  2219. }
  2220. return false;
  2221. }
  2222. static bool TokenizeCStyleNumber(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
  2223. {
  2224. const char * p = in_begin;
  2225. const bool startsWithNumber = *p >= '0' && *p <= '9';
  2226. if (*p != '+' && *p != '-' && !startsWithNumber)
  2227. return false;
  2228. p++;
  2229. bool hasNumber = startsWithNumber;
  2230. while (p < in_end && (*p >= '0' && *p <= '9'))
  2231. {
  2232. hasNumber = true;
  2233. p++;
  2234. }
  2235. if (hasNumber == false)
  2236. return false;
  2237. bool isFloat = false;
  2238. bool isHex = false;
  2239. bool isBinary = false;
  2240. if (p < in_end)
  2241. {
  2242. if (*p == '.')
  2243. {
  2244. isFloat = true;
  2245. p++;
  2246. while (p < in_end && (*p >= '0' && *p <= '9'))
  2247. p++;
  2248. }
  2249. else if (*p == 'x' || *p == 'X')
  2250. {
  2251. // hex formatted integer of the type 0xef80
  2252. isHex = true;
  2253. p++;
  2254. while (p < in_end && ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')))
  2255. p++;
  2256. }
  2257. else if (*p == 'b' || *p == 'B')
  2258. {
  2259. // binary formatted integer of the type 0b01011101
  2260. isBinary = true;
  2261. p++;
  2262. while (p < in_end && (*p >= '0' && *p <= '1'))
  2263. p++;
  2264. }
  2265. }
  2266. if (isHex == false && isBinary == false)
  2267. {
  2268. // floating point exponent
  2269. if (p < in_end && (*p == 'e' || *p == 'E'))
  2270. {
  2271. isFloat = true;
  2272. p++;
  2273. if (p < in_end && (*p == '+' || *p == '-'))
  2274. p++;
  2275. bool hasDigits = false;
  2276. while (p < in_end && (*p >= '0' && *p <= '9'))
  2277. {
  2278. hasDigits = true;
  2279. p++;
  2280. }
  2281. if (hasDigits == false)
  2282. return false;
  2283. }
  2284. // single precision floating point type
  2285. if (p < in_end && *p == 'f')
  2286. p++;
  2287. }
  2288. if (isFloat == false)
  2289. {
  2290. // integer size type
  2291. while (p < in_end && (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L'))
  2292. p++;
  2293. }
  2294. out_begin = in_begin;
  2295. out_end = p;
  2296. return true;
  2297. }
  2298. static bool TokenizeCStylePunctuation(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
  2299. {
  2300. (void)in_end;
  2301. switch (*in_begin)
  2302. {
  2303. case '[':
  2304. case ']':
  2305. case '{':
  2306. case '}':
  2307. case '!':
  2308. case '%':
  2309. case '^':
  2310. case '&':
  2311. case '*':
  2312. case '(':
  2313. case ')':
  2314. case '-':
  2315. case '+':
  2316. case '=':
  2317. case '~':
  2318. case '|':
  2319. case '<':
  2320. case '>':
  2321. case '?':
  2322. case ':':
  2323. case '/':
  2324. case ';':
  2325. case ',':
  2326. case '.':
  2327. out_begin = in_begin;
  2328. out_end = in_begin + 1;
  2329. return true;
  2330. }
  2331. return false;
  2332. }
  2333. const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::CPlusPlus()
  2334. {
  2335. static bool inited = false;
  2336. static LanguageDefinition langDef;
  2337. if (!inited)
  2338. {
  2339. static const char* const cppKeywords[] = {
  2340. "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",
  2341. "compl", "concept", "const", "constexpr", "const_cast", "continue", "decltype", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float",
  2342. "for", "friend", "goto", "if", "import", "inline", "int", "long", "module", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public",
  2343. "register", "reinterpret_cast", "requires", "return", "short", "signed", "sizeof", "static", "static_assert", "static_cast", "struct", "switch", "synchronized", "template", "this", "thread_local",
  2344. "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq"
  2345. };
  2346. for (auto& k : cppKeywords)
  2347. langDef.mKeywords.insert(k);
  2348. static const char* const identifiers[] = {
  2349. "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph",
  2350. "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",
  2351. "std", "string", "vector", "map", "unordered_map", "set", "unordered_set", "min", "max"
  2352. };
  2353. for (auto& k : identifiers)
  2354. {
  2355. Identifier id;
  2356. id.mDeclaration = "Built-in function";
  2357. langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
  2358. }
  2359. langDef.mTokenize = [](const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex) -> bool
  2360. {
  2361. paletteIndex = PaletteIndex::Max;
  2362. while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin))
  2363. in_begin++;
  2364. if (in_begin == in_end)
  2365. {
  2366. out_begin = in_end;
  2367. out_end = in_end;
  2368. paletteIndex = PaletteIndex::Default;
  2369. }
  2370. else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end))
  2371. paletteIndex = PaletteIndex::String;
  2372. else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end))
  2373. paletteIndex = PaletteIndex::CharLiteral;
  2374. else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end))
  2375. paletteIndex = PaletteIndex::Identifier;
  2376. else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end))
  2377. paletteIndex = PaletteIndex::Number;
  2378. else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end))
  2379. paletteIndex = PaletteIndex::Punctuation;
  2380. return paletteIndex != PaletteIndex::Max;
  2381. };
  2382. langDef.mCommentStart = "/*";
  2383. langDef.mCommentEnd = "*/";
  2384. langDef.mSingleLineComment = "//";
  2385. langDef.mCaseSensitive = true;
  2386. langDef.mAutoIndentation = true;
  2387. langDef.mName = "C++";
  2388. inited = true;
  2389. }
  2390. return langDef;
  2391. }
  2392. const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::HLSL()
  2393. {
  2394. static bool inited = false;
  2395. static LanguageDefinition langDef;
  2396. if (!inited)
  2397. {
  2398. static const char* const keywords[] = {
  2399. "AppendStructuredBuffer", "asm", "asm_fragment", "BlendState", "bool", "break", "Buffer", "ByteAddressBuffer", "case", "cbuffer", "centroid", "class", "column_major", "compile", "compile_fragment",
  2400. "CompileShader", "const", "continue", "ComputeShader", "ConsumeStructuredBuffer", "default", "DepthStencilState", "DepthStencilView", "discard", "do", "double", "DomainShader", "dword", "else",
  2401. "export", "extern", "false", "float", "for", "fxgroup", "GeometryShader", "groupshared", "half", "Hullshader", "if", "in", "inline", "inout", "InputPatch", "int", "interface", "line", "lineadj",
  2402. "linear", "LineStream", "matrix", "min16float", "min10float", "min16int", "min12int", "min16uint", "namespace", "nointerpolation", "noperspective", "NULL", "out", "OutputPatch", "packoffset",
  2403. "pass", "pixelfragment", "PixelShader", "point", "PointStream", "precise", "RasterizerState", "RenderTargetView", "return", "register", "row_major", "RWBuffer", "RWByteAddressBuffer", "RWStructuredBuffer",
  2404. "RWTexture1D", "RWTexture1DArray", "RWTexture2D", "RWTexture2DArray", "RWTexture3D", "sample", "sampler", "SamplerState", "SamplerComparisonState", "shared", "snorm", "stateblock", "stateblock_state",
  2405. "static", "string", "struct", "switch", "StructuredBuffer", "tbuffer", "technique", "technique10", "technique11", "texture", "Texture1D", "Texture1DArray", "Texture2D", "Texture2DArray", "Texture2DMS",
  2406. "Texture2DMSArray", "Texture3D", "TextureCube", "TextureCubeArray", "true", "typedef", "triangle", "triangleadj", "TriangleStream", "uint", "uniform", "unorm", "unsigned", "vector", "vertexfragment",
  2407. "VertexShader", "void", "volatile", "while",
  2408. "bool1","bool2","bool3","bool4","double1","double2","double3","double4", "float1", "float2", "float3", "float4", "int1", "int2", "int3", "int4", "in", "out", "inout",
  2409. "uint1", "uint2", "uint3", "uint4", "dword1", "dword2", "dword3", "dword4", "half1", "half2", "half3", "half4",
  2410. "float1x1","float2x1","float3x1","float4x1","float1x2","float2x2","float3x2","float4x2",
  2411. "float1x3","float2x3","float3x3","float4x3","float1x4","float2x4","float3x4","float4x4",
  2412. "half1x1","half2x1","half3x1","half4x1","half1x2","half2x2","half3x2","half4x2",
  2413. "half1x3","half2x3","half3x3","half4x3","half1x4","half2x4","half3x4","half4x4",
  2414. };
  2415. for (auto& k : keywords)
  2416. langDef.mKeywords.insert(k);
  2417. static const char* const identifiers[] = {
  2418. "abort", "abs", "acos", "all", "AllMemoryBarrier", "AllMemoryBarrierWithGroupSync", "any", "asdouble", "asfloat", "asin", "asint", "asint", "asuint",
  2419. "asuint", "atan", "atan2", "ceil", "CheckAccessFullyMapped", "clamp", "clip", "cos", "cosh", "countbits", "cross", "D3DCOLORtoUBYTE4", "ddx",
  2420. "ddx_coarse", "ddx_fine", "ddy", "ddy_coarse", "ddy_fine", "degrees", "determinant", "DeviceMemoryBarrier", "DeviceMemoryBarrierWithGroupSync",
  2421. "distance", "dot", "dst", "errorf", "EvaluateAttributeAtCentroid", "EvaluateAttributeAtSample", "EvaluateAttributeSnapped", "exp", "exp2",
  2422. "f16tof32", "f32tof16", "faceforward", "firstbithigh", "firstbitlow", "floor", "fma", "fmod", "frac", "frexp", "fwidth", "GetRenderTargetSampleCount",
  2423. "GetRenderTargetSamplePosition", "GroupMemoryBarrier", "GroupMemoryBarrierWithGroupSync", "InterlockedAdd", "InterlockedAnd", "InterlockedCompareExchange",
  2424. "InterlockedCompareStore", "InterlockedExchange", "InterlockedMax", "InterlockedMin", "InterlockedOr", "InterlockedXor", "isfinite", "isinf", "isnan",
  2425. "ldexp", "length", "lerp", "lit", "log", "log10", "log2", "mad", "max", "min", "modf", "msad4", "mul", "noise", "normalize", "pow", "printf",
  2426. "Process2DQuadTessFactorsAvg", "Process2DQuadTessFactorsMax", "Process2DQuadTessFactorsMin", "ProcessIsolineTessFactors", "ProcessQuadTessFactorsAvg",
  2427. "ProcessQuadTessFactorsMax", "ProcessQuadTessFactorsMin", "ProcessTriTessFactorsAvg", "ProcessTriTessFactorsMax", "ProcessTriTessFactorsMin",
  2428. "radians", "rcp", "reflect", "refract", "reversebits", "round", "rsqrt", "saturate", "sign", "sin", "sincos", "sinh", "smoothstep", "sqrt", "step",
  2429. "tan", "tanh", "tex1D", "tex1D", "tex1Dbias", "tex1Dgrad", "tex1Dlod", "tex1Dproj", "tex2D", "tex2D", "tex2Dbias", "tex2Dgrad", "tex2Dlod", "tex2Dproj",
  2430. "tex3D", "tex3D", "tex3Dbias", "tex3Dgrad", "tex3Dlod", "tex3Dproj", "texCUBE", "texCUBE", "texCUBEbias", "texCUBEgrad", "texCUBElod", "texCUBEproj", "transpose", "trunc"
  2431. };
  2432. for (auto& k : identifiers)
  2433. {
  2434. Identifier id;
  2435. id.mDeclaration = "Built-in function";
  2436. langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
  2437. }
  2438. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));
  2439. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
  2440. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));
  2441. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
  2442. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
  2443. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
  2444. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
  2445. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
  2446. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
  2447. langDef.mCommentStart = "/*";
  2448. langDef.mCommentEnd = "*/";
  2449. langDef.mSingleLineComment = "//";
  2450. langDef.mCaseSensitive = true;
  2451. langDef.mAutoIndentation = true;
  2452. langDef.mName = "HLSL";
  2453. inited = true;
  2454. }
  2455. return langDef;
  2456. }
  2457. const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL()
  2458. {
  2459. static bool inited = false;
  2460. static LanguageDefinition langDef;
  2461. if (!inited)
  2462. {
  2463. static const char* const keywords[] = {
  2464. "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short",
  2465. "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary",
  2466. "_Noreturn", "_Static_assert", "_Thread_local"
  2467. };
  2468. for (auto& k : keywords)
  2469. langDef.mKeywords.insert(k);
  2470. static const char* const identifiers[] = {
  2471. "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph",
  2472. "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"
  2473. };
  2474. for (auto& k : identifiers)
  2475. {
  2476. Identifier id;
  2477. id.mDeclaration = "Built-in function";
  2478. langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
  2479. }
  2480. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));
  2481. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
  2482. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));
  2483. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
  2484. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
  2485. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
  2486. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
  2487. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
  2488. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
  2489. langDef.mCommentStart = "/*";
  2490. langDef.mCommentEnd = "*/";
  2491. langDef.mSingleLineComment = "//";
  2492. langDef.mCaseSensitive = true;
  2493. langDef.mAutoIndentation = true;
  2494. langDef.mName = "GLSL";
  2495. inited = true;
  2496. }
  2497. return langDef;
  2498. }
  2499. const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::C()
  2500. {
  2501. static bool inited = false;
  2502. static LanguageDefinition langDef;
  2503. if (!inited)
  2504. {
  2505. static const char* const keywords[] = {
  2506. "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short",
  2507. "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary",
  2508. "_Noreturn", "_Static_assert", "_Thread_local"
  2509. };
  2510. for (auto& k : keywords)
  2511. langDef.mKeywords.insert(k);
  2512. static const char* const identifiers[] = {
  2513. "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph",
  2514. "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"
  2515. };
  2516. for (auto& k : identifiers)
  2517. {
  2518. Identifier id;
  2519. id.mDeclaration = "Built-in function";
  2520. langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
  2521. }
  2522. langDef.mTokenize = [](const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex) -> bool
  2523. {
  2524. paletteIndex = PaletteIndex::Max;
  2525. while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin))
  2526. in_begin++;
  2527. if (in_begin == in_end)
  2528. {
  2529. out_begin = in_end;
  2530. out_end = in_end;
  2531. paletteIndex = PaletteIndex::Default;
  2532. }
  2533. else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end))
  2534. paletteIndex = PaletteIndex::String;
  2535. else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end))
  2536. paletteIndex = PaletteIndex::CharLiteral;
  2537. else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end))
  2538. paletteIndex = PaletteIndex::Identifier;
  2539. else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end))
  2540. paletteIndex = PaletteIndex::Number;
  2541. else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end))
  2542. paletteIndex = PaletteIndex::Punctuation;
  2543. return paletteIndex != PaletteIndex::Max;
  2544. };
  2545. langDef.mCommentStart = "/*";
  2546. langDef.mCommentEnd = "*/";
  2547. langDef.mSingleLineComment = "//";
  2548. langDef.mCaseSensitive = true;
  2549. langDef.mAutoIndentation = true;
  2550. langDef.mName = "C";
  2551. inited = true;
  2552. }
  2553. return langDef;
  2554. }
  2555. const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::SQL()
  2556. {
  2557. static bool inited = false;
  2558. static LanguageDefinition langDef;
  2559. if (!inited)
  2560. {
  2561. static const char* const keywords[] = {
  2562. "ADD", "EXCEPT", "PERCENT", "ALL", "EXEC", "PLAN", "ALTER", "EXECUTE", "PRECISION", "AND", "EXISTS", "PRIMARY", "ANY", "EXIT", "PRINT", "AS", "FETCH", "PROC", "ASC", "FILE", "PROCEDURE",
  2563. "AUTHORIZATION", "FILLFACTOR", "PUBLIC", "BACKUP", "FOR", "RAISERROR", "BEGIN", "FOREIGN", "READ", "BETWEEN", "FREETEXT", "READTEXT", "BREAK", "FREETEXTTABLE", "RECONFIGURE",
  2564. "BROWSE", "FROM", "REFERENCES", "BULK", "FULL", "REPLICATION", "BY", "FUNCTION", "RESTORE", "CASCADE", "GOTO", "RESTRICT", "CASE", "GRANT", "RETURN", "CHECK", "GROUP", "REVOKE",
  2565. "CHECKPOINT", "HAVING", "RIGHT", "CLOSE", "HOLDLOCK", "ROLLBACK", "CLUSTERED", "IDENTITY", "ROWCOUNT", "COALESCE", "IDENTITY_INSERT", "ROWGUIDCOL", "COLLATE", "IDENTITYCOL", "RULE",
  2566. "COLUMN", "IF", "SAVE", "COMMIT", "IN", "SCHEMA", "COMPUTE", "INDEX", "SELECT", "CONSTRAINT", "INNER", "SESSION_USER", "CONTAINS", "INSERT", "SET", "CONTAINSTABLE", "INTERSECT", "SETUSER",
  2567. "CONTINUE", "INTO", "SHUTDOWN", "CONVERT", "IS", "SOME", "CREATE", "JOIN", "STATISTICS", "CROSS", "KEY", "SYSTEM_USER", "CURRENT", "KILL", "TABLE", "CURRENT_DATE", "LEFT", "TEXTSIZE",
  2568. "CURRENT_TIME", "LIKE", "THEN", "CURRENT_TIMESTAMP", "LINENO", "TO", "CURRENT_USER", "LOAD", "TOP", "CURSOR", "NATIONAL", "TRAN", "DATABASE", "NOCHECK", "TRANSACTION",
  2569. "DBCC", "NONCLUSTERED", "TRIGGER", "DEALLOCATE", "NOT", "TRUNCATE", "DECLARE", "NULL", "TSEQUAL", "DEFAULT", "NULLIF", "UNION", "DELETE", "OF", "UNIQUE", "DENY", "OFF", "UPDATE",
  2570. "DESC", "OFFSETS", "UPDATETEXT", "DISK", "ON", "USE", "DISTINCT", "OPEN", "USER", "DISTRIBUTED", "OPENDATASOURCE", "VALUES", "DOUBLE", "OPENQUERY", "VARYING","DROP", "OPENROWSET", "VIEW",
  2571. "DUMMY", "OPENXML", "WAITFOR", "DUMP", "OPTION", "WHEN", "ELSE", "OR", "WHERE", "END", "ORDER", "WHILE", "ERRLVL", "OUTER", "WITH", "ESCAPE", "OVER", "WRITETEXT"
  2572. };
  2573. for (auto& k : keywords)
  2574. langDef.mKeywords.insert(k);
  2575. static const char* const identifiers[] = {
  2576. "ABS", "ACOS", "ADD_MONTHS", "ASCII", "ASCIISTR", "ASIN", "ATAN", "ATAN2", "AVG", "BFILENAME", "BIN_TO_NUM", "BITAND", "CARDINALITY", "CASE", "CAST", "CEIL",
  2577. "CHARTOROWID", "CHR", "COALESCE", "COMPOSE", "CONCAT", "CONVERT", "CORR", "COS", "COSH", "COUNT", "COVAR_POP", "COVAR_SAMP", "CUME_DIST", "CURRENT_DATE",
  2578. "CURRENT_TIMESTAMP", "DBTIMEZONE", "DECODE", "DECOMPOSE", "DENSE_RANK", "DUMP", "EMPTY_BLOB", "EMPTY_CLOB", "EXP", "EXTRACT", "FIRST_VALUE", "FLOOR", "FROM_TZ", "GREATEST",
  2579. "GROUP_ID", "HEXTORAW", "INITCAP", "INSTR", "INSTR2", "INSTR4", "INSTRB", "INSTRC", "LAG", "LAST_DAY", "LAST_VALUE", "LEAD", "LEAST", "LENGTH", "LENGTH2", "LENGTH4",
  2580. "LENGTHB", "LENGTHC", "LISTAGG", "LN", "LNNVL", "LOCALTIMESTAMP", "LOG", "LOWER", "LPAD", "LTRIM", "MAX", "MEDIAN", "MIN", "MOD", "MONTHS_BETWEEN", "NANVL", "NCHR",
  2581. "NEW_TIME", "NEXT_DAY", "NTH_VALUE", "NULLIF", "NUMTODSINTERVAL", "NUMTOYMINTERVAL", "NVL", "NVL2", "POWER", "RANK", "RAWTOHEX", "REGEXP_COUNT", "REGEXP_INSTR",
  2582. "REGEXP_REPLACE", "REGEXP_SUBSTR", "REMAINDER", "REPLACE", "ROUND", "ROWNUM", "RPAD", "RTRIM", "SESSIONTIMEZONE", "SIGN", "SIN", "SINH",
  2583. "SOUNDEX", "SQRT", "STDDEV", "SUBSTR", "SUM", "SYS_CONTEXT", "SYSDATE", "SYSTIMESTAMP", "TAN", "TANH", "TO_CHAR", "TO_CLOB", "TO_DATE", "TO_DSINTERVAL", "TO_LOB",
  2584. "TO_MULTI_BYTE", "TO_NCLOB", "TO_NUMBER", "TO_SINGLE_BYTE", "TO_TIMESTAMP", "TO_TIMESTAMP_TZ", "TO_YMINTERVAL", "TRANSLATE", "TRIM", "TRUNC", "TZ_OFFSET", "UID", "UPPER",
  2585. "USER", "USERENV", "VAR_POP", "VAR_SAMP", "VARIANCE", "VSIZE "
  2586. };
  2587. for (auto& k : identifiers)
  2588. {
  2589. Identifier id;
  2590. id.mDeclaration = "Built-in function";
  2591. langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
  2592. }
  2593. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
  2594. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String));
  2595. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
  2596. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
  2597. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
  2598. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
  2599. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
  2600. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
  2601. langDef.mCommentStart = "/*";
  2602. langDef.mCommentEnd = "*/";
  2603. langDef.mSingleLineComment = "//";
  2604. langDef.mCaseSensitive = false;
  2605. langDef.mAutoIndentation = false;
  2606. langDef.mName = "SQL";
  2607. inited = true;
  2608. }
  2609. return langDef;
  2610. }
  2611. const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::AngelScript()
  2612. {
  2613. static bool inited = false;
  2614. static LanguageDefinition langDef;
  2615. if (!inited)
  2616. {
  2617. static const char* const keywords[] = {
  2618. "and", "abstract", "auto", "bool", "break", "case", "cast", "class", "const", "continue", "default", "do", "double", "else", "enum", "false", "final", "float", "for",
  2619. "from", "funcdef", "function", "get", "if", "import", "in", "inout", "int", "interface", "int8", "int16", "int32", "int64", "is", "mixin", "namespace", "not",
  2620. "null", "or", "out", "override", "private", "protected", "return", "set", "shared", "super", "switch", "this ", "true", "typedef", "uint", "uint8", "uint16", "uint32",
  2621. "uint64", "void", "while", "xor"
  2622. };
  2623. for (auto& k : keywords)
  2624. langDef.mKeywords.insert(k);
  2625. static const char* const identifiers[] = {
  2626. "cos", "sin", "tab", "acos", "asin", "atan", "atan2", "cosh", "sinh", "tanh", "log", "log10", "pow", "sqrt", "abs", "ceil", "floor", "fraction", "closeTo", "fpFromIEEE", "fpToIEEE",
  2627. "complex", "opEquals", "opAddAssign", "opSubAssign", "opMulAssign", "opDivAssign", "opAdd", "opSub", "opMul", "opDiv"
  2628. };
  2629. for (auto& k : identifiers)
  2630. {
  2631. Identifier id;
  2632. id.mDeclaration = "Built-in function";
  2633. langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
  2634. }
  2635. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
  2636. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::String));
  2637. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
  2638. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
  2639. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
  2640. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
  2641. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
  2642. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
  2643. langDef.mCommentStart = "/*";
  2644. langDef.mCommentEnd = "*/";
  2645. langDef.mSingleLineComment = "//";
  2646. langDef.mCaseSensitive = true;
  2647. langDef.mAutoIndentation = true;
  2648. langDef.mName = "AngelScript";
  2649. inited = true;
  2650. }
  2651. return langDef;
  2652. }
  2653. const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::Lua()
  2654. {
  2655. static bool inited = false;
  2656. static LanguageDefinition langDef;
  2657. if (!inited)
  2658. {
  2659. static const char* const keywords[] = {
  2660. "and", "break", "do", "", "else", "elseif", "end", "false", "for", "function", "if", "in", "", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while"
  2661. };
  2662. for (auto& k : keywords)
  2663. langDef.mKeywords.insert(k);
  2664. static const char* const identifiers[] = {
  2665. "assert", "collectgarbage", "dofile", "error", "getmetatable", "ipairs", "loadfile", "load", "loadstring", "next", "pairs", "pcall", "print", "rawequal", "rawlen", "rawget", "rawset",
  2666. "select", "setmetatable", "tonumber", "tostring", "type", "xpcall", "_G", "_VERSION","arshift", "band", "bnot", "bor", "bxor", "btest", "extract", "lrotate", "lshift", "replace",
  2667. "rrotate", "rshift", "create", "resume", "running", "status", "wrap", "yield", "isyieldable", "debug","getuservalue", "gethook", "getinfo", "getlocal", "getregistry", "getmetatable",
  2668. "getupvalue", "upvaluejoin", "upvalueid", "setuservalue", "sethook", "setlocal", "setmetatable", "setupvalue", "traceback", "close", "flush", "input", "lines", "open", "output", "popen",
  2669. "read", "tmpfile", "type", "write", "close", "flush", "lines", "read", "seek", "setvbuf", "write", "__gc", "__tostring", "abs", "acos", "asin", "atan", "ceil", "cos", "deg", "exp", "tointeger",
  2670. "floor", "fmod", "ult", "log", "max", "min", "modf", "rad", "random", "randomseed", "sin", "sqrt", "string", "tan", "type", "atan2", "cosh", "sinh", "tanh",
  2671. "pow", "frexp", "ldexp", "log10", "pi", "huge", "maxinteger", "mininteger", "loadlib", "searchpath", "seeall", "preload", "cpath", "path", "searchers", "loaded", "module", "require", "clock",
  2672. "date", "difftime", "execute", "exit", "getenv", "remove", "rename", "setlocale", "time", "tmpname", "byte", "char", "dump", "find", "format", "gmatch", "gsub", "len", "lower", "match", "rep",
  2673. "reverse", "sub", "upper", "pack", "packsize", "unpack", "concat", "maxn", "insert", "pack", "unpack", "remove", "move", "sort", "offset", "codepoint", "char", "len", "codes", "charpattern",
  2674. "coroutine", "table", "io", "os", "string", "utf8", "bit32", "math", "debug", "package"
  2675. };
  2676. for (auto& k : identifiers)
  2677. {
  2678. Identifier id;
  2679. id.mDeclaration = "Built-in function";
  2680. langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
  2681. }
  2682. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
  2683. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String));
  2684. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
  2685. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
  2686. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
  2687. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
  2688. langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
  2689. langDef.mCommentStart = "--[[";
  2690. langDef.mCommentEnd = "]]";
  2691. langDef.mSingleLineComment = "--";
  2692. langDef.mCaseSensitive = true;
  2693. langDef.mAutoIndentation = false;
  2694. langDef.mName = "Lua";
  2695. inited = true;
  2696. }
  2697. return langDef;
  2698. }