diff --git a/book/src/themes.md b/book/src/themes.md index 0a49053f..d982dee9 100644 --- a/book/src/themes.md +++ b/book/src/themes.md @@ -36,13 +36,6 @@ For inspiration, you can find the default `theme.toml` user-submitted themes [here](https://github.com/helix-editor/helix/blob/master/runtime/themes). -### Using the linter - -Use the supplied linting tool to check for errors and missing scopes: - -```sh -cargo xtask themelint onedark # replace onedark with -``` ## The details of theme creation diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 1421fd1a..aba5c449 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -2,7 +2,6 @@ mod docgen; mod helpers; mod path; mod querycheck; -mod themelint; use std::{env, error::Error}; @@ -12,7 +11,6 @@ pub mod tasks { use crate::docgen::{lang_features, typable_commands, write}; use crate::docgen::{LANG_SUPPORT_MD_OUTPUT, TYPABLE_COMMANDS_MD_OUTPUT}; use crate::querycheck::query_check; - use crate::themelint::{lint, lint_all}; use crate::DynError; pub fn docgen() -> Result<(), DynError> { @@ -21,13 +19,6 @@ pub mod tasks { Ok(()) } - pub fn themelint(file: Option) -> Result<(), DynError> { - match file { - Some(file) => lint(file), - None => lint_all(), - } - } - pub fn querycheck() -> Result<(), DynError> { query_check() } @@ -39,7 +30,6 @@ Usage: Run with `cargo xtask `, eg. `cargo xtask docgen`. Tasks: docgen: Generate files to be included in the mdbook output. - themelint : Report errors for , or all themes if no theme is specified. query-check: Check that tree-sitter queries are valid. " ); @@ -52,7 +42,6 @@ fn main() -> Result<(), DynError> { None => tasks::print_help(), Some(t) => match t.as_str() { "docgen" => tasks::docgen()?, - "themelint" => tasks::themelint(env::args().nth(2))?, "query-check" => tasks::querycheck()?, invalid => return Err(format!("Invalid task name: {}", invalid).into()), }, diff --git a/xtask/src/path.rs b/xtask/src/path.rs index 6f4545c2..62d884f2 100644 --- a/xtask/src/path.rs +++ b/xtask/src/path.rs @@ -18,7 +18,3 @@ pub fn ts_queries() -> PathBuf { pub fn lang_config() -> PathBuf { project_root().join("languages.toml") } - -pub fn themes() -> PathBuf { - project_root().join("runtime/themes") -} diff --git a/xtask/src/themelint.rs b/xtask/src/themelint.rs deleted file mode 100644 index f7efb7d9..00000000 --- a/xtask/src/themelint.rs +++ /dev/null @@ -1,200 +0,0 @@ -use crate::path; -use crate::DynError; -use helix_view::theme::Loader; -use helix_view::theme::Modifier; -use helix_view::Theme; - -struct Rule { - fg: Option<&'static str>, - bg: Option<&'static str>, - check_both: bool, -} - -enum Require { - Existence(Rule), - Difference(&'static str, &'static str), -} - -// Placed in an fn here, so it's the first thing you see -fn get_rules() -> Vec { - vec![ - // Check for ui.selection, which is required - Require::Existence(Rule::has_either("ui.selection")), - Require::Existence(Rule::has_either("ui.selection.primary")), - Require::Difference("ui.selection", "ui.selection.primary"), - // Check for planned readable text - Require::Existence(Rule::has_fg("ui.text")), - Require::Existence(Rule::has_bg("ui.background")), - // Check for complete editor.statusline bare minimum - Require::Existence(Rule::has_both("ui.statusline")), - Require::Existence(Rule::has_both("ui.statusline.inactive")), - // Check for editor.color-modes - Require::Existence(Rule::has_either("ui.statusline.normal")), - Require::Existence(Rule::has_either("ui.statusline.insert")), - Require::Existence(Rule::has_either("ui.statusline.select")), - Require::Difference("ui.statusline.normal", "ui.statusline.insert"), - Require::Difference("ui.statusline.normal", "ui.statusline.select"), - // Check for editor.cursorline - Require::Existence(Rule::has_bg("ui.cursorline.primary")), - // Check for general ui.virtual (such as inlay-hint) - Require::Existence(Rule::has_fg("ui.virtual")), - // Check for editor.whitespace - Require::Existence(Rule::has_fg("ui.virtual.whitespace")), - // Check fir rulers - Require::Existence(Rule::has_either("ui.virtual.indent-guide")), - // Check for editor.rulers - Require::Existence(Rule::has_either("ui.virtual.ruler")), - // Check for menus and prompts - Require::Existence(Rule::has_both("ui.menu")), - Require::Existence(Rule::has_both("ui.help")), - Require::Existence(Rule::has_bg("ui.popup")), - Require::Existence(Rule::has_either("ui.window")), - // Check for visible cursor - Require::Existence(Rule::has_bg("ui.cursor.primary")), - Require::Existence(Rule::has_either("ui.cursor.match")), - ] -} - -impl Rule { - fn has_bg(bg: &'static str) -> Rule { - Rule { - fg: None, - bg: Some(bg), - check_both: true, - } - } - fn has_fg(fg: &'static str) -> Rule { - Rule { - fg: Some(fg), - bg: None, - check_both: true, - } - } - fn has_either(item: &'static str) -> Rule { - Rule { - fg: Some(item), - bg: Some(item), - check_both: false, - } - } - fn has_both(item: &'static str) -> Rule { - Rule { - fg: Some(item), - bg: Some(item), - check_both: true, - } - } - fn found_fg(&self, theme: &Theme) -> bool { - if let Some(fg) = &self.fg { - if theme.get(fg).fg.is_none() && theme.get(fg).add_modifier == Modifier::empty() { - return false; - } - } - true - } - fn found_bg(&self, theme: &Theme) -> bool { - if let Some(bg) = &self.bg { - if theme.get(bg).bg.is_none() && theme.get(bg).add_modifier == Modifier::empty() { - return false; - } - } - true - } - fn rule_name(&self) -> &'static str { - if self.fg.is_some() { - self.fg.unwrap() - } else if self.bg.is_some() { - self.bg.unwrap() - } else { - "LINTER_ERROR_NO_RULE" - } - } - - fn check_difference( - theme: &Theme, - a: &'static str, - b: &'static str, - messages: &mut Vec, - ) { - let theme_a = theme.get(a); - let theme_b = theme.get(b); - if theme_a == theme_b { - messages.push(format!("$THEME: `{}` and `{}` cannot be equal", a, b)); - } - } - - fn check_existence(rule: &Rule, theme: &Theme, messages: &mut Vec) { - let found_fg = rule.found_fg(theme); - let found_bg = rule.found_bg(theme); - - if !rule.check_both && (found_fg || found_bg) { - return; - } - if !found_fg || !found_bg { - let mut missing = vec![]; - if !found_fg { - missing.push("`fg`"); - } - if !found_bg { - missing.push("`bg`"); - } - let entry = if !rule.check_both && !found_fg && !found_bg { - missing.join(" or ") - } else { - missing.join(" and ") - }; - messages.push(format!( - "$THEME: missing {} for `{}`", - entry, - rule.rule_name() - )) - } - } -} - -pub fn lint(file: String) -> Result<(), DynError> { - if file.contains("base16") { - println!("Skipping base16: {}", file); - return Ok(()); - } - let path = path::themes().join(file.clone() + ".toml"); - let theme = std::fs::read_to_string(path).unwrap(); - let theme: Theme = toml::from_str(&theme).expect("Failed to parse theme"); - - let mut messages: Vec = vec![]; - get_rules().iter().for_each(|lint| match lint { - Require::Existence(rule) => Rule::check_existence(rule, &theme, &mut messages), - Require::Difference(a, b) => Rule::check_difference(&theme, a, b, &mut messages), - }); - - if !messages.is_empty() { - messages.iter().for_each(|m| { - let theme = file.clone(); - let message = m.replace("$THEME", theme.as_str()); - println!("{}", message); - }); - Err(format!("{} has issues", file).into()) - } else { - Ok(()) - } -} - -pub fn lint_all() -> Result<(), DynError> { - let files = Loader::read_names(path::themes().as_path()); - let files_count = files.len(); - let ok_files_count = files - .into_iter() - .filter_map(|path| lint(path.replace(".toml", "")).ok()) - .count(); - - if files_count != ok_files_count { - Err(format!( - "{} of {} themes had issues", - files_count - ok_files_count, - files_count - ) - .into()) - } else { - Ok(()) - } -}