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