RFC 23: Consolidate Configurations
Goals
- Organize and consolidate Dendron configurations into one place.
- Define sensible structure of configuration and create logical namespaces.
- Provide an easy way to view and edit configurations.
- Migrate old configuration to newly proposed structure.
- Update all relevant documentations for configuration.
Context
- Dendron has amassed a lot of new features recently, and it is becoming harder to keep track of configurable behaviors of these features.
- This complicates both the end user's experience and the process of introducing new configurations and modifying existing configurations.
Proposal
- Consolidate configurations divided in
dendron.yml
anddendron.code-workspace
so that it only exists in one place. - Organize the structure of these configurations so that they are logically grouped.
- Set a guideline for contributing new configurable behavior to Dendron so that the implementation and user experience is consistent in the future.
Execution plans
- This shall be done in multiple phases:
Phase I
- Introduce new configuration structure while keeping backwards compatibility
- In this phase, the existing configuration shall remain untouched, while a newly proposed structure is being implemented.
- Configurations that are planned to be deprecated will be notified during this phase.
- Configurations that are planned to be mapped to the new structure will be notified during this phase.
Phase II
- Migrate to the new configuration.
- Old configurations will be automatically migrated to the new structure.
- All relevant documentations will be updated and announced.
Details
Structure
-
Top level consists of only the groupings. It should not have single configurations.
-
Top level is grouped by areas certain features affect.
- rationale:
- Top level group should divide settings as roughly as possible in order to keep the top level namespace simple
- This also makes it easily navigatable (Private)
- Alternative to this is grouping features that affect certain areas.
- This makes the top level grouping very crowded and hard to navigate
- Some features do not affect multiple groups, leaving a lot of singles on the top level.
- rationale:
-
Each configuration key should affect one behavior
- rationale:
- Decoupling of configuration
- Clear cut expectation
- rationale:
-
If a setting affects multiple top level groups, they should be in each group rather than that setting being an object and having multiple keys to different groups.
-
rationale:
- We are grouping by areas certain features affect.
- Turning certain features into an object and giving them the areas as key is going against the initial grouping decision.
- This also forces us to pull out features that affect more than one area out of their namespaces, separating them from the group, adding complexity to the organization of the configuration.
-
example:
- prefer:
publishing: { ... mermaid: true ... } workspace: { ... mermaid: true ... }
- avoid:
mermaid: { publishing: true, workspace: true, }
-
-
Allow duplicates as long as they are in different groups. prefer this over pulling keys out of their namespaces and giving them the ability to affect multiple behavior
-
rationale:
-
example:
- prefer
publishing: { mermaid: true } workspace: { mermaid: true }
- avoid
// enable mermaid diagram rendering for both workspace and publishing mermaid: true
-
Presentation
- Individual configurations should be reachable with the smallest clicks possible
- Also promotes avoiding implementation of features that does too much.
Selecting the top level group exposes all possible settings under this group
- If there are duplicates in different groups, this should properly signalled to the user
- by documentation, or
- by way of presenting them a clearly visible message that this behavior is also configurable in another area.
- e.g.) as a convenience feature that is exposed to the end user
If a config that affects behavior in all possible groups is necessary, this should be done in a way that is agnostic to how the namespace is structure
- The configuration UI is a place where a user both edits and views configurations.
- It is important that the experience is smooth for both scenarios
- editing:
- Possible choices for configuration needs to be apparent at the time of editing
- Editing a configuration that can affect multiple areas should be intuitive
- Idea: Filtering of configurations
- e.g. Typing
mermaid
in the search bar displaysconfig.publishing.mermaid
andconfig.workspace.mermaid
- e.g. Typing
- Idea: Filtering of configurations
- viewing:
- When a user uses the configuration UI with the intent to view a certain group, every key in that group should be presented so that they could be identifiable at a glance.
- Even if the groups are deeply nested, the UI should present them in a flat manner unless it generates clutter
- Configurations should not be structured to be too deep in the first place in order to avoid this situation, and also promote a simpler/leaner implementation.
- Even if the groups are deeply nested, the UI should present them in a flat manner unless it generates clutter
- When a user uses the configuration UI with the intent to view a certain group, every key in that group should be presented so that they could be identifiable at a glance.
- The configuration should be presented in a way that is easily understandable.
- Configuration keys are defined to be a string without any spaces. The naming is selected to be as clear as possible, but some could use a bit more descriptive labeling.
- While the documentation should be sufficient when it comes to fully explaining what every configuration does, it would be better if the way we present the configurations can give a good enough explanation to what it does.
- While keeping it backwards compatible, we can add descriptions and labels that are plain English sentences to enable this in the configuration UI.
- With the introduction of JSON schema validation, we can also present configurations in a more descriptive way. (i.e. Have the validator display descriptions.)
Cleanup
1. Define configurable behaviors in the most common package.
- Type / enum definitions of configurable behaviors should be defined where the configuration is defined, which is
common-all
- This is to reduce duplicate type / enum definitions at the place it is actually used (e.g. plugin-core).
example
LookupSelectionType
should be defined incommon-all
and imported toplugin-core
.- As is, this is defined in both
common-all
andplugin-core
as a duplicate.
prefer:
// in common-all
export enum LookupSelectionTypeEnum {
"selectionExtract" = "selectionExtract",
"selection2link" = "selection2link",
"none" = "none"
}
// in plugin-core
import { LookupSelectionTypeEnum } from "@dendronhq/common-all"
// this is equivalent to "selectionExtract" | "selection2link" | "none"
type LookupSelectionType = keyof typeof LookupSelectionTypeEnum
avoid:
// in common-all
enum LookupSelectionTypeEnum {
"selectionExtract" = "SelectionExtract",
"selection2link" = "selection2link",
"none" = "none"
}
// in plugin-core
// duplicate definitions
export type LookupSelectionType =
"selection2link" | "selectionExtract" | "none";
export enum LookupSelectionTypeEnum {
"selection2link" = "selection2link",
"selectionExtract" = "selectionExtract",
"none" = "none"
}
2. Constant-initializing string literals for enums
- Prefer initializing enums of possible choices to constant-initialized string literals over letting it implicitly initialize to a numeric enum and auto-increment.
- They will be used in
dendron.yml
and other places where users can pass it in as an argument (e.g. keybindings.json). - Passing in
someConfig: "someChoice"
is more intuitive thansomeConfig: 0
Example
prefer:
export enum someModeForAMagicCommand {
foo = "foo",
bar = "bar",
baz = "baz",
}
// in keybindings.json
{
"command": "dendron.magicCommand",
"key": "cmd+m",
"args": {
"someModeForAMagicCommand": "foo"
}
}
avoid:
export enum someModeForAMagicCommand {
foo, // this will initialize to 0 and autoincrement
bar,
baz,
}
// in keybindings.json
{
"command": "dendron.magicCommand",
"key": "cmd+m",
"args": {
"someModeForAMagicCommand": 0
}
}