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