From ceca984e211ed7a12227354d9d2cfcfb44dcfde2 Mon Sep 17 00:00:00 2001 From: Fuma Nama <76240755+fuma-nama@users.noreply.github.com> Date: Mon, 20 Jan 2025 15:45:55 +0800 Subject: [PATCH] feat(transformers): introduce `matchAlgorithm` option for new matching algorithm (#835) Co-authored-by: Anthony Fu --- docs/packages/transformers.md | 38 +++++ packages/transformers/src/index.ts | 1 - .../transformers/src/shared/highlight-word.ts | 9 +- .../src/shared/notation-transformer.ts | 97 +++++++++++++ .../transformers/src/shared/parse-comments.ts | 134 ++++++++++++++++++ .../src/transformers/meta-highlight.ts | 12 +- .../src/transformers/notation-diff.ts | 4 +- .../src/transformers/notation-error-level.ts | 4 +- .../src/transformers/notation-focus.ts | 4 +- .../transformers/notation-highlight-word.ts | 18 ++- .../src/transformers/notation-highlight.ts | 4 +- .../src/transformers/notation-map.ts | 18 +-- packages/transformers/src/utils.ts | 63 -------- packages/transformers/test/fixtures.test.ts | 75 ++++++++-- packages/transformers/test/fixtures/all/a.js | 2 +- .../highlight-word/complex.js.output.html | 2 +- .../mutliple-words.js.output.html | 2 +- .../test/fixtures/match-algorithm/v1.js | 22 +++ .../match-algorithm/v1.js.output.html | 11 ++ .../test/fixtures/match-algorithm/v3.js | 22 +++ .../match-algorithm/v3.js.output.html | 11 ++ 21 files changed, 448 insertions(+), 105 deletions(-) create mode 100644 packages/transformers/src/shared/notation-transformer.ts create mode 100644 packages/transformers/src/shared/parse-comments.ts delete mode 100644 packages/transformers/src/utils.ts create mode 100644 packages/transformers/test/fixtures/match-algorithm/v1.js create mode 100644 packages/transformers/test/fixtures/match-algorithm/v1.js.output.html create mode 100644 packages/transformers/test/fixtures/match-algorithm/v3.js create mode 100644 packages/transformers/test/fixtures/match-algorithm/v3.js.output.html diff --git a/docs/packages/transformers.md b/docs/packages/transformers.md index f1106e7b6..151ff589a 100644 --- a/docs/packages/transformers.md +++ b/docs/packages/transformers.md @@ -41,6 +41,44 @@ const html = await codeToHtml(code, { Transformers only applies classes and does not come with styles; you can provide your own CSS rules to style them properly. +## Matching Algorithm + +We found that the algorithm for matching comments in v1 is sometime conterintuitive, where we are trying to fix it in a progressive way. Since v1.29.0, we introduced a new `matchAlgorithm` option to most of the transformer for you to toggle between different matching algorithms. Right now, the default is `v1` which is the old algorithm, and `v3` is the new algorithm. When Shiki v3 is landed, the default will be `v3`. + +```ts +const html = await codeToHtml(code, { + lang: 'ts', + theme: 'nord', + transformers: [ + transformerNotationDiff({ + matchAlgorithm: 'v3', // [!code hl] + }), + ], +}) +``` + +### `matchAlgorithm: 'v1'` + +The matching algorithm mostly affects the single-line comment matching, in `v1`, it will count the comment line as the first line, while in `v3`, it will count start from the comment line: + +```ts +// [\!code highlight:3] +console.log('highlighted') // [!code hl] +console.log('highlighted') // [!code hl] +console.log('not highlighted') +``` + +### `matchAlgorithm: 'v3'` + +In `v3`, the matching algorithm will start counting from the line below the comment: + +```ts +// [\!code highlight:2] +console.log('highlighted') // [!code hl] +console.log('highlighted') // [!code hl] +console.log('not highlighted') +``` + ## Transformers ### `transformerNotationDiff` diff --git a/packages/transformers/src/index.ts b/packages/transformers/src/index.ts index 05ed4d218..306724f79 100644 --- a/packages/transformers/src/index.ts +++ b/packages/transformers/src/index.ts @@ -11,4 +11,3 @@ export * from './transformers/remove-line-breaks' export * from './transformers/remove-notation-escape' export * from './transformers/render-whitespace' export * from './transformers/style-to-class' -export * from './utils' diff --git a/packages/transformers/src/shared/highlight-word.ts b/packages/transformers/src/shared/highlight-word.ts index 7aef3b04c..9f9b1ffec 100644 --- a/packages/transformers/src/shared/highlight-word.ts +++ b/packages/transformers/src/shared/highlight-word.ts @@ -24,6 +24,7 @@ function getTextContent(element: ElementContent): string { * @param ignoredElement * @param index highlight beginning index * @param len highlight length + * @param className class name to add to highlighted nodes */ function highlightRange( this: ShikiTransformerContext, @@ -64,14 +65,14 @@ function highlightRange( } } -function hasOverlap(range1: [number, number], range2: [ number, number]): boolean { +function hasOverlap(range1: [number, number], range2: [number, number]): boolean { return (range1[0] <= range2[1]) && (range1[1]) >= range2[0] } function separateToken(span: Element, textNode: Text, index: number, len: number): [ - before: Element | undefined, - med: Element, - after: Element | undefined, + before: Element | undefined, + med: Element, + after: Element | undefined, ] { const text = textNode.value diff --git a/packages/transformers/src/shared/notation-transformer.ts b/packages/transformers/src/shared/notation-transformer.ts new file mode 100644 index 000000000..b6bef7174 --- /dev/null +++ b/packages/transformers/src/shared/notation-transformer.ts @@ -0,0 +1,97 @@ +import type { Element, Text } from 'hast' +import type { ShikiTransformer, ShikiTransformerContext } from 'shiki' +import { parseComments, type ParsedComments, v1ClearEndCommentPrefix } from './parse-comments' + +export type MatchAlgorithm = 'v1' | 'v3' + +export interface MatchAlgorithmOptions { + /** + * Match algorithm to use + * + * @see https://shiki.style/packages/transformers#matching-algorithm + * @default 'v1' + */ + matchAlgorithm?: MatchAlgorithm +} + +export function createCommentNotationTransformer( + name: string, + regex: RegExp, + onMatch: ( + this: ShikiTransformerContext, + match: string[], + line: Element, + commentNode: Element, + lines: Element[], + index: number + ) => boolean, + matchAlgorithm: MatchAlgorithm = 'v1', +): ShikiTransformer { + return { + name, + code(code) { + const lines = code.children.filter(i => i.type === 'element') + const linesToRemove: (Element | Text)[] = [] + + code.data ??= {} as any + const data = code.data as { + _shiki_notation?: ParsedComments + } + + data._shiki_notation ??= parseComments(lines, ['jsx', 'tsx'].includes(this.options.lang), matchAlgorithm) + const parsed = data._shiki_notation + + for (const comment of parsed) { + if (comment.info[1].length === 0) + continue + + const isLineCommentOnly = comment.line.children.length === (comment.isJsxStyle ? 3 : 1) + let lineIdx = lines.indexOf(comment.line) + if (isLineCommentOnly && matchAlgorithm !== 'v1') + lineIdx++ + + let replaced = false + comment.info[1] = comment.info[1].replace(regex, (...match) => { + if (onMatch.call(this, match, comment.line, comment.token, lines, lineIdx)) { + replaced = true + return '' + } + + return match[0] + }) + + if (!replaced) + continue + + if (matchAlgorithm === 'v1') { + comment.info[1] = v1ClearEndCommentPrefix(comment.info[1]) + } + + const isEmpty = comment.info[1].trim().length === 0 + // ignore comment node + if (isEmpty) + comment.info[1] = '' + + if (isEmpty && isLineCommentOnly) { + linesToRemove.push(comment.line) + } + else if (isEmpty && comment.isJsxStyle) { + comment.line.children.splice(comment.line.children.indexOf(comment.token) - 1, 3) + } + else if (isEmpty) { + comment.line.children.splice(comment.line.children.indexOf(comment.token), 1) + } + else { + const head = comment.token.children[0] + + if (head.type === 'text') { + head.value = comment.info.join('') + } + } + } + + for (const line of linesToRemove) + code.children.splice(code.children.indexOf(line), 1) + }, + } +} diff --git a/packages/transformers/src/shared/parse-comments.ts b/packages/transformers/src/shared/parse-comments.ts new file mode 100644 index 000000000..fcc80996f --- /dev/null +++ b/packages/transformers/src/shared/parse-comments.ts @@ -0,0 +1,134 @@ +import type { Element, ElementContent } from 'hast' +import type { MatchAlgorithm } from './notation-transformer' + +export type ParsedComments = { + line: Element + token: Element + info: [prefix: string, content: string, suffix?: string] + isJsxStyle: boolean +}[] + +/** + * some comment formats have to be located at the end of line + * hence we can skip matching them for other tokens + */ +const matchers: [re: RegExp, endOfLine: boolean][] = [ + [/^()$/, false], + [/^(\/\*)(.+)(\*\/)$/, false], + [/^(\/\/|["'#]|;{1,2}|%{1,2}|--)(.*)$/, true], + /** + * for multi-line comments like this + */ + [/^(\*)(.+)$/, true], +] + +/** + * @param lines line tokens + * @param jsx enable JSX parsing + * @param matchAlgorithm matching algorithm + */ +export function parseComments( + lines: Element[], + jsx: boolean, + matchAlgorithm: MatchAlgorithm, +): ParsedComments { + const out: ParsedComments = [] + + for (const line of lines) { + const elements = line.children + let start = elements.length - 1 + if (matchAlgorithm === 'v1') + start = 0 + else if (jsx) + // one step further for JSX as comment is inside curly brackets + start = elements.length - 2 + + for (let i = Math.max(start, 0); i < elements.length; i++) { + const token = elements[i] + if (token.type !== 'element') + continue + const head = token.children.at(0) + if (head?.type !== 'text') + continue + + const isLast = i === elements.length - 1 + const match = matchToken(head.value, isLast) + if (!match) + continue + + if (jsx && !isLast && i !== 0) { + out.push({ + info: match, + line, + token, + isJsxStyle: isValue(elements[i - 1], '{') && isValue(elements[i + 1], '}'), + }) + } + else { + out.push({ + info: match, + line, + token, + isJsxStyle: false, + }) + } + } + } + + return out +} + +function isValue(element: ElementContent, value: string): boolean { + if (element.type !== 'element') + return false + const text = element.children[0] + if (text.type !== 'text') + return false + + return text.value.trim() === value +} + +/** + * @param text text value of comment node + * @param isLast whether the token is located at the end of line + */ +function matchToken(text: string, isLast: boolean): [prefix: string, content: string, suffix?: string] | undefined { + // no leading and trailing spaces allowed for matchers + // we extract the spaces + let trimmed = text.trimStart() + const spaceFront = text.length - trimmed.length + + trimmed = trimmed.trimEnd() + const spaceEnd = text.length - trimmed.length - spaceFront + + for (const [matcher, endOfLine] of matchers) { + if (endOfLine && !isLast) + continue + + const result = matcher.exec(trimmed) + if (!result) + continue + + return [ + ' '.repeat(spaceFront) + result[1], + result[2], + result[3] ? result[3] + ' '.repeat(spaceEnd) : undefined, + ] + } +} + +/** + * Remove empty comment prefixes at line end, e.g. `// ` + * + * For matchAlgorithm v1 + */ +export function v1ClearEndCommentPrefix(text: string): string { + const regex = /(?:\/\/|["'#]|;{1,2}|%{1,2}|--)(.*)$/ + const result = regex.exec(text) + + if (result && result[1].trim().length === 0) { + return text.slice(0, result.index) + } + + return text +} diff --git a/packages/transformers/src/transformers/meta-highlight.ts b/packages/transformers/src/transformers/meta-highlight.ts index cc5c7ae08..0c6037e01 100644 --- a/packages/transformers/src/transformers/meta-highlight.ts +++ b/packages/transformers/src/transformers/meta-highlight.ts @@ -12,8 +12,7 @@ export function parseMetaHighlightString(meta: string): number[] | null { const num = v.split('-').map(v => Number.parseInt(v, 10)) if (num.length === 1) return [num[0]] - else - return Array.from({ length: num[1] - num[0] + 1 }, (_, i) => i + num[0]) + return Array.from({ length: num[1] - num[0] + 1 }, (_, i) => i + num[0]) }) return lines } @@ -45,8 +44,13 @@ export function transformerMetaHighlight( if (!this.options.meta?.__raw) { return } - ;(this.meta as any)[symbol] ||= parseMetaHighlightString(this.options.meta.__raw) - const lines: number[] = (this.meta as any)[symbol] || [] + const meta = this.meta as { + [symbol]: number[] | null + } + + meta[symbol] ??= parseMetaHighlightString(this.options.meta.__raw) + const lines: number[] = meta[symbol] ?? [] + if (lines.includes(line)) this.addClassToHast(node, className) return node diff --git a/packages/transformers/src/transformers/notation-diff.ts b/packages/transformers/src/transformers/notation-diff.ts index b21f9b22f..d3907ff54 100644 --- a/packages/transformers/src/transformers/notation-diff.ts +++ b/packages/transformers/src/transformers/notation-diff.ts @@ -1,7 +1,8 @@ import type { ShikiTransformer } from 'shiki' +import type { MatchAlgorithmOptions } from '../shared/notation-transformer' import { transformerNotationMap } from './notation-map' -export interface TransformerNotationDiffOptions { +export interface TransformerNotationDiffOptions extends MatchAlgorithmOptions { /** * Class for added lines */ @@ -35,6 +36,7 @@ export function transformerNotationDiff( '--': classLineRemove, }, classActivePre, + matchAlgorithm: options.matchAlgorithm, }, '@shikijs/transformers:notation-diff', ) diff --git a/packages/transformers/src/transformers/notation-error-level.ts b/packages/transformers/src/transformers/notation-error-level.ts index 03a514f83..9764d7123 100644 --- a/packages/transformers/src/transformers/notation-error-level.ts +++ b/packages/transformers/src/transformers/notation-error-level.ts @@ -1,7 +1,8 @@ import type { ShikiTransformer } from 'shiki' +import type { MatchAlgorithmOptions } from '../shared/notation-transformer' import { transformerNotationMap } from './notation-map' -export interface TransformerNotationErrorLevelOptions { +export interface TransformerNotationErrorLevelOptions extends MatchAlgorithmOptions { classMap?: Record /** * Class added to the
 element when the current code has diff
@@ -27,6 +28,7 @@ export function transformerNotationErrorLevel(
     {
       classMap,
       classActivePre,
+      matchAlgorithm: options.matchAlgorithm,
     },
     '@shikijs/transformers:notation-error-level',
   )
diff --git a/packages/transformers/src/transformers/notation-focus.ts b/packages/transformers/src/transformers/notation-focus.ts
index 1af4dce16..74a02d9ba 100644
--- a/packages/transformers/src/transformers/notation-focus.ts
+++ b/packages/transformers/src/transformers/notation-focus.ts
@@ -1,7 +1,8 @@
 import type { ShikiTransformer } from 'shiki'
+import type { MatchAlgorithmOptions } from '../shared/notation-transformer'
 import { transformerNotationMap } from './notation-map'
 
-export interface TransformerNotationFocusOptions {
+export interface TransformerNotationFocusOptions extends MatchAlgorithmOptions {
   /**
    * Class for focused lines
    */
@@ -29,6 +30,7 @@ export function transformerNotationFocus(
         focus: classActiveLine,
       },
       classActivePre,
+      matchAlgorithm: options.matchAlgorithm,
     },
     '@shikijs/transformers:notation-focus',
   )
diff --git a/packages/transformers/src/transformers/notation-highlight-word.ts b/packages/transformers/src/transformers/notation-highlight-word.ts
index 1e5cc5cbc..43b505925 100644
--- a/packages/transformers/src/transformers/notation-highlight-word.ts
+++ b/packages/transformers/src/transformers/notation-highlight-word.ts
@@ -1,8 +1,9 @@
 import type { ShikiTransformer } from 'shiki'
+import type { MatchAlgorithmOptions } from '../shared/notation-transformer'
 import { highlightWordInLine } from '../shared/highlight-word'
-import { createCommentNotationTransformer } from '../utils'
+import { createCommentNotationTransformer } from '../shared/notation-transformer'
 
-export interface TransformerNotationWordHighlightOptions {
+export interface TransformerNotationWordHighlightOptions extends MatchAlgorithmOptions {
   /**
    * Class for highlighted words
    */
@@ -23,23 +24,20 @@ export function transformerNotationWordHighlight(
 
   return createCommentNotationTransformer(
     '@shikijs/transformers:notation-highlight-word',
-    // comment-start             | marker    | word           | range | comment-end
-    /^\s*(?:\/\/|\/\*|)?/,
+    /\s*\[!code word:((?:\\.|[^:\]])+)(:\d+)?\]/,
     function ([_, word, range], _line, comment, lines, index) {
       const lineNum = range ? Number.parseInt(range.slice(1), 10) : lines.length
 
       // escape backslashes
       word = word.replace(/\\(.)/g, '$1')
-
-      lines
-        // Don't include the comment itself
-        .slice(index + 1, index + 1 + lineNum)
-        .forEach(line => highlightWordInLine.call(this, line, comment, word, classActiveWord))
+      for (let i = index; i < Math.min(index + lineNum, lines.length); i++) {
+        highlightWordInLine.call(this, lines[i], comment, word, classActiveWord)
+      }
 
       if (classActivePre)
         this.addClassToHast(this.pre, classActivePre)
       return true
     },
-    true, // remove empty lines
+    options.matchAlgorithm,
   )
 }
diff --git a/packages/transformers/src/transformers/notation-highlight.ts b/packages/transformers/src/transformers/notation-highlight.ts
index f0287971f..b8e6bca74 100644
--- a/packages/transformers/src/transformers/notation-highlight.ts
+++ b/packages/transformers/src/transformers/notation-highlight.ts
@@ -1,7 +1,8 @@
 import type { ShikiTransformer } from 'shiki'
+import type { MatchAlgorithmOptions } from '../shared/notation-transformer'
 import { transformerNotationMap } from './notation-map'
 
-export interface TransformerNotationHighlightOptions {
+export interface TransformerNotationHighlightOptions extends MatchAlgorithmOptions {
   /**
    * Class for highlighted lines
    */
@@ -30,6 +31,7 @@ export function transformerNotationHighlight(
         hl: classActiveLine,
       },
       classActivePre,
+      matchAlgorithm: options.matchAlgorithm,
     },
     '@shikijs/transformers:notation-highlight',
   )
diff --git a/packages/transformers/src/transformers/notation-map.ts b/packages/transformers/src/transformers/notation-map.ts
index 728f7e763..c22690081 100644
--- a/packages/transformers/src/transformers/notation-map.ts
+++ b/packages/transformers/src/transformers/notation-map.ts
@@ -1,7 +1,8 @@
 import type { ShikiTransformer } from 'shiki'
-import { createCommentNotationTransformer } from '../utils'
+import type { MatchAlgorithmOptions } from '../shared/notation-transformer'
+import { createCommentNotationTransformer } from '../shared/notation-transformer'
 
-export interface TransformerNotationMapOptions {
+export interface TransformerNotationMapOptions extends MatchAlgorithmOptions {
   classMap?: Record
   /**
    * Class added to the 
 element when the current code has diff
@@ -24,17 +25,18 @@ export function transformerNotationMap(
 
   return createCommentNotationTransformer(
     name,
-    new RegExp(`\\s*(?://|/\\*|)?\\s*$`),
+    new RegExp(`\\s*\\[!code (${Object.keys(classMap).map(escapeRegExp).join('|')})(:\\d+)?\\]`),
     function ([_, match, range = ':1'], _line, _comment, lines, index) {
       const lineNum = Number.parseInt(range.slice(1), 10)
-      lines
-        .slice(index, index + lineNum)
-        .forEach((line) => {
-          this.addClassToHast(line, classMap[match])
-        })
+
+      for (let i = index; i < Math.min(index + lineNum, lines.length); i++) {
+        this.addClassToHast(lines[i], classMap[match])
+      }
+
       if (classActivePre)
         this.addClassToHast(this.pre, classActivePre)
       return true
     },
+    options.matchAlgorithm,
   )
 }
diff --git a/packages/transformers/src/utils.ts b/packages/transformers/src/utils.ts
deleted file mode 100644
index 2095b9eb8..000000000
--- a/packages/transformers/src/utils.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import type { Element, Text } from 'hast'
-import type { ShikiTransformer, ShikiTransformerContext } from 'shiki'
-
-export function createCommentNotationTransformer(
-  name: string,
-  regex: RegExp,
-  onMatch: (
-    this: ShikiTransformerContext,
-    match: string[],
-    line: Element,
-    commentNode: Element,
-    lines: Element[],
-    index: number,
-  ) => boolean,
-  removeEmptyLines = false,
-): ShikiTransformer {
-  return {
-    name,
-    code(code) {
-      const lines = code.children.filter(i => i.type === 'element') as Element[]
-      const linesToRemove: (Element | Text)[] = []
-      lines.forEach((line, idx) => {
-        let nodeToRemove: Element | undefined
-
-        for (const child of line.children) {
-          if (child.type !== 'element')
-            continue
-          const text = child.children[0]
-          if (text.type !== 'text')
-            continue
-
-          let replaced = false
-          text.value = text.value.replace(regex, (...match) => {
-            if (onMatch.call(this, match, line, child, lines, idx)) {
-              replaced = true
-              return ''
-            }
-            return match[0]
-          })
-          if (replaced && !text.value.trim())
-            nodeToRemove = child
-        }
-
-        if (nodeToRemove) {
-          line.children.splice(line.children.indexOf(nodeToRemove), 1)
-
-          // Remove if empty
-          if (line.children.length === 0) {
-            linesToRemove.push(line)
-            if (removeEmptyLines) {
-              const next = code.children[code.children.indexOf(line) + 1]
-              if (next && next.type === 'text' && next.value === '\n')
-                linesToRemove.push(next)
-            }
-          }
-        }
-      })
-
-      for (const line of linesToRemove)
-        code.children.splice(code.children.indexOf(line), 1)
-    },
-  }
-}
diff --git a/packages/transformers/test/fixtures.test.ts b/packages/transformers/test/fixtures.test.ts
index bc3ef5de6..ffd20191e 100644
--- a/packages/transformers/test/fixtures.test.ts
+++ b/packages/transformers/test/fixtures.test.ts
@@ -61,7 +61,10 @@ body { margin: 0; }
 suite(
   'diff',
   import.meta.glob('./fixtures/diff/*.*', { query: '?raw', import: 'default', eager: true }),
-  [transformerNotationDiff(), transformerRemoveLineBreak()],
+  [
+    transformerNotationDiff(),
+    transformerRemoveLineBreak(),
+  ],
   code => `${code}
 `,
 )
+
+const CSS_COMPARE = ``
+
+suite(
+  'compare-v1',
+  import.meta.glob('./fixtures/match-algorithm/v1.*', { query: '?raw', import: 'default', eager: true }),
+  [
+    transformerNotationFocus({ matchAlgorithm: 'v1' }),
+    transformerNotationHighlight({ matchAlgorithm: 'v1' }),
+    transformerNotationErrorLevel({ matchAlgorithm: 'v1' }),
+    transformerNotationWordHighlight({ matchAlgorithm: 'v1' }),
+    transformerRemoveLineBreak(),
+  ],
+  code => `${code}${CSS_COMPARE}`,
+)
+
+suite(
+  'compare-v3',
+  import.meta.glob('./fixtures/match-algorithm/v3.*', { query: '?raw', import: 'default', eager: true }),
+  [
+    transformerNotationFocus({ matchAlgorithm: 'v3' }),
+    transformerNotationHighlight({ matchAlgorithm: 'v3' }),
+    transformerNotationErrorLevel({ matchAlgorithm: 'v3' }),
+    transformerNotationWordHighlight({ matchAlgorithm: 'v3' }),
+    transformerRemoveLineBreak(),
+  ],
+  code => `${code}${CSS_COMPARE}`,
+)
diff --git a/packages/transformers/test/fixtures/all/a.js b/packages/transformers/test/fixtures/all/a.js
index d00d765cc..1f4f6e8b2 100644
--- a/packages/transformers/test/fixtures/all/a.js
+++ b/packages/transformers/test/fixtures/all/a.js
@@ -1,5 +1,5 @@
 function hello(indentSize, type) {
   if (indentSize === 4 && type !== 'tab') {
-    	console.log('Each next indentation will increase on 4 spaces'); // [!code error] // [!code focus]
+    	console.log('Each next indentation will increase on 4 spaces'); // [!code error] [!code focus]
   }
 }
diff --git a/packages/transformers/test/fixtures/highlight-word/complex.js.output.html b/packages/transformers/test/fixtures/highlight-word/complex.js.output.html
index 7ce28d971..5233e7c2e 100644
--- a/packages/transformers/test/fixtures/highlight-word/complex.js.output.html
+++ b/packages/transformers/test/fixtures/highlight-word/complex.js.output.html
@@ -1,4 +1,4 @@
-
export function transformerNotationFocus(  options = {}, ) {  const options = 'options'  console.log(options)  options.a = "HELLO"  console.log('// [!code word:options.a]')}
+
export function transformerNotationFocus(  options = {}, ) {  const options = 'options'  console.log(options)  options.a = "HELLO"  console.log('// [!code word:options.a]')}
\ No newline at end of file diff --git a/packages/transformers/test/fixtures/match-algorithm/v3.js b/packages/transformers/test/fixtures/match-algorithm/v3.js new file mode 100644 index 000000000..77831b825 --- /dev/null +++ b/packages/transformers/test/fixtures/match-algorithm/v3.js @@ -0,0 +1,22 @@ +function hello(indentSize, type) { + console.log('error and focus'); // [!code error] [!code focus] +} + +// [!code focus:4] +console.log('focus') +console.log('focus') +console.log('focus') +console.log('focus') + +// [!code highlight:2] +console.log('highlighted') +console.log('highlighted') + +console.log('highlighted') // [!code highlight:2] +console.log('highlighted') + +// [!code word:options:3] +let options = 'options' +options = {}, // [!code word:log] +console.log(options) +options.a = "HELLO" // should not be highlighted diff --git a/packages/transformers/test/fixtures/match-algorithm/v3.js.output.html b/packages/transformers/test/fixtures/match-algorithm/v3.js.output.html new file mode 100644 index 000000000..51374d0b6 --- /dev/null +++ b/packages/transformers/test/fixtures/match-algorithm/v3.js.output.html @@ -0,0 +1,11 @@ +
function hello(indentSize, type) {  console.log('error and focus'); }console.log('focus')console.log('focus')console.log('focus')console.log('focus')console.log('highlighted')console.log('highlighted')console.log('highlighted') console.log('highlighted')let options = 'options'options = {}, console.log(options)options.a = "HELLO" // should not be highlighted
\ No newline at end of file