Geany Highlighting

Robert Crowther Jan 2022
Last Modified: Feb 2023

The aim is to configure a custom highlighter for the Geany IDE.

Note that,

How Geany highlights

Underneath, Geany uses a component Scintilla to display the code area. For highlighting, it also uses Exuberant cTags. This is a powerful combination. Both components can build custom parsers for a language. The Exuberant cTags parsers can use regex matching, and both parsers can use character stepping. If you look at a few, parsers are not too complex. And naturally, they can can do more, much more, than a regex.

But you must build these parsers, and that must be done in C and C++. As far as I am concerned, any so‐called ‘configuration’ in C/C++ goes beyond configuration into ‘code extension’, and is not worth my or your time. That’s right, whatever the effect, it is a waste of time. And for sure it is beyond the reach of this article.

…so the limitations on configuration of highlighting

Which are severe. Now, documentation seems to suggest you can configure a custom highlighter, and I give basic instructions below. However, you must build from an existing parser. Not only that, but one of the basics of constructing a highlighter is to match keywords. I failed to do that by either method I tried. So you are limited to taking an existing language definition, called in Geany a ‘filetype’, and subtracting anything you do not need. No way I found to add anything. And, on top of that, there is no ‘base’ parser you can construct something rough from. Be warned.

Howto

Where are the files?

Geany has a wonderful feature, it can report and edit it’s own configuration. Open Geany itself, then,

Help > Debug Messages

On my system, Geany 1.32, I get

22:04:52: Geany INFO		: System data dir: /usr/share/geany
22:04:52: Geany INFO		: User config dir: /home/rob/.config/geany

Neat, huh?

Decide if you want to try the job

You need a builtin language definition that matches using the same kind of matches as you would like, and runs roughly the same matches.

One way to poke at this—see if you can find a likely lexer—is to load into the Geany GUI a file of the source you want to highlight, then try various filetypes,

Document > Set Filetype > …

I tried this for one kind of file and settled on the Ruby parser, because it highlighted comments (having comments highlighted is not an afterthought, it is a first move). When I started, I han’t thought of Ruby.

Read the discussion above.

Look at source

There’s no way round this. Contrary to most web writing, I think this is a system fail. But for Geany, you must. So download a copy of Geany source, Look at the following files to see how the parsers work.

The main place of interest is the the Scintilla parser. It contains the parse for a language,

geany-master/scintilla/lexers

Another place of interest is the cTag lexer. If it exists, that contains a second parser,

geany/ctags/parsers

Finally, the highlightmapping header contains sections for each language and the tokens the parsers use. It will not tell you how the tokens are matched, but is a useful short list,

geany-master/src/highlightingmappings.h

Setup a new highlighting configuration

Copy the chosen base definition file,

cp /usr/share/geany/filedefs/filetypes.xxxxx /home/rob/.config/geany/filedefs

Renane. Note closely, the original filetype definition may be of the form, ‘filetypes.xxx’ but the new filedef must be of the custom form ‘filetypes.xxx.conf’,

mv /home/rob/.config/geany/filedefs/filetypes.markdown /home/rob/.config/geany/filedefs/filetypes.garbage.conf

Edit the file. Modify the mime type entry,

# MIME type
mime_type=text/x-garbage

Add a lexer entry to [settings], because the lexer may default and we want to use to the lexer we decided on,

lexer_filetype=ruby

Now edit the filetype extensions file. Surprisingly, this does not need the commandline, though you could edit like that (/usr/share/geany/filetype_extensions.conf). But it can be edited in the Geany GUI,

Tools > Configuration Files > filetype_extensions.conf

Add this. Note that I advise adding an extension for sure. Recognition of mimetype for custom configurion did not work for me,

Garbage=*.garbage;

Also edit the groups section,

Script=...;Garbage;

Shut down all instances of Geany then restart. Files with the right extension should now open as that filetype in Geany. Check in the Geany menus,

Document > set file type ...

Any problems, try looking at,

Help > Debug Messages

Sadly, on my system, these custom definitions do not recognise mimetypes. Or perhaps, since configuration states mimetypes, I should say custom configuration does not honour the mimetype attribute.

Edit the new file for highlighting

You probably want to edit or delete the contents of [build‐menu]. What these attributes do is obvious. And, tempting as it is, ignore [keywords].

Remove unwanted matches

Move down the main lexer, tooking for unwanted match chode. When you find one, comment out the token/identifier from the filetype file you have created e.g.

#preproc=preprocessor
#postproc=preprocessor

Geany has a tool to help—‐you can reload configuration without restarting the Geany itself,

Tools > ReloadCconfiguration

Crunchtime

Give it a go. Try loading a file with the extension into Geany, see if you got the highlighting you requested. You do not need to restart Geany. Try,

Tools > Reload Configuration

But you could reassure yourself by a full restart.

Notes on Editing

Exuberant Ctags, background

A misleading name, it now works on many programming languages. Made for the vi editor. Tags are a bunch of prebuilt matches. The matches are stepping parse or regex, include tricks that are useful in programming langiuage lexing, and can handle some scope/context.

Geany uses this code, though not fully. Reputedly Geany accepts tag files in the cTags format.

The limitations on highlighting, documentation

…by configuration. I’m warning you before you get involved, I quote the manual at length,

Custom filetypes

… Custom filetypes are not as powerful as built‐in filetypes, but support for the following has been implemented:

Note that regex matching from configuration is not possible. Note also that keyword support is lacking. And note that on custom configuration I failed to get tag files to work.

Keywords

Some parsers offer keyword recognition. This is supported by Geany code. Unfortunately, far as I can tell, overriding of keyword recognition in custom filetypes is not possible.

Workplace symbols

On the left is a menu of symbol names. Frankly, I didn’t get far enough to discover how these are differentiated from programming language identifiers, or where the parsing is located. And documentation does not exist.

Global tag Files

Tags provide autocompletion and calltips. If they gave me keywords, I’d be interested. But they do not. So they are not a primary interest for this article.

Global tag files are for keyword matching of builtin functions. Tags can be simple, as from the HTML tags file, where they match HTML escapes. But they can include return and argument detail, like a function signature.

Global tag files, if you look at the builtins, are big. The keywords include the basic language keywords but also keywords from common libraries. Here’s some examples from the Python tags file,

ReturnÌ1Í(stmt)
WhileÌ1Í(stmt)
ClassÌ1Í(SymbolTable)
pprintÌ128Íself, objectÎPrettyPrinter
namedtupleÌ128Ítypename, field_names, verbose=False, rename=False

Geany can read tagfiles in several formats, including cTags. The prefered format is the Geany‐specific Tagmanager format. Geany has functionality to generate these files in this format.

Most important is this note on custom filetypes,

[on global tags files] sharing the tag_parser filetype’s namespace.

That’s right, sharing the same namespace e.g. if using the Ruby parser, you can extend the Ruby tags. But not replace them, And if you build a new language definition, as far as I know you inherit the original tags. You could switch tag_parser, but then you would get a different set of keywords.

Howto

I’m not going to write much about this. After several attempts, I got a tag file to work. This further encouraged me to believe tag files will not help build a new language definition. Indeed, the lack of implementation is another loss from the Geany implementation of configurable language definition.

Create a new tags file. Name is important, it is form ‘name.lang_ext.tags’. The first ‘name’ section is a namespace, so many tag files can be joined. So the name can be anything, but should be about your set of tags. If the base set is called ‘std.garbage.tags’, then your set could be called ‘robs.garbage.tags’, or ‘mutlitask.garbage.tags’. The second field is what it says, a lang_ext. If that sounds unlikely, well, it probably suited the Geany codebase someplace. For example, if the base language is Ruby then the second field could be ‘rb’. Let’s extend the Ruby parser with some ‘garbage’ tags,

nano /home/rob/.config/geany/tags/garbage.rb.tags

Start with this line,

# format=tagmanager

You can start with the next lines, but I said I’m not going to talk about this subject much,

# format=pipe
# format=ctags

Now add some basic matches e.g.

Robert
&
whileBored
!!!

then save.

Open a Ruby file and you should find auto‐complete popups and so forth to help you add ‘whileBored’ commands. For what that is worth.

The common file

All ‘filetype’ files are based in one common file. This is not a base theme, so much as it configures the icon for folding, colours for highlighting, and so forth. Not many would be interested in changing these functional‐related configurations,

cat /usr/share/geany/filedefs/filetypes.common

Overall config

On my system, doesn’t exist. From documentation is located at,

/usr/share/geany/geany.conf

Snippets

These are a Geany thing, not Scintilla or Exuberant Ctags. They let you do some text completion. From files, they are not widely used, usually in heavy languages. My list includes, C C++ Java, PHP, Javascript, C#, Vala, ActionScript, Python, Erlang… Here is the Vala entry,

[Vala]
if=if (%cursor%)%block_cursor%
else=else%block_cursor%
for=for (i = 0; i < %cursor%; i++)%block_cursor%
while=while (%cursor%)%block_cursor%
do=do\n{\n\t%cursor%\n} while (%cursor%)\n
switch=switch (%cursor%)%brace_open%case %cursor%:\n\t\t%cursor%\n\t\tbreak;\n\tdefault:\n\t\t%cursor%\n%brace_close%
try=try%block%\ncatch (%cursor%)%block_cursor%

Why is this difficult?

Partly because Geany uses the Scintilla gadget for rendering, which is an uncommitted piece of code with a variable configuration. And Geany uses Exuberant cTags for some wide‐supported, accurate, and very fast custom highlighting. And partly because, together, this means Geany has two parsing systems. Both of which are written in C or C++. On top of that, Geany has little configuration available for highlighting or themes.

Thoughts

Geany is better‐speced than basic text‐editors, and powered by the Scintilla widget it is capable and fast. But Geaney’s configuration seems strung out. On the one had, it is highly configurable for general settings, a detailed piece of work. The ability to set configuration through Geany itself is a joy. On the other hand, the highlighting configuration has no generality whatsoever, is spread across several files, and documentation is scattered.

Maybe this is because Geany was constructed for speed. That’s what the documentation claims. But I see signs the coders built Geany from what was available. Documentation seems to have been built as though Geany was intended to do what it can not. Files in source suggest the coders were aware of these limitations, and even that they intended to work on them. My conclusion is that they did not. Geany is good enough, and there was no call for the extra work to be done. Or if there was a call, that call was never answered.

References

Geany Manual,

https://www.geany.org/manual

Exuberant cTags site,

http://ctags.sourceforge.net/

Add a new language to Exuberant Ctags. This is what you are doing with Geany tag files,

http://ctags.sourceforge.net/EXTENDING.html

Request for Geany to handle Restructured Text. In my version this is answered, but the last post in this ticket tells you everything,

https://github.com/geany/geany/issues/300