rust_analyzer/cli/
ssr.rs

1//! Applies structured search replace rules from the command line.
2
3use anyhow::Context;
4use ide_db::{EditionedFileId, base_db::SourceDatabase};
5use ide_ssr::MatchFinder;
6use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at};
7use project_model::{CargoConfig, RustLibSource};
8
9use crate::cli::flags;
10
11impl flags::Ssr {
12    pub fn run(self) -> anyhow::Result<()> {
13        let cargo_config = CargoConfig {
14            sysroot: Some(RustLibSource::Discover),
15            all_targets: true,
16            set_test: true,
17            ..Default::default()
18        };
19        let load_cargo_config = LoadCargoConfig {
20            load_out_dirs_from_check: true,
21            with_proc_macro_server: ProcMacroServerChoice::Sysroot,
22            prefill_caches: false,
23        };
24        let (ref db, vfs, _proc_macro) = load_workspace_at(
25            &std::env::current_dir()?,
26            &cargo_config,
27            &load_cargo_config,
28            &|_| {},
29        )?;
30        let mut match_finder = MatchFinder::at_first_file(db)?;
31        for rule in self.rule {
32            match_finder.add_rule(rule)?;
33        }
34        let edits = match_finder.edits();
35        for (file_id, edit) in edits {
36            if let Some(path) = vfs.file_path(file_id).as_path() {
37                let mut contents = db.file_text(file_id).text(db).to_string();
38                edit.apply(&mut contents);
39                std::fs::write(path, contents)
40                    .with_context(|| format!("failed to write {path}"))?;
41            }
42        }
43        Ok(())
44    }
45}
46
47impl flags::Search {
48    /// Searches for `patterns`, printing debug information for any nodes whose text exactly matches
49    /// `debug_snippet`. This is intended for debugging and probably isn't in it's current form useful
50    /// for much else.
51    pub fn run(self) -> anyhow::Result<()> {
52        use ide_db::base_db::SourceDatabase;
53        use ide_db::symbol_index::SymbolsDatabase;
54        let cargo_config =
55            CargoConfig { all_targets: true, set_test: true, ..CargoConfig::default() };
56        let load_cargo_config = LoadCargoConfig {
57            load_out_dirs_from_check: true,
58            with_proc_macro_server: ProcMacroServerChoice::Sysroot,
59            prefill_caches: false,
60        };
61        let (ref db, _vfs, _proc_macro) = load_workspace_at(
62            &std::env::current_dir()?,
63            &cargo_config,
64            &load_cargo_config,
65            &|_| {},
66        )?;
67        let mut match_finder = MatchFinder::at_first_file(db)?;
68        for pattern in self.pattern {
69            match_finder.add_search_pattern(pattern)?;
70        }
71        if let Some(debug_snippet) = &self.debug {
72            for &root in db.local_roots().iter() {
73                let sr = db.source_root(root).source_root(db);
74                for file_id in sr.iter() {
75                    for debug_info in match_finder.debug_where_text_equal(
76                        EditionedFileId::current_edition(db, file_id),
77                        debug_snippet,
78                    ) {
79                        println!("{debug_info:#?}");
80                    }
81                }
82            }
83        } else {
84            for m in match_finder.matches().flattened().matches {
85                // We could possibly at some point do something more useful than just printing
86                // the matched text. For now though, that's the easiest thing to do.
87                println!("{}", m.matched_text());
88            }
89        }
90        Ok(())
91    }
92}