Skip to content

Commit

Permalink
SourceMap class, preserve by Civet worker
Browse files Browse the repository at this point in the history
  • Loading branch information
edemaine committed Dec 30, 2024
1 parent 3e8ffb9 commit 3458384
Show file tree
Hide file tree
Showing 15 changed files with 126 additions and 118 deletions.
4 changes: 2 additions & 2 deletions civet.dev/public/playground.worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ onmessage = async (e) => {
if (errors.length) {
// Rerun with SourceMap to get error location
errors = []
tsCode = Civet.generate(ast, { errors, sourceMap: Civet.SourceMap(code) });
tsCode = Civet.generate(ast, { errors, sourceMap: new Civet.SourceMap(code) });
error = errors[0]
}
} catch (e) {
Expand Down Expand Up @@ -61,7 +61,7 @@ onmessage = async (e) => {
if (errors.length) {
// Rerun with SourceMap to get error location
errors = []
jsCode = Civet.generate(ast, { js: true, errors, sourceMap: Civet.SourceMap(code) });
jsCode = Civet.generate(ast, { js: true, errors, sourceMap: new Civet.SourceMap(code) });
// Don't postError(errors[0]) here so that we still display TypeScript
// transpilation; only show error when trying to Run code
throw errors[0]
Expand Down
2 changes: 1 addition & 1 deletion integration/eslint/source/index.civet
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function civet(options: Options = {js: true}): ESLint.Plugin
// Length 1 corresponding to the return value of `preprocess`
[messages] := _messages
if sourceMap := sourceMaps.get filename
sourceMapLines := sourceMap.data.lines
sourceMapLines := sourceMap.lines ?? sourceMap.data.lines // older Civet
for each message of messages
if message.line?
[message.line, message.column] = remap message.line, message.column
Expand Down
12 changes: 6 additions & 6 deletions lsp/source/lib/typescript-service.mts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,8 @@ try {
}

interface SourceMap {
data: {
lines: CivetSourceMap["data"]["lines"]
}
lines: CivetSourceMap["lines"]
data: CivetSourceMap["data"]
}

// ts doesn't have this key in the type
Expand All @@ -57,7 +56,7 @@ interface ResolvedModuleWithFailedLookupLocations extends ts.ResolvedModuleWithF
}

export interface FileMeta {
sourcemapLines: SourceMap["data"]["lines"] | undefined
sourcemapLines: SourceMap["lines"] | undefined
transpiledDoc: TextDocument | undefined
parseErrors: (Error | ParseError)[] | undefined
fatal: boolean // whether errors were fatal during compilation, so no doc
Expand Down Expand Up @@ -384,7 +383,7 @@ function TSHost(compilationSettings: CompilerOptions, initialFileNames: string[]
return snapshot
}

function createOrUpdateMeta(path: string, transpiledDoc: TextDocument, sourcemapLines?: SourceMap["data"]["lines"], parseErrors?: (Error | ParseError)[], fatal?: boolean) {
function createOrUpdateMeta(path: string, transpiledDoc: TextDocument, sourcemapLines?: SourceMap["lines"], parseErrors?: (Error | ParseError)[], fatal?: boolean) {
let meta = fileMetaData.get(path)

if (!meta) {
Expand Down Expand Up @@ -415,7 +414,8 @@ function TSHost(compilationSettings: CompilerOptions, initialFileNames: string[]

if (result) {
const { code: transpiledCode, sourceMap, errors } = result
createOrUpdateMeta(sourcePath, transpiledDoc, sourceMap?.data.lines, errors, false)
const sourceMapLines = sourceMap?.lines ?? sourceMap?.data.lines // older Civet
createOrUpdateMeta(sourcePath, transpiledDoc, sourceMapLines, errors, false)
TextDocument.update(transpiledDoc, [{ text: transpiledCode }], version)

return transpiledCode
Expand Down
2 changes: 1 addition & 1 deletion lsp/source/lib/util.mts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
remapRange,
} from '@danielx/civet/ts-diagnostic';

export type SourcemapLines = SourceMap['data']['lines'];
export type SourcemapLines = SourceMap['lines'];

export {
flattenDiagnosticMessageText,
Expand Down
8 changes: 4 additions & 4 deletions lsp/test/util.civet
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// TODO: figure out the magic ts-note/TypeScript config to make this work without destructuring from default import
{ intersectRanges, containsRange, makeRange, remapPosition, forwardMap, convertCoffeeScriptSourceMap } from ../source/lib/util.mjs
{ intersectRanges, containsRange, makeRange, remapPosition, forwardMap } from ../source/lib/util.mjs
assert from assert
Civet from @danielx/civet

Expand All @@ -25,7 +25,7 @@ describe "util", ->
sourceMap: true
})

linesMap := sourceMap.data.lines
linesMap := sourceMap.lines
// console.log code, linesMap

assert.deepEqual remapPosition({
Expand All @@ -52,7 +52,7 @@ describe "util", ->

[0..6].forEach (i) ->
srcColumn := i + 13
pos := forwardMap(sourceMap.data.lines, {line: 0, character: srcColumn})
pos := forwardMap(sourceMap.lines, {line: 0, character: srcColumn})
srcStr := src.slice(srcColumn, srcColumn + 7)
assert.equal srcStr.replace(" ", "("), generatedLines[pos.line].slice(pos.character, pos.character + 7)

Expand All @@ -71,7 +71,7 @@ describe "util", ->
generatedLines := code.split("\n")

[2..6].forEach (srcColumn, i) ->
pos := forwardMap(sourceMap.data.lines, {line: 1, character: srcColumn})
pos := forwardMap(sourceMap.lines, {line: 1, character: srcColumn})
srcStr := srcLines[1].slice(srcColumn, srcColumn + 5 - i)
generatedStr := generatedLines[pos.line].slice(pos.character, pos.character + 5 - i)
assert.equal srcStr, generatedStr
2 changes: 1 addition & 1 deletion source/cli.civet
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ export function repl(args: string[], options: Options)
if errors#
// Rerun with sourceMap
errors = []
generate ast, { ...options, errors, sourceMap: SourceMap(input) }
generate ast, { ...options, errors, sourceMap: new SourceMap input }
showError errors[0]
return callback null, undefined

Expand Down
16 changes: 9 additions & 7 deletions source/generate.civet
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ type { ASTNode } from './parser/types.civet'
export type Options =
sourceMap?: undefined |
updateSourceMap: (token: string, pos?: number) => void
data: { srcLine: number, srcColumn: number, srcOffset: number }
srcLine: number
srcColumn: number
srcOffset: number
js?: boolean
filename?: string
errors?: ParseError[]
Expand All @@ -17,7 +19,7 @@ function stringify(node: ASTNode): string
return `${node}`

function gen(root: ASTNode, options: Options): string
updateSourceMap := options?.sourceMap?.updateSourceMap
updateSourceMap := options?.sourceMap?@updateSourceMap
return recurse root

function recurse(node: ASTNode): string
Expand All @@ -26,7 +28,7 @@ function gen(root: ASTNode, options: Options): string
if node <? "string"
// increment output line/column
updateSourceMap? node
//console.log 'advance', JSON.stringify(node), options.sourceMap.data.srcLine, options.sourceMap.data.srcColumn if options?.sourceMap?
//console.log 'advance', JSON.stringify(node), options.sourceMap.srcLine, options.sourceMap.srcColumn if options?.sourceMap?

return node

Expand All @@ -50,10 +52,10 @@ function gen(root: ASTNode, options: Options): string
if node.$loc?
sourceMap.updateSourceMap "", node.$loc.pos
// Convert 0-based to 1-based
line = sourceMap.data.srcLine + 1
column = sourceMap.data.srcColumn + 1
line = sourceMap.srcLine + 1
column = sourceMap.srcColumn + 1
//console.log 'error', node.message, 'at', line, column, offset
offset = sourceMap.data.srcOffset
offset = sourceMap.srcOffset
options.errors ?= []
options.errors.push new ParseError
node.message
Expand All @@ -67,7 +69,7 @@ function gen(root: ASTNode, options: Options): string
if "$loc" in node
{token, $loc} := node
updateSourceMap? token, $loc.pos if $loc?
//console.log 'set', node, options.sourceMap.data.srcLine, options.sourceMap.data.srcColumn if options?.sourceMap?
//console.log 'set', node, options.sourceMap.srcLine, options.sourceMap.srcColumn if options?.sourceMap?
return token

unless node.children
Expand Down
4 changes: 2 additions & 2 deletions source/main.civet
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export function compile<const T extends CompilerOptions>(src: string, options?:

if options.sourceMap or options.inlineMap
//@ts-ignore sourceMap option for generate
options.sourceMap = SourceMap(src)
options.sourceMap = new SourceMap src
code := generate ast, options
checkErrors()

Expand All @@ -211,7 +211,7 @@ export function compile<const T extends CompilerOptions>(src: string, options?:
if options!.errors?.length
delete options.errors
//@ts-ignore sourceMap option for generate
options.sourceMap = SourceMap(src)
options.sourceMap = new SourceMap src
generate ast, options
checkErrors()

Expand Down
163 changes: 82 additions & 81 deletions source/sourcemap.civet
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
type SourceMapEntries = (
export type SourceMapEntries = (
[number, number, number, number, number] |
[number, number, number, number] |
[number]
)[][]

// Utility function to create a line/column lookup table for an input string
export locationTable = (input: string) ->
export function locationTable(input: string): number[]
linesRe := /([^\r\n]*)(\r\n|\r|\n|$)/y
lines := []
line .= 0
Expand All @@ -19,7 +19,7 @@ export locationTable = (input: string) ->

return lines

export lookupLineColumn = (table: number[], pos: number) ->
export function lookupLineColumn(table: number[], pos: number)
l .= 0
prevEnd .= 0

Expand All @@ -29,83 +29,84 @@ export lookupLineColumn = (table: number[], pos: number) ->
// [line, column]; zero based
return [l, pos - prevEnd]

export SourceMap = (sourceString: string) ->
srcTable := locationTable sourceString

sm :=
lines: [[]] as SourceMapEntries
line: 0
colOffset: 0 // relative to previous entry
srcLine: 0
srcColumn: 0
srcOffset: 0
srcTable: srcTable

EOL := /\r?\n|\r/

return
data: sm
source: ->
sourceString
renderMappings: ->
lastSourceLine .= 0
lastSourceColumn .= 0

sm.lines.map (line) =>
line.map (entry) =>
if entry.length is 4
[colDelta, sourceFileIndex, srcLine, srcCol] .= entry
lineDelta := srcLine - lastSourceLine
colDelta = srcCol - lastSourceColumn
lastSourceLine = srcLine
lastSourceColumn = srcCol
`${encodeVlq(entry[0])}${encodeVlq(sourceFileIndex)}${encodeVlq(lineDelta)}${encodeVlq(colDelta)}`
else
encodeVlq entry[0]
.join(",")
.join(";")

json: (srcFileName: string, outFileName: string) ->
version: 3
file: outFileName
sources: [srcFileName]
mappings: @renderMappings()
names: []
sourcesContent: [sourceString]
toString: ->
JSON.stringify this

updateSourceMap: (outputStr: string, inputPos?: number, colOffset=0) ->
outLines := outputStr.split(EOL)

let srcLine: number, srcCol: number
EOL := /\r?\n|\r/
export class SourceMap
lines: SourceMapEntries
line: number
colOffset: number // relative to previous entry
srcLine: number
srcColumn: number
srcOffset: number
srcTable: number[]

@(@source: string)
@lines = [[]]
@line = 0
@colOffset = 0 // relative to previous entry
@srcLine = 0
@srcColumn = 0
@srcOffset = 0
@srcTable = locationTable @source

renderMappings(): string
lastSourceLine .= 0
lastSourceColumn .= 0

for each line of @lines
for each entry of line
if entry.length is 4
[colDelta, sourceFileIndex, srcLine, srcCol] .= entry
lineDelta := srcLine - lastSourceLine
colDelta = srcCol - lastSourceColumn
lastSourceLine = srcLine
lastSourceColumn = srcCol
`${encodeVlq(entry[0])}${encodeVlq(sourceFileIndex)}${encodeVlq(lineDelta)}${encodeVlq(colDelta)}`
else
encodeVlq entry[0]
.join(",")
.join(";")

json(srcFileName: string, outFileName: string)
version: 3
file: outFileName
sources: [srcFileName]
mappings: @renderMappings()
names: []
sourcesContent: [@source]
toString: ->
JSON.stringify this

updateSourceMap(outputStr: string, inputPos?: number, colOffset=0)
outLines := outputStr.split(EOL)

let srcLine: number, srcCol: number

if inputPos?
[srcLine, srcCol] = lookupLineColumn @srcTable, inputPos
srcCol += colOffset
@srcLine = srcLine
@srcColumn = srcCol
@srcOffset = inputPos + outputStr#

for each line, i of outLines
if i > 0
@line++
@srcLine++
@colOffset = 0
@lines[@line] = []
@srcColumn = srcCol = colOffset

l := @colOffset
@colOffset = line.length
@srcColumn += line.length

if inputPos?
[srcLine, srcCol] = lookupLineColumn(srcTable, inputPos)
srcCol += colOffset
sm.srcLine = srcLine
sm.srcColumn = srcCol
sm.srcOffset = inputPos + outputStr#

for each line, i of outLines
if i > 0
sm.line++
sm.srcLine++
sm.colOffset = 0
sm.lines[sm.line] = []
sm.srcColumn = srcCol = colOffset

l := sm.colOffset
sm.colOffset = line.length
sm.srcColumn += line.length

if inputPos?
// srcLine and srcCol are absolute here
sm.lines[sm.line].push [l, 0, srcLine+i, srcCol]
else if l != 0
sm.lines[sm.line].push [l]

return
// srcLine and srcCol are absolute here
@lines[@line].push [l, 0, srcLine+i, srcCol]
else if l != 0
@lines[@line].push [l]

return

smRegexp := /\n\/\/# sourceMappingURL=data:application\/json;charset=utf-8;base64,([+a-zA-Z0-9\/]*=?=?)$/

Expand All @@ -114,16 +115,16 @@ Remap a string with compiled code and a source map to use a new source map
referencing upstream source files.
*/
/* c8 ignore start */
remap := (codeWithSourceMap: string, upstreamMap: {data: {lines: SourceMapEntries}, json: any}, sourcePath: string, targetPath: string) ->
remap := (codeWithSourceMap: string, upstreamMap: {lines: SourceMapEntries, json: any}, sourcePath: string, targetPath: string) ->
let sourceMapText?: string
codeWithoutSourceMap := codeWithSourceMap.replace smRegexp, (match, sm) =>
sourceMapText = sm
""

if sourceMapText
parsed := parseWithLines sourceMapText
composedLines := composeLines upstreamMap.data.lines, parsed.lines
upstreamMap.data.lines = composedLines
composedLines := composeLines upstreamMap.lines, parsed.lines
upstreamMap.lines = composedLines

remappedSourceMapJSON := upstreamMap.json(sourcePath, targetPath)

Expand Down
Loading

0 comments on commit 3458384

Please sign in to comment.