diff --git a/src/features/completion/component_environment.rs b/src/features/completion/component_environment.rs index d4340a16..ce2891e5 100644 --- a/src/features/completion/component_environment.rs +++ b/src/features/completion/component_environment.rs @@ -155,4 +155,24 @@ mod tests { assert_eq!(item.range, TextRange::new(7.into(), 8.into())); } } + + #[test] + fn test_command_definition() { + let request = FeatureTester::builder() + .files(vec![("main.tex", "\\newcommand{\\foo}{\\begin{\nd}")]) + .main("main.tex") + .line(1) + .character(1) + .build() + .completion(); + + let context = CursorContext::new(request); + let mut actual_items = Vec::new(); + complete_component_environments(&context, &mut actual_items, CancellationToken::none()); + + assert!(!actual_items.is_empty()); + for item in actual_items { + assert_eq!(item.range, TextRange::new(26.into(), 27.into())); + } + } } diff --git a/src/syntax/latex/parser.rs b/src/syntax/latex/parser.rs index 8dc7fa9b..ac73113a 100644 --- a/src/syntax/latex/parser.rs +++ b/src/syntax/latex/parser.rs @@ -189,6 +189,21 @@ impl<'a> Parser<'a> { self.builder.finish_node(); } + fn curly_group_impl(&mut self) { + self.builder.start_node(CURLY_GROUP.into()); + self.eat(); + while let Some(kind) = self.peek() { + match kind { + R_CURLY => break, + BEGIN_ENVIRONMENT_NAME => self.begin(), + END_ENVIRONMENT_NAME => self.end(), + _ => self.content(ParserContext::default()), + }; + } + self.expect(R_CURLY); + self.builder.finish_node(); + } + fn curly_group_without_environments(&mut self) { self.builder.start_node(CURLY_GROUP.into()); self.eat(); @@ -1020,7 +1035,7 @@ impl<'a> Parser<'a> { } if self.lexer.peek() == Some(L_CURLY) { - self.curly_group(); + self.curly_group_impl(); } else { self.builder.token(MISSING.into(), ""); } @@ -1040,7 +1055,7 @@ impl<'a> Parser<'a> { } if self.lexer.peek() == Some(L_CURLY) { - self.curly_group(); + self.curly_group_impl(); } else { self.builder.token(MISSING.into(), ""); } @@ -1703,6 +1718,13 @@ mod tests { assert_debug_snapshot!(setup(r#"\newcommand{\foo"#)); } + #[test] + fn test_command_definition_with_begin() { + assert_debug_snapshot!(setup( + r#"\newcommand{\CVSubHeadingListStart}{\begin{itemize}[leftmargin=0.5cm, label={}]}"# + )); + } + #[test] fn test_math_operator_simple() { assert_debug_snapshot!(setup(r#"\DeclareMathOperator{\foo}{foo}"#)); diff --git a/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__command_definition_with_begin.snap b/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__command_definition_with_begin.snap new file mode 100644 index 00000000..6b7724c0 --- /dev/null +++ b/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__command_definition_with_begin.snap @@ -0,0 +1,39 @@ +--- +source: src/syntax/latex/parser.rs +assertion_line: 1723 +expression: "setup(r#\"\\newcommand{\\CVSubHeadingListStart}{\\begin{itemize}[leftmargin=0.5cm, label={}]}\"#)" +--- +ROOT@0..80 + PREAMBLE@0..80 + COMMAND_DEFINITION@0..80 + COMMAND_DEFINITION_NAME@0..11 "\\newcommand" + CURLY_GROUP_COMMAND@11..35 + L_CURLY@11..12 "{" + GENERIC_COMMAND_NAME@12..34 "\\CVSubHeadingListStart" + R_CURLY@34..35 "}" + CURLY_GROUP@35..80 + L_CURLY@35..36 "{" + BEGIN@36..79 + BEGIN_ENVIRONMENT_NAME@36..42 "\\begin" + CURLY_GROUP_WORD@42..51 + L_CURLY@42..43 "{" + KEY@43..50 + WORD@43..50 "itemize" + R_CURLY@50..51 "}" + BRACK_GROUP@51..79 + L_BRACK@51..52 "[" + TEXT@52..62 + WORD@52..62 "leftmargin" + EQUALITY_SIGN@62..63 "=" + TEXT@63..75 + WORD@63..68 "0.5cm" + COMMA@68..69 "," + WHITESPACE@69..70 " " + WORD@70..75 "label" + EQUALITY_SIGN@75..76 "=" + CURLY_GROUP@76..78 + L_CURLY@76..77 "{" + R_CURLY@77..78 "}" + R_BRACK@78..79 "]" + R_CURLY@79..80 "}" +