You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

text_editor.cpp 96 kB

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