on master help sign in
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.

commit3e5f24a9d5cec26697a75e515bff46de418b32da authorBlaž Hrastnik <blaz@mxxn.io> dateWed 14 Apr 2021, 15:30 (6 years ago) parent811f952
8 files changed+179−52
helix-lsp/src/client.rs+11−6 helix-lsp/src/lib.rs+62−15 helix-term/src/application.rs+14−2 helix-term/src/commands.rs+59−20 helix-term/src/ui/completion.rs+11−3 helix-term/src/ui/editor.rs+2−1 helix-term/src/ui/mod.rs+1−1 helix-view/src/document.rs+19−4
helix-lsp/src/client.rs+11−6
@@ -1,6 +1,6 @@
11use crate::{
22 transport::{Payload, Transport},
3− Call, Error, Result,
3+ Call, Error, OffsetEncoding, Result,
44};
55
66use helix_core::{ChangeSet, Rope};
@@ -29,8 +29,7 @@ pub struct Client {
2929 pub request_counter: AtomicU64,
3030
3131 capabilities: Option<lsp::ServerCapabilities>,
32− // TODO: handle PublishDiagnostics Version
33− // diagnostics: HashMap<lsp::Url, Vec<lsp::Diagnostic>>,
32+ offset_encoding: OffsetEncoding,
3433}
3534
3635impl Client {
@@ -70,6 +69,7 @@ impl Client {
7069
7170 capabilities: None,
7271 // diagnostics: HashMap::new(),
72+ offset_encoding: OffsetEncoding::Utf8,
7373 };
7474
7575 // TODO: async client.initialize()
@@ -100,6 +100,10 @@ impl Client {
100100 .expect("language server not yet initialized!")
101101 }
102102
103+ pub fn offset_encoding(&self) -> OffsetEncoding {
104+ self.offset_encoding
105+ }
106+
103107 /// Execute a RPC request on the language server.
104108 pub async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result>
105109 where
@@ -291,6 +295,7 @@ impl Client {
291295 old_text: &Rope,
292296 new_text: &Rope,
293297 changeset: &ChangeSet,
298+ offset_encoding: OffsetEncoding,
294299 ) -> Vec<lsp::TextDocumentContentChangeEvent> {
295300 let mut iter = changeset.changes().iter().peekable();
296301 let mut old_pos = 0;
@@ -340,7 +345,7 @@ impl Client {
340345 new_pos += i;
341346 }
342347 Delete(_) => {
343− let start = pos_to_lsp_pos(new_text, new_pos);
348+ let start = pos_to_lsp_pos(new_text, new_pos, offset_encoding);
344349 let end = traverse(start, old_text.slice(old_pos..old_end));
345350
346351 // deletion
@@ -351,7 +356,7 @@ impl Client {
351356 });
352357 }
353358 Insert(s) => {
354− let start = pos_to_lsp_pos(new_text, new_pos);
359+ let start = pos_to_lsp_pos(new_text, new_pos, offset_encoding);
355360
356361 new_pos += s.chars().count();
357362
@@ -413,7 +418,7 @@ impl Client {
413418 }]
414419 }
415420 lsp::TextDocumentSyncKind::Incremental => {
416− Self::changeset_to_changes(old_text, new_text, changes)
421+ Self::changeset_to_changes(old_text, new_text, changes, self.offset_encoding)
417422 }
418423 lsp::TextDocumentSyncKind::None => return Ok(()),
419424 };
helix-lsp/src/lib.rs+62−15
@@ -16,6 +16,8 @@ use thiserror::Error;
1616
1717use std::{collections::HashMap, sync::Arc};
1818
19+use serde::{Deserialize, Serialize};
20+
1921#[derive(Error, Debug)]
2022pub enum Error {
2123 #[error("protocol error: {0}")]
@@ -28,31 +30,76 @@ pub enum Error {
2830 Other(#[from] anyhow::Error),
2931}
3032
33+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
34+pub enum OffsetEncoding {
35+ /// UTF-8 code units aka bytes
36+ #[serde(rename = "utf-8")]
37+ Utf8,
38+ /// UTF-16 code units
39+ #[serde(rename = "utf-16")]
40+ Utf16,
41+}
42+
3143pub mod util {
3244 use super::*;
3345 use helix_core::{Range, Rope, Transaction};
3446
35− pub fn lsp_pos_to_pos(doc: &Rope, pos: lsp::Position) -> usize {
36− let line = doc.line_to_char(pos.line as usize);
37− let line_start = doc.char_to_utf16_cu(line);
38− doc.utf16_cu_to_char(line_start + pos.character as usize)
47+ pub fn lsp_pos_to_pos(
48+ doc: &Rope,
49+ pos: lsp::Position,
50+ offset_encoding: OffsetEncoding,
51+ ) -> usize {
52+ match offset_encoding {
53+ OffsetEncoding::Utf8 => {
54+ let line = doc.line_to_char(pos.line as usize);
55+ line + pos.character as usize
56+ }
57+ OffsetEncoding::Utf16 => {
58+ let line = doc.line_to_char(pos.line as usize);
59+ let line_start = doc.char_to_utf16_cu(line);
60+ doc.utf16_cu_to_char(line_start + pos.character as usize)
61+ }
62+ }
3963 }
40− pub fn pos_to_lsp_pos(doc: &Rope, pos: usize) -> lsp::Position {
41− let line = doc.char_to_line(pos);
42− let line_start = doc.char_to_utf16_cu(doc.line_to_char(line));
43− let col = doc.char_to_utf16_cu(pos) - line_start;
64+ pub fn pos_to_lsp_pos(
65+ doc: &Rope,
66+ pos: usize,
67+ offset_encoding: OffsetEncoding,
68+ ) -> lsp::Position {
69+ match offset_encoding {
70+ OffsetEncoding::Utf8 => {
71+ let line = doc.char_to_line(pos);
72+ let line_start = doc.line_to_char(line);
73+ let col = pos - line_start;
74+
75+ lsp::Position::new(line as u32, col as u32)
76+ }
77+ OffsetEncoding::Utf16 => {
78+ let line = doc.char_to_line(pos);
79+ let line_start = doc.char_to_utf16_cu(doc.line_to_char(line));
80+ let col = doc.char_to_utf16_cu(pos) - line_start;
4481
45− lsp::Position::new(line as u32, col as u32)
82+ lsp::Position::new(line as u32, col as u32)
83+ }
84+ }
4685 }
4786
48− pub fn range_to_lsp_range(doc: &Rope, range: Range) -> lsp::Range {
49− let start = pos_to_lsp_pos(doc, range.from());
50− let end = pos_to_lsp_pos(doc, range.to());
87+ pub fn range_to_lsp_range(
88+ doc: &Rope,
89+ range: Range,
90+ offset_encoding: OffsetEncoding,
91+ ) -> lsp::Range {
92+ let start = pos_to_lsp_pos(doc, range.from(), offset_encoding);
93+ let end = pos_to_lsp_pos(doc, range.to(), offset_encoding);
5194
5295 lsp::Range::new(start, end)
5396 }
5497
55− pub fn generate_transaction_from_edits(doc: &Rope, edits: Vec<lsp::TextEdit>) -> Transaction {
98+ pub fn generate_transaction_from_edits(
99+ doc: &Rope,
100+ edits: Vec<lsp::TextEdit>,
101+ offset_encoding: OffsetEncoding,
102+ ) -> Transaction {
56103 Transaction::change(
57104 doc,
58105 edits.into_iter().map(|edit| {
@@ -63,8 +110,8 @@ pub mod util {
63110 None
64111 };
65112
66− let start = lsp_pos_to_pos(doc, edit.range.start);
67− let end = lsp_pos_to_pos(doc, edit.range.end);
113+ let start = lsp_pos_to_pos(doc, edit.range.start, offset_encoding);
114+ let end = lsp_pos_to_pos(doc, edit.range.end, offset_encoding);
68115 (start, end, replacement)
69116 }),
70117 )
helix-term/src/application.rs+14−2
@@ -175,8 +175,20 @@ impl Application {
175175 };
176176 use helix_lsp::{lsp, util::lsp_pos_to_pos};
177177 use lsp::DiagnosticSeverity;
178− let start = lsp_pos_to_pos(text, diagnostic.range.start);
179− let end = lsp_pos_to_pos(text, diagnostic.range.end);
178+
179+ let language_server = doc.language_server().unwrap();
180+
181+ // TODO: convert inside server
182+ let start = lsp_pos_to_pos(
183+ text,
184+ diagnostic.range.start,
185+ language_server.offset_encoding(),
186+ );
187+ let end = lsp_pos_to_pos(
188+ text,
189+ diagnostic.range.end,
190+ language_server.offset_encoding(),
191+ );
180192
181193 Diagnostic {
182194 range: Range { start, end },
helix-term/src/commands.rs+59−20
@@ -16,6 +16,7 @@ use helix_view::{
1616use helix_lsp::{
1717 lsp,
1818 util::{lsp_pos_to_pos, pos_to_lsp_pos, range_to_lsp_range},
19+ OffsetEncoding,
1920};
2021
2122use crate::{
@@ -1117,18 +1118,24 @@ pub fn exit_select_mode(cx: &mut Context) {
11171118 cx.doc().mode = Mode::Normal;
11181119}
11191120
1120−fn _goto(cx: &mut Context, locations: Vec<lsp::Location>) {
1121+fn _goto(cx: &mut Context, locations: Vec<lsp::Location>, offset_encoding: OffsetEncoding) {
11211122 use helix_view::editor::Action;
11221123
11231124 push_jump(cx);
11241125
1125− fn jump_to(editor: &mut Editor, location: &lsp::Location, action: Action) {
1126+ fn jump_to(
1127+ editor: &mut Editor,
1128+ location: &lsp::Location,
1129+ offset_encoding: OffsetEncoding,
1130+ action: Action,
1131+ ) {
11261132 let id = editor
11271133 .open(PathBuf::from(location.uri.path()), action)
11281134 .expect("editor.open failed");
11291135 let (view, doc) = editor.current();
11301136 let definition_pos = location.range.start;
1131− let new_pos = lsp_pos_to_pos(doc.text(), definition_pos);
1137+ // TODO: convert inside server
1138+ let new_pos = lsp_pos_to_pos(doc.text(), definition_pos, offset_encoding);
11321139 doc.set_selection(view.id, Selection::point(new_pos));
11331140 let line = doc.text().char_to_line(new_pos);
11341141 view.first_line = line.saturating_sub(view.area.height as usize / 2);
@@ -1136,7 +1143,7 @@ fn _goto(cx: &mut Context, locations: Vec<lsp::Location>) {
11361143
11371144 match locations.as_slice() {
11381145 [location] => {
1139− jump_to(cx.editor, location, Action::Replace);
1146+ jump_to(cx.editor, location, offset_encoding, Action::Replace);
11401147 }
11411148 [] => (), // maybe show user message that no definition was found?
11421149 _locations => {
@@ -1147,7 +1154,9 @@ fn _goto(cx: &mut Context, locations: Vec<lsp::Location>) {
11471154 let line = location.range.start.line;
11481155 format!("{}:{}", file, line).into()
11491156 },
1150− move |editor: &mut Editor, location, action| jump_to(editor, location, action),
1157+ move |editor: &mut Editor, location, action| {
1158+ jump_to(editor, location, offset_encoding, action)
1159+ },
11511160 );
11521161 cx.push_layer(Box::new(picker));
11531162 }
@@ -1161,12 +1170,14 @@ pub fn goto_definition(cx: &mut Context) {
11611170 None => return,
11621171 };
11631172
1164− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor());
1173+ let offset_encoding = language_server.offset_encoding();
1174+
1175+ let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding);
11651176
11661177 // TODO: handle fails
11671178 let res =
11681179 smol::block_on(language_server.goto_definition(doc.identifier(), pos)).unwrap_or_default();
1169− _goto(cx, res);
1180+ _goto(cx, res, offset_encoding);
11701181}
11711182
11721183pub fn goto_type_definition(cx: &mut Context) {
@@ -1176,12 +1187,14 @@ pub fn goto_type_definition(cx: &mut Context) {
11761187 None => return,
11771188 };
11781189
1179− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor());
1190+ let offset_encoding = language_server.offset_encoding();
1191+
1192+ let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding);
11801193
11811194 // TODO: handle fails
11821195 let res = smol::block_on(language_server.goto_type_definition(doc.identifier(), pos))
11831196 .unwrap_or_default();
1184− _goto(cx, res);
1197+ _goto(cx, res, offset_encoding);
11851198}
11861199
11871200pub fn goto_implementation(cx: &mut Context) {
@@ -1191,12 +1204,14 @@ pub fn goto_implementation(cx: &mut Context) {
11911204 None => return,
11921205 };
11931206
1194− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor());
1207+ let offset_encoding = language_server.offset_encoding();
1208+
1209+ let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding);
11951210
11961211 // TODO: handle fails
11971212 let res = smol::block_on(language_server.goto_implementation(doc.identifier(), pos))
11981213 .unwrap_or_default();
1199− _goto(cx, res);
1214+ _goto(cx, res, offset_encoding);
12001215}
12011216
12021217pub fn goto_reference(cx: &mut Context) {
@@ -1206,12 +1221,14 @@ pub fn goto_reference(cx: &mut Context) {
12061221 None => return,
12071222 };
12081223
1209− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor());
1224+ let offset_encoding = language_server.offset_encoding();
1225+
1226+ let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding);
12101227
12111228 // TODO: handle fails
12121229 let res =
12131230 smol::block_on(language_server.goto_reference(doc.identifier(), pos)).unwrap_or_default();
1214− _goto(cx, res);
1231+ _goto(cx, res, offset_encoding);
12151232}
12161233
12171234pub fn signature_help(cx: &mut Context) {
@@ -1222,7 +1239,11 @@ pub fn signature_help(cx: &mut Context) {
12221239 None => return,
12231240 };
12241241
1225− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor());
1242+ let pos = pos_to_lsp_pos(
1243+ doc.text(),
1244+ doc.selection(view.id).cursor(),
1245+ language_server.offset_encoding(),
1246+ );
12261247
12271248 // TODO: handle fails
12281249
@@ -1579,10 +1600,15 @@ pub fn format_selections(cx: &mut Context) {
15791600 // via lsp if available
15801601 // else via tree-sitter indentation calculations
15811602
1603+ let language_server = match doc.language_server() {
1604+ Some(language_server) => language_server,
1605+ None => return,
1606+ };
1607+
15821608 let ranges: Vec<lsp::Range> = doc
15831609 .selection(view.id)
15841610 .iter()
1585− .map(|range| range_to_lsp_range(doc.text(), *range))
1611+ .map(|range| range_to_lsp_range(doc.text(), *range, language_server.offset_encoding()))
15861612 .collect();
15871613
15881614 for range in ranges {
@@ -1590,7 +1616,6 @@ pub fn format_selections(cx: &mut Context) {
15901616 Some(language_server) => language_server,
15911617 None => return,
15921618 };
1593−
15941619 // TODO: handle fails
15951620 // TODO: concurrent map
15961621 let edits = smol::block_on(language_server.text_document_range_formatting(
@@ -1600,7 +1625,11 @@ pub fn format_selections(cx: &mut Context) {
16001625 ))
16011626 .unwrap_or_default();
16021627
1603− let transaction = helix_lsp::util::generate_transaction_from_edits(doc.text(), edits);
1628+ let transaction = helix_lsp::util::generate_transaction_from_edits(
1629+ doc.text(),
1630+ edits,
1631+ language_server.offset_encoding(),
1632+ );
16041633
16051634 doc.apply(&transaction, view.id);
16061635 }
@@ -1726,7 +1755,13 @@ pub fn completion(cx: &mut Context) {
17261755 None => return,
17271756 };
17281757
1729− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor());
1758+ let offset_encoding = language_server.offset_encoding();
1759+
1760+ let pos = pos_to_lsp_pos(
1761+ doc.text(),
1762+ doc.selection(view.id).cursor(),
1763+ language_server.offset_encoding(),
1764+ );
17301765
17311766 // TODO: handle fails
17321767 let res = smol::block_on(language_server.completion(doc.identifier(), pos)).unwrap();
@@ -1754,7 +1789,7 @@ pub fn completion(cx: &mut Context) {
17541789 let size = compositor.size();
17551790 let ui = compositor.find("hx::ui::editor::EditorView").unwrap();
17561791 if let Some(ui) = ui.as_any_mut().downcast_mut::<ui::EditorView>() {
1757− ui.set_completion(items, trigger_offset, size);
1792+ ui.set_completion(items, offset_encoding, trigger_offset, size);
17581793 };
17591794 }
17601795 },
@@ -1779,7 +1814,11 @@ pub fn hover(cx: &mut Context) {
17791814
17801815 // TODO: blocking here is not ideal, make commands async fn?
17811816 // not like we can process additional input meanwhile though
1782− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor());
1817+ let pos = pos_to_lsp_pos(
1818+ doc.text(),
1819+ doc.selection(view.id).cursor(),
1820+ language_server.offset_encoding(),
1821+ );
17831822
17841823 // TODO: handle fails
17851824 let res = smol::block_on(language_server.text_document_hover(doc.identifier(), pos))
helix-term/src/ui/completion.rs+11−3
@@ -26,7 +26,11 @@ pub struct Completion {
2626}
2727
2828impl Completion {
29− pub fn new(items: Vec<CompletionItem>, trigger_offset: usize) -> Self {
29+ pub fn new(
30+ items: Vec<CompletionItem>,
31+ offset_encoding: helix_lsp::OffsetEncoding,
32+ trigger_offset: usize,
33+ ) -> Self {
3034 // let items: Vec<CompletionItem> = Vec::new();
3135 let mut menu = Menu::new(
3236 items,
@@ -99,8 +103,12 @@ impl Completion {
99103 doc.apply(&remove, view.id);
100104 }
101105
102− let transaction =
103− util::generate_transaction_from_edits(doc.text(), vec![edit]);
106+ use helix_lsp::OffsetEncoding;
107+ let transaction = util::generate_transaction_from_edits(
108+ doc.text(),
109+ vec![edit],
110+ offset_encoding, // TODO: should probably transcode in Client
111+ );
104112 doc.apply(&transaction, view.id);
105113 }
106114 _ => (),
helix-term/src/ui/editor.rs+2−1
@@ -517,10 +517,11 @@ impl EditorView {
517517 pub fn set_completion(
518518 &mut self,
519519 items: Vec<helix_lsp::lsp::CompletionItem>,
520+ offset_encoding: helix_lsp::OffsetEncoding,
520521 trigger_offset: usize,
521522 size: Rect,
522523 ) {
523− let mut completion = Completion::new(items, trigger_offset);
524+ let mut completion = Completion::new(items, offset_encoding, trigger_offset);
524525 // TODO : propagate required size on resize to completion too
525526 completion.required_size((size.width, size.height));
526527 self.completion = Some(completion);
helix-term/src/ui/mod.rs+1−1
@@ -20,7 +20,7 @@ pub use tui::layout::Rect;
2020pub use tui::style::{Color, Modifier, Style};
2121
2222use helix_core::regex::Regex;
23−use helix_view::{View, Document, Editor};
23+use helix_view::{Document, Editor, View};
2424
2525use std::path::{Path, PathBuf};
2626
helix-view/src/document.rs+19−4
@@ -406,7 +406,7 @@ mod test {
406406
407407 #[test]
408408 fn changeset_to_changes() {
409− use helix_lsp::{lsp, Client};
409+ use helix_lsp::{lsp, Client, OffsetEncoding};
410410 let text = Rope::from("hello");
411411 let mut doc = Document::new(text);
412412 let view = ViewId::default();
@@ -417,7 +417,12 @@ mod test {
417417 let transaction = Transaction::insert(doc.text(), doc.selection(view), " world".into());
418418 let old_doc = doc.text().clone();
419419 doc.apply(&transaction, view);
420− let changes = Client::changeset_to_changes(&old_doc, doc.text(), transaction.changes());
420+ let changes = Client::changeset_to_changes(
421+ &old_doc,
422+ doc.text(),
423+ transaction.changes(),
424+ OffsetEncoding::Utf8,
425+ );
421426
422427 assert_eq!(
423428 changes,
@@ -436,7 +441,12 @@ mod test {
436441 let transaction = transaction.invert(&old_doc);
437442 let old_doc = doc.text().clone();
438443 doc.apply(&transaction, view);
439− let changes = Client::changeset_to_changes(&old_doc, doc.text(), transaction.changes());
444+ let changes = Client::changeset_to_changes(
445+ &old_doc,
446+ doc.text(),
447+ transaction.changes(),
448+ OffsetEncoding::Utf8,
449+ );
440450
441451 // line: 0-based.
442452 // col: 0-based, gaps between chars.
@@ -468,7 +478,12 @@ mod test {
468478 // aeilou
469479 let old_doc = doc.text().clone();
470480 doc.apply(&transaction, view);
471− let changes = Client::changeset_to_changes(&old_doc, doc.text(), transaction.changes());
481+ let changes = Client::changeset_to_changes(
482+ &old_doc,
483+ doc.text(),
484+ transaction.changes(),
485+ OffsetEncoding::Utf8,
486+ );
472487
473488 assert_eq!(
474489 changes,