@spyglassmc/language-server 0.4.54 → 0.4.56

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/lib/server.js CHANGED
@@ -9,6 +9,7 @@ import url from 'url';
9
9
  import * as util from 'util';
10
10
  import * as ls from 'vscode-languageserver/node.js';
11
11
  import { toCore, toLS } from './util/index.js';
12
+ import { LspFileWatcher } from './util/LspFileWatcher.js';
12
13
  export * from './util/types.js';
13
14
  if (process.argv.length === 2) {
14
15
  // When the server is launched from the cmd script, the process arguments
@@ -21,6 +22,7 @@ const cacheRoot = fileUtil.ensureEndingSlash(url.pathToFileURL(cacheRootPath).to
21
22
  const connection = ls.createConnection();
22
23
  let capabilities;
23
24
  let workspaceFolders;
25
+ let projectRoots;
24
26
  let hasShutdown = false;
25
27
  const externals = getNodeJsExternals({ cacheRoot });
26
28
  const logger = {
@@ -30,9 +32,17 @@ const logger = {
30
32
  warn: (msg, ...args) => connection.console.warn(util.format(msg, ...args)),
31
33
  };
32
34
  let service;
33
- function buildSemanticTokensCapability() {
35
+ function buildSemanticTokensCapability(isDynamic) {
36
+ // Always register everything for static registration, so all changes to the config can be
37
+ // processed by the request handlers instead
38
+ const semanticTokensConfig = service.project.config.env.feature.semanticColoring;
39
+ let disabledLanguages = [];
40
+ if (isDynamic && typeof semanticTokensConfig === 'object'
41
+ && Array.isArray(semanticTokensConfig.disabledLanguages)) {
42
+ disabledLanguages = semanticTokensConfig.disabledLanguages;
43
+ }
34
44
  return {
35
- documentSelector: toLS.documentSelector(service.project.meta),
45
+ documentSelector: toLS.documentSelector(service.project.meta, { disabledLanguages }),
36
46
  legend: toLS.semanticTokensLegend(),
37
47
  full: { delta: false },
38
48
  range: true,
@@ -45,6 +55,7 @@ connection.onInitialize(async (params) => {
45
55
  logger.info(`[onInitialize] initializationOptions = ${JSON.stringify(initializationOptions)}`);
46
56
  capabilities = params.capabilities;
47
57
  workspaceFolders = params.workspaceFolders ?? [];
58
+ projectRoots = workspaceFolders.map(f => core.fileUtil.ensureEndingSlash(f.uri));
48
59
  if (initializationOptions?.inDevelopmentMode) {
49
60
  await new Promise((resolve) => setTimeout(resolve, 3000));
50
61
  logger.warn('Delayed 3 seconds manually. If you see this in production, it means SPGoding messed up.');
@@ -67,13 +78,11 @@ connection.onInitialize(async (params) => {
67
78
  'project#ready#bind',
68
79
  ]),
69
80
  project: {
70
- defaultConfig: core.ConfigService.merge(core.VanillaConfig, {
71
- env: { gameVersion: initializationOptions?.gameVersion },
72
- }),
81
+ defaultConfig: core.ConfigService.merge(core.VanillaConfig, initializationOptions?.defaultConfig ?? {}),
73
82
  cacheRoot,
74
83
  externals,
75
84
  initializers: [mcdoc.initialize, je.initialize],
76
- projectRoots: workspaceFolders.map(f => core.fileUtil.ensureEndingSlash(f.uri)),
85
+ projectRoots,
77
86
  },
78
87
  });
79
88
  service.project.on('documentErrored', async ({ errors, uri, version }) => {
@@ -101,7 +110,7 @@ connection.onInitialize(async (params) => {
101
110
  let semanticTokensProvider = undefined;
102
111
  if (!capabilities.textDocument?.semanticTokens?.dynamicRegistration) {
103
112
  logger.info("[startDynamicSemanticTokensRegistration] LanguageClient didn't permit dynamic registration for semantic tokens. Registering semantic tokens statically instead...");
104
- semanticTokensProvider = buildSemanticTokensCapability();
113
+ semanticTokensProvider = buildSemanticTokensCapability(false);
105
114
  }
106
115
  const customCapabilities = {
107
116
  dataHackPubify: true,
@@ -141,8 +150,47 @@ connection.onInitialized(async () => {
141
150
  if (capabilities.textDocument?.formatting?.dynamicRegistration) {
142
151
  void connection.client.register(ls.DocumentFormattingRequest.type, { documentSelector: [{ language: 'mcdoc' }] });
143
152
  }
153
+ if (capabilities.workspace?.didChangeConfiguration?.dynamicRegistration) {
154
+ void connection.client.register(ls.DidChangeConfigurationNotification.type, { section: ['spyglassmc'] });
155
+ }
156
+ // In case the initializationOptions were incomplete (for example because the client doesn't support them)
157
+ await updateEditorConfiguration();
144
158
  startDynamicSemanticTokensRegistration();
145
- await service.project.ready();
159
+ // Initializes LspFileWatcher only when client supports didChangeWatchedFiles notifications.
160
+ const fileWatcher = capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration
161
+ ? new LspFileWatcher({
162
+ capabilities,
163
+ connection,
164
+ externals,
165
+ locations: projectRoots,
166
+ logger,
167
+ predicate: (uri) => !service.project.shouldExclude(uri),
168
+ })
169
+ .on('ready', () => logger.info('[FileWatcher] ready'))
170
+ .on('add', (uri) => logger.info('[FileWatcher] added', uri))
171
+ .on('change', (uri) => logger.info('[FileWatcher] changed', uri))
172
+ .on('unlink', (uri) => logger.info('[FileWatcher] unlinked', uri))
173
+ .on('error', (e) => logger.error('[FileWatcher]', e))
174
+ : undefined;
175
+ if (fileWatcher) {
176
+ // Listen for config changes and reconcile the internal state of the file watcher if
177
+ // `env.exclude` has changed.
178
+ service.project.on('configChanged', async ({ oldConfig, newConfig }) => {
179
+ const oldExclude = new Set(oldConfig.env.exclude);
180
+ const newExclude = new Set(newConfig.env.exclude);
181
+ if (oldExclude.size === newExclude.size && oldExclude.isSubsetOf(newExclude)) {
182
+ // `env.exclude` has not changed. Skip.
183
+ return;
184
+ }
185
+ logger.info('[FileWatcher] env.exclude config has changed. Reconciling...');
186
+ for (const root of projectRoots) {
187
+ await fileWatcher.reconcile(root);
188
+ }
189
+ });
190
+ }
191
+ await service.project.ready({
192
+ projectRootsWatcher: fileWatcher,
193
+ });
146
194
  if (capabilities.workspace?.workspaceFolders) {
147
195
  connection.workspace.onDidChangeWorkspaceFolders(async () => {
148
196
  // FIXME
@@ -164,7 +212,7 @@ function startDynamicSemanticTokensRegistration() {
164
212
  return;
165
213
  }
166
214
  logger.info('[registerDynamicSemanticTokens] Registering dynamic semantic tokens');
167
- dynamicSemanticTokensDiposable = connection.client.register(ls.SemanticTokensRegistrationType.type, buildSemanticTokensCapability());
215
+ dynamicSemanticTokensDiposable = connection.client.register(ls.SemanticTokensRegistrationType.type, buildSemanticTokensCapability(true));
168
216
  }
169
217
  function unregisterDynamicSemanticTokens() {
170
218
  logger.info('[unregisterDynamicSemanticTokens] Unregistering dynamic semantic tokens');
@@ -174,13 +222,39 @@ function startDynamicSemanticTokensRegistration() {
174
222
  if (service.project.config.env.feature.semanticColoring) {
175
223
  registerDynamicSemanticTokens();
176
224
  }
177
- service.project.on('configChanged', config => {
178
- if (config.env.feature.semanticColoring) {
179
- registerDynamicSemanticTokens();
225
+ function didConfigChange(oldSemanticTokensConfig, newSemanticTokensConfig) {
226
+ if (oldSemanticTokensConfig === newSemanticTokensConfig) {
227
+ return false;
180
228
  }
181
- else {
229
+ if (typeof oldSemanticTokensConfig !== 'object'
230
+ || typeof newSemanticTokensConfig !== 'object') {
231
+ return true;
232
+ }
233
+ if (!oldSemanticTokensConfig.disabledLanguages
234
+ && !newSemanticTokensConfig.disabledLanguages) {
235
+ return false;
236
+ }
237
+ if (Array.isArray(oldSemanticTokensConfig.disabledLanguages)
238
+ && Array.isArray(newSemanticTokensConfig.disabledLanguages)
239
+ && oldSemanticTokensConfig.disabledLanguages.length
240
+ === newSemanticTokensConfig.disabledLanguages.length
241
+ && oldSemanticTokensConfig.disabledLanguages.every((language, index) => language === newSemanticTokensConfig.disabledLanguages[index])) {
242
+ return false;
243
+ }
244
+ return true;
245
+ }
246
+ service.project.on('configChanged', ({ oldConfig, newConfig }) => {
247
+ const oldSemanticTokensConfig = oldConfig.env.feature.semanticColoring;
248
+ const newSemanticTokensConfig = newConfig.env.feature.semanticColoring;
249
+ if (!didConfigChange(oldSemanticTokensConfig, newSemanticTokensConfig)) {
250
+ return;
251
+ }
252
+ if (oldSemanticTokensConfig) {
182
253
  unregisterDynamicSemanticTokens();
183
254
  }
255
+ if (newSemanticTokensConfig) {
256
+ registerDynamicSemanticTokens();
257
+ }
184
258
  });
185
259
  }
186
260
  connection.onDidOpenTextDocument(({ textDocument: { text, uri, version, languageId: languageID } }) => {
@@ -192,7 +266,6 @@ connection.onDidChangeTextDocument(({ contentChanges, textDocument: { uri, versi
192
266
  connection.onDidCloseTextDocument(({ textDocument: { uri } }) => {
193
267
  service.project.onDidClose(uri);
194
268
  });
195
- connection.workspace.onDidRenameFiles(({}) => { });
196
269
  connection.onCodeAction(async ({ textDocument: { uri }, range }) => {
197
270
  const docAndNode = await service.project.ensureClientManagedChecked(uri);
198
271
  if (!docAndNode || !service.project.config.env.feature.codeActions) {
@@ -378,6 +451,12 @@ connection.onDocumentFormatting(async ({ textDocument: { uri }, options }) => {
378
451
  }
379
452
  return [toLS.textEdit(node.range, text, doc)];
380
453
  });
454
+ connection.onDidChangeConfiguration(updateEditorConfiguration);
455
+ async function updateEditorConfiguration() {
456
+ const settings = await connection.workspace.getConfiguration({ section: 'spyglassmc' });
457
+ const config = core.PartialConfig.buildConfigFromEditorSettingsSafe(settings);
458
+ await service.project.onEditorConfigurationUpdate(config);
459
+ }
381
460
  connection.onShutdown(async () => {
382
461
  await service.project.close();
383
462
  hasShutdown = true;
@@ -0,0 +1,33 @@
1
+ import * as core from '@spyglassmc/core';
2
+ import EventEmitter from 'events';
3
+ import * as ls from 'vscode-languageserver/node.js';
4
+ type Predicate = (uri: string) => boolean;
5
+ export interface LspFileWatcherOptions {
6
+ capabilities: ls.ClientCapabilities;
7
+ connection: ls.Connection;
8
+ externals: core.Externals;
9
+ locations: readonly core.FsLocation[];
10
+ logger: core.Logger;
11
+ predicate: Predicate;
12
+ }
13
+ /**
14
+ * A file watcher based on Language Server Protocol's `workspace/didChangeWatchedFiles`
15
+ * notification.
16
+ */
17
+ export declare class LspFileWatcher extends EventEmitter implements core.FileWatcher {
18
+ #private;
19
+ get watchedFiles(): core.UriStore;
20
+ constructor({ capabilities, connection, externals, locations, logger, predicate }: LspFileWatcherOptions);
21
+ ready(): Promise<void>;
22
+ close(): Promise<void>;
23
+ /**
24
+ * Reconcile the internal URI store with the actual directories and files on the disk.
25
+ * @param uri URI that should be reconciled. If it's a file URI, ensure the file exists in the
26
+ * internal URI store; if it's a directory URI, ensure all contents of it exists and no extra
27
+ * content is recorded; if the URI does not exist, reconcile its parent URI up until the watched
28
+ * `locations`.
29
+ */
30
+ reconcile(uri: core.FsLocation): Promise<void>;
31
+ }
32
+ export {};
33
+ //# sourceMappingURL=LspFileWatcher.d.ts.map
@@ -0,0 +1,226 @@
1
+ import * as core from '@spyglassmc/core';
2
+ import EventEmitter from 'events';
3
+ import * as ls from 'vscode-languageserver/node.js';
4
+ /**
5
+ * A file watcher based on Language Server Protocol's `workspace/didChangeWatchedFiles`
6
+ * notification.
7
+ */
8
+ export class LspFileWatcher extends EventEmitter {
9
+ #ready = false;
10
+ #connection;
11
+ #externals;
12
+ #locations;
13
+ #logger;
14
+ #predicate;
15
+ #watchedFiles = new core.UriStore();
16
+ #lspDisposables = [];
17
+ get watchedFiles() {
18
+ return this.#watchedFiles;
19
+ }
20
+ constructor({ capabilities, connection, externals, locations, logger, predicate }) {
21
+ super();
22
+ this.#connection = connection;
23
+ this.#externals = externals;
24
+ this.#locations = locations.map((uri) => core.normalizeUri(uri.toString()));
25
+ this.#logger = logger;
26
+ this.#predicate = predicate;
27
+ if (!capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration) {
28
+ throw new Error('LspFileWatcher requires dynamic registration capability for didChangeWatchedFiles notifications');
29
+ }
30
+ }
31
+ async ready() {
32
+ try {
33
+ this.#lspDisposables = [
34
+ this.#connection.onDidChangeWatchedFiles((params) => {
35
+ this.#logger.info('[LspFileWatcher] raw LSP changes', JSON.stringify(params));
36
+ return this.#onLspDidChangeWatchedFiles(params);
37
+ }),
38
+ await this.#connection.client.register(ls.DidChangeWatchedFilesNotification.type, {
39
+ // "**/*" is needed to watch changes to folders as well.
40
+ // https://github.com/microsoft/vscode/issues/60813#issuecomment-1145821690
41
+ watchers: [{ globPattern: '**/*' }],
42
+ }),
43
+ ];
44
+ for (const location of this.#locations) {
45
+ for (const uri of await core.fileUtil.getAllFiles(this.#externals, location)) {
46
+ if (this.#predicate(uri)) {
47
+ this.#watchedFiles.add(uri);
48
+ }
49
+ }
50
+ }
51
+ this.#ready = true;
52
+ this.emit('ready');
53
+ }
54
+ catch (e) {
55
+ this.emit('error', e);
56
+ }
57
+ }
58
+ async close() {
59
+ for (const disposable of this.#lspDisposables) {
60
+ disposable.dispose();
61
+ }
62
+ this.#lspDisposables = [];
63
+ }
64
+ async #onLspDidChangeWatchedFiles({ changes }) {
65
+ if (!this.#ready) {
66
+ throw new Error('Callback #onLspDidChangeWatchedFiles executed before ready');
67
+ }
68
+ for (const { type, uri } of changes) {
69
+ try {
70
+ const normalizedUri = core.normalizeUri(uri);
71
+ switch (type) {
72
+ case ls.FileChangeType.Created: {
73
+ await this.#handleAdd(core.fileUtil.trimEndingSlash(normalizedUri));
74
+ break;
75
+ }
76
+ case ls.FileChangeType.Changed:
77
+ await this.#handleChange(core.fileUtil.trimEndingSlash(normalizedUri));
78
+ break;
79
+ case ls.FileChangeType.Deleted: {
80
+ this.#handleDelete(core.fileUtil.trimEndingSlash(normalizedUri));
81
+ break;
82
+ }
83
+ }
84
+ }
85
+ catch (e) {
86
+ this.#logger.error('[LspFileWatcher] handle error', e);
87
+ }
88
+ }
89
+ }
90
+ async #handleAdd(uri) {
91
+ let stat;
92
+ try {
93
+ stat = await this.#externals.fs.stat(uri);
94
+ }
95
+ catch (e) {
96
+ if (!this.#externals.error.isKind(e, 'ENOENT')) {
97
+ throw e;
98
+ }
99
+ // LSP gave us a non-existent URI. Reconcile its parent directory just to be safe.
100
+ this.#logger.warn(`[LspFileWatcher] non-existent URI ${uri}; will reconcile parent directory`);
101
+ return this.#reconcileParentOf(uri);
102
+ }
103
+ if (stat.isDirectory()) {
104
+ // Find all files under the added URI and send 'add' events for them.
105
+ for (const fileUri of await core.fileUtil.getAllFiles(this.#externals, uri)) {
106
+ this.#addIfNeeded(fileUri);
107
+ }
108
+ }
109
+ else if (stat.isFile()) {
110
+ this.#addIfNeeded(uri);
111
+ }
112
+ }
113
+ async #handleChange(uri) {
114
+ if (!this.#predicate(uri) || this.#watchedFiles.has(core.fileUtil.ensureEndingSlash(uri))) {
115
+ // Skip non-predicate matching URIs and directory URIs.
116
+ return;
117
+ }
118
+ if (!this.#watchedFiles.has(uri)) {
119
+ this.#logger.warn(`[LspFileWatcher] unknown changed URI ${uri}; handling as add instead`);
120
+ return this.#handleAdd(uri);
121
+ }
122
+ this.emit('change', uri);
123
+ }
124
+ #handleDelete(uri) {
125
+ const dirUri = core.fileUtil.ensureEndingSlash(uri);
126
+ if (this.#watchedFiles.has(uri)) {
127
+ // Is a file URI. Delete it directly.
128
+ this.#watchedFiles.delete(uri);
129
+ this.emit('unlink', uri);
130
+ }
131
+ else if (this.#watchedFiles.has(dirUri)) {
132
+ // Is a directory URI.
133
+ // Find all files under the deleted URI and send 'unlink' events for them.
134
+ // getSubFiles() returns an iterator that would return nothing after dirUri
135
+ // is deleted from #watchedFiles, therefore we need to collect the results of
136
+ // the iterator before it is deleted.
137
+ const subFiles = [...this.#watchedFiles.getSubFiles(dirUri)];
138
+ this.#watchedFiles.delete(dirUri);
139
+ for (const watchedUri of subFiles) {
140
+ this.emit('unlink', watchedUri);
141
+ }
142
+ }
143
+ }
144
+ /**
145
+ * Add the file URI to the internal store and emit the `add` event if the file hasn't been
146
+ * excluded by predicate and doesn't already exist in the store.
147
+ */
148
+ #addIfNeeded(uri) {
149
+ if (!this.#watchedFiles.has(uri) && this.#predicate(uri)) {
150
+ this.#watchedFiles.add(uri);
151
+ this.emit('add', uri);
152
+ }
153
+ }
154
+ /**
155
+ * Delete the file URI from the internal store and emit the `unlink` event if the file has been
156
+ * excluded by predicate but exist in the store.
157
+ */
158
+ #deleteIfExcluded(uri) {
159
+ if (this.#watchedFiles.has(uri) && !this.#predicate(uri)) {
160
+ this.#watchedFiles.delete(uri);
161
+ this.emit('unlink', uri);
162
+ }
163
+ }
164
+ /**
165
+ * Reconcile the internal URI store with the actual directories and files on the disk.
166
+ * @param uri URI that should be reconciled. If it's a file URI, ensure the file exists in the
167
+ * internal URI store; if it's a directory URI, ensure all contents of it exists and no extra
168
+ * content is recorded; if the URI does not exist, reconcile its parent URI up until the watched
169
+ * `locations`.
170
+ */
171
+ async reconcile(uri) {
172
+ return this.#reconcile(core.normalizeUri(uri.toString()));
173
+ }
174
+ async #reconcile(uri, stat) {
175
+ uri = core.fileUtil.trimEndingSlash(uri);
176
+ try {
177
+ stat ??= await this.#externals.fs.stat(uri);
178
+ if (stat.isDirectory()) {
179
+ // For directory, reconcile all its entries recursively.
180
+ const dirUri = core.fileUtil.ensureEndingSlash(uri);
181
+ const diskEntries = await this.#externals.fs.readdir(dirUri);
182
+ const diskEntryNames = new Set();
183
+ for (const diskEntry of diskEntries) {
184
+ diskEntryNames.add(diskEntry.name);
185
+ await this.#reconcile(core.fileUtil.join(dirUri, diskEntry.name), diskEntry);
186
+ }
187
+ // Remove extra entries of this directory, if any, from the internal URI store.
188
+ const storeEntryNames = this.#watchedFiles.getChildrenNames(dirUri);
189
+ for (const storeEntryName of storeEntryNames) {
190
+ if (!diskEntryNames.has(storeEntryName)) {
191
+ this.#handleDelete(core.fileUtil.join(dirUri, storeEntryName));
192
+ }
193
+ }
194
+ }
195
+ else if (stat.isFile()) {
196
+ // For file, ensure it exists in the internal store if it belongs there.
197
+ this.#addIfNeeded(uri);
198
+ // And delete it if it should be excluded.
199
+ this.#deleteIfExcluded(uri);
200
+ }
201
+ }
202
+ catch (e) {
203
+ if (!this.#externals.error.isKind(e, 'ENOENT')) {
204
+ throw e;
205
+ }
206
+ // The URI does not exist. Remove it from internal store.
207
+ this.#logger.warn(`[LspFileWatcher] non-existent URI during reconcilation ${uri}; will reconcile with further parent directory`);
208
+ this.#handleDelete(uri);
209
+ // It is weird that reconcile() was called on a non-existent URI. We will go up a
210
+ // directory and reconcile there as well just to fix anything that might be wrong.
211
+ return this.#reconcileParentOf(uri);
212
+ }
213
+ }
214
+ async #reconcileParentOf(uri) {
215
+ const parentUri = core.fileUtil.trimEndingSlash(core.fileUtil.getParentOfUri(uri).toString());
216
+ // We only reconcile the parent if it is different from the current URI (to avoid an infinite
217
+ // loop) and if it is under the watched locations of the file watcher.
218
+ if (!(parentUri !== uri
219
+ && this.#locations.some((loc) => core.fileUtil.isSubUriOf(parentUri, loc.toString())))) {
220
+ this.#logger.warn(`[LspFileWatcher] reconcilation stopped after ${uri} at ${parentUri}`);
221
+ return;
222
+ }
223
+ return this.reconcile(parentUri);
224
+ }
225
+ }
226
+ //# sourceMappingURL=LspFileWatcher.js.map
@@ -10,7 +10,9 @@ export declare function diagnostic(error: core.PosRangeLanguageError): ls.Diagno
10
10
  export declare function diagnostics(errors: readonly core.PosRangeLanguageError[]): ls.Diagnostic[];
11
11
  export declare function diagnosticSeverity(severity: core.ErrorSeverity): ls.DiagnosticSeverity;
12
12
  export declare function documentHighlight(locations: core.SymbolLocations | undefined): ls.DocumentHighlight[] | undefined;
13
- export declare function documentSelector(meta: core.MetaRegistry): ls.DocumentSelector;
13
+ export declare function documentSelector(meta: core.MetaRegistry, { disabledLanguages }?: {
14
+ disabledLanguages?: string[];
15
+ }): ls.DocumentSelector;
14
16
  export declare function documentSymbol(symbol: core.Symbol, symLoc: core.SymbolLocation, doc: TextDocument, hierarchicalSupport: boolean | undefined, supportedKinds?: ls.SymbolKind[]): ls.DocumentSymbol;
15
17
  export declare function documentSymbols(map: core.SymbolMap | undefined, doc: TextDocument, hierarchicalSupport: boolean | undefined, supportedKinds?: ls.SymbolKind[]): ls.DocumentSymbol[];
16
18
  export declare function documentSymbolsFromTable(table: core.SymbolTable, doc: TextDocument, hierarchicalSupport: boolean | undefined, supportedKinds?: ls.SymbolKind[]): ls.DocumentSymbol[];
package/lib/util/toLS.js CHANGED
@@ -65,8 +65,8 @@ export function documentHighlight(locations) {
65
65
  range: loc.posRange,
66
66
  }));
67
67
  }
68
- export function documentSelector(meta) {
69
- const ans = meta.getLanguages().map((id) => ({ language: id }));
68
+ export function documentSelector(meta, { disabledLanguages } = {}) {
69
+ const ans = meta.getLanguages().filter((id) => disabledLanguages === undefined || !disabledLanguages.includes(id)).map((id) => ({ language: id }));
70
70
  return ans;
71
71
  }
72
72
  export function documentSymbol(symbol, symLoc, doc, hierarchicalSupport, supportedKinds = []) {
@@ -1,6 +1,7 @@
1
+ import type { PartialConfig } from '@spyglassmc/core';
1
2
  export interface CustomInitializationOptions {
2
3
  inDevelopmentMode?: boolean;
3
- gameVersion?: string;
4
+ defaultConfig?: PartialConfig;
4
5
  }
5
6
  export interface CustomServerCapabilities {
6
7
  dataHackPubify?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spyglassmc/language-server",
3
- "version": "0.4.54",
3
+ "version": "0.4.56",
4
4
  "type": "module",
5
5
  "main": "lib/server.js",
6
6
  "types": "lib/server.d.ts",
@@ -35,10 +35,10 @@
35
35
  "env-paths": "^2.2.1",
36
36
  "vscode-languageserver": "^9.0.1",
37
37
  "vscode-languageserver-textdocument": "^1.0.11",
38
- "@spyglassmc/core": "0.4.42",
39
- "@spyglassmc/java-edition": "0.3.54",
40
- "@spyglassmc/locales": "0.3.21",
41
- "@spyglassmc/mcdoc": "0.3.46"
38
+ "@spyglassmc/core": "0.4.44",
39
+ "@spyglassmc/java-edition": "0.3.56",
40
+ "@spyglassmc/locales": "0.3.23",
41
+ "@spyglassmc/mcdoc": "0.3.48"
42
42
  },
43
43
  "publishConfig": {
44
44
  "access": "public"