Tutorial
The completed example code for this tutorial can be found in the git repository.
To use TreeSitterHighlighter, you need to provide three pieces of information:
- The language you would like to parse in form of a tree-sitter language.
- A highlight query which extracts the nodes to highlight from the syntax tree of your source file.
- A format map that maps highlight query capture names to their highlighting format.
Let's start with the language. In most cases one will be able to make use of one of the available tree-sitter parsers. For this tutorial, let's use tree-sitter-cpp. To make use of it in our example we declare the tree_sitter_cpp
function it provides and to which we will link later:
// Declare the 'tree_sitter_cpp' function provided by the 'tree-sitter-cpp' parser library.
extern "C" {
const TSLanguage* tree_sitter_cpp(void);
}
Next up is the highlighting query. Many parsers come with a query in their repository (usually under queries/highlights.scm
) which provides a good starting point. For this tutorial we will use a custom (minimal) query:
// A minimal highlights query for C++
// See https://tree-sitter.github.io/tree-sitter/using-parsers/queries/1-syntax.html for query syntax.
std::string query = R"(
["extern" "catch" "class" "delete" "namespace" "new" "private" "protected" "public"
"throw" "try" "requires" "virtual" "break" "case" "const" "continue" "do" "else"
"enum" "for" "if" "inline" "return" "static" "struct" "switch" "typedef" "while" ] @keyword
["#define" "#elif" "#else" "#endif" "#if" "#ifdef" "#ifndef" "#include" ] @preproc
(preproc_directive) @preproc
(number_literal) @number
(true) @bool
(false) @bool
(string_literal) @string.std
(raw_string_literal) @string.raw
(type_identifier) @type
(primitive_type) @type
(sized_type_specifier) @type
(field_identifier) @variable
(identifier) @variable
(call_expression
function: (identifier) @function)
(call_expression
function: (field_expression
field: (field_identifier) @function))
(function_declarator
declarator: (identifier) @function)
(comment) @comment
)";
Most statements in this query simply match a syntax tree node and assign it a capture name, e.g: (tree_node) @capture_name
.
For information on how to write a highlighting query, please refer to the tree-sitter documentation.
Finally, we need to specify how these captures are highlighted.
For this, we define a map from (some of) the capture_names
above to a QTextCharFormat with which the capture should be highlighted.
In this example we again use a minimal map:
// A format map from capture names to the appropriate QTextCharFormat format
FormatMap format_map() {
FormatMap map;
map[""].setForeground(QColor("black"));
map["keyword"].setForeground(QColor("#b055cc"));
map["preproc"].setForeground(QColor("purple"));
map["number"].setFontUnderline(true);
map["bool"].setFontWeight(QFont::Bold);
map["string"].setForeground(QColor("green"));
map["string.raw"].setForeground(QColor("dark green"));
map["type"].setForeground(QColor("#c08000"));
map["variable"].setForeground(QColor("#ad1d4d"));
map["function"].setForeground(QColor("blue"));
map["comment"].setForeground(QColor("gray"));
map["comment"].setFontItalic(true);
return map;
}
To use TreeSitterHighlighter we first create a QPlainTextEdit instance to which it will be connected:
QPlainTextEdit* editor = new QPlainTextEdit();
Now we create a TreeSitterHighlighter instance using the three ingredients prepared above and connect it do the editor's document:
TreeSitterHighlighter* highlighter = new TreeSitterHighlighter(tree_sitter_cpp(), query, format_map(), editor->document());
That's it, TreeSitterHighlighter will now automatically rehighlight every time the editor's text changes.
The complete example can be obtained, built and run as follows:
git clone https://github.com/Bzero/Qt-TreeSitterHighlighter --recurse-submodules
cd Qt-TreeSitterHighlighter
cmake . -B build
cmake --build build
./build/tree-sitter-highlighter-example