From e4f0e2b3f6e3269a87e175dd0e52ccdceac0a2e4 Mon Sep 17 00:00:00 2001 From: AgentHagu Date: Mon, 13 Jan 2025 22:58:09 +0800 Subject: [PATCH 01/12] Update highlighter functionality to support new regex --- .../highlight/HighlightRuleComponent.ts | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts index 61e16cd3a9..eeba208466 100644 --- a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts +++ b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts @@ -1,6 +1,6 @@ import { splitCodeAndIndentation } from './helper'; -const LINESLICE_CHAR_REGEX = /(\d+)\[(\d*):(\d*)]/; +const LINESLICE_CHAR_REGEX = /(\d+)\[(\d*):(\d*)](\+?)/; const LINESLICE_WORD_REGEX = /(\d+)\[(\d*)::(\d*)]/; const LINEPART_REGEX = /(\d+)\[(["'])((?:\\.|[^\\])*?)\2]/; const UNBOUNDED = -1; @@ -51,6 +51,7 @@ export class HighlightRuleComponent { if (!lineNumber) return null; const isUnbounded = groups.every(x => x === ''); + const highlightSpaces = groups.pop() === '+'; if (isUnbounded) { return new HighlightRuleComponent(lineNumber, true, []); } @@ -58,7 +59,7 @@ export class HighlightRuleComponent { let bound = groups.map(x => (x !== '' ? parseInt(x, 10) : UNBOUNDED)) as [number, number]; const isCharSlice = sliceMatch === linesliceCharMatch; bound = isCharSlice - ? HighlightRuleComponent.computeCharBounds(bound, lines[lineNumber - 1]) + ? HighlightRuleComponent.computeCharBounds(bound, lines[lineNumber - 1], highlightSpaces) : HighlightRuleComponent.computeWordBounds(bound, lines[lineNumber - 1]); return new HighlightRuleComponent(lineNumber, true, [bound]); @@ -101,13 +102,18 @@ export class HighlightRuleComponent { * @param line The given line * @returns {[number, number]} The actual bound computed */ - static computeCharBounds(bound: [number, number], line: string): [number, number] { + static computeCharBounds(bound: [number, number], line: string, + highlightSpaces: boolean): [number, number] { const [indents] = splitCodeAndIndentation(line); let [start, end] = bound; if (start === UNBOUNDED) { - start = indents.length; - } else { + if (highlightSpaces) { + start = 0; + } else { + start = indents.length; + } + } else if (!highlightSpaces) { start += indents.length; // Clamp values if (start < indents.length) { @@ -115,18 +121,23 @@ export class HighlightRuleComponent { } else if (start > line.length) { start = line.length; } + } else if (start > line.length) { + start = line.length; } if (end === UNBOUNDED) { end = line.length; - } else { + } else if (!highlightSpaces) { end += indents.length; + // Clamp values if (end < indents.length) { end = indents.length; } else if (end > line.length) { end = line.length; } + } else if (end > line.length) { + end = line.length; } return [start, end]; From 78c8e84bc9eb8c8dc77e06935df5a5089072b2eb Mon Sep 17 00:00:00 2001 From: AgentHagu Date: Mon, 13 Jan 2025 23:17:06 +0800 Subject: [PATCH 02/12] Add new test cases for highlighter --- .../highlight/HighlightRuleComponent.test.ts | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts b/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts index 593cb30fe6..0a17b159a9 100644 --- a/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts +++ b/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts @@ -44,28 +44,55 @@ describe('parseRuleComponent', () => { }); }); -describe('computeCharBounds', () => { +describe('computeCharBounds, no whitespace highlighting', () => { test('computes character bounds correctly', () => { - const bounds = HighlightRuleComponent.computeCharBounds([2, 5], ' some text'); + const bounds = HighlightRuleComponent.computeCharBounds([2, 5], ' some text', false); expect(bounds).toEqual([4, 7]); }); test('handles unbounded start correctly', () => { - const bounds = HighlightRuleComponent.computeCharBounds([-1, 4], ' some text'); + const bounds = HighlightRuleComponent.computeCharBounds([-1, 4], ' some text', false); expect(bounds).toEqual([2, 6]); }); test('handles unbounded end correctly', () => { - const bounds = HighlightRuleComponent.computeCharBounds([3, -1], ' some text'); + const bounds = HighlightRuleComponent.computeCharBounds([3, -1], ' some text', false); expect(bounds).toEqual([5, ' some text'.length]); }); test('handles out-of-range bounds correctly', () => { - const bounds = HighlightRuleComponent.computeCharBounds([30, 40], ' some text'); + const bounds = HighlightRuleComponent.computeCharBounds([30, 40], ' some text', false); expect(bounds).toEqual([' some text'.length, ' some text'.length]); }); }); +describe('computeCharBounds, with whitespace highlighting', () => { + test('computes character bounds correctly', () => { + const bounds = HighlightRuleComponent.computeCharBounds([2, 5], ' some text', true); + expect(bounds).toEqual([2, 5]); + }); + + test('handles unbounded start correctly', () => { + const bounds = HighlightRuleComponent.computeCharBounds([-1, 4], ' some text', true); + expect(bounds).toEqual([0, 4]); + }); + + test('handles unbounded end correctly', () => { + const bounds = HighlightRuleComponent.computeCharBounds([3, -1], ' some text', true); + expect(bounds).toEqual([3, ' some text'.length]); + }); + + test('handles out-of-range bounds correctly', () => { + const bounds = HighlightRuleComponent.computeCharBounds([30, 40], ' some text', true); + expect(bounds).toEqual([' some text'.length, ' some text'.length]); + }); + + test('handles line-length end correctly', () => { + const bounds = HighlightRuleComponent.computeCharBounds([0, 4], ' abcd', true); + expect(bounds).toEqual([0, 4]); + }); +}); + describe('computeWordBounds', () => { test('computes word bounds correctly', () => { const bounds = HighlightRuleComponent.computeWordBounds([1, 2], ' some text here'); From 46901a374f811767fae4f798e1b184b6173e685e Mon Sep 17 00:00:00 2001 From: AgentHagu Date: Fri, 17 Jan 2025 12:27:20 +0800 Subject: [PATCH 03/12] Update documentation for computeCharBounds --- .../src/lib/markdown-it/highlight/HighlightRuleComponent.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts index eeba208466..60dcaa02f3 100644 --- a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts +++ b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts @@ -96,10 +96,12 @@ export class HighlightRuleComponent { * comparing the bounds and the line's range. * * If the bound does not specify either the start or the end bound, the computed bound will default - * to the start or end of line, excluding leading whitespaces. + * to the start or end of line. The bound will either include or exclude leading whitespaces depending + * on the user's preference. * * @param bound The user-defined bound * @param line The given line + * @param highlightSpaces Whether to highlight leading whitespaces * @returns {[number, number]} The actual bound computed */ static computeCharBounds(bound: [number, number], line: string, From f679edbf0271fb0d37eedb096a26a4b46c2c1deb Mon Sep 17 00:00:00 2001 From: Adrian Leonardo Liang Date: Mon, 20 Jan 2025 12:38:42 +0800 Subject: [PATCH 04/12] Fix issue regarding failing test cases groups.pop() was called without considering the number of capturing groups within. This caused some issues with the end bound of word matching being popped and removed from the array. --- .../markdown-it/highlight/HighlightRuleComponent.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts index 60dcaa02f3..9e919544de 100644 --- a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts +++ b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts @@ -43,15 +43,21 @@ export class HighlightRuleComponent { const linesliceWordMatch = compString.match(LINESLICE_WORD_REGEX); const sliceMatch = linesliceCharMatch || linesliceWordMatch; if (sliceMatch) { - // There are four capturing groups: [full match, line number, start bound, end bound] + // There are four/five capturing groups: [full match, line number, start bound, + // end bound,highlight spaces] const groups = sliceMatch.slice(1); // discard full match const lineNumber = HighlightRuleComponent .isValidLineNumber(groups.shift() ?? '', 1, lines.length, lineNumberOffset); if (!lineNumber) return null; + let highlightSpaces = false; + if (sliceMatch === linesliceCharMatch) { + highlightSpaces = groups.pop() === '+'; + } + const isUnbounded = groups.every(x => x === ''); - const highlightSpaces = groups.pop() === '+'; + if (isUnbounded) { return new HighlightRuleComponent(lineNumber, true, []); } From fe14d7524d8dd88fcb1d5e75420abff24e42654f Mon Sep 17 00:00:00 2001 From: Adrian Leonardo Liang Date: Mon, 20 Jan 2025 13:44:20 +0800 Subject: [PATCH 05/12] Update symbol and position --- .../highlight/HighlightRuleComponent.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts index 9e919544de..7ddc47a104 100644 --- a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts +++ b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts @@ -1,6 +1,6 @@ import { splitCodeAndIndentation } from './helper'; -const LINESLICE_CHAR_REGEX = /(\d+)\[(\d*):(\d*)](\+?)/; +const LINESLICE_CHAR_REGEX = /(_?)(\d+)\[(\d*):(\d*)]/; const LINESLICE_WORD_REGEX = /(\d+)\[(\d*)::(\d*)]/; const LINEPART_REGEX = /(\d+)\[(["'])((?:\\.|[^\\])*?)\2]/; const UNBOUNDED = -1; @@ -43,19 +43,19 @@ export class HighlightRuleComponent { const linesliceWordMatch = compString.match(LINESLICE_WORD_REGEX); const sliceMatch = linesliceCharMatch || linesliceWordMatch; if (sliceMatch) { - // There are four/five capturing groups: [full match, line number, start bound, - // end bound,highlight spaces] + // There are four/five capturing groups: [full match, is highlight spaces (for char slice), + // line number, start bound, end bound] const groups = sliceMatch.slice(1); // discard full match + let isHighlightSpaces = false; + if (sliceMatch === linesliceCharMatch) { + isHighlightSpaces = groups.shift() === '_'; + } + const lineNumber = HighlightRuleComponent .isValidLineNumber(groups.shift() ?? '', 1, lines.length, lineNumberOffset); if (!lineNumber) return null; - let highlightSpaces = false; - if (sliceMatch === linesliceCharMatch) { - highlightSpaces = groups.pop() === '+'; - } - const isUnbounded = groups.every(x => x === ''); if (isUnbounded) { @@ -65,7 +65,7 @@ export class HighlightRuleComponent { let bound = groups.map(x => (x !== '' ? parseInt(x, 10) : UNBOUNDED)) as [number, number]; const isCharSlice = sliceMatch === linesliceCharMatch; bound = isCharSlice - ? HighlightRuleComponent.computeCharBounds(bound, lines[lineNumber - 1], highlightSpaces) + ? HighlightRuleComponent.computeCharBounds(bound, lines[lineNumber - 1], isHighlightSpaces) : HighlightRuleComponent.computeWordBounds(bound, lines[lineNumber - 1]); return new HighlightRuleComponent(lineNumber, true, [bound]); @@ -107,21 +107,21 @@ export class HighlightRuleComponent { * * @param bound The user-defined bound * @param line The given line - * @param highlightSpaces Whether to highlight leading whitespaces + * @param isHighlightSpaces Whether to highlight leading whitespaces * @returns {[number, number]} The actual bound computed */ static computeCharBounds(bound: [number, number], line: string, - highlightSpaces: boolean): [number, number] { + isHighlightSpaces: boolean): [number, number] { const [indents] = splitCodeAndIndentation(line); let [start, end] = bound; if (start === UNBOUNDED) { - if (highlightSpaces) { + if (isHighlightSpaces) { start = 0; } else { start = indents.length; } - } else if (!highlightSpaces) { + } else if (!isHighlightSpaces) { start += indents.length; // Clamp values if (start < indents.length) { @@ -135,7 +135,7 @@ export class HighlightRuleComponent { if (end === UNBOUNDED) { end = line.length; - } else if (!highlightSpaces) { + } else if (!isHighlightSpaces) { end += indents.length; // Clamp values From 7cb451b974b541733e97e03b48017a38f6eb5cfd Mon Sep 17 00:00:00 2001 From: Adrian Leonardo Liang Date: Mon, 27 Jan 2025 12:41:24 +0800 Subject: [PATCH 06/12] Update documentation to follow idea of absolute char indexing --- .../highlight/HighlightRuleComponent.ts | 23 +++++++++---------- .../highlight/HighlightRuleComponent.test.ts | 4 ++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts index 7ddc47a104..67ee973afe 100644 --- a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts +++ b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts @@ -1,6 +1,6 @@ import { splitCodeAndIndentation } from './helper'; -const LINESLICE_CHAR_REGEX = /(_?)(\d+)\[(\d*):(\d*)]/; +const LINESLICE_CHAR_REGEX = /(\+?)(\d+)\[(\d*):(\d*)]/; const LINESLICE_WORD_REGEX = /(\d+)\[(\d*)::(\d*)]/; const LINEPART_REGEX = /(\d+)\[(["'])((?:\\.|[^\\])*?)\2]/; const UNBOUNDED = -1; @@ -43,13 +43,13 @@ export class HighlightRuleComponent { const linesliceWordMatch = compString.match(LINESLICE_WORD_REGEX); const sliceMatch = linesliceCharMatch || linesliceWordMatch; if (sliceMatch) { - // There are four/five capturing groups: [full match, is highlight spaces (for char slice), + // There are four/five capturing groups: [full match, is absolute indexing (for char match), // line number, start bound, end bound] const groups = sliceMatch.slice(1); // discard full match - let isHighlightSpaces = false; + let isAbsoluteIndexing = false; if (sliceMatch === linesliceCharMatch) { - isHighlightSpaces = groups.shift() === '_'; + isAbsoluteIndexing = groups.shift() === '+'; } const lineNumber = HighlightRuleComponent @@ -65,7 +65,7 @@ export class HighlightRuleComponent { let bound = groups.map(x => (x !== '' ? parseInt(x, 10) : UNBOUNDED)) as [number, number]; const isCharSlice = sliceMatch === linesliceCharMatch; bound = isCharSlice - ? HighlightRuleComponent.computeCharBounds(bound, lines[lineNumber - 1], isHighlightSpaces) + ? HighlightRuleComponent.computeCharBounds(bound, lines[lineNumber - 1], isAbsoluteIndexing) : HighlightRuleComponent.computeWordBounds(bound, lines[lineNumber - 1]); return new HighlightRuleComponent(lineNumber, true, [bound]); @@ -102,26 +102,25 @@ export class HighlightRuleComponent { * comparing the bounds and the line's range. * * If the bound does not specify either the start or the end bound, the computed bound will default - * to the start or end of line. The bound will either include or exclude leading whitespaces depending - * on the user's preference. + * to the start or end of line. The bound can be either absolute or relative to the indentation level. * * @param bound The user-defined bound * @param line The given line - * @param isHighlightSpaces Whether to highlight leading whitespaces + * @param isAbsoluteIndexing Whether the bound is absolute or relative to the indentation level * @returns {[number, number]} The actual bound computed */ static computeCharBounds(bound: [number, number], line: string, - isHighlightSpaces: boolean): [number, number] { + isAbsoluteIndexing: boolean): [number, number] { const [indents] = splitCodeAndIndentation(line); let [start, end] = bound; if (start === UNBOUNDED) { - if (isHighlightSpaces) { + if (isAbsoluteIndexing) { start = 0; } else { start = indents.length; } - } else if (!isHighlightSpaces) { + } else if (!isAbsoluteIndexing) { start += indents.length; // Clamp values if (start < indents.length) { @@ -135,7 +134,7 @@ export class HighlightRuleComponent { if (end === UNBOUNDED) { end = line.length; - } else if (!isHighlightSpaces) { + } else if (!isAbsoluteIndexing) { end += indents.length; // Clamp values diff --git a/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts b/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts index 0a17b159a9..12e5eef6ef 100644 --- a/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts +++ b/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts @@ -44,7 +44,7 @@ describe('parseRuleComponent', () => { }); }); -describe('computeCharBounds, no whitespace highlighting', () => { +describe('computeCharBounds, relative to indent-level', () => { test('computes character bounds correctly', () => { const bounds = HighlightRuleComponent.computeCharBounds([2, 5], ' some text', false); expect(bounds).toEqual([4, 7]); @@ -66,7 +66,7 @@ describe('computeCharBounds, no whitespace highlighting', () => { }); }); -describe('computeCharBounds, with whitespace highlighting', () => { +describe('computeCharBounds, absolute value bounds', () => { test('computes character bounds correctly', () => { const bounds = HighlightRuleComponent.computeCharBounds([2, 5], ' some text', true); expect(bounds).toEqual([2, 5]); From 2245743a9cd60b8c707dd3637a8dfcded2a47205 Mon Sep 17 00:00:00 2001 From: Adrian Leonardo Liang Date: Mon, 27 Jan 2025 13:17:25 +0800 Subject: [PATCH 07/12] Refactor code to improve readability --- .../highlight/HighlightRuleComponent.ts | 34 ++++--------------- .../highlight/HighlightRuleComponent.test.ts | 6 ++-- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts index 67ee973afe..d6ed452dfc 100644 --- a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts +++ b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.ts @@ -57,7 +57,6 @@ export class HighlightRuleComponent { if (!lineNumber) return null; const isUnbounded = groups.every(x => x === ''); - if (isUnbounded) { return new HighlightRuleComponent(lineNumber, true, []); } @@ -115,36 +114,17 @@ export class HighlightRuleComponent { let [start, end] = bound; if (start === UNBOUNDED) { - if (isAbsoluteIndexing) { - start = 0; - } else { - start = indents.length; - } - } else if (!isAbsoluteIndexing) { - start += indents.length; - // Clamp values - if (start < indents.length) { - start = indents.length; - } else if (start > line.length) { - start = line.length; - } - } else if (start > line.length) { - start = line.length; + start = isAbsoluteIndexing ? 0 : indents.length; + } else { + start = isAbsoluteIndexing ? start : Math.max(start + indents.length, indents.length); + start = Math.min(start, line.length); } if (end === UNBOUNDED) { end = line.length; - } else if (!isAbsoluteIndexing) { - end += indents.length; - - // Clamp values - if (end < indents.length) { - end = indents.length; - } else if (end > line.length) { - end = line.length; - } - } else if (end > line.length) { - end = line.length; + } else { + end = isAbsoluteIndexing ? end : Math.max(end + indents.length, indents.length); + end = Math.min(end, line.length); } return [start, end]; diff --git a/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts b/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts index 12e5eef6ef..8ee9420f9a 100644 --- a/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts +++ b/packages/core/test/unit/lib/markdown-it/highlight/HighlightRuleComponent.test.ts @@ -87,9 +87,9 @@ describe('computeCharBounds, absolute value bounds', () => { expect(bounds).toEqual([' some text'.length, ' some text'.length]); }); - test('handles line-length end correctly', () => { - const bounds = HighlightRuleComponent.computeCharBounds([0, 4], ' abcd', true); - expect(bounds).toEqual([0, 4]); + test('handles bounds spanning from start to line length correctly', () => { + const bounds = HighlightRuleComponent.computeCharBounds([0, 11], ' some text', true); + expect(bounds).toEqual([0, 11]); }); }); From ff20a2477b3b35bb8115b9634404a5a1d6cba801 Mon Sep 17 00:00:00 2001 From: Adrian Leonardo Liang Date: Mon, 10 Feb 2025 12:36:30 +0800 Subject: [PATCH 08/12] Add default tab conversion --- packages/core/src/lib/markdown-it/highlight/Highlighter.ts | 3 ++- .../unit/lib/markdown-it/highlight/Highlighter.test.ts | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/core/src/lib/markdown-it/highlight/Highlighter.ts b/packages/core/src/lib/markdown-it/highlight/Highlighter.ts index ee6cc2325b..430e46d618 100644 --- a/packages/core/src/lib/markdown-it/highlight/Highlighter.ts +++ b/packages/core/src/lib/markdown-it/highlight/Highlighter.ts @@ -18,6 +18,7 @@ export class Highlighter { */ const mergedBounds = collateAllIntervals(bounds); const dataStr = mergedBounds.map(bound => bound.join('-')).join(','); - return `${code}\n`; + const formattedCode = code.replace('\t', ' '); // Convert tabs to 4 spaces by default + return `${formattedCode}\n`; } } diff --git a/packages/core/test/unit/lib/markdown-it/highlight/Highlighter.test.ts b/packages/core/test/unit/lib/markdown-it/highlight/Highlighter.test.ts index 83f742abc0..44b712ff3c 100644 --- a/packages/core/test/unit/lib/markdown-it/highlight/Highlighter.test.ts +++ b/packages/core/test/unit/lib/markdown-it/highlight/Highlighter.test.ts @@ -36,4 +36,11 @@ describe('highlightPartOfText', () => { const result = Highlighter.highlightPartOfText(code, bounds); expect(result).toBe('const x = 10;\n'); }); + + test('handles tabs in code correctly', () => { + const code = '\tconst x = 10;'; + const bounds: Array<[number, number]> = [[0, 4], [8, 10]]; + const result = Highlighter.highlightPartOfText(code, bounds); + expect(result).toBe(' const x = 10;\n'); + }); }); From 23f9830d68e169028007bc85c5f70912d45d531b Mon Sep 17 00:00:00 2001 From: Adrian Leonardo Liang Date: Mon, 10 Feb 2025 13:27:19 +0800 Subject: [PATCH 09/12] Update User Guide to reflect new behavior --- docs/userGuide/syntax/code.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userGuide/syntax/code.md b/docs/userGuide/syntax/code.md index 96a01eb264..55dfe747a3 100644 --- a/docs/userGuide/syntax/code.md +++ b/docs/userGuide/syntax/code.md @@ -124,7 +124,7 @@ function subtract(a, b) { **Character-bounded highlight** -```js {.line-numbers highlight-lines="1[0:3], 1[6:10], 2[5:], 3[:6]"} +```js {.line-numbers highlight-lines="1[0:3], 1[6:10], 2[5:], 3[:6], +4[:6]"} function multiply(a, b) { const product = a * b; console.log('Product = ${product}'); @@ -203,7 +203,7 @@ Type | Format | Example -----|--------|-------- **Full text highlight**
Highlights the entirety of the text portion of the line | The line numbers as-is (subject to the starting line number set in `start-from`). | `3`, `5` **Substring highlight**
Highlights _all_ occurrences of a substring in the line | `lineNumber[part]`

_Limitations_: `part` must be wrapped in quotes. If `part` contains a quote, escape it with a backslash (`\`). | `3['Inventory']`,`4['It\'s designed']` -**Character-bounded highlight**
Highlights a specific range of characters in the line | `lineNumber[start:end]`, highlights from character position `start` up to (but not including) `end`.

Character positions start from `0` as the first non-whitespace character, upwards.

Omit either `start`/`end` to highlight from the start / up to the end, respectively. | `19[1:5]`,`30[10:]`,`35[:20]` +**Character-bounded highlight**
Highlights a specific range of characters in the line | `lineNumber[start:end]`, highlights from character position `start` up to (but not including) `end`.

Character positions start from `0` as the first non-whitespace character, upwards.

Omit either `start`/`end` to highlight from the start / up to the end, respectively.

To use absolute positions (i.e. including leading whitespace), add a `+` symbol to the beginning of the rule: `+lineNumber[start:end]`

_Note: Tabs are automatically converted to 4 spaces by default._ | `19[1:5]`,`30[10:]`,`35[:20]`, `+3[1:4]` **Word-bounded highlight**
Highlights a specific range of words in the line | `lineNumber[start::end]`, highlights from word position `start` up to (but not including) `end`.

Word positions start from `0` as the first word (sequence of non-whitespace characters), upwards.

Omit either `start`/`end` to highlight from the start / up to the end, respectively. | `5[2::4]`,`9[1::]`,`11[::5]` **Full line highlight**
Highlights the entirety of the line | `lineNumber[:]` | `7[:]` From 2cff619739f81efe5b9b44d40b01021d48c8b2f3 Mon Sep 17 00:00:00 2001 From: AgentHagu Date: Sun, 16 Feb 2025 12:50:06 +0800 Subject: [PATCH 10/12] Fix issues with tab conversion --- packages/core/src/lib/markdown-it/highlight/Highlighter.ts | 3 +-- packages/core/src/lib/markdown-it/index.ts | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/lib/markdown-it/highlight/Highlighter.ts b/packages/core/src/lib/markdown-it/highlight/Highlighter.ts index 430e46d618..ee6cc2325b 100644 --- a/packages/core/src/lib/markdown-it/highlight/Highlighter.ts +++ b/packages/core/src/lib/markdown-it/highlight/Highlighter.ts @@ -18,7 +18,6 @@ export class Highlighter { */ const mergedBounds = collateAllIntervals(bounds); const dataStr = mergedBounds.map(bound => bound.join('-')).join(','); - const formattedCode = code.replace('\t', ' '); // Convert tabs to 4 spaces by default - return `${formattedCode}\n`; + return `${code}\n`; } } diff --git a/packages/core/src/lib/markdown-it/index.ts b/packages/core/src/lib/markdown-it/index.ts index 565d2d0e4a..91d355c5cc 100644 --- a/packages/core/src/lib/markdown-it/index.ts +++ b/packages/core/src/lib/markdown-it/index.ts @@ -72,6 +72,7 @@ markdownIt.renderer.rules.fence = (tokens: Token[], const token = tokens[idx]; const lang = token.info || ''; let str = token.content; + str = str.replace(/\t/g, ' '); // Convert tabs to 4 spaces by default let highlighted = false; let lines: string[] = []; From 8c9849aab4b0b474f00de623aff1a75d48fd9a07 Mon Sep 17 00:00:00 2001 From: AgentHagu Date: Sun, 16 Feb 2025 14:04:27 +0800 Subject: [PATCH 11/12] Fix bug with failing test case --- packages/core/src/lib/markdown-it/highlight/Highlighter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/lib/markdown-it/highlight/Highlighter.ts b/packages/core/src/lib/markdown-it/highlight/Highlighter.ts index ee6cc2325b..3743930b86 100644 --- a/packages/core/src/lib/markdown-it/highlight/Highlighter.ts +++ b/packages/core/src/lib/markdown-it/highlight/Highlighter.ts @@ -18,6 +18,7 @@ export class Highlighter { */ const mergedBounds = collateAllIntervals(bounds); const dataStr = mergedBounds.map(bound => bound.join('-')).join(','); - return `${code}\n`; + const formattedCode = code.replace(/\t/g, ' '); // Convert tabs to 4 spaces by default + return `${formattedCode}\n`; } } From 3ed3d840a9f54ebab8902be978b97d3d6986a2af Mon Sep 17 00:00:00 2001 From: AgentHagu Date: Sun, 16 Feb 2025 15:33:20 +0800 Subject: [PATCH 12/12] Update user guide to better reflect tab conversion behavior --- docs/userGuide/syntax/code.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/userGuide/syntax/code.md b/docs/userGuide/syntax/code.md index 55dfe747a3..85e6330353 100644 --- a/docs/userGuide/syntax/code.md +++ b/docs/userGuide/syntax/code.md @@ -24,6 +24,10 @@ Features: * Line highlighting * Code block headers + +Tabs (i.e. "\t") are automatically converted to 4 whitespaces by default. + + ##### Syntax coloring To enable syntax coloring, specify a language next to the backticks before the fenced code block. @@ -203,7 +207,7 @@ Type | Format | Example -----|--------|-------- **Full text highlight**
Highlights the entirety of the text portion of the line | The line numbers as-is (subject to the starting line number set in `start-from`). | `3`, `5` **Substring highlight**
Highlights _all_ occurrences of a substring in the line | `lineNumber[part]`

_Limitations_: `part` must be wrapped in quotes. If `part` contains a quote, escape it with a backslash (`\`). | `3['Inventory']`,`4['It\'s designed']` -**Character-bounded highlight**
Highlights a specific range of characters in the line | `lineNumber[start:end]`, highlights from character position `start` up to (but not including) `end`.

Character positions start from `0` as the first non-whitespace character, upwards.

Omit either `start`/`end` to highlight from the start / up to the end, respectively.

To use absolute positions (i.e. including leading whitespace), add a `+` symbol to the beginning of the rule: `+lineNumber[start:end]`

_Note: Tabs are automatically converted to 4 spaces by default._ | `19[1:5]`,`30[10:]`,`35[:20]`, `+3[1:4]` +**Character-bounded highlight**
Highlights a specific range of characters in the line | `lineNumber[start:end]`, highlights from character position `start` up to (but not including) `end`.

Character positions start from `0` as the first non-whitespace character, upwards.

Omit either `start`/`end` to highlight from the start / up to the end, respectively.

To use absolute positions (i.e. including leading whitespace), add a `+` symbol to the beginning of the rule: `+lineNumber[start:end]` | `19[1:5]`,`30[10:]`,`35[:20]`, `+3[1:4]` **Word-bounded highlight**
Highlights a specific range of words in the line | `lineNumber[start::end]`, highlights from word position `start` up to (but not including) `end`.

Word positions start from `0` as the first word (sequence of non-whitespace characters), upwards.

Omit either `start`/`end` to highlight from the start / up to the end, respectively. | `5[2::4]`,`9[1::]`,`11[::5]` **Full line highlight**
Highlights the entirety of the line | `lineNumber[:]` | `7[:]`