From 5ca58be753149fb39e3e6a2e9e0dd7bc0879525e Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Mon, 10 Jun 2024 15:35:31 +0200 Subject: [PATCH 01/12] Add file-based scoping --- hugo/content/docs/recipes/scoping/_index.md | 6 + .../docs/recipes/scoping/file-based.md | 188 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 hugo/content/docs/recipes/scoping/file-based.md diff --git a/hugo/content/docs/recipes/scoping/_index.md b/hugo/content/docs/recipes/scoping/_index.md index b97b2708..405327e2 100644 --- a/hugo/content/docs/recipes/scoping/_index.md +++ b/hugo/content/docs/recipes/scoping/_index.md @@ -30,3 +30,9 @@ In this guide, we'll look at different scoping kinds and styles and see how we c Note that these are just example implementations for commonly used scoping methods. The scoping API of Langium is designed to be flexible and extensible for any kind of use case. + +## Other kinds of scoping + +Also mind the following scoping kinds: + +- [File-based scoping](/docs/recipes/scoping/file-based) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md new file mode 100644 index 00000000..3df4175c --- /dev/null +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -0,0 +1,188 @@ +--- +title: "File-based scoping" +weight: 300 +--- + +## Goal + +Our goal here is to mimic the TypeScript import/export mechanism. By that I mean: + +* you can export certain symbols using an `export` keyword from your current file to make it available to the other files +* you can import certain symbols using the `import` keyword from a different file + +To make things easier I will modify the "Hello World" example from the [learning section](/docs/learn/workflow). + +## Step 1: Change the grammar + +First thing, we are changing the grammar to support the `export` and the `import` keywords. Here is the modified grammar: + +```langium +grammar HelloWorld + +entry Model: + ( + fileImports+=FileImport //NEW: imports per file + | persons+=Person + | greetings+=Greeting + )*; + +FileImport: + 'import' '{' personImports+=PersonImport (',' personImports+=PersonImport)* '}' 'from' file=STRING + ; //NEW: imports of the same file are gathered in a list + +PersonImport: + person=[Person:ID] ('as' name=ID)? + ; + +Person: + published?='export'? 'person' name=ID; //NEW: export keyword + +type Greetable = PersonImport | Person + +Greeting: + 'Hello' person=[Greetable:ID] '!'; + +hidden terminal WS: /\s+/; +terminal ID: /[_a-zA-Z][\w_]*/; +terminal STRING: /"(\\.|[^"\\])*"|'(\\.|[^'\\])*'/; + +hidden terminal ML_COMMENT: /\/\*[\s\S]*?\*\//; +hidden terminal SL_COMMENT: /\/\/[^\n\r]*/; +``` + +After changing the grammar you need to regenerate the abstract syntax tree (AST) and the language infrastructure. You can do that by running the following command: + +```bash +npm run langium:generate +``` + +## Step 2: Exporting persons to the global scope + +The index manager shall get all persons that are marked with the export keyword. In Langium this is done by overriding the `ScopeComputation.getExports(…)` function. Here is the implementation: + +```typescript +export class HelloWorldScopeComputation extends DefaultScopeComputation { + override async computeExports(document: LangiumDocument, _cancelToken?: CancellationToken | undefined): Promise { + const model = document.parseResult.value as Model; + return model.persons + .filter(p => p.published) + .map(p => this.descriptions.createDescription(p, p.name)) + ; + } +} +``` + +After that, you need to register the `HelloWorldScopeComputation` in the `HelloWorldModule`: + +```typescript +export const HelloWorldModule: Module = { + //... + references: { + ScopeComputation: (services) => new HelloWorldScopeComputation(services) + } +}; +``` + +Having done this, will make all persons that are marked with the `export` keyword available to the other files through the index manager. + +## Step 3: Bending the cross-reference resolution + +The final step is to adjust the cross-reference resolution through overriding the `DefaultScopeProvider.getScope(…)` function. Here is the implementation: + +```typescript +export class HelloWorldScopeProvider extends DefaultScopeProvider { + override getScope(context: ReferenceInfo): Scope { + switch(context.container.$type as keyof HelloWorldAstType) { + case 'PersonImport': + if(context.property === 'person') { + return this.getExportedPersonsFromGlobalScope(context); + } + break; + case 'Greeting': + if(context.property === 'person') { + return this.getImportedPersonsFromCurrentFile(context); + } + break; + } + return EMPTY_SCOPE; + } + //... +} +``` + +You noticed the two missing functions? Here is what they have to do. + +The first function (`getExportedPersonsFromGlobalScope(context)`) will take a look at the global scope and return all exported persons respecting the files that were touched. Not that we are outputting all persons that are marked with the `export` keyword. The actual name resolution is done later by the linker. + +```typescript +protected getExportedPersonsFromGlobalScope(context: ReferenceInfo): Scope { + //get document for current reference + const document = AstUtils.getDocument(context.container); + //get model of document + const model = document.parseResult.value as Model; + //determine current directory + const currentDir = dirname(document.uri.fsPath); + //look at all imports of the current document + const astNodeDescriptions = model.fileImports.flatMap((fileImport) => { + const fileName = resolve(currentDir, fileImport.file); + const uri = URI.file(fileName); + return this.indexManager.allElements(Person, new Set([uri.toString()])).toArray(); + }); + return this.createScope(astNodeDescriptions); +} +``` + +The second function (`getImportedPersonsFromCurrentFile(context)`) will take a look at the current file and return all persons that are imported from other files. + +```typescript +private getImportedPersonsFromCurrentFile(context: ReferenceInfo) { + //get current document of reference + const document = AstUtils.getDocument(context.container); + //get current model + const model = document.parseResult.value as Model; + //go through all imports + const descriptions = model.fileImports.flatMap(fi => fi.personImports.map(pi => { + //if the import is name, return the import + if (pi.name) { + return this.descriptions.createDescription(pi, pi.name); + } + //if import references to a person, return that person + if (pi.person.ref) { + return this.descriptions.createDescription(pi.person.ref, pi.person.ref.name); + } + //otherwise return nothing + return undefined; + }).filter(d => d != undefined)).map(d => d!); + return this.createScope(descriptions); +} +``` + +## Result + +Now, let's test the editor by `npm run build` and starting the extension. +Try using these two files. The first file contains the Simpsons family. + +``` +export person Homer +export person Marge +person Bart +person Lisa +export person Maggy +``` + +The second file tries to import and greet them. + +``` +import { + Marge, + Homer, + Lisa, //reference error + Maggy as Baby +} from "persons.hello" + +Hello Lisa! //reference error +Hello Maggy! //reference error +Hello Homer! +Hello Marge! +Hello Baby! +``` \ No newline at end of file From 48d63cd647c14a06f322c9217bcc5cd8655ae21f Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Mon, 10 Jun 2024 15:44:02 +0200 Subject: [PATCH 02/12] Add a hint about the module --- .../docs/recipes/scoping/file-based.md | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md index 3df4175c..6b169428 100644 --- a/hugo/content/docs/recipes/scoping/file-based.md +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -110,9 +110,21 @@ export class HelloWorldScopeProvider extends DefaultScopeProvider { } ``` +Do not forget to add the new service to the `HelloWorldModule`: + +```typescript +export const HelloWorldModule: Module = { + //... + references: { + ScopeComputation: (services) => new HelloWorldScopeComputation(services), + ScopeProvider: (services) => new HelloWorldScopeProvider(services) //NEW! + } +}; +``` + You noticed the two missing functions? Here is what they have to do. -The first function (`getExportedPersonsFromGlobalScope(context)`) will take a look at the global scope and return all exported persons respecting the files that were touched. Not that we are outputting all persons that are marked with the `export` keyword. The actual name resolution is done later by the linker. +The first function (`getExportedPersonsFromGlobalScope(context)`) will take a look at the global scope and return all exported persons respecting the files that were touched by the file imports. Note that we are outputting all persons that are marked with the `export` keyword. The actual name resolution is done internally later by the linker. ```typescript protected getExportedPersonsFromGlobalScope(context: ReferenceInfo): Scope { @@ -162,7 +174,7 @@ private getImportedPersonsFromCurrentFile(context: ReferenceInfo) { Now, let's test the editor by `npm run build` and starting the extension. Try using these two files. The first file contains the Simpsons family. -``` +```plain export person Homer export person Marge person Bart @@ -172,17 +184,17 @@ export person Maggy The second file tries to import and greet them. -``` +```plain import { Marge, Homer, - Lisa, //reference error + Lisa, //reference error, because not exported Maggy as Baby } from "persons.hello" -Hello Lisa! //reference error -Hello Maggy! //reference error +Hello Lisa! //reference error, because no valid import +Hello Maggy! //reference error, because name was overwritten with 'Baby' Hello Homer! Hello Marge! Hello Baby! -``` \ No newline at end of file +``` From b1defacc4b43e5785f7670a7d257bb3c3b649fdc Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Mon, 10 Jun 2024 16:48:32 +0200 Subject: [PATCH 03/12] Add adjustments from Mark --- .../docs/recipes/scoping/file-based.md | 111 ++++++++++++++++-- 1 file changed, 103 insertions(+), 8 deletions(-) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md index 6b169428..85f7b803 100644 --- a/hugo/content/docs/recipes/scoping/file-based.md +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -132,14 +132,23 @@ protected getExportedPersonsFromGlobalScope(context: ReferenceInfo): Scope { const document = AstUtils.getDocument(context.container); //get model of document const model = document.parseResult.value as Model; - //determine current directory - const currentDir = dirname(document.uri.fsPath); - //look at all imports of the current document - const astNodeDescriptions = model.fileImports.flatMap((fileImport) => { - const fileName = resolve(currentDir, fileImport.file); - const uri = URI.file(fileName); - return this.indexManager.allElements(Person, new Set([uri.toString()])).toArray(); - }); + //get URI of current document + const currentUri = document.uri; + //get folder of current document + const currentDir = dirname(currentUri.path); + const uris = new Set(); + //for all file imports of the current file + for (const fileImport of model.fileImports) { + //resolve the file name relatively to the current file + const filePath = join(currentDir, fileImport.file); + //create back an URI + const uri = currentUri.with({ path: filePath }); + //add the URI to URI list + uris.add(uri.toString()); + } + //get all possible persons from these files + const astNodeDescriptions = this.indexManager.allElements(Person, uris).toArray(); + //convert them to descriptions inside of a scope return this.createScope(astNodeDescriptions); } ``` @@ -198,3 +207,89 @@ Hello Homer! Hello Marge! Hello Baby! ``` + +
+Full Implementation + +```ts +import { AstNode, AstNodeDescription, AstUtils, DefaultScopeComputation, DefaultScopeProvider, EMPTY_SCOPE, LangiumDocument, ReferenceInfo, Scope } from "langium"; +import { CancellationToken } from "vscode-languageclient"; +import { HelloWorldAstType, Model, Person } from "./generated/ast.js"; +import { dirname, join } from "node:path"; + +export class HelloWorldScopeComputation extends DefaultScopeComputation { + override async computeExports(document: LangiumDocument, _cancelToken?: CancellationToken | undefined): Promise { + const model = document.parseResult.value as Model; + return model.persons + .filter(p => p.published) + .map(p => this.descriptions.createDescription(p, p.name)) + ; + } +} + +export class HelloWorldScopeProvider extends DefaultScopeProvider { + override getScope(context: ReferenceInfo): Scope { + switch(context.container.$type as keyof HelloWorldAstType) { + case 'PersonImport': + if(context.property === 'person') { + return this.getExportedPersonsFromGlobalScope(context); + } + break; + case 'Greeting': + if(context.property === 'person') { + return this.getImportedPersonsFromCurrentFile(context); + } + break; + } + return EMPTY_SCOPE; + } + + protected getExportedPersonsFromGlobalScope(context: ReferenceInfo): Scope { + //get document for current reference + const document = AstUtils.getDocument(context.container); + //get model of document + const model = document.parseResult.value as Model; + //get URI of current document + const currentUri = document.uri; + //get folder of current document + const currentDir = dirname(currentUri.path); + const uris = new Set(); + //for all file imports of the current file + for (const fileImport of model.fileImports) { + //resolve the file name relatively to the current file + const filePath = join(currentDir, fileImport.file); + //create back an URI + const uri = currentUri.with({ path: filePath }); + //add the URI to URI list + uris.add(uri.toString()); + } + //get all possible persons from these files + const astNodeDescriptions = this.indexManager.allElements(Person, uris).toArray(); + //convert them to descriptions inside of a scope + return this.createScope(astNodeDescriptions); + } + + private getImportedPersonsFromCurrentFile(context: ReferenceInfo) { + //get current document of reference + const document = AstUtils.getDocument(context.container); + //get current model + const model = document.parseResult.value as Model; + //go through all imports + const descriptions = model.fileImports.flatMap(fi => fi.personImports.map(pi => { + //if the import is name, return the import + if (pi.name) { + return this.descriptions.createDescription(pi, pi.name); + } + //if import references to a person, return that person + if (pi.person.ref) { + return this.descriptions.createDescription(pi.person.ref, pi.person.ref.name); + } + //otherwise return nothing + return undefined; + }).filter(d => d != undefined)).map(d => d!); + return this.createScope(descriptions); + } +} +``` + +
\ No newline at end of file From 131196b6f43bfdcc7d2d1c67e99c8018a61f78b0 Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Tue, 6 Aug 2024 17:19:12 +0200 Subject: [PATCH 04/12] Update hugo/content/docs/recipes/scoping/file-based.md Co-authored-by: Mark Sujew --- hugo/content/docs/recipes/scoping/file-based.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md index 85f7b803..0243a950 100644 --- a/hugo/content/docs/recipes/scoping/file-based.md +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -5,10 +5,10 @@ weight: 300 ## Goal -Our goal here is to mimic the TypeScript import/export mechanism. By that I mean: +By default, Langium will always expose all top-level AST elements to the global scope. That means they are visible to all other documents in your workspace. However, a lot of languages are better served with a JavaScript-like `import`/`export` mechanism: -* you can export certain symbols using an `export` keyword from your current file to make it available to the other files -* you can import certain symbols using the `import` keyword from a different file +* Using `export` makes a symbol from the current file available for referencing from another file. +* Using `import` allows to reference symbols for a different file. To make things easier I will modify the "Hello World" example from the [learning section](/docs/learn/workflow). From 0c955176587ce3577942e018f27067f00089eed7 Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Tue, 6 Aug 2024 17:19:34 +0200 Subject: [PATCH 05/12] Update hugo/content/docs/recipes/scoping/file-based.md Co-authored-by: Mark Sujew --- hugo/content/docs/recipes/scoping/file-based.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md index 0243a950..6929d2ed 100644 --- a/hugo/content/docs/recipes/scoping/file-based.md +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -14,7 +14,7 @@ To make things easier I will modify the "Hello World" example from the [learning ## Step 1: Change the grammar -First thing, we are changing the grammar to support the `export` and the `import` keywords. Here is the modified grammar: +First off, we are changing the grammar to support the `export` and the `import` statements. Let's take a look at the modified grammar: ```langium grammar HelloWorld From f68853cbcc674efd3c9341a4fa410077419c629f Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Tue, 6 Aug 2024 17:20:14 +0200 Subject: [PATCH 06/12] Update hugo/content/docs/recipes/scoping/file-based.md Co-authored-by: Mark Sujew --- hugo/content/docs/recipes/scoping/file-based.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md index 6929d2ed..2f73c080 100644 --- a/hugo/content/docs/recipes/scoping/file-based.md +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -26,9 +26,10 @@ entry Model: | greetings+=Greeting )*; -FileImport: - 'import' '{' personImports+=PersonImport (',' personImports+=PersonImport)* '}' 'from' file=STRING - ; //NEW: imports of the same file are gathered in a list +FileImport: //NEW: imports of the same file are gathered in a list + 'import' '{' + personImports+=PersonImport (',' personImports+=PersonImport)* + '}' 'from' file=STRING; PersonImport: person=[Person:ID] ('as' name=ID)? From 52034a2b981bd821bf233bbb5f9ee56a06871537 Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Tue, 6 Aug 2024 17:20:33 +0200 Subject: [PATCH 07/12] Update hugo/content/docs/recipes/scoping/file-based.md Co-authored-by: Mark Sujew --- hugo/content/docs/recipes/scoping/file-based.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md index 2f73c080..306327ec 100644 --- a/hugo/content/docs/recipes/scoping/file-based.md +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -32,8 +32,7 @@ FileImport: //NEW: imports of the same file are gathered in a list '}' 'from' file=STRING; PersonImport: - person=[Person:ID] ('as' name=ID)? - ; + person=[Person:ID] ('as' name=ID)?; Person: published?='export'? 'person' name=ID; //NEW: export keyword From d6c1685317e1d1da73a50bdfb60b8abb2529d6a3 Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Tue, 6 Aug 2024 17:20:56 +0200 Subject: [PATCH 08/12] Update hugo/content/docs/recipes/scoping/file-based.md Co-authored-by: Mark Sujew --- hugo/content/docs/recipes/scoping/file-based.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md index 306327ec..9a2cb6c7 100644 --- a/hugo/content/docs/recipes/scoping/file-based.md +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -85,7 +85,7 @@ export const HelloWorldModule: Module Date: Tue, 6 Aug 2024 17:21:29 +0200 Subject: [PATCH 09/12] Update hugo/content/docs/recipes/scoping/file-based.md Co-authored-by: Mark Sujew --- hugo/content/docs/recipes/scoping/file-based.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md index 9a2cb6c7..7f7daa75 100644 --- a/hugo/content/docs/recipes/scoping/file-based.md +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -62,12 +62,11 @@ The index manager shall get all persons that are marked with the export keyword. ```typescript export class HelloWorldScopeComputation extends DefaultScopeComputation { - override async computeExports(document: LangiumDocument, _cancelToken?: CancellationToken | undefined): Promise { + override async computeExports(document: LangiumDocument): Promise { const model = document.parseResult.value as Model; return model.persons .filter(p => p.published) - .map(p => this.descriptions.createDescription(p, p.name)) - ; + .map(p => this.descriptions.createDescription(p, p.name)); } } ``` From acc632c9051a80b6e9135d2ea352f39f511f9c9b Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Tue, 6 Aug 2024 17:21:47 +0200 Subject: [PATCH 10/12] Update hugo/content/docs/recipes/scoping/file-based.md Co-authored-by: Mark Sujew --- hugo/content/docs/recipes/scoping/file-based.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md index 7f7daa75..41055187 100644 --- a/hugo/content/docs/recipes/scoping/file-based.md +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -86,7 +86,7 @@ Having done this, will make all persons that are marked with the `export` keywor ## Step 3: Importing from specific files -The final step is to adjust the cross-reference resolution through overriding the `DefaultScopeProvider.getScope(…)` function. Here is the implementation: +The final step is to adjust the cross-reference resolution by overriding the `DefaultScopeProvider.getScope(…)` function: ```typescript export class HelloWorldScopeProvider extends DefaultScopeProvider { From aca7c97a4c9982e1cac9fa1459e7a478fd7f3d0d Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Tue, 6 Aug 2024 17:22:03 +0200 Subject: [PATCH 11/12] Update hugo/content/docs/recipes/scoping/file-based.md Co-authored-by: Mark Sujew --- hugo/content/docs/recipes/scoping/file-based.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md index 41055187..13fa49c2 100644 --- a/hugo/content/docs/recipes/scoping/file-based.md +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -126,7 +126,7 @@ You noticed the two missing functions? Here is what they have to do. The first function (`getExportedPersonsFromGlobalScope(context)`) will take a look at the global scope and return all exported persons respecting the files that were touched by the file imports. Note that we are outputting all persons that are marked with the `export` keyword. The actual name resolution is done internally later by the linker. ```typescript -protected getExportedPersonsFromGlobalScope(context: ReferenceInfo): Scope { +private getExportedPersonsFromGlobalScope(context: ReferenceInfo): Scope { //get document for current reference const document = AstUtils.getDocument(context.container); //get model of document From 538591184dbbac6a3ea68c35fbbd95127ae733e5 Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Tue, 6 Aug 2024 17:27:02 +0200 Subject: [PATCH 12/12] Fix the introduction and appendix --- hugo/content/docs/recipes/scoping/_index.md | 9 ++------- hugo/content/docs/recipes/scoping/file-based.md | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/hugo/content/docs/recipes/scoping/_index.md b/hugo/content/docs/recipes/scoping/_index.md index 405327e2..78b4d172 100644 --- a/hugo/content/docs/recipes/scoping/_index.md +++ b/hugo/content/docs/recipes/scoping/_index.md @@ -23,16 +23,11 @@ In general, the way we resolve references is split into three phases of the docu - [Scope computation](/docs/reference/document-lifecycle#computing-scopes) determines which elements are reachable from a given position in your document. - Finally, the [linking phase](/docs/reference/document-lifecycle#linking) eagerly links each reference within a document to its target using your language's scoping rules. -In this guide, we'll look at different scoping kinds and styles and see how we can achieve them using Langium: +In this recipe, we'll look at different scoping kinds and styles and see how we can achieve them using Langium: 1. [Qualified Name Scoping](/docs/recipes/scoping/qualified-name) 2. [Class Member Scoping](/docs/recipes/scoping/class-member) +3. [File-based scoping](/docs/recipes/scoping/file-based) Note that these are just example implementations for commonly used scoping methods. The scoping API of Langium is designed to be flexible and extensible for any kind of use case. - -## Other kinds of scoping - -Also mind the following scoping kinds: - -- [File-based scoping](/docs/recipes/scoping/file-based) diff --git a/hugo/content/docs/recipes/scoping/file-based.md b/hugo/content/docs/recipes/scoping/file-based.md index 13fa49c2..754b866c 100644 --- a/hugo/content/docs/recipes/scoping/file-based.md +++ b/hugo/content/docs/recipes/scoping/file-based.md @@ -217,7 +217,7 @@ import { HelloWorldAstType, Model, Person } from "./generated/ast.js"; import { dirname, join } from "node:path"; export class HelloWorldScopeComputation extends DefaultScopeComputation { - override async computeExports(document: LangiumDocument, _cancelToken?: CancellationToken | undefined): Promise { + override async computeExports(document: LangiumDocument): Promise { const model = document.parseResult.value as Model; return model.persons .filter(p => p.published)