diff options
-rw-r--r-- | crates/ide/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ide_db/src/line_index.rs | 29 | ||||
-rw-r--r-- | crates/ide_db/src/line_index/tests.rs | 33 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/main.rs | 8 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/analysis_bench.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/analysis_stats.rs | 8 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 12 | ||||
-rw-r--r-- | crates/rust-analyzer/src/from_proto.rs | 32 | ||||
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 11 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 91 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/line_index.rs (renamed from crates/rust-analyzer/src/line_endings.rs) | 16 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 4 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_utils.rs | 19 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 90 | ||||
-rw-r--r-- | docs/dev/lsp-extensions.md | 8 |
16 files changed, 221 insertions, 146 deletions
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 89e7bef7d..a2c8db505 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -95,7 +95,7 @@ pub use ide_db::{ | |||
95 | }, | 95 | }, |
96 | call_info::CallInfo, | 96 | call_info::CallInfo, |
97 | label::Label, | 97 | label::Label, |
98 | line_index::{LineCol, LineIndex}, | 98 | line_index::{LineCol, LineColUtf16, LineIndex}, |
99 | search::{ReferenceAccess, SearchScope}, | 99 | search::{ReferenceAccess, SearchScope}, |
100 | source_change::{FileSystemEdit, SourceChange}, | 100 | source_change::{FileSystemEdit, SourceChange}, |
101 | symbol_index::Query, | 101 | symbol_index::Query, |
diff --git a/crates/ide_db/src/line_index.rs b/crates/ide_db/src/line_index.rs index 41226305e..8e9d8cca2 100644 --- a/crates/ide_db/src/line_index.rs +++ b/crates/ide_db/src/line_index.rs | |||
@@ -15,11 +15,19 @@ pub struct LineIndex { | |||
15 | } | 15 | } |
16 | 16 | ||
17 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 17 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
18 | pub struct LineCol { | 18 | pub struct LineColUtf16 { |
19 | /// Zero-based | 19 | /// Zero-based |
20 | pub line: u32, | 20 | pub line: u32, |
21 | /// Zero-based | 21 | /// Zero-based |
22 | pub col_utf16: u32, | 22 | pub col: u32, |
23 | } | ||
24 | |||
25 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
26 | pub struct LineCol { | ||
27 | /// Zero-based | ||
28 | pub line: u32, | ||
29 | /// Zero-based utf8 offset | ||
30 | pub col: u32, | ||
23 | } | 31 | } |
24 | 32 | ||
25 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] | 33 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] |
@@ -92,14 +100,21 @@ impl LineIndex { | |||
92 | let line = partition_point(&self.newlines, |&it| it <= offset) - 1; | 100 | let line = partition_point(&self.newlines, |&it| it <= offset) - 1; |
93 | let line_start_offset = self.newlines[line]; | 101 | let line_start_offset = self.newlines[line]; |
94 | let col = offset - line_start_offset; | 102 | let col = offset - line_start_offset; |
95 | 103 | LineCol { line: line as u32, col: col.into() } | |
96 | LineCol { line: line as u32, col_utf16: self.utf8_to_utf16_col(line as u32, col) as u32 } | ||
97 | } | 104 | } |
98 | 105 | ||
99 | pub fn offset(&self, line_col: LineCol) -> TextSize { | 106 | pub fn offset(&self, line_col: LineCol) -> TextSize { |
100 | //FIXME: return Result | 107 | self.newlines[line_col.line as usize] + TextSize::from(line_col.col) |
101 | let col = self.utf16_to_utf8_col(line_col.line, line_col.col_utf16); | 108 | } |
102 | self.newlines[line_col.line as usize] + col | 109 | |
110 | pub fn to_utf16(&self, line_col: LineCol) -> LineColUtf16 { | ||
111 | let col = self.utf8_to_utf16_col(line_col.line, line_col.col.into()); | ||
112 | LineColUtf16 { line: line_col.line, col: col as u32 } | ||
113 | } | ||
114 | |||
115 | pub fn to_utf8(&self, line_col: LineColUtf16) -> LineCol { | ||
116 | let col = self.utf16_to_utf8_col(line_col.line, line_col.col); | ||
117 | LineCol { line: line_col.line, col: col.into() } | ||
103 | } | 118 | } |
104 | 119 | ||
105 | pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ { | 120 | pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ { |
diff --git a/crates/ide_db/src/line_index/tests.rs b/crates/ide_db/src/line_index/tests.rs index 05f7484e8..09f3bca62 100644 --- a/crates/ide_db/src/line_index/tests.rs +++ b/crates/ide_db/src/line_index/tests.rs | |||
@@ -3,24 +3,29 @@ use super::*; | |||
3 | #[test] | 3 | #[test] |
4 | fn test_line_index() { | 4 | fn test_line_index() { |
5 | let text = "hello\nworld"; | 5 | let text = "hello\nworld"; |
6 | let table = [ | ||
7 | (00, 0, 0), | ||
8 | (01, 0, 1), | ||
9 | (05, 0, 5), | ||
10 | (06, 1, 0), | ||
11 | (07, 1, 1), | ||
12 | (08, 1, 2), | ||
13 | (10, 1, 4), | ||
14 | (11, 1, 5), | ||
15 | (12, 1, 6), | ||
16 | ]; | ||
17 | |||
6 | let index = LineIndex::new(text); | 18 | let index = LineIndex::new(text); |
7 | assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); | 19 | for &(offset, line, col) in &table { |
8 | assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 }); | 20 | assert_eq!(index.line_col(offset.into()), LineCol { line, col }); |
9 | assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 }); | 21 | } |
10 | assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 }); | ||
11 | assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 }); | ||
12 | assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 }); | ||
13 | assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 }); | ||
14 | assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 }); | ||
15 | assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 }); | ||
16 | 22 | ||
17 | let text = "\nhello\nworld"; | 23 | let text = "\nhello\nworld"; |
24 | let table = [(0, 0, 0), (1, 1, 0), (2, 1, 1), (6, 1, 5), (7, 2, 0)]; | ||
18 | let index = LineIndex::new(text); | 25 | let index = LineIndex::new(text); |
19 | assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); | 26 | for &(offset, line, col) in &table { |
20 | assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 }); | 27 | assert_eq!(index.line_col(offset.into()), LineCol { line, col }); |
21 | assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 }); | 28 | } |
22 | assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 }); | ||
23 | assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 }); | ||
24 | } | 29 | } |
25 | 30 | ||
26 | #[test] | 31 | #[test] |
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 93d0ad4ec..89482b952 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -8,7 +8,7 @@ use std::{convert::TryFrom, env, fs, path::PathBuf, process}; | |||
8 | 8 | ||
9 | use lsp_server::Connection; | 9 | use lsp_server::Connection; |
10 | use project_model::ProjectManifest; | 10 | use project_model::ProjectManifest; |
11 | use rust_analyzer::{cli, config::Config, from_json, Result}; | 11 | use rust_analyzer::{cli, config::Config, from_json, lsp_ext::supports_utf8, Result}; |
12 | use vfs::AbsPathBuf; | 12 | use vfs::AbsPathBuf; |
13 | 13 | ||
14 | #[cfg(all(feature = "mimalloc"))] | 14 | #[cfg(all(feature = "mimalloc"))] |
@@ -127,7 +127,11 @@ fn run_server() -> Result<()> { | |||
127 | name: String::from("rust-analyzer"), | 127 | name: String::from("rust-analyzer"), |
128 | version: Some(String::from(env!("REV"))), | 128 | version: Some(String::from(env!("REV"))), |
129 | }), | 129 | }), |
130 | offset_encoding: None, | 130 | offset_encoding: if supports_utf8(&initialize_params.capabilities) { |
131 | Some("utf-8".to_string()) | ||
132 | } else { | ||
133 | None | ||
134 | }, | ||
131 | }; | 135 | }; |
132 | 136 | ||
133 | let initialize_result = serde_json::to_value(initialize_result).unwrap(); | 137 | let initialize_result = serde_json::to_value(initialize_result).unwrap(); |
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs index 6735b6388..877abd12b 100644 --- a/crates/rust-analyzer/src/cli/analysis_bench.rs +++ b/crates/rust-analyzer/src/cli/analysis_bench.rs | |||
@@ -97,7 +97,7 @@ impl BenchCmd { | |||
97 | let offset = host | 97 | let offset = host |
98 | .analysis() | 98 | .analysis() |
99 | .file_line_index(file_id)? | 99 | .file_line_index(file_id)? |
100 | .offset(LineCol { line: pos.line - 1, col_utf16: pos.column }); | 100 | .offset(LineCol { line: pos.line - 1, col: pos.column }); |
101 | let file_position = FilePosition { file_id, offset }; | 101 | let file_position = FilePosition { file_id, offset }; |
102 | 102 | ||
103 | if is_completion { | 103 | if is_completion { |
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 3417af687..6d6f398f4 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs | |||
@@ -218,9 +218,9 @@ impl AnalysisStatsCmd { | |||
218 | bar.println(format!( | 218 | bar.println(format!( |
219 | "{}:{}-{}:{}: {}", | 219 | "{}:{}-{}:{}: {}", |
220 | start.line + 1, | 220 | start.line + 1, |
221 | start.col_utf16, | 221 | start.col, |
222 | end.line + 1, | 222 | end.line + 1, |
223 | end.col_utf16, | 223 | end.col, |
224 | ty.display(db) | 224 | ty.display(db) |
225 | )); | 225 | )); |
226 | } else { | 226 | } else { |
@@ -250,9 +250,9 @@ impl AnalysisStatsCmd { | |||
250 | "{} {}:{}-{}:{}: Expected {}, got {}", | 250 | "{} {}:{}-{}:{}: Expected {}, got {}", |
251 | path, | 251 | path, |
252 | start.line + 1, | 252 | start.line + 1, |
253 | start.col_utf16, | 253 | start.col, |
254 | end.line + 1, | 254 | end.line + 1, |
255 | end.col_utf16, | 255 | end.col, |
256 | mismatch.expected.display(db), | 256 | mismatch.expected.display(db), |
257 | mismatch.actual.display(db) | 257 | mismatch.actual.display(db) |
258 | )); | 258 | )); |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 04a77d677..556fc2eeb 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -23,7 +23,10 @@ use rustc_hash::FxHashSet; | |||
23 | use serde::{de::DeserializeOwned, Deserialize}; | 23 | use serde::{de::DeserializeOwned, Deserialize}; |
24 | use vfs::AbsPathBuf; | 24 | use vfs::AbsPathBuf; |
25 | 25 | ||
26 | use crate::{caps::completion_item_edit_resolve, diagnostics::DiagnosticsMapConfig}; | 26 | use crate::{ |
27 | caps::completion_item_edit_resolve, diagnostics::DiagnosticsMapConfig, | ||
28 | line_index::OffsetEncoding, lsp_ext::supports_utf8, | ||
29 | }; | ||
27 | 30 | ||
28 | config_data! { | 31 | config_data! { |
29 | struct ConfigData { | 32 | struct ConfigData { |
@@ -415,6 +418,13 @@ impl Config { | |||
415 | false | 418 | false |
416 | ) | 419 | ) |
417 | } | 420 | } |
421 | pub fn offset_encoding(&self) -> OffsetEncoding { | ||
422 | if supports_utf8(&self.caps) { | ||
423 | OffsetEncoding::Utf8 | ||
424 | } else { | ||
425 | OffsetEncoding::Utf16 | ||
426 | } | ||
427 | } | ||
418 | 428 | ||
419 | fn experimental(&self, index: &'static str) -> bool { | 429 | fn experimental(&self, index: &'static str) -> bool { |
420 | try_or!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?, false) | 430 | try_or!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?, false) |
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs index 6676eebf4..5b02b2598 100644 --- a/crates/rust-analyzer/src/from_proto.rs +++ b/crates/rust-analyzer/src/from_proto.rs | |||
@@ -1,12 +1,17 @@ | |||
1 | //! Conversion lsp_types types to rust-analyzer specific ones. | 1 | //! Conversion lsp_types types to rust-analyzer specific ones. |
2 | use std::convert::TryFrom; | 2 | use std::convert::TryFrom; |
3 | 3 | ||
4 | use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineIndex}; | 4 | use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineColUtf16}; |
5 | use ide_db::base_db::{FileId, FilePosition, FileRange}; | 5 | use ide_db::base_db::{FileId, FilePosition, FileRange}; |
6 | use syntax::{TextRange, TextSize}; | 6 | use syntax::{TextRange, TextSize}; |
7 | use vfs::AbsPathBuf; | 7 | use vfs::AbsPathBuf; |
8 | 8 | ||
9 | use crate::{from_json, global_state::GlobalStateSnapshot, lsp_ext, Result}; | 9 | use crate::{ |
10 | from_json, | ||
11 | global_state::GlobalStateSnapshot, | ||
12 | line_index::{LineIndex, OffsetEncoding}, | ||
13 | lsp_ext, Result, | ||
14 | }; | ||
10 | 15 | ||
11 | pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> { | 16 | pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> { |
12 | let path = url.to_file_path().map_err(|()| "url is not a file")?; | 17 | let path = url.to_file_path().map_err(|()| "url is not a file")?; |
@@ -18,8 +23,17 @@ pub(crate) fn vfs_path(url: &lsp_types::Url) -> Result<vfs::VfsPath> { | |||
18 | } | 23 | } |
19 | 24 | ||
20 | pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { | 25 | pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { |
21 | let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 }; | 26 | let line_col = match line_index.encoding { |
22 | line_index.offset(line_col) | 27 | OffsetEncoding::Utf8 => { |
28 | LineCol { line: position.line as u32, col: position.character as u32 } | ||
29 | } | ||
30 | OffsetEncoding::Utf16 => { | ||
31 | let line_col = | ||
32 | LineColUtf16 { line: position.line as u32, col: position.character as u32 }; | ||
33 | line_index.index.to_utf8(line_col) | ||
34 | } | ||
35 | }; | ||
36 | line_index.index.offset(line_col) | ||
23 | } | 37 | } |
24 | 38 | ||
25 | pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> TextRange { | 39 | pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> TextRange { |
@@ -37,8 +51,8 @@ pub(crate) fn file_position( | |||
37 | tdpp: lsp_types::TextDocumentPositionParams, | 51 | tdpp: lsp_types::TextDocumentPositionParams, |
38 | ) -> Result<FilePosition> { | 52 | ) -> Result<FilePosition> { |
39 | let file_id = file_id(world, &tdpp.text_document.uri)?; | 53 | let file_id = file_id(world, &tdpp.text_document.uri)?; |
40 | let line_index = world.analysis.file_line_index(file_id)?; | 54 | let line_index = world.file_line_index(file_id)?; |
41 | let offset = offset(&*line_index, tdpp.position); | 55 | let offset = offset(&line_index, tdpp.position); |
42 | Ok(FilePosition { file_id, offset }) | 56 | Ok(FilePosition { file_id, offset }) |
43 | } | 57 | } |
44 | 58 | ||
@@ -48,7 +62,7 @@ pub(crate) fn file_range( | |||
48 | range: lsp_types::Range, | 62 | range: lsp_types::Range, |
49 | ) -> Result<FileRange> { | 63 | ) -> Result<FileRange> { |
50 | let file_id = file_id(world, &text_document_identifier.uri)?; | 64 | let file_id = file_id(world, &text_document_identifier.uri)?; |
51 | let line_index = world.analysis.file_line_index(file_id)?; | 65 | let line_index = world.file_line_index(file_id)?; |
52 | let range = text_range(&line_index, range); | 66 | let range = text_range(&line_index, range); |
53 | Ok(FileRange { file_id, range }) | 67 | Ok(FileRange { file_id, range }) |
54 | } | 68 | } |
@@ -78,7 +92,7 @@ pub(crate) fn annotation( | |||
78 | lsp_ext::CodeLensResolveData::Impls(params) => { | 92 | lsp_ext::CodeLensResolveData::Impls(params) => { |
79 | let file_id = | 93 | let file_id = |
80 | world.url_to_file_id(¶ms.text_document_position_params.text_document.uri)?; | 94 | world.url_to_file_id(¶ms.text_document_position_params.text_document.uri)?; |
81 | let line_index = world.analysis.file_line_index(file_id)?; | 95 | let line_index = world.file_line_index(file_id)?; |
82 | 96 | ||
83 | Ok(Annotation { | 97 | Ok(Annotation { |
84 | range: text_range(&line_index, code_lens.range), | 98 | range: text_range(&line_index, code_lens.range), |
@@ -90,7 +104,7 @@ pub(crate) fn annotation( | |||
90 | } | 104 | } |
91 | lsp_ext::CodeLensResolveData::References(params) => { | 105 | lsp_ext::CodeLensResolveData::References(params) => { |
92 | let file_id = world.url_to_file_id(¶ms.text_document.uri)?; | 106 | let file_id = world.url_to_file_id(¶ms.text_document.uri)?; |
93 | let line_index = world.analysis.file_line_index(file_id)?; | 107 | let line_index = world.file_line_index(file_id)?; |
94 | 108 | ||
95 | Ok(Annotation { | 109 | Ok(Annotation { |
96 | range: text_range(&line_index, code_lens.range), | 110 | range: text_range(&line_index, code_lens.range), |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index c3bc8791d..52c249713 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -7,7 +7,7 @@ use std::{sync::Arc, time::Instant}; | |||
7 | 7 | ||
8 | use crossbeam_channel::{unbounded, Receiver, Sender}; | 8 | use crossbeam_channel::{unbounded, Receiver, Sender}; |
9 | use flycheck::FlycheckHandle; | 9 | use flycheck::FlycheckHandle; |
10 | use ide::{Analysis, AnalysisHost, Change, FileId}; | 10 | use ide::{Analysis, AnalysisHost, Cancelable, Change, FileId}; |
11 | use ide_db::base_db::{CrateId, VfsPath}; | 11 | use ide_db::base_db::{CrateId, VfsPath}; |
12 | use lsp_types::{SemanticTokens, Url}; | 12 | use lsp_types::{SemanticTokens, Url}; |
13 | use parking_lot::{Mutex, RwLock}; | 13 | use parking_lot::{Mutex, RwLock}; |
@@ -22,7 +22,7 @@ use crate::{ | |||
22 | diagnostics::{CheckFixes, DiagnosticCollection}, | 22 | diagnostics::{CheckFixes, DiagnosticCollection}, |
23 | document::DocumentData, | 23 | document::DocumentData, |
24 | from_proto, | 24 | from_proto, |
25 | line_endings::LineEndings, | 25 | line_index::{LineEndings, LineIndex}, |
26 | main_loop::Task, | 26 | main_loop::Task, |
27 | op_queue::OpQueue, | 27 | op_queue::OpQueue, |
28 | reload::SourceRootConfig, | 28 | reload::SourceRootConfig, |
@@ -271,8 +271,11 @@ impl GlobalStateSnapshot { | |||
271 | file_id_to_url(&self.vfs.read().0, id) | 271 | file_id_to_url(&self.vfs.read().0, id) |
272 | } | 272 | } |
273 | 273 | ||
274 | pub(crate) fn file_line_endings(&self, id: FileId) -> LineEndings { | 274 | pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancelable<LineIndex> { |
275 | self.vfs.read().1[&id] | 275 | let endings = self.vfs.read().1[&file_id]; |
276 | let index = self.analysis.file_line_index(file_id)?; | ||
277 | let res = LineIndex { index, endings, encoding: self.config.offset_encoding() }; | ||
278 | Ok(res) | ||
276 | } | 279 | } |
277 | 280 | ||
278 | pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> { | 281 | pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> { |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 2d697c75f..4f6f250d6 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -5,12 +5,11 @@ | |||
5 | use std::{ | 5 | use std::{ |
6 | io::Write as _, | 6 | io::Write as _, |
7 | process::{self, Stdio}, | 7 | process::{self, Stdio}, |
8 | sync::Arc, | ||
9 | }; | 8 | }; |
10 | 9 | ||
11 | use ide::{ | 10 | use ide::{ |
12 | AnnotationConfig, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, | 11 | AnnotationConfig, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, Query, |
13 | Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, TextEdit, | 12 | RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, TextEdit, |
14 | }; | 13 | }; |
15 | use ide_db::SymbolKind; | 14 | use ide_db::SymbolKind; |
16 | use itertools::Itertools; | 15 | use itertools::Itertools; |
@@ -37,7 +36,7 @@ use crate::{ | |||
37 | diff::diff, | 36 | diff::diff, |
38 | from_proto, | 37 | from_proto, |
39 | global_state::{GlobalState, GlobalStateSnapshot}, | 38 | global_state::{GlobalState, GlobalStateSnapshot}, |
40 | line_endings::LineEndings, | 39 | line_index::{LineEndings, LineIndex}, |
41 | lsp_ext::{self, InlayHint, InlayHintsParams}, | 40 | lsp_ext::{self, InlayHint, InlayHintsParams}, |
42 | lsp_utils::all_edits_are_disjoint, | 41 | lsp_utils::all_edits_are_disjoint, |
43 | to_proto, LspError, Result, | 42 | to_proto, LspError, Result, |
@@ -100,7 +99,7 @@ pub(crate) fn handle_syntax_tree( | |||
100 | ) -> Result<String> { | 99 | ) -> Result<String> { |
101 | let _p = profile::span("handle_syntax_tree"); | 100 | let _p = profile::span("handle_syntax_tree"); |
102 | let id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 101 | let id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
103 | let line_index = snap.analysis.file_line_index(id)?; | 102 | let line_index = snap.file_line_index(id)?; |
104 | let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); | 103 | let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); |
105 | let res = snap.analysis.syntax_tree(id, text_range)?; | 104 | let res = snap.analysis.syntax_tree(id, text_range)?; |
106 | Ok(res) | 105 | Ok(res) |
@@ -122,7 +121,7 @@ pub(crate) fn handle_expand_macro( | |||
122 | ) -> Result<Option<lsp_ext::ExpandedMacro>> { | 121 | ) -> Result<Option<lsp_ext::ExpandedMacro>> { |
123 | let _p = profile::span("handle_expand_macro"); | 122 | let _p = profile::span("handle_expand_macro"); |
124 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 123 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
125 | let line_index = snap.analysis.file_line_index(file_id)?; | 124 | let line_index = snap.file_line_index(file_id)?; |
126 | let offset = from_proto::offset(&line_index, params.position); | 125 | let offset = from_proto::offset(&line_index, params.position); |
127 | 126 | ||
128 | let res = snap.analysis.expand_macro(FilePosition { file_id, offset })?; | 127 | let res = snap.analysis.expand_macro(FilePosition { file_id, offset })?; |
@@ -135,7 +134,7 @@ pub(crate) fn handle_selection_range( | |||
135 | ) -> Result<Option<Vec<lsp_types::SelectionRange>>> { | 134 | ) -> Result<Option<Vec<lsp_types::SelectionRange>>> { |
136 | let _p = profile::span("handle_selection_range"); | 135 | let _p = profile::span("handle_selection_range"); |
137 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 136 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
138 | let line_index = snap.analysis.file_line_index(file_id)?; | 137 | let line_index = snap.file_line_index(file_id)?; |
139 | let res: Result<Vec<lsp_types::SelectionRange>> = params | 138 | let res: Result<Vec<lsp_types::SelectionRange>> = params |
140 | .positions | 139 | .positions |
141 | .into_iter() | 140 | .into_iter() |
@@ -178,7 +177,7 @@ pub(crate) fn handle_matching_brace( | |||
178 | ) -> Result<Vec<Position>> { | 177 | ) -> Result<Vec<Position>> { |
179 | let _p = profile::span("handle_matching_brace"); | 178 | let _p = profile::span("handle_matching_brace"); |
180 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 179 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
181 | let line_index = snap.analysis.file_line_index(file_id)?; | 180 | let line_index = snap.file_line_index(file_id)?; |
182 | let res = params | 181 | let res = params |
183 | .positions | 182 | .positions |
184 | .into_iter() | 183 | .into_iter() |
@@ -200,8 +199,7 @@ pub(crate) fn handle_join_lines( | |||
200 | ) -> Result<Vec<lsp_types::TextEdit>> { | 199 | ) -> Result<Vec<lsp_types::TextEdit>> { |
201 | let _p = profile::span("handle_join_lines"); | 200 | let _p = profile::span("handle_join_lines"); |
202 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 201 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
203 | let line_index = snap.analysis.file_line_index(file_id)?; | 202 | let line_index = snap.file_line_index(file_id)?; |
204 | let line_endings = snap.file_line_endings(file_id); | ||
205 | let mut res = TextEdit::default(); | 203 | let mut res = TextEdit::default(); |
206 | for range in params.ranges { | 204 | for range in params.ranges { |
207 | let range = from_proto::text_range(&line_index, range); | 205 | let range = from_proto::text_range(&line_index, range); |
@@ -213,7 +211,7 @@ pub(crate) fn handle_join_lines( | |||
213 | } | 211 | } |
214 | } | 212 | } |
215 | } | 213 | } |
216 | let res = to_proto::text_edit_vec(&line_index, line_endings, res); | 214 | let res = to_proto::text_edit_vec(&line_index, res); |
217 | Ok(res) | 215 | Ok(res) |
218 | } | 216 | } |
219 | 217 | ||
@@ -227,9 +225,8 @@ pub(crate) fn handle_on_enter( | |||
227 | None => return Ok(None), | 225 | None => return Ok(None), |
228 | Some(it) => it, | 226 | Some(it) => it, |
229 | }; | 227 | }; |
230 | let line_index = snap.analysis.file_line_index(position.file_id)?; | 228 | let line_index = snap.file_line_index(position.file_id)?; |
231 | let line_endings = snap.file_line_endings(position.file_id); | 229 | let edit = to_proto::snippet_text_edit_vec(&line_index, true, edit); |
232 | let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit); | ||
233 | Ok(Some(edit)) | 230 | Ok(Some(edit)) |
234 | } | 231 | } |
235 | 232 | ||
@@ -240,8 +237,7 @@ pub(crate) fn handle_on_type_formatting( | |||
240 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { | 237 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { |
241 | let _p = profile::span("handle_on_type_formatting"); | 238 | let _p = profile::span("handle_on_type_formatting"); |
242 | let mut position = from_proto::file_position(&snap, params.text_document_position)?; | 239 | let mut position = from_proto::file_position(&snap, params.text_document_position)?; |
243 | let line_index = snap.analysis.file_line_index(position.file_id)?; | 240 | let line_index = snap.file_line_index(position.file_id)?; |
244 | let line_endings = snap.file_line_endings(position.file_id); | ||
245 | 241 | ||
246 | // in `ide`, the `on_type` invariant is that | 242 | // in `ide`, the `on_type` invariant is that |
247 | // `text.char_at(position) == typed_char`. | 243 | // `text.char_at(position) == typed_char`. |
@@ -269,7 +265,7 @@ pub(crate) fn handle_on_type_formatting( | |||
269 | // This should be a single-file edit | 265 | // This should be a single-file edit |
270 | let (_, edit) = edit.source_file_edits.into_iter().next().unwrap(); | 266 | let (_, edit) = edit.source_file_edits.into_iter().next().unwrap(); |
271 | 267 | ||
272 | let change = to_proto::text_edit_vec(&line_index, line_endings, edit); | 268 | let change = to_proto::text_edit_vec(&line_index, edit); |
273 | Ok(Some(change)) | 269 | Ok(Some(change)) |
274 | } | 270 | } |
275 | 271 | ||
@@ -279,7 +275,7 @@ pub(crate) fn handle_document_symbol( | |||
279 | ) -> Result<Option<lsp_types::DocumentSymbolResponse>> { | 275 | ) -> Result<Option<lsp_types::DocumentSymbolResponse>> { |
280 | let _p = profile::span("handle_document_symbol"); | 276 | let _p = profile::span("handle_document_symbol"); |
281 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 277 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
282 | let line_index = snap.analysis.file_line_index(file_id)?; | 278 | let line_index = snap.file_line_index(file_id)?; |
283 | 279 | ||
284 | let mut parents: Vec<(lsp_types::DocumentSymbol, Option<usize>)> = Vec::new(); | 280 | let mut parents: Vec<(lsp_types::DocumentSymbol, Option<usize>)> = Vec::new(); |
285 | 281 | ||
@@ -535,7 +531,7 @@ pub(crate) fn handle_runnables( | |||
535 | ) -> Result<Vec<lsp_ext::Runnable>> { | 531 | ) -> Result<Vec<lsp_ext::Runnable>> { |
536 | let _p = profile::span("handle_runnables"); | 532 | let _p = profile::span("handle_runnables"); |
537 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 533 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
538 | let line_index = snap.analysis.file_line_index(file_id)?; | 534 | let line_index = snap.file_line_index(file_id)?; |
539 | let offset = params.position.map(|it| from_proto::offset(&line_index, it)); | 535 | let offset = params.position.map(|it| from_proto::offset(&line_index, it)); |
540 | let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; | 536 | let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; |
541 | 537 | ||
@@ -645,14 +641,12 @@ pub(crate) fn handle_completion( | |||
645 | None => return Ok(None), | 641 | None => return Ok(None), |
646 | Some(items) => items, | 642 | Some(items) => items, |
647 | }; | 643 | }; |
648 | let line_index = snap.analysis.file_line_index(position.file_id)?; | 644 | let line_index = snap.file_line_index(position.file_id)?; |
649 | let line_endings = snap.file_line_endings(position.file_id); | ||
650 | 645 | ||
651 | let items: Vec<CompletionItem> = items | 646 | let items: Vec<CompletionItem> = items |
652 | .into_iter() | 647 | .into_iter() |
653 | .flat_map(|item| { | 648 | .flat_map(|item| { |
654 | let mut new_completion_items = | 649 | let mut new_completion_items = to_proto::completion_item(&line_index, item.clone()); |
655 | to_proto::completion_item(&line_index, line_endings, item.clone()); | ||
656 | 650 | ||
657 | if completion_config.enable_imports_on_the_fly { | 651 | if completion_config.enable_imports_on_the_fly { |
658 | for new_item in &mut new_completion_items { | 652 | for new_item in &mut new_completion_items { |
@@ -693,8 +687,7 @@ pub(crate) fn handle_completion_resolve( | |||
693 | }; | 687 | }; |
694 | 688 | ||
695 | let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?; | 689 | let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?; |
696 | let line_index = snap.analysis.file_line_index(file_id)?; | 690 | let line_index = snap.file_line_index(file_id)?; |
697 | let line_endings = snap.file_line_endings(file_id); | ||
698 | let offset = from_proto::offset(&line_index, resolve_data.position.position); | 691 | let offset = from_proto::offset(&line_index, resolve_data.position.position); |
699 | 692 | ||
700 | let additional_edits = snap | 693 | let additional_edits = snap |
@@ -707,9 +700,7 @@ pub(crate) fn handle_completion_resolve( | |||
707 | resolve_data.import_for_trait_assoc_item, | 700 | resolve_data.import_for_trait_assoc_item, |
708 | )? | 701 | )? |
709 | .into_iter() | 702 | .into_iter() |
710 | .flat_map(|edit| { | 703 | .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) |
711 | edit.into_iter().map(|indel| to_proto::text_edit(&line_index, line_endings, indel)) | ||
712 | }) | ||
713 | .collect_vec(); | 704 | .collect_vec(); |
714 | 705 | ||
715 | if !all_edits_are_disjoint(&original_completion, &additional_edits) { | 706 | if !all_edits_are_disjoint(&original_completion, &additional_edits) { |
@@ -738,7 +729,7 @@ pub(crate) fn handle_folding_range( | |||
738 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 729 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
739 | let folds = snap.analysis.folding_ranges(file_id)?; | 730 | let folds = snap.analysis.folding_ranges(file_id)?; |
740 | let text = snap.analysis.file_text(file_id)?; | 731 | let text = snap.analysis.file_text(file_id)?; |
741 | let line_index = snap.analysis.file_line_index(file_id)?; | 732 | let line_index = snap.file_line_index(file_id)?; |
742 | let line_folding_only = snap.config.line_folding_only(); | 733 | let line_folding_only = snap.config.line_folding_only(); |
743 | let res = folds | 734 | let res = folds |
744 | .into_iter() | 735 | .into_iter() |
@@ -775,7 +766,7 @@ pub(crate) fn handle_hover( | |||
775 | None => return Ok(None), | 766 | None => return Ok(None), |
776 | Some(info) => info, | 767 | Some(info) => info, |
777 | }; | 768 | }; |
778 | let line_index = snap.analysis.file_line_index(position.file_id)?; | 769 | let line_index = snap.file_line_index(position.file_id)?; |
779 | let range = to_proto::range(&line_index, info.range); | 770 | let range = to_proto::range(&line_index, info.range); |
780 | let hover = lsp_ext::Hover { | 771 | let hover = lsp_ext::Hover { |
781 | hover: lsp_types::Hover { | 772 | hover: lsp_types::Hover { |
@@ -797,7 +788,7 @@ pub(crate) fn handle_prepare_rename( | |||
797 | 788 | ||
798 | let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?; | 789 | let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?; |
799 | 790 | ||
800 | let line_index = snap.analysis.file_line_index(position.file_id)?; | 791 | let line_index = snap.file_line_index(position.file_id)?; |
801 | let range = to_proto::range(&line_index, change.range); | 792 | let range = to_proto::range(&line_index, change.range); |
802 | Ok(Some(PrepareRenameResponse::Range(range))) | 793 | Ok(Some(PrepareRenameResponse::Range(range))) |
803 | } | 794 | } |
@@ -857,8 +848,7 @@ pub(crate) fn handle_formatting( | |||
857 | let file = snap.analysis.file_text(file_id)?; | 848 | let file = snap.analysis.file_text(file_id)?; |
858 | let crate_ids = snap.analysis.crate_for(file_id)?; | 849 | let crate_ids = snap.analysis.crate_for(file_id)?; |
859 | 850 | ||
860 | let file_line_index = snap.analysis.file_line_index(file_id)?; | 851 | let line_index = snap.file_line_index(file_id)?; |
861 | let file_line_endings = snap.file_line_endings(file_id); | ||
862 | 852 | ||
863 | let mut rustfmt = match snap.config.rustfmt() { | 853 | let mut rustfmt = match snap.config.rustfmt() { |
864 | RustfmtConfig::Rustfmt { extra_args } => { | 854 | RustfmtConfig::Rustfmt { extra_args } => { |
@@ -935,24 +925,19 @@ pub(crate) fn handle_formatting( | |||
935 | 925 | ||
936 | let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout); | 926 | let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout); |
937 | 927 | ||
938 | if file_line_endings != new_line_endings { | 928 | if line_index.endings != new_line_endings { |
939 | // If line endings are different, send the entire file. | 929 | // If line endings are different, send the entire file. |
940 | // Diffing would not work here, as the line endings might be the only | 930 | // Diffing would not work here, as the line endings might be the only |
941 | // difference. | 931 | // difference. |
942 | Ok(Some(to_proto::text_edit_vec( | 932 | Ok(Some(to_proto::text_edit_vec( |
943 | &file_line_index, | 933 | &line_index, |
944 | new_line_endings, | ||
945 | TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text), | 934 | TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text), |
946 | ))) | 935 | ))) |
947 | } else if *file == new_text { | 936 | } else if *file == new_text { |
948 | // The document is already formatted correctly -- no edits needed. | 937 | // The document is already formatted correctly -- no edits needed. |
949 | Ok(None) | 938 | Ok(None) |
950 | } else { | 939 | } else { |
951 | Ok(Some(to_proto::text_edit_vec( | 940 | Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text)))) |
952 | &file_line_index, | ||
953 | file_line_endings, | ||
954 | diff(&file, &new_text), | ||
955 | ))) | ||
956 | } | 941 | } |
957 | } | 942 | } |
958 | 943 | ||
@@ -969,7 +954,7 @@ pub(crate) fn handle_code_action( | |||
969 | } | 954 | } |
970 | 955 | ||
971 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 956 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
972 | let line_index = snap.analysis.file_line_index(file_id)?; | 957 | let line_index = snap.file_line_index(file_id)?; |
973 | let range = from_proto::text_range(&line_index, params.range); | 958 | let range = from_proto::text_range(&line_index, params.range); |
974 | let frange = FileRange { file_id, range }; | 959 | let frange = FileRange { file_id, range }; |
975 | 960 | ||
@@ -1010,7 +995,7 @@ pub(crate) fn handle_code_action( | |||
1010 | fn add_quick_fixes( | 995 | fn add_quick_fixes( |
1011 | snap: &GlobalStateSnapshot, | 996 | snap: &GlobalStateSnapshot, |
1012 | frange: FileRange, | 997 | frange: FileRange, |
1013 | line_index: &Arc<LineIndex>, | 998 | line_index: &LineIndex, |
1014 | acc: &mut Vec<lsp_ext::CodeAction>, | 999 | acc: &mut Vec<lsp_ext::CodeAction>, |
1015 | ) -> Result<()> { | 1000 | ) -> Result<()> { |
1016 | let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics(), frange.file_id)?; | 1001 | let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics(), frange.file_id)?; |
@@ -1052,7 +1037,7 @@ pub(crate) fn handle_code_action_resolve( | |||
1052 | }; | 1037 | }; |
1053 | 1038 | ||
1054 | let file_id = from_proto::file_id(&snap, ¶ms.code_action_params.text_document.uri)?; | 1039 | let file_id = from_proto::file_id(&snap, ¶ms.code_action_params.text_document.uri)?; |
1055 | let line_index = snap.analysis.file_line_index(file_id)?; | 1040 | let line_index = snap.file_line_index(file_id)?; |
1056 | let range = from_proto::text_range(&line_index, params.code_action_params.range); | 1041 | let range = from_proto::text_range(&line_index, params.code_action_params.range); |
1057 | let frange = FileRange { file_id, range }; | 1042 | let frange = FileRange { file_id, range }; |
1058 | 1043 | ||
@@ -1131,7 +1116,7 @@ pub(crate) fn handle_document_highlight( | |||
1131 | ) -> Result<Option<Vec<DocumentHighlight>>> { | 1116 | ) -> Result<Option<Vec<DocumentHighlight>>> { |
1132 | let _p = profile::span("handle_document_highlight"); | 1117 | let _p = profile::span("handle_document_highlight"); |
1133 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; | 1118 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
1134 | let line_index = snap.analysis.file_line_index(position.file_id)?; | 1119 | let line_index = snap.file_line_index(position.file_id)?; |
1135 | 1120 | ||
1136 | let refs = match snap | 1121 | let refs = match snap |
1137 | .analysis | 1122 | .analysis |
@@ -1192,7 +1177,7 @@ pub(crate) fn publish_diagnostics( | |||
1192 | file_id: FileId, | 1177 | file_id: FileId, |
1193 | ) -> Result<Vec<Diagnostic>> { | 1178 | ) -> Result<Vec<Diagnostic>> { |
1194 | let _p = profile::span("publish_diagnostics"); | 1179 | let _p = profile::span("publish_diagnostics"); |
1195 | let line_index = snap.analysis.file_line_index(file_id)?; | 1180 | let line_index = snap.file_line_index(file_id)?; |
1196 | 1181 | ||
1197 | let diagnostics: Vec<Diagnostic> = snap | 1182 | let diagnostics: Vec<Diagnostic> = snap |
1198 | .analysis | 1183 | .analysis |
@@ -1226,7 +1211,7 @@ pub(crate) fn handle_inlay_hints( | |||
1226 | ) -> Result<Vec<InlayHint>> { | 1211 | ) -> Result<Vec<InlayHint>> { |
1227 | let _p = profile::span("handle_inlay_hints"); | 1212 | let _p = profile::span("handle_inlay_hints"); |
1228 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 1213 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
1229 | let line_index = snap.analysis.file_line_index(file_id)?; | 1214 | let line_index = snap.file_line_index(file_id)?; |
1230 | Ok(snap | 1215 | Ok(snap |
1231 | .analysis | 1216 | .analysis |
1232 | .inlay_hints(file_id, &snap.config.inlay_hints())? | 1217 | .inlay_hints(file_id, &snap.config.inlay_hints())? |
@@ -1277,7 +1262,7 @@ pub(crate) fn handle_call_hierarchy_incoming( | |||
1277 | 1262 | ||
1278 | for call_item in call_items.into_iter() { | 1263 | for call_item in call_items.into_iter() { |
1279 | let file_id = call_item.target.file_id; | 1264 | let file_id = call_item.target.file_id; |
1280 | let line_index = snap.analysis.file_line_index(file_id)?; | 1265 | let line_index = snap.file_line_index(file_id)?; |
1281 | let item = to_proto::call_hierarchy_item(&snap, call_item.target)?; | 1266 | let item = to_proto::call_hierarchy_item(&snap, call_item.target)?; |
1282 | res.push(CallHierarchyIncomingCall { | 1267 | res.push(CallHierarchyIncomingCall { |
1283 | from: item, | 1268 | from: item, |
@@ -1312,7 +1297,7 @@ pub(crate) fn handle_call_hierarchy_outgoing( | |||
1312 | 1297 | ||
1313 | for call_item in call_items.into_iter() { | 1298 | for call_item in call_items.into_iter() { |
1314 | let file_id = call_item.target.file_id; | 1299 | let file_id = call_item.target.file_id; |
1315 | let line_index = snap.analysis.file_line_index(file_id)?; | 1300 | let line_index = snap.file_line_index(file_id)?; |
1316 | let item = to_proto::call_hierarchy_item(&snap, call_item.target)?; | 1301 | let item = to_proto::call_hierarchy_item(&snap, call_item.target)?; |
1317 | res.push(CallHierarchyOutgoingCall { | 1302 | res.push(CallHierarchyOutgoingCall { |
1318 | to: item, | 1303 | to: item, |
@@ -1335,7 +1320,7 @@ pub(crate) fn handle_semantic_tokens_full( | |||
1335 | 1320 | ||
1336 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 1321 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
1337 | let text = snap.analysis.file_text(file_id)?; | 1322 | let text = snap.analysis.file_text(file_id)?; |
1338 | let line_index = snap.analysis.file_line_index(file_id)?; | 1323 | let line_index = snap.file_line_index(file_id)?; |
1339 | 1324 | ||
1340 | let highlights = snap.analysis.highlight(file_id)?; | 1325 | let highlights = snap.analysis.highlight(file_id)?; |
1341 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | 1326 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); |
@@ -1354,7 +1339,7 @@ pub(crate) fn handle_semantic_tokens_full_delta( | |||
1354 | 1339 | ||
1355 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 1340 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
1356 | let text = snap.analysis.file_text(file_id)?; | 1341 | let text = snap.analysis.file_text(file_id)?; |
1357 | let line_index = snap.analysis.file_line_index(file_id)?; | 1342 | let line_index = snap.file_line_index(file_id)?; |
1358 | 1343 | ||
1359 | let highlights = snap.analysis.highlight(file_id)?; | 1344 | let highlights = snap.analysis.highlight(file_id)?; |
1360 | 1345 | ||
@@ -1384,7 +1369,7 @@ pub(crate) fn handle_semantic_tokens_range( | |||
1384 | 1369 | ||
1385 | let frange = from_proto::file_range(&snap, params.text_document, params.range)?; | 1370 | let frange = from_proto::file_range(&snap, params.text_document, params.range)?; |
1386 | let text = snap.analysis.file_text(frange.file_id)?; | 1371 | let text = snap.analysis.file_text(frange.file_id)?; |
1387 | let line_index = snap.analysis.file_line_index(frange.file_id)?; | 1372 | let line_index = snap.file_line_index(frange.file_id)?; |
1388 | 1373 | ||
1389 | let highlights = snap.analysis.highlight_range(frange)?; | 1374 | let highlights = snap.analysis.highlight_range(frange)?; |
1390 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | 1375 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); |
@@ -1432,7 +1417,7 @@ fn show_impl_command_link( | |||
1432 | if snap.config.hover().implementations { | 1417 | if snap.config.hover().implementations { |
1433 | if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) { | 1418 | if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) { |
1434 | let uri = to_proto::url(snap, position.file_id); | 1419 | let uri = to_proto::url(snap, position.file_id); |
1435 | let line_index = snap.analysis.file_line_index(position.file_id).ok()?; | 1420 | let line_index = snap.file_line_index(position.file_id).ok()?; |
1436 | let position = to_proto::position(&line_index, position.offset); | 1421 | let position = to_proto::position(&line_index, position.offset); |
1437 | let locations: Vec<_> = nav_data | 1422 | let locations: Vec<_> = nav_data |
1438 | .info | 1423 | .info |
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 2207b9a87..8b874239c 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -29,7 +29,7 @@ mod from_proto; | |||
29 | mod semantic_tokens; | 29 | mod semantic_tokens; |
30 | mod markdown; | 30 | mod markdown; |
31 | mod diagnostics; | 31 | mod diagnostics; |
32 | mod line_endings; | 32 | mod line_index; |
33 | mod request_metrics; | 33 | mod request_metrics; |
34 | mod lsp_utils; | 34 | mod lsp_utils; |
35 | mod thread_pool; | 35 | mod thread_pool; |
diff --git a/crates/rust-analyzer/src/line_endings.rs b/crates/rust-analyzer/src/line_index.rs index bf0e255d9..c116414da 100644 --- a/crates/rust-analyzer/src/line_endings.rs +++ b/crates/rust-analyzer/src/line_index.rs | |||
@@ -1,7 +1,23 @@ | |||
1 | //! Enhances `ide::LineIndex` with additional info required to convert offsets | ||
2 | //! into lsp positions. | ||
3 | //! | ||
1 | //! We maintain invariant that all internal strings use `\n` as line separator. | 4 | //! We maintain invariant that all internal strings use `\n` as line separator. |
2 | //! This module does line ending conversion and detection (so that we can | 5 | //! This module does line ending conversion and detection (so that we can |
3 | //! convert back to `\r\n` on the way out). | 6 | //! convert back to `\r\n` on the way out). |
4 | 7 | ||
8 | use std::sync::Arc; | ||
9 | |||
10 | pub enum OffsetEncoding { | ||
11 | Utf8, | ||
12 | Utf16, | ||
13 | } | ||
14 | |||
15 | pub(crate) struct LineIndex { | ||
16 | pub(crate) index: Arc<ide::LineIndex>, | ||
17 | pub(crate) endings: LineEndings, | ||
18 | pub(crate) encoding: OffsetEncoding, | ||
19 | } | ||
20 | |||
5 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 21 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
6 | pub(crate) enum LineEndings { | 22 | pub(crate) enum LineEndings { |
7 | Unix, | 23 | Unix, |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index a1ad855c3..0d2c8f7ff 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -385,3 +385,7 @@ pub(crate) enum CodeLensResolveData { | |||
385 | Impls(lsp_types::request::GotoImplementationParams), | 385 | Impls(lsp_types::request::GotoImplementationParams), |
386 | References(lsp_types::TextDocumentPositionParams), | 386 | References(lsp_types::TextDocumentPositionParams), |
387 | } | 387 | } |
388 | |||
389 | pub fn supports_utf8(caps: &lsp_types::ClientCapabilities) -> bool { | ||
390 | caps.offset_encoding.as_deref().unwrap_or_default().iter().any(|it| it == "utf-8") | ||
391 | } | ||
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 2d06fe538..84f78b5b8 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -1,11 +1,14 @@ | |||
1 | //! Utilities for LSP-related boilerplate code. | 1 | //! Utilities for LSP-related boilerplate code. |
2 | use std::{error::Error, ops::Range}; | 2 | use std::{error::Error, ops::Range, sync::Arc}; |
3 | 3 | ||
4 | use ide::LineIndex; | ||
5 | use ide_db::base_db::Canceled; | 4 | use ide_db::base_db::Canceled; |
6 | use lsp_server::Notification; | 5 | use lsp_server::Notification; |
7 | 6 | ||
8 | use crate::{from_proto, global_state::GlobalState}; | 7 | use crate::{ |
8 | from_proto, | ||
9 | global_state::GlobalState, | ||
10 | line_index::{LineEndings, LineIndex, OffsetEncoding}, | ||
11 | }; | ||
9 | 12 | ||
10 | pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { | 13 | pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { |
11 | e.downcast_ref::<Canceled>().is_some() | 14 | e.downcast_ref::<Canceled>().is_some() |
@@ -90,7 +93,13 @@ pub(crate) fn apply_document_changes( | |||
90 | old_text: &mut String, | 93 | old_text: &mut String, |
91 | content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>, | 94 | content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>, |
92 | ) { | 95 | ) { |
93 | let mut line_index = LineIndex::new(old_text); | 96 | let mut line_index = LineIndex { |
97 | index: Arc::new(ide::LineIndex::new(old_text)), | ||
98 | // We don't care about line endings or offset encoding here. | ||
99 | endings: LineEndings::Unix, | ||
100 | encoding: OffsetEncoding::Utf16, | ||
101 | }; | ||
102 | |||
94 | // The changes we got must be applied sequentially, but can cross lines so we | 103 | // The changes we got must be applied sequentially, but can cross lines so we |
95 | // have to keep our line index updated. | 104 | // have to keep our line index updated. |
96 | // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we | 105 | // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we |
@@ -115,7 +124,7 @@ pub(crate) fn apply_document_changes( | |||
115 | match change.range { | 124 | match change.range { |
116 | Some(range) => { | 125 | Some(range) => { |
117 | if !index_valid.covers(range.end.line) { | 126 | if !index_valid.covers(range.end.line) { |
118 | line_index = LineIndex::new(&old_text); | 127 | line_index.index = Arc::new(ide::LineIndex::new(&old_text)); |
119 | } | 128 | } |
120 | index_valid = IndexValid::UpToLineExclusive(range.start.line); | 129 | index_valid = IndexValid::UpToLineExclusive(range.start.line); |
121 | let range = from_proto::text_range(&line_index, range); | 130 | let range = from_proto::text_range(&line_index, range); |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index b0ddb603d..70cb7fbab 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -7,22 +7,29 @@ use std::{ | |||
7 | use ide::{ | 7 | use ide::{ |
8 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, | 8 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, |
9 | Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, | 9 | Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, |
10 | HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, | 10 | HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, Markup, NavigationTarget, |
11 | NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange, TextEdit, | 11 | ReferenceAccess, RenameError, Runnable, Severity, SourceChange, TextEdit, TextRange, TextSize, |
12 | TextRange, TextSize, | ||
13 | }; | 12 | }; |
14 | use ide_db::SymbolKind; | 13 | use ide_db::SymbolKind; |
15 | use itertools::Itertools; | 14 | use itertools::Itertools; |
16 | use serde_json::to_value; | 15 | use serde_json::to_value; |
17 | 16 | ||
18 | use crate::{ | 17 | use crate::{ |
19 | cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot, | 18 | cargo_target_spec::CargoTargetSpec, |
20 | line_endings::LineEndings, lsp_ext, semantic_tokens, Result, | 19 | global_state::GlobalStateSnapshot, |
20 | line_index::{LineEndings, LineIndex, OffsetEncoding}, | ||
21 | lsp_ext, semantic_tokens, Result, | ||
21 | }; | 22 | }; |
22 | 23 | ||
23 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { | 24 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { |
24 | let line_col = line_index.line_col(offset); | 25 | let line_col = line_index.index.line_col(offset); |
25 | lsp_types::Position::new(line_col.line, line_col.col_utf16) | 26 | match line_index.encoding { |
27 | OffsetEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col), | ||
28 | OffsetEncoding::Utf16 => { | ||
29 | let line_col = line_index.index.to_utf16(line_col); | ||
30 | lsp_types::Position::new(line_col.line, line_col.col) | ||
31 | } | ||
32 | } | ||
26 | } | 33 | } |
27 | 34 | ||
28 | pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range { | 35 | pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range { |
@@ -122,13 +129,9 @@ pub(crate) fn completion_item_kind( | |||
122 | } | 129 | } |
123 | } | 130 | } |
124 | 131 | ||
125 | pub(crate) fn text_edit( | 132 | pub(crate) fn text_edit(line_index: &LineIndex, indel: Indel) -> lsp_types::TextEdit { |
126 | line_index: &LineIndex, | ||
127 | line_endings: LineEndings, | ||
128 | indel: Indel, | ||
129 | ) -> lsp_types::TextEdit { | ||
130 | let range = range(line_index, indel.delete); | 133 | let range = range(line_index, indel.delete); |
131 | let new_text = match line_endings { | 134 | let new_text = match line_index.endings { |
132 | LineEndings::Unix => indel.insert, | 135 | LineEndings::Unix => indel.insert, |
133 | LineEndings::Dos => indel.insert.replace('\n', "\r\n"), | 136 | LineEndings::Dos => indel.insert.replace('\n', "\r\n"), |
134 | }; | 137 | }; |
@@ -137,11 +140,10 @@ pub(crate) fn text_edit( | |||
137 | 140 | ||
138 | pub(crate) fn snippet_text_edit( | 141 | pub(crate) fn snippet_text_edit( |
139 | line_index: &LineIndex, | 142 | line_index: &LineIndex, |
140 | line_endings: LineEndings, | ||
141 | is_snippet: bool, | 143 | is_snippet: bool, |
142 | indel: Indel, | 144 | indel: Indel, |
143 | ) -> lsp_ext::SnippetTextEdit { | 145 | ) -> lsp_ext::SnippetTextEdit { |
144 | let text_edit = text_edit(line_index, line_endings, indel); | 146 | let text_edit = text_edit(line_index, indel); |
145 | let insert_text_format = | 147 | let insert_text_format = |
146 | if is_snippet { Some(lsp_types::InsertTextFormat::Snippet) } else { None }; | 148 | if is_snippet { Some(lsp_types::InsertTextFormat::Snippet) } else { None }; |
147 | lsp_ext::SnippetTextEdit { | 149 | lsp_ext::SnippetTextEdit { |
@@ -153,27 +155,24 @@ pub(crate) fn snippet_text_edit( | |||
153 | 155 | ||
154 | pub(crate) fn text_edit_vec( | 156 | pub(crate) fn text_edit_vec( |
155 | line_index: &LineIndex, | 157 | line_index: &LineIndex, |
156 | line_endings: LineEndings, | ||
157 | text_edit: TextEdit, | 158 | text_edit: TextEdit, |
158 | ) -> Vec<lsp_types::TextEdit> { | 159 | ) -> Vec<lsp_types::TextEdit> { |
159 | text_edit.into_iter().map(|indel| self::text_edit(line_index, line_endings, indel)).collect() | 160 | text_edit.into_iter().map(|indel| self::text_edit(line_index, indel)).collect() |
160 | } | 161 | } |
161 | 162 | ||
162 | pub(crate) fn snippet_text_edit_vec( | 163 | pub(crate) fn snippet_text_edit_vec( |
163 | line_index: &LineIndex, | 164 | line_index: &LineIndex, |
164 | line_endings: LineEndings, | ||
165 | is_snippet: bool, | 165 | is_snippet: bool, |
166 | text_edit: TextEdit, | 166 | text_edit: TextEdit, |
167 | ) -> Vec<lsp_ext::SnippetTextEdit> { | 167 | ) -> Vec<lsp_ext::SnippetTextEdit> { |
168 | text_edit | 168 | text_edit |
169 | .into_iter() | 169 | .into_iter() |
170 | .map(|indel| self::snippet_text_edit(line_index, line_endings, is_snippet, indel)) | 170 | .map(|indel| self::snippet_text_edit(line_index, is_snippet, indel)) |
171 | .collect() | 171 | .collect() |
172 | } | 172 | } |
173 | 173 | ||
174 | pub(crate) fn completion_item( | 174 | pub(crate) fn completion_item( |
175 | line_index: &LineIndex, | 175 | line_index: &LineIndex, |
176 | line_endings: LineEndings, | ||
177 | completion_item: CompletionItem, | 176 | completion_item: CompletionItem, |
178 | ) -> Vec<lsp_types::CompletionItem> { | 177 | ) -> Vec<lsp_types::CompletionItem> { |
179 | fn set_score(res: &mut lsp_types::CompletionItem, label: &str) { | 178 | fn set_score(res: &mut lsp_types::CompletionItem, label: &str) { |
@@ -190,19 +189,19 @@ pub(crate) fn completion_item( | |||
190 | for indel in completion_item.text_edit().iter() { | 189 | for indel in completion_item.text_edit().iter() { |
191 | if indel.delete.contains_range(source_range) { | 190 | if indel.delete.contains_range(source_range) { |
192 | text_edit = Some(if indel.delete == source_range { | 191 | text_edit = Some(if indel.delete == source_range { |
193 | self::text_edit(line_index, line_endings, indel.clone()) | 192 | self::text_edit(line_index, indel.clone()) |
194 | } else { | 193 | } else { |
195 | assert!(source_range.end() == indel.delete.end()); | 194 | assert!(source_range.end() == indel.delete.end()); |
196 | let range1 = TextRange::new(indel.delete.start(), source_range.start()); | 195 | let range1 = TextRange::new(indel.delete.start(), source_range.start()); |
197 | let range2 = source_range; | 196 | let range2 = source_range; |
198 | let indel1 = Indel::replace(range1, String::new()); | 197 | let indel1 = Indel::replace(range1, String::new()); |
199 | let indel2 = Indel::replace(range2, indel.insert.clone()); | 198 | let indel2 = Indel::replace(range2, indel.insert.clone()); |
200 | additional_text_edits.push(self::text_edit(line_index, line_endings, indel1)); | 199 | additional_text_edits.push(self::text_edit(line_index, indel1)); |
201 | self::text_edit(line_index, line_endings, indel2) | 200 | self::text_edit(line_index, indel2) |
202 | }) | 201 | }) |
203 | } else { | 202 | } else { |
204 | assert!(source_range.intersect(indel.delete).is_none()); | 203 | assert!(source_range.intersect(indel.delete).is_none()); |
205 | let text_edit = self::text_edit(line_index, line_endings, indel.clone()); | 204 | let text_edit = self::text_edit(line_index, indel.clone()); |
206 | additional_text_edits.push(text_edit); | 205 | additional_text_edits.push(text_edit); |
207 | } | 206 | } |
208 | } | 207 | } |
@@ -358,7 +357,7 @@ pub(crate) fn semantic_tokens( | |||
358 | let token_index = semantic_tokens::type_index(type_); | 357 | let token_index = semantic_tokens::type_index(type_); |
359 | let modifier_bitset = mods.0; | 358 | let modifier_bitset = mods.0; |
360 | 359 | ||
361 | for mut text_range in line_index.lines(highlight_range.range) { | 360 | for mut text_range in line_index.index.lines(highlight_range.range) { |
362 | if text[text_range].ends_with('\n') { | 361 | if text[text_range].ends_with('\n') { |
363 | text_range = | 362 | text_range = |
364 | TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n')); | 363 | TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n')); |
@@ -565,7 +564,7 @@ pub(crate) fn location( | |||
565 | frange: FileRange, | 564 | frange: FileRange, |
566 | ) -> Result<lsp_types::Location> { | 565 | ) -> Result<lsp_types::Location> { |
567 | let url = url(snap, frange.file_id); | 566 | let url = url(snap, frange.file_id); |
568 | let line_index = snap.analysis.file_line_index(frange.file_id)?; | 567 | let line_index = snap.file_line_index(frange.file_id)?; |
569 | let range = range(&line_index, frange.range); | 568 | let range = range(&line_index, frange.range); |
570 | let loc = lsp_types::Location::new(url, range); | 569 | let loc = lsp_types::Location::new(url, range); |
571 | Ok(loc) | 570 | Ok(loc) |
@@ -577,7 +576,7 @@ pub(crate) fn location_from_nav( | |||
577 | nav: NavigationTarget, | 576 | nav: NavigationTarget, |
578 | ) -> Result<lsp_types::Location> { | 577 | ) -> Result<lsp_types::Location> { |
579 | let url = url(snap, nav.file_id); | 578 | let url = url(snap, nav.file_id); |
580 | let line_index = snap.analysis.file_line_index(nav.file_id)?; | 579 | let line_index = snap.file_line_index(nav.file_id)?; |
581 | let range = range(&line_index, nav.full_range); | 580 | let range = range(&line_index, nav.full_range); |
582 | let loc = lsp_types::Location::new(url, range); | 581 | let loc = lsp_types::Location::new(url, range); |
583 | Ok(loc) | 582 | Ok(loc) |
@@ -590,7 +589,7 @@ pub(crate) fn location_link( | |||
590 | ) -> Result<lsp_types::LocationLink> { | 589 | ) -> Result<lsp_types::LocationLink> { |
591 | let origin_selection_range = match src { | 590 | let origin_selection_range = match src { |
592 | Some(src) => { | 591 | Some(src) => { |
593 | let line_index = snap.analysis.file_line_index(src.file_id)?; | 592 | let line_index = snap.file_line_index(src.file_id)?; |
594 | let range = range(&line_index, src.range); | 593 | let range = range(&line_index, src.range); |
595 | Some(range) | 594 | Some(range) |
596 | } | 595 | } |
@@ -610,7 +609,7 @@ fn location_info( | |||
610 | snap: &GlobalStateSnapshot, | 609 | snap: &GlobalStateSnapshot, |
611 | target: NavigationTarget, | 610 | target: NavigationTarget, |
612 | ) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { | 611 | ) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { |
613 | let line_index = snap.analysis.file_line_index(target.file_id)?; | 612 | let line_index = snap.file_line_index(target.file_id)?; |
614 | 613 | ||
615 | let target_uri = url(snap, target.file_id); | 614 | let target_uri = url(snap, target.file_id); |
616 | let target_range = range(&line_index, target.full_range); | 615 | let target_range = range(&line_index, target.full_range); |
@@ -648,12 +647,8 @@ pub(crate) fn snippet_text_document_edit( | |||
648 | edit: TextEdit, | 647 | edit: TextEdit, |
649 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { | 648 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { |
650 | let text_document = optional_versioned_text_document_identifier(snap, file_id); | 649 | let text_document = optional_versioned_text_document_identifier(snap, file_id); |
651 | let line_index = snap.analysis.file_line_index(file_id)?; | 650 | let line_index = snap.file_line_index(file_id)?; |
652 | let line_endings = snap.file_line_endings(file_id); | 651 | let edits = edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect(); |
653 | let edits = edit | ||
654 | .into_iter() | ||
655 | .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it)) | ||
656 | .collect(); | ||
657 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) | 652 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) |
658 | } | 653 | } |
659 | 654 | ||
@@ -674,9 +669,8 @@ pub(crate) fn snippet_text_document_ops( | |||
674 | if !initial_contents.is_empty() { | 669 | if !initial_contents.is_empty() { |
675 | let text_document = | 670 | let text_document = |
676 | lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version: None }; | 671 | lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version: None }; |
677 | let range = range(&LineIndex::new(""), TextRange::empty(TextSize::from(0))); | ||
678 | let text_edit = lsp_ext::SnippetTextEdit { | 672 | let text_edit = lsp_ext::SnippetTextEdit { |
679 | range, | 673 | range: lsp_types::Range::default(), |
680 | new_text: initial_contents, | 674 | new_text: initial_contents, |
681 | insert_text_format: Some(lsp_types::InsertTextFormat::PlainText), | 675 | insert_text_format: Some(lsp_types::InsertTextFormat::PlainText), |
682 | }; | 676 | }; |
@@ -867,7 +861,7 @@ pub(crate) fn code_lens( | |||
867 | ) -> Result<lsp_types::CodeLens> { | 861 | ) -> Result<lsp_types::CodeLens> { |
868 | match annotation.kind { | 862 | match annotation.kind { |
869 | AnnotationKind::Runnable { debug, runnable: run } => { | 863 | AnnotationKind::Runnable { debug, runnable: run } => { |
870 | let line_index = snap.analysis.file_line_index(run.nav.file_id)?; | 864 | let line_index = snap.file_line_index(run.nav.file_id)?; |
871 | let annotation_range = range(&line_index, annotation.range); | 865 | let annotation_range = range(&line_index, annotation.range); |
872 | 866 | ||
873 | let action = run.action(); | 867 | let action = run.action(); |
@@ -883,7 +877,7 @@ pub(crate) fn code_lens( | |||
883 | Ok(lsp_types::CodeLens { range: annotation_range, command: Some(command), data: None }) | 877 | Ok(lsp_types::CodeLens { range: annotation_range, command: Some(command), data: None }) |
884 | } | 878 | } |
885 | AnnotationKind::HasImpls { position: file_position, data } => { | 879 | AnnotationKind::HasImpls { position: file_position, data } => { |
886 | let line_index = snap.analysis.file_line_index(file_position.file_id)?; | 880 | let line_index = snap.file_line_index(file_position.file_id)?; |
887 | let annotation_range = range(&line_index, annotation.range); | 881 | let annotation_range = range(&line_index, annotation.range); |
888 | let url = url(snap, file_position.file_id); | 882 | let url = url(snap, file_position.file_id); |
889 | 883 | ||
@@ -926,7 +920,7 @@ pub(crate) fn code_lens( | |||
926 | }) | 920 | }) |
927 | } | 921 | } |
928 | AnnotationKind::HasReferences { position: file_position, data } => { | 922 | AnnotationKind::HasReferences { position: file_position, data } => { |
929 | let line_index = snap.analysis.file_line_index(file_position.file_id)?; | 923 | let line_index = snap.file_line_index(file_position.file_id)?; |
930 | let annotation_range = range(&line_index, annotation.range); | 924 | let annotation_range = range(&line_index, annotation.range); |
931 | let url = url(snap, file_position.file_id); | 925 | let url = url(snap, file_position.file_id); |
932 | 926 | ||
@@ -1060,6 +1054,8 @@ pub(crate) fn rename_error(err: RenameError) -> crate::LspError { | |||
1060 | 1054 | ||
1061 | #[cfg(test)] | 1055 | #[cfg(test)] |
1062 | mod tests { | 1056 | mod tests { |
1057 | use std::sync::Arc; | ||
1058 | |||
1063 | use hir::PrefixKind; | 1059 | use hir::PrefixKind; |
1064 | use ide::Analysis; | 1060 | use ide::Analysis; |
1065 | use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap}; | 1061 | use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap}; |
@@ -1077,7 +1073,11 @@ mod tests { | |||
1077 | }"#; | 1073 | }"#; |
1078 | 1074 | ||
1079 | let (offset, text) = test_utils::extract_offset(fixture); | 1075 | let (offset, text) = test_utils::extract_offset(fixture); |
1080 | let line_index = LineIndex::new(&text); | 1076 | let line_index = LineIndex { |
1077 | index: Arc::new(ide::LineIndex::new(&text)), | ||
1078 | endings: LineEndings::Unix, | ||
1079 | encoding: OffsetEncoding::Utf16, | ||
1080 | }; | ||
1081 | let (analysis, file_id) = Analysis::from_single_file(text); | 1081 | let (analysis, file_id) = Analysis::from_single_file(text); |
1082 | let completions: Vec<(String, Option<String>)> = analysis | 1082 | let completions: Vec<(String, Option<String>)> = analysis |
1083 | .completions( | 1083 | .completions( |
@@ -1095,7 +1095,7 @@ mod tests { | |||
1095 | .unwrap() | 1095 | .unwrap() |
1096 | .into_iter() | 1096 | .into_iter() |
1097 | .filter(|c| c.label().ends_with("arg")) | 1097 | .filter(|c| c.label().ends_with("arg")) |
1098 | .map(|c| completion_item(&line_index, LineEndings::Unix, c)) | 1098 | .map(|c| completion_item(&line_index, c)) |
1099 | .flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text))) | 1099 | .flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text))) |
1100 | .collect(); | 1100 | .collect(); |
1101 | expect_test::expect![[r#" | 1101 | expect_test::expect![[r#" |
@@ -1133,7 +1133,11 @@ fn main() { | |||
1133 | let folds = analysis.folding_ranges(file_id).unwrap(); | 1133 | let folds = analysis.folding_ranges(file_id).unwrap(); |
1134 | assert_eq!(folds.len(), 4); | 1134 | assert_eq!(folds.len(), 4); |
1135 | 1135 | ||
1136 | let line_index = LineIndex::new(&text); | 1136 | let line_index = LineIndex { |
1137 | index: Arc::new(ide::LineIndex::new(&text)), | ||
1138 | endings: LineEndings::Unix, | ||
1139 | encoding: OffsetEncoding::Utf16, | ||
1140 | }; | ||
1137 | let converted: Vec<lsp_types::FoldingRange> = | 1141 | let converted: Vec<lsp_types::FoldingRange> = |
1138 | folds.into_iter().map(|it| folding_range(&text, &line_index, true, it)).collect(); | 1142 | folds.into_iter().map(|it| folding_range(&text, &line_index, true, it)).collect(); |
1139 | 1143 | ||
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index a4e108724..164c8482e 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -1,5 +1,5 @@ | |||
1 | <!--- | 1 | <!--- |
2 | lsp_ext.rs hash: 34aec6bfeaeb97a | 2 | lsp_ext.rs hash: d279d971d4f62cd7 |
3 | 3 | ||
4 | If you need to change the above hash to make the test pass, please check if you | 4 | If you need to change the above hash to make the test pass, please check if you |
5 | need to adjust this doc as well and ping this issue: | 5 | need to adjust this doc as well and ping this issue: |
@@ -19,6 +19,12 @@ Requests, which are likely to always remain specific to `rust-analyzer` are unde | |||
19 | 19 | ||
20 | If you want to be notified about the changes to this document, subscribe to [#4604](https://github.com/rust-analyzer/rust-analyzer/issues/4604). | 20 | If you want to be notified about the changes to this document, subscribe to [#4604](https://github.com/rust-analyzer/rust-analyzer/issues/4604). |
21 | 21 | ||
22 | ## UTF-8 offsets | ||
23 | |||
24 | rust-analyzer supports clangd's extension for opting into UTF-8 as the coordinate space for offsets (by default, LSP uses UTF-16 offsets). | ||
25 | |||
26 | https://clangd.llvm.org/extensions.html#utf-8-offsets | ||
27 | |||
22 | ## `initializationOptions` | 28 | ## `initializationOptions` |
23 | 29 | ||
24 | For `initializationOptions`, `rust-analyzer` expects `"rust-analyzer"` section of the configuration. | 30 | For `initializationOptions`, `rust-analyzer` expects `"rust-analyzer"` section of the configuration. |