Skip to content

Commit

Permalink
feat: apply full line decoration when possible
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Feb 2, 2024
1 parent e72886f commit 52f6a92
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 19 deletions.
41 changes: 25 additions & 16 deletions packages/core/src/transformer-decorations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Element, ElementContent } from 'hast'
import type { DecorationItem, OffsetOrPosition, ResolvedDecorationItem, ResolvedPosition, ShikiTransformer, ShikiTransformerContextMeta, ShikiTransformerContextSource } from './types'
import type { DecorationItem, DecorationTransformType, OffsetOrPosition, ResolvedDecorationItem, ResolvedPosition, ShikiTransformer, ShikiTransformerContextMeta, ShikiTransformerContextSource } from './types'
import { addClassToHast, createPositionConverter, splitTokens } from './utils'
import { ShikiError } from './error'

Expand Down Expand Up @@ -133,25 +133,34 @@ export function transformerDecorations(): ShikiTransformer {
throw new ShikiError(`Failed to find end index for decoration ${JSON.stringify(decoration.end)}`)

const children = lineEl.children.slice(startIndex, endIndex)
const element: Element = !decoration.alwaysWrap && children.length === 1 && children[0].type === 'element'
? children[0]
: {
type: 'element',
tagName: 'span',
properties: {},
children,
}

applyDecoration(element, decoration, false)

lineEl.children.splice(startIndex, children.length, element)

// Full line decoration
if (!decoration.alwaysWrap && children.length === lineEl.children.length) {
applyDecoration(lineEl, decoration, 'line')
}
// Single token decoration
else if (!decoration.alwaysWrap && children.length === 1 && children[0].type === 'element') {
applyDecoration(children[0], decoration, 'token')
}
// Create a wrapper for the decoration
else {
const wrapper: Element = {
type: 'element',
tagName: 'span',
properties: {},
children,
}

applyDecoration(wrapper, decoration, 'wrapper')
lineEl.children.splice(startIndex, children.length, wrapper)
}
}

function applyLine(line: number, decoration: DecorationItem) {
lines[line] = applyDecoration(lines[line], decoration, true)
lines[line] = applyDecoration(lines[line], decoration, 'line')
}

function applyDecoration(el: Element, decoration: DecorationItem, isLine: boolean) {
function applyDecoration(el: Element, decoration: DecorationItem, type: DecorationTransformType) {
const properties = decoration.properties || {}
const transform = decoration.transform || (i => i)

Expand All @@ -163,7 +172,7 @@ export function transformerDecorations(): ShikiTransformer {
}
if (decoration.properties?.class)
addClassToHast(el, decoration.properties.class as string[])
el = transform(el, isLine) || el
el = transform(el, type) || el
return el
}

Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/types/decorations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface DecorationItem {
/**
* A custom function to transform the element after it has been created.
*/
transform?: (element: Element, isEntireLine: boolean) => Element | void
transform?: (element: Element, type: DecorationTransformType) => Element | void

/**
* By default when the decoration contains only one token, the decoration will be applied to the token.
Expand All @@ -47,6 +47,8 @@ export interface ResolvedDecorationItem extends Omit<DecorationItem, 'start' | '
end: ResolvedPosition
}

export type DecorationTransformType = 'wrapper' | 'line' | 'token'

export interface Position {
line: number
character: number
Expand Down
4 changes: 2 additions & 2 deletions packages/shiki/test/out/decorations/basic.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 52f6a92

Please sign in to comment.