Currently, all CodeMirror preferences (except colorblind mode) are saved under a single user option, `codemirror-preferences`. It is a JSON blob with the keys as the feature names (i.e. `codeFolding`, `lint`) and the values being either `1` or `0`.
{T419337} takes it a step further and introduces "mode IDs" so the preferences can be applied to specific environments (though that's likely to remain only wikitext and non-wikitext). The storage for the values is still an integer, meaning we have a small footprint with still plenty of room left for more features to be added. It does however mean we can't then also use the value to store anything other than whether the feature is off, or where it is enabled.
We need to expand the [[ https://doc.wikimedia.org/CodeMirror/master/js/js/CodeMirrorPreferences.html | CodeMirrorPreferences ]] logic and storage schema to support something more than off/on values. For example, we'll need at least integers great than 1 to for theming (T163533) and linter options (T408729).
Preventing `user_properties` bloat (T286270, T54777) as I understand it is more about row count, but at scale we want to keep the storage footprint reasonable as well. In production it is a MariaDB `BLOB`, which gives us 64KB… so I think we can easily squeeze in whatever we need into the existing `codemirror-preferences`, and have it be backwards-compatible.
Keeping on with T419337, we'll want the same content model (aka mode) granularity too. Something like:
**Old schema**
Boolean-only, applies to all editing environments.
```lang=json
{
"featureA": 0,
"featureB": 1,
"featureC": 0
}
```
**After T419337**
Still keyed by feature, but with an integer "mode ID"Same as the valuebefore, allowing it to apply to specific editing environments.
```lang=json
{
"featureA": 0,
"featureB": 3,
"featureC": 2
}
```but now with a separate user option `codemirror-preferences-code` that applies to non-wikitext.
**New schema**
Keyed by feature, values are either a mode ID (justSame as before), or it's an object with mode IDs as keys and the actual "preference value" as the value.but in addition to `0` / `1` (disabled or enabled), `0` and `1` still mean off/on as alwaysa string value can be provided which can take any form, anything else means "on" but with the provided valuesuch as serialized JSON.
```lang=json
{
"featureA": 0,
"featureB": {
"3": "custom data for mode ID 3"
}",
"featureC": {
"2": 1,
"3": "different data for mode ID 3"
}"{\"foo\":1,\"bar\":2}"
}
```
In the future, we might have things like custom lint rules, custom highlighting of characters (T383285), so I'm trying to think ahead. So long as it's JSON-serializable, and we don't store any key where the value matches the same as the default, I think this will suffice.
With this level of complexity, it might warrant moving the storage logic to the server with an internal `action=codemirror-preferences` API. All it would need to be given is a page title for context, the feature name, and the preferred value. An `action=query&meta=codemirror-preferences` endpoint might make sense, too. We could pre-supply the preference data via JS config var from Hooks.php where we know we will need it, or lazy-load it from the API as needed.