Skip to content

Commit c9672a0

Browse files
bors[bot]matklad
andauthored
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]>
2 parents f7b7a09 + 3f09e3f commit c9672a0

File tree

16 files changed

+221
-146
lines changed

16 files changed

+221
-146
lines changed

crates/ide/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ pub use ide_db::{
9595
},
9696
call_info::CallInfo,
9797
label::Label,
98-
line_index::{LineCol, LineIndex},
98+
line_index::{LineCol, LineColUtf16, LineIndex},
9999
search::{ReferenceAccess, SearchScope},
100100
source_change::{FileSystemEdit, SourceChange},
101101
symbol_index::Query,

crates/ide_db/src/line_index.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,19 @@ pub struct LineIndex {
1515
}
1616

1717
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
18-
pub struct LineCol {
18+
pub struct LineColUtf16 {
1919
/// Zero-based
2020
pub line: u32,
2121
/// 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,
2331
}
2432

2533
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
@@ -92,14 +100,21 @@ impl LineIndex {
92100
let line = partition_point(&self.newlines, |&it| it <= offset) - 1;
93101
let line_start_offset = self.newlines[line];
94102
let col = offset - line_start_offset;
95-
96-
LineCol { line: line as u32, col_utf16: self.utf8_to_utf16_col(line as u32, col) as u32 }
103+
LineCol { line: line as u32, col: col.into() }
97104
}
98105

99106
pub fn offset(&self, line_col: LineCol) -> TextSize {
100-
//FIXME: return Result
101-
let col = self.utf16_to_utf8_col(line_col.line, line_col.col_utf16);
102-
self.newlines[line_col.line as usize] + col
107+
self.newlines[line_col.line as usize] + TextSize::from(line_col.col)
108+
}
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() }
103118
}
104119

105120
pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {

crates/ide_db/src/line_index/tests.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,29 @@ use super::*;
33
#[test]
44
fn test_line_index() {
55
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+
618
let index = LineIndex::new(text);
7-
assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
8-
assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 });
9-
assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 });
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 });
19+
for &(offset, line, col) in &table {
20+
assert_eq!(index.line_col(offset.into()), LineCol { line, col });
21+
}
1622

1723
let text = "\nhello\nworld";
24+
let table = [(0, 0, 0), (1, 1, 0), (2, 1, 1), (6, 1, 5), (7, 2, 0)];
1825
let index = LineIndex::new(text);
19-
assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
20-
assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 });
21-
assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 });
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 });
26+
for &(offset, line, col) in &table {
27+
assert_eq!(index.line_col(offset.into()), LineCol { line, col });
28+
}
2429
}
2530

2631
#[test]

crates/rust-analyzer/src/bin/main.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{convert::TryFrom, env, fs, path::PathBuf, process};
88

99
use lsp_server::Connection;
1010
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};
1212
use vfs::AbsPathBuf;
1313

1414
#[cfg(all(feature = "mimalloc"))]
@@ -127,7 +127,11 @@ fn run_server() -> Result<()> {
127127
name: String::from("rust-analyzer"),
128128
version: Some(String::from(env!("REV"))),
129129
}),
130-
offset_encoding: None,
130+
offset_encoding: if supports_utf8(&initialize_params.capabilities) {
131+
Some("utf-8".to_string())
132+
} else {
133+
None
134+
},
131135
};
132136

133137
let initialize_result = serde_json::to_value(initialize_result).unwrap();

crates/rust-analyzer/src/cli/analysis_bench.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl BenchCmd {
9797
let offset = host
9898
.analysis()
9999
.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 });
101101
let file_position = FilePosition { file_id, offset };
102102

103103
if is_completion {

crates/rust-analyzer/src/cli/analysis_stats.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,9 @@ impl AnalysisStatsCmd {
218218
bar.println(format!(
219219
"{}:{}-{}:{}: {}",
220220
start.line + 1,
221-
start.col_utf16,
221+
start.col,
222222
end.line + 1,
223-
end.col_utf16,
223+
end.col,
224224
ty.display(db)
225225
));
226226
} else {
@@ -250,9 +250,9 @@ impl AnalysisStatsCmd {
250250
"{} {}:{}-{}:{}: Expected {}, got {}",
251251
path,
252252
start.line + 1,
253-
start.col_utf16,
253+
start.col,
254254
end.line + 1,
255-
end.col_utf16,
255+
end.col,
256256
mismatch.expected.display(db),
257257
mismatch.actual.display(db)
258258
));

crates/rust-analyzer/src/config.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ use rustc_hash::FxHashSet;
2323
use serde::{de::DeserializeOwned, Deserialize};
2424
use vfs::AbsPathBuf;
2525

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+
};
2730

2831
config_data! {
2932
struct ConfigData {
@@ -415,6 +418,13 @@ impl Config {
415418
false
416419
)
417420
}
421+
pub fn offset_encoding(&self) -> OffsetEncoding {
422+
if supports_utf8(&self.caps) {
423+
OffsetEncoding::Utf8
424+
} else {
425+
OffsetEncoding::Utf16
426+
}
427+
}
418428

419429
fn experimental(&self, index: &'static str) -> bool {
420430
try_or!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?, false)

crates/rust-analyzer/src/from_proto.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
//! Conversion lsp_types types to rust-analyzer specific ones.
22
use std::convert::TryFrom;
33

4-
use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineIndex};
4+
use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineColUtf16};
55
use ide_db::base_db::{FileId, FilePosition, FileRange};
66
use syntax::{TextRange, TextSize};
77
use vfs::AbsPathBuf;
88

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+
};
1015

1116
pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> {
1217
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> {
1823
}
1924

2025
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 };
22-
line_index.offset(line_col)
26+
let line_col = match line_index.encoding {
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)
2337
}
2438

2539
pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> TextRange {
@@ -37,8 +51,8 @@ pub(crate) fn file_position(
3751
tdpp: lsp_types::TextDocumentPositionParams,
3852
) -> Result<FilePosition> {
3953
let file_id = file_id(world, &tdpp.text_document.uri)?;
40-
let line_index = world.analysis.file_line_index(file_id)?;
41-
let offset = offset(&*line_index, tdpp.position);
54+
let line_index = world.file_line_index(file_id)?;
55+
let offset = offset(&line_index, tdpp.position);
4256
Ok(FilePosition { file_id, offset })
4357
}
4458

@@ -48,7 +62,7 @@ pub(crate) fn file_range(
4862
range: lsp_types::Range,
4963
) -> Result<FileRange> {
5064
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)?;
5266
let range = text_range(&line_index, range);
5367
Ok(FileRange { file_id, range })
5468
}
@@ -78,7 +92,7 @@ pub(crate) fn annotation(
7892
lsp_ext::CodeLensResolveData::Impls(params) => {
7993
let file_id =
8094
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)?;
8296

8397
Ok(Annotation {
8498
range: text_range(&line_index, code_lens.range),
@@ -90,7 +104,7 @@ pub(crate) fn annotation(
90104
}
91105
lsp_ext::CodeLensResolveData::References(params) => {
92106
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)?;
94108

95109
Ok(Annotation {
96110
range: text_range(&line_index, code_lens.range),

crates/rust-analyzer/src/global_state.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{sync::Arc, time::Instant};
77

88
use crossbeam_channel::{unbounded, Receiver, Sender};
99
use flycheck::FlycheckHandle;
10-
use ide::{Analysis, AnalysisHost, Change, FileId};
10+
use ide::{Analysis, AnalysisHost, Cancelable, Change, FileId};
1111
use ide_db::base_db::{CrateId, VfsPath};
1212
use lsp_types::{SemanticTokens, Url};
1313
use parking_lot::{Mutex, RwLock};
@@ -22,7 +22,7 @@ use crate::{
2222
diagnostics::{CheckFixes, DiagnosticCollection},
2323
document::DocumentData,
2424
from_proto,
25-
line_endings::LineEndings,
25+
line_index::{LineEndings, LineIndex},
2626
main_loop::Task,
2727
op_queue::OpQueue,
2828
reload::SourceRootConfig,
@@ -271,8 +271,11 @@ impl GlobalStateSnapshot {
271271
file_id_to_url(&self.vfs.read().0, id)
272272
}
273273

274-
pub(crate) fn file_line_endings(&self, id: FileId) -> LineEndings {
275-
self.vfs.read().1[&id]
274+
pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancelable<LineIndex> {
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)
276279
}
277280

278281
pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {

0 commit comments

Comments
 (0)