Handle command definitions without curly braces (#1422)

Parse structures like \newcommand\foo{bar} correctly.
This commit is contained in:
Patrick Förster 2025-06-24 20:08:42 +02:00 committed by GitHub
parent 6013783776
commit 7f09abcd0f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 58 additions and 11 deletions

View file

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Make sort order of workspace symbols deterministic ([#1421](https://github.com/latex-lsp/texlab/issues/1421))
- Parse command definitions without curly braces correctly, e. g. `\newcommand\foo{bar}` ([#1409](https://github.com/latex-lsp/texlab/issues/1409))
## [5.23.0] - 2025-06-14

View file

@ -49,6 +49,6 @@ fn process_old_definition(node: latex::SyntaxNode) -> Option<(TextRange, latex::
fn process_new_definition(node: latex::SyntaxNode) -> Option<(TextRange, latex::SyntaxToken)> {
let node = latex::NewCommandDefinition::cast(node)?;
let name = node.name()?.command()?;
let name = node.name()?;
Some((latex::small_range(&node), name))
}

View file

@ -63,6 +63,20 @@ fn test_new_command_definition() {
)
}
#[test]
fn test_new_command_definition_without_curly() {
check(
r#"
%! main.tex
\newcommand\foo{foo}
^^^^
^^^^^^^^^^^^^^^^^^^^
\foo
|
^^^^"#,
)
}
#[test]
fn test_document() {
check(

View file

@ -946,7 +946,11 @@ impl<'a> Parser<'a> {
self.eat();
self.trivia();
if self.lexer.peek() == Some(Token::LCurly) {
let token = self.lexer.peek();
if matches!(token, Some(Token::CommandName(_))) {
self.eat();
self.trivia();
} else if token == Some(Token::LCurly) {
self.curly_group_command();
}

View file

@ -718,6 +718,26 @@ fn test_command_definition_with_begin() {
);
}
#[test]
fn test_command_definition_without_curly() {
check(
r#"\newcommand\foo{bar}"#,
expect![[r#"
ROOT@0..20
PREAMBLE@0..20
NEW_COMMAND_DEFINITION@0..20
COMMAND_NAME@0..11 "\\newcommand"
COMMAND_NAME@11..15 "\\foo"
CURLY_GROUP@15..20
L_CURLY@15..16 "{"
TEXT@16..19
WORD@16..19 "bar"
R_CURLY@19..20 "}"
"#]],
);
}
#[test]
fn test_math_operator_no_impl() {
check(
@ -2681,9 +2701,8 @@ fn test_issue_857() {
expect![[r#"
ROOT@0..55
PREAMBLE@0..55
NEW_COMMAND_DEFINITION@0..11
NEW_COMMAND_DEFINITION@0..17
COMMAND_NAME@0..11 "\\newcommand"
GENERIC_COMMAND@11..17
COMMAND_NAME@11..14 "\\ö"
CURLY_GROUP@14..17
L_CURLY@14..15 "{"
@ -2699,9 +2718,8 @@ fn test_issue_857() {
L_CURLY@35..36 "{"
R_CURLY@36..37 "}"
WHITESPACE@37..38 "\n"
NEW_COMMAND_DEFINITION@38..49
NEW_COMMAND_DEFINITION@38..55
COMMAND_NAME@38..49 "\\newcommand"
GENERIC_COMMAND@49..55
COMMAND_NAME@49..53 "\\123"
CURLY_GROUP@53..55
L_CURLY@53..54 "{"

View file

@ -23,9 +23,7 @@ pub(super) fn find_all(context: &mut ReferenceContext) -> Option<()> {
latex::OldCommandDefinition::cast(node.clone())
.and_then(|node| node.name())
.or_else(|| {
latex::NewCommandDefinition::cast(node)
.and_then(|node| node.name())
.and_then(|group| group.command())
latex::NewCommandDefinition::cast(node).and_then(|node| node.name())
})
.map(|name| Span::command(&name))
})

View file

@ -615,8 +615,20 @@ impl NewCommandDefinition {
self.syntax().first_token()
}
pub fn name(&self) -> Option<CurlyGroupCommand> {
self.syntax().children().find_map(CurlyGroupCommand::cast)
pub fn name(&self) -> Option<SyntaxToken> {
let token = self
.syntax()
.children_with_tokens()
.skip(1)
.filter_map(|elem| elem.into_token())
.find(|token| token.kind() == COMMAND_NAME);
token.or_else(|| {
self.syntax()
.children()
.find_map(CurlyGroupCommand::cast)
.and_then(|x| x.command())
})
}
pub fn implementation(&self) -> Option<CurlyGroup> {