1 //! Benchmark operations like highlighting or goto definition.
10 use anyhow::{format_err, Result};
12 salsa::{Database, Durability},
13 FileId, SourceDatabaseExt,
15 use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionConfig, FilePosition, LineCol};
17 use crate::cli::{load_cargo::load_cargo, Verbosity};
20 Highlight { path: PathBuf },
31 impl FromStr for Position {
32 type Err = anyhow::Error;
33 fn from_str(s: &str) -> Result<Self> {
34 let (path_line, column) = rsplit_at_char(s, ':')?;
35 let (path, line) = rsplit_at_char(path_line, ':')?;
36 Ok(Position { path: path.into(), line: line.parse()?, column: column.parse()? })
40 fn rsplit_at_char(s: &str, c: char) -> Result<(&str, &str)> {
41 let idx = s.rfind(c).ok_or_else(|| format_err!("no `{}` in {}", c, s))?;
42 Ok((&s[..idx], &s[idx + 1..]))
45 pub fn analysis_bench(
49 load_output_dirs: bool,
53 let start = Instant::now();
55 let (mut host, roots) = load_cargo(path, load_output_dirs)?;
56 let db = host.raw_database();
57 eprintln!("{:?}\n", start.elapsed());
60 let path = match &what {
61 BenchWhat::Highlight { path } => path,
62 BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => &pos.path,
64 let path = std::env::current_dir()?.join(path).canonicalize()?;
67 .find_map(|(source_root_id, project_root)| {
68 if project_root.is_member {
69 for file_id in db.source_root(*source_root_id).walk() {
70 let rel_path = db.file_relative_path(file_id);
71 let abs_path = rel_path.to_path(&project_root.path);
79 .ok_or_else(|| format_err!("Can't find {}", path.display()))?
83 BenchWhat::Highlight { .. } => {
84 let res = do_work(&mut host, file_id, |analysis| {
85 analysis.diagnostics(file_id).unwrap();
86 analysis.highlight_as_html(file_id, false).unwrap()
88 if verbosity.is_verbose() {
89 println!("\n{}", res);
92 BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => {
93 let is_completion = match what {
94 BenchWhat::Complete(..) => true,
100 .file_line_index(file_id)?
101 .offset(LineCol { line: pos.line - 1, col_utf16: pos.column });
102 let file_position = FilePosition { file_id, offset };
105 let options = CompletionConfig::default();
106 let res = do_work(&mut host, file_id, |analysis| {
107 analysis.completions(file_position, &options)
109 if verbosity.is_verbose() {
110 println!("\n{:#?}", res);
114 do_work(&mut host, file_id, |analysis| analysis.goto_definition(file_position));
115 if verbosity.is_verbose() {
116 println!("\n{:#?}", res);
124 fn do_work<F: Fn(&Analysis) -> T, T>(host: &mut AnalysisHost, file_id: FileId, work: F) -> T {
126 let start = Instant::now();
127 eprint!("from scratch: ");
128 work(&host.analysis());
129 eprintln!("{:?}", start.elapsed());
132 let start = Instant::now();
133 eprint!("no change: ");
134 work(&host.analysis());
135 eprintln!("{:?}", start.elapsed());
138 let start = Instant::now();
139 eprint!("trivial change: ");
140 host.raw_database_mut().salsa_runtime_mut().synthetic_write(Durability::LOW);
141 work(&host.analysis());
142 eprintln!("{:?}", start.elapsed());
145 let start = Instant::now();
146 eprint!("comment change: ");
148 let mut text = host.analysis().file_text(file_id).unwrap().to_string();
149 text.push_str("\n/* Hello world */\n");
150 let mut change = AnalysisChange::new();
151 change.change_file(file_id, Arc::new(text));
152 host.apply_change(change);
154 work(&host.analysis());
155 eprintln!("{:?}", start.elapsed());
158 let start = Instant::now();
159 eprint!("const change: ");
160 host.raw_database_mut().salsa_runtime_mut().synthetic_write(Durability::HIGH);
161 let res = work(&host.analysis());
162 eprintln!("{:?}", start.elapsed());