]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/ide/src/lib.rs
239456cb281676809f5105aeef75bd844f9fb89f
[rustc.git] / src / tools / rust-analyzer / crates / ide / src / lib.rs
1 //! ide crate provides "ide-centric" APIs for the rust-analyzer. That is,
2 //! it generally operates with files and text ranges, and returns results as
3 //! Strings, suitable for displaying to the human.
4 //!
5 //! What powers this API are the `RootDatabase` struct, which defines a `salsa`
6 //! database, and the `hir` crate, where majority of the analysis happens.
7 //! However, IDE specific bits of the analysis (most notably completion) happen
8 //! in this crate.
9
10 // For proving that RootDatabase is RefUnwindSafe.
11 #![recursion_limit = "128"]
12 #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
13
14 #[allow(unused)]
15 macro_rules! eprintln {
16 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
17 }
18
19 #[cfg(test)]
20 mod fixture;
21
22 mod markup;
23 mod prime_caches;
24 mod navigation_target;
25
26 mod annotations;
27 mod call_hierarchy;
28 mod signature_help;
29 mod doc_links;
30 mod highlight_related;
31 mod expand_macro;
32 mod extend_selection;
33 mod file_structure;
34 mod folding_ranges;
35 mod goto_declaration;
36 mod goto_definition;
37 mod goto_implementation;
38 mod goto_type_definition;
39 mod hover;
40 mod inlay_hints;
41 mod join_lines;
42 mod markdown_remove;
43 mod matching_brace;
44 mod moniker;
45 mod move_item;
46 mod parent_module;
47 mod references;
48 mod rename;
49 mod runnables;
50 mod ssr;
51 mod static_index;
52 mod status;
53 mod syntax_highlighting;
54 mod syntax_tree;
55 mod typing;
56 mod view_crate_graph;
57 mod view_hir;
58 mod view_item_tree;
59 mod shuffle_crate_graph;
60
61 use std::sync::Arc;
62
63 use cfg::CfgOptions;
64 use ide_db::{
65 base_db::{
66 salsa::{self, ParallelDatabase},
67 CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
68 },
69 symbol_index, LineIndexDatabase,
70 };
71 use syntax::SourceFile;
72
73 use crate::navigation_target::{ToNav, TryToNav};
74
75 pub use crate::{
76 annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation},
77 call_hierarchy::CallItem,
78 expand_macro::ExpandedMacro,
79 file_structure::{StructureNode, StructureNodeKind},
80 folding_ranges::{Fold, FoldKind},
81 highlight_related::{HighlightRelatedConfig, HighlightedRange},
82 hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
83 inlay_hints::{
84 AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint,
85 InlayHintLabel, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints,
86 },
87 join_lines::JoinLinesConfig,
88 markup::Markup,
89 moniker::{MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation},
90 move_item::Direction,
91 navigation_target::NavigationTarget,
92 prime_caches::ParallelPrimeCachesProgress,
93 references::ReferenceSearchResult,
94 rename::RenameError,
95 runnables::{Runnable, RunnableKind, TestId},
96 signature_help::SignatureHelp,
97 static_index::{StaticIndex, StaticIndexedFile, TokenId, TokenStaticData},
98 syntax_highlighting::{
99 tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
100 HighlightConfig, HlRange,
101 },
102 };
103 pub use hir::{Documentation, Semantics};
104 pub use ide_assists::{
105 Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve,
106 };
107 pub use ide_completion::{
108 CallableSnippets, CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance,
109 Snippet, SnippetScope,
110 };
111 pub use ide_db::{
112 base_db::{
113 Cancelled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange,
114 SourceRoot, SourceRootId,
115 },
116 label::Label,
117 line_index::{LineCol, LineColUtf16, LineIndex},
118 search::{ReferenceCategory, SearchScope},
119 source_change::{FileSystemEdit, SourceChange},
120 symbol_index::Query,
121 RootDatabase, SymbolKind,
122 };
123 pub use ide_diagnostics::{Diagnostic, DiagnosticsConfig, ExprFillDefaultMode, Severity};
124 pub use ide_ssr::SsrError;
125 pub use syntax::{TextRange, TextSize};
126 pub use text_edit::{Indel, TextEdit};
127
128 pub type Cancellable<T> = Result<T, Cancelled>;
129
130 /// Info associated with a text range.
131 #[derive(Debug)]
132 pub struct RangeInfo<T> {
133 pub range: TextRange,
134 pub info: T,
135 }
136
137 impl<T> RangeInfo<T> {
138 pub fn new(range: TextRange, info: T) -> RangeInfo<T> {
139 RangeInfo { range, info }
140 }
141 }
142
143 /// `AnalysisHost` stores the current state of the world.
144 #[derive(Debug)]
145 pub struct AnalysisHost {
146 db: RootDatabase,
147 }
148
149 impl AnalysisHost {
150 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
151 AnalysisHost { db: RootDatabase::new(lru_capacity) }
152 }
153
154 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
155 self.db.update_lru_capacity(lru_capacity);
156 }
157
158 /// Returns a snapshot of the current state, which you can query for
159 /// semantic information.
160 pub fn analysis(&self) -> Analysis {
161 Analysis { db: self.db.snapshot() }
162 }
163
164 /// Applies changes to the current state of the world. If there are
165 /// outstanding snapshots, they will be canceled.
166 pub fn apply_change(&mut self, change: Change) {
167 self.db.apply_change(change)
168 }
169
170 /// NB: this clears the database
171 pub fn per_query_memory_usage(&mut self) -> Vec<(String, profile::Bytes)> {
172 self.db.per_query_memory_usage()
173 }
174 pub fn request_cancellation(&mut self) {
175 self.db.request_cancellation();
176 }
177 pub fn raw_database(&self) -> &RootDatabase {
178 &self.db
179 }
180 pub fn raw_database_mut(&mut self) -> &mut RootDatabase {
181 &mut self.db
182 }
183
184 pub fn shuffle_crate_graph(&mut self) {
185 shuffle_crate_graph::shuffle_crate_graph(&mut self.db);
186 }
187 }
188
189 impl Default for AnalysisHost {
190 fn default() -> AnalysisHost {
191 AnalysisHost::new(None)
192 }
193 }
194
195 /// Analysis is a snapshot of a world state at a moment in time. It is the main
196 /// entry point for asking semantic information about the world. When the world
197 /// state is advanced using `AnalysisHost::apply_change` method, all existing
198 /// `Analysis` are canceled (most method return `Err(Canceled)`).
199 #[derive(Debug)]
200 pub struct Analysis {
201 db: salsa::Snapshot<RootDatabase>,
202 }
203
204 // As a general design guideline, `Analysis` API are intended to be independent
205 // from the language server protocol. That is, when exposing some functionality
206 // we should think in terms of "what API makes most sense" and not in terms of
207 // "what types LSP uses". Although currently LSP is the only consumer of the
208 // API, the API should in theory be usable as a library, or via a different
209 // protocol.
210 impl Analysis {
211 // Creates an analysis instance for a single file, without any external
212 // dependencies, stdlib support or ability to apply changes. See
213 // `AnalysisHost` for creating a fully-featured analysis.
214 pub fn from_single_file(text: String) -> (Analysis, FileId) {
215 let mut host = AnalysisHost::default();
216 let file_id = FileId(0);
217 let mut file_set = FileSet::default();
218 file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_string()));
219 let source_root = SourceRoot::new_local(file_set);
220
221 let mut change = Change::new();
222 change.set_roots(vec![source_root]);
223 let mut crate_graph = CrateGraph::default();
224 // FIXME: cfg options
225 // Default to enable test for single file.
226 let mut cfg_options = CfgOptions::default();
227 cfg_options.insert_atom("test".into());
228 crate_graph.add_crate_root(
229 file_id,
230 Edition::CURRENT,
231 None,
232 None,
233 cfg_options.clone(),
234 cfg_options,
235 Env::default(),
236 Ok(Vec::new()),
237 false,
238 CrateOrigin::CratesIo { repo: None, name: None },
239 None,
240 );
241 change.change_file(file_id, Some(Arc::new(text)));
242 change.set_crate_graph(crate_graph);
243 host.apply_change(change);
244 (host.analysis(), file_id)
245 }
246
247 /// Debug info about the current state of the analysis.
248 pub fn status(&self, file_id: Option<FileId>) -> Cancellable<String> {
249 self.with_db(|db| status::status(&*db, file_id))
250 }
251
252 pub fn parallel_prime_caches<F>(&self, num_worker_threads: u8, cb: F) -> Cancellable<()>
253 where
254 F: Fn(ParallelPrimeCachesProgress) + Sync + std::panic::UnwindSafe,
255 {
256 self.with_db(move |db| prime_caches::parallel_prime_caches(db, num_worker_threads, &cb))
257 }
258
259 /// Gets the text of the source file.
260 pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<String>> {
261 self.with_db(|db| db.file_text(file_id))
262 }
263
264 /// Gets the syntax tree of the file.
265 pub fn parse(&self, file_id: FileId) -> Cancellable<SourceFile> {
266 self.with_db(|db| db.parse(file_id).tree())
267 }
268
269 /// Returns true if this file belongs to an immutable library.
270 pub fn is_library_file(&self, file_id: FileId) -> Cancellable<bool> {
271 use ide_db::base_db::SourceDatabaseExt;
272 self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library)
273 }
274
275 /// Gets the file's `LineIndex`: data structure to convert between absolute
276 /// offsets and line/column representation.
277 pub fn file_line_index(&self, file_id: FileId) -> Cancellable<Arc<LineIndex>> {
278 self.with_db(|db| db.line_index(file_id))
279 }
280
281 /// Selects the next syntactic nodes encompassing the range.
282 pub fn extend_selection(&self, frange: FileRange) -> Cancellable<TextRange> {
283 self.with_db(|db| extend_selection::extend_selection(db, frange))
284 }
285
286 /// Returns position of the matching brace (all types of braces are
287 /// supported).
288 pub fn matching_brace(&self, position: FilePosition) -> Cancellable<Option<TextSize>> {
289 self.with_db(|db| {
290 let parse = db.parse(position.file_id);
291 let file = parse.tree();
292 matching_brace::matching_brace(&file, position.offset)
293 })
294 }
295
296 /// Returns a syntax tree represented as `String`, for debug purposes.
297 // FIXME: use a better name here.
298 pub fn syntax_tree(
299 &self,
300 file_id: FileId,
301 text_range: Option<TextRange>,
302 ) -> Cancellable<String> {
303 self.with_db(|db| syntax_tree::syntax_tree(db, file_id, text_range))
304 }
305
306 pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> {
307 self.with_db(|db| view_hir::view_hir(db, position))
308 }
309
310 pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
311 self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
312 }
313
314 /// Renders the crate graph to GraphViz "dot" syntax.
315 pub fn view_crate_graph(&self, full: bool) -> Cancellable<Result<String, String>> {
316 self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
317 }
318
319 pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
320 self.with_db(|db| expand_macro::expand_macro(db, position))
321 }
322
323 /// Returns an edit to remove all newlines in the range, cleaning up minor
324 /// stuff like trailing commas.
325 pub fn join_lines(&self, config: &JoinLinesConfig, frange: FileRange) -> Cancellable<TextEdit> {
326 self.with_db(|db| {
327 let parse = db.parse(frange.file_id);
328 join_lines::join_lines(config, &parse.tree(), frange.range)
329 })
330 }
331
332 /// Returns an edit which should be applied when opening a new line, fixing
333 /// up minor stuff like continuing the comment.
334 /// The edit will be a snippet (with `$0`).
335 pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> {
336 self.with_db(|db| typing::on_enter(db, position))
337 }
338
339 /// Returns an edit which should be applied after a character was typed.
340 ///
341 /// This is useful for some on-the-fly fixups, like adding `;` to `let =`
342 /// automatically.
343 pub fn on_char_typed(
344 &self,
345 position: FilePosition,
346 char_typed: char,
347 autoclose: bool,
348 ) -> Cancellable<Option<SourceChange>> {
349 // Fast path to not even parse the file.
350 if !typing::TRIGGER_CHARS.contains(char_typed) {
351 return Ok(None);
352 }
353 if char_typed == '<' && !autoclose {
354 return Ok(None);
355 }
356
357 self.with_db(|db| typing::on_char_typed(db, position, char_typed))
358 }
359
360 /// Returns a tree representation of symbols in the file. Useful to draw a
361 /// file outline.
362 pub fn file_structure(&self, file_id: FileId) -> Cancellable<Vec<StructureNode>> {
363 self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree()))
364 }
365
366 /// Returns a list of the places in the file where type hints can be displayed.
367 pub fn inlay_hints(
368 &self,
369 config: &InlayHintsConfig,
370 file_id: FileId,
371 range: Option<TextRange>,
372 ) -> Cancellable<Vec<InlayHint>> {
373 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config))
374 }
375
376 /// Returns the set of folding ranges.
377 pub fn folding_ranges(&self, file_id: FileId) -> Cancellable<Vec<Fold>> {
378 self.with_db(|db| folding_ranges::folding_ranges(&db.parse(file_id).tree()))
379 }
380
381 /// Fuzzy searches for a symbol.
382 pub fn symbol_search(&self, query: Query) -> Cancellable<Vec<NavigationTarget>> {
383 self.with_db(|db| {
384 symbol_index::world_symbols(db, query)
385 .into_iter() // xx: should we make this a par iter?
386 .filter_map(|s| s.try_to_nav(db))
387 .collect::<Vec<_>>()
388 })
389 }
390
391 /// Returns the definitions from the symbol at `position`.
392 pub fn goto_definition(
393 &self,
394 position: FilePosition,
395 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
396 self.with_db(|db| goto_definition::goto_definition(db, position))
397 }
398
399 /// Returns the declaration from the symbol at `position`.
400 pub fn goto_declaration(
401 &self,
402 position: FilePosition,
403 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
404 self.with_db(|db| goto_declaration::goto_declaration(db, position))
405 }
406
407 /// Returns the impls from the symbol at `position`.
408 pub fn goto_implementation(
409 &self,
410 position: FilePosition,
411 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
412 self.with_db(|db| goto_implementation::goto_implementation(db, position))
413 }
414
415 /// Returns the type definitions for the symbol at `position`.
416 pub fn goto_type_definition(
417 &self,
418 position: FilePosition,
419 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
420 self.with_db(|db| goto_type_definition::goto_type_definition(db, position))
421 }
422
423 /// Finds all usages of the reference at point.
424 pub fn find_all_refs(
425 &self,
426 position: FilePosition,
427 search_scope: Option<SearchScope>,
428 ) -> Cancellable<Option<Vec<ReferenceSearchResult>>> {
429 self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope))
430 }
431
432 /// Returns a short text describing element at position.
433 pub fn hover(
434 &self,
435 config: &HoverConfig,
436 range: FileRange,
437 ) -> Cancellable<Option<RangeInfo<HoverResult>>> {
438 self.with_db(|db| hover::hover(db, range, config))
439 }
440
441 /// Returns moniker of symbol at position.
442 pub fn moniker(
443 &self,
444 position: FilePosition,
445 ) -> Cancellable<Option<RangeInfo<Vec<moniker::MonikerResult>>>> {
446 self.with_db(|db| moniker::moniker(db, position))
447 }
448
449 /// Return URL(s) for the documentation of the symbol under the cursor.
450 pub fn external_docs(
451 &self,
452 position: FilePosition,
453 ) -> Cancellable<Option<doc_links::DocumentationLink>> {
454 self.with_db(|db| doc_links::external_docs(db, &position))
455 }
456
457 /// Computes parameter information at the given position.
458 pub fn signature_help(&self, position: FilePosition) -> Cancellable<Option<SignatureHelp>> {
459 self.with_db(|db| signature_help::signature_help(db, position))
460 }
461
462 /// Computes call hierarchy candidates for the given file position.
463 pub fn call_hierarchy(
464 &self,
465 position: FilePosition,
466 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
467 self.with_db(|db| call_hierarchy::call_hierarchy(db, position))
468 }
469
470 /// Computes incoming calls for the given file position.
471 pub fn incoming_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> {
472 self.with_db(|db| call_hierarchy::incoming_calls(db, position))
473 }
474
475 /// Computes outgoing calls for the given file position.
476 pub fn outgoing_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> {
477 self.with_db(|db| call_hierarchy::outgoing_calls(db, position))
478 }
479
480 /// Returns a `mod name;` declaration which created the current module.
481 pub fn parent_module(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> {
482 self.with_db(|db| parent_module::parent_module(db, position))
483 }
484
485 /// Returns crates this file belongs too.
486 pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
487 self.with_db(|db| parent_module::crates_for(db, file_id))
488 }
489
490 /// Returns crates this file belongs too.
491 pub fn transitive_rev_deps(&self, crate_id: CrateId) -> Cancellable<Vec<CrateId>> {
492 self.with_db(|db| db.crate_graph().transitive_rev_deps(crate_id).collect())
493 }
494
495 /// Returns crates this file *might* belong too.
496 pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
497 self.with_db(|db| db.relevant_crates(file_id).iter().copied().collect())
498 }
499
500 /// Returns the edition of the given crate.
501 pub fn crate_edition(&self, crate_id: CrateId) -> Cancellable<Edition> {
502 self.with_db(|db| db.crate_graph()[crate_id].edition)
503 }
504
505 /// Returns the root file of the given crate.
506 pub fn crate_root(&self, crate_id: CrateId) -> Cancellable<FileId> {
507 self.with_db(|db| db.crate_graph()[crate_id].root_file_id)
508 }
509
510 /// Returns the set of possible targets to run for the current file.
511 pub fn runnables(&self, file_id: FileId) -> Cancellable<Vec<Runnable>> {
512 self.with_db(|db| runnables::runnables(db, file_id))
513 }
514
515 /// Returns the set of tests for the given file position.
516 pub fn related_tests(
517 &self,
518 position: FilePosition,
519 search_scope: Option<SearchScope>,
520 ) -> Cancellable<Vec<Runnable>> {
521 self.with_db(|db| runnables::related_tests(db, position, search_scope))
522 }
523
524 /// Computes syntax highlighting for the given file
525 pub fn highlight(
526 &self,
527 highlight_config: HighlightConfig,
528 file_id: FileId,
529 ) -> Cancellable<Vec<HlRange>> {
530 self.with_db(|db| syntax_highlighting::highlight(db, highlight_config, file_id, None))
531 }
532
533 /// Computes all ranges to highlight for a given item in a file.
534 pub fn highlight_related(
535 &self,
536 config: HighlightRelatedConfig,
537 position: FilePosition,
538 ) -> Cancellable<Option<Vec<HighlightedRange>>> {
539 self.with_db(|db| {
540 highlight_related::highlight_related(&Semantics::new(db), config, position)
541 })
542 }
543
544 /// Computes syntax highlighting for the given file range.
545 pub fn highlight_range(
546 &self,
547 highlight_config: HighlightConfig,
548 frange: FileRange,
549 ) -> Cancellable<Vec<HlRange>> {
550 self.with_db(|db| {
551 syntax_highlighting::highlight(db, highlight_config, frange.file_id, Some(frange.range))
552 })
553 }
554
555 /// Computes syntax highlighting for the given file.
556 pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable<String> {
557 self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow))
558 }
559
560 /// Computes completions at the given position.
561 pub fn completions(
562 &self,
563 config: &CompletionConfig,
564 position: FilePosition,
565 trigger_character: Option<char>,
566 ) -> Cancellable<Option<Vec<CompletionItem>>> {
567 self.with_db(|db| {
568 ide_completion::completions(db, config, position, trigger_character).map(Into::into)
569 })
570 }
571
572 /// Resolves additional completion data at the position given.
573 pub fn resolve_completion_edits(
574 &self,
575 config: &CompletionConfig,
576 position: FilePosition,
577 imports: impl IntoIterator<Item = (String, String)> + std::panic::UnwindSafe,
578 ) -> Cancellable<Vec<TextEdit>> {
579 Ok(self
580 .with_db(|db| ide_completion::resolve_completion_edits(db, config, position, imports))?
581 .unwrap_or_default())
582 }
583
584 /// Computes the set of diagnostics for the given file.
585 pub fn diagnostics(
586 &self,
587 config: &DiagnosticsConfig,
588 resolve: AssistResolveStrategy,
589 file_id: FileId,
590 ) -> Cancellable<Vec<Diagnostic>> {
591 self.with_db(|db| ide_diagnostics::diagnostics(db, config, &resolve, file_id))
592 }
593
594 /// Convenience function to return assists + quick fixes for diagnostics
595 pub fn assists_with_fixes(
596 &self,
597 assist_config: &AssistConfig,
598 diagnostics_config: &DiagnosticsConfig,
599 resolve: AssistResolveStrategy,
600 frange: FileRange,
601 ) -> Cancellable<Vec<Assist>> {
602 let include_fixes = match &assist_config.allowed {
603 Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix),
604 None => true,
605 };
606
607 self.with_db(|db| {
608 let diagnostic_assists = if include_fixes {
609 ide_diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id)
610 .into_iter()
611 .flat_map(|it| it.fixes.unwrap_or_default())
612 .filter(|it| it.target.intersect(frange.range).is_some())
613 .collect()
614 } else {
615 Vec::new()
616 };
617 let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
618 let assists = ide_assists::assists(db, assist_config, resolve, frange);
619
620 let mut res = diagnostic_assists;
621 res.extend(ssr_assists.into_iter());
622 res.extend(assists.into_iter());
623
624 res
625 })
626 }
627
628 /// Returns the edit required to rename reference at the position to the new
629 /// name.
630 pub fn rename(
631 &self,
632 position: FilePosition,
633 new_name: &str,
634 ) -> Cancellable<Result<SourceChange, RenameError>> {
635 self.with_db(|db| rename::rename(db, position, new_name))
636 }
637
638 pub fn prepare_rename(
639 &self,
640 position: FilePosition,
641 ) -> Cancellable<Result<RangeInfo<()>, RenameError>> {
642 self.with_db(|db| rename::prepare_rename(db, position))
643 }
644
645 pub fn will_rename_file(
646 &self,
647 file_id: FileId,
648 new_name_stem: &str,
649 ) -> Cancellable<Option<SourceChange>> {
650 self.with_db(|db| rename::will_rename_file(db, file_id, new_name_stem))
651 }
652
653 pub fn structural_search_replace(
654 &self,
655 query: &str,
656 parse_only: bool,
657 resolve_context: FilePosition,
658 selections: Vec<FileRange>,
659 ) -> Cancellable<Result<SourceChange, SsrError>> {
660 self.with_db(|db| {
661 let rule: ide_ssr::SsrRule = query.parse()?;
662 let mut match_finder =
663 ide_ssr::MatchFinder::in_context(db, resolve_context, selections)?;
664 match_finder.add_rule(rule)?;
665 let edits = if parse_only { Default::default() } else { match_finder.edits() };
666 Ok(SourceChange::from(edits))
667 })
668 }
669
670 pub fn annotations(
671 &self,
672 config: &AnnotationConfig,
673 file_id: FileId,
674 ) -> Cancellable<Vec<Annotation>> {
675 self.with_db(|db| annotations::annotations(db, config, file_id))
676 }
677
678 pub fn resolve_annotation(&self, annotation: Annotation) -> Cancellable<Annotation> {
679 self.with_db(|db| annotations::resolve_annotation(db, annotation))
680 }
681
682 pub fn move_item(
683 &self,
684 range: FileRange,
685 direction: Direction,
686 ) -> Cancellable<Option<TextEdit>> {
687 self.with_db(|db| move_item::move_item(db, range, direction))
688 }
689
690 /// Performs an operation on the database that may be canceled.
691 ///
692 /// rust-analyzer needs to be able to answer semantic questions about the
693 /// code while the code is being modified. A common problem is that a
694 /// long-running query is being calculated when a new change arrives.
695 ///
696 /// We can't just apply the change immediately: this will cause the pending
697 /// query to see inconsistent state (it will observe an absence of
698 /// repeatable read). So what we do is we **cancel** all pending queries
699 /// before applying the change.
700 ///
701 /// Salsa implements cancellation by unwinding with a special value and
702 /// catching it on the API boundary.
703 fn with_db<F, T>(&self, f: F) -> Cancellable<T>
704 where
705 F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe,
706 {
707 Cancelled::catch(|| f(&self.db))
708 }
709 }
710
711 #[test]
712 fn analysis_is_send() {
713 fn is_send<T: Send>() {}
714 is_send::<Analysis>();
715 }