diff options
Diffstat (limited to 'support/texlab/crates/parser')
-rw-r--r-- | support/texlab/crates/parser/Cargo.toml | 8 | ||||
-rw-r--r-- | support/texlab/crates/parser/src/bibtex.rs | 54 | ||||
-rw-r--r-- | support/texlab/crates/parser/src/bibtex/tests.rs | 277 | ||||
-rw-r--r-- | support/texlab/crates/parser/src/build_log.rs | 2 | ||||
-rw-r--r-- | support/texlab/crates/parser/src/config.rs | 26 | ||||
-rw-r--r-- | support/texlab/crates/parser/src/file_list.rs | 44 | ||||
-rw-r--r-- | support/texlab/crates/parser/src/latex/lexer/types.rs | 49 | ||||
-rw-r--r-- | support/texlab/crates/parser/src/latexmkrc.rs | 8 | ||||
-rw-r--r-- | support/texlab/crates/parser/src/lib.rs | 6 | ||||
-rw-r--r-- | support/texlab/crates/parser/src/util.rs | 52 |
10 files changed, 441 insertions, 85 deletions
diff --git a/support/texlab/crates/parser/Cargo.toml b/support/texlab/crates/parser/Cargo.toml index 39fa34ef63..2faadcb262 100644 --- a/support/texlab/crates/parser/Cargo.toml +++ b/support/texlab/crates/parser/Cargo.toml @@ -7,16 +7,16 @@ edition.workspace = true rust-version.workspace = true [dependencies] -log = "0.4.21" +log = "0.4.22" logos = "0.14.0" once_cell = "1.19.0" pathdiff = "0.2.1" -regex = "1.10.4" +regex = "1.10.5" rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" syntax = { path = "../syntax" } tempfile = "3.10.1" -versions = "6.2.0" +versions = "6.3.0" [dev-dependencies] expect-test = "1.5.0" diff --git a/support/texlab/crates/parser/src/bibtex.rs b/support/texlab/crates/parser/src/bibtex.rs index e2cde27dec..4d1e561dbe 100644 --- a/support/texlab/crates/parser/src/bibtex.rs +++ b/support/texlab/crates/parser/src/bibtex.rs @@ -2,6 +2,8 @@ use logos::Logos; use rowan::{GreenNode, GreenNodeBuilder}; use syntax::bibtex::SyntaxKind::{self, *}; +use crate::util::lex_command_name; + pub fn parse_bibtex(input: &str) -> GreenNode { let mut ptr = TokenPtr { builder: GreenNodeBuilder::new(), @@ -135,8 +137,8 @@ fn curly_group(mut ptr: TokenPtr<ContentToken>) -> TokenPtr<ContentToken> { | ContentToken::Quote | ContentToken::Word => ptr.bump(), ContentToken::LCurly => ptr = curly_group(ptr), - ContentToken::AccentName => ptr = accent(ptr), - ContentToken::CommandName => ptr = command(ptr), + ContentToken::CommandName(CommandName::Accent) => ptr = accent(ptr), + ContentToken::CommandName(CommandName::Generic) => ptr = command(ptr), }; } @@ -160,8 +162,8 @@ fn quote_group(mut ptr: TokenPtr<ContentToken>) -> TokenPtr<ContentToken> { | ContentToken::Integer | ContentToken::Word => ptr.bump(), ContentToken::LCurly => ptr = curly_group(ptr), - ContentToken::AccentName => ptr = accent(ptr), - ContentToken::CommandName => ptr = command(ptr), + ContentToken::CommandName(CommandName::Accent) => ptr = accent(ptr), + ContentToken::CommandName(CommandName::Generic) => ptr = command(ptr), }; } @@ -181,7 +183,7 @@ fn accent(mut ptr: TokenPtr<ContentToken>) -> TokenPtr<ContentToken> { ptr.expect(ContentToken::Whitespace); } - if ptr.at(ContentToken::Word) || ptr.at(ContentToken::CommandName) { + if ptr.at(ContentToken::Word) || ptr.at(ContentToken::CommandName(CommandName::Generic)) { ptr.bump(); } @@ -355,31 +357,29 @@ enum ContentToken { #[token(r#"~"#)] Nbsp, - #[token(r#"\`"#)] - #[token(r#"\'"#)] - #[token(r#"\^"#)] - #[token(r#"\""#)] - #[token(r#"\H"#)] - #[token(r#"\~"#)] - #[token(r#"\c"#)] - #[token(r#"\k"#)] - #[token(r#"\="#)] - #[token(r#"\b"#)] - #[token(r#"\."#)] - #[token(r#"\d"#)] - #[token(r#"\r"#)] - #[token(r#"\u"#)] - #[token(r#"\v"#)] - #[token(r#"\t"#)] - AccentName, - - #[regex(r"\\([^\r\n]|[@a-zA-Z:_]+\*?)?")] - CommandName, + #[regex(r"\\", |lexer| { CommandName::from(lex_command_name(lexer)) })] + CommandName(CommandName), #[regex(r#"[^\s"\{\}\\~,]+"#)] Word, } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +enum CommandName { + Generic, + Accent, +} + +impl From<&str> for CommandName { + fn from(value: &str) -> Self { + match value { + "`" | "'" | "^" | "\"" | "H" | "~" | "c" | "k" | "=" | "b" | "." | "d" | "r" | "u" + | "v" | "t" => CommandName::Accent, + _ => CommandName::Generic, + } + } +} + impl From<RootToken> for SyntaxKind { fn from(token: RootToken) -> Self { match token { @@ -428,8 +428,8 @@ impl From<ContentToken> for SyntaxKind { ContentToken::Quote => QUOTE, ContentToken::Integer => INTEGER, ContentToken::Nbsp => NBSP, - ContentToken::AccentName => ACCENT_NAME, - ContentToken::CommandName => COMMAND_NAME, + ContentToken::CommandName(CommandName::Accent) => ACCENT_NAME, + ContentToken::CommandName(CommandName::Generic) => COMMAND_NAME, ContentToken::Word => WORD, } } diff --git a/support/texlab/crates/parser/src/bibtex/tests.rs b/support/texlab/crates/parser/src/bibtex/tests.rs index e2bb1314d8..f0872b4ca6 100644 --- a/support/texlab/crates/parser/src/bibtex/tests.rs +++ b/support/texlab/crates/parser/src/bibtex/tests.rs @@ -3992,3 +3992,280 @@ fn test_rivest_1978() { "#]], ); } + +#[test] +fn test_cuesta_2002() { + check( + r#"@INPROCEEDINGS{Cuesta02, + author = {Cuesta, Carlos E. and de la Fuente, Pablo and Barrio-Sol\órzano, Manuel and Beato, Encarnaci\ón}, + title = {{Coordination in a Reflective Architecture Description Language}}, + booktitle = {{Proceedings of the 5th International Conference on Coordination Models and Languages (COORDINATION'02)}}, + year = 2002, + editor = {Arbab, Fahrad and Talcott, Carolyn}, + pages = {141--148}, + address = {York, United Kingdom}, + organization = {}, + publisher = {Springer}, + volume = {}, + number = {}, + series = {Lecture Notes in Computer Science 2315}, + month = apr, + note = {} +}"#, + expect![[r#" + ROOT@0..617 + ENTRY@0..617 + TYPE@0..14 "@INPROCEEDINGS" + L_DELIM@14..15 "{" + NAME@15..23 "Cuesta02" + COMMA@23..24 "," + WHITESPACE@24..27 "\n " + FIELD@27..136 + NAME@27..33 "author" + WHITESPACE@33..34 " " + EQ@34..35 "=" + WHITESPACE@35..36 " " + CURLY_GROUP@36..135 + L_CURLY@36..37 "{" + WORD@37..43 "Cuesta" + COMMA@43..44 "," + WHITESPACE@44..45 " " + WORD@45..51 "Carlos" + WHITESPACE@51..52 " " + WORD@52..54 "E." + WHITESPACE@54..55 " " + WORD@55..58 "and" + WHITESPACE@58..59 " " + WORD@59..61 "de" + WHITESPACE@61..62 " " + WORD@62..64 "la" + WHITESPACE@64..65 " " + WORD@65..71 "Fuente" + COMMA@71..72 "," + WHITESPACE@72..73 " " + WORD@73..78 "Pablo" + WHITESPACE@78..79 " " + WORD@79..82 "and" + WHITESPACE@82..83 " " + WORD@83..93 "Barrio-Sol" + COMMAND@93..101 + COMMAND_NAME@93..101 "\\órzano" + COMMA@101..102 "," + WHITESPACE@102..103 " " + WORD@103..109 "Manuel" + WHITESPACE@109..110 " " + WORD@110..113 "and" + WHITESPACE@113..114 " " + WORD@114..119 "Beato" + COMMA@119..120 "," + WHITESPACE@120..121 " " + WORD@121..130 "Encarnaci" + COMMAND@130..134 + COMMAND_NAME@130..134 "\\ón" + R_CURLY@134..135 "}" + COMMA@135..136 "," + WHITESPACE@136..139 "\n " + FIELD@139..214 + NAME@139..144 "title" + WHITESPACE@144..145 " " + EQ@145..146 "=" + WHITESPACE@146..147 " " + CURLY_GROUP@147..213 + L_CURLY@147..148 "{" + CURLY_GROUP@148..212 + L_CURLY@148..149 "{" + WORD@149..161 "Coordination" + WHITESPACE@161..162 " " + WORD@162..164 "in" + WHITESPACE@164..165 " " + WORD@165..166 "a" + WHITESPACE@166..167 " " + WORD@167..177 "Reflective" + WHITESPACE@177..178 " " + WORD@178..190 "Architecture" + WHITESPACE@190..191 " " + WORD@191..202 "Description" + WHITESPACE@202..203 " " + WORD@203..211 "Language" + R_CURLY@211..212 "}" + R_CURLY@212..213 "}" + COMMA@213..214 "," + WHITESPACE@214..217 "\n " + FIELD@217..336 + NAME@217..226 "booktitle" + WHITESPACE@226..227 " " + EQ@227..228 "=" + WHITESPACE@228..229 " " + CURLY_GROUP@229..335 + L_CURLY@229..230 "{" + CURLY_GROUP@230..334 + L_CURLY@230..231 "{" + WORD@231..242 "Proceedings" + WHITESPACE@242..243 " " + WORD@243..245 "of" + WHITESPACE@245..246 " " + WORD@246..249 "the" + WHITESPACE@249..250 " " + WORD@250..253 "5th" + WHITESPACE@253..254 " " + WORD@254..267 "International" + WHITESPACE@267..268 " " + WORD@268..278 "Conference" + WHITESPACE@278..279 " " + WORD@279..281 "on" + WHITESPACE@281..282 " " + WORD@282..294 "Coordination" + WHITESPACE@294..295 " " + WORD@295..301 "Models" + WHITESPACE@301..302 " " + WORD@302..305 "and" + WHITESPACE@305..306 " " + WORD@306..315 "Languages" + WHITESPACE@315..316 " " + WORD@316..333 "(COORDINATION'02)" + R_CURLY@333..334 "}" + R_CURLY@334..335 "}" + COMMA@335..336 "," + WHITESPACE@336..339 "\n " + FIELD@339..351 + NAME@339..343 "year" + WHITESPACE@343..344 " " + EQ@344..345 "=" + WHITESPACE@345..346 " " + LITERAL@346..350 + INTEGER@346..350 "2002" + COMMA@350..351 "," + WHITESPACE@351..354 "\n " + FIELD@354..400 + NAME@354..360 "editor" + WHITESPACE@360..361 " " + EQ@361..362 "=" + WHITESPACE@362..363 " " + CURLY_GROUP@363..399 + L_CURLY@363..364 "{" + WORD@364..369 "Arbab" + COMMA@369..370 "," + WHITESPACE@370..371 " " + WORD@371..377 "Fahrad" + WHITESPACE@377..378 " " + WORD@378..381 "and" + WHITESPACE@381..382 " " + WORD@382..389 "Talcott" + COMMA@389..390 "," + WHITESPACE@390..391 " " + WORD@391..398 "Carolyn" + R_CURLY@398..399 "}" + COMMA@399..400 "," + WHITESPACE@400..403 "\n " + FIELD@403..422 + NAME@403..408 "pages" + WHITESPACE@408..409 " " + EQ@409..410 "=" + WHITESPACE@410..411 " " + CURLY_GROUP@411..421 + L_CURLY@411..412 "{" + WORD@412..420 "141--148" + R_CURLY@420..421 "}" + COMMA@421..422 "," + WHITESPACE@422..425 "\n " + FIELD@425..458 + NAME@425..432 "address" + WHITESPACE@432..433 " " + EQ@433..434 "=" + WHITESPACE@434..435 " " + CURLY_GROUP@435..457 + L_CURLY@435..436 "{" + WORD@436..440 "York" + COMMA@440..441 "," + WHITESPACE@441..442 " " + WORD@442..448 "United" + WHITESPACE@448..449 " " + WORD@449..456 "Kingdom" + R_CURLY@456..457 "}" + COMMA@457..458 "," + WHITESPACE@458..461 "\n " + FIELD@461..479 + NAME@461..473 "organization" + WHITESPACE@473..474 " " + EQ@474..475 "=" + WHITESPACE@475..476 " " + CURLY_GROUP@476..478 + L_CURLY@476..477 "{" + R_CURLY@477..478 "}" + COMMA@478..479 "," + WHITESPACE@479..482 "\n " + FIELD@482..505 + NAME@482..491 "publisher" + WHITESPACE@491..492 " " + EQ@492..493 "=" + WHITESPACE@493..494 " " + CURLY_GROUP@494..504 + L_CURLY@494..495 "{" + WORD@495..503 "Springer" + R_CURLY@503..504 "}" + COMMA@504..505 "," + WHITESPACE@505..508 "\n " + FIELD@508..520 + NAME@508..514 "volume" + WHITESPACE@514..515 " " + EQ@515..516 "=" + WHITESPACE@516..517 " " + CURLY_GROUP@517..519 + L_CURLY@517..518 "{" + R_CURLY@518..519 "}" + COMMA@519..520 "," + WHITESPACE@520..523 "\n " + FIELD@523..535 + NAME@523..529 "number" + WHITESPACE@529..530 " " + EQ@530..531 "=" + WHITESPACE@531..532 " " + CURLY_GROUP@532..534 + L_CURLY@532..533 "{" + R_CURLY@533..534 "}" + COMMA@534..535 "," + WHITESPACE@535..538 "\n " + FIELD@538..588 + NAME@538..544 "series" + WHITESPACE@544..545 " " + EQ@545..546 "=" + WHITESPACE@546..547 " " + CURLY_GROUP@547..587 + L_CURLY@547..548 "{" + WORD@548..555 "Lecture" + WHITESPACE@555..556 " " + WORD@556..561 "Notes" + WHITESPACE@561..562 " " + WORD@562..564 "in" + WHITESPACE@564..565 " " + WORD@565..573 "Computer" + WHITESPACE@573..574 " " + WORD@574..581 "Science" + WHITESPACE@581..582 " " + INTEGER@582..586 "2315" + R_CURLY@586..587 "}" + COMMA@587..588 "," + WHITESPACE@588..591 "\n " + FIELD@591..603 + NAME@591..596 "month" + WHITESPACE@596..597 " " + EQ@597..598 "=" + WHITESPACE@598..599 " " + LITERAL@599..602 + NAME@599..602 "apr" + COMMA@602..603 "," + WHITESPACE@603..606 "\n " + FIELD@606..616 + NAME@606..610 "note" + WHITESPACE@610..611 " " + EQ@611..612 "=" + WHITESPACE@612..613 " " + CURLY_GROUP@613..615 + L_CURLY@613..614 "{" + R_CURLY@614..615 "}" + WHITESPACE@615..616 "\n" + R_DELIM@616..617 "}" + + "#]], + ) +} diff --git a/support/texlab/crates/parser/src/build_log.rs b/support/texlab/crates/parser/src/build_log.rs index 420d2dda20..c1f59bd086 100644 --- a/support/texlab/crates/parser/src/build_log.rs +++ b/support/texlab/crates/parser/src/build_log.rs @@ -36,7 +36,7 @@ pub fn parse_build_log(log: &str) -> BuildLog { let warnings = extract_matches(&log, &ranges, &WARNING_REGEX, BuildErrorLevel::Warning); let bad_boxes = extract_matches(&log, &ranges, &BAD_BOX_REGEX, BuildErrorLevel::Warning); - let errors = vec![tex_errors, warnings, bad_boxes].concat(); + let errors = [tex_errors, warnings, bad_boxes].concat(); BuildLog { errors } } diff --git a/support/texlab/crates/parser/src/config.rs b/support/texlab/crates/parser/src/config.rs index f81083f132..2c5380448f 100644 --- a/support/texlab/crates/parser/src/config.rs +++ b/support/texlab/crates/parser/src/config.rs @@ -3,12 +3,15 @@ use rustc_hash::FxHashSet; #[derive(Debug)] pub struct SyntaxConfig { pub follow_package_links: bool, + pub use_file_list: bool, pub math_environments: FxHashSet<String>, pub enum_environments: FxHashSet<String>, pub verbatim_environments: FxHashSet<String>, pub citation_commands: FxHashSet<String>, pub label_definition_commands: FxHashSet<String>, + pub label_definition_prefixes: Vec<(String, String)>, pub label_reference_commands: FxHashSet<String>, + pub label_reference_prefixes: Vec<(String, String)>, } impl Default for SyntaxConfig { @@ -38,19 +41,32 @@ impl Default for SyntaxConfig { .map(ToString::to_string) .collect(); + let label_definition_prefixes = DEFAULT_LABEL_DEFINITION_PREFIXES + .iter() + .map(|(x, y)| (ToString::to_string(x), ToString::to_string(y))) + .collect(); + let label_reference_commands = DEFAULT_LABEL_REFERENCE_COMMANDS .iter() .map(ToString::to_string) .collect(); + let label_reference_prefixes = DEFAULT_LABEL_REFERENCE_PREFIXES + .iter() + .map(|(x, y)| (ToString::to_string(x), ToString::to_string(y))) + .collect(); + Self { follow_package_links: false, + use_file_list: false, math_environments, enum_environments, verbatim_environments, citation_commands, label_definition_commands, + label_definition_prefixes, label_reference_commands, + label_reference_prefixes, } } } @@ -170,7 +186,9 @@ static DEFAULT_CITATION_COMMANDS: &[&str] = &[ "citeA*", ]; -static DEFAULT_LABEL_DEFINITION_COMMANDS: &[&str] = &["label"]; +static DEFAULT_LABEL_DEFINITION_COMMANDS: &[&str] = &["label", "zlabel"]; + +static DEFAULT_LABEL_DEFINITION_PREFIXES: &[(&str, &str)] = &[]; static DEFAULT_LABEL_REFERENCE_COMMANDS: &[&str] = &[ "ref", @@ -182,6 +200,10 @@ static DEFAULT_LABEL_REFERENCE_COMMANDS: &[&str] = &[ "cref*", "Cref", "Cref*", + "zcref", + "zcref*", + "zcpageref", + "zcpageref*", "namecref", "nameCref", "lcnamecref", @@ -192,3 +214,5 @@ static DEFAULT_LABEL_REFERENCE_COMMANDS: &[&str] = &[ "labelcpageref", "eqref", ]; + +static DEFAULT_LABEL_REFERENCE_PREFIXES: &[(&str, &str)] = &[]; diff --git a/support/texlab/crates/parser/src/file_list.rs b/support/texlab/crates/parser/src/file_list.rs new file mode 100644 index 0000000000..c892ebfeef --- /dev/null +++ b/support/texlab/crates/parser/src/file_list.rs @@ -0,0 +1,44 @@ +use syntax::file_list::FileList; + +pub fn parse_file_list(input: &str) -> FileList { + let mut file_list = FileList::default(); + for line in input.lines() { + if let Some(working_dir) = line.strip_prefix("PWD ") { + file_list.working_dir = Some(working_dir.into()); + } else if let Some(input) = line.strip_prefix("INPUT ") { + file_list.inputs.insert(input.into()); + } else if let Some(output) = line.strip_prefix("OUTPUT ") { + file_list.outputs.insert(output.into()); + } + } + + file_list +} + +#[cfg(test)] +mod tests { + use rustc_hash::FxHashSet; + + use super::*; + + #[test] + fn test_parse_file_list() { + let input = r#" +PWD /home/user +INPUT file1.tex +INPUT file1.tex +OUTPUT file2.pdf"#; + + let expected_file_list = FileList { + working_dir: Some("/home/user".into()), + inputs: FxHashSet::from_iter(["file1.tex".into()]), + outputs: FxHashSet::from_iter(["file2.pdf".into()]), + }; + let actual_file_list = parse_file_list(input); + assert_eq!(actual_file_list, expected_file_list); + + assert_eq!(actual_file_list.working_dir, Some("/home/user".into())); + assert_eq!(actual_file_list.inputs.len(), 1); + assert_eq!(actual_file_list.outputs.len(), 1); + } +} diff --git a/support/texlab/crates/parser/src/latex/lexer/types.rs b/support/texlab/crates/parser/src/latex/lexer/types.rs index 896ce01c05..85e542792b 100644 --- a/support/texlab/crates/parser/src/latex/lexer/types.rs +++ b/support/texlab/crates/parser/src/latex/lexer/types.rs @@ -1,5 +1,7 @@ use logos::Logos; +use crate::util::lex_command_name; + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Logos)] pub enum Token { #[regex(r"[\r\n]+", priority = 2)] @@ -44,55 +46,10 @@ pub enum Token { #[regex(r"\$\$?")] Dollar, - #[regex(r"\\", lex_command_name)] + #[regex(r"\\", |lexer| { lex_command_name(lexer); CommandName::Generic } )] CommandName(CommandName), } -fn lex_command_name(lexer: &mut logos::Lexer<Token>) -> CommandName { - let input = &lexer.source()[lexer.span().end..]; - - let mut chars = input.chars().peekable(); - let Some(c) = chars.next() else { - return CommandName::Generic; - }; - - if c.is_whitespace() { - return CommandName::Generic; - } - - lexer.bump(c.len_utf8()); - if !c.is_alphanumeric() && c != '@' { - return CommandName::Generic; - } - - while let Some(c) = chars.next() { - match c { - '*' => { - lexer.bump(c.len_utf8()); - break; - } - c if c.is_alphanumeric() => { - lexer.bump(c.len_utf8()); - } - '_' => { - if !matches!(chars.peek(), Some(c) if c.is_alphanumeric()) { - break; - } - - lexer.bump(c.len_utf8()); - } - '@' | ':' => { - lexer.bump(c.len_utf8()); - } - _ => { - break; - } - } - } - - CommandName::Generic -} - #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] pub enum CommandName { Generic, diff --git a/support/texlab/crates/parser/src/latexmkrc.rs b/support/texlab/crates/parser/src/latexmkrc.rs index 6013a5f646..58c348d01f 100644 --- a/support/texlab/crates/parser/src/latexmkrc.rs +++ b/support/texlab/crates/parser/src/latexmkrc.rs @@ -28,9 +28,9 @@ mod v483 { .current_dir(temp_dir.path()) .output()?; - let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); - let (aux_dir, out_dir) = stderr.lines().find_map(extract_dirs).ok_or_else(|| { + let (aux_dir, out_dir) = stdout.lines().find_map(extract_dirs).ok_or_else(|| { std::io::Error::new( std::io::ErrorKind::InvalidData, "Normalized aux and out dir were not found in latexmk output", @@ -106,7 +106,7 @@ mod v484 { let mut it = lines .skip_while(|line| !line.starts_with("Latexmk: Normalized aux dir and out dirs:")) .nth(1)? - .split(","); + .split(','); let aux_dir = it.next()?.trim().strip_prefix('\'')?.strip_suffix('\'')?; @@ -133,7 +133,7 @@ pub fn parse_latexmkrc(input: &str, src_dir: &Path) -> std::io::Result<Latexmkrc .as_ref() .and_then(|line| Some((line.find("Version")?, line))) .and_then(|(i, line)| line[i..].trim_end().strip_prefix("Version ")) - .and_then(|text| versions::Versioning::new(text)); + .and_then(versions::Versioning::new); let result = if version.map_or(false, |v| v >= versions::Versioning::new("4.84").unwrap()) { v484::parse_latexmkrc(input, src_dir) diff --git a/support/texlab/crates/parser/src/lib.rs b/support/texlab/crates/parser/src/lib.rs index 2a4190b7e2..b3952d6ea5 100644 --- a/support/texlab/crates/parser/src/lib.rs +++ b/support/texlab/crates/parser/src/lib.rs @@ -1,10 +1,12 @@ mod bibtex; mod build_log; mod config; +mod file_list; mod latex; mod latexmkrc; +pub(crate) mod util; pub use self::{ - bibtex::parse_bibtex, build_log::parse_build_log, config::*, latex::parse_latex, - latexmkrc::parse_latexmkrc, + bibtex::parse_bibtex, build_log::parse_build_log, config::*, file_list::parse_file_list, + latex::parse_latex, latexmkrc::parse_latexmkrc, }; diff --git a/support/texlab/crates/parser/src/util.rs b/support/texlab/crates/parser/src/util.rs new file mode 100644 index 0000000000..cb147c493b --- /dev/null +++ b/support/texlab/crates/parser/src/util.rs @@ -0,0 +1,52 @@ +use std::ops::{Index, Range}; + +use logos::{Logos, Source}; + +pub fn lex_command_name<'a, T>(lexer: &mut logos::Lexer<'a, T>) -> &'a str +where + T: Logos<'a>, + T::Source: Index<Range<usize>, Output = str>, +{ + let start = lexer.span().end; + let input = &lexer.source()[start..lexer.source().len()]; + + let mut chars = input.chars().peekable(); + let Some(c) = chars.next() else { + return ""; + }; + + if c.is_whitespace() { + return ""; + } + + lexer.bump(c.len_utf8()); + + if c.is_alphanumeric() || c == '@' { + while let Some(c) = chars.next() { + match c { + '*' => { + lexer.bump(c.len_utf8()); + break; + } + c if c.is_alphanumeric() => { + lexer.bump(c.len_utf8()); + } + '_' => { + if !matches!(chars.peek(), Some(c) if c.is_alphanumeric()) { + break; + } + + lexer.bump(c.len_utf8()); + } + '@' | ':' => { + lexer.bump(c.len_utf8()); + } + _ => { + break; + } + } + } + } + + &lexer.source()[start..lexer.span().end] +} |