yaze 0.2.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
text_editor.cc
Go to the documentation of this file.
1#include "text_editor.h"
2
3#include <algorithm>
4#include <chrono>
5#include <cmath>
6#include <regex>
7#include <string>
8
9#define IMGUI_DEFINE_MATH_OPERATORS
10#include "imgui.h" // for imGui::GetCurrentWindow()
11
12// TODO
13// - multiline comments vs single-line: latter is blocking start of a ML
14
15template <class InputIt1, class InputIt2, class BinaryPredicate>
16bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
17 BinaryPredicate p) {
18 for (; first1 != last1 && first2 != last2; ++first1, ++first2) {
19 if (!p(*first1, *first2)) return false;
20 }
21 return first1 == last1 && first2 == last2;
22}
23
25 : mLineSpacing(1.0f),
26 mUndoIndex(0),
27 mTabSize(4),
28 mOverwrite(false),
29 mReadOnly(false),
30 mWithinRender(false),
31 mScrollToCursor(false),
32 mScrollToTop(false),
33 mTextChanged(false),
35 mTextStart(20.0f),
36 mLeftMargin(10),
41 mCheckComments(true),
42 mLastClick(-1.0f),
45 mIgnoreImGuiChild(false),
46 mShowWhitespaces(true),
47 mStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(
48 std::chrono::system_clock::now().time_since_epoch())
49 .count()) {
52 mLines.push_back(Line());
53}
54
56
58 mLanguageDefinition = aLanguageDef;
59 mRegexList.clear();
60
61 for (auto& r : mLanguageDefinition.mTokenRegexStrings)
62 mRegexList.push_back(std::make_pair(
63 std::regex(r.first, std::regex_constants::optimize), r.second));
64
65 Colorize();
66}
67
68void TextEditor::SetPalette(const Palette& aValue) { mPaletteBase = aValue; }
69
70std::string TextEditor::GetText(const Coordinates& aStart,
71 const Coordinates& aEnd) const {
72 std::string result;
73
74 auto lstart = aStart.mLine;
75 auto lend = aEnd.mLine;
76 auto istart = GetCharacterIndex(aStart);
77 auto iend = GetCharacterIndex(aEnd);
78 size_t s = 0;
79
80 for (size_t i = lstart; i < lend; i++) s += mLines[i].size();
81
82 result.reserve(s + s / 8);
83
84 while (istart < iend || lstart < lend) {
85 if (lstart >= (int)mLines.size()) break;
86
87 auto& line = mLines[lstart];
88 if (istart < (int)line.size()) {
89 result += line[istart].mChar;
90 istart++;
91 } else {
92 istart = 0;
93 ++lstart;
94 result += '\n';
95 }
96 }
97
98 return result;
99}
100
104
106 const Coordinates& aValue) const {
107 auto line = aValue.mLine;
108 auto column = aValue.mColumn;
109 if (line >= (int)mLines.size()) {
110 if (mLines.empty()) {
111 line = 0;
112 column = 0;
113 } else {
114 line = (int)mLines.size() - 1;
115 column = GetLineMaxColumn(line);
116 }
117 return Coordinates(line, column);
118 } else {
119 column = mLines.empty() ? 0 : std::min(column, GetLineMaxColumn(line));
120 return Coordinates(line, column);
121 }
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
126// an UTF-8 code sequence (non-10xxxxxx code)
127static int UTF8CharLength(TextEditor::Char c) {
128 if ((c & 0xFE) == 0xFC) return 6;
129 if ((c & 0xFC) == 0xF8) return 5;
130 if ((c & 0xF8) == 0xF0)
131 return 4;
132 else if ((c & 0xF0) == 0xE0)
133 return 3;
134 else if ((c & 0xE0) == 0xC0)
135 return 2;
136 return 1;
137}
138
139// "Borrowed" from ImGui source
140static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) {
141 if (c < 0x80) {
142 buf[0] = (char)c;
143 return 1;
144 }
145 if (c < 0x800) {
146 if (buf_size < 2) return 0;
147 buf[0] = (char)(0xc0 + (c >> 6));
148 buf[1] = (char)(0x80 + (c & 0x3f));
149 return 2;
150 }
151 if (c >= 0xdc00 && c < 0xe000) {
152 return 0;
153 }
154 if (c >= 0xd800 && c < 0xdc00) {
155 if (buf_size < 4) return 0;
156 buf[0] = (char)(0xf0 + (c >> 18));
157 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
158 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
159 buf[3] = (char)(0x80 + ((c) & 0x3f));
160 return 4;
161 }
162 // else if (c < 0x10000)
163 {
164 if (buf_size < 3) return 0;
165 buf[0] = (char)(0xe0 + (c >> 12));
166 buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
167 buf[2] = (char)(0x80 + ((c) & 0x3f));
168 return 3;
169 }
170}
171
172void TextEditor::Advance(Coordinates& aCoordinates) const {
173 if (aCoordinates.mLine < (int)mLines.size()) {
174 auto& line = mLines[aCoordinates.mLine];
175 auto cindex = GetCharacterIndex(aCoordinates);
176
177 if (cindex + 1 < (int)line.size()) {
178 auto delta = UTF8CharLength(line[cindex].mChar);
179 cindex = std::min(cindex + delta, (int)line.size() - 1);
180 } else {
181 ++aCoordinates.mLine;
182 cindex = 0;
183 }
184 aCoordinates.mColumn = GetCharacterColumn(aCoordinates.mLine, cindex);
185 }
186}
187
189 const Coordinates& aEnd) {
190 assert(aEnd >= aStart);
191 assert(!mReadOnly);
192
193 // printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine,
194 // aEnd.mColumn);
195
196 if (aEnd == aStart) return;
197
198 auto start = GetCharacterIndex(aStart);
199 auto end = GetCharacterIndex(aEnd);
200
201 if (aStart.mLine == aEnd.mLine) {
202 auto& line = mLines[aStart.mLine];
203 auto n = GetLineMaxColumn(aStart.mLine);
204 if (aEnd.mColumn >= n)
205 line.erase(line.begin() + start, line.end());
206 else
207 line.erase(line.begin() + start, line.begin() + end);
208 } else {
209 auto& firstLine = mLines[aStart.mLine];
210 auto& lastLine = mLines[aEnd.mLine];
211
212 firstLine.erase(firstLine.begin() + start, firstLine.end());
213 lastLine.erase(lastLine.begin(), lastLine.begin() + end);
214
215 if (aStart.mLine < aEnd.mLine)
216 firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end());
217
218 if (aStart.mLine < aEnd.mLine) RemoveLine(aStart.mLine + 1, aEnd.mLine + 1);
219 }
220
221 mTextChanged = true;
222}
223
224int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere,
225 const char* aValue) {
226 assert(!mReadOnly);
227
228 int cindex = GetCharacterIndex(aWhere);
229 int totalLines = 0;
230 while (*aValue != '\0') {
231 assert(!mLines.empty());
232
233 if (*aValue == '\r') {
234 // skip
235 ++aValue;
236 } else if (*aValue == '\n') {
237 if (cindex < (int)mLines[aWhere.mLine].size()) {
238 auto& newLine = InsertLine(aWhere.mLine + 1);
239 auto& line = mLines[aWhere.mLine];
240 newLine.insert(newLine.begin(), line.begin() + cindex, line.end());
241 line.erase(line.begin() + cindex, line.end());
242 } else {
243 InsertLine(aWhere.mLine + 1);
244 }
245 ++aWhere.mLine;
246 aWhere.mColumn = 0;
247 cindex = 0;
248 ++totalLines;
249 ++aValue;
250 } else {
251 auto& line = mLines[aWhere.mLine];
252 auto d = UTF8CharLength(*aValue);
253 while (d-- > 0 && *aValue != '\0')
254 line.insert(line.begin() + cindex++,
255 Glyph(*aValue++, PaletteIndex::Default));
256 ++aWhere.mColumn;
257 }
258
259 mTextChanged = true;
260 }
261
262 return totalLines;
263}
264
266 assert(!mReadOnly);
267 // printf("AddUndo: (@%d.%d) +\'%s' [%d.%d .. %d.%d], -\'%s', [%d.%d .. %d.%d]
268 // (@%d.%d)\n", aValue.mBefore.mCursorPosition.mLine,
269 //aValue.mBefore.mCursorPosition.mColumn, aValue.mAdded.c_str(),
270 //aValue.mAddedStart.mLine, aValue.mAddedStart.mColumn,
271 //aValue.mAddedEnd.mLine, aValue.mAddedEnd.mColumn, aValue.mRemoved.c_str(),
272 //aValue.mRemovedStart.mLine, aValue.mRemovedStart.mColumn,
273 //aValue.mRemovedEnd.mLine, aValue.mRemovedEnd.mColumn,
274 // aValue.mAfter.mCursorPosition.mLine,
275 //aValue.mAfter.mCursorPosition.mColumn
276 // );
277
278 mUndoBuffer.resize((size_t)(mUndoIndex + 1));
279 mUndoBuffer.back() = aValue;
280 ++mUndoIndex;
281}
282
284 const ImVec2& aPosition) const {
285 ImVec2 origin = ImGui::GetCursorScreenPos();
286 ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y);
287
288 int lineNo = std::max(0, (int)floor(local.y / mCharAdvance.y));
289
290 int columnCoord = 0;
291
292 if (lineNo >= 0 && lineNo < (int)mLines.size()) {
293 auto& line = mLines.at(lineNo);
294
295 int columnIndex = 0;
296 float columnX = 0.0f;
297
298 while ((size_t)columnIndex < line.size()) {
299 float columnWidth = 0.0f;
300
301 if (line[columnIndex].mChar == '\t') {
302 float spaceSize =
303 ImGui::GetFont()
304 ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ")
305 .x;
306 float oldX = columnX;
307 float newColumnX = (1.0f + std::floor((1.0f + columnX) /
308 (float(mTabSize) * spaceSize))) *
309 (float(mTabSize) * spaceSize);
310 columnWidth = newColumnX - oldX;
311 if (mTextStart + columnX + columnWidth * 0.5f > local.x) break;
312 columnX = newColumnX;
313 columnCoord = (columnCoord / mTabSize) * mTabSize + mTabSize;
314 columnIndex++;
315 } else {
316 char buf[7];
317 auto d = UTF8CharLength(line[columnIndex].mChar);
318 int i = 0;
319 while (i < 6 && d-- > 0) buf[i++] = line[columnIndex++].mChar;
320 buf[i] = '\0';
321 columnWidth =
322 ImGui::GetFont()
323 ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf)
324 .x;
325 if (mTextStart + columnX + columnWidth * 0.5f > local.x) break;
326 columnX += columnWidth;
327 columnCoord++;
328 }
329 }
330 }
331
332 return SanitizeCoordinates(Coordinates(lineNo, columnCoord));
333}
334
336 const Coordinates& aFrom) const {
337 Coordinates at = aFrom;
338 if (at.mLine >= (int)mLines.size()) return at;
339
340 auto& line = mLines[at.mLine];
341 auto cindex = GetCharacterIndex(at);
342
343 if (cindex >= (int)line.size()) return at;
344
345 while (cindex > 0 && isspace(line[cindex].mChar)) --cindex;
346
347 auto cstart = (PaletteIndex)line[cindex].mColorIndex;
348 while (cindex > 0) {
349 auto c = line[cindex].mChar;
350 if ((c & 0xC0) != 0x80) // not UTF code sequence 10xxxxxx
351 {
352 if (c <= 32 && isspace(c)) {
353 cindex++;
354 break;
355 }
356 if (cstart != (PaletteIndex)line[size_t(cindex - 1)].mColorIndex) break;
357 }
358 --cindex;
359 }
360 return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex));
361}
362
364 const Coordinates& aFrom) const {
365 Coordinates at = aFrom;
366 if (at.mLine >= (int)mLines.size()) return at;
367
368 auto& line = mLines[at.mLine];
369 auto cindex = GetCharacterIndex(at);
370
371 if (cindex >= (int)line.size()) return at;
372
373 bool prevspace = (bool)isspace(line[cindex].mChar);
374 auto cstart = (PaletteIndex)line[cindex].mColorIndex;
375 while (cindex < (int)line.size()) {
376 auto c = line[cindex].mChar;
377 auto d = UTF8CharLength(c);
378 if (cstart != (PaletteIndex)line[cindex].mColorIndex) break;
379
380 if (prevspace != !!isspace(c)) {
381 if (isspace(c))
382 while (cindex < (int)line.size() && isspace(line[cindex].mChar))
383 ++cindex;
384 break;
385 }
386 cindex += d;
387 }
388 return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex));
389}
390
392 const Coordinates& aFrom) const {
393 Coordinates at = aFrom;
394 if (at.mLine >= (int)mLines.size()) return at;
395
396 // skip to the next non-word character
397 auto cindex = GetCharacterIndex(aFrom);
398 bool isword = false;
399 bool skip = false;
400 if (cindex < (int)mLines[at.mLine].size()) {
401 auto& line = mLines[at.mLine];
402 isword = isalnum(line[cindex].mChar);
403 skip = isword;
404 }
405
406 while (!isword || skip) {
407 if (at.mLine >= mLines.size()) {
408 auto l = std::max(0, (int)mLines.size() - 1);
409 return Coordinates(l, GetLineMaxColumn(l));
410 }
411
412 auto& line = mLines[at.mLine];
413 if (cindex < (int)line.size()) {
414 isword = isalnum(line[cindex].mChar);
415
416 if (isword && !skip)
417 return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex));
418
419 if (!isword) skip = false;
420
421 cindex++;
422 } else {
423 cindex = 0;
424 ++at.mLine;
425 skip = false;
426 isword = false;
427 }
428 }
429
430 return at;
431}
432
433int TextEditor::GetCharacterIndex(const Coordinates& aCoordinates) const {
434 if (aCoordinates.mLine >= mLines.size()) return -1;
435 auto& line = mLines[aCoordinates.mLine];
436 int c = 0;
437 int i = 0;
438 for (; i < line.size() && c < aCoordinates.mColumn;) {
439 if (line[i].mChar == '\t')
440 c = (c / mTabSize) * mTabSize + mTabSize;
441 else
442 ++c;
443 i += UTF8CharLength(line[i].mChar);
444 }
445 return i;
446}
447
448int TextEditor::GetCharacterColumn(int aLine, int aIndex) const {
449 if (aLine >= mLines.size()) return 0;
450 auto& line = mLines[aLine];
451 int col = 0;
452 int i = 0;
453 while (i < aIndex && i < (int)line.size()) {
454 auto c = line[i].mChar;
455 i += UTF8CharLength(c);
456 if (c == '\t')
457 col = (col / mTabSize) * mTabSize + mTabSize;
458 else
459 col++;
460 }
461 return col;
462}
463
465 if (aLine >= mLines.size()) return 0;
466 auto& line = mLines[aLine];
467 int c = 0;
468 for (unsigned i = 0; i < line.size(); c++) i += UTF8CharLength(line[i].mChar);
469 return c;
470}
471
472int TextEditor::GetLineMaxColumn(int aLine) const {
473 if (aLine >= mLines.size()) return 0;
474 auto& line = mLines[aLine];
475 int col = 0;
476 for (unsigned i = 0; i < line.size();) {
477 auto c = line[i].mChar;
478 if (c == '\t')
479 col = (col / mTabSize) * mTabSize + mTabSize;
480 else
481 col++;
482 i += UTF8CharLength(c);
483 }
484 return col;
485}
486
488 if (aAt.mLine >= (int)mLines.size() || aAt.mColumn == 0) return true;
489
490 auto& line = mLines[aAt.mLine];
491 auto cindex = GetCharacterIndex(aAt);
492 if (cindex >= (int)line.size()) return true;
493
495 return line[cindex].mColorIndex != line[size_t(cindex - 1)].mColorIndex;
496
497 return isspace(line[cindex].mChar) != isspace(line[cindex - 1].mChar);
498}
499
500void TextEditor::RemoveLine(int aStart, int aEnd) {
501 assert(!mReadOnly);
502 assert(aEnd >= aStart);
503 assert(mLines.size() > (size_t)(aEnd - aStart));
504
505 ErrorMarkers etmp;
506 for (auto& i : mErrorMarkers) {
507 ErrorMarkers::value_type e(i.first >= aStart ? i.first - 1 : i.first,
508 i.second);
509 if (e.first >= aStart && e.first <= aEnd) continue;
510 etmp.insert(e);
511 }
512 mErrorMarkers = std::move(etmp);
513
514 Breakpoints btmp;
515 for (auto i : mBreakpoints) {
516 if (i >= aStart && i <= aEnd) continue;
517 btmp.insert(i >= aStart ? i - 1 : i);
518 }
519 mBreakpoints = std::move(btmp);
520
521 mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd);
522 assert(!mLines.empty());
523
524 mTextChanged = true;
525}
526
527void TextEditor::RemoveLine(int aIndex) {
528 assert(!mReadOnly);
529 assert(mLines.size() > 1);
530
531 ErrorMarkers etmp;
532 for (auto& i : mErrorMarkers) {
533 ErrorMarkers::value_type e(i.first > aIndex ? i.first - 1 : i.first,
534 i.second);
535 if (e.first - 1 == aIndex) continue;
536 etmp.insert(e);
537 }
538 mErrorMarkers = std::move(etmp);
539
540 Breakpoints btmp;
541 for (auto i : mBreakpoints) {
542 if (i == aIndex) continue;
543 btmp.insert(i >= aIndex ? i - 1 : i);
544 }
545 mBreakpoints = std::move(btmp);
546
547 mLines.erase(mLines.begin() + aIndex);
548 assert(!mLines.empty());
549
550 mTextChanged = true;
551}
552
554 assert(!mReadOnly);
555
556 auto& result = *mLines.insert(mLines.begin() + aIndex, Line());
557
558 ErrorMarkers etmp;
559 for (auto& i : mErrorMarkers)
560 etmp.insert(ErrorMarkers::value_type(
561 i.first >= aIndex ? i.first + 1 : i.first, i.second));
562 mErrorMarkers = std::move(etmp);
563
564 Breakpoints btmp;
565 for (auto i : mBreakpoints) btmp.insert(i >= aIndex ? i + 1 : i);
566 mBreakpoints = std::move(btmp);
567
568 return result;
569}
570
572 auto c = GetCursorPosition();
573 return GetWordAt(c);
574}
575
576std::string TextEditor::GetWordAt(const Coordinates& aCoords) const {
577 auto start = FindWordStart(aCoords);
578 auto end = FindWordEnd(aCoords);
579
580 std::string r;
581
582 auto istart = GetCharacterIndex(start);
583 auto iend = GetCharacterIndex(end);
584
585 for (auto it = istart; it < iend; ++it)
586 r.push_back(mLines[aCoords.mLine][it].mChar);
587
588 return r;
589}
590
591ImU32 TextEditor::GetGlyphColor(const Glyph& aGlyph) const {
593 if (aGlyph.mComment) return mPalette[(int)PaletteIndex::Comment];
594 if (aGlyph.mMultiLineComment)
596 auto const color = mPalette[(int)aGlyph.mColorIndex];
597 if (aGlyph.mPreprocessor) {
598 const auto ppcolor = mPalette[(int)PaletteIndex::Preprocessor];
599 const int c0 = ((ppcolor & 0xff) + (color & 0xff)) / 2;
600 const int c1 = (((ppcolor >> 8) & 0xff) + ((color >> 8) & 0xff)) / 2;
601 const int c2 = (((ppcolor >> 16) & 0xff) + ((color >> 16) & 0xff)) / 2;
602 const int c3 = (((ppcolor >> 24) & 0xff) + ((color >> 24) & 0xff)) / 2;
603 return ImU32(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24));
604 }
605 return color;
606}
607
609 ImGuiIO& io = ImGui::GetIO();
610 auto shift = io.KeyShift;
611 auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
612 auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
613
614 if (ImGui::IsWindowFocused()) {
615 if (ImGui::IsWindowHovered())
616 ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput);
617 // ImGui::CaptureKeyboardFromApp(true);
618
619 io.WantCaptureKeyboard = true;
620 io.WantTextInput = true;
621
622 if (!IsReadOnly() && ctrl && !shift && !alt &&
623 ImGui::IsKeyPressed(ImGuiKey_Z))
624 Undo();
625 else if (!IsReadOnly() && !ctrl && !shift && alt &&
626 ImGui::IsKeyPressed(ImGuiKey_Backspace))
627 Undo();
628 else if (!IsReadOnly() && ctrl && !shift && !alt &&
629 ImGui::IsKeyPressed(ImGuiKey_Y))
630 Redo();
631 else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_UpArrow))
632 MoveUp(1, shift);
633 else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_DownArrow))
634 MoveDown(1, shift);
635 else if (!alt && ImGui::IsKeyPressed(ImGuiKey_LeftArrow))
636 MoveLeft(1, shift, ctrl);
637 else if (!alt && ImGui::IsKeyPressed(ImGuiKey_RightArrow))
638 MoveRight(1, shift, ctrl);
639 else if (!alt && ImGui::IsKeyPressed(ImGuiKey_PageUp))
640 MoveUp(GetPageSize() - 4, shift);
641 else if (!alt && ImGui::IsKeyPressed(ImGuiKey_PageDown))
642 MoveDown(GetPageSize() - 4, shift);
643 else if (!alt && ctrl && ImGui::IsKeyPressed(ImGuiKey_Home))
644 MoveTop(shift);
645 else if (ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_End))
646 MoveBottom(shift);
647 else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Home))
648 MoveHome(shift);
649 else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_End))
650 MoveEnd(shift);
651 else if (!IsReadOnly() && !ctrl && !shift && !alt &&
652 ImGui::IsKeyPressed(ImGuiKey_Delete))
653 Delete();
654 else if (!IsReadOnly() && !ctrl && !shift && !alt &&
655 ImGui::IsKeyPressed(ImGuiKey_Backspace))
656 Backspace();
657 else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert))
658 mOverwrite ^= true;
659 else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert))
660 Copy();
661 else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_C))
662 Copy();
663 else if (!IsReadOnly() && !ctrl && shift && !alt &&
664 ImGui::IsKeyPressed(ImGuiKey_Insert))
665 Paste();
666 else if (!IsReadOnly() && ctrl && !shift && !alt &&
667 ImGui::IsKeyPressed(ImGuiKey_V))
668 Paste();
669 else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_X))
670 Cut();
671 else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Delete))
672 Cut();
673 else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_A))
674 SelectAll();
675 else if (!IsReadOnly() && !ctrl && !shift && !alt &&
676 ImGui::IsKeyPressed(ImGuiKey_Enter))
677 EnterCharacter('\n', false);
678 else if (!IsReadOnly() && !ctrl && !alt &&
679 ImGui::IsKeyPressed(ImGuiKey_Tab))
680 EnterCharacter('\t', shift);
681
682 if (!IsReadOnly() && !io.InputQueueCharacters.empty()) {
683 for (int i = 0; i < io.InputQueueCharacters.Size; i++) {
684 auto c = io.InputQueueCharacters[i];
685 if (c != 0 && (c == '\n' || c >= 32)) EnterCharacter(c, shift);
686 }
687 io.InputQueueCharacters.resize(0);
688 }
689 }
690}
691
693 ImGuiIO& io = ImGui::GetIO();
694 auto shift = io.KeyShift;
695 auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
696 auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
697
698 if (ImGui::IsWindowHovered()) {
699 if (!shift && !alt) {
700 auto click = ImGui::IsMouseClicked(0);
701 auto doubleClick = ImGui::IsMouseDoubleClicked(0);
702 auto t = ImGui::GetTime();
703 auto tripleClick =
704 click && !doubleClick &&
705 (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime);
706
707 /*
708 Left mouse button triple click
709 */
710
711 if (tripleClick) {
712 if (!ctrl) {
713 mState.mCursorPosition = mInteractiveStart = mInteractiveEnd =
714 ScreenPosToCoordinates(ImGui::GetMousePos());
717 }
718
719 mLastClick = -1.0f;
720 }
721
722 /*
723 Left mouse button double click
724 */
725
726 else if (doubleClick) {
727 if (!ctrl) {
728 mState.mCursorPosition = mInteractiveStart = mInteractiveEnd =
729 ScreenPosToCoordinates(ImGui::GetMousePos());
732 else
735 }
736
737 mLastClick = (float)ImGui::GetTime();
738 }
739
740 /*
741 Left mouse button click
742 */
743 else if (click) {
744 mState.mCursorPosition = mInteractiveStart = mInteractiveEnd =
745 ScreenPosToCoordinates(ImGui::GetMousePos());
746 if (ctrl)
748 else
751
752 mLastClick = (float)ImGui::GetTime();
753 }
754 // Mouse left button dragging (=> update selection)
755 else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) {
756 io.WantCaptureMouse = true;
757 mState.mCursorPosition = mInteractiveEnd =
758 ScreenPosToCoordinates(ImGui::GetMousePos());
760 }
761 }
762 }
763}
764
766 /* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/
767 const float fontSize = ImGui::GetFont()
768 ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX,
769 -1.0f, "#", nullptr, nullptr)
770 .x;
772 ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing);
773
774 /* Update palette with the current alpha from style */
775 for (int i = 0; i < (int)PaletteIndex::Max; ++i) {
776 auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]);
777 color.w *= ImGui::GetStyle().Alpha;
778 mPalette[i] = ImGui::ColorConvertFloat4ToU32(color);
779 }
780
781 assert(mLineBuffer.empty());
782
783 auto contentSize = ImGui::GetWindowContentRegionMax();
784 auto drawList = ImGui::GetWindowDrawList();
785 float longest(mTextStart);
786
787 if (mScrollToTop) {
788 mScrollToTop = false;
789 ImGui::SetScrollY(0.f);
790 }
791
792 ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos();
793 auto scrollX = ImGui::GetScrollX();
794 auto scrollY = ImGui::GetScrollY();
795
796 auto lineNo = (int)floor(scrollY / mCharAdvance.y);
797 auto globalLineMax = (int)mLines.size();
798 auto lineMax = std::max(
799 0, std::min(
800 (int)mLines.size() - 1,
801 lineNo + (int)floor((scrollY + contentSize.y) / mCharAdvance.y)));
802
803 // Deduce mTextStart by evaluating mLines size (global lineMax) plus two
804 // spaces as text width
805 char buf[16];
806 snprintf(buf, 16, " %d ", globalLineMax);
807 mTextStart = ImGui::GetFont()
808 ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf,
809 nullptr, nullptr)
810 .x +
812
813 if (!mLines.empty()) {
814 float spaceSize = ImGui::GetFont()
815 ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f,
816 " ", nullptr, nullptr)
817 .x;
818
819 while (lineNo <= lineMax) {
820 ImVec2 lineStartScreenPos = ImVec2(
821 cursorScreenPos.x, cursorScreenPos.y + lineNo * mCharAdvance.y);
822 ImVec2 textScreenPos =
823 ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y);
824
825 auto& line = mLines[lineNo];
827 lineNo, GetLineMaxColumn(lineNo))),
828 longest);
829 auto columnNo = 0;
830 Coordinates lineStartCoord(lineNo, 0);
831 Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo));
832
833 // Draw selection for the current line
834 float sstart = -1.0f;
835 float ssend = -1.0f;
836
837 assert(mState.mSelectionStart <= mState.mSelectionEnd);
838 if (mState.mSelectionStart <= lineEndCoord)
839 sstart = mState.mSelectionStart > lineStartCoord
840 ? TextDistanceToLineStart(mState.mSelectionStart)
841 : 0.0f;
842 if (mState.mSelectionEnd > lineStartCoord)
843 ssend = TextDistanceToLineStart(mState.mSelectionEnd < lineEndCoord
844 ? mState.mSelectionEnd
845 : lineEndCoord);
846
847 if (mState.mSelectionEnd.mLine > lineNo) ssend += mCharAdvance.x;
848
849 if (sstart != -1 && ssend != -1 && sstart < ssend) {
850 ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart,
851 lineStartScreenPos.y);
852 ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend,
853 lineStartScreenPos.y + mCharAdvance.y);
854 drawList->AddRectFilled(vstart, vend,
856 }
857
858 // Draw breakpoints
859 auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y);
860
861 if (mBreakpoints.count(lineNo + 1) != 0) {
862 auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX,
863 lineStartScreenPos.y + mCharAdvance.y);
864 drawList->AddRectFilled(start, end,
866 }
867
868 // Draw error markers
869 auto errorIt = mErrorMarkers.find(lineNo + 1);
870 if (errorIt != mErrorMarkers.end()) {
871 auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX,
872 lineStartScreenPos.y + mCharAdvance.y);
873 drawList->AddRectFilled(start, end,
875
876 if (ImGui::IsMouseHoveringRect(lineStartScreenPos, end)) {
877 ImGui::BeginTooltip();
878 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f));
879 ImGui::Text("Error at line %d:", errorIt->first);
880 ImGui::PopStyleColor();
881 ImGui::Separator();
882 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.2f, 1.0f));
883 ImGui::Text("%s", errorIt->second.c_str());
884 ImGui::PopStyleColor();
885 ImGui::EndTooltip();
886 }
887 }
888
889 // Draw line number (right aligned)
890 snprintf(buf, 16, "%d ", lineNo + 1);
891
892 auto lineNoWidth = ImGui::GetFont()
893 ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX,
894 -1.0f, buf, nullptr, nullptr)
895 .x;
896 drawList->AddText(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth,
897 lineStartScreenPos.y),
899
900 if (mState.mCursorPosition.mLine == lineNo) {
901 auto focused = ImGui::IsWindowFocused();
902
903 // Highlight the current line (where the cursor is)
904 if (!HasSelection()) {
905 auto end = ImVec2(start.x + contentSize.x + scrollX,
906 start.y + mCharAdvance.y);
907 drawList->AddRectFilled(
908 start, end,
911 drawList->AddRect(start, end,
913 }
914
915 // Render the cursor
916 if (focused) {
917 auto timeEnd =
918 std::chrono::duration_cast<std::chrono::milliseconds>(
919 std::chrono::system_clock::now().time_since_epoch())
920 .count();
921 auto elapsed = timeEnd - mStartTime;
922 if (elapsed > 400) {
923 float width = 1.0f;
924 auto cindex = GetCharacterIndex(mState.mCursorPosition);
925 float cx = TextDistanceToLineStart(mState.mCursorPosition);
926
927 if (mOverwrite && cindex < (int)line.size()) {
928 auto c = line[cindex].mChar;
929 if (c == '\t') {
930 auto x = (1.0f + std::floor((1.0f + cx) /
931 (float(mTabSize) * spaceSize))) *
932 (float(mTabSize) * spaceSize);
933 width = x - cx;
934 } else {
935 char buf2[2];
936 buf2[0] = line[cindex].mChar;
937 buf2[1] = '\0';
938 width = ImGui::GetFont()
939 ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX,
940 -1.0f, buf2)
941 .x;
942 }
943 }
944 ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y);
945 ImVec2 cend(textScreenPos.x + cx + width,
946 lineStartScreenPos.y + mCharAdvance.y);
947 drawList->AddRectFilled(cstart, cend,
949 if (elapsed > 800) mStartTime = timeEnd;
950 }
951 }
952 }
953
954 // Render colorized text
955 auto prevColor = line.empty() ? mPalette[(int)PaletteIndex::Default]
956 : GetGlyphColor(line[0]);
957 ImVec2 bufferOffset;
958
959 for (int i = 0; i < line.size();) {
960 auto& glyph = line[i];
961 auto color = GetGlyphColor(glyph);
962
963 if ((color != prevColor || glyph.mChar == '\t' || glyph.mChar == ' ') &&
964 !mLineBuffer.empty()) {
965 const ImVec2 newOffset(textScreenPos.x + bufferOffset.x,
966 textScreenPos.y + bufferOffset.y);
967 drawList->AddText(newOffset, prevColor, mLineBuffer.c_str());
968 auto textSize = ImGui::GetFont()->CalcTextSizeA(
969 ImGui::GetFontSize(), FLT_MAX, -1.0f, mLineBuffer.c_str(),
970 nullptr, nullptr);
971 bufferOffset.x += textSize.x;
972 mLineBuffer.clear();
973 }
974 prevColor = color;
975
976 if (glyph.mChar == '\t') {
977 auto oldX = bufferOffset.x;
978 bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) /
979 (float(mTabSize) * spaceSize))) *
980 (float(mTabSize) * spaceSize);
981 ++i;
982
983 if (mShowWhitespaces) {
984 const auto s = ImGui::GetFontSize();
985 const auto x1 = textScreenPos.x + oldX + 1.0f;
986 const auto x2 = textScreenPos.x + bufferOffset.x - 1.0f;
987 const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f;
988 const ImVec2 p1(x1, y);
989 const ImVec2 p2(x2, y);
990 const ImVec2 p3(x2 - s * 0.2f, y - s * 0.2f);
991 const ImVec2 p4(x2 - s * 0.2f, y + s * 0.2f);
992 drawList->AddLine(p1, p2, 0x90909090);
993 drawList->AddLine(p2, p3, 0x90909090);
994 drawList->AddLine(p2, p4, 0x90909090);
995 }
996 } else if (glyph.mChar == ' ') {
997 if (mShowWhitespaces) {
998 const auto s = ImGui::GetFontSize();
999 const auto x = textScreenPos.x + bufferOffset.x + spaceSize * 0.5f;
1000 const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f;
1001 drawList->AddCircleFilled(ImVec2(x, y), 1.5f, 0x80808080, 4);
1002 }
1003 bufferOffset.x += spaceSize;
1004 i++;
1005 } else {
1006 auto l = UTF8CharLength(glyph.mChar);
1007 while (l-- > 0) mLineBuffer.push_back(line[i++].mChar);
1008 }
1009 ++columnNo;
1010 }
1011
1012 if (!mLineBuffer.empty()) {
1013 const ImVec2 newOffset(textScreenPos.x + bufferOffset.x,
1014 textScreenPos.y + bufferOffset.y);
1015 drawList->AddText(newOffset, prevColor, mLineBuffer.c_str());
1016 mLineBuffer.clear();
1017 }
1018
1019 ++lineNo;
1020 }
1021
1022 // Draw a tooltip on known identifiers/preprocessor symbols
1023 if (ImGui::IsMousePosValid()) {
1024 auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos()));
1025 if (!id.empty()) {
1026 auto it = mLanguageDefinition.mIdentifiers.find(id);
1027 if (it != mLanguageDefinition.mIdentifiers.end()) {
1028 ImGui::BeginTooltip();
1029 ImGui::TextUnformatted(it->second.mDeclaration.c_str());
1030 ImGui::EndTooltip();
1031 } else {
1032 auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id);
1033 if (pi != mLanguageDefinition.mPreprocIdentifiers.end()) {
1034 ImGui::BeginTooltip();
1035 ImGui::TextUnformatted(pi->second.mDeclaration.c_str());
1036 ImGui::EndTooltip();
1037 }
1038 }
1039 }
1040 }
1041 }
1042
1043 ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y));
1044
1045 if (mScrollToCursor) {
1047 ImGui::SetWindowFocus();
1048 mScrollToCursor = false;
1049 }
1050}
1051
1052void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) {
1053 mWithinRender = true;
1054 mTextChanged = false;
1055 mCursorPositionChanged = false;
1056
1057 ImGui::PushStyleColor(
1058 ImGuiCol_ChildBg,
1059 ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background]));
1060 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
1061 if (!mIgnoreImGuiChild)
1062 ImGui::BeginChild(aTitle, aSize, aBorder,
1063 ImGuiWindowFlags_HorizontalScrollbar |
1064 ImGuiWindowFlags_AlwaysHorizontalScrollbar |
1065 ImGuiWindowFlags_NoMove);
1066
1069 ImGui::PushAllowKeyboardFocus(true);
1070 }
1071
1073
1075 Render();
1076
1077 if (mHandleKeyboardInputs) ImGui::PopAllowKeyboardFocus();
1078
1079 if (!mIgnoreImGuiChild) ImGui::EndChild();
1080
1081 ImGui::PopStyleVar();
1082 ImGui::PopStyleColor();
1083
1084 mWithinRender = false;
1085}
1086
1087void TextEditor::SetText(const std::string& aText) {
1088 mLines.clear();
1089 mLines.emplace_back(Line());
1090 for (auto chr : aText) {
1091 if (chr == '\r') {
1092 // ignore the carriage return character
1093 } else if (chr == '\n')
1094 mLines.emplace_back(Line());
1095 else {
1096 mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default));
1097 }
1098 }
1099
1100 mTextChanged = true;
1101 mScrollToTop = true;
1102
1103 mUndoBuffer.clear();
1104 mUndoIndex = 0;
1105
1106 Colorize();
1107}
1108
1109void TextEditor::SetTextLines(const std::vector<std::string>& aLines) {
1110 mLines.clear();
1111
1112 if (aLines.empty()) {
1113 mLines.emplace_back(Line());
1114 } else {
1115 mLines.resize(aLines.size());
1116
1117 for (size_t i = 0; i < aLines.size(); ++i) {
1118 const std::string& aLine = aLines[i];
1119
1120 mLines[i].reserve(aLine.size());
1121 for (size_t j = 0; j < aLine.size(); ++j)
1122 mLines[i].emplace_back(Glyph(aLine[j], PaletteIndex::Default));
1123 }
1124 }
1125
1126 mTextChanged = true;
1127 mScrollToTop = true;
1128
1129 mUndoBuffer.clear();
1130 mUndoIndex = 0;
1131
1132 Colorize();
1133}
1134
1135void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) {
1136 assert(!mReadOnly);
1137
1138 UndoRecord u;
1139
1140 u.mBefore = mState;
1141
1142 if (HasSelection()) {
1143 if (aChar == '\t' &&
1144 mState.mSelectionStart.mLine != mState.mSelectionEnd.mLine) {
1145 auto start = mState.mSelectionStart;
1146 auto end = mState.mSelectionEnd;
1147 auto originalEnd = end;
1148
1149 if (start > end) std::swap(start, end);
1150 start.mColumn = 0;
1151 // end.mColumn = end.mLine < mLines.size() ?
1152 //mLines[end.mLine].size() : 0;
1153 if (end.mColumn == 0 && end.mLine > 0) --end.mLine;
1154 if (end.mLine >= (int)mLines.size())
1155 end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1;
1156 end.mColumn = GetLineMaxColumn(end.mLine);
1157
1158 // if (end.mColumn >= GetLineMaxColumn(end.mLine))
1159 // end.mColumn = GetLineMaxColumn(end.mLine) - 1;
1160
1161 u.mRemovedStart = start;
1162 u.mRemovedEnd = end;
1163 u.mRemoved = GetText(start, end);
1164
1165 bool modified = false;
1166
1167 for (int i = start.mLine; i <= end.mLine; i++) {
1168 auto& line = mLines[i];
1169 if (aShift) {
1170 if (!line.empty()) {
1171 if (line.front().mChar == '\t') {
1172 line.erase(line.begin());
1173 modified = true;
1174 } else {
1175 for (int j = 0;
1176 j < mTabSize && !line.empty() && line.front().mChar == ' ';
1177 j++) {
1178 line.erase(line.begin());
1179 modified = true;
1180 }
1181 }
1182 }
1183 } else {
1184 line.insert(line.begin(),
1186 modified = true;
1187 }
1188 }
1189
1190 if (modified) {
1191 start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0));
1192 Coordinates rangeEnd;
1193 if (originalEnd.mColumn != 0) {
1194 end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine));
1195 rangeEnd = end;
1196 u.mAdded = GetText(start, end);
1197 } else {
1198 end = Coordinates(originalEnd.mLine, 0);
1199 rangeEnd =
1200 Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1));
1201 u.mAdded = GetText(start, rangeEnd);
1202 }
1203
1204 u.mAddedStart = start;
1205 u.mAddedEnd = rangeEnd;
1206 u.mAfter = mState;
1207
1208 mState.mSelectionStart = start;
1209 mState.mSelectionEnd = end;
1210 AddUndo(u);
1211
1212 mTextChanged = true;
1213
1215 }
1216
1217 return;
1218 } // c == '\t'
1219 else {
1221 u.mRemovedStart = mState.mSelectionStart;
1222 u.mRemovedEnd = mState.mSelectionEnd;
1224 }
1225 } // HasSelection
1226
1227 auto coord = GetActualCursorCoordinates();
1228 u.mAddedStart = coord;
1229
1230 assert(!mLines.empty());
1231
1232 if (aChar == '\n') {
1233 InsertLine(coord.mLine + 1);
1234 auto& line = mLines[coord.mLine];
1235 auto& newLine = mLines[coord.mLine + 1];
1236
1237 if (mLanguageDefinition.mAutoIndentation)
1238 for (size_t it = 0; it < line.size() && isascii(line[it].mChar) &&
1239 isblank(line[it].mChar);
1240 ++it)
1241 newLine.push_back(line[it]);
1242
1243 const size_t whitespaceSize = newLine.size();
1244 auto cindex = GetCharacterIndex(coord);
1245 newLine.insert(newLine.end(), line.begin() + cindex, line.end());
1246 line.erase(line.begin() + cindex, line.begin() + line.size());
1248 Coordinates(coord.mLine + 1,
1249 GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize)));
1250 u.mAdded = (char)aChar;
1251 } else {
1252 char buf[7];
1253 int e = ImTextCharToUtf8(buf, 7, aChar);
1254 if (e > 0) {
1255 buf[e] = '\0';
1256 auto& line = mLines[coord.mLine];
1257 auto cindex = GetCharacterIndex(coord);
1258
1259 if (mOverwrite && cindex < (int)line.size()) {
1260 auto d = UTF8CharLength(line[cindex].mChar);
1261
1262 u.mRemovedStart = mState.mCursorPosition;
1264 coord.mLine, GetCharacterColumn(coord.mLine, cindex + d));
1265
1266 while (d-- > 0 && cindex < (int)line.size()) {
1267 u.mRemoved += line[cindex].mChar;
1268 line.erase(line.begin() + cindex);
1269 }
1270 }
1271
1272 for (auto p = buf; *p != '\0'; p++, ++cindex)
1273 line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default));
1274 u.mAdded = buf;
1275
1277 Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex)));
1278 } else
1279 return;
1280 }
1281
1282 mTextChanged = true;
1283
1285 u.mAfter = mState;
1286
1287 AddUndo(u);
1288
1289 Colorize(coord.mLine - 1, 3);
1291}
1292
1293void TextEditor::SetReadOnly(bool aValue) { mReadOnly = aValue; }
1294
1296
1298 if (mState.mCursorPosition != aPosition) {
1299 mState.mCursorPosition = aPosition;
1302 }
1303}
1304
1306 mState.mSelectionStart = SanitizeCoordinates(aPosition);
1307 if (mState.mSelectionStart > mState.mSelectionEnd)
1308 std::swap(mState.mSelectionStart, mState.mSelectionEnd);
1309}
1310
1312 mState.mSelectionEnd = SanitizeCoordinates(aPosition);
1313 if (mState.mSelectionStart > mState.mSelectionEnd)
1314 std::swap(mState.mSelectionStart, mState.mSelectionEnd);
1315}
1316
1318 const Coordinates& aEnd, SelectionMode aMode) {
1319 auto oldSelStart = mState.mSelectionStart;
1320 auto oldSelEnd = mState.mSelectionEnd;
1321
1322 mState.mSelectionStart = SanitizeCoordinates(aStart);
1323 mState.mSelectionEnd = SanitizeCoordinates(aEnd);
1324 if (mState.mSelectionStart > mState.mSelectionEnd)
1325 std::swap(mState.mSelectionStart, mState.mSelectionEnd);
1326
1327 switch (aMode) {
1329 break;
1331 mState.mSelectionStart = FindWordStart(mState.mSelectionStart);
1332 if (!IsOnWordBoundary(mState.mSelectionEnd))
1333 mState.mSelectionEnd = FindWordEnd(FindWordStart(mState.mSelectionEnd));
1334 break;
1335 }
1337 const auto lineNo = mState.mSelectionEnd.mLine;
1338 const auto lineSize =
1339 (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0;
1340 mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0);
1341 mState.mSelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo));
1342 break;
1343 }
1344 default:
1345 break;
1346 }
1347
1348 if (mState.mSelectionStart != oldSelStart ||
1349 mState.mSelectionEnd != oldSelEnd)
1351}
1352
1353void TextEditor::SetTabSize(int aValue) {
1354 mTabSize = std::max(0, std::min(32, aValue));
1355}
1356
1357void TextEditor::InsertText(const std::string& aValue) {
1358 InsertText(aValue.c_str());
1359}
1360
1361void TextEditor::InsertText(const char* aValue) {
1362 if (aValue == nullptr) return;
1363
1364 auto pos = GetActualCursorCoordinates();
1365 auto start = std::min(pos, mState.mSelectionStart);
1366 int totalLines = pos.mLine - start.mLine;
1367
1368 totalLines += InsertTextAt(pos, aValue);
1369
1370 SetSelection(pos, pos);
1371 SetCursorPosition(pos);
1372 Colorize(start.mLine - 1, totalLines + 2);
1373}
1374
1376 assert(mState.mSelectionEnd >= mState.mSelectionStart);
1377
1378 if (mState.mSelectionEnd == mState.mSelectionStart) return;
1379
1380 DeleteRange(mState.mSelectionStart, mState.mSelectionEnd);
1381
1382 SetSelection(mState.mSelectionStart, mState.mSelectionStart);
1383 SetCursorPosition(mState.mSelectionStart);
1384 Colorize(mState.mSelectionStart.mLine, 1);
1385}
1386
1387void TextEditor::MoveUp(int aAmount, bool aSelect) {
1388 auto oldPos = mState.mCursorPosition;
1389 mState.mCursorPosition.mLine =
1390 std::max(0, mState.mCursorPosition.mLine - aAmount);
1391 if (oldPos != mState.mCursorPosition) {
1392 if (aSelect) {
1393 if (oldPos == mInteractiveStart)
1394 mInteractiveStart = mState.mCursorPosition;
1395 else if (oldPos == mInteractiveEnd)
1396 mInteractiveEnd = mState.mCursorPosition;
1397 else {
1398 mInteractiveStart = mState.mCursorPosition;
1399 mInteractiveEnd = oldPos;
1400 }
1401 } else
1402 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1404
1406 }
1407}
1408
1409void TextEditor::MoveDown(int aAmount, bool aSelect) {
1410 assert(mState.mCursorPosition.mColumn >= 0);
1411 auto oldPos = mState.mCursorPosition;
1412 mState.mCursorPosition.mLine = std::max(
1413 0,
1414 std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount));
1415
1416 if (mState.mCursorPosition != oldPos) {
1417 if (aSelect) {
1418 if (oldPos == mInteractiveEnd)
1419 mInteractiveEnd = mState.mCursorPosition;
1420 else if (oldPos == mInteractiveStart)
1421 mInteractiveStart = mState.mCursorPosition;
1422 else {
1423 mInteractiveStart = oldPos;
1424 mInteractiveEnd = mState.mCursorPosition;
1425 }
1426 } else
1427 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1429
1431 }
1432}
1433
1434static bool IsUTFSequence(char c) { return (c & 0xC0) == 0x80; }
1435
1436void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) {
1437 if (mLines.empty()) return;
1438
1439 auto oldPos = mState.mCursorPosition;
1440 mState.mCursorPosition = GetActualCursorCoordinates();
1441 auto line = mState.mCursorPosition.mLine;
1442 auto cindex = GetCharacterIndex(mState.mCursorPosition);
1443
1444 while (aAmount-- > 0) {
1445 if (cindex == 0) {
1446 if (line > 0) {
1447 --line;
1448 if ((int)mLines.size() > line)
1449 cindex = (int)mLines[line].size();
1450 else
1451 cindex = 0;
1452 }
1453 } else {
1454 --cindex;
1455 if (cindex > 0) {
1456 if ((int)mLines.size() > line) {
1457 while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar))
1458 --cindex;
1459 }
1460 }
1461 }
1462
1463 mState.mCursorPosition =
1464 Coordinates(line, GetCharacterColumn(line, cindex));
1465 if (aWordMode) {
1466 mState.mCursorPosition = FindWordStart(mState.mCursorPosition);
1467 cindex = GetCharacterIndex(mState.mCursorPosition);
1468 }
1469 }
1470
1471 mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex));
1472
1473 assert(mState.mCursorPosition.mColumn >= 0);
1474 if (aSelect) {
1475 if (oldPos == mInteractiveStart)
1476 mInteractiveStart = mState.mCursorPosition;
1477 else if (oldPos == mInteractiveEnd)
1478 mInteractiveEnd = mState.mCursorPosition;
1479 else {
1480 mInteractiveStart = mState.mCursorPosition;
1481 mInteractiveEnd = oldPos;
1482 }
1483 } else
1484 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1487 aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);
1488
1490}
1491
1492void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) {
1493 auto oldPos = mState.mCursorPosition;
1494
1495 if (mLines.empty() || oldPos.mLine >= mLines.size()) return;
1496
1497 auto cindex = GetCharacterIndex(mState.mCursorPosition);
1498 while (aAmount-- > 0) {
1499 auto lindex = mState.mCursorPosition.mLine;
1500 auto& line = mLines[lindex];
1501
1502 if (cindex >= line.size()) {
1503 if (mState.mCursorPosition.mLine < mLines.size() - 1) {
1504 mState.mCursorPosition.mLine = std::max(
1505 0,
1506 std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + 1));
1507 mState.mCursorPosition.mColumn = 0;
1508 } else
1509 return;
1510 } else {
1511 cindex += UTF8CharLength(line[cindex].mChar);
1512 mState.mCursorPosition =
1513 Coordinates(lindex, GetCharacterColumn(lindex, cindex));
1514 if (aWordMode)
1515 mState.mCursorPosition = FindNextWord(mState.mCursorPosition);
1516 }
1517 }
1518
1519 if (aSelect) {
1520 if (oldPos == mInteractiveEnd)
1521 mInteractiveEnd = SanitizeCoordinates(mState.mCursorPosition);
1522 else if (oldPos == mInteractiveStart)
1523 mInteractiveStart = mState.mCursorPosition;
1524 else {
1525 mInteractiveStart = oldPos;
1526 mInteractiveEnd = mState.mCursorPosition;
1527 }
1528 } else
1529 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1532 aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);
1533
1535}
1536
1537void TextEditor::MoveTop(bool aSelect) {
1538 auto oldPos = mState.mCursorPosition;
1540
1541 if (mState.mCursorPosition != oldPos) {
1542 if (aSelect) {
1543 mInteractiveEnd = oldPos;
1544 mInteractiveStart = mState.mCursorPosition;
1545 } else
1546 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1548 }
1549}
1550
1551void TextEditor::TextEditor::MoveBottom(bool aSelect) {
1552 auto oldPos = GetCursorPosition();
1553 auto newPos = Coordinates((int)mLines.size() - 1, 0);
1554 SetCursorPosition(newPos);
1555 if (aSelect) {
1556 mInteractiveStart = oldPos;
1557 mInteractiveEnd = newPos;
1558 } else
1559 mInteractiveStart = mInteractiveEnd = newPos;
1560 SetSelection(mInteractiveStart, mInteractiveEnd);
1561}
1562
1563void TextEditor::MoveHome(bool aSelect) {
1564 auto oldPos = mState.mCursorPosition;
1565 SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, 0));
1566
1567 if (mState.mCursorPosition != oldPos) {
1568 if (aSelect) {
1569 if (oldPos == mInteractiveStart)
1570 mInteractiveStart = mState.mCursorPosition;
1571 else if (oldPos == mInteractiveEnd)
1572 mInteractiveEnd = mState.mCursorPosition;
1573 else {
1574 mInteractiveStart = mState.mCursorPosition;
1575 mInteractiveEnd = oldPos;
1576 }
1577 } else
1578 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1580 }
1581}
1582
1583void TextEditor::MoveEnd(bool aSelect) {
1584 auto oldPos = mState.mCursorPosition;
1585 SetCursorPosition(Coordinates(mState.mCursorPosition.mLine,
1586 GetLineMaxColumn(oldPos.mLine)));
1587
1588 if (mState.mCursorPosition != oldPos) {
1589 if (aSelect) {
1590 if (oldPos == mInteractiveEnd)
1591 mInteractiveEnd = mState.mCursorPosition;
1592 else if (oldPos == mInteractiveStart)
1593 mInteractiveStart = mState.mCursorPosition;
1594 else {
1595 mInteractiveStart = oldPos;
1596 mInteractiveEnd = mState.mCursorPosition;
1597 }
1598 } else
1599 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1601 }
1602}
1603
1605 assert(!mReadOnly);
1606
1607 if (mLines.empty()) return;
1608
1609 UndoRecord u;
1610 u.mBefore = mState;
1611
1612 if (HasSelection()) {
1614 u.mRemovedStart = mState.mSelectionStart;
1615 u.mRemovedEnd = mState.mSelectionEnd;
1616
1618 } else {
1619 auto pos = GetActualCursorCoordinates();
1620 SetCursorPosition(pos);
1621 auto& line = mLines[pos.mLine];
1622
1623 if (pos.mColumn == GetLineMaxColumn(pos.mLine)) {
1624 if (pos.mLine == (int)mLines.size() - 1) return;
1625
1626 u.mRemoved = '\n';
1629
1630 auto& nextLine = mLines[pos.mLine + 1];
1631 line.insert(line.end(), nextLine.begin(), nextLine.end());
1632 RemoveLine(pos.mLine + 1);
1633 } else {
1634 auto cindex = GetCharacterIndex(pos);
1636 u.mRemovedEnd.mColumn++;
1638
1639 auto d = UTF8CharLength(line[cindex].mChar);
1640 while (d-- > 0 && cindex < (int)line.size())
1641 line.erase(line.begin() + cindex);
1642 }
1643
1644 mTextChanged = true;
1645
1646 Colorize(pos.mLine, 1);
1647 }
1648
1649 u.mAfter = mState;
1650 AddUndo(u);
1651}
1652
1654 assert(!mReadOnly);
1655
1656 if (mLines.empty()) return;
1657
1658 UndoRecord u;
1659 u.mBefore = mState;
1660
1661 if (HasSelection()) {
1663 u.mRemovedStart = mState.mSelectionStart;
1664 u.mRemovedEnd = mState.mSelectionEnd;
1665
1667 } else {
1668 auto pos = GetActualCursorCoordinates();
1669 SetCursorPosition(pos);
1670
1671 if (mState.mCursorPosition.mColumn == 0) {
1672 if (mState.mCursorPosition.mLine == 0) return;
1673
1674 u.mRemoved = '\n';
1676 Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1));
1678
1679 auto& line = mLines[mState.mCursorPosition.mLine];
1680 auto& prevLine = mLines[mState.mCursorPosition.mLine - 1];
1681 auto prevSize = GetLineMaxColumn(mState.mCursorPosition.mLine - 1);
1682 prevLine.insert(prevLine.end(), line.begin(), line.end());
1683
1684 ErrorMarkers etmp;
1685 for (auto& i : mErrorMarkers)
1686 etmp.insert(ErrorMarkers::value_type(
1687 i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first,
1688 i.second));
1689 mErrorMarkers = std::move(etmp);
1690
1691 RemoveLine(mState.mCursorPosition.mLine);
1692 --mState.mCursorPosition.mLine;
1693 mState.mCursorPosition.mColumn = prevSize;
1694 } else {
1695 auto& line = mLines[mState.mCursorPosition.mLine];
1696 auto cindex = GetCharacterIndex(pos) - 1;
1697 auto cend = cindex + 1;
1698 while (cindex > 0 && IsUTFSequence(line[cindex].mChar)) --cindex;
1699
1700 // if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1)
1701 // --cindex;
1702
1705 --mState.mCursorPosition.mColumn;
1706
1707 while (cindex < line.size() && cend-- > cindex) {
1708 u.mRemoved += line[cindex].mChar;
1709 line.erase(line.begin() + cindex);
1710 }
1711 }
1712
1713 mTextChanged = true;
1714
1716 Colorize(mState.mCursorPosition.mLine, 1);
1717 }
1718
1719 u.mAfter = mState;
1720 AddUndo(u);
1721}
1722
1727
1729 SetSelection(Coordinates(0, 0), Coordinates((int)mLines.size(), 0));
1730}
1731
1733 return mState.mSelectionEnd > mState.mSelectionStart;
1734}
1735
1737 if (HasSelection()) {
1738 ImGui::SetClipboardText(GetSelectedText().c_str());
1739 } else {
1740 if (!mLines.empty()) {
1741 std::string str;
1742 auto& line = mLines[GetActualCursorCoordinates().mLine];
1743 for (auto& g : line) str.push_back(g.mChar);
1744 ImGui::SetClipboardText(str.c_str());
1745 }
1746 }
1747}
1748
1750 if (IsReadOnly()) {
1751 Copy();
1752 } else {
1753 if (HasSelection()) {
1754 UndoRecord u;
1755 u.mBefore = mState;
1757 u.mRemovedStart = mState.mSelectionStart;
1758 u.mRemovedEnd = mState.mSelectionEnd;
1759
1760 Copy();
1762
1763 u.mAfter = mState;
1764 AddUndo(u);
1765 }
1766 }
1767}
1768
1770 if (IsReadOnly()) return;
1771
1772 auto clipText = ImGui::GetClipboardText();
1773 if (clipText != nullptr && strlen(clipText) > 0) {
1774 UndoRecord u;
1775 u.mBefore = mState;
1776
1777 if (HasSelection()) {
1779 u.mRemovedStart = mState.mSelectionStart;
1780 u.mRemovedEnd = mState.mSelectionEnd;
1782 }
1783
1784 u.mAdded = clipText;
1786
1787 InsertText(clipText);
1788
1790 u.mAfter = mState;
1791 AddUndo(u);
1792 }
1793}
1794
1795bool TextEditor::CanUndo() const { return !mReadOnly && mUndoIndex > 0; }
1796
1798 return !mReadOnly && mUndoIndex < (int)mUndoBuffer.size();
1799}
1800
1801void TextEditor::Undo(int aSteps) {
1802 while (CanUndo() && aSteps-- > 0) mUndoBuffer[--mUndoIndex].Undo(this);
1803}
1804
1805void TextEditor::Redo(int aSteps) {
1806 while (CanRedo() && aSteps-- > 0) mUndoBuffer[mUndoIndex++].Redo(this);
1807}
1808
1810 const static Palette p = {{
1811 0xff7f7f7f, // Default
1812 0xffd69c56, // Keyword
1813 0xff00ff00, // Number
1814 0xff7070e0, // String
1815 0xff70a0e0, // Char literal
1816 0xffffffff, // Punctuation
1817 0xff408080, // Preprocessor
1818 0xffaaaaaa, // Identifier
1819 0xff9bc64d, // Known identifier
1820 0xffc040a0, // Preproc identifier
1821 0xff206020, // Comment (single line)
1822 0xff406020, // Comment (multi line)
1823 0xff101010, // Background
1824 0xffe0e0e0, // Cursor
1825 0x80a06020, // Selection
1826 0x800020ff, // ErrorMarker
1827 0x40f08000, // Breakpoint
1828 0xff707000, // Line number
1829 0x40000000, // Current line fill
1830 0x40808080, // Current line fill (inactive)
1831 0x40a0a0a0, // Current line edge
1832 }};
1833 return p;
1834}
1835
1837 const static Palette p = {{
1838 0xff7f7f7f, // None
1839 0xffff0c06, // Keyword
1840 0xff008000, // Number
1841 0xff2020a0, // String
1842 0xff304070, // Char literal
1843 0xff000000, // Punctuation
1844 0xff406060, // Preprocessor
1845 0xff404040, // Identifier
1846 0xff606010, // Known identifier
1847 0xffc040a0, // Preproc identifier
1848 0xff205020, // Comment (single line)
1849 0xff405020, // Comment (multi line)
1850 0xffffffff, // Background
1851 0xff000000, // Cursor
1852 0x80600000, // Selection
1853 0xa00010ff, // ErrorMarker
1854 0x80f08000, // Breakpoint
1855 0xff505000, // Line number
1856 0x40000000, // Current line fill
1857 0x40808080, // Current line fill (inactive)
1858 0x40000000, // Current line edge
1859 }};
1860 return p;
1861}
1862
1864 const static Palette p = {{
1865 0xff00ffff, // None
1866 0xffffff00, // Keyword
1867 0xff00ff00, // Number
1868 0xff808000, // String
1869 0xff808000, // Char literal
1870 0xffffffff, // Punctuation
1871 0xff008000, // Preprocessor
1872 0xff00ffff, // Identifier
1873 0xffffffff, // Known identifier
1874 0xffff00ff, // Preproc identifier
1875 0xff808080, // Comment (single line)
1876 0xff404040, // Comment (multi line)
1877 0xff800000, // Background
1878 0xff0080ff, // Cursor
1879 0x80ffff00, // Selection
1880 0xa00000ff, // ErrorMarker
1881 0x80ff8000, // Breakpoint
1882 0xff808000, // Line number
1883 0x40000000, // Current line fill
1884 0x40808080, // Current line fill (inactive)
1885 0x40000000, // Current line edge
1886 }};
1887 return p;
1888}
1889
1890std::string TextEditor::GetText() const {
1891 return GetText(Coordinates(), Coordinates((int)mLines.size(), 0));
1892}
1893
1894std::vector<std::string> TextEditor::GetTextLines() const {
1895 std::vector<std::string> result;
1896
1897 result.reserve(mLines.size());
1898
1899 for (auto& line : mLines) {
1900 std::string text;
1901
1902 text.resize(line.size());
1903
1904 for (size_t i = 0; i < line.size(); ++i) text[i] = line[i].mChar;
1905
1906 result.emplace_back(std::move(text));
1907 }
1908
1909 return result;
1910}
1911
1912std::string TextEditor::GetSelectedText() const {
1913 return GetText(mState.mSelectionStart, mState.mSelectionEnd);
1914}
1915
1917 auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine);
1918 return GetText(Coordinates(mState.mCursorPosition.mLine, 0),
1919 Coordinates(mState.mCursorPosition.mLine, lineLength));
1920}
1921
1923
1924void TextEditor::Colorize(int aFromLine, int aLines) {
1925 int toLine = aLines == -1 ? (int)mLines.size()
1926 : std::min((int)mLines.size(), aFromLine + aLines);
1927 mColorRangeMin = std::min(mColorRangeMin, aFromLine);
1928 mColorRangeMax = std::max(mColorRangeMax, toLine);
1929 mColorRangeMin = std::max(0, mColorRangeMin);
1931 mCheckComments = true;
1932}
1933
1934void TextEditor::ColorizeRange(int aFromLine, int aToLine) {
1935 if (mLines.empty() || aFromLine >= aToLine) return;
1936
1937 std::string buffer;
1938 std::cmatch results;
1939 std::string id;
1940
1941 int endLine = std::max(0, std::min((int)mLines.size(), aToLine));
1942 for (int i = aFromLine; i < endLine; ++i) {
1943 auto& line = mLines[i];
1944
1945 if (line.empty()) continue;
1946
1947 buffer.resize(line.size());
1948 for (size_t j = 0; j < line.size(); ++j) {
1949 auto& col = line[j];
1950 buffer[j] = col.mChar;
1951 col.mColorIndex = PaletteIndex::Default;
1952 }
1953
1954 const char* bufferBegin = &buffer.front();
1955 const char* bufferEnd = bufferBegin + buffer.size();
1956
1957 auto last = bufferEnd;
1958
1959 for (auto first = bufferBegin; first != last;) {
1960 const char* token_begin = nullptr;
1961 const char* token_end = nullptr;
1962 PaletteIndex token_color = PaletteIndex::Default;
1963
1964 bool hasTokenizeResult = false;
1965
1966 if (mLanguageDefinition.mTokenize != nullptr) {
1967 if (mLanguageDefinition.mTokenize(first, last, token_begin, token_end,
1968 token_color))
1969 hasTokenizeResult = true;
1970 }
1971
1972 if (hasTokenizeResult == false) {
1973 // todo : remove
1974 // printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last -
1975 // first), first);
1976
1977 for (auto& p : mRegexList) {
1978 if (std::regex_search(first, last, results, p.first,
1979 std::regex_constants::match_continuous)) {
1980 hasTokenizeResult = true;
1981
1982 auto& v = *results.begin();
1983 token_begin = v.first;
1984 token_end = v.second;
1985 token_color = p.second;
1986 break;
1987 }
1988 }
1989 }
1990
1991 if (hasTokenizeResult == false) {
1992 first++;
1993 } else {
1994 const size_t token_length = token_end - token_begin;
1995
1996 if (token_color == PaletteIndex::Identifier) {
1997 id.assign(token_begin, token_end);
1998
1999 // todo : allmost all language definitions use lower case to specify
2000 // keywords, so shouldn't this use ::tolower ?
2001 if (!mLanguageDefinition.mCaseSensitive)
2002 std::transform(id.begin(), id.end(), id.begin(), ::toupper);
2003
2004 if (!line[first - bufferBegin].mPreprocessor) {
2005 if (mLanguageDefinition.mKeywords.count(id) != 0)
2006 token_color = PaletteIndex::Keyword;
2007 else if (mLanguageDefinition.mIdentifiers.count(id) != 0)
2008 token_color = PaletteIndex::KnownIdentifier;
2009 else if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0)
2010 token_color = PaletteIndex::PreprocIdentifier;
2011 } else {
2012 if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0)
2013 token_color = PaletteIndex::PreprocIdentifier;
2014 }
2015 }
2016
2017 for (size_t j = 0; j < token_length; ++j)
2018 line[(token_begin - bufferBegin) + j].mColorIndex = token_color;
2019
2020 first = token_end;
2021 }
2022 }
2023 }
2024}
2025
2027 if (mLines.empty() || !mColorizerEnabled) return;
2028
2029 if (mCheckComments) {
2030 auto endLine = mLines.size();
2031 auto endIndex = 0;
2032 auto commentStartLine = endLine;
2033 auto commentStartIndex = endIndex;
2034 auto withinString = false;
2035 auto withinSingleLineComment = false;
2036 auto withinPreproc = false;
2037 auto firstChar =
2038 true; // there is no other non-whitespace characters in the line before
2039 auto concatenate = false; // '\' on the very end of the line
2040 auto currentLine = 0;
2041 auto currentIndex = 0;
2042 while (currentLine < endLine || currentIndex < endIndex) {
2043 auto& line = mLines[currentLine];
2044
2045 if (currentIndex == 0 && !concatenate) {
2046 withinSingleLineComment = false;
2047 withinPreproc = false;
2048 firstChar = true;
2049 }
2050
2051 concatenate = false;
2052
2053 if (!line.empty()) {
2054 auto& g = line[currentIndex];
2055 auto c = g.mChar;
2056
2057 if (c != mLanguageDefinition.mPreprocChar && !isspace(c))
2058 firstChar = false;
2059
2060 if (currentIndex == (int)line.size() - 1 &&
2061 line[line.size() - 1].mChar == '\\')
2062 concatenate = true;
2063
2064 bool inComment = (commentStartLine < currentLine ||
2065 (commentStartLine == currentLine &&
2066 commentStartIndex <= currentIndex));
2067
2068 if (withinString) {
2069 line[currentIndex].mMultiLineComment = inComment;
2070
2071 if (c == '\"') {
2072 if (currentIndex + 1 < (int)line.size() &&
2073 line[currentIndex + 1].mChar == '\"') {
2074 currentIndex += 1;
2075 if (currentIndex < (int)line.size())
2076 line[currentIndex].mMultiLineComment = inComment;
2077 } else
2078 withinString = false;
2079 } else if (c == '\\') {
2080 currentIndex += 1;
2081 if (currentIndex < (int)line.size())
2082 line[currentIndex].mMultiLineComment = inComment;
2083 }
2084 } else {
2085 if (firstChar && c == mLanguageDefinition.mPreprocChar)
2086 withinPreproc = true;
2087
2088 if (c == '\"') {
2089 withinString = true;
2090 line[currentIndex].mMultiLineComment = inComment;
2091 } else {
2092 auto pred = [](const char& a, const Glyph& b) {
2093 return a == b.mChar;
2094 };
2095 auto from = line.begin() + currentIndex;
2096 auto& startStr = mLanguageDefinition.mCommentStart;
2097 auto& singleStartStr = mLanguageDefinition.mSingleLineComment;
2098
2099 if (singleStartStr.size() > 0 &&
2100 currentIndex + singleStartStr.size() <= line.size() &&
2101 equals(singleStartStr.begin(), singleStartStr.end(), from,
2102 from + singleStartStr.size(), pred)) {
2103 withinSingleLineComment = true;
2104 } else if (!withinSingleLineComment &&
2105 currentIndex + startStr.size() <= line.size() &&
2106 equals(startStr.begin(), startStr.end(), from,
2107 from + startStr.size(), pred)) {
2108 commentStartLine = currentLine;
2109 commentStartIndex = currentIndex;
2110 }
2111
2112 inComment = inComment = (commentStartLine < currentLine ||
2113 (commentStartLine == currentLine &&
2114 commentStartIndex <= currentIndex));
2115
2116 line[currentIndex].mMultiLineComment = inComment;
2117 line[currentIndex].mComment = withinSingleLineComment;
2118
2119 auto& endStr = mLanguageDefinition.mCommentEnd;
2120 if (currentIndex + 1 >= (int)endStr.size() &&
2121 equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(),
2122 from + 1, pred)) {
2123 commentStartIndex = endIndex;
2124 commentStartLine = endLine;
2125 }
2126 }
2127 }
2128 line[currentIndex].mPreprocessor = withinPreproc;
2129 currentIndex += UTF8CharLength(c);
2130 if (currentIndex >= (int)line.size()) {
2131 currentIndex = 0;
2132 ++currentLine;
2133 }
2134 } else {
2135 currentIndex = 0;
2136 ++currentLine;
2137 }
2138 }
2139 mCheckComments = false;
2140 }
2141
2143 const int increment =
2144 (mLanguageDefinition.mTokenize == nullptr) ? 10 : 10000;
2145 const int to = std::min(mColorRangeMin + increment, mColorRangeMax);
2147 mColorRangeMin = to;
2148
2150 mColorRangeMin = std::numeric_limits<int>::max();
2151 mColorRangeMax = 0;
2152 }
2153 return;
2154 }
2155}
2156
2158 auto& line = mLines[aFrom.mLine];
2159 float distance = 0.0f;
2160 float spaceSize = ImGui::GetFont()
2161 ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f,
2162 " ", nullptr, nullptr)
2163 .x;
2164 int colIndex = GetCharacterIndex(aFrom);
2165 for (size_t it = 0u; it < line.size() && it < colIndex;) {
2166 if (line[it].mChar == '\t') {
2167 distance = (1.0f + std::floor((1.0f + distance) /
2168 (float(mTabSize) * spaceSize))) *
2169 (float(mTabSize) * spaceSize);
2170 ++it;
2171 } else {
2172 auto d = UTF8CharLength(line[it].mChar);
2173 char tempCString[7];
2174 int i = 0;
2175 for (; i < 6 && d-- > 0 && it < (int)line.size(); i++, it++)
2176 tempCString[i] = line[it].mChar;
2177
2178 tempCString[i] = '\0';
2179 distance += ImGui::GetFont()
2180 ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f,
2181 tempCString, nullptr, nullptr)
2182 .x;
2183 }
2184 }
2185
2186 return distance;
2187}
2188
2190 if (!mWithinRender) {
2191 mScrollToCursor = true;
2192 return;
2193 }
2194
2195 float scrollX = ImGui::GetScrollX();
2196 float scrollY = ImGui::GetScrollY();
2197
2198 auto height = ImGui::GetWindowHeight();
2199 auto width = ImGui::GetWindowWidth();
2200
2201 auto top = 1 + (int)ceil(scrollY / mCharAdvance.y);
2202 auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y);
2203
2204 auto left = (int)ceil(scrollX / mCharAdvance.x);
2205 auto right = (int)ceil((scrollX + width) / mCharAdvance.x);
2206
2207 auto pos = GetActualCursorCoordinates();
2208 auto len = TextDistanceToLineStart(pos);
2209
2210 if (pos.mLine < top)
2211 ImGui::SetScrollY(std::max(0.0f, (pos.mLine - 1) * mCharAdvance.y));
2212 if (pos.mLine > bottom - 4)
2213 ImGui::SetScrollY(
2214 std::max(0.0f, (pos.mLine + 4) * mCharAdvance.y - height));
2215 if (len + mTextStart < left + 4)
2216 ImGui::SetScrollX(std::max(0.0f, len + mTextStart - 4));
2217 if (len + mTextStart > right - 4)
2218 ImGui::SetScrollX(std::max(0.0f, len + mTextStart + 4 - width));
2219}
2220
2222 auto height = ImGui::GetWindowHeight() - 20.0f;
2223 return (int)floor(height / mCharAdvance.y);
2224}
2225
2226TextEditor::UndoRecord::UndoRecord(const std::string& aAdded,
2227 const TextEditor::Coordinates aAddedStart,
2228 const TextEditor::Coordinates aAddedEnd,
2229 const std::string& aRemoved,
2230 const TextEditor::Coordinates aRemovedStart,
2231 const TextEditor::Coordinates aRemovedEnd,
2232 TextEditor::EditorState& aBefore,
2234 : mAdded(aAdded),
2235 mAddedStart(aAddedStart),
2236 mAddedEnd(aAddedEnd),
2237 mRemoved(aRemoved),
2238 mRemovedStart(aRemovedStart),
2239 mRemovedEnd(aRemovedEnd),
2240 mBefore(aBefore),
2241 mAfter(aAfter) {
2242 assert(mAddedStart <= mAddedEnd);
2243 assert(mRemovedStart <= mRemovedEnd);
2244}
2245
2247 if (!mAdded.empty()) {
2249 aEditor->Colorize(mAddedStart.mLine - 1,
2250 mAddedEnd.mLine - mAddedStart.mLine + 2);
2251 }
2252
2253 if (!mRemoved.empty()) {
2254 auto start = mRemovedStart;
2255 aEditor->InsertTextAt(start, mRemoved.c_str());
2256 aEditor->Colorize(mRemovedStart.mLine - 1,
2257 mRemovedEnd.mLine - mRemovedStart.mLine + 2);
2258 }
2259
2260 aEditor->mState = mBefore;
2261 aEditor->EnsureCursorVisible();
2262}
2263
2265 if (!mRemoved.empty()) {
2267 aEditor->Colorize(mRemovedStart.mLine - 1,
2268 mRemovedEnd.mLine - mRemovedStart.mLine + 1);
2269 }
2270
2271 if (!mAdded.empty()) {
2272 auto start = mAddedStart;
2273 aEditor->InsertTextAt(start, mAdded.c_str());
2274 aEditor->Colorize(mAddedStart.mLine - 1,
2275 mAddedEnd.mLine - mAddedStart.mLine + 1);
2276 }
2277
2278 aEditor->mState = mAfter;
2279 aEditor->EnsureCursorVisible();
2280}
2281
2282static bool TokenizeCStyleString(const char* in_begin, const char* in_end,
2283 const char*& out_begin, const char*& out_end) {
2284 const char* p = in_begin;
2285
2286 if (*p == '"') {
2287 p++;
2288
2289 while (p < in_end) {
2290 // handle end of string
2291 if (*p == '"') {
2292 out_begin = in_begin;
2293 out_end = p + 1;
2294 return true;
2295 }
2296
2297 // handle escape character for "
2298 if (*p == '\\' && p + 1 < in_end && p[1] == '"') p++;
2299
2300 p++;
2301 }
2302 }
2303
2304 return false;
2305}
2306
2307static bool TokenizeCStyleCharacterLiteral(const char* in_begin,
2308 const char* in_end,
2309 const char*& out_begin,
2310 const char*& out_end) {
2311 const char* p = in_begin;
2312
2313 if (*p == '\'') {
2314 p++;
2315
2316 // handle escape characters
2317 if (p < in_end && *p == '\\') p++;
2318
2319 if (p < in_end) p++;
2320
2321 // handle end of character literal
2322 if (p < in_end && *p == '\'') {
2323 out_begin = in_begin;
2324 out_end = p + 1;
2325 return true;
2326 }
2327 }
2328
2329 return false;
2330}
2331
2332static bool TokenizeCStyleIdentifier(const char* in_begin, const char* in_end,
2333 const char*& out_begin,
2334 const char*& out_end) {
2335 const char* p = in_begin;
2336
2337 if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_') {
2338 p++;
2339
2340 while ((p < in_end) &&
2341 ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') ||
2342 (*p >= '0' && *p <= '9') || *p == '_'))
2343 p++;
2344
2345 out_begin = in_begin;
2346 out_end = p;
2347 return true;
2348 }
2349
2350 return false;
2351}
2352
2353static bool TokenizeCStyleNumber(const char* in_begin, const char* in_end,
2354 const char*& out_begin, const char*& out_end) {
2355 const char* p = in_begin;
2356
2357 const bool startsWithNumber = *p >= '0' && *p <= '9';
2358
2359 if (*p != '+' && *p != '-' && !startsWithNumber) return false;
2360
2361 p++;
2362
2363 bool hasNumber = startsWithNumber;
2364
2365 while (p < in_end && (*p >= '0' && *p <= '9')) {
2366 hasNumber = true;
2367
2368 p++;
2369 }
2370
2371 if (hasNumber == false) return false;
2372
2373 bool isFloat = false;
2374 bool isHex = false;
2375 bool isBinary = false;
2376
2377 if (p < in_end) {
2378 if (*p == '.') {
2379 isFloat = true;
2380
2381 p++;
2382
2383 while (p < in_end && (*p >= '0' && *p <= '9')) p++;
2384 } else if (*p == 'x' || *p == 'X') {
2385 // hex formatted integer of the type 0xef80
2386
2387 isHex = true;
2388
2389 p++;
2390
2391 while (p < in_end &&
2392 ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') ||
2393 (*p >= 'A' && *p <= 'F')))
2394 p++;
2395 } else if (*p == 'b' || *p == 'B') {
2396 // binary formatted integer of the type 0b01011101
2397
2398 isBinary = true;
2399
2400 p++;
2401
2402 while (p < in_end && (*p >= '0' && *p <= '1')) p++;
2403 }
2404 }
2405
2406 if (isHex == false && isBinary == false) {
2407 // floating point exponent
2408 if (p < in_end && (*p == 'e' || *p == 'E')) {
2409 isFloat = true;
2410
2411 p++;
2412
2413 if (p < in_end && (*p == '+' || *p == '-')) p++;
2414
2415 bool hasDigits = false;
2416
2417 while (p < in_end && (*p >= '0' && *p <= '9')) {
2418 hasDigits = true;
2419
2420 p++;
2421 }
2422
2423 if (hasDigits == false) return false;
2424 }
2425
2426 // single precision floating point type
2427 if (p < in_end && *p == 'f') p++;
2428 }
2429
2430 if (isFloat == false) {
2431 // integer size type
2432 while (p < in_end && (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L'))
2433 p++;
2434 }
2435
2436 out_begin = in_begin;
2437 out_end = p;
2438 return true;
2439}
2440
2441static bool TokenizeCStylePunctuation(const char* in_begin, const char* in_end,
2442 const char*& out_begin,
2443 const char*& out_end) {
2444 (void)in_end;
2445
2446 switch (*in_begin) {
2447 case '[':
2448 case ']':
2449 case '{':
2450 case '}':
2451 case '!':
2452 case '%':
2453 case '^':
2454 case '&':
2455 case '*':
2456 case '(':
2457 case ')':
2458 case '-':
2459 case '+':
2460 case '=':
2461 case '~':
2462 case '|':
2463 case '<':
2464 case '>':
2465 case '?':
2466 case ':':
2467 case '/':
2468 case ';':
2469 case ',':
2470 case '.':
2471 out_begin = in_begin;
2472 out_end = in_begin + 1;
2473 return true;
2474 }
2475
2476 return false;
2477}
2478
2479const TextEditor::LanguageDefinition&
2481 static bool inited = false;
2482 static LanguageDefinition langDef;
2483 if (!inited) {
2484 static const char* const cppKeywords[] = {"alignas",
2485 "alignof",
2486 "and",
2487 "and_eq",
2488 "asm",
2489 "atomic_cancel",
2490 "atomic_commit",
2491 "atomic_noexcept",
2492 "auto",
2493 "bitand",
2494 "bitor",
2495 "bool",
2496 "break",
2497 "case",
2498 "catch",
2499 "char",
2500 "char16_t",
2501 "char32_t",
2502 "class",
2503 "compl",
2504 "concept",
2505 "const",
2506 "constexpr",
2507 "const_cast",
2508 "continue",
2509 "decltype",
2510 "default",
2511 "delete",
2512 "do",
2513 "double",
2514 "dynamic_cast",
2515 "else",
2516 "enum",
2517 "explicit",
2518 "export",
2519 "extern",
2520 "false",
2521 "float",
2522 "for",
2523 "friend",
2524 "goto",
2525 "if",
2526 "import",
2527 "inline",
2528 "int",
2529 "long",
2530 "module",
2531 "mutable",
2532 "namespace",
2533 "new",
2534 "noexcept",
2535 "not",
2536 "not_eq",
2537 "nullptr",
2538 "operator",
2539 "or",
2540 "or_eq",
2541 "private",
2542 "protected",
2543 "public",
2544 "register",
2545 "reinterpret_cast",
2546 "requires",
2547 "return",
2548 "short",
2549 "signed",
2550 "sizeof",
2551 "static",
2552 "static_assert",
2553 "static_cast",
2554 "struct",
2555 "switch",
2556 "synchronized",
2557 "template",
2558 "this",
2559 "thread_local",
2560 "throw",
2561 "true",
2562 "try",
2563 "typedef",
2564 "typeid",
2565 "typename",
2566 "union",
2567 "unsigned",
2568 "using",
2569 "virtual",
2570 "void",
2571 "volatile",
2572 "wchar_t",
2573 "while",
2574 "xor",
2575 "xor_eq"};
2576 for (auto& k : cppKeywords) langDef.mKeywords.insert(k);
2577
2578 static const char* const identifiers[] = {
2579 "abort", "abs", "acos", "asin", "atan",
2580 "atexit", "atof", "atoi", "atol", "ceil",
2581 "clock", "cosh", "ctime", "div", "exit",
2582 "fabs", "floor", "fmod", "getchar", "getenv",
2583 "isalnum", "isalpha", "isdigit", "isgraph", "ispunct",
2584 "isspace", "isupper", "kbhit", "log10", "log2",
2585 "log", "memcmp", "modf", "pow", "printf",
2586 "sprintf", "snprintf", "putchar", "putenv", "puts",
2587 "rand", "remove", "rename", "sinh", "sqrt",
2588 "srand", "strcat", "strcmp", "strerror", "time",
2589 "tolower", "toupper", "std", "string", "vector",
2590 "map", "unordered_map", "set", "unordered_set", "min",
2591 "max"};
2592 for (auto& k : identifiers) {
2593 Identifier id;
2594 id.mDeclaration = "Built-in function";
2595 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
2596 }
2597
2598 langDef.mTokenize = [](const char* in_begin, const char* in_end,
2599 const char*& out_begin, const char*& out_end,
2600 PaletteIndex& paletteIndex) -> bool {
2601 paletteIndex = PaletteIndex::Max;
2602
2603 while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin))
2604 in_begin++;
2605
2606 if (in_begin == in_end) {
2607 out_begin = in_end;
2608 out_end = in_end;
2609 paletteIndex = PaletteIndex::Default;
2610 } else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end))
2611 paletteIndex = PaletteIndex::String;
2612 else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin,
2613 out_end))
2614 paletteIndex = PaletteIndex::CharLiteral;
2615 else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end))
2616 paletteIndex = PaletteIndex::Identifier;
2617 else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end))
2618 paletteIndex = PaletteIndex::Number;
2619 else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end))
2620 paletteIndex = PaletteIndex::Punctuation;
2621
2622 return paletteIndex != PaletteIndex::Max;
2623 };
2624
2625 langDef.mCommentStart = "/*";
2626 langDef.mCommentEnd = "*/";
2627 langDef.mSingleLineComment = "//";
2628
2629 langDef.mCaseSensitive = true;
2630 langDef.mAutoIndentation = true;
2631
2632 langDef.mName = "C++";
2633
2634 inited = true;
2635 }
2636 return langDef;
2637}
2638
2640 static bool inited = false;
2641 static LanguageDefinition langDef;
2642 if (!inited) {
2643 static const char* const keywords[] = {
2644 "AppendStructuredBuffer",
2645 "asm",
2646 "asm_fragment",
2647 "BlendState",
2648 "bool",
2649 "break",
2650 "Buffer",
2651 "ByteAddressBuffer",
2652 "case",
2653 "cbuffer",
2654 "centroid",
2655 "class",
2656 "column_major",
2657 "compile",
2658 "compile_fragment",
2659 "CompileShader",
2660 "const",
2661 "continue",
2662 "ComputeShader",
2663 "ConsumeStructuredBuffer",
2664 "default",
2665 "DepthStencilState",
2666 "DepthStencilView",
2667 "discard",
2668 "do",
2669 "double",
2670 "DomainShader",
2671 "dword",
2672 "else",
2673 "export",
2674 "extern",
2675 "false",
2676 "float",
2677 "for",
2678 "fxgroup",
2679 "GeometryShader",
2680 "groupshared",
2681 "half",
2682 "Hullshader",
2683 "if",
2684 "in",
2685 "inline",
2686 "inout",
2687 "InputPatch",
2688 "int",
2689 "interface",
2690 "line",
2691 "lineadj",
2692 "linear",
2693 "LineStream",
2694 "matrix",
2695 "min16float",
2696 "min10float",
2697 "min16int",
2698 "min12int",
2699 "min16uint",
2700 "namespace",
2701 "nointerpolation",
2702 "noperspective",
2703 "NULL",
2704 "out",
2705 "OutputPatch",
2706 "packoffset",
2707 "pass",
2708 "pixelfragment",
2709 "PixelShader",
2710 "point",
2711 "PointStream",
2712 "precise",
2713 "RasterizerState",
2714 "RenderTargetView",
2715 "return",
2716 "register",
2717 "row_major",
2718 "RWBuffer",
2719 "RWByteAddressBuffer",
2720 "RWStructuredBuffer",
2721 "RWTexture1D",
2722 "RWTexture1DArray",
2723 "RWTexture2D",
2724 "RWTexture2DArray",
2725 "RWTexture3D",
2726 "sample",
2727 "sampler",
2728 "SamplerState",
2729 "SamplerComparisonState",
2730 "shared",
2731 "snorm",
2732 "stateblock",
2733 "stateblock_state",
2734 "static",
2735 "string",
2736 "struct",
2737 "switch",
2738 "StructuredBuffer",
2739 "tbuffer",
2740 "technique",
2741 "technique10",
2742 "technique11",
2743 "texture",
2744 "Texture1D",
2745 "Texture1DArray",
2746 "Texture2D",
2747 "Texture2DArray",
2748 "Texture2DMS",
2749 "Texture2DMSArray",
2750 "Texture3D",
2751 "TextureCube",
2752 "TextureCubeArray",
2753 "true",
2754 "typedef",
2755 "triangle",
2756 "triangleadj",
2757 "TriangleStream",
2758 "uint",
2759 "uniform",
2760 "unorm",
2761 "unsigned",
2762 "vector",
2763 "vertexfragment",
2764 "VertexShader",
2765 "void",
2766 "volatile",
2767 "while",
2768 "bool1",
2769 "bool2",
2770 "bool3",
2771 "bool4",
2772 "double1",
2773 "double2",
2774 "double3",
2775 "double4",
2776 "float1",
2777 "float2",
2778 "float3",
2779 "float4",
2780 "int1",
2781 "int2",
2782 "int3",
2783 "int4",
2784 "in",
2785 "out",
2786 "inout",
2787 "uint1",
2788 "uint2",
2789 "uint3",
2790 "uint4",
2791 "dword1",
2792 "dword2",
2793 "dword3",
2794 "dword4",
2795 "half1",
2796 "half2",
2797 "half3",
2798 "half4",
2799 "float1x1",
2800 "float2x1",
2801 "float3x1",
2802 "float4x1",
2803 "float1x2",
2804 "float2x2",
2805 "float3x2",
2806 "float4x2",
2807 "float1x3",
2808 "float2x3",
2809 "float3x3",
2810 "float4x3",
2811 "float1x4",
2812 "float2x4",
2813 "float3x4",
2814 "float4x4",
2815 "half1x1",
2816 "half2x1",
2817 "half3x1",
2818 "half4x1",
2819 "half1x2",
2820 "half2x2",
2821 "half3x2",
2822 "half4x2",
2823 "half1x3",
2824 "half2x3",
2825 "half3x3",
2826 "half4x3",
2827 "half1x4",
2828 "half2x4",
2829 "half3x4",
2830 "half4x4",
2831 };
2832 for (auto& k : keywords) langDef.mKeywords.insert(k);
2833
2834 static const char* const identifiers[] = {
2835 "abort",
2836 "abs",
2837 "acos",
2838 "all",
2839 "AllMemoryBarrier",
2840 "AllMemoryBarrierWithGroupSync",
2841 "any",
2842 "asdouble",
2843 "asfloat",
2844 "asin",
2845 "asint",
2846 "asint",
2847 "asuint",
2848 "asuint",
2849 "atan",
2850 "atan2",
2851 "ceil",
2852 "CheckAccessFullyMapped",
2853 "clamp",
2854 "clip",
2855 "cos",
2856 "cosh",
2857 "countbits",
2858 "cross",
2859 "D3DCOLORtoUBYTE4",
2860 "ddx",
2861 "ddx_coarse",
2862 "ddx_fine",
2863 "ddy",
2864 "ddy_coarse",
2865 "ddy_fine",
2866 "degrees",
2867 "determinant",
2868 "DeviceMemoryBarrier",
2869 "DeviceMemoryBarrierWithGroupSync",
2870 "distance",
2871 "dot",
2872 "dst",
2873 "errorf",
2874 "EvaluateAttributeAtCentroid",
2875 "EvaluateAttributeAtSample",
2876 "EvaluateAttributeSnapped",
2877 "exp",
2878 "exp2",
2879 "f16tof32",
2880 "f32tof16",
2881 "faceforward",
2882 "firstbithigh",
2883 "firstbitlow",
2884 "floor",
2885 "fma",
2886 "fmod",
2887 "frac",
2888 "frexp",
2889 "fwidth",
2890 "GetRenderTargetSampleCount",
2891 "GetRenderTargetSamplePosition",
2892 "GroupMemoryBarrier",
2893 "GroupMemoryBarrierWithGroupSync",
2894 "InterlockedAdd",
2895 "InterlockedAnd",
2896 "InterlockedCompareExchange",
2897 "InterlockedCompareStore",
2898 "InterlockedExchange",
2899 "InterlockedMax",
2900 "InterlockedMin",
2901 "InterlockedOr",
2902 "InterlockedXor",
2903 "isfinite",
2904 "isinf",
2905 "isnan",
2906 "ldexp",
2907 "length",
2908 "lerp",
2909 "lit",
2910 "log",
2911 "log10",
2912 "log2",
2913 "mad",
2914 "max",
2915 "min",
2916 "modf",
2917 "msad4",
2918 "mul",
2919 "noise",
2920 "normalize",
2921 "pow",
2922 "printf",
2923 "Process2DQuadTessFactorsAvg",
2924 "Process2DQuadTessFactorsMax",
2925 "Process2DQuadTessFactorsMin",
2926 "ProcessIsolineTessFactors",
2927 "ProcessQuadTessFactorsAvg",
2928 "ProcessQuadTessFactorsMax",
2929 "ProcessQuadTessFactorsMin",
2930 "ProcessTriTessFactorsAvg",
2931 "ProcessTriTessFactorsMax",
2932 "ProcessTriTessFactorsMin",
2933 "radians",
2934 "rcp",
2935 "reflect",
2936 "refract",
2937 "reversebits",
2938 "round",
2939 "rsqrt",
2940 "saturate",
2941 "sign",
2942 "sin",
2943 "sincos",
2944 "sinh",
2945 "smoothstep",
2946 "sqrt",
2947 "step",
2948 "tan",
2949 "tanh",
2950 "tex1D",
2951 "tex1D",
2952 "tex1Dbias",
2953 "tex1Dgrad",
2954 "tex1Dlod",
2955 "tex1Dproj",
2956 "tex2D",
2957 "tex2D",
2958 "tex2Dbias",
2959 "tex2Dgrad",
2960 "tex2Dlod",
2961 "tex2Dproj",
2962 "tex3D",
2963 "tex3D",
2964 "tex3Dbias",
2965 "tex3Dgrad",
2966 "tex3Dlod",
2967 "tex3Dproj",
2968 "texCUBE",
2969 "texCUBE",
2970 "texCUBEbias",
2971 "texCUBEgrad",
2972 "texCUBElod",
2973 "texCUBEproj",
2974 "transpose",
2975 "trunc"};
2976 for (auto& k : identifiers) {
2977 Identifier id;
2978 id.mDeclaration = "Built-in function";
2979 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
2980 }
2981
2982 langDef.mTokenRegexStrings.push_back(
2983 std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+",
2985 langDef.mTokenRegexStrings.push_back(
2986 std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"",
2988 langDef.mTokenRegexStrings.push_back(
2989 std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'",
2991 langDef.mTokenRegexStrings.push_back(
2992 std::make_pair<std::string, PaletteIndex>(
2993 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
2995 langDef.mTokenRegexStrings.push_back(
2996 std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?",
2998 langDef.mTokenRegexStrings.push_back(
2999 std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?",
3001 langDef.mTokenRegexStrings.push_back(
3002 std::make_pair<std::string, PaletteIndex>(
3003 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
3004 langDef.mTokenRegexStrings.push_back(
3005 std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*",
3007 langDef.mTokenRegexStrings.push_back(
3008 std::make_pair<std::string, PaletteIndex>(
3009 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
3010 "\\;\\,\\.]",
3012
3013 langDef.mCommentStart = "/*";
3014 langDef.mCommentEnd = "*/";
3015 langDef.mSingleLineComment = "//";
3016
3017 langDef.mCaseSensitive = true;
3018 langDef.mAutoIndentation = true;
3019
3020 langDef.mName = "HLSL";
3021
3022 inited = true;
3023 }
3024 return langDef;
3025}
3026
3028 static bool inited = false;
3029 static LanguageDefinition langDef;
3030 if (!inited) {
3031 static const char* const keywords[] = {
3032 "auto", "break", "case", "char",
3033 "const", "continue", "default", "do",
3034 "double", "else", "enum", "extern",
3035 "float", "for", "goto", "if",
3036 "inline", "int", "long", "register",
3037 "restrict", "return", "short", "signed",
3038 "sizeof", "static", "struct", "switch",
3039 "typedef", "union", "unsigned", "void",
3040 "volatile", "while", "_Alignas", "_Alignof",
3041 "_Atomic", "_Bool", "_Complex", "_Generic",
3042 "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local"};
3043 for (auto& k : keywords) langDef.mKeywords.insert(k);
3044
3045 static const char* const identifiers[] = {
3046 "abort", "abs", "acos", "asin", "atan", "atexit",
3047 "atof", "atoi", "atol", "ceil", "clock", "cosh",
3048 "ctime", "div", "exit", "fabs", "floor", "fmod",
3049 "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph",
3050 "ispunct", "isspace", "isupper", "kbhit", "log10", "log2",
3051 "log", "memcmp", "modf", "pow", "putchar", "putenv",
3052 "puts", "rand", "remove", "rename", "sinh", "sqrt",
3053 "srand", "strcat", "strcmp", "strerror", "time", "tolower",
3054 "toupper"};
3055 for (auto& k : identifiers) {
3056 Identifier id;
3057 id.mDeclaration = "Built-in function";
3058 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
3059 }
3060
3061 langDef.mTokenRegexStrings.push_back(
3062 std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+",
3064 langDef.mTokenRegexStrings.push_back(
3065 std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"",
3067 langDef.mTokenRegexStrings.push_back(
3068 std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'",
3070 langDef.mTokenRegexStrings.push_back(
3071 std::make_pair<std::string, PaletteIndex>(
3072 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
3074 langDef.mTokenRegexStrings.push_back(
3075 std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?",
3077 langDef.mTokenRegexStrings.push_back(
3078 std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?",
3080 langDef.mTokenRegexStrings.push_back(
3081 std::make_pair<std::string, PaletteIndex>(
3082 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
3083 langDef.mTokenRegexStrings.push_back(
3084 std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*",
3086 langDef.mTokenRegexStrings.push_back(
3087 std::make_pair<std::string, PaletteIndex>(
3088 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
3089 "\\;\\,\\.]",
3091
3092 langDef.mCommentStart = "/*";
3093 langDef.mCommentEnd = "*/";
3094 langDef.mSingleLineComment = "//";
3095
3096 langDef.mCaseSensitive = true;
3097 langDef.mAutoIndentation = true;
3098
3099 langDef.mName = "GLSL";
3100
3101 inited = true;
3102 }
3103 return langDef;
3104}
3105
3107 static bool inited = false;
3108 static LanguageDefinition langDef;
3109 if (!inited) {
3110 static const char* const keywords[] = {
3111 "auto", "break", "case", "char",
3112 "const", "continue", "default", "do",
3113 "double", "else", "enum", "extern",
3114 "float", "for", "goto", "if",
3115 "inline", "int", "long", "register",
3116 "restrict", "return", "short", "signed",
3117 "sizeof", "static", "struct", "switch",
3118 "typedef", "union", "unsigned", "void",
3119 "volatile", "while", "_Alignas", "_Alignof",
3120 "_Atomic", "_Bool", "_Complex", "_Generic",
3121 "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local"};
3122 for (auto& k : keywords) langDef.mKeywords.insert(k);
3123
3124 static const char* const identifiers[] = {
3125 "abort", "abs", "acos", "asin", "atan", "atexit",
3126 "atof", "atoi", "atol", "ceil", "clock", "cosh",
3127 "ctime", "div", "exit", "fabs", "floor", "fmod",
3128 "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph",
3129 "ispunct", "isspace", "isupper", "kbhit", "log10", "log2",
3130 "log", "memcmp", "modf", "pow", "putchar", "putenv",
3131 "puts", "rand", "remove", "rename", "sinh", "sqrt",
3132 "srand", "strcat", "strcmp", "strerror", "time", "tolower",
3133 "toupper"};
3134 for (auto& k : identifiers) {
3135 Identifier id;
3136 id.mDeclaration = "Built-in function";
3137 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
3138 }
3139
3140 langDef.mTokenize = [](const char* in_begin, const char* in_end,
3141 const char*& out_begin, const char*& out_end,
3142 PaletteIndex& paletteIndex) -> bool {
3143 paletteIndex = PaletteIndex::Max;
3144
3145 while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin))
3146 in_begin++;
3147
3148 if (in_begin == in_end) {
3149 out_begin = in_end;
3150 out_end = in_end;
3151 paletteIndex = PaletteIndex::Default;
3152 } else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end))
3153 paletteIndex = PaletteIndex::String;
3154 else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin,
3155 out_end))
3156 paletteIndex = PaletteIndex::CharLiteral;
3157 else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end))
3158 paletteIndex = PaletteIndex::Identifier;
3159 else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end))
3160 paletteIndex = PaletteIndex::Number;
3161 else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end))
3162 paletteIndex = PaletteIndex::Punctuation;
3163
3164 return paletteIndex != PaletteIndex::Max;
3165 };
3166
3167 langDef.mCommentStart = "/*";
3168 langDef.mCommentEnd = "*/";
3169 langDef.mSingleLineComment = "//";
3170
3171 langDef.mCaseSensitive = true;
3172 langDef.mAutoIndentation = true;
3173
3174 langDef.mName = "C";
3175
3176 inited = true;
3177 }
3178 return langDef;
3179}
3180
3182 static bool inited = false;
3183 static LanguageDefinition langDef;
3184 if (!inited) {
3185 static const char* const keywords[] = {"ADD",
3186 "EXCEPT",
3187 "PERCENT",
3188 "ALL",
3189 "EXEC",
3190 "PLAN",
3191 "ALTER",
3192 "EXECUTE",
3193 "PRECISION",
3194 "AND",
3195 "EXISTS",
3196 "PRIMARY",
3197 "ANY",
3198 "EXIT",
3199 "PRINT",
3200 "AS",
3201 "FETCH",
3202 "PROC",
3203 "ASC",
3204 "FILE",
3205 "PROCEDURE",
3206 "AUTHORIZATION",
3207 "FILLFACTOR",
3208 "PUBLIC",
3209 "BACKUP",
3210 "FOR",
3211 "RAISERROR",
3212 "BEGIN",
3213 "FOREIGN",
3214 "READ",
3215 "BETWEEN",
3216 "FREETEXT",
3217 "READTEXT",
3218 "BREAK",
3219 "FREETEXTTABLE",
3220 "RECONFIGURE",
3221 "BROWSE",
3222 "FROM",
3223 "REFERENCES",
3224 "BULK",
3225 "FULL",
3226 "REPLICATION",
3227 "BY",
3228 "FUNCTION",
3229 "RESTORE",
3230 "CASCADE",
3231 "GOTO",
3232 "RESTRICT",
3233 "CASE",
3234 "GRANT",
3235 "RETURN",
3236 "CHECK",
3237 "GROUP",
3238 "REVOKE",
3239 "CHECKPOINT",
3240 "HAVING",
3241 "RIGHT",
3242 "CLOSE",
3243 "HOLDLOCK",
3244 "ROLLBACK",
3245 "CLUSTERED",
3246 "IDENTITY",
3247 "ROWCOUNT",
3248 "COALESCE",
3249 "IDENTITY_INSERT",
3250 "ROWGUIDCOL",
3251 "COLLATE",
3252 "IDENTITYCOL",
3253 "RULE",
3254 "COLUMN",
3255 "IF",
3256 "SAVE",
3257 "COMMIT",
3258 "IN",
3259 "SCHEMA",
3260 "COMPUTE",
3261 "INDEX",
3262 "SELECT",
3263 "CONSTRAINT",
3264 "INNER",
3265 "SESSION_USER",
3266 "CONTAINS",
3267 "INSERT",
3268 "SET",
3269 "CONTAINSTABLE",
3270 "INTERSECT",
3271 "SETUSER",
3272 "CONTINUE",
3273 "INTO",
3274 "SHUTDOWN",
3275 "CONVERT",
3276 "IS",
3277 "SOME",
3278 "CREATE",
3279 "JOIN",
3280 "STATISTICS",
3281 "CROSS",
3282 "KEY",
3283 "SYSTEM_USER",
3284 "CURRENT",
3285 "KILL",
3286 "TABLE",
3287 "CURRENT_DATE",
3288 "LEFT",
3289 "TEXTSIZE",
3290 "CURRENT_TIME",
3291 "LIKE",
3292 "THEN",
3293 "CURRENT_TIMESTAMP",
3294 "LINENO",
3295 "TO",
3296 "CURRENT_USER",
3297 "LOAD",
3298 "TOP",
3299 "CURSOR",
3300 "NATIONAL",
3301 "TRAN",
3302 "DATABASE",
3303 "NOCHECK",
3304 "TRANSACTION",
3305 "DBCC",
3306 "NONCLUSTERED",
3307 "TRIGGER",
3308 "DEALLOCATE",
3309 "NOT",
3310 "TRUNCATE",
3311 "DECLARE",
3312 "NULL",
3313 "TSEQUAL",
3314 "DEFAULT",
3315 "NULLIF",
3316 "UNION",
3317 "DELETE",
3318 "OF",
3319 "UNIQUE",
3320 "DENY",
3321 "OFF",
3322 "UPDATE",
3323 "DESC",
3324 "OFFSETS",
3325 "UPDATETEXT",
3326 "DISK",
3327 "ON",
3328 "USE",
3329 "DISTINCT",
3330 "OPEN",
3331 "USER",
3332 "DISTRIBUTED",
3333 "OPENDATASOURCE",
3334 "VALUES",
3335 "DOUBLE",
3336 "OPENQUERY",
3337 "VARYING",
3338 "DROP",
3339 "OPENROWSET",
3340 "VIEW",
3341 "DUMMY",
3342 "OPENXML",
3343 "WAITFOR",
3344 "DUMP",
3345 "OPTION",
3346 "WHEN",
3347 "ELSE",
3348 "OR",
3349 "WHERE",
3350 "END",
3351 "ORDER",
3352 "WHILE",
3353 "ERRLVL",
3354 "OUTER",
3355 "WITH",
3356 "ESCAPE",
3357 "OVER",
3358 "WRITETEXT"};
3359
3360 for (auto& k : keywords) langDef.mKeywords.insert(k);
3361
3362 static const char* const identifiers[] = {"ABS",
3363 "ACOS",
3364 "ADD_MONTHS",
3365 "ASCII",
3366 "ASCIISTR",
3367 "ASIN",
3368 "ATAN",
3369 "ATAN2",
3370 "AVG",
3371 "BFILENAME",
3372 "BIN_TO_NUM",
3373 "BITAND",
3374 "CARDINALITY",
3375 "CASE",
3376 "CAST",
3377 "CEIL",
3378 "CHARTOROWID",
3379 "CHR",
3380 "COALESCE",
3381 "COMPOSE",
3382 "CONCAT",
3383 "CONVERT",
3384 "CORR",
3385 "COS",
3386 "COSH",
3387 "COUNT",
3388 "COVAR_POP",
3389 "COVAR_SAMP",
3390 "CUME_DIST",
3391 "CURRENT_DATE",
3392 "CURRENT_TIMESTAMP",
3393 "DBTIMEZONE",
3394 "DECODE",
3395 "DECOMPOSE",
3396 "DENSE_RANK",
3397 "DUMP",
3398 "EMPTY_BLOB",
3399 "EMPTY_CLOB",
3400 "EXP",
3401 "EXTRACT",
3402 "FIRST_VALUE",
3403 "FLOOR",
3404 "FROM_TZ",
3405 "GREATEST",
3406 "GROUP_ID",
3407 "HEXTORAW",
3408 "INITCAP",
3409 "INSTR",
3410 "INSTR2",
3411 "INSTR4",
3412 "INSTRB",
3413 "INSTRC",
3414 "LAG",
3415 "LAST_DAY",
3416 "LAST_VALUE",
3417 "LEAD",
3418 "LEAST",
3419 "LENGTH",
3420 "LENGTH2",
3421 "LENGTH4",
3422 "LENGTHB",
3423 "LENGTHC",
3424 "LISTAGG",
3425 "LN",
3426 "LNNVL",
3427 "LOCALTIMESTAMP",
3428 "LOG",
3429 "LOWER",
3430 "LPAD",
3431 "LTRIM",
3432 "MAX",
3433 "MEDIAN",
3434 "MIN",
3435 "MOD",
3436 "MONTHS_BETWEEN",
3437 "NANVL",
3438 "NCHR",
3439 "NEW_TIME",
3440 "NEXT_DAY",
3441 "NTH_VALUE",
3442 "NULLIF",
3443 "NUMTODSINTERVAL",
3444 "NUMTOYMINTERVAL",
3445 "NVL",
3446 "NVL2",
3447 "POWER",
3448 "RANK",
3449 "RAWTOHEX",
3450 "REGEXP_COUNT",
3451 "REGEXP_INSTR",
3452 "REGEXP_REPLACE",
3453 "REGEXP_SUBSTR",
3454 "REMAINDER",
3455 "REPLACE",
3456 "ROUND",
3457 "ROWNUM",
3458 "RPAD",
3459 "RTRIM",
3460 "SESSIONTIMEZONE",
3461 "SIGN",
3462 "SIN",
3463 "SINH",
3464 "SOUNDEX",
3465 "SQRT",
3466 "STDDEV",
3467 "SUBSTR",
3468 "SUM",
3469 "SYS_CONTEXT",
3470 "SYSDATE",
3471 "SYSTIMESTAMP",
3472 "TAN",
3473 "TANH",
3474 "TO_CHAR",
3475 "TO_CLOB",
3476 "TO_DATE",
3477 "TO_DSINTERVAL",
3478 "TO_LOB",
3479 "TO_MULTI_BYTE",
3480 "TO_NCLOB",
3481 "TO_NUMBER",
3482 "TO_SINGLE_BYTE",
3483 "TO_TIMESTAMP",
3484 "TO_TIMESTAMP_TZ",
3485 "TO_YMINTERVAL",
3486 "TRANSLATE",
3487 "TRIM",
3488 "TRUNC",
3489 "TZ_OFFSET",
3490 "UID",
3491 "UPPER",
3492 "USER",
3493 "USERENV",
3494 "VAR_POP",
3495 "VAR_SAMP",
3496 "VARIANCE",
3497 "VSIZE "};
3498 for (auto& k : identifiers) {
3499 Identifier id;
3500 id.mDeclaration = "Built-in function";
3501 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
3502 }
3503
3504 langDef.mTokenRegexStrings.push_back(
3505 std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"",
3507 langDef.mTokenRegexStrings.push_back(
3508 std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'",
3510 langDef.mTokenRegexStrings.push_back(
3511 std::make_pair<std::string, PaletteIndex>(
3512 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
3514 langDef.mTokenRegexStrings.push_back(
3515 std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?",
3517 langDef.mTokenRegexStrings.push_back(
3518 std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?",
3520 langDef.mTokenRegexStrings.push_back(
3521 std::make_pair<std::string, PaletteIndex>(
3522 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
3523 langDef.mTokenRegexStrings.push_back(
3524 std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*",
3526 langDef.mTokenRegexStrings.push_back(
3527 std::make_pair<std::string, PaletteIndex>(
3528 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
3529 "\\;\\,\\.]",
3531
3532 langDef.mCommentStart = "/*";
3533 langDef.mCommentEnd = "*/";
3534 langDef.mSingleLineComment = "//";
3535
3536 langDef.mCaseSensitive = false;
3537 langDef.mAutoIndentation = false;
3538
3539 langDef.mName = "SQL";
3540
3541 inited = true;
3542 }
3543 return langDef;
3544}
3545
3548 static bool inited = false;
3549 static LanguageDefinition langDef;
3550 if (!inited) {
3551 static const char* const keywords[] = {
3552 "and", "abstract", "auto", "bool", "break",
3553 "case", "cast", "class", "const", "continue",
3554 "default", "do", "double", "else", "enum",
3555 "false", "final", "float", "for", "from",
3556 "funcdef", "function", "get", "if", "import",
3557 "in", "inout", "int", "interface", "int8",
3558 "int16", "int32", "int64", "is", "mixin",
3559 "namespace", "not", "null", "or", "out",
3560 "override", "private", "protected", "return", "set",
3561 "shared", "super", "switch", "this ", "true",
3562 "typedef", "uint", "uint8", "uint16", "uint32",
3563 "uint64", "void", "while", "xor"};
3564
3565 for (auto& k : keywords) langDef.mKeywords.insert(k);
3566
3567 static const char* const identifiers[] = {
3568 "cos", "sin", "tab", "acos", "asin",
3569 "atan", "atan2", "cosh", "sinh", "tanh",
3570 "log", "log10", "pow", "sqrt", "abs",
3571 "ceil", "floor", "fraction", "closeTo", "fpFromIEEE",
3572 "fpToIEEE", "complex", "opEquals", "opAddAssign", "opSubAssign",
3573 "opMulAssign", "opDivAssign", "opAdd", "opSub", "opMul",
3574 "opDiv"};
3575 for (auto& k : identifiers) {
3576 Identifier id;
3577 id.mDeclaration = "Built-in function";
3578 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
3579 }
3580
3581 langDef.mTokenRegexStrings.push_back(
3582 std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"",
3584 langDef.mTokenRegexStrings.push_back(
3585 std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'",
3587 langDef.mTokenRegexStrings.push_back(
3588 std::make_pair<std::string, PaletteIndex>(
3589 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
3591 langDef.mTokenRegexStrings.push_back(
3592 std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?",
3594 langDef.mTokenRegexStrings.push_back(
3595 std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?",
3597 langDef.mTokenRegexStrings.push_back(
3598 std::make_pair<std::string, PaletteIndex>(
3599 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
3600 langDef.mTokenRegexStrings.push_back(
3601 std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*",
3603 langDef.mTokenRegexStrings.push_back(
3604 std::make_pair<std::string, PaletteIndex>(
3605 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
3606 "\\;\\,\\.]",
3608
3609 langDef.mCommentStart = "/*";
3610 langDef.mCommentEnd = "*/";
3611 langDef.mSingleLineComment = "//";
3612
3613 langDef.mCaseSensitive = true;
3614 langDef.mAutoIndentation = true;
3615
3616 langDef.mName = "AngelScript";
3617
3618 inited = true;
3619 }
3620 return langDef;
3621}
3622
3624 static bool inited = false;
3625 static LanguageDefinition langDef;
3626 if (!inited) {
3627 static const char* const keywords[] = {
3628 "and", "break", "do", "", "else", "elseif", "end", "false",
3629 "for", "function", "if", "in", "", "local", "nil", "not",
3630 "or", "repeat", "return", "then", "true", "until", "while"};
3631
3632 for (auto& k : keywords) langDef.mKeywords.insert(k);
3633
3634 static const char* const identifiers[] = {"assert", "collectgarbage",
3635 "dofile", "error",
3636 "getmetatable", "ipairs",
3637 "loadfile", "load",
3638 "loadstring", "next",
3639 "pairs", "pcall",
3640 "print", "rawequal",
3641 "rawlen", "rawget",
3642 "rawset", "select",
3643 "setmetatable", "tonumber",
3644 "tostring", "type",
3645 "xpcall", "_G",
3646 "_VERSION", "arshift",
3647 "band", "bnot",
3648 "bor", "bxor",
3649 "btest", "extract",
3650 "lrotate", "lshift",
3651 "replace", "rrotate",
3652 "rshift", "create",
3653 "resume", "running",
3654 "status", "wrap",
3655 "yield", "isyieldable",
3656 "debug", "getuservalue",
3657 "gethook", "getinfo",
3658 "getlocal", "getregistry",
3659 "getmetatable", "getupvalue",
3660 "upvaluejoin", "upvalueid",
3661 "setuservalue", "sethook",
3662 "setlocal", "setmetatable",
3663 "setupvalue", "traceback",
3664 "close", "flush",
3665 "input", "lines",
3666 "open", "output",
3667 "popen", "read",
3668 "tmpfile", "type",
3669 "write", "close",
3670 "flush", "lines",
3671 "read", "seek",
3672 "setvbuf", "write",
3673 "__gc", "__tostring",
3674 "abs", "acos",
3675 "asin", "atan",
3676 "ceil", "cos",
3677 "deg", "exp",
3678 "tointeger", "floor",
3679 "fmod", "ult",
3680 "log", "max",
3681 "min", "modf",
3682 "rad", "random",
3683 "randomseed", "sin",
3684 "sqrt", "string",
3685 "tan", "type",
3686 "atan2", "cosh",
3687 "sinh", "tanh",
3688 "pow", "frexp",
3689 "ldexp", "log10",
3690 "pi", "huge",
3691 "maxinteger", "mininteger",
3692 "loadlib", "searchpath",
3693 "seeall", "preload",
3694 "cpath", "path",
3695 "searchers", "loaded",
3696 "module", "require",
3697 "clock", "date",
3698 "difftime", "execute",
3699 "exit", "getenv",
3700 "remove", "rename",
3701 "setlocale", "time",
3702 "tmpname", "byte",
3703 "char", "dump",
3704 "find", "format",
3705 "gmatch", "gsub",
3706 "len", "lower",
3707 "match", "rep",
3708 "reverse", "sub",
3709 "upper", "pack",
3710 "packsize", "unpack",
3711 "concat", "maxn",
3712 "insert", "pack",
3713 "unpack", "remove",
3714 "move", "sort",
3715 "offset", "codepoint",
3716 "char", "len",
3717 "codes", "charpattern",
3718 "coroutine", "table",
3719 "io", "os",
3720 "string", "utf8",
3721 "bit32", "math",
3722 "debug", "package"};
3723 for (auto& k : identifiers) {
3724 Identifier id;
3725 id.mDeclaration = "Built-in function";
3726 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
3727 }
3728
3729 langDef.mTokenRegexStrings.push_back(
3730 std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"",
3732 langDef.mTokenRegexStrings.push_back(
3733 std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'",
3735 langDef.mTokenRegexStrings.push_back(
3736 std::make_pair<std::string, PaletteIndex>(
3737 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
3738 langDef.mTokenRegexStrings.push_back(
3739 std::make_pair<std::string, PaletteIndex>(
3740 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
3742 langDef.mTokenRegexStrings.push_back(
3743 std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?",
3745 langDef.mTokenRegexStrings.push_back(
3746 std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*",
3748 langDef.mTokenRegexStrings.push_back(
3749 std::make_pair<std::string, PaletteIndex>(
3750 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
3751 "\\;\\,\\.]",
3753
3754 langDef.mCommentStart = "--[[";
3755 langDef.mCommentEnd = "]]";
3756 langDef.mSingleLineComment = "--";
3757
3758 langDef.mCaseSensitive = true;
3759 langDef.mAutoIndentation = false;
3760
3761 langDef.mName = "Lua";
3762
3763 inited = true;
3764 }
3765 return langDef;
3766}
void Redo(TextEditor *aEditor)
Coordinates mRemovedStart
void Undo(TextEditor *aEditor)
void HandleKeyboardInputs()
bool mScrollToCursor
void MoveBottom(bool aSelect=false)
bool mScrollToTop
void InsertText(const std::string &aValue)
ImU32 GetGlyphColor(const Glyph &aGlyph) const
bool mHandleMouseInputs
std::vector< std::string > GetTextLines() const
float mLastClick
void SelectWordUnderCursor()
uint64_t mStartTime
UndoBuffer mUndoBuffer
static const Palette & GetLightPalette()
void HandleMouseInputs()
void MoveLeft(int aAmount=1, bool aSelect=false, bool aWordMode=false)
bool mOverwrite
void SetColorizerEnable(bool aValue)
void MoveHome(bool aSelect=false)
int GetLineMaxColumn(int aLine) const
ImVec2 mCharAdvance
void SetSelectionEnd(const Coordinates &aPosition)
static const Palette & GetDarkPalette()
void MoveEnd(bool aSelect=false)
std::string GetWordUnderCursor() const
Breakpoints mBreakpoints
int mColorRangeMax
static const Palette & GetRetroBluePalette()
int GetCharacterIndex(const Coordinates &aCoordinates) const
float mTextStart
Coordinates GetCursorPosition() const
float mLineSpacing
void EnsureCursorVisible()
bool HasSelection() const
Coordinates FindWordStart(const Coordinates &aFrom) const
void Colorize(int aFromLine=0, int aCount=-1)
std::string GetWordAt(const Coordinates &aCoords) const
Coordinates FindNextWord(const Coordinates &aFrom) const
void Render()
void SetReadOnly(bool aValue)
void Backspace()
std::string GetText() const
bool IsReadOnly() const
int mColorRangeMin
int GetCharacterColumn(int aLine, int aIndex) const
void SetSelectionStart(const Coordinates &aPosition)
void MoveTop(bool aSelect=false)
RegexList mRegexList
std::string GetCurrentLineText() const
bool mColorizerEnabled
void AddUndo(UndoRecord &aValue)
void MoveUp(int aAmount=1, bool aSelect=false)
std::vector< Glyph > Line
void Undo(int aSteps=1)
Coordinates FindWordEnd(const Coordinates &aFrom) const
void DeleteSelection()
void SetPalette(const Palette &aValue)
std::map< int, std::string > ErrorMarkers
void RemoveLine(int aStart, int aEnd)
Palette mPalette
void ProcessInputs()
void SetText(const std::string &aText)
void SetTextLines(const std::vector< std::string > &aLines)
float TextDistanceToLineStart(const Coordinates &aFrom) const
Coordinates SanitizeCoordinates(const Coordinates &aValue) const
int GetPageSize() const
ErrorMarkers mErrorMarkers
bool mTextChanged
void ColorizeRange(int aFromLine=0, int aToLine=0)
EditorState mState
void SelectAll()
std::string mLineBuffer
Line & InsertLine(int aIndex)
Coordinates mInteractiveEnd
bool mHandleKeyboardInputs
void EnterCharacter(ImWchar aChar, bool aShift)
Palette mPaletteBase
void ColorizeInternal()
void MoveDown(int aAmount=1, bool aSelect=false)
SelectionMode mSelectionMode
bool mWithinRender
Coordinates mInteractiveStart
void Advance(Coordinates &aCoordinates) const
bool mShowWhitespaces
bool mCursorPositionChanged
bool mIgnoreImGuiChild
bool IsOnWordBoundary(const Coordinates &aAt) const
Coordinates GetActualCursorCoordinates() const
void Redo(int aSteps=1)
bool CanRedo() const
void SetCursorPosition(const Coordinates &aPosition)
std::unordered_set< int > Breakpoints
std::string GetSelectedText() const
int GetLineCharacterCount(int aLine) const
Coordinates ScreenPosToCoordinates(const ImVec2 &aPosition) const
void SetTabSize(int aValue)
void DeleteRange(const Coordinates &aStart, const Coordinates &aEnd)
bool mCheckComments
std::array< ImU32,(unsigned) PaletteIndex::Max > Palette
uint8_t Char
bool CanUndo() const
int InsertTextAt(Coordinates &aWhere, const char *aValue)
void SetSelection(const Coordinates &aStart, const Coordinates &aEnd, SelectionMode aMode=SelectionMode::Normal)
LanguageDefinition mLanguageDefinition
void SetLanguageDefinition(const LanguageDefinition &aLanguageDef)
void MoveRight(int aAmount=1, bool aSelect=false, bool aWordMode=false)
PaletteIndex mColorIndex
static const LanguageDefinition & SQL()
TokenRegexStrings mTokenRegexStrings
static const LanguageDefinition & Lua()
static const LanguageDefinition & C()
static const LanguageDefinition & GLSL()
static const LanguageDefinition & AngelScript()
static const LanguageDefinition & CPlusPlus()
static const LanguageDefinition & HLSL()
bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p)