Skip to main content

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