aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuciano Bestia <[email protected]>2021-01-18 18:45:42 +0000
committerLuciano Bestia <[email protected]>2021-01-18 18:45:42 +0000
commit9f1d341ee9bf399fa8fa2a5d2fb5f91e1b319702 (patch)
tree2aef832786e9d6eaccfe7cf566113fd35421a46a
parenta1c72451bb8b657f4e5a015428112090402de106 (diff)
added region folding
-rw-r--r--Cargo.lock23
-rw-r--r--crates/ide/Cargo.toml4
-rw-r--r--crates/ide/src/folding_ranges.rs76
-rw-r--r--crates/rust-analyzer/src/to_proto.rs2
4 files changed, 92 insertions, 13 deletions
diff --git a/Cargo.lock b/Cargo.lock
index aac473191..674c75450 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -16,6 +16,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
16checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" 16checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
17 17
18[[package]] 18[[package]]
19name = "aho-corasick"
20version = "0.7.15"
21source = "registry+https://github.com/rust-lang/crates.io-index"
22checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
23dependencies = [
24 "memchr",
25]
26
27[[package]]
19name = "ansi_term" 28name = "ansi_term"
20version = "0.12.1" 29version = "0.12.1"
21source = "registry+https://github.com/rust-lang/crates.io-index" 30source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -634,16 +643,19 @@ dependencies = [
634 "cfg", 643 "cfg",
635 "completion", 644 "completion",
636 "either", 645 "either",
646 "env_logger",
637 "expect-test", 647 "expect-test",
638 "hir", 648 "hir",
639 "ide_db", 649 "ide_db",
640 "indexmap", 650 "indexmap",
641 "itertools 0.10.0", 651 "itertools 0.10.0",
652 "lazy_static",
642 "log", 653 "log",
643 "oorandom", 654 "oorandom",
644 "profile", 655 "profile",
645 "pulldown-cmark", 656 "pulldown-cmark",
646 "pulldown-cmark-to-cmark", 657 "pulldown-cmark-to-cmark",
658 "regex",
647 "rustc-hash", 659 "rustc-hash",
648 "ssr", 660 "ssr",
649 "stdx", 661 "stdx",
@@ -1301,11 +1313,14 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
1301 1313
1302[[package]] 1314[[package]]
1303name = "regex" 1315name = "regex"
1304version = "1.4.2" 1316version = "1.4.3"
1305source = "registry+https://github.com/rust-lang/crates.io-index" 1317source = "registry+https://github.com/rust-lang/crates.io-index"
1306checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" 1318checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
1307dependencies = [ 1319dependencies = [
1320 "aho-corasick",
1321 "memchr",
1308 "regex-syntax", 1322 "regex-syntax",
1323 "thread_local",
1309] 1324]
1310 1325
1311[[package]] 1326[[package]]
@@ -1320,9 +1335,9 @@ dependencies = [
1320 1335
1321[[package]] 1336[[package]]
1322name = "regex-syntax" 1337name = "regex-syntax"
1323version = "0.6.21" 1338version = "0.6.22"
1324source = "registry+https://github.com/rust-lang/crates.io-index" 1339source = "registry+https://github.com/rust-lang/crates.io-index"
1325checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" 1340checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
1326 1341
1327[[package]] 1342[[package]]
1328name = "rowan" 1343name = "rowan"
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" }
31ssr = { path = "../ssr", version = "0.0.0" } 31ssr = { path = "../ssr", version = "0.0.0" }
32completion = { path = "../completion", version = "0.0.0" } 32completion = { path = "../completion", version = "0.0.0" }
33 33
34lazy_static = "1.4.0"
35regex = "1.4.3"
36env_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`.
36hir = { path = "../hir", version = "0.0.0" } 40hir = { 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
12use lazy_static::lazy_static;
13
12#[derive(Debug, PartialEq, Eq)] 14#[derive(Debug, PartialEq, Eq)]
13pub enum FoldKind { 15pub 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
472calling_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);