diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bb44e8d..536031b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). middle are adjusted to retain their original significant leading whitespace. 2. Internal line endings are normalised to match the configured line ending. 3. Invalid multiline strings are left untouched. +- All keywords are now set to lowercase. This includes reserved words (like `begin` and `const`) but also includes + non-reserved words (like `absolute` and `override`) when used in the context that makes them special. ## [0.6.0] - 2025-08-25 diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 206df1e3..35147f9c 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added setting to enable internal formatting of multiline strings: `OptimisingLineFormatterSettings.format_multiline_strings`. +- Added `LowercaseKeywords` to convert instances of keywords to lowercase. ### Changed diff --git a/core/src/rules/lowercase_keywords.rs b/core/src/rules/lowercase_keywords.rs new file mode 100644 index 00000000..b1e11d15 --- /dev/null +++ b/core/src/rules/lowercase_keywords.rs @@ -0,0 +1,59 @@ +use crate::prelude::*; + +pub struct LowercaseKeywords {} + +impl LogicalLineFileFormatter for LowercaseKeywords { + fn format(&self, formatted_tokens: &mut FormattedTokens<'_>, _input: &[LogicalLine]) { + for (tok, _) in formatted_tokens.tokens_mut() { + let Ok(tok) = tok else { continue }; + if matches!(tok.get_token_type(), TokenType::Keyword(_)) + && tok.get_content().bytes().any(|b| b.is_ascii_uppercase()) + { + tok.set_content(tok.get_content().to_ascii_lowercase()); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn formatter() -> Formatter { + Formatter::builder() + .lexer(DelphiLexer {}) + .parser(DelphiLogicalLineParser {}) + .token_ignorer(FormattingToggler {}) + .file_formatter(LowercaseKeywords {}) + .reconstructor(default_test_reconstructor()) + .build() + } + + formatter_test_group!( + tests, + full_uppercase = { + "BEGIN END", + "begin end" + }, + partial_uppercase = { + "begIn enD", + "begin end" + }, + lowercase = { + "begin end", + "begin end" + }, + impure_keyword_is_ignored = { + "ABSOLUTE := 0", + "ABSOLUTE := 0", + }, + impure_keyword_is_formatted = { + "var a: b ABSOLUTE c", + "var a: b absolute c", + }, + ignored_tokens = { + "{pasfmt off} BEGIN {pasfmt on} END", + "{pasfmt off} BEGIN {pasfmt on} end", + } + ); +} diff --git a/core/src/rules/mod.rs b/core/src/rules/mod.rs index 37561412..1472765a 100644 --- a/core/src/rules/mod.rs +++ b/core/src/rules/mod.rs @@ -4,6 +4,7 @@ pub mod eof_newline; pub mod formatting_toggle; pub mod generics_consolidator; pub mod ignore_asm_instructions; +pub mod lowercase_keywords; pub mod optimising_line_formatter; pub mod token_spacing; @@ -13,5 +14,6 @@ pub use eof_newline::*; pub use formatting_toggle::*; pub use generics_consolidator::*; pub use ignore_asm_instructions::*; +pub use lowercase_keywords::*; pub use optimising_line_formatter::*; pub use token_spacing::*; diff --git a/front-end/src/lib.rs b/front-end/src/lib.rs index 93d68319..bed2d258 100644 --- a/front-end/src/lib.rs +++ b/front-end/src/lib.rs @@ -322,6 +322,7 @@ pub fn make_formatter(config: &FormattingConfig) -> Formatter { .token_ignorer(FormattingToggler {}) .token_ignorer(IgnoreAsmIstructions {}) .file_formatter(TokenSpacing {}) + .file_formatter(LowercaseKeywords {}) .line_formatter(FormatterSelector::new( |logical_line_type| match logical_line_type { LogicalLineType::Eof => Some(eof_newline_formatter),