Skip to content

Commit 08f34c8

Browse files
authored
perf: improve trace performance (#15)
1 parent d77d016 commit 08f34c8

File tree

7 files changed

+105
-2020
lines changed

7 files changed

+105
-2020
lines changed

messages/src/messages.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,25 @@ export const gotoLocation = z.object({
2525
})
2626
export type GotoLocation = z.infer<typeof gotoPosition>
2727

28-
export const traceFile = z.object({
29-
message: z.literal('traceFile'),
28+
export const traceFileStart = z.object({
29+
message: z.literal('traceFileStart'),
3030
fileName: z.string(),
31-
traceString: z.string(),
31+
size: z.number(),
3232
})
33-
export type TraceFile = z.infer<typeof traceFile>
33+
export type TraceFileStart = z.infer<typeof traceFileStart>
34+
35+
export const traceFileEnd = z.object({
36+
message: z.literal('traceFileEnd'),
37+
fileName: z.string(),
38+
})
39+
export type TraceFileEnd = z.infer<typeof traceFileEnd>
40+
41+
export const traceFileChunk = z.object({
42+
message: z.literal('traceFileChunk'),
43+
fileName: z.string(),
44+
chunk: z.string(),
45+
})
46+
export type TraceFileChunk = z.infer<typeof traceFileChunk>
3447

3548
export const gotoTracePosition = z.object({
3649
message: z.literal('gotoTracePosition'),
@@ -62,4 +75,4 @@ export const fileStats = z.object({
6275
export type FileStats = z.infer<typeof fileStats>
6376

6477
export type Message = z.infer<typeof message>
65-
export const message = z.union([ping, pong, gotoLocation, gotoPosition, traceFile, gotoTracePosition, positionTypeCounts, fileStats])
78+
export const message = z.union([ping, pong, gotoLocation, gotoPosition, traceFileChunk, traceFileEnd, traceFileStart, gotoTracePosition, positionTypeCounts, fileStats])

playground/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,7 @@ function lazy() {
1515
}
1616
//
1717
//
18+
const multiLine = `a
19+
b
20+
c
21+
d`

playground/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,14 @@ function lazy() {
5151
}
5252
//
5353
//
54+
55+
const multiLine = `a
56+
b
57+
c
58+
d`
59+
60+
type MultiLine = typeof multiLine | `
61+
1
62+
2
63+
3
64+
4`

playground/traces/trace.json

Lines changed: 0 additions & 1511 deletions
This file was deleted.

playground/traces/types.json

Lines changed: 0 additions & 483 deletions
This file was deleted.

src/commands.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { join } from 'node:path'
22
import { spawnSync } from 'node:child_process'
3-
import { mkdirSync, readFileSync, readdirSync } from 'node:fs'
3+
import { createReadStream, mkdirSync, readdirSync, statSync } from 'node:fs'
44
import * as vscode from 'vscode'
55
import { getTracePanel, postMessage, prepareWebView } from './webview'
66
import { getCurrentConfig } from './configuration'
@@ -21,11 +21,32 @@ const commandHandlers = {
2121
if (!document)
2222
return
2323
const fileName = document.fileName
24-
const traceString = document.getText()
25-
postMessage({ message: 'traceFile', fileName, traceString })
24+
sendTrace('', fileName)
2625
},
2726
} as const
2827

28+
function sendTrace(dirName: string, fileName: string) {
29+
const fullFileName = join(dirName, fileName)
30+
const stat = statSync(fullFileName)
31+
const size = stat.size
32+
33+
const stream = createReadStream(fullFileName, { autoClose: true, emitClose: true, encoding: 'utf-8' })
34+
35+
postMessage({ message: 'traceFileStart', fileName, size })
36+
37+
stream.on('end', () => postMessage({ message: 'traceFileEnd', fileName }))
38+
39+
function readChunks() {
40+
const chunk = stream.read()
41+
if (chunk === null)
42+
return
43+
44+
postMessage({ message: 'traceFileChunk', fileName, chunk })
45+
setImmediate(readChunks)
46+
}
47+
stream.on('readable', readChunks)
48+
}
49+
2950
export function registerCommands(context: vscode.ExtensionContext) {
3051
for (const cmd in commandHandlers) {
3152
const disposable = vscode.commands.registerCommand(
@@ -67,7 +88,7 @@ function runTrace(context: vscode.ExtensionContext) {
6788

6889
const projectPath = getProjectPath()
6990
if (!projectPath) {
70-
vscode.window.showErrorMessage('could not get procject path from workspace folders')
91+
vscode.window.showErrorMessage('could not get project path from workspace folders')
7192
return
7293
}
7394

@@ -79,10 +100,8 @@ function runTrace(context: vscode.ExtensionContext) {
79100

80101
try {
81102
const fileNames = readdirSync(traceDir)
82-
for (const fileName of fileNames) {
83-
const traceString = readFileSync(join(traceDir, fileName)).toString()
84-
postMessage({ message: 'traceFile', fileName, traceString })
85-
}
103+
for (const fileName of fileNames)
104+
sendTrace(traceDir, fileName)
86105
}
87106
catch (e) {
88107
vscode.window.showErrorMessage(e instanceof Error ? e.message : `${e}`)

ui/components/fileManager.client.vue

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,65 @@ import { type Tree, toTree } from '~/src/traceTree'
55
66
const files = useState('files', () => ({}) as Record<string, TraceData>)
77
8-
const traceTree = useState('traceTree', () => undefined as Tree | undefined)
8+
const traceTree = useState('traceTree', () => shallowRef(undefined as Tree | undefined))
9+
10+
const tmpTraceStrings: Record<string, string> = {}
11+
12+
const fileProgress = reactive({} as Record<string, string>)
13+
14+
const fileSizes = reactive({} as Record<string, [number, number]>)
15+
16+
const inProcess = ref(0)
917
1018
function handleMessage(e: MessageEvent<unknown>) {
1119
const parsed = Messages.message.safeParse(e.data)
1220
if (!parsed.success)
1321
return
1422
15-
if (parsed.data.message === 'traceFile') {
16-
try {
17-
const json = JSON.parse(parsed.data.traceString)
23+
switch (parsed.data.message) {
24+
case 'traceFileStart':
25+
inProcess.value++
26+
fileSizes[parsed.data.fileName] = [0, parsed.data.size]
27+
tmpTraceStrings[parsed.data.fileName] = ''
28+
files.value[parsed.data.fileName] ??= []
29+
break
30+
31+
case 'traceFileChunk': {
32+
const fileName = parsed.data.fileName
33+
const size = fileSizes[fileName]
34+
size[0] += parsed.data.chunk.length
35+
fileProgress[fileName] = size.join(' / ')
36+
tmpTraceStrings[fileName] += parsed.data.chunk
37+
break
38+
}
39+
40+
case 'traceFileEnd': {
41+
inProcess.value--
42+
fileProgress[parsed.data.fileName] = 'Received'
43+
44+
try {
45+
const json = JSON.parse(tmpTraceStrings[parsed.data.fileName])
1846
19-
const arr = traceData.safeParse(json)
20-
if (!arr.success)
21-
return
47+
const arr = traceData.safeParse(json)
48+
if (!arr.success)
49+
return
2250
23-
files.value[parsed.data.fileName] = arr.data
51+
files.value[parsed.data.fileName] = shallowReactive(arr.data)
52+
}
53+
catch (_e) {}
54+
55+
break
2456
}
25-
catch (_e) {}
2657
}
2758
}
2859
60+
const filters = useState('treeFilters', () => ({ startsWith: 'check', sourceFileName: '', position: 0 }))
61+
2962
function processTraces() {
3063
const values = Object.values(files.value).flat(1) as unknown as TraceData
31-
// eslint-disable-next-line no-debugger, no-restricted-syntax
32-
debugger
3364
const tree = toTree(values)
3465
traceTree.value = tree
66+
filters.value = { startsWith: 'check', sourceFileName: '', position: 0 }
3567
}
3668
3769
onMounted(() => {
@@ -42,9 +74,9 @@ onMounted(() => {
4274
<template>
4375
<div>
4476
<div v-for="(_data, fileName) in files" :key="fileName">
45-
<div>{{ fileName }}</div>
77+
<div>{{ fileName }} {{ fileProgress[fileName] }}</div>
4678
</div>
47-
<vscode-button @click="processTraces">
79+
<vscode-button :disable="inProcess > 0" @click="processTraces">
4880
Process Traces
4981
</vscode-button>
5082
<div />

0 commit comments

Comments
 (0)