Skip to content

Commit

Permalink
Dedent virtual markdown chunks (#370)
Browse files Browse the repository at this point in the history
MDX allows to use indentation. In regular markdown however, indented
code represents a code block. To work around this, all virtual markdown
chunks are now dedented. This may change the semantic meaning of certain
markdown, but it doesn’t affect editor features as far as I’m aware.
  • Loading branch information
remcohaszing authored Dec 14, 2023
1 parent f7ac84b commit 8690a6a
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 10 deletions.
52 changes: 42 additions & 10 deletions packages/language-service/lib/language-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,24 @@ function addOffset(mapping, source, generated, startOffset, endOffset) {
return generated
}

mapping.sourceOffsets.push(startOffset)
mapping.generatedOffsets.push(generated.length)
mapping.lengths.push(endOffset - startOffset)
const length = endOffset - startOffset
const previousSourceOffset = mapping.sourceOffsets.at(-1)
const previousGeneratedOffset = mapping.generatedOffsets.at(-1)
const previousLength = mapping.lengths.at(-1)
if (
previousSourceOffset !== undefined &&
previousGeneratedOffset !== undefined &&
previousLength !== undefined &&
previousSourceOffset + previousLength === startOffset &&
previousGeneratedOffset + previousLength === generated.length
) {
mapping.lengths[mapping.lengths.length - 1] += length
} else {
mapping.sourceOffsets.push(startOffset)
mapping.generatedOffsets.push(generated.length)
mapping.lengths.push(length)
}

return generated + source.slice(startOffset, endOffset)
}

Expand Down Expand Up @@ -323,13 +338,30 @@ function getVirtualFiles(fileName, snapshot, ts, processor) {
*/
function updateMarkdownFromOffsets(startOffset, endOffset) {
if (nextMarkdownSourceStart !== startOffset) {
markdown = addOffset(
markdownMapping,
mdx,
markdown,
nextMarkdownSourceStart,
startOffset
)
const slice = mdx.slice(nextMarkdownSourceStart, startOffset)
for (const match of slice.matchAll(/^[\t ]*(.*\r?\n?)/gm)) {
if (match.index === undefined) {
continue
}

const [line, lineContent] = match
if (line.length === 0) {
continue
}

const lineEnd = nextMarkdownSourceStart + match.index + line.length
let lineStart = lineEnd - lineContent.length
if (
match.index === 0 &&
nextMarkdownSourceStart !== 0 &&
mdx[lineStart - 1] !== '\n'
) {
lineStart = nextMarkdownSourceStart + match.index
}

markdown = addOffset(markdownMapping, mdx, markdown, lineStart, lineEnd)
}

if (startOffset !== endOffset) {
markdown += '<!---->'
}
Expand Down
89 changes: 89 additions & 0 deletions packages/language-service/test/language-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,95 @@ test('create virtual file w/ mdxTextExpression', () => {
})
})

test('create virtual file w/ dedented markdown content', () => {
const module = getLanguageModule(typescript)

const snapshot = snapshotFromLines(
' | Language |',
' | --- |',
' | MDX |',
' | JavaScript |',
'| TypeScript |'
)

const file = module.createVirtualFile('/test.mdx', 'mdx', snapshot)

assert.deepEqual(file, {
fileName: '/test.mdx',
languageId: 'mdx',
mappings: [
{
sourceOffsets: [0],
generatedOffsets: [0],
lengths: [81],
data: {
completion: true,
format: true,
navigation: true,
semantic: true,
structure: true,
verification: true
}
}
],
snapshot,
embeddedFiles: [
{
embeddedFiles: [],
fileName: '/test.mdx.jsx',
languageId: 'javascriptreact',
typescript: {
scriptKind: 2
},
mappings: [],
snapshot: snapshotFromLines(
'',
'/**',
' * Render the MDX contents.',
' *',
' * @param {{readonly [K in keyof MDXContentProps]: MDXContentProps[K]}} props',
' * The [props](https://mdxjs.com/docs/using-mdx/#props) that have been passed to the MDX component.',
' */',
'export default function MDXContent(props) {',
" return <><>{''}</></>",
'}',
'',
'// @ts-ignore',
'/** @typedef {0 extends 1 & Props ? {} : Props} MDXContentProps */',
''
)
},
{
embeddedFiles: [],
fileName: '/test.mdx.md',
languageId: 'markdown',
mappings: [
{
sourceOffsets: [5, 19, 39, 52],
generatedOffsets: [0, 13, 21, 29],
lengths: [13, 8, 8, 29],
data: {
completion: true,
format: false,
navigation: true,
semantic: true,
structure: true,
verification: true
}
}
],
snapshot: snapshotFromLines(
'| Language |',
'| --- |',
'| MDX |',
'| JavaScript |',
'| TypeScript |'
)
}
]
})
})

test('create virtual file w/ syntax error', () => {
const module = getLanguageModule(typescript)

Expand Down

0 comments on commit 8690a6a

Please sign in to comment.