Skip to content

Commit 2ce9b40

Browse files
committed
feat: stricter settings.json snippets schema validation
For example now we report unknown properties in snippets configuration Also now we display a suggestion in hover when possible to move property to when clause
1 parent 0d405d4 commit 2ce9b40

File tree

7 files changed

+52
-18
lines changed

7 files changed

+52
-18
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@ experiment-*
2626

2727
out
2828
src/generated.ts
29-
# TODO have no idea why TJS generates it
3029
src/configurationType.js
30+
src/configurationTypeCache.jsonc

src/configurationType.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as vscode from 'vscode'
2+
import { Simplify } from 'type-fest'
23
import { snippetLocation } from './constants'
34
// import { SyntaxKind } from 'typescript/lib/tsserverlibrary'
45

@@ -46,6 +47,8 @@ export type GeneralSnippet = {
4647
* }]
4748
*/
4849
body: string | string[] | false
50+
/** @suggestSortText "3" */
51+
extends?: string
4952
/**
5053
* @suggestSortText "4"
5154
*/
@@ -180,11 +183,6 @@ export type TypingSnippetUnresolved = GeneralSnippet & {
180183
}
181184
}
182185

183-
type TypingSnippetResolved = (Pick<TypingSnippetUnresolved, 'body' | 'sequence'> | { /** @suggestSortText "3" */ extends: string }) &
184-
Omit<TypingSnippetUnresolved, 'body' | 'sequence'>
185-
type CustomSnippetResolved = (Pick<CustomSnippetUnresolved, 'body' | 'name'> | { /** @suggestSortText "3" */ extends: string }) &
186-
Omit<CustomSnippetUnresolved, 'body' | 'name'>
187-
188186
export type Configuration = {
189187
/**
190188
* Include builtin JS/MD snippets
@@ -231,11 +229,11 @@ export type Configuration = {
231229
/**
232230
* @suggestSortText betterSnippets.1
233231
*/
234-
customSnippets: CustomSnippetResolved[]
232+
customSnippets: Array<Simplify<CustomSnippetUnresolved>>
235233
/**
236234
* @suggestSortText betterSnippets.2
237235
*/
238-
typingSnippets: TypingSnippetResolved[]
236+
typingSnippets: Array<Simplify<TypingSnippetUnresolved>>
239237
/** @default true */
240238
typingSnippetsUndoStops: boolean
241239
/**
@@ -254,7 +252,10 @@ export type Configuration = {
254252
* @default true
255253
* */
256254
'snippetCreator.showSnippetAfterCreation': boolean
257-
/** Override default values for every snippet */
255+
/**
256+
* Override default values for every snippet
257+
* @additionalProperties false
258+
*/
258259
customSnippetDefaults: {
259260
sortText?: string
260261
iconType?: SnippetType

src/debugCommands.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import * as vscode from 'vscode'
22
import { showQuickPick } from '@zardoy/vscode-utils/build/quickPick'
3-
import { getExtensionSetting, getExtensionSettingId, registerExtensionCommand, VSCodeQuickPickItem } from 'vscode-framework'
3+
import { getExtensionSetting, getExtensionSettingId, registerExtensionCommand } from 'vscode-framework'
44
import { compact } from '@zardoy/utils'
5-
import { CustomSnippetUnresolved, TypingSnippetUnresolved } from './configurationType'
65
import { getAllExtensionSnippets, mergeSnippetWithDefaults } from './snippet'
76
import { getConfigValueFromAllScopes } from './util'
87

98
export default () => {
109
registerExtensionCommand('showAllResolvedSnippets', async () => {
1110
// todo reuse getters from views.ts
12-
const customUser = (getConfigValueFromAllScopes('customSnippets') as CustomSnippetUnresolved[]).map(snippet => mergeSnippetWithDefaults(snippet))
13-
const typingUser = (getConfigValueFromAllScopes('typingSnippets') as TypingSnippetUnresolved[]).map(snippet => mergeSnippetWithDefaults(snippet))
11+
const customUser = getConfigValueFromAllScopes('customSnippets').map(snippet => mergeSnippetWithDefaults(snippet))
12+
const typingUser = getConfigValueFromAllScopes('typingSnippets').map(snippet => mergeSnippetWithDefaults(snippet))
1413

1514
const customExt = getAllExtensionSnippets('customSnippets')
1615
const typingExt = getAllExtensionSnippets('typingSnippets')

src/extension.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import {
2929
import { getAllLoadedSnippets } from './loadedSnippets'
3030
import registerForceInsertSnippet from './forceInsertSnippet'
3131
import { ExposedExtensionApi } from './extensionApi'
32-
import { TypingSnippetUnresolved } from './configurationType'
3332
import registerDebugCommands from './debugCommands'
3433
import { isOtherLinesMatches } from './otherLines'
3534

@@ -154,7 +153,7 @@ export const activate = () => {
154153

155154
const snippetsToLoadByLang = getAllLoadedSnippets()
156155
const typingSnippets: CustomTypingSnippet[] = [
157-
...(getConfigValueFromAllScopes('typingSnippets') as TypingSnippetUnresolved[]).map(snippet => mergeSnippetWithDefaults(snippet)),
156+
...getConfigValueFromAllScopes('typingSnippets').map(snippet => mergeSnippetWithDefaults(snippet)),
158157
...getAllExtensionSnippets('typingSnippets'),
159158
]
160159
const snippetsToLoadFlattened = [...Object.values(snippetsToLoadByLang).flat(1), ...typingSnippets]

src/settingsHelper.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as vscode from 'vscode'
22
import { findNodeAtLocation, getLocation, getNodeValue, JSONPath, Node, parseTree } from 'jsonc-parser'
3-
import { getExtensionSettingId } from 'vscode-framework'
3+
import { extensionCtx, getExtensionSettingId } from 'vscode-framework'
44
import { getJsonCompletingInfo, jsonPathEquals, jsonValuesToCompletions } from '@zardoy/vscode-utils/build/jsonCompletions'
55
import { oneOf } from '@zardoy/utils'
66
import { uniqBy } from 'lodash'
@@ -198,9 +198,28 @@ export default () => {
198198

199199
vscode.languages.registerHoverProvider(selector, {
200200
provideHover(document: vscode.TextDocument, position: vscode.Position, token: any) {
201+
const location = getLocation(document.getText(), document.offsetAt(position))
202+
if (location.isAtPropertyKey && location.path[0]?.toString().startsWith('betterSnippets')) {
203+
const diagnosticUnderCursor = vscode.languages.getDiagnostics(document.uri).find(({ range }) => range.contains(position))
204+
const unknownSnippetProp = diagnosticUnderCursor && /Property (\w+) is not allowed./.exec(diagnosticUnderCursor.message)?.[1]
205+
if (unknownSnippetProp) {
206+
const allPossibleWhenProps: string[] = []
207+
for (const { properties } of extensionCtx.extension.packageJSON.contributes.configuration.properties['betterSnippets.customSnippets'].items
208+
.properties.when.allOf) {
209+
allPossibleWhenProps.push(...Object.keys(properties))
210+
}
211+
212+
if (allPossibleWhenProps.includes(unknownSnippetProp)) {
213+
return {
214+
contents: [new vscode.MarkdownString(`Did you mean to move \`${unknownSnippetProp}\` to \`when\` clause?`)],
215+
}
216+
}
217+
}
218+
}
219+
201220
const sharedParsingInfo = getSharedParsingInfo(document, position)
202221
if (!sharedParsingInfo) return
203-
const { isAnySnippet, localPath, insideStringRange, nodeValue } = sharedParsingInfo
222+
const { nodeValue, insideStringRange, isAnySnippet, localPath } = sharedParsingInfo
204223
if (!insideStringRange) return
205224
let providerResult: vscode.Hover | undefined
206225
try {

src/views.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { CommandHandler, getExtensionCommandId, getExtensionContributionsPrefix,
55
import { groupBy, sort } from 'rambda'
66
import { watchExtensionSettings } from '@zardoy/vscode-utils/build/settings'
77
import { CommonSnippet, getSnippetsSettingValue, RevealSnippetOptions } from './settingsJsonSnippetCommands'
8-
import { GeneralSnippet } from './configurationType'
98
import { getSnippetsDefaults, mergeSnippetWithDefaults, normalizeWhenLangs } from './snippet'
109

1110
const SCHEME = `${getExtensionContributionsPrefix()}virtualSnippets`

vscode-framework.config.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,23 @@ const config = defineConfig({
4040
},
4141
}
4242
},
43+
({ generatedManifest }) => {
44+
// @ts-ignore
45+
const { properties } = generatedManifest.contributes.configuration
46+
for (const [i, key] of ['betterSnippets.customSnippets', 'betterSnippets.typingSnippets'].entries()) {
47+
const snippetsProp = properties[key].items
48+
const requiredProps = [[i ? 'sequence' : 'name', 'body'], ['extends']]
49+
snippetsProp.anyOf = requiredProps.map(props => ({ required: props }))
50+
snippetsProp.required = undefined
51+
snippetsProp.additionalProperties = false
52+
snippetsProp.patternProperties = {
53+
'^\\$': {},
54+
}
55+
}
56+
57+
properties['betterSnippets.extendsGroups'].additionalProperties.additionalProperties = false
58+
return {}
59+
},
4360
],
4461
})
4562

0 commit comments

Comments
 (0)