From cff468c18ca39d94991f8bb5c70107c2d697a046 Mon Sep 17 00:00:00 2001 From: Maruthan G Date: Sat, 11 Apr 2026 14:19:05 +0530 Subject: [PATCH] fix: respect beautify ignore tags during range formatting (#215) Range formatting now expands to include ignore block boundaries so js-beautify can recognise and skip ignored content. Also relaxed the directive regex in beautify-html.js and beautify-css.js to allow flexible whitespace in ignore comments. --- src/beautify/beautify-css.js | 2 +- src/beautify/beautify-html.js | 2 +- src/services/htmlFormatter.ts | 47 +++++++++++++++++- src/test/formatter.test.ts | 92 +++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 3 deletions(-) diff --git a/src/beautify/beautify-css.js b/src/beautify/beautify-css.js index 9127d65..b5b0b5b 100644 --- a/src/beautify/beautify-css.js +++ b/src/beautify/beautify-css.js @@ -936,7 +936,7 @@ module.exports.InputScanner = InputScanner; function Directives(start_block_pattern, end_block_pattern) { start_block_pattern = typeof start_block_pattern === 'string' ? start_block_pattern : start_block_pattern.source; end_block_pattern = typeof end_block_pattern === 'string' ? end_block_pattern : end_block_pattern.source; - this.__directives_block_pattern = new RegExp(start_block_pattern + / beautify( \w+[:]\w+)+ /.source + end_block_pattern, 'g'); + this.__directives_block_pattern = new RegExp(start_block_pattern + /\s*beautify(\s+\w+[:]\w+)+\s*/.source + end_block_pattern, 'g'); this.__directive_pattern = / (\w+)[:](\w+)/g; this.__directives_end_ignore_pattern = new RegExp(start_block_pattern + /\sbeautify\signore:end\s/.source + end_block_pattern, 'g'); diff --git a/src/beautify/beautify-html.js b/src/beautify/beautify-html.js index cc452ea..b6a70c6 100644 --- a/src/beautify/beautify-html.js +++ b/src/beautify/beautify-html.js @@ -1445,7 +1445,7 @@ module.exports.Pattern = Pattern; function Directives(start_block_pattern, end_block_pattern) { start_block_pattern = typeof start_block_pattern === 'string' ? start_block_pattern : start_block_pattern.source; end_block_pattern = typeof end_block_pattern === 'string' ? end_block_pattern : end_block_pattern.source; - this.__directives_block_pattern = new RegExp(start_block_pattern + / beautify( \w+[:]\w+)+ /.source + end_block_pattern, 'g'); + this.__directives_block_pattern = new RegExp(start_block_pattern + /\s*beautify(\s+\w+[:]\w+)+\s*/.source + end_block_pattern, 'g'); this.__directive_pattern = / (\w+)[:](\w+)/g; this.__directives_end_ignore_pattern = new RegExp(start_block_pattern + /\sbeautify\signore:end\s/.source + end_block_pattern, 'g'); diff --git a/src/services/htmlFormatter.ts b/src/services/htmlFormatter.ts index b9ec4d7..c1cfa63 100644 --- a/src/services/htmlFormatter.ts +++ b/src/services/htmlFormatter.ts @@ -14,6 +14,16 @@ export function format(document: TextDocument, range: Range | undefined, options const tabSize = options.tabSize || 4; if (range) { let startOffset = document.offsetAt(range.start); + let endOffset = document.offsetAt(range.end); + + // If the range falls within a block, + // expand the range to include the ignore boundaries so js-beautify can + // recognise them and skip formatting the ignored content. + const ignoreExpanded = expandRangeToIgnoreBlock(value, startOffset, endOffset); + if (ignoreExpanded) { + startOffset = ignoreExpanded.start; + endOffset = ignoreExpanded.end; + } // include all leading whitespace iff at the beginning of the line let extendedStart = startOffset; @@ -30,7 +40,6 @@ export function format(document: TextDocument, range: Range | undefined, options } // include all following whitespace until the end of the line - let endOffset = document.offsetAt(range.end); let extendedEnd = endOffset; while (extendedEnd < value.length && isWhitespace(value, extendedEnd)) { extendedEnd++; @@ -174,3 +183,39 @@ function isEOL(text: string, offset: number) { function isWhitespace(text: string, offset: number) { return ' \t'.indexOf(text.charAt(offset)) !== -1; } + +const ignoreStartPattern = //g; +const ignoreEndPattern = //g; + +function expandRangeToIgnoreBlock(text: string, startOffset: number, endOffset: number): { start: number; end: number } | null { + // Find all ignore:start markers before (or at) the range start + ignoreStartPattern.lastIndex = 0; + let lastIgnoreStart: RegExpExecArray | null = null; + let match: RegExpExecArray | null; + while ((match = ignoreStartPattern.exec(text)) !== null) { + if (match.index > startOffset) { + break; + } + lastIgnoreStart = match; + } + + if (!lastIgnoreStart) { + return null; + } + + // Find the matching ignore:end after the ignore:start + ignoreEndPattern.lastIndex = lastIgnoreStart.index + lastIgnoreStart[0].length; + const ignoreEnd = ignoreEndPattern.exec(text); + if (!ignoreEnd) { + return null; + } + + const blockEnd = ignoreEnd.index + ignoreEnd[0].length; + + // Check that the range falls within this ignore block + if (startOffset >= lastIgnoreStart.index && endOffset <= blockEnd) { + return { start: lastIgnoreStart.index, end: blockEnd }; + } + + return null; +} diff --git a/src/test/formatter.test.ts b/src/test/formatter.test.ts index 0e50a7f..83c824a 100644 --- a/src/test/formatter.test.ts +++ b/src/test/formatter.test.ts @@ -346,6 +346,98 @@ suite('HTML Formatter', () => { format(content, expected); }); + test('beautify ignore:start/end - full document', () => { + var content = [ + '', + '', + '', + '
', + '

text

', + '
', + '', + '
', + '
', + '', + '' + ].join('\n'); + + var expected = [ + '', + '', + '', + ' ', + '
', + '

text

', + '
', + '', + '
', + '
', + '', + '', + '', + ].join('\n'); + + format(content, expected); + }); + + test('beautify ignore:start/end - range format inside ignore block', () => { + var content = [ + '', + '|
', + '

text

', + '
|', + '' + ].join('\n'); + + var expected = [ + '', + '
', + '

text

', + '
', + '' + ].join('\n'); + + format(content, expected); + }); + + test('beautify ignore - extra whitespace in comment', () => { + var content = [ + '', + '
', + '
', + '' + ].join('\n'); + + var expected = [ + '', + '
', + '
', + '' + ].join('\n'); + + format(content, expected); + }); + + test('beautify ignore - go template syntax preserved', () => { + var content = [ + '', + '
', + ' {{template "scripts/file-size".}}', + '
', + '' + ].join('\n'); + + var expected = [ + '', + '
', + ' {{template "scripts/file-size".}}', + '
', + '' + ].join('\n'); + + format(content, expected); + }); + }); suite('HTML Formatter - Embedded CSS', () => {