📜 ⬆️ ⬇️

Developing a cross-platform editor with syntax highlighting based on wxStyledTextCtrl

Good time of the day Habrazhiteli!


I want to tell you about my experience in developing an editor with syntax highlighting or code editor. This is a custom language editor. The essence of this post is not in the detailed description of the development process - I will focus only on the most interesting points, poorly consecrated or generally omitted from the documentation.

Scintilla is one of the most well-known freeware components for creating code editors, wxStyledTextCtrl its wrapper in the wxWidgets .
Scintilla supports most of the programming languages ​​used, so adding a new lexer is often done by analogy with the closest language. In more detail this process is consecrated here . This method is very simple to implement, but it has one major drawback - you have to include all the scintilla code in your project. For small and relatively stable projects this is not very significant, but for cumbersome and actively developing it causes a lot of problems. Support is even more complicated if you use not the Scintilla component itself, but its wrapper, for example, wxStyledTextCtrl . An alternative is the so-called implementation of the lexer in the container .

Integration of lexer into scintilla

First, a few words about a simple way. In general, to create your lexer you need to write only one function that will be responsible for coloring the changed part of the code:
static void ColouriseDoc (unsigned int startPos, int length, int initStyle, WordList *keywordlists[], Accessor &styler) . To color or stylize the specified range means to specify a style for each character. It does not worth the component itself optimizes the drawing of the text, and also ensures that the range [startPos, startPos + length - 1] is an integer number of lines, which facilitates the implementation of the parser.

Implementing a lexer in a container

I started by creating a class - a descendant of wxStyledTextCtrl as indicated in the example . As in the previous method, the lexer requires only one function, which in this case will be called on the EVT_STC_STYLENEEDED event.
Experience has shown that if the size of the edited files does not exceed 50-60 lines, then you can safely ignore the range transmitted by the event and stylize the entire contents of the file, which greatly simplifies the lexer. If the number of lines is more, then it is better to update the style not from the line GetEndStyled() , but from the previous line. As in the previous method, the event is called for an integer number of lines.
')
What forgot to mention in the manual

One of the difficulties I encountered at this stage is that the event is triggered too often. A small study of the behavior of the component in the debugger showed that after styling the range [n, m], the last stylized symbol is not m at all. Most often it was the previous character, sometimes the character is 2-3 characters away from it. There were several reasons:

I implemented a string parser and in order to avoid unnecessary event calls, I had to store the index of the last stylized character.

SetStyling via SetStyleBytes

In almost all examples, the use of style is indicated as a sequence of commands:
StartStyling(...);
SetStyling(...);

in this case, a character-by-character style is used, which is completely inefficient and it is better to use this one, which allows you to transfer to the component a previously prepared vector of style indices
StartStyling(...);
SetStyleBytes(...);

To be honest, I never found cases in which the SetStyling function should have been used.

Russian letters

In order for styles to correctly begin to use words with Russian letters, it is necessary to correctly calculate the index of the last character by sequentially performing pos = PositionAfter(pos) . A similar method of calculating the position should be used to get a part of the word when invoking the automatic substitution pos = PositionBefore(pos) .

Hiding part of the code (folding)

The implementation of this function did not cause any special problems, and there were no gaps in the documentation. I can only say that folding is based on the levels of the SetFoldLevel(...) lines, which, for example, I set right in the lexer (this is why the line-based parser is more convenient).

Automatic substitution function (autocomplete)

Although the function is relatively detailed in the documentation, some things are missing. First of all, it turned out that the standard feature of the AutoCompShow(...) component does not filter by the entered part of the word, but only by positioning. Then the AutoCompSelect() function which should substitute the user-selected word was useless, because it often caused an exception for no apparent reason. Instead, it is better to set the flag AutoCompSetChooseSingle ().

Call Tips

Another equally wxStyledTextCtrl function of wxStyledTextCtrl is the display of the CallTipShow () call hints. Working with it is similar to working with the AutoCompShow() substitution function. It should be noted that they are combined simultaneously.

Given the above features, wxStyledTextCtrl allows wxStyledTextCtrl to implement a fairly complex lexer in a container.

Source: https://habr.com/ru/post/129019/


All Articles