From 6b559c2f4202fb977c57174e313e9d003a30b58e Mon Sep 17 00:00:00 2001 From: Damien Daspit Date: Fri, 8 Nov 2024 17:57:09 -0500 Subject: [PATCH] Add support for localization --- .vscode/launch.json | 6 +- package-lock.json | 64 +++++++++++++++++ packages/core/package.json | 5 +- .../src/diagnostic/diagnostic-provider.ts | 1 + .../formatting/on-type-formatting-provider.ts | 1 + packages/core/src/workspace/index.ts | 1 + packages/core/src/workspace/localizer.ts | 69 +++++++++++++++++++ packages/core/src/workspace/workspace.ts | 14 ++++ packages/eslint-config/library.mjs | 1 + packages/examples/eslint.config.mjs | 12 ++++ packages/examples/package.json | 44 ++++++++++++ packages/examples/src/index.ts | 2 + .../examples/src/locales/en/verse-order.json | 9 +++ .../examples/src/locales/es/verse-order.json | 9 +++ .../src/simple-quote-formatting-provider.ts} | 8 ++- .../verse-order-diagnostic-provider.test.ts | 41 +++++++++++ .../src/verse-order-diagnostic-provider.ts | 45 ++++++++---- packages/examples/tsconfig.json | 9 +++ packages/usfm/package.json | 4 +- packages/vscode/package.json | 1 + packages/vscode/src/server.ts | 19 +++-- 21 files changed, 334 insertions(+), 31 deletions(-) create mode 100644 packages/core/src/workspace/localizer.ts create mode 100644 packages/examples/eslint.config.mjs create mode 100644 packages/examples/package.json create mode 100644 packages/examples/src/index.ts create mode 100644 packages/examples/src/locales/en/verse-order.json create mode 100644 packages/examples/src/locales/es/verse-order.json rename packages/{vscode/src/smart-quote-formatting-provider.ts => examples/src/simple-quote-formatting-provider.ts} (87%) create mode 100644 packages/examples/src/verse-order-diagnostic-provider.test.ts rename packages/{vscode => examples}/src/verse-order-diagnostic-provider.ts (73%) create mode 100644 packages/examples/tsconfig.json diff --git a/.vscode/launch.json b/.vscode/launch.json index b6c3eff..138ca26 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,11 +8,7 @@ "name": "Launch Client", "runtimeExecutable": "${execPath}", "args": ["--extensionDevelopmentPath=${workspaceRoot}/packages/vscode"], - "outFiles": [ - "${workspaceRoot}/packages/vscode/dist/**/*.js", - "${workspaceRoot}/packages/core/dist/**/*.cjs", - "${workspaceRoot}/packages/usfm/dist/**/*.cjs" - ], + "outFiles": ["${workspaceRoot}/packages/*/dist/**/*.c?js"], "autoAttachChildProcesses": true, "preLaunchTask": "npm: dev", "sourceMaps": true diff --git a/package-lock.json b/package-lock.json index 136f622..ead6ebf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,6 +66,18 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/types": { "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", @@ -1053,6 +1065,10 @@ "resolved": "packages/core", "link": true }, + "node_modules/@sillsdev/lynx-examples": { + "resolved": "packages/examples", + "link": true + }, "node_modules/@sillsdev/lynx-usfm": { "resolved": "packages/usfm", "link": true @@ -3328,6 +3344,29 @@ "node": ">=10.17.0" } }, + "node_modules/i18next": { + "version": "23.16.5", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.5.tgz", + "integrity": "sha512-KTlhE3EP9x6pPTAW7dy0WKIhoCpfOGhRQlO+jttQLgzVaoOjWwBWramu7Pp0i+8wDNduuzXfe3kkVbzrKyrbTA==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4596,6 +4635,12 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", @@ -6663,6 +6708,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "i18next": "^23.16.5", "rxjs": "^7.8.1" }, "devDependencies": { @@ -6689,6 +6735,23 @@ "typescript-eslint": "^8.3.0" } }, + "packages/examples": { + "name": "@sillsdev/lynx-examples", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@sillsdev/lynx": "*" + }, + "devDependencies": { + "@repo/eslint-config": "*", + "@repo/typescript-config": "*", + "@vitest/coverage-v8": "^2.1.4", + "eslint": "^9.9.1", + "tsup": "^8.3.0", + "typescript": "^5.5.4", + "vitest": "^2.1.4" + } + }, "packages/typescript-config": { "name": "@repo/typescript-config", "version": "0.0.0", @@ -6718,6 +6781,7 @@ "license": "MIT", "dependencies": { "@sillsdev/lynx": "*", + "@sillsdev/lynx-examples": "*", "@sillsdev/lynx-usfm": "*", "vscode-languageclient": "^9.0.1", "vscode-languageserver": "^9.0.1" diff --git a/packages/core/package.json b/packages/core/package.json index d47429a..a786d93 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -19,8 +19,8 @@ } }, "scripts": { - "build": "tsup src/index.ts --dts --format cjs,esm --clean --sourcemap", - "dev": "tsup src/index.ts --dts --format cjs,esm --watch --sourcemap", + "build": "tsup-node src/index.ts --dts --format cjs,esm --clean --sourcemap", + "dev": "tsup-node src/index.ts --dts --format cjs,esm --watch --sourcemap", "check-types": "tsc --noEmit", "lint": "eslint .", "test": "vitest", @@ -30,6 +30,7 @@ "author": "SIL Global", "license": "MIT", "dependencies": { + "i18next": "^23.16.5", "rxjs": "^7.8.1" }, "devDependencies": { diff --git a/packages/core/src/diagnostic/diagnostic-provider.ts b/packages/core/src/diagnostic/diagnostic-provider.ts index d051290..ce66ad4 100644 --- a/packages/core/src/diagnostic/diagnostic-provider.ts +++ b/packages/core/src/diagnostic/diagnostic-provider.ts @@ -12,6 +12,7 @@ export interface DiagnosticsChanged { export interface DiagnosticProvider { readonly id: string; readonly diagnosticsChanged$: Observable; + init(): Promise; getDiagnostics(uri: string): Promise; getDiagnosticFixes(uri: string, diagnostic: Diagnostic): Promise; } diff --git a/packages/core/src/formatting/on-type-formatting-provider.ts b/packages/core/src/formatting/on-type-formatting-provider.ts index 471433a..8908918 100644 --- a/packages/core/src/formatting/on-type-formatting-provider.ts +++ b/packages/core/src/formatting/on-type-formatting-provider.ts @@ -6,5 +6,6 @@ export interface OnTypeFormattingProvider { readonly onTypeTriggerCharacters: ReadonlySet; + init(): Promise; getOnTypeEdits(uri: string, position: Position, ch: string): Promise; } diff --git a/packages/core/src/workspace/index.ts b/packages/core/src/workspace/index.ts index 85f25ab..7704467 100644 --- a/packages/core/src/workspace/index.ts +++ b/packages/core/src/workspace/index.ts @@ -1,2 +1,3 @@ +export { Localizer } from './localizer'; export type { WorkspaceConfig } from './workspace'; export { Workspace } from './workspace'; diff --git a/packages/core/src/workspace/localizer.ts b/packages/core/src/workspace/localizer.ts new file mode 100644 index 0000000..f95d019 --- /dev/null +++ b/packages/core/src/workspace/localizer.ts @@ -0,0 +1,69 @@ +import i18next, { i18n } from 'i18next'; + +export class Localizer { + private readonly i18n: i18n; + private readonly backend: LocalBackend; + + constructor(language?: string) { + this.backend = new LocalBackend(); + this.i18n = i18next + .createInstance({ + lng: language, + fallbackLng: 'en', + partialBundledLanguages: true, + ns: [], + resources: {}, + }) + .use(this.backend); + } + + get language(): string { + return this.i18n.language; + } + + async init(): Promise { + await this.i18n.init(); + } + + async addNamespace(namespace: string, loader: (language: string) => Promise): Promise { + if (this.i18n.hasLoadedNamespace(namespace)) { + return; + } + this.backend.addNamespace(namespace, loader); + await this.i18n.loadNamespaces(namespace); + } + + async changeLanguage(language: string): Promise { + await this.i18n.changeLanguage(language); + } + + t(key: string, options?: Record): string { + return this.i18n.t(key, options); + } +} + +class LocalBackend { + private readonly namespaceLoaders = new Map Promise>(); + readonly type = 'backend'; + + init(_services: unknown, _backendOptions: unknown, _i18nextOptions: unknown): void { + // do nothing + } + + addNamespace(namespace: string, loader: (language: string) => Promise): void { + this.namespaceLoaders.set(namespace, loader); + } + + read(language: string, namespace: string, callback: (err: unknown, data?: unknown) => void): void { + const loader = this.namespaceLoaders.get(namespace); + if (loader == null) { + callback(new Error(`No loader for namespace ${namespace}`)); + return; + } + loader(language) + .then((data) => { + callback(null, data); + }) + .catch(callback); + } +} diff --git a/packages/core/src/workspace/workspace.ts b/packages/core/src/workspace/workspace.ts index df3e3c9..5fdce11 100644 --- a/packages/core/src/workspace/workspace.ts +++ b/packages/core/src/workspace/workspace.ts @@ -6,13 +6,16 @@ import { Diagnostic } from '../diagnostic/diagnostic'; import { DiagnosticFix } from '../diagnostic/diagnostic-fix'; import { DiagnosticProvider, DiagnosticsChanged } from '../diagnostic/diagnostic-provider'; import { OnTypeFormattingProvider } from '../formatting/on-type-formatting-provider'; +import { Localizer } from './localizer'; export interface WorkspaceConfig { + localizer: Localizer; diagnosticProviders?: DiagnosticProvider[]; onTypeFormattingProviders?: OnTypeFormattingProvider[]; } export class Workspace { + private readonly localizer: Localizer; private readonly diagnosticProviders: Map; private readonly onTypeFormattingProviders: Map; private readonly lastDiagnosticChangedEvents = new Map(); @@ -20,6 +23,7 @@ export class Workspace { public readonly diagnosticsChanged$: Observable; constructor(config: WorkspaceConfig) { + this.localizer = config.localizer; this.diagnosticProviders = new Map(config.diagnosticProviders?.map((provider) => [provider.id, provider])); this.diagnosticsChanged$ = merge( ...Array.from(this.diagnosticProviders.values()).map((provider, i) => @@ -35,6 +39,16 @@ export class Workspace { ); } + async init(): Promise { + await this.localizer.init(); + await Promise.all(Array.from(this.diagnosticProviders.values()).map((provider) => provider.init())); + await Promise.all(Array.from(this.onTypeFormattingProviders.values()).map((provider) => provider.init())); + } + + changeLanguage(language: string): Promise { + return this.localizer.changeLanguage(language); + } + async getDiagnostics(uri: string): Promise { const diagnostics: Diagnostic[] = []; for (const provider of this.diagnosticProviders.values()) { diff --git a/packages/eslint-config/library.mjs b/packages/eslint-config/library.mjs index 7cc92fa..dddb853 100644 --- a/packages/eslint-config/library.mjs +++ b/packages/eslint-config/library.mjs @@ -35,6 +35,7 @@ export default tseslint.config( '@typescript-eslint/no-unsafe-assignment': 'off', '@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', }, }, { diff --git a/packages/examples/eslint.config.mjs b/packages/examples/eslint.config.mjs new file mode 100644 index 0000000..10132c0 --- /dev/null +++ b/packages/examples/eslint.config.mjs @@ -0,0 +1,12 @@ +import library from '@repo/eslint-config/library.mjs'; + +export default [ + { + languageOptions: { + parserOptions: { + project: './tsconfig.json', + }, + }, + }, + ...library, +]; diff --git a/packages/examples/package.json b/packages/examples/package.json new file mode 100644 index 0000000..75f3476 --- /dev/null +++ b/packages/examples/package.json @@ -0,0 +1,44 @@ +{ + "name": "@sillsdev/lynx-examples", + "version": "1.0.0", + "description": "", + "type": "module", + "types": "./dist/index.d.ts", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "scripts": { + "build": "tsup-node src/index.ts --dts --format cjs,esm --clean --sourcemap", + "dev": "tsup-node src/index.ts --dts --format cjs,esm --watch --sourcemap", + "check-types": "tsc --noEmit", + "lint": "eslint .", + "test": "vitest", + "coverage": "vitest run --coverage" + }, + "keywords": [], + "author": "SIL Global", + "license": "MIT", + "dependencies": { + "@sillsdev/lynx": "*" + }, + "devDependencies": { + "@repo/eslint-config": "*", + "@repo/typescript-config": "*", + "@vitest/coverage-v8": "^2.1.4", + "eslint": "^9.9.1", + "tsup": "^8.3.0", + "typescript": "^5.5.4", + "vitest": "^2.1.4" + } +} diff --git a/packages/examples/src/index.ts b/packages/examples/src/index.ts new file mode 100644 index 0000000..84b5e63 --- /dev/null +++ b/packages/examples/src/index.ts @@ -0,0 +1,2 @@ +export { SimpleQuoteFormattingProvider } from './simple-quote-formatting-provider'; +export { VerseOrderDiagnosticProvider } from './verse-order-diagnostic-provider'; diff --git a/packages/examples/src/locales/en/verse-order.json b/packages/examples/src/locales/en/verse-order.json new file mode 100644 index 0000000..4d23bf4 --- /dev/null +++ b/packages/examples/src/locales/en/verse-order.json @@ -0,0 +1,9 @@ +{ + "missingVerse": { + "description": "Verse {{verse}} is missing from chapter {{chapter}}. Insert the missing verse to fix.", + "fixTitle": "Insert missing verse" + }, + "verseOutOfOrder": { + "description": "Verse {{verse}} occurs out of order in chapter {{chapter}}." + } +} diff --git a/packages/examples/src/locales/es/verse-order.json b/packages/examples/src/locales/es/verse-order.json new file mode 100644 index 0000000..cb0e41c --- /dev/null +++ b/packages/examples/src/locales/es/verse-order.json @@ -0,0 +1,9 @@ +{ + "missingVerse": { + "description": "Falta el versículo {{verse}} del capítulo {{chapter}}. Inserta el versículo que falta para arreglarlo.", + "fixTitle": "Inserta el versículo que falta" + }, + "verseOutOfOrder": { + "description": "El versículo {{verse}} aparece fuera de orden en el capítulo {{chapter}}." + } +} diff --git a/packages/vscode/src/smart-quote-formatting-provider.ts b/packages/examples/src/simple-quote-formatting-provider.ts similarity index 87% rename from packages/vscode/src/smart-quote-formatting-provider.ts rename to packages/examples/src/simple-quote-formatting-provider.ts index a6b1f58..437ed9f 100644 --- a/packages/vscode/src/smart-quote-formatting-provider.ts +++ b/packages/examples/src/simple-quote-formatting-provider.ts @@ -1,11 +1,15 @@ import { DocumentManager, OnTypeFormattingProvider, Position, TextDocument, TextEdit } from '@sillsdev/lynx'; -export class SmartQuoteFormattingProvider implements OnTypeFormattingProvider { - readonly id = 'smart-quote'; +export class SimpleQuoteFormattingProvider implements OnTypeFormattingProvider { + readonly id = 'simple-quote'; readonly onTypeTriggerCharacters: ReadonlySet = new Set(['"', '“', '”']); constructor(private readonly documentManager: DocumentManager) {} + init(): Promise { + return Promise.resolve(); + } + async getOnTypeEdits(uri: string, _position: Position, _ch: string): Promise { const doc = await this.documentManager.get(uri); if (doc == null) { diff --git a/packages/examples/src/verse-order-diagnostic-provider.test.ts b/packages/examples/src/verse-order-diagnostic-provider.test.ts new file mode 100644 index 0000000..6e0b44d --- /dev/null +++ b/packages/examples/src/verse-order-diagnostic-provider.test.ts @@ -0,0 +1,41 @@ +import { + DocumentFactory, + DocumentManager, + Localizer, + ScriptureChapter, + ScriptureDocument, + ScriptureParagraph, + ScriptureSerializer, +} from '@sillsdev/lynx'; +import { describe, expect, it } from 'vitest'; +import { mock, MockProxy } from 'vitest-mock-extended'; + +import { VerseOrderDiagnosticProvider } from './verse-order-diagnostic-provider'; + +describe('VerseOrderDiagnosticProvider', () => { + it('missing verse', () => { + const env = new TestEnvironment(); + env.docManager.fireOpened('file1', 'plaintext', 1, ''); + env.provider.getDiagnostics(''); + }); +}); + +class TestEnvironment { + private readonly docFactory: MockProxy>; + readonly docManager: DocumentManager; + private readonly localizer: Localizer; + private readonly serializer: MockProxy; + readonly provider: VerseOrderDiagnosticProvider; + + constructor() { + this.docFactory = mock>(); + this.docFactory.create.mockImplementation((uri, _format, version, content) => { + return new ScriptureDocument(uri, version, content, [new ScriptureChapter('1'), new ScriptureParagraph('p')]); + }); + + this.docManager = new DocumentManager(this.docFactory); + this.localizer = new Localizer(); + this.serializer = mock(); + this.provider = new VerseOrderDiagnosticProvider(this.localizer, this.docManager, this.serializer); + } +} diff --git a/packages/vscode/src/verse-order-diagnostic-provider.ts b/packages/examples/src/verse-order-diagnostic-provider.ts similarity index 73% rename from packages/vscode/src/verse-order-diagnostic-provider.ts rename to packages/examples/src/verse-order-diagnostic-provider.ts index 6cda052..bd6a05e 100644 --- a/packages/vscode/src/verse-order-diagnostic-provider.ts +++ b/packages/examples/src/verse-order-diagnostic-provider.ts @@ -5,6 +5,7 @@ import { DiagnosticsChanged, DiagnosticSeverity, DocumentManager, + Localizer, ScriptureChapter, ScriptureDocument, ScriptureNodeType, @@ -18,6 +19,7 @@ export class VerseOrderDiagnosticProvider implements DiagnosticProvider { public readonly diagnosticsChanged$: Observable; constructor( + private readonly localizer: Localizer, private readonly documentManager: DocumentManager, private readonly serializer: ScriptureSerializer, ) { @@ -45,6 +47,13 @@ export class VerseOrderDiagnosticProvider implements DiagnosticProvider { ); } + init(): Promise { + return this.localizer.addNamespace( + 'verseOrder', + (language: string) => import(`./locales/${language}/verse-order.json`), + ); + } + async getDiagnostics(uri: string): Promise { const doc = await this.documentManager.get(uri); if (doc == null) { @@ -58,7 +67,7 @@ export class VerseOrderDiagnosticProvider implements DiagnosticProvider { if (diagnostic.code === 2) { const verseNumber = diagnostic.data as number; fixes.push({ - title: `Insert missing verse`, + title: this.localizer.t('missingVerse.fixTitle', { ns: 'verseOrder' }), isPreferred: true, diagnostic, edits: [ @@ -84,19 +93,25 @@ export class VerseOrderDiagnosticProvider implements DiagnosticProvider { verseNodes.length = 0; } else if (node instanceof ScriptureVerse) { const verseNumber = parseInt(node.number); - if (verseNodes.length > 0) { - const [prevVerseNumber, prevVerseNode] = verseNodes[verseNodes.length - 1]; - if (verseNumber <= prevVerseNumber) { - diagnostics.push({ - range: prevVerseNode.range, - severity: DiagnosticSeverity.Error, - code: 1, - message: `Verse ${prevVerseNumber.toString()} occurs out of order in chapter ${chapterNumber}.`, - source: this.id, - }); + if (!isNaN(verseNumber)) { + if (verseNodes.length > 0) { + const [prevVerseNumber, prevVerseNode] = verseNodes[verseNodes.length - 1]; + if (verseNumber <= prevVerseNumber) { + diagnostics.push({ + range: prevVerseNode.range, + severity: DiagnosticSeverity.Error, + code: 1, + message: this.localizer.t('verseOutOfOrder.description', { + ns: 'verseOrder', + chapter: chapterNumber, + verse: prevVerseNumber.toString(), + }), + source: this.id, + }); + } } + verseNodes.push([verseNumber, node]); } - verseNodes.push([verseNumber, node]); } } @@ -114,7 +129,11 @@ export class VerseOrderDiagnosticProvider implements DiagnosticProvider { range: node.range, severity: DiagnosticSeverity.Warning, code: 2, - message: `Verse ${missingVerse.toString()} is missing from chapter ${chapterNumber}. Insert the missing verse to fix.`, + message: this.localizer.t('missingVerse.description', { + ns: 'verseOrder', + chapter: chapterNumber, + verse: missingVerse.toString(), + }), source: this.id, data: missingVerse, }); diff --git a/packages/examples/tsconfig.json b/packages/examples/tsconfig.json new file mode 100644 index 0000000..08c05db --- /dev/null +++ b/packages/examples/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@repo/typescript-config/base.json", + + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "resolveJsonModule": true + } +} diff --git a/packages/usfm/package.json b/packages/usfm/package.json index 7de2ea1..d2b1527 100644 --- a/packages/usfm/package.json +++ b/packages/usfm/package.json @@ -19,8 +19,8 @@ } }, "scripts": { - "build": "tsup src/index.ts --dts --format cjs,esm --clean --sourcemap", - "dev": "tsup src/index.ts --dts --format cjs,esm --watch --sourcemap", + "build": "tsup-node src/index.ts --dts --format cjs,esm --clean --sourcemap", + "dev": "tsup-node src/index.ts --dts --format cjs,esm --watch --sourcemap", "check-types": "tsc --noEmit", "lint": "eslint .", "test": "vitest", diff --git a/packages/vscode/package.json b/packages/vscode/package.json index a93866b..ae533b5 100644 --- a/packages/vscode/package.json +++ b/packages/vscode/package.json @@ -36,6 +36,7 @@ "dependencies": { "@sillsdev/lynx": "*", "@sillsdev/lynx-usfm": "*", + "@sillsdev/lynx-examples": "*", "vscode-languageclient": "^9.0.1", "vscode-languageserver": "^9.0.1" }, diff --git a/packages/vscode/src/server.ts b/packages/vscode/src/server.ts index 6d91b60..d085c20 100644 --- a/packages/vscode/src/server.ts +++ b/packages/vscode/src/server.ts @@ -1,4 +1,5 @@ -import { Diagnostic, DocumentManager, ScriptureDocument, Workspace } from '@sillsdev/lynx'; +import { Diagnostic, DocumentManager, Localizer, ScriptureDocument, Workspace } from '@sillsdev/lynx'; +import { SimpleQuoteFormattingProvider, VerseOrderDiagnosticProvider } from '@sillsdev/lynx-examples'; import { UsfmDocumentFactory, UsfmScriptureSerializer } from '@sillsdev/lynx-usfm'; import { UsfmStylesheet } from '@sillsdev/machine/corpora'; import { @@ -13,25 +14,29 @@ import { TextDocumentSyncKind, } from 'vscode-languageserver/node'; -import { SmartQuoteFormattingProvider } from './smart-quote-formatting-provider'; -import { VerseOrderDiagnosticProvider } from './verse-order-diagnostic-provider'; - // Create a connection for the server, using Node's IPC as a transport. // Also include all preview / proposed LSP features. const connection = createConnection(ProposedFeatures.all); +const localizer = new Localizer(); const stylesheet = new UsfmStylesheet('usfm.sty'); const documentFactory = new UsfmDocumentFactory(stylesheet); const scriptureSerializer = new UsfmScriptureSerializer(stylesheet); const documentManager = new DocumentManager(documentFactory); const workspace = new Workspace({ - diagnosticProviders: [new VerseOrderDiagnosticProvider(documentManager, scriptureSerializer)], - onTypeFormattingProviders: [new SmartQuoteFormattingProvider(documentManager)], + localizer, + diagnosticProviders: [new VerseOrderDiagnosticProvider(localizer, documentManager, scriptureSerializer)], + onTypeFormattingProviders: [new SimpleQuoteFormattingProvider(documentManager)], }); let hasWorkspaceFolderCapability = false; -connection.onInitialize((params: InitializeParams) => { +connection.onInitialize(async (params: InitializeParams) => { + await workspace.init(); + if (params.locale != null) { + await workspace.changeLanguage(params.locale); + } + const capabilities = params.capabilities; hasWorkspaceFolderCapability = capabilities.workspace?.workspaceFolders ?? false;