Skip to main content

ide/
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
12#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
13#![recursion_limit = "128"]
14
15#[cfg(test)]
16mod fixture;
17
18mod markup;
19mod navigation_target;
20
21mod annotations;
22mod call_hierarchy;
23mod child_modules;
24mod doc_links;
25mod expand_macro;
26mod extend_selection;
27mod fetch_crates;
28mod file_structure;
29mod folding_ranges;
30mod goto_declaration;
31mod goto_definition;
32mod goto_implementation;
33mod goto_type_definition;
34mod highlight_related;
35mod hover;
36mod inlay_hints;
37mod interpret;
38mod join_lines;
39mod markdown_remove;
40mod matching_brace;
41mod moniker;
42mod move_item;
43mod parent_module;
44mod predicate_eval;
45mod references;
46mod rename;
47mod runnables;
48mod signature_help;
49mod ssr;
50mod static_index;
51mod status;
52mod syntax_highlighting;
53mod test_explorer;
54mod typing;
55mod view_crate_graph;
56mod view_hir;
57mod view_item_tree;
58mod view_memory_layout;
59mod view_mir;
60mod view_syntax_tree;
61
62use std::panic::{AssertUnwindSafe, UnwindSafe};
63use std::time::Duration;
64
65use cfg::CfgOptions;
66use fetch_crates::CrateInfo;
67use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym};
68use ide_db::{
69    FxHashMap, FxIndexSet,
70    base_db::{
71        AbsPathBuf, CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath,
72        relevant_crates,
73        salsa::{Cancelled, Database, Durability},
74    },
75    line_index, prime_caches,
76    ra_fixture::RaFixtureAnalysis,
77    symbol_index,
78};
79use macros::UpmapFromRaFixture;
80use syntax::{AstNode, SourceFile, ast};
81use triomphe::Arc;
82use view_memory_layout::{RecursiveMemoryLayout, view_memory_layout};
83
84use crate::navigation_target::ToNav;
85
86pub use crate::{
87    annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation},
88    call_hierarchy::{CallHierarchyConfig, CallItem},
89    expand_macro::ExpandedMacro,
90    file_structure::{FileStructureConfig, StructureNode, StructureNodeKind},
91    folding_ranges::{Fold, FoldKind},
92    goto_definition::GotoDefinitionConfig,
93    goto_implementation::GotoImplementationConfig,
94    highlight_related::{HighlightRelatedConfig, HighlightedRange},
95    hover::{
96        HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
97        MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, SubstTyLen,
98    },
99    inlay_hints::{
100        AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
101        GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart,
102        InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty,
103        LifetimeElisionHints, TypeHintsPlacement,
104    },
105    join_lines::JoinLinesConfig,
106    markup::Markup,
107    moniker::{
108        Moniker, MonikerDescriptorKind, MonikerIdentifier, MonikerKind, MonikerResult,
109        PackageInformation, SymbolInformationKind,
110    },
111    move_item::Direction,
112    navigation_target::{NavigationTarget, TryToNav, UpmappingResult},
113    references::{FindAllRefsConfig, ReferenceSearchResult},
114    rename::{RenameConfig, RenameError},
115    runnables::{Runnable, RunnableKind, TestId, UpdateTest},
116    signature_help::SignatureHelp,
117    static_index::{
118        StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, VendoredLibrariesConfig,
119    },
120    syntax_highlighting::{
121        HighlightConfig, HlRange,
122        tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
123    },
124    test_explorer::{TestItem, TestItemKind},
125};
126pub use hir::{PredicateEvaluationResult, PredicateEvaluationStatus, Semantics};
127pub use ide_assists::{
128    Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve,
129};
130pub use ide_completion::{
131    CallableSnippets, CompletionConfig, CompletionFieldsToResolve, CompletionItem,
132    CompletionItemImport, CompletionItemKind, CompletionItemRefMode, CompletionRelevance, Snippet,
133    SnippetScope,
134};
135pub use ide_db::{
136    FileId, FilePosition, FileRange, RootDatabase, Severity, SymbolKind,
137    assists::ExprFillDefaultMode,
138    base_db::{Crate, CrateGraphBuilder, FileChange, SourceRoot, SourceRootId},
139    documentation::Documentation,
140    label::Label,
141    line_index::{LineCol, LineIndex},
142    prime_caches::ParallelPrimeCachesProgress,
143    ra_fixture::RaFixtureConfig,
144    search::{ReferenceCategory, SearchScope},
145    source_change::{FileSystemEdit, SnippetEdit, SourceChange},
146    symbol_index::Query,
147    text_edit::{Indel, TextEdit},
148};
149pub use ide_diagnostics::{Diagnostic, DiagnosticCode, DiagnosticsConfig};
150pub use ide_ssr::SsrError;
151pub use span::Edition;
152pub use syntax::{TextRange, TextSize};
153
154pub type Cancellable<T> = Result<T, Cancelled>;
155
156/// Info associated with a text range.
157#[derive(Debug, UpmapFromRaFixture)]
158pub struct RangeInfo<T> {
159    pub range: TextRange,
160    pub info: T,
161}
162
163impl<T> RangeInfo<T> {
164    pub fn new(range: TextRange, info: T) -> RangeInfo<T> {
165        RangeInfo { range, info }
166    }
167}
168
169/// `AnalysisHost` stores the current state of the world.
170#[derive(Debug)]
171pub struct AnalysisHost {
172    db: RootDatabase,
173}
174
175impl AnalysisHost {
176    pub fn new(lru_capacity: Option<u16>) -> AnalysisHost {
177        AnalysisHost { db: RootDatabase::new(lru_capacity) }
178    }
179
180    pub fn with_database(db: RootDatabase) -> AnalysisHost {
181        AnalysisHost { db }
182    }
183
184    pub fn update_lru_capacity(&mut self, lru_capacity: Option<u16>) {
185        self.db.update_base_query_lru_capacities(lru_capacity);
186    }
187
188    pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, u16>) {
189        self.db.update_lru_capacities(lru_capacities);
190    }
191
192    /// Returns a snapshot of the current state, which you can query for
193    /// semantic information.
194    pub fn analysis(&self) -> Analysis {
195        Analysis { db: self.db.clone() }
196    }
197
198    /// Applies changes to the current state of the world. If there are
199    /// outstanding snapshots, they will be canceled.
200    pub fn apply_change(&mut self, change: ChangeWithProcMacros) -> Duration {
201        self.db.apply_change(change)
202    }
203
204    /// NB: this clears the database
205    pub fn per_query_memory_usage(&mut self) -> Vec<(String, profile::Bytes, usize)> {
206        self.db.per_query_memory_usage()
207    }
208    pub fn trigger_cancellation(&mut self) {
209        // We need to do a synthetic write right now due to how fixpoint cycles handle cancellation
210        // the revision bump there is a reset marker for clearing fixpoint poisoning.
211        // That is `trigger_cancellation` is currently bugged wrt to cancellation.
212        // self.db.trigger_cancellation();
213        self.db.synthetic_write(Durability::LOW);
214    }
215    pub fn trigger_garbage_collection(&mut self) {
216        // We need to do a synthetic write right now due to how fixpoint cycles handle cancellation
217        // the revision bump there is a reset marker for clearing fixpoint poisoning.
218        // That is `trigger_lru_eviction` is currently bugged wrt to cancellation.
219        // self.db.trigger_lru_eviction();
220        self.db.synthetic_write(Durability::LOW);
221        // SAFETY: `trigger_lru_eviction` triggers cancellation, so all running queries were canceled.
222        unsafe { hir::collect_ty_garbage() };
223    }
224    pub fn raw_database(&self) -> &RootDatabase {
225        &self.db
226    }
227    pub fn raw_database_mut(&mut self) -> &mut RootDatabase {
228        &mut self.db
229    }
230}
231
232impl Default for AnalysisHost {
233    fn default() -> AnalysisHost {
234        AnalysisHost::new(None)
235    }
236}
237
238/// Analysis is a snapshot of a world state at a moment in time. It is the main
239/// entry point for asking semantic information about the world. When the world
240/// state is advanced using `AnalysisHost::apply_change` method, all existing
241/// `Analysis` are canceled (most method return `Err(Canceled)`).
242#[derive(Debug)]
243pub struct Analysis {
244    db: RootDatabase,
245}
246
247// As a general design guideline, `Analysis` API are intended to be independent
248// from the language server protocol. That is, when exposing some functionality
249// we should think in terms of "what API makes most sense" and not in terms of
250// "what types LSP uses". Although currently LSP is the only consumer of the
251// API, the API should in theory be usable as a library, or via a different
252// protocol.
253impl Analysis {
254    // Creates an analysis instance for a single file, without any external
255    // dependencies, stdlib support or ability to apply changes. See
256    // `AnalysisHost` for creating a fully-featured analysis.
257    pub fn from_single_file(text: String, proc_macro_cwd: Arc<AbsPathBuf>) -> (Analysis, FileId) {
258        let mut host = AnalysisHost::default();
259        let file_id = FileId::from_raw(0);
260        let mut file_set = FileSet::default();
261        file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_owned()));
262        let source_root = SourceRoot::new_local(file_set);
263
264        let mut change = ChangeWithProcMacros::default();
265        change.set_roots(vec![source_root]);
266        let mut crate_graph = CrateGraphBuilder::default();
267        // FIXME: cfg options
268        // Default to enable test for single file.
269        let mut cfg_options = CfgOptions::default();
270
271        let crate_attrs = Vec::new();
272        cfg_options.insert_atom(sym::test);
273        crate_graph.add_crate_root(
274            file_id,
275            Edition::CURRENT,
276            None,
277            None,
278            cfg_options,
279            None,
280            Env::default(),
281            CrateOrigin::Local { repo: None, name: None },
282            crate_attrs,
283            false,
284            proc_macro_cwd,
285            Arc::new(CrateWorkspaceData {
286                target: Err("fixture has no layout".into()),
287                toolchain: None,
288            }),
289        );
290        change.change_file(file_id, Some(text));
291        change.set_crate_graph(crate_graph);
292
293        host.apply_change(change);
294        (host.analysis(), file_id)
295    }
296
297    pub(crate) fn from_ra_fixture(
298        sema: &Semantics<'_, RootDatabase>,
299        literal: ast::String,
300        expanded: &ast::String,
301        config: &RaFixtureConfig<'_>,
302    ) -> Option<(Analysis, RaFixtureAnalysis)> {
303        Self::from_ra_fixture_with_on_cursor(sema, literal, expanded, config, &mut |_| {})
304    }
305
306    /// Like [`Analysis::from_ra_fixture()`], but also calls `on_cursor` with the cursor position.
307    pub(crate) fn from_ra_fixture_with_on_cursor(
308        sema: &Semantics<'_, RootDatabase>,
309        literal: ast::String,
310        expanded: &ast::String,
311        config: &RaFixtureConfig<'_>,
312        on_cursor: &mut dyn FnMut(TextRange),
313    ) -> Option<(Analysis, RaFixtureAnalysis)> {
314        let analysis =
315            RaFixtureAnalysis::analyze_ra_fixture(sema, literal, expanded, config, on_cursor)?;
316        Some((Analysis { db: analysis.db.clone() }, analysis))
317    }
318
319    /// Debug info about the current state of the analysis.
320    pub fn status(&self, file_id: Option<FileId>) -> Cancellable<String> {
321        self.with_db(|db| status::status(db, file_id))
322    }
323
324    pub fn source_root_id(&self, file_id: FileId) -> Cancellable<SourceRootId> {
325        self.with_db(|db| db.file_source_root(file_id).source_root_id(db))
326    }
327
328    pub fn is_local_source_root(&self, source_root_id: SourceRootId) -> Cancellable<bool> {
329        self.with_db(|db| {
330            let sr = db.source_root(source_root_id).source_root(db);
331            !sr.is_library
332        })
333    }
334
335    /// Warm caches for the given `scope`. `scope` must be closed under
336    /// transitive dependencies; callers that want to prime everything pass
337    /// `&base_db::all_crates(db)`.
338    pub fn parallel_prime_caches<F>(
339        &self,
340        scope: &[Crate],
341        num_worker_threads: usize,
342        cb: F,
343    ) -> Cancellable<()>
344    where
345        F: Fn(ParallelPrimeCachesProgress) + Sync + std::panic::UnwindSafe,
346    {
347        self.with_db(move |db| {
348            prime_caches::parallel_prime_caches(db, scope, num_worker_threads, &cb)
349        })
350    }
351
352    /// Gets the text of the source file.
353    pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<str>> {
354        self.with_db(|db| SourceDatabase::file_text(db, file_id).text(db).clone())
355    }
356
357    /// Gets the syntax tree of the file.
358    pub fn parse(&self, file_id: FileId) -> Cancellable<SourceFile> {
359        // FIXME edition
360        self.with_db(|db| {
361            let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
362
363            editioned_file_id_wrapper.parse(db).tree()
364        })
365    }
366
367    /// Returns true if this file belongs to an immutable library.
368    pub fn is_library_file(&self, file_id: FileId) -> Cancellable<bool> {
369        self.with_db(|db| {
370            let source_root = db.file_source_root(file_id).source_root_id(db);
371            db.source_root(source_root).source_root(db).is_library
372        })
373    }
374
375    /// Gets the file's `LineIndex`: data structure to convert between absolute
376    /// offsets and line/column representation.
377    pub fn file_line_index(&self, file_id: FileId) -> Cancellable<Arc<LineIndex>> {
378        self.with_db(|db| line_index(db, file_id).clone())
379    }
380
381    /// Selects the next syntactic nodes encompassing the range.
382    pub fn extend_selection(&self, frange: FileRange) -> Cancellable<TextRange> {
383        self.with_db(|db| extend_selection::extend_selection(db, frange))
384    }
385
386    /// Returns position of the matching brace (all types of braces are
387    /// supported).
388    pub fn matching_brace(&self, position: FilePosition) -> Cancellable<Option<TextSize>> {
389        self.with_db(|db| {
390            let file_id = EditionedFileId::current_edition(&self.db, position.file_id);
391            let parse = file_id.parse(db);
392            let file = parse.tree();
393            matching_brace::matching_brace(&file, position.offset)
394        })
395    }
396
397    pub fn view_syntax_tree(&self, file_id: FileId) -> Cancellable<String> {
398        self.with_db(|db| view_syntax_tree::view_syntax_tree(db, file_id))
399    }
400
401    pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> {
402        self.with_db(|db| view_hir::view_hir(db, position))
403    }
404
405    pub fn evaluate_predicate(
406        &self,
407        text: String,
408        position: FilePosition,
409    ) -> Cancellable<PredicateEvaluationResult> {
410        self.with_db(|db| predicate_eval::evaluate_predicate(db, text, position))
411    }
412
413    pub fn view_mir(&self, position: FilePosition) -> Cancellable<String> {
414        self.with_db(|db| view_mir::view_mir(db, position))
415    }
416
417    pub fn interpret_function(&self, position: FilePosition) -> Cancellable<String> {
418        self.with_db(|db| interpret::interpret(db, position))
419    }
420
421    pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
422        self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
423    }
424
425    pub fn discover_test_roots(&self) -> Cancellable<Vec<TestItem>> {
426        self.with_db(test_explorer::discover_test_roots)
427    }
428
429    pub fn discover_tests_in_crate_by_test_id(&self, crate_id: &str) -> Cancellable<Vec<TestItem>> {
430        self.with_db(|db| test_explorer::discover_tests_in_crate_by_test_id(db, crate_id))
431    }
432
433    pub fn discover_tests_in_crate(&self, crate_id: Crate) -> Cancellable<Vec<TestItem>> {
434        self.with_db(|db| test_explorer::discover_tests_in_crate(db, crate_id))
435    }
436
437    pub fn discover_tests_in_file(&self, file_id: FileId) -> Cancellable<Vec<TestItem>> {
438        self.with_db(|db| test_explorer::discover_tests_in_file(db, file_id))
439    }
440
441    /// Renders the crate graph to GraphViz "dot" syntax.
442    pub fn view_crate_graph(&self, full: bool) -> Cancellable<String> {
443        self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
444    }
445
446    pub fn fetch_crates(&self) -> Cancellable<FxIndexSet<CrateInfo>> {
447        self.with_db(fetch_crates::fetch_crates)
448    }
449
450    pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
451        self.with_db(|db| expand_macro::expand_macro(db, position))
452    }
453
454    /// Returns an edit to remove all newlines in the range, cleaning up minor
455    /// stuff like trailing commas.
456    pub fn join_lines(&self, config: &JoinLinesConfig, frange: FileRange) -> Cancellable<TextEdit> {
457        self.with_db(|db| {
458            let editioned_file_id_wrapper =
459                EditionedFileId::current_edition(&self.db, frange.file_id);
460            let parse = editioned_file_id_wrapper.parse(db);
461            join_lines::join_lines(config, &parse.tree(), frange.range)
462        })
463    }
464
465    /// Returns an edit which should be applied when opening a new line, fixing
466    /// up minor stuff like continuing the comment.
467    /// The edit will be a snippet (with `$0`).
468    pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> {
469        self.with_db(|db| typing::on_enter(db, position))
470    }
471
472    pub const SUPPORTED_TRIGGER_CHARS: &[char] = typing::TRIGGER_CHARS;
473
474    /// Returns an edit which should be applied after a character was typed.
475    ///
476    /// This is useful for some on-the-fly fixups, like adding `;` to `let =`
477    /// automatically.
478    pub fn on_char_typed(
479        &self,
480        position: FilePosition,
481        char_typed: char,
482    ) -> Cancellable<Option<SourceChange>> {
483        // Fast path to not even parse the file.
484        if !typing::TRIGGER_CHARS.contains(&char_typed) {
485            return Ok(None);
486        }
487
488        self.with_db(|db| typing::on_char_typed(db, position, char_typed))
489    }
490
491    /// Returns a tree representation of symbols in the file. Useful to draw a
492    /// file outline.
493    pub fn file_structure(
494        &self,
495        config: &FileStructureConfig,
496        file_id: FileId,
497    ) -> Cancellable<Vec<StructureNode>> {
498        // FIXME: Edition
499        self.with_db(|db| {
500            let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
501            let source_file = editioned_file_id_wrapper.parse(db).tree();
502            file_structure::file_structure(&source_file, config)
503        })
504    }
505
506    /// Returns a list of the places in the file where type hints can be displayed.
507    pub fn inlay_hints(
508        &self,
509        config: &InlayHintsConfig<'_>,
510        file_id: FileId,
511        range: Option<TextRange>,
512    ) -> Cancellable<Vec<InlayHint>> {
513        self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config))
514    }
515    pub fn inlay_hints_resolve(
516        &self,
517        config: &InlayHintsConfig<'_>,
518        file_id: FileId,
519        resolve_range: TextRange,
520        hash: u64,
521        hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe,
522    ) -> Cancellable<Option<InlayHint>> {
523        self.with_db(|db| {
524            inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher)
525        })
526    }
527
528    /// Returns the set of folding ranges.
529    pub fn folding_ranges(&self, file_id: FileId, collapsed_text: bool) -> Cancellable<Vec<Fold>> {
530        self.with_db(|db| {
531            let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
532
533            folding_ranges::folding_ranges(
534                &editioned_file_id_wrapper.parse(db).tree(),
535                collapsed_text,
536            )
537        })
538    }
539
540    /// Fuzzy searches for a symbol.
541    pub fn symbol_search(&self, query: Query, limit: usize) -> Cancellable<Vec<NavigationTarget>> {
542        // `world_symbols` currently clones the database to run stuff in parallel, which will make any query panic
543        // if we were to attach it here.
544        Cancelled::catch(|| {
545            let symbols = symbol_index::world_symbols(&self.db, query);
546            hir::attach_db(&self.db, || {
547                symbols
548                    .into_iter()
549                    .filter_map(|s| s.try_to_nav(&Semantics::new(&self.db)))
550                    .take(limit)
551                    .map(UpmappingResult::call_site)
552                    .collect::<Vec<_>>()
553            })
554        })
555    }
556
557    /// Returns the definitions from the symbol at `position`.
558    pub fn goto_definition(
559        &self,
560        position: FilePosition,
561        config: &GotoDefinitionConfig<'_>,
562    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
563        self.with_db(|db| goto_definition::goto_definition(db, position, config))
564    }
565
566    /// Returns the declaration from the symbol at `position`.
567    pub fn goto_declaration(
568        &self,
569        position: FilePosition,
570        config: &GotoDefinitionConfig<'_>,
571    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
572        self.with_db(|db| goto_declaration::goto_declaration(db, position, config))
573    }
574
575    /// Returns the impls from the symbol at `position`.
576    pub fn goto_implementation(
577        &self,
578        config: &GotoImplementationConfig,
579        position: FilePosition,
580    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
581        self.with_db(|db| goto_implementation::goto_implementation(db, config, position))
582    }
583
584    /// Returns the type definitions for the symbol at `position`.
585    pub fn goto_type_definition(
586        &self,
587        position: FilePosition,
588    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
589        self.with_db(|db| goto_type_definition::goto_type_definition(db, position))
590    }
591
592    pub fn find_all_refs(
593        &self,
594        position: FilePosition,
595        config: &FindAllRefsConfig<'_>,
596    ) -> Cancellable<Option<Vec<ReferenceSearchResult>>> {
597        let config = AssertUnwindSafe(config);
598        self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, &config))
599    }
600
601    /// Returns a short text describing element at position.
602    pub fn hover(
603        &self,
604        config: &HoverConfig<'_>,
605        range: FileRange,
606    ) -> Cancellable<Option<RangeInfo<HoverResult>>> {
607        self.with_db(|db| hover::hover(db, range, config))
608    }
609
610    /// Returns moniker of symbol at position.
611    pub fn moniker(
612        &self,
613        position: FilePosition,
614    ) -> Cancellable<Option<RangeInfo<Vec<moniker::MonikerResult>>>> {
615        self.with_db(|db| moniker::moniker(db, position))
616    }
617
618    /// Returns URL(s) for the documentation of the symbol under the cursor.
619    /// # Arguments
620    /// * `position` - Position in the file.
621    /// * `target_dir` - Directory where the build output is stored.
622    pub fn external_docs(
623        &self,
624        position: FilePosition,
625        target_dir: Option<&str>,
626        sysroot: Option<&str>,
627    ) -> Cancellable<doc_links::DocumentationLinks> {
628        self.with_db(|db| {
629            doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default()
630        })
631    }
632
633    /// Computes parameter information at the given position.
634    pub fn signature_help(&self, position: FilePosition) -> Cancellable<Option<SignatureHelp>> {
635        self.with_db(|db| signature_help::signature_help(db, position))
636    }
637
638    /// Computes call hierarchy candidates for the given file position.
639    pub fn call_hierarchy(
640        &self,
641        position: FilePosition,
642        config: &CallHierarchyConfig<'_>,
643    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
644        self.with_db(|db| call_hierarchy::call_hierarchy(db, position, config))
645    }
646
647    /// Computes incoming calls for the given file position.
648    pub fn incoming_calls(
649        &self,
650        config: &CallHierarchyConfig<'_>,
651        position: FilePosition,
652    ) -> Cancellable<Option<Vec<CallItem>>> {
653        self.with_db(|db| call_hierarchy::incoming_calls(db, config, position))
654    }
655
656    /// Computes outgoing calls for the given file position.
657    pub fn outgoing_calls(
658        &self,
659        config: &CallHierarchyConfig<'_>,
660        position: FilePosition,
661    ) -> Cancellable<Option<Vec<CallItem>>> {
662        self.with_db(|db| call_hierarchy::outgoing_calls(db, config, position))
663    }
664
665    /// Returns a `mod name;` declaration which created the current module.
666    pub fn parent_module(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> {
667        self.with_db(|db| parent_module::parent_module(db, position))
668    }
669
670    /// Returns vec of `mod name;` declaration which are created by the current module.
671    pub fn child_modules(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> {
672        self.with_db(|db| child_modules::child_modules(db, position))
673    }
674
675    /// Returns crates that this file belongs to.
676    pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> {
677        self.with_db(|db| parent_module::crates_for(db, file_id))
678    }
679
680    /// Returns crates that this file belongs to.
681    pub fn transitive_rev_deps(&self, crate_id: Crate) -> Cancellable<Vec<Crate>> {
682        self.with_db(|db| Vec::from_iter(crate_id.transitive_rev_deps(db)))
683    }
684
685    /// Returns crates that this file *might* belong to.
686    pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> {
687        self.with_db(|db| relevant_crates(db, file_id).to_vec())
688    }
689
690    /// Returns the edition of the given crate.
691    pub fn crate_edition(&self, crate_id: Crate) -> Cancellable<Edition> {
692        self.with_db(|db| crate_id.data(db).edition)
693    }
694
695    /// Returns whether the given crate is a proc macro.
696    pub fn is_proc_macro_crate(&self, crate_id: Crate) -> Cancellable<bool> {
697        self.with_db(|db| crate_id.data(db).is_proc_macro)
698    }
699
700    /// Returns true if this crate has `no_std` or `no_core` specified.
701    pub fn is_crate_no_std(&self, crate_id: Crate) -> Cancellable<bool> {
702        self.with_db(|db| crate_def_map(db, crate_id).is_no_std())
703    }
704
705    /// Returns the root file of the given crate.
706    pub fn crate_root(&self, crate_id: Crate) -> Cancellable<FileId> {
707        self.with_db(|db| crate_id.data(db).root_file_id)
708    }
709
710    /// Returns the set of possible targets to run for the current file.
711    pub fn runnables(&self, file_id: FileId) -> Cancellable<Vec<Runnable>> {
712        self.with_db(|db| runnables::runnables(db, file_id))
713    }
714
715    /// Returns the set of tests for the given file position.
716    pub fn related_tests(
717        &self,
718        position: FilePosition,
719        search_scope: Option<SearchScope>,
720    ) -> Cancellable<Vec<Runnable>> {
721        let search_scope = AssertUnwindSafe(search_scope);
722        self.with_db(|db| {
723            let _ = &search_scope;
724            runnables::related_tests(db, position, search_scope.0)
725        })
726    }
727
728    /// Computes all ranges to highlight for a given item in a file.
729    pub fn highlight_related(
730        &self,
731        config: HighlightRelatedConfig,
732        position: FilePosition,
733    ) -> Cancellable<Option<Vec<HighlightedRange>>> {
734        self.with_db(|db| {
735            highlight_related::highlight_related(&Semantics::new(db), config, position)
736        })
737    }
738
739    /// Computes syntax highlighting for the given file
740    pub fn highlight(
741        &self,
742        highlight_config: HighlightConfig<'_>,
743        file_id: FileId,
744    ) -> Cancellable<Vec<HlRange>> {
745        self.with_db(|db| syntax_highlighting::highlight(db, &highlight_config, file_id, None))
746    }
747
748    /// Computes syntax highlighting for the given file range.
749    pub fn highlight_range(
750        &self,
751        highlight_config: HighlightConfig<'_>,
752        frange: FileRange,
753    ) -> Cancellable<Vec<HlRange>> {
754        self.with_db(|db| {
755            syntax_highlighting::highlight(
756                db,
757                &highlight_config,
758                frange.file_id,
759                Some(frange.range),
760            )
761        })
762    }
763
764    /// Computes syntax highlighting for the given file.
765    pub fn highlight_as_html_with_config(
766        &self,
767        config: HighlightConfig<'_>,
768        file_id: FileId,
769        rainbow: bool,
770    ) -> Cancellable<String> {
771        self.with_db(|db| {
772            syntax_highlighting::highlight_as_html_with_config(db, &config, file_id, rainbow)
773        })
774    }
775
776    /// Computes syntax highlighting for the given file.
777    pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable<String> {
778        self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow))
779    }
780
781    /// Computes completions at the given position.
782    pub fn completions(
783        &self,
784        config: &CompletionConfig<'_>,
785        position: FilePosition,
786        trigger_character: Option<char>,
787    ) -> Cancellable<Option<Vec<CompletionItem>>> {
788        self.with_db(|db| ide_completion::completions(db, config, position, trigger_character))
789    }
790
791    /// Resolves additional completion data at the position given.
792    pub fn resolve_completion_edits(
793        &self,
794        config: &CompletionConfig<'_>,
795        position: FilePosition,
796        imports: impl IntoIterator<Item = CompletionItemImport> + std::panic::UnwindSafe,
797    ) -> Cancellable<Vec<TextEdit>> {
798        Ok(self
799            .with_db(|db| ide_completion::resolve_completion_edits(db, config, position, imports))?
800            .unwrap_or_default())
801    }
802
803    /// Computes the set of parser level diagnostics for the given file.
804    pub fn syntax_diagnostics(
805        &self,
806        config: &DiagnosticsConfig,
807        file_id: FileId,
808    ) -> Cancellable<Vec<Diagnostic>> {
809        self.with_db(|db| ide_diagnostics::syntax_diagnostics(db, config, file_id))
810    }
811
812    /// Computes the set of semantic diagnostics for the given file.
813    pub fn semantic_diagnostics(
814        &self,
815        config: &DiagnosticsConfig,
816        resolve: AssistResolveStrategy,
817        file_id: FileId,
818    ) -> Cancellable<Vec<Diagnostic>> {
819        self.with_db(|db| ide_diagnostics::semantic_diagnostics(db, config, &resolve, file_id))
820    }
821
822    /// Computes the set of both syntax and semantic diagnostics for the given file.
823    pub fn full_diagnostics(
824        &self,
825        config: &DiagnosticsConfig,
826        resolve: AssistResolveStrategy,
827        file_id: FileId,
828    ) -> Cancellable<Vec<Diagnostic>> {
829        self.with_db(|db| ide_diagnostics::full_diagnostics(db, config, &resolve, file_id))
830    }
831
832    /// Convenience function to return assists + quick fixes for diagnostics
833    pub fn assists_with_fixes(
834        &self,
835        assist_config: &AssistConfig,
836        diagnostics_config: &DiagnosticsConfig,
837        resolve: AssistResolveStrategy,
838        frange: FileRange,
839    ) -> Cancellable<Vec<Assist>> {
840        let include_fixes = match &assist_config.allowed {
841            Some(it) => it.contains(&AssistKind::QuickFix),
842            None => true,
843        };
844
845        self.with_db(|db| {
846            let diagnostic_assists = if diagnostics_config.enabled && include_fixes {
847                ide_diagnostics::full_diagnostics(db, diagnostics_config, &resolve, frange.file_id)
848                    .into_iter()
849                    .flat_map(|it| it.fixes.unwrap_or_default())
850                    .filter(|it| it.target.intersect(frange.range).is_some())
851                    .collect()
852            } else {
853                Vec::new()
854            };
855            let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
856            let assists = ide_assists::assists(db, assist_config, resolve, frange);
857
858            let mut res = diagnostic_assists;
859            res.extend(ssr_assists);
860            res.extend(assists);
861
862            res
863        })
864    }
865
866    /// Returns the edit required to rename reference at the position to the new
867    /// name.
868    pub fn rename(
869        &self,
870        position: FilePosition,
871        new_name: &str,
872        config: &RenameConfig,
873    ) -> Cancellable<Result<SourceChange, RenameError>> {
874        self.with_db(|db| rename::rename(db, position, new_name, config))
875    }
876
877    pub fn prepare_rename(
878        &self,
879        position: FilePosition,
880    ) -> Cancellable<Result<RangeInfo<()>, RenameError>> {
881        self.with_db(|db| rename::prepare_rename(db, position))
882    }
883
884    pub fn will_rename_file(
885        &self,
886        file_id: FileId,
887        new_name_stem: &str,
888        config: &RenameConfig,
889    ) -> Cancellable<Option<SourceChange>> {
890        self.with_db(|db| rename::will_rename_file(db, file_id, new_name_stem, config))
891    }
892
893    pub fn structural_search_replace(
894        &self,
895        query: &str,
896        parse_only: bool,
897        resolve_context: FilePosition,
898        selections: Vec<FileRange>,
899    ) -> Cancellable<Result<SourceChange, SsrError>> {
900        self.with_db(|db| {
901            let rule: ide_ssr::SsrRule = query.parse()?;
902            let mut match_finder =
903                ide_ssr::MatchFinder::in_context(db, resolve_context, selections)?;
904            match_finder.add_rule(rule)?;
905            let edits = if parse_only { Default::default() } else { match_finder.edits() };
906            Ok(SourceChange::from_iter(edits))
907        })
908    }
909
910    pub fn annotations(
911        &self,
912        config: &AnnotationConfig<'_>,
913        file_id: FileId,
914    ) -> Cancellable<Vec<Annotation>> {
915        self.with_db(|db| annotations::annotations(db, config, file_id))
916    }
917
918    pub fn resolve_annotation(
919        &self,
920        config: &AnnotationConfig<'_>,
921        annotation: Annotation,
922    ) -> Cancellable<Annotation> {
923        self.with_db(|db| annotations::resolve_annotation(db, config, annotation))
924    }
925
926    pub fn move_item(
927        &self,
928        range: FileRange,
929        direction: Direction,
930    ) -> Cancellable<Option<TextEdit>> {
931        self.with_db(|db| move_item::move_item(db, range, direction))
932    }
933
934    pub fn get_recursive_memory_layout(
935        &self,
936        position: FilePosition,
937    ) -> Cancellable<Option<RecursiveMemoryLayout>> {
938        self.with_db(|db| view_memory_layout(db, position))
939    }
940
941    pub fn get_failed_obligations(&self, offset: TextSize, file_id: FileId) -> Cancellable<String> {
942        self.with_db(|db| {
943            let sema = Semantics::new(db);
944            let source_file = sema.parse_guess_edition(file_id);
945
946            let Some(token) = source_file.syntax().token_at_offset(offset).next() else {
947                return String::new();
948            };
949            sema.get_failed_obligations(token).unwrap_or_default()
950        })
951    }
952
953    pub fn editioned_file_id_to_vfs(&self, file_id: hir::EditionedFileId) -> FileId {
954        file_id.file_id(&self.db)
955    }
956
957    /// Performs an operation on the database that may be canceled.
958    ///
959    /// rust-analyzer needs to be able to answer semantic questions about the
960    /// code while the code is being modified. A common problem is that a
961    /// long-running query is being calculated when a new change arrives.
962    ///
963    /// We can't just apply the change immediately: this will cause the pending
964    /// query to see inconsistent state (it will observe an absence of
965    /// repeatable read). So what we do is we **cancel** all pending queries
966    /// before applying the change.
967    ///
968    /// Salsa implements cancellation by unwinding with a special value and
969    /// catching it on the API boundary.
970    fn with_db<F, T>(&self, f: F) -> Cancellable<T>
971    where
972        F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe,
973    {
974        // We use `attach_db_allow_change()` and not `attach_db()` because fixture injection can change the database.
975        hir::attach_db_allow_change(&self.db, || Cancelled::catch(|| f(&self.db)))
976    }
977}
978
979#[test]
980fn analysis_is_send() {
981    fn is_send<T: Send>() {}
982    is_send::<Analysis>();
983}