Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(lsp): implement show document request #8865

Merged
merged 5 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions helix-lsp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ pub enum MethodCall {
WorkspaceConfiguration(lsp::ConfigurationParams),
RegisterCapability(lsp::RegistrationParams),
UnregisterCapability(lsp::UnregistrationParams),
ShowDocument(lsp::ShowDocumentParams),
}

impl MethodCall {
Expand Down Expand Up @@ -576,6 +577,10 @@ impl MethodCall {
let params: lsp::UnregistrationParams = params.parse()?;
Self::UnregisterCapability(params)
}
lsp::request::ShowDocument::METHOD => {
let params: lsp::ShowDocumentParams = params.parse()?;
Self::ShowDocument(params)
}
_ => {
return Err(Error::Unhandled);
}
Expand Down
70 changes: 70 additions & 0 deletions helix-term/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use futures_util::Stream;
use helix_core::{path::get_relative_path, pos_at_coords, syntax, Selection};
use helix_lsp::{
lsp::{self, notification::Notification},
util::lsp_range_to_range,
LspProgressMap,
};
use helix_view::{
Expand Down Expand Up @@ -1100,6 +1101,13 @@ impl Application {
}
Ok(serde_json::Value::Null)
}
Ok(MethodCall::ShowDocument(params)) => {
let language_server = language_server!();
let offset_encoding = language_server.offset_encoding();

let result = self.handle_show_document(params, offset_encoding);
Ok(json!(result))
}
};

tokio::spawn(language_server!().reply(id, reply));
Expand All @@ -1108,6 +1116,68 @@ impl Application {
}
}

fn handle_show_document(
&mut self,
params: lsp::ShowDocumentParams,
offset_encoding: helix_lsp::OffsetEncoding,
) -> lsp::ShowDocumentResult {
if let lsp::ShowDocumentParams {
external: Some(true),
uri,
..
} = params
{
self.jobs.callback(crate::open_external_url_callback(uri));
return lsp::ShowDocumentResult { success: true };
};

let lsp::ShowDocumentParams {
uri,
selection,
take_focus,
..
} = params;

let path = match uri.to_file_path() {
Ok(path) => path,
Err(err) => {
log::error!("unsupported file URI: {}: {:?}", uri, err);
return lsp::ShowDocumentResult { success: false };
}
};

let action = match take_focus {
Some(true) => helix_view::editor::Action::Replace,
_ => helix_view::editor::Action::VerticalSplit,
};

let doc_id = match self.editor.open(&path, action) {
Ok(id) => id,
Err(err) => {
log::error!("failed to open path: {:?}: {:?}", uri, err);
return lsp::ShowDocumentResult { success: false };
}
};

let doc = doc_mut!(self.editor, &doc_id);
if let Some(range) = selection {
// TODO: convert inside server
if let Some(new_range) = lsp_range_to_range(doc.text(), range, offset_encoding) {
let view = view_mut!(self.editor);

// we flip the range so that the cursor sits on the start of the symbol
// (for example start of the function).
doc.set_selection(view.id, Selection::single(new_range.head, new_range.anchor));
if action.align_view(view, doc.id()) {
align_view(doc, view, Align::Center);
}
} else {
log::warn!("lsp position out of bounds - {:?}", range);
};
};
lsp::ShowDocumentResult { success: true }
}

async fn claim_term(&mut self) -> std::io::Result<()> {
let terminal_config = self.config.load().editor.clone().into();
self.terminal.claim(terminal_config)
Expand Down
23 changes: 4 additions & 19 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1227,7 +1227,7 @@ fn open_url(cx: &mut Context, url: Url, action: Action) {
.unwrap_or_default();

if url.scheme() != "file" {
return open_external_url(cx, url);
return cx.jobs.callback(crate::open_external_url_callback(url));
}

let content_type = std::fs::File::open(url.path()).and_then(|file| {
Expand All @@ -1240,7 +1240,9 @@ fn open_url(cx: &mut Context, url: Url, action: Action) {
// we attempt to open binary files - files that can't be open in helix - using external
// program as well, e.g. pdf files or images
match content_type {
Ok(content_inspector::ContentType::BINARY) => open_external_url(cx, url),
Ok(content_inspector::ContentType::BINARY) => {
cx.jobs.callback(crate::open_external_url_callback(url))
}
Ok(_) | Err(_) => {
let path = &rel_path.join(url.path());
if path.is_dir() {
Expand All @@ -1253,23 +1255,6 @@ fn open_url(cx: &mut Context, url: Url, action: Action) {
}
}

/// Opens URL in external program.
fn open_external_url(cx: &mut Context, url: Url) {
let commands = open::commands(url.as_str());
cx.jobs.callback(async {
for cmd in commands {
let mut command = tokio::process::Command::new(cmd.get_program());
command.args(cmd.get_args());
if command.output().await.is_ok() {
return Ok(job::Callback::Editor(Box::new(|_| {})));
}
}
Ok(job::Callback::Editor(Box::new(move |editor| {
editor.set_error("Opening URL in external program failed")
})))
});
}

fn extend_word_impl<F>(cx: &mut Context, extend_fn: F)
where
F: Fn(RopeSlice, Range, usize) -> Range,
Expand Down
23 changes: 23 additions & 0 deletions helix-term/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ pub mod keymap;
pub mod ui;
use std::path::Path;

use futures_util::Future;
use ignore::DirEntry;
use url::Url;

pub use keymap::macros::*;

#[cfg(not(windows))]
fn true_color() -> bool {
Expand Down Expand Up @@ -46,3 +50,22 @@ fn filter_picker_entry(entry: &DirEntry, root: &Path, dedup_symlinks: bool) -> b

true
}

/// Opens URL in external program.
fn open_external_url_callback(
matoous marked this conversation as resolved.
Show resolved Hide resolved
url: Url,
) -> impl Future<Output = Result<job::Callback, anyhow::Error>> + Send + 'static {
let commands = open::commands(url.as_str());
async {
for cmd in commands {
let mut command = tokio::process::Command::new(cmd.get_program());
command.args(cmd.get_args());
if command.output().await.is_ok() {
return Ok(job::Callback::Editor(Box::new(|_| {})));
}
}
Ok(job::Callback::Editor(Box::new(move |editor| {
editor.set_error("Opening URL in external program failed")
})))
}
}
Loading