Skip to content

Commit

Permalink
Add runtime language configuration (#1794) (#1866)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
zen3ger authored Apr 5, 2022
1 parent 6fc6f87 commit d962e06
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 1 deletion.
1 change: 1 addition & 0 deletions book/src/generated/typable-cmd.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,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. |
Expand Down
11 changes: 11 additions & 0 deletions helix-core/src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,13 @@ impl Loader {
.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(
&self,
string: &str,
Expand All @@ -529,6 +536,10 @@ impl Loader {
None
}

pub fn language_configs(&self) -> impl Iterator<Item = &Arc<LanguageConfiguration>> {
self.language_configs.iter()
}

pub fn set_scopes(&self, scopes: Vec<String>) {
self.scopes.store(Arc::new(scopes));

Expand Down
26 changes: 26 additions & 0 deletions helix-term/src/commands/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::

if path.is_some() {
let id = doc.id();
doc.detect_language(cx.editor.syn_loader.clone());
let _ = cx.editor.refresh_language_server(id);
}
Ok(())
Expand Down Expand Up @@ -931,6 +932,24 @@ fn setting(
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(
cx: &mut compositor::Context,
args: &[Cow<str>],
Expand Down Expand Up @@ -1408,6 +1427,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"],
Expand Down
21 changes: 21 additions & 0 deletions helix-term/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
filename_impl(input, |entry| {
let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
Expand Down
11 changes: 11 additions & 0 deletions helix-view/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,17 @@ impl Document {
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.
pub fn set_language_server(&mut self, language_server: Option<Arc<helix_lsp::Client>>) {
self.language_server = language_server;
Expand Down
1 change: 0 additions & 1 deletion helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,6 @@ impl Editor {
/// Refreshes the language server for a given document
pub fn refresh_language_server(&mut self, doc_id: DocumentId) -> Option<()> {
let doc = self.documents.get_mut(&doc_id)?;
doc.detect_language(self.syn_loader.clone());
Self::launch_language_server(&mut self.language_servers, doc)
}

Expand Down

0 comments on commit d962e06

Please sign in to comment.