Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | 14x 14x 773x 34x 34x 1x 1x 34x 89x 89x 89x 2x 87x 38x 19x 19x 19x 19x 19x 19x 4x 4x 4x 15x | import * as vscode from 'vscode';
import { ConfigurationScope, ScopeKey, keyToScope } from '../config-scope';
/**
* Converts a Mentor {@link ConfigurationScope} or {@link ScopeKey} into the VS Code
* configuration target to write to. The numeric {@link ConfigurationScope} values
* deliberately equal their `vscode.ConfigurationTarget` counterparts; this helper
* keeps that invariant in one place.
* @param scope A configuration scope or its serializable key.
* @returns The corresponding VS Code configuration target.
*/
export function toConfigurationTarget(scope: ConfigurationScope | ScopeKey): vscode.ConfigurationTarget {
const value = typeof scope === 'string' ? keyToScope(scope) : scope;
return value === ConfigurationScope.User
? vscode.ConfigurationTarget.Global
: vscode.ConfigurationTarget.Workspace;
}
/**
* Retrieves the VS Code configuration section for the Mentor extension.
* @returns The Mentor extension configuration.
*/
export function getConfig(section: string = ''): vscode.WorkspaceConfiguration {
return vscode.workspace.getConfiguration(`mentor${section ? `.${section}` : ''}`);
}
let predicatesCache: { label: string[]; description: string[] } | undefined;
let predicatesCacheWatcher: vscode.Disposable | undefined;
/**
* The annotation predicates from the `mentor.predicates.*` settings, cached and
* invalidated on configuration changes. Resolving resource labels reads these
* per tree node / hover, and each raw configuration read deep-clones both
* arrays — the cache keeps that off the hot path while setting changes still
* apply immediately.
*/
export function getPredicatesConfig(): { label: string[]; description: string[] } {
// One listener for the extension lifetime; disposed with the extension host.
predicatesCacheWatcher ??= vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('mentor.predicates')) {
predicatesCache = undefined;
}
});
if (!predicatesCache) {
const config = getConfig();
predicatesCache = {
label: config.get<string[]>('predicates.label') ?? [],
description: config.get<string[]>('predicates.description') ?? [],
};
}
return predicatesCache;
}
/**
* The workspace-relative folder a preset's shapes are copied into when a
* validation profile is created from it. Reads `mentor.shacl.shapesFolder`,
* falling back to `.mentor/shapes`.
* @returns The configured shapes folder as a workspace-relative POSIX path.
*/
export function getShapesFolder(): string {
const folder = getConfig('shacl').get<string>('shapesFolder', '.mentor/shapes').trim();
// Normalize away leading/trailing slashes so callers can join freely.
return folder.replace(/^\/+|\/+$/g, '') || '.mentor/shapes';
}
/**
* Resolves a formatting option using the cascading model: a language-specific
* override (`mentor.formatting.<language>.<key>`) wins when explicitly set,
* otherwise the shared common value (`mentor.formatting.common.<key>`) is used,
* falling back to {@link fallback} when neither is configured.
*
* The language override keys are declared without an explicit `default` in
* `package.json`, but VS Code still synthesizes a type-based default
* (`false`/`0`/`''`) for any registered key. `get()` therefore never returns
* `undefined` for them, which would make the override always win and hide the
* common value. We instead read the override via `inspect()` and honor it only
* when the user actually set it at the global, workspace or folder scope.
* @param language The formatting language id (e.g. `'turtle'`, `'sparql'`).
* @param key The option key (e.g. `'maxLineWidth'`).
* @param fallback The value to use when neither override nor common is set.
* @returns The effective option value.
*/
export function resolveFormattingConfig<T>(language: string, key: string, fallback: T): T {
const inspected = getConfig(`formatting.${language}`).inspect<T>(key);
const override = inspected?.workspaceFolderValue
?? inspected?.workspaceValue
?? inspected?.globalValue;
if (override !== undefined) {
return override;
}
return getConfig('formatting.common').get<T>(key, fallback);
}
/**
* Returns the first explicitly-configured value of an inspected `editor.*` key
* across the user/workspace/folder scopes and their per-language `[languageId]`
* override variants, or `undefined` when the key is only at its default. Unlike
* `get()`, this ignores the value VS Code derives from `editor.detectIndentation`,
* which lives on the editor model rather than the configuration.
*/
function explicitEditorValue<T>(inspected: ReturnType<vscode.WorkspaceConfiguration['inspect']>): T | undefined {
return (inspected?.workspaceFolderLanguageValue
?? inspected?.workspaceLanguageValue
?? inspected?.globalLanguageValue
?? inspected?.workspaceFolderValue
?? inspected?.workspaceValue
?? inspected?.globalValue) as T | undefined;
}
/**
* Resolves the indentation string a document formatter should use.
*
* When the user has explicitly configured `editor.tabSize` or `editor.insertSpaces`
* (including a per-language `[languageId]` override, which the Mentor settings UI
* writes), the two are honored as a unit: the pair takes precedence over the
* {@link vscode.FormattingOptions} VS Code passes to the formatter, and any half the
* user did not set is filled from VS Code's own default rather than from those
* options. This matters because the FormattingOptions are derived from the editor
* model and therefore reflect `editor.detectIndentation` — so a file whose body
* happens to be indented with tabs would otherwise force tab output even though the
* user configured a spaces-based tab size (and vice versa). Falls back to the
* FormattingOptions (respecting detection) only when neither setting is configured.
* @param document The document being formatted.
* @param options The FormattingOptions supplied by VS Code.
* @returns The indentation string for one indent level.
*/
export function resolveFormattingIndent(document: vscode.TextDocument, options: vscode.FormattingOptions): string {
const config = vscode.workspace.getConfiguration('editor', { languageId: document.languageId, uri: document.uri });
const tabSizeInspect = config.inspect<number>('tabSize');
const insertSpacesInspect = config.inspect<boolean>('insertSpaces');
const explicitTabSize = explicitEditorValue<number>(tabSizeInspect);
const explicitInsertSpaces = explicitEditorValue<boolean>(insertSpacesInspect);
if (explicitTabSize !== undefined || explicitInsertSpaces !== undefined) {
const tabSize = explicitTabSize ?? tabSizeInspect?.defaultValue as number ?? options.tabSize;
const insertSpaces = explicitInsertSpaces ?? insertSpacesInspect?.defaultValue as boolean ?? options.insertSpaces;
return insertSpaces ? ' '.repeat(tabSize) : '\t';
}
return options.insertSpaces ? ' '.repeat(options.tabSize) : '\t';
} |