aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-02-16 16:34:22 +0000
committerGitHub <[email protected]>2021-02-16 16:34:22 +0000
commitc9672a0539a59b626619724092077f57c70e6ffe (patch)
tree77097454dc598a5e0a0b1d98b8c9645b9e213289 /crates
parentf7b7a09f752caba21f2b21ccb8f74421a599d2c6 (diff)
parent3f09e3fba62839f26da2f27ce27a2335b1dca7ef (diff)
Merge #7657
7657: utf8 r=matklad a=matklad - Prepare for utf-8 offsets - reduce code duplication in tests - Make utf8 default, implement utf16 in terms of it - Make it easy to add additional context for offset conversion - Implement utf8 offsets closes #7453 Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/ide_db/src/line_index.rs29
-rw-r--r--crates/ide_db/src/line_index/tests.rs33
-rw-r--r--crates/rust-analyzer/src/bin/main.rs8
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs8
-rw-r--r--crates/rust-analyzer/src/config.rs12
-rw-r--r--crates/rust-analyzer/src/from_proto.rs32
-rw-r--r--crates/rust-analyzer/src/global_state.rs11
-rw-r--r--crates/rust-analyzer/src/handlers.rs91
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-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.rs4
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs19
-rw-r--r--crates/rust-analyzer/src/to_proto.rs90
15 files changed, 214 insertions, 145 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)]
18pub struct LineCol { 18pub 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)]
26pub 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]
4fn test_line_index() { 4fn 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
9use lsp_server::Connection; 9use lsp_server::Connection;
10use project_model::ProjectManifest; 10use project_model::ProjectManifest;
11use rust_analyzer::{cli, config::Config, from_json, Result}; 11use rust_analyzer::{cli, config::Config, from_json, lsp_ext::supports_utf8, Result};
12use vfs::AbsPathBuf; 12use 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;
23use serde::{de::DeserializeOwned, Deserialize}; 23use serde::{de::DeserializeOwned, Deserialize};
24use vfs::AbsPathBuf; 24use vfs::AbsPathBuf;
25 25
26use crate::{caps::completion_item_edit_resolve, diagnostics::DiagnosticsMapConfig}; 26use crate::{
27 caps::completion_item_edit_resolve, diagnostics::DiagnosticsMapConfig,
28 line_index::OffsetEncoding, lsp_ext::supports_utf8,
29};
27 30
28config_data! { 31config_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.
2use std::convert::TryFrom; 2use std::convert::TryFrom;
3 3
4use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineIndex}; 4use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineColUtf16};
5use ide_db::base_db::{FileId, FilePosition, FileRange}; 5use ide_db::base_db::{FileId, FilePosition, FileRange};
6use syntax::{TextRange, TextSize}; 6use syntax::{TextRange, TextSize};
7use vfs::AbsPathBuf; 7use vfs::AbsPathBuf;
8 8
9use crate::{from_json, global_state::GlobalStateSnapshot, lsp_ext, Result}; 9use crate::{
10 from_json,
11 global_state::GlobalStateSnapshot,
12 line_index::{LineIndex, OffsetEncoding},
13 lsp_ext, Result,
14};
10 15
11pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> { 16pub(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
20pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { 25pub(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
25pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> TextRange { 39pub(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(&params.text_document_position_params.text_document.uri)?; 94 world.url_to_file_id(&params.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(&params.text_document.uri)?; 106 let file_id = world.url_to_file_id(&params.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
8use crossbeam_channel::{unbounded, Receiver, Sender}; 8use crossbeam_channel::{unbounded, Receiver, Sender};
9use flycheck::FlycheckHandle; 9use flycheck::FlycheckHandle;
10use ide::{Analysis, AnalysisHost, Change, FileId}; 10use ide::{Analysis, AnalysisHost, Cancelable, Change, FileId};
11use ide_db::base_db::{CrateId, VfsPath}; 11use ide_db::base_db::{CrateId, VfsPath};
12use lsp_types::{SemanticTokens, Url}; 12use lsp_types::{SemanticTokens, Url};
13use parking_lot::{Mutex, RwLock}; 13use 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 @@
5use std::{ 5use std::{
6 io::Write as _, 6 io::Write as _,
7 process::{self, Stdio}, 7 process::{self, Stdio},
8 sync::Arc,
9}; 8};
10 9
11use ide::{ 10use 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};
15use ide_db::SymbolKind; 14use ide_db::SymbolKind;
16use itertools::Itertools; 15use 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, &params.text_document.uri)?; 101 let id = from_proto::file_id(&snap, &params.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, &params.text_document.uri)?; 123 let file_id = from_proto::file_id(&snap, &params.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, &params.text_document.uri)?; 136 let file_id = from_proto::file_id(&snap, &params.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, &params.text_document.uri)?; 179 let file_id = from_proto::file_id(&snap, &params.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, &params.text_document.uri)?; 201 let file_id = from_proto::file_id(&snap, &params.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, &params.text_document.uri)?; 277 let file_id = from_proto::file_id(&snap, &params.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, &params.text_document.uri)?; 533 let file_id = from_proto::file_id(&snap, &params.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, &params.text_document.uri)?; 729 let file_id = from_proto::file_id(&snap, &params.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, &params.text_document.uri)?; 956 let file_id = from_proto::file_id(&snap, &params.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(
1010fn add_quick_fixes( 995fn 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, &params.code_action_params.text_document.uri)?; 1039 let file_id = from_proto::file_id(&snap, &params.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, &params.text_document.uri)?; 1213 let file_id = from_proto::file_id(&snap, &params.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, &params.text_document.uri)?; 1321 let file_id = from_proto::file_id(&snap, &params.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, &params.text_document.uri)?; 1340 let file_id = from_proto::file_id(&snap, &params.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;
29mod semantic_tokens; 29mod semantic_tokens;
30mod markdown; 30mod markdown;
31mod diagnostics; 31mod diagnostics;
32mod line_endings; 32mod line_index;
33mod request_metrics; 33mod request_metrics;
34mod lsp_utils; 34mod lsp_utils;
35mod thread_pool; 35mod 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
8use std::sync::Arc;
9
10pub enum OffsetEncoding {
11 Utf8,
12 Utf16,
13}
14
15pub(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)]
6pub(crate) enum LineEndings { 22pub(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
389pub 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.
2use std::{error::Error, ops::Range}; 2use std::{error::Error, ops::Range, sync::Arc};
3 3
4use ide::LineIndex;
5use ide_db::base_db::Canceled; 4use ide_db::base_db::Canceled;
6use lsp_server::Notification; 5use lsp_server::Notification;
7 6
8use crate::{from_proto, global_state::GlobalState}; 7use crate::{
8 from_proto,
9 global_state::GlobalState,
10 line_index::{LineEndings, LineIndex, OffsetEncoding},
11};
9 12
10pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { 13pub(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::{
7use ide::{ 7use 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};
14use ide_db::SymbolKind; 13use ide_db::SymbolKind;
15use itertools::Itertools; 14use itertools::Itertools;
16use serde_json::to_value; 15use serde_json::to_value;
17 16
18use crate::{ 17use 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
23pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { 24pub(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
28pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range { 35pub(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
125pub(crate) fn text_edit( 132pub(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
138pub(crate) fn snippet_text_edit( 141pub(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
154pub(crate) fn text_edit_vec( 156pub(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
162pub(crate) fn snippet_text_edit_vec( 163pub(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
174pub(crate) fn completion_item( 174pub(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)]
1062mod tests { 1056mod 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