| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | #pragma once |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
| 15 | #include <algorithm> |
| 16 | #include <array> |
| 17 | #include <chrono> |
| 18 | #include <functional> |
| 19 | #include <iterator> |
| 20 | #include <memory> |
| 21 | #include <string> |
| 22 | #include <string_view> |
| 23 | #include <unordered_map> |
| 24 | #include <unordered_set> |
| 25 | #include <vector> |
| 26 | |
| 27 | #include "imgui.h" |
| 28 | |
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| 34 | class IMGUI_API TextEditor { |
| 35 | public: |
| 36 | |
| 37 | TextEditor(); |
| 38 | |
| 39 | |
| 40 | |
| 41 | |
| 42 | |
| 43 | |
| 44 | |
| 45 | inline void SetTabSize(int value) { |
| 46 | |
| 47 | if (document.isEmpty() && transactions.empty()) { |
| 48 | document.setTabSize(std::max(1, std::min(8, value))); |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | inline int GetTabSize() const { return document.getTabSize(); } |
| 53 | inline void SetInsertSpacesOnTabs(bool value) { document.setInsertSpacesOnTabs(value); } |
| 54 | inline bool IsInsertSpacesOnTabs() const { return document.isInsertSpacesOnTabs(); } |
| 55 | inline void SetLineSpacing(float value) { lineSpacing = std::max(1.0f, std::min(2.0f, value)); } |
| 56 | inline float GetLineSpacing() const { return lineSpacing; } |
| 57 | inline void SetReadOnlyEnabled(bool value) { readOnly = value; } |
| 58 | inline bool IsReadOnlyEnabled() const { return readOnly; } |
| 59 | inline void SetAutoIndentEnabled(bool value) { autoIndent = value; } |
| 60 | inline bool IsAutoIndentEnabled() const { return autoIndent; } |
| 61 | inline void SetShowWhitespacesEnabled(bool value) { showSpaces = value; showTabs = value; } |
| 62 | inline bool IsShowWhitespacesEnabled() const { return showSpaces && showTabs; } |
| 63 | inline void SetShowSpacesEnabled(bool value) { showSpaces = value; } |
| 64 | inline bool IsShowSpacesEnabled() const { return showSpaces; } |
| 65 | inline void SetShowTabsEnabled(bool value) { showTabs = value; } |
| 66 | inline bool IsShowTabsEnabled() const { return showTabs; } |
| 67 | inline void SetShowLineNumbersEnabled(bool value) { showLineNumbers = value; } |
| 68 | inline bool IsShowLineNumbersEnabled() const { return showLineNumbers; } |
| 69 | inline void SetShowScrollbarMiniMapEnabled(bool value) { showScrollbarMiniMap = value; } |
| 70 | inline bool IsShowScrollbarMiniMapEnabled() const { return showScrollbarMiniMap; } |
| 71 | inline void SetShowPanScrollIndicatorEnabled(bool value) { showPanScrollIndicator = value; } |
| 72 | inline bool IsShowPanScrollIndicatorEnabled() const { return showPanScrollIndicator; } |
| 73 | inline void SetShowMatchingBrackets(bool value) { showMatchingBrackets = value; showMatchingBracketsChanged = true; } |
| 74 | inline bool IsShowingMatchingBrackets() const { return showMatchingBrackets; } |
| 75 | inline void SetCompletePairedGlyphs(bool value) { completePairedGlyphs = value; } |
| 76 | inline bool IsCompletingPairedGlyphs() const { return completePairedGlyphs; } |
| 77 | inline void SetOverwriteEnabled(bool value) { overwrite = value; } |
| 78 | inline bool IsOverwriteEnabled() const { return overwrite; } |
| 79 | inline void SetMiddleMousePanMode() { panMode = true; } |
| 80 | inline void SetMiddleMouseScrollMode() { panMode = false; } |
| 81 | inline bool IsMiddleMousePanMode() const { return panMode; } |
| 82 | |
| 83 | |
| 84 | |
| 85 | inline void SetText(const std::string_view& text) { setText(text); } |
| 86 | inline std::string GetText() const { return document.getText(); } |
| 87 | inline std::string GetCursorText(size_t cursor) const { return getCursorText(cursor); } |
| 88 | |
| 89 | inline std::string GetLineText(int line) const { |
| 90 | return (line < 0 || line > static_cast<int>(document.size())) ? "" : document.getLineText(line); |
| 91 | } |
| 92 | |
| 93 | inline std::string GetSectionText(int startLine, int startColumn, int endLine, int endColumn) const { |
| 94 | return document.getSectionText( |
| 95 | document.normalizeCoordinate(Coordinate(startLine, startColumn)), |
| 96 | document.normalizeCoordinate(Coordinate(endLine, endColumn))); |
| 97 | } |
| 98 | |
| 99 | inline void ReplaceSectionText(int startLine, int startColumn, int endLine, int endColumn, const std::string_view& text) { |
| 100 | return replaceSectionText( |
| 101 | document.normalizeCoordinate(Coordinate(startLine, startColumn)), |
| 102 | document.normalizeCoordinate(Coordinate(endLine, endColumn)), |
| 103 | text |
| 104 | ); |
| 105 | } |
| 106 | |
| 107 | inline void ClearText() { SetText(""); } |
| 108 | |
| 109 | inline bool IsEmpty() const { return document.isEmpty(); } |
| 110 | inline int GetLineCount() const { return document.lineCount(); } |
| 111 | |
| 112 | |
| 113 | inline void Render(const char* title, const ImVec2& size=ImVec2(), bool border=false) { render(title, size, border); } |
| 114 | |
| 115 | |
| 116 | inline void SetFocus() { focusOnEditor = true; } |
| 117 | |
| 118 | |
| 119 | inline void Cut() { if (!readOnly) cut(); } |
| 120 | inline void Copy() const { copy(); } |
| 121 | inline void Paste() { if (!readOnly) paste(); } |
| 122 | inline void Undo() { if (!readOnly) undo(); } |
| 123 | inline void Redo() { if (!readOnly) redo(); } |
| 124 | inline bool CanUndo() const { return !readOnly && transactions.canUndo(); }; |
| 125 | inline bool CanRedo() const { return !readOnly && transactions.canRedo(); }; |
| 126 | inline size_t GetUndoIndex() const { return transactions.getUndoIndex(); }; |
| 127 | |
| 128 | |
| 129 | inline void SetCursor(int line, int column) { moveTo(document.normalizeCoordinate(Coordinate(line, column)), false); } |
| 130 | inline void SelectAll() { selectAll(); } |
| 131 | inline void SelectLine(int line) { if (line >= 0 && line < document.lineCount()) selectLine(line); } |
| 132 | inline void SelectLines(int start, int end) { if (start >= 0 && end < document.lineCount() && start <= end) selectLines(start, end); } |
| 133 | inline void SelectRegion(int startLine, int startColumn, int endLine, int endColumn) { selectRegion(startLine, startColumn, endLine, endColumn); } |
| 134 | inline void SelectToBrackets(bool includeBrackets=true) { selectToBrackets(includeBrackets); } |
| 135 | inline void GrowSelectionsToCurlyBrackets() { growSelectionsToCurlyBrackets(); } |
| 136 | inline void ShrinkSelectionsToCurlyBrackets() { shrinkSelectionsToCurlyBrackets(); } |
| 137 | inline void AddNextOccurrence() { addNextOccurrence(); } |
| 138 | inline void SelectAllOccurrences() { selectAllOccurrences(); } |
| 139 | inline bool AnyCursorHasSelection() const { return cursors.anyHasSelection(); } |
| 140 | inline bool AllCursorsHaveSelection() const { return cursors.allHaveSelection(); } |
| 141 | inline bool CurrentCursorHasSelection() const { return cursors.currentCursorHasSelection(); } |
| 142 | inline void ClearCursors() { cursors.clearAll(); } |
| 143 | |
| 144 | |
| 145 | inline size_t GetNumberOfCursors() const { return cursors.size(); } |
| 146 | inline void GetCursor(int& line, int& column, size_t cursor) const { return getCursor(line, column, cursor); } |
| 147 | inline void GetCursor(int& startLine, int& startColumn, int& endLine, int& endColumn, size_t cursor) const { return getCursor(startLine, startColumn, endLine, endColumn, cursor); } |
| 148 | inline void GetMainCursor(int& line, int& column) const { return getCursor(line, column, cursors.getMainIndex()); } |
| 149 | inline void GetCurrentCursor(int& line, int& column) const { return getCursor(line, column, cursors.getCurrentIndex()); } |
| 150 | |
| 151 | |
| 152 | struct CursorPosition { |
| 153 | CursorPosition() = default; |
| 154 | CursorPosition(int l, int c) : line(l), column(c) {} |
| 155 | int line = 0; |
| 156 | int column = 0; |
| 157 | }; |
| 158 | |
| 159 | struct CursorSelection { |
| 160 | CursorSelection() = default; |
| 161 | CursorSelection(CursorPosition s, CursorPosition e) : start(s), end(e) {} |
| 162 | CursorPosition start; |
| 163 | CursorPosition end; |
| 164 | }; |
| 165 | |
| 166 | inline CursorPosition GetMainCursorPosition() const { CursorPosition p; getCursor(p.line, p.column, cursors.getMainIndex()); return p; } |
| 167 | inline CursorPosition GetCurrentCursorPosition() const { CursorPosition p; getCursor(p.line, p.column, cursors.getCurrentIndex()); return p; } |
| 168 | inline CursorPosition GetCursorPosition(size_t cursor) const { CursorPosition p; getCursor(p.line, p.column, cursor); return p; } |
| 169 | inline CursorSelection GetCursorSelection(size_t cursor) const { CursorSelection s; getCursor(s.start.line, s.start.column, s.end.line, s.end.column, cursor); return s; } |
| 170 | inline CursorSelection GetMainCursorSelection() const { return GetCursorSelection(cursors.getMainIndex()); } |
| 171 | |
| 172 | |
| 173 | std::string GetWordAtScreenPos(const ImVec2& screenPos) const; |
| 174 | |
| 175 | |
| 176 | enum class Scroll { |
| 177 | alignTop, |
| 178 | alignMiddle, |
| 179 | alignBottom |
| 180 | }; |
| 181 | |
| 182 | inline void ScrollToLine(int line, Scroll alignment) { scrollToLine(line, alignment); } |
| 183 | inline int GetFirstVisibleLine() const { return firstVisibleLine; } |
| 184 | inline int GetLastVisibleLine() const { return lastVisibleLine; } |
| 185 | inline int GetFirstVisibleColumn() const { return firstVisibleColumn; } |
| 186 | inline int GetLastVisibleColumn() const { return lastVisibleColumn; } |
| 187 | |
| 188 | inline float GetLineHeight() const { return glyphSize.y; } |
| 189 | inline float GetGlyphWidth() const { return glyphSize.x; } |
| 190 | |
| 191 | |
| 192 | |
| 193 | |
| 194 | |
| 195 | |
| 196 | |
| 197 | |
| 198 | |
| 199 | |
| 200 | |
| 201 | |
| 202 | |
| 203 | |
| 204 | |
| 205 | |
| 206 | |
| 207 | |
| 208 | inline void SelectFirstOccurrenceOf(const std::string_view& text, bool caseSensitive=true, bool wholeWord=false) { selectFirstOccurrenceOf(text, caseSensitive, wholeWord); } |
| 209 | inline void SelectNextOccurrenceOf(const std::string_view& text, bool caseSensitive=true, bool wholeWord=false) { selectNextOccurrenceOf(text, caseSensitive, wholeWord); } |
| 210 | inline void SelectAllOccurrencesOf(const std::string_view& text, bool caseSensitive=true, bool wholeWord=false) { selectAllOccurrencesOf(text, caseSensitive, wholeWord); } |
| 211 | inline void ReplaceTextInCurrentCursor(const std::string_view& text) { if (!readOnly) replaceTextInCurrentCursor(text); } |
| 212 | inline void ReplaceTextInAllCursors(const std::string_view& text) { if (!readOnly) replaceTextInAllCursors(text); } |
| 213 | |
| 214 | inline void OpenFindReplaceWindow() { openFindReplace(); } |
| 215 | inline void CloseFindReplaceWindow() { closeFindReplace(); } |
| 216 | inline void SetFindButtonLabel(const std::string_view& label) { findButtonLabel = label; } |
| 217 | inline void SetFindAllButtonLabel(const std::string_view& label) { findAllButtonLabel = label; } |
| 218 | inline void SetReplaceButtonLabel(const std::string_view& label) { replaceButtonLabel = label; } |
| 219 | inline void SetReplaceAllButtonLabel(const std::string_view& label) { replaceAllButtonLabel = label; } |
| 220 | inline bool HasFindString() const { return findText.size(); } |
| 221 | inline void FindNext() { findNext(); } |
| 222 | inline void FindAll() { findAll(); } |
| 223 | |
| 224 | |
| 225 | inline void AddMarker(int line, ImU32 lineNumberColor, ImU32 textColor, const std::string_view& lineNumberTooltip, const std::string_view& textTooltip) { addMarker(line, lineNumberColor, textColor, lineNumberTooltip, textTooltip); } |
| 226 | inline void ClearMarkers() { clearMarkers(); } |
| 227 | inline bool HasMarkers() const { return markers.size() != 0; } |
| 228 | |
| 229 | |
| 230 | |
| 231 | |
| 232 | |
| 233 | inline void SetChangeCallback(std::function<void()> callback, int delay=0) { |
| 234 | delayedChangeCallback = callback; |
| 235 | delayedChangeDelay = std::chrono::milliseconds{delay}; |
| 236 | } |
| 237 | |
| 238 | |
| 239 | |
| 240 | |
| 241 | |
| 242 | |
| 243 | |
| 244 | |
| 245 | |
| 246 | struct Change { |
| 247 | bool insert; |
| 248 | int startLine; |
| 249 | int startColumn; |
| 250 | int startIndex; |
| 251 | int endLine; |
| 252 | int endColumn; |
| 253 | int endIndex; |
| 254 | std::string text; |
| 255 | }; |
| 256 | |
| 257 | |
| 258 | |
| 259 | |
| 260 | |
| 261 | inline void SetTransactionCallback(std::function<void(std::vector<Change>&)> callback) { transactionCallback = callback; } |
| 262 | |
| 263 | |
| 264 | |
| 265 | |
| 266 | |
| 267 | inline void SetInsertor(std::function<void*(int line)> callback) { document.setInsertor(callback); } |
| 268 | inline void SetDeletor(std::function<void(int line, void* data)> callback) { document.setDeletor(callback); } |
| 269 | |
| 270 | |
| 271 | |
| 272 | |
| 273 | |
| 274 | |
| 275 | |
| 276 | |
| 277 | inline void SetUserData(int line, void* data) { document.setUserData(line, data); } |
| 278 | inline void* GetUserData(int line) const { return document.getUserData(line); } |
| 279 | inline void IterateUserData(std::function<void(int line, void* data)> callback) const { document.iterateUserData(callback); } |
| 280 | |
| 281 | |
| 282 | struct Decorator { |
| 283 | int line; |
| 284 | float width; |
| 285 | float height; |
| 286 | ImVec2 glyphSize; |
| 287 | void* userData; |
| 288 | }; |
| 289 | |
| 290 | |
| 291 | inline void SetLineDecorator(float width, std::function<void(Decorator& decorator)> callback) { |
| 292 | decoratorWidth = width; |
| 293 | decoratorCallback = callback; |
| 294 | } |
| 295 | |
| 296 | inline void ClearLineDecorator() { SetLineDecorator(0.0f, nullptr); } |
| 297 | inline bool HasLineDecorator() const { return decoratorWidth != 0.0f && decoratorCallback != nullptr; } |
| 298 | |
| 299 | |
| 300 | |
| 301 | inline void SetLineNumberContextMenuCallback(std::function<void(int line)> callback) { lineNumberContextMenuCallback = callback; } |
| 302 | inline void ClearLineNumberContextMenuCallback() { SetLineNumberContextMenuCallback(nullptr); } |
| 303 | inline bool HasLineNumberContextMenuCallback() const { return lineNumberContextMenuCallback != nullptr; } |
| 304 | |
| 305 | inline void SetTextContextMenuCallback(std::function<void(int line, int column)> callback) { textContextMenuCallback = callback; } |
| 306 | inline void ClearTextContextMenuCallback() { SetTextContextMenuCallback(nullptr); } |
| 307 | inline bool HasTextContextMenuCallback() const { return textContextMenuCallback != nullptr; } |
| 308 | |
| 309 | |
| 310 | |
| 311 | inline void IndentLines() { if (!readOnly) indentLines(); } |
| 312 | inline void DeindentLines() { if (!readOnly) deindentLines(); } |
| 313 | inline void MoveUpLines() { if (!readOnly) moveUpLines(); } |
| 314 | inline void MoveDownLines() { if (!readOnly) moveDownLines(); } |
| 315 | inline void ToggleComments() { if (!readOnly && language) toggleComments(); } |
| 316 | inline void FilterSelections(std::function<std::string(std::string_view)> filter) { if (!readOnly) filterSelections(filter); } |
| 317 | inline void SelectionToLowerCase() { if (!readOnly) selectionToLowerCase(); } |
| 318 | inline void SelectionToUpperCase() { if (!readOnly) selectionToUpperCase(); } |
| 319 | |
| 320 | |
| 321 | inline void StripTrailingWhitespaces() { if (!readOnly) stripTrailingWhitespaces(); } |
| 322 | inline void FilterLines(std::function<std::string(std::string_view)> filter) { if (!readOnly) filterLines(filter); } |
| 323 | inline void TabsToSpaces() { if (!readOnly) tabsToSpaces(); } |
| 324 | inline void SpacesToTabs() { if (!readOnly) spacesToTabs(); } |
| 325 | |
| 326 | |
| 327 | enum class Color : char { |
| 328 | text, |
| 329 | keyword, |
| 330 | declaration, |
| 331 | number, |
| 332 | string, |
| 333 | punctuation, |
| 334 | preprocessor, |
| 335 | identifier, |
| 336 | knownIdentifier, |
| 337 | comment, |
| 338 | background, |
| 339 | cursor, |
| 340 | selection, |
| 341 | whitespace, |
| 342 | matchingBracketBackground, |
| 343 | matchingBracketActive, |
| 344 | matchingBracketLevel1, |
| 345 | matchingBracketLevel2, |
| 346 | matchingBracketLevel3, |
| 347 | matchingBracketError, |
| 348 | lineNumber, |
| 349 | currentLineNumber, |
| 350 | count |
| 351 | }; |
| 352 | |
| 353 | class Palette : public std::array<ImU32, static_cast<size_t>(Color::count)> { |
| 354 | public: |
| 355 | inline ImU32 get(Color color) const { return at(static_cast<size_t>(color)); } |
| 356 | }; |
| 357 | |
| 358 | inline void SetPalette(const Palette& newPalette) { paletteBase = newPalette; paletteAlpha = -1.0f; } |
| 359 | inline const Palette& GetPalette() const { return paletteBase; } |
| 360 | static inline void SetDefaultPalette(const Palette& aValue) { defaultPalette = aValue; } |
| 361 | static inline Palette& GetDefaultPalette() { return defaultPalette; } |
| 362 | |
| 363 | static const Palette& GetDarkPalette(); |
| 364 | static const Palette& GetLightPalette(); |
| 365 | |
| 366 | |
| 367 | class Glyph { |
| 368 | public: |
| 369 | |
| 370 | Glyph() = default; |
| 371 | Glyph(ImWchar cp) : codepoint(cp) {} |
| 372 | Glyph(ImWchar cp, Color col) : codepoint(cp), color(col) {} |
| 373 | |
| 374 | |
| 375 | ImWchar codepoint = 0; |
| 376 | Color color = Color::text; |
| 377 | }; |
| 378 | |
| 379 | |
| 380 | |
| 381 | class Iterator { |
| 382 | public: |
| 383 | |
| 384 | Iterator() = default; |
| 385 | Iterator(Glyph* g) : glyph(g) {} |
| 386 | |
| 387 | using iterator_category = std::forward_iterator_tag; |
| 388 | using difference_type = std::ptrdiff_t; |
| 389 | using value_type = ImWchar; |
| 390 | using pointer = ImWchar*; |
| 391 | using reference = ImWchar&; |
| 392 | |
| 393 | inline reference operator*() const { return glyph->codepoint; } |
| 394 | inline pointer operator->() const { return &(glyph->codepoint); } |
| 395 | inline Iterator& operator++() { glyph++; return *this; } |
| 396 | inline Iterator operator++(int) { Iterator tmp = *this; glyph++; return tmp; } |
| 397 | inline size_t operator-(const Iterator& a) { return glyph - a.glyph; } |
| 398 | inline friend bool operator==(const Iterator& a, const Iterator& b) { return a.glyph == b.glyph; }; |
| 399 | inline friend bool operator!=(const Iterator& a, const Iterator& b) { return !(a.glyph == b.glyph); }; |
| 400 | inline friend bool operator<(const Iterator& a, const Iterator& b) { return a.glyph < b.glyph; }; |
| 401 | inline friend bool operator<=(const Iterator& a, const Iterator& b) { return a.glyph <= b.glyph; }; |
| 402 | inline friend bool operator>(const Iterator& a, const Iterator& b) { return a.glyph > b.glyph; }; |
| 403 | inline friend bool operator>=(const Iterator& a, const Iterator& b) { return a.glyph >= b.glyph; }; |
| 404 | |
| 405 | private: |
| 406 | |
| 407 | Glyph* glyph; |
| 408 | }; |
| 409 | |
| 410 | |
| 411 | class Language { |
| 412 | public: |
| 413 | |
| 414 | std::string name; |
| 415 | |
| 416 | |
| 417 | bool caseSensitive = true; |
| 418 | |
| 419 | |
| 420 | ImWchar preprocess = 0; |
| 421 | |
| 422 | |
| 423 | std::string singleLineComment; |
| 424 | |
| 425 | |
| 426 | std::string singleLineCommentAlt; |
| 427 | |
| 428 | |
| 429 | std::string commentStart; |
| 430 | std::string commentEnd; |
| 431 | |
| 432 | |
| 433 | bool hasSingleQuotedStrings = false; |
| 434 | bool hasDoubleQuotedStrings = false; |
| 435 | |
| 436 | |
| 437 | std::string otherStringStart; |
| 438 | std::string otherStringEnd; |
| 439 | |
| 440 | |
| 441 | std::string otherStringAltStart; |
| 442 | std::string otherStringAltEnd; |
| 443 | |
| 444 | |
| 445 | ImWchar stringEscape = 0; |
| 446 | |
| 447 | |
| 448 | |
| 449 | std::unordered_set<std::string> keywords; |
| 450 | std::unordered_set<std::string> declarations; |
| 451 | std::unordered_set<std::string> identifiers; |
| 452 | |
| 453 | |
| 454 | std::function<bool(ImWchar)> isPunctuation; |
| 455 | |
| 456 | |
| 457 | |
| 458 | |
| 459 | |
| 460 | std::function<Iterator(Iterator start, Iterator end)> getIdentifier; |
| 461 | std::function<Iterator(Iterator start, Iterator end)> getNumber; |
| 462 | |
| 463 | |
| 464 | |
| 465 | std::function<Iterator(Iterator start, Iterator end, Color& color)> customTokenizer; |
| 466 | |
| 467 | |
| 468 | static const Language* C(); |
| 469 | static const Language* Cpp(); |
| 470 | static const Language* Cs(); |
| 471 | static const Language* AngelScript(); |
| 472 | static const Language* Lua(); |
| 473 | static const Language* Python(); |
| 474 | static const Language* Glsl(); |
| 475 | static const Language* Hlsl(); |
| 476 | static const Language* Json(); |
| 477 | static const Language* Markdown(); |
| 478 | static const Language* Sql(); |
| 479 | }; |
| 480 | |
| 481 | inline void SetLanguage(const Language* l) { language = l; languageChanged = true; } |
| 482 | inline const Language* GetLanguage() const { return language; }; |
| 483 | inline bool HasLanguage() const { return language != nullptr; } |
| 484 | inline std::string GetLanguageName() const { return language == nullptr ? "None" : language->name; } |
| 485 | |
| 486 | |
| 487 | inline void IterateIdentifiers(std::function<void(const std::string& identifier)> callback) const { document.iterateIdentifiers(callback); } |
| 488 | |
| 489 | |
| 490 | class AutoCompleteState { |
| 491 | public: |
| 492 | |
| 493 | |
| 494 | std::string searchTerm; |
| 495 | size_t line; |
| 496 | size_t searchTermStartColumn; |
| 497 | size_t searchTermStartIndex; |
| 498 | size_t searchTermEndColumn; |
| 499 | size_t searchTermEndIndex; |
| 500 | |
| 501 | bool inIdentifier; |
| 502 | bool inNumber; |
| 503 | bool inComment; |
| 504 | bool inString; |
| 505 | |
| 506 | |
| 507 | const Language* language; |
| 508 | |
| 509 | |
| 510 | void* userData; |
| 511 | |
| 512 | |
| 513 | |
| 514 | |
| 515 | |
| 516 | |
| 517 | std::vector<std::string> suggestions; |
| 518 | |
| 519 | |
| 520 | |
| 521 | bool suggestionsPromise = false; |
| 522 | }; |
| 523 | |
| 524 | |
| 525 | class AutoCompleteConfig { |
| 526 | public: |
| 527 | |
| 528 | bool triggerOnTyping = true; |
| 529 | |
| 530 | |
| 531 | bool triggerOnShortcut = true; |
| 532 | |
| 533 | |
| 534 | bool triggerInComments = false; |
| 535 | bool triggerInStrings = false; |
| 536 | |
| 537 | |
| 538 | |
| 539 | #if __APPLE__ |
| 540 | ImGuiKeyChord triggerShortcut = ImGuiMod_Super | ImGuiKey_Space; |
| 541 | #else |
| 542 | ImGuiKeyChord triggerShortcut = ImGuiMod_Ctrl | ImGuiKey_Space; |
| 543 | #endif |
| 544 | |
| 545 | |
| 546 | |
| 547 | bool autoInsertSingleSuggestions = false; |
| 548 | |
| 549 | |
| 550 | std::chrono::milliseconds triggerDelay{200}; |
| 551 | |
| 552 | |
| 553 | std::string noSuggestionsLabel = "No suggestions"; |
| 554 | |
| 555 | |
| 556 | |
| 557 | |
| 558 | |
| 559 | |
| 560 | |
| 561 | |
| 562 | |
| 563 | std::function<void(AutoCompleteState&)> callback; |
| 564 | |
| 565 | |
| 566 | void* userData = nullptr; |
| 567 | }; |
| 568 | |
| 569 | |
| 570 | inline void SetAutoCompleteConfig(const AutoCompleteConfig* config) { autocomplete.setConfig(config); } |
| 571 | |
| 572 | |
| 573 | |
| 574 | inline void SetAutoCompleteSuggestions(const std::vector<std::string>& suggestions) { autocomplete.setSuggestions(suggestions); } |
| 575 | |
| 576 | |
| 577 | |
| 578 | class Trie { |
| 579 | public: |
| 580 | |
| 581 | Trie() { clear(); } |
| 582 | |
| 583 | |
| 584 | inline void clear() { root = std::make_unique<Node>(); } |
| 585 | |
| 586 | |
| 587 | void insert(const std::string_view& word); |
| 588 | |
| 589 | |
| 590 | |
| 591 | |
| 592 | |
| 593 | void findSuggestions(std::vector<std::string>& suggestions, const std::string_view& searchTerm, size_t limit=20, size_t maxSkippedLetters=2); |
| 594 | |
| 595 | private: |
| 596 | |
| 597 | struct Node { |
| 598 | std::unordered_map<ImWchar, std::unique_ptr<Node>> children; |
| 599 | std::string word; |
| 600 | }; |
| 601 | |
| 602 | |
| 603 | std::unique_ptr<Node> root; |
| 604 | |
| 605 | |
| 606 | size_t maxSkip; |
| 607 | |
| 608 | |
| 609 | std::vector<ImWchar> searchCodepoints; |
| 610 | |
| 611 | |
| 612 | struct Candidate { |
| 613 | Candidate(const Node* n, size_t c) : node(n), cost(c) {} |
| 614 | bool operator<(const Candidate& rhs) const { return cost < rhs.cost; } |
| 615 | bool operator==(const Candidate& rhs) const { return node == rhs.node; } |
| 616 | const Node* node; |
| 617 | size_t cost; |
| 618 | }; |
| 619 | |
| 620 | std::vector<Candidate> candidates; |
| 621 | |
| 622 | |
| 623 | void evaluateNode(const Node* node, size_t index, size_t cost, size_t skip); |
| 624 | void addCandidates(const Node* node, size_t cost); |
| 625 | }; |
| 626 | |
| 627 | |
| 628 | class CodePoint { |
| 629 | public: |
| 630 | static std::string_view::const_iterator skipBOM(std::string_view::const_iterator i, std::string_view::const_iterator end); |
| 631 | static std::string_view::const_iterator read(std::string_view::const_iterator i, std::string_view::const_iterator end, ImWchar* codepoint); |
| 632 | static size_t write(char* i, ImWchar codepoint); |
| 633 | static bool isLetter(ImWchar codepoint); |
| 634 | static bool isNumber(ImWchar codepoint); |
| 635 | static bool isWord(ImWchar codepoint); |
| 636 | static bool isWhiteSpace(ImWchar codepoint); |
| 637 | static bool isXidStart(ImWchar codepoint); |
| 638 | static bool isXidContinue(ImWchar codepoint); |
| 639 | static bool isLower(ImWchar codepoint); |
| 640 | static bool isUpper(ImWchar codepoint); |
| 641 | static ImWchar toUpper(ImWchar codepoint); |
| 642 | static ImWchar toLower(ImWchar codepoint); |
| 643 | |
| 644 | static constexpr ImWchar singleQuote = '\''; |
| 645 | static constexpr ImWchar doubleQuote = '"'; |
| 646 | static constexpr ImWchar openCurlyBracket = '{'; |
| 647 | static constexpr ImWchar closeCurlyBracket = '}'; |
| 648 | static constexpr ImWchar openSquareBracket = '['; |
| 649 | static constexpr ImWchar closeSquareBracket = ']'; |
| 650 | static constexpr ImWchar openParenthesis = '('; |
| 651 | static constexpr ImWchar closeParenthesis = ')'; |
| 652 | |
| 653 | static inline bool isPairOpener(ImWchar ch) { |
| 654 | return |
| 655 | ch == openCurlyBracket || |
| 656 | ch == openSquareBracket || |
| 657 | ch == openParenthesis || |
| 658 | ch == singleQuote || |
| 659 | ch == doubleQuote; |
| 660 | } |
| 661 | |
| 662 | static inline bool isPairCloser(ImWchar ch) { |
| 663 | return |
| 664 | ch == closeCurlyBracket || |
| 665 | ch == closeSquareBracket || |
| 666 | ch == closeParenthesis || |
| 667 | ch == singleQuote || |
| 668 | ch == doubleQuote; |
| 669 | } |
| 670 | |
| 671 | static inline ImWchar toPairCloser(ImWchar ch) { |
| 672 | return |
| 673 | (ch == openCurlyBracket) ? closeCurlyBracket : |
| 674 | (ch == openSquareBracket) ? closeSquareBracket : |
| 675 | (ch == openParenthesis) ? closeParenthesis: |
| 676 | ch; |
| 677 | } |
| 678 | |
| 679 | static inline ImWchar toPairOpener(ImWchar ch) { |
| 680 | return |
| 681 | (ch == closeCurlyBracket) ? openCurlyBracket : |
| 682 | (ch == closeSquareBracket) ? openSquareBracket : |
| 683 | (ch == closeParenthesis) ? openParenthesis: |
| 684 | ch; |
| 685 | } |
| 686 | |
| 687 | static inline bool isMatchingPair(ImWchar open, ImWchar close) { |
| 688 | return isPairOpener(open) && close == toPairCloser(open); |
| 689 | } |
| 690 | |
| 691 | static inline bool isBracketOpener(ImWchar ch) { |
| 692 | return |
| 693 | ch == openCurlyBracket || |
| 694 | ch == openSquareBracket || |
| 695 | ch == openParenthesis; |
| 696 | } |
| 697 | |
| 698 | static inline bool isBracketCloser(ImWchar ch) { |
| 699 | return |
| 700 | ch == closeCurlyBracket || |
| 701 | ch == closeSquareBracket || |
| 702 | ch == closeParenthesis; |
| 703 | } |
| 704 | |
| 705 | static inline bool isMatchingBrackets(ImWchar open, ImWchar close) { |
| 706 | return isBracketOpener(open) && close == toPairCloser(open); |
| 707 | } |
| 708 | }; |
| 709 | |
| 710 | |
| 711 | |
| 712 | |
| 713 | |
| 714 | static inline void SetImGuiContext(ImGuiContext* ctx) { ImGui::SetCurrentContext(ctx); } |
| 715 | |
| 716 | protected: |
| 717 | |
| 718 | |
| 719 | |
| 720 | |
| 721 | |
| 722 | |
| 723 | class Coordinate { |
| 724 | |
| 725 | |
| 726 | |
| 727 | |
| 728 | |
| 729 | |
| 730 | |
| 731 | |
| 732 | |
| 733 | public: |
| 734 | Coordinate() = default; |
| 735 | Coordinate(int l, int c) : line(l), column(c) {} |
| 736 | |
| 737 | inline bool operator ==(const Coordinate& o) const { return line == o.line && column == o.column; } |
| 738 | inline bool operator !=(const Coordinate& o) const { return line != o.line || column != o.column; } |
| 739 | inline bool operator <(const Coordinate& o) const { return line != o.line ? line < o.line : column < o.column; } |
| 740 | inline bool operator >(const Coordinate& o) const { return line != o.line ? line > o.line : column > o.column; } |
| 741 | inline bool operator <=(const Coordinate& o) const { return line != o.line ? line < o.line : column <= o.column; } |
| 742 | inline bool operator >=(const Coordinate& o) const { return line != o.line ? line > o.line : column >= o.column; } |
| 743 | |
| 744 | inline Coordinate operator -(const Coordinate& o) const { return Coordinate(line - o.line, column - o.column); } |
| 745 | inline Coordinate operator +(const Coordinate& o) const { return Coordinate(line + o.line, column + o.column); } |
| 746 | |
| 747 | static inline Coordinate invalid() { static Coordinate invalid(-1, -1); return invalid; } |
| 748 | inline bool isValid() const { return line >= 0 && column >= 0; } |
| 749 | |
| 750 | int line = 0; |
| 751 | int column = 0; |
| 752 | }; |
| 753 | |
| 754 | |
| 755 | class Cursor { |
| 756 | public: |
| 757 | |
| 758 | Cursor() = default; |
| 759 | Cursor(Coordinate coordinate) : start(coordinate), end(coordinate) {} |
| 760 | Cursor(Coordinate s, Coordinate e) : start(s), end(e) {} |
| 761 | |
| 762 | |
| 763 | inline void update(Coordinate coordinate) { end = coordinate; updated = true; } |
| 764 | inline void update(Coordinate s, Coordinate e) { start = s; end = e; updated = true; } |
| 765 | inline void update(Coordinate coordinate, bool keep) { if (keep) update(coordinate); else update(coordinate, coordinate); updated = true; } |
| 766 | |
| 767 | |
| 768 | |
| 769 | void adjustForInsert(Coordinate insertStart, Coordinate insertEnd); |
| 770 | void adjustForDelete(Coordinate deleteStart, Coordinate deleteEnd); |
| 771 | |
| 772 | |
| 773 | inline Coordinate getInteractiveStart() const { return start; } |
| 774 | inline Coordinate getInteractiveEnd() const { return end; } |
| 775 | inline Coordinate getSelectionStart() const { return start < end ? start : end; } |
| 776 | inline Coordinate getSelectionEnd() const { return start > end ? start : end; } |
| 777 | inline bool hasSelection() const { return start != end; } |
| 778 | |
| 779 | inline void resetToStart() { update(getSelectionStart(), getSelectionStart()); } |
| 780 | inline void resetToEnd() { update(getSelectionEnd(), getSelectionEnd()); } |
| 781 | |
| 782 | inline void setMain(bool value) { main = value; } |
| 783 | inline bool isMain() const { return main; } |
| 784 | |
| 785 | inline void setCurrent(bool value) { current = value; } |
| 786 | inline bool isCurrent() const { return current; } |
| 787 | |
| 788 | inline void setUpdated(bool value) { updated = value; } |
| 789 | inline bool isUpdated() const { return updated; } |
| 790 | |
| 791 | private: |
| 792 | |
| 793 | Coordinate adjustCoordinateForInsert(Coordinate coordinate, Coordinate insertStart, Coordinate insertEnd); |
| 794 | Coordinate adjustCoordinateForDelete(Coordinate coordinate, Coordinate deleteStart, Coordinate deleteEnd); |
| 795 | |
| 796 | |
| 797 | Coordinate start{0, 0}; |
| 798 | Coordinate end{0, 0}; |
| 799 | bool main = false; |
| 800 | bool current = true; |
| 801 | bool updated = true; |
| 802 | }; |
| 803 | |
| 804 | |
| 805 | class Cursors : public std::vector<Cursor> { |
| 806 | public: |
| 807 | |
| 808 | Cursors() { clearAll(); } |
| 809 | |
| 810 | |
| 811 | void reset(); |
| 812 | |
| 813 | |
| 814 | inline void setCursor(Coordinate coordinate) { setCursor(coordinate, coordinate); } |
| 815 | void setCursor(Coordinate start, Coordinate end); |
| 816 | |
| 817 | |
| 818 | inline void addCursor(Coordinate c) { addCursor(c, c); } |
| 819 | void addCursor(Coordinate cursorStart, Coordinate cursorEnd); |
| 820 | |
| 821 | |
| 822 | inline void updateCurrentCursor(Coordinate coordinate) { at(current).update(coordinate); } |
| 823 | inline void updateCurrentCursor(Coordinate start, Coordinate end) { at(current).update(start, end); } |
| 824 | inline void updateCurrentCursor(Coordinate coordinate, bool keep) { at(current).update(coordinate, keep); } |
| 825 | |
| 826 | |
| 827 | inline bool hasMultiple() const { return size() > 1; } |
| 828 | bool anyHasSelection() const; |
| 829 | bool allHaveSelection() const; |
| 830 | inline bool mainCursorHasSelection() const { return at(main).hasSelection(); } |
| 831 | inline bool currentCursorHasSelection() const { return at(current).hasSelection(); } |
| 832 | inline bool mainHasUpdate() const { return at(main).isUpdated(); } |
| 833 | bool anyHasUpdate() const; |
| 834 | |
| 835 | |
| 836 | void clearAll(); |
| 837 | |
| 838 | |
| 839 | void clearAdditional(bool reset=false); |
| 840 | |
| 841 | |
| 842 | void clearUpdated(); |
| 843 | |
| 844 | |
| 845 | inline Cursor& getMain() { return at(main); } |
| 846 | inline size_t getMainIndex() const { return main; } |
| 847 | inline Cursor& getCurrent() { return at(current); } |
| 848 | inline size_t getCurrentIndex() const { return current; } |
| 849 | inline iterator getMainAsIterator() { return begin() + main; } |
| 850 | inline iterator getCurrentAsIterator() { return begin() + current; } |
| 851 | |
| 852 | |
| 853 | void update(); |
| 854 | |
| 855 | |
| 856 | |
| 857 | void adjustForInsert(iterator start, Coordinate insertStart, Coordinate insertEnd); |
| 858 | void adjustForDelete(iterator start, Coordinate deleteStart, Coordinate deleteEnd); |
| 859 | |
| 860 | private: |
| 861 | size_t main = 0; |
| 862 | size_t current = 0; |
| 863 | } cursors; |
| 864 | |
| 865 | |
| 866 | class Marker { |
| 867 | public: |
| 868 | Marker(ImU32 lc, ImU32 tc, const std::string_view& lt, const std::string_view& tt) : |
| 869 | lineNumberColor(lc), textColor(tc), lineNumberTooltip(lt), textTooltip(tt) {} |
| 870 | |
| 871 | ImU32 lineNumberColor; |
| 872 | ImU32 textColor; |
| 873 | std::string lineNumberTooltip; |
| 874 | std::string textTooltip; |
| 875 | }; |
| 876 | |
| 877 | std::vector<Marker> markers; |
| 878 | |
| 879 | |
| 880 | enum class State : char { |
| 881 | inText, |
| 882 | inComment, |
| 883 | inSingleQuotedString, |
| 884 | inDoubleQuotedString, |
| 885 | inOtherString, |
| 886 | inOtherStringAlt |
| 887 | }; |
| 888 | |
| 889 | |
| 890 | class Line : public std::vector<Glyph> { |
| 891 | public: |
| 892 | |
| 893 | State state = State::inText; |
| 894 | |
| 895 | |
| 896 | size_t marker = 0; |
| 897 | |
| 898 | |
| 899 | int maxColumn = 0; |
| 900 | |
| 901 | |
| 902 | bool colorize = true; |
| 903 | |
| 904 | |
| 905 | void* userData = nullptr; |
| 906 | }; |
| 907 | |
| 908 | |
| 909 | class Document : public std::vector<Line> { |
| 910 | public: |
| 911 | |
| 912 | Document() { emplace_back(); } |
| 913 | |
| 914 | |
| 915 | inline void setTabSize(int value) { tabSize = value; } |
| 916 | inline int getTabSize() const { return tabSize; } |
| 917 | inline void setInsertSpacesOnTabs(bool value) { insertSpacesOnTabs = value; } |
| 918 | inline bool isInsertSpacesOnTabs() const { return insertSpacesOnTabs; } |
| 919 | |
| 920 | |
| 921 | void setText(const std::string_view& text); |
| 922 | void setText(const std::vector<std::string_view>& text); |
| 923 | Coordinate insertText(Coordinate start, const std::string_view& text); |
| 924 | void deleteText(Coordinate start, Coordinate end); |
| 925 | |
| 926 | |
| 927 | std::string getText() const; |
| 928 | std::string getLineText(int line) const; |
| 929 | std::string getSectionText(Coordinate start, Coordinate end) const; |
| 930 | ImWchar getCodePoint(Coordinate location) const; |
| 931 | |
| 932 | |
| 933 | inline State getLineState(int line) const { return at(line).state; } |
| 934 | Color getColor(Coordinate location) const; |
| 935 | |
| 936 | |
| 937 | inline bool isEmpty() const { return size() == 1 && at(0).size() == 0; } |
| 938 | |
| 939 | |
| 940 | inline int lineCount() const { return static_cast<int>(size()); } |
| 941 | |
| 942 | |
| 943 | void updateMaximumColumn(int first, int last); |
| 944 | inline int getMaxColumn() const { return maxColumn; } |
| 945 | |
| 946 | |
| 947 | size_t getIndex(const Line& line, int column) const; |
| 948 | inline size_t getIndex(Coordinate coordinate) const { return getIndex(at(coordinate.line), coordinate.column); } |
| 949 | int getColumn(const Line& line, size_t index) const; |
| 950 | inline int getColumn(int line, size_t index) const { return getColumn(at(line), index); } |
| 951 | |
| 952 | |
| 953 | Coordinate getUp(Coordinate from, int lines=1) const; |
| 954 | Coordinate getDown(Coordinate from, int lines=1) const; |
| 955 | Coordinate getLeft(Coordinate from, bool wordMode=false) const; |
| 956 | Coordinate getRight(Coordinate from, bool wordMode=false) const; |
| 957 | Coordinate getTop() const; |
| 958 | Coordinate getBottom() const; |
| 959 | Coordinate getStartOfLine(Coordinate from) const; |
| 960 | Coordinate getEndOfLine(Coordinate from) const; |
| 961 | inline Coordinate getNextLine(Coordinate from) const { return getRight(getEndOfLine(from)); } |
| 962 | |
| 963 | |
| 964 | Coordinate findWordStart(Coordinate from, bool wordOnly=false) const; |
| 965 | Coordinate findWordEnd(Coordinate from, bool wordOnly=false) const; |
| 966 | bool findText(Coordinate from, const std::string_view& text, bool caseSensitive, bool wholeWord, Coordinate& start, Coordinate& end) const; |
| 967 | |
| 968 | |
| 969 | inline bool isUpdated() { auto result = updated; updated = false; return result; } |
| 970 | inline void resetUpdated() { updated = false; } |
| 971 | |
| 972 | |
| 973 | inline void setInsertor(std::function<void*(int line)> callback) { insertor = callback; } |
| 974 | inline void setDeletor(std::function<void(int line, void* data)> callback) { deletor = callback; } |
| 975 | |
| 976 | |
| 977 | void setUserData(int line, void* data); |
| 978 | void* getUserData(int line) const; |
| 979 | void iterateUserData(std::function<void(int line, void* data)> callback) const; |
| 980 | |
| 981 | |
| 982 | void iterateIdentifiers(std::function<void(const std::string& identifier)> callback) const; |
| 983 | |
| 984 | |
| 985 | bool isWholeWord(Coordinate start, Coordinate end) const; |
| 986 | inline bool isEndOfLine(Coordinate from) const { return getIndex(from) == at(from.line).size(); } |
| 987 | inline bool isLastLine(int line) const { return line == lineCount() - 1; } |
| 988 | Coordinate findPreviousNonWhiteSpace(Coordinate from, bool includeEndOfLine=true) const; |
| 989 | Coordinate findNextNonWhiteSpace(Coordinate from, bool includeEndOfLine=true) const; |
| 990 | Coordinate normalizeCoordinate(Coordinate coordinate) const; |
| 991 | void normalizeCoordinate(float line, float column, Coordinate& glyphCoordinate, Coordinate& cursorCoordinate) const; |
| 992 | |
| 993 | private: |
| 994 | int tabSize = 4; |
| 995 | bool insertSpacesOnTabs = false; |
| 996 | int maxColumn = 0; |
| 997 | bool updated = false; |
| 998 | |
| 999 | std::function<void*(int)> insertor; |
| 1000 | std::function<void(int, void*)> deletor; |
| 1001 | |
| 1002 | void appendLine(); |
| 1003 | void insertLine(int line); |
| 1004 | void deleteLines(int start, int end); |
| 1005 | void clearDocument(); |
| 1006 | } document; |
| 1007 | |
| 1008 | |
| 1009 | class Action { |
| 1010 | public: |
| 1011 | |
| 1012 | enum class Type : char { |
| 1013 | insertText, |
| 1014 | deleteText |
| 1015 | }; |
| 1016 | |
| 1017 | |
| 1018 | Action() = default; |
| 1019 | Action(Type t, Coordinate s, Coordinate e, const std::string_view& txt) : type(t), start(s), end(e), text(txt) {} |
| 1020 | |
| 1021 | |
| 1022 | Type type; |
| 1023 | Coordinate start; |
| 1024 | Coordinate end; |
| 1025 | std::string text; |
| 1026 | }; |
| 1027 | |
| 1028 | |
| 1029 | class Transaction : public std::vector<Action> { |
| 1030 | public: |
| 1031 | |
| 1032 | inline void setBeforeState(const Cursors& cursors) { before = cursors; } |
| 1033 | inline const Cursors& getBeforeState() const { return before; } |
| 1034 | inline void setAfterState(const Cursors& cursors) { after = cursors; } |
| 1035 | inline const Cursors& getAfterState() const { return after; } |
| 1036 | |
| 1037 | |
| 1038 | void addInsert(Coordinate start, Coordinate end, std::string_view text) { emplace_back(Action::Type::insertText, start, end, text); }; |
| 1039 | void addDelete(Coordinate start, Coordinate end, std::string_view text) { emplace_back(Action::Type::deleteText, start, end, text); }; |
| 1040 | |
| 1041 | |
| 1042 | inline int actions() const { return static_cast<int>(size()); } |
| 1043 | |
| 1044 | private: |
| 1045 | |
| 1046 | Cursors before; |
| 1047 | Cursors after; |
| 1048 | }; |
| 1049 | |
| 1050 | |
| 1051 | class Transactions : public std::vector<std::shared_ptr<Transaction>> { |
| 1052 | public: |
| 1053 | |
| 1054 | void reset(); |
| 1055 | |
| 1056 | |
| 1057 | static inline std::shared_ptr<Transaction> create() { return std::make_shared<Transaction>(); } |
| 1058 | |
| 1059 | |
| 1060 | void add(std::shared_ptr<Transaction> transaction); |
| 1061 | |
| 1062 | |
| 1063 | void undo(Document& document, Cursors& cursors); |
| 1064 | |
| 1065 | |
| 1066 | void redo(Document& document, Cursors& cursors); |
| 1067 | |
| 1068 | |
| 1069 | inline size_t getUndoIndex() const { return undoIndex; } |
| 1070 | inline bool canUndo() const { return undoIndex > 0; } |
| 1071 | inline bool canRedo() const { return undoIndex < size(); } |
| 1072 | inline size_t getVersion() const { return version; } |
| 1073 | |
| 1074 | private: |
| 1075 | size_t undoIndex = 0; |
| 1076 | size_t version = 0; |
| 1077 | } transactions; |
| 1078 | |
| 1079 | |
| 1080 | class Colorizer { |
| 1081 | public: |
| 1082 | |
| 1083 | void updateEntireDocument(Document& document, const Language* language); |
| 1084 | |
| 1085 | |
| 1086 | void updateChangedLines(Document& document, const Language* language); |
| 1087 | |
| 1088 | private: |
| 1089 | |
| 1090 | State update(Line& line, const Language* language); |
| 1091 | |
| 1092 | |
| 1093 | bool matches(Line::iterator start, Line::iterator end, const std::string_view& text); |
| 1094 | |
| 1095 | |
| 1096 | inline void setColor(Line::iterator start, Line::iterator end, Color color) { while (start < end) (start++)->color = color; } |
| 1097 | } colorizer; |
| 1098 | |
| 1099 | |
| 1100 | class BracketPair { |
| 1101 | public: |
| 1102 | BracketPair(ImWchar sc, Coordinate s, ImWchar ec, Coordinate e, int l) : startChar(sc), start(s), endChar(ec), end(e), level(l) {} |
| 1103 | ImWchar startChar; |
| 1104 | Coordinate start; |
| 1105 | ImWchar endChar; |
| 1106 | Coordinate end; |
| 1107 | int level; |
| 1108 | |
| 1109 | inline bool isAfter(Coordinate location) const { return start > location; } |
| 1110 | inline bool isAround(Coordinate location) const { return start < location && end >= location; } |
| 1111 | }; |
| 1112 | |
| 1113 | |
| 1114 | class Bracketeer : public std::vector<BracketPair> { |
| 1115 | public: |
| 1116 | |
| 1117 | void reset(); |
| 1118 | |
| 1119 | |
| 1120 | void update(Document& document); |
| 1121 | |
| 1122 | |
| 1123 | iterator getEnclosingBrackets(Coordinate location); |
| 1124 | iterator getEnclosingCurlyBrackets(Coordinate first, Coordinate last); |
| 1125 | iterator getInnerCurlyBrackets(Coordinate first, Coordinate last); |
| 1126 | |
| 1127 | |
| 1128 | static inline bool isBracketCandidate(Glyph& glyph) { |
| 1129 | return glyph.color == Color::punctuation || |
| 1130 | glyph.color == Color::matchingBracketLevel1 || |
| 1131 | glyph.color == Color::matchingBracketLevel2 || |
| 1132 | glyph.color == Color::matchingBracketLevel3 || |
| 1133 | glyph.color == Color::matchingBracketError; |
| 1134 | } |
| 1135 | } bracketeer; |
| 1136 | |
| 1137 | |
| 1138 | class Autocomplete { |
| 1139 | public: |
| 1140 | |
| 1141 | void setConfig(const AutoCompleteConfig* c); |
| 1142 | |
| 1143 | |
| 1144 | |
| 1145 | bool startTyping(Cursors& cursors); |
| 1146 | bool startShortcut(Cursors& cursors); |
| 1147 | |
| 1148 | |
| 1149 | void cancel(); |
| 1150 | |
| 1151 | |
| 1152 | bool render(Document& document, Cursors& cursors, const Language* language, float textOffset, ImVec2 glyphSize); |
| 1153 | |
| 1154 | |
| 1155 | void setSuggestions(const std::vector<std::string>& suggestions); |
| 1156 | |
| 1157 | |
| 1158 | inline bool isActive() const { return active; } |
| 1159 | inline bool hasSuggestions() const { return state.suggestions.size() > 0 || state.suggestionsPromise; } |
| 1160 | bool isSpecialKeyPressed() const; |
| 1161 | inline ImGuiKeyChord getTriggerShortcut() const { return configuration.triggerShortcut; } |
| 1162 | inline Coordinate getStart() const { return startLocation; } |
| 1163 | inline std::string getReplacement() { return currentSelection < state.suggestions.size() ? state.suggestions[currentSelection] : ""; } |
| 1164 | |
| 1165 | private: |
| 1166 | |
| 1167 | bool configured = false; |
| 1168 | bool active = false; |
| 1169 | bool requestActivation = false; |
| 1170 | bool requestDeactivation = false; |
| 1171 | Coordinate currentLocation; |
| 1172 | Coordinate startLocation; |
| 1173 | std::chrono::system_clock::time_point activationTime; |
| 1174 | AutoCompleteConfig configuration; |
| 1175 | AutoCompleteState state; |
| 1176 | bool triggeredManually = false; |
| 1177 | size_t currentSelection = 0; |
| 1178 | static constexpr float suggestionWidth = 250.0f; |
| 1179 | |
| 1180 | |
| 1181 | void start(Cursors& cursors); |
| 1182 | void updateState(Document& document, const Language* language); |
| 1183 | void refreshSuggestions(); |
| 1184 | } autocomplete; |
| 1185 | |
| 1186 | |
| 1187 | void setText(const std::string_view& text); |
| 1188 | |
| 1189 | |
| 1190 | void render(const char* title, const ImVec2& size, bool border); |
| 1191 | void renderSelections(); |
| 1192 | void renderMarkers(); |
| 1193 | void renderMatchingBrackets(); |
| 1194 | void renderText(); |
| 1195 | void renderCursors(); |
| 1196 | void renderMargin(); |
| 1197 | void renderLineNumbers(); |
| 1198 | void renderDecorations(); |
| 1199 | void renderScrollbarMiniMap(); |
| 1200 | void renderPanScrollIndicator(); |
| 1201 | void renderFindReplace(ImVec2 pos, float width); |
| 1202 | |
| 1203 | |
| 1204 | void handleKeyboardInputs(); |
| 1205 | void handleMouseInteractions(); |
| 1206 | |
| 1207 | |
| 1208 | void selectAll(); |
| 1209 | void selectLine(int line); |
| 1210 | void selectLines(int startLine, int endLine); |
| 1211 | void selectRegion(int startLine, int startColumn, int endLine, int endColumn); |
| 1212 | void selectToBrackets(bool includeBrackets); |
| 1213 | void growSelectionsToCurlyBrackets(); |
| 1214 | void shrinkSelectionsToCurlyBrackets(); |
| 1215 | |
| 1216 | void cut(); |
| 1217 | void copy() const; |
| 1218 | void paste(); |
| 1219 | void undo(); |
| 1220 | void redo(); |
| 1221 | |
| 1222 | |
| 1223 | void getCursor(int& line, int& column, size_t cursor) const; |
| 1224 | void getCursor(int& startLine, int& startColumn, int& endLine, int& endColumn, size_t cursor) const; |
| 1225 | std::string getCursorText(size_t cursor) const; |
| 1226 | |
| 1227 | |
| 1228 | void makeCursorVisible(); |
| 1229 | void scrollToLine(int line, Scroll alignment); |
| 1230 | |
| 1231 | |
| 1232 | void selectFirstOccurrenceOf(const std::string_view& text, bool caseSensitive, bool wholeWord); |
| 1233 | void selectNextOccurrenceOf(const std::string_view& text, bool caseSensitive, bool wholeWord); |
| 1234 | void selectAllOccurrencesOf(const std::string_view& text, bool caseSensitive, bool wholeWord); |
| 1235 | void addNextOccurrence(); |
| 1236 | void selectAllOccurrences(); |
| 1237 | |
| 1238 | void replaceTextInCurrentCursor(const std::string_view& text); |
| 1239 | void replaceTextInAllCursors(const std::string_view& text); |
| 1240 | void replaceSectionText(const Coordinate& start, const Coordinate& end, const std::string_view& text); |
| 1241 | |
| 1242 | void openFindReplace(); |
| 1243 | void closeFindReplace(); |
| 1244 | void find(); |
| 1245 | void findNext(); |
| 1246 | void findAll(); |
| 1247 | void replace(); |
| 1248 | void replaceAll(); |
| 1249 | |
| 1250 | |
| 1251 | void addMarker(int line, ImU32 lineNumberColor, ImU32 textColor, const std::string_view& lineNumberTooltip, const std::string_view& textTooltip); |
| 1252 | void clearMarkers(); |
| 1253 | |
| 1254 | |
| 1255 | void moveUp(int lines, bool select); |
| 1256 | void moveDown(int lines, bool select); |
| 1257 | void moveLeft(bool select, bool wordMode); |
| 1258 | void moveRight(bool select, bool wordMode); |
| 1259 | void moveToTop(bool select); |
| 1260 | void moveToBottom(bool select); |
| 1261 | void moveToStartOfLine(bool select); |
| 1262 | void moveToEndOfLine(bool select); |
| 1263 | void moveTo(Coordinate coordinate, bool select); |
| 1264 | |
| 1265 | |
| 1266 | void handleCharacter(ImWchar character); |
| 1267 | void handleBackspace(bool wordMode); |
| 1268 | void handleDelete(bool wordMode); |
| 1269 | |
| 1270 | |
| 1271 | void removeSelectedLines(); |
| 1272 | void insertLineAbove(); |
| 1273 | void insertLineBelow(); |
| 1274 | |
| 1275 | |
| 1276 | void indentLines(); |
| 1277 | void deindentLines(); |
| 1278 | void moveUpLines(); |
| 1279 | void moveDownLines(); |
| 1280 | void toggleComments(); |
| 1281 | |
| 1282 | |
| 1283 | void filterSelections(std::function<std::string(std::string_view)> filter); |
| 1284 | void selectionToLowerCase(); |
| 1285 | void selectionToUpperCase(); |
| 1286 | |
| 1287 | |
| 1288 | void stripTrailingWhitespaces(); |
| 1289 | void filterLines(std::function<std::string(std::string_view)> filter); |
| 1290 | void tabsToSpaces(); |
| 1291 | void spacesToTabs(); |
| 1292 | |
| 1293 | |
| 1294 | |
| 1295 | std::shared_ptr<Transaction> startTransaction(bool cancelsAutoComplete=true); |
| 1296 | bool endTransaction(std::shared_ptr<Transaction> transaction); |
| 1297 | |
| 1298 | void insertTextIntoAllCursors(std::shared_ptr<Transaction> transaction, const std::string_view& text); |
| 1299 | void deleteTextFromAllCursors(std::shared_ptr<Transaction> transaction); |
| 1300 | void autoIndentAllCursors(std::shared_ptr<Transaction> transaction); |
| 1301 | Coordinate insertText(std::shared_ptr<Transaction> transaction, Coordinate start, const std::string_view& text); |
| 1302 | void deleteText(std::shared_ptr<Transaction> transaction, Coordinate start, Coordinate end); |
| 1303 | |
| 1304 | |
| 1305 | float lineSpacing = 1.0f; |
| 1306 | bool readOnly = false; |
| 1307 | bool autoIndent = true; |
| 1308 | bool showSpaces = true; |
| 1309 | bool showTabs = true; |
| 1310 | bool showLineNumbers = true; |
| 1311 | bool showScrollbarMiniMap = true; |
| 1312 | bool showMatchingBrackets = true; |
| 1313 | bool completePairedGlyphs = true; |
| 1314 | bool overwrite = false; |
| 1315 | |
| 1316 | |
| 1317 | ImFont* font; |
| 1318 | float fontSize; |
| 1319 | ImVec2 glyphSize; |
| 1320 | ImVec2 lastRenderOrigin; |
| 1321 | float lineNumberLeftOffset; |
| 1322 | float lineNumberRightOffset; |
| 1323 | float decorationOffset; |
| 1324 | float textOffset; |
| 1325 | float visibleHeight; |
| 1326 | int visibleLines; |
| 1327 | int firstVisibleLine; |
| 1328 | int lastVisibleLine; |
| 1329 | float visibleWidth; |
| 1330 | int visibleColumns; |
| 1331 | int firstVisibleColumn; |
| 1332 | int lastVisibleColumn; |
| 1333 | float verticalScrollBarSize; |
| 1334 | float horizontalScrollBarSize; |
| 1335 | float cursorAnimationTimer = 0.0f; |
| 1336 | bool ensureCursorIsVisible = false; |
| 1337 | int scrollToLineNumber = -1; |
| 1338 | Scroll scrollToAlignment = Scroll::alignMiddle; |
| 1339 | bool showMatchingBracketsChanged = false; |
| 1340 | bool languageChanged = false; |
| 1341 | |
| 1342 | float decoratorWidth = 0.0f; |
| 1343 | std::function<void(Decorator&)> decoratorCallback; |
| 1344 | |
| 1345 | std::function<void(int line)> lineNumberContextMenuCallback; |
| 1346 | std::function<void(int line, int column)> textContextMenuCallback; |
| 1347 | int contextMenuLine = 0; |
| 1348 | int contextMenuColumn = 0; |
| 1349 | |
| 1350 | static constexpr int leftMargin = 1; |
| 1351 | static constexpr int decorationMargin = 1; |
| 1352 | static constexpr int textMargin = 2; |
| 1353 | static constexpr int cursorWidth = 1; |
| 1354 | |
| 1355 | |
| 1356 | std::string findButtonLabel = "Find"; |
| 1357 | std::string findAllButtonLabel = "Find All"; |
| 1358 | std::string replaceButtonLabel = "Replace"; |
| 1359 | std::string replaceAllButtonLabel = "Replace All"; |
| 1360 | bool findReplaceVisible = false; |
| 1361 | bool focusOnEditor = true; |
| 1362 | bool focusOnFind = false; |
| 1363 | bool findCancelledAutocomplete = false; |
| 1364 | std::string findText; |
| 1365 | std::string replaceText; |
| 1366 | bool caseSensitiveFind = false; |
| 1367 | bool wholeWordFind = false; |
| 1368 | |
| 1369 | |
| 1370 | float lastClickTime = -1.0f; |
| 1371 | ImWchar completePairCloser = 0; |
| 1372 | Coordinate completePairLocation; |
| 1373 | bool panMode = true; |
| 1374 | bool panning = false; |
| 1375 | bool scrolling = false; |
| 1376 | ImVec2 scrollStart; |
| 1377 | bool showPanScrollIndicator = true; |
| 1378 | std::function<void()> delayedChangeCallback; |
| 1379 | std::chrono::milliseconds delayedChangeDelay; |
| 1380 | std::chrono::system_clock::time_point delayedChangeReportTime; |
| 1381 | bool delayedChangeDetected = false; |
| 1382 | std::function<void(std::vector<Change>&)> transactionCallback; |
| 1383 | |
| 1384 | |
| 1385 | void updatePalette(); |
| 1386 | static Palette defaultPalette; |
| 1387 | Palette paletteBase; |
| 1388 | Palette palette; |
| 1389 | float paletteAlpha; |
| 1390 | |
| 1391 | |
| 1392 | const Language* language = nullptr; |
| 1393 | }; |