]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/src/cli/analysis_bench.rs
Align config's API with usage
[rust.git] / crates / rust-analyzer / src / cli / analysis_bench.rs
1 //! Benchmark operations like highlighting or goto definition.
2
3 use std::{env, path::PathBuf, str::FromStr, sync::Arc, time::Instant};
4
5 use anyhow::{bail, format_err, Result};
6 use ide::{
7     Analysis, AnalysisHost, Change, CompletionConfig, DiagnosticsConfig, FilePosition, LineCol,
8 };
9 use ide_db::{
10     base_db::{
11         salsa::{Database, Durability},
12         FileId,
13     },
14     helpers::SnippetCap,
15 };
16 use vfs::AbsPathBuf;
17
18 use crate::cli::{load_cargo::load_cargo, print_memory_usage, Verbosity};
19
20 pub struct BenchCmd {
21     pub path: PathBuf,
22     pub what: BenchWhat,
23     pub memory_usage: bool,
24     pub load_output_dirs: bool,
25     pub with_proc_macro: bool,
26 }
27
28 pub enum BenchWhat {
29     Highlight { path: AbsPathBuf },
30     Complete(Position),
31     GotoDef(Position),
32 }
33
34 pub struct Position {
35     pub path: AbsPathBuf,
36     pub line: u32,
37     pub column: u32,
38 }
39
40 impl FromStr for Position {
41     type Err = anyhow::Error;
42     fn from_str(s: &str) -> Result<Self> {
43         let mut split = s.rsplitn(3, ':');
44         match (split.next(), split.next(), split.next()) {
45             (Some(column), Some(line), Some(path)) => {
46                 let path = env::current_dir().unwrap().join(path);
47                 let path = AbsPathBuf::assert(path);
48                 Ok(Position { path, line: line.parse()?, column: column.parse()? })
49             }
50             _ => bail!("position should be in file:line:column format: {:?}", s),
51         }
52     }
53 }
54
55 impl BenchCmd {
56     pub fn run(self, verbosity: Verbosity) -> Result<()> {
57         profile::init();
58
59         let start = Instant::now();
60         eprint!("loading: ");
61         let (mut host, vfs) = load_cargo(&self.path, self.load_output_dirs, self.with_proc_macro)?;
62         eprintln!("{:?}\n", start.elapsed());
63
64         let file_id = {
65             let path = match &self.what {
66                 BenchWhat::Highlight { path } => path,
67                 BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => &pos.path,
68             };
69             let path = path.clone().into();
70             vfs.file_id(&path).ok_or_else(|| format_err!("Can't find {}", path))?
71         };
72
73         match &self.what {
74             BenchWhat::Highlight { .. } => {
75                 let res = do_work(&mut host, file_id, |analysis| {
76                     analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap();
77                     analysis.highlight_as_html(file_id, false).unwrap()
78                 });
79                 if verbosity.is_verbose() {
80                     println!("\n{}", res);
81                 }
82             }
83             BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => {
84                 let is_completion = matches!(self.what, BenchWhat::Complete(..));
85
86                 let offset = host
87                     .analysis()
88                     .file_line_index(file_id)?
89                     .offset(LineCol { line: pos.line - 1, col_utf16: pos.column });
90                 let file_position = FilePosition { file_id, offset };
91
92                 if is_completion {
93                     let options = CompletionConfig {
94                         enable_postfix_completions: true,
95                         enable_autoimport_completions: true,
96                         add_call_parenthesis: true,
97                         add_call_argument_snippets: true,
98                         snippet_cap: SnippetCap::new(true),
99                         merge: None,
100                     };
101                     let res = do_work(&mut host, file_id, |analysis| {
102                         analysis.completions(&options, file_position)
103                     });
104                     if verbosity.is_verbose() {
105                         println!("\n{:#?}", res);
106                     }
107                 } else {
108                     let res = do_work(&mut host, file_id, |analysis| {
109                         analysis.goto_definition(file_position)
110                     });
111                     if verbosity.is_verbose() {
112                         println!("\n{:#?}", res);
113                     }
114                 }
115             }
116         }
117
118         if self.memory_usage {
119             print_memory_usage(host, vfs);
120         }
121
122         Ok(())
123     }
124 }
125
126 fn do_work<F: Fn(&Analysis) -> T, T>(host: &mut AnalysisHost, file_id: FileId, work: F) -> T {
127     {
128         let start = Instant::now();
129         eprint!("from scratch:   ");
130         work(&host.analysis());
131         eprintln!("{:?}", start.elapsed());
132     }
133     {
134         let start = Instant::now();
135         eprint!("no change:      ");
136         work(&host.analysis());
137         eprintln!("{:?}", start.elapsed());
138     }
139     {
140         let start = Instant::now();
141         eprint!("trivial change: ");
142         host.raw_database_mut().salsa_runtime_mut().synthetic_write(Durability::LOW);
143         work(&host.analysis());
144         eprintln!("{:?}", start.elapsed());
145     }
146     {
147         let start = Instant::now();
148         eprint!("comment change: ");
149         {
150             let mut text = host.analysis().file_text(file_id).unwrap().to_string();
151             text.push_str("\n/* Hello world */\n");
152             let mut change = Change::new();
153             change.change_file(file_id, Some(Arc::new(text)));
154             host.apply_change(change);
155         }
156         work(&host.analysis());
157         eprintln!("{:?}", start.elapsed());
158     }
159     {
160         let start = Instant::now();
161         eprint!("item change:    ");
162         {
163             let mut text = host.analysis().file_text(file_id).unwrap().to_string();
164             text.push_str("\npub fn _dummy() {}\n");
165             let mut change = Change::new();
166             change.change_file(file_id, Some(Arc::new(text)));
167             host.apply_change(change);
168         }
169         work(&host.analysis());
170         eprintln!("{:?}", start.elapsed());
171     }
172     {
173         let start = Instant::now();
174         eprint!("const change:   ");
175         host.raw_database_mut().salsa_runtime_mut().synthetic_write(Durability::HIGH);
176         let res = work(&host.analysis());
177         eprintln!("{:?}", start.elapsed());
178         res
179     }
180 }