Autocomplete
March 12, 2026 ยท View on GitHub
The text editor provides an optional autocomplete framework. Once configured, the editor takes care of activation events (triggering), state tracking, visualization and insertion of suggestions with full undo/redo. The application is responsible for providing the list of suggestions through a callback or the API. This allows simple implementations to provide suggestions in realtime and allows other implementations to do things asynchronously like reaching out to a language server. Autocomplete can't be triggered when multiple cursors are active as this causes a mess. Try it in Visual Studio Code if you want to see what I mean.

To activate the feature, the app must provide a configuration like:
TextEditor::AutoCompleteConfig config;
config.callback = [this](TextEditor::AutoCompleteState& state) {
....
};
editor.SetAutoCompleteConfig(&config);
Deactivation can be achieved by passing nullptr to SetAutoCompleteConfig. The TextEditor::AutoCompleteConfig class contains all the configuration options and is defined as (please note defaults):
class AutoCompleteConfig {
public:
// specifies whether typing by the user triggers autocomplete
bool triggerOnTyping = true;
// specifies whether the specified shortcut triggers autocomplete
bool triggerOnShortcut = true;
// specifies whether typing (or shortcut) in comments or strings triggers autocomplete
bool triggerInComments = false;
bool triggerInStrings = false;
// manual trigger key sequence (default is Ctrl+space on all platforms, even MacOS)
// remember Dear ImGui reverses Ctrl and Command on MacOS
#if __APPLE__
ImGuiKeyChord triggerShortcut = ImGuiMod_Super | ImGuiKey_Space;
#else
ImGuiKeyChord triggerShortcut = ImGuiMod_Ctrl | ImGuiKey_Space;
#endif
// see if single suggestions are automatically inserted
// this only works when triggered manually
bool autoInsertSingleSuggestions = false;
// delay in milliseconds between autocomplete trigger and suggestions popup
std::chrono::milliseconds triggerDelay{200};
// text label used when no suggestions are available (this allows for internationalization)
std::string noSuggestionsLabel = "No suggestions";
// called when autocomplete is configured, active and the editor needs an updated suggestions list
// callback must populate and order suggestions in state object
// suggestion list is not cleared by editor between callbacks
// callback is called during the rendering process (so don't take too long)
// if it takes too long, applications should do search in separate thread and
// use API to report results (see SetAutoCompleteSuggestions)
std::function<void(AutoCompleteState&)> callback;
// optional opaque void* that must be managed externally but passed to callback
void* userData = nullptr;
};
When the callback is activated, a TextEditor::AutoCompleteState object is passed informing the app about the context and providing space to return suggestions. It is defined as:
class AutoCompleteState {
public:
// current context (strings = UTF-8, columns = Nth visible column and indices = Nth codepoint)
// to understand the difference between column and index, think like a tab :-)
std::string searchTerm;
size_t line;
size_t searchTermStartColumn;
size_t searchTermStartIndex;
size_t searchTermEndColumn;
size_t searchTermEndIndex;
bool inIdentifier;
bool inNumber;
bool inComment;
bool inString;
// currently selected language (could be nullptr if no language is selected)
const Language* language;
// opaque void* provided by app when autocomplete was setup
void* userData;
// auto complete suggestions te be provided by app callback (the app is responsible for sorting)
// the editor does not automatically include language specific keywords or identifiers in the suggestion list
// this is left to the application so it can be context specific in case a language server is used
// a pointer to the current language definition is provided so callbacks have easy access
std::vector<std::string> suggestions;
};
The editor also comes with a Trie class that implements fuzzy searching and the example app shows how it can be used. Given that this is a primitive, poor-man's solution, more sophisticated solutions probably required external language engines/services that are beyond the scope of this editor. With the provided API however, connections to external capabilities can be established and context-sensitive suggestions can be provided based on the most advanced algorithms (even good-old AI slop :-).
Below is a quick snippet that shows how to use the Trie class to implement a poor-man's autocomplete without using a language server. This snippet was taken from the example application so can see it in context.
void Editor::setAutocompleteMode(bool flag) {
// see we are turning autocomplete on or off
if (flag) {
// rebuild word list
buildAutocompleteTrie();
// setup autocomplete by submitting a new configuration
TextEditor::AutoCompleteConfig config;
config.callback = [this](TextEditor::AutoCompleteState& state) {
trie.findSuggestions(state.suggestions, state.searchTerm);
};
editor.SetAutoCompleteConfig(&config);
// enable change tracking
// we don't track every keystroke, callbacks can be delayed up to 3000 milliseconds
// if you want live tracking, change 3000 to 0 (performance hit will be minimal for small documents)
editor.SetChangeCallback([this]() {
buildAutocompleteTrie();
}, 3000);
} else {
// disable autocomplete and change tracking
editor.SetAutoCompleteConfig(nullptr);
editor.SetChangeCallback(nullptr);
}
}
void Editor::buildAutocompleteTrie() {
// empty list first
trie.clear();
// add language words (if required)
auto language = editor.GetLanguage();
if (language) {
for (auto& word : language->keywords) { trie.insert(word); }
for (auto& word : language->declarations) { trie.insert(word); }
for (auto& word : language->identifiers) { trie.insert(word); }
}
// add all identifiers in current document
editor.IterateIdentifiers([this](const std::string& identifier) {
trie.insert(identifier);
});
}