Skip to content

Commit e2275a5

Browse files
authored
Merge pull request #16 from Artifizer/main
feat: make nice colored GTS IDs validation and suggestions in the VS Code editor
2 parents 141ff4a + a15dc64 commit e2275a5

16 files changed

Lines changed: 898 additions & 19 deletions

File tree

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,13 @@ Version 0.1.0:
308308
- [x] Refresh resilience: restore selected entity and viewport after reload
309309
- [x] Secure webview (CSP + nonce) and asset routing in extension
310310

311-
Backlog:
311+
Version 0.2.0:
312312
- [x] VS Code editor: inline diagnostics and quick fixes (code actions)
313-
- [ ] VS Code editor: jump to entity file / peek references
313+
- [x] VS Code editor: jump to entity file / peek references
314+
- [x] VS Code editor: ensure referred GTS ID exists in the project
315+
- [x] VS Code editor: suggest GTS IDs in case of misprints
316+
317+
Backlog:
314318
- [ ] Diagram: multi-select, align/distribute, grouping and subgraphs
315319
- [ ] Diagram: export PNG/SVG; import/export layout snapshots
316320
- [ ] Performance: worker-based parsing, incremental graph build, virtualized lists

apps/electron/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@gts-viewer/electron",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "GTS Viewer Electron Application",
55
"main": "dist/main/main.js",
66
"scripts": {

apps/server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@gts/server",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"private": true,
55
"type": "module",
66
"main": "./dist/index.js",

apps/server/src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ export async function createApp(dbFile: string, defaultWorkspace: string = 'defa
222222
// Health
223223
app.get('/health', (_req, res) => {
224224
const totalEntities = registry.jsonObjs.size + registry.jsonSchemas.size
225-
res.json({ status: 'ok', db: 'ok', backendVersion: '0.1.0', gtsEntities: totalEntities })
225+
res.json({ status: 'ok', db: 'ok', backendVersion: '0.2.0', gtsEntities: totalEntities })
226226
})
227227

228228
// Get GTS entity by ID

apps/vscode-extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "gts-viewer-vscode",
33
"displayName": "GTS Viewer",
44
"description": "View and manage GTS JSON schemas and instances",
5-
"version": "0.1.0",
5+
"version": "0.2.0",
66
"private": true,
77
"publisher": "gts",
88
"repository": {

apps/vscode-extension/src/extension.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import { setLastScanFiles } from './scanStore'
55
import { RepoLayoutStorage } from './storage'
66
import { initValidation, notifyInitialScanComplete } from './validation'
77
import { isGtsCandidateFile } from './helpers'
8+
import { GtsLinkProvider } from './linkProvider'
89
import type { LayoutSaveRequest, LayoutTarget, LayoutSnapshot } from '@gts/layout-storage'
910

1011
let viewerPanel: vscode.WebviewPanel | null = null
1112
let layoutStorage: RepoLayoutStorage | null = null
1213
let hasPerformedInitialScan: boolean = false // Track if initial scan with default file has been done
14+
let gtsLinkProvider: GtsLinkProvider | null = null
1315

1416
function getNonce(): string {
1517
let text = ''
@@ -75,6 +77,15 @@ async function scanAndPost(includeGlob: string = '**/*.{json,jsonc,gts}', isInit
7577
viewerPanel!.webview.postMessage({ type: 'gts-scan-result', detail: { files, defaultFilePath: selectedFilePath } })
7678
try { setLastScanFiles(files) } catch {}
7779

80+
// Refresh the link provider with the latest registry
81+
if (gtsLinkProvider) {
82+
try {
83+
await gtsLinkProvider.refresh()
84+
} catch (e) {
85+
console.error('[GTS] Error refreshing link provider:', e)
86+
}
87+
}
88+
7889
try {
7990
const objs = Array.from(registry.jsonObjs.values()).map(o => ({ id: o.id, listSequence: o.listSequence, filePath: o.file?.path, schemaId: o.schemaId, validation: o.validation }))
8091
const schemas = Array.from(registry.jsonSchemas.values()).map(s => ({ id: s.id, filePath: s.file?.path, validation: s.validation }))
@@ -107,13 +118,104 @@ export async function activate(context: vscode.ExtensionContext) {
107118

108119
initValidation(context)
109120

121+
// Create diagnostic collection for GTS validation
122+
const gtsDiagnostics = vscode.languages.createDiagnosticCollection('gts')
123+
context.subscriptions.push(gtsDiagnostics)
124+
125+
// Initialize and register GTS link provider for clickable GTS IDs
126+
gtsLinkProvider = new GtsLinkProvider(gtsDiagnostics)
127+
128+
// Register link provider for JSON, JSONC, and GTS files
129+
const documentSelector: vscode.DocumentSelector = [
130+
{ language: 'json', scheme: 'file' },
131+
{ language: 'jsonc', scheme: 'file' },
132+
{ language: 'gts', scheme: 'file' }
133+
]
134+
135+
context.subscriptions.push(
136+
vscode.languages.registerDocumentLinkProvider(documentSelector, gtsLinkProvider)
137+
)
138+
139+
context.subscriptions.push(
140+
vscode.languages.registerHoverProvider(documentSelector, gtsLinkProvider)
141+
)
142+
143+
// Update decorations when active editor changes
144+
context.subscriptions.push(
145+
vscode.window.onDidChangeActiveTextEditor(editor => {
146+
if (editor && gtsLinkProvider) {
147+
gtsLinkProvider.updateDecorations(editor)
148+
}
149+
})
150+
)
151+
152+
// Update decorations when document changes
153+
context.subscriptions.push(
154+
vscode.workspace.onDidChangeTextDocument(event => {
155+
const editor = vscode.window.activeTextEditor
156+
if (editor && editor.document === event.document && gtsLinkProvider) {
157+
gtsLinkProvider.updateDecorations(editor)
158+
}
159+
})
160+
)
161+
162+
// Initial decoration for all visible editors
163+
if (gtsLinkProvider) {
164+
for (const editor of vscode.window.visibleTextEditors) {
165+
gtsLinkProvider.updateDecorations(editor)
166+
}
167+
}
168+
169+
console.log('[GTS] Link provider registered for JSON/JSONC/GTS files')
170+
110171
// Register commands
111172
context.subscriptions.push(
112173
vscode.commands.registerCommand('gts.openViewer', (resource?: vscode.Uri) => {
113174
openViewer(context, resource)
114175
})
115176
)
116177

178+
// Register command to replace erroneous GTS ID with suggestion
179+
context.subscriptions.push(
180+
vscode.commands.registerCommand('gts.replaceGtsId', async (documentUri: string, rangeData: any, newText: string, includeQuotes: boolean) => {
181+
try {
182+
const uri = vscode.Uri.parse(documentUri)
183+
const document = await vscode.workspace.openTextDocument(uri)
184+
const editor = await vscode.window.showTextDocument(document)
185+
186+
// Reconstruct the Range from serialized data
187+
let range = new vscode.Range(
188+
rangeData.start.line,
189+
rangeData.start.character,
190+
rangeData.end.line,
191+
rangeData.end.character
192+
)
193+
194+
// If we need to include quotes, extend the range and wrap the text
195+
let replacementText = newText
196+
if (includeQuotes) {
197+
// Extend range to include the quotes (one character before and after)
198+
range = new vscode.Range(
199+
rangeData.start.line,
200+
rangeData.start.character - 1,
201+
rangeData.end.line,
202+
rangeData.end.character + 1
203+
)
204+
replacementText = `"${newText}"`
205+
}
206+
207+
await editor.edit(editBuilder => {
208+
editBuilder.replace(range, replacementText)
209+
})
210+
211+
// Show success message
212+
vscode.window.showInformationMessage(`Replaced with: ${newText}`)
213+
} catch (error) {
214+
vscode.window.showErrorMessage(`Failed to replace GTS ID: ${error}`)
215+
}
216+
})
217+
)
218+
117219
// Show welcome message
118220
vscode.window.showInformationMessage('GTS Viewer is ready! Use "GTS: Open Viewer" to start.')
119221
}
@@ -157,6 +259,16 @@ async function performInitialScan() {
157259
await registry.ingestFiles(files, DEFAULT_GTS_CONFIG)
158260
console.log(`[GTS] Registry initialized: ${registry.jsonSchemas.size} schemas, ${registry.jsonObjs.size} objects`)
159261

262+
// Refresh the link provider with the initial scan data
263+
if (gtsLinkProvider) {
264+
try {
265+
await gtsLinkProvider.refresh()
266+
console.log('[GTS] Link provider refreshed with initial scan data')
267+
} catch (e) {
268+
console.error('[GTS] Error refreshing link provider:', e)
269+
}
270+
}
271+
160272
// Notify validation system that initial scan is complete
161273
notifyInitialScanComplete()
162274
} catch (error) {
@@ -173,6 +285,11 @@ export async function deactivate() {
173285
viewerPanel = null
174286
}
175287

288+
if (gtsLinkProvider) {
289+
gtsLinkProvider.dispose()
290+
gtsLinkProvider = null
291+
}
292+
176293
layoutStorage = null
177294
}
178295

0 commit comments

Comments
 (0)