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 }
1070
1072
1074 Render();
1075
1076
1077 if (!mIgnoreImGuiChild) ImGui::EndChild();
1078
1079 ImGui::PopStyleVar();
1080 ImGui::PopStyleColor();
1081
1082 mWithinRender = false;
1083}
1084
1085void TextEditor::SetText(const std::string& aText) {
1086 mLines.clear();
1087 mLines.emplace_back(Line());
1088 for (auto chr : aText) {
1089 if (chr == '\r') {
1090 // ignore the carriage return character
1091 } else if (chr == '\n')
1092 mLines.emplace_back(Line());
1093 else {
1094 mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default));
1095 }
1096 }
1097
1098 mTextChanged = true;
1099 mScrollToTop = true;
1100
1101 mUndoBuffer.clear();
1102 mUndoIndex = 0;
1103
1104 Colorize();
1105}
1106
1107void TextEditor::SetTextLines(const std::vector<std::string>& aLines) {
1108 mLines.clear();
1109
1110 if (aLines.empty()) {
1111 mLines.emplace_back(Line());
1112 } else {
1113 mLines.resize(aLines.size());
1114
1115 for (size_t i = 0; i < aLines.size(); ++i) {
1116 const std::string& aLine = aLines[i];
1117
1118 mLines[i].reserve(aLine.size());
1119 for (size_t j = 0; j < aLine.size(); ++j)
1120 mLines[i].emplace_back(Glyph(aLine[j], PaletteIndex::Default));
1121 }
1122 }
1123
1124 mTextChanged = true;
1125 mScrollToTop = true;
1126
1127 mUndoBuffer.clear();
1128 mUndoIndex = 0;
1129
1130 Colorize();
1131}
1132
1133void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) {
1134 assert(!mReadOnly);
1135
1136 UndoRecord u;
1137
1138 u.mBefore = mState;
1139
1140 if (HasSelection()) {
1141 if (aChar == '\t' &&
1142 mState.mSelectionStart.mLine != mState.mSelectionEnd.mLine) {
1143 auto start = mState.mSelectionStart;
1144 auto end = mState.mSelectionEnd;
1145 auto originalEnd = end;
1146
1147 if (start > end) std::swap(start, end);
1148 start.mColumn = 0;
1149 // end.mColumn = end.mLine < mLines.size() ?
1150 //mLines[end.mLine].size() : 0;
1151 if (end.mColumn == 0 && end.mLine > 0) --end.mLine;
1152 if (end.mLine >= (int)mLines.size())
1153 end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1;
1154 end.mColumn = GetLineMaxColumn(end.mLine);
1155
1156 // if (end.mColumn >= GetLineMaxColumn(end.mLine))
1157 // end.mColumn = GetLineMaxColumn(end.mLine) - 1;
1158
1159 u.mRemovedStart = start;
1160 u.mRemovedEnd = end;
1161 u.mRemoved = GetText(start, end);
1162
1163 bool modified = false;
1164
1165 for (int i = start.mLine; i <= end.mLine; i++) {
1166 auto& line = mLines[i];
1167 if (aShift) {
1168 if (!line.empty()) {
1169 if (line.front().mChar == '\t') {
1170 line.erase(line.begin());
1171 modified = true;
1172 } else {
1173 for (int j = 0;
1174 j < mTabSize && !line.empty() && line.front().mChar == ' ';
1175 j++) {
1176 line.erase(line.begin());
1177 modified = true;
1178 }
1179 }
1180 }
1181 } else {
1182 line.insert(line.begin(),
1184 modified = true;
1185 }
1186 }
1187
1188 if (modified) {
1189 start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0));
1190 Coordinates rangeEnd;
1191 if (originalEnd.mColumn != 0) {
1192 end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine));
1193 rangeEnd = end;
1194 u.mAdded = GetText(start, end);
1195 } else {
1196 end = Coordinates(originalEnd.mLine, 0);
1197 rangeEnd =
1198 Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1));
1199 u.mAdded = GetText(start, rangeEnd);
1200 }
1201
1202 u.mAddedStart = start;
1203 u.mAddedEnd = rangeEnd;
1204 u.mAfter = mState;
1205
1206 mState.mSelectionStart = start;
1207 mState.mSelectionEnd = end;
1208 AddUndo(u);
1209
1210 mTextChanged = true;
1211
1213 }
1214
1215 return;
1216 } // c == '\t'
1217 else {
1219 u.mRemovedStart = mState.mSelectionStart;
1220 u.mRemovedEnd = mState.mSelectionEnd;
1222 }
1223 } // HasSelection
1224
1225 auto coord = GetActualCursorCoordinates();
1226 u.mAddedStart = coord;
1227
1228 assert(!mLines.empty());
1229
1230 if (aChar == '\n') {
1231 InsertLine(coord.mLine + 1);
1232 auto& line = mLines[coord.mLine];
1233 auto& newLine = mLines[coord.mLine + 1];
1234
1235 if (mLanguageDefinition.mAutoIndentation)
1236 for (size_t it = 0; it < line.size() && isascii(line[it].mChar) &&
1237 isblank(line[it].mChar);
1238 ++it)
1239 newLine.push_back(line[it]);
1240
1241 const size_t whitespaceSize = newLine.size();
1242 auto cindex = GetCharacterIndex(coord);
1243 newLine.insert(newLine.end(), line.begin() + cindex, line.end());
1244 line.erase(line.begin() + cindex, line.begin() + line.size());
1246 Coordinates(coord.mLine + 1,
1247 GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize)));
1248 u.mAdded = (char)aChar;
1249 } else {
1250 char buf[7];
1251 int e = ImTextCharToUtf8(buf, 7, aChar);
1252 if (e > 0) {
1253 buf[e] = '\0';
1254 auto& line = mLines[coord.mLine];
1255 auto cindex = GetCharacterIndex(coord);
1256
1257 if (mOverwrite && cindex < (int)line.size()) {
1258 auto d = UTF8CharLength(line[cindex].mChar);
1259
1260 u.mRemovedStart = mState.mCursorPosition;
1262 coord.mLine, GetCharacterColumn(coord.mLine, cindex + d));
1263
1264 while (d-- > 0 && cindex < (int)line.size()) {
1265 u.mRemoved += line[cindex].mChar;
1266 line.erase(line.begin() + cindex);
1267 }
1268 }
1269
1270 for (auto p = buf; *p != '\0'; p++, ++cindex)
1271 line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default));
1272 u.mAdded = buf;
1273
1275 Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex)));
1276 } else
1277 return;
1278 }
1279
1280 mTextChanged = true;
1281
1283 u.mAfter = mState;
1284
1285 AddUndo(u);
1286
1287 Colorize(coord.mLine - 1, 3);
1289}
1290
1291void TextEditor::SetReadOnly(bool aValue) { mReadOnly = aValue; }
1292
1294
1296 if (mState.mCursorPosition != aPosition) {
1297 mState.mCursorPosition = aPosition;
1300 }
1301}
1302
1304 mState.mSelectionStart = SanitizeCoordinates(aPosition);
1305 if (mState.mSelectionStart > mState.mSelectionEnd)
1306 std::swap(mState.mSelectionStart, mState.mSelectionEnd);
1307}
1308
1310 mState.mSelectionEnd = SanitizeCoordinates(aPosition);
1311 if (mState.mSelectionStart > mState.mSelectionEnd)
1312 std::swap(mState.mSelectionStart, mState.mSelectionEnd);
1313}
1314
1316 const Coordinates& aEnd, SelectionMode aMode) {
1317 auto oldSelStart = mState.mSelectionStart;
1318 auto oldSelEnd = mState.mSelectionEnd;
1319
1320 mState.mSelectionStart = SanitizeCoordinates(aStart);
1321 mState.mSelectionEnd = SanitizeCoordinates(aEnd);
1322 if (mState.mSelectionStart > mState.mSelectionEnd)
1323 std::swap(mState.mSelectionStart, mState.mSelectionEnd);
1324
1325 switch (aMode) {
1327 break;
1329 mState.mSelectionStart = FindWordStart(mState.mSelectionStart);
1330 if (!IsOnWordBoundary(mState.mSelectionEnd))
1331 mState.mSelectionEnd = FindWordEnd(FindWordStart(mState.mSelectionEnd));
1332 break;
1333 }
1335 const auto lineNo = mState.mSelectionEnd.mLine;
1336 const auto lineSize =
1337 (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0;
1338 mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0);
1339 mState.mSelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo));
1340 break;
1341 }
1342 default:
1343 break;
1344 }
1345
1346 if (mState.mSelectionStart != oldSelStart ||
1347 mState.mSelectionEnd != oldSelEnd)
1349}
1350
1351void TextEditor::SetTabSize(int aValue) {
1352 mTabSize = std::max(0, std::min(32, aValue));
1353}
1354
1355void TextEditor::InsertText(const std::string& aValue) {
1356 InsertText(aValue.c_str());
1357}
1358
1359void TextEditor::InsertText(const char* aValue) {
1360 if (aValue == nullptr) return;
1361
1362 auto pos = GetActualCursorCoordinates();
1363 auto start = std::min(pos, mState.mSelectionStart);
1364 int totalLines = pos.mLine - start.mLine;
1365
1366 totalLines += InsertTextAt(pos, aValue);
1367
1368 SetSelection(pos, pos);
1369 SetCursorPosition(pos);
1370 Colorize(start.mLine - 1, totalLines + 2);
1371}
1372
1374 assert(mState.mSelectionEnd >= mState.mSelectionStart);
1375
1376 if (mState.mSelectionEnd == mState.mSelectionStart) return;
1377
1378 DeleteRange(mState.mSelectionStart, mState.mSelectionEnd);
1379
1380 SetSelection(mState.mSelectionStart, mState.mSelectionStart);
1381 SetCursorPosition(mState.mSelectionStart);
1382 Colorize(mState.mSelectionStart.mLine, 1);
1383}
1384
1385void TextEditor::MoveUp(int aAmount, bool aSelect) {
1386 auto oldPos = mState.mCursorPosition;
1387 mState.mCursorPosition.mLine =
1388 std::max(0, mState.mCursorPosition.mLine - aAmount);
1389 if (oldPos != mState.mCursorPosition) {
1390 if (aSelect) {
1391 if (oldPos == mInteractiveStart)
1392 mInteractiveStart = mState.mCursorPosition;
1393 else if (oldPos == mInteractiveEnd)
1394 mInteractiveEnd = mState.mCursorPosition;
1395 else {
1396 mInteractiveStart = mState.mCursorPosition;
1397 mInteractiveEnd = oldPos;
1398 }
1399 } else
1400 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1402
1404 }
1405}
1406
1407void TextEditor::MoveDown(int aAmount, bool aSelect) {
1408 assert(mState.mCursorPosition.mColumn >= 0);
1409 auto oldPos = mState.mCursorPosition;
1410 mState.mCursorPosition.mLine = std::max(
1411 0,
1412 std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount));
1413
1414 if (mState.mCursorPosition != oldPos) {
1415 if (aSelect) {
1416 if (oldPos == mInteractiveEnd)
1417 mInteractiveEnd = mState.mCursorPosition;
1418 else if (oldPos == mInteractiveStart)
1419 mInteractiveStart = mState.mCursorPosition;
1420 else {
1421 mInteractiveStart = oldPos;
1422 mInteractiveEnd = mState.mCursorPosition;
1423 }
1424 } else
1425 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1427
1429 }
1430}
1431
1432static bool IsUTFSequence(char c) { return (c & 0xC0) == 0x80; }
1433
1434void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) {
1435 if (mLines.empty()) return;
1436
1437 auto oldPos = mState.mCursorPosition;
1438 mState.mCursorPosition = GetActualCursorCoordinates();
1439 auto line = mState.mCursorPosition.mLine;
1440 auto cindex = GetCharacterIndex(mState.mCursorPosition);
1441
1442 while (aAmount-- > 0) {
1443 if (cindex == 0) {
1444 if (line > 0) {
1445 --line;
1446 if ((int)mLines.size() > line)
1447 cindex = (int)mLines[line].size();
1448 else
1449 cindex = 0;
1450 }
1451 } else {
1452 --cindex;
1453 if (cindex > 0) {
1454 if ((int)mLines.size() > line) {
1455 while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar))
1456 --cindex;
1457 }
1458 }
1459 }
1460
1461 mState.mCursorPosition =
1462 Coordinates(line, GetCharacterColumn(line, cindex));
1463 if (aWordMode) {
1464 mState.mCursorPosition = FindWordStart(mState.mCursorPosition);
1465 cindex = GetCharacterIndex(mState.mCursorPosition);
1466 }
1467 }
1468
1469 mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex));
1470
1471 assert(mState.mCursorPosition.mColumn >= 0);
1472 if (aSelect) {
1473 if (oldPos == mInteractiveStart)
1474 mInteractiveStart = mState.mCursorPosition;
1475 else if (oldPos == mInteractiveEnd)
1476 mInteractiveEnd = mState.mCursorPosition;
1477 else {
1478 mInteractiveStart = mState.mCursorPosition;
1479 mInteractiveEnd = oldPos;
1480 }
1481 } else
1482 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1485 aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);
1486
1488}
1489
1490void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) {
1491 auto oldPos = mState.mCursorPosition;
1492
1493 if (mLines.empty() || oldPos.mLine >= mLines.size()) return;
1494
1495 auto cindex = GetCharacterIndex(mState.mCursorPosition);
1496 while (aAmount-- > 0) {
1497 auto lindex = mState.mCursorPosition.mLine;
1498 auto& line = mLines[lindex];
1499
1500 if (cindex >= line.size()) {
1501 if (mState.mCursorPosition.mLine < mLines.size() - 1) {
1502 mState.mCursorPosition.mLine = std::max(
1503 0,
1504 std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + 1));
1505 mState.mCursorPosition.mColumn = 0;
1506 } else
1507 return;
1508 } else {
1509 cindex += UTF8CharLength(line[cindex].mChar);
1510 mState.mCursorPosition =
1511 Coordinates(lindex, GetCharacterColumn(lindex, cindex));
1512 if (aWordMode)
1513 mState.mCursorPosition = FindNextWord(mState.mCursorPosition);
1514 }
1515 }
1516
1517 if (aSelect) {
1518 if (oldPos == mInteractiveEnd)
1519 mInteractiveEnd = SanitizeCoordinates(mState.mCursorPosition);
1520 else if (oldPos == mInteractiveStart)
1521 mInteractiveStart = mState.mCursorPosition;
1522 else {
1523 mInteractiveStart = oldPos;
1524 mInteractiveEnd = mState.mCursorPosition;
1525 }
1526 } else
1527 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1530 aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);
1531
1533}
1534
1535void TextEditor::MoveTop(bool aSelect) {
1536 auto oldPos = mState.mCursorPosition;
1538
1539 if (mState.mCursorPosition != oldPos) {
1540 if (aSelect) {
1541 mInteractiveEnd = oldPos;
1542 mInteractiveStart = mState.mCursorPosition;
1543 } else
1544 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1546 }
1547}
1548
1549void TextEditor::TextEditor::MoveBottom(bool aSelect) {
1550 auto oldPos = GetCursorPosition();
1551 auto newPos = Coordinates((int)mLines.size() - 1, 0);
1552 SetCursorPosition(newPos);
1553 if (aSelect) {
1554 mInteractiveStart = oldPos;
1555 mInteractiveEnd = newPos;
1556 } else
1557 mInteractiveStart = mInteractiveEnd = newPos;
1558 SetSelection(mInteractiveStart, mInteractiveEnd);
1559}
1560
1561void TextEditor::MoveHome(bool aSelect) {
1562 auto oldPos = mState.mCursorPosition;
1563 SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, 0));
1564
1565 if (mState.mCursorPosition != oldPos) {
1566 if (aSelect) {
1567 if (oldPos == mInteractiveStart)
1568 mInteractiveStart = mState.mCursorPosition;
1569 else if (oldPos == mInteractiveEnd)
1570 mInteractiveEnd = mState.mCursorPosition;
1571 else {
1572 mInteractiveStart = mState.mCursorPosition;
1573 mInteractiveEnd = oldPos;
1574 }
1575 } else
1576 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1578 }
1579}
1580
1581void TextEditor::MoveEnd(bool aSelect) {
1582 auto oldPos = mState.mCursorPosition;
1583 SetCursorPosition(Coordinates(mState.mCursorPosition.mLine,
1584 GetLineMaxColumn(oldPos.mLine)));
1585
1586 if (mState.mCursorPosition != oldPos) {
1587 if (aSelect) {
1588 if (oldPos == mInteractiveEnd)
1589 mInteractiveEnd = mState.mCursorPosition;
1590 else if (oldPos == mInteractiveStart)
1591 mInteractiveStart = mState.mCursorPosition;
1592 else {
1593 mInteractiveStart = oldPos;
1594 mInteractiveEnd = mState.mCursorPosition;
1595 }
1596 } else
1597 mInteractiveStart = mInteractiveEnd = mState.mCursorPosition;
1599 }
1600}
1601
1603 assert(!mReadOnly);
1604
1605 if (mLines.empty()) return;
1606
1607 UndoRecord u;
1608 u.mBefore = mState;
1609
1610 if (HasSelection()) {
1612 u.mRemovedStart = mState.mSelectionStart;
1613 u.mRemovedEnd = mState.mSelectionEnd;
1614
1616 } else {
1617 auto pos = GetActualCursorCoordinates();
1618 SetCursorPosition(pos);
1619 auto& line = mLines[pos.mLine];
1620
1621 if (pos.mColumn == GetLineMaxColumn(pos.mLine)) {
1622 if (pos.mLine == (int)mLines.size() - 1) return;
1623
1624 u.mRemoved = '\n';
1627
1628 auto& nextLine = mLines[pos.mLine + 1];
1629 line.insert(line.end(), nextLine.begin(), nextLine.end());
1630 RemoveLine(pos.mLine + 1);
1631 } else {
1632 auto cindex = GetCharacterIndex(pos);
1634 u.mRemovedEnd.mColumn++;
1636
1637 auto d = UTF8CharLength(line[cindex].mChar);
1638 while (d-- > 0 && cindex < (int)line.size())
1639 line.erase(line.begin() + cindex);
1640 }
1641
1642 mTextChanged = true;
1643
1644 Colorize(pos.mLine, 1);
1645 }
1646
1647 u.mAfter = mState;
1648 AddUndo(u);
1649}
1650
1652 assert(!mReadOnly);
1653
1654 if (mLines.empty()) return;
1655
1656 UndoRecord u;
1657 u.mBefore = mState;
1658
1659 if (HasSelection()) {
1661 u.mRemovedStart = mState.mSelectionStart;
1662 u.mRemovedEnd = mState.mSelectionEnd;
1663
1665 } else {
1666 auto pos = GetActualCursorCoordinates();
1667 SetCursorPosition(pos);
1668
1669 if (mState.mCursorPosition.mColumn == 0) {
1670 if (mState.mCursorPosition.mLine == 0) return;
1671
1672 u.mRemoved = '\n';
1674 Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1));
1676
1677 auto& line = mLines[mState.mCursorPosition.mLine];
1678 auto& prevLine = mLines[mState.mCursorPosition.mLine - 1];
1679 auto prevSize = GetLineMaxColumn(mState.mCursorPosition.mLine - 1);
1680 prevLine.insert(prevLine.end(), line.begin(), line.end());
1681
1682 ErrorMarkers etmp;
1683 for (auto& i : mErrorMarkers)
1684 etmp.insert(ErrorMarkers::value_type(
1685 i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first,
1686 i.second));
1687 mErrorMarkers = std::move(etmp);
1688
1689 RemoveLine(mState.mCursorPosition.mLine);
1690 --mState.mCursorPosition.mLine;
1691 mState.mCursorPosition.mColumn = prevSize;
1692 } else {
1693 auto& line = mLines[mState.mCursorPosition.mLine];
1694 auto cindex = GetCharacterIndex(pos) - 1;
1695 auto cend = cindex + 1;
1696 while (cindex > 0 && IsUTFSequence(line[cindex].mChar)) --cindex;
1697
1698 // if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1)
1699 // --cindex;
1700
1703 --mState.mCursorPosition.mColumn;
1704
1705 while (cindex < line.size() && cend-- > cindex) {
1706 u.mRemoved += line[cindex].mChar;
1707 line.erase(line.begin() + cindex);
1708 }
1709 }
1710
1711 mTextChanged = true;
1712
1714 Colorize(mState.mCursorPosition.mLine, 1);
1715 }
1716
1717 u.mAfter = mState;
1718 AddUndo(u);
1719}
1720
1725
1727 SetSelection(Coordinates(0, 0), Coordinates((int)mLines.size(), 0));
1728}
1729
1731 return mState.mSelectionEnd > mState.mSelectionStart;
1732}
1733
1735 if (HasSelection()) {
1736 ImGui::SetClipboardText(GetSelectedText().c_str());
1737 } else {
1738 if (!mLines.empty()) {
1739 std::string str;
1740 auto& line = mLines[GetActualCursorCoordinates().mLine];
1741 for (auto& g : line) str.push_back(g.mChar);
1742 ImGui::SetClipboardText(str.c_str());
1743 }
1744 }
1745}
1746
1748 if (IsReadOnly()) {
1749 Copy();
1750 } else {
1751 if (HasSelection()) {
1752 UndoRecord u;
1753 u.mBefore = mState;
1755 u.mRemovedStart = mState.mSelectionStart;
1756 u.mRemovedEnd = mState.mSelectionEnd;
1757
1758 Copy();
1760
1761 u.mAfter = mState;
1762 AddUndo(u);
1763 }
1764 }
1765}
1766
1768 if (IsReadOnly()) return;
1769
1770 auto clipText = ImGui::GetClipboardText();
1771 if (clipText != nullptr && strlen(clipText) > 0) {
1772 UndoRecord u;
1773 u.mBefore = mState;
1774
1775 if (HasSelection()) {
1777 u.mRemovedStart = mState.mSelectionStart;
1778 u.mRemovedEnd = mState.mSelectionEnd;
1780 }
1781
1782 u.mAdded = clipText;
1784
1785 InsertText(clipText);
1786
1788 u.mAfter = mState;
1789 AddUndo(u);
1790 }
1791}
1792
1793bool TextEditor::CanUndo() const { return !mReadOnly && mUndoIndex > 0; }
1794
1796 return !mReadOnly && mUndoIndex < (int)mUndoBuffer.size();
1797}
1798
1799void TextEditor::Undo(int aSteps) {
1800 while (CanUndo() && aSteps-- > 0) mUndoBuffer[--mUndoIndex].Undo(this);
1801}
1802
1803void TextEditor::Redo(int aSteps) {
1804 while (CanRedo() && aSteps-- > 0) mUndoBuffer[mUndoIndex++].Redo(this);
1805}
1806
1808 const static Palette p = {{
1809 0xff7f7f7f, // Default
1810 0xffd69c56, // Keyword
1811 0xff00ff00, // Number
1812 0xff7070e0, // String
1813 0xff70a0e0, // Char literal
1814 0xffffffff, // Punctuation
1815 0xff408080, // Preprocessor
1816 0xffaaaaaa, // Identifier
1817 0xff9bc64d, // Known identifier
1818 0xffc040a0, // Preproc identifier
1819 0xff206020, // Comment (single line)
1820 0xff406020, // Comment (multi line)
1821 0xff101010, // Background
1822 0xffe0e0e0, // Cursor
1823 0x80a06020, // Selection
1824 0x800020ff, // ErrorMarker
1825 0x40f08000, // Breakpoint
1826 0xff707000, // Line number
1827 0x40000000, // Current line fill
1828 0x40808080, // Current line fill (inactive)
1829 0x40a0a0a0, // Current line edge
1830 }};
1831 return p;
1832}
1833
1835 const static Palette p = {{
1836 0xff7f7f7f, // None
1837 0xffff0c06, // Keyword
1838 0xff008000, // Number
1839 0xff2020a0, // String
1840 0xff304070, // Char literal
1841 0xff000000, // Punctuation
1842 0xff406060, // Preprocessor
1843 0xff404040, // Identifier
1844 0xff606010, // Known identifier
1845 0xffc040a0, // Preproc identifier
1846 0xff205020, // Comment (single line)
1847 0xff405020, // Comment (multi line)
1848 0xffffffff, // Background
1849 0xff000000, // Cursor
1850 0x80600000, // Selection
1851 0xa00010ff, // ErrorMarker
1852 0x80f08000, // Breakpoint
1853 0xff505000, // Line number
1854 0x40000000, // Current line fill
1855 0x40808080, // Current line fill (inactive)
1856 0x40000000, // Current line edge
1857 }};
1858 return p;
1859}
1860
1862 const static Palette p = {{
1863 0xff00ffff, // None
1864 0xffffff00, // Keyword
1865 0xff00ff00, // Number
1866 0xff808000, // String
1867 0xff808000, // Char literal
1868 0xffffffff, // Punctuation
1869 0xff008000, // Preprocessor
1870 0xff00ffff, // Identifier
1871 0xffffffff, // Known identifier
1872 0xffff00ff, // Preproc identifier
1873 0xff808080, // Comment (single line)
1874 0xff404040, // Comment (multi line)
1875 0xff800000, // Background
1876 0xff0080ff, // Cursor
1877 0x80ffff00, // Selection
1878 0xa00000ff, // ErrorMarker
1879 0x80ff8000, // Breakpoint
1880 0xff808000, // Line number
1881 0x40000000, // Current line fill
1882 0x40808080, // Current line fill (inactive)
1883 0x40000000, // Current line edge
1884 }};
1885 return p;
1886}
1887
1888std::string TextEditor::GetText() const {
1889 return GetText(Coordinates(), Coordinates((int)mLines.size(), 0));
1890}
1891
1892std::vector<std::string> TextEditor::GetTextLines() const {
1893 std::vector<std::string> result;
1894
1895 result.reserve(mLines.size());
1896
1897 for (auto& line : mLines) {
1898 std::string text;
1899
1900 text.resize(line.size());
1901
1902 for (size_t i = 0; i < line.size(); ++i) text[i] = line[i].mChar;
1903
1904 result.emplace_back(std::move(text));
1905 }
1906
1907 return result;
1908}
1909
1910std::string TextEditor::GetSelectedText() const {
1911 return GetText(mState.mSelectionStart, mState.mSelectionEnd);
1912}
1913
1915 auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine);
1916 return GetText(Coordinates(mState.mCursorPosition.mLine, 0),
1917 Coordinates(mState.mCursorPosition.mLine, lineLength));
1918}
1919
1921
1922void TextEditor::Colorize(int aFromLine, int aLines) {
1923 int toLine = aLines == -1 ? (int)mLines.size()
1924 : std::min((int)mLines.size(), aFromLine + aLines);
1925 mColorRangeMin = std::min(mColorRangeMin, aFromLine);
1926 mColorRangeMax = std::max(mColorRangeMax, toLine);
1927 mColorRangeMin = std::max(0, mColorRangeMin);
1929 mCheckComments = true;
1930}
1931
1932void TextEditor::ColorizeRange(int aFromLine, int aToLine) {
1933 if (mLines.empty() || aFromLine >= aToLine) return;
1934
1935 std::string buffer;
1936 std::cmatch results;
1937 std::string id;
1938
1939 int endLine = std::max(0, std::min((int)mLines.size(), aToLine));
1940 for (int i = aFromLine; i < endLine; ++i) {
1941 auto& line = mLines[i];
1942
1943 if (line.empty()) continue;
1944
1945 buffer.resize(line.size());
1946 for (size_t j = 0; j < line.size(); ++j) {
1947 auto& col = line[j];
1948 buffer[j] = col.mChar;
1949 col.mColorIndex = PaletteIndex::Default;
1950 }
1951
1952 const char* bufferBegin = &buffer.front();
1953 const char* bufferEnd = bufferBegin + buffer.size();
1954
1955 auto last = bufferEnd;
1956
1957 for (auto first = bufferBegin; first != last;) {
1958 const char* token_begin = nullptr;
1959 const char* token_end = nullptr;
1960 PaletteIndex token_color = PaletteIndex::Default;
1961
1962 bool hasTokenizeResult = false;
1963
1964 if (mLanguageDefinition.mTokenize != nullptr) {
1965 if (mLanguageDefinition.mTokenize(first, last, token_begin, token_end,
1966 token_color))
1967 hasTokenizeResult = true;
1968 }
1969
1970 if (hasTokenizeResult == false) {
1971 // todo : remove
1972 // printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last -
1973 // first), first);
1974
1975 for (auto& p : mRegexList) {
1976 if (std::regex_search(first, last, results, p.first,
1977 std::regex_constants::match_continuous)) {
1978 hasTokenizeResult = true;
1979
1980 auto& v = *results.begin();
1981 token_begin = v.first;
1982 token_end = v.second;
1983 token_color = p.second;
1984 break;
1985 }
1986 }
1987 }
1988
1989 if (hasTokenizeResult == false) {
1990 first++;
1991 } else {
1992 const size_t token_length = token_end - token_begin;
1993
1994 if (token_color == PaletteIndex::Identifier) {
1995 id.assign(token_begin, token_end);
1996
1997 // todo : allmost all language definitions use lower case to specify
1998 // keywords, so shouldn't this use ::tolower ?
1999 if (!mLanguageDefinition.mCaseSensitive)
2000 std::transform(id.begin(), id.end(), id.begin(), ::toupper);
2001
2002 if (!line[first - bufferBegin].mPreprocessor) {
2003 if (mLanguageDefinition.mKeywords.count(id) != 0)
2004 token_color = PaletteIndex::Keyword;
2005 else if (mLanguageDefinition.mIdentifiers.count(id) != 0)
2006 token_color = PaletteIndex::KnownIdentifier;
2007 else if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0)
2008 token_color = PaletteIndex::PreprocIdentifier;
2009 } else {
2010 if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0)
2011 token_color = PaletteIndex::PreprocIdentifier;
2012 }
2013 }
2014
2015 for (size_t j = 0; j < token_length; ++j)
2016 line[(token_begin - bufferBegin) + j].mColorIndex = token_color;
2017
2018 first = token_end;
2019 }
2020 }
2021 }
2022}
2023
2025 if (mLines.empty() || !mColorizerEnabled) return;
2026
2027 if (mCheckComments) {
2028 auto endLine = mLines.size();
2029 auto endIndex = 0;
2030 auto commentStartLine = endLine;
2031 auto commentStartIndex = endIndex;
2032 auto withinString = false;
2033 auto withinSingleLineComment = false;
2034 auto withinPreproc = false;
2035 auto firstChar =
2036 true; // there is no other non-whitespace characters in the line before
2037 auto concatenate = false; // '\' on the very end of the line
2038 auto currentLine = 0;
2039 auto currentIndex = 0;
2040 while (currentLine < endLine || currentIndex < endIndex) {
2041 auto& line = mLines[currentLine];
2042
2043 if (currentIndex == 0 && !concatenate) {
2044 withinSingleLineComment = false;
2045 withinPreproc = false;
2046 firstChar = true;
2047 }
2048
2049 concatenate = false;
2050
2051 if (!line.empty()) {
2052 auto& g = line[currentIndex];
2053 auto c = g.mChar;
2054
2055 if (c != mLanguageDefinition.mPreprocChar && !isspace(c))
2056 firstChar = false;
2057
2058 if (currentIndex == (int)line.size() - 1 &&
2059 line[line.size() - 1].mChar == '\\')
2060 concatenate = true;
2061
2062 bool inComment = (commentStartLine < currentLine ||
2063 (commentStartLine == currentLine &&
2064 commentStartIndex <= currentIndex));
2065
2066 if (withinString) {
2067 line[currentIndex].mMultiLineComment = inComment;
2068
2069 if (c == '\"') {
2070 if (currentIndex + 1 < (int)line.size() &&
2071 line[currentIndex + 1].mChar == '\"') {
2072 currentIndex += 1;
2073 if (currentIndex < (int)line.size())
2074 line[currentIndex].mMultiLineComment = inComment;
2075 } else
2076 withinString = false;
2077 } else if (c == '\\') {
2078 currentIndex += 1;
2079 if (currentIndex < (int)line.size())
2080 line[currentIndex].mMultiLineComment = inComment;
2081 }
2082 } else {
2083 if (firstChar && c == mLanguageDefinition.mPreprocChar)
2084 withinPreproc = true;
2085
2086 if (c == '\"') {
2087 withinString = true;
2088 line[currentIndex].mMultiLineComment = inComment;
2089 } else {
2090 auto pred = [](const char& a, const Glyph& b) {
2091 return a == b.mChar;
2092 };
2093 auto from = line.begin() + currentIndex;
2094 auto& startStr = mLanguageDefinition.mCommentStart;
2095 auto& singleStartStr = mLanguageDefinition.mSingleLineComment;
2096
2097 if (singleStartStr.size() > 0 &&
2098 currentIndex + singleStartStr.size() <= line.size() &&
2099 equals(singleStartStr.begin(), singleStartStr.end(), from,
2100 from + singleStartStr.size(), pred)) {
2101 withinSingleLineComment = true;
2102 } else if (!withinSingleLineComment &&
2103 currentIndex + startStr.size() <= line.size() &&
2104 equals(startStr.begin(), startStr.end(), from,
2105 from + startStr.size(), pred)) {
2106 commentStartLine = currentLine;
2107 commentStartIndex = currentIndex;
2108 }
2109
2110 inComment = inComment = (commentStartLine < currentLine ||
2111 (commentStartLine == currentLine &&
2112 commentStartIndex <= currentIndex));
2113
2114 line[currentIndex].mMultiLineComment = inComment;
2115 line[currentIndex].mComment = withinSingleLineComment;
2116
2117 auto& endStr = mLanguageDefinition.mCommentEnd;
2118 if (currentIndex + 1 >= (int)endStr.size() &&
2119 equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(),
2120 from + 1, pred)) {
2121 commentStartIndex = endIndex;
2122 commentStartLine = endLine;
2123 }
2124 }
2125 }
2126 line[currentIndex].mPreprocessor = withinPreproc;
2127 currentIndex += UTF8CharLength(c);
2128 if (currentIndex >= (int)line.size()) {
2129 currentIndex = 0;
2130 ++currentLine;
2131 }
2132 } else {
2133 currentIndex = 0;
2134 ++currentLine;
2135 }
2136 }
2137 mCheckComments = false;
2138 }
2139
2141 const int increment =
2142 (mLanguageDefinition.mTokenize == nullptr) ? 10 : 10000;
2143 const int to = std::min(mColorRangeMin + increment, mColorRangeMax);
2145 mColorRangeMin = to;
2146
2148 mColorRangeMin = std::numeric_limits<int>::max();
2149 mColorRangeMax = 0;
2150 }
2151 return;
2152 }
2153}
2154
2156 auto& line = mLines[aFrom.mLine];
2157 float distance = 0.0f;
2158 float spaceSize = ImGui::GetFont()
2159 ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f,
2160 " ", nullptr, nullptr)
2161 .x;
2162 int colIndex = GetCharacterIndex(aFrom);
2163 for (size_t it = 0u; it < line.size() && it < colIndex;) {
2164 if (line[it].mChar == '\t') {
2165 distance = (1.0f + std::floor((1.0f + distance) /
2166 (float(mTabSize) * spaceSize))) *
2167 (float(mTabSize) * spaceSize);
2168 ++it;
2169 } else {
2170 auto d = UTF8CharLength(line[it].mChar);
2171 char tempCString[7];
2172 int i = 0;
2173 for (; i < 6 && d-- > 0 && it < (int)line.size(); i++, it++)
2174 tempCString[i] = line[it].mChar;
2175
2176 tempCString[i] = '\0';
2177 distance += ImGui::GetFont()
2178 ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f,
2179 tempCString, nullptr, nullptr)
2180 .x;
2181 }
2182 }
2183
2184 return distance;
2185}
2186
2188 if (!mWithinRender) {
2189 mScrollToCursor = true;
2190 return;
2191 }
2192
2193 float scrollX = ImGui::GetScrollX();
2194 float scrollY = ImGui::GetScrollY();
2195
2196 auto height = ImGui::GetWindowHeight();
2197 auto width = ImGui::GetWindowWidth();
2198
2199 auto top = 1 + (int)ceil(scrollY / mCharAdvance.y);
2200 auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y);
2201
2202 auto left = (int)ceil(scrollX / mCharAdvance.x);
2203 auto right = (int)ceil((scrollX + width) / mCharAdvance.x);
2204
2205 auto pos = GetActualCursorCoordinates();
2206 auto len = TextDistanceToLineStart(pos);
2207
2208 if (pos.mLine < top)
2209 ImGui::SetScrollY(std::max(0.0f, (pos.mLine - 1) * mCharAdvance.y));
2210 if (pos.mLine > bottom - 4)
2211 ImGui::SetScrollY(
2212 std::max(0.0f, (pos.mLine + 4) * mCharAdvance.y - height));
2213 if (len + mTextStart < left + 4)
2214 ImGui::SetScrollX(std::max(0.0f, len + mTextStart - 4));
2215 if (len + mTextStart > right - 4)
2216 ImGui::SetScrollX(std::max(0.0f, len + mTextStart + 4 - width));
2217}
2218
2220 auto height = ImGui::GetWindowHeight() - 20.0f;
2221 return (int)floor(height / mCharAdvance.y);
2222}
2223
2224TextEditor::UndoRecord::UndoRecord(const std::string& aAdded,
2225 const TextEditor::Coordinates aAddedStart,
2226 const TextEditor::Coordinates aAddedEnd,
2227 const std::string& aRemoved,
2228 const TextEditor::Coordinates aRemovedStart,
2229 const TextEditor::Coordinates aRemovedEnd,
2230 TextEditor::EditorState& aBefore,
2232 : mAdded(aAdded),
2233 mAddedStart(aAddedStart),
2234 mAddedEnd(aAddedEnd),
2235 mRemoved(aRemoved),
2236 mRemovedStart(aRemovedStart),
2237 mRemovedEnd(aRemovedEnd),
2238 mBefore(aBefore),
2239 mAfter(aAfter) {
2240 assert(mAddedStart <= mAddedEnd);
2241 assert(mRemovedStart <= mRemovedEnd);
2242}
2243
2245 if (!mAdded.empty()) {
2247 aEditor->Colorize(mAddedStart.mLine - 1,
2248 mAddedEnd.mLine - mAddedStart.mLine + 2);
2249 }
2250
2251 if (!mRemoved.empty()) {
2252 auto start = mRemovedStart;
2253 aEditor->InsertTextAt(start, mRemoved.c_str());
2254 aEditor->Colorize(mRemovedStart.mLine - 1,
2255 mRemovedEnd.mLine - mRemovedStart.mLine + 2);
2256 }
2257
2258 aEditor->mState = mBefore;
2259 aEditor->EnsureCursorVisible();
2260}
2261
2263 if (!mRemoved.empty()) {
2265 aEditor->Colorize(mRemovedStart.mLine - 1,
2266 mRemovedEnd.mLine - mRemovedStart.mLine + 1);
2267 }
2268
2269 if (!mAdded.empty()) {
2270 auto start = mAddedStart;
2271 aEditor->InsertTextAt(start, mAdded.c_str());
2272 aEditor->Colorize(mAddedStart.mLine - 1,
2273 mAddedEnd.mLine - mAddedStart.mLine + 1);
2274 }
2275
2276 aEditor->mState = mAfter;
2277 aEditor->EnsureCursorVisible();
2278}
2279
2280static bool TokenizeCStyleString(const char* in_begin, const char* in_end,
2281 const char*& out_begin, const char*& out_end) {
2282 const char* p = in_begin;
2283
2284 if (*p == '"') {
2285 p++;
2286
2287 while (p < in_end) {
2288 // handle end of string
2289 if (*p == '"') {
2290 out_begin = in_begin;
2291 out_end = p + 1;
2292 return true;
2293 }
2294
2295 // handle escape character for "
2296 if (*p == '\\' && p + 1 < in_end && p[1] == '"') p++;
2297
2298 p++;
2299 }
2300 }
2301
2302 return false;
2303}
2304
2305static bool TokenizeCStyleCharacterLiteral(const char* in_begin,
2306 const char* in_end,
2307 const char*& out_begin,
2308 const char*& out_end) {
2309 const char* p = in_begin;
2310
2311 if (*p == '\'') {
2312 p++;
2313
2314 // handle escape characters
2315 if (p < in_end && *p == '\\') p++;
2316
2317 if (p < in_end) p++;
2318
2319 // handle end of character literal
2320 if (p < in_end && *p == '\'') {
2321 out_begin = in_begin;
2322 out_end = p + 1;
2323 return true;
2324 }
2325 }
2326
2327 return false;
2328}
2329
2330static bool TokenizeCStyleIdentifier(const char* in_begin, const char* in_end,
2331 const char*& out_begin,
2332 const char*& out_end) {
2333 const char* p = in_begin;
2334
2335 if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_') {
2336 p++;
2337
2338 while ((p < in_end) &&
2339 ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') ||
2340 (*p >= '0' && *p <= '9') || *p == '_'))
2341 p++;
2342
2343 out_begin = in_begin;
2344 out_end = p;
2345 return true;
2346 }
2347
2348 return false;
2349}
2350
2351static bool TokenizeCStyleNumber(const char* in_begin, const char* in_end,
2352 const char*& out_begin, const char*& out_end) {
2353 const char* p = in_begin;
2354
2355 const bool startsWithNumber = *p >= '0' && *p <= '9';
2356
2357 if (*p != '+' && *p != '-' && !startsWithNumber) return false;
2358
2359 p++;
2360
2361 bool hasNumber = startsWithNumber;
2362
2363 while (p < in_end && (*p >= '0' && *p <= '9')) {
2364 hasNumber = true;
2365
2366 p++;
2367 }
2368
2369 if (hasNumber == false) return false;
2370
2371 bool isFloat = false;
2372 bool isHex = false;
2373 bool isBinary = false;
2374
2375 if (p < in_end) {
2376 if (*p == '.') {
2377 isFloat = true;
2378
2379 p++;
2380
2381 while (p < in_end && (*p >= '0' && *p <= '9')) p++;
2382 } else if (*p == 'x' || *p == 'X') {
2383 // hex formatted integer of the type 0xef80
2384
2385 isHex = true;
2386
2387 p++;
2388
2389 while (p < in_end &&
2390 ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') ||
2391 (*p >= 'A' && *p <= 'F')))
2392 p++;
2393 } else if (*p == 'b' || *p == 'B') {
2394 // binary formatted integer of the type 0b01011101
2395
2396 isBinary = true;
2397
2398 p++;
2399
2400 while (p < in_end && (*p >= '0' && *p <= '1')) p++;
2401 }
2402 }
2403
2404 if (isHex == false && isBinary == false) {
2405 // floating point exponent
2406 if (p < in_end && (*p == 'e' || *p == 'E')) {
2407 isFloat = true;
2408
2409 p++;
2410
2411 if (p < in_end && (*p == '+' || *p == '-')) p++;
2412
2413 bool hasDigits = false;
2414
2415 while (p < in_end && (*p >= '0' && *p <= '9')) {
2416 hasDigits = true;
2417
2418 p++;
2419 }
2420
2421 if (hasDigits == false) return false;
2422 }
2423
2424 // single precision floating point type
2425 if (p < in_end && *p == 'f') p++;
2426 }
2427
2428 if (isFloat == false) {
2429 // integer size type
2430 while (p < in_end && (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L'))
2431 p++;
2432 }
2433
2434 out_begin = in_begin;
2435 out_end = p;
2436 return true;
2437}
2438
2439static bool TokenizeCStylePunctuation(const char* in_begin, const char* in_end,
2440 const char*& out_begin,
2441 const char*& out_end) {
2442 (void)in_end;
2443
2444 switch (*in_begin) {
2445 case '[':
2446 case ']':
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 out_begin = in_begin;
2470 out_end = in_begin + 1;
2471 return true;
2472 }
2473
2474 return false;
2475}
2476
2477const TextEditor::LanguageDefinition&
2479 static bool inited = false;
2480 static LanguageDefinition langDef;
2481 if (!inited) {
2482 static const char* const cppKeywords[] = {"alignas",
2483 "alignof",
2484 "and",
2485 "and_eq",
2486 "asm",
2487 "atomic_cancel",
2488 "atomic_commit",
2489 "atomic_noexcept",
2490 "auto",
2491 "bitand",
2492 "bitor",
2493 "bool",
2494 "break",
2495 "case",
2496 "catch",
2497 "char",
2498 "char16_t",
2499 "char32_t",
2500 "class",
2501 "compl",
2502 "concept",
2503 "const",
2504 "constexpr",
2505 "const_cast",
2506 "continue",
2507 "decltype",
2508 "default",
2509 "delete",
2510 "do",
2511 "double",
2512 "dynamic_cast",
2513 "else",
2514 "enum",
2515 "explicit",
2516 "export",
2517 "extern",
2518 "false",
2519 "float",
2520 "for",
2521 "friend",
2522 "goto",
2523 "if",
2524 "import",
2525 "inline",
2526 "int",
2527 "long",
2528 "module",
2529 "mutable",
2530 "namespace",
2531 "new",
2532 "noexcept",
2533 "not",
2534 "not_eq",
2535 "nullptr",
2536 "operator",
2537 "or",
2538 "or_eq",
2539 "private",
2540 "protected",
2541 "public",
2542 "register",
2543 "reinterpret_cast",
2544 "requires",
2545 "return",
2546 "short",
2547 "signed",
2548 "sizeof",
2549 "static",
2550 "static_assert",
2551 "static_cast",
2552 "struct",
2553 "switch",
2554 "synchronized",
2555 "template",
2556 "this",
2557 "thread_local",
2558 "throw",
2559 "true",
2560 "try",
2561 "typedef",
2562 "typeid",
2563 "typename",
2564 "union",
2565 "unsigned",
2566 "using",
2567 "virtual",
2568 "void",
2569 "volatile",
2570 "wchar_t",
2571 "while",
2572 "xor",
2573 "xor_eq"};
2574 for (auto& k : cppKeywords) langDef.mKeywords.insert(k);
2575
2576 static const char* const identifiers[] = {
2577 "abort", "abs", "acos", "asin", "atan",
2578 "atexit", "atof", "atoi", "atol", "ceil",
2579 "clock", "cosh", "ctime", "div", "exit",
2580 "fabs", "floor", "fmod", "getchar", "getenv",
2581 "isalnum", "isalpha", "isdigit", "isgraph", "ispunct",
2582 "isspace", "isupper", "kbhit", "log10", "log2",
2583 "log", "memcmp", "modf", "pow", "printf",
2584 "sprintf", "snprintf", "putchar", "putenv", "puts",
2585 "rand", "remove", "rename", "sinh", "sqrt",
2586 "srand", "strcat", "strcmp", "strerror", "time",
2587 "tolower", "toupper", "std", "string", "vector",
2588 "map", "unordered_map", "set", "unordered_set", "min",
2589 "max"};
2590 for (auto& k : identifiers) {
2591 Identifier id;
2592 id.mDeclaration = "Built-in function";
2593 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
2594 }
2595
2596 langDef.mTokenize = [](const char* in_begin, const char* in_end,
2597 const char*& out_begin, const char*& out_end,
2598 PaletteIndex& paletteIndex) -> bool {
2599 paletteIndex = PaletteIndex::Max;
2600
2601 while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin))
2602 in_begin++;
2603
2604 if (in_begin == in_end) {
2605 out_begin = in_end;
2606 out_end = in_end;
2607 paletteIndex = PaletteIndex::Default;
2608 } else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end))
2609 paletteIndex = PaletteIndex::String;
2610 else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin,
2611 out_end))
2612 paletteIndex = PaletteIndex::CharLiteral;
2613 else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end))
2614 paletteIndex = PaletteIndex::Identifier;
2615 else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end))
2616 paletteIndex = PaletteIndex::Number;
2617 else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end))
2618 paletteIndex = PaletteIndex::Punctuation;
2619
2620 return paletteIndex != PaletteIndex::Max;
2621 };
2622
2623 langDef.mCommentStart = "/*";
2624 langDef.mCommentEnd = "*/";
2625 langDef.mSingleLineComment = "//";
2626
2627 langDef.mCaseSensitive = true;
2628 langDef.mAutoIndentation = true;
2629
2630 langDef.mName = "C++";
2631
2632 inited = true;
2633 }
2634 return langDef;
2635}
2636
2638 static bool inited = false;
2639 static LanguageDefinition langDef;
2640 if (!inited) {
2641 static const char* const keywords[] = {
2642 "AppendStructuredBuffer",
2643 "asm",
2644 "asm_fragment",
2645 "BlendState",
2646 "bool",
2647 "break",
2648 "Buffer",
2649 "ByteAddressBuffer",
2650 "case",
2651 "cbuffer",
2652 "centroid",
2653 "class",
2654 "column_major",
2655 "compile",
2656 "compile_fragment",
2657 "CompileShader",
2658 "const",
2659 "continue",
2660 "ComputeShader",
2661 "ConsumeStructuredBuffer",
2662 "default",
2663 "DepthStencilState",
2664 "DepthStencilView",
2665 "discard",
2666 "do",
2667 "double",
2668 "DomainShader",
2669 "dword",
2670 "else",
2671 "export",
2672 "extern",
2673 "false",
2674 "float",
2675 "for",
2676 "fxgroup",
2677 "GeometryShader",
2678 "groupshared",
2679 "half",
2680 "Hullshader",
2681 "if",
2682 "in",
2683 "inline",
2684 "inout",
2685 "InputPatch",
2686 "int",
2687 "interface",
2688 "line",
2689 "lineadj",
2690 "linear",
2691 "LineStream",
2692 "matrix",
2693 "min16float",
2694 "min10float",
2695 "min16int",
2696 "min12int",
2697 "min16uint",
2698 "namespace",
2699 "nointerpolation",
2700 "noperspective",
2701 "NULL",
2702 "out",
2703 "OutputPatch",
2704 "packoffset",
2705 "pass",
2706 "pixelfragment",
2707 "PixelShader",
2708 "point",
2709 "PointStream",
2710 "precise",
2711 "RasterizerState",
2712 "RenderTargetView",
2713 "return",
2714 "register",
2715 "row_major",
2716 "RWBuffer",
2717 "RWByteAddressBuffer",
2718 "RWStructuredBuffer",
2719 "RWTexture1D",
2720 "RWTexture1DArray",
2721 "RWTexture2D",
2722 "RWTexture2DArray",
2723 "RWTexture3D",
2724 "sample",
2725 "sampler",
2726 "SamplerState",
2727 "SamplerComparisonState",
2728 "shared",
2729 "snorm",
2730 "stateblock",
2731 "stateblock_state",
2732 "static",
2733 "string",
2734 "struct",
2735 "switch",
2736 "StructuredBuffer",
2737 "tbuffer",
2738 "technique",
2739 "technique10",
2740 "technique11",
2741 "texture",
2742 "Texture1D",
2743 "Texture1DArray",
2744 "Texture2D",
2745 "Texture2DArray",
2746 "Texture2DMS",
2747 "Texture2DMSArray",
2748 "Texture3D",
2749 "TextureCube",
2750 "TextureCubeArray",
2751 "true",
2752 "typedef",
2753 "triangle",
2754 "triangleadj",
2755 "TriangleStream",
2756 "uint",
2757 "uniform",
2758 "unorm",
2759 "unsigned",
2760 "vector",
2761 "vertexfragment",
2762 "VertexShader",
2763 "void",
2764 "volatile",
2765 "while",
2766 "bool1",
2767 "bool2",
2768 "bool3",
2769 "bool4",
2770 "double1",
2771 "double2",
2772 "double3",
2773 "double4",
2774 "float1",
2775 "float2",
2776 "float3",
2777 "float4",
2778 "int1",
2779 "int2",
2780 "int3",
2781 "int4",
2782 "in",
2783 "out",
2784 "inout",
2785 "uint1",
2786 "uint2",
2787 "uint3",
2788 "uint4",
2789 "dword1",
2790 "dword2",
2791 "dword3",
2792 "dword4",
2793 "half1",
2794 "half2",
2795 "half3",
2796 "half4",
2797 "float1x1",
2798 "float2x1",
2799 "float3x1",
2800 "float4x1",
2801 "float1x2",
2802 "float2x2",
2803 "float3x2",
2804 "float4x2",
2805 "float1x3",
2806 "float2x3",
2807 "float3x3",
2808 "float4x3",
2809 "float1x4",
2810 "float2x4",
2811 "float3x4",
2812 "float4x4",
2813 "half1x1",
2814 "half2x1",
2815 "half3x1",
2816 "half4x1",
2817 "half1x2",
2818 "half2x2",
2819 "half3x2",
2820 "half4x2",
2821 "half1x3",
2822 "half2x3",
2823 "half3x3",
2824 "half4x3",
2825 "half1x4",
2826 "half2x4",
2827 "half3x4",
2828 "half4x4",
2829 };
2830 for (auto& k : keywords) langDef.mKeywords.insert(k);
2831
2832 static const char* const identifiers[] = {
2833 "abort",
2834 "abs",
2835 "acos",
2836 "all",
2837 "AllMemoryBarrier",
2838 "AllMemoryBarrierWithGroupSync",
2839 "any",
2840 "asdouble",
2841 "asfloat",
2842 "asin",
2843 "asint",
2844 "asint",
2845 "asuint",
2846 "asuint",
2847 "atan",
2848 "atan2",
2849 "ceil",
2850 "CheckAccessFullyMapped",
2851 "clamp",
2852 "clip",
2853 "cos",
2854 "cosh",
2855 "countbits",
2856 "cross",
2857 "D3DCOLORtoUBYTE4",
2858 "ddx",
2859 "ddx_coarse",
2860 "ddx_fine",
2861 "ddy",
2862 "ddy_coarse",
2863 "ddy_fine",
2864 "degrees",
2865 "determinant",
2866 "DeviceMemoryBarrier",
2867 "DeviceMemoryBarrierWithGroupSync",
2868 "distance",
2869 "dot",
2870 "dst",
2871 "errorf",
2872 "EvaluateAttributeAtCentroid",
2873 "EvaluateAttributeAtSample",
2874 "EvaluateAttributeSnapped",
2875 "exp",
2876 "exp2",
2877 "f16tof32",
2878 "f32tof16",
2879 "faceforward",
2880 "firstbithigh",
2881 "firstbitlow",
2882 "floor",
2883 "fma",
2884 "fmod",
2885 "frac",
2886 "frexp",
2887 "fwidth",
2888 "GetRenderTargetSampleCount",
2889 "GetRenderTargetSamplePosition",
2890 "GroupMemoryBarrier",
2891 "GroupMemoryBarrierWithGroupSync",
2892 "InterlockedAdd",
2893 "InterlockedAnd",
2894 "InterlockedCompareExchange",
2895 "InterlockedCompareStore",
2896 "InterlockedExchange",
2897 "InterlockedMax",
2898 "InterlockedMin",
2899 "InterlockedOr",
2900 "InterlockedXor",
2901 "isfinite",
2902 "isinf",
2903 "isnan",
2904 "ldexp",
2905 "length",
2906 "lerp",
2907 "lit",
2908 "log",
2909 "log10",
2910 "log2",
2911 "mad",
2912 "max",
2913 "min",
2914 "modf",
2915 "msad4",
2916 "mul",
2917 "noise",
2918 "normalize",
2919 "pow",
2920 "printf",
2921 "Process2DQuadTessFactorsAvg",
2922 "Process2DQuadTessFactorsMax",
2923 "Process2DQuadTessFactorsMin",
2924 "ProcessIsolineTessFactors",
2925 "ProcessQuadTessFactorsAvg",
2926 "ProcessQuadTessFactorsMax",
2927 "ProcessQuadTessFactorsMin",
2928 "ProcessTriTessFactorsAvg",
2929 "ProcessTriTessFactorsMax",
2930 "ProcessTriTessFactorsMin",
2931 "radians",
2932 "rcp",
2933 "reflect",
2934 "refract",
2935 "reversebits",
2936 "round",
2937 "rsqrt",
2938 "saturate",
2939 "sign",
2940 "sin",
2941 "sincos",
2942 "sinh",
2943 "smoothstep",
2944 "sqrt",
2945 "step",
2946 "tan",
2947 "tanh",
2948 "tex1D",
2949 "tex1D",
2950 "tex1Dbias",
2951 "tex1Dgrad",
2952 "tex1Dlod",
2953 "tex1Dproj",
2954 "tex2D",
2955 "tex2D",
2956 "tex2Dbias",
2957 "tex2Dgrad",
2958 "tex2Dlod",
2959 "tex2Dproj",
2960 "tex3D",
2961 "tex3D",
2962 "tex3Dbias",
2963 "tex3Dgrad",
2964 "tex3Dlod",
2965 "tex3Dproj",
2966 "texCUBE",
2967 "texCUBE",
2968 "texCUBEbias",
2969 "texCUBEgrad",
2970 "texCUBElod",
2971 "texCUBEproj",
2972 "transpose",
2973 "trunc"};
2974 for (auto& k : identifiers) {
2975 Identifier id;
2976 id.mDeclaration = "Built-in function";
2977 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
2978 }
2979
2980 langDef.mTokenRegexStrings.push_back(
2981 std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+",
2983 langDef.mTokenRegexStrings.push_back(
2984 std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"",
2986 langDef.mTokenRegexStrings.push_back(
2987 std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'",
2989 langDef.mTokenRegexStrings.push_back(
2990 std::make_pair<std::string, PaletteIndex>(
2991 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
2993 langDef.mTokenRegexStrings.push_back(
2994 std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?",
2996 langDef.mTokenRegexStrings.push_back(
2997 std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?",
2999 langDef.mTokenRegexStrings.push_back(
3000 std::make_pair<std::string, PaletteIndex>(
3001 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
3002 langDef.mTokenRegexStrings.push_back(
3003 std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*",
3005 langDef.mTokenRegexStrings.push_back(
3006 std::make_pair<std::string, PaletteIndex>(
3007 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
3008 "\\;\\,\\.]",
3010
3011 langDef.mCommentStart = "/*";
3012 langDef.mCommentEnd = "*/";
3013 langDef.mSingleLineComment = "//";
3014
3015 langDef.mCaseSensitive = true;
3016 langDef.mAutoIndentation = true;
3017
3018 langDef.mName = "HLSL";
3019
3020 inited = true;
3021 }
3022 return langDef;
3023}
3024
3026 static bool inited = false;
3027 static LanguageDefinition langDef;
3028 if (!inited) {
3029 static const char* const keywords[] = {
3030 "auto", "break", "case", "char",
3031 "const", "continue", "default", "do",
3032 "double", "else", "enum", "extern",
3033 "float", "for", "goto", "if",
3034 "inline", "int", "long", "register",
3035 "restrict", "return", "short", "signed",
3036 "sizeof", "static", "struct", "switch",
3037 "typedef", "union", "unsigned", "void",
3038 "volatile", "while", "_Alignas", "_Alignof",
3039 "_Atomic", "_Bool", "_Complex", "_Generic",
3040 "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local"};
3041 for (auto& k : keywords) langDef.mKeywords.insert(k);
3042
3043 static const char* const identifiers[] = {
3044 "abort", "abs", "acos", "asin", "atan", "atexit",
3045 "atof", "atoi", "atol", "ceil", "clock", "cosh",
3046 "ctime", "div", "exit", "fabs", "floor", "fmod",
3047 "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph",
3048 "ispunct", "isspace", "isupper", "kbhit", "log10", "log2",
3049 "log", "memcmp", "modf", "pow", "putchar", "putenv",
3050 "puts", "rand", "remove", "rename", "sinh", "sqrt",
3051 "srand", "strcat", "strcmp", "strerror", "time", "tolower",
3052 "toupper"};
3053 for (auto& k : identifiers) {
3054 Identifier id;
3055 id.mDeclaration = "Built-in function";
3056 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
3057 }
3058
3059 langDef.mTokenRegexStrings.push_back(
3060 std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+",
3062 langDef.mTokenRegexStrings.push_back(
3063 std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"",
3065 langDef.mTokenRegexStrings.push_back(
3066 std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'",
3068 langDef.mTokenRegexStrings.push_back(
3069 std::make_pair<std::string, PaletteIndex>(
3070 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
3072 langDef.mTokenRegexStrings.push_back(
3073 std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?",
3075 langDef.mTokenRegexStrings.push_back(
3076 std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?",
3078 langDef.mTokenRegexStrings.push_back(
3079 std::make_pair<std::string, PaletteIndex>(
3080 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
3081 langDef.mTokenRegexStrings.push_back(
3082 std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*",
3084 langDef.mTokenRegexStrings.push_back(
3085 std::make_pair<std::string, PaletteIndex>(
3086 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
3087 "\\;\\,\\.]",
3089
3090 langDef.mCommentStart = "/*";
3091 langDef.mCommentEnd = "*/";
3092 langDef.mSingleLineComment = "//";
3093
3094 langDef.mCaseSensitive = true;
3095 langDef.mAutoIndentation = true;
3096
3097 langDef.mName = "GLSL";
3098
3099 inited = true;
3100 }
3101 return langDef;
3102}
3103
3105 static bool inited = false;
3106 static LanguageDefinition langDef;
3107 if (!inited) {
3108 static const char* const keywords[] = {
3109 "auto", "break", "case", "char",
3110 "const", "continue", "default", "do",
3111 "double", "else", "enum", "extern",
3112 "float", "for", "goto", "if",
3113 "inline", "int", "long", "register",
3114 "restrict", "return", "short", "signed",
3115 "sizeof", "static", "struct", "switch",
3116 "typedef", "union", "unsigned", "void",
3117 "volatile", "while", "_Alignas", "_Alignof",
3118 "_Atomic", "_Bool", "_Complex", "_Generic",
3119 "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local"};
3120 for (auto& k : keywords) langDef.mKeywords.insert(k);
3121
3122 static const char* const identifiers[] = {
3123 "abort", "abs", "acos", "asin", "atan", "atexit",
3124 "atof", "atoi", "atol", "ceil", "clock", "cosh",
3125 "ctime", "div", "exit", "fabs", "floor", "fmod",
3126 "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph",
3127 "ispunct", "isspace", "isupper", "kbhit", "log10", "log2",
3128 "log", "memcmp", "modf", "pow", "putchar", "putenv",
3129 "puts", "rand", "remove", "rename", "sinh", "sqrt",
3130 "srand", "strcat", "strcmp", "strerror", "time", "tolower",
3131 "toupper"};
3132 for (auto& k : identifiers) {
3133 Identifier id;
3134 id.mDeclaration = "Built-in function";
3135 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
3136 }
3137
3138 langDef.mTokenize = [](const char* in_begin, const char* in_end,
3139 const char*& out_begin, const char*& out_end,
3140 PaletteIndex& paletteIndex) -> bool {
3141 paletteIndex = PaletteIndex::Max;
3142
3143 while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin))
3144 in_begin++;
3145
3146 if (in_begin == in_end) {
3147 out_begin = in_end;
3148 out_end = in_end;
3149 paletteIndex = PaletteIndex::Default;
3150 } else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end))
3151 paletteIndex = PaletteIndex::String;
3152 else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin,
3153 out_end))
3154 paletteIndex = PaletteIndex::CharLiteral;
3155 else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end))
3156 paletteIndex = PaletteIndex::Identifier;
3157 else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end))
3158 paletteIndex = PaletteIndex::Number;
3159 else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end))
3160 paletteIndex = PaletteIndex::Punctuation;
3161
3162 return paletteIndex != PaletteIndex::Max;
3163 };
3164
3165 langDef.mCommentStart = "/*";
3166 langDef.mCommentEnd = "*/";
3167 langDef.mSingleLineComment = "//";
3168
3169 langDef.mCaseSensitive = true;
3170 langDef.mAutoIndentation = true;
3171
3172 langDef.mName = "C";
3173
3174 inited = true;
3175 }
3176 return langDef;
3177}
3178
3180 static bool inited = false;
3181 static LanguageDefinition langDef;
3182 if (!inited) {
3183 static const char* const keywords[] = {"ADD",
3184 "EXCEPT",
3185 "PERCENT",
3186 "ALL",
3187 "EXEC",
3188 "PLAN",
3189 "ALTER",
3190 "EXECUTE",
3191 "PRECISION",
3192 "AND",
3193 "EXISTS",
3194 "PRIMARY",
3195 "ANY",
3196 "EXIT",
3197 "PRINT",
3198 "AS",
3199 "FETCH",
3200 "PROC",
3201 "ASC",
3202 "FILE",
3203 "PROCEDURE",
3204 "AUTHORIZATION",
3205 "FILLFACTOR",
3206 "PUBLIC",
3207 "BACKUP",
3208 "FOR",
3209 "RAISERROR",
3210 "BEGIN",
3211 "FOREIGN",
3212 "READ",
3213 "BETWEEN",
3214 "FREETEXT",
3215 "READTEXT",
3216 "BREAK",
3217 "FREETEXTTABLE",
3218 "RECONFIGURE",
3219 "BROWSE",
3220 "FROM",
3221 "REFERENCES",
3222 "BULK",
3223 "FULL",
3224 "REPLICATION",
3225 "BY",
3226 "FUNCTION",
3227 "RESTORE",
3228 "CASCADE",
3229 "GOTO",
3230 "RESTRICT",
3231 "CASE",
3232 "GRANT",
3233 "RETURN",
3234 "CHECK",
3235 "GROUP",
3236 "REVOKE",
3237 "CHECKPOINT",
3238 "HAVING",
3239 "RIGHT",
3240 "CLOSE",
3241 "HOLDLOCK",
3242 "ROLLBACK",
3243 "CLUSTERED",
3244 "IDENTITY",
3245 "ROWCOUNT",
3246 "COALESCE",
3247 "IDENTITY_INSERT",
3248 "ROWGUIDCOL",
3249 "COLLATE",
3250 "IDENTITYCOL",
3251 "RULE",
3252 "COLUMN",
3253 "IF",
3254 "SAVE",
3255 "COMMIT",
3256 "IN",
3257 "SCHEMA",
3258 "COMPUTE",
3259 "INDEX",
3260 "SELECT",
3261 "CONSTRAINT",
3262 "INNER",
3263 "SESSION_USER",
3264 "CONTAINS",
3265 "INSERT",
3266 "SET",
3267 "CONTAINSTABLE",
3268 "INTERSECT",
3269 "SETUSER",
3270 "CONTINUE",
3271 "INTO",
3272 "SHUTDOWN",
3273 "CONVERT",
3274 "IS",
3275 "SOME",
3276 "CREATE",
3277 "JOIN",
3278 "STATISTICS",
3279 "CROSS",
3280 "KEY",
3281 "SYSTEM_USER",
3282 "CURRENT",
3283 "KILL",
3284 "TABLE",
3285 "CURRENT_DATE",
3286 "LEFT",
3287 "TEXTSIZE",
3288 "CURRENT_TIME",
3289 "LIKE",
3290 "THEN",
3291 "CURRENT_TIMESTAMP",
3292 "LINENO",
3293 "TO",
3294 "CURRENT_USER",
3295 "LOAD",
3296 "TOP",
3297 "CURSOR",
3298 "NATIONAL",
3299 "TRAN",
3300 "DATABASE",
3301 "NOCHECK",
3302 "TRANSACTION",
3303 "DBCC",
3304 "NONCLUSTERED",
3305 "TRIGGER",
3306 "DEALLOCATE",
3307 "NOT",
3308 "TRUNCATE",
3309 "DECLARE",
3310 "NULL",
3311 "TSEQUAL",
3312 "DEFAULT",
3313 "NULLIF",
3314 "UNION",
3315 "DELETE",
3316 "OF",
3317 "UNIQUE",
3318 "DENY",
3319 "OFF",
3320 "UPDATE",
3321 "DESC",
3322 "OFFSETS",
3323 "UPDATETEXT",
3324 "DISK",
3325 "ON",
3326 "USE",
3327 "DISTINCT",
3328 "OPEN",
3329 "USER",
3330 "DISTRIBUTED",
3331 "OPENDATASOURCE",
3332 "VALUES",
3333 "DOUBLE",
3334 "OPENQUERY",
3335 "VARYING",
3336 "DROP",
3337 "OPENROWSET",
3338 "VIEW",
3339 "DUMMY",
3340 "OPENXML",
3341 "WAITFOR",
3342 "DUMP",
3343 "OPTION",
3344 "WHEN",
3345 "ELSE",
3346 "OR",
3347 "WHERE",
3348 "END",
3349 "ORDER",
3350 "WHILE",
3351 "ERRLVL",
3352 "OUTER",
3353 "WITH",
3354 "ESCAPE",
3355 "OVER",
3356 "WRITETEXT"};
3357
3358 for (auto& k : keywords) langDef.mKeywords.insert(k);
3359
3360 static const char* const identifiers[] = {"ABS",
3361 "ACOS",
3362 "ADD_MONTHS",
3363 "ASCII",
3364 "ASCIISTR",
3365 "ASIN",
3366 "ATAN",
3367 "ATAN2",
3368 "AVG",
3369 "BFILENAME",
3370 "BIN_TO_NUM",
3371 "BITAND",
3372 "CARDINALITY",
3373 "CASE",
3374 "CAST",
3375 "CEIL",
3376 "CHARTOROWID",
3377 "CHR",
3378 "COALESCE",
3379 "COMPOSE",
3380 "CONCAT",
3381 "CONVERT",
3382 "CORR",
3383 "COS",
3384 "COSH",
3385 "COUNT",
3386 "COVAR_POP",
3387 "COVAR_SAMP",
3388 "CUME_DIST",
3389 "CURRENT_DATE",
3390 "CURRENT_TIMESTAMP",
3391 "DBTIMEZONE",
3392 "DECODE",
3393 "DECOMPOSE",
3394 "DENSE_RANK",
3395 "DUMP",
3396 "EMPTY_BLOB",
3397 "EMPTY_CLOB",
3398 "EXP",
3399 "EXTRACT",
3400 "FIRST_VALUE",
3401 "FLOOR",
3402 "FROM_TZ",
3403 "GREATEST",
3404 "GROUP_ID",
3405 "HEXTORAW",
3406 "INITCAP",
3407 "INSTR",
3408 "INSTR2",
3409 "INSTR4",
3410 "INSTRB",
3411 "INSTRC",
3412 "LAG",
3413 "LAST_DAY",
3414 "LAST_VALUE",
3415 "LEAD",
3416 "LEAST",
3417 "LENGTH",
3418 "LENGTH2",
3419 "LENGTH4",
3420 "LENGTHB",
3421 "LENGTHC",
3422 "LISTAGG",
3423 "LN",
3424 "LNNVL",
3425 "LOCALTIMESTAMP",
3426 "LOG",
3427 "LOWER",
3428 "LPAD",
3429 "LTRIM",
3430 "MAX",
3431 "MEDIAN",
3432 "MIN",
3433 "MOD",
3434 "MONTHS_BETWEEN",
3435 "NANVL",
3436 "NCHR",
3437 "NEW_TIME",
3438 "NEXT_DAY",
3439 "NTH_VALUE",
3440 "NULLIF",
3441 "NUMTODSINTERVAL",
3442 "NUMTOYMINTERVAL",
3443 "NVL",
3444 "NVL2",
3445 "POWER",
3446 "RANK",
3447 "RAWTOHEX",
3448 "REGEXP_COUNT",
3449 "REGEXP_INSTR",
3450 "REGEXP_REPLACE",
3451 "REGEXP_SUBSTR",
3452 "REMAINDER",
3453 "REPLACE",
3454 "ROUND",
3455 "ROWNUM",
3456 "RPAD",
3457 "RTRIM",
3458 "SESSIONTIMEZONE",
3459 "SIGN",
3460 "SIN",
3461 "SINH",
3462 "SOUNDEX",
3463 "SQRT",
3464 "STDDEV",
3465 "SUBSTR",
3466 "SUM",
3467 "SYS_CONTEXT",
3468 "SYSDATE",
3469 "SYSTIMESTAMP",
3470 "TAN",
3471 "TANH",
3472 "TO_CHAR",
3473 "TO_CLOB",
3474 "TO_DATE",
3475 "TO_DSINTERVAL",
3476 "TO_LOB",
3477 "TO_MULTI_BYTE",
3478 "TO_NCLOB",
3479 "TO_NUMBER",
3480 "TO_SINGLE_BYTE",
3481 "TO_TIMESTAMP",
3482 "TO_TIMESTAMP_TZ",
3483 "TO_YMINTERVAL",
3484 "TRANSLATE",
3485 "TRIM",
3486 "TRUNC",
3487 "TZ_OFFSET",
3488 "UID",
3489 "UPPER",
3490 "USER",
3491 "USERENV",
3492 "VAR_POP",
3493 "VAR_SAMP",
3494 "VARIANCE",
3495 "VSIZE "};
3496 for (auto& k : identifiers) {
3497 Identifier id;
3498 id.mDeclaration = "Built-in function";
3499 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
3500 }
3501
3502 langDef.mTokenRegexStrings.push_back(
3503 std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"",
3505 langDef.mTokenRegexStrings.push_back(
3506 std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'",
3508 langDef.mTokenRegexStrings.push_back(
3509 std::make_pair<std::string, PaletteIndex>(
3510 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
3512 langDef.mTokenRegexStrings.push_back(
3513 std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?",
3515 langDef.mTokenRegexStrings.push_back(
3516 std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?",
3518 langDef.mTokenRegexStrings.push_back(
3519 std::make_pair<std::string, PaletteIndex>(
3520 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
3521 langDef.mTokenRegexStrings.push_back(
3522 std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*",
3524 langDef.mTokenRegexStrings.push_back(
3525 std::make_pair<std::string, PaletteIndex>(
3526 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
3527 "\\;\\,\\.]",
3529
3530 langDef.mCommentStart = "/*";
3531 langDef.mCommentEnd = "*/";
3532 langDef.mSingleLineComment = "//";
3533
3534 langDef.mCaseSensitive = false;
3535 langDef.mAutoIndentation = false;
3536
3537 langDef.mName = "SQL";
3538
3539 inited = true;
3540 }
3541 return langDef;
3542}
3543
3546 static bool inited = false;
3547 static LanguageDefinition langDef;
3548 if (!inited) {
3549 static const char* const keywords[] = {
3550 "and", "abstract", "auto", "bool", "break",
3551 "case", "cast", "class", "const", "continue",
3552 "default", "do", "double", "else", "enum",
3553 "false", "final", "float", "for", "from",
3554 "funcdef", "function", "get", "if", "import",
3555 "in", "inout", "int", "interface", "int8",
3556 "int16", "int32", "int64", "is", "mixin",
3557 "namespace", "not", "null", "or", "out",
3558 "override", "private", "protected", "return", "set",
3559 "shared", "super", "switch", "this ", "true",
3560 "typedef", "uint", "uint8", "uint16", "uint32",
3561 "uint64", "void", "while", "xor"};
3562
3563 for (auto& k : keywords) langDef.mKeywords.insert(k);
3564
3565 static const char* const identifiers[] = {
3566 "cos", "sin", "tab", "acos", "asin",
3567 "atan", "atan2", "cosh", "sinh", "tanh",
3568 "log", "log10", "pow", "sqrt", "abs",
3569 "ceil", "floor", "fraction", "closeTo", "fpFromIEEE",
3570 "fpToIEEE", "complex", "opEquals", "opAddAssign", "opSubAssign",
3571 "opMulAssign", "opDivAssign", "opAdd", "opSub", "opMul",
3572 "opDiv"};
3573 for (auto& k : identifiers) {
3574 Identifier id;
3575 id.mDeclaration = "Built-in function";
3576 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
3577 }
3578
3579 langDef.mTokenRegexStrings.push_back(
3580 std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"",
3582 langDef.mTokenRegexStrings.push_back(
3583 std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'",
3585 langDef.mTokenRegexStrings.push_back(
3586 std::make_pair<std::string, PaletteIndex>(
3587 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
3589 langDef.mTokenRegexStrings.push_back(
3590 std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?",
3592 langDef.mTokenRegexStrings.push_back(
3593 std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?",
3595 langDef.mTokenRegexStrings.push_back(
3596 std::make_pair<std::string, PaletteIndex>(
3597 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
3598 langDef.mTokenRegexStrings.push_back(
3599 std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*",
3601 langDef.mTokenRegexStrings.push_back(
3602 std::make_pair<std::string, PaletteIndex>(
3603 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
3604 "\\;\\,\\.]",
3606
3607 langDef.mCommentStart = "/*";
3608 langDef.mCommentEnd = "*/";
3609 langDef.mSingleLineComment = "//";
3610
3611 langDef.mCaseSensitive = true;
3612 langDef.mAutoIndentation = true;
3613
3614 langDef.mName = "AngelScript";
3615
3616 inited = true;
3617 }
3618 return langDef;
3619}
3620
3622 static bool inited = false;
3623 static LanguageDefinition langDef;
3624 if (!inited) {
3625 static const char* const keywords[] = {
3626 "and", "break", "do", "", "else", "elseif", "end", "false",
3627 "for", "function", "if", "in", "", "local", "nil", "not",
3628 "or", "repeat", "return", "then", "true", "until", "while"};
3629
3630 for (auto& k : keywords) langDef.mKeywords.insert(k);
3631
3632 static const char* const identifiers[] = {"assert", "collectgarbage",
3633 "dofile", "error",
3634 "getmetatable", "ipairs",
3635 "loadfile", "load",
3636 "loadstring", "next",
3637 "pairs", "pcall",
3638 "print", "rawequal",
3639 "rawlen", "rawget",
3640 "rawset", "select",
3641 "setmetatable", "tonumber",
3642 "tostring", "type",
3643 "xpcall", "_G",
3644 "_VERSION", "arshift",
3645 "band", "bnot",
3646 "bor", "bxor",
3647 "btest", "extract",
3648 "lrotate", "lshift",
3649 "replace", "rrotate",
3650 "rshift", "create",
3651 "resume", "running",
3652 "status", "wrap",
3653 "yield", "isyieldable",
3654 "debug", "getuservalue",
3655 "gethook", "getinfo",
3656 "getlocal", "getregistry",
3657 "getmetatable", "getupvalue",
3658 "upvaluejoin", "upvalueid",
3659 "setuservalue", "sethook",
3660 "setlocal", "setmetatable",
3661 "setupvalue", "traceback",
3662 "close", "flush",
3663 "input", "lines",
3664 "open", "output",
3665 "popen", "read",
3666 "tmpfile", "type",
3667 "write", "close",
3668 "flush", "lines",
3669 "read", "seek",
3670 "setvbuf", "write",
3671 "__gc", "__tostring",
3672 "abs", "acos",
3673 "asin", "atan",
3674 "ceil", "cos",
3675 "deg", "exp",
3676 "tointeger", "floor",
3677 "fmod", "ult",
3678 "log", "max",
3679 "min", "modf",
3680 "rad", "random",
3681 "randomseed", "sin",
3682 "sqrt", "string",
3683 "tan", "type",
3684 "atan2", "cosh",
3685 "sinh", "tanh",
3686 "pow", "frexp",
3687 "ldexp", "log10",
3688 "pi", "huge",
3689 "maxinteger", "mininteger",
3690 "loadlib", "searchpath",
3691 "seeall", "preload",
3692 "cpath", "path",
3693 "searchers", "loaded",
3694 "module", "require",
3695 "clock", "date",
3696 "difftime", "execute",
3697 "exit", "getenv",
3698 "remove", "rename",
3699 "setlocale", "time",
3700 "tmpname", "byte",
3701 "char", "dump",
3702 "find", "format",
3703 "gmatch", "gsub",
3704 "len", "lower",
3705 "match", "rep",
3706 "reverse", "sub",
3707 "upper", "pack",
3708 "packsize", "unpack",
3709 "concat", "maxn",
3710 "insert", "pack",
3711 "unpack", "remove",
3712 "move", "sort",
3713 "offset", "codepoint",
3714 "char", "len",
3715 "codes", "charpattern",
3716 "coroutine", "table",
3717 "io", "os",
3718 "string", "utf8",
3719 "bit32", "math",
3720 "debug", "package"};
3721 for (auto& k : identifiers) {
3722 Identifier id;
3723 id.mDeclaration = "Built-in function";
3724 langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
3725 }
3726
3727 langDef.mTokenRegexStrings.push_back(
3728 std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"",
3730 langDef.mTokenRegexStrings.push_back(
3731 std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'",
3733 langDef.mTokenRegexStrings.push_back(
3734 std::make_pair<std::string, PaletteIndex>(
3735 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
3736 langDef.mTokenRegexStrings.push_back(
3737 std::make_pair<std::string, PaletteIndex>(
3738 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
3740 langDef.mTokenRegexStrings.push_back(
3741 std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?",
3743 langDef.mTokenRegexStrings.push_back(
3744 std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*",
3746 langDef.mTokenRegexStrings.push_back(
3747 std::make_pair<std::string, PaletteIndex>(
3748 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
3749 "\\;\\,\\.]",
3751
3752 langDef.mCommentStart = "--[[";
3753 langDef.mCommentEnd = "]]";
3754 langDef.mSingleLineComment = "--";
3755
3756 langDef.mCaseSensitive = true;
3757 langDef.mAutoIndentation = false;
3758
3759 langDef.mName = "Lua";
3760
3761 inited = true;
3762 }
3763 return langDef;
3764}
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)