@spyglassmc/core 0.4.43 → 0.4.45

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.
@@ -1,4 +1,4 @@
1
- import type { ExternalEventEmitter } from '../common/index.js';
1
+ import type { DeepPartial, ExternalEventEmitter } from '../common/index.js';
2
2
  import { Arrayable } from '../common/index.js';
3
3
  import { ErrorSeverity } from '../source/index.js';
4
4
  import type { Project } from './Project.js';
@@ -44,15 +44,6 @@ export interface CustomResourceConfig {
44
44
  until?: `${bigint}.${bigint}.${bigint}` | `${bigint}.${bigint}`;
45
45
  }
46
46
  export interface EnvConfig {
47
- /**
48
- * Where to download data like `mcmeta` or `vanilla-mcdoc` from (case-insensitive).
49
- *
50
- * * `GitHub`: Recommended, unless you have trouble connecting to `raw.githubusercontent.com`.
51
- * * `fastly`
52
- * * `jsDelivr`
53
- * * A custom URL, with placeholder variables: `${user}`, `${repo}`, `${tag}`, and `${path}`.
54
- */
55
- dataSource: string;
56
47
  /**
57
48
  * A list of data packs the current project depends on. Each value in this array can be either an absolute file path
58
49
  * to a data pack folder or data pack archive (e.g. `.zip` or `.tar.gz`), or a special string like `@vanilla-mcdoc`.
@@ -84,7 +75,9 @@ export interface EnvConfig {
84
75
  inlayHint: boolean | {
85
76
  enabledNodes: string[];
86
77
  };
87
- semanticColoring: boolean;
78
+ semanticColoring: boolean | {
79
+ disabledLanguages?: string[];
80
+ };
88
81
  selectionRanges: boolean;
89
82
  signatures: boolean;
90
83
  };
@@ -97,10 +90,6 @@ export interface EnvConfig {
97
90
  * - A version identifier or name found in [mcmeta's version data](https://github.com/misode/mcmeta/blob/summary/versions/data.json) that is older than `1.15` (inclusive).
98
91
  */
99
92
  gameVersion: string;
100
- /**
101
- * Locale language for error messages and other texts provided by Spyglass.
102
- */
103
- language: string;
104
93
  /**
105
94
  * Use custom files as mcmeta summaries.
106
95
  *
@@ -118,16 +107,6 @@ export interface EnvConfig {
118
107
  * May become corrupt after changing game versions, so this is currently disabled by default.
119
108
  */
120
109
  enableMcdocCaching: boolean;
121
- /**
122
- * Makes the file-watcher use polling to watch for file changes.
123
- * Comes at a performance cost for very large datapacks.
124
- *
125
- * On Windows, enabling this can fix file-lock issues when Spyglass is running.
126
- * See: https://github.com/SpyglassMC/Spyglass/issues/1414
127
- *
128
- * **You should only consider enabling this for Windows machines.**
129
- */
130
- useFilePolling: boolean;
131
110
  }
132
111
  export type LinterSeverity = 'hint' | 'information' | 'warning' | 'error';
133
112
  export declare namespace LinterSeverity {
@@ -244,6 +223,15 @@ export declare namespace SymbolLinterConfig {
244
223
  * Config which simulates the default vanilla command system.
245
224
  */
246
225
  export declare const VanillaConfig: Config;
226
+ export type PartialConfig = DeepPartial<Config>;
227
+ export declare namespace PartialConfig {
228
+ /**
229
+ * Filters a config for valid editor settings
230
+ * @param spyglassmcConfiguration An unchecked config, should have the same layout as {@link PartialConfig}
231
+ * @returns The config, only keeping editor settings that have valid values
232
+ */
233
+ function buildConfigFromEditorSettingsSafe(spyglassmcConfiguration: any): PartialConfig;
234
+ }
247
235
  type ConfigEvent = {
248
236
  config: Config;
249
237
  };
@@ -256,6 +244,7 @@ export declare class ConfigService implements ExternalEventEmitter {
256
244
  private readonly project;
257
245
  private readonly defaultConfig;
258
246
  static readonly ConfigFileNames: readonly ["spyglass.json", ".spyglassrc", ".spyglassrc.json"];
247
+ private currentEditorConfiguration;
259
248
  constructor(project: Project, defaultConfig?: Config);
260
249
  on(event: 'changed', callbackFn: (data: ConfigEvent) => void): this;
261
250
  on(event: 'error', callbackFn: (data: ErrorEvent) => void): this;
@@ -263,6 +252,7 @@ export declare class ConfigService implements ExternalEventEmitter {
263
252
  once(event: 'error', callbackFn: (data: ErrorEvent) => void): this;
264
253
  emit(event: 'changed', data: ConfigEvent): boolean;
265
254
  emit(event: 'error', data: ErrorEvent): boolean;
255
+ onEditorConfigurationUpdate(editorConfiguration: PartialConfig): Promise<void>;
266
256
  load(): Promise<Config>;
267
257
  static isConfigFile(this: void, uri: string): boolean;
268
258
  static merge(base: Config, ...overrides: any[]): Config;
@@ -106,7 +106,6 @@ export var SymbolLinterConfig;
106
106
  */
107
107
  export const VanillaConfig = {
108
108
  env: {
109
- dataSource: 'GitHub',
110
109
  dependencies: ['@vanilla-datapack', '@vanilla-resourcepack', '@vanilla-mcdoc'],
111
110
  exclude: [
112
111
  '.*/**',
@@ -140,12 +139,10 @@ export const VanillaConfig = {
140
139
  signatures: true,
141
140
  },
142
141
  gameVersion: 'Auto',
143
- language: 'Default',
144
142
  permissionLevel: 2,
145
143
  plugins: [],
146
144
  mcmetaSummaryOverrides: {},
147
145
  enableMcdocCaching: false,
148
- useFilePolling: false,
149
146
  },
150
147
  format: {
151
148
  blockStateBracketSpacing: { inside: 0 },
@@ -217,11 +214,94 @@ export const VanillaConfig = {
217
214
  summonAec: 'summon minecraft:area_effect_cloud ~ ~ ~ {Age: -2147483648, Duration: -1, WaitTime: -2147483648, Tags: ["${1:tag}"]}',
218
215
  },
219
216
  };
217
+ export var PartialConfig;
218
+ (function (PartialConfig) {
219
+ /**
220
+ * Filters a config for valid editor settings
221
+ * @param spyglassmcConfiguration An unchecked config, should have the same layout as {@link PartialConfig}
222
+ * @returns The config, only keeping editor settings that have valid values
223
+ */
224
+ function buildConfigFromEditorSettingsSafe(spyglassmcConfiguration) {
225
+ const result = {};
226
+ if (!spyglassmcConfiguration || typeof spyglassmcConfiguration !== 'object') {
227
+ return result;
228
+ }
229
+ if (spyglassmcConfiguration.env && typeof spyglassmcConfiguration.env === 'object') {
230
+ result.env = {};
231
+ if (typeof spyglassmcConfiguration.env.gameVersion === 'string') {
232
+ result.env.gameVersion = spyglassmcConfiguration.env.gameVersion;
233
+ }
234
+ if (typeof spyglassmcConfiguration.env.enableMcdocCaching === 'boolean') {
235
+ result.env.enableMcdocCaching = spyglassmcConfiguration.env.enableMcdocCaching;
236
+ }
237
+ if (spyglassmcConfiguration.env.feature
238
+ && typeof spyglassmcConfiguration.env.feature === 'object') {
239
+ result.env.feature = {};
240
+ if (typeof spyglassmcConfiguration.env.feature.codeActions === 'boolean') {
241
+ result.env.feature.codeActions = spyglassmcConfiguration.env.feature.codeActions;
242
+ }
243
+ if (typeof spyglassmcConfiguration.env.feature.colors === 'boolean') {
244
+ result.env.feature.colors = spyglassmcConfiguration.env.feature.colors;
245
+ }
246
+ if (typeof spyglassmcConfiguration.env.feature.completions === 'boolean') {
247
+ result.env.feature.completions = spyglassmcConfiguration.env.feature.completions;
248
+ }
249
+ if (typeof spyglassmcConfiguration.env.feature.documentHighlighting === 'boolean') {
250
+ result.env.feature.documentHighlighting =
251
+ spyglassmcConfiguration.env.feature.documentHighlighting;
252
+ }
253
+ if (typeof spyglassmcConfiguration.env.feature.documentLinks === 'boolean') {
254
+ result.env.feature.documentLinks = spyglassmcConfiguration.env.feature.documentLinks;
255
+ }
256
+ if (typeof spyglassmcConfiguration.env.feature.foldingRanges === 'boolean') {
257
+ result.env.feature.foldingRanges = spyglassmcConfiguration.env.feature.foldingRanges;
258
+ }
259
+ if (typeof spyglassmcConfiguration.env.feature.formatting === 'boolean') {
260
+ result.env.feature.formatting = spyglassmcConfiguration.env.feature.formatting;
261
+ }
262
+ if (typeof spyglassmcConfiguration.env.feature.hover === 'boolean') {
263
+ result.env.feature.hover = spyglassmcConfiguration.env.feature.hover;
264
+ }
265
+ if (spyglassmcConfiguration.env.feature.inlayHint
266
+ && typeof spyglassmcConfiguration.env.feature.inlayHint === 'object'
267
+ && Array.isArray(spyglassmcConfiguration.env.feature.inlayHint.enabledNodes)) {
268
+ result.env.feature.inlayHint = {
269
+ enabledNodes: spyglassmcConfiguration.env.feature.inlayHint.enabledNodes
270
+ .filter((element) => typeof element === 'string'),
271
+ };
272
+ }
273
+ if (typeof spyglassmcConfiguration.env.feature.semanticColoring === 'boolean') {
274
+ result.env.feature.semanticColoring =
275
+ spyglassmcConfiguration.env.feature.semanticColoring;
276
+ }
277
+ if (spyglassmcConfiguration.env.feature.semanticColoring
278
+ && typeof spyglassmcConfiguration.env.feature.semanticColoring === 'object'
279
+ && Array.isArray(spyglassmcConfiguration.env.feature.semanticColoring.disabledLanguages)) {
280
+ result.env.feature.semanticColoring = {
281
+ disabledLanguages: spyglassmcConfiguration.env.feature.semanticColoring
282
+ .disabledLanguages
283
+ .filter((element) => typeof element === 'string'),
284
+ };
285
+ }
286
+ if (typeof spyglassmcConfiguration.env.feature.selectionRanges === 'boolean') {
287
+ result.env.feature.selectionRanges =
288
+ spyglassmcConfiguration.env.feature.selectionRanges;
289
+ }
290
+ if (typeof spyglassmcConfiguration.env.feature.signatures === 'boolean') {
291
+ result.env.feature.signatures = spyglassmcConfiguration.env.feature.signatures;
292
+ }
293
+ }
294
+ }
295
+ return result;
296
+ }
297
+ PartialConfig.buildConfigFromEditorSettingsSafe = buildConfigFromEditorSettingsSafe;
298
+ })(PartialConfig || (PartialConfig = {}));
220
299
  export class ConfigService {
221
300
  project;
222
301
  defaultConfig;
223
302
  static ConfigFileNames = Object.freeze(['spyglass.json', '.spyglassrc', '.spyglassrc.json']);
224
303
  #eventEmitter;
304
+ currentEditorConfiguration = {};
225
305
  constructor(project, defaultConfig = VanillaConfig) {
226
306
  this.project = project;
227
307
  this.defaultConfig = defaultConfig;
@@ -246,6 +326,10 @@ export class ConfigService {
246
326
  emit(event, ...args) {
247
327
  return this.#eventEmitter.emit(event, ...args);
248
328
  }
329
+ async onEditorConfigurationUpdate(editorConfiguration) {
330
+ this.currentEditorConfiguration = editorConfiguration;
331
+ this.emit('changed', { config: await this.load() });
332
+ }
249
333
  async load() {
250
334
  const overrides = [];
251
335
  for (const projectRoot of this.project.projectRoots) {
@@ -265,7 +349,7 @@ export class ConfigService {
265
349
  break;
266
350
  }
267
351
  }
268
- return ConfigService.merge(this.defaultConfig, ...overrides);
352
+ return ConfigService.merge(this.defaultConfig, this.currentEditorConfiguration, ...overrides);
269
353
  }
270
354
  static isConfigFile(uri) {
271
355
  return ConfigService.ConfigFileNames.some((n) => uri.endsWith(`/${n}`));
@@ -0,0 +1,21 @@
1
+ import type { UriStore } from '../common/index.js';
2
+ /**
3
+ * A file watcher that reports additions, changes, and deletions of files.
4
+ * Changes to directories should not be reported.
5
+ */
6
+ export interface FileWatcher {
7
+ get watchedFiles(): UriStore;
8
+ ready(): Promise<void>;
9
+ on(eventName: 'ready', listener: () => unknown): this;
10
+ once(eventName: 'ready', listener: () => unknown): this;
11
+ on(eventName: 'add', listener: (uri: string) => unknown): this;
12
+ once(eventName: 'add', listener: (uri: string) => unknown): this;
13
+ on(eventName: 'change', listener: (uri: string) => unknown): this;
14
+ once(eventName: 'change', listener: (uri: string) => unknown): this;
15
+ on(eventName: 'unlink', listener: (uri: string) => unknown): this;
16
+ once(eventName: 'unlink', listener: (uri: string) => unknown): this;
17
+ on(eventName: 'error', listener: (error: Error) => unknown): this;
18
+ once(eventName: 'error', listener: (error: Error) => unknown): this;
19
+ close(): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=FileWatcher.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=FileWatcher.js.map
@@ -1,15 +1,16 @@
1
1
  import type { TextDocumentContentChangeEvent } from 'vscode-languageserver-textdocument';
2
2
  import { TextDocument } from 'vscode-languageserver-textdocument';
3
3
  import type { ExternalEventEmitter, Externals } from '../common/index.js';
4
- import { Logger } from '../common/index.js';
4
+ import { Logger, UriStore } from '../common/index.js';
5
5
  import type { AstNode } from '../node/index.js';
6
6
  import { FileNode } from '../node/index.js';
7
7
  import type { PosRangeLanguageError } from '../source/index.js';
8
8
  import { SymbolUtil } from '../symbol/index.js';
9
9
  import { CacheService } from './CacheService.js';
10
- import type { Config } from './Config.js';
10
+ import type { Config, PartialConfig } from './Config.js';
11
11
  import { FileService } from './FileService.js';
12
12
  import type { RootUriString } from './fileUtil.js';
13
+ import type { FileWatcher } from './FileWatcher.js';
13
14
  import { MetaRegistry } from './MetaRegistry.js';
14
15
  import { ProfilerFactory } from './Profiler.js';
15
16
  export type ProjectInitializerContext = Pick<Project, 'cacheRoot' | 'config' | 'externals' | 'isDebugging' | 'logger' | 'meta' | 'projectRoots'>;
@@ -31,6 +32,9 @@ export interface ProjectOptions {
31
32
  projectRoots: RootUriString[];
32
33
  symbols?: SymbolUtil;
33
34
  }
35
+ export interface ProjectReadyOptions {
36
+ projectRootsWatcher?: FileWatcher;
37
+ }
34
38
  export interface DocAndNode {
35
39
  doc: TextDocument;
36
40
  node: FileNode<AstNode>;
@@ -42,6 +46,10 @@ interface DocumentErrorEvent {
42
46
  uri: string;
43
47
  version?: number;
44
48
  }
49
+ export interface ConfigChangeEvent {
50
+ oldConfig: Config;
51
+ newConfig: Config;
52
+ }
45
53
  interface FileEvent {
46
54
  uri: string;
47
55
  }
@@ -96,6 +104,7 @@ export declare class Project implements ExternalEventEmitter {
96
104
  #private;
97
105
  private static readonly RootSuffix;
98
106
  readonly cacheService: CacheService;
107
+ get watchedFiles(): UriStore;
99
108
  get isReady(): boolean;
100
109
  config: Config;
101
110
  readonly externals: Externals;
@@ -132,7 +141,7 @@ export declare class Project implements ExternalEventEmitter {
132
141
  on(event: 'ready', callbackFn: (data: EmptyEvent) => void): this;
133
142
  on(event: 'rootsUpdated', callbackFn: (data: RootsEvent) => void): this;
134
143
  on(event: 'symbolRegistrarExecuted', callbackFn: (data: SymbolRegistrarEvent) => void): this;
135
- on(event: 'configChanged', callbackFn: (data: Config) => void): this;
144
+ on(event: 'configChanged', callbackFn: (data: ConfigChangeEvent) => void): this;
136
145
  once(event: 'documentErrored', callbackFn: (data: DocumentErrorEvent) => void): this;
137
146
  once(event: 'documentUpdated', callbackFn: (data: DocumentEvent) => void): this;
138
147
  once(event: 'documentRemoved', callbackFn: (data: FileEvent) => void): this;
@@ -140,7 +149,7 @@ export declare class Project implements ExternalEventEmitter {
140
149
  once(event: 'ready', callbackFn: (data: EmptyEvent) => void): this;
141
150
  once(event: 'rootsUpdated', callbackFn: (data: RootsEvent) => void): this;
142
151
  once(event: 'symbolRegistrarExecuted', callbackFn: (data: SymbolRegistrarEvent) => void): this;
143
- once(event: 'configChanged', callbackFn: (data: Config) => void): this;
152
+ once(event: 'configChanged', callbackFn: (data: ConfigChangeEvent) => void): this;
144
153
  emit(event: 'documentErrored', data: DocumentErrorEvent): boolean;
145
154
  emit(event: 'documentUpdated', data: DocumentEvent): boolean;
146
155
  emit(event: 'documentRemoved', data: FileEvent): boolean;
@@ -148,7 +157,7 @@ export declare class Project implements ExternalEventEmitter {
148
157
  emit(event: 'ready', data: EmptyEvent): boolean;
149
158
  emit(event: 'rootsUpdated', data: RootsEvent): boolean;
150
159
  emit(event: 'symbolRegistrarExecuted', data: SymbolRegistrarEvent): boolean;
151
- emit(event: 'configChanged', data: Config): boolean;
160
+ emit(event: 'configChanged', data: ConfigChangeEvent): boolean;
152
161
  /**
153
162
  * Get all files that are tracked and supported.
154
163
  *
@@ -157,8 +166,6 @@ export declare class Project implements ExternalEventEmitter {
157
166
  */
158
167
  getTrackedFiles(): string[];
159
168
  constructor({ cacheRoot, defaultConfig, externals, fs, initializers, isDebugging, logger, profilers, projectRoots, }: ProjectOptions);
160
- private setInitPromise;
161
- private setReadyPromise;
162
169
  /**
163
170
  * Load the config file and initialize parsers and processors.
164
171
  */
@@ -166,7 +173,7 @@ export declare class Project implements ExternalEventEmitter {
166
173
  /**
167
174
  * Finish the initial run of parsing, binding, and checking the entire project.
168
175
  */
169
- ready(): Promise<this>;
176
+ ready(options?: ProjectReadyOptions): Promise<this>;
170
177
  /**
171
178
  * Behavior of the `Project` instance is undefined after this function has settled.
172
179
  */
@@ -215,6 +222,7 @@ export declare class Project implements ExternalEventEmitter {
215
222
  private tryClearingCache;
216
223
  private shouldRemove;
217
224
  private isOnlyWatched;
225
+ onEditorConfigurationUpdate(editorConfiguration: PartialConfig): Promise<void>;
218
226
  }
219
227
  export {};
220
228
  //# sourceMappingURL=Project.d.ts.map