From 3e5f24a9d5cec26697a75e515bff46de418b32da Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Wed, 14 Apr 2021 15:30:15 +0900 Subject: lsp: support both utf-8 and utf-16 offsets. Still need to implement the clangd encoding negotiation, but it's a start. Should also manually override to utf8 for pyls. --- helix-lsp/src/client.rs | 17 +++++++---- helix-lsp/src/lib.rs | 77 +++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 21 deletions(-) (limited to 'helix-lsp') diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 64070be0..c70e6e78 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -1,6 +1,6 @@ use crate::{ transport::{Payload, Transport}, - Call, Error, Result, + Call, Error, OffsetEncoding, Result, }; use helix_core::{ChangeSet, Rope}; @@ -29,8 +29,7 @@ pub struct Client { pub request_counter: AtomicU64, capabilities: Option, - // TODO: handle PublishDiagnostics Version - // diagnostics: HashMap>, + offset_encoding: OffsetEncoding, } impl Client { @@ -70,6 +69,7 @@ impl Client { capabilities: None, // diagnostics: HashMap::new(), + offset_encoding: OffsetEncoding::Utf8, }; // TODO: async client.initialize() @@ -100,6 +100,10 @@ impl Client { .expect("language server not yet initialized!") } + pub fn offset_encoding(&self) -> OffsetEncoding { + self.offset_encoding + } + /// Execute a RPC request on the language server. pub async fn request(&self, params: R::Params) -> Result where @@ -291,6 +295,7 @@ impl Client { old_text: &Rope, new_text: &Rope, changeset: &ChangeSet, + offset_encoding: OffsetEncoding, ) -> Vec { let mut iter = changeset.changes().iter().peekable(); let mut old_pos = 0; @@ -340,7 +345,7 @@ impl Client { new_pos += i; } Delete(_) => { - let start = pos_to_lsp_pos(new_text, new_pos); + let start = pos_to_lsp_pos(new_text, new_pos, offset_encoding); let end = traverse(start, old_text.slice(old_pos..old_end)); // deletion @@ -351,7 +356,7 @@ impl Client { }); } Insert(s) => { - let start = pos_to_lsp_pos(new_text, new_pos); + let start = pos_to_lsp_pos(new_text, new_pos, offset_encoding); new_pos += s.chars().count(); @@ -413,7 +418,7 @@ impl Client { }] } lsp::TextDocumentSyncKind::Incremental => { - Self::changeset_to_changes(old_text, new_text, changes) + Self::changeset_to_changes(old_text, new_text, changes, self.offset_encoding) } lsp::TextDocumentSyncKind::None => return Ok(()), }; diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 40d2e477..dd925c14 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -16,6 +16,8 @@ use thiserror::Error; use std::{collections::HashMap, sync::Arc}; +use serde::{Deserialize, Serialize}; + #[derive(Error, Debug)] pub enum Error { #[error("protocol error: {0}")] @@ -28,31 +30,76 @@ pub enum Error { Other(#[from] anyhow::Error), } +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub enum OffsetEncoding { + /// UTF-8 code units aka bytes + #[serde(rename = "utf-8")] + Utf8, + /// UTF-16 code units + #[serde(rename = "utf-16")] + Utf16, +} + pub mod util { use super::*; use helix_core::{Range, Rope, Transaction}; - pub fn lsp_pos_to_pos(doc: &Rope, pos: lsp::Position) -> usize { - let line = doc.line_to_char(pos.line as usize); - let line_start = doc.char_to_utf16_cu(line); - doc.utf16_cu_to_char(line_start + pos.character as usize) + pub fn lsp_pos_to_pos( + doc: &Rope, + pos: lsp::Position, + offset_encoding: OffsetEncoding, + ) -> usize { + match offset_encoding { + OffsetEncoding::Utf8 => { + let line = doc.line_to_char(pos.line as usize); + line + pos.character as usize + } + OffsetEncoding::Utf16 => { + let line = doc.line_to_char(pos.line as usize); + let line_start = doc.char_to_utf16_cu(line); + doc.utf16_cu_to_char(line_start + pos.character as usize) + } + } } - pub fn pos_to_lsp_pos(doc: &Rope, pos: usize) -> lsp::Position { - let line = doc.char_to_line(pos); - let line_start = doc.char_to_utf16_cu(doc.line_to_char(line)); - let col = doc.char_to_utf16_cu(pos) - line_start; + pub fn pos_to_lsp_pos( + doc: &Rope, + pos: usize, + offset_encoding: OffsetEncoding, + ) -> lsp::Position { + match offset_encoding { + OffsetEncoding::Utf8 => { + 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) + } + OffsetEncoding::Utf16 => { + let line = doc.char_to_line(pos); + let line_start = doc.char_to_utf16_cu(doc.line_to_char(line)); + let col = doc.char_to_utf16_cu(pos) - line_start; - lsp::Position::new(line as u32, col as u32) + lsp::Position::new(line as u32, col as u32) + } + } } - pub fn range_to_lsp_range(doc: &Rope, range: Range) -> lsp::Range { - let start = pos_to_lsp_pos(doc, range.from()); - let end = pos_to_lsp_pos(doc, range.to()); + pub fn range_to_lsp_range( + doc: &Rope, + range: Range, + offset_encoding: OffsetEncoding, + ) -> lsp::Range { + let start = pos_to_lsp_pos(doc, range.from(), offset_encoding); + let end = pos_to_lsp_pos(doc, range.to(), offset_encoding); lsp::Range::new(start, end) } - pub fn generate_transaction_from_edits(doc: &Rope, edits: Vec) -> Transaction { + pub fn generate_transaction_from_edits( + doc: &Rope, + edits: Vec, + offset_encoding: OffsetEncoding, + ) -> Transaction { Transaction::change( doc, edits.into_iter().map(|edit| { @@ -63,8 +110,8 @@ pub mod util { None }; - let start = lsp_pos_to_pos(doc, edit.range.start); - let end = lsp_pos_to_pos(doc, edit.range.end); + let start = lsp_pos_to_pos(doc, edit.range.start, offset_encoding); + let end = lsp_pos_to_pos(doc, edit.range.end, offset_encoding); (start, end, replacement) }), ) -- cgit v1.2.3-70-g09d2