Working with the config system
This document is intended for the authors of front-end clients.
xi-core
supports two mechanisms for handling persistent user preferences:
file-based and RPC-based (unmanaged). The file-based system is similar to that employed by
editors such as vim or Sublime Text; the RPC system is an alternative provided
for front-ends that wish to manage preferences on their own, or which cannot
support the file-based approach for platform reasons.
Clients which wish to use the file-based mechanism must explicitly opt-in by
including "config_dir": "$CONFIG_PATH"
in the params of the client_started
RPC,
where $CONFIG_PATH
is a path to a directory that will contain config files.
If using file-based config, xi-core
is responsible for watching those files
for changes, and will reload them appropriately.
File based config
xi-core
user config files are TOML files with
the .xiconfig
file extension. These files should exist in the root of the
user’s config directory. General preferences should be in a file named
preferences.xiconfig
, and language/syntax-specifc preferences should be given
the lowercase language name and the extension, for instance yaml.xiconfig
,
cpp.xiconfig
, and markdown.xiconfig
. Each file represents to what we call
a ‘config domain’, and the keys & values in the file constitute a ‘config
table’.
Config table format
Internally, all config tables are represented as JSON objects; all keys must be strings, and values must be objects, arrays, strings, or bools. Null values are not allowed.
When we load config files, we convert from TOML to JSON. The TOML ‘Datetime’ type is converted to a string.
Defaults
xi-core
includes a number of default config tables. In the source, these are
included as TOML
files (see
/rust/core-lib/assets/).
At compile time these are baked into the binary.
Config Domains
We refer to a particular group of config settings as a ‘config domain’. A domain may contain both default and user settings; user settings always override default settings for that given domain. Not all domains are persistent; for instance there may be a ‘user override’ domain for each active view, which stores settings specific to that view, and which is forgotten when that view is closed. This would be used, for instance, if the user has manually changed an individual view’s indentation settings.
Generating the view’s config table
Each view has its own config table, generated by merging the tables from various domains relevant to that view. Those tables are merged in a predetermined order; here in order of application (reverse priority):
- General config, including platform-specific overrides
- Syntax config
- User Overrides
When a config changes, either because a file is modified or an RPC is received,
then the config_changed
notification is sent to the client for each affected
view. If a change does not affect any views (for instance if rust.xiconfig
is
modified, but no Rust files are open) then no notification is sent.
RPC based config
Configs can be set or modified with the modify_user_config
RPC notification.
This notification has two paramaters, domain
and changes
. domain
can be
either the string "general"
, for the general user preferences domain, or else
an object with a single key, which should be either "syntax"
or
"user_override"
; the value in this case should be either a syntax name
(identical to the file names, minus the extension, used for file-based config)
or a view identifier.
Note it is an error to modify one of the ‘general’ or ‘syntax’ domains over RPC if the client has opted into the file-based config mechanism.
Examples
If a client is not opting in to file-based config (and is persisting preferences through another mechanism) than it should send those preferences immediately after launch, and before opening any views:
// send the user's general preferences
{
"method": "modify_user_config",
"params": {
"domain": "general",
"changes": {
"font_face": "Monaco",
"font_size": 18.0,
"translate_tabs_to_spaces": false
}
}
}
// and their markdown-specific preferences
{
"method": "modify_user_config",
"params": {
"domain": { "syntax": "markdown" },
"changes": {
"font_face": "Chalkboard"
}
}
}
Regardless of whether the file-based config system is being used, non-persistent view-specific settings can only be modified over RPC. For instance, if the same user as above wanted to specifically use four-space indentation for a view, the client would send the following RPC:
// send the user's general preferences
{
"method": "modify_user_config",
"params": {
"domain": { "user_override": "view-id-1" },
"changes": {
"translate_tabs_to_spaces": true,
"tab_size": 4
}
}
}
Null values
Null values are allowed in tables sent over RPC; these are interpreted as the deletion of any existing value for that key in the given domain.
Validation
Whenever a config table is modified, either through the RPC mechanism or by editing a file, the updated table is passed through a validator. If the table is invalid (for instance if it contains unrecognized keys) then an error is reported and the new table is ignored.