24.6 KB、406行 表示 生 Blame 履歴
1<div align="center">
2
3![MacOS status](https://img.shields.io/github/actions/workflow/status/goossens/ImGuiColorTextEdit/macos.yml?branch=master&label=MacOS&style=for-the-badge)
4![Linux status](https://img.shields.io/github/actions/workflow/status/goossens/ImGuiColorTextEdit/linux.yml?branch=master&label=Linux&style=for-the-badge)
5![Windows status](https://img.shields.io/github/actions/workflow/status/goossens/ImGuiColorTextEdit/windows.yml?branch=master&label=Windows&style=for-the-badge)
6<br/>
7![Repo size](https://img.shields.io/github/repo-size/goossens/ImGuiColorTextEdit?style=for-the-badge)
8![Repo activity](https://img.shields.io/github/commit-activity/m/goossens/ImGuiColorTextEdit?label=Commits&style=for-the-badge)
9<br/>
10[![License](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)
11![Maintained](https://img.shields.io/maintenance/yes/2026?style=for-the-badge)
12![Version](https://img.shields.io/badge/version-0.9-blue?style=for-the-badge)
13
14# Colorizing Text Editor and Text Diff for Dear ImGui
15
16</div>
17
18> [!NOTE]
19> The editor is being refactored to enable new features like Word Wrap, Line Folding, full MiniMaps, Autocomplete and Language Server connectivity. This refactoring will break backwards compatibility. A preview is avalable in the [future branch](https://github.com/goossens/ImGuiColorTextEdit/tree/future). Once more testing is done, this new architecture will become the master branch.
20
21TextEdit is a syntax highlighting text editor for
22[Dear ImGui](https://github.com/ocornut/imgui) and it was originally developed by
23[Balázs Jákó](https://github.com/BalazsJako/ImGuiColorTextEdit). Unfortunately, he no
24longer has time to work on the project. In fact, the last update to his repository was
25in June 2019. As a result, over 200 forks exist by people who like his work but want
26to fix bugs and/or add new features. A fork by
27[Santiago](https://github.com/santaclose/ImGuiColorTextEdit), also known as santaclose,
28was the most actively maintained version (over 220 commits ahead of the original version)
29when I was looking for a syntax highlighting text editor for Dear ImGui in late 2023.
30This repository was originally a fork of Santiago's version and I tried to enhance it.
31
32In late December 2024, I decided that it was better for me to rewrite the code from
33the ground up while preserving the majority of the public APIs so it would be relatively
34simple for me (and others) to reuse my rewrite. One of my fundamental arguments
35for the rewrite was that the code had become hard to read and maintain (at least for me).
36I decided to create a more object-oriented internal architecture (see below) that made
37it clear who was responsible for what by using a layered approach. While rewriting
38the code, a number of new features were also added.
39
40The rewrite and now the maintenance of this code is part of a larger project
41([ObjectTalk](https://github.com/goossens/ObjectTalk)) and this repository is simply
42a snapshot of the relevant editor code to provide reuse. As part of this code is
43automatically generated, references to the master project are provided where required.
44You can find all text editor source code components here in the
45[ObjectTalk repository](https://github.com/goossens/ObjectTalk/tree/master/3rdparty/imguieditor).
46
47To respect its origins, this repository will remain a fork (of a fork) although
48there is now little code in common.
49
50![Screenshot](docs/textEditor.png)
51
52Note: In the screenshot above, the tabs and the menubar are not part of the
53text editor widget. They are part of a custom enclosing IDE (which is part of
54the larger [ObjectTalk](https://github.com/goossens/ObjectTalk) project).
55By not putting those things in the editor, integrators have maximum
56flexibility to wrap the editor in their own context in the way they see fit. The
57public API to externally implement these features is however included.
58
59## Features
60
61- Works on MacOS, Linux and Windows.
62- Has look and feel similar to Visual Studio Code.
63- Works with latest Dear ImGui version (currently v1.92.7) and does not use deprecated functions.
64- Supports dynamic font sizes (courtesy of Dear ImGui v1.92+). Implemented in example application.
65- Supports UTF-8 encoding with 16/32 bit codepoints (based on Dear ImGui configuration, see below).
66- Is C++17 based (not unreasonable in 2026 I think) although Dear ImGui still uses C++11.
67- Has no runtime dependencies other than Dear ImGui and the C++17 Standard Template Library (STL).
68- Has full undo/redo capabilities and supports native clipboard with UTF-8 encoded strings.
69- Has extendable syntax highlighting for multiple languages and API for custom languages.
70- Has customizable color palettes (a dark and light version that work with Dear ImGui defaults are included).
71- Has option to automatically insert spaces on tabs.
72- Has find/replace user interface and API with full undo/redo.
73- Find has options for whole word and/or case-sensitive searches.
74- Provides optional autocomplete framework (see [more information here](docs/autocomplete.md)).
75- Has marker API to specify lines and/or line numbers to highlight and optional show tooltips (see [example](docs/markers.md)).
76- Has optional scrollbar minimap to render cursor, selection and marker locations.
77- Provides middle-mouse pan and scroll functions like CAD programs and browsers.
78- Has API to attach user data to select lines or all lines (see [example](docs/userData.md)).
79- Has API to decorate each line (useful for debuggers and IDEs) (see [example](docs/lineDecorator.md)).
80- Provides optional and customizable right click (control + click on MacOS) context menus for line numbers or text lines (see [example](docs/contextMenus.md))
81- Provides auto completion for paired glyphs (\[, \{, \(, \", \') (can be turned on and off).
82- If auto complete is turned on, accidentally typed closing glyphs are ignored.
83- If auto complete is turned on, selections can be surrounded by paired glyphs.
84- Supports blinking cursor (can be turned on/off using Dear ImGui's global io.ConfigInputTextCursorBlink flag).
85- Supports bracket matching and coloring similar to Visual Studio Code (can be turned on and off).
86- Supports multiple cursors and multiple selections.
87- Provides auto indent with simplified implementation (can be turned on and off).
88- Has API to filter selections (with full undo support).
89- Uppercase/lowercase filter is example of selection filtering.
90- Has API to filter each line in editor (with full undo support).
91- Tabs to Space (and visa versa) are examples of line filtering.
92- Has API to strip trailing whitespaces.
93- Provides whitespace indicators for tabs and spaces (can be turned on and off individually or collectively).
94- No longer uses regular expressions for colorizing text (see below).
95- Provides an optional companion widget to show source code differences between versions (see below).
96
97## Integration
98
99As explained above, the editor is developed and maintained as part of a larger project.
100In that bigger project, the editor is spread out over multiple source files and some
101of those are automatically generated using tools or derived from datasets (like the
102unicode database). Every time the editor is updated in the bigger project, relevant
103files are concatenated into a simple 2 (or 5 if you also want to use TextDiff)
104file distribution which is included here.
105
106This repository therefore provides a simple mechanism to reuse the editor in any
107Dear ImGui context by doing the following:
108
109- Include the TextEditor.cpp and TextEditor.h files in your project.
110- Instantiate a TextEditor object for each editor widget you need.
111- Use the public API to set editor options or interact with the editor's contents.
112- Call the TextEditor's Render member function every frame in your Dear ImGui loop.
113- If you plan to use non-ASCII characters in your text, see the Unicode section below.
114- Configure Dear ImGui's clipboard functions since that is what this editor uses.
115
116If you also want to use the TextDiff widget, you must:
117
118- Include TextDiff.cpp, TextDiff.h and dtl.h in your project.
119- Instantiate a TextDiff object for each diff widget you need.
120- Use the public API to set diff options.
121- Call the TextDiff's Render member function every frame in your Dear ImGui loop.
122- If you properly configured Dear ImGui for the TextEditor (see above), you are good to go.
123
124For a complete example, please see the [example folder](example/).
125
126**Note**: Like ImGui, it is recommended that you compile and link TextEditor and TextDiff
127as a static library or directly as part of your sources (as suggested above).
128However, if you must and are compiling TextEditor and/or TextDiff as separate DLLs, make sure
129you set the current ImGui context with TextEditor::SetImGuiContext(ImGuiContext* ctx).
130This ensures that global ImGui variables are correctly shared across the DLL boundary.
131See GImGui documentation in imgui.cpp for more details.
132
133## Default Keyboard and Mouse Mappings
134
135- In the mappings listed below, the following modifier keys are used:
136 - Ctrl: this refers to the Command key on MacOS and the Control key on Linux and Windows.
137 - Alt: this refers to the Option key on MacOS and the Alt key on Linux and Windows.
138 - Shift: as you would expect on all platforms.
139 - If a keyboard has a left and right version of these modifiers, the meaning is not different.
140
141- Cursor Movements:
142 - Single left mouse click moves the cursor.
143 - Arrow keys move cursor left, right, up and down.
144 - PageUp and PageDown do what you expect.
145 - Ctrl-UpArrow and Ctrl-DownArrow move to the start/end of the document.
146 - Home and End keys move to the start/end of the line.
147 - Holding down the Shift key extends the current selection(s).
148 - Holding down the Alt key with the left or right arrow moves a whole word on MacOS.
149 - Holding down the Ctrl key with the left or right arrow moves a whole word on Linux and Windows.
150
151- Panning and Scrolling:
152 - The text scrolls automatically when you move the cursor through keyboard actions.
153 - Mouse actions that extend the selections also apply auto scrolling.
154 - The text in the editor can still be scrolled using those little bars that were invented in the 1970's.
155 - Devices with scroll wheels or those that simulated vertical and horizontal scroll wheels (like a touch pad, a mouse with a builtin touch pad or a pen) can also scroll the text. This is actually implemented in Dear ImGui (and used by the editor) and must be supported by your backend.
156 - The middle mouse button on a three-button mouse (or whatever is reported by your OS as a middle mouse button event) enters pan or scroll mode mode depending on the configuration. Pan mode is the default and you can switch this to Scroll mode by calling SetMiddleMouseScrollMode(). Calling SetMiddleMousePanMode() switches it back. The example application uses a menu option to toggle modes.
157 - In pan mode, the text is grabbed and dragged as the cursor moves and as long as the middle mouse button is down.
158 - When you mouse approaches the edges of the editor window, it enters continuous panning mode and the further you move away form the window, the faster it pans.
159 - Panning as described above is typical in CAD or 3D type applications.
160 - In scroll mode, you can release the middle mouse button and scroll the text just like you can in some browsers.
161 - Scroll mode is ended by any clicking any mouse button.
162 - Panning and scrolling operate in opposite directions as they are different paradigms.
163 - An optional indicator (default is on) is shown in the center of the editor window when entering pan/scroll mode. If anybody finds this annoying, it can be turned off through an API by calling SetShowPanScrollIndicatorEnabled(false).
164
165- Cursors and Selections:
166 - Alt with single left mouse click creates a new cursor on MacOS.
167 - Ctrl with single left mouse click creates a new cursor on Linux and Windows.
168 - Ctrl-A select all text.
169 - Ctrl-D creates a new cursor and selects the next instance of the current selection.
170 - Double left mouse clicks on a curly bracket select the content of the relevant block and replaces all previous cursors.
171 - Shift + Double left mouse clicks on a curly bracket select the content of the relevant block including the brackets and replaces all previous cursors.
172 - Double left mouse clicks not on a bracket or parenthesis, select a word. Adding Shift extends current selection.
173 - Triple left mouse clicks select a line. Adding Shift extends current selection.
174 - Dragging mouse with left mouse button selects text. Shift extends current selection.
175 - Alt-Shift-RightArrow (on MacOS) and Ctrl-Shift-RightArrow (on Linux and Windows) grows all selections to outer blocks. First just the content of the block, than including the curly brackets. Continuously hitting the key combination keeps growing the selections.
176 - Alt-Shift-LeftArrow (on MacOS) and Ctrl-Shift-LeftArrow (on Linux and Windows) shrinks all selections to inner blocks. First including the curly brackets, that just the content of the block. Continuously hitting the key combination keeps shrinking the selections.
177 - Left mouse clicking or dragging over line numbers select line(s).
178 - When multiple cursors are active, hitting the ESC key cancels them.
179
180- Clipboard Operations:
181 - Ctrl-X or Shift-Delete cuts selected text or current line if no selection.
182 - Ctrl-C or Ctrl-Insert copies selected text or current line if no selection.
183 - Ctrl-V or Shift-Insert pastes clipboard content.
184 - Ctrl-Z undos the last action.
185 - Shift-Ctrl-Z or Ctrl-Y redos the last undone action.
186
187- Insert Text:
188 - Ctrl-Enter adds line below.
189 - Ctrl-Shift-Enter adds line above.
190
191- Delete Text:
192 - Backspace deletes character to the left of cursor or deletes selection(s).
193 - Delete deletes character to the right of cursor or deletes selection(s).
194 - Ctrl-Shift-K deletes all selected lines.
195
196- Text Manipulation:
197 - Ctrl-] indents current line or current lines.
198 - Ctrl-[ de-indents current line or current lines.
199 - Tab with selection indents current line or current lines.
200 - Ctrl-UpArrow moves current or all selected lines up.
201 - Ctrl-DownArrow moves current or all selected lines down.
202 - Ctrl-/ toggles comments if language with single line comments is specified.
203
204- Find & Replace:
205 - Ctrl-f opens the find and replace window.
206 - If text is selected when Ctrl-f is hit, the selected text is used as the search term if the start and end are on the same line.
207 - Else if the cursor is over a word when Ctrl-f is hit, that word is used as the search term.
208 - In all other cases, the previous search term is used.
209 - When the search and replace window is visible, hitting the ESC button closes it.
210 - Shift-Ctrl-f finds all instances and makes them separate cursors.
211 - Ctrl-g finds n
212
213- Autocomplete:
214 - Once configured, Ctrl-space (or a custom key combination) triggers a manual autocomplete (even on MacOS as Cmd-space globally triggers Spotlight searching on that platform).
215 - Typing also triggers autocomplete (based on context).
216 - You can configure which triggers you want (the default is for both manual and automatic (by just typing) to be active)
217 - Autocomplete does not work when multiple cursors are active.
218
219- Other:
220 - Insert key toggles between insert (default) and overwrite modes.
221
222## Unicode and UTF-8
223
224This rewrite of TextEditor uses UTF-8 encoded strings for the public API
225and to interact with the clipboard like the original TextEditor versions.
226The original and most of its forks use UTF-8 on the private side as well
227which is effective from a storage perspective but it makes all the
228processing a lot harder as you constantly have to parse UTF-8 sequences.
229
230This rewrite internally uses unicode codepoints that are either 16 or 32 bits
231depending on how Dear ImGui is configured. If IMGUI_USE_WCHAR32 is defined,
232Dear ImGui as well as this editor use 32 bits to store a codepoint. If it is not
233defined (which is the default), 16 bits are used for each codepoint which
234basically limits unicode support to the Basic Multilingual Plane (see
235[this article on Wikipedia](https://en.wikipedia.org/wiki/Plane_(Unicode))).
236
237Regardless of whether 16 or 32 bit codepoints are used, it is your responsibility
238to ensure Dear ImGui is configured with the correct font and font size. As of
239Dear ImGui v1.92, you can now use dynamically sized fonts and use any font glyph
240without setting up glyph ranges or a font atlas. All this functionality is in the
241Dear ImGui core and the TextEditor uses it. Please see the example application on
242how to use PushFont/PopFont around the TextEditor Render function. The demo app
243also shows how to increase/decrease the font size on the fly.
244
245## Architecture
246
247While this editor relies on [Omar Cornut's Dear ImGui](https://github.com/ocornut/imgui),
248it does not follow the "pure" one widget - one function approach. Since the editor
249has to maintain a relatively complex and large internal state, it did not seem
250to be practical to try and enforce full immediate mode. It therefore stores
251its internal state in an object instance which is reused across frames. This
252object is an instance of the TextEditor class which not only stores the
253internal state, it also provides the public API. The "Render" member functions
254however works like a normal Dear ImGui widget. It creates a child window to
255render the editor so you can call Dear ImGui window functions like SetNextWindowSize
256or set the font before calling Render.
257
258The block diagram below shows the architecture of this widget. At the top is
259the public facing TextEditor instance and at the bottom are the private classes
260that store and maintain the internal state.
261
262![Architecture](docs/architecture.png)
263
264#### Document
265
266This class, as the name suggests, stores the document that's being edited.
267Internally, a Document is a std::vector of Lines that themselves are
268std::vectors of Glyphs.
269
270The Document class translates from the external UTF-8 encoding to the internal
271codepoints and provides a number of member functions to manipulate contents,
272calculate coordinates, assist searches and track overall content state.
273
274The Line and Glyph classes are used to represent the document's structure with
275Glyph just holding a codepoint and a color.
276
277#### Cursors
278
279The Cursors class maintains a list of Cursor instances and operates very much
280like cursors do in Visual Studio Code. The list of cursors consists of the
281main cursor (which is always present) and a optional set of additional cursors.
282The last cursor added is considered the current cursor which has meaning for
283some actions or API calls.
284
285#### Transactions
286
287This class contains a list of Transaction records to support do/undo/redo
288operations. Each Transaction record contains insert and delete operations
289performed on a document.
290
291#### Colorizer
292
293The colorizer maintains the color state of each glyph based on the rules
294provided by the specifications in the Language object. The original version
295used regular expressions for some languages but this was/is not very
296performant. The new colorizer is based on multiple state-transition engines
297that make it easier to express language rules and also improves performance.
298The new colorizer only effects lines that have changed but keep in mind, that
299for instance opening a multiline comment at the start of the document,
300causes the entire document to be re-colored. Luckily the new engine is fast
301enough that you don't notice this and it would only affect a single frame. I
302think it is also important to point out that this widget is not really intended
303for mega/gigabyte size text files. For those, I would still use a regular text
304editors (if you can find one that can handle that requirement).
305
306#### Bracketeer
307
308This class keeps track of where bracket pairs (parenthesis, square brackets and
309curly brackets) are in the document so they can be highlighted and colorized
310when this feature is activated. It will also colorize unbalanced brackets as
311errors. Brackets in comments and strings are ignored.
312
313#### Autocomplete
314
315This class keeps track of autocomplete activities when the feature is configured.
316Once autocomplete is triggered, this class tracks the state, interacts with the
317user and uses an external callback to provide suggestions in the current context
318(see [details here](docs/autocomplete.md)).
319
320#### Coordinate
321
322Coordinates represent a character coordinate from the user's point of view,
323i. e. consider an uniform grid on the screen as it is rendered, and each cell
324has its own coordinate, starting from Tabs are counted as [1..tabsize]
325spaces, depending on how many spaces are necessary to reach the next tab stop.
326For example, coordinate (1, 5) represents the character 'B' in a line "\tABC",
327when tabsize = 4, because it is rendered as "&nbsp;&nbsp;&nbsp;&nbsp;ABC" on the screen.
328
329#### TextEditor
330
331As mentioned above, the TextEditor class provides the public API. As is true
332for all Dear InGui and the original text editor, all public member functions start
333with an uppercase letter. Internally, all private member functions and variables
334start with a lowercase letter so it's easy to see what's public and what's private.
335
336In addition to being the public interface, the TextEditor class is also responsible
337for synchronizing the state of the lower levels of the architecture. When for
338instance the user pastes some text, TextEditor ensures that the Document gets
339updated, Cursors get adjusted (if required), Transaction records are created (so
340this paste operation can be undone and redone) and the Colorizer/Bracketeer are
341informed of the changes.
342
343The final responsibility of the TextEditor class is overall rendering and
344user input (keyboard and mouse) processing.
345
346#### TextDiff
347
348TextDiff is a separate widget that is derived from TextEditor which allows
349you to show the differences between two versions of some code while preserving color
350highlighting. The code for this widget is in separate files (see integration above)
351so it is optional. TextDiff is readonly and has two modes:
352
353- **Integrated view** where differences are depicted vertically using the standard "diff" look and feel. This view is implemented as a read-only TextEditor with markings and a line decorator which means that functions like text select and copy are available. This is the default view.
354
355- **Side-by-side view** where the original and altered versions are shown/compared side-by-side. This a a custom static view that does not have the usual TextEditor features like text select or copy. Scrolling however is available.
356
357In both modes, the provided text can be colored based on a specified language and
358color palette like in the TextEditor. The background colors for the difference
359highlighting are not part of the palette but can still be changed through a the public API.
360
361Below are two screenshots of its use in both modes. Have a look at the code in the [example application](example/) to see how easy it is to use this TextDiff widget.
362
363![Screenshot 2](docs/textDiffCombined.png)
364![Screenshot 3](docs/textDiffSideBySide.png)
365
366## Issues
367
368If you are interested in using this Text Editor, steal parts of the code, make
369suggestions for improvements or contribute fixes/enhancements, be my guest as this
370repository is released under the MIT license. For people that want to contribute,
371[Contributing Guidelines](CONTRIBUTING.md) and a [Code of Conduct](CODE_OF_CONDUCT.md)
372are available. If you find any problems or want to make a suggestion for improvement, please
373[raise an issue on this repository](https://github.com/goossens/ImGuiColorTextEdit/issues).
374
375## Credits
376
377This version of ImGuiColorTextEdit was written from scratch by [Johan A. Goossens](https://github.com/goossens)
378and if you end up using (parts of) this repository a shoutout or Github star would be appreciated.
379
380Thank you to [Omar Cornut](https://github.com/ocornut/imgui) for creating Dear ImGui
381in the first place. Without you, this editor would not exist.
382
383I owe a great deal of gratitude to [Balázs Jákó](https://github.com/BalazsJako/ImGuiColorTextEdit)
384who started this editor many years ago.
385
386I also have to thank [Santiago (santaclose)](https://github.com/santaclose/ImGuiColorTextEdit)
387for reviving this project, merging the unfinished developments and pull requests.
388He was also responsible for adding features to the editor like multiple cursors.
389
390Given that neither Balázs nor Santiago kept a credits list, it is highly likely
391that many others have also made contributions. Theoretically I could go through
392all the issues on the original and the fork but I think I have better use for my
393time. If you believe you deserve credit here, please raise an issue with reference
394to your work. I'll gladly add it.
395
396For the Text Diff widget, credit goes to [Tatsuhiko Kubo (cubicdaiya)](https://github.com/cubicdaiya)
397for his [Diff Template Library (DTL)](https://github.com/cubicdaiya/dtl) which is
398released under the BSD License.
399
400## License
401
402This work is licensed under the terms of the MIT license.
403For a copy, see <https://opensource.org/licenses/MIT>.
404
405The included DTL library (which is only used in the Diff Widget) is released under the
406[BSD license](https://github.com/cubicdaiya/dtl/blob/master/COPYING).