7335: added region folding r=matklad a=LucianoBestia

Regions of code that you'd like to be folded can be wrapped with  `// #region` and `// #endregion` line comments.
This is called "Region Folding". It is originally available for many languages in VSCode. But Rust-analyzer has its own folding function and this is missing.
With this Pull Request I am suggesting a simple solution. 
The regions are a special kind of comments, so I added a bit of code in the comment folding function.
The regex to match are: `^\s*//\s*#?region\b` and `^\s*//\s*#?endregion\b`.
The number of space characters is not important. There is an optional # character. The line can end with a name of the region.
Example:
```rust
// 1. some normal comment
// region: test
// 2. some normal comment
calling_function(x,y);
// endregion: test
```
I added a test for this new functionality in `folding_ranges.rs`.
Please, take a look and comment. 
I found that these exact regexes are already present in the file `language-configuration.json`, but I don't find a way to read this configuration. So my regex is hardcoded in the code.

7691: Suggest name in extract variable r=matklad a=cpud36

Generate better default name in extract variable assist as was mentioned in issue #1587

# Currently supported
(in order of declining precedence)
1. Expr is argument to a function; use corresponding parameter name
2. Expr is result of a function or method call; use this function/method's name
3. Use expr type name (if possible)
4. Fallback to `var_name` otherwise

# Showcase

![generate_derive_variable_name_from_method](https://user-images.githubusercontent.com/4218373/108013304-72105400-701c-11eb-9f13-eec52e74d0cc.gif)
![generate_derive_variable_name_from_param](https://user-images.githubusercontent.com/4218373/108013305-72a8ea80-701c-11eb-957e-2214f7f005de.gif)

# Questions

* Should we more aggressively strip known types? E.g. we already strip `&T -> T`; should we strip `Option<T> -> T`, `Result<T, E> -> T`, and others?
* Integers and floats use `var_name` by default. Should we introduce a name, like `i`, `f` etc?
* Can we return a list and suggest a name when renaming(like IntelliJ does)?
* Should we add counters to remove duplicate variables? E.g. `type`, `type1`, type2`, etc.


Co-authored-by: Luciano Bestia <LucianoBestia@gmail.com>
Co-authored-by: Luciano <31509965+LucianoBestia@users.noreply.github.com>
Co-authored-by: Vladyslav Katasonov <cpud47@gmail.com>
This commit is contained in:
bors[bot] 2021-03-02 13:32:06 +00:00 committed by GitHub
commit 657ec3616f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 1174 additions and 16 deletions

View file

@ -6,7 +6,7 @@ use syntax::{
ast::{self, AstNode, AstToken, VisibilityOwner},
Direction, NodeOrToken, SourceFile,
SyntaxKind::{self, *},
SyntaxNode, TextRange,
SyntaxNode, TextRange, TextSize,
};
#[derive(Debug, PartialEq, Eq)]
@ -16,6 +16,7 @@ pub enum FoldKind {
Mods,
Block,
ArgList,
Region,
}
#[derive(Debug)]
@ -29,6 +30,8 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
let mut visited_comments = FxHashSet::default();
let mut visited_imports = FxHashSet::default();
let mut visited_mods = FxHashSet::default();
// regions can be nested, here is a LIFO buffer
let mut regions_starts: Vec<TextSize> = vec![];
for element in file.syntax().descendants_with_tokens() {
// Fold items that span multiple lines
@ -48,10 +51,25 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
// Fold groups of comments
if let Some(comment) = ast::Comment::cast(token) {
if !visited_comments.contains(&comment) {
if let Some(range) =
contiguous_range_for_comment(comment, &mut visited_comments)
{
res.push(Fold { range, kind: FoldKind::Comment })
// regions are not real comments
if comment.text().trim().starts_with("// region:") {
regions_starts.push(comment.syntax().text_range().start());
} else if comment.text().trim().starts_with("// endregion") {
if let Some(region) = regions_starts.pop() {
res.push(Fold {
range: TextRange::new(
region,
comment.syntax().text_range().end(),
),
kind: FoldKind::Region,
})
}
} else {
if let Some(range) =
contiguous_range_for_comment(comment, &mut visited_comments)
{
res.push(Fold { range, kind: FoldKind::Comment })
}
}
}
}
@ -175,9 +193,16 @@ fn contiguous_range_for_comment(
}
if let Some(c) = ast::Comment::cast(token) {
if c.kind() == group_kind {
visited.insert(c.clone());
last = c;
continue;
// regions are not real comments
if c.text().trim().starts_with("// region:")
|| c.text().trim().starts_with("// endregion")
{
break;
} else {
visited.insert(c.clone());
last = c;
continue;
}
}
}
// The comment group ends because either:
@ -224,6 +249,7 @@ mod tests {
FoldKind::Mods => "mods",
FoldKind::Block => "block",
FoldKind::ArgList => "arglist",
FoldKind::Region => "region",
};
assert_eq!(kind, &attr.unwrap());
}
@ -415,6 +441,19 @@ fn foo<fold arglist>(
x: i32,
y: String,
)</fold> {}
"#,
)
}
#[test]
fn fold_region() {
check(
r#"
// 1. some normal comment
<fold region>// region: test
// 2. some normal comment
calling_function(x,y);
// endregion: test</fold>
"#,
)
}