aboutsummaryrefslogtreecommitdiff
path: root/crates/server/src/main_loop/handlers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/server/src/main_loop/handlers.rs')
-rw-r--r--crates/server/src/main_loop/handlers.rs384
1 files changed, 131 insertions, 253 deletions
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs
index 3ee0873f4..45083b084 100644
--- a/crates/server/src/main_loop/handlers.rs
+++ b/crates/server/src/main_loop/handlers.rs
@@ -2,16 +2,13 @@ use std::collections::HashMap;
2 2
3use languageserver_types::{ 3use languageserver_types::{
4 Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, 4 Diagnostic, DiagnosticSeverity, Url, DocumentSymbol,
5 Command, TextDocumentIdentifier, WorkspaceEdit, 5 Command, TextDocumentIdentifier,
6 SymbolInformation, Position, Location, TextEdit, 6 SymbolInformation, Position, Location, TextEdit,
7 CompletionItem, InsertTextFormat, CompletionItemKind, 7 CompletionItem, InsertTextFormat, CompletionItemKind,
8}; 8};
9use serde_json::{to_value, from_value}; 9use serde_json::to_value;
10use url_serde; 10use libanalysis::{Query, FileId, RunnableKind};
11use libanalysis::{self, Query, FileId};
12use libeditor;
13use libsyntax2::{ 11use libsyntax2::{
14 TextUnit,
15 text_utils::contains_offset_nonstrict, 12 text_utils::contains_offset_nonstrict,
16}; 13};
17 14
@@ -26,8 +23,8 @@ pub fn handle_syntax_tree(
26 params: req::SyntaxTreeParams, 23 params: req::SyntaxTreeParams,
27) -> Result<String> { 24) -> Result<String> {
28 let id = params.text_document.try_conv_with(&world)?; 25 let id = params.text_document.try_conv_with(&world)?;
29 let file = world.analysis().file_syntax(id)?; 26 let res = world.analysis().syntax_tree(id);
30 Ok(libeditor::syntax_tree(&file)) 27 Ok(res)
31} 28}
32 29
33pub fn handle_extend_selection( 30pub fn handle_extend_selection(
@@ -35,11 +32,11 @@ pub fn handle_extend_selection(
35 params: req::ExtendSelectionParams, 32 params: req::ExtendSelectionParams,
36) -> Result<req::ExtendSelectionResult> { 33) -> Result<req::ExtendSelectionResult> {
37 let file_id = params.text_document.try_conv_with(&world)?; 34 let file_id = params.text_document.try_conv_with(&world)?;
38 let file = world.analysis().file_syntax(file_id)?; 35 let file = world.analysis().file_syntax(file_id);
39 let line_index = world.analysis().file_line_index(file_id)?; 36 let line_index = world.analysis().file_line_index(file_id);
40 let selections = params.selections.into_iter() 37 let selections = params.selections.into_iter()
41 .map_conv_with(&line_index) 38 .map_conv_with(&line_index)
42 .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r)) 39 .map(|r| world.analysis().extend_selection(&file, r))
43 .map_conv_with(&line_index) 40 .map_conv_with(&line_index)
44 .collect(); 41 .collect();
45 Ok(req::ExtendSelectionResult { selections }) 42 Ok(req::ExtendSelectionResult { selections })
@@ -50,13 +47,13 @@ pub fn handle_find_matching_brace(
50 params: req::FindMatchingBraceParams, 47 params: req::FindMatchingBraceParams,
51) -> Result<Vec<Position>> { 48) -> Result<Vec<Position>> {
52 let file_id = params.text_document.try_conv_with(&world)?; 49 let file_id = params.text_document.try_conv_with(&world)?;
53 let file = world.analysis().file_syntax(file_id)?; 50 let file = world.analysis().file_syntax(file_id);
54 let line_index = world.analysis().file_line_index(file_id)?; 51 let line_index = world.analysis().file_line_index(file_id);
55 let res = params.offsets 52 let res = params.offsets
56 .into_iter() 53 .into_iter()
57 .map_conv_with(&line_index) 54 .map_conv_with(&line_index)
58 .map(|offset| { 55 .map(|offset| {
59 libeditor::matching_brace(&file, offset).unwrap_or(offset) 56 world.analysis().matching_brace(&file, offset).unwrap_or(offset)
60 }) 57 })
61 .map_conv_with(&line_index) 58 .map_conv_with(&line_index)
62 .collect(); 59 .collect();
@@ -66,13 +63,31 @@ pub fn handle_find_matching_brace(
66pub fn handle_join_lines( 63pub fn handle_join_lines(
67 world: ServerWorld, 64 world: ServerWorld,
68 params: req::JoinLinesParams, 65 params: req::JoinLinesParams,
69) -> Result<Vec<TextEdit>> { 66) -> Result<req::SourceChange> {
70 let file_id = params.text_document.try_conv_with(&world)?; 67 let file_id = params.text_document.try_conv_with(&world)?;
71 let file = world.analysis().file_syntax(file_id)?; 68 let line_index = world.analysis().file_line_index(file_id);
72 let line_index = world.analysis().file_line_index(file_id)?;
73 let range = params.range.conv_with(&line_index); 69 let range = params.range.conv_with(&line_index);
74 let res = libeditor::join_lines(&file, range); 70 world.analysis().join_lines(file_id, range)
75 Ok(res.edit.conv_with(&line_index)) 71 .try_conv_with(&world)
72}
73
74pub fn handle_on_type_formatting(
75 world: ServerWorld,
76 params: req::DocumentOnTypeFormattingParams,
77) -> Result<Option<Vec<TextEdit>>> {
78 if params.ch != "=" {
79 return Ok(None);
80 }
81
82 let file_id = params.text_document.try_conv_with(&world)?;
83 let line_index = world.analysis().file_line_index(file_id);
84 let offset = params.position.conv_with(&line_index);
85 let edits = match world.analysis().on_eq_typed(file_id, offset) {
86 None => return Ok(None),
87 Some(mut action) => action.source_file_edits.pop().unwrap().edits,
88 };
89 let edits = edits.into_iter().map_conv_with(&line_index).collect();
90 Ok(Some(edits))
76} 91}
77 92
78pub fn handle_document_symbol( 93pub fn handle_document_symbol(
@@ -80,12 +95,11 @@ pub fn handle_document_symbol(
80 params: req::DocumentSymbolParams, 95 params: req::DocumentSymbolParams,
81) -> Result<Option<req::DocumentSymbolResponse>> { 96) -> Result<Option<req::DocumentSymbolResponse>> {
82 let file_id = params.text_document.try_conv_with(&world)?; 97 let file_id = params.text_document.try_conv_with(&world)?;
83 let file = world.analysis().file_syntax(file_id)?; 98 let line_index = world.analysis().file_line_index(file_id);
84 let line_index = world.analysis().file_line_index(file_id)?;
85 99
86 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); 100 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
87 101
88 for symbol in libeditor::file_structure(&file) { 102 for symbol in world.analysis().file_structure(file_id) {
89 let doc_symbol = DocumentSymbol { 103 let doc_symbol = DocumentSymbol {
90 name: symbol.label, 104 name: symbol.label,
91 detail: Some("".to_string()), 105 detail: Some("".to_string()),
@@ -114,130 +128,6 @@ pub fn handle_document_symbol(
114 Ok(Some(req::DocumentSymbolResponse::Nested(res))) 128 Ok(Some(req::DocumentSymbolResponse::Nested(res)))
115} 129}
116 130
117pub fn handle_code_action(
118 world: ServerWorld,
119 params: req::CodeActionParams,
120) -> Result<Option<Vec<Command>>> {
121 let file_id = params.text_document.try_conv_with(&world)?;
122 let file = world.analysis().file_syntax(file_id)?;
123 let line_index = world.analysis().file_line_index(file_id)?;
124 let offset = params.range.conv_with(&line_index).start();
125 let mut res = Vec::new();
126
127 let actions = &[
128 (ActionId::FlipComma, libeditor::flip_comma(&file, offset).is_some()),
129 (ActionId::AddDerive, libeditor::add_derive(&file, offset).is_some()),
130 (ActionId::AddImpl, libeditor::add_impl(&file, offset).is_some()),
131 ];
132
133 for (id, edit) in actions {
134 if *edit {
135 let cmd = apply_code_action_cmd(*id, params.text_document.clone(), offset);
136 res.push(cmd);
137 }
138 }
139
140 for (diag, quick_fix) in world.analysis().diagnostics(file_id)? {
141 let quick_fix = match quick_fix {
142 Some(quick_fix) => quick_fix,
143 None => continue,
144 };
145 if !contains_offset_nonstrict(diag.range, offset) {
146 continue;
147 }
148 let mut ops = Vec::new();
149 for op in quick_fix.fs_ops {
150 let op = match op {
151 libanalysis::FsOp::CreateFile { anchor, path } => {
152 let uri = world.file_id_to_uri(anchor)?;
153 let path = &path.as_str()[3..]; // strip `../` b/c url is weird
154 let uri = uri.join(path)?;
155 FsOp::CreateFile { uri }
156 },
157 libanalysis::FsOp::MoveFile { file, path } => {
158 let src = world.file_id_to_uri(file)?;
159 let path = &path.as_str()[3..]; // strip `../` b/c url is weird
160 let dst = src.join(path)?;
161 FsOp::MoveFile { src, dst }
162 },
163 };
164 ops.push(op)
165 }
166 let cmd = Command {
167 title: "Create module".to_string(),
168 command: "libsyntax-rust.fsEdit".to_string(),
169 arguments: Some(vec![to_value(ops).unwrap()]),
170 };
171 res.push(cmd)
172 }
173 return Ok(Some(res));
174}
175
176#[derive(Serialize)]
177#[serde(tag = "type", rename_all = "camelCase")]
178enum FsOp {
179 CreateFile {
180 #[serde(with = "url_serde")]
181 uri: Url
182 },
183 MoveFile {
184 #[serde(with = "url_serde")]
185 src: Url,
186 #[serde(with = "url_serde")]
187 dst: Url,
188 }
189}
190
191pub fn handle_runnables(
192 world: ServerWorld,
193 params: req::RunnablesParams,
194) -> Result<Vec<req::Runnable>> {
195 let file_id = params.text_document.try_conv_with(&world)?;
196 let file = world.analysis().file_syntax(file_id)?;
197 let line_index = world.analysis().file_line_index(file_id)?;
198 let offset = params.position.map(|it| it.conv_with(&line_index));
199 let mut res = Vec::new();
200 for runnable in libeditor::runnables(&file) {
201 if let Some(offset) = offset {
202 if !contains_offset_nonstrict(runnable.range, offset) {
203 continue;
204 }
205 }
206
207 let r = req::Runnable {
208 range: runnable.range.conv_with(&line_index),
209 label: match &runnable.kind {
210 libeditor::RunnableKind::Test { name } =>
211 format!("test {}", name),
212 libeditor::RunnableKind::Bin =>
213 "run binary".to_string(),
214 },
215 bin: "cargo".to_string(),
216 args: match runnable.kind {
217 libeditor::RunnableKind::Test { name } => {
218 vec![
219 "test".to_string(),
220 "--".to_string(),
221 name,
222 "--nocapture".to_string(),
223 ]
224 }
225 libeditor::RunnableKind::Bin => vec!["run".to_string()]
226 },
227 env: {
228 let mut m = HashMap::new();
229 m.insert(
230 "RUST_BACKTRACE".to_string(),
231 "short".to_string(),
232 );
233 m
234 }
235 };
236 res.push(r);
237 }
238 return Ok(res);
239}
240
241pub fn handle_workspace_symbol( 131pub fn handle_workspace_symbol(
242 world: ServerWorld, 132 world: ServerWorld,
243 params: req::WorkspaceSymbolParams, 133 params: req::WorkspaceSymbolParams,
@@ -265,8 +155,8 @@ pub fn handle_workspace_symbol(
265 155
266 fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> { 156 fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> {
267 let mut res = Vec::new(); 157 let mut res = Vec::new();
268 for (file_id, symbol) in world.analysis().world_symbols(query) { 158 for (file_id, symbol) in world.analysis().symbol_search(query) {
269 let line_index = world.analysis().file_line_index(file_id)?; 159 let line_index = world.analysis().file_line_index(file_id);
270 let info = SymbolInformation { 160 let info = SymbolInformation {
271 name: symbol.name.to_string(), 161 name: symbol.name.to_string(),
272 kind: symbol.kind.conv(), 162 kind: symbol.kind.conv(),
@@ -287,11 +177,11 @@ pub fn handle_goto_definition(
287 params: req::TextDocumentPositionParams, 177 params: req::TextDocumentPositionParams,
288) -> Result<Option<req::GotoDefinitionResponse>> { 178) -> Result<Option<req::GotoDefinitionResponse>> {
289 let file_id = params.text_document.try_conv_with(&world)?; 179 let file_id = params.text_document.try_conv_with(&world)?;
290 let line_index = world.analysis().file_line_index(file_id)?; 180 let line_index = world.analysis().file_line_index(file_id);
291 let offset = params.position.conv_with(&line_index); 181 let offset = params.position.conv_with(&line_index);
292 let mut res = Vec::new(); 182 let mut res = Vec::new();
293 for (file_id, symbol) in world.analysis().approximately_resolve_symbol(file_id, offset)? { 183 for (file_id, symbol) in world.analysis().approximately_resolve_symbol(file_id, offset) {
294 let line_index = world.analysis().file_line_index(file_id)?; 184 let line_index = world.analysis().file_line_index(file_id);
295 let location = to_location( 185 let location = to_location(
296 file_id, symbol.node_range, 186 file_id, symbol.node_range,
297 &world, &line_index, 187 &world, &line_index,
@@ -308,7 +198,7 @@ pub fn handle_parent_module(
308 let file_id = params.try_conv_with(&world)?; 198 let file_id = params.try_conv_with(&world)?;
309 let mut res = Vec::new(); 199 let mut res = Vec::new();
310 for (file_id, symbol) in world.analysis().parent_module(file_id) { 200 for (file_id, symbol) in world.analysis().parent_module(file_id) {
311 let line_index = world.analysis().file_line_index(file_id)?; 201 let line_index = world.analysis().file_line_index(file_id);
312 let location = to_location( 202 let location = to_location(
313 file_id, symbol.node_range, 203 file_id, symbol.node_range,
314 &world, &line_index 204 &world, &line_index
@@ -318,15 +208,71 @@ pub fn handle_parent_module(
318 Ok(res) 208 Ok(res)
319} 209}
320 210
211pub fn handle_runnables(
212 world: ServerWorld,
213 params: req::RunnablesParams,
214) -> Result<Vec<req::Runnable>> {
215 let file_id = params.text_document.try_conv_with(&world)?;
216 let line_index = world.analysis().file_line_index(file_id);
217 let offset = params.position.map(|it| it.conv_with(&line_index));
218 let mut res = Vec::new();
219 for runnable in world.analysis().runnables(file_id) {
220 if let Some(offset) = offset {
221 if !contains_offset_nonstrict(runnable.range, offset) {
222 continue;
223 }
224 }
225
226 let r = req::Runnable {
227 range: runnable.range.conv_with(&line_index),
228 label: match &runnable.kind {
229 RunnableKind::Test { name } =>
230 format!("test {}", name),
231 RunnableKind::Bin =>
232 "run binary".to_string(),
233 },
234 bin: "cargo".to_string(),
235 args: match runnable.kind {
236 RunnableKind::Test { name } => {
237 vec![
238 "test".to_string(),
239 "--".to_string(),
240 name,
241 "--nocapture".to_string(),
242 ]
243 }
244 RunnableKind::Bin => vec!["run".to_string()]
245 },
246 env: {
247 let mut m = HashMap::new();
248 m.insert(
249 "RUST_BACKTRACE".to_string(),
250 "short".to_string(),
251 );
252 m
253 }
254 };
255 res.push(r);
256 }
257 return Ok(res);
258}
259
260pub fn handle_decorations(
261 world: ServerWorld,
262 params: TextDocumentIdentifier,
263) -> Result<Vec<Decoration>> {
264 let file_id = params.try_conv_with(&world)?;
265 Ok(highlight(&world, file_id))
266}
267
321pub fn handle_completion( 268pub fn handle_completion(
322 world: ServerWorld, 269 world: ServerWorld,
323 params: req::CompletionParams, 270 params: req::CompletionParams,
324) -> Result<Option<req::CompletionResponse>> { 271) -> Result<Option<req::CompletionResponse>> {
325 let file_id = params.text_document.try_conv_with(&world)?; 272 let file_id = params.text_document.try_conv_with(&world)?;
326 let file = world.analysis().file_syntax(file_id)?; 273 let line_index = world.analysis().file_line_index(file_id);
327 let line_index = world.analysis().file_line_index(file_id)?;
328 let offset = params.position.conv_with(&line_index); 274 let offset = params.position.conv_with(&line_index);
329 let items = match libeditor::scope_completion(&file, offset) { 275 let items = match world.analysis().completions(file_id, offset) {
330 None => return Ok(None), 276 None => return Ok(None),
331 Some(items) => items, 277 Some(items) => items,
332 }; 278 };
@@ -348,91 +294,33 @@ pub fn handle_completion(
348 Ok(Some(req::CompletionResponse::Array(items))) 294 Ok(Some(req::CompletionResponse::Array(items)))
349} 295}
350 296
351pub fn handle_on_type_formatting( 297pub fn handle_code_action(
352 world: ServerWorld, 298 world: ServerWorld,
353 params: req::DocumentOnTypeFormattingParams, 299 params: req::CodeActionParams,
354) -> Result<Option<Vec<TextEdit>>> { 300) -> Result<Option<Vec<Command>>> {
355 if params.ch != "=" {
356 return Ok(None);
357 }
358
359 let file_id = params.text_document.try_conv_with(&world)?; 301 let file_id = params.text_document.try_conv_with(&world)?;
360 let line_index = world.analysis().file_line_index(file_id)?; 302 let line_index = world.analysis().file_line_index(file_id);
361 let offset = params.position.conv_with(&line_index); 303 let offset = params.range.conv_with(&line_index).start();
362 let file = world.analysis().file_syntax(file_id)?;
363 let action = match libeditor::on_eq_typed(&file, offset) {
364 None => return Ok(None),
365 Some(action) => action,
366 };
367 Ok(Some(action.edit.conv_with(&line_index)))
368}
369
370pub fn handle_execute_command(
371 world: ServerWorld,
372 mut params: req::ExecuteCommandParams,
373) -> Result<(req::ApplyWorkspaceEditParams, Option<Position>)> {
374 if params.command.as_str() != "apply_code_action" {
375 bail!("unknown cmd: {:?}", params.command);
376 }
377 if params.arguments.len() != 1 {
378 bail!("expected single arg, got {}", params.arguments.len());
379 }
380 let arg = params.arguments.pop().unwrap();
381 let arg: ActionRequest = from_value(arg)?;
382 let file_id = arg.text_document.try_conv_with(&world)?;
383 let file = world.analysis().file_syntax(file_id)?;
384 let action_result = match arg.id {
385 ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|f| f()),
386 ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|f| f()),
387 ActionId::AddImpl => libeditor::add_impl(&file, arg.offset).map(|f| f()),
388 }.ok_or_else(|| format_err!("command not applicable"))?;
389 let line_index = world.analysis().file_line_index(file_id)?;
390 let mut changes = HashMap::new();
391 changes.insert(
392 arg.text_document.uri,
393 action_result.edit.conv_with(&line_index),
394 );
395 let edit = WorkspaceEdit {
396 changes: Some(changes),
397 document_changes: None,
398 };
399 let edit = req::ApplyWorkspaceEditParams { edit };
400 let cursor_pos = action_result.cursor_position
401 .map(|off| off.conv_with(&line_index));
402 Ok((edit, cursor_pos))
403}
404 304
405#[derive(Serialize, Deserialize)] 305 let assists = world.analysis().assists(file_id, offset).into_iter();
406struct ActionRequest { 306 let fixes = world.analysis().diagnostics(file_id).into_iter()
407 id: ActionId, 307 .filter_map(|d| Some((d.range, d.fix?)))
408 text_document: TextDocumentIdentifier, 308 .filter(|(range, _fix)| contains_offset_nonstrict(*range, offset))
409 offset: TextUnit, 309 .map(|(_range, fix)| fix);
410}
411 310
412fn apply_code_action_cmd(id: ActionId, doc: TextDocumentIdentifier, offset: TextUnit) -> Command { 311 let mut res = Vec::new();
413 let action_request = ActionRequest { id, text_document: doc, offset }; 312 for source_edit in assists.chain(fixes) {
414 Command { 313 let title = source_edit.label.clone();
415 title: id.title().to_string(), 314 let edit = source_edit.try_conv_with(&world)?;
416 command: "apply_code_action".to_string(), 315 let cmd = Command {
417 arguments: Some(vec![to_value(action_request).unwrap()]), 316 title,
317 command: "libsyntax-rust.applySourceChange".to_string(),
318 arguments: Some(vec![to_value(edit).unwrap()]),
319 };
320 res.push(cmd);
418 } 321 }
419}
420 322
421#[derive(Serialize, Deserialize, Clone, Copy)] 323 Ok(Some(res))
422enum ActionId {
423 FlipComma,
424 AddDerive,
425 AddImpl,
426}
427
428impl ActionId {
429 fn title(&self) -> &'static str {
430 match *self {
431 ActionId::FlipComma => "Flip `,`",
432 ActionId::AddDerive => "Add `#[derive]`",
433 ActionId::AddImpl => "Add impl",
434 }
435 }
436} 324}
437 325
438pub fn publish_diagnostics( 326pub fn publish_diagnostics(
@@ -440,28 +328,20 @@ pub fn publish_diagnostics(
440 uri: Url 328 uri: Url
441) -> Result<req::PublishDiagnosticsParams> { 329) -> Result<req::PublishDiagnosticsParams> {
442 let file_id = world.uri_to_file_id(&uri)?; 330 let file_id = world.uri_to_file_id(&uri)?;
443 let line_index = world.analysis().file_line_index(file_id)?; 331 let line_index = world.analysis().file_line_index(file_id);
444 let diagnostics = world.analysis().diagnostics(file_id)? 332 let diagnostics = world.analysis().diagnostics(file_id)
445 .into_iter() 333 .into_iter()
446 .map(|(d, _quick_fix)| Diagnostic { 334 .map(|d| Diagnostic {
447 range: d.range.conv_with(&line_index), 335 range: d.range.conv_with(&line_index),
448 severity: Some(DiagnosticSeverity::Error), 336 severity: Some(DiagnosticSeverity::Error),
449 code: None, 337 code: None,
450 source: Some("libsyntax2".to_string()), 338 source: Some("libsyntax2".to_string()),
451 message: d.msg, 339 message: d.message,
452 related_information: None, 340 related_information: None,
453 }).collect(); 341 }).collect();
454 Ok(req::PublishDiagnosticsParams { uri, diagnostics }) 342 Ok(req::PublishDiagnosticsParams { uri, diagnostics })
455} 343}
456 344
457pub fn handle_decorations(
458 world: ServerWorld,
459 params: TextDocumentIdentifier,
460) -> Result<Vec<Decoration>> {
461 let file_id = params.try_conv_with(&world)?;
462 highlight(&world, file_id)
463}
464
465pub fn publish_decorations( 345pub fn publish_decorations(
466 world: ServerWorld, 346 world: ServerWorld,
467 uri: Url 347 uri: Url
@@ -469,18 +349,16 @@ pub fn publish_decorations(
469 let file_id = world.uri_to_file_id(&uri)?; 349 let file_id = world.uri_to_file_id(&uri)?;
470 Ok(req::PublishDecorationsParams { 350 Ok(req::PublishDecorationsParams {
471 uri, 351 uri,
472 decorations: highlight(&world, file_id)? 352 decorations: highlight(&world, file_id),
473 }) 353 })
474} 354}
475 355
476fn highlight(world: &ServerWorld, file_id: FileId) -> Result<Vec<Decoration>> { 356fn highlight(world: &ServerWorld, file_id: FileId) -> Vec<Decoration> {
477 let file = world.analysis().file_syntax(file_id)?; 357 let line_index = world.analysis().file_line_index(file_id);
478 let line_index = world.analysis().file_line_index(file_id)?; 358 world.analysis().highlight(file_id)
479 let res = libeditor::highlight(&file)
480 .into_iter() 359 .into_iter()
481 .map(|h| Decoration { 360 .map(|h| Decoration {
482 range: h.range.conv_with(&line_index), 361 range: h.range.conv_with(&line_index),
483 tag: h.tag, 362 tag: h.tag,
484 }).collect(); 363 }).collect()
485 Ok(res)
486} 364}