aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server/src/main_loop/handlers.rs
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-10-20 21:04:06 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-10-20 21:04:06 +0100
commitfd336d1134d405d833b762101a25c00076bc7fd2 (patch)
tree040ab6dc1286ab9fe5da0002d29ae4eb7a37850a /crates/ra_lsp_server/src/main_loop/handlers.rs
parent73dd870da2dcc991b0fdcdde8bee91f05cb9e182 (diff)
parent0102a01f76c855da447e25eb81191047a3ca79b8 (diff)
Merge #147
147: Cancelation r=matklad a=matklad This series of commits switch cancellation strategy from `JobToken` (which are cancellation tokens, explicitly controlled by the called) to salsa built-in auto cancellation. "Auto" means that, as soon as we advance the revision, all pending queries are cancelled automatically, and this looks like a semantic we actually want. "client-side" cancellation is a rare event, and it's ok to just punt on it. Automatic cancellation after the user types something in happens all the time. Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_lsp_server/src/main_loop/handlers.rs')
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs78
1 files changed, 29 insertions, 49 deletions
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 639fe4553..f5dff4c80 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -1,12 +1,13 @@
1use rustc_hash::FxHashMap; 1use std::collections::HashMap;
2 2
3use rustc_hash::FxHashMap;
3use languageserver_types::{ 4use languageserver_types::{
4 CodeActionResponse, Command, CompletionItem, CompletionItemKind, Diagnostic, 5 CodeActionResponse, Command, CompletionItem, CompletionItemKind, Diagnostic,
5 DiagnosticSeverity, DocumentSymbol, FoldingRange, FoldingRangeKind, FoldingRangeParams, 6 DiagnosticSeverity, DocumentSymbol, FoldingRange, FoldingRangeKind, FoldingRangeParams,
6 InsertTextFormat, Location, Position, SymbolInformation, TextDocumentIdentifier, TextEdit, 7 InsertTextFormat, Location, Position, SymbolInformation, TextDocumentIdentifier, TextEdit,
7 RenameParams, WorkspaceEdit, PrepareRenameResponse 8 RenameParams, WorkspaceEdit, PrepareRenameResponse
8}; 9};
9use ra_analysis::{FileId, FoldKind, JobToken, Query, RunnableKind}; 10use ra_analysis::{FileId, FoldKind, Query, RunnableKind};
10use ra_syntax::text_utils::contains_offset_nonstrict; 11use ra_syntax::text_utils::contains_offset_nonstrict;
11use serde_json::to_value; 12use serde_json::to_value;
12 13
@@ -18,12 +19,9 @@ use crate::{
18 Result, 19 Result,
19}; 20};
20 21
21use std::collections::HashMap;
22
23pub fn handle_syntax_tree( 22pub fn handle_syntax_tree(
24 world: ServerWorld, 23 world: ServerWorld,
25 params: req::SyntaxTreeParams, 24 params: req::SyntaxTreeParams,
26 _token: JobToken,
27) -> Result<String> { 25) -> Result<String> {
28 let id = params.text_document.try_conv_with(&world)?; 26 let id = params.text_document.try_conv_with(&world)?;
29 let res = world.analysis().syntax_tree(id); 27 let res = world.analysis().syntax_tree(id);
@@ -33,7 +31,6 @@ pub fn handle_syntax_tree(
33pub fn handle_extend_selection( 31pub fn handle_extend_selection(
34 world: ServerWorld, 32 world: ServerWorld,
35 params: req::ExtendSelectionParams, 33 params: req::ExtendSelectionParams,
36 _token: JobToken,
37) -> Result<req::ExtendSelectionResult> { 34) -> Result<req::ExtendSelectionResult> {
38 let file_id = params.text_document.try_conv_with(&world)?; 35 let file_id = params.text_document.try_conv_with(&world)?;
39 let file = world.analysis().file_syntax(file_id); 36 let file = world.analysis().file_syntax(file_id);
@@ -51,7 +48,6 @@ pub fn handle_extend_selection(
51pub fn handle_find_matching_brace( 48pub fn handle_find_matching_brace(
52 world: ServerWorld, 49 world: ServerWorld,
53 params: req::FindMatchingBraceParams, 50 params: req::FindMatchingBraceParams,
54 _token: JobToken,
55) -> Result<Vec<Position>> { 51) -> Result<Vec<Position>> {
56 let file_id = params.text_document.try_conv_with(&world)?; 52 let file_id = params.text_document.try_conv_with(&world)?;
57 let file = world.analysis().file_syntax(file_id); 53 let file = world.analysis().file_syntax(file_id);
@@ -74,7 +70,6 @@ pub fn handle_find_matching_brace(
74pub fn handle_join_lines( 70pub fn handle_join_lines(
75 world: ServerWorld, 71 world: ServerWorld,
76 params: req::JoinLinesParams, 72 params: req::JoinLinesParams,
77 _token: JobToken,
78) -> Result<req::SourceChange> { 73) -> Result<req::SourceChange> {
79 let file_id = params.text_document.try_conv_with(&world)?; 74 let file_id = params.text_document.try_conv_with(&world)?;
80 let line_index = world.analysis().file_line_index(file_id); 75 let line_index = world.analysis().file_line_index(file_id);
@@ -88,7 +83,6 @@ pub fn handle_join_lines(
88pub fn handle_on_enter( 83pub fn handle_on_enter(
89 world: ServerWorld, 84 world: ServerWorld,
90 params: req::TextDocumentPositionParams, 85 params: req::TextDocumentPositionParams,
91 _token: JobToken,
92) -> Result<Option<req::SourceChange>> { 86) -> Result<Option<req::SourceChange>> {
93 let file_id = params.text_document.try_conv_with(&world)?; 87 let file_id = params.text_document.try_conv_with(&world)?;
94 let line_index = world.analysis().file_line_index(file_id); 88 let line_index = world.analysis().file_line_index(file_id);
@@ -102,7 +96,6 @@ pub fn handle_on_enter(
102pub fn handle_on_type_formatting( 96pub fn handle_on_type_formatting(
103 world: ServerWorld, 97 world: ServerWorld,
104 params: req::DocumentOnTypeFormattingParams, 98 params: req::DocumentOnTypeFormattingParams,
105 _token: JobToken,
106) -> Result<Option<Vec<TextEdit>>> { 99) -> Result<Option<Vec<TextEdit>>> {
107 if params.ch != "=" { 100 if params.ch != "=" {
108 return Ok(None); 101 return Ok(None);
@@ -122,7 +115,6 @@ pub fn handle_on_type_formatting(
122pub fn handle_document_symbol( 115pub fn handle_document_symbol(
123 world: ServerWorld, 116 world: ServerWorld,
124 params: req::DocumentSymbolParams, 117 params: req::DocumentSymbolParams,
125 _token: JobToken,
126) -> Result<Option<req::DocumentSymbolResponse>> { 118) -> Result<Option<req::DocumentSymbolResponse>> {
127 let file_id = params.text_document.try_conv_with(&world)?; 119 let file_id = params.text_document.try_conv_with(&world)?;
128 let line_index = world.analysis().file_line_index(file_id); 120 let line_index = world.analysis().file_line_index(file_id);
@@ -161,7 +153,6 @@ pub fn handle_document_symbol(
161pub fn handle_workspace_symbol( 153pub fn handle_workspace_symbol(
162 world: ServerWorld, 154 world: ServerWorld,
163 params: req::WorkspaceSymbolParams, 155 params: req::WorkspaceSymbolParams,
164 token: JobToken,
165) -> Result<Option<Vec<SymbolInformation>>> { 156) -> Result<Option<Vec<SymbolInformation>>> {
166 let all_symbols = params.query.contains("#"); 157 let all_symbols = params.query.contains("#");
167 let libs = params.query.contains("*"); 158 let libs = params.query.contains("*");
@@ -181,11 +172,11 @@ pub fn handle_workspace_symbol(
181 q.limit(128); 172 q.limit(128);
182 q 173 q
183 }; 174 };
184 let mut res = exec_query(&world, query, &token)?; 175 let mut res = exec_query(&world, query)?;
185 if res.is_empty() && !all_symbols { 176 if res.is_empty() && !all_symbols {
186 let mut query = Query::new(params.query); 177 let mut query = Query::new(params.query);
187 query.limit(128); 178 query.limit(128);
188 res = exec_query(&world, query, &token)?; 179 res = exec_query(&world, query)?;
189 } 180 }
190 181
191 return Ok(Some(res)); 182 return Ok(Some(res));
@@ -193,10 +184,9 @@ pub fn handle_workspace_symbol(
193 fn exec_query( 184 fn exec_query(
194 world: &ServerWorld, 185 world: &ServerWorld,
195 query: Query, 186 query: Query,
196 token: &JobToken,
197 ) -> Result<Vec<SymbolInformation>> { 187 ) -> Result<Vec<SymbolInformation>> {
198 let mut res = Vec::new(); 188 let mut res = Vec::new();
199 for (file_id, symbol) in world.analysis().symbol_search(query, token) { 189 for (file_id, symbol) in world.analysis().symbol_search(query)? {
200 let line_index = world.analysis().file_line_index(file_id); 190 let line_index = world.analysis().file_line_index(file_id);
201 let info = SymbolInformation { 191 let info = SymbolInformation {
202 name: symbol.name.to_string(), 192 name: symbol.name.to_string(),
@@ -214,7 +204,6 @@ pub fn handle_workspace_symbol(
214pub fn handle_goto_definition( 204pub fn handle_goto_definition(
215 world: ServerWorld, 205 world: ServerWorld,
216 params: req::TextDocumentPositionParams, 206 params: req::TextDocumentPositionParams,
217 token: JobToken,
218) -> Result<Option<req::GotoDefinitionResponse>> { 207) -> Result<Option<req::GotoDefinitionResponse>> {
219 let file_id = params.text_document.try_conv_with(&world)?; 208 let file_id = params.text_document.try_conv_with(&world)?;
220 let line_index = world.analysis().file_line_index(file_id); 209 let line_index = world.analysis().file_line_index(file_id);
@@ -222,7 +211,7 @@ pub fn handle_goto_definition(
222 let mut res = Vec::new(); 211 let mut res = Vec::new();
223 for (file_id, symbol) in world 212 for (file_id, symbol) in world
224 .analysis() 213 .analysis()
225 .approximately_resolve_symbol(file_id, offset, &token) 214 .approximately_resolve_symbol(file_id, offset)?
226 { 215 {
227 let line_index = world.analysis().file_line_index(file_id); 216 let line_index = world.analysis().file_line_index(file_id);
228 let location = to_location(file_id, symbol.node_range, &world, &line_index)?; 217 let location = to_location(file_id, symbol.node_range, &world, &line_index)?;
@@ -234,11 +223,10 @@ pub fn handle_goto_definition(
234pub fn handle_parent_module( 223pub fn handle_parent_module(
235 world: ServerWorld, 224 world: ServerWorld,
236 params: TextDocumentIdentifier, 225 params: TextDocumentIdentifier,
237 _token: JobToken,
238) -> Result<Vec<Location>> { 226) -> Result<Vec<Location>> {
239 let file_id = params.try_conv_with(&world)?; 227 let file_id = params.try_conv_with(&world)?;
240 let mut res = Vec::new(); 228 let mut res = Vec::new();
241 for (file_id, symbol) in world.analysis().parent_module(file_id) { 229 for (file_id, symbol) in world.analysis().parent_module(file_id)? {
242 let line_index = world.analysis().file_line_index(file_id); 230 let line_index = world.analysis().file_line_index(file_id);
243 let location = to_location(file_id, symbol.node_range, &world, &line_index)?; 231 let location = to_location(file_id, symbol.node_range, &world, &line_index)?;
244 res.push(location); 232 res.push(location);
@@ -249,20 +237,19 @@ pub fn handle_parent_module(
249pub fn handle_runnables( 237pub fn handle_runnables(
250 world: ServerWorld, 238 world: ServerWorld,
251 params: req::RunnablesParams, 239 params: req::RunnablesParams,
252 _token: JobToken,
253) -> Result<Vec<req::Runnable>> { 240) -> Result<Vec<req::Runnable>> {
254 let file_id = params.text_document.try_conv_with(&world)?; 241 let file_id = params.text_document.try_conv_with(&world)?;
255 let line_index = world.analysis().file_line_index(file_id); 242 let line_index = world.analysis().file_line_index(file_id);
256 let offset = params.position.map(|it| it.conv_with(&line_index)); 243 let offset = params.position.map(|it| it.conv_with(&line_index));
257 let mut res = Vec::new(); 244 let mut res = Vec::new();
258 for runnable in world.analysis().runnables(file_id) { 245 for runnable in world.analysis().runnables(file_id)? {
259 if let Some(offset) = offset { 246 if let Some(offset) = offset {
260 if !contains_offset_nonstrict(runnable.range, offset) { 247 if !contains_offset_nonstrict(runnable.range, offset) {
261 continue; 248 continue;
262 } 249 }
263 } 250 }
264 251
265 let args = runnable_args(&world, file_id, &runnable.kind); 252 let args = runnable_args(&world, file_id, &runnable.kind)?;
266 253
267 let r = req::Runnable { 254 let r = req::Runnable {
268 range: runnable.range.conv_with(&line_index), 255 range: runnable.range.conv_with(&line_index),
@@ -282,9 +269,9 @@ pub fn handle_runnables(
282 } 269 }
283 return Ok(res); 270 return Ok(res);
284 271
285 fn runnable_args(world: &ServerWorld, file_id: FileId, kind: &RunnableKind) -> Vec<String> { 272 fn runnable_args(world: &ServerWorld, file_id: FileId, kind: &RunnableKind) -> Result<Vec<String>> {
286 let spec = if let Some(&crate_id) = world.analysis().crate_for(file_id).first() { 273 let spec = if let Some(&crate_id) = world.analysis().crate_for(file_id)?.first() {
287 let file_id = world.analysis().crate_root(crate_id); 274 let file_id = world.analysis().crate_root(crate_id)?;
288 let path = world.path_map.get_path(file_id); 275 let path = world.path_map.get_path(file_id);
289 world 276 world
290 .workspaces 277 .workspaces
@@ -319,7 +306,7 @@ pub fn handle_runnables(
319 } 306 }
320 } 307 }
321 } 308 }
322 res 309 Ok(res)
323 } 310 }
324 311
325 fn spec_args(pkg_name: &str, tgt_name: &str, tgt_kind: TargetKind, buf: &mut Vec<String>) { 312 fn spec_args(pkg_name: &str, tgt_name: &str, tgt_kind: TargetKind, buf: &mut Vec<String>) {
@@ -353,21 +340,19 @@ pub fn handle_runnables(
353pub fn handle_decorations( 340pub fn handle_decorations(
354 world: ServerWorld, 341 world: ServerWorld,
355 params: TextDocumentIdentifier, 342 params: TextDocumentIdentifier,
356 _token: JobToken,
357) -> Result<Vec<Decoration>> { 343) -> Result<Vec<Decoration>> {
358 let file_id = params.try_conv_with(&world)?; 344 let file_id = params.try_conv_with(&world)?;
359 Ok(highlight(&world, file_id)) 345 highlight(&world, file_id)
360} 346}
361 347
362pub fn handle_completion( 348pub fn handle_completion(
363 world: ServerWorld, 349 world: ServerWorld,
364 params: req::CompletionParams, 350 params: req::CompletionParams,
365 _token: JobToken,
366) -> Result<Option<req::CompletionResponse>> { 351) -> Result<Option<req::CompletionResponse>> {
367 let file_id = params.text_document.try_conv_with(&world)?; 352 let file_id = params.text_document.try_conv_with(&world)?;
368 let line_index = world.analysis().file_line_index(file_id); 353 let line_index = world.analysis().file_line_index(file_id);
369 let offset = params.position.conv_with(&line_index); 354 let offset = params.position.conv_with(&line_index);
370 let items = match world.analysis().completions(file_id, offset) { 355 let items = match world.analysis().completions(file_id, offset)? {
371 None => return Ok(None), 356 None => return Ok(None),
372 Some(items) => items, 357 Some(items) => items,
373 }; 358 };
@@ -394,7 +379,6 @@ pub fn handle_completion(
394pub fn handle_folding_range( 379pub fn handle_folding_range(
395 world: ServerWorld, 380 world: ServerWorld,
396 params: FoldingRangeParams, 381 params: FoldingRangeParams,
397 _token: JobToken,
398) -> Result<Option<Vec<FoldingRange>>> { 382) -> Result<Option<Vec<FoldingRange>>> {
399 let file_id = params.text_document.try_conv_with(&world)?; 383 let file_id = params.text_document.try_conv_with(&world)?;
400 let line_index = world.analysis().file_line_index(file_id); 384 let line_index = world.analysis().file_line_index(file_id);
@@ -427,7 +411,6 @@ pub fn handle_folding_range(
427pub fn handle_signature_help( 411pub fn handle_signature_help(
428 world: ServerWorld, 412 world: ServerWorld,
429 params: req::TextDocumentPositionParams, 413 params: req::TextDocumentPositionParams,
430 token: JobToken,
431) -> Result<Option<req::SignatureHelp>> { 414) -> Result<Option<req::SignatureHelp>> {
432 use languageserver_types::{ParameterInformation, SignatureInformation}; 415 use languageserver_types::{ParameterInformation, SignatureInformation};
433 416
@@ -436,7 +419,7 @@ pub fn handle_signature_help(
436 let offset = params.position.conv_with(&line_index); 419 let offset = params.position.conv_with(&line_index);
437 420
438 if let Some((descriptor, active_param)) = 421 if let Some((descriptor, active_param)) =
439 world.analysis().resolve_callable(file_id, offset, &token) 422 world.analysis().resolve_callable(file_id, offset)?
440 { 423 {
441 let parameters: Vec<ParameterInformation> = descriptor 424 let parameters: Vec<ParameterInformation> = descriptor
442 .params 425 .params
@@ -466,7 +449,6 @@ pub fn handle_signature_help(
466pub fn handle_prepare_rename( 449pub fn handle_prepare_rename(
467 world: ServerWorld, 450 world: ServerWorld,
468 params: req::TextDocumentPositionParams, 451 params: req::TextDocumentPositionParams,
469 token: JobToken,
470) -> Result<Option<PrepareRenameResponse>> { 452) -> Result<Option<PrepareRenameResponse>> {
471 let file_id = params.text_document.try_conv_with(&world)?; 453 let file_id = params.text_document.try_conv_with(&world)?;
472 let line_index = world.analysis().file_line_index(file_id); 454 let line_index = world.analysis().file_line_index(file_id);
@@ -474,7 +456,7 @@ pub fn handle_prepare_rename(
474 456
475 // We support renaming references like handle_rename does. 457 // We support renaming references like handle_rename does.
476 // In the future we may want to reject the renaming of things like keywords here too. 458 // In the future we may want to reject the renaming of things like keywords here too.
477 let refs = world.analysis().find_all_refs(file_id, offset, &token); 459 let refs = world.analysis().find_all_refs(file_id, offset)?;
478 if refs.is_empty() { 460 if refs.is_empty() {
479 return Ok(None); 461 return Ok(None);
480 } 462 }
@@ -488,7 +470,6 @@ pub fn handle_prepare_rename(
488pub fn handle_rename( 470pub fn handle_rename(
489 world: ServerWorld, 471 world: ServerWorld,
490 params: RenameParams, 472 params: RenameParams,
491 token: JobToken,
492) -> Result<Option<WorkspaceEdit>> { 473) -> Result<Option<WorkspaceEdit>> {
493 let file_id = params.text_document.try_conv_with(&world)?; 474 let file_id = params.text_document.try_conv_with(&world)?;
494 let line_index = world.analysis().file_line_index(file_id); 475 let line_index = world.analysis().file_line_index(file_id);
@@ -498,7 +479,7 @@ pub fn handle_rename(
498 return Ok(None); 479 return Ok(None);
499 } 480 }
500 481
501 let refs = world.analysis().find_all_refs(file_id, offset, &token); 482 let refs = world.analysis().find_all_refs(file_id, offset)?;
502 if refs.is_empty() { 483 if refs.is_empty() {
503 return Ok(None); 484 return Ok(None);
504 } 485 }
@@ -525,13 +506,12 @@ pub fn handle_rename(
525pub fn handle_references( 506pub fn handle_references(
526 world: ServerWorld, 507 world: ServerWorld,
527 params: req::ReferenceParams, 508 params: req::ReferenceParams,
528 token: JobToken,
529) -> Result<Option<Vec<Location>>> { 509) -> Result<Option<Vec<Location>>> {
530 let file_id = params.text_document.try_conv_with(&world)?; 510 let file_id = params.text_document.try_conv_with(&world)?;
531 let line_index = world.analysis().file_line_index(file_id); 511 let line_index = world.analysis().file_line_index(file_id);
532 let offset = params.position.conv_with(&line_index); 512 let offset = params.position.conv_with(&line_index);
533 513
534 let refs = world.analysis().find_all_refs(file_id, offset, &token); 514 let refs = world.analysis().find_all_refs(file_id, offset)?;
535 515
536 Ok(Some(refs.into_iter() 516 Ok(Some(refs.into_iter()
537 .filter_map(|r| to_location(r.0, r.1, &world, &line_index).ok()) 517 .filter_map(|r| to_location(r.0, r.1, &world, &line_index).ok())
@@ -541,16 +521,15 @@ pub fn handle_references(
541pub fn handle_code_action( 521pub fn handle_code_action(
542 world: ServerWorld, 522 world: ServerWorld,
543 params: req::CodeActionParams, 523 params: req::CodeActionParams,
544 _token: JobToken,
545) -> Result<Option<CodeActionResponse>> { 524) -> Result<Option<CodeActionResponse>> {
546 let file_id = params.text_document.try_conv_with(&world)?; 525 let file_id = params.text_document.try_conv_with(&world)?;
547 let line_index = world.analysis().file_line_index(file_id); 526 let line_index = world.analysis().file_line_index(file_id);
548 let range = params.range.conv_with(&line_index); 527 let range = params.range.conv_with(&line_index);
549 528
550 let assists = world.analysis().assists(file_id, range).into_iter(); 529 let assists = world.analysis().assists(file_id, range)?.into_iter();
551 let fixes = world 530 let fixes = world
552 .analysis() 531 .analysis()
553 .diagnostics(file_id) 532 .diagnostics(file_id)?
554 .into_iter() 533 .into_iter()
555 .filter_map(|d| Some((d.range, d.fix?))) 534 .filter_map(|d| Some((d.range, d.fix?)))
556 .filter(|(range, _fix)| contains_offset_nonstrict(*range, range.start())) 535 .filter(|(range, _fix)| contains_offset_nonstrict(*range, range.start()))
@@ -579,7 +558,7 @@ pub fn publish_diagnostics(
579 let line_index = world.analysis().file_line_index(file_id); 558 let line_index = world.analysis().file_line_index(file_id);
580 let diagnostics = world 559 let diagnostics = world
581 .analysis() 560 .analysis()
582 .diagnostics(file_id) 561 .diagnostics(file_id)?
583 .into_iter() 562 .into_iter()
584 .map(|d| Diagnostic { 563 .map(|d| Diagnostic {
585 range: d.range.conv_with(&line_index), 564 range: d.range.conv_with(&line_index),
@@ -600,19 +579,20 @@ pub fn publish_decorations(
600 let uri = world.file_id_to_uri(file_id)?; 579 let uri = world.file_id_to_uri(file_id)?;
601 Ok(req::PublishDecorationsParams { 580 Ok(req::PublishDecorationsParams {
602 uri, 581 uri,
603 decorations: highlight(&world, file_id), 582 decorations: highlight(&world, file_id)?,
604 }) 583 })
605} 584}
606 585
607fn highlight(world: &ServerWorld, file_id: FileId) -> Vec<Decoration> { 586fn highlight(world: &ServerWorld, file_id: FileId) -> Result<Vec<Decoration>> {
608 let line_index = world.analysis().file_line_index(file_id); 587 let line_index = world.analysis().file_line_index(file_id);
609 world 588 let res = world
610 .analysis() 589 .analysis()
611 .highlight(file_id) 590 .highlight(file_id)?
612 .into_iter() 591 .into_iter()
613 .map(|h| Decoration { 592 .map(|h| Decoration {
614 range: h.range.conv_with(&line_index), 593 range: h.range.conv_with(&line_index),
615 tag: h.tag, 594 tag: h.tag,
616 }) 595 })
617 .collect() 596 .collect();
597 Ok(res)
618} 598}