correctly 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -0
- package/LICENSE +373 -0
- package/README.md +24 -0
- package/dist/cli.mjs +294 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/core.d.mts +183 -0
- package/dist/core.d.mts.map +1 -0
- package/dist/core.mjs +2 -0
- package/dist/engine-BgWBnb1A.mjs +746 -0
- package/dist/engine-BgWBnb1A.mjs.map +1 -0
- package/dist/lsp.mjs +315 -0
- package/dist/lsp.mjs.map +1 -0
- package/docs/guide.md +114 -0
- package/examples/basic/correctly.config.json +10 -0
- package/examples/basic/project.json +1 -0
- package/examples/basic/project.jsonc +5 -0
- package/examples/basic/project.schema.json +10 -0
- package/package.json +61 -0
- package/src/cli/check.ts +83 -0
- package/src/cli/main.ts +86 -0
- package/src/cli/readable.ts +295 -0
- package/src/cli/vsix.ts +86 -0
- package/src/core/config.schema.json +47 -0
- package/src/core/config.ts +200 -0
- package/src/core/diagnostics.ts +114 -0
- package/src/core/engine.ts +247 -0
- package/src/core/index.ts +10 -0
- package/src/core/parse.ts +168 -0
- package/src/core/schemas.ts +379 -0
- package/src/core/types.ts +56 -0
- package/src/lsp/hints.ts +82 -0
- package/src/lsp/server.ts +265 -0
- package/src/lsp/workspace.ts +132 -0
- package/src/vscode/README.md +7 -0
- package/src/vscode/extension.ts +33 -0
- package/src/vscode/package.json +43 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from "node:url"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import {
|
|
5
|
+
createConnection,
|
|
6
|
+
TextDocuments,
|
|
7
|
+
TextDocumentSyncKind,
|
|
8
|
+
DidChangeWatchedFilesNotification,
|
|
9
|
+
ResponseError,
|
|
10
|
+
LSPErrorCodes,
|
|
11
|
+
type CancellationToken,
|
|
12
|
+
} from "vscode-languageserver/node"
|
|
13
|
+
import { TextDocument } from "vscode-languageserver-textdocument"
|
|
14
|
+
import { Workspace } from "./workspace.ts"
|
|
15
|
+
import { Hints } from "./hints.ts"
|
|
16
|
+
import type { FileResult } from "../core/types.ts"
|
|
17
|
+
import { diagnostic } from "../core/parse.ts"
|
|
18
|
+
import { diagnosticViews, diagnosticDetails } from "../core/diagnostics.ts"
|
|
19
|
+
|
|
20
|
+
export function lspDiagnostics(result: FileResult, text: string) {
|
|
21
|
+
return diagnosticViews([
|
|
22
|
+
...result.diagnostics,
|
|
23
|
+
...result.failures
|
|
24
|
+
.filter(
|
|
25
|
+
(f) =>
|
|
26
|
+
!result.diagnostics.some(
|
|
27
|
+
(d) => d.code === f.code && d.message === f.message,
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
.map((f) => diagnostic(text, f.code, f.message)),
|
|
31
|
+
]).map((d) => ({
|
|
32
|
+
range: d.range,
|
|
33
|
+
message: d.message,
|
|
34
|
+
code: d.code,
|
|
35
|
+
severity: 1 as const,
|
|
36
|
+
source: "correctly",
|
|
37
|
+
data: { pointer: d.pointer },
|
|
38
|
+
...(d.branches.length
|
|
39
|
+
? {
|
|
40
|
+
relatedInformation: diagnosticDetails(d).map(
|
|
41
|
+
({ label, diagnostic }) => ({
|
|
42
|
+
location: {
|
|
43
|
+
uri: pathToFileURL(result.file).href,
|
|
44
|
+
range: diagnostic.range,
|
|
45
|
+
},
|
|
46
|
+
message: `${label}: ${diagnostic.message}`,
|
|
47
|
+
}),
|
|
48
|
+
),
|
|
49
|
+
}
|
|
50
|
+
: {}),
|
|
51
|
+
}))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function startServer() {
|
|
55
|
+
const connection = createConnection()
|
|
56
|
+
const documents = new TextDocuments(TextDocument)
|
|
57
|
+
const workspace = new Workspace([])
|
|
58
|
+
const hints = new Hints()
|
|
59
|
+
const jobs = new Map<
|
|
60
|
+
string,
|
|
61
|
+
{ controller: AbortController; timer: ReturnType<typeof setTimeout> }
|
|
62
|
+
>()
|
|
63
|
+
let dynamicWatch = false
|
|
64
|
+
const watchedSchemas = new Set<string>()
|
|
65
|
+
workspace.onSchema = (uri) => {
|
|
66
|
+
if (!dynamicWatch || !uri.startsWith("file:") || watchedSchemas.has(uri))
|
|
67
|
+
return
|
|
68
|
+
watchedSchemas.add(uri)
|
|
69
|
+
const file = fileURLToPath(uri)
|
|
70
|
+
void connection.client
|
|
71
|
+
.register(DidChangeWatchedFilesNotification.type, {
|
|
72
|
+
watchers: [
|
|
73
|
+
{
|
|
74
|
+
globPattern: {
|
|
75
|
+
baseUri: pathToFileURL(path.dirname(file)).href,
|
|
76
|
+
pattern: path.basename(file),
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
})
|
|
81
|
+
.catch((error: unknown) => connection.console.error(String(error)))
|
|
82
|
+
}
|
|
83
|
+
connection.onInitialize((params) => {
|
|
84
|
+
workspace.roots = (
|
|
85
|
+
params.workspaceFolders?.map((f) => f.uri) ??
|
|
86
|
+
(params.rootUri ? [params.rootUri] : [])
|
|
87
|
+
)
|
|
88
|
+
.filter((uri) => uri.startsWith("file:"))
|
|
89
|
+
.map((uri) => fileURLToPath(uri))
|
|
90
|
+
dynamicWatch =
|
|
91
|
+
params.capabilities.workspace?.didChangeWatchedFiles
|
|
92
|
+
?.dynamicRegistration ?? false
|
|
93
|
+
return {
|
|
94
|
+
capabilities: {
|
|
95
|
+
textDocumentSync: TextDocumentSyncKind.Incremental,
|
|
96
|
+
completionProvider: { triggerCharacters: ['"', ":"] },
|
|
97
|
+
hoverProvider: true,
|
|
98
|
+
workspace: {
|
|
99
|
+
workspaceFolders: { supported: true, changeNotifications: true },
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
serverInfo: { name: "correctly", version: "0.0.0" },
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
connection.onInitialized(() => {
|
|
106
|
+
if (dynamicWatch)
|
|
107
|
+
void connection.client
|
|
108
|
+
.register(DidChangeWatchedFilesNotification.type, {
|
|
109
|
+
watchers: [{ globPattern: "**/*.{json,jsonc}" }],
|
|
110
|
+
})
|
|
111
|
+
.catch((error: unknown) => connection.console.error(String(error)))
|
|
112
|
+
connection.workspace.onDidChangeWorkspaceFolders((event) => {
|
|
113
|
+
const removed = new Set(event.removed.map((f) => fileURLToPath(f.uri)))
|
|
114
|
+
workspace.roots = [
|
|
115
|
+
...workspace.roots.filter((r) => !removed.has(r)),
|
|
116
|
+
...event.added.map((f) => fileURLToPath(f.uri)),
|
|
117
|
+
]
|
|
118
|
+
refresh()
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
function schedule(document: TextDocument) {
|
|
122
|
+
if (!document.uri.startsWith("file:")) return
|
|
123
|
+
const previous = jobs.get(document.uri)
|
|
124
|
+
if (previous) {
|
|
125
|
+
clearTimeout(previous.timer)
|
|
126
|
+
previous.controller.abort()
|
|
127
|
+
}
|
|
128
|
+
const controller = new AbortController()
|
|
129
|
+
const generation = workspace.generation
|
|
130
|
+
const timer = setTimeout(() => {
|
|
131
|
+
void workspace
|
|
132
|
+
.validate(document)
|
|
133
|
+
.then((result) => {
|
|
134
|
+
if (
|
|
135
|
+
controller.signal.aborted ||
|
|
136
|
+
workspace.generation !== generation ||
|
|
137
|
+
documents.get(document.uri)?.version !== document.version
|
|
138
|
+
)
|
|
139
|
+
return
|
|
140
|
+
return connection.sendDiagnostics({
|
|
141
|
+
uri: document.uri,
|
|
142
|
+
version: document.version,
|
|
143
|
+
diagnostics: lspDiagnostics(result, document.getText()),
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
.catch((error: unknown) => connection.console.error(String(error)))
|
|
147
|
+
}, 40)
|
|
148
|
+
jobs.set(document.uri, { controller, timer })
|
|
149
|
+
}
|
|
150
|
+
function scheduleAll() {
|
|
151
|
+
for (const document of documents.all()) schedule(document)
|
|
152
|
+
}
|
|
153
|
+
function refresh() {
|
|
154
|
+
workspace.invalidate()
|
|
155
|
+
scheduleAll()
|
|
156
|
+
}
|
|
157
|
+
documents.onDidChangeContent(({ document }) => {
|
|
158
|
+
if (!document.uri.startsWith("file:")) return
|
|
159
|
+
const resourceChanged = workspace.open(document)
|
|
160
|
+
if (resourceChanged) scheduleAll()
|
|
161
|
+
else schedule(document)
|
|
162
|
+
})
|
|
163
|
+
documents.onDidClose(async ({ document }) => {
|
|
164
|
+
const job = jobs.get(document.uri)
|
|
165
|
+
if (job) {
|
|
166
|
+
clearTimeout(job.timer)
|
|
167
|
+
job.controller.abort()
|
|
168
|
+
jobs.delete(document.uri)
|
|
169
|
+
}
|
|
170
|
+
await connection.sendDiagnostics({ uri: document.uri, diagnostics: [] })
|
|
171
|
+
if (document.uri.startsWith("file:") && workspace.close(document.uri))
|
|
172
|
+
scheduleAll()
|
|
173
|
+
})
|
|
174
|
+
connection.onDidChangeWatchedFiles((params) => {
|
|
175
|
+
// Cache writes and unrelated JSON files must not trigger schema reloads.
|
|
176
|
+
if (params.changes.some((change) => workspace.resourceChanged(change.uri)))
|
|
177
|
+
refresh()
|
|
178
|
+
})
|
|
179
|
+
connection.onDidChangeConfiguration(refresh)
|
|
180
|
+
async function request<T>(
|
|
181
|
+
uri: string,
|
|
182
|
+
token: CancellationToken,
|
|
183
|
+
action: (
|
|
184
|
+
engine: Awaited<ReturnType<Workspace["engineFor"]>>,
|
|
185
|
+
document: TextDocument,
|
|
186
|
+
) => Promise<T>,
|
|
187
|
+
): Promise<T | null> {
|
|
188
|
+
const document = documents.get(uri)
|
|
189
|
+
if (!document || !uri.startsWith("file:")) return null
|
|
190
|
+
const generation = workspace.generation
|
|
191
|
+
const cancelled = () =>
|
|
192
|
+
token.isCancellationRequested ||
|
|
193
|
+
workspace.generation !== generation ||
|
|
194
|
+
documents.get(uri)?.version !== document.version
|
|
195
|
+
if (cancelled())
|
|
196
|
+
throw new ResponseError(
|
|
197
|
+
LSPErrorCodes.RequestCancelled,
|
|
198
|
+
"Request cancelled",
|
|
199
|
+
)
|
|
200
|
+
const operation = (async () => {
|
|
201
|
+
try {
|
|
202
|
+
const engine = await workspace.engineFor(uri)
|
|
203
|
+
if (cancelled())
|
|
204
|
+
throw new ResponseError(
|
|
205
|
+
LSPErrorCodes.RequestCancelled,
|
|
206
|
+
"Request cancelled",
|
|
207
|
+
)
|
|
208
|
+
const result = await action(engine, document)
|
|
209
|
+
if (cancelled())
|
|
210
|
+
throw new ResponseError(
|
|
211
|
+
LSPErrorCodes.RequestCancelled,
|
|
212
|
+
"Request cancelled",
|
|
213
|
+
)
|
|
214
|
+
return result
|
|
215
|
+
} catch (error) {
|
|
216
|
+
if (error instanceof ResponseError) throw error
|
|
217
|
+
if (cancelled())
|
|
218
|
+
throw new ResponseError(
|
|
219
|
+
LSPErrorCodes.RequestCancelled,
|
|
220
|
+
"Request cancelled",
|
|
221
|
+
)
|
|
222
|
+
connection.console.error(String(error))
|
|
223
|
+
return null
|
|
224
|
+
}
|
|
225
|
+
})()
|
|
226
|
+
let listener: { dispose(): void } | undefined
|
|
227
|
+
const cancellation = new Promise<never>((_resolve, reject) => {
|
|
228
|
+
listener = token.onCancellationRequested(() =>
|
|
229
|
+
reject(
|
|
230
|
+
new ResponseError(
|
|
231
|
+
LSPErrorCodes.RequestCancelled,
|
|
232
|
+
"Request cancelled",
|
|
233
|
+
),
|
|
234
|
+
),
|
|
235
|
+
)
|
|
236
|
+
})
|
|
237
|
+
try {
|
|
238
|
+
return await Promise.race([operation, cancellation])
|
|
239
|
+
} finally {
|
|
240
|
+
listener?.dispose()
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
connection.onCompletion((params, token) =>
|
|
244
|
+
request(params.textDocument.uri, token, (engine, document) =>
|
|
245
|
+
hints.complete(engine, document, params.position),
|
|
246
|
+
),
|
|
247
|
+
)
|
|
248
|
+
connection.onHover((params, token) =>
|
|
249
|
+
request(params.textDocument.uri, token, (engine, document) =>
|
|
250
|
+
hints.hover(engine, document, params.position),
|
|
251
|
+
),
|
|
252
|
+
)
|
|
253
|
+
connection.onShutdown(() => {
|
|
254
|
+
workspace.invalidate()
|
|
255
|
+
for (const job of jobs.values()) {
|
|
256
|
+
clearTimeout(job.timer)
|
|
257
|
+
job.controller.abort()
|
|
258
|
+
}
|
|
259
|
+
jobs.clear()
|
|
260
|
+
})
|
|
261
|
+
documents.listen(connection)
|
|
262
|
+
connection.listen()
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
startServer()
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import path from "node:path"
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from "node:url"
|
|
3
|
+
import { TextDocument } from "vscode-languageserver-textdocument"
|
|
4
|
+
import {
|
|
5
|
+
contains,
|
|
6
|
+
discoverConfig,
|
|
7
|
+
loadProject,
|
|
8
|
+
readText,
|
|
9
|
+
type ReadText,
|
|
10
|
+
} from "../core/config.ts"
|
|
11
|
+
import { Engine } from "../core/engine.ts"
|
|
12
|
+
import { CorrectlyError, failure, type FileResult } from "../core/types.ts"
|
|
13
|
+
import { diagnostic } from "../core/parse.ts"
|
|
14
|
+
|
|
15
|
+
export class Workspace {
|
|
16
|
+
onSchema: ((uri: string) => void) | undefined
|
|
17
|
+
roots: string[]
|
|
18
|
+
private readonly read: ReadText
|
|
19
|
+
readonly documents = new Map<string, TextDocument>()
|
|
20
|
+
private readonly engines = new Map<string, Promise<Engine>>()
|
|
21
|
+
private controller = new AbortController()
|
|
22
|
+
generation = 0
|
|
23
|
+
constructor(roots: string[], read: ReadText = readText) {
|
|
24
|
+
this.roots = roots
|
|
25
|
+
this.read = read
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
invalidate() {
|
|
29
|
+
this.generation++
|
|
30
|
+
this.controller.abort()
|
|
31
|
+
this.controller = new AbortController()
|
|
32
|
+
this.engines.clear()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
open(document: TextDocument): boolean {
|
|
36
|
+
this.documents.set(document.uri, document)
|
|
37
|
+
const resource =
|
|
38
|
+
path.basename(fileURLToPath(document.uri)) === "correctly.config.json" ||
|
|
39
|
+
this.isSchema(document.uri)
|
|
40
|
+
if (resource) this.invalidate()
|
|
41
|
+
return resource
|
|
42
|
+
}
|
|
43
|
+
close(uri: string): boolean {
|
|
44
|
+
const resource =
|
|
45
|
+
uri.endsWith("/correctly.config.json") || this.isSchema(uri)
|
|
46
|
+
this.documents.delete(uri)
|
|
47
|
+
if (resource) this.invalidate()
|
|
48
|
+
return resource
|
|
49
|
+
}
|
|
50
|
+
private readonly schemaUris = new Set<string>()
|
|
51
|
+
private isSchema(uri: string): boolean {
|
|
52
|
+
return this.schemaUris.has(uri)
|
|
53
|
+
}
|
|
54
|
+
resourceChanged(uri: string): boolean {
|
|
55
|
+
return uri.endsWith("/correctly.config.json") || this.isSchema(uri)
|
|
56
|
+
}
|
|
57
|
+
private readonly readBuffer: ReadText = (uri) => {
|
|
58
|
+
const document = this.documents.get(uri)
|
|
59
|
+
return document ? Promise.resolve(document.getText()) : this.read(uri)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async engineFor(uri: string): Promise<Engine> {
|
|
63
|
+
const file = fileURLToPath(uri)
|
|
64
|
+
const root = this.roots
|
|
65
|
+
.filter((r) => contains(r, file))
|
|
66
|
+
.sort((a, b) => b.length - a.length)[0]
|
|
67
|
+
if (this.roots.length && !root)
|
|
68
|
+
throw new CorrectlyError(
|
|
69
|
+
"config",
|
|
70
|
+
`Document is outside the editor workspace: ${file}`,
|
|
71
|
+
)
|
|
72
|
+
const virtualConfigs = new Set(
|
|
73
|
+
[...this.documents.keys()]
|
|
74
|
+
.filter((key) => key.endsWith("/correctly.config.json"))
|
|
75
|
+
.map((key) => fileURLToPath(key)),
|
|
76
|
+
)
|
|
77
|
+
const configPath = await discoverConfig(
|
|
78
|
+
path.dirname(file),
|
|
79
|
+
root,
|
|
80
|
+
virtualConfigs,
|
|
81
|
+
)
|
|
82
|
+
let promise = this.engines.get(configPath)
|
|
83
|
+
if (!promise) {
|
|
84
|
+
const signal = this.controller.signal
|
|
85
|
+
promise = (async () => {
|
|
86
|
+
const project = await loadProject(configPath, this.readBuffer)
|
|
87
|
+
const readSchema: ReadText = async (schema) => {
|
|
88
|
+
this.schemaUris.add(schema)
|
|
89
|
+
this.onSchema?.(schema)
|
|
90
|
+
return this.readBuffer(schema)
|
|
91
|
+
}
|
|
92
|
+
const engine = new Engine(project, { read: readSchema, signal })
|
|
93
|
+
await engine.prepare()
|
|
94
|
+
for (const resource of engine.store.resources.values())
|
|
95
|
+
this.schemaUris.add(resource.uri)
|
|
96
|
+
signal.throwIfAborted()
|
|
97
|
+
return engine
|
|
98
|
+
})()
|
|
99
|
+
this.engines.set(configPath, promise)
|
|
100
|
+
}
|
|
101
|
+
return promise
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async validate(document: TextDocument): Promise<FileResult> {
|
|
105
|
+
try {
|
|
106
|
+
const engine = await this.engineFor(document.uri)
|
|
107
|
+
return await engine.validate(
|
|
108
|
+
fileURLToPath(document.uri),
|
|
109
|
+
document.getText(),
|
|
110
|
+
)
|
|
111
|
+
} catch (error) {
|
|
112
|
+
const problem = failure(error, fileURLToPath(document.uri))
|
|
113
|
+
return {
|
|
114
|
+
file: fileURLToPath(document.uri),
|
|
115
|
+
mode: document.languageId === "jsonc" ? "jsonc" : "json",
|
|
116
|
+
association: null,
|
|
117
|
+
coverage: "syntax-only",
|
|
118
|
+
diagnostics: [
|
|
119
|
+
diagnostic(document.getText(), problem.code, problem.message),
|
|
120
|
+
],
|
|
121
|
+
failures: [problem],
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
documentPath(uri: string): string {
|
|
127
|
+
return fileURLToPath(uri)
|
|
128
|
+
}
|
|
129
|
+
rootUris(): string[] {
|
|
130
|
+
return this.roots.map((root) => pathToFileURL(root).href)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Correctly for VS Code
|
|
2
|
+
|
|
3
|
+
Install the VSIX, add `correctly.config.json` to each workspace root, and open JSON or JSONC files. Correctly uses that same external configuration for validation, completion, and hover. It reads unsaved buffers and refreshes when configurations and schemas change.
|
|
4
|
+
|
|
5
|
+
The client and server are bundled; no npm install or separate schema map is needed. Run **Correctly: Restart Server** to restart the language server. JSON formatting remains the responsibility of your formatter.
|
|
6
|
+
|
|
7
|
+
VS Code's built-in JSON features can also provide their own diagnostics or suggestions. Correctly diagnostics have source `correctly`. For an editor experience using only Correctly validation, set `json.validate.enable` to `false`; this does not affect Correctly or require another schema association map.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as vscode from "vscode"
|
|
2
|
+
import { LanguageClient, TransportKind } from "vscode-languageclient/node"
|
|
3
|
+
|
|
4
|
+
let client: LanguageClient | undefined
|
|
5
|
+
export async function activate(context: vscode.ExtensionContext) {
|
|
6
|
+
const watcher = vscode.workspace.createFileSystemWatcher("**/*")
|
|
7
|
+
context.subscriptions.push(watcher)
|
|
8
|
+
client = new LanguageClient(
|
|
9
|
+
"correctly",
|
|
10
|
+
"Correctly",
|
|
11
|
+
{
|
|
12
|
+
module: context.asAbsolutePath("dist/server.mjs"),
|
|
13
|
+
transport: TransportKind.stdio,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
documentSelector: [
|
|
17
|
+
{ scheme: "file", language: "json" },
|
|
18
|
+
{ scheme: "file", language: "jsonc" },
|
|
19
|
+
],
|
|
20
|
+
synchronize: { fileEvents: watcher },
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
context.subscriptions.push(
|
|
24
|
+
vscode.commands.registerCommand("correctly.restartServer", async () => {
|
|
25
|
+
await client?.restart()
|
|
26
|
+
}),
|
|
27
|
+
)
|
|
28
|
+
await client.start()
|
|
29
|
+
}
|
|
30
|
+
export async function deactivate() {
|
|
31
|
+
await client?.stop()
|
|
32
|
+
client = undefined
|
|
33
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "correctly-vscode",
|
|
3
|
+
"displayName": "Correctly",
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"private": true,
|
|
6
|
+
"description": "JSON Schema validation and editor hints from one external configuration",
|
|
7
|
+
"categories": [
|
|
8
|
+
"Linters",
|
|
9
|
+
"Programming Languages"
|
|
10
|
+
],
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/jeremybanka/correctly.git",
|
|
14
|
+
"directory": "packages/correctly/src/vscode"
|
|
15
|
+
},
|
|
16
|
+
"license": "MPL-2.0",
|
|
17
|
+
"publisher": "jeremybanka",
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "./dist/extension.mjs",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"contributes": {
|
|
26
|
+
"commands": [
|
|
27
|
+
{
|
|
28
|
+
"command": "correctly.restartServer",
|
|
29
|
+
"title": "Correctly: Restart Server"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"activationEvents": [
|
|
34
|
+
"onLanguage:json",
|
|
35
|
+
"onLanguage:jsonc"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"vscode": "^1.100.0"
|
|
39
|
+
},
|
|
40
|
+
"extensionKind": [
|
|
41
|
+
"workspace"
|
|
42
|
+
]
|
|
43
|
+
}
|