aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPascal Kuthe2023-02-11 06:50:01 +0000
committerGitHub2023-02-11 06:50:01 +0000
commit93c7afc4ed2b8d848fa2779f43202ba7f837263b (patch)
tree866e07e173686a41b5f164ee40b63b585037232a
parent6929a12f291fa5dee50cde9c89845b206b7333fd (diff)
Negotiate LSP Position Encoding (#5894)
So far LSP always required that `PositionEncoding.characters` is an UTF-16 offset. Now that LSP 3.17 is available in `lsp-types` request the server to send char offsets (UTF-32) or byte offsets (UTF-8) instead. For compatability with old servers, UTF-16 remains as the fallback as required by the standard.
-rw-r--r--Cargo.lock4
-rw-r--r--helix-lsp/Cargo.toml2
-rw-r--r--helix-lsp/src/client.rs29
-rw-r--r--helix-lsp/src/lib.rs22
-rw-r--r--helix-term/src/commands/lsp.rs6
5 files changed, 49 insertions, 14 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1a76beed..2e4ebe20 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1481,9 +1481,9 @@ dependencies = [
[[package]]
name = "lsp-types"
-version = "0.93.2"
+version = "0.94.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9be6e9c7e2d18f651974370d7aff703f9513e0df6e464fd795660edc77e6ca51"
+checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237"
dependencies = [
"bitflags",
"serde",
diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml
index 0db61ad4..733f9727 100644
--- a/helix-lsp/Cargo.toml
+++ b/helix-lsp/Cargo.toml
@@ -19,7 +19,7 @@ anyhow = "1.0"
futures-executor = "0.3"
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
log = "0.4"
-lsp-types = { version = "0.93", features = ["proposed"] }
+lsp-types = { version = "0.94" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs
index cb6e5d81..2f5b498d 100644
--- a/helix-lsp/src/client.rs
+++ b/helix-lsp/src/client.rs
@@ -6,6 +6,7 @@ use crate::{
use helix_core::{find_root, ChangeSet, Rope};
use helix_loader::{self, VERSION_AND_GIT_HASH};
+use lsp::PositionEncodingKind;
use lsp_types as lsp;
use serde::Deserialize;
use serde_json::Value;
@@ -32,7 +33,6 @@ pub struct Client {
server_tx: UnboundedSender<Payload>,
request_counter: AtomicU64,
pub(crate) capabilities: OnceCell<lsp::ServerCapabilities>,
- offset_encoding: OffsetEncoding,
config: Option<Value>,
root_path: std::path::PathBuf,
root_uri: Option<lsp::Url>,
@@ -104,7 +104,6 @@ impl Client {
server_tx,
request_counter: AtomicU64::new(0),
capabilities: OnceCell::new(),
- offset_encoding: OffsetEncoding::Utf16,
config,
req_timeout,
@@ -147,7 +146,19 @@ impl Client {
}
pub fn offset_encoding(&self) -> OffsetEncoding {
- self.offset_encoding
+ self.capabilities()
+ .position_encoding
+ .as_ref()
+ .and_then(|encoding| match encoding.as_str() {
+ "utf-8" => Some(OffsetEncoding::Utf8),
+ "utf-16" => Some(OffsetEncoding::Utf16),
+ "utf-32" => Some(OffsetEncoding::Utf32),
+ encoding => {
+ log::error!("Server provided invalid position encording {encoding}, defaulting to utf-16");
+ None
+ },
+ })
+ .unwrap_or_default()
}
pub fn config(&self) -> Option<&Value> {
@@ -377,6 +388,14 @@ impl Client {
work_done_progress: Some(true),
..Default::default()
}),
+ general: Some(lsp::GeneralClientCapabilities {
+ position_encodings: Some(vec![
+ PositionEncodingKind::UTF32,
+ PositionEncodingKind::UTF8,
+ PositionEncodingKind::UTF16,
+ ]),
+ ..Default::default()
+ }),
..Default::default()
},
trace: None,
@@ -577,7 +596,7 @@ impl Client {
}]
}
lsp::TextDocumentSyncKind::INCREMENTAL => {
- Self::changeset_to_changes(old_text, new_text, changes, self.offset_encoding)
+ Self::changeset_to_changes(old_text, new_text, changes, self.offset_encoding())
}
lsp::TextDocumentSyncKind::NONE => return None,
kind => unimplemented!("{:?}", kind),
@@ -1027,7 +1046,7 @@ impl Client {
partial_result_params: lsp::PartialResultParams::default(),
};
- Some(self.call::<lsp::request::WorkspaceSymbol>(params))
+ Some(self.call::<lsp::request::WorkspaceSymbolRequest>(params))
}
pub fn code_actions(
diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs
index bcaff10a..72456b37 100644
--- a/helix-lsp/src/lib.rs
+++ b/helix-lsp/src/lib.rs
@@ -20,7 +20,6 @@ use std::{
},
};
-use serde::{Deserialize, Serialize};
use thiserror::Error;
use tokio_stream::wrappers::UnboundedReceiverStream;
@@ -45,13 +44,14 @@ pub enum Error {
Other(#[from] anyhow::Error),
}
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, Default)]
pub enum OffsetEncoding {
/// UTF-8 code units aka bytes
- #[serde(rename = "utf-8")]
Utf8,
+ /// UTF-32 code units aka chars
+ Utf32,
/// UTF-16 code units
- #[serde(rename = "utf-16")]
+ #[default]
Utf16,
}
@@ -168,6 +168,11 @@ pub mod util {
let line_end = line_end_char_index(&doc.slice(..), pos_line);
doc.char_to_utf16_cu(line_start)..doc.char_to_utf16_cu(line_end)
}
+ OffsetEncoding::Utf32 => {
+ let line_start = doc.line_to_char(pos_line);
+ let line_end = line_end_char_index(&doc.slice(..), pos_line);
+ line_start..line_end
+ }
};
// The LSP spec demands that the offset is capped to the end of the line
@@ -177,10 +182,10 @@ pub mod util {
.unwrap_or(line.end)
.min(line.end);
- // TODO prefer UTF32/char indices to avoid this step
match offset_encoding {
OffsetEncoding::Utf8 => doc.try_byte_to_char(pos).ok(),
OffsetEncoding::Utf16 => doc.try_utf16_cu_to_char(pos).ok(),
+ OffsetEncoding::Utf32 => Some(pos),
}
}
@@ -207,6 +212,13 @@ pub mod util {
lsp::Position::new(line as u32, col as u32)
}
+ OffsetEncoding::Utf32 => {
+ let line = doc.char_to_line(pos);
+ let line_start = doc.line_to_char(line);
+ let col = pos - line_start;
+
+ lsp::Position::new(line as u32, col as u32)
+ }
}
}
diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs
index d12aa436..d1fb32a8 100644
--- a/helix-term/src/commands/lsp.rs
+++ b/helix-term/src/commands/lsp.rs
@@ -1,7 +1,10 @@
use futures_util::FutureExt;
use helix_lsp::{
block_on,
- lsp::{self, CodeAction, CodeActionOrCommand, DiagnosticSeverity, NumberOrString},
+ lsp::{
+ self, CodeAction, CodeActionOrCommand, CodeActionTriggerKind, DiagnosticSeverity,
+ NumberOrString,
+ },
util::{diagnostic_to_lsp_diagnostic, lsp_pos_to_pos, lsp_range_to_range, range_to_lsp_range},
OffsetEncoding,
};
@@ -561,6 +564,7 @@ pub fn code_action(cx: &mut Context) {
.map(|diag| diagnostic_to_lsp_diagnostic(doc.text(), diag, offset_encoding))
.collect(),
only: None,
+ trigger_kind: Some(CodeActionTriggerKind::INVOKED),
},
) {
Some(future) => future,