feat(cli): support selecting bindings in init cmd

This commit is contained in:
sammyne 2025-06-08 06:44:30 +00:00 committed by ObserverOfTime
parent 64760ffa76
commit 4c51f27b0a
3 changed files with 78 additions and 4 deletions

View file

@ -139,6 +139,7 @@ pub struct JsonConfigOpts {
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<Url>,
pub bindings: Bindings,
}
impl JsonConfigOpts {
@ -185,7 +186,7 @@ impl JsonConfigOpts {
}),
namespace: None,
},
bindings: Bindings::default(),
bindings: self.bindings,
}
}
}
@ -206,6 +207,7 @@ impl Default for JsonConfigOpts {
author: String::new(),
email: None,
url: None,
bindings: Bindings::default(),
}
}
}

View file

@ -8,7 +8,7 @@ use anstyle::{AnsiColor, Color, Style};
use anyhow::{anyhow, Context, Result};
use clap::{crate_authors, Args, Command, FromArgMatches as _, Subcommand, ValueEnum};
use clap_complete::generate;
use dialoguer::{theme::ColorfulTheme, Confirm, FuzzySelect, Input};
use dialoguer::{theme::ColorfulTheme, Confirm, FuzzySelect, Input, MultiSelect};
use heck::ToUpperCamelCase;
use regex::Regex;
use semver::Version as SemverVersion;
@ -30,7 +30,7 @@ use tree_sitter_cli::{
};
use tree_sitter_config::Config;
use tree_sitter_highlight::Highlighter;
use tree_sitter_loader::{self as loader, TreeSitterJSON};
use tree_sitter_loader::{self as loader, Bindings, TreeSitterJSON};
use tree_sitter_tags::TagsContext;
use url::Url;
@ -682,6 +682,21 @@ impl Init {
.map(|e| (!e.trim().is_empty()).then(|| Url::parse(&e).unwrap()))
};
let bindings = || {
let languages = Bindings::default().languages();
let enabled = MultiSelect::new()
.with_prompt("Bindings")
.items_checked(&languages)
.interact()?
.into_iter()
.map(|i| languages[i].0);
let out = Bindings::with_enabled_languages(enabled)
.expect("unexpected unsupported language");
anyhow::Ok(out)
};
let choices = [
"name",
"camelcase",
@ -696,6 +711,7 @@ impl Init {
"author",
"email",
"url",
"bindings",
"exit",
];
@ -715,6 +731,7 @@ impl Init {
"author" => opts.author = author()?,
"email" => opts.email = email()?,
"url" => opts.url = url()?,
"bindings" => opts.bindings = bindings()?,
"exit" => break,
_ => unreachable!(),
}

View file

@ -222,7 +222,7 @@ pub struct Links {
pub homepage: Option<String>,
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Clone)]
#[serde(default)]
pub struct Bindings {
pub c: bool,
@ -238,6 +238,61 @@ pub struct Bindings {
pub zig: bool,
}
impl Bindings {
/// return available languages and its default enabled state.
#[must_use]
pub const fn languages(&self) -> [(&'static str, bool); 7] {
[
("c", true),
("go", true),
// Comment out Java and Kotlin until the bindings are actually available.
// ("java", false),
// ("kotlin", false),
("node", true),
("python", true),
("rust", true),
("swift", true),
("zig", false),
]
}
/// construct Bindings from a language list. If a language isn't supported, its name will be put on the error part.
pub fn with_enabled_languages<'a, I>(languages: I) -> Result<Self, &'a str>
where
I: Iterator<Item = &'a str>,
{
let mut out = Self {
c: false,
go: false,
java: false,
kotlin: false,
node: false,
python: false,
rust: false,
swift: false,
zig: false,
};
for v in languages {
match v {
"c" => out.c = true,
"go" => out.go = true,
// Comment out Java and Kotlin until the bindings are actually available.
// "java" => out.java = true,
// "kotlin" => out.kotlin = true,
"node" => out.node = true,
"python" => out.python = true,
"rust" => out.rust = true,
"swift" => out.swift = true,
"zig" => out.zig = true,
unsupported => return Err(unsupported),
}
}
Ok(out)
}
}
impl Default for Bindings {
fn default() -> Self {
Self {