overhaul ConfigManager, add basic tests, add basic docs
This commit is contained in:
parent
dd724ee1ff
commit
fd32cfd5ba
238
README.md
238
README.md
@ -1,233 +1,11 @@
|
|||||||
# autoconf
|
# Overview
|
||||||
The `autoconf` project is an attempt at wrangling the complexity of configuring many
|
`symconf` is a CLI tool for managing local application configuration. It uses a simple
|
||||||
applications across one's Linux system. It provides a simple operational model for pulling
|
operational model that symlinks centralized config files to their expected locations across
|
||||||
many application config files into one place, as well as generating/setting color schemes
|
the system. This central config directory can then be version controlled.
|
||||||
across apps.
|
|
||||||
|
|
||||||
Quick terminology rundown for theme-related items:
|
`symconf` also facilitates dynamically setting system and application "themes," symlinking
|
||||||
|
matching theme config files for registered apps and running config reloading scripts.
|
||||||
|
|
||||||
- **Theme**: loose term referring generally to the overall aesthetic of a visual setting.
|
For
|
||||||
Ignoring stylistic changes (only applicable to some apps; example here might be a
|
example, the following `symconf` call coordinates a light to dark mode switch
|
||||||
a particular setting of the `waybar` layout), a theme is often just the wrapper term for
|
|
||||||
a choice of color _palette_ and _scheme_. For example, "tone4-light" could be a _theme_
|
|
||||||
setting for an app like `kitty`, referring to both a palette and scheme.
|
|
||||||
- **Palette**: a set of base colors used to style text or other aspects of an app's
|
|
||||||
displayed assets
|
|
||||||
- **Scheme**: an indication of lightness, typically either "light" or "dark.
|
|
||||||
|
|
||||||
As far as managing settings across apps, there are current two useful classifications
|
|
||||||
here:
|
|
||||||
|
|
||||||
1. **Inseparable from theme**: some apps (e.g., `sway`, `waybar`) have color scheme
|
|
||||||
components effectively built in to their canonical configuration file. This can make it
|
|
||||||
hard to set themes dynamically, as it would likely require some involved
|
|
||||||
matching/substitution rules. This is not a level of complexity I'm willing to embrace,
|
|
||||||
so we simply split the config files according to theme and/or scheme.
|
|
||||||
2. **Can load an external theme file**: some apps (e.g., `kitty`) have a clear mechanism
|
|
||||||
for loading themes. This typically implies some distinct color format, although usually
|
|
||||||
somewhat easy to generate (don't have to navigate non-color settings, for instance).
|
|
||||||
Such apps allow for an even less "invasive" config swapping process when setting a new
|
|
||||||
theme, as one can just swap out the external theme file.
|
|
||||||
|
|
||||||
To be clear on operation implications here: apps of type (1) must have _manually
|
|
||||||
maintained_ config variations according the desired themes. General theme settings must
|
|
||||||
follow the naming scheme `<app-name>-<palette>-<scheme>.<ext>`. For example, if I wanted to set
|
|
||||||
`sway` to a light variation (which, at the time of writing, would just entail changing a
|
|
||||||
single background color), I must have explicitly created a `sway-tone4-light.conf` file
|
|
||||||
that captures this setting. The canonical config file will then be symlinked to the
|
|
||||||
theme-specific file when the theme is set. (Note that the palette in this example is pretty much
|
|
||||||
irrelevant, but it needs to be present in order to match the overarching setting; here you
|
|
||||||
can just think of the format being `<app-name>-<theme>.<ext>`, where `tone4-light` is the
|
|
||||||
provided theme name.)
|
|
||||||
|
|
||||||
For apps of type (2), the canonical config file can remain untouched so long as it refers
|
|
||||||
to a fixed, generic theme file. For example, with `kitty`, my config file can point to a
|
|
||||||
`current-theme.conf` file, which will be symlinked to a specific theme file here in
|
|
||||||
`autoconf` when a change is requested. This enables a couple of conveniences:
|
|
||||||
|
|
||||||
- The true config directory on disk remains unpolluted with theme variants.
|
|
||||||
- If the set theme is regenerated, there is no intervention necessary to propagate its
|
|
||||||
changes to the target app. The symlinked file itself will be updated when the theme
|
|
||||||
does, ensuring the latest theme version is always immediately available and pointed to
|
|
||||||
by the app.
|
|
||||||
|
|
||||||
Keep in mind that some apps may fall into some grey area here, allowing some external
|
|
||||||
customization but locking down other settings internally. In such instances, there's no
|
|
||||||
need to overcomplicate things; just stick to explicit config variants under the type (1)
|
|
||||||
umbrella. Type (2) only works for generated themes anyhow; even if the target app can load
|
|
||||||
an external theme, type (1) should be used if preset themes are fixed.
|
|
||||||
|
|
||||||
## Naming standards
|
|
||||||
To keep things simple, we use a few fixed naming standards for setting app config files
|
|
||||||
and their themed counterparts. The app registry requires each theme-eligible app to
|
|
||||||
provide a config directory (`config_dir`), containing some canonical config file
|
|
||||||
(`config_file`) and to serve as a place for theme-specific config variations. The
|
|
||||||
following naming schemes must be used in order for theme switching to behave
|
|
||||||
appropriately:
|
|
||||||
|
|
||||||
- When setting a theme for a particular app, the following variables will be available:
|
|
||||||
* `<app-name>`
|
|
||||||
* `<palette>`
|
|
||||||
* `<scheme>`
|
|
||||||
- For apps with `external_theme = False`, config variants must named as
|
|
||||||
`<app-name>-<palette>-<scheme>.<ext>`, where `<ext>` is the app's default config file
|
|
||||||
extension.
|
|
||||||
- For apps with `external_theme = True`, the file `<config-dir>/current-theme.conf` will
|
|
||||||
be used when symlinking the requested theme. The config file thus must point to this
|
|
||||||
file in order to change with the set theme.
|
|
||||||
|
|
||||||
Additionally, the theme symlink will be created from the file
|
|
||||||
|
|
||||||
```
|
|
||||||
<autoconf-root>/autoconf/themes/<palette>/apps/<app-name>/generated/<scheme>.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
to `<config-dir>/current-theme.conf`.
|
|
||||||
|
|
||||||
## Directory structure
|
|
||||||
|
|
||||||
- `autoconf/`: main repo directory
|
|
||||||
* `config/`: app-specific configuration files. Each folder inside this directory is
|
|
||||||
app-specific, and the target of associated copy operations when a config sync is
|
|
||||||
performed. Nothing in this directory should pertain to any repo functionality; it
|
|
||||||
should only contain config files that originated elsewhere on the system.
|
|
||||||
* `themes/`: app-independent theme data files. Each folder in this directory should
|
|
||||||
correspond to a specific color palette and house any relevant color spec files
|
|
||||||
(currently likely be a `colors.json`). Also servers the output location for
|
|
||||||
generated theme files
|
|
||||||
* `<palette>/colors.json`: JSON formatted color key-value pairings for palette
|
|
||||||
colors. There's no standard here aside from the filename and format; downstream
|
|
||||||
app-specific TOML templates can be dependent on any key naming scheme within the
|
|
||||||
JSON.
|
|
||||||
+ `<palette>/apps/<app-name>/templates/`: houses the TOML maps for the color
|
|
||||||
palette `<palette>` under app `<app-name>`. Files `<fname>.toml` will be mapped to
|
|
||||||
`<fname>.conf` in the theme output folder (below), so ensure the naming
|
|
||||||
standards align with those outlined above.
|
|
||||||
+ `<palette>/apps/<app-name>/generated/`: output directory for generated scheme
|
|
||||||
variants. These are the symlink targets for dynamically set external themes.
|
|
||||||
* `app_registry.toml`: global application "registry" used by sync and theme-setting
|
|
||||||
scripts. This lets apps be dynamically added or removed from being eligible for
|
|
||||||
config-related operations.
|
|
||||||
|
|
||||||
## Scripts
|
|
||||||
|
|
||||||
`set_theme.py`: sets a theme across select apps.
|
|
||||||
|
|
||||||
- Applies to specific app with `-a <app>` , or to all apps in the `app_registry.toml` with
|
|
||||||
`-a "*"`.
|
|
||||||
- Uses symlinks to set canonical config files to theme-based variations. Which files get
|
|
||||||
set depends on the _app type_ (see above), which really just boils down to whether
|
|
||||||
theming (1) can be specified with an external format, and (2) if it depends on
|
|
||||||
auto-generated theme files from within `autoconf`.
|
|
||||||
- Palette and scheme are specified as expected. They are used to infer proper paths
|
|
||||||
according to naming and structure standards.
|
|
||||||
- Real config files will never be overwritten. To begin setting themes with the script,
|
|
||||||
you must delete the canonical config file expected by the app (and specified in the app
|
|
||||||
registry) to allow the first symlink to be set. From there on out, symlinks will be
|
|
||||||
automatically flushed.
|
|
||||||
- A report will be provided on which apps were successfully set to the requested theme,
|
|
||||||
along with the file stems. A number of checks are in place for the existence of involved
|
|
||||||
files and directories. Overall, the risk of overwritting a real config file is low; we
|
|
||||||
only flush existing symlinks, and if the would-be target for the requested theme (be it
|
|
||||||
from an auto-generated theme file, or from a manually manage config variant) doesn't
|
|
||||||
exist, that app's config will be completed skipped. Essentially, everything must be in
|
|
||||||
perfect shape before the symlink trigger is officially pulled.
|
|
||||||
|
|
||||||
|
|
||||||
`gen_theme.py`: generates theme files for palettes by mapping their color definitions
|
|
||||||
through app-specific templates. These templates specific how to relate an app's theme
|
|
||||||
variables to the color names provided by the template.
|
|
||||||
|
|
||||||
- An app and palette are the two required parameters. If no template or output paths are
|
|
||||||
provided, they will be inferred according to the theme path standards seen above.
|
|
||||||
- The `--template` argument can be a directory or a file, depending on what theme files
|
|
||||||
you'd like to render.
|
|
||||||
- The `--output` path, if specified, must be a directory. Generated theme files take on
|
|
||||||
a name with the same stem as their source template, but using the `.conf` extension.
|
|
||||||
- The TOML templates should make config variable names to JSON dot-notation accessors. If
|
|
||||||
color definitions are nested, the dot notation should be properly expanded by the script
|
|
||||||
when mapping the colors to keyword values.
|
|
||||||
- There are a number of checks for existing paths, even those inferred (e.g., template and
|
|
||||||
output) from the palette and app. If the appropriate setup hasn't been followed, the
|
|
||||||
script will fail. Make sure the `theme` folder in question and it's nested `app`
|
|
||||||
directory are correctly setup before running the script. (Perhaps down the line there
|
|
||||||
are some easy auto-setup steps to take here, but I'm not making that jump now.)
|
|
||||||
- TODO: open up different app "writers," or make it easy to extend output syntax based on
|
|
||||||
the app in question. This would like be as simple as mapping app names to
|
|
||||||
line-generating functions, which accept the keyword and color (among other items). This
|
|
||||||
can be fleshed out as needed.
|
|
||||||
|
|
||||||
`sync.sh`: copies relevant configuration files from local paths into the `autoconf`
|
|
||||||
subpath. Markdown files in the docs directory then reference the local copies of these
|
|
||||||
files, meaning the documentation updates dynamically when the configuration files do. That
|
|
||||||
is, the (possibly extracted) config snippets will change with the current state of my
|
|
||||||
system config without any manual intervention of the documentation files.
|
|
||||||
|
|
||||||
### Specific theme-setting example
|
|
||||||
To make clear how the theme setting script works on my system, the following breaks down
|
|
||||||
exactly what steps are taken to exert as much scheme control as possible. Everything at this
|
|
||||||
point is wrapped up in a single `make set-<palette>-<scheme>` call; suppose we're
|
|
||||||
currently running the dark scheme (see first image) and I run `make set-tone4-light`:
|
|
||||||
|
|
||||||
![
|
|
||||||
Starting point; have a GTK app (GNOME files), `kitty`, and Firefox (with the
|
|
||||||
system-dependent default theme set). In Firefox, I have open `localsys` with its
|
|
||||||
scheme-mode to set to "auto," which should reflect the theme setting picked up by the
|
|
||||||
browser (and note the white tab icon).
|
|
||||||
](_static/set-theme-1.png)
|
|
||||||
|
|
||||||
_(Starting point; have a GTK app (GNOME files), `kitty`, and Firefox (with the
|
|
||||||
system-dependent default theme set). In Firefox, I have open `localsys` with its
|
|
||||||
scheme-mode to set to "auto," which should reflect the theme setting picked up by the
|
|
||||||
browser (and note the white tab icon).)_
|
|
||||||
|
|
||||||
1. `set_theme.py` is invoked. Global settings are applied first, based on my OS (`Linux`),
|
|
||||||
which calls
|
|
||||||
|
|
||||||
```
|
|
||||||
gsettings set org.gnome.desktop.interface color-scheme 'prefer-light'
|
|
||||||
```
|
|
||||||
|
|
||||||
controlling settings for GTK apps and other `desktop-portal`-aware programs. This
|
|
||||||
yields the following:
|
|
||||||
|
|
||||||
![Portal-aware apps changed, config apps not yet set](_static/set-theme-2.png)
|
|
||||||
|
|
||||||
_(Portal-aware apps changed, config apps not yet set. Scheme-aware sites are updated
|
|
||||||
without page refresh.)_
|
|
||||||
2. Specific application styles are set. For now the list is small, including `kitty`,
|
|
||||||
`waybar`, and `sway`. `kitty` is the only type (2) application here, whereas the other
|
|
||||||
two are type (1).
|
|
||||||
|
|
||||||
a. For the type (1) apps, the canonical config files as specified in the app registry
|
|
||||||
are symlinked to their light variants. For `sway`, this is `~/.config/sway/config`,
|
|
||||||
and if we look at the `file`:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
.config/sway/config: symbolic link to ~/.config/sway/sway-tone4-light
|
|
||||||
```
|
|
||||||
b. For the type (2) apps, just the `current-theme.conf` file is symlinked to the
|
|
||||||
relevant palette-scheme file. `kitty` is such an app, with a supported theme file
|
|
||||||
for `tone4`, and those files have been auto-generated via `gen_theme.py`. Looking at
|
|
||||||
this file under the `kitty` config directory:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
.config/kitty/current-theme.conf: symbolic link to ~/Documents/projects/autoconf/autoconf/themes/tone4/apps/kitty/generated/light.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
The `kitty.conf` file isn't changed, as all palette-related items are specified in
|
|
||||||
the theme file. (Note that the general notion of a "theme" could include changes to
|
|
||||||
other stylistic aspects, like the font family; this would likely require some hybrid
|
|
||||||
type 1-2 approach not yet implemented).
|
|
||||||
3. Live application instances are reloaded, according to the registered `refresh_cmd`s.
|
|
||||||
Here the apps with style/config files set in step (2) are reloaded to reflect those
|
|
||||||
changes. Again, in this example, this is `kitty`, `sway`, and the `waybar`.
|
|
||||||
|
|
||||||
![Final light setting: portal-dependent apps _and_ config-based apps changed](_static/set-theme-3.png)
|
|
||||||
|
|
||||||
_(Final light setting: portal-dependent apps _and_ config-based apps changed)_
|
|
||||||
4. `set_theme.py` provides a report for the actions taken; in this case, the following was
|
|
||||||
printed:
|
|
||||||
|
|
||||||
![`set_theme.py` output](_static/set-theme-4.png)
|
|
||||||
_(`set_theme.py` output)_
|
|
||||||
|
@ -52,3 +52,4 @@
|
|||||||
- Refresh scripts should likely specify a shell shabang at the top of the file
|
- Refresh scripts should likely specify a shell shabang at the top of the file
|
||||||
- `apps` can serve as a dotfiles folder
|
- `apps` can serve as a dotfiles folder
|
||||||
- Support symlinking whole folders?
|
- Support symlinking whole folders?
|
||||||
|
- `any` might prefer to match configs with none over specific options, but will match any
|
||||||
|
@ -1 +0,0 @@
|
|||||||
from autoconf.config import ConfigManager
|
|
17
docs/conf.py
17
docs/conf.py
@ -6,7 +6,7 @@
|
|||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||||
|
|
||||||
project = '<project-name>'
|
project = 'symconf'
|
||||||
copyright = '2024, Sam Griesemer'
|
copyright = '2024, Sam Griesemer'
|
||||||
author = 'Sam Griesemer'
|
author = 'Sam Griesemer'
|
||||||
|
|
||||||
@ -15,18 +15,25 @@ author = 'Sam Griesemer'
|
|||||||
|
|
||||||
extensions = [
|
extensions = [
|
||||||
"sphinx.ext.autodoc",
|
"sphinx.ext.autodoc",
|
||||||
"sphinx.ext.autosummary",
|
"sphinx.ext.autosummary", # enables a directive to be specified manually that gathers
|
||||||
"sphinx.ext.viewcode",
|
# module/object summary details in a table
|
||||||
"myst_parser",
|
"sphinx.ext.viewcode", # allow viewing source in the HTML pages
|
||||||
|
"myst_parser", # only really applies to manual docs; docstrings still need RST-like
|
||||||
|
"sphinx.ext.napoleon", # enables Google-style docstring formats
|
||||||
|
"sphinx_autodoc_typehints", # external extension that allows arg types to be inferred by type hints
|
||||||
]
|
]
|
||||||
autosummary_generate = True
|
autosummary_generate = True
|
||||||
autosummary_imported_members = True
|
autosummary_imported_members = True
|
||||||
|
|
||||||
|
# include __init__ definitions in autodoc
|
||||||
|
autodoc_default_options = {
|
||||||
|
'special-members': '__init__',
|
||||||
|
}
|
||||||
|
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
# -- Options for HTML output -------------------------------------------------
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# `autoconf` package docs
|
# `symconf` package docs
|
||||||
{ref}`genindex`
|
{ref}`genindex`
|
||||||
{ref}`modindex`
|
{ref}`modindex`
|
||||||
{ref}`search`
|
{ref}`search`
|
||||||
@ -14,15 +14,17 @@
|
|||||||
:maxdepth: 3
|
:maxdepth: 3
|
||||||
:caption: Autoref
|
:caption: Autoref
|
||||||
|
|
||||||
_autoref/autoconf.rst
|
_autoref/symconf.rst
|
||||||
```
|
```
|
||||||
|
|
||||||
```{toctree}
|
```{toctree}
|
||||||
:maxdepth: 3
|
:maxdepth: 3
|
||||||
:caption: Contents
|
:caption: Contents
|
||||||
|
|
||||||
|
reference/configuring
|
||||||
|
reference/usage
|
||||||
|
reference/archive
|
||||||
reference/documentation/index
|
reference/documentation/index
|
||||||
reference/site/index
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```{include} ../README.md
|
```{include} ../README.md
|
||||||
|
233
docs/reference/archive.md
Normal file
233
docs/reference/archive.md
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
# Archive
|
||||||
|
The `autoconf` project is an attempt at wrangling the complexity of configuring many
|
||||||
|
applications across one's Linux system. It provides a simple operational model for pulling
|
||||||
|
many application config files into one place, as well as generating/setting color schemes
|
||||||
|
across apps.
|
||||||
|
|
||||||
|
Quick terminology rundown for theme-related items:
|
||||||
|
|
||||||
|
- **Theme**: loose term referring generally to the overall aesthetic of a visual setting.
|
||||||
|
Ignoring stylistic changes (only applicable to some apps; example here might be a
|
||||||
|
a particular setting of the `waybar` layout), a theme is often just the wrapper term for
|
||||||
|
a choice of color _palette_ and _scheme_. For example, "tone4-light" could be a _theme_
|
||||||
|
setting for an app like `kitty`, referring to both a palette and scheme.
|
||||||
|
- **Palette**: a set of base colors used to style text or other aspects of an app's
|
||||||
|
displayed assets
|
||||||
|
- **Scheme**: an indication of lightness, typically either "light" or "dark.
|
||||||
|
|
||||||
|
As far as managing settings across apps, there are current two useful classifications
|
||||||
|
here:
|
||||||
|
|
||||||
|
1. **Inseparable from theme**: some apps (e.g., `sway`, `waybar`) have color scheme
|
||||||
|
components effectively built in to their canonical configuration file. This can make it
|
||||||
|
hard to set themes dynamically, as it would likely require some involved
|
||||||
|
matching/substitution rules. This is not a level of complexity I'm willing to embrace,
|
||||||
|
so we simply split the config files according to theme and/or scheme.
|
||||||
|
2. **Can load an external theme file**: some apps (e.g., `kitty`) have a clear mechanism
|
||||||
|
for loading themes. This typically implies some distinct color format, although usually
|
||||||
|
somewhat easy to generate (don't have to navigate non-color settings, for instance).
|
||||||
|
Such apps allow for an even less "invasive" config swapping process when setting a new
|
||||||
|
theme, as one can just swap out the external theme file.
|
||||||
|
|
||||||
|
To be clear on operation implications here: apps of type (1) must have _manually
|
||||||
|
maintained_ config variations according the desired themes. General theme settings must
|
||||||
|
follow the naming scheme `<app-name>-<palette>-<scheme>.<ext>`. For example, if I wanted to set
|
||||||
|
`sway` to a light variation (which, at the time of writing, would just entail changing a
|
||||||
|
single background color), I must have explicitly created a `sway-tone4-light.conf` file
|
||||||
|
that captures this setting. The canonical config file will then be symlinked to the
|
||||||
|
theme-specific file when the theme is set. (Note that the palette in this example is pretty much
|
||||||
|
irrelevant, but it needs to be present in order to match the overarching setting; here you
|
||||||
|
can just think of the format being `<app-name>-<theme>.<ext>`, where `tone4-light` is the
|
||||||
|
provided theme name.)
|
||||||
|
|
||||||
|
For apps of type (2), the canonical config file can remain untouched so long as it refers
|
||||||
|
to a fixed, generic theme file. For example, with `kitty`, my config file can point to a
|
||||||
|
`current-theme.conf` file, which will be symlinked to a specific theme file here in
|
||||||
|
`autoconf` when a change is requested. This enables a couple of conveniences:
|
||||||
|
|
||||||
|
- The true config directory on disk remains unpolluted with theme variants.
|
||||||
|
- If the set theme is regenerated, there is no intervention necessary to propagate its
|
||||||
|
changes to the target app. The symlinked file itself will be updated when the theme
|
||||||
|
does, ensuring the latest theme version is always immediately available and pointed to
|
||||||
|
by the app.
|
||||||
|
|
||||||
|
Keep in mind that some apps may fall into some grey area here, allowing some external
|
||||||
|
customization but locking down other settings internally. In such instances, there's no
|
||||||
|
need to overcomplicate things; just stick to explicit config variants under the type (1)
|
||||||
|
umbrella. Type (2) only works for generated themes anyhow; even if the target app can load
|
||||||
|
an external theme, type (1) should be used if preset themes are fixed.
|
||||||
|
|
||||||
|
## Naming standards
|
||||||
|
To keep things simple, we use a few fixed naming standards for setting app config files
|
||||||
|
and their themed counterparts. The app registry requires each theme-eligible app to
|
||||||
|
provide a config directory (`config_dir`), containing some canonical config file
|
||||||
|
(`config_file`) and to serve as a place for theme-specific config variations. The
|
||||||
|
following naming schemes must be used in order for theme switching to behave
|
||||||
|
appropriately:
|
||||||
|
|
||||||
|
- When setting a theme for a particular app, the following variables will be available:
|
||||||
|
* `<app-name>`
|
||||||
|
* `<palette>`
|
||||||
|
* `<scheme>`
|
||||||
|
- For apps with `external_theme = False`, config variants must named as
|
||||||
|
`<app-name>-<palette>-<scheme>.<ext>`, where `<ext>` is the app's default config file
|
||||||
|
extension.
|
||||||
|
- For apps with `external_theme = True`, the file `<config-dir>/current-theme.conf` will
|
||||||
|
be used when symlinking the requested theme. The config file thus must point to this
|
||||||
|
file in order to change with the set theme.
|
||||||
|
|
||||||
|
Additionally, the theme symlink will be created from the file
|
||||||
|
|
||||||
|
```
|
||||||
|
<autoconf-root>/autoconf/themes/<palette>/apps/<app-name>/generated/<scheme>.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
to `<config-dir>/current-theme.conf`.
|
||||||
|
|
||||||
|
## Directory structure
|
||||||
|
|
||||||
|
- `autoconf/`: main repo directory
|
||||||
|
* `config/`: app-specific configuration files. Each folder inside this directory is
|
||||||
|
app-specific, and the target of associated copy operations when a config sync is
|
||||||
|
performed. Nothing in this directory should pertain to any repo functionality; it
|
||||||
|
should only contain config files that originated elsewhere on the system.
|
||||||
|
* `themes/`: app-independent theme data files. Each folder in this directory should
|
||||||
|
correspond to a specific color palette and house any relevant color spec files
|
||||||
|
(currently likely be a `colors.json`). Also servers the output location for
|
||||||
|
generated theme files
|
||||||
|
* `<palette>/colors.json`: JSON formatted color key-value pairings for palette
|
||||||
|
colors. There's no standard here aside from the filename and format; downstream
|
||||||
|
app-specific TOML templates can be dependent on any key naming scheme within the
|
||||||
|
JSON.
|
||||||
|
+ `<palette>/apps/<app-name>/templates/`: houses the TOML maps for the color
|
||||||
|
palette `<palette>` under app `<app-name>`. Files `<fname>.toml` will be mapped to
|
||||||
|
`<fname>.conf` in the theme output folder (below), so ensure the naming
|
||||||
|
standards align with those outlined above.
|
||||||
|
+ `<palette>/apps/<app-name>/generated/`: output directory for generated scheme
|
||||||
|
variants. These are the symlink targets for dynamically set external themes.
|
||||||
|
* `app_registry.toml`: global application "registry" used by sync and theme-setting
|
||||||
|
scripts. This lets apps be dynamically added or removed from being eligible for
|
||||||
|
config-related operations.
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
|
||||||
|
`set_theme.py`: sets a theme across select apps.
|
||||||
|
|
||||||
|
- Applies to specific app with `-a <app>` , or to all apps in the `app_registry.toml` with
|
||||||
|
`-a "*"`.
|
||||||
|
- Uses symlinks to set canonical config files to theme-based variations. Which files get
|
||||||
|
set depends on the _app type_ (see above), which really just boils down to whether
|
||||||
|
theming (1) can be specified with an external format, and (2) if it depends on
|
||||||
|
auto-generated theme files from within `autoconf`.
|
||||||
|
- Palette and scheme are specified as expected. They are used to infer proper paths
|
||||||
|
according to naming and structure standards.
|
||||||
|
- Real config files will never be overwritten. To begin setting themes with the script,
|
||||||
|
you must delete the canonical config file expected by the app (and specified in the app
|
||||||
|
registry) to allow the first symlink to be set. From there on out, symlinks will be
|
||||||
|
automatically flushed.
|
||||||
|
- A report will be provided on which apps were successfully set to the requested theme,
|
||||||
|
along with the file stems. A number of checks are in place for the existence of involved
|
||||||
|
files and directories. Overall, the risk of overwritting a real config file is low; we
|
||||||
|
only flush existing symlinks, and if the would-be target for the requested theme (be it
|
||||||
|
from an auto-generated theme file, or from a manually manage config variant) doesn't
|
||||||
|
exist, that app's config will be completed skipped. Essentially, everything must be in
|
||||||
|
perfect shape before the symlink trigger is officially pulled.
|
||||||
|
|
||||||
|
|
||||||
|
`gen_theme.py`: generates theme files for palettes by mapping their color definitions
|
||||||
|
through app-specific templates. These templates specific how to relate an app's theme
|
||||||
|
variables to the color names provided by the template.
|
||||||
|
|
||||||
|
- An app and palette are the two required parameters. If no template or output paths are
|
||||||
|
provided, they will be inferred according to the theme path standards seen above.
|
||||||
|
- The `--template` argument can be a directory or a file, depending on what theme files
|
||||||
|
you'd like to render.
|
||||||
|
- The `--output` path, if specified, must be a directory. Generated theme files take on
|
||||||
|
a name with the same stem as their source template, but using the `.conf` extension.
|
||||||
|
- The TOML templates should make config variable names to JSON dot-notation accessors. If
|
||||||
|
color definitions are nested, the dot notation should be properly expanded by the script
|
||||||
|
when mapping the colors to keyword values.
|
||||||
|
- There are a number of checks for existing paths, even those inferred (e.g., template and
|
||||||
|
output) from the palette and app. If the appropriate setup hasn't been followed, the
|
||||||
|
script will fail. Make sure the `theme` folder in question and it's nested `app`
|
||||||
|
directory are correctly setup before running the script. (Perhaps down the line there
|
||||||
|
are some easy auto-setup steps to take here, but I'm not making that jump now.)
|
||||||
|
- TODO: open up different app "writers," or make it easy to extend output syntax based on
|
||||||
|
the app in question. This would like be as simple as mapping app names to
|
||||||
|
line-generating functions, which accept the keyword and color (among other items). This
|
||||||
|
can be fleshed out as needed.
|
||||||
|
|
||||||
|
`sync.sh`: copies relevant configuration files from local paths into the `autoconf`
|
||||||
|
subpath. Markdown files in the docs directory then reference the local copies of these
|
||||||
|
files, meaning the documentation updates dynamically when the configuration files do. That
|
||||||
|
is, the (possibly extracted) config snippets will change with the current state of my
|
||||||
|
system config without any manual intervention of the documentation files.
|
||||||
|
|
||||||
|
### Specific theme-setting example
|
||||||
|
To make clear how the theme setting script works on my system, the following breaks down
|
||||||
|
exactly what steps are taken to exert as much scheme control as possible. Everything at this
|
||||||
|
point is wrapped up in a single `make set-<palette>-<scheme>` call; suppose we're
|
||||||
|
currently running the dark scheme (see first image) and I run `make set-tone4-light`:
|
||||||
|
|
||||||
|
![
|
||||||
|
Starting point; have a GTK app (GNOME files), `kitty`, and Firefox (with the
|
||||||
|
system-dependent default theme set). In Firefox, I have open `localsys` with its
|
||||||
|
scheme-mode to set to "auto," which should reflect the theme setting picked up by the
|
||||||
|
browser (and note the white tab icon).
|
||||||
|
](_static/set-theme-1.png)
|
||||||
|
|
||||||
|
_(Starting point; have a GTK app (GNOME files), `kitty`, and Firefox (with the
|
||||||
|
system-dependent default theme set). In Firefox, I have open `localsys` with its
|
||||||
|
scheme-mode to set to "auto," which should reflect the theme setting picked up by the
|
||||||
|
browser (and note the white tab icon).)_
|
||||||
|
|
||||||
|
1. `set_theme.py` is invoked. Global settings are applied first, based on my OS (`Linux`),
|
||||||
|
which calls
|
||||||
|
|
||||||
|
```
|
||||||
|
gsettings set org.gnome.desktop.interface color-scheme 'prefer-light'
|
||||||
|
```
|
||||||
|
|
||||||
|
controlling settings for GTK apps and other `desktop-portal`-aware programs. This
|
||||||
|
yields the following:
|
||||||
|
|
||||||
|
![Portal-aware apps changed, config apps not yet set](_static/set-theme-2.png)
|
||||||
|
|
||||||
|
_(Portal-aware apps changed, config apps not yet set. Scheme-aware sites are updated
|
||||||
|
without page refresh.)_
|
||||||
|
2. Specific application styles are set. For now the list is small, including `kitty`,
|
||||||
|
`waybar`, and `sway`. `kitty` is the only type (2) application here, whereas the other
|
||||||
|
two are type (1).
|
||||||
|
|
||||||
|
a. For the type (1) apps, the canonical config files as specified in the app registry
|
||||||
|
are symlinked to their light variants. For `sway`, this is `~/.config/sway/config`,
|
||||||
|
and if we look at the `file`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
.config/sway/config: symbolic link to ~/.config/sway/sway-tone4-light
|
||||||
|
```
|
||||||
|
b. For the type (2) apps, just the `current-theme.conf` file is symlinked to the
|
||||||
|
relevant palette-scheme file. `kitty` is such an app, with a supported theme file
|
||||||
|
for `tone4`, and those files have been auto-generated via `gen_theme.py`. Looking at
|
||||||
|
this file under the `kitty` config directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
.config/kitty/current-theme.conf: symbolic link to ~/Documents/projects/autoconf/autoconf/themes/tone4/apps/kitty/generated/light.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
The `kitty.conf` file isn't changed, as all palette-related items are specified in
|
||||||
|
the theme file. (Note that the general notion of a "theme" could include changes to
|
||||||
|
other stylistic aspects, like the font family; this would likely require some hybrid
|
||||||
|
type 1-2 approach not yet implemented).
|
||||||
|
3. Live application instances are reloaded, according to the registered `refresh_cmd`s.
|
||||||
|
Here the apps with style/config files set in step (2) are reloaded to reflect those
|
||||||
|
changes. Again, in this example, this is `kitty`, `sway`, and the `waybar`.
|
||||||
|
|
||||||
|
![Final light setting: portal-dependent apps _and_ config-based apps changed](_static/set-theme-3.png)
|
||||||
|
|
||||||
|
_(Final light setting: portal-dependent apps _and_ config-based apps changed)_
|
||||||
|
4. `set_theme.py` provides a report for the actions taken; in this case, the following was
|
||||||
|
printed:
|
||||||
|
|
||||||
|
![`set_theme.py` output](_static/set-theme-4.png)
|
||||||
|
_(`set_theme.py` output)_
|
170
docs/reference/configuring.md
Normal file
170
docs/reference/configuring.md
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
# Configuring
|
||||||
|
`symconf` operates on a central directory that houses all of the config files you may wish
|
||||||
|
to apply. The default location for this directory is your `$XDG_CONFIG_HOME` (e.g.,
|
||||||
|
`~/.config/symconf/`), but it can be any location on your system so long as it's specified
|
||||||
|
(see more in Usage).
|
||||||
|
|
||||||
|
`symconf` expects you to create two top-level components in your config directory: an
|
||||||
|
`apps/` directory and an `app_registry.toml` file.
|
||||||
|
|
||||||
|
## Apps directory
|
||||||
|
An `apps/` directory should be created in your config home, with a subdirectory
|
||||||
|
`apps/<app-name>/` for each app with config files that you'd like to be visible to
|
||||||
|
`symconf`. Note that simply populating an app's config folder here will do nothing on its
|
||||||
|
own; the app must also have been registered (discussed in item #2) in order for these
|
||||||
|
files to be used when `symconf` is invoked. (This just means you can populate your `apps/`
|
||||||
|
folder safely without expecting any default behavior. More often than not you'll be
|
||||||
|
expected to tell `symconf` exactly where your config files should end up, meaning you know
|
||||||
|
exactly what it's doing.)
|
||||||
|
|
||||||
|
### User config
|
||||||
|
Inside your app-specific subdirectory, your managed config files should be placed in a
|
||||||
|
`user/` subdirectory (distinguishing them from those generated by templates; see more
|
||||||
|
in Themes). Your config files themselves are then expected to follow a specific naming
|
||||||
|
scheme:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
<palette>-<scheme>.<config-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
This ties your config file to a particular theme setting as needed, and `symconf` will
|
||||||
|
apply it if it matches the theme setting you provide when invoked. The specific values are
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
- `scheme`: can be `light`, `dark`, or `none`. Indicates whether the config file should
|
||||||
|
be applied specifically when requesting a light or dark mode. Use `none` to indicate
|
||||||
|
that the config file does not have settings specific to a light/dark mode.
|
||||||
|
- `palette`: a "palette name" of your choosing, or `none`. The palette name you use
|
||||||
|
here may refer specifically to a color palette used by the config file, but can be
|
||||||
|
used generally to indicate any particular group of config settings (e.g., fonts,
|
||||||
|
transparency, etc). Use `none` to indicate that the file does not correspond to any
|
||||||
|
particular style group.
|
||||||
|
- `config-name`: the _name_ of the config file. This should correspond to _same path
|
||||||
|
name_ that is expected by the app. For example, if your app expects a config file at
|
||||||
|
`a/b/c/d.conf`, "`d.conf`" is the path name.
|
||||||
|
|
||||||
|
When invoking `symconf` with specific scheme and palette settings (see more in Usage),
|
||||||
|
appropriate config files can be matched based on how you've named your files.
|
||||||
|
|
||||||
|
For example, suppose I want to set up a simple light/dark mode switch for the `kitty`
|
||||||
|
terminal emulator. The following tree demonstrates a valid setup:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
<config-home>
|
||||||
|
└── apps/
|
||||||
|
└── kitty/
|
||||||
|
└── user/
|
||||||
|
├── none-light.kitty.conf
|
||||||
|
└── none-dark.kitty.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
where `none-light.kitty.conf` may set a light background and `none-dark.kitty.conf` a dark
|
||||||
|
one. `none` is used for the `<palette>` part of the name to indicate the configuration does
|
||||||
|
not pertain to any specific palette and can be matched even if one is not provided. With
|
||||||
|
an appropriate `app_regsitry.toml` file (see below), invoking
|
||||||
|
|
||||||
|
```sh
|
||||||
|
symconf --theme=light --apps=kitty
|
||||||
|
```
|
||||||
|
|
||||||
|
would symlink `$XDG_CONFIG_HOME/symconf/apps/kitty/user/none-light.kitty.conf` to
|
||||||
|
`~/.config/kitty/kitty.conf`.
|
||||||
|
|
||||||
|
### Templatized config
|
||||||
|
Note the potential inconvenience in needing to manage two separate config files in the
|
||||||
|
above example, very likely with all but one line of difference. Templating enables
|
||||||
|
populating config template files dynamically with theme-specific variables of your
|
||||||
|
choosing.
|
||||||
|
|
||||||
|
|
||||||
|
### Reload scripts
|
||||||
|
After symlinking a new set of config files, it is often necessary to reload the system or
|
||||||
|
relevant apps in order for the new config settings to apply. Within an app's subdirectory,
|
||||||
|
a `call/` folder can be created to hold scripts that should apply based on certain schemes
|
||||||
|
or palettes (matching them in the same way as config files). For example,
|
||||||
|
|
||||||
|
```sh
|
||||||
|
<config-home>
|
||||||
|
└── apps/
|
||||||
|
└── kitty/
|
||||||
|
└── call/
|
||||||
|
└── none-none.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`none-none.sh` might simply contain `kill -s USR1 $(pgrep kitty)`, which is a way to tell
|
||||||
|
all running `kitty` instances to reload their config settings. Again, following the naming
|
||||||
|
scheme for config files, a script named `none-none.sh` will apply under any scheme or
|
||||||
|
palette specification. Thus, in our light/dark mode switch example, invoking `symconf
|
||||||
|
--theme=light --apps=kitty` would:
|
||||||
|
|
||||||
|
1. Search and match the config name `none-light.kitty.conf` and symlink it to
|
||||||
|
`~/.config/kitty/kitty.conf`.
|
||||||
|
2. Search and match `none-none.sh` and execute it, applying the new light mode settings to
|
||||||
|
all running `kitty` instances.
|
||||||
|
|
||||||
|
|
||||||
|
## App registry
|
||||||
|
An `app_registry.toml` file, used to specify the target locations for app-specific
|
||||||
|
config files. To "register" an app, you simply need to add the following text block to
|
||||||
|
the `app_registry.toml` file:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[app.<app-name>]
|
||||||
|
# DEFINE *ONE* OF THE FOLLOWING
|
||||||
|
config_dir = "<path-to-app-config-folder>"
|
||||||
|
# OR
|
||||||
|
config_map = {
|
||||||
|
"<conf-pathname-1>" = "<path-to-exact-config-file>"
|
||||||
|
"<conf-pathname-2>" = "<path-to-exact-config-file>"
|
||||||
|
# ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(Note that text in angle brackets refers to values that should be replaced.) This tells
|
||||||
|
`symconf` how it should handle each app's config files. The `<app-name>` (e.g.,
|
||||||
|
`kitty`) should correspond to a subdirectory under `apps/` (e.g., `apps/kitty/`) that
|
||||||
|
holds your config files for that app. As shown, you then need to supply either of the
|
||||||
|
following options:
|
||||||
|
|
||||||
|
- `config_dir`: specifies a single directory where all of the app's matching config
|
||||||
|
files should be symlinked. In the `kitty` example, this might be `~/.config/kitty`.
|
||||||
|
This is the simplest and most common option provided most apps expect all of their
|
||||||
|
config files to be in a single directory.
|
||||||
|
- `config_map`: a dictionary mapping config file _path names_ to the _exact paths_ that
|
||||||
|
should be created during the symlink process. This is typically needed when an app
|
||||||
|
has many config files that need to be set in several disparate locations across your
|
||||||
|
system. In the `kitty` example, although not necessary (and in general you should
|
||||||
|
prefer to set `config_dir` when applicable), we could have the following
|
||||||
|
`config_map`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[app.kitty]
|
||||||
|
config_map = {
|
||||||
|
"kitty.conf": "~/.config/kitty/kitty.conf"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This tells `symconf` to symlink the exact location `~/.config/kitty/kitty.conf` to
|
||||||
|
the matching `kitty.conf` under the `apps/kitty` directory.
|
||||||
|
|
||||||
|
## Directory structure
|
||||||
|
In total, the structure of your base config directory can look as follows:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
~/.config/symconf/
|
||||||
|
├── app_registry.toml
|
||||||
|
└── apps/
|
||||||
|
└── <app>/
|
||||||
|
├── user/ # user managed
|
||||||
|
│ └── none-none.<config-name>
|
||||||
|
├── generated/ # automatically populated
|
||||||
|
│ └── none-none.<config-name>
|
||||||
|
├── templates/ # config templates
|
||||||
|
│ └── none-none.template
|
||||||
|
└── call/ # reload scripts
|
||||||
|
└── none-none.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Misc remarks
|
||||||
|
|
||||||
|
### Multiple config files with same path name
|
@ -1,8 +1,5 @@
|
|||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
```{toctree}
|
```{toctree}
|
||||||
:hidden:
|
|
||||||
|
|
||||||
sphinx
|
sphinx
|
||||||
```
|
```
|
||||||
|
|
||||||
|
6
docs/reference/usage.md
Normal file
6
docs/reference/usage.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Usage
|
||||||
|
|
||||||
|
```{toctree}
|
||||||
|
:hidden:
|
||||||
|
|
||||||
|
```
|
@ -25,6 +25,7 @@ classifiers = [
|
|||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pyxdg",
|
"pyxdg",
|
||||||
|
"colorama",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
@ -36,6 +37,7 @@ docs = [
|
|||||||
"furo",
|
"furo",
|
||||||
"myst-parser",
|
"myst-parser",
|
||||||
]
|
]
|
||||||
|
build = ["build", "twine"]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = "https://doc.olog.io/symconf"
|
Homepage = "https://doc.olog.io/symconf"
|
||||||
|
5
symconf/__init__.py
Normal file
5
symconf/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from symconf.config import ConfigManager
|
||||||
|
|
||||||
|
from symconf import config
|
||||||
|
from symconf import theme
|
||||||
|
from symconf import util
|
@ -1,13 +1,12 @@
|
|||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
import util
|
from symconf import util
|
||||||
#from gen_theme import generate_theme_files
|
from symconf.config import ConfigManager
|
||||||
from autoconf.config import ConfigManager
|
|
||||||
|
|
||||||
|
|
||||||
def add_set_subparser(subparsers):
|
def add_set_subparser(subparsers):
|
||||||
def update_app_settings(args):
|
def update_app_settings(args):
|
||||||
cm = ConfigManager(args.config_dif)
|
cm = ConfigManager(args.config_dir)
|
||||||
cm.update_apps(
|
cm.update_apps(
|
||||||
apps=args.apps,
|
apps=args.apps,
|
||||||
scheme=args.scheme,
|
scheme=args.scheme,
|
||||||
@ -35,8 +34,8 @@ def add_set_subparser(subparsers):
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-a', '--apps',
|
'-a', '--apps',
|
||||||
required = False,
|
required = False,
|
||||||
default = "any",
|
default = "*",
|
||||||
type = lambda s: s.split(',') if s != '*' else s
|
type = lambda s: s.split(',') if s != '*' else s,
|
||||||
help = 'Application target for theme. App must be present in the registry. ' \
|
help = 'Application target for theme. App must be present in the registry. ' \
|
||||||
+ 'Use "*" to apply to all registered apps'
|
+ 'Use "*" to apply to all registered apps'
|
||||||
)
|
)
|
||||||
@ -75,7 +74,7 @@ def add_gen_subparser(subparsers):
|
|||||||
|
|
||||||
# central argparse entry point
|
# central argparse entry point
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
'autoconf',
|
'symconf',
|
||||||
description='Generate theme files for various applications. Uses a template (in TOML ' \
|
description='Generate theme files for various applications. Uses a template (in TOML ' \
|
||||||
+ 'format) to map application-specific config keywords to colors (in JSON ' \
|
+ 'format) to map application-specific config keywords to colors (in JSON ' \
|
||||||
+ 'format).'
|
+ 'format).'
|
||||||
@ -88,13 +87,14 @@ parser.add_argument(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# add subparsers
|
# add subparsers
|
||||||
subparsers = parser.get_subparsers()
|
subparsers = parser.add_subparsers(title='subcommand actions')
|
||||||
#add_gen_subparser(subparsers)
|
#add_gen_subparser(subparsers)
|
||||||
add_set_subparser(subparsers)
|
add_set_subparser(subparsers)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
print(args)
|
||||||
|
|
||||||
if 'func' in args:
|
if 'func' in args:
|
||||||
args.func(args)
|
args.func(args)
|
@ -8,7 +8,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
from colorama import Fore, Back, Style
|
from colorama import Fore, Back, Style
|
||||||
|
|
||||||
from autoconf import util
|
from symconf import util
|
||||||
|
|
||||||
|
|
||||||
class ConfigManager:
|
class ConfigManager:
|
||||||
@ -23,7 +23,7 @@ class ConfigManager:
|
|||||||
Parameters:
|
Parameters:
|
||||||
config_dir: config parent directory housing expected files (registry,
|
config_dir: config parent directory housing expected files (registry,
|
||||||
app-specific conf files, etc). Defaults to
|
app-specific conf files, etc). Defaults to
|
||||||
``"$XDG_CONFIG_HOME/autoconf/"``.
|
``"$XDG_CONFIG_HOME/symconf/"``.
|
||||||
disable_registry: disable checks for a registry file in the ``config_dir``.
|
disable_registry: disable checks for a registry file in the ``config_dir``.
|
||||||
Should really only be set when using this programmatically
|
Should really only be set when using this programmatically
|
||||||
and manually supplying app settings.
|
and manually supplying app settings.
|
||||||
@ -64,7 +64,6 @@ class ConfigManager:
|
|||||||
def _check_registry(self):
|
def _check_registry(self):
|
||||||
registry_path = Path(self.config_dir, 'app_registry.toml')
|
registry_path = Path(self.config_dir, 'app_registry.toml')
|
||||||
|
|
||||||
self.app_registry = {}
|
|
||||||
if not registry_path.exists():
|
if not registry_path.exists():
|
||||||
print(
|
print(
|
||||||
Fore.YELLOW \
|
Fore.YELLOW \
|
||||||
@ -82,7 +81,7 @@ class ConfigManager:
|
|||||||
|
|
||||||
self.app_registry = app_registry.get('app', {})
|
self.app_registry = app_registry.get('app', {})
|
||||||
|
|
||||||
def resolve_scheme(self, scheme):
|
def _resolve_scheme(self, scheme):
|
||||||
# if scheme == 'auto':
|
# if scheme == 'auto':
|
||||||
# os_cmd_groups = {
|
# os_cmd_groups = {
|
||||||
# 'Linux': (
|
# 'Linux': (
|
||||||
@ -105,13 +104,13 @@ class ConfigManager:
|
|||||||
|
|
||||||
return scheme
|
return scheme
|
||||||
|
|
||||||
def resolve_palette(self, palette):
|
def _resolve_palette(self, palette):
|
||||||
if palette == 'auto':
|
if palette == 'auto':
|
||||||
return 'any'
|
return 'any'
|
||||||
|
|
||||||
return palette
|
return palette
|
||||||
|
|
||||||
def app_config_map(self, app_name):
|
def app_config_map(self, app_name) -> dict[str, Path]:
|
||||||
'''
|
'''
|
||||||
Get the config map for a provided app.
|
Get the config map for a provided app.
|
||||||
|
|
||||||
@ -125,8 +124,8 @@ class ConfigManager:
|
|||||||
For example,
|
For example,
|
||||||
|
|
||||||
```
|
```
|
||||||
palette1-light.conf.ini -> ~/.config/autoconf/apps/user/palette1-light.conf.ini
|
palette1-light.conf.ini -> ~/.config/symconf/apps/user/palette1-light.conf.ini
|
||||||
palette2-dark.app.conf -> ~/.config/autoconf/apps/generated/palette2-dark.app.conf
|
palette2-dark.app.conf -> ~/.config/symconf/apps/generated/palette2-dark.app.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
This ensures we have unique config names pointing to appropriate locations (which
|
This ensures we have unique config names pointing to appropriate locations (which
|
||||||
@ -147,12 +146,109 @@ class ConfigManager:
|
|||||||
|
|
||||||
return file_map
|
return file_map
|
||||||
|
|
||||||
|
def _get_file_parts(self, pathnames):
|
||||||
|
# now match theme files in order of inc. specificity; for each unique config file
|
||||||
|
# tail, only the most specific matching file sticks
|
||||||
|
file_parts = []
|
||||||
|
for pathname in pathnames:
|
||||||
|
parts = str(pathname).split('.')
|
||||||
|
|
||||||
|
if len(parts) < 2:
|
||||||
|
print(f'Filename "{pathname}" incorrectly formatted, ignoring')
|
||||||
|
continue
|
||||||
|
|
||||||
|
theme_part, conf_part = parts[0], '.'.join(parts[1:])
|
||||||
|
file_parts.append((theme_part, conf_part, pathname))
|
||||||
|
|
||||||
|
return file_parts
|
||||||
|
|
||||||
|
def _get_prefix_order(
|
||||||
|
self,
|
||||||
|
scheme,
|
||||||
|
palette,
|
||||||
|
strict=False,
|
||||||
|
):
|
||||||
|
if strict:
|
||||||
|
theme_order = [
|
||||||
|
(palette, scheme),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
# inverse order of match relaxation; intention being to overwrite with
|
||||||
|
# results from increasingly relevant groups given the conditions
|
||||||
|
if palette == 'any' and scheme == 'any':
|
||||||
|
# prefer both be "none", with preference for specific scheme
|
||||||
|
theme_order = [
|
||||||
|
(palette , scheme),
|
||||||
|
(palette , 'none'),
|
||||||
|
('none' , scheme),
|
||||||
|
('none' , 'none'),
|
||||||
|
]
|
||||||
|
elif palette == 'any':
|
||||||
|
# prefer palette to be "none", then specific, then relax specific scheme
|
||||||
|
# to "none"
|
||||||
|
theme_order = [
|
||||||
|
(palette , 'none'),
|
||||||
|
('none' , 'none'),
|
||||||
|
(palette , scheme),
|
||||||
|
('none' , scheme),
|
||||||
|
]
|
||||||
|
elif scheme == 'any':
|
||||||
|
# prefer scheme to be "none", then specific, then relax specific palette
|
||||||
|
# to "none"
|
||||||
|
theme_order = [
|
||||||
|
('none' , scheme),
|
||||||
|
('none' , 'none'),
|
||||||
|
(palette , scheme),
|
||||||
|
(palette , 'none'),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
# neither component is any; prefer most specific
|
||||||
|
theme_order = [
|
||||||
|
('none' , 'none'),
|
||||||
|
('none' , scheme),
|
||||||
|
(palette , 'none'),
|
||||||
|
(palette , scheme),
|
||||||
|
]
|
||||||
|
|
||||||
|
return theme_order
|
||||||
|
|
||||||
|
def match_pathnames(
|
||||||
|
self,
|
||||||
|
pathnames,
|
||||||
|
scheme,
|
||||||
|
palette,
|
||||||
|
prefix_order=None,
|
||||||
|
strict=False,
|
||||||
|
):
|
||||||
|
file_parts = self._get_file_parts(pathnames)
|
||||||
|
|
||||||
|
if prefix_order is None:
|
||||||
|
prefix_order = self._get_prefix_order(
|
||||||
|
scheme,
|
||||||
|
palette,
|
||||||
|
strict=strict,
|
||||||
|
)
|
||||||
|
|
||||||
|
ordered_matches = []
|
||||||
|
for palette_prefix, scheme_prefix in prefix_order:
|
||||||
|
for theme_part, conf_part, pathname in file_parts:
|
||||||
|
theme_split = theme_part.split('-')
|
||||||
|
palette_part, scheme_part = '-'.join(theme_split[:-1]), theme_split[-1]
|
||||||
|
|
||||||
|
palette_match = palette_prefix == palette_part or palette_prefix == 'any'
|
||||||
|
scheme_match = scheme_prefix == scheme_part or scheme_prefix == 'any'
|
||||||
|
if palette_match and scheme_match:
|
||||||
|
ordered_matches.append((conf_part, theme_part, pathname))
|
||||||
|
|
||||||
|
return ordered_matches
|
||||||
|
|
||||||
def get_matching_configs(
|
def get_matching_configs(
|
||||||
self,
|
self,
|
||||||
app_name,
|
app_name,
|
||||||
scheme='auto',
|
scheme='auto',
|
||||||
palette='auto',
|
palette='auto',
|
||||||
) -> dict[str, str]:
|
strict=False,
|
||||||
|
) -> dict[str, Path]:
|
||||||
'''
|
'''
|
||||||
Get app config files that match the provided scheme and palette.
|
Get app config files that match the provided scheme and palette.
|
||||||
|
|
||||||
@ -161,42 +257,74 @@ class ConfigManager:
|
|||||||
where ``<palette>-<scheme>`` is the "theme part" and ``<path-name>`` is the "conf
|
where ``<palette>-<scheme>`` is the "theme part" and ``<path-name>`` is the "conf
|
||||||
part." For those config files with the same "conf part," only the entry with the
|
part." For those config files with the same "conf part," only the entry with the
|
||||||
most specific "theme part" will be stored. By "most specific," we mean those
|
most specific "theme part" will be stored. By "most specific," we mean those
|
||||||
entries with the fewest possible components named ``any``, with ties broken in
|
entries with the fewest possible components named ``none``, with ties broken in
|
||||||
favor of a more specific ``palette`` (the only "tie" really possible here is when
|
favor of a more specific ``palette`` (the only "tie" really possible here is when
|
||||||
`any-<scheme>` and `<palette>-any` are both available, in which case the latter
|
``none-<scheme>`` and ``<palette>-none`` are both available, in which case the latter
|
||||||
will overwrite the former).
|
will overwrite the former).
|
||||||
|
|
||||||
|
.. admonition: Edge cases
|
||||||
|
|
||||||
|
There are a few quirks to this matching scheme that yield potentially
|
||||||
|
unintuitive results. As a recap:
|
||||||
|
|
||||||
|
- The "theme part" of a config file name includes both a palette and a scheme
|
||||||
|
component. Either of those parts may be "none," which simply indicates that
|
||||||
|
that particular file does not attempt to change that factor. "none-light,"
|
||||||
|
for instance, might simply set a light background, having no effect on other
|
||||||
|
theme settings.
|
||||||
|
- Non-keyword queries for scheme and palette will always be matched exactly.
|
||||||
|
However, if an exact match is not available, we also look for "none" in each
|
||||||
|
component's place. For example, if we wanted to set "solarized-light" but
|
||||||
|
only "none-light" was available, it would still be set because we can still
|
||||||
|
satisfy the desire scheme (light). The same goes for the palette
|
||||||
|
specification, and if neither match, "none-none" will always be matched if
|
||||||
|
available. Note that if "none" is specified exactly, it will be matched
|
||||||
|
exactly, just like any other value.
|
||||||
|
- During a query, "any" may also be specified for either component, indicating
|
||||||
|
we're okay to match any file's text for that part. For example, if I have
|
||||||
|
two config files ``"p1-dark"`` and ``"p2-dark"``, the query for ``("any",
|
||||||
|
"dark")`` would suggest I'd like the dark scheme but am okay with either
|
||||||
|
palette.
|
||||||
|
|
||||||
|
It's under the "any" keyword where possibly counter-intuitive results may come
|
||||||
|
about. Specifying "any" does not change the mechanism that seeks to optionally
|
||||||
|
match "none" if no specific match is available. For example, suppose we have
|
||||||
|
the config file ``red-none`` (setting red colors regardless of a light/dark
|
||||||
|
mode). If I query for ``("any", "dark")``, ``red-none`` will be matched
|
||||||
|
(supposing there are no more direct matches available). Because we don't a
|
||||||
|
match specifically for the scheme "dark," it gets relaxed to "none." But we
|
||||||
|
indicated we're okay to match any palette. So despite asking for a config that
|
||||||
|
sets a dark scheme and not caring about the palette, we end up with a config
|
||||||
|
that explicitly does nothing about the scheme but sets a particular palette.
|
||||||
|
This matching process is still consistent with what we expect the keywords to
|
||||||
|
do, it just slightly muddies the waters with regard to what can be matched
|
||||||
|
(mostly due to the amount that's happening under the hood here).
|
||||||
|
|
||||||
|
This example is the primary driver behind the optional ``strict`` setting,
|
||||||
|
which in this case would force the dark scheme to be matched (and ultimately
|
||||||
|
find no matches).
|
||||||
|
|
||||||
|
Also: when "any" is used for a component, options with "none" are prioritized,
|
||||||
|
allowing "any" to be as flexible and unassuming as possible (only matching a
|
||||||
|
random specific config among the options if there is no "none" available).
|
||||||
'''
|
'''
|
||||||
app_dir = Path(self.apps_dir, app_name)
|
app_dir = Path(self.apps_dir, app_name)
|
||||||
|
|
||||||
scheme = self.resolve_scheme(scheme)
|
scheme = self._resolve_scheme(scheme)
|
||||||
palette = self.resolve_palette(palette)
|
palette = self._resolve_palette(palette)
|
||||||
|
|
||||||
# now match theme files in order of inc. specificity; for each unique config file
|
|
||||||
# tail, only the most specific matching file sticks
|
|
||||||
file_parts = []
|
|
||||||
app_config_map = self.app_config_map(app_name)
|
app_config_map = self.app_config_map(app_name)
|
||||||
for pathname in app_config_map:
|
|
||||||
parts = pathname.split('.')
|
|
||||||
|
|
||||||
if len(parts) < 2:
|
ordered_matches = self.match_pathnames(
|
||||||
print(f'Filename "{filename}" incorrectly formatted, ignoring')
|
app_config_map,
|
||||||
continue
|
scheme,
|
||||||
|
palette,
|
||||||
theme_part, conf_part = parts[0], '.'.join(parts[1:])
|
strict=strict,
|
||||||
file_parts.append((theme_part, conf_part, pathname))
|
)
|
||||||
|
|
||||||
theme_prefixes = [
|
|
||||||
'any-any',
|
|
||||||
f'any-{scheme}',
|
|
||||||
f'{palette}-any',
|
|
||||||
f'{palette}-{scheme}'
|
|
||||||
]
|
|
||||||
|
|
||||||
matching_file_map = {}
|
matching_file_map = {}
|
||||||
for theme_prefix in theme_prefixes:
|
for conf_part, theme_part, pathname in ordered_matches:
|
||||||
for theme_part, conf_part, pathname in file_parts:
|
matching_file_map[conf_part] = app_config_map[pathname]
|
||||||
if theme_part == theme_prefix:
|
|
||||||
matching_file_map[conf_part] = app_config_map[pathname]
|
|
||||||
|
|
||||||
return matching_file_map
|
return matching_file_map
|
||||||
|
|
||||||
@ -216,7 +344,13 @@ class ConfigManager:
|
|||||||
```
|
```
|
||||||
|
|
||||||
and are matched using the same heuristic employed by config file symlinking
|
and are matched using the same heuristic employed by config file symlinking
|
||||||
procedure (see ``get_matching_configs()``).
|
procedure (see ``get_matching_configs()``), albeit with a forced ``prefix_order``,
|
||||||
|
ordered by increasing specificity. The order is then reversed, and the final list
|
||||||
|
orders the scripts by the first time they appear (intention being to reload
|
||||||
|
specific settings first).
|
||||||
|
|
||||||
|
TODO: consider running just the most specific script? Users might want to design
|
||||||
|
their scripts to be stackable, or they may just be independent.
|
||||||
'''
|
'''
|
||||||
app_dir = Path(self.apps_dir, app_name)
|
app_dir = Path(self.apps_dir, app_name)
|
||||||
call_dir = Path(app_dir, 'call')
|
call_dir = Path(app_dir, 'call')
|
||||||
@ -224,22 +358,23 @@ class ConfigManager:
|
|||||||
if not call_dir.is_dir():
|
if not call_dir.is_dir():
|
||||||
return
|
return
|
||||||
|
|
||||||
theme_prefixes = [
|
prefix_order = [
|
||||||
'any-any',
|
('none' , 'none'),
|
||||||
f'any-{scheme}',
|
('none' , scheme),
|
||||||
f'{palette}-any',
|
(palette , 'none'),
|
||||||
f'{palette}-{scheme}'
|
(palette , scheme),
|
||||||
]
|
]
|
||||||
|
|
||||||
# do it this way to keep order for downstream exec
|
pathnames = [path.name for path in call_dir.iterdir()]
|
||||||
script_list = []
|
ordered_matches = self.match_pathnames(
|
||||||
for theme_prefix in theme_prefixes:
|
pathnames,
|
||||||
for script_path in call_dir.iterdir():
|
scheme,
|
||||||
theme_part = script_path.stem
|
palette,
|
||||||
if theme_part == theme_prefix:
|
prefix_order=prefix_order
|
||||||
script_list.append(script_path)
|
)
|
||||||
|
|
||||||
return list(set(script_list))
|
# flip list to execute by decreasing specificity
|
||||||
|
return list(dict.fromkeys(map(lambda x:Path(call_dir, x[2]), ordered_matches)))[::-1]
|
||||||
|
|
||||||
def update_app_config(
|
def update_app_config(
|
||||||
self,
|
self,
|
||||||
@ -320,19 +455,19 @@ class ConfigManager:
|
|||||||
from_path.symlink_to(to_path)
|
from_path.symlink_to(to_path)
|
||||||
links_succ.append((from_path, to_path))
|
links_succ.append((from_path, to_path))
|
||||||
|
|
||||||
# run matching scripts for app-specific reload
|
# run matching scripts for app-specific reload
|
||||||
# TODO: store the status of this cmd & print with the messages
|
script_list = self.get_matching_scripts(
|
||||||
script_list = self.get_matching_scripts(
|
app_name,
|
||||||
app_name,
|
scheme=scheme,
|
||||||
scheme=scheme,
|
palette=palette,
|
||||||
palette=palette,
|
)
|
||||||
|
|
||||||
|
for script in script_list:
|
||||||
|
print(Fore.BLUE + f'> Running script "{script.relative_to(self.config_dir)}"')
|
||||||
|
output = subprocess.check_output(str(script), shell=True)
|
||||||
|
print(
|
||||||
|
Fore.BLUE + Style.DIM + f'-> Captured script output "{output.decode().strip()}"' + Style.RESET_ALL
|
||||||
)
|
)
|
||||||
for script in script_list:
|
|
||||||
print(Fore.BLUE + f'> Running script "{script.relative_to(self.config_dir}"')
|
|
||||||
output = subprocess.check_output(str(script), shell=True)
|
|
||||||
print(
|
|
||||||
Fore.BLUE + Style.DIM + f'-> Captured script output "{output.decode().strip()}"' + Style.RESET
|
|
||||||
)
|
|
||||||
|
|
||||||
for from_p, to_p in links_succ:
|
for from_p, to_p in links_succ:
|
||||||
from_p = from_p
|
from_p = from_p
|
||||||
@ -355,16 +490,16 @@ class ConfigManager:
|
|||||||
app_list = list(self.app_registry.keys())
|
app_list = list(self.app_registry.keys())
|
||||||
else:
|
else:
|
||||||
# get requested apps that overlap with registry
|
# get requested apps that overlap with registry
|
||||||
app_list = [a for a in app_list if a in app_registry]
|
app_list = [a for a in apps if a in self.app_registry]
|
||||||
|
|
||||||
if not app_list:
|
if not app_list:
|
||||||
print(f'None of the apps "apps" are registered, exiting')
|
print(f'None of the apps "{apps}" are registered, exiting')
|
||||||
return
|
return
|
||||||
|
|
||||||
for app_name in app_list:
|
for app_name in app_list:
|
||||||
self.update_app_config(
|
self.update_app_config(
|
||||||
app_name,
|
app_name,
|
||||||
app_settings=app_registry[app_name],
|
app_settings=self.app_registry[app_name],
|
||||||
scheme=scheme,
|
scheme=scheme,
|
||||||
palette=palette,
|
palette=palette,
|
||||||
)
|
)
|
@ -6,4 +6,4 @@ def absolute_path(path: str | Path) -> Path:
|
|||||||
return Path(path).expanduser().absolute()
|
return Path(path).expanduser().absolute()
|
||||||
|
|
||||||
def xdg_config_path():
|
def xdg_config_path():
|
||||||
return Path(BaseDirectory.save_config_path('autoconf'))
|
return Path(BaseDirectory.save_config_path('symconf'))
|
@ -1 +0,0 @@
|
|||||||
echo "> Testing script"
|
|
1
tests/test-config-dir/apps/test/call/none-light.sh
Executable file
1
tests/test-config-dir/apps/test/call/none-light.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
echo "> none-light ran"
|
1
tests/test-config-dir/apps/test/call/none-none.sh
Executable file
1
tests/test-config-dir/apps/test/call/none-none.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
echo "> none-none ran"
|
1
tests/test-config-dir/apps/test/call/test-none.sh
Executable file
1
tests/test-config-dir/apps/test/call/test-none.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
echo "> test-none ran"
|
0
tests/test-config-dir/apps/test/user/none-light.aaa
Normal file
0
tests/test-config-dir/apps/test/user/none-light.aaa
Normal file
0
tests/test-config-dir/apps/test/user/none-none.aaa
Normal file
0
tests/test-config-dir/apps/test/user/none-none.aaa
Normal file
0
tests/test-config-dir/apps/test/user/test-dark.bbb
Normal file
0
tests/test-config-dir/apps/test/user/test-dark.bbb
Normal file
0
tests/test-config-dir/apps/test/user/test-light.ccc
Normal file
0
tests/test-config-dir/apps/test/user/test-light.ccc
Normal file
1
tests/test-config-dir/sym_tgt/aaa
Symbolic link
1
tests/test-config-dir/sym_tgt/aaa
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
/home/smgr/Documents/projects/olog/symconf/tests/test-config-dir/apps/test/user/none-none.aaa
|
1
tests/test-config-dir/sym_tgt/bbb
Symbolic link
1
tests/test-config-dir/sym_tgt/bbb
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
/home/smgr/Documents/projects/olog/symconf/tests/test-config-dir/apps/test/user/test-dark.bbb
|
1
tests/test-config-dir/sym_tgt/ccc
Symbolic link
1
tests/test-config-dir/sym_tgt/ccc
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
/home/smgr/Documents/projects/olog/symconf/tests/test-config-dir/apps/test/user/test-light.ccc
|
1
tests/test-config-dir/sym_tgt/ddd
Symbolic link
1
tests/test-config-dir/sym_tgt/ddd
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
/home/smgr/Documents/projects/olog/symconf/tests/test-config-dir/apps/test/generated/test-none.ddd
|
@ -0,0 +1,131 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from symconf import ConfigManager
|
||||||
|
|
||||||
|
|
||||||
|
config_dir = Path(
|
||||||
|
__file__, '..', 'test-config-dir/'
|
||||||
|
).resolve()
|
||||||
|
cm = ConfigManager(config_dir)
|
||||||
|
|
||||||
|
def test_config_map():
|
||||||
|
file_map = cm.app_config_map('test')
|
||||||
|
|
||||||
|
# from user
|
||||||
|
assert 'none-none.aaa' in file_map
|
||||||
|
assert 'none-light.aaa' in file_map
|
||||||
|
assert 'test-dark.bbb' in file_map
|
||||||
|
assert 'test-light.ccc' in file_map
|
||||||
|
|
||||||
|
# from generated
|
||||||
|
assert 'test-none.aaa' in file_map
|
||||||
|
|
||||||
|
def test_matching_configs_exact():
|
||||||
|
'''
|
||||||
|
Test matching exact palette and scheme. Given strict mode not set (allowing relaxation
|
||||||
|
to "none"), the order of matching should be
|
||||||
|
|
||||||
|
1. (none, none) :: none-none.aaa
|
||||||
|
2. (none, scheme) :: none-light.aaa
|
||||||
|
3. (palette, none) :: test-none.aaa & test-none.ddd
|
||||||
|
4. (palette, scheme) :: test-light.ccc
|
||||||
|
|
||||||
|
Yielding "test-none.aaa", "test-light.ccc", "test-none.ddd" (unique only on path name).
|
||||||
|
'''
|
||||||
|
any_light = cm.get_matching_configs(
|
||||||
|
'test',
|
||||||
|
palette='test',
|
||||||
|
scheme='light',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(any_light) == 3
|
||||||
|
assert any_light['aaa'].name == 'test-none.aaa'
|
||||||
|
assert any_light['ccc'].name == 'test-light.ccc'
|
||||||
|
assert any_light['ddd'].name == 'test-none.ddd'
|
||||||
|
|
||||||
|
def test_matching_configs_any_palette():
|
||||||
|
'''
|
||||||
|
Test matching exact palette and scheme. Given strict mode not set (allowing relaxation
|
||||||
|
to "none"), the order of matching should be
|
||||||
|
|
||||||
|
1. (palette, none) :: test-none.aaa & test-none.ddd & none-none.aaa
|
||||||
|
2. (none, none) :: none-none.aaa
|
||||||
|
3. (palette, scheme) :: test-dark.bbb
|
||||||
|
4. (none, scheme) :: (nothing)
|
||||||
|
|
||||||
|
Yielding "none-none.aaa" (should always overwrite "test-none.aaa" due to "any"'s
|
||||||
|
preference for non-specific matches, i.e., "none"s), "test-none.ddd", "test-dark.bbb"
|
||||||
|
(unique only on path name).
|
||||||
|
'''
|
||||||
|
any_dark = cm.get_matching_configs(
|
||||||
|
'test',
|
||||||
|
palette='any',
|
||||||
|
scheme='dark',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(any_dark) == 3
|
||||||
|
assert any_dark['aaa'].name == 'none-none.aaa'
|
||||||
|
assert any_dark['bbb'].name == 'test-dark.bbb'
|
||||||
|
assert any_dark['ddd'].name == 'test-none.ddd'
|
||||||
|
|
||||||
|
def test_matching_configs_any_scheme():
|
||||||
|
'''
|
||||||
|
Test matching exact palette and scheme. Given strict mode not set (allowing relaxation
|
||||||
|
to "none"), the order of matching should be
|
||||||
|
|
||||||
|
1. (none, scheme) :: none-light.aaa & none-none.aaa
|
||||||
|
2. (none, none) :: none-none.aaa
|
||||||
|
3. (palette, scheme) :: test-dark.bbb & test-light.ccc & test-none.aaa & test-none.ddd
|
||||||
|
4. (palette, none) :: test-none.aaa & test-none.ddd
|
||||||
|
|
||||||
|
Yielding "test-none.aaa", "test-none.ddd", "test-light.ccc", "test-dark.bbb"
|
||||||
|
'''
|
||||||
|
test_any = cm.get_matching_configs(
|
||||||
|
'test',
|
||||||
|
palette='test',
|
||||||
|
scheme='any',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(test_any) == 4
|
||||||
|
assert test_any['aaa'].name == 'test-none.aaa'
|
||||||
|
assert test_any['bbb'].name == 'test-dark.bbb'
|
||||||
|
assert test_any['ccc'].name == 'test-light.ccc'
|
||||||
|
assert test_any['ddd'].name == 'test-none.ddd'
|
||||||
|
|
||||||
|
def test_matching_scripts():
|
||||||
|
'''
|
||||||
|
Test matching exact palette and scheme. Given strict mode not set (allowing relaxation
|
||||||
|
to "none"), the order of matching should be
|
||||||
|
|
||||||
|
1. (none, none) :: none-none.sh
|
||||||
|
2. (none, scheme) :: none-light.sh
|
||||||
|
3. (palette, none) :: test-none.sh
|
||||||
|
4. (palette, scheme) :: (nothing)
|
||||||
|
|
||||||
|
Yielding (ordered by dec specificity) "test-none.sh", "none-light.sh", "none-none.sh".
|
||||||
|
'''
|
||||||
|
test_any = cm.get_matching_scripts(
|
||||||
|
'test',
|
||||||
|
palette='test',
|
||||||
|
scheme='any',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(test_any) == 3
|
||||||
|
assert test_any == ['test-none.sh', 'none-light.sh', 'none-none.sh']
|
||||||
|
|
||||||
|
any_light = cm.get_matching_scripts(
|
||||||
|
'test',
|
||||||
|
palette='any',
|
||||||
|
scheme='light',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(any_light) == 3
|
||||||
|
assert any_light == ['test-none.sh', 'none-light.sh', 'none-none.sh']
|
||||||
|
|
||||||
|
any_dark = cm.get_matching_scripts(
|
||||||
|
'test',
|
||||||
|
palette='any',
|
||||||
|
scheme='dark',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(any_dark) == 2
|
||||||
|
assert any_dark == ['test-none.sh', 'none-none.sh']
|
@ -1,5 +1,5 @@
|
|||||||
def test_imports():
|
def test_imports():
|
||||||
from autoconf import ConfigManager
|
from symconf import ConfigManager
|
||||||
|
|
||||||
from autoconf import config
|
from symconf import config
|
||||||
from autoconf import theme
|
from symconf import theme
|
||||||
|
Loading…
Reference in New Issue
Block a user