@@ -1,255 +1,35 @@
1use std::{env, error::Error};
7use std::{env, error::Error};
3type DynError = Box<dyn Error>;
9type DynError = Box<dyn Error>;
6− use std::path::{Path, PathBuf};
9− use helix_core::syntax::Configuration as LangConfig;
10− use helix_term::health::TsFeature;
12− /// Get the list of languages that support a particular tree-sitter
14− pub fn ts_lang_support(feat: TsFeature) -> Vec<String> {
15− let queries_dir = path::ts_queries();
17− find_files(&queries_dir, feat.runtime_filename())
20− // .../helix/runtime/queries/python/highlights.scm
21− let tail = f.strip_prefix(&queries_dir).unwrap(); // python/highlights.scm
22− let lang = tail.components().next().unwrap(); // python
23− lang.as_os_str().to_string_lossy().to_string()
28− /// Get the list of languages that have any form of tree-sitter
29− /// queries defined in the runtime directory.
30− pub fn langs_with_ts_queries() -> Vec<String> {
31− std::fs::read_dir(path::ts_queries())
33− .filter_map(|entry| {
34− let entry = entry.ok()?;
39− .then(|| entry.file_name().to_string_lossy().to_string())
44− // naive implementation, but suffices for our needs
45− pub fn find_files(dir: &Path, filename: &str) -> Vec<PathBuf> {
46− std::fs::read_dir(dir)
48− .filter_map(|entry| {
49− let path = entry.ok()?.path();
51− Some(find_files(&path, filename))
53− (path.file_name()?.to_string_lossy() == filename).then(|| vec![path])
60− pub fn lang_config() -> LangConfig {
61− let bytes = std::fs::read(path::lang_config()).unwrap();
62− toml::from_slice(&bytes).unwrap()
71− use helix_term::commands::TYPABLE_COMMAND_LIST;
72− use helix_term::health::TsFeature;
75− pub const TYPABLE_COMMANDS_MD_OUTPUT: &str = "typable-cmd.md";
76− pub const LANG_SUPPORT_MD_OUTPUT: &str = "lang-support.md";
78− fn md_table_heading(cols: &[String]) -> String {
79− let mut header = String::new();
80− header += &md_table_row(cols);
81− header += &md_table_row(&vec!["---".to_string(); cols.len()]);
85− fn md_table_row(cols: &[String]) -> String {
86− format!("| {} |\n", cols.join(" | "))
89− fn md_mono(s: &str) -> String {
93− pub fn typable_commands() -> Result<String, DynError> {
94− let mut md = String::new();
95− md.push_str(&md_table_heading(&[
97− "Description".to_owned(),
100− let cmdify = |s: &str| format!("`:{}`", s);
102− for cmd in TYPABLE_COMMAND_LIST {
103− let names = std::iter::once(&cmd.name)
104− .chain(cmd.aliases.iter())
106− .collect::<Vec<_>>()
109− let doc = cmd.doc.replace('\n', "<br>");
111− md.push_str(&md_table_row(&[names.to_owned(), doc.to_owned()]));
117− pub fn lang_features() -> Result<String, DynError> {
118− let mut md = String::new();
119− let ts_features = TsFeature::all();
121− let mut cols = vec!["Language".to_owned()];
125− .map(|t| t.long_title().to_string())
126− .collect::<Vec<_>>(),
128− cols.push("Default LSP".to_owned());
130− md.push_str(&md_table_heading(&cols));
131− let config = helpers::lang_config();
133− let mut langs = config
136− .map(|l| l.language_id.clone())
137− .collect::<Vec<_>>();
138− langs.sort_unstable();
140− let mut ts_features_to_langs = Vec::new();
141− for &feat in ts_features {
142− ts_features_to_langs.push((feat, helpers::ts_lang_support(feat)));
145− let mut row = Vec::new();
150− .find(|l| l.language_id == lang)
151− .unwrap(); // lang comes from config
152− row.push(lc.language_id.clone());
154− for (_feat, support_list) in &ts_features_to_langs {
156− if support_list.contains(&lang) {
167− .map(|s| s.command.clone())
168− .map(|c| md_mono(&c))
169− .unwrap_or_default(),
172− md.push_str(&md_table_row(&row));
179− pub fn write(filename: &str, data: &str) {
180− let error = format!("Could not write to {}", filename);
181− let path = path::book_gen().join(filename);
182− fs::write(path, data).expect(&error);
187− use std::path::{Path, PathBuf};
189− pub fn project_root() -> PathBuf {
190− Path::new(env!("CARGO_MANIFEST_DIR"))
196− pub fn book_gen() -> PathBuf {
197− project_root().join("book/src/generated/")
200− pub fn ts_queries() -> PathBuf {
201− project_root().join("runtime/queries")
204− pub fn lang_config() -> PathBuf {
205− project_root().join("languages.toml")
209pub mod tasks {
11pub mod tasks {
210− use crate::md_gen;
12+ use crate::docgen::{lang_features, typable_commands, write};
13+ use crate::docgen::{LANG_SUPPORT_MD_OUTPUT, TYPABLE_COMMANDS_MD_OUTPUT};
14+ use crate::querycheck::query_check;
15+ use crate::themelint::{lint, lint_all};
211 use crate::DynError;
16 use crate::DynError;
213 pub fn docgen() -> Result<(), DynError> {
18 pub fn docgen() -> Result<(), DynError> {
215 write(TYPABLE_COMMANDS_MD_OUTPUT, &typable_commands()?);
19 write(TYPABLE_COMMANDS_MD_OUTPUT, &typable_commands()?);
216 write(LANG_SUPPORT_MD_OUTPUT, &lang_features()?);
20 write(LANG_SUPPORT_MD_OUTPUT, &lang_features()?);
220− pub fn query_check() -> Result<(), String> {
24+ pub fn themelint(file: Option<String>) -> Result<(), DynError> {
221− use crate::helpers::lang_config;
25+ match file {
222− use helix_core::{syntax::read_query, tree_sitter::Query};
26+ Some(file) => lint(file),
223− use helix_loader::grammar::get_language;
27+ None => lint_all(),
233− for language in lang_config().language {
234− let language_name = language.language_id.to_ascii_lowercase();
235− let grammar_name = language.grammar.unwrap_or(language.language_id);
236− for query_file in query_files {
237− let language = get_language(&grammar_name);
238− let query_text = read_query(&language_name, query_file);
239− if !query_text.is_empty() && language.is_ok() {
240− if let Err(reason) = Query::new(language.unwrap(), &query_text) {
242− "Failed to parse {} queries for {}: {}",
243− query_file, language_name, reason
250− println!("Query check succeeded");
31+ pub fn querycheck() -> Result<(), DynError> {
255 pub fn print_help() {
35 pub fn print_help() {
@@ -259,6 +39,7 @@ Usage: Run with `cargo xtask <task>`, eg. `cargo xtask docgen`.
261 docgen: Generate files to be included in the mdbook output.
41 docgen: Generate files to be included in the mdbook output.
42+ themelint <theme>: Report errors for <theme>, or all themes if no theme is specified.
262 query-check: Check that tree-sitter queries are valid.
43 query-check: Check that tree-sitter queries are valid.
@@ -271,7 +52,8 @@ fn main() -> Result<(), DynError> {
271 None => tasks::print_help(),
52 None => tasks::print_help(),
272 Some(t) => match t.as_str() {
53 Some(t) => match t.as_str() {
273 "docgen" => tasks::docgen()?,
54 "docgen" => tasks::docgen()?,
55+ "themelint" => tasks::themelint(env::args().nth(2))?,
274− "query-check" => tasks::query_check()?,
56+ "query-check" => tasks::querycheck()?,
275 invalid => return Err(format!("Invalid task name: {}", invalid).into()),
57 invalid => return Err(format!("Invalid task name: {}", invalid).into()),