rust_analyzer/lsp/
from_proto.rs1use anyhow::format_err;
3use ide::{Annotation, AnnotationKind, AssistKind, LineCol};
4use ide_db::{FileId, FilePosition, FileRange, line_index::WideLineCol};
5use paths::Utf8PathBuf;
6use syntax::{TextRange, TextSize};
7use vfs::AbsPathBuf;
8
9use crate::{
10 global_state::GlobalStateSnapshot,
11 line_index::{LineIndex, PositionEncoding},
12 lsp_ext, try_default,
13};
14
15pub(crate) fn abs_path(url: &lsp_types::Url) -> anyhow::Result<AbsPathBuf> {
16 let path = url.to_file_path().map_err(|()| anyhow::format_err!("url is not a file"))?;
17 Ok(AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).unwrap()).unwrap())
18}
19
20pub(crate) fn vfs_path(url: &lsp_types::Url) -> anyhow::Result<vfs::VfsPath> {
21 abs_path(url).map(vfs::VfsPath::from)
22}
23
24pub(crate) fn offset(
25 line_index: &LineIndex,
26 position: lsp_types::Position,
27) -> anyhow::Result<TextSize> {
28 let line_col = match line_index.encoding {
29 PositionEncoding::Utf8 => LineCol { line: position.line, col: position.character },
30 PositionEncoding::Wide(enc) => {
31 let line_col = WideLineCol { line: position.line, col: position.character };
32 line_index
33 .index
34 .to_utf8(enc, line_col)
35 .ok_or_else(|| format_err!("Invalid wide col offset"))?
36 }
37 };
38 let line_range = line_index.index.line(line_col.line).ok_or_else(|| {
39 format_err!("Invalid offset {line_col:?} (line index length: {:?})", line_index.index.len())
40 })?;
41 let col = TextSize::from(line_col.col);
42 let clamped_len = col.min(line_range.len());
43 Ok(line_range.start() + clamped_len)
51}
52
53pub(crate) fn text_range(
54 line_index: &LineIndex,
55 range: lsp_types::Range,
56) -> anyhow::Result<TextRange> {
57 let start = offset(line_index, range.start)?;
58 let end = offset(line_index, range.end)?;
59 match end < start {
60 true => Err(format_err!("Invalid Range")),
61 false => Ok(TextRange::new(start, end)),
62 }
63}
64
65pub(crate) fn file_id(
67 snap: &GlobalStateSnapshot,
68 url: &lsp_types::Url,
69) -> anyhow::Result<Option<FileId>> {
70 snap.url_to_file_id(url)
71}
72
73pub(crate) fn file_position(
75 snap: &GlobalStateSnapshot,
76 tdpp: lsp_types::TextDocumentPositionParams,
77) -> anyhow::Result<Option<FilePosition>> {
78 let file_id = try_default!(file_id(snap, &tdpp.text_document.uri)?);
79 let line_index = snap.file_line_index(file_id)?;
80 let offset = offset(&line_index, tdpp.position)?;
81 Ok(Some(FilePosition { file_id, offset }))
82}
83
84pub(crate) fn file_range(
86 snap: &GlobalStateSnapshot,
87 text_document_identifier: &lsp_types::TextDocumentIdentifier,
88 range: lsp_types::Range,
89) -> anyhow::Result<Option<FileRange>> {
90 file_range_uri(snap, &text_document_identifier.uri, range)
91}
92
93pub(crate) fn file_range_uri(
95 snap: &GlobalStateSnapshot,
96 document: &lsp_types::Url,
97 range: lsp_types::Range,
98) -> anyhow::Result<Option<FileRange>> {
99 let file_id = try_default!(file_id(snap, document)?);
100 let line_index = snap.file_line_index(file_id)?;
101 let range = text_range(&line_index, range)?;
102 Ok(Some(FileRange { file_id, range }))
103}
104
105pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind> {
106 let assist_kind = match &kind {
107 k if k == &lsp_types::CodeActionKind::EMPTY => AssistKind::Generate,
108 k if k == &lsp_types::CodeActionKind::QUICKFIX => AssistKind::QuickFix,
109 k if k == &lsp_types::CodeActionKind::REFACTOR => AssistKind::Refactor,
110 k if k == &lsp_types::CodeActionKind::REFACTOR_EXTRACT => AssistKind::RefactorExtract,
111 k if k == &lsp_types::CodeActionKind::REFACTOR_INLINE => AssistKind::RefactorInline,
112 k if k == &lsp_types::CodeActionKind::REFACTOR_REWRITE => AssistKind::RefactorRewrite,
113 _ => return None,
114 };
115
116 Some(assist_kind)
117}
118
119pub(crate) fn annotation(
121 snap: &GlobalStateSnapshot,
122 range: lsp_types::Range,
123 data: lsp_ext::CodeLensResolveData,
124) -> anyhow::Result<Option<Annotation>> {
125 match data.kind {
126 lsp_ext::CodeLensResolveDataKind::Impls(params) => {
127 if snap.url_file_version(¶ms.text_document_position_params.text_document.uri)
128 != Some(data.version)
129 {
130 return Ok(None);
131 }
132 let pos @ FilePosition { file_id, .. } =
133 try_default!(file_position(snap, params.text_document_position_params)?);
134 let line_index = snap.file_line_index(file_id)?;
135
136 Ok(Annotation {
137 range: text_range(&line_index, range)?,
138 kind: AnnotationKind::HasImpls { pos, data: None },
139 })
140 }
141 lsp_ext::CodeLensResolveDataKind::References(params) => {
142 if snap.url_file_version(¶ms.text_document.uri) != Some(data.version) {
143 return Ok(None);
144 }
145 let pos @ FilePosition { file_id, .. } = try_default!(file_position(snap, params)?);
146 let line_index = snap.file_line_index(file_id)?;
147
148 Ok(Annotation {
149 range: text_range(&line_index, range)?,
150 kind: AnnotationKind::HasReferences { pos, data: None },
151 })
152 }
153 }
154 .map(Some)
155}