GtkSourceView: styles

Current Situation

GtkSourceViewTagStyles are stored in the GtkSourceDefaultStyleScheme->styles hash table using the translatable name as a key. These GtkSourceTagStyles are the only ones available (you need to create a new GtkSourceStyleScehme to add new ones).

When gtk_source_buffer_set_language() is called, the GtkSourceBuffer gets the GtkSourceTags from the GtkSourceLanguage with gtk_source_language_get_tags() and stores them in the GtkSourceTagTable. The list is created by the GtkSourceLanguage by asking the parser (without passing any GtkSourceEngine) to read the .lang file and create the GtkSourceTags and associate them to the GtkSourceTagStyles.

To associate a GtkSourceTag to a GtkSourceTagStyle, gtk_source_language_get_tag_style() is called. This function looks first in tag_id_to_style (user defined), then in tag_id_to_style_name and finally, if no match is found, normal_style is used.

Gedit, to visualize the GtkSourceTag to customize, uses gtk_source_language_get_tags() and shows the "name" property which holds the translated name of the GtkSourceTag. The "id" property is instead used to store the values in GConf (/gedit-2/preferences/syntax_highlighting/{language_id}/{tag_id})

Problems:

  • GtkSourceTagTable and GtkTextTagTable use the "name" property to index their contents, which can lead to problems since "name" is translated, for instance if two names are translated in the same way. Another issue with the use of "name" can be seen in perl.lang: there are three type of string which should refer to the same style: the current workaround is to use different names (String1, String2, String3), but it doesn't work well for the new engine since the issue is more frequent (e.g. html:comment, js:comment)

  • .lang files are reread each time the list of tags is get
  • using styles is awkward, thus no one uses GtkSourceStyleScheme

  • the default StyleScheme has hardcoded colors

  • the current system doesn't work well for nested languages

Proposed Changes

Each .lang file has some style tags, each style has an identifier, a translatable name and an optional "default-to" attribute. The real styles (i.e. color, bold and so on) are stored in a theme file and handled using GtkSourceTheme. To allow the user to change only single styles (and not just the theme) the programmer should implement the interface GtkSourceStyleConfigurator.

xml.lang:

<styles>
  <style id="comment" _name="Comment" />
  <style id="entity" _name="Entity" default-to="escape" />
</styles>

When the library needs the style named "entity" the following operations are executed until a style is found:

If a "default-to" attribute is not set we use the value of the "id" attribute, so, in the example, if "xml:comment" does not exist in the theme the library will use "def:comment".

Some languages, such as C++, use the styles from another language, to do so the "use" attribute is required.

cpp.lang:

<styles use="c" />

If the library needs the style for C++ keywords the following operations are executed:

Styles of an included language (such as Javascript in HTML) are not configurable from the container language.

If there is a style that cannot be mapped to a default style and there is not a "default-to" attribute then we should just use the normal style.

<styles>
  <style id="my-unknown-style" _name="..." />
</styles>

TODO: In c.lang we have:

<context ref="def:decimal" />

and in def.lang:

<context id="decimal" style-ref="decimal">
  <match>...</match>
</context>

So the user cannot configure the style for the numbers in C, because "def" will be considered an external language, such as Javascript in HTML. I have no clue how to resolve this.

The idea behind the new API is that the programer should never use directly GtkSourceTag, we could even make this class private to gtksourceview (note that GtkSyntaxTag and GtkPatternTag need to be private as they are only used by the old engine.)

These are the steps required to implement a configuration window for single styles:

  • ask to the GtkSourceLanguagesManager the list of the languages

  • when a language is selected the list of styles is requested to the GtkSourceLanguage

  • when an element is selected the GtkSourceStyle is fetched with gtk_source_language_get_style (GtkSourceLanguage *, const gchar *id, gboolean *is_default). is_default replaces the is_default member of GtkSourceTagStyle used in the current version. The restore button is enabled only if is_default is false

  • gtk_source_language_set_style (language, id, style) sets a style, this function:
  • the restore button restores the original style, when it is pressed gtk_source_language_restore() is called

Theme files

This is an example of a theme file:

<styles>
  <style id="def:normal">
  </style>
  <style id="def:comment">
    <bold>true</bold>
    <foreground>#0000FF</foreground>
  </style>
  <style id="c:comment" extends="def:comment">
    <italic>true</italic>
  </style>
</styles>

Because of the "extends" attribute the "c:comment" style in the previous example is equivalent to:

<style id="c:comment">
  <bold>true</bold>
  <foreground>#0000FF</foreground>
  <italic>true</italic>
</style>

Proposed API

gtksourcestyle.h

/* GtkSourceStyle is different from the old GtkSourceTagStyle because it does not
 * contain the "is_default" variable. As we break compatibility I prefer to change
 * the name removing "Tag" because the programmer will not have to handle directly
 * tags anymore. */
typedef struct _GtkSourceStyle GtkSourceStyle;

typedef enum {
        GTK_SOURCE_STYLE_USE_BACKGROUND = 1 << 0,
        GTK_SOURCE_STYLE_USE_FOREGROUND = 1 << 1
} GtkSourceStyleMask;

struct _GtkSourceStyle {
        GtkSourceStyleMask      mask;

        GdkColor                foreground;
        GdkColor                background;

        gboolean                italic;
        gboolean                bold;
        gboolean                underline;
        gboolean                strikethrough;

        /* Reserved for future expansion. */
        guint8                  reserved[16];
};

/* PaoloMaggi suggests to use something similar to PangoAttrList to represent a style. */

gtksourcelanguagesmanager.h

/* Now we have a gtk_source_language_[s|g]et_theme, but I think we do not need a different theme for
 * every language. */
GtkSourceStyleTheme         *gtk_source_languages_manager_get_style_theme               (GtkSourceLanguagesManager *lm);
void                     gtk_source_languages_manager_set_style_theme           (GtkSourceLanguagesManager *lm,
                                                                                 GtkSourceStyleTheme       *theme);

GtkSourceStyleConfigurator      *gtk_source_languages_manager_get_style_configurator    (GtkSourceLanguagesManager  *lm);
void                             gtk_source_languages_manager_set_style_configurator    (GtkSourceLanguagesManager  *lm,
                                                                                         GtkSourceStyleConfigurator *configurator);

/* Returns the list of the ids of the available themes. */
const GSList                    *gtk_source_languages_manager_get_available_themes
                                                                                (GtkSourceLanguagesManager *sm);

/* Returns the translated name of the theme. */
gchar                           *gtk_source_languages_manager_get_theme_name
                                                                                (GtkSourceLanguagesManager *sm,
                                                                                 const gchar                  *theme_id);

/* Returns the list of directory where to search the themes, this value can be set
 * as a construct time property. */
const GSList                    *gtk_source_languages_manager_get_theme_files_dirs
                                                                                (GtkSourceLanguagesManager *sm);

gtksourcelanguage.h

/* GSList of const gchar pointers. */
GSList *gtk_source_language_get_style_ids (const GtkSourceLanguage *language);

/* Returns the (translated) name associated to style_id. */
const gchar *gtk_source_language_get_style_name (const GtkSourceLanguage *language,
                                                 const gchar             *style_id);

/* Returns a copy of the style for style_id.
 * is_default is TRUE if this style has not been customized by the user, so
 * the restore button in a configuration dialog can be disabled. */
GtkSourceStyle *gtk_source_language_get_style (const GtkSourceLanguage *language,
                                               const gchar            *style_id,
                                               gboolean               *is_default);

/* Sets the style associated to style_id.
 * If there is a GtkSourceStyleConfigurator its set_style method will
 * be called. If style in NULL then the style is restored to its default. */
gboolean gtk_source_language_set_style (GtkSourceLanguage    *language,
                                        const gchar          *style_id,
                                        const GtkSourceStyle *style);

gtksourcestyleconfigurator.h

/* To allow styles customization the programmer should implement the
 * interface GtkSourceStyleConfigurator. */
typedef struct _GtkSourceStyleConfiguratorClass  GtkSourceStyleConfiguratorClass;

struct _GtkSourceStyleConfiguratorClass
{
        GTypeInterface  base_iface;

        /* vtable */
        /* Returns NULL if the style has not be configured. */
        GtkSourceStyle  * (* get_style)         (GtkSourceStyleConfigurator *configurator,
                                                 const gchar                *language_id,
                                                 const gchar                *style_id);

        /* If style is NULL than the association is removed. */
        void            * (* set_style)         (GtkSourceStyleConfigurator *configurator,
                                                 const gchar                *language_id,
                                                 const gchar                *style_id,
                                                 const GtkSourceStyle       *style);

        /* Padding for future expansion */
        void (*_gtk_source_reserved1) (void);
        void (*_gtk_source_reserved2) (void);
        void (*_gtk_source_reserved3) (void);
        void (*_gtk_source_reserved4) (void);
};

Removed or modified functions

  • GtkSourceBuffer:

    • gtk_source_buffer_set_language(): after this function gedit calls the private function gedit_language_init_tag_styles(), this sets the styles, reading them from the configuration. This will not be needed anymore as the configuration will be automatically read during the creation of a language (using the GtkSourceStyleConfigurator.)

  • GtkSourceLanguage:

    • gtk_source_language_get_tag_default_style(): not needed.
    • gtk_source_language_get_tag_style(): rename to gtk_source_language_get_style. The style will be read from the configuration (using the GtkSourceStyleConfigurator.)

    • gtk_source_language_set_tag_style(): rename to gtk_source_language_set_style. The style will be stored in the configuration (using the GtkSourceStyleConfigurator.)

  • GtkSourceTag

    • Make it private.
  • GtkSourceTagTable

    • Make it private.

Thoughts / ideas / opinions

  • Jeroen Zwartepoorte: API looks good. The following are some ideas i have on the GUI part:
    • Language ComboBox in the preferences dialog has too many items. Use submenus in the combobox to make it easier to navigate (same as the View->Styles menu in Gedit).

  • MarcoBarisione: this is a problem in gedit, not in GtkSourceView, however PaoloMaggi wants to change the dialog when we will have GtkSourceView 2.

  • PanosLaganakos: What's the opinion on using simple CSS files to define the colour schemes, rather than XML? I think it's more design/artist oriented that way, not sure if it will add another dependency or the need to implement a custom CSS parser, but I'm just putting the idea out here. Personally, if wanted to write a colour scheme, I'd be glad if I was allowed to do so in CSS rather than XML.

Old thoughts / ideas / opinions

This section contains thoughts about the old proposal (without schemes representing themes.)

  • PaoloBorelli: I haven't thought this thoroughly, but here are some comments... My first thought is: do we really need to tackle this all at once? Some of the problems exposed here seem orthogonal to me and solving them all at once may introduce new api flaws while adressing the old ones. I think we should fix one thing at a time.

    • The more compelling issue seems the fact that tag names are used in the hash tables, which makes it impossible to have two kind of 'comment tags' in the same doc. I think that each GtkSourceTag should be modified to use a 'name' of the form lang_name::tag_name not transleted and used in the hash tables and a 'nick' property which is the translated name to be used in the UI. If it's not needed internally (I haven't looked closely) I think that 'id' (the escaped string to be used in GConf) should be dropped since it looks ad hoc for GConf and I think that the escaping should be left to the api consumer if he wants to use GConf. As a tangent some of the api in gtk_source_tag seems to me to dangerously using the gtk_ namespace instead of the more verbose but safer gtk_source_ namespace.

    • ".lang files are reread each time the list of tags is get": is this really a problem? keeping all the tags for each language around may become a memory usage problem
    • with regard to style schemes (and here I may *really* be on total crack): I think we should add the ability to 'compose' two schemes, so that modifications to the current theme could be obtained by composing the modifications with the default theme. Gedit would implemente a GtkSourceStyleSchemeGConf that builds a style scheme from gconf and then compose it with the default theme of gtksourceview. We could also provide a GtkSourceStyleSchemeFile utility object that creates a style scheme starting from a file description (xml, ini or whatever).

    • more later :)

PaoloMaggi: I have several comments about both the API and the XML format. BTW, as pbor suggested I think we should try to solve the problems step by step also because it seems we all have very few time and at the same time want to see the new_hl_engine out.

So, let us focus first on the XML format. I'd like to see language definition and style definitions somewhat uncoupled. So I think that we should not define styles in the .lang file. As we already have in the new .lang file format, the .lang file should contain only the list of styles used by the language (it could be eventually empty if only "default styles are used", i.e. we should be able to use for example "def::command" in style-ref). We can then create a second XML file containing the definition of the styles used by the default style scheme. We can have something like:

<styles>
  <style id="def::normal">
  </style>
  <style id="def::comment">
    <bold>true</bold>
    <foreground>#0000FF</foreground>
  </style>
  <style id="c::comment" ref="def:comment">
    <italic>true</italic>
  </style>
</styles>

Projects/GtkSourceView/Styles (last edited 2013-11-22 15:58:41 by WilliamJonMcCann)