feat(config): P3 config batch — group 1 (clean config + plumbing)
Config plumbing for the whole config batch (HtmlConfig + WikiConfig fields, RawWiki deserialization, From/defaults) plus these behaviors: - rss_name / rss_max_items: HtmlConfig fields (wired into write_rss in the RSS group). - toc_link_format: build_toc_text emits [[#anchor]] (no description) for 1, [[#anchor|title]] for 0. - generated_links_caption: build_links_text emits [[page|FirstHeading]] from a page->heading map (ops::page_captions) when enabled. - table_reduce_last_col: column_widths clamps the last column to width 1 when set; threaded through table_align_edit/table_move_column_edit + commands. - color_dic now ships a populated default palette (red/green/blue/...). - commentstring aligned to upstream's no-space `%%%s`. - auto_chdir (default off): :lcd into the owning wiki root on buffer enter; both clients (ftplugin.lua setup_auto_chdir + Vim NuwikiAutoChdir augroup), with config.wiki_root_for / nuwiki#commands#wiki_root_for resolvers. Also seeded HtmlConfig fields for later groups (valid_html_tags, list/text_ignore_newline, emoji_enable, user_htmls, color_tag_template) and WikiConfig (create_link, dir_link, bullet_types, cycle_bullets). Tests: toc_link_format, generated_links_caption, table_reduce_last_col, config defaults + JSON round-trip for every new key. Full rust suite + both keymap harnesses green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -108,6 +108,28 @@ pub struct WikiConfig {
|
||||
/// (`tags_header` / `tags_header_level`).
|
||||
pub tags_header: String,
|
||||
pub tags_header_level: u8,
|
||||
/// vimwiki `create_link` (default `true`): when following a link to a
|
||||
/// missing page, create it. When `false`, missing-link follow is a no-op.
|
||||
pub create_link: bool,
|
||||
/// vimwiki `dir_link`: index file opened when following a link to a
|
||||
/// directory (e.g. `index` → `dir/index.wiki`). Empty = open the directory.
|
||||
pub dir_link: String,
|
||||
/// vimwiki `bullet_types`: ordered list of unordered-bullet glyphs for
|
||||
/// this wiki's syntax (default `-`, `*`, `#`); drives `cycle_bullets`.
|
||||
pub bullet_types: Vec<String>,
|
||||
/// vimwiki `cycle_bullets` (default `false`): rotate an unordered item's
|
||||
/// glyph through `bullet_types` as it is indented/dedented.
|
||||
pub cycle_bullets: bool,
|
||||
/// vimwiki `generated_links_caption` (default `false`): emit
|
||||
/// `[[page|Heading]]` (first heading as caption) from `:VimwikiGenerateLinks`.
|
||||
pub generated_links_caption: bool,
|
||||
/// vimwiki `toc_link_format` (default `0`): `0` = `[[#anchor|title]]`
|
||||
/// (leaf anchor, with description); `1` = `[[#parent#child]]` (full path,
|
||||
/// no description).
|
||||
pub toc_link_format: u8,
|
||||
/// vimwiki `table_reduce_last_col` (default `false`): don't pad the last
|
||||
/// table column to fill — keep it at its minimum width.
|
||||
pub table_reduce_last_col: bool,
|
||||
/// HTML export options.
|
||||
pub html: HtmlConfig,
|
||||
}
|
||||
@@ -155,6 +177,32 @@ pub struct HtmlConfig {
|
||||
/// vimwiki `html_header_numbering_sym`: symbol appended after the
|
||||
/// section number (e.g. `.` → `1.2. Heading`). Empty by default.
|
||||
pub html_header_numbering_sym: String,
|
||||
/// vimwiki `rss_name`: filename of the generated RSS feed (default
|
||||
/// `rss.xml`), relative to `html_path`.
|
||||
pub rss_name: String,
|
||||
/// vimwiki `rss_max_items`: cap on the number of diary items in the RSS
|
||||
/// feed (default `10`; `0` = unlimited-but-empty per upstream's min).
|
||||
pub rss_max_items: usize,
|
||||
/// vimwiki `valid_html_tags`: inline HTML tag names that pass through to
|
||||
/// HTML export unescaped (everything else has `<`/`>` escaped). Default
|
||||
/// `b,i,s,u,sub,sup,kbd,br,hr,div,center,strong,em`.
|
||||
pub valid_html_tags: Vec<String>,
|
||||
/// vimwiki `list_ignore_newline` (default `true`): a single newline inside
|
||||
/// a list item is a space in HTML export; when `false` it becomes `<br>`.
|
||||
pub list_ignore_newline: bool,
|
||||
/// vimwiki `text_ignore_newline` (default `true`): a single newline inside
|
||||
/// a paragraph is a space in HTML export; when `false` it becomes `<br>`.
|
||||
pub text_ignore_newline: bool,
|
||||
/// vimwiki `emoji_enable` (default `true`): substitute `:alias:` emoji
|
||||
/// shortcodes with their glyph during HTML export.
|
||||
pub emoji_enable: bool,
|
||||
/// vimwiki `user_htmls`: basenames of HTML files with no wiki source that
|
||||
/// `allToHtml` must not prune. Empty by default.
|
||||
pub user_htmls: Vec<String>,
|
||||
/// vimwiki `color_tag_template`: HTML template for a colour span. The
|
||||
/// `__COLORFG__` / `__COLORBG__` / `__CONTENT__` placeholders are filled
|
||||
/// from `color_dic`. Default matches upstream's `<span style=…>` template.
|
||||
pub color_tag_template: String,
|
||||
}
|
||||
|
||||
impl Default for HtmlConfig {
|
||||
@@ -169,16 +217,56 @@ impl Default for HtmlConfig {
|
||||
auto_export: false,
|
||||
html_filename_parameterization: false,
|
||||
exclude_files: Vec::new(),
|
||||
color_dic: std::collections::HashMap::new(),
|
||||
color_dic: default_color_dic(),
|
||||
custom_wiki2html: String::new(),
|
||||
custom_wiki2html_args: String::new(),
|
||||
base_url: String::new(),
|
||||
html_header_numbering: 0,
|
||||
html_header_numbering_sym: String::new(),
|
||||
rss_name: "rss.xml".into(),
|
||||
rss_max_items: 10,
|
||||
valid_html_tags: default_valid_html_tags(),
|
||||
list_ignore_newline: true,
|
||||
text_ignore_newline: true,
|
||||
emoji_enable: true,
|
||||
user_htmls: Vec::new(),
|
||||
color_tag_template: default_color_tag_template(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// vimwiki's default `valid_html_tags` — inline tags allowed through export.
|
||||
fn default_valid_html_tags() -> Vec<String> {
|
||||
"b,i,s,u,sub,sup,kbd,br,hr,div,center,strong,em"
|
||||
.split(',')
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// vimwiki's default `color_tag_template`.
|
||||
fn default_color_tag_template() -> String {
|
||||
"<span style=\"__STYLE__\">__CONTENT__</span>".to_string()
|
||||
}
|
||||
|
||||
/// A populated default `color_dic` (vimwiki ships a palette rather than the
|
||||
/// empty map nuwiki previously defaulted to). Names → CSS colour value.
|
||||
fn default_color_dic() -> std::collections::HashMap<String, String> {
|
||||
[
|
||||
("default", "inherit"),
|
||||
("red", "red"),
|
||||
("green", "green"),
|
||||
("blue", "blue"),
|
||||
("yellow", "#b8860b"),
|
||||
("magenta", "magenta"),
|
||||
("cyan", "darkcyan"),
|
||||
("gray", "gray"),
|
||||
("grey", "gray"),
|
||||
]
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl HtmlConfig {
|
||||
/// Synthesise default paths under `root` when the user hasn't
|
||||
/// specified explicit ones. `html_path` defaults to `<root>/_html`
|
||||
@@ -257,6 +345,13 @@ impl WikiConfig {
|
||||
links_header_level: d.links_header_level,
|
||||
tags_header: d.tags_header,
|
||||
tags_header_level: d.tags_header_level,
|
||||
create_link: d.create_link,
|
||||
dir_link: d.dir_link,
|
||||
bullet_types: d.bullet_types,
|
||||
cycle_bullets: d.cycle_bullets,
|
||||
generated_links_caption: d.generated_links_caption,
|
||||
toc_link_format: d.toc_link_format,
|
||||
table_reduce_last_col: d.table_reduce_last_col,
|
||||
html: HtmlConfig::default(),
|
||||
}
|
||||
}
|
||||
@@ -297,6 +392,13 @@ impl WikiConfig {
|
||||
links_header_level: d.links_header_level,
|
||||
tags_header: d.tags_header,
|
||||
tags_header_level: d.tags_header_level,
|
||||
create_link: d.create_link,
|
||||
dir_link: d.dir_link,
|
||||
bullet_types: d.bullet_types,
|
||||
cycle_bullets: d.cycle_bullets,
|
||||
generated_links_caption: d.generated_links_caption,
|
||||
toc_link_format: d.toc_link_format,
|
||||
table_reduce_last_col: d.table_reduce_last_col,
|
||||
html,
|
||||
}
|
||||
}
|
||||
@@ -462,9 +564,21 @@ fn wiki_defaults() -> WikiDefaults {
|
||||
links_header_level: 1,
|
||||
tags_header: default_tags_header(),
|
||||
tags_header_level: 1,
|
||||
create_link: true,
|
||||
dir_link: String::new(),
|
||||
bullet_types: default_bullet_types(),
|
||||
cycle_bullets: false,
|
||||
generated_links_caption: false,
|
||||
toc_link_format: 0,
|
||||
table_reduce_last_col: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// vimwiki's default `bullet_types` for the `vimwiki` syntax.
|
||||
fn default_bullet_types() -> Vec<String> {
|
||||
["-", "*", "#"].iter().map(|s| s.to_string()).collect()
|
||||
}
|
||||
|
||||
struct WikiDefaults {
|
||||
index: String,
|
||||
diary_rel_path: String,
|
||||
@@ -491,6 +605,13 @@ struct WikiDefaults {
|
||||
links_header_level: u8,
|
||||
tags_header: String,
|
||||
tags_header_level: u8,
|
||||
create_link: bool,
|
||||
dir_link: String,
|
||||
bullet_types: Vec<String>,
|
||||
cycle_bullets: bool,
|
||||
generated_links_caption: bool,
|
||||
toc_link_format: u8,
|
||||
table_reduce_last_col: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -699,6 +820,22 @@ struct RawWiki {
|
||||
html_header_numbering: Option<u8>,
|
||||
#[serde(default)]
|
||||
html_header_numbering_sym: Option<String>,
|
||||
#[serde(default)]
|
||||
rss_name: Option<String>,
|
||||
#[serde(default)]
|
||||
rss_max_items: Option<usize>,
|
||||
#[serde(default)]
|
||||
valid_html_tags: Option<String>,
|
||||
#[serde(default, deserialize_with = "opt_bool_or_int::deserialize")]
|
||||
list_ignore_newline: Option<bool>,
|
||||
#[serde(default, deserialize_with = "opt_bool_or_int::deserialize")]
|
||||
text_ignore_newline: Option<bool>,
|
||||
#[serde(default, deserialize_with = "opt_bool_or_int::deserialize")]
|
||||
emoji_enable: Option<bool>,
|
||||
#[serde(default)]
|
||||
user_htmls: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
color_tag_template: Option<String>,
|
||||
// per-wiki keys mirroring vimwiki globals. All optional so
|
||||
// existing single-wiki configs migrate without touching anything.
|
||||
#[serde(default)]
|
||||
@@ -747,6 +884,20 @@ struct RawWiki {
|
||||
tags_header: Option<String>,
|
||||
#[serde(default)]
|
||||
tags_header_level: Option<u8>,
|
||||
#[serde(default, deserialize_with = "opt_bool_or_int::deserialize")]
|
||||
create_link: Option<bool>,
|
||||
#[serde(default)]
|
||||
dir_link: Option<String>,
|
||||
#[serde(default)]
|
||||
bullet_types: Option<Vec<String>>,
|
||||
#[serde(default, deserialize_with = "opt_bool_or_int::deserialize")]
|
||||
cycle_bullets: Option<bool>,
|
||||
#[serde(default, deserialize_with = "opt_bool_or_int::deserialize")]
|
||||
generated_links_caption: Option<bool>,
|
||||
#[serde(default)]
|
||||
toc_link_format: Option<u8>,
|
||||
#[serde(default, deserialize_with = "opt_bool_or_int::deserialize")]
|
||||
table_reduce_last_col: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<RawWiki> for WikiConfig {
|
||||
@@ -802,6 +953,30 @@ impl From<RawWiki> for WikiConfig {
|
||||
if let Some(s) = r.html_header_numbering_sym {
|
||||
html.html_header_numbering_sym = s;
|
||||
}
|
||||
if let Some(s) = r.rss_name {
|
||||
html.rss_name = s;
|
||||
}
|
||||
if let Some(n) = r.rss_max_items {
|
||||
html.rss_max_items = n;
|
||||
}
|
||||
if let Some(s) = r.valid_html_tags {
|
||||
html.valid_html_tags = s.split(',').map(|t| t.trim().to_string()).collect();
|
||||
}
|
||||
if let Some(b) = r.list_ignore_newline {
|
||||
html.list_ignore_newline = b;
|
||||
}
|
||||
if let Some(b) = r.text_ignore_newline {
|
||||
html.text_ignore_newline = b;
|
||||
}
|
||||
if let Some(b) = r.emoji_enable {
|
||||
html.emoji_enable = b;
|
||||
}
|
||||
if let Some(v) = r.user_htmls {
|
||||
html.user_htmls = v;
|
||||
}
|
||||
if let Some(s) = r.color_tag_template {
|
||||
html.color_tag_template = s;
|
||||
}
|
||||
let d = wiki_defaults();
|
||||
let syntax = r.syntax.unwrap_or_else(|| "vimwiki".into());
|
||||
// vimwiki derives `list_margin = 0` for markdown wikis when the key is
|
||||
@@ -841,6 +1016,18 @@ impl From<RawWiki> for WikiConfig {
|
||||
links_header_level: r.links_header_level.unwrap_or(d.links_header_level),
|
||||
tags_header: r.tags_header.unwrap_or(d.tags_header),
|
||||
tags_header_level: r.tags_header_level.unwrap_or(d.tags_header_level),
|
||||
create_link: r.create_link.unwrap_or(d.create_link),
|
||||
dir_link: r.dir_link.unwrap_or(d.dir_link),
|
||||
bullet_types: r
|
||||
.bullet_types
|
||||
.filter(|v| !v.is_empty())
|
||||
.unwrap_or(d.bullet_types),
|
||||
cycle_bullets: r.cycle_bullets.unwrap_or(d.cycle_bullets),
|
||||
generated_links_caption: r
|
||||
.generated_links_caption
|
||||
.unwrap_or(d.generated_links_caption),
|
||||
toc_link_format: r.toc_link_format.unwrap_or(d.toc_link_format),
|
||||
table_reduce_last_col: r.table_reduce_last_col.unwrap_or(d.table_reduce_last_col),
|
||||
html,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user