diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 1ee2fac4b09e..3c021936b403 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -51,6 +51,7 @@ | `:hsplit-new`, `:hnew` | Open a scratch buffer in a horizontal split. | | `:tutor` | Open the tutorial. | | `:goto`, `:g` | Go to line number. | +| `:set-language`, `:lang` | Set the language of current buffer. | | `:set-option`, `:set` | Set a config option at runtime | | `:sort` | Sort ranges in selection. | | `:rsort` | Sort ranges in selection in reverse order. | diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index d3750e757c6c..5bd712e4eee2 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -537,6 +537,10 @@ impl Loader { None } + pub fn language_configs(&self) -> impl Iterator> { + self.language_configs.iter() + } + pub fn set_scopes(&self, scopes: Vec) { self.scopes.store(Arc::new(scopes)); diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 8b7f481b9c19..4c8b132f7ba0 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -913,6 +913,29 @@ fn setting( Ok(()) } +/// Change the language of the current buffer at runtime. +fn language( + cx: &mut compositor::Context, + args: &[Cow], + _event: PromptEvent, +) -> anyhow::Result<()> { + if args.len() != 1 { + anyhow::bail!("Bad arguments. Usage: `:set-language language`"); + } + + let doc = doc_mut!(cx.editor); + let loader = Some(cx.editor.syn_loader.clone()); + let config = cx.editor.syn_loader.language_configs().find_map(|config| { + if config.language_id == args[0] { + Some(config.clone()) + } else { + None + } + }); + doc.set_language(config, loader); + Ok(()) +} + fn sort( cx: &mut compositor::Context, args: &[Cow], @@ -1376,6 +1399,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ fun: goto_line_number, completer: None, }, + TypableCommand { + name: "set-language", + aliases: &["lang"], + doc: "Set the language of current buffer.", + fun: language, + completer: Some(completers::language), + }, TypableCommand { name: "set-option", aliases: &["set"], diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 6242ea2eed4d..ca1529af4cbe 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -284,6 +284,27 @@ pub mod completers { }) } + pub fn language(editor: &Editor, input: &str) -> Vec { + let matcher = Matcher::default(); + + let mut matches: Vec<_> = editor + .syn_loader + .language_configs() + .filter_map(|config| { + matcher + .fuzzy_match(&config.language_id, input) + .map(|score| (config.language_id.clone(), score)) + }) + .collect(); + + matches.sort_unstable_by_key(|(_language, score)| Reverse(*score)); + + matches + .into_iter() + .map(|(language, _score)| ((0..), language.into())) + .collect() + } + pub fn directory(_editor: &Editor, input: &str) -> Vec { filename_impl(input, |entry| { let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());