feat: add DSH IDE open-file references
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { basename } from 'node:path'
|
||||
import * as vscode from 'vscode'
|
||||
import { createSnapshot, type OpenDocument } from './model.js'
|
||||
import { SnapshotPublisher } from './publisher.js'
|
||||
|
||||
const HEARTBEAT_MS = 15_000
|
||||
const DEBOUNCE_MS = 150
|
||||
|
||||
function localFile(uri: vscode.Uri): string | undefined {
|
||||
return uri.scheme === 'file' && uri.fsPath ? uri.fsPath : undefined
|
||||
}
|
||||
|
||||
function tabDocuments(tab: vscode.Tab): OpenDocument[] {
|
||||
const input = tab.input
|
||||
const paths: string[] = []
|
||||
if (input instanceof vscode.TabInputText) {
|
||||
const path = localFile(input.uri)
|
||||
if (path) paths.push(path)
|
||||
} else if (input instanceof vscode.TabInputTextDiff) {
|
||||
const original = localFile(input.original)
|
||||
const modified = localFile(input.modified)
|
||||
if (original) paths.push(original)
|
||||
if (modified) paths.push(modified)
|
||||
}
|
||||
return paths.map((path) => ({ path, label: basename(path), dirty: tab.isDirty }))
|
||||
}
|
||||
|
||||
export function collectOpenDocuments(): OpenDocument[] {
|
||||
return vscode.window.tabGroups.all.flatMap((group) => group.tabs.flatMap(tabDocuments))
|
||||
}
|
||||
|
||||
function workspaceLabel(): string | undefined {
|
||||
const workspaceFile = vscode.workspace.workspaceFile
|
||||
if (workspaceFile?.scheme === 'file') return workspaceFile.fsPath
|
||||
const folders = vscode.workspace.workspaceFolders
|
||||
if (folders?.length === 1 && folders[0]?.uri.scheme === 'file') return folders[0].uri.fsPath
|
||||
return vscode.workspace.name
|
||||
}
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
||||
const output = vscode.window.createOutputChannel('DSH Open File Bridge', { log: true })
|
||||
const publisher = new SnapshotPublisher()
|
||||
let debounce: NodeJS.Timeout | undefined
|
||||
let disposed = false
|
||||
|
||||
const publish = async () => {
|
||||
if (disposed) return
|
||||
const snapshot = createSnapshot({
|
||||
instanceId: publisher.instanceId,
|
||||
processId: process.pid,
|
||||
workspace: workspaceLabel(),
|
||||
documents: collectOpenDocuments(),
|
||||
})
|
||||
try {
|
||||
await publisher.publish(snapshot)
|
||||
output.debug(`Published ${snapshot.documents.length} open files`)
|
||||
} catch (error) {
|
||||
output.error(`Snapshot publish failed: ${error instanceof Error ? error.message : String(error)}`)
|
||||
}
|
||||
}
|
||||
|
||||
const schedule = () => {
|
||||
if (debounce) clearTimeout(debounce)
|
||||
debounce = setTimeout(() => void publish(), DEBOUNCE_MS)
|
||||
}
|
||||
|
||||
context.subscriptions.push(
|
||||
output,
|
||||
vscode.window.tabGroups.onDidChangeTabs(schedule),
|
||||
vscode.window.onDidChangeActiveTextEditor(schedule),
|
||||
vscode.workspace.onDidChangeWorkspaceFolders(schedule),
|
||||
)
|
||||
const heartbeat = setInterval(() => void publish(), HEARTBEAT_MS)
|
||||
const onExit = () => publisher.disposeSync()
|
||||
process.once('exit', onExit)
|
||||
|
||||
context.subscriptions.push({
|
||||
dispose() {
|
||||
disposed = true
|
||||
if (debounce) clearTimeout(debounce)
|
||||
clearInterval(heartbeat)
|
||||
process.off('exit', onExit)
|
||||
void publisher.dispose()
|
||||
},
|
||||
})
|
||||
await publish()
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
// ExtensionContext disposal owns cleanup.
|
||||
}
|
||||
Reference in New Issue
Block a user