Add runtime language configuration (#1794) (#1866)

* Add runtime language configuration (#1794)

* Add set-language typable command to change the language of current buffer.
* Add completer for available language options.

* Update set-language to refresh language server as well

* Add language id based config lookup on `syntax::Loader`.
* Add `Document::set_language3` to set programming language based on language
  id.
* Update `Editor::refresh_language_server` to try language detection only if
  language is not already set.

* Remove language detection from Editor::refresh_language_server

* Move document language detection to where the scratch buffer is saved.
* Rename Document::set_language3 to Document::set_language_by_language_id.

* Remove unnecessary clone in completers::language
pull/1962/head
Roland Kovacs 3 years ago committed by GitHub
parent 6fc6f87260
commit d962e06e91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -53,6 +53,7 @@
| `:hsplit-new`, `:hnew` | Open a scratch buffer in a horizontal split. | | `:hsplit-new`, `:hnew` | Open a scratch buffer in a horizontal split. |
| `:tutor` | Open the tutorial. | | `:tutor` | Open the tutorial. |
| `:goto`, `:g` | Go to line number. | | `:goto`, `:g` | Go to line number. |
| `:set-language`, `:lang` | Set the language of current buffer. |
| `:set-option`, `:set` | Set a config option at runtime | | `:set-option`, `:set` | Set a config option at runtime |
| `:sort` | Sort ranges in selection. | | `:sort` | Sort ranges in selection. |
| `:rsort` | Sort ranges in selection in reverse order. | | `:rsort` | Sort ranges in selection in reverse order. |

@ -504,6 +504,13 @@ impl Loader {
.cloned() .cloned()
} }
pub fn language_config_for_language_id(&self, id: &str) -> Option<Arc<LanguageConfiguration>> {
self.language_configs
.iter()
.find(|config| config.language_id == id)
.cloned()
}
pub fn language_configuration_for_injection_string( pub fn language_configuration_for_injection_string(
&self, &self,
string: &str, string: &str,
@ -529,6 +536,10 @@ impl Loader {
None None
} }
pub fn language_configs(&self) -> impl Iterator<Item = &Arc<LanguageConfiguration>> {
self.language_configs.iter()
}
pub fn set_scopes(&self, scopes: Vec<String>) { pub fn set_scopes(&self, scopes: Vec<String>) {
self.scopes.store(Arc::new(scopes)); self.scopes.store(Arc::new(scopes));

@ -217,6 +217,7 @@ fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::
if path.is_some() { if path.is_some() {
let id = doc.id(); let id = doc.id();
doc.detect_language(cx.editor.syn_loader.clone());
let _ = cx.editor.refresh_language_server(id); let _ = cx.editor.refresh_language_server(id);
} }
Ok(()) Ok(())
@ -931,6 +932,24 @@ fn setting(
Ok(()) Ok(())
} }
/// Change the language of the current buffer at runtime.
fn language(
cx: &mut compositor::Context,
args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
if args.len() != 1 {
anyhow::bail!("Bad arguments. Usage: `:set-language language`");
}
let doc = doc_mut!(cx.editor);
doc.set_language_by_language_id(&args[0], cx.editor.syn_loader.clone());
let id = doc.id();
cx.editor.refresh_language_server(id);
Ok(())
}
fn sort( fn sort(
cx: &mut compositor::Context, cx: &mut compositor::Context,
args: &[Cow<str>], args: &[Cow<str>],
@ -1408,6 +1427,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
fun: goto_line_number, fun: goto_line_number,
completer: None, completer: None,
}, },
TypableCommand {
name: "set-language",
aliases: &["lang"],
doc: "Set the language of current buffer.",
fun: language,
completer: Some(completers::language),
},
TypableCommand { TypableCommand {
name: "set-option", name: "set-option",
aliases: &["set"], aliases: &["set"],

@ -298,6 +298,27 @@ pub mod completers {
}) })
} }
pub fn language(editor: &Editor, input: &str) -> Vec<Completion> {
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, score))
})
.collect();
matches.sort_unstable_by_key(|(_language, score)| Reverse(*score));
matches
.into_iter()
.map(|(language, _score)| ((0..), language.clone().into()))
.collect()
}
pub fn directory(_editor: &Editor, input: &str) -> Vec<Completion> { pub fn directory(_editor: &Editor, input: &str) -> Vec<Completion> {
filename_impl(input, |entry| { filename_impl(input, |entry| {
let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir()); let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());

@ -607,6 +607,17 @@ impl Document {
self.set_language(language_config, Some(config_loader)); self.set_language(language_config, Some(config_loader));
} }
/// Set the programming language for the file if you know the language but don't have the
/// [`syntax::LanguageConfiguration`] for it.
pub fn set_language_by_language_id(
&mut self,
language_id: &str,
config_loader: Arc<syntax::Loader>,
) {
let language_config = config_loader.language_config_for_language_id(language_id);
self.set_language(language_config, Some(config_loader));
}
/// Set the LSP. /// Set the LSP.
pub fn set_language_server(&mut self, language_server: Option<Arc<helix_lsp::Client>>) { pub fn set_language_server(&mut self, language_server: Option<Arc<helix_lsp::Client>>) {
self.language_server = language_server; self.language_server = language_server;

@ -448,7 +448,6 @@ impl Editor {
/// Refreshes the language server for a given document /// Refreshes the language server for a given document
pub fn refresh_language_server(&mut self, doc_id: DocumentId) -> Option<()> { pub fn refresh_language_server(&mut self, doc_id: DocumentId) -> Option<()> {
let doc = self.documents.get_mut(&doc_id)?; let doc = self.documents.get_mut(&doc_id)?;
doc.detect_language(self.syn_loader.clone());
Self::launch_language_server(&mut self.language_servers, doc) Self::launch_language_server(&mut self.language_servers, doc)
} }

Loading…
Cancel
Save