From 8295dc42a0fc9e8641606f75a5ba2a46fe48379c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 1 Jul 2020 18:17:08 +0200 Subject: Fold multiline calls --- crates/ra_ide/src/folding_ranges.rs | 161 ++++++++++++++++++----------------- crates/rust-analyzer/src/to_proto.rs | 31 +++---- crates/test_utils/src/lib.rs | 27 ++++-- 3 files changed, 114 insertions(+), 105 deletions(-) diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs index 8657377de..5cec689f8 100644 --- a/crates/ra_ide/src/folding_ranges.rs +++ b/crates/ra_ide/src/folding_ranges.rs @@ -15,6 +15,7 @@ pub enum FoldKind { Imports, Mods, Block, + ArgList, } #[derive(Debug)] @@ -83,6 +84,7 @@ fn fold_kind(kind: SyntaxKind) -> Option { match kind { COMMENT => Some(FoldKind::Comment), USE_ITEM => Some(FoldKind::Imports), + ARG_LIST => Some(FoldKind::ArgList), RECORD_FIELD_DEF_LIST | RECORD_FIELD_PAT_LIST | ITEM_LIST @@ -196,89 +198,85 @@ fn contiguous_range_for_comment( #[cfg(test)] mod tests { + use test_utils::extract_tags; + use super::*; - use test_utils::extract_ranges; - fn do_check(text: &str, fold_kinds: &[FoldKind]) { - let (ranges, text) = extract_ranges(text, "fold"); + fn check(ra_fixture: &str) { + let (ranges, text) = extract_tags(ra_fixture, "fold"); + let parse = SourceFile::parse(&text); let folds = folding_ranges(&parse.tree()); - assert_eq!( folds.len(), ranges.len(), "The amount of folds is different than the expected amount" ); - assert_eq!( - folds.len(), - fold_kinds.len(), - "The amount of fold kinds is different than the expected amount" - ); - for ((fold, range), fold_kind) in - folds.iter().zip(ranges.into_iter()).zip(fold_kinds.iter()) - { + + for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) { assert_eq!(fold.range.start(), range.start()); assert_eq!(fold.range.end(), range.end()); - assert_eq!(&fold.kind, fold_kind); + + let kind = match fold.kind { + FoldKind::Comment => "comment", + FoldKind::Imports => "imports", + FoldKind::Mods => "mods", + FoldKind::Block => "block", + FoldKind::ArgList => "arglist", + }; + assert_eq!(kind, &attr.unwrap()); } } #[test] fn test_fold_comments() { - let text = r#" -// Hello + check( + r#" +// Hello // this is a multiline // comment // // But this is not -fn main() { - // We should +fn main() { + // We should // also // fold // this one. - //! But this one is different + //! But this one is different //! because it has another flavor - /* As does this + /* As does this multiline comment */ -}"#; - - let fold_kinds = &[ - FoldKind::Comment, - FoldKind::Block, - FoldKind::Comment, - FoldKind::Comment, - FoldKind::Comment, - ]; - do_check(text, fold_kinds); +}"#, + ); } #[test] fn test_fold_imports() { - let text = r#" -use std::{ + check( + r#" +use std::{ str, vec, io as iop }; -fn main() { -}"#; - - let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block]; - do_check(text, folds); +fn main() { +}"#, + ); } #[test] fn test_fold_mods() { - let text = r#" + check( + r#" pub mod foo; -mod after_pub; +mod after_pub; mod after_pub_next; -mod before_pub; +mod before_pub; mod before_pub_next; pub mod bar; @@ -286,90 +284,93 @@ mod not_folding_single; pub mod foobar; pub not_folding_single_next; -#[cfg(test)] +#[cfg(test)] mod with_attribute; mod with_attribute_next; -fn main() { -}"#; - - let folds = &[FoldKind::Mods, FoldKind::Mods, FoldKind::Mods, FoldKind::Block]; - do_check(text, folds); +fn main() { +}"#, + ); } #[test] fn test_fold_import_groups() { - let text = r#" -use std::str; + check( + r#" +use std::str; use std::vec; use std::io as iop; -use std::mem; +use std::mem; use std::f64; use std::collections::HashMap; // Some random comment use std::collections::VecDeque; -fn main() { -}"#; - - let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block]; - do_check(text, folds); +fn main() { +}"#, + ); } #[test] fn test_fold_import_and_groups() { - let text = r#" -use std::str; + check( + r#" +use std::str; use std::vec; use std::io as iop; -use std::mem; +use std::mem; use std::f64; -use std::collections::{ +use std::collections::{ HashMap, VecDeque, }; // Some random comment -fn main() { -}"#; - - let folds = &[ - FoldKind::Imports, - FoldKind::Imports, - FoldKind::Imports, - FoldKind::Block, - FoldKind::Block, - ]; - do_check(text, folds); +fn main() { +}"#, + ); } #[test] fn test_folds_macros() { - let text = r#" -macro_rules! foo { + check( + r#" +macro_rules! foo { ($($tt:tt)*) => { $($tt)* } } -"#; - - let folds = &[FoldKind::Block]; - do_check(text, folds); +"#, + ); } #[test] fn test_fold_match_arms() { - let text = r#" -fn main() { - match 0 { + check( + r#" +fn main() { + match 0 { 0 => 0, _ => 1, } -}"#; +}"#, + ); + } - let folds = &[FoldKind::Block, FoldKind::Block]; - do_check(text, folds); + #[test] + fn fold_big_calls() { + check( + r#" +fn main() { + frobnicate( + 1, + 2, + 3, + ) +} + "#, + ) } } diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index a0a58f689..95dd8e408 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -352,7 +352,7 @@ pub(crate) fn folding_range( let kind = match fold.kind { FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), - FoldKind::Mods | FoldKind::Block => None, + FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None, }; let range = range(line_index, fold.range); @@ -685,32 +685,27 @@ pub(crate) fn runnable( #[cfg(test)] mod tests { - use test_utils::extract_ranges; + use ra_ide::Analysis; use super::*; #[test] fn conv_fold_line_folding_only_fixup() { - let text = r#"mod a; + let text = r#"mod a; mod b; -mod c; +mod c; -fn main() { - if cond { +fn main() { + if cond { a::do_a(); - } else { + } else { b::do_b(); - } -}"#; - - let (ranges, text) = extract_ranges(text, "fold"); - assert_eq!(ranges.len(), 4); - let folds = vec![ - Fold { range: ranges[0], kind: FoldKind::Mods }, - Fold { range: ranges[1], kind: FoldKind::Block }, - Fold { range: ranges[2], kind: FoldKind::Block }, - Fold { range: ranges[3], kind: FoldKind::Block }, - ]; + } +}"#; + + let (analysis, file_id) = Analysis::from_single_file(text.to_string()); + let folds = analysis.folding_ranges(file_id).unwrap(); + assert_eq!(folds.len(), 4); let line_index = LineIndex::new(&text); let converted: Vec = diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index fba5f4281..e4aa894ac 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -118,8 +118,8 @@ pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) { } /// Extracts ranges, marked with ` ` pairs from the `text` -pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec, String) { - let open = format!("<{}>", tag); +pub fn extract_tags(mut text: &str, tag: &str) -> (Vec<(TextRange, Option)>, String) { + let open = format!("<{}", tag); let close = format!("", tag); let mut ranges = Vec::new(); let mut res = String::new(); @@ -134,22 +134,35 @@ pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec, String) { res.push_str(&text[..i]); text = &text[i..]; if text.starts_with(&open) { - text = &text[open.len()..]; + let close_open = text.find('>').unwrap(); + let attr = text[open.len()..close_open].trim(); + let attr = if attr.is_empty() { None } else { Some(attr.to_string()) }; + text = &text[close_open + '>'.len_utf8()..]; let from = TextSize::of(&res); - stack.push(from); + stack.push((from, attr)); } else if text.starts_with(&close) { text = &text[close.len()..]; - let from = stack.pop().unwrap_or_else(|| panic!("unmatched ", tag)); + let (from, attr) = + stack.pop().unwrap_or_else(|| panic!("unmatched ", tag)); let to = TextSize::of(&res); - ranges.push(TextRange::new(from, to)); + ranges.push((TextRange::new(from, to), attr)); + } else { + res.push('<'); + text = &text['<'.len_utf8()..]; } } } } assert!(stack.is_empty(), "unmatched <{}>", tag); - ranges.sort_by_key(|r| (r.start(), r.end())); + ranges.sort_by_key(|r| (r.0.start(), r.0.end())); (ranges, res) } +#[test] +fn test_extract_tags() { + let (tags, text) = extract_tags(r#"fn main() {}"#, "tag"); + let actual = tags.into_iter().map(|(range, attr)| (&text[range], attr)).collect::>(); + assert_eq!(actual, vec![("fn main() {}", Some("fn".into())), ("main", None),]); +} /// Inserts `<|>` marker into the `text` at `offset`. pub fn add_cursor(text: &str, offset: TextSize) -> String { -- cgit v1.2.3