on master help sign in
Add runtime language configuration (#1794) (#1866)
d962e06Roland Kovacs
book/src/generated/typable-cmd.md+1
@@ -53,6 +53,7 @@
53| `:hsplit-new`, `:hnew` | Open a scratch buffer in a horizontal split. |
53| `:hsplit-new`, `:hnew` | Open a scratch buffer in a horizontal split. |
54| `:tutor` | Open the tutorial. |
54| `:tutor` | Open the tutorial. |
55| `:goto`, `:g` | Go to line number. |
55| `:goto`, `:g` | Go to line number. |
56+| `:set-language`, `:lang` | Set the language of current buffer. |
56| `:set-option`, `:set` | Set a config option at runtime |
57| `:set-option`, `:set` | Set a config option at runtime |
57| `:sort` | Sort ranges in selection. |
58| `:sort` | Sort ranges in selection. |
58| `:rsort` | Sort ranges in selection in reverse order. |
59| `:rsort` | Sort ranges in selection in reverse order. |
helix-core/src/syntax.rs+11
@@ -504,6 +504,13 @@ impl Loader {
504 .cloned()
504 .cloned()
505 }
505 }
506
506
507+ pub fn language_config_for_language_id(&self, id: &str) -> Option<Arc<LanguageConfiguration>> {
508+ self.language_configs
509+ .iter()
510+ .find(|config| config.language_id == id)
511+ .cloned()
512+ }
513+
507 pub fn language_configuration_for_injection_string(
514 pub fn language_configuration_for_injection_string(
508 &self,
515 &self,
509 string: &str,
516 string: &str,
@@ -529,6 +536,10 @@ impl Loader {
529 None
536 None
530 }
537 }
531
538
539+ pub fn language_configs(&self) -> impl Iterator<Item = &Arc<LanguageConfiguration>> {
540+ self.language_configs.iter()
541+ }
542+
532 pub fn set_scopes(&self, scopes: Vec<String>) {
543 pub fn set_scopes(&self, scopes: Vec<String>) {
533 self.scopes.store(Arc::new(scopes));
544 self.scopes.store(Arc::new(scopes));
534
545
helix-term/src/commands/typed.rs+26
@@ -217,6 +217,7 @@ fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::
217
217
218 if path.is_some() {
218 if path.is_some() {
219 let id = doc.id();
219 let id = doc.id();
220+ doc.detect_language(cx.editor.syn_loader.clone());
220 let _ = cx.editor.refresh_language_server(id);
221 let _ = cx.editor.refresh_language_server(id);
221 }
222 }
222 Ok(())
223 Ok(())
@@ -931,6 +932,24 @@ fn setting(
931 Ok(())
932 Ok(())
932}
933}
933
934
935+/// Change the language of the current buffer at runtime.
936+fn language(
937+ cx: &mut compositor::Context,
938+ args: &[Cow<str>],
939+ _event: PromptEvent,
940+) -> anyhow::Result<()> {
941+ if args.len() != 1 {
942+ anyhow::bail!("Bad arguments. Usage: `:set-language language`");
943+ }
944+
945+ let doc = doc_mut!(cx.editor);
946+ doc.set_language_by_language_id(&args[0], cx.editor.syn_loader.clone());
947+
948+ let id = doc.id();
949+ cx.editor.refresh_language_server(id);
950+ Ok(())
951+}
952+
934fn sort(
953fn sort(
935 cx: &mut compositor::Context,
954 cx: &mut compositor::Context,
936 args: &[Cow<str>],
955 args: &[Cow<str>],
@@ -1408,6 +1427,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
1408 fun: goto_line_number,
1427 fun: goto_line_number,
1409 completer: None,
1428 completer: None,
1410 },
1429 },
1430+ TypableCommand {
1431+ name: "set-language",
1432+ aliases: &["lang"],
1433+ doc: "Set the language of current buffer.",
1434+ fun: language,
1435+ completer: Some(completers::language),
1436+ },
1411 TypableCommand {
1437 TypableCommand {
1412 name: "set-option",
1438 name: "set-option",
1413 aliases: &["set"],
1439 aliases: &["set"],
helix-term/src/ui/mod.rs+21
@@ -298,6 +298,27 @@ pub mod completers {
298 })
298 })
299 }
299 }
300
300
301+ pub fn language(editor: &Editor, input: &str) -> Vec<Completion> {
302+ let matcher = Matcher::default();
303+
304+ let mut matches: Vec<_> = editor
305+ .syn_loader
306+ .language_configs()
307+ .filter_map(|config| {
308+ matcher
309+ .fuzzy_match(&config.language_id, input)
310+ .map(|score| (&config.language_id, score))
311+ })
312+ .collect();
313+
314+ matches.sort_unstable_by_key(|(_language, score)| Reverse(*score));
315+
316+ matches
317+ .into_iter()
318+ .map(|(language, _score)| ((0..), language.clone().into()))
319+ .collect()
320+ }
321+
301 pub fn directory(_editor: &Editor, input: &str) -> Vec<Completion> {
322 pub fn directory(_editor: &Editor, input: &str) -> Vec<Completion> {
302 filename_impl(input, |entry| {
323 filename_impl(input, |entry| {
303 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
324 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
helix-view/src/document.rs+11
@@ -607,6 +607,17 @@ impl Document {
607 self.set_language(language_config, Some(config_loader));
607 self.set_language(language_config, Some(config_loader));
608 }
608 }
609
609
610+ /// Set the programming language for the file if you know the language but don't have the
611+ /// [`syntax::LanguageConfiguration`] for it.
612+ pub fn set_language_by_language_id(
613+ &mut self,
614+ language_id: &str,
615+ config_loader: Arc<syntax::Loader>,
616+ ) {
617+ let language_config = config_loader.language_config_for_language_id(language_id);
618+ self.set_language(language_config, Some(config_loader));
619+ }
620+
610 /// Set the LSP.
621 /// Set the LSP.
611 pub fn set_language_server(&mut self, language_server: Option<Arc<helix_lsp::Client>>) {
622 pub fn set_language_server(&mut self, language_server: Option<Arc<helix_lsp::Client>>) {
612 self.language_server = language_server;
623 self.language_server = language_server;
helix-view/src/editor.rs−1
@@ -448,7 +448,6 @@ impl Editor {
448 /// Refreshes the language server for a given document
448 /// Refreshes the language server for a given document
449 pub fn refresh_language_server(&mut self, doc_id: DocumentId) -> Option<()> {
449 pub fn refresh_language_server(&mut self, doc_id: DocumentId) -> Option<()> {
450 let doc = self.documents.get_mut(&doc_id)?;
450 let doc = self.documents.get_mut(&doc_id)?;
451− doc.detect_language(self.syn_loader.clone());
452 Self::launch_language_server(&mut self.language_servers, doc)
451 Self::launch_language_server(&mut self.language_servers, doc)
453 }
452 }
454
453