mirror of
https://github.com/ilyakooo0/helix.git
synced 2024-11-29 13:32:09 +03:00
Add LSP workspace command picker (#3140)
* Add workspace command picker * Make command typable * Add optional argument to lsp-workspace-command
This commit is contained in:
parent
3e84434c69
commit
dee5b2a983
@ -45,6 +45,7 @@
|
|||||||
| `:encoding` | Set encoding. Based on `https://encoding.spec.whatwg.org`. |
|
| `:encoding` | Set encoding. Based on `https://encoding.spec.whatwg.org`. |
|
||||||
| `:reload` | Discard changes and reload from the source file. |
|
| `:reload` | Discard changes and reload from the source file. |
|
||||||
| `:update` | Write changes only if the file has been modified. |
|
| `:update` | Write changes only if the file has been modified. |
|
||||||
|
| `:lsp-workspace-command` | Open workspace command picker |
|
||||||
| `:lsp-restart` | Restarts the Language Server that is in use by the current doc |
|
| `:lsp-restart` | Restarts the Language Server that is in use by the current doc |
|
||||||
| `:tree-sitter-scopes` | Display tree sitter scopes, primarily for theming and development. |
|
| `:tree-sitter-scopes` | Display tree sitter scopes, primarily for theming and development. |
|
||||||
| `:debug-start`, `:dbg` | Start a debug session from a given template with given parameters. |
|
| `:debug-start`, `:dbg` | Start a debug session from a given template with given parameters. |
|
||||||
|
@ -298,6 +298,9 @@ impl Client {
|
|||||||
dynamic_registration: Some(false),
|
dynamic_registration: Some(false),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
execute_command: Some(lsp::DynamicRegistrationClientCapabilities {
|
||||||
|
dynamic_registration: Some(false),
|
||||||
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
text_document: Some(lsp::TextDocumentClientCapabilities {
|
text_document: Some(lsp::TextDocumentClientCapabilities {
|
||||||
|
@ -603,6 +603,14 @@ pub fn code_action(cx: &mut Context) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ui::menu::Item for lsp::Command {
|
||||||
|
type Data = ();
|
||||||
|
fn label(&self, _data: &Self::Data) -> Spans {
|
||||||
|
self.title.as_str().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn execute_lsp_command(editor: &mut Editor, cmd: lsp::Command) {
|
pub fn execute_lsp_command(editor: &mut Editor, cmd: lsp::Command) {
|
||||||
let doc = doc!(editor);
|
let doc = doc!(editor);
|
||||||
let language_server = language_server!(editor, doc);
|
let language_server = language_server!(editor, doc);
|
||||||
|
@ -1052,6 +1052,77 @@ fn update(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lsp_workspace_command(
|
||||||
|
cx: &mut compositor::Context,
|
||||||
|
args: &[Cow<str>],
|
||||||
|
event: PromptEvent,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
if event != PromptEvent::Validate {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, doc) = current!(cx.editor);
|
||||||
|
|
||||||
|
let language_server = match doc.language_server() {
|
||||||
|
Some(language_server) => language_server,
|
||||||
|
None => {
|
||||||
|
cx.editor
|
||||||
|
.set_status("Language server not active for current buffer");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let options = match &language_server.capabilities().execute_command_provider {
|
||||||
|
Some(options) => options,
|
||||||
|
None => {
|
||||||
|
cx.editor
|
||||||
|
.set_status("Workspace commands are not supported for this language server");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if args.is_empty() {
|
||||||
|
let commands = options
|
||||||
|
.commands
|
||||||
|
.iter()
|
||||||
|
.map(|command| helix_lsp::lsp::Command {
|
||||||
|
title: command.clone(),
|
||||||
|
command: command.clone(),
|
||||||
|
arguments: None,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let callback = async move {
|
||||||
|
let call: job::Callback = Callback::EditorCompositor(Box::new(
|
||||||
|
move |_editor: &mut Editor, compositor: &mut Compositor| {
|
||||||
|
let picker = ui::Picker::new(commands, (), |cx, command, _action| {
|
||||||
|
execute_lsp_command(cx.editor, command.clone());
|
||||||
|
});
|
||||||
|
compositor.push(Box::new(overlayed(picker)))
|
||||||
|
},
|
||||||
|
));
|
||||||
|
Ok(call)
|
||||||
|
};
|
||||||
|
cx.jobs.callback(callback);
|
||||||
|
} else {
|
||||||
|
let command = args.join(" ");
|
||||||
|
if options.commands.iter().any(|c| c == &command) {
|
||||||
|
execute_lsp_command(
|
||||||
|
cx.editor,
|
||||||
|
helix_lsp::lsp::Command {
|
||||||
|
title: command.clone(),
|
||||||
|
arguments: None,
|
||||||
|
command,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
cx.editor.set_status(format!(
|
||||||
|
"`{command}` is not supported for this language server"
|
||||||
|
));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn lsp_restart(
|
fn lsp_restart(
|
||||||
cx: &mut compositor::Context,
|
cx: &mut compositor::Context,
|
||||||
_args: &[Cow<str>],
|
_args: &[Cow<str>],
|
||||||
@ -1987,6 +2058,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
|
|||||||
fun: update,
|
fun: update,
|
||||||
completer: None,
|
completer: None,
|
||||||
},
|
},
|
||||||
|
TypableCommand {
|
||||||
|
name: "lsp-workspace-command",
|
||||||
|
aliases: &[],
|
||||||
|
doc: "Open workspace command picker",
|
||||||
|
fun: lsp_workspace_command,
|
||||||
|
completer: Some(completers::lsp_workspace_command),
|
||||||
|
},
|
||||||
TypableCommand {
|
TypableCommand {
|
||||||
name: "lsp-restart",
|
name: "lsp-restart",
|
||||||
aliases: &[],
|
aliases: &[],
|
||||||
|
@ -390,6 +390,45 @@ pub mod completers {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lsp_workspace_command(editor: &Editor, input: &str) -> Vec<Completion> {
|
||||||
|
let matcher = Matcher::default();
|
||||||
|
|
||||||
|
let (_, doc) = current_ref!(editor);
|
||||||
|
|
||||||
|
let language_server = match doc.language_server() {
|
||||||
|
Some(language_server) => language_server,
|
||||||
|
None => {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let options = match &language_server.capabilities().execute_command_provider {
|
||||||
|
Some(options) => options,
|
||||||
|
None => {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut matches: Vec<_> = options
|
||||||
|
.commands
|
||||||
|
.iter()
|
||||||
|
.filter_map(|command| {
|
||||||
|
matcher
|
||||||
|
.fuzzy_match(command, input)
|
||||||
|
.map(|score| (command, score))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
matches.sort_unstable_by(|(command1, score1), (command2, score2)| {
|
||||||
|
(Reverse(*score1), command1).cmp(&(Reverse(*score2), command2))
|
||||||
|
});
|
||||||
|
|
||||||
|
matches
|
||||||
|
.into_iter()
|
||||||
|
.map(|(command, _score)| ((0..), command.clone().into()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn directory(editor: &Editor, input: &str) -> Vec<Completion> {
|
pub fn directory(editor: &Editor, input: &str) -> Vec<Completion> {
|
||||||
filename_impl(editor, input, |entry| {
|
filename_impl(editor, 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());
|
||||||
|
Loading…
Reference in New Issue
Block a user