From 2a1c3ad20ce61bf8dd5fcafaaafecb5f055f5ed0 Mon Sep 17 00:00:00 2001 From: Dianyi Yang Date: Wed, 30 Oct 2024 07:56:30 -0400 Subject: [PATCH 01/10] add folding_range.rs --- crates/ark/src/lsp/folding_range.rs | 74 +++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 crates/ark/src/lsp/folding_range.rs diff --git a/crates/ark/src/lsp/folding_range.rs b/crates/ark/src/lsp/folding_range.rs new file mode 100644 index 000000000..8ee749261 --- /dev/null +++ b/crates/ark/src/lsp/folding_range.rs @@ -0,0 +1,74 @@ +use tower_lsp::lsp_types::FoldingRange; +use tower_lsp::lsp_types::FoldingRangeKind; + +use crate::lsp::documents::Document; +use crate::lsp::log_info; +use crate::lsp::symbols::parse_comment_as_section; + +/// Detects and returns folding ranges for comment sections and curly-bracketed blocks +pub fn folding_range(document: &Document) -> anyhow::Result> { + let mut folding_ranges: Vec = Vec::new(); + let text = &document.contents; // Assuming `contents()` gives the text of the document + let mut line_iter = text.lines().enumerate().peekable(); + + let mut comment_stack: Vec<(usize, usize)> = Vec::new(); // a stack of (level, start_line) tuples + + while let Some((line_idx, line)) = line_iter.next() { + let line_text = line.to_string(); + (folding_ranges, comment_stack) = + comment_processor(folding_ranges, comment_stack, line_idx, &line_text); + log_info!("line_idx: {:#?} line_text: {:#?}", line_idx, line_text); + } + + // TODO: End line handling + + Ok(folding_ranges) +} + +fn comment_processor( + mut folding_ranges: Vec, + mut comment_stack: Vec<(usize, usize)>, + line_idx: usize, + line_text: &str, +) -> (Vec, Vec<(usize, usize)>) { + let Some((level, _title)) = parse_comment_as_section(line_text) else { + return (folding_ranges, comment_stack); // return if the line is not a comment section + }; + + if comment_stack.is_empty() { + comment_stack.push((level, line_idx)); + return (folding_ranges, comment_stack); // return if the stack is empty + } + + while let Some((last_level, _)) = comment_stack.last() { + if *last_level < level { + comment_stack.push((level, line_idx)); + break; + } else if *last_level == level { + folding_ranges.push(comment_range(comment_stack.last().unwrap().1, line_idx - 1)); + comment_stack.pop(); + comment_stack.push((level, line_idx)); + break; + } else { + folding_ranges.push(comment_range(comment_stack.last().unwrap().1, line_idx - 1)); + comment_stack.pop(); + } + } + + // log a copy of comment_stack + let comment_stack_copy = comment_stack.clone(); + log_info!("comment_stack_copy: {:#?}", comment_stack_copy); + + (folding_ranges, comment_stack) +} + +fn comment_range(start_line: usize, end_line: usize) -> FoldingRange { + FoldingRange { + start_line: start_line.try_into().unwrap(), + start_character: None, + end_line: end_line.try_into().unwrap(), + end_character: None, + kind: Some(FoldingRangeKind::Region), + collapsed_text: None, + } +} From 8e0483f2bfa2118d3cd4c41f4c3e21a8ecdc847c Mon Sep 17 00:00:00 2001 From: Dianyi Yang Date: Wed, 30 Oct 2024 07:57:10 -0400 Subject: [PATCH 02/10] make parse_comment_as_section public --- crates/ark/src/lsp/symbols.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ark/src/lsp/symbols.rs b/crates/ark/src/lsp/symbols.rs index c427930dd..c6a1c8a28 100644 --- a/crates/ark/src/lsp/symbols.rs +++ b/crates/ark/src/lsp/symbols.rs @@ -335,7 +335,7 @@ fn index_assignment_with_function( } // Function to parse a comment and return the section level and title -fn parse_comment_as_section(comment: &str) -> Option<(usize, String)> { +pub fn parse_comment_as_section(comment: &str) -> Option<(usize, String)> { // Match lines starting with one or more '#' followed by some non-empty content and must end with 4 or more '-', '#', or `=` // Ensure that there's actual content between the start and the trailing symbols. if let Some(caps) = indexer::RE_COMMENT_SECTION.captures(comment) { From 56347845bead6becf190b457c3c391b13eef7b2e Mon Sep 17 00:00:00 2001 From: Dianyi Yang Date: Wed, 30 Oct 2024 08:04:59 -0400 Subject: [PATCH 03/10] Add backend support --- crates/ark/src/lsp/backend.rs | 10 ++++++++++ crates/ark/src/lsp/handlers.rs | 19 +++++++++++++++++++ crates/ark/src/lsp/main_loop.rs | 3 +++ crates/ark/src/lsp/mod.rs | 1 + crates/ark/src/lsp/state_handlers.rs | 2 ++ 5 files changed, 35 insertions(+) diff --git a/crates/ark/src/lsp/backend.rs b/crates/ark/src/lsp/backend.rs index 8433ad380..d476b62d1 100644 --- a/crates/ark/src/lsp/backend.rs +++ b/crates/ark/src/lsp/backend.rs @@ -19,6 +19,7 @@ use tower_lsp::jsonrpc; use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::request::GotoImplementationParams; use tower_lsp::lsp_types::request::GotoImplementationResponse; +use tower_lsp::lsp_types::FoldingRange; use tower_lsp::lsp_types::SelectionRange; use tower_lsp::lsp_types::*; use tower_lsp::Client; @@ -90,6 +91,7 @@ pub(crate) enum LspRequest { GotoDefinition(GotoDefinitionParams), GotoImplementation(GotoImplementationParams), SelectionRange(SelectionRangeParams), + FoldingRange(FoldingRangeParams), References(ReferenceParams), StatementRange(StatementRangeParams), HelpTopic(HelpTopicParams), @@ -113,6 +115,7 @@ pub(crate) enum LspResponse { GotoImplementation(Option), SelectionRange(Option>), References(Option>), + FoldingRange(Option>), StatementRange(Option), HelpTopic(Option), OnTypeFormatting(Option>), @@ -288,6 +291,13 @@ impl LanguageServer for Backend { ) } + async fn folding_range(&self, params: FoldingRangeParams) -> Result>> { + cast_response!( + self.request(LspRequest::FoldingRange(params)).await, + LspResponse::FoldingRange + ) + } + async fn references(&self, params: ReferenceParams) -> Result>> { cast_response!( self.request(LspRequest::References(params)).await, diff --git a/crates/ark/src/lsp/handlers.rs b/crates/ark/src/lsp/handlers.rs index 71bac71c3..282fbcc1e 100644 --- a/crates/ark/src/lsp/handlers.rs +++ b/crates/ark/src/lsp/handlers.rs @@ -17,6 +17,8 @@ use tower_lsp::lsp_types::CompletionResponse; use tower_lsp::lsp_types::DocumentOnTypeFormattingParams; use tower_lsp::lsp_types::DocumentSymbolParams; use tower_lsp::lsp_types::DocumentSymbolResponse; +use tower_lsp::lsp_types::FoldingRange; +use tower_lsp::lsp_types::FoldingRangeParams; use tower_lsp::lsp_types::GotoDefinitionParams; use tower_lsp::lsp_types::GotoDefinitionResponse; use tower_lsp::lsp_types::Hover; @@ -47,6 +49,7 @@ use crate::lsp::config::VscDocumentConfig; use crate::lsp::definitions::goto_definition; use crate::lsp::document_context::DocumentContext; use crate::lsp::encoding::convert_position_to_point; +use crate::lsp::folding_range::folding_range; use crate::lsp::help_topic::help_topic; use crate::lsp::help_topic::HelpTopicParams; use crate::lsp::help_topic::HelpTopicResponse; @@ -312,6 +315,22 @@ pub(crate) fn handle_selection_range( Ok(Some(selections)) } +#[tracing::instrument(level = "info", skip_all)] +pub(crate) fn handle_folding_range( + params: FoldingRangeParams, + state: &WorldState, +) -> anyhow::Result>> { + let uri = params.text_document.uri; + let document = state.get_document(&uri)?; + match folding_range(document) { + Ok(foldings) => Ok(Some(foldings)), + Err(err) => { + lsp::log_error!("{err:?}"); + Ok(None) + }, + } +} + #[tracing::instrument(level = "info", skip_all)] pub(crate) fn handle_references( params: ReferenceParams, diff --git a/crates/ark/src/lsp/main_loop.rs b/crates/ark/src/lsp/main_loop.rs index de773762e..ae4937d0e 100644 --- a/crates/ark/src/lsp/main_loop.rs +++ b/crates/ark/src/lsp/main_loop.rs @@ -287,6 +287,9 @@ impl GlobalState { LspRequest::SelectionRange(params) => { respond(tx, handlers::handle_selection_range(params, &self.world), LspResponse::SelectionRange)?; }, + LspRequest::FoldingRange(params) => { + respond(tx, handlers::handle_folding_range(params, &self.world), LspResponse::FoldingRange)?; + } LspRequest::References(params) => { respond(tx, handlers::handle_references(params, &self.world), LspResponse::References)?; }, diff --git a/crates/ark/src/lsp/mod.rs b/crates/ark/src/lsp/mod.rs index 5ed95bfef..d7b54ea3f 100644 --- a/crates/ark/src/lsp/mod.rs +++ b/crates/ark/src/lsp/mod.rs @@ -17,6 +17,7 @@ pub mod document_context; pub mod documents; pub mod encoding; pub mod events; +pub mod folding_range; pub mod handler; pub mod handlers; pub mod help; diff --git a/crates/ark/src/lsp/state_handlers.rs b/crates/ark/src/lsp/state_handlers.rs index 6970b40bd..6647eacec 100644 --- a/crates/ark/src/lsp/state_handlers.rs +++ b/crates/ark/src/lsp/state_handlers.rs @@ -18,6 +18,7 @@ use tower_lsp::lsp_types::DidCloseTextDocumentParams; use tower_lsp::lsp_types::DidOpenTextDocumentParams; use tower_lsp::lsp_types::DocumentOnTypeFormattingOptions; use tower_lsp::lsp_types::ExecuteCommandOptions; +use tower_lsp::lsp_types::FoldingRangeProviderCapability; use tower_lsp::lsp_types::FormattingOptions; use tower_lsp::lsp_types::HoverProviderCapability; use tower_lsp::lsp_types::ImplementationProviderCapability; @@ -114,6 +115,7 @@ pub(crate) fn initialize( TextDocumentSyncKind::INCREMENTAL, )), selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), + folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), hover_provider: Some(HoverProviderCapability::from(true)), completion_provider: Some(CompletionOptions { resolve_provider: Some(true), From 653087906162332f5601009f5f49dcf8dbb14817 Mon Sep 17 00:00:00 2001 From: Dianyi Yang Date: Wed, 30 Oct 2024 14:55:06 -0400 Subject: [PATCH 04/10] add foldable brackets --- crates/ark/src/lsp/folding_range.rs | 113 ++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 7 deletions(-) diff --git a/crates/ark/src/lsp/folding_range.rs b/crates/ark/src/lsp/folding_range.rs index 8ee749261..66e19f306 100644 --- a/crates/ark/src/lsp/folding_range.rs +++ b/crates/ark/src/lsp/folding_range.rs @@ -2,6 +2,7 @@ use tower_lsp::lsp_types::FoldingRange; use tower_lsp::lsp_types::FoldingRangeKind; use crate::lsp::documents::Document; +use crate::lsp::log_error; use crate::lsp::log_info; use crate::lsp::symbols::parse_comment_as_section; @@ -11,20 +12,109 @@ pub fn folding_range(document: &Document) -> anyhow::Result> { let text = &document.contents; // Assuming `contents()` gives the text of the document let mut line_iter = text.lines().enumerate().peekable(); + let mut bracket_stack: Vec<(usize, usize)> = Vec::new(); // a stack of (start_line, start_character) tuples let mut comment_stack: Vec<(usize, usize)> = Vec::new(); // a stack of (level, start_line) tuples while let Some((line_idx, line)) = line_iter.next() { let line_text = line.to_string(); + (folding_ranges, bracket_stack) = + bracket_processor(folding_ranges, bracket_stack, line_idx, &line_text); (folding_ranges, comment_stack) = comment_processor(folding_ranges, comment_stack, line_idx, &line_text); - log_info!("line_idx: {:#?} line_text: {:#?}", line_idx, line_text); + // log_info!("line_idx: {:#?} line_text: {:#?}", line_idx, line_text); } // TODO: End line handling + // Log the final folding ranges + log_info!("folding_ranges: {:#?}", folding_ranges); + Ok(folding_ranges) } +fn bracket_processor( + mut folding_ranges: Vec, + mut bracket_stack: Vec<(usize, usize)>, + line_idx: usize, + line_text: &str, +) -> (Vec, Vec<(usize, usize)>) { + // Remove any trailing comments (starting with #) and \n in line_text + let line_text = line_text.split('#').next().unwrap_or("").trim_end(); + let mut whitespace_count = 0; + + // Iterate over each character in line_text to find the positions of `{` and `}` + for (char_idx, c) in line_text.char_indices() { + match c { + '{' => { + bracket_stack.push((line_idx, char_idx)); + }, + '}' => { + // If '}' is found, pop from the bracket_stack if it is not empty + if let Some((start_line, start_char)) = bracket_stack.pop() { + // Count the number of leading whitespace characters + + // Create a new FoldingRange from the start `{` to the current `}` + let folding_range = bracket_range( + start_line, + start_char, + line_idx, + char_idx, + &whitespace_count, + ); + + // Log a copy of the folding range + // let folding_range_copy = folding_range.clone(); + // log_info!("folding_range_copy: {:#?}", folding_range_copy); + + // Add the folding range to the list + folding_ranges.push(folding_range); + } + }, + ' ' => whitespace_count += 1, + _ => {}, + } + } + + (folding_ranges, bracket_stack) +} + +fn bracket_range( + start_line: usize, + start_char: usize, + end_line: usize, + end_char: usize, + white_space_count: &usize, +) -> FoldingRange { + let mut end_line: u32 = end_line.try_into().unwrap(); + let mut end_char: Option = Some(end_char.try_into().unwrap()); + + let adjusted_end_char = end_char.and_then(|val| val.checked_sub(*white_space_count as u32)); + + match adjusted_end_char { + Some(0) => { + end_line -= 1; + end_char = None; + }, + Some(_) => { + if let Some(ref mut value) = end_char { + *value -= 1; + } + }, + None => { + log_error!("Folding Range (bracket_range): adjusted_end_char should not be None here"); + }, + } + + FoldingRange { + start_line: start_line.try_into().unwrap(), + start_character: Some(start_char as u32), + end_line, + end_character: end_char, + kind: Some(FoldingRangeKind::Region), + collapsed_text: None, + } +} + fn comment_processor( mut folding_ranges: Vec, mut comment_stack: Vec<(usize, usize)>, @@ -35,23 +125,32 @@ fn comment_processor( return (folding_ranges, comment_stack); // return if the line is not a comment section }; - if comment_stack.is_empty() { - comment_stack.push((level, line_idx)); - return (folding_ranges, comment_stack); // return if the stack is empty - } + loop { + if comment_stack.is_empty() { + comment_stack.push((level, line_idx)); + return (folding_ranges, comment_stack); // return if the stack is empty + } - while let Some((last_level, _)) = comment_stack.last() { + let Some((last_level, _)) = comment_stack.last() else { + log_error!("Folding Range: comment_stacks should not be empty here"); + return (folding_ranges, comment_stack); + }; if *last_level < level { comment_stack.push((level, line_idx)); break; } else if *last_level == level { folding_ranges.push(comment_range(comment_stack.last().unwrap().1, line_idx - 1)); + + // Log a copy of folding_range + let folding_range_copy = folding_ranges.last().unwrap().clone(); + log_info!("folding_range_copy: {:#?}", folding_range_copy); + comment_stack.pop(); comment_stack.push((level, line_idx)); break; } else { folding_ranges.push(comment_range(comment_stack.last().unwrap().1, line_idx - 1)); - comment_stack.pop(); + comment_stack.pop(); // TODO: Handle case where comment_stack is empty } } From 69404f7c917995e3d133db077a0cf318515d60f6 Mon Sep 17 00:00:00 2001 From: Dianyi Yang Date: Wed, 30 Oct 2024 15:25:35 -0400 Subject: [PATCH 05/10] handle comments within functions --- crates/ark/src/lsp/folding_range.rs | 81 +++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/crates/ark/src/lsp/folding_range.rs b/crates/ark/src/lsp/folding_range.rs index 66e19f306..f30c45e55 100644 --- a/crates/ark/src/lsp/folding_range.rs +++ b/crates/ark/src/lsp/folding_range.rs @@ -13,12 +13,17 @@ pub fn folding_range(document: &Document) -> anyhow::Result> { let mut line_iter = text.lines().enumerate().peekable(); let mut bracket_stack: Vec<(usize, usize)> = Vec::new(); // a stack of (start_line, start_character) tuples - let mut comment_stack: Vec<(usize, usize)> = Vec::new(); // a stack of (level, start_line) tuples + let mut comment_stack: Vec> = vec![Vec::new()]; // a vector of stacks for each bracket level, within each stack is a vector of (level, start_line) tuples while let Some((line_idx, line)) = line_iter.next() { let line_text = line.to_string(); - (folding_ranges, bracket_stack) = - bracket_processor(folding_ranges, bracket_stack, line_idx, &line_text); + (folding_ranges, bracket_stack, comment_stack) = bracket_processor( + folding_ranges, + bracket_stack, + comment_stack, + line_idx, + &line_text, + ); (folding_ranges, comment_stack) = comment_processor(folding_ranges, comment_stack, line_idx, &line_text); // log_info!("line_idx: {:#?} line_text: {:#?}", line_idx, line_text); @@ -35,9 +40,14 @@ pub fn folding_range(document: &Document) -> anyhow::Result> { fn bracket_processor( mut folding_ranges: Vec, mut bracket_stack: Vec<(usize, usize)>, + mut comment_stack: Vec>, line_idx: usize, line_text: &str, -) -> (Vec, Vec<(usize, usize)>) { +) -> ( + Vec, + Vec<(usize, usize)>, + Vec>, +) { // Remove any trailing comments (starting with #) and \n in line_text let line_text = line_text.split('#').next().unwrap_or("").trim_end(); let mut whitespace_count = 0; @@ -47,8 +57,11 @@ fn bracket_processor( match c { '{' => { bracket_stack.push((line_idx, char_idx)); + comment_stack.push(Vec::new()); }, '}' => { + (folding_ranges, comment_stack) = + end_bracket_handler(folding_ranges, comment_stack, line_idx); // If '}' is found, pop from the bracket_stack if it is not empty if let Some((start_line, start_char)) = bracket_stack.pop() { // Count the number of leading whitespace characters @@ -75,7 +88,7 @@ fn bracket_processor( } } - (folding_ranges, bracket_stack) + (folding_ranges, bracket_stack, comment_stack) } fn bracket_range( @@ -115,42 +128,68 @@ fn bracket_range( } } +fn end_bracket_handler( + mut folding_ranges: Vec, + mut comment_stack: Vec>, + line_idx: usize, +) -> (Vec, Vec>) { + // TODO: Iterate over the last elment of the comment stack and add it to the folding ranges by using the comment_range function + if let Some(last_section) = comment_stack.last() { + // Iterate over each (start level, start line) in the last section + for &(_level, start_line) in last_section.iter() { + // Add a new folding range for each range in the last section + folding_ranges.push(comment_range(start_line, line_idx - 1)); + } + } + + // Remove the last element from the comment stack after processing + comment_stack.pop(); + + (folding_ranges, comment_stack) +} + fn comment_processor( mut folding_ranges: Vec, - mut comment_stack: Vec<(usize, usize)>, + mut comment_stack: Vec>, line_idx: usize, line_text: &str, -) -> (Vec, Vec<(usize, usize)>) { +) -> (Vec, Vec>) { let Some((level, _title)) = parse_comment_as_section(line_text) else { return (folding_ranges, comment_stack); // return if the line is not a comment section }; + if comment_stack.is_empty() { + log_error!("Folding Range: comment_stack should always contain at least one element here"); + return (folding_ranges, comment_stack); + } + loop { - if comment_stack.is_empty() { - comment_stack.push((level, line_idx)); + if comment_stack.last().unwrap().is_empty() { + comment_stack.last_mut().unwrap().push((level, line_idx)); return (folding_ranges, comment_stack); // return if the stack is empty } - let Some((last_level, _)) = comment_stack.last() else { + let Some((last_level, _)) = comment_stack.last().unwrap().last() else { log_error!("Folding Range: comment_stacks should not be empty here"); return (folding_ranges, comment_stack); }; if *last_level < level { - comment_stack.push((level, line_idx)); + comment_stack.last_mut().unwrap().push((level, line_idx)); break; } else if *last_level == level { - folding_ranges.push(comment_range(comment_stack.last().unwrap().1, line_idx - 1)); - - // Log a copy of folding_range - let folding_range_copy = folding_ranges.last().unwrap().clone(); - log_info!("folding_range_copy: {:#?}", folding_range_copy); - - comment_stack.pop(); - comment_stack.push((level, line_idx)); + folding_ranges.push(comment_range( + comment_stack.last().unwrap().last().unwrap().1, + line_idx - 1, + )); + comment_stack.last_mut().unwrap().pop(); + comment_stack.last_mut().unwrap().push((level, line_idx)); break; } else { - folding_ranges.push(comment_range(comment_stack.last().unwrap().1, line_idx - 1)); - comment_stack.pop(); // TODO: Handle case where comment_stack is empty + folding_ranges.push(comment_range( + comment_stack.last().unwrap().last().unwrap().1, + line_idx - 1, + )); + comment_stack.last_mut().unwrap().pop(); // TODO: Handle case where comment_stack is empty } } From 268803976444ef2e83492a90e15c97d5757dc33b Mon Sep 17 00:00:00 2001 From: Dianyi Yang Date: Wed, 30 Oct 2024 16:40:01 -0400 Subject: [PATCH 06/10] Fix end of document handling --- crates/ark/src/lsp/folding_range.rs | 36 ++++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/crates/ark/src/lsp/folding_range.rs b/crates/ark/src/lsp/folding_range.rs index f30c45e55..cbed0f185 100644 --- a/crates/ark/src/lsp/folding_range.rs +++ b/crates/ark/src/lsp/folding_range.rs @@ -10,12 +10,16 @@ use crate::lsp::symbols::parse_comment_as_section; pub fn folding_range(document: &Document) -> anyhow::Result> { let mut folding_ranges: Vec = Vec::new(); let text = &document.contents; // Assuming `contents()` gives the text of the document - let mut line_iter = text.lines().enumerate().peekable(); - let mut bracket_stack: Vec<(usize, usize)> = Vec::new(); // a stack of (start_line, start_character) tuples - let mut comment_stack: Vec> = vec![Vec::new()]; // a vector of stacks for each bracket level, within each stack is a vector of (level, start_line) tuples + // This is a stack of (start_line, start_character) tuples + let mut bracket_stack: Vec<(usize, usize)> = Vec::new(); + // This is a stack of stacks for each bracket level, within each stack is a vector of (level, start_line) tuples + let mut comment_stack: Vec> = vec![Vec::new()]; + let mut line_iter = text.lines().enumerate().peekable(); + let mut line_count = 0; while let Some((line_idx, line)) = line_iter.next() { + line_count += 1; let line_text = line.to_string(); (folding_ranges, bracket_stack, comment_stack) = bracket_processor( folding_ranges, @@ -26,13 +30,19 @@ pub fn folding_range(document: &Document) -> anyhow::Result> { ); (folding_ranges, comment_stack) = comment_processor(folding_ranges, comment_stack, line_idx, &line_text); - // log_info!("line_idx: {:#?} line_text: {:#?}", line_idx, line_text); } - // TODO: End line handling + // Use `end_bracket_handler` to close any remaining comments + // There should only be one element in `comment_stack` though + while !comment_stack.is_empty() && !comment_stack.last().unwrap().is_empty() { + (folding_ranges, comment_stack) = + end_bracket_handler(folding_ranges, comment_stack, line_count); + } - // Log the final folding ranges + // Log the final folding ranges and comment stacks log_info!("folding_ranges: {:#?}", folding_ranges); + log_info!("comment_stack: {:#?}", comment_stack); // Should be empty + log_info!("bracket_stack: {:#?}", bracket_stack); // Should be empty Ok(folding_ranges) } @@ -75,10 +85,6 @@ fn bracket_processor( &whitespace_count, ); - // Log a copy of the folding range - // let folding_range_copy = folding_range.clone(); - // log_info!("folding_range_copy: {:#?}", folding_range_copy); - // Add the folding range to the list folding_ranges.push(folding_range); } @@ -133,12 +139,14 @@ fn end_bracket_handler( mut comment_stack: Vec>, line_idx: usize, ) -> (Vec, Vec>) { - // TODO: Iterate over the last elment of the comment stack and add it to the folding ranges by using the comment_range function + // Iterate over the last elment of the comment stack and add it to the folding ranges by using the comment_range function if let Some(last_section) = comment_stack.last() { // Iterate over each (start level, start line) in the last section for &(_level, start_line) in last_section.iter() { // Add a new folding range for each range in the last section - folding_ranges.push(comment_range(start_line, line_idx - 1)); + let folding_range = comment_range(start_line, line_idx - 1); + + folding_ranges.push(folding_range); } } @@ -193,10 +201,6 @@ fn comment_processor( } } - // log a copy of comment_stack - let comment_stack_copy = comment_stack.clone(); - log_info!("comment_stack_copy: {:#?}", comment_stack_copy); - (folding_ranges, comment_stack) } From b5ea37ecf8358d153c9a0a0b75a6cfb3e5d1c6dc Mon Sep 17 00:00:00 2001 From: Dianyi Yang Date: Wed, 30 Oct 2024 16:52:58 -0400 Subject: [PATCH 07/10] treat "[]" and "()" the same as "{}" --- crates/ark/src/lsp/folding_range.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ark/src/lsp/folding_range.rs b/crates/ark/src/lsp/folding_range.rs index cbed0f185..d3aa583bf 100644 --- a/crates/ark/src/lsp/folding_range.rs +++ b/crates/ark/src/lsp/folding_range.rs @@ -65,11 +65,11 @@ fn bracket_processor( // Iterate over each character in line_text to find the positions of `{` and `}` for (char_idx, c) in line_text.char_indices() { match c { - '{' => { + '{' | '(' | '[' => { bracket_stack.push((line_idx, char_idx)); comment_stack.push(Vec::new()); }, - '}' => { + '}' | ')' | ']' => { (folding_ranges, comment_stack) = end_bracket_handler(folding_ranges, comment_stack, line_idx); // If '}' is found, pop from the bracket_stack if it is not empty From e4e150cfeab7578cf42a9a6f102354335eb78b37 Mon Sep 17 00:00:00 2001 From: Dianyi Yang Date: Wed, 30 Oct 2024 22:28:04 -0400 Subject: [PATCH 08/10] add comment region handling --- crates/ark/src/lsp/folding_range.rs | 48 +++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/crates/ark/src/lsp/folding_range.rs b/crates/ark/src/lsp/folding_range.rs index d3aa583bf..67c4a0175 100644 --- a/crates/ark/src/lsp/folding_range.rs +++ b/crates/ark/src/lsp/folding_range.rs @@ -1,3 +1,4 @@ +use regex::Regex; use tower_lsp::lsp_types::FoldingRange; use tower_lsp::lsp_types::FoldingRangeKind; @@ -15,6 +16,7 @@ pub fn folding_range(document: &Document) -> anyhow::Result> { let mut bracket_stack: Vec<(usize, usize)> = Vec::new(); // This is a stack of stacks for each bracket level, within each stack is a vector of (level, start_line) tuples let mut comment_stack: Vec> = vec![Vec::new()]; + let mut region_marker: Option = None; let mut line_iter = text.lines().enumerate().peekable(); let mut line_count = 0; @@ -30,6 +32,8 @@ pub fn folding_range(document: &Document) -> anyhow::Result> { ); (folding_ranges, comment_stack) = comment_processor(folding_ranges, comment_stack, line_idx, &line_text); + (folding_ranges, region_marker) = + region_processor(folding_ranges, region_marker, line_idx, &line_text); } // Use `end_bracket_handler` to close any remaining comments @@ -43,6 +47,7 @@ pub fn folding_range(document: &Document) -> anyhow::Result> { log_info!("folding_ranges: {:#?}", folding_ranges); log_info!("comment_stack: {:#?}", comment_stack); // Should be empty log_info!("bracket_stack: {:#?}", bracket_stack); // Should be empty + log_info!("region_marker: {:#?}", region_marker); // Should be None Ok(folding_ranges) } @@ -214,3 +219,46 @@ fn comment_range(start_line: usize, end_line: usize) -> FoldingRange { collapsed_text: None, } } + +fn region_processor( + mut folding_ranges: Vec, + mut region_marker: Option, + line_idx: usize, + line_text: &str, +) -> (Vec, Option) { + let Some(region_type) = parse_region_type(line_text) else { + return (folding_ranges, region_marker); // return if the line is not a region section + }; + match region_type.as_str() { + "start" => { + region_marker = Some(line_idx); + }, + "end" => { + if let Some(region_start) = region_marker { + let folding_range = comment_range(region_start, line_idx); + folding_ranges.push(folding_range); + region_marker = None; + } + }, + _ => {}, + } + + (folding_ranges, region_marker) +} + +fn parse_region_type(line_text: &str) -> Option { + // TODO: return the region type + // "start": "^\\s*#\\s*region\\b" + // "end": "^\\s*#\\s*endregion\\b" + // None: otherwise + let region_start = Regex::new(r"^\s*#\s*region\b").unwrap(); + let region_end = Regex::new(r"^\s*#\s*endregion\b").unwrap(); + + if region_start.is_match(line_text) { + Some("start".to_string()) + } else if region_end.is_match(line_text) { + Some("end".to_string()) + } else { + None + } +} From 92c1e9d2b2c8d9b988b7f19670813d88d31da9a9 Mon Sep 17 00:00:00 2001 From: Dianyi Yang Date: Thu, 31 Oct 2024 09:38:43 -0400 Subject: [PATCH 09/10] add code cell handling --- crates/ark/src/lsp/folding_range.rs | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/crates/ark/src/lsp/folding_range.rs b/crates/ark/src/lsp/folding_range.rs index 67c4a0175..e1e9454b9 100644 --- a/crates/ark/src/lsp/folding_range.rs +++ b/crates/ark/src/lsp/folding_range.rs @@ -1,3 +1,5 @@ +use std::cell; + use regex::Regex; use tower_lsp::lsp_types::FoldingRange; use tower_lsp::lsp_types::FoldingRangeKind; @@ -17,6 +19,7 @@ pub fn folding_range(document: &Document) -> anyhow::Result> { // This is a stack of stacks for each bracket level, within each stack is a vector of (level, start_line) tuples let mut comment_stack: Vec> = vec![Vec::new()]; let mut region_marker: Option = None; + let mut cell_marker: Option = None; let mut line_iter = text.lines().enumerate().peekable(); let mut line_count = 0; @@ -34,6 +37,8 @@ pub fn folding_range(document: &Document) -> anyhow::Result> { comment_processor(folding_ranges, comment_stack, line_idx, &line_text); (folding_ranges, region_marker) = region_processor(folding_ranges, region_marker, line_idx, &line_text); + (folding_ranges, cell_marker) = + cell_processor(folding_ranges, cell_marker, line_idx, &line_text); } // Use `end_bracket_handler` to close any remaining comments @@ -42,12 +47,19 @@ pub fn folding_range(document: &Document) -> anyhow::Result> { (folding_ranges, comment_stack) = end_bracket_handler(folding_ranges, comment_stack, line_count); } + // Deal with unclosed cells + if cell_marker.is_some() { + let fold_range = comment_range(cell_marker.unwrap(), line_count - 1); + folding_ranges.push(fold_range); + cell_marker = None; + } // Log the final folding ranges and comment stacks log_info!("folding_ranges: {:#?}", folding_ranges); log_info!("comment_stack: {:#?}", comment_stack); // Should be empty log_info!("bracket_stack: {:#?}", bracket_stack); // Should be empty log_info!("region_marker: {:#?}", region_marker); // Should be None + log_info!("cell_marker: {:#?}", cell_marker); // Should be None Ok(folding_ranges) } @@ -262,3 +274,28 @@ fn parse_region_type(line_text: &str) -> Option { None } } + +fn cell_processor( + // Almost identical to region_processor + mut folding_ranges: Vec, + mut region_marker: Option, + line_idx: usize, + line_text: &str, +) -> (Vec, Option) { + let cell_pattern: Regex = Regex::new(r"^#\s*(%%|\+)(.*)").unwrap(); + + if !cell_pattern.is_match(line_text) { + return (folding_ranges, region_marker); + } else { + let Some(start_line) = region_marker else { + region_marker = Some(line_idx); + return (folding_ranges, region_marker); + }; + + let folding_range = comment_range(start_line, line_idx - 1); + folding_ranges.push(folding_range); + region_marker = Some(line_idx); + + return (folding_ranges, region_marker); + } +} From cfd062688550201c7102e8f6b4916b981b5fe252 Mon Sep 17 00:00:00 2001 From: Dianyi Yang Date: Thu, 31 Oct 2024 09:41:51 -0400 Subject: [PATCH 10/10] Remove unneeded imports and uncomment logging --- crates/ark/src/lsp/folding_range.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/ark/src/lsp/folding_range.rs b/crates/ark/src/lsp/folding_range.rs index e1e9454b9..5a9ef2a99 100644 --- a/crates/ark/src/lsp/folding_range.rs +++ b/crates/ark/src/lsp/folding_range.rs @@ -1,12 +1,10 @@ -use std::cell; - use regex::Regex; use tower_lsp::lsp_types::FoldingRange; use tower_lsp::lsp_types::FoldingRangeKind; use crate::lsp::documents::Document; use crate::lsp::log_error; -use crate::lsp::log_info; +// use crate::lsp::log_info; // Uncomment to enable logging use crate::lsp::symbols::parse_comment_as_section; /// Detects and returns folding ranges for comment sections and curly-bracketed blocks @@ -55,11 +53,11 @@ pub fn folding_range(document: &Document) -> anyhow::Result> { } // Log the final folding ranges and comment stacks - log_info!("folding_ranges: {:#?}", folding_ranges); - log_info!("comment_stack: {:#?}", comment_stack); // Should be empty - log_info!("bracket_stack: {:#?}", bracket_stack); // Should be empty - log_info!("region_marker: {:#?}", region_marker); // Should be None - log_info!("cell_marker: {:#?}", cell_marker); // Should be None + // log_info!("folding_ranges: {:#?}", folding_ranges); // Contains all folding ranges + // log_info!("comment_stack: {:#?}", comment_stack); // Should be empty + // log_info!("bracket_stack: {:#?}", bracket_stack); // Should be empty + // log_info!("region_marker: {:#?}", region_marker); // Should be None + // log_info!("cell_marker: {:#?}", cell_marker); // Should be None Ok(folding_ranges) }