diff options
author | Luciano Bestia <[email protected]> | 2021-01-18 18:45:42 +0000 |
---|---|---|
committer | Luciano Bestia <[email protected]> | 2021-01-18 18:45:42 +0000 |
commit | 9f1d341ee9bf399fa8fa2a5d2fb5f91e1b319702 (patch) | |
tree | 2aef832786e9d6eaccfe7cf566113fd35421a46a /crates | |
parent | a1c72451bb8b657f4e5a015428112090402de106 (diff) |
added region folding
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ide/Cargo.toml | 4 | ||||
-rw-r--r-- | crates/ide/src/folding_ranges.rs | 76 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 2 |
3 files changed, 73 insertions, 9 deletions
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index bb28cca4d..6ec106426 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml | |||
@@ -31,6 +31,10 @@ assists = { path = "../assists", version = "0.0.0" } | |||
31 | ssr = { path = "../ssr", version = "0.0.0" } | 31 | ssr = { path = "../ssr", version = "0.0.0" } |
32 | completion = { path = "../completion", version = "0.0.0" } | 32 | completion = { path = "../completion", version = "0.0.0" } |
33 | 33 | ||
34 | lazy_static = "1.4.0" | ||
35 | regex = "1.4.3" | ||
36 | env_logger = { version = "0.8.1", default-features = false } | ||
37 | |||
34 | # ide should depend only on the top-level `hir` package. if you need | 38 | # ide should depend only on the top-level `hir` package. if you need |
35 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. | 39 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. |
36 | hir = { path = "../hir", version = "0.0.0" } | 40 | hir = { path = "../hir", version = "0.0.0" } |
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs index 45170dd29..99f0c3c99 100644 --- a/crates/ide/src/folding_ranges.rs +++ b/crates/ide/src/folding_ranges.rs | |||
@@ -6,9 +6,11 @@ use syntax::{ | |||
6 | ast::{self, AstNode, AstToken, VisibilityOwner}, | 6 | ast::{self, AstNode, AstToken, VisibilityOwner}, |
7 | Direction, NodeOrToken, SourceFile, | 7 | Direction, NodeOrToken, SourceFile, |
8 | SyntaxKind::{self, *}, | 8 | SyntaxKind::{self, *}, |
9 | SyntaxNode, TextRange, | 9 | SyntaxNode, TextRange, TextSize, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use lazy_static::lazy_static; | ||
13 | |||
12 | #[derive(Debug, PartialEq, Eq)] | 14 | #[derive(Debug, PartialEq, Eq)] |
13 | pub enum FoldKind { | 15 | pub enum FoldKind { |
14 | Comment, | 16 | Comment, |
@@ -16,6 +18,7 @@ pub enum FoldKind { | |||
16 | Mods, | 18 | Mods, |
17 | Block, | 19 | Block, |
18 | ArgList, | 20 | ArgList, |
21 | Region, | ||
19 | } | 22 | } |
20 | 23 | ||
21 | #[derive(Debug)] | 24 | #[derive(Debug)] |
@@ -29,6 +32,8 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { | |||
29 | let mut visited_comments = FxHashSet::default(); | 32 | let mut visited_comments = FxHashSet::default(); |
30 | let mut visited_imports = FxHashSet::default(); | 33 | let mut visited_imports = FxHashSet::default(); |
31 | let mut visited_mods = FxHashSet::default(); | 34 | let mut visited_mods = FxHashSet::default(); |
35 | // regions can be nested, here is a LIFO buffer | ||
36 | let mut regions_starts: Vec<TextSize> = vec![]; | ||
32 | 37 | ||
33 | for element in file.syntax().descendants_with_tokens() { | 38 | for element in file.syntax().descendants_with_tokens() { |
34 | // Fold items that span multiple lines | 39 | // Fold items that span multiple lines |
@@ -48,10 +53,32 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { | |||
48 | // Fold groups of comments | 53 | // Fold groups of comments |
49 | if let Some(comment) = ast::Comment::cast(token) { | 54 | if let Some(comment) = ast::Comment::cast(token) { |
50 | if !visited_comments.contains(&comment) { | 55 | if !visited_comments.contains(&comment) { |
51 | if let Some(range) = | 56 | // regions are not really comments |
52 | contiguous_range_for_comment(comment, &mut visited_comments) | 57 | use regex::Regex; |
53 | { | 58 | lazy_static! { |
54 | res.push(Fold { range, kind: FoldKind::Comment }) | 59 | static ref RE_START: Regex = |
60 | Regex::new(r"^\s*//\s*#?region\b").unwrap(); | ||
61 | static ref RE_END: Regex = | ||
62 | Regex::new(r"^\s*//\s*#?endregion\b").unwrap(); | ||
63 | } | ||
64 | if RE_START.is_match(comment.text()) { | ||
65 | regions_starts.push(comment.syntax().text_range().start()); | ||
66 | } else if RE_END.is_match(comment.text()) { | ||
67 | if !regions_starts.is_empty() { | ||
68 | res.push(Fold { | ||
69 | range: TextRange::new( | ||
70 | regions_starts.pop().unwrap(), | ||
71 | comment.syntax().text_range().end(), | ||
72 | ), | ||
73 | kind: FoldKind::Region, | ||
74 | }) | ||
75 | } | ||
76 | } else { | ||
77 | if let Some(range) = | ||
78 | contiguous_range_for_comment(comment, &mut visited_comments) | ||
79 | { | ||
80 | res.push(Fold { range, kind: FoldKind::Comment }) | ||
81 | } | ||
55 | } | 82 | } |
56 | } | 83 | } |
57 | } | 84 | } |
@@ -175,9 +202,21 @@ fn contiguous_range_for_comment( | |||
175 | } | 202 | } |
176 | if let Some(c) = ast::Comment::cast(token) { | 203 | if let Some(c) = ast::Comment::cast(token) { |
177 | if c.kind() == group_kind { | 204 | if c.kind() == group_kind { |
178 | visited.insert(c.clone()); | 205 | // regions are not really comments |
179 | last = c; | 206 | use regex::Regex; |
180 | continue; | 207 | lazy_static! { |
208 | static ref RE_START: Regex = | ||
209 | Regex::new(r"^\s*//\s*#?region\b").unwrap(); | ||
210 | static ref RE_END: Regex = | ||
211 | Regex::new(r"^\s*//\s*#?endregion\b").unwrap(); | ||
212 | } | ||
213 | if RE_START.is_match(c.text()) || RE_END.is_match(c.text()) { | ||
214 | break; | ||
215 | } else { | ||
216 | visited.insert(c.clone()); | ||
217 | last = c; | ||
218 | continue; | ||
219 | } | ||
181 | } | 220 | } |
182 | } | 221 | } |
183 | // The comment group ends because either: | 222 | // The comment group ends because either: |
@@ -224,6 +263,7 @@ mod tests { | |||
224 | FoldKind::Mods => "mods", | 263 | FoldKind::Mods => "mods", |
225 | FoldKind::Block => "block", | 264 | FoldKind::Block => "block", |
226 | FoldKind::ArgList => "arglist", | 265 | FoldKind::ArgList => "arglist", |
266 | FoldKind::Region => "region", | ||
227 | }; | 267 | }; |
228 | assert_eq!(kind, &attr.unwrap()); | 268 | assert_eq!(kind, &attr.unwrap()); |
229 | } | 269 | } |
@@ -418,4 +458,24 @@ fn foo<fold arglist>( | |||
418 | "#, | 458 | "#, |
419 | ) | 459 | ) |
420 | } | 460 | } |
461 | |||
462 | #[test] | ||
463 | fn fold_region() { | ||
464 | log_init_for_test_debug(); | ||
465 | // only error level log is printed on the terminal | ||
466 | log::error!("test fold_region"); | ||
467 | check( | ||
468 | r#" | ||
469 | // 1. some normal comment | ||
470 | <fold region>// region: test | ||
471 | // 2. some normal comment | ||
472 | calling_function(x,y); | ||
473 | // endregion: test</fold> | ||
474 | "#, | ||
475 | ) | ||
476 | } | ||
477 | |||
478 | fn log_init_for_test_debug() { | ||
479 | let _ = env_logger::builder().is_test(true).try_init(); | ||
480 | } | ||
421 | } | 481 | } |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 1ff2d3fea..c903ab523 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -465,7 +465,7 @@ pub(crate) fn folding_range( | |||
465 | let kind = match fold.kind { | 465 | let kind = match fold.kind { |
466 | FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), | 466 | FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), |
467 | FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), | 467 | FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), |
468 | FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None, | 468 | FoldKind::Mods | FoldKind::Block | FoldKind::ArgList | FoldKind::Region => None, |
469 | }; | 469 | }; |
470 | 470 | ||
471 | let range = range(line_index, fold.range); | 471 | let range = range(line_index, fold.range); |