diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/diagnostics.rs | 16 | ||||
-rw-r--r-- | crates/ide/src/expand_macro.rs | 43 | ||||
-rw-r--r-- | crates/ide/src/folding_ranges.rs | 17 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/highlight.rs | 14 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/tests.rs | 96 | ||||
-rw-r--r-- | crates/ide/src/typing/on_enter.rs | 20 |
6 files changed, 184 insertions, 22 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index dd42116a7..0ace80a1e 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -20,7 +20,7 @@ use itertools::Itertools; | |||
20 | use rustc_hash::FxHashSet; | 20 | use rustc_hash::FxHashSet; |
21 | use syntax::{ | 21 | use syntax::{ |
22 | ast::{self, AstNode}, | 22 | ast::{self, AstNode}, |
23 | SyntaxNode, SyntaxNodePtr, TextRange, | 23 | SyntaxNode, SyntaxNodePtr, TextRange, TextSize, |
24 | }; | 24 | }; |
25 | use text_edit::TextEdit; | 25 | use text_edit::TextEdit; |
26 | use unlinked_file::UnlinkedFile; | 26 | use unlinked_file::UnlinkedFile; |
@@ -159,14 +159,16 @@ pub(crate) fn diagnostics( | |||
159 | ); | 159 | ); |
160 | }) | 160 | }) |
161 | .on::<UnlinkedFile, _>(|d| { | 161 | .on::<UnlinkedFile, _>(|d| { |
162 | // Limit diagnostic to the first few characters in the file. This matches how VS Code | ||
163 | // renders it with the full span, but on other editors, and is less invasive. | ||
164 | let range = sema.diagnostics_display_range(d.display_source()).range; | ||
165 | let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range); | ||
166 | |||
162 | // Override severity and mark as unused. | 167 | // Override severity and mark as unused. |
163 | res.borrow_mut().push( | 168 | res.borrow_mut().push( |
164 | Diagnostic::hint( | 169 | Diagnostic::hint(range, d.message()) |
165 | sema.diagnostics_display_range(d.display_source()).range, | 170 | .with_fix(d.fix(&sema)) |
166 | d.message(), | 171 | .with_code(Some(d.code())), |
167 | ) | ||
168 | .with_fix(d.fix(&sema)) | ||
169 | .with_code(Some(d.code())), | ||
170 | ); | 172 | ); |
171 | }) | 173 | }) |
172 | .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| { | 174 | .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| { |
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index d5628e3df..be0ee03bf 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | use std::iter; | ||
2 | |||
1 | use hir::Semantics; | 3 | use hir::Semantics; |
2 | use ide_db::RootDatabase; | 4 | use ide_db::RootDatabase; |
3 | use syntax::{ | 5 | use syntax::{ |
@@ -91,27 +93,42 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { | |||
91 | let is_last = | 93 | let is_last = |
92 | |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) }; | 94 | |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) }; |
93 | 95 | ||
94 | res += &match token.kind() { | 96 | match token.kind() { |
95 | k if is_text(k) && is_next(|it| !it.is_punct(), true) => token.text().to_string() + " ", | 97 | k if is_text(k) && is_next(|it| !it.is_punct(), true) => { |
98 | res.push_str(token.text()); | ||
99 | res.push(' '); | ||
100 | } | ||
96 | L_CURLY if is_next(|it| it != R_CURLY, true) => { | 101 | L_CURLY if is_next(|it| it != R_CURLY, true) => { |
97 | indent += 1; | 102 | indent += 1; |
98 | let leading_space = if is_last(is_text, false) { " " } else { "" }; | 103 | if is_last(is_text, false) { |
99 | format!("{}{{\n{}", leading_space, " ".repeat(indent)) | 104 | res.push(' '); |
105 | } | ||
106 | res.push_str("{\n"); | ||
107 | res.extend(iter::repeat(" ").take(2 * indent)); | ||
100 | } | 108 | } |
101 | R_CURLY if is_last(|it| it != L_CURLY, true) => { | 109 | R_CURLY if is_last(|it| it != L_CURLY, true) => { |
102 | indent = indent.saturating_sub(1); | 110 | indent = indent.saturating_sub(1); |
103 | format!("\n{}}}", " ".repeat(indent)) | 111 | res.push('\n'); |
112 | res.extend(iter::repeat(" ").take(2 * indent)); | ||
113 | res.push_str("}"); | ||
114 | } | ||
115 | R_CURLY => { | ||
116 | res.push_str("}\n"); | ||
117 | res.extend(iter::repeat(" ").take(2 * indent)); | ||
104 | } | 118 | } |
105 | R_CURLY => format!("}}\n{}", " ".repeat(indent)), | ||
106 | LIFETIME_IDENT if is_next(|it| it == IDENT, true) => { | 119 | LIFETIME_IDENT if is_next(|it| it == IDENT, true) => { |
107 | format!("{} ", token.text().to_string()) | 120 | res.push_str(token.text()); |
121 | res.push(' '); | ||
108 | } | 122 | } |
109 | T![;] => format!(";\n{}", " ".repeat(indent)), | 123 | T![;] => { |
110 | T![->] => " -> ".to_string(), | 124 | res.push_str(";\n"); |
111 | T![=] => " = ".to_string(), | 125 | res.extend(iter::repeat(" ").take(2 * indent)); |
112 | T![=>] => " => ".to_string(), | 126 | } |
113 | _ => token.text().to_string(), | 127 | T![->] => res.push_str(" -> "), |
114 | }; | 128 | T![=] => res.push_str(" = "), |
129 | T![=>] => res.push_str(" => "), | ||
130 | _ => res.push_str(token.text()), | ||
131 | } | ||
115 | 132 | ||
116 | last = Some(token.kind()); | 133 | last = Some(token.kind()); |
117 | } | 134 | } |
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs index 153726ce8..2b9ed123c 100644 --- a/crates/ide/src/folding_ranges.rs +++ b/crates/ide/src/folding_ranges.rs | |||
@@ -19,6 +19,7 @@ pub enum FoldKind { | |||
19 | Region, | 19 | Region, |
20 | Consts, | 20 | Consts, |
21 | Statics, | 21 | Statics, |
22 | Array, | ||
22 | } | 23 | } |
23 | 24 | ||
24 | #[derive(Debug)] | 25 | #[derive(Debug)] |
@@ -119,6 +120,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { | |||
119 | match kind { | 120 | match kind { |
120 | COMMENT => Some(FoldKind::Comment), | 121 | COMMENT => Some(FoldKind::Comment), |
121 | ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), | 122 | ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), |
123 | ARRAY_EXPR => Some(FoldKind::Array), | ||
122 | ASSOC_ITEM_LIST | 124 | ASSOC_ITEM_LIST |
123 | | RECORD_FIELD_LIST | 125 | | RECORD_FIELD_LIST |
124 | | RECORD_PAT_FIELD_LIST | 126 | | RECORD_PAT_FIELD_LIST |
@@ -269,6 +271,7 @@ mod tests { | |||
269 | FoldKind::Region => "region", | 271 | FoldKind::Region => "region", |
270 | FoldKind::Consts => "consts", | 272 | FoldKind::Consts => "consts", |
271 | FoldKind::Statics => "statics", | 273 | FoldKind::Statics => "statics", |
274 | FoldKind::Array => "array", | ||
272 | }; | 275 | }; |
273 | assert_eq!(kind, &attr.unwrap()); | 276 | assert_eq!(kind, &attr.unwrap()); |
274 | } | 277 | } |
@@ -465,6 +468,20 @@ fn foo<fold arglist>( | |||
465 | } | 468 | } |
466 | 469 | ||
467 | #[test] | 470 | #[test] |
471 | fn fold_multiline_array() { | ||
472 | check( | ||
473 | r#" | ||
474 | const FOO: [usize; 4] = <fold array>[ | ||
475 | 1, | ||
476 | 2, | ||
477 | 3, | ||
478 | 4, | ||
479 | ]</fold>; | ||
480 | "#, | ||
481 | ) | ||
482 | } | ||
483 | |||
484 | #[test] | ||
468 | fn fold_region() { | 485 | fn fold_region() { |
469 | check( | 486 | check( |
470 | r#" | 487 | r#" |
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 5ccb84714..e921784bf 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs | |||
@@ -323,8 +323,18 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | |||
323 | hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait), | 323 | hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait), |
324 | hir::ModuleDef::TypeAlias(type_) => { | 324 | hir::ModuleDef::TypeAlias(type_) => { |
325 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); | 325 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); |
326 | if type_.as_assoc_item(db).is_some() { | 326 | if let Some(item) = type_.as_assoc_item(db) { |
327 | h |= HlMod::Associated | 327 | h |= HlMod::Associated; |
328 | match item.container(db) { | ||
329 | AssocItemContainer::Impl(i) => { | ||
330 | if i.trait_(db).is_some() { | ||
331 | h |= HlMod::Trait; | ||
332 | } | ||
333 | } | ||
334 | AssocItemContainer::Trait(_t) => { | ||
335 | h |= HlMod::Trait; | ||
336 | } | ||
337 | } | ||
328 | } | 338 | } |
329 | return h; | 339 | return h; |
330 | } | 340 | } |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 1b02857ec..de2d22ac7 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -1,5 +1,8 @@ | |||
1 | use std::time::Instant; | ||
2 | |||
1 | use expect_test::{expect_file, ExpectFile}; | 3 | use expect_test::{expect_file, ExpectFile}; |
2 | use ide_db::SymbolKind; | 4 | use ide_db::SymbolKind; |
5 | use stdx::format_to; | ||
3 | use test_utils::{bench, bench_fixture, skip_slow_tests}; | 6 | use test_utils::{bench, bench_fixture, skip_slow_tests}; |
4 | 7 | ||
5 | use crate::{fixture, FileRange, HlTag, TextRange}; | 8 | use crate::{fixture, FileRange, HlTag, TextRange}; |
@@ -258,6 +261,99 @@ fn benchmark_syntax_highlighting_long_struct() { | |||
258 | } | 261 | } |
259 | 262 | ||
260 | #[test] | 263 | #[test] |
264 | fn syntax_highlighting_not_quadratic() { | ||
265 | if skip_slow_tests() { | ||
266 | return; | ||
267 | } | ||
268 | |||
269 | let mut measures = Vec::new(); | ||
270 | for i in 6..=10 { | ||
271 | let n = 1 << i; | ||
272 | let fixture = bench_fixture::big_struct_n(n); | ||
273 | let (analysis, file_id) = fixture::file(&fixture); | ||
274 | |||
275 | let time = Instant::now(); | ||
276 | |||
277 | let hash = analysis | ||
278 | .highlight(file_id) | ||
279 | .unwrap() | ||
280 | .iter() | ||
281 | .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct)) | ||
282 | .count(); | ||
283 | assert!(hash > n as usize); | ||
284 | |||
285 | let elapsed = time.elapsed(); | ||
286 | measures.push((n as f64, elapsed.as_millis() as f64)) | ||
287 | } | ||
288 | |||
289 | assert_linear(&measures) | ||
290 | } | ||
291 | |||
292 | /// Checks that a set of measurements looks like a liner function rather than | ||
293 | /// like a quadratic function. Algorithm: | ||
294 | /// | ||
295 | /// 1. Linearly scale input to be in [0; 1) | ||
296 | /// 2. Using linear regression, compute the best linear function approximating | ||
297 | /// the input. | ||
298 | /// 3. Compute RMSE and maximal absolute error. | ||
299 | /// 4. Check that errors are within tolerances and that the constant term is not | ||
300 | /// too negative. | ||
301 | /// | ||
302 | /// Ideally, we should use a proper "model selection" to directly compare | ||
303 | /// quadratic and linear models, but that sounds rather complicated: | ||
304 | /// | ||
305 | /// https://stats.stackexchange.com/questions/21844/selecting-best-model-based-on-linear-quadratic-and-cubic-fit-of-data | ||
306 | fn assert_linear(xy: &[(f64, f64)]) { | ||
307 | let (mut xs, mut ys): (Vec<_>, Vec<_>) = xy.iter().copied().unzip(); | ||
308 | normalize(&mut xs); | ||
309 | normalize(&mut ys); | ||
310 | let xy = xs.iter().copied().zip(ys.iter().copied()); | ||
311 | |||
312 | // Linear regression: finding a and b to fit y = a + b*x. | ||
313 | |||
314 | let mean_x = mean(&xs); | ||
315 | let mean_y = mean(&ys); | ||
316 | |||
317 | let b = { | ||
318 | let mut num = 0.0; | ||
319 | let mut denom = 0.0; | ||
320 | for (x, y) in xy.clone() { | ||
321 | num += (x - mean_x) * (y - mean_y); | ||
322 | denom += (x - mean_x).powi(2); | ||
323 | } | ||
324 | num / denom | ||
325 | }; | ||
326 | |||
327 | let a = mean_y - b * mean_x; | ||
328 | |||
329 | let mut plot = format!("y_pred = {:.3} + {:.3} * x\n\nx y y_pred\n", a, b); | ||
330 | |||
331 | let mut se = 0.0; | ||
332 | let mut max_error = 0.0f64; | ||
333 | for (x, y) in xy { | ||
334 | let y_pred = a + b * x; | ||
335 | se += (y - y_pred).powi(2); | ||
336 | max_error = max_error.max((y_pred - y).abs()); | ||
337 | |||
338 | format_to!(plot, "{:.3} {:.3} {:.3}\n", x, y, y_pred); | ||
339 | } | ||
340 | |||
341 | let rmse = (se / xs.len() as f64).sqrt(); | ||
342 | format_to!(plot, "\nrmse = {:.3} max error = {:.3}", rmse, max_error); | ||
343 | |||
344 | assert!(rmse < 0.05 && max_error < 0.1 && a > -0.1, "\nLooks quadratic\n{}", plot); | ||
345 | |||
346 | fn normalize(xs: &mut Vec<f64>) { | ||
347 | let max = xs.iter().copied().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap(); | ||
348 | xs.iter_mut().for_each(|it| *it /= max); | ||
349 | } | ||
350 | |||
351 | fn mean(xs: &[f64]) -> f64 { | ||
352 | xs.iter().copied().sum::<f64>() / (xs.len() as f64) | ||
353 | } | ||
354 | } | ||
355 | |||
356 | #[test] | ||
261 | fn benchmark_syntax_highlighting_parser() { | 357 | fn benchmark_syntax_highlighting_parser() { |
262 | if skip_slow_tests() { | 358 | if skip_slow_tests() { |
263 | return; | 359 | return; |
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index 9144681bf..6f1ce3689 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs | |||
@@ -18,6 +18,7 @@ use text_edit::TextEdit; | |||
18 | // | 18 | // |
19 | // - kbd:[Enter] inside triple-slash comments automatically inserts `///` | 19 | // - kbd:[Enter] inside triple-slash comments automatically inserts `///` |
20 | // - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//` | 20 | // - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//` |
21 | // - kbd:[Enter] inside `//!` doc comments automatically inserts `//!` | ||
21 | // | 22 | // |
22 | // This action needs to be assigned to shortcut explicitly. | 23 | // This action needs to be assigned to shortcut explicitly. |
23 | // | 24 | // |
@@ -187,6 +188,25 @@ fn foo() { | |||
187 | } | 188 | } |
188 | 189 | ||
189 | #[test] | 190 | #[test] |
191 | fn continues_another_doc_comment() { | ||
192 | do_check( | ||
193 | r#" | ||
194 | fn main() { | ||
195 | //! Documentation for$0 on enter | ||
196 | let x = 1 + 1; | ||
197 | } | ||
198 | "#, | ||
199 | r#" | ||
200 | fn main() { | ||
201 | //! Documentation for | ||
202 | //! $0 on enter | ||
203 | let x = 1 + 1; | ||
204 | } | ||
205 | "#, | ||
206 | ); | ||
207 | } | ||
208 | |||
209 | #[test] | ||
190 | fn continues_code_comment_in_the_middle_of_line() { | 210 | fn continues_code_comment_in_the_middle_of_line() { |
191 | do_check( | 211 | do_check( |
192 | r" | 212 | r" |