Skip to main content

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