TextEditor.cpp 108 KB

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