First commit.

Signed-off-by: Chen Xiao <abigwc@gmail.com>
This commit is contained in:
Chen Xiao
2026-05-08 14:43:16 +08:00
commit 0b64e2de94
10989 changed files with 2253791 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
Copyright 2021 TypeFox GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+50
View File
@@ -0,0 +1,50 @@
# Langium
Langium is a language engineering tool with built-in support for the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/). It has a simple and direct integration with the [VS Code extension API](https://code.visualstudio.com/api/language-extensions/overview).
More information: 🌍 https://langium.org
## Getting Started
Langium offers a [Yeoman](https://yeoman.io) generator to create a new language extension for VS Code. The only prerequisite for the following terminal commands is [NodeJS](https://nodejs.org/) version 16 or higher.
1. Install Yeoman and the Langium extension generator.
```
npm install -g yo generator-langium
```
2. Run the generator and answer a few questions.
```
yo langium
```
3. Open the new folder in VS Code (replace `hello-world` with the extension name you chose).
```
code hello-world
```
4. Press **F5** to launch the extension in a new Extension Development Host window.
5. Open a folder, create a file with your chosen file name extension (`.hello` is the default), and see that validation and completion (ctrl+space) works:
Follow the instructions in `langium-quickstart.md` (in your extension folder) and the [documentation on the website](https://langium.org/docs/) to go further.
## How Does it Work?
The core of Langium is a _grammar declaration language_ in which you describe multiple aspects of your language:
- Tokens (keywords and terminal rules)
- Syntax (parser rules)
- Abstract syntax tree (AST)
Please follow the [Langium documentation](https://langium.org/docs/grammar-language/) to learn how to use this language.
Langium features a command line interface ([langium-cli](https://www.npmjs.com/package/langium-cli)) that reads a grammar declaration and generates TypeScript type declarations for the AST and more.
Integration with the Language Server Protocol (LSP) is done with [vscode-languageserver](https://www.npmjs.com/package/vscode-languageserver). You have full access to the LSP API in Langium, so you can register additional message handlers or extend the protocol in a breeze.
The main code of Langium consists of a set of services that are connected via dependency injection (DI). You can override the default functionality and add your own service classes by specifying a DI module.
## Examples
The source repository of Langium includes [examples](https://github.com/eclipse-langium/langium/tree/main/examples) that demonstrate different use cases.
+38
View File
@@ -0,0 +1,38 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { Module } from './dependency-injection.js';
import type { LangiumDefaultCoreServices, LangiumDefaultSharedCoreServices, LangiumCoreServices, LangiumSharedCoreServices } from './services.js';
import type { FileSystemProvider } from './workspace/file-system-provider.js';
/**
* Context required for creating the default language-specific dependency injection module.
*/
export interface DefaultCoreModuleContext {
shared: LangiumSharedCoreServices;
}
/**
* Creates a dependency injection module configuring the default core services.
* This is a set of services that are dedicated to a specific language.
*/
export declare function createDefaultCoreModule(context: DefaultCoreModuleContext): Module<LangiumCoreServices, LangiumDefaultCoreServices>;
/**
* Context required for creating the default shared dependency injection module.
*/
export interface DefaultSharedCoreModuleContext {
/**
* Factory function to create a {@link FileSystemProvider}.
*
* Langium exposes an `EmptyFileSystem` and `NodeFileSystem`, exported through `langium/node`.
* When running Langium as part of a vscode language server or a Node.js app, using the `NodeFileSystem` is recommended,
* the `EmptyFileSystem` in every other use case.
*/
fileSystemProvider: (services: LangiumSharedCoreServices) => FileSystemProvider;
}
/**
* Creates a dependency injection module configuring the default shared core services.
* This is the set of services that are shared between multiple languages.
*/
export declare function createDefaultSharedCoreModule(context: DefaultSharedCoreModuleContext): Module<LangiumSharedCoreServices, LangiumDefaultSharedCoreServices>;
//# sourceMappingURL=default-module.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"default-module.d.ts","sourceRoot":"","sources":["../src/default-module.ts"],"names":[],"mappings":"AAAA;;;;+EAI+E;AAE/E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,KAAK,EAAE,0BAA0B,EAAE,gCAAgC,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAClJ,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AA8B9E;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACrC,MAAM,EAAE,yBAAyB,CAAC;CACrC;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,CAAC,mBAAmB,EAAE,0BAA0B,CAAC,CAuClI;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC3C;;;;;;OAMG;IACH,kBAAkB,EAAE,CAAC,QAAQ,EAAE,yBAAyB,KAAK,kBAAkB,CAAC;CACnF;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,8BAA8B,GAAG,MAAM,CAAC,yBAAyB,EAAE,gCAAgC,CAAC,CAe1J"}
+98
View File
@@ -0,0 +1,98 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { createGrammarConfig } from './languages/grammar-config.js';
import { createCompletionParser } from './parser/completion-parser-builder.js';
import { createLangiumParser } from './parser/langium-parser-builder.js';
import { DefaultTokenBuilder } from './parser/token-builder.js';
import { DefaultValueConverter } from './parser/value-converter.js';
import { DefaultLinker } from './references/linker.js';
import { DefaultNameProvider } from './references/name-provider.js';
import { DefaultReferences } from './references/references.js';
import { DefaultScopeComputation } from './references/scope-computation.js';
import { DefaultScopeProvider } from './references/scope-provider.js';
import { DefaultJsonSerializer } from './serializer/json-serializer.js';
import { DefaultServiceRegistry } from './service-registry.js';
import { DefaultDocumentValidator } from './validation/document-validator.js';
import { ValidationRegistry } from './validation/validation-registry.js';
import { DefaultAstNodeDescriptionProvider, DefaultReferenceDescriptionProvider } from './workspace/ast-descriptions.js';
import { DefaultAstNodeLocator } from './workspace/ast-node-locator.js';
import { DefaultConfigurationProvider } from './workspace/configuration.js';
import { DefaultDocumentBuilder } from './workspace/document-builder.js';
import { DefaultLangiumDocumentFactory, DefaultLangiumDocuments } from './workspace/documents.js';
import { DefaultIndexManager } from './workspace/index-manager.js';
import { DefaultWorkspaceManager } from './workspace/workspace-manager.js';
import { DefaultLexer, DefaultLexerErrorMessageProvider } from './parser/lexer.js';
import { JSDocDocumentationProvider } from './documentation/documentation-provider.js';
import { DefaultCommentProvider } from './documentation/comment-provider.js';
import { LangiumParserErrorMessageProvider } from './parser/langium-parser.js';
import { DefaultAsyncParser } from './parser/async-parser.js';
import { DefaultWorkspaceLock } from './workspace/workspace-lock.js';
import { DefaultHydrator } from './serializer/hydrator.js';
/**
* Creates a dependency injection module configuring the default core services.
* This is a set of services that are dedicated to a specific language.
*/
export function createDefaultCoreModule(context) {
return {
documentation: {
CommentProvider: (services) => new DefaultCommentProvider(services),
DocumentationProvider: (services) => new JSDocDocumentationProvider(services)
},
parser: {
AsyncParser: (services) => new DefaultAsyncParser(services),
GrammarConfig: (services) => createGrammarConfig(services),
LangiumParser: (services) => createLangiumParser(services),
CompletionParser: (services) => createCompletionParser(services),
ValueConverter: () => new DefaultValueConverter(),
TokenBuilder: () => new DefaultTokenBuilder(),
Lexer: (services) => new DefaultLexer(services),
ParserErrorMessageProvider: () => new LangiumParserErrorMessageProvider(),
LexerErrorMessageProvider: () => new DefaultLexerErrorMessageProvider()
},
workspace: {
AstNodeLocator: () => new DefaultAstNodeLocator(),
AstNodeDescriptionProvider: (services) => new DefaultAstNodeDescriptionProvider(services),
ReferenceDescriptionProvider: (services) => new DefaultReferenceDescriptionProvider(services)
},
references: {
Linker: (services) => new DefaultLinker(services),
NameProvider: () => new DefaultNameProvider(),
ScopeProvider: (services) => new DefaultScopeProvider(services),
ScopeComputation: (services) => new DefaultScopeComputation(services),
References: (services) => new DefaultReferences(services)
},
serializer: {
Hydrator: (services) => new DefaultHydrator(services),
JsonSerializer: (services) => new DefaultJsonSerializer(services)
},
validation: {
DocumentValidator: (services) => new DefaultDocumentValidator(services),
ValidationRegistry: (services) => new ValidationRegistry(services)
},
shared: () => context.shared
};
}
/**
* Creates a dependency injection module configuring the default shared core services.
* This is the set of services that are shared between multiple languages.
*/
export function createDefaultSharedCoreModule(context) {
return {
ServiceRegistry: (services) => new DefaultServiceRegistry(services),
workspace: {
LangiumDocuments: (services) => new DefaultLangiumDocuments(services),
LangiumDocumentFactory: (services) => new DefaultLangiumDocumentFactory(services),
DocumentBuilder: (services) => new DefaultDocumentBuilder(services),
IndexManager: (services) => new DefaultIndexManager(services),
WorkspaceManager: (services) => new DefaultWorkspaceManager(services),
FileSystemProvider: (services) => context.fileSystemProvider(services),
WorkspaceLock: () => new DefaultWorkspaceLock(),
ConfigurationProvider: (services) => new DefaultConfigurationProvider(services),
},
profilers: {}
};
}
//# sourceMappingURL=default-module.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"default-module.js","sourceRoot":"","sources":["../src/default-module.ts"],"names":[],"mappings":"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"}
+59
View File
@@ -0,0 +1,59 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
/**
* A `Module<I>` is a description of possibly grouped service factories.
*
* Given a type I = { group: { service: A } },
* Module<I> := { group: { service: (injector: I) => A } }
*
* Making `I` available during the creation of `I` allows us to create cyclic
* dependencies.
*/
export type Module<I, T = I> = {
[K in keyof T]: Module<I, T[K]> | ((injector: I) => T[K]);
};
export declare namespace Module {
/**
* Merges two dependency injection modules into a new (third) one that is returned.
* At that `m1` and `m2` stay unchanged. Therefore, `m1` is deep-copied first,
* and m2 is merged onto the copy afterwards.
*
* Note that the leaf values of `m1` and `m2`, i.e. the service constructor functions,
* cannot be copied generically, since they are functions. They are shared by the source and merged modules.
*
* @returns the merged module being a deep copy of `m1` with `m2` merged onto it.
*/
const merge: <M1, M2, R extends M1 & M2>(m1: Module<R, M1>, m2: Module<R, M2>) => Module<R, M1 & M2>;
}
/**
* Given a set of modules, the inject function returns a lazily evaluated injector
* that injects dependencies into the requested service when it is requested the
* first time. Subsequent requests will return the same service.
*
* In the case of cyclic dependencies, an Error will be thrown. This can be fixed
* by injecting a provider `() => T` instead of a `T`.
*
* Please note that the arguments may be objects or arrays. However, the result will
* be an object. Using it with for..of will have no effect.
*
* @param module1 first Module
* @param module2 (optional) second Module
* @param module3 (optional) third Module
* @param module4 (optional) fourth Module
* @param module5 (optional) fifth Module
* @param module6 (optional) sixth Module
* @param module7 (optional) seventh Module
* @param module8 (optional) eighth Module
* @param module9 (optional) ninth Module
* @returns a new object of type I
*/
export declare function inject<I1, I2, I3, I4, I5, I6, I7, I8, I9, I extends I1 & I2 & I3 & I4 & I5 & I6 & I7 & I8 & I9>(module1: Module<I, I1>, module2?: Module<I, I2>, module3?: Module<I, I3>, module4?: Module<I, I4>, module5?: Module<I, I5>, module6?: Module<I, I6>, module7?: Module<I, I7>, module8?: Module<I, I8>, module9?: Module<I, I9>): I;
/**
* Eagerly load all services in the given dependency injection container. This is sometimes
* necessary because services can register event listeners in their constructors.
*/
export declare function eagerLoad<T>(item: T): T;
//# sourceMappingURL=dependency-injection.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"dependency-injection.d.ts","sourceRoot":"","sources":["../src/dependency-injection.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAIhF;;;;;;;;GAQG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI;KAC1B,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5D,CAAA;AAED,yBAAiB,MAAM,CAAC;IACpB;;;;;;;;;OASG;IACI,MAAM,KAAK,GAAI,EAAE,EAAE,EAAE,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,IAAI,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,KAAoC,MAAM,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,CAAE,CAAC;CAChJ;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAC3G,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,GAC/N,CAAC,CAGH;AAID;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAOvC"}
+164
View File
@@ -0,0 +1,164 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
export var Module;
(function (Module) {
/**
* Merges two dependency injection modules into a new (third) one that is returned.
* At that `m1` and `m2` stay unchanged. Therefore, `m1` is deep-copied first,
* and m2 is merged onto the copy afterwards.
*
* Note that the leaf values of `m1` and `m2`, i.e. the service constructor functions,
* cannot be copied generically, since they are functions. They are shared by the source and merged modules.
*
* @returns the merged module being a deep copy of `m1` with `m2` merged onto it.
*/
Module.merge = (m1, m2) => _merge(_merge({}, m1), m2);
})(Module || (Module = {}));
/**
* Given a set of modules, the inject function returns a lazily evaluated injector
* that injects dependencies into the requested service when it is requested the
* first time. Subsequent requests will return the same service.
*
* In the case of cyclic dependencies, an Error will be thrown. This can be fixed
* by injecting a provider `() => T` instead of a `T`.
*
* Please note that the arguments may be objects or arrays. However, the result will
* be an object. Using it with for..of will have no effect.
*
* @param module1 first Module
* @param module2 (optional) second Module
* @param module3 (optional) third Module
* @param module4 (optional) fourth Module
* @param module5 (optional) fifth Module
* @param module6 (optional) sixth Module
* @param module7 (optional) seventh Module
* @param module8 (optional) eighth Module
* @param module9 (optional) ninth Module
* @returns a new object of type I
*/
export function inject(module1, module2, module3, module4, module5, module6, module7, module8, module9) {
const module = [module1, module2, module3, module4, module5, module6, module7, module8, module9].reduce(_merge, {});
return _inject(module);
}
const isProxy = Symbol('isProxy');
/**
* Eagerly load all services in the given dependency injection container. This is sometimes
* necessary because services can register event listeners in their constructors.
*/
export function eagerLoad(item) {
if (item && item[isProxy]) {
for (const value of Object.values(item)) {
eagerLoad(value);
}
}
return item;
}
/**
* Helper function that returns an injector by creating a proxy.
* Invariant: injector is of type I. If injector is undefined, then T = I.
*/
function _inject(module, injector) {
const proxy = new Proxy({}, {
deleteProperty: () => false,
set: () => {
throw new Error('Cannot set property on injected service container');
},
get: (obj, prop) => {
if (prop === isProxy) {
return true;
}
else {
return _resolve(obj, prop, module, injector || proxy);
}
},
getOwnPropertyDescriptor: (obj, prop) => (_resolve(obj, prop, module, injector || proxy), Object.getOwnPropertyDescriptor(obj, prop)), // used by for..in
has: (_, prop) => prop in module, // used by ..in..
ownKeys: () => [...Object.getOwnPropertyNames(module)] // used by for..in
});
return proxy;
}
/**
* Internally used to tag a requested dependency, directly before calling the factory.
* This allows us to find cycles during instance creation.
*/
const __requested__ = Symbol();
/**
* Returns the value `obj[prop]`. If the value does not exist, yet, it is resolved from
* the module description. The result of service factories is cached. Groups are
* recursively proxied.
*
* @param obj an object holding all group proxies and services
* @param prop the key of a value within obj
* @param module an object containing groups and service factories
* @param injector the first level proxy that provides access to all values
* @returns the requested value `obj[prop]`
* @throws Error if a dependency cycle is detected
*/
function _resolve(obj, prop, module, injector) {
if (prop in obj) {
if (obj[prop] instanceof Error) {
throw new Error('Construction failure. Please make sure that your dependencies are constructable. Cause: ' + obj[prop]);
}
if (obj[prop] === __requested__) {
throw new Error('Cycle detected. Please make "' + String(prop) + '" lazy. Visit https://langium.org/docs/reference/configuration-services/#resolving-cyclic-dependencies');
}
return obj[prop];
}
else if (prop in module) {
const value = module[prop];
obj[prop] = __requested__;
try {
obj[prop] = (typeof value === 'function') ? value(injector) : _inject(value, injector);
}
catch (error) {
obj[prop] = error instanceof Error ? error : undefined;
throw error;
}
return obj[prop];
}
else {
return undefined;
}
}
/**
* Performs a deep-merge of two modules by writing source entries into the target module.
*
* @param target the module which is written
* @param source the module which is read
* @returns the target module
*/
function _merge(target, source) {
if (source) {
for (const [key, sourceValue] of Object.entries(source)) {
if (sourceValue !== undefined && sourceValue !== null) {
if (typeof sourceValue === 'object') {
const targetValue = target[key];
if (typeof targetValue === 'object' && targetValue !== null) {
// in case both values are real (non-null) objects merge them recursively
target[key] = _merge(targetValue, sourceValue);
}
else {
// in case 'target[key]' is not a non-null object
// we overwrite any existing value with a deep copy of 'sourceValue'
// by recursively calling this function with a new 'target' object to be populated
// that is assigned to 'target[key]' afterwards
target[key] = _merge({}, sourceValue);
}
}
else {
// in case 'sourceValue' is defined and assigned (non-null) but not an object
// we assume it to be a service constructor function according to the Module<I> type definition
target[key] = sourceValue;
// note the following for such service constructor functions:
// 'target[key]' will now reference the same function object being referenced by 'source[key]'.
// This is accepted here, since function objects cannot be safely copied in general.
}
}
}
}
return target;
}
//# sourceMappingURL=dependency-injection.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"dependency-injection.js","sourceRoot":"","sources":["../src/dependency-injection.ts"],"names":[],"mappings":"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"}
+25
View File
@@ -0,0 +1,25 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { GrammarConfig } from '../languages/grammar-config.js';
import type { LangiumCoreServices } from '../services.js';
import type { AstNode } from '../syntax-tree.js';
/**
* Provides comments for AST nodes.
*/
export interface CommentProvider {
/**
* Returns the comment associated with the specified AST node.
* @param node The AST node to get the comment for.
* @returns The comment associated with the specified AST node or `undefined` if there is no comment.
*/
getComment(node: AstNode): string | undefined;
}
export declare class DefaultCommentProvider implements CommentProvider {
protected readonly grammarConfig: () => GrammarConfig;
constructor(services: LangiumCoreServices);
getComment(node: AstNode): string | undefined;
}
//# sourceMappingURL=comment-provider.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"comment-provider.d.ts","sourceRoot":"","sources":["../../src/documentation/comment-provider.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGjD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;CACjD;AAED,qBAAa,sBAAuB,YAAW,eAAe;IAC1D,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,aAAa,CAAC;gBAC1C,QAAQ,EAAE,mBAAmB;IAGzC,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS;CAMhD"}
+19
View File
@@ -0,0 +1,19 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { isAstNodeWithComment } from '../serializer/json-serializer.js';
import { findCommentNode } from '../utils/cst-utils.js';
export class DefaultCommentProvider {
constructor(services) {
this.grammarConfig = () => services.parser.GrammarConfig;
}
getComment(node) {
if (isAstNodeWithComment(node)) {
return node.$comment;
}
return findCommentNode(node.$cstNode, this.grammarConfig().multilineCommentRules)?.text;
}
}
//# sourceMappingURL=comment-provider.js.map
@@ -0,0 +1 @@
{"version":3,"file":"comment-provider.js","sourceRoot":"","sources":["../../src/documentation/comment-provider.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAGhF,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAGxE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAcxD,MAAM,OAAO,sBAAsB;IAE/B,YAAY,QAA6B;QACrC,IAAI,CAAC,aAAa,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC;IAC7D,CAAC;IACD,UAAU,CAAC,IAAa;QACpB,IAAG,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,QAAQ,CAAC;QACzB,CAAC;QACD,OAAO,eAAe,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,qBAAqB,CAAC,EAAE,IAAI,CAAC;IAC5F,CAAC;CACJ"}
@@ -0,0 +1,32 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { LangiumCoreServices } from '../services.js';
import type { AstNode, AstNodeDescription } from '../syntax-tree.js';
import type { IndexManager } from '../workspace/index-manager.js';
import type { CommentProvider } from './comment-provider.js';
import type { JSDocTag } from './jsdoc.js';
/**
* Provides documentation for AST nodes.
*/
export interface DocumentationProvider {
/**
* Returns a markdown documentation string for the specified AST node.
*
* The default implementation `JSDocDocumentationProvider` will inspect the comment associated with the specified node.
*/
getDocumentation(node: AstNode): string | undefined;
}
export declare class JSDocDocumentationProvider implements DocumentationProvider {
protected readonly indexManager: IndexManager;
protected readonly commentProvider: CommentProvider;
constructor(services: LangiumCoreServices);
getDocumentation(node: AstNode): string | undefined;
protected documentationLinkRenderer(node: AstNode, name: string, display: string): string | undefined;
protected documentationTagRenderer(_node: AstNode, _tag: JSDocTag): string | undefined;
protected findNameInLocalSymbols(node: AstNode, name: string): AstNodeDescription | undefined;
protected findNameInGlobalScope(node: AstNode, name: string): AstNodeDescription | undefined;
}
//# sourceMappingURL=documentation-provider.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"documentation-provider.d.ts","sourceRoot":"","sources":["../../src/documentation/documentation-provider.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;CACvD;AAED,qBAAa,0BAA2B,YAAW,qBAAqB;IAEpE,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IAC9C,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;gBAExC,QAAQ,EAAE,mBAAmB;IAKzC,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS;IAgBnD,SAAS,CAAC,yBAAyB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAYrG,SAAS,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS;IAKtF,SAAS,CAAC,sBAAsB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAmB7F,SAAS,CAAC,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;CAI/F"}
@@ -0,0 +1,66 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { getDocument } from '../utils/ast-utils.js';
import { isJSDoc, parseJSDoc } from './jsdoc.js';
export class JSDocDocumentationProvider {
constructor(services) {
this.indexManager = services.shared.workspace.IndexManager;
this.commentProvider = services.documentation.CommentProvider;
}
getDocumentation(node) {
const comment = this.commentProvider.getComment(node);
if (comment && isJSDoc(comment)) {
const parsedJSDoc = parseJSDoc(comment);
return parsedJSDoc.toMarkdown({
renderLink: (link, display) => {
return this.documentationLinkRenderer(node, link, display);
},
renderTag: (tag) => {
return this.documentationTagRenderer(node, tag);
}
});
}
return undefined;
}
documentationLinkRenderer(node, name, display) {
const description = this.findNameInLocalSymbols(node, name) ?? this.findNameInGlobalScope(node, name);
if (description && description.nameSegment) {
const line = description.nameSegment.range.start.line + 1;
const character = description.nameSegment.range.start.character + 1;
const uri = description.documentUri.with({ fragment: `L${line},${character}` });
return `[${display}](${uri.toString()})`;
}
else {
return undefined;
}
}
documentationTagRenderer(_node, _tag) {
// Fall back to the default tag rendering
return undefined;
}
findNameInLocalSymbols(node, name) {
const document = getDocument(node);
const precomputed = document.localSymbols;
if (!precomputed) {
return undefined;
}
let currentNode = node;
do {
const allDescriptions = precomputed.getStream(currentNode);
const description = allDescriptions.find(e => e.name === name);
if (description) {
return description;
}
currentNode = currentNode.$container;
} while (currentNode);
return undefined;
}
findNameInGlobalScope(node, name) {
const description = this.indexManager.allElements().find(e => e.name === name);
return description;
}
}
//# sourceMappingURL=documentation-provider.js.map
@@ -0,0 +1 @@
{"version":3,"file":"documentation-provider.js","sourceRoot":"","sources":["../../src/documentation/documentation-provider.ts"],"names":[],"mappings":"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"}
+9
View File
@@ -0,0 +1,9 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
export * from './comment-provider.js';
export * from './documentation-provider.js';
export * from './jsdoc.js';
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/documentation/index.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,YAAY,CAAC"}
+9
View File
@@ -0,0 +1,9 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
export * from './comment-provider.js';
export * from './documentation-provider.js';
export * from './jsdoc.js';
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/documentation/index.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,YAAY,CAAC"}
+93
View File
@@ -0,0 +1,93 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { Position, Range } from 'vscode-languageserver-types';
import type { CstNode } from '../syntax-tree.js';
export interface JSDocComment extends JSDocValue {
readonly elements: JSDocElement[];
getTag(name: string): JSDocTag | undefined;
getTags(name: string): JSDocTag[];
}
export type JSDocElement = JSDocParagraph | JSDocTag;
export type JSDocInline = JSDocTag | JSDocLine;
export interface JSDocValue {
/**
* Represents the range that this JSDoc element occupies.
* If the JSDoc was parsed from a `CstNode`, the range will represent the location in the source document.
*/
readonly range: Range;
/**
* Renders this JSDoc element to a plain text representation.
*/
toString(): string;
/**
* Renders this JSDoc element to a markdown representation.
*
* @param options Rendering options to customize the markdown result.
*/
toMarkdown(options?: JSDocRenderOptions): string;
}
export interface JSDocParagraph extends JSDocValue {
readonly inlines: JSDocInline[];
}
export interface JSDocLine extends JSDocValue {
readonly text: string;
}
export interface JSDocTag extends JSDocValue {
readonly name: string;
readonly content: JSDocParagraph;
readonly inline: boolean;
}
export interface JSDocParseOptions {
/**
* The start symbol of your comment format. Defaults to `/**`.
*/
readonly start?: RegExp | string;
/**
* The symbol that start a line of your comment format. Defaults to `*`.
*/
readonly line?: RegExp | string;
/**
* The end symbol of your comment format. Defaults to `*\/`.
*/
readonly end?: RegExp | string;
}
export interface JSDocRenderOptions {
/**
* Determines the style for rendering tags. Defaults to `italic`.
*/
tag?: 'plain' | 'italic' | 'bold' | 'bold-italic';
/**
* Determines the default for rendering `@link` tags. Defaults to `plain`.
*/
link?: 'code' | 'plain';
/**
* Custom tag rendering function.
* Return a markdown formatted tag or `undefined` to fall back to the default rendering.
*/
renderTag?(tag: JSDocTag): string | undefined;
/**
* Custom link rendering function. Accepts a link target and a display value for the link.
* Return a markdown formatted link with the format `[$display]($link)` or `undefined` if the link is not a valid target.
*/
renderLink?(link: string, display: string): string | undefined;
}
/**
* Parses a JSDoc from a `CstNode` containing a comment.
*
* @param node A `CstNode` from a parsed Langium document.
* @param options Parsing options specialized to your language. See {@link JSDocParseOptions}.
*/
export declare function parseJSDoc(node: CstNode, options?: JSDocParseOptions): JSDocComment;
/**
* Parses a JSDoc from a string comment.
*
* @param content A string containing the source of the JSDoc comment.
* @param start The start position the comment occupies in the source document.
* @param options Parsing options specialized to your language. See {@link JSDocParseOptions}.
*/
export declare function parseJSDoc(content: string, start?: Position, options?: JSDocParseOptions): JSDocComment;
export declare function isJSDoc(node: CstNode | string, options?: JSDocParseOptions): boolean;
//# sourceMappingURL=jsdoc.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"jsdoc.d.ts","sourceRoot":"","sources":["../../src/documentation/jsdoc.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAIjD,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC5C,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;IACjC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IAC1C,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAA;CACpC;AAED,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,QAAQ,CAAC;AAErD,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE/C,MAAM,WAAW,UAAU;IACvB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB;;OAEG;IACH,QAAQ,IAAI,MAAM,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAA;CACnD;AAED,MAAM,WAAW,cAAe,SAAQ,UAAU;IAC9C,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAClC;AAED,MAAM,WAAW,SAAU,SAAQ,UAAU;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,QAAS,SAAQ,UAAU;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAC9B;;OAEG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAChC;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC/B;;OAEG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACjC;AAED,MAAM,WAAW,kBAAkB;IAC/B;;OAEG;IACH,GAAG,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,aAAa,CAAA;IACjD;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACvB;;;OAGG;IACH,SAAS,CAAC,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAA;IAC7C;;;OAGG;IACH,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CACjE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,YAAY,CAAC;AACrF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,YAAY,CAAC;AA+BzG,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAapF"}
+504
View File
@@ -0,0 +1,504 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { Position, Range } from 'vscode-languageserver-types';
import { NEWLINE_REGEXP, escapeRegExp } from '../utils/regexp-utils.js';
import { URI } from '../utils/uri-utils.js';
export function parseJSDoc(node, start, options) {
let opts;
let position;
if (typeof node === 'string') {
position = start;
opts = options;
}
else {
position = node.range.start;
opts = start;
}
if (!position) {
position = Position.create(0, 0);
}
const lines = getLines(node);
const normalizedOptions = normalizeOptions(opts);
const tokens = tokenize({
lines,
position,
options: normalizedOptions
});
return parseJSDocComment({
index: 0,
tokens,
position
});
}
export function isJSDoc(node, options) {
const normalizedOptions = normalizeOptions(options);
const lines = getLines(node);
if (lines.length === 0) {
return false;
}
const first = lines[0];
const last = lines[lines.length - 1];
const firstRegex = normalizedOptions.start;
const lastRegex = normalizedOptions.end;
return Boolean(firstRegex?.exec(first)) && Boolean(lastRegex?.exec(last));
}
function getLines(node) {
let content = '';
if (typeof node === 'string') {
content = node;
}
else {
content = node.text;
}
const lines = content.split(NEWLINE_REGEXP);
return lines;
}
const tagRegex = /\s*(@([\p{L}][\p{L}\p{N}]*)?)/uy;
const inlineTagRegex = /\{(@[\p{L}][\p{L}\p{N}]*)(\s*)([^\r\n}]+)?\}/gu;
function tokenize(context) {
const tokens = [];
let currentLine = context.position.line;
let currentCharacter = context.position.character;
for (let i = 0; i < context.lines.length; i++) {
const first = i === 0;
const last = i === context.lines.length - 1;
let line = context.lines[i];
let index = 0;
if (first && context.options.start) {
const match = context.options.start?.exec(line);
if (match) {
index = match.index + match[0].length;
}
}
else {
const match = context.options.line?.exec(line);
if (match) {
index = match.index + match[0].length;
}
}
if (last) {
const match = context.options.end?.exec(line);
if (match) {
line = line.substring(0, match.index);
}
}
line = line.substring(0, lastCharacter(line));
const whitespaceEnd = skipWhitespace(line, index);
if (whitespaceEnd >= line.length) {
// Only create a break token when we already have previous tokens
if (tokens.length > 0) {
const position = Position.create(currentLine, currentCharacter);
tokens.push({
type: 'break',
content: '',
range: Range.create(position, position)
});
}
}
else {
tagRegex.lastIndex = index;
const tagMatch = tagRegex.exec(line);
if (tagMatch) {
const fullMatch = tagMatch[0];
const value = tagMatch[1];
const start = Position.create(currentLine, currentCharacter + index);
const end = Position.create(currentLine, currentCharacter + index + fullMatch.length);
tokens.push({
type: 'tag',
content: value,
range: Range.create(start, end)
});
index += fullMatch.length;
index = skipWhitespace(line, index);
}
if (index < line.length) {
const rest = line.substring(index);
const inlineTagMatches = Array.from(rest.matchAll(inlineTagRegex));
tokens.push(...buildInlineTokens(inlineTagMatches, rest, currentLine, currentCharacter + index));
}
}
currentLine++;
currentCharacter = 0;
}
// Remove last break token if there is one
if (tokens.length > 0 && tokens[tokens.length - 1].type === 'break') {
return tokens.slice(0, -1);
}
return tokens;
}
function buildInlineTokens(tags, line, lineIndex, characterIndex) {
const tokens = [];
if (tags.length === 0) {
const start = Position.create(lineIndex, characterIndex);
const end = Position.create(lineIndex, characterIndex + line.length);
tokens.push({
type: 'text',
content: line,
range: Range.create(start, end)
});
}
else {
let lastIndex = 0;
for (const match of tags) {
const matchIndex = match.index;
const startContent = line.substring(lastIndex, matchIndex);
if (startContent.length > 0) {
tokens.push({
type: 'text',
content: line.substring(lastIndex, matchIndex),
range: Range.create(Position.create(lineIndex, lastIndex + characterIndex), Position.create(lineIndex, matchIndex + characterIndex))
});
}
let offset = startContent.length + 1;
const tagName = match[1];
tokens.push({
type: 'inline-tag',
content: tagName,
range: Range.create(Position.create(lineIndex, lastIndex + offset + characterIndex), Position.create(lineIndex, lastIndex + offset + tagName.length + characterIndex))
});
offset += tagName.length;
if (match.length === 4) {
offset += match[2].length;
const value = match[3];
tokens.push({
type: 'text',
content: value,
range: Range.create(Position.create(lineIndex, lastIndex + offset + characterIndex), Position.create(lineIndex, lastIndex + offset + value.length + characterIndex))
});
}
else {
tokens.push({
type: 'text',
content: '',
range: Range.create(Position.create(lineIndex, lastIndex + offset + characterIndex), Position.create(lineIndex, lastIndex + offset + characterIndex))
});
}
lastIndex = matchIndex + match[0].length;
}
const endContent = line.substring(lastIndex);
if (endContent.length > 0) {
tokens.push({
type: 'text',
content: endContent,
range: Range.create(Position.create(lineIndex, lastIndex + characterIndex), Position.create(lineIndex, lastIndex + characterIndex + endContent.length))
});
}
}
return tokens;
}
const nonWhitespaceRegex = /\S/;
const whitespaceEndRegex = /\s*$/;
function skipWhitespace(line, index) {
const match = line.substring(index).match(nonWhitespaceRegex);
if (match) {
return index + match.index;
}
else {
return line.length;
}
}
function lastCharacter(line) {
const match = line.match(whitespaceEndRegex);
if (match && typeof match.index === 'number') {
return match.index;
}
return undefined;
}
// Parsing
function parseJSDocComment(context) {
const startPosition = Position.create(context.position.line, context.position.character);
if (context.tokens.length === 0) {
return new JSDocCommentImpl([], Range.create(startPosition, startPosition));
}
const elements = [];
while (context.index < context.tokens.length) {
const element = parseJSDocElement(context, elements[elements.length - 1]);
if (element) {
elements.push(element);
}
}
const start = elements[0]?.range.start ?? startPosition;
const end = elements[elements.length - 1]?.range.end ?? startPosition;
return new JSDocCommentImpl(elements, Range.create(start, end));
}
function parseJSDocElement(context, last) {
const next = context.tokens[context.index];
if (next.type === 'tag') {
return parseJSDocTag(context, false);
}
else if (next.type === 'text' || next.type === 'inline-tag') {
return parseJSDocText(context);
}
else {
appendEmptyLine(next, last);
context.index++;
return undefined;
}
}
function appendEmptyLine(token, element) {
if (element) {
const line = new JSDocLineImpl('', token.range);
if ('inlines' in element) {
element.inlines.push(line);
}
else {
element.content.inlines.push(line);
}
}
}
function parseJSDocText(context) {
let token = context.tokens[context.index];
const firstToken = token;
let lastToken = token;
const lines = [];
while (token && token.type !== 'break' && token.type !== 'tag') {
lines.push(parseJSDocInline(context));
lastToken = token;
token = context.tokens[context.index];
}
return new JSDocTextImpl(lines, Range.create(firstToken.range.start, lastToken.range.end));
}
function parseJSDocInline(context) {
const token = context.tokens[context.index];
if (token.type === 'inline-tag') {
return parseJSDocTag(context, true);
}
else {
return parseJSDocLine(context);
}
}
function parseJSDocTag(context, inline) {
const tagToken = context.tokens[context.index++];
const name = tagToken.content.substring(1);
const nextToken = context.tokens[context.index];
if (nextToken?.type === 'text') {
if (inline) {
const docLine = parseJSDocLine(context);
return new JSDocTagImpl(name, new JSDocTextImpl([docLine], docLine.range), inline, Range.create(tagToken.range.start, docLine.range.end));
}
else {
const textDoc = parseJSDocText(context);
return new JSDocTagImpl(name, textDoc, inline, Range.create(tagToken.range.start, textDoc.range.end));
}
}
else {
const range = tagToken.range;
return new JSDocTagImpl(name, new JSDocTextImpl([], range), inline, range);
}
}
function parseJSDocLine(context) {
const token = context.tokens[context.index++];
return new JSDocLineImpl(token.content, token.range);
}
function normalizeOptions(options) {
if (!options) {
return normalizeOptions({
start: '/**',
end: '*/',
line: '*'
});
}
const { start, end, line } = options;
return {
start: normalizeOption(start, true),
end: normalizeOption(end, false),
line: normalizeOption(line, true)
};
}
function normalizeOption(option, start) {
if (typeof option === 'string' || typeof option === 'object') {
const escaped = typeof option === 'string' ? escapeRegExp(option) : option.source;
if (start) {
return new RegExp(`^\\s*${escaped}`);
}
else {
return new RegExp(`\\s*${escaped}\\s*$`);
}
}
else {
return option;
}
}
class JSDocCommentImpl {
constructor(elements, range) {
this.elements = elements;
this.range = range;
}
getTag(name) {
return this.getAllTags().find(e => e.name === name);
}
getTags(name) {
return this.getAllTags().filter(e => e.name === name);
}
getAllTags() {
return this.elements.filter(e => 'name' in e);
}
toString() {
let value = '';
for (const element of this.elements) {
if (value.length === 0) {
value = element.toString();
}
else {
const text = element.toString();
value += fillNewlines(value) + text;
}
}
return value.trim();
}
toMarkdown(options) {
let value = '';
for (const element of this.elements) {
if (value.length === 0) {
value = element.toMarkdown(options);
}
else {
const text = element.toMarkdown(options);
value += fillNewlines(value) + text;
}
}
return value.trim();
}
}
class JSDocTagImpl {
constructor(name, content, inline, range) {
this.name = name;
this.content = content;
this.inline = inline;
this.range = range;
}
toString() {
let text = `@${this.name}`;
const content = this.content.toString();
if (this.content.inlines.length === 1) {
text = `${text} ${content}`;
}
else if (this.content.inlines.length > 1) {
text = `${text}\n${content}`;
}
if (this.inline) {
// Inline tags are surrounded by curly braces
return `{${text}}`;
}
else {
return text;
}
}
toMarkdown(options) {
return options?.renderTag?.(this) ?? this.toMarkdownDefault(options);
}
toMarkdownDefault(options) {
const content = this.content.toMarkdown(options);
if (this.inline) {
const rendered = renderInlineTag(this.name, content, options ?? {});
if (typeof rendered === 'string') {
return rendered;
}
}
let marker = '';
if (options?.tag === 'italic' || options?.tag === undefined) {
marker = '*';
}
else if (options?.tag === 'bold') {
marker = '**';
}
else if (options?.tag === 'bold-italic') {
marker = '***';
}
let text = `${marker}@${this.name}${marker}`;
if (this.content.inlines.length === 1) {
text = `${text}${content}`;
}
else if (this.content.inlines.length > 1) {
text = `${text}\n${content}`;
}
if (this.inline) {
// Inline tags are surrounded by curly braces
return `{${text}}`;
}
else {
return text;
}
}
}
function renderInlineTag(tag, content, options) {
if (tag === 'linkplain' || tag === 'linkcode' || tag === 'link') {
const index = content.indexOf(' ');
let display = content;
if (index > 0) {
const displayStart = skipWhitespace(content, index);
display = content.substring(displayStart);
content = content.substring(0, index);
}
if (tag === 'linkcode' || (tag === 'link' && options.link === 'code')) {
// Surround the display value in a markdown inline code block
display = `\`${display}\``;
}
const renderedLink = options.renderLink?.(content, display) ?? renderLinkDefault(content, display);
return renderedLink;
}
return undefined;
}
function renderLinkDefault(content, display) {
try {
URI.parse(content, true);
return `[${display}](${content})`;
}
catch {
return content;
}
}
class JSDocTextImpl {
constructor(lines, range) {
this.inlines = lines;
this.range = range;
}
toString() {
let text = '';
for (let i = 0; i < this.inlines.length; i++) {
const inline = this.inlines[i];
const next = this.inlines[i + 1];
text += inline.toString();
if (next && next.range.start.line > inline.range.start.line) {
text += '\n';
}
}
return text;
}
toMarkdown(options) {
let text = '';
for (let i = 0; i < this.inlines.length; i++) {
const inline = this.inlines[i];
const next = this.inlines[i + 1];
text += inline.toMarkdown(options);
if (next && next.range.start.line > inline.range.start.line) {
text += '\n';
}
}
return text;
}
}
class JSDocLineImpl {
constructor(text, range) {
this.text = text;
this.range = range;
}
toString() {
return this.text;
}
toMarkdown() {
return this.text;
}
}
function fillNewlines(text) {
if (text.endsWith('\n')) {
return '\n';
}
else {
return '\n\n';
}
}
//# sourceMappingURL=jsdoc.js.map
File diff suppressed because one or more lines are too long
+967
View File
@@ -0,0 +1,967 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { AstNode, Properties } from '../syntax-tree.js';
import type { SourceRegion, TraceRegion, TraceSourceSpec } from './generator-tracing.js';
export declare const EOL: string;
/**
* Common type of expected results of functions contributing to code generation.
* Includes `undefined` for allowing contributing functions to explicitly contribute
* nothing, if required, in contrast to contributing empty strings,
* which facilitates better formatting of the desired output, for example.
*/
export type Generated = GeneratorNode | string | undefined;
export type GeneratorNode = CompositeGeneratorNode | IndentNode | NewLineNode;
export interface IndentConfig {
indentedChildren?: Generated[] | ((indented: IndentNode) => void);
indentation?: string | number;
indentEmptyLines?: boolean;
indentImmediately?: boolean;
}
export declare function isGeneratorNode(node: unknown): node is CompositeGeneratorNode | IndentNode | NewLineNode;
export declare function isNewLineNode(node: unknown): node is NewLineNode;
/**
* Converts instances of {@link GeneratorNode} into a `string`, defaults to {@link String String(...)} for any other `input`.
*
* @param defaultIndentation the indentation to be applied if no explicit indentation is configured
* for particular {@link IndentNode IndentNodes}, either a `string` or a `number` of repeated single spaces,
* defaults to 4 single spaces, see {@link processGeneratorNode} -> `Context`.
*
* @returns the plain `string` represented by the given input.
*/
export declare function toString(input: unknown, defaultIndentation?: string | number): string;
/**
* Converts instances of {@link GeneratorNode} into `text` accompanied by a corresponding `trace`.
*
* @param defaultIndentation the indentation to be applied if no explicit indentation is configured
* for particular {@link IndentNode IndentNodes}, either a `string` or a `number` of repeated single spaces,
* defaults to 4 single spaces, see {@link processGeneratorNode} -> `Context`.
*
* @returns an object of type `{ text: string, trace: TraceRegion }` containing the desired `text` and `trace` data
*/
export declare function toStringAndTrace(input: GeneratorNode, defaultIndentation?: string | number): {
text: string;
trace: TraceRegion;
};
/**
* Implementation of {@link GeneratorNode} serving as container for `string` segments, {@link NewLineNode newline indicators},
* and further {@link CompositeGeneratorNode CompositeGeneratorNodes}, esp. {@link IndentNode IndentNodes}.
*
* See usage examples in the `append...` methods' documentations for details.
*/
export declare class CompositeGeneratorNode {
readonly contents: Array<(GeneratorNode | string)>;
tracedSource?: TraceSourceSpec | SourceRegion | SourceRegion[];
/**
* Constructor.
*
* @param content a var arg mixture of `strings` and {@link GeneratorNode GeneratorNodes}
* describing the initial content of this {@link CompositeGeneratorNode}
*
* @example
* new CompositeGeneratorNode(
* 'Hello World!', NL
* );
*/
constructor(...content: Generated[]);
isEmpty(): boolean;
/**
* Adds tracing information in form of `{astNode, property?, index: undefined}` to `this` generator node.
* Overwrites existing trace data, if set previously.
*
* The given data are kept as they are, the actual resolution of text positions within the DSL text
* is done at the final processing of `this` node as part of {@link toStringAndTrace()}.
*
* @param astNode the AstNode corresponding to `this` node's content
*
* @param property the value property name (string) corresponding to `this` node's content,
* e.g. if this node's content corresponds to some `string` or `number` property; is optional
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*/
trace<T extends AstNode>(astNode: T, property?: Properties<T>): this;
/**
* Adds tracing information in form of `{astNode, property, index}` to `this` generator node.
* Overwrites existing trace data, if set previously.
*
* The given data are kept as they are, the actual resolution of text positions within the DSL text
* is done at the final processing of `this` node as part of {@link toStringAndTrace()}.
*
* @param astNode the AstNode corresponding to `this` node's content
*
* @param property the value property name (string) corresponding to `this` node's content,
* e.g. if this node's content corresponds to some `string` or `number` property
*
* @param index the index of the value within a list property corresponding to `this` node's content,
* if the property contains a list of elements, is ignored otherwise
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*/
trace<T extends AstNode>(astNode: T, property: Properties<T>, index: number | undefined): this;
/**
* Adds tracing information in form of concrete coordinates to `this` generator node. Complete coordinates
* are provided by the {@link AstNode AstNodes}' corresponding {@link AstNode.$cstNode AstNode.$cstNodes}.
* Overwrites existing trace data, if set previously.
*
* @param sourceRegion a text region within some file in form of concrete coordinates,
* if `undefined` no tracing will happen
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*/
trace(sourceRegion: SourceRegion | undefined): this;
/**
* Adds tracing information in form of a list of concrete coordinates to `this` generator node.
* Overwrites existing trace data, if set previously.
*
* The list of regions in `sourceRegion` is reduced to the smallest encompassing region
* of all the contained source regions.
*
* @param sourceRegion a list of text regions within some file in form of concrete coordinates,
* if empty no tracing will happen
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*/
trace(sourceRegion: SourceRegion[]): this;
/**
* Appends `strings` and instances of {@link GeneratorNode} to `this` generator node.
*
* @param content a var arg mixture of `strings`, {@link GeneratorNode GeneratorNodes}, or single param
* functions that are immediately called with `this` node as argument, and which may append elements themselves.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* 'Hello', ' ', `${name}!`, NL, someOtherNode, 'NL', node => generateContent(node)
* ).append(
* 'The end!'
* );
*/
append(...content: Array<Generated | ((node: this) => void)>): this;
/**
* Prepends `strings` and instances of {@link GeneratorNode} to the content of `this` generator node.
*
* @param content a var arg mixture of `strings` or {@link GeneratorNode GeneratorNodes}.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* generateSomeContent()?.prepend(
* 'Some preamble text:', NL
* ).append(
* 'Some postamble text:', NL
* );
*/
prepend(...content: Generated[]): this;
/**
* Appends `strings` and instances of {@link GeneratorNode} to `this` generator node, if `condition` is equal to `true`.
*
* If `condition` is satisfied this method delegates to {@link append}, otherwise it returns just `this`.
*
* @param condition a boolean value indicating whether to append the elements of `args` to `this`.
*
* @param content a var arg mixture of `strings`, {@link GeneratorNode GeneratorNodes}, or single param
* functions that are immediately called with `this` node as argument, and which may append elements themselves.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* 'Hello World!'
* ).appendNewLine().appendIf(
* entity !== undefined, `Hello ${entity?.name}!`
* ).appendNewLineIfNotEmpty();
*/
appendIf(condition: boolean, ...content: Array<Generated | ((node: CompositeGeneratorNode) => void)>): this;
/**
* Prepends `strings` and instances of {@link GeneratorNode} to the content of `this` generator node, if `condition` is equal to `true`.
*
* If `condition` is satisfied this method delegates to {@link prepend}, otherwise it returns just `this`.
*
* @param condition a boolean value indicating whether to prepend the elements of `args` to the content of `this`.
*
* @param content a var arg mixture of `strings` or {@link GeneratorNode GeneratorNodes}.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* generateSomeContent()?.prependIf(
* generatePreamble === true,
* 'Some preamble', NL
* ).appendIf(
* generatePostamble === true,
* 'Some postamble', NL
* );
*/
prependIf(condition: boolean, ...content: Generated[]): this;
/**
* Appends a strict {@link NewLineNode} to `this` node.
* Strict {@link NewLineNode}s yield mandatory linebreaks in the derived generated text.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* 'Hello World!'
* ).appendNewLine();
*/
appendNewLine(): this;
/**
* Appends a strict {@link NewLineNode} to `this` node, if `condition` is equal to `true`.
* Strict {@link NewLineNode}s yield mandatory linebreaks in the derived generated text.
*
* @param condition a boolean value indicating whether to append a {@link NewLineNode} to `this`.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* 'Hello World!'
* ).appendNewLineIf(entity !== undefined).appendIf(
* entity !== undefined, `Hello ${entity?.name}!`
* )
*/
appendNewLineIf(condition: boolean): this;
/**
* Appends a soft {@link NewLineNode} to `this` node.
* Soft {@link NewLineNode}s yield linebreaks in the derived generated text only if the preceding line is non-empty,
* i.e. there are non-whitespace characters added to the generated text since the last linebreak.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendIf(
* entity !== undefined, `Hello ${entity?.name}!`
* ).appendNewLineIfNotEmpty();
*/
appendNewLineIfNotEmpty(): this;
/**
* Appends a soft {@link NewLineNode} to `this` node, if `condition` is equal to `true`.
* Soft {@link NewLineNode}s yield linebreaks in the derived generated text only if the preceding line is non-empty,
* i.e. there are non-whitespace characters added to the generated text since the last linebreak.
*
* @param condition a boolean value indicating whether to append a {@link NewLineNode} to `this`.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* entity.label ?? ''
* ).appendNewLineIfNotEmptyIf(entity.description !== undefined).append(
* entity.description
* )
*/
appendNewLineIfNotEmptyIf(condition: boolean): this;
/**
* Convenience method for appending content in form of a template to `this` generation node.
*
* See {@link expandToNode} for details.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine()
*/
appendTemplate(staticParts: TemplateStringsArray, ...substitutions: unknown[]): this;
/**
* Convenience method for appending content in form of a template to `this` generator node, if `condition` is equal to `true`.
*
* This method returns a tag function that takes the desired template and does the processing.
*
* If `condition` is satisfied the tagged template delegates to {@link appendTemplate}, otherwise it returns just `this`.
*
* See {@link expandToNode} for details.
*
* @param condition a boolean value indicating whether to append the template content to `this`.
*
* @returns a tag function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTemplateIf(entity !== undefined)
* `Hello ${entity?.name}!`
* .appendNewLineIfNotEmpty()
*/
appendTemplateIf(condition: boolean): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => this;
/**
* Adds an area of indented text output.
* The content to be indented can be provided as an array consisting of strings and/or generation nodes
* (undefined is permitted), or via a callback offering the `indentingNode` to which the content shall be appended.
* Alternatively, an object satisfying {@link IndentConfig} can be provided taking the children as Array or via
* a callback as described previously via the `indentedChildren` property.
*
* The remaining properties of {@link IndentConfig} have the following effects:
* - `indentation`: a specific indentation length or string, defaults to the global indentation setting if omitted, see {@link toString},
* - `indentEmptyLines`: apply indentation to empty lines, defaults to `false`
* - `indentImmediately`: apply the indentation immediately starting at the first line, defaults to `true`, might be set to `false`
* if preceding content is not terminated by any `newline`. If `false` the indentation is inserted only after child `newline` nodes
* followed by further content.
*
* @param childrenOrConfig an {@link Array} or callback contributing the children, or a config object satisfying {@link IndentConfig} alternatively.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* '{'
* ).indent(indentingNode =>
* indentingNode.append(
* 'name:', name, ','
* ).appendNewLine().appendIf(description !== undefined,
* 'description:', description
* ).appendNewLineIfNotEmpty()
* ).append(
* '}'
* );
*/
indent(childrenOrConfig?: Generated[] | ((indented: IndentNode) => void) | IndentConfig): this;
/**
* Convenience method for appending content to `this` generator node including tracing information
* in form of `{astNode, property?, index: undefined}`.
*
* This method returns a helper function that takes the desired `content` and does the processing.
* The returned function delegates to {@link append}, with the provided `content` being
* wrapped by an additional {@link CompositeGeneratorNode} configured with the tracing information.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`, is optional
*
* @returns a function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().append('Hello ').appendTraced(entity, 'name')(entity.name)
* .appendNewLineIfNotEmpty()
*/
appendTraced<T extends AstNode>(astNode: T, property?: Properties<T>): (...content: Array<Generated | ((node: CompositeGeneratorNode) => void)>) => this;
/**
* Convenience method for appending content to `this` generator node including tracing information
* in form of `{astNode, property, index}`.
*
* This method returns a helper function that takes the desired `content` and does the processing.
* The returned function delegates to {@link append}, with the provided `content` being
* wrapped by an additional {@link CompositeGeneratorNode} configured with the tracing information.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`
*
* @param index the index of the value within a list property corresponding to the appended content,
* if the property contains a list of elements, is ignored otherwise
*
* @returns a function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().append('Hello ').appendTraced(entity, 'name')(entity.name)
* .appendNewLineIfNotEmpty()
*/
appendTraced<T extends AstNode>(astNode: T, property: Properties<T>, index: number | undefined): (...content: Array<Generated | ((node: CompositeGeneratorNode) => void)>) => this;
/**
* Convenience method for appending content to `this` generator node including tracing information
* in form of concrete coordinates.
*
* This method returns a helper function that takes the desired `content` and does the processing.
* The returned function delegates to {@link append}, with the provided `content` being
* wrapped by an additional {@link CompositeGeneratorNode} configured with the tracing information.
*
* @param sourceRegion a text region within some file in form of concrete coordinates,
* if `undefined` no tracing will happen
*
* @returns a function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().append('Hello ').appendTraced(findNodeForProperty(entity.$cstNode, 'name'))(entity.name)
* .appendNewLineIfNotEmpty()
*/
appendTraced(sourceRegion: SourceRegion | undefined): (...content: Array<Generated | ((node: CompositeGeneratorNode) => void)>) => this;
/**
* Convenience method for appending content to `this` generator node including tracing information
* in form of a list of concrete coordinates.
*
* This method returns a helper function that takes the desired `content` and does the processing.
* The returned function delegates to {@link append}, with the provided `content` being
* wrapped by an additional {@link CompositeGeneratorNode} configured with the tracing information.
*
* The list of regions in `sourceRegions` will later be reduced to the smallest encompassing region
* of all the contained source regions.
*
* @param sourceRegions a list of text regions within some file in form of concrete coordinates,
* if empty no tracing will happen
*
* @returns a function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().append('Hello ').appendTraced([ findNodeForProperty(entity.$cstNode, 'name') ])(entity.name)
* .appendNewLineIfNotEmpty()
*/
appendTraced(sourceRegions: SourceRegion[]): (...content: Array<Generated | ((node: CompositeGeneratorNode) => void)>) => this;
/**
* Convenience method for appending content to `this` generator node including tracing information
* in form of `{astNode, property?, index: undefined}`, if `condition` is equal to `true`.
*
* This method returns a tag function that takes the desired template and does the processing.
*
* If `condition` is satisfied the returned function delegates to {@link appendTraced}, otherwise it returns just `this`.
*
* @param condition a boolean value indicating whether to append the template content to `this`.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`, is optional
*
* @returns a function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendIf(entity !== undefined, 'Hello ').appendTracedIf(entity !== undefined, entity, 'name')(entity?.name)
* .appendNewLineIfNotEmpty()
*/
appendTracedIf<T extends AstNode>(condition: boolean, astNode: T, property?: Properties<T>): (...content: Array<Generated | ((node: CompositeGeneratorNode) => void)>) => this;
/**
* Convenience method for appending content to `this` generator node including tracing information
* in form of `{astNode, property, index}`, if `condition` is equal to `true`.
*
* This method returns a tag function that takes the desired template and does the processing.
*
* If `condition` is satisfied the returned function delegates to {@link appendTraced}, otherwise it returns just `this`.
*
* @param condition a boolean value indicating whether to append the template content to `this`.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`
*
* @param index the index of the value within a list property corresponding to the appended content,
* if the property contains a list of elements, is ignored otherwise
*
* @returns a function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendIf(entity !== undefined, 'Hello ').appendTracedIf(entity !== undefined, entity, 'name')(entity?.name)
* .appendNewLineIfNotEmpty()
*/
appendTracedIf<T extends AstNode>(condition: boolean, astNode: T, property: Properties<T>, index: number | undefined): (...content: Array<Generated | ((node: CompositeGeneratorNode) => void)>) => this;
/**
* Convenience method for appending content to `this` generator node including tracing information
* in form of concrete coordinates, if `condition` is equal to `true`.
*
* This method returns a tag function that takes the desired template and does the processing.
*
* If `condition` is satisfied the returned function delegates to {@link appendTraced}, otherwise it returns just `this`.
*
* If `sourceRegion` is a function supplying the corresponding region, it's only called if `condition` is satisfied.
*
* @param condition a boolean value indicating whether to append the template content to `this`.
*
* @param sourceRegion a text region within some file in form of concrete coordinates,
* if `undefined` no tracing will happen
*
* @returns a function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendIf(entity !== undefined, 'Hello ').appendTracedIf(entity !== undefined, () => findNodeForProperty(entity.$cstNode, 'name'))(entity?.name)
* .appendNewLineIfNotEmpty()
*/
appendTracedIf(condition: boolean, sourceRegion: SourceRegion | undefined | (() => SourceRegion | undefined)): (...content: Array<Generated | ((node: CompositeGeneratorNode) => void)>) => this;
/**
* Convenience method for appending content to `this` generator node including tracing information
* in form of a list of concrete coordinates, if `condition` is equal to `true`.
*
* This method returns a tag function that takes the desired template and does the processing.
*
* If `condition` is satisfied the returned function delegates to {@link appendTraced}, otherwise it returns just `this`.
*
* The list of regions in `sourceRegions` will later be reduced to the smallest encompassing region
* of all the contained source regions.
* If `sourceRegions` is a function supplying the corresponding regions, it's only called if `condition` is satisfied.
*
* @param condition a boolean value indicating whether to append the template content to `this`.
*
* @param sourceRegions a list of text regions within some file in form of concrete coordinates,
* if empty no tracing will happen
*
* @returns a function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendIf(entity !== undefined, 'Hello ').appendTracedIf(entity !== undefined, () => [ findNodeForProperty(entity.$cstNode, 'name') ])(entity?.name)
* .appendNewLineIfNotEmpty()
*/
appendTracedIf(condition: boolean, sourceRegions: SourceRegion[] | (() => SourceRegion[])): (...content: Array<Generated | ((node: CompositeGeneratorNode) => void)>) => this;
/**
* Convenience method for appending content in form of a template to `this` generator node including tracing
* information in form of `{astNode, property?, index: undefined}`.
*
* This method returns a tag function that takes the desired template and does the processing by delegating to
* {@link expandTracedToNode}.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`, is optional
*
* @returns a tag function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate(entity, 'name')
* `Hello ${entity?.name}!`
* .appendNewLineIfNotEmpty()
*/
appendTracedTemplate<T extends AstNode>(astNode: T, property?: Properties<T>): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => this;
/**
* Convenience method for appending content in form of a template to `this` generator node including tracing
* information in form of `{astNode, property, index}`.
*
* This method returns a tag function that takes the desired template and does the processing by delegating to
* {@link expandTracedToNode}.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`
*
* @param index the index of the value within a list property corresponding to the appended content,
* if the property contains a list of elements, is ignored otherwise, is optinal
*
* @returns a tag function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate(entity, 'name')
* `Hello ${entity?.name}!`
* .appendNewLineIfNotEmpty()
*/
appendTracedTemplate<T extends AstNode>(astNode: T, property: Properties<T>, index: number | undefined): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => this;
/**
* Convenience method for appending content in form of a template to `this` generator node including tracing
* information in form of concrete coordinates.
*
* This method returns a tag function that takes the desired template and does the processing by delegating to
* {@link expandTracedToNode}.
*
* @param sourceRegion a text region within some file in form of concrete coordinates,
* if `undefined` no tracing will happen
*
* @returns a tag function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate(findNodeForProperty(entity.$cstNode, 'name'))
* `Hello ${entity?.name}!`
* .appendNewLineIfNotEmpty()
*/
appendTracedTemplate(sourceRegion: SourceRegion | undefined): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => this;
/**
* Convenience method for appending content in form of a template to `this` generator node including tracing
* information in form of concrete coordinates.
*
* This method returns a tag function that takes the desired template and does the processing by delegating to
* {@link expandTracedToNode}.
*
* The list of regions in `sourceRegions` will later be reduced to the smallest encompassing region
* of all the contained source regions.
*
* @param sourceRegions a list of text regions within some file in form of concrete coordinates,
* if empty no tracing will happen
*
* @returns a tag function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate( findNodesForProperty(entity.$cstNode, 'name'))
* `Hello ${entity?.name}!`
* .appendNewLineIfNotEmpty()
*/
appendTracedTemplate(sourceRegions: SourceRegion[]): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => this;
/**
* Convenience method for appending content in form of a template to `this` generator node including tracing information
* in form of `{astNode, property?, index: undefined}`, if `condition` is equal to `true`.
*
* This method returns a tag function that takes the desired template and does the processing.
*
* If `condition` is satisfied the tagged template delegates to {@link appendTracedTemplate}, otherwise it returns just `this`.
* See also {@link expandTracedToNode} for details.
*
* @param condition a boolean value indicating whether to append the template content to `this`.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`, is optional
*
* @returns a tag function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplateIf(entity?.name !== undefined, entity)
* `Hello ${entity?.name}!`
* .appendNewLineIfNotEmpty()
*/
appendTracedTemplateIf<T extends AstNode>(condition: boolean, astNode: T, property?: Properties<T>): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => this;
/**
* Convenience method for appending content in form of a template to `this` generator node including tracing information
* in form of `{astNode, property, index}`, if `condition` is equal to `true`.
*
* This method returns a tag function that takes the desired template and does the processing.
*
* If `condition` is satisfied the tagged template delegates to {@link appendTracedTemplate}, otherwise it returns just `this`.
* See also {@link expandTracedToNode} for details.
*
* @param condition a boolean value indicating whether to append the template content to `this`.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`
*
* @param index the index of the value within a list property corresponding to the appended content,
* if the property contains a list of elements, is ignored otherwise
*
* @returns a tag function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplateIf(entity?.name !== undefined, entity)
* `Hello ${entity?.name}!`
* .appendNewLineIfNotEmpty()
*/
appendTracedTemplateIf<T extends AstNode>(condition: boolean, astNode: T, property: Properties<T>, index: number | undefined): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => this;
/**
* Convenience method for appending content in form of a template to `this` generator node including tracing information
* in form of concrete coordinates, if `condition` is equal to `true`.
*
* This method returns a tag function that takes the desired template and does the processing.
*
* If `condition` is satisfied the tagged template delegates to {@link appendTracedTemplate}, otherwise it returns just `this`.
* See also {@link expandTracedToNode} for details.
*
* If `sourceRegion` is a function supplying the corresponding region, it's only called if `condition` is satisfied.
*
* @param condition a boolean value indicating whether to append the template content to `this`.
*
* @param sourceRegion a text region within some file in form of concrete coordinates,
* if `undefined` no tracing will happen
*
* @returns a tag function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplateIf(entity?.name !== undefined, entity.$cstNode)
* `Hello ${entity?.name}!`
* .appendNewLineIfNotEmpty()
*/
appendTracedTemplateIf(condition: boolean, sourceRegion: SourceRegion | undefined | (() => SourceRegion | undefined)): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => this;
/**
* Convenience method for appending content in form of a template to `this` generator node including tracing information
* in form of a list of concrete coordinates, if `condition` is equal to `true`.
*
* This method returns a tag function that takes the desired template and does the processing.
*
* If `condition` is satisfied the tagged template delegates to {@link appendTracedTemplate}, otherwise it returns just `this`.
* See also {@link expandTracedToNode} for details.
*
* The list of regions in `sourceRegions` will later be reduced to the smallest encompassing region
* of all the contained source regions.
* If `sourceRegions` is a function supplying the corresponding regions, it's only called if `condition` is satisfied.
*
* @param condition a boolean value indicating whether to append the template content to `this`.
*
* @param sourceRegions a list of text regions within some file in form of concrete coordinates,
* if empty no tracing will happen
*
* @returns a tag function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplateIf(entity?.name !== undefined, () => [ findNodeForProperty(entity.$cstNode, 'name')! ])
* `Hello ${entity?.name}!`
* .appendNewLineIfNotEmpty()
*/
appendTracedTemplateIf(condition: boolean, sourceRegions: SourceRegion[] | (() => SourceRegion[])): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => this;
}
/**
* Convenience function for attaching tracing information to content of type `Generated`,
* in form of `{astNode, property?, index: undefined}`.
*
* This method returns a helper function that takes the desired `content` and does the processing.
* The returned function will create and return a new {@link CompositeGeneratorNode} being initialized
* with the given tracing information and add some `content`, if provided.
*
* Exception: if `content` is already a {@link CompositeGeneratorNode} containing no tracing information,
* that node is enriched with the given tracing information and returned, and no wrapping node is created.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`, is optional
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate(entity)
* `Hello ${ traceToNode(entity, 'name')(entity.name) }`
* .appendNewLineIfNotEmpty()
*/
export declare function traceToNode<T extends AstNode>(astNode: T, property?: Properties<T>): (content?: Generated | ((node: CompositeGeneratorNode) => void)) => CompositeGeneratorNode;
/**
* Convenience function for attaching tracing information to content of type `Generated`,
* in form of `{astNode, property, index}`.
*
* This method returns a helper function that takes the desired `content` and does the processing.
* The returned function will create and return a new {@link CompositeGeneratorNode} being initialized
* with the given tracing information and add some `content`, if provided.
*
* Exception: if `content` is already a {@link CompositeGeneratorNode} containing no tracing information,
* that node is enriched with the given tracing information and returned, and no wrapping node is created.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`
*
* @param index the index of the value within a list property corresponding to the appended content,
* if the property contains a list of elements, is ignored otherwise
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate(entity)
* `Hello ${ traceToNode(entity, 'name')(entity.name) }`
* .appendNewLineIfNotEmpty()
*/
export declare function traceToNode<T extends AstNode>(astNode: T, property: Properties<T>, index: number | undefined): (content?: Generated | ((node: CompositeGeneratorNode) => void)) => CompositeGeneratorNode;
/**
* Convenience function for attaching tracing information to content of type `Generated`,
* in form of concrete coordinates.
*
* This method returns a helper function that takes the desired `content` and does the processing.
* The returned function will create and return a new {@link CompositeGeneratorNode} being initialized
* with the given tracing information and add some `content`, if provided.
*
* Exception: if `content` is already a {@link CompositeGeneratorNode} containing no tracing information,
* that node is enriched with the given tracing information and returned, and no wrapping node is created.
*
* @param sourceRegion a text region within some file in form of concrete coordinates,
* if `undefined` no tracing will happen
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate(entity.$cstNode)
* `Hello ${ traceToNode(findNodeForProperty(entity.$cstNode, 'name'))(entity.name) }`
* .appendNewLineIfNotEmpty()
*/
export declare function traceToNode(sourceRegion: SourceRegion | undefined): (content?: Generated | ((node: CompositeGeneratorNode) => void)) => CompositeGeneratorNode;
/**
* Convenience function for attaching tracing information to content of type `Generated`,
* in form of a list of concrete coordinates.
*
* This method returns a helper function that takes the desired `content` and does the processing.
* The returned function will create and return a new {@link CompositeGeneratorNode} being initialized
* with the given tracing information and add some `content`, if provided.
*
* Exception: if `content` is already a {@link CompositeGeneratorNode} containing no tracing information,
* that node is enriched with the given tracing information and returned, and no wrapping node is created.
*
* The list of regions in `sourceRegions` will later be reduced to the smallest encompassing region
* of all the contained source regions.
*
* @param sourceRegions a list of text region within some file in form of concrete coordinates,
* if empty no tracing will happen
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate(entity.$cstNode)
* `Hello ${ traceToNode(findNodesForProperty(entity.$cstNode, 'name'))(entity.name) }`
* .appendNewLineIfNotEmpty()
*/
export declare function traceToNode(sourceRegions: SourceRegion[]): (content?: Generated | ((node: CompositeGeneratorNode) => void)) => CompositeGeneratorNode;
/**
* Convenience function for attaching tracing information to content of type `Generated`,
* in form of `{astNode, property?, index: undefined}`, if `condition` is equal to `true`.
*
* If `condition` is satisfied, this method returns a helper function that takes the desired
* `content` and does the processing. The returned function will create and return a new
* {@link CompositeGeneratorNode} being initialized with the given tracing information and
* add some `content`, if provided. Otherwise, the returned function just returns `undefined`.
*
* Exception: if `content` is already a {@link CompositeGeneratorNode} containing no tracing information,
* that node is enriched with the given tracing information and returned, and no wrapping node is created.
*
* @param condition a boolean value indicating whether to apply the provided tracing information to the desired content.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`, is optional
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate(entity)
* `Hello ${ traceToNodeIf(!!entity.name, entity, 'name')(entity.name) }`
* .appendNewLineIfNotEmpty()
*/
export declare function traceToNodeIf<T extends AstNode>(condition: boolean, astNode: T, property?: Properties<T>): (content?: Generated | ((node: CompositeGeneratorNode) => void)) => CompositeGeneratorNode | undefined;
/**
* Convenience function for attaching tracing information to content of type `Generated`,
* in form of `{astNode, property, index}`, if `condition` is equal to `true`.
*
* If `condition` is satisfied, this method returns a helper function that takes the desired
* `content` and does the processing. The returned function will create and return a new
* {@link CompositeGeneratorNode} being initialized with the given tracing information and
* add some `content`, if provided. Otherwise, the returned function just returns `undefined`.
*
* Exception: if `content` is already a {@link CompositeGeneratorNode} containing no tracing information,
* that node is enriched with the given tracing information and returned, and no wrapping node is created.
*
* @param condition a boolean value indicating whether to apply the provided tracing information to the desired content.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`
*
* @param index the index of the value within a list property corresponding to the appended content,
* if the property contains a list of elements, is ignored otherwise
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate(entity)
* `Hello ${ traceToNodeIf(!!entity.name, entity, 'name')(entity.name) }`
* .appendNewLineIfNotEmpty()
*/
export declare function traceToNodeIf<T extends AstNode>(condition: boolean, astNode: T, property: Properties<T>, index: number | undefined): (content?: Generated | ((node: CompositeGeneratorNode) => void)) => CompositeGeneratorNode | undefined;
/**
* Convenience function for attaching tracing information to content of type `Generated`,
* in form of concrete coordinates, if `condition` is equal to `true`.
*
* If `condition` is satisfied, this method returns a helper function that takes the desired
* `content` and does the processing. The returned function will create and return a new
* {@link CompositeGeneratorNode} being initialized with the given tracing information and
* add some `content`, if provided. Otherwise, the returned function just returns `undefined`.
*
* Exception: if `content` is already a {@link CompositeGeneratorNode} containing no tracing information,
* that node is enriched with the given tracing information and returned, and no wrapping node is created.
*
* If `sourceRegions` is a function supplying the corresponding regions, it's only called if `condition` is satisfied.
*
* @param condition a boolean value indicating whether to apply the provided tracing information to the desired content.
*
* @param sourceRegion a text region within some file in form of concrete coordinates,
* if `undefined` no tracing will happen
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate(entity.$cstNode)
* `Hello ${ traceToNodeIf(!!entity.name, () => findNodeForProperty(entity.$cstNode, 'name'))(entity.name) }`
* .appendNewLineIfNotEmpty()
*/
export declare function traceToNodeIf(condition: boolean, sourceRegion: SourceRegion | undefined | (() => SourceRegion | undefined)): (content?: Generated | ((node: CompositeGeneratorNode) => void)) => CompositeGeneratorNode | undefined;
/**
* Convenience function for attaching tracing information to content of type `Generated`,
* in form of a list of concrete coordinates, if `condition` is equal to `true`.
*
* If `condition` is satisfied, this method returns a helper function that takes the desired
* `content` and does the processing. The returned function will create and return a new
* {@link CompositeGeneratorNode} being initialized with the given tracing information and
* add some `content`, if provided. Otherwise, the returned function just returns `undefined`.
*
* Exception: if `content` is already a {@link CompositeGeneratorNode} containing no tracing information,
* that node is enriched with the given tracing information and returned, and no wrapping node is created.
*
* The list of regions in `sourceRegions` will later be reduced to the smallest encompassing region
* of all the contained source regions.
* If `sourceRegions` is a function supplying the corresponding regions, it's only called if `condition` is satisfied.
*
* @param condition a boolean value indicating whether to apply the provided tracing information to the desired content.
*
* @param sourceRegions a list of text region within some file in form of concrete coordinates,
* if empty no tracing will happen
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTracedTemplate(entity.$cstNode)
* `Hello ${ traceToNodeIf(!!entity.name, () => findNodesForProperty(entity.$cstNode, 'name'))(entity.name) }`
* .appendNewLineIfNotEmpty()
*/
export declare function traceToNodeIf(condition: boolean, sourceRegions: SourceRegion[] | (() => SourceRegion[])): (content?: Generated | ((node: CompositeGeneratorNode) => void)) => CompositeGeneratorNode | undefined;
/**
* Implementation of @{link GeneratorNode} denoting areas within the desired generated text of common increased indentation.
*/
export declare class IndentNode extends CompositeGeneratorNode {
readonly indentation?: string;
readonly indentImmediately: boolean;
readonly indentEmptyLines: boolean;
constructor(indentation?: string | number, indentImmediately?: boolean, indentEmptyLines?: boolean);
}
export declare namespace NewLineNode {
/**
* Integer range type allowing to specify 1 to 6 consecutive line breaks, i.e. up to 5 empty lines with a single `NewLineNode`.
*/
type NoOfLineBreaks = 1 | 2 | 3 | 4 | 5 | 6;
}
/**
* Implementation of @{link GeneratorNode} denoting linebreaks in the desired generated text.
*/
export declare class NewLineNode {
readonly lineDelimiter: string;
readonly ifNotEmpty: boolean;
readonly count: NewLineNode.NoOfLineBreaks;
constructor(lineDelimiter?: string, ifNotEmpty?: boolean, count?: NewLineNode.NoOfLineBreaks);
}
export declare const NL: NewLineNode;
export declare const NLEmpty: NewLineNode;
//# sourceMappingURL=generator-node.d.ts.map
File diff suppressed because one or more lines are too long
+386
View File
@@ -0,0 +1,386 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { isAstNode } from '../syntax-tree.js';
import { processGeneratorNode } from './node-processor.js';
import { expandToNode, expandTracedToNode } from './template-node.js';
export const EOL = (typeof process === 'undefined') ? '\n' : (process.platform === 'win32') ? '\r\n' : '\n';
export function isGeneratorNode(node) {
return node instanceof CompositeGeneratorNode
|| node instanceof IndentNode
|| node instanceof NewLineNode;
}
export function isNewLineNode(node) {
return node instanceof NewLineNode;
}
/**
* Converts instances of {@link GeneratorNode} into a `string`, defaults to {@link String String(...)} for any other `input`.
*
* @param defaultIndentation the indentation to be applied if no explicit indentation is configured
* for particular {@link IndentNode IndentNodes}, either a `string` or a `number` of repeated single spaces,
* defaults to 4 single spaces, see {@link processGeneratorNode} -> `Context`.
*
* @returns the plain `string` represented by the given input.
*/
export function toString(input, defaultIndentation) {
if (isGeneratorNode(input))
return processGeneratorNode(input, defaultIndentation).text;
else
return String(input);
}
/**
* Converts instances of {@link GeneratorNode} into `text` accompanied by a corresponding `trace`.
*
* @param defaultIndentation the indentation to be applied if no explicit indentation is configured
* for particular {@link IndentNode IndentNodes}, either a `string` or a `number` of repeated single spaces,
* defaults to 4 single spaces, see {@link processGeneratorNode} -> `Context`.
*
* @returns an object of type `{ text: string, trace: TraceRegion }` containing the desired `text` and `trace` data
*/
export function toStringAndTrace(input, defaultIndentation) {
return processGeneratorNode(input, defaultIndentation);
}
/**
* Implementation of {@link GeneratorNode} serving as container for `string` segments, {@link NewLineNode newline indicators},
* and further {@link CompositeGeneratorNode CompositeGeneratorNodes}, esp. {@link IndentNode IndentNodes}.
*
* See usage examples in the `append...` methods' documentations for details.
*/
export class CompositeGeneratorNode {
/**
* Constructor.
*
* @param content a var arg mixture of `strings` and {@link GeneratorNode GeneratorNodes}
* describing the initial content of this {@link CompositeGeneratorNode}
*
* @example
* new CompositeGeneratorNode(
* 'Hello World!', NL
* );
*/
constructor(...content) {
this.contents = [];
this.append(...content);
}
isEmpty() {
return this.contents.length === 0;
}
trace(source, property, index) {
if (isAstNode(source)) {
this.tracedSource = { astNode: source, property, index };
if (this.tracedSource.property === undefined && this.tracedSource.index !== undefined && this.tracedSource.index > -1) {
throw new Error("Generation support: 'property' argument must not be 'undefined' if a non-negative value is assigned to 'index' in 'CompositeGeneratorNode.trace(...)'.");
}
}
else {
this.tracedSource = source;
}
return this;
}
/**
* Appends `strings` and instances of {@link GeneratorNode} to `this` generator node.
*
* @param content a var arg mixture of `strings`, {@link GeneratorNode GeneratorNodes}, or single param
* functions that are immediately called with `this` node as argument, and which may append elements themselves.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* 'Hello', ' ', `${name}!`, NL, someOtherNode, 'NL', node => generateContent(node)
* ).append(
* 'The end!'
* );
*/
append(...content) {
for (const arg of content) {
if (typeof arg === 'function') {
arg(this);
}
else if (arg) {
this.contents.push(arg);
}
}
return this;
}
/**
* Prepends `strings` and instances of {@link GeneratorNode} to the content of `this` generator node.
*
* @param content a var arg mixture of `strings` or {@link GeneratorNode GeneratorNodes}.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* generateSomeContent()?.prepend(
* 'Some preamble text:', NL
* ).append(
* 'Some postamble text:', NL
* );
*/
prepend(...content) {
this.contents.unshift(...content.filter(c => c !== undefined));
return this;
}
/**
* Appends `strings` and instances of {@link GeneratorNode} to `this` generator node, if `condition` is equal to `true`.
*
* If `condition` is satisfied this method delegates to {@link append}, otherwise it returns just `this`.
*
* @param condition a boolean value indicating whether to append the elements of `args` to `this`.
*
* @param content a var arg mixture of `strings`, {@link GeneratorNode GeneratorNodes}, or single param
* functions that are immediately called with `this` node as argument, and which may append elements themselves.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* 'Hello World!'
* ).appendNewLine().appendIf(
* entity !== undefined, `Hello ${entity?.name}!`
* ).appendNewLineIfNotEmpty();
*/
appendIf(condition, ...content) {
return condition ? this.append(...content) : this;
}
/**
* Prepends `strings` and instances of {@link GeneratorNode} to the content of `this` generator node, if `condition` is equal to `true`.
*
* If `condition` is satisfied this method delegates to {@link prepend}, otherwise it returns just `this`.
*
* @param condition a boolean value indicating whether to prepend the elements of `args` to the content of `this`.
*
* @param content a var arg mixture of `strings` or {@link GeneratorNode GeneratorNodes}.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* generateSomeContent()?.prependIf(
* generatePreamble === true,
* 'Some preamble', NL
* ).appendIf(
* generatePostamble === true,
* 'Some postamble', NL
* );
*/
prependIf(condition, ...content) {
return condition ? this.prepend(...content) : this;
}
/**
* Appends a strict {@link NewLineNode} to `this` node.
* Strict {@link NewLineNode}s yield mandatory linebreaks in the derived generated text.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* 'Hello World!'
* ).appendNewLine();
*/
appendNewLine() {
return this.append(NL);
}
/**
* Appends a strict {@link NewLineNode} to `this` node, if `condition` is equal to `true`.
* Strict {@link NewLineNode}s yield mandatory linebreaks in the derived generated text.
*
* @param condition a boolean value indicating whether to append a {@link NewLineNode} to `this`.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* 'Hello World!'
* ).appendNewLineIf(entity !== undefined).appendIf(
* entity !== undefined, `Hello ${entity?.name}!`
* )
*/
appendNewLineIf(condition) {
return condition ? this.append(NL) : this;
}
/**
* Appends a soft {@link NewLineNode} to `this` node.
* Soft {@link NewLineNode}s yield linebreaks in the derived generated text only if the preceding line is non-empty,
* i.e. there are non-whitespace characters added to the generated text since the last linebreak.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendIf(
* entity !== undefined, `Hello ${entity?.name}!`
* ).appendNewLineIfNotEmpty();
*/
appendNewLineIfNotEmpty() {
return this.append(NLEmpty);
}
/**
* Appends a soft {@link NewLineNode} to `this` node, if `condition` is equal to `true`.
* Soft {@link NewLineNode}s yield linebreaks in the derived generated text only if the preceding line is non-empty,
* i.e. there are non-whitespace characters added to the generated text since the last linebreak.
*
* @param condition a boolean value indicating whether to append a {@link NewLineNode} to `this`.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* entity.label ?? ''
* ).appendNewLineIfNotEmptyIf(entity.description !== undefined).append(
* entity.description
* )
*/
appendNewLineIfNotEmptyIf(condition) {
return condition ? this.appendNewLineIfNotEmpty() : this;
}
/**
* Convenience method for appending content in form of a template to `this` generation node.
*
* See {@link expandToNode} for details.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine()
*/
appendTemplate(staticParts, ...substitutions) {
return this.append(expandToNode(staticParts, ...substitutions));
}
/**
* Convenience method for appending content in form of a template to `this` generator node, if `condition` is equal to `true`.
*
* This method returns a tag function that takes the desired template and does the processing.
*
* If `condition` is satisfied the tagged template delegates to {@link appendTemplate}, otherwise it returns just `this`.
*
* See {@link expandToNode} for details.
*
* @param condition a boolean value indicating whether to append the template content to `this`.
*
* @returns a tag function behaving as described above, which in turn returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().appendTemplate
* `Hello World!`
* .appendNewLine().appendTemplateIf(entity !== undefined)
* `Hello ${entity?.name}!`
* .appendNewLineIfNotEmpty()
*/
appendTemplateIf(condition) {
return condition ? (staticParts, ...substitutions) => this.appendTemplate(staticParts, ...substitutions) : () => this;
}
/**
* Adds an area of indented text output.
* The content to be indented can be provided as an array consisting of strings and/or generation nodes
* (undefined is permitted), or via a callback offering the `indentingNode` to which the content shall be appended.
* Alternatively, an object satisfying {@link IndentConfig} can be provided taking the children as Array or via
* a callback as described previously via the `indentedChildren` property.
*
* The remaining properties of {@link IndentConfig} have the following effects:
* - `indentation`: a specific indentation length or string, defaults to the global indentation setting if omitted, see {@link toString},
* - `indentEmptyLines`: apply indentation to empty lines, defaults to `false`
* - `indentImmediately`: apply the indentation immediately starting at the first line, defaults to `true`, might be set to `false`
* if preceding content is not terminated by any `newline`. If `false` the indentation is inserted only after child `newline` nodes
* followed by further content.
*
* @param childrenOrConfig an {@link Array} or callback contributing the children, or a config object satisfying {@link IndentConfig} alternatively.
*
* @returns `this` {@link CompositeGeneratorNode} for convenience.
*
* @example
* new CompositeGeneratorNode().append(
* '{'
* ).indent(indentingNode =>
* indentingNode.append(
* 'name:', name, ','
* ).appendNewLine().appendIf(description !== undefined,
* 'description:', description
* ).appendNewLineIfNotEmpty()
* ).append(
* '}'
* );
*/
indent(childrenOrConfig) {
const { indentedChildren, indentation, indentEmptyLines, indentImmediately } = Array.isArray(childrenOrConfig) || typeof childrenOrConfig === 'function'
? { indentedChildren: childrenOrConfig }
: typeof childrenOrConfig === 'object' ? childrenOrConfig : {};
const node = new IndentNode(indentation, indentImmediately, indentEmptyLines);
this.contents.push(node);
if (Array.isArray(indentedChildren)) {
node.append(...indentedChildren);
}
else if (indentedChildren) {
node.append(indentedChildren);
}
return this;
}
// implementation:
appendTraced(source, property, index) {
return content => {
return this.append(new CompositeGeneratorNode().trace(source, property, index).append(content));
};
}
// implementation:
appendTracedIf(condition, source, property, index) {
return condition ? this.appendTraced((typeof source === 'function' ? source() : source), property, index) : () => this;
}
// implementation:
appendTracedTemplate(source, property, index) {
return (staticParts, ...substitutions) => {
return this.append(expandTracedToNode(source, property, index)(staticParts, ...substitutions));
};
}
// implementation:
appendTracedTemplateIf(condition, source, property, index) {
return condition ? this.appendTracedTemplate((typeof source === 'function' ? source() : source), property, index) : () => this;
}
}
// implementation
export function traceToNode(astNode, property, index) {
return content => {
if (content instanceof CompositeGeneratorNode && content.tracedSource === undefined) {
return content.trace(astNode, property, index);
}
else {
// a `content !== undefined` check is skipped here on purpose in order to let this method always return a result;
// dropping empty generator nodes is considered a post processing optimization.
return new CompositeGeneratorNode().trace(astNode, property, index).append(content);
}
};
}
// implementation
export function traceToNodeIf(condition, source, property, index) {
return condition ? traceToNode((typeof source === 'function' ? source() : source), property, index) : () => undefined;
}
/**
* Implementation of @{link GeneratorNode} denoting areas within the desired generated text of common increased indentation.
*/
export class IndentNode extends CompositeGeneratorNode {
constructor(indentation, indentImmediately = true, indentEmptyLines = false) {
super();
if (typeof (indentation) === 'string') {
this.indentation = indentation;
}
else if (typeof (indentation) === 'number') {
this.indentation = ''.padStart(indentation);
}
this.indentImmediately = indentImmediately;
this.indentEmptyLines = indentEmptyLines;
}
}
/**
* Implementation of @{link GeneratorNode} denoting linebreaks in the desired generated text.
*/
export class NewLineNode {
constructor(lineDelimiter = EOL, ifNotEmpty = false, count = 1) {
this.lineDelimiter = lineDelimiter;
this.ifNotEmpty = ifNotEmpty;
this.count = count;
}
}
export const NL = new NewLineNode();
export const NLEmpty = new NewLineNode(undefined, true);
//# sourceMappingURL=generator-node.js.map
File diff suppressed because one or more lines are too long
+40
View File
@@ -0,0 +1,40 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { Range } from 'vscode-languageserver-textdocument';
import type { AstNode, CstNode } from '../syntax-tree.js';
import type { DocumentSegment } from '../workspace/documents.js';
export interface TraceSourceSpec {
astNode: AstNode;
property?: string;
index?: number;
}
export type SourceRegion = TextRegion | TextRegion2 | DocumentSegmentWithFileURI;
export interface TextRegion {
fileURI?: string;
offset: number;
end: number;
length?: number;
range?: Range;
}
interface TextRegion2 {
fileURI?: string;
offset: number;
length: number;
end?: number;
range?: Range;
}
export interface TraceRegion {
sourceRegion?: TextRegion;
targetRegion: TextRegion;
children?: TraceRegion[];
}
interface DocumentSegmentWithFileURI extends Omit<DocumentSegment, 'range'> {
fileURI?: string;
range?: Range;
}
export declare function getSourceRegion(sourceSpec: TraceSourceSpec | undefined | SourceRegion | SourceRegion[]): DocumentSegmentWithFileURI | CstNode | undefined;
export {};
//# sourceMappingURL=generator-tracing.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"generator-tracing.d.ts","sourceRoot":"","sources":["../../src/generate/generator-tracing.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oCAAoC,CAAC;AAEhE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAkB,MAAM,mBAAmB,CAAC;AAI1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,WAAW,GAAG,0BAA0B,CAAA;AAEhF,MAAM,WAAW,UAAU;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,UAAU,WAAW;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IACxB,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,YAAY,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;CAC5B;AAED,UAAU,0BAA2B,SAAQ,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACjB;AAKD,wBAAgB,eAAe,CAAC,UAAU,EAAE,eAAe,GAAG,SAAS,GAAG,YAAY,GAAG,YAAY,EAAE,GAAG,0BAA0B,GAAG,OAAO,GAAG,SAAS,CA0BzJ"}
+135
View File
@@ -0,0 +1,135 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { getDocument } from '../utils/ast-utils.js';
import { findNodesForProperty } from '../utils/grammar-utils.js';
import { TreeStreamImpl } from '../utils/stream.js';
export function getSourceRegion(sourceSpec) {
if (!sourceSpec) {
return undefined;
}
else if ('astNode' in sourceSpec) {
return getSourceRegionOfAstNode(sourceSpec);
}
else if (Array.isArray(sourceSpec)) {
return sourceSpec.reduce(mergeDocumentSegment, undefined); // apply mergeDocumentSegment for single entry sourcSpec lists, too, thus start with 'undefined' as initial value
}
else {
// some special treatment of cstNodes for revealing the uri of the defining DSL text file
// is currently only done for single cstNode tracings, like "expandTracedToNode(source.$cstNode)`...`",
// is _not done_ for multi node tracings like below, see if case above
// joinTracedToNode( [
// findNodeForKeyword(source.$cstNode, '{')!,
// findNodeForKeyword(source.$cstNode, '}')!
// ] )(source.children, c => c.name)
const sourceRegion = sourceSpec;
const sourceFileURIviaCstNode = isCstNode(sourceRegion)
? getDocumentURIOrUndefined(sourceRegion?.root?.astNode ?? sourceRegion?.astNode) : undefined;
return copyDocumentSegment(sourceRegion, sourceFileURIviaCstNode);
}
}
function isCstNode(segment) {
return typeof segment !== 'undefined' && 'element' in segment && 'text' in segment;
}
function getDocumentURIOrUndefined(astNode) {
try {
return getDocument(astNode).uri.toString();
}
catch (_error) {
return undefined;
}
}
function getSourceRegionOfAstNode(sourceSpec) {
const { astNode, property, index } = sourceSpec ?? {};
const textRegion = astNode?.$cstNode ?? astNode?.$textRegion;
if (astNode === undefined || textRegion === undefined) {
return undefined;
}
else if (property === undefined) {
return copyDocumentSegment(textRegion, getDocumentURI(astNode));
}
else {
const getSingleOrCompoundRegion = (regions) => {
if (index !== undefined && index > -1 && Array.isArray(astNode[property])) {
return index < regions.length ? regions[index] : undefined;
}
else {
return regions.reduce(mergeDocumentSegment, undefined);
}
};
if (textRegion.assignments?.[property]) {
const region = getSingleOrCompoundRegion(textRegion.assignments[property]);
return region && copyDocumentSegment(region, getDocumentURI(astNode));
}
else if (astNode.$cstNode) {
const region = getSingleOrCompoundRegion(findNodesForProperty(astNode.$cstNode, property));
return region && copyDocumentSegment(region, getDocumentURI(astNode));
}
else {
return undefined;
}
}
}
function getDocumentURI(astNode) {
if (astNode.$cstNode) {
return getDocument(astNode)?.uri?.toString();
}
else if (astNode.$textRegion) {
return astNode.$textRegion.documentURI
|| new TreeStreamImpl(astNode, n => n.$container ? [n.$container] : []).find(n => n.$textRegion?.documentURI)?.$textRegion?.documentURI;
}
else {
return undefined;
}
}
function copyDocumentSegment(region, fileURI) {
const result = {
offset: region.offset,
end: region.end ?? region.offset + region.length,
length: region.length ?? region.end - region.offset,
};
if (region.range) {
result.range = region.range;
}
fileURI ?? (fileURI = region.fileURI);
if (fileURI) {
result.fileURI = fileURI;
}
return result;
}
function mergeDocumentSegment(prev, curr) {
if (!prev) {
return curr && copyDocumentSegment(curr);
}
else if (!curr) {
return prev && copyDocumentSegment(prev);
}
const prevEnd = prev.end ?? prev.offset + prev.length;
const currEnd = curr.end ?? curr.offset + curr.length;
const offset = Math.min(prev.offset, curr.offset);
const end = Math.max(prevEnd, currEnd);
const length = end - offset;
const result = {
offset, end, length,
};
if (prev.range && curr.range) {
result.range = {
start: curr.range.start.line < prev.range.start.line
|| curr.range.start.line === prev.range.start.line && curr.range.start.character < prev.range.start.character
? curr.range.start : prev.range.start,
end: curr.range.end.line > prev.range.end.line
|| curr.range.end.line === prev.range.end.line && curr.range.end.character > prev.range.end.character
? curr.range.end : prev.range.end
};
}
if (prev.fileURI || curr.fileURI) {
const prevURI = prev.fileURI;
const currURI = curr.fileURI;
const fileURI = prevURI && currURI && prevURI !== currURI ? `<unmergable text regions of ${prevURI}, ${currURI}>` : prevURI ?? currURI;
result.fileURI = fileURI;
}
return result;
}
//# sourceMappingURL=generator-tracing.js.map
File diff suppressed because one or more lines are too long
+13
View File
@@ -0,0 +1,13 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
*
* @module langium/generate
*/
export * from './generator-node.js';
export type { SourceRegion, TextRegion, TraceRegion, TraceSourceSpec } from './generator-tracing.js';
export * from './node-joiner.js';
export * from './template-node.js';
export { expandToString, expandToStringLF, expandToStringLFWithNL, expandToStringWithNL, normalizeEOL } from './template-string.js';
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generate/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,qBAAqB,CAAC;AACpC,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACrG,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC"}
+12
View File
@@ -0,0 +1,12 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
*
* @module langium/generate
*/
export * from './generator-node.js';
export * from './node-joiner.js';
export * from './template-node.js';
export { expandToString, expandToStringLF, expandToStringLFWithNL, expandToStringWithNL, normalizeEOL } from './template-string.js';
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generate/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,qBAAqB,CAAC;AAEpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC"}
+308
View File
@@ -0,0 +1,308 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { AstNode, Properties } from '../syntax-tree.js';
import { CompositeGeneratorNode, type Generated, NewLineNode } from './generator-node.js';
import type { SourceRegion } from './generator-tracing.js';
export interface JoinOptions<T, U extends T = T> {
/**
* A plain or type guard filter function.
*
* Benefit compared to pre-filtering the joined iterable: Original indices of the elements are preserved and forwarded to the `toGenerated` function.
*/
filter?: ((element: T, index: number, isLast: boolean) => boolean) | ((element: T, index: number, isLast: boolean) => element is U);
/** A fixed prefix or prefix computation function to be prepended before each element of the iterable. */
prefix?: Generated | ((element: U, index: number, isLast: boolean) => Generated | undefined);
/** A fixed suffix or suffix computation function to be appended after each element of the iterable. */
suffix?: Generated | ((element: U, index: number, isLast: boolean) => Generated | undefined);
/** A fixed element separator to be inserted between 2 consecutive non-undefined item representations incl. their suffixes and prefixes. */
separator?: Generated;
/**
* Activates appending of up 6 line breaks after a non-undefined element + suffix + separator if given.
*
* If `true` a single line break is appended.
*
* If a number `> 6` is required you can achieve that via the `separator` or `suffix` options,
* e.g. `separator: new CompositeGeneratorNode(calcLineBreaks(...))`.
*/
appendNewLineIfNotEmpty?: true | NewLineNode.NoOfLineBreaks;
/**
* Suppresses appending trailing line breaks after the last item in the iterable if activated via `appendNewLineIfNotEmpty`.
*/
skipNewLineAfterLastItem?: true;
}
/**
* Joins the elements of the given `iterable` of pre-computed instances of {@link Generated}
* by appending the results to a {@link CompositeGeneratorNode} being returned finally.
* Each individual element is tested to be a string, a {@link CompositeGeneratorNode},
* or `undefined` and included as is if that test is satisfied. Otherwise the result of
* applying {@link String} (string constructor) to the element is included.
*
* Note: empty strings being included in `iterable` are treated as ordinary string
* representations, while the value of `undefined` makes this function to ignore the
* corresponding item and no separator is appended, if configured.
*
* Examples:
* ```
* expandToNode`
* ${ joinToNode(['a', 'b'], { appendNewLineIfNotEmpty: true }) }
*
* ${ joinToNode(new Set(['a', undefined, getElementNode()]), { separator: ',', appendNewLineIfNotEmpty: true }) }
* `
* ```
*
* @param iterable an {@link Array} or {@link Iterable} providing the elements to be joined
*
* @param options optional config object for defining a `separator`, contributing specialized
* `prefix` and/or `suffix` providers, and activating conditional line-break insertion. In addition,
* a dedicated `filter` function can be provided that enables the provision of the original
* element indices to the aforementioned functions, if the list is to be filtered. If
* {@link Array.filter} would be applied to the original list, the indices will be those of the
* filtered list during subsequent processing that in particular will cause confusion when using
* the tracing variant of this function named ({@link joinTracedToNode}).
* @returns the resulting {@link CompositeGeneratorNode} representing `iterable`'s content
*/
export declare function joinToNode<Generated>(iterable: Iterable<Generated> | Generated[], options?: JoinOptions<Generated>): CompositeGeneratorNode | undefined;
/**
* Joins the elements of the given `iterable` by applying `toGenerated` to each element
* and appending the results to a {@link CompositeGeneratorNode} being returned finally.
*
* Note: empty strings being returned by `toGenerated` are treated as ordinary string
* representations, while the result of `undefined` makes this function to ignore the
* corresponding item and no separator is appended, if configured.
*
* Examples:
* ```
* expandToNode`
* ${ joinToNode(['a', 'b'], String, { appendNewLineIfNotEmpty: true }) }
*
* ${ joinToNode(new Set(['a', undefined, 'b']), e => e && String(e), { separator: ',', appendNewLineIfNotEmpty: true }) }
* `
* ```
*
* @param iterable an {@link Array} or {@link Iterable} providing the elements to be joined
*
* @param toGenerated a callback converting each individual element to a string, a
* {@link CompositeGeneratorNode}, or to `undefined` if to be omitted, defaults to the `identity`
* for strings, generator nodes, and `undefined`, and to {@link String} otherwise.
*
* @param options optional config object for defining a `separator`, contributing specialized
* `prefix` and/or `suffix` providers, and activating conditional line-break insertion. In addition,
* a dedicated `filter` function can be provided that enables the provision of the
* original element indices to the aforementioned functions, if the list is to be filtered. If
* {@link Array.filter} would be applied to the original list, the indices will be those of the
* filtered list during subsequent processing that in particular will cause confusion when using
* the tracing variant of this function named ({@link joinTracedToNode}).
* @returns the resulting {@link CompositeGeneratorNode} representing `iterable`'s content
*/
export declare function joinToNode<T>(iterable: Iterable<T> | T[], toGenerated?: ((element: T, index: number, isLast: boolean) => Generated), options?: JoinOptions<T>): CompositeGeneratorNode | undefined;
/**
* Joins the elements of the given `iterable` by applying `toGenerated` to each element
* and appending the results to a {@link CompositeGeneratorNode} being returned finally.
*
* Here the mandatory type guard `filter` function is used to filter the elements of
* `iterable` and to apply the `toGenerated` function as well as the optional
* `prefix` and `suffix` functions to the accepted items with the more specific type
* `U` and their original indices.
*
* Note: empty strings being returned by `toGenerated` are treated as ordinary string
* representations, while the result of `undefined` makes this function to ignore the
* corresponding item and no separator is appended, if configured.
*
* Example:
* ```
* expandToNode`
* ${ joinToNode([x, y], e => e.propertyOfX, { filter: (e): e is X => e.$type === 'X' }) }
* `
* ```
*
* @param iterable an {@link Array} or {@link Iterable} providing the elements to be joined
*
* @param toGenerated a callback converting each individual element to a string, a
* {@link CompositeGeneratorNode}, or to `undefined` if to be omitted, defaults to the `identity`
* for strings, generator nodes, and `undefined`, and to {@link String} otherwise.
*
* @param options config object including the here required type guard filter function, as well as
* optional `separator` and `prefix` and/or `suffix` providers, and activating conditional
* line-break insertion.
* In contrast to {@link Array.filter} the dedicated `filter` function enables the provision of the
* original element indices to `toGenerated` and the aforementioned functions, if the list is to be
* filtered.
* @returns the resulting {@link CompositeGeneratorNode} representing `iterable`'s content
*/
export declare function joinToNode<T, U extends T>(iterable: Iterable<T> | T[], toGenerated: ((element: U, index: number, isLast: boolean) => Generated), options: JoinOptions<T, U> & {
filter: (element: T, index: number, isLast: boolean) => element is U;
}): CompositeGeneratorNode | undefined;
/**
* Convenience function for joining the elements of some `iterable` and gathering tracing information.
*
* This function returns another function that does the processing, and that expects same list of
* arguments as expected by {@link joinToNode}, i.e. an `iterable`, a function `toGenerated`
* converting each element into a `Generated`, as well as some `options`.
*
* That function then joins the elements of `iterable` by delegating to {@link joinToNode}.
* Via {@link traceToNode} the resulting generator node is supplemented with the provided tracing
* information in form of `{astNode, property?, index?}`, and finally returned. In addition,
* if `property` is given each element's generator node representation is augmented with the
* provided tracing information plus the index of the element within `iterable`.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`, is optional
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandToNode`
* children: ${ joinTracedToNode(entity, 'children')(entity.children, child => child.name, { separator: ' ' }) };
* `.appendNewLine()
*/
export declare function joinTracedToNode<T extends AstNode>(astNode: T, property?: Properties<T>): <E>(iterable: Iterable<E> | E[], toGenerated?: ((element: E, index: number, isLast: boolean) => Generated) | JoinOptions<E>, options?: JoinOptions<E>) => CompositeGeneratorNode;
/**
* Convenience function for joining the elements of some `iterable` and gathering tracing information
* in form of concrete coordinates.
*
* This function returns another function that does the processing, and that expects same list of
* arguments as expected by {@link joinToNode}, i.e. an `iterable`, a function `toGenerated`
* converting each element into a `Generated`, as well as some `options`.
*
* That function then joins the elements of `iterable` by delegating to {@link joinToNode}.
* Via {@link traceToNode} the resulting generator node is supplemented with the provided tracing
* information, and finally returned. Elementwise tracing need to be implemented by client code
* within `toGenerated`, if required.
*
* @param sourceRegion a text region within some file in form of concrete coordinates,
* if `undefined` no tracing will happen
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandToNode`
* children: ${ joinTracedToNode(findNodesForProperty(entity.$cstNode, 'children'))(entity.children, child => child.name, { separator: ' ' }) };
* `.appendNewLine()
*/
export declare function joinTracedToNode(sourceRegion: SourceRegion | undefined): <E>(iterable: Iterable<E> | E[], toGenerated?: ((element: E, index: number, isLast: boolean) => Generated) | JoinOptions<E>, options?: JoinOptions<E>) => CompositeGeneratorNode;
/**
* Convenience function for joining the elements of some `iterable` and gathering tracing information
* in form of a list of concrete coordinates.
*
* This function returns another function that does the processing, and that expects same list of
* arguments as expected by {@link joinToNode}, i.e. an `iterable`, a function `toGenerated`
* converting each element into a `Generated`, as well as some `options`.
*
* That function then joins the elements of `iterable` by delegating to {@link joinToNode}.
* Via {@link traceToNode} the resulting generator node is supplemented with the provided tracing
* information, and finally returned. Elementwise tracing need to be implemented by client code
* within `toGenerated`, if required.
*
* The list of regions in `sourceRegions` will later be reduced to the smallest encompassing region
* of all the contained source regions.
*
* @param sourceRegions a list of text regions within some file in form of concrete coordinates,
* if empty no tracing will happen
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandToNode`
* children: ${ joinTracedToNode(findNodesForProperty(entity.$cstNode, 'children'))(entity.children, child => child.name, { separator: ' ' }) };
* `.appendNewLine()
*/
export declare function joinTracedToNode(sourceRegions: SourceRegion[]): <E>(iterable: Iterable<E> | E[], toGenerated?: ((element: E, index: number, isLast: boolean) => Generated) | JoinOptions<E>, options?: JoinOptions<E>) => CompositeGeneratorNode;
/**
* Convenience function for joining the elements of some `iterable` and gathering tracing information,
* if `condition` is equal to `true`.
*
* If `condition` is satisfied, this function returns another function that does the processing,
* and that expects same list of arguments as expected by {@link joinToNode}, i.e. an `iterable`,
* a function `toGenerated` converting each element into a `Generated`, as well as some `options`.
*
* That function then joins the elements of `iterable` by delegating to {@link joinToNode}.
* Via {@link traceToNode} the resulting generator node is supplemented with the provided tracing
* information, and finally returned. In addition, if `property` is given each element's
* generator node representation is augmented with the provided tracing information
* plus the index of the element within `iterable`.
*
* Otherwise, if `condition` is equal to false, the returned function just returns `undefined`.
*
* @param condition a boolean value indicating whether to evaluate the provided iterable.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`, is optional
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode} or `undefined`.
*
* @example
* expandToNode`
* children: ${ joinTracedToNode(entity, 'children')(entity.children, child => child.name, { separator: ' ' }) };
* `.appendNewLine()
*/
export declare function joinTracedToNodeIf<T extends AstNode>(condition: boolean, astNode: T, property?: Properties<T>): <E>(iterable: Iterable<E> | E[], toGenerated?: ((element: E, index: number, isLast: boolean) => Generated) | JoinOptions<E>, options?: JoinOptions<E>) => CompositeGeneratorNode | undefined;
/**
* Convenience function for joining the elements of some `iterable` and gathering tracing information
* in form of a list of concrete coordinates, if `condition` is equal to `true`.
*
* If `condition` is satisfied, this function returns another function that does the processing,
* and that expects same list of arguments as expected by {@link joinToNode}, i.e. an `iterable`,
* a function `toGenerated` converting each element into a `Generated`, as well as some `options`.
*
* That function then joins the elements of `iterable` by delegating to {@link joinToNode}.
* Via {@link traceToNode} the resulting generator node is supplemented with the provided tracing
* information, and finally returned. Element-wise tracing need to be implemented by client code
* within `toGenerated`, if required.
*
* Otherwise, if `condition` is equal to false, the returned function just returns `undefined`.
*
* If `sourceRegion` is a function supplying the corresponding regions, it's only called if `condition` is satisfied.
*
* @param condition a boolean value indicating whether to evaluate the provided iterable.
*
* @param sourceRegion a text region within some file in form of concrete coordinates or a supplier function,
* if `undefined` no tracing will happen
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandToNode`
* children: ${ joinTracedToNodeIf(entity !== undefined, () => entity.$cstNode)(entity.children, child => child.name, { separator: ' ' }) };
* `.appendNewLine()
*/
export declare function joinTracedToNodeIf(condition: boolean, sourceRegion: SourceRegion | undefined | (() => SourceRegion | undefined)): <E>(iterable: Iterable<E> | E[], toGenerated?: ((element: E, index: number, isLast: boolean) => Generated) | JoinOptions<E>, options?: JoinOptions<E>) => CompositeGeneratorNode | undefined;
/**
* Convenience function for joining the elements of some `iterable` and gathering tracing information
* in form of a list of concrete coordinates, if `condition` is equal to `true`.
*
* If `condition` is satisfied, this function returns another function that does the processing,
* and that expects same list of arguments as expected by {@link joinToNode}, i.e. an `iterable`,
* a function `toGenerated` converting each element into a `Generated`, as well as some `options`.
*
* That function then joins the elements of `iterable` by delegating to {@link joinToNode}.
* Via {@link traceToNode} the resulting generator node is supplemented with the provided tracing
* information, and finally returned. Element-wise tracing need to be implemented by client code
* within `toGenerated`, if required.
*
* Otherwise, if `condition` is equal to false, the returned function just returns `undefined`.
*
* The list of regions in `sourceRegions` will later be reduced to the smallest encompassing region
* of all the contained source regions.
* If `sourceRegions` is a function supplying the corresponding regions, it's only called if `condition` is satisfied.
*
* @param condition a boolean value indicating whether to evaluate the provided iterable.
*
* @param sourceRegions a list of text regions within some file in form of concrete coordinates or a supplier function,
* if empty no tracing will happen
*
* @returns a function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandToNode`
* children: ${ joinTracedToNodeIf(entity !== undefined, () => findNodesForProperty(entity.$cstNode, 'children'))(entity.children, child => child.name, { separator: ' ' }) };
* `.appendNewLine()
*/
export declare function joinTracedToNodeIf(condition: boolean, sourceRegions: SourceRegion[] | (() => SourceRegion[])): <E>(iterable: Iterable<E> | E[], toGenerated?: ((element: E, index: number, isLast: boolean) => Generated) | JoinOptions<E>, options?: JoinOptions<E>) => CompositeGeneratorNode | undefined;
//# sourceMappingURL=node-joiner.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"node-joiner.d.ts","sourceRoot":"","sources":["../../src/generate/node-joiner.ts"],"names":[],"mappings":"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"}
+67
View File
@@ -0,0 +1,67 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { CompositeGeneratorNode, isGeneratorNode, NewLineNode, traceToNode } from './generator-node.js';
const defaultToGenerated = (e) => e === undefined || typeof e === 'string' || isGeneratorNode(e) ? e : String(e);
export function joinToNode(iterable, toGeneratedOrOptions = defaultToGenerated, options = {}) {
const toGenerated = typeof toGeneratedOrOptions === 'function' ? toGeneratedOrOptions : defaultToGenerated;
const { filter, prefix, suffix, separator, appendNewLineIfNotEmpty, skipNewLineAfterLastItem } = typeof toGeneratedOrOptions === 'object' ? toGeneratedOrOptions : options;
const prefixFunc = typeof prefix === 'function' ? prefix : (() => prefix);
const suffixFunc = typeof suffix === 'function' ? suffix : (() => suffix);
const doAppendNewLines = (node) => {
if (node.isEmpty()) {
// do nothing
return;
}
else if (appendNewLineIfNotEmpty === true || appendNewLineIfNotEmpty === 1) {
node.appendNewLineIfNotEmpty();
}
else {
node.append(new NewLineNode(undefined, true, appendNewLineIfNotEmpty));
}
};
return reduceWithIsLast(iterable, (node, it, i, isLast) => {
if (filter && !filter(it, i, isLast)) {
return node;
}
const content = toGenerated(it, i, isLast);
return content === undefined ? /* in this case don't append anything to */ node : /* otherwise: */ (node ?? (node = new CompositeGeneratorNode()))
.appendIf(!node.isEmpty(), separator)
.appendIf(
// append 'newLineIfNotEmpty' elements only if 'node' has some content already,
// as if the parent is an IndentNode with 'indentImmediately' set to 'false'
// the indentation is not properly applied to the first non-empty line of the (this) child node
appendNewLineIfNotEmpty !== undefined, doAppendNewLines)
.append(prefixFunc(it, i, isLast))
.append(content)
.append(suffixFunc(it, i, isLast));
})?.appendIf(!skipNewLineAfterLastItem && appendNewLineIfNotEmpty !== undefined, doAppendNewLines);
}
// implementation:
export function joinTracedToNode(source, property) {
return (iterable, toGeneratedOrOptions, options) => {
options ?? (options = typeof toGeneratedOrOptions === 'object' ? toGeneratedOrOptions : undefined);
const toGenerated = typeof toGeneratedOrOptions === 'function' ? toGeneratedOrOptions : defaultToGenerated;
return traceToNode(source, property)(joinToNode(iterable, source && property ? (element, index, isLast) => traceToNode(source, property, index)(toGenerated(element, index, isLast)) : toGenerated, options));
};
}
// implementation:
export function joinTracedToNodeIf(condition, source, property) {
return condition ? joinTracedToNode((typeof source === 'function' ? source() : source), property) : () => undefined;
}
function reduceWithIsLast(iterable, callbackfn, initial) {
const iterator = iterable[Symbol.iterator]();
let next = iterator.next();
let index = 0;
let result = initial;
while (!next.done) {
const nextNext = iterator.next();
result = callbackfn(result, next.value, index, Boolean(nextNext.done));
next = nextNext;
index++;
}
return result;
}
//# sourceMappingURL=node-joiner.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"node-joiner.js","sourceRoot":"","sources":["../../src/generate/node-joiner.ts"],"names":[],"mappings":"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"}
+12
View File
@@ -0,0 +1,12 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { GeneratorNode } from './generator-node.js';
import type { TraceRegion } from './generator-tracing.js';
export declare function processGeneratorNode(node: GeneratorNode, defaultIndentation?: string | number): {
text: string;
trace: TraceRegion;
};
//# sourceMappingURL=node-processor.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"node-processor.d.ts","sourceRoot":"","sources":["../../src/generate/node-processor.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAGhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAA4B,WAAW,EAAE,MAAM,wBAAwB,CAAC;AA2KpF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,EAAE,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAwBpI"}
+258
View File
@@ -0,0 +1,258 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { CompositeGeneratorNode, IndentNode, NewLineNode } from './generator-node.js';
import { getSourceRegion } from './generator-tracing.js';
class Context {
constructor(defaultIndent) {
this.defaultIndentation = ' ';
this.pendingIndent = true;
this.currentIndents = [];
this.recentNonImmediateIndents = [];
this.traceData = [];
this.lines = [[]];
this.length = 0;
if (typeof defaultIndent === 'string') {
this.defaultIndentation = defaultIndent;
}
else if (typeof defaultIndent === 'number') {
this.defaultIndentation = ''.padStart(defaultIndent);
}
}
get content() {
return this.lines.map(e => e.join('')).join('');
}
get contentLength() {
return this.length;
}
get currentLineNumber() {
return this.lines.length - 1;
}
get currentLineContent() {
return this.lines[this.currentLineNumber].join('');
}
get currentPosition() {
return {
offset: this.contentLength,
line: this.currentLineNumber,
character: this.currentLineContent.length
};
}
append(value, isIndent) {
if (value.length > 0) {
const beforePos = isIndent && this.currentPosition;
this.lines[this.currentLineNumber].push(value);
this.length += value.length;
if (beforePos) {
this.indentPendingTraceRegions(beforePos);
}
}
}
indentPendingTraceRegions(before) {
for (let i = this.traceData.length - 1; i >= 0; i--) {
const tr = this.traceData[i];
if (tr.targetStart && tr.targetStart.offset === before.offset /* tr.targetStart.line == before.line && tr.targetStart.character === before.character*/)
tr.targetStart = this.currentPosition;
}
}
increaseIndent(node) {
this.currentIndents.push(node);
if (!node.indentImmediately) {
this.recentNonImmediateIndents.push(node);
}
}
decreaseIndent() {
this.currentIndents.pop();
}
get relevantIndents() {
return this.currentIndents.filter(i => !this.recentNonImmediateIndents.includes(i));
}
resetCurrentLine() {
this.length -= this.lines[this.currentLineNumber].join('').length;
this.lines[this.currentLineNumber] = [];
this.pendingIndent = true;
this.recentNonImmediateIndents.length = 0;
}
addNewLine() {
this.lines.push([]);
this.pendingIndent = true;
this.recentNonImmediateIndents.length = 0;
}
pushTraceRegion(sourceRegion) {
const region = createTraceRegion(sourceRegion, this.currentPosition, it => this.traceData[this.traceData.length - 1]?.children?.push(it));
this.traceData.push(region);
return region;
}
popTraceRegion(expected) {
const traceRegion = this.traceData.pop();
// the following assertion can be dropped once the tracing is considered stable
this.assertTrue(traceRegion === expected, 'Trace region mismatch!');
return traceRegion;
}
getParentTraceSourceFileURI() {
for (let i = this.traceData.length - 1; i > -1; i--) {
const fileUri = this.traceData[i].sourceRegion?.fileURI;
if (fileUri)
return fileUri;
}
return undefined;
}
assertTrue(condition, msg) {
if (!condition) {
throw new Error(msg);
}
}
}
function createTraceRegion(sourceRegion, targetStart, accept) {
const result = {
sourceRegion,
targetRegion: undefined,
children: [],
targetStart,
complete: (targetEnd) => {
result.targetRegion = {
offset: result.targetStart.offset,
end: targetEnd.offset,
length: targetEnd.offset - result.targetStart.offset,
range: {
start: {
line: result.targetStart.line,
character: result.targetStart.character
},
end: {
line: targetEnd.line,
character: targetEnd.character
},
}
};
delete result.targetStart;
if (result.children?.length === 0) {
delete result.children;
}
if (result.targetRegion?.length) {
accept(result);
}
delete result.complete;
return result;
}
};
return result;
}
export function processGeneratorNode(node, defaultIndentation) {
const context = new Context(defaultIndentation);
const trace = context.pushTraceRegion(undefined);
processNodeInternal(node, context);
context.popTraceRegion(trace);
trace.complete && trace.complete(context.currentPosition);
const singleChild = trace.children && trace.children.length === 1 ? trace.children[0] : undefined;
const singleChildTargetRegion = singleChild?.targetRegion;
const rootTargetRegion = trace.targetRegion;
if (singleChildTargetRegion && singleChild.sourceRegion
&& singleChildTargetRegion.offset === rootTargetRegion.offset
&& singleChildTargetRegion.length === rootTargetRegion.length) {
// some optimization:
// if (the root) `node` is traced (`singleChild.sourceRegion` !== undefined) and spans the entire `context.content`
// we skip the wrapping root trace object created above at the beginning of this method
return { text: context.content, trace: singleChild };
}
else {
return { text: context.content, trace };
}
}
function processNodeInternal(node, context) {
if (typeof (node) === 'string') {
processStringNode(node, context);
}
else if (node instanceof IndentNode) {
processIndentNode(node, context);
}
else if (node instanceof CompositeGeneratorNode) {
processCompositeNode(node, context);
}
else if (node instanceof NewLineNode) {
processNewLineNode(node, context);
}
}
function hasContent(node, ctx) {
if (typeof (node) === 'string') {
return node.length !== 0; // cs: do not ignore ws only content here, enclosed within other nodes it will matter!
}
else if (node instanceof CompositeGeneratorNode) {
return node.contents.some(e => hasContent(e, ctx));
}
else if (node instanceof NewLineNode) {
return !(node.ifNotEmpty && ctx.currentLineContent.length === 0);
}
else {
return false;
}
}
function processStringNode(node, context) {
if (node) {
handlePendingIndent(context, false);
context.append(node);
}
}
function handlePendingIndent(ctx, endOfLine) {
if (ctx.pendingIndent) {
let indent = '';
for (const indentNode of ctx.relevantIndents.filter(e => e.indentEmptyLines || !endOfLine)) {
indent += indentNode.indentation ?? ctx.defaultIndentation;
}
ctx.append(indent, true);
ctx.pendingIndent = false;
}
}
function processCompositeNode(node, context) {
let traceRegion = undefined;
const sourceRegion = getSourceRegion(node.tracedSource);
if (sourceRegion) {
traceRegion = context.pushTraceRegion(sourceRegion);
}
for (const child of node.contents) {
processNodeInternal(child, context);
}
if (traceRegion) {
context.popTraceRegion(traceRegion);
const parentsFileURI = context.getParentTraceSourceFileURI();
if (parentsFileURI && sourceRegion?.fileURI === parentsFileURI) {
// if some parent's sourceRegion refers to the same source file uri (and no other source file was referenced inbetween)
// we can drop the file uri in order to reduce repeated strings
delete sourceRegion.fileURI;
}
traceRegion.complete && traceRegion.complete(context.currentPosition);
}
}
function processIndentNode(node, context) {
if (hasContent(node, context)) {
if (node.indentImmediately && !context.pendingIndent) {
context.append(node.indentation ?? context.defaultIndentation, true);
}
try {
context.increaseIndent(node);
processCompositeNode(node, context);
}
finally {
context.decreaseIndent();
}
}
}
function processNewLineNode(node, context) {
if (node.ifNotEmpty && !hasNonWhitespace(context.currentLineContent)) {
context.resetCurrentLine();
}
else {
handlePendingIndent(context, true);
let count = node.count;
while (count-- > 0) {
context.append(node.lineDelimiter);
context.addNewLine();
}
}
}
function hasNonWhitespace(text) {
return text.trimStart() !== '';
}
//# sourceMappingURL=node-processor.js.map
File diff suppressed because one or more lines are too long
+280
View File
@@ -0,0 +1,280 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { AstNode, Properties } from '../syntax-tree.js';
import { CompositeGeneratorNode } from './generator-node.js';
import type { SourceRegion } from './generator-tracing.js';
/**
* A tag function that attaches the template's content to a {@link CompositeGeneratorNode}.
*
* This is done segment by segment, and static template portions as well as substitutions
* are added individually to the returned {@link CompositeGeneratorNode}.
* At that common leading indentation of all the template's static parts is trimmed,
* whereas additional indentations of particular lines within that static parts as well as
* any line breaks and indentation within the substitutions are kept.
*
* For the sake of good readability and good composability of results of this function like
* in the following example, the subsequent rule is applied.
*
* ```ts
* expandToNode`
* This is the beginning of something
*
* ${foo.bar ? expandToNode`
* bla bla bla ${foo.bar}
*
* `: undefined}
* end of something
* `
* ```
*
* Rule:
* In case of a multiline template
* 1. the content of the first line including its terminating line break is ignored,
* if and only if it is empty or contains whitespace only.
* 2. the content of the last line including its preceding line break (last line within the template excluding the trailing backtick) is ignored,
* if and only if it is empty or contains whitespace only, and the whitespace in-between the last line break and the trailing backtick
* is a real prefix of the common indentation among all none-empty lines of the template, i.e.,
* it is shorter than the common indentation that is trimmed during processing the template provided that common indentation is identifiable.
*
* Thus, the results of all of the following invocations are identical and equal to `generatedContent`.
* ```ts
* expandToNode`generatedContent`
* expandToNode`
* generatedContent`
* expandToNode`
* generatedContent
* `
* expandToNode`
* generatedContent
* `
* ```
* In contrast, the results of the following invocations are equal to `generatedContent\n` (or `generatedContent\r\n` on MS Windows)
* ```ts
* expandToNode`generatedContent
* `
* expandToNode`
* generatedContent
* `
* ```
*
* In addition, a third rule is applied while processing line breaks:
* If a line's last substitution contributes `undefined` or an object of type {@link GeneratorNode},
* the subsequent line break will be appended via {@link CompositeGeneratorNode.appendNewLineIfNotEmpty}.
* Hence, if all other segments of that line contribute whitespace characters only,
* the entire line will be omitted while rendering the desired output.
* Otherwise, linebreaks will be added via {@link CompositeGeneratorNode.appendNewLine}.
* That holds in particular, if the last substitution contributes an empty string. In consequence,
* adding `${''}` to the end of a line consisting of whitespace and substitutions only
* enforces the line break to be rendered, no matter what the substitutions actually contribute.
*
* @param staticParts the static parts of a tagged template literal
* @param substitutions the variable parts of a tagged template literal
* @returns a 'CompositeGeneratorNode' containing the particular aligned lines
* after resolving and inserting the substitutions into the given parts
*/
export declare function expandToNode(staticParts: TemplateStringsArray, ...substitutions: unknown[]): CompositeGeneratorNode;
/**
* Convenience function for creating a {@link CompositeGeneratorNode} being configured with the
* provided tracing information in form of `{astNode, property?, index: undefined}` and appending content
* in form of a template.
*
* This function returns a tag function that takes the desired template and does the processing
* by delegating to {@link expandToNode} and {@link traceToNode} and finally returning the
* resulting generator node.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`, is optional
*
* @returns a tag function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandTracedToNode(entity)`
* Hello ${ traceToNode(entity, 'name')(entity.name) }
* `.appendNewLine()
*/
export declare function expandTracedToNode<T extends AstNode>(astNode: T, property?: Properties<T>): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => CompositeGeneratorNode;
/**
* Convenience function for creating a {@link CompositeGeneratorNode} being configured with the
* provided tracing information in form of `{astNode, property, index}` and appending content
* in form of a template.
*
* This function returns a tag function that takes the desired template and does the processing
* by delegating to {@link expandToNode} and {@link traceToNode} and finally returning the
* resulting generator node.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`
*
* @param index the index of the value within a list property corresponding to the appended content,
* if the property contains a list of elements, is ignored otherwise
*
* @returns a tag function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandTracedToNode(entity, 'definitions', 0)`
* Hello ${ traceToNode(entity, 'name')(entity.name) }
* `.appendNewLine()
*/
export declare function expandTracedToNode<T extends AstNode>(astNode: T, property: Properties<T>, index?: number | undefined): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => CompositeGeneratorNode;
/**
* Convenience function for creating a {@link CompositeGeneratorNode} being configured with the
* provided tracing information in form of concrete coordinates and appending content
* in form of a template. Complete coordinates are provided by the {@link AstNode AstNodes}'
* corresponding {@link AstNode.$cstNode AstNode.$cstNodes}.
*
* This function returns a tag function that takes the desired template and does the processing
* by delegating to {@link expandToNode} and {@link traceToNode} and finally returning the
* resulting generator node.
*
* @param sourceRegion a text region within some file in form of concrete coordinates,
* if `undefined` no tracing will happen
*
* @returns a tag function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandTracedToNode(entity.$cstNode)`
* Hello ${ traceToNode(entity, 'name')(entity.name) }
* `.appendNewLine()
*/
export declare function expandTracedToNode(sourceRegion: SourceRegion | undefined): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => CompositeGeneratorNode;
/**
* Convenience function for creating a {@link CompositeGeneratorNode} being configured with the
* provided tracing information in form of a list of concrete coordinates and appending content
* in form of a template. Complete coordinates are provided by the {@link AstNode AstNodes}'
* corresponding {@link AstNode.$cstNode AstNode.$cstNodes}.
*
* This function returns a tag function that takes the desired template and does the processing
* by delegating to {@link expandToNode} and {@link traceToNode} and finally returning the
* resulting generator node.
*
* The list of regions in `sourceRegions` will later be reduced to the smallest encompassing region
* of all the contained source regions.
*
* @param sourceRegions a list of text regions within some file in form of concrete coordinates,
* if empty no tracing will happen
*
* @returns a tag function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandTracedToNode([
* findNodeForKeyword(entity.$cstNode, '{')!,
* findNodeForKeyword(entity.$cstNode, '}')!
* ])`
* Hello ${ traceToNode(entity, 'name')(entity.name) }
* `.appendNewLine()
*/
export declare function expandTracedToNode(sourceRegions: SourceRegion[]): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => CompositeGeneratorNode;
/**
* Convenience function for creating a {@link CompositeGeneratorNode} being configured with the
* provided tracing information in form of `{astNode, property?, index: undefined}` and appending content
* in form of a template, if `condition` is equal to `true`.
*
* If `condition` is satisfied, this function returns a tag function that takes the desired template
* and does the processing by delegating to {@link expandToNode} and {@link traceToNode} and
* finally returning the resulting generator node. Otherwise, the returned function just returns `undefined`.
*
* @param condition a boolean value indicating whether to evaluate the provided template.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`, is optional
*
* @returns a tag function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandTracedToNodeIf(entity !== undefined, entity)`
* Hello ${ traceToNode(entity, 'name')(entity.name) }
* `.appendNewLine()
*/
export declare function expandTracedToNodeIf<T extends AstNode>(condition: boolean, astNode: T, property?: Properties<T>): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => CompositeGeneratorNode | undefined;
/**
* Convenience function for creating a {@link CompositeGeneratorNode} being configured with the
* provided tracing information in form of `{astNode, property, index}` and appending content
* in form of a template, if `condition` is equal to `true`.
*
* If `condition` is satisfied, this function returns a tag function that takes the desired template
* and does the processing by delegating to {@link expandToNode} and {@link traceToNode} and
* finally returning the resulting generator node. Otherwise, the returned function just returns `undefined`.
*
* @param condition a boolean value indicating whether to evaluate the provided template.
*
* @param astNode the AstNode corresponding to the appended content
*
* @param property the value property name (string) corresponding to the appended content,
* if e.g. the content corresponds to some `string` or `number` property of `astNode`
*
* @param index the index of the value within a list property corresponding to the appended content,
* if the property contains a list of elements, is ignored otherwise
*
* @returns a tag function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandTracedToNodeIf(entity !== undefined, entity, 'definitions', 0)`
* Hello ${ traceToNode(entity, 'name')(entity.name) }
* `.appendNewLine()
*/
export declare function expandTracedToNodeIf<T extends AstNode>(condition: boolean, astNode: T, property: Properties<T>, index: number | undefined): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => CompositeGeneratorNode | undefined;
/**
* Convenience function for creating a {@link CompositeGeneratorNode} being configured with the
* provided tracing information in form of concrete coordinates and appending content in form
* of a template, if `condition` is equal to `true`. Complete coordinates are provided by
* the {@link AstNode AstNodes}' corresponding {@link AstNode.$cstNode AstNode.$cstNodes}.
*
* This function returns a tag function that takes the desired template and does the processing
* by delegating to {@link expandToNode} and {@link traceToNode} and finally returning the
* resulting generator node.
*
* If `sourceRegion` is a function supplying the corresponding region, it's only called if `condition` is satisfied.
*
* @param condition a boolean value indicating whether to evaluate the provided template.
*
* @param sourceRegion a text region within some file in form of concrete coordinates or a supplier function,
* if `undefined` no tracing will happen
*
* @returns a tag function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandTracedToNodeIf(entity !== undefined, entity.$cstNode)`
* Hello ${ traceToNode(entity, 'name')(entity.name) }
* `.appendNewLine()
*/
export declare function expandTracedToNodeIf(condition: boolean, sourceRegion: SourceRegion | undefined | (() => SourceRegion | undefined)): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => CompositeGeneratorNode | undefined;
/**
* Convenience function for creating a {@link CompositeGeneratorNode} being configured with the
* provided tracing information in form of a list of concrete coordinates and appending content
* in form of a template, if `condition` is equal to `true`. Complete coordinates are provided
* by the {@link AstNode AstNodes}' corresponding {@link AstNode.$cstNode AstNode.$cstNodes}.
*
* This function returns a tag function that takes the desired template and does the processing
* by delegating to {@link expandToNode} and {@link traceToNode} and finally returning the
* resulting generator node.
*
* The list of regions in `sourceRegions` will later be reduced to the smallest encompassing region
* of all the contained source regions.
* If `sourceRegions` is a function supplying the corresponding regions, it's only called if `condition` is satisfied.
*
* @param condition a boolean value indicating whether to evaluate the provided template.
*
* @param sourceRegions a list of text regions within some file in form of concrete coordinates,
* if empty no tracing will happen
*
* @returns a tag function behaving as described above, which in turn returns a {@link CompositeGeneratorNode}.
*
* @example
* expandTracedToNodeIf(entity !== undefined, [
* findNodeForKeyword(entity.$cstNode, '{')!,
* findNodeForKeyword(entity.$cstNode, '}')!
* ])`
* Hello ${ traceToNode(entity, 'name')(entity.name) }
* `.appendNewLine()
*/
export declare function expandTracedToNodeIf(condition: boolean, sourceRegions: SourceRegion[]): (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => CompositeGeneratorNode | undefined;
//# sourceMappingURL=template-node.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"template-node.d.ts","sourceRoot":"","sources":["../../src/generate/template-node.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG7D,OAAO,EAAE,sBAAsB,EAAgC,MAAM,qBAAqB,CAAC;AAC3F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,GAAG,sBAAsB,CAUnH;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,KAAK,sBAAsB,CAAC;AAExL;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,KAAK,sBAAsB,CAAC;AAEnN;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,YAAY,GAAG,SAAS,GAAG,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,KAAK,sBAAsB,CAAC;AAEvK;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,YAAY,EAAE,GAAG,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,KAAK,sBAAsB,CAAC;AAW9J;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GACxG,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,KAAK,sBAAsB,GAAG,SAAS,CAAC;AAE/G;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAClI,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,KAAK,sBAAsB,GAAG,SAAS,CAAC;AAE/G;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,GAAG,SAAS,GAAG,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC,GAC1H,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,KAAK,sBAAsB,GAAG,SAAS,CAAC;AAE/G;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,GAC9E,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,KAAK,sBAAsB,GAAG,SAAS,CAAC"}
+301
View File
@@ -0,0 +1,301 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { NEWLINE_REGEXP } from '../utils/regexp-utils.js';
import { CompositeGeneratorNode, isGeneratorNode, traceToNode } from './generator-node.js';
import { findIndentation } from './template-string.js';
/**
* A tag function that attaches the template's content to a {@link CompositeGeneratorNode}.
*
* This is done segment by segment, and static template portions as well as substitutions
* are added individually to the returned {@link CompositeGeneratorNode}.
* At that common leading indentation of all the template's static parts is trimmed,
* whereas additional indentations of particular lines within that static parts as well as
* any line breaks and indentation within the substitutions are kept.
*
* For the sake of good readability and good composability of results of this function like
* in the following example, the subsequent rule is applied.
*
* ```ts
* expandToNode`
* This is the beginning of something
*
* ${foo.bar ? expandToNode`
* bla bla bla ${foo.bar}
*
* `: undefined}
* end of something
* `
* ```
*
* Rule:
* In case of a multiline template
* 1. the content of the first line including its terminating line break is ignored,
* if and only if it is empty or contains whitespace only.
* 2. the content of the last line including its preceding line break (last line within the template excluding the trailing backtick) is ignored,
* if and only if it is empty or contains whitespace only, and the whitespace in-between the last line break and the trailing backtick
* is a real prefix of the common indentation among all none-empty lines of the template, i.e.,
* it is shorter than the common indentation that is trimmed during processing the template provided that common indentation is identifiable.
*
* Thus, the results of all of the following invocations are identical and equal to `generatedContent`.
* ```ts
* expandToNode`generatedContent`
* expandToNode`
* generatedContent`
* expandToNode`
* generatedContent
* `
* expandToNode`
* generatedContent
* `
* ```
* In contrast, the results of the following invocations are equal to `generatedContent\n` (or `generatedContent\r\n` on MS Windows)
* ```ts
* expandToNode`generatedContent
* `
* expandToNode`
* generatedContent
* `
* ```
*
* In addition, a third rule is applied while processing line breaks:
* If a line's last substitution contributes `undefined` or an object of type {@link GeneratorNode},
* the subsequent line break will be appended via {@link CompositeGeneratorNode.appendNewLineIfNotEmpty}.
* Hence, if all other segments of that line contribute whitespace characters only,
* the entire line will be omitted while rendering the desired output.
* Otherwise, linebreaks will be added via {@link CompositeGeneratorNode.appendNewLine}.
* That holds in particular, if the last substitution contributes an empty string. In consequence,
* adding `${''}` to the end of a line consisting of whitespace and substitutions only
* enforces the line break to be rendered, no matter what the substitutions actually contribute.
*
* @param staticParts the static parts of a tagged template literal
* @param substitutions the variable parts of a tagged template literal
* @returns a 'CompositeGeneratorNode' containing the particular aligned lines
* after resolving and inserting the substitutions into the given parts
*/
export function expandToNode(staticParts, ...substitutions) {
// first part: determine the common indentation of all the template lines with the substitutions being ignored
const templateProps = findIndentationAndTemplateStructure(staticParts);
// 2nd part: for all the static template parts: split them and inject a NEW_LINE marker where line breaks shall be a present in the final result,
// and create a flatten list of strings, NEW_LINE marker occurrences, and substitutions
const splitAndMerged = splitTemplateLinesAndMergeWithSubstitutions(staticParts, substitutions, templateProps);
// eventually, inject indentation nodes and append the segments to final desired composite generator node
return composeFinalGeneratorNode(splitAndMerged);
}
// implementation:
export function expandTracedToNode(source, property, index) {
return (staticParts, ...substitutions) => {
return traceToNode(source, property, index)(expandToNode(staticParts, ...substitutions));
};
}
// implementation:
export function expandTracedToNodeIf(condition, source, property, index) {
return condition ? expandTracedToNode((typeof source === 'function' ? source() : source), property, index) : () => undefined;
}
function findIndentationAndTemplateStructure(staticParts) {
const lines = staticParts.join('_').split(NEWLINE_REGEXP);
const omitFirstLine = lines.length > 1 && lines[0].trim().length === 0;
const omitLastLine = omitFirstLine && lines.length > 1 && lines[lines.length - 1].trim().length === 0;
if (lines.length === 1 || lines.length !== 0 && lines[0].trim().length !== 0 || lines.length === 2 && lines[1].trim().length === 0) {
// for cases of non-adjusted templates like
// const n1 = expandToNode` `;
// const n2 = expandToNode` something `;
// const n3 = expandToNode` something
// `;
// ... consider the indentation to be empty, and all the leading whitespace to be relevant, except for the last (empty) line of n3!
return {
indentation: 0, //''
omitFirstLine,
omitLastLine,
trimLastLine: lines.length !== 1 && lines[lines.length - 1].trim().length === 0
};
}
else {
// otherwise:
// for cases of non-adjusted templates like
// const n4 = expandToNode` abc
// def `;
// const n5 = expandToNode`<maybe with some WS here>
// abc
// def`;
// const n6 = expandToNode`<maybe with some WS here>
// abc
// def
// `;
// ... the indentation shall be determined by the non-empty lines, excluding the last line if it contains whitespace only
// if we have a multi-line template and the first line is empty, see n5, n6
// ignore the first line;
let sliced = omitFirstLine ? lines.slice(1) : lines;
// if there're more than one line remaining and the last one only contains WS, see n6,
// ignore the last line
sliced = omitLastLine ? sliced.slice(0, sliced.length - 1) : sliced;
// ignore empty lines during indentation calculation, as linting rules might forbid lines containing just whitespace
sliced = sliced.filter(e => e.length !== 0);
const indentation = findIndentation(sliced);
return {
indentation,
omitFirstLine,
// in the subsequent steps omit the last line only if it is empty or if it only contains whitespace of which the common indentation is not a valid prefix;
// in other words: keep the last line if it matches the common indentation (and maybe contains non-whitespace), a non-match may be due to mistaken usage of tabs and spaces
omitLastLine: omitLastLine && (lines[lines.length - 1].length < indentation || !lines[lines.length - 1].startsWith(sliced[0].substring(0, indentation)))
};
}
}
function splitTemplateLinesAndMergeWithSubstitutions(staticParts, substitutions, { indentation, omitFirstLine, omitLastLine, trimLastLine }) {
const splitAndMerged = [];
staticParts.forEach((part, i) => {
splitAndMerged.push(...part.split(NEWLINE_REGEXP).map((e, j) => j === 0 || e.length < indentation ? e : e.substring(indentation)).reduce(
// treat the particular (potentially multiple) lines of the <i>th template segment (part),
// s.t. all the effective lines are collected and separated by the NEWLINE node
// note: different reduce functions are provided for the initial template segment vs. the remaining segments
i === 0
? (result, line, j) =>
// special handling of the initial template segment, which may contain line-breaks;
// suppresses the injection of unintended NEWLINE indicators for templates like
// expandToNode`
// someText
// ${something}
// `
j === 0
? (omitFirstLine // for templates with empty first lines like above (expandToNode`\n ...`)
? [] // skip adding the initial line
: [line] // take the initial line if non-empty
)
: (j === 1 && result.length === 0 // when looking on the 2nd line in case the first line (in the first segment) is skipped ('result' is still empty)
? [line] // skip the insertion of the NEWLINE marker and just return the current line
: result.concat(NEWLINE, line) // otherwise append the NEWLINE marker and the current line
)
: (result, line, j) =>
// handling of the remaining template segments
j === 0 ? [line] : result.concat(NEWLINE, line) // except for the first line in the current segment prepend each line with NEWLINE
, [] // start with an empty array
).filter(e => !(typeof e === 'string' && e.length === 0) // drop empty strings, they don't contribute anything but might confuse subsequent processing
).concat(
// append the corresponding substitution after each segment (part),
// note that 'substitutions[i]' will be undefined for the last segment
isGeneratorNode(substitutions[i])
// if the substitution is a generator node, take it as it is
? substitutions[i]
: substitutions[i] !== undefined
// if the substitution is something else, convert it to a string and wrap it;
// allows us below to distinguish template strings from substitution (esp. empty) ones
? { content: String(substitutions[i]) }
: i < substitutions.length
// if 'substitutions[i]' is undefined and we are treating a substitution "in the middle"
// we found a substitution that is assumed to not contribute anything on purpose!
? UNDEFINED_SEGMENT // add a corresponding marker, see below for details on the rational
: [] /* don't concat anything as we passed behind the last substitution, since 'i' enumerates the indices of 'staticParts',
but 'substitutions' has one entry less and 'substitutions[staticParts.length -1 ]' will always be undefined */));
});
// for templates like
// expandToNode`
// someText
// `
// TODO add more documentation here
const splitAndMergedLength = splitAndMerged.length;
const lastItem = splitAndMergedLength !== 0 ? splitAndMerged[splitAndMergedLength - 1] : undefined;
if ((omitLastLine || trimLastLine) && typeof lastItem === 'string' && lastItem.trim().length === 0) {
if (omitFirstLine && splitAndMergedLength !== 1 && splitAndMerged[splitAndMergedLength - 2] === NEWLINE) {
return splitAndMerged.slice(0, splitAndMergedLength - 2);
}
else {
return splitAndMerged.slice(0, splitAndMergedLength - 1);
}
}
else {
return splitAndMerged;
}
}
const NEWLINE = { isNewLine: true };
const UNDEFINED_SEGMENT = { isUndefinedSegment: true };
const isNewLineMarker = (nl) => nl === NEWLINE;
const isUndefinedSegmentMarker = (us) => us === UNDEFINED_SEGMENT;
const isSubstitutionWrapper = (s) => s.content !== undefined;
function composeFinalGeneratorNode(splitAndMerged) {
// in order to properly handle the indentation of nested multi-line substitutions,
// track the length of static (string) parts per line and wrap the substitution(s) in indentation nodes, if needed
//
// of course, this only works nicely if a multi-line substitution is preceded by static string parts on the same line only;
// in case of dynamic content (with a potentially unknown length) followed by a multi-line substitution
// the latter's indentation cannot be determined properly...
const result = splitAndMerged.reduce((res, segment, i) => isUndefinedSegmentMarker(segment)
// ignore all occurrences of UNDEFINED_SEGMENT, they are just in there for the below test
// of 'isNewLineMarker(splitAndMerged[i-1])' not to evaluate to 'truthy' in case of consecutive lines
// with no actual content in templates like
// expandToNode`
// Foo
// ${undefined} <<----- here
// ${undefined} <<----- and here
//
// Bar
// `
? res
: isNewLineMarker(segment)
? {
// in case of a newLine marker append an 'ifNotEmpty' newLine by default, but
// append an unconditional newLine if and only if:
// * the template starts with the current line break, i.e. the first line is empty
// * the current newLine marker directly follows another one, i.e. the current line is empty
// * the current newline marker directly follows a substitution contributing a string (or some non-GeneratorNode being converted to a string)
// * the current newline marker directly follows a (template static) string that
// * is the initial token of the template
// * is the initial token of the line, maybe just indentation
// * follows a a substitution contributing a string (or some non-GeneratorNode being converted to a string), maybe is just irrelevant trailing whitespace
// in particular do _not_ append an unconditional newLine if the last substitution of a line contributes 'undefined' or an instance of 'GeneratorNode'
// which may be a newline itself or be empty or (transitively) contain a trailing newline itself
// node: i === 0
// || isNewLineMarker(splitAndMerged[i - 1]) || isSubstitutionWrapper(splitAndMerged[i - 1]) /* implies: typeof content === 'string', esp. !undefined */
// || typeof splitAndMerged[i - 1] === 'string' && (
// i === 1 || isNewLineMarker(splitAndMerged[i - 2]) || isSubstitutionWrapper(splitAndMerged[i - 2]) /* implies: typeof content === 'string', esp. !undefined */
// )
// ? res.node.appendNewLine() : res.node.appendNewLineIfNotEmpty()
//
// UPDATE cs: inverting the logic leads to the following, I hope I didn't miss anything:
// in case of a newLine marker append an unconditional newLine by default, but
// append an 'ifNotEmpty' newLine if and only if:
// * the template doesn't start with a newLine marker and
// * the current newline marker directly follows a substitution contributing an `undefined` or an instance of 'GeneratorNode', or
// * the current newline marker directly follows a (template static) string (containing potentially unintended trailing whitespace)
// that in turn directly follows a substitution contributing an `undefined` or an instance of 'GeneratorNode'
node: i !== 0 && (isUndefinedSegmentMarker(splitAndMerged[i - 1]) || isGeneratorNode(splitAndMerged[i - 1]))
|| i > 1 && typeof splitAndMerged[i - 1] === 'string' && (isUndefinedSegmentMarker(splitAndMerged[i - 2]) || isGeneratorNode(splitAndMerged[i - 2]))
? res.node.appendNewLineIfNotEmpty() : res.node.appendNewLine()
} : (() => {
// the indentation handling is supposed to handle use cases like
// bla bla bla {
// ${foo(bar)}
// }
// and
// bla bla bla {
// return ${foo(bar)}
// }
// assuming that ${foo(bar)} yields a multiline result;
// the whitespace between 'return' and '${foo(bar)}' shall not add to the indentation of '${foo(bar)}'s result!
const indent = (i === 0 || isNewLineMarker(splitAndMerged[i - 1])) && typeof segment === 'string' && segment.length !== 0 ? segment.substring(0, segment.length - segment.trimStart().length) : '';
const content = isSubstitutionWrapper(segment) ? segment.content : segment;
let indented;
return {
node: res.indented
// in case an indentNode has been registered earlier for the current line,
// just return 'node' without manipulation, the current segment will be added to the indentNode
? res.node
// otherwise (no indentNode is registered by now)...
: indent.length !== 0
// in case an indentation has been identified add a non-immediate indentNode to 'node' and
// add the current segment (containing its the indentation) to that indentNode,
// and keep the indentNode in a local variable 'indented' for registering below,
// and return 'node'
? res.node.indent({ indentation: indent, indentImmediately: false, indentedChildren: ind => indented = ind.append(content) })
// otherwise just add the content to 'node' and return it
: res.node.append(content),
indented:
// if an indentNode has been created in this cycle, just register it,
// otherwise check for a earlier registered indentNode and add the current segment to that one
indented ?? res.indented?.append(content),
};
})(), { node: new CompositeGeneratorNode() });
return result.node;
}
//# sourceMappingURL=template-node.js.map
File diff suppressed because one or more lines are too long
+29
View File
@@ -0,0 +1,29 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
export declare function expandToStringWithNL(staticParts: TemplateStringsArray, ...substitutions: unknown[]): string;
export declare function expandToStringLFWithNL(staticParts: TemplateStringsArray, ...substitutions: unknown[]): string;
/**
* A tag function that automatically aligns embedded multiline strings.
* Multiple lines are joined with the platform-specific line separator.
*
* @param staticParts the static parts of a tagged template literal
* @param substitutions the variable parts of a tagged template literal
* @returns an aligned string that consists of the given parts
*/
export declare function expandToString(staticParts: TemplateStringsArray, ...substitutions: unknown[]): string;
/**
* A tag function that automatically aligns embedded multiline strings.
* Multiple lines are joined with the LINE_FEED (`\n`) line separator.
*
* @param staticParts the static parts of a tagged template literal
* @param substitutions the variable parts of a tagged template literal
* @returns an aligned string that consists of the given parts
*/
export declare function expandToStringLF(staticParts: TemplateStringsArray, ...substitutions: unknown[]): string;
export declare const SNLE: string;
export declare function findIndentation(lines: string[]): number;
export declare function normalizeEOL(input: string): string;
//# sourceMappingURL=template-string.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"template-string.d.ts","sourceRoot":"","sources":["../../src/generate/template-string.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAKhF,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,GAAG,MAAM,CAE3G;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,GAAG,MAAM,CAE7G;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,GAAG,MAAM,CAErG;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,oBAAoB,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,GAAG,MAAM,CAEvG;AAyCD,eAAO,MAAM,IAAI,QAAgD,CAAC;AAWlE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAIvD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD"}
+87
View File
@@ -0,0 +1,87 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { NEWLINE_REGEXP } from '../utils/regexp-utils.js';
import { EOL, toString } from './generator-node.js';
export function expandToStringWithNL(staticParts, ...substitutions) {
return expandToString(staticParts, ...substitutions) + EOL;
}
export function expandToStringLFWithNL(staticParts, ...substitutions) {
return expandToStringLF(staticParts, ...substitutions) + '\n';
}
/**
* A tag function that automatically aligns embedded multiline strings.
* Multiple lines are joined with the platform-specific line separator.
*
* @param staticParts the static parts of a tagged template literal
* @param substitutions the variable parts of a tagged template literal
* @returns an aligned string that consists of the given parts
*/
export function expandToString(staticParts, ...substitutions) {
return internalExpandToString(EOL, staticParts, ...substitutions);
}
/**
* A tag function that automatically aligns embedded multiline strings.
* Multiple lines are joined with the LINE_FEED (`\n`) line separator.
*
* @param staticParts the static parts of a tagged template literal
* @param substitutions the variable parts of a tagged template literal
* @returns an aligned string that consists of the given parts
*/
export function expandToStringLF(staticParts, ...substitutions) {
return internalExpandToString('\n', staticParts, ...substitutions);
}
function internalExpandToString(lineSep, staticParts, ...substitutions) {
let lines = substitutions
// align substitutions and fuse them with static parts
.reduce((acc, subst, i) => acc + (subst === undefined ? SNLE : align(toString(subst), acc)) + (staticParts[i + 1] ?? ''), staticParts[0])
// converts text to lines
.split(NEWLINE_REGEXP)
.filter(l => l.trim() !== SNLE)
// whitespace-only lines are empty (preserving leading whitespace)
.map(l => l.replace(SNLE, '').trimEnd());
// in order to nicely handle single line templates with the leading and trailing termintators (``) on separate lines, like
// expandToString`foo
// `,
// expandToString`
// foo
// `,
// expandToString`
// foo`,
// the same way as true single line templates like
// expandToString`foo`
// ...
// ... drop initial linebreak if the first line is empty or contains white space only, ...
const containsLeadingLinebreak = lines.length > 1 && lines[0].trim().length === 0;
lines = containsLeadingLinebreak ? lines.slice(1) : lines;
// .. and drop the last linebreak if it's the last charactor or is followed by white space
const containsTrailingLinebreak = lines.length !== 0 && lines[lines.length - 1].trimEnd().length === 0;
lines = containsTrailingLinebreak ? lines.slice(0, lines.length - 1) : lines;
// finds the minimum indentation
const indent = findIndentation(lines);
return lines
// shifts lines to the left
.map(line => line.slice(indent).trimEnd())
// convert lines to string
.join(lineSep);
}
export const SNLE = Object.freeze('__«SKIP^NEW^LINE^IF^EMPTY»__');
const nonWhitespace = /\S|$/;
// add the alignment of the previous static part to all lines of the following substitution
function align(subst, acc) {
const length = Math.max(0, acc.length - acc.lastIndexOf('\n') - 1);
const indent = ' '.repeat(length);
return subst.replace(NEWLINE_REGEXP, EOL + indent);
}
// finds the indentation of a text block represented by a sequence of lines
export function findIndentation(lines) {
const indents = lines.filter(line => line.length > 0).map(line => line.search(nonWhitespace));
const min = indents.length === 0 ? 0 : Math.min(...indents); // min(...[]) = min() = Infinity
return Math.max(0, min);
}
export function normalizeEOL(input) {
return input.replace(NEWLINE_REGEXP, EOL);
}
//# sourceMappingURL=template-string.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"template-string.js","sourceRoot":"","sources":["../../src/generate/template-string.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { AstReflection } from '../syntax-tree.js';
import type { LangiumCoreServices } from '../index.js';
import type { Grammar } from '../languages/generated/ast.js';
import type { AstTypes } from './type-system/type-collector/types.js';
export declare function interpretAstReflection(astTypes: AstTypes): AstReflection;
export declare function interpretAstReflection(grammar: Grammar, services?: LangiumCoreServices): AstReflection;
//# sourceMappingURL=ast-reflection-interpreter.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"ast-reflection-interpreter.d.ts","sourceRoot":"","sources":["../../src/grammar/ast-reflection-interpreter.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,aAAa,EAAkC,MAAM,mBAAmB,CAAC;AACvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,uCAAuC,CAAC;AAOhF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,GAAG,aAAa,CAAC;AAC1E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,mBAAmB,GAAG,aAAa,CAAC"}
+127
View File
@@ -0,0 +1,127 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { AbstractAstReflection } from '../syntax-tree.js';
import { MultiMap } from '../utils/collections.js';
import { isGrammar } from '../languages/generated/ast.js';
import { collectAst } from './type-system/ast-collector.js';
import { collectTypeHierarchy, findReferenceTypes, isAstType, mergeTypesAndInterfaces } from './type-system/types-util.js';
export function interpretAstReflection(grammarOrTypes, services) {
let collectedTypes;
if (isGrammar(grammarOrTypes)) {
collectedTypes = collectAst(grammarOrTypes, { services });
}
else {
collectedTypes = grammarOrTypes;
}
const allTypes = collectedTypes.interfaces.map(e => e.name).concat(collectedTypes.unions.filter(e => isAstType(e.type)).map(e => e.name));
const references = buildReferenceTypes(collectedTypes);
const metaData = buildTypeMetaData(collectedTypes);
const superTypes = collectTypeHierarchy(mergeTypesAndInterfaces(collectedTypes)).superTypes;
return new InterpretedAstReflection({
allTypes,
references,
metaData,
superTypes
});
}
class InterpretedAstReflection extends AbstractAstReflection {
constructor(options) {
// Build the types object required by AbstractAstReflection
const types = {};
for (const typeName of options.allTypes) {
const typeMetaData = options.metaData.get(typeName);
if (typeMetaData) {
const properties = {};
// Convert properties array to object and add reference types
if (Array.isArray(typeMetaData.properties)) {
for (const prop of typeMetaData.properties) {
const referenceKey = `${typeName}:${prop.name}`;
const referenceType = options.references.get(referenceKey);
properties[prop.name] = {
name: prop.name,
defaultValue: prop.defaultValue,
...(referenceType && { referenceType })
};
}
}
else {
// If properties is already an object, copy it and add reference types
for (const [propName, prop] of Object.entries(typeMetaData.properties)) {
const referenceKey = `${typeName}:${propName}`;
const referenceType = options.references.get(referenceKey);
properties[propName] = {
...prop,
...(referenceType && { referenceType })
};
}
}
types[typeName] = {
name: typeName,
properties,
superTypes: Array.from(options.superTypes.get(typeName))
};
}
}
super();
// Initialize the readonly types field
Object.defineProperty(this, 'types', { value: types });
}
computeIsSubtype(subtype, originalSuperType) {
const typeMetaData = this.types[subtype];
if (!typeMetaData) {
return false;
}
for (const superType of typeMetaData.superTypes) {
if (this.isSubtype(superType, originalSuperType)) {
return true;
}
}
return false;
}
}
function buildReferenceTypes(astTypes) {
const references = new MultiMap();
for (const interfaceType of astTypes.interfaces) {
for (const property of interfaceType.properties) {
for (const referenceType of findReferenceTypes(property.type)) {
references.add(interfaceType.name, [property.name, referenceType]);
}
}
for (const superType of interfaceType.interfaceSuperTypes) {
const superTypeReferences = references.get(superType.name);
references.addAll(interfaceType.name, superTypeReferences);
}
}
const map = new Map();
for (const [type, [property, target]] of references) {
map.set(`${type}:${property}`, target);
}
return map;
}
function buildTypeMetaData(astTypes) {
const map = new Map();
for (const interfaceType of astTypes.interfaces) {
const props = interfaceType.superProperties;
map.set(interfaceType.name, {
name: interfaceType.name,
properties: buildPropertyMetaData(props),
superTypes: [] // Will be populated later from superTypes data
});
}
return map;
}
function buildPropertyMetaData(props) {
const properties = {};
const all = props.sort((a, b) => a.name.localeCompare(b.name));
for (const property of all) {
properties[property.name] = {
name: property.name,
defaultValue: property.defaultValue
};
}
return properties;
}
//# sourceMappingURL=ast-reflection-interpreter.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ast-reflection-interpreter.js","sourceRoot":"","sources":["../../src/grammar/ast-reflection-interpreter.ts"],"names":[],"mappings":"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"}
+7
View File
@@ -0,0 +1,7 @@
/******************************************************************************
* This file was generated by langium-cli 4.2.1.
* DO NOT EDIT MANUALLY!
******************************************************************************/
import type { Grammar } from '../../languages/generated/ast.js';
export declare const LangiumGrammarGrammar: () => Grammar;
//# sourceMappingURL=grammar.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../../../src/grammar/generated/grammar.ts"],"names":[],"mappings":"AAAA;;;gFAGgF;AAEhF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAIhE,eAAO,MAAM,qBAAqB,QAAO,OAAgz5D,CAAC"}
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"grammar.js","sourceRoot":"","sources":["../../../src/grammar/generated/grammar.ts"],"names":[],"mappings":"AAAA;;;gFAGgF;AAGhF,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEpE,IAAI,2BAAgD,CAAC;AACrD,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAY,EAAE,CAAC,2BAA2B,IAAI,CAAC,2BAA2B,GAAG,mBAAmB,CAAC,it5DAAit5D,CAAC,CAAC,CAAC"}
+17
View File
@@ -0,0 +1,17 @@
/******************************************************************************
* This file was generated by langium-cli 4.2.1.
* DO NOT EDIT MANUALLY!
******************************************************************************/
import type { Module } from '../../dependency-injection.js';
import type { LangiumSharedCoreServices, LangiumCoreServices, LangiumGeneratedCoreServices, LangiumGeneratedSharedCoreServices } from '../../services.js';
import type { IParserConfig } from '../../parser/parser-config.js';
export declare const LangiumGrammarLanguageMetaData: {
readonly languageId: "langium";
readonly fileExtensions: readonly [".langium"];
readonly caseInsensitive: false;
readonly mode: "production";
};
export declare const LangiumGrammarParserConfig: IParserConfig;
export declare const LangiumGrammarGeneratedSharedModule: Module<LangiumSharedCoreServices, LangiumGeneratedSharedCoreServices>;
export declare const LangiumGrammarGeneratedModule: Module<LangiumCoreServices, LangiumGeneratedCoreServices>;
//# sourceMappingURL=module.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../src/grammar/generated/module.ts"],"names":[],"mappings":"AAAA;;;gFAGgF;AAIhF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,KAAK,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,4BAA4B,EAAE,kCAAkC,EAAE,MAAM,mBAAmB,CAAC;AAC1J,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAGnE,eAAO,MAAM,8BAA8B;;;;;CAKN,CAAC;AAEtC,eAAO,MAAM,0BAA0B,EAAE,aAExC,CAAC;AAEF,eAAO,MAAM,mCAAmC,EAAE,MAAM,CAAC,yBAAyB,EAAE,kCAAkC,CAErH,CAAC;AAEF,eAAO,MAAM,6BAA6B,EAAE,MAAM,CAAC,mBAAmB,EAAE,4BAA4B,CAMnG,CAAC"}
+26
View File
@@ -0,0 +1,26 @@
/******************************************************************************
* This file was generated by langium-cli 4.2.1.
* DO NOT EDIT MANUALLY!
******************************************************************************/
import { LangiumGrammarAstReflection } from '../../languages/generated/ast.js';
import { LangiumGrammarGrammar } from './grammar.js';
export const LangiumGrammarLanguageMetaData = {
languageId: 'langium',
fileExtensions: ['.langium'],
caseInsensitive: false,
mode: 'production'
};
export const LangiumGrammarParserConfig = {
maxLookahead: 3,
};
export const LangiumGrammarGeneratedSharedModule = {
AstReflection: () => new LangiumGrammarAstReflection()
};
export const LangiumGrammarGeneratedModule = {
Grammar: () => LangiumGrammarGrammar(),
LanguageMetaData: () => LangiumGrammarLanguageMetaData,
parser: {
ParserConfig: () => LangiumGrammarParserConfig
}
};
//# sourceMappingURL=module.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"module.js","sourceRoot":"","sources":["../../../src/grammar/generated/module.ts"],"names":[],"mappings":"AAAA;;;gFAGgF;AAGhF,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAI/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC1C,UAAU,EAAE,SAAS;IACrB,cAAc,EAAE,CAAC,UAAU,CAAC;IAC5B,eAAe,EAAE,KAAK;IACtB,IAAI,EAAE,YAAY;CACe,CAAC;AAEtC,MAAM,CAAC,MAAM,0BAA0B,GAAkB;IACrD,YAAY,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,MAAM,mCAAmC,GAA0E;IACtH,aAAa,EAAE,GAAG,EAAE,CAAC,IAAI,2BAA2B,EAAE;CACzD,CAAC;AAEF,MAAM,CAAC,MAAM,6BAA6B,GAA8D;IACpG,OAAO,EAAE,GAAG,EAAE,CAAC,qBAAqB,EAAE;IACtC,gBAAgB,EAAE,GAAG,EAAE,CAAC,8BAA8B;IACtD,MAAM,EAAE;QACJ,YAAY,EAAE,GAAG,EAAE,CAAC,0BAA0B;KACjD;CACJ,CAAC"}
+27
View File
@@ -0,0 +1,27 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
*
* @module langium/grammar
*/
export * from './generated/grammar.js';
export * from './generated/module.js';
export * from './lsp/grammar-call-hierarchy.js';
export * from './lsp/grammar-code-actions.js';
export * from './lsp/grammar-completion-provider.js';
export * from './lsp/grammar-definition.js';
export * from './lsp/grammar-folding-ranges.js';
export * from './lsp/grammar-formatter.js';
export * from './lsp/grammar-semantic-tokens.js';
export * from './references/grammar-naming.js';
export * from './references/grammar-references.js';
export * from './references/grammar-scope.js';
export * from './validation/types-validator.js';
export * from './validation/validation-resources-collector.js';
export * from './validation/validator.js';
export * from './type-system/index.js';
export * from './langium-grammar-module.js';
export * from './internal-grammar-util.js';
export * from './ast-reflection-interpreter.js';
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/grammar/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iCAAiC,CAAC;AAChD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iCAAiC,CAAC;AAChD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC"}
+30
View File
@@ -0,0 +1,30 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
*
* @module langium/grammar
*/
// This file contains Langium grammar language internals.
// It is not supposed to be exported with the general `langium` export.
// Instead, it is available from `langium/grammar`.
export * from './generated/grammar.js';
export * from './generated/module.js';
export * from './lsp/grammar-call-hierarchy.js';
export * from './lsp/grammar-code-actions.js';
export * from './lsp/grammar-completion-provider.js';
export * from './lsp/grammar-definition.js';
export * from './lsp/grammar-folding-ranges.js';
export * from './lsp/grammar-formatter.js';
export * from './lsp/grammar-semantic-tokens.js';
export * from './references/grammar-naming.js';
export * from './references/grammar-references.js';
export * from './references/grammar-scope.js';
export * from './validation/types-validator.js';
export * from './validation/validation-resources-collector.js';
export * from './validation/validator.js';
export * from './type-system/index.js';
export * from './langium-grammar-module.js';
export * from './internal-grammar-util.js';
export * from './ast-reflection-interpreter.js';
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/grammar/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,yDAAyD;AACzD,uEAAuE;AACvE,mDAAmD;AAEnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iCAAiC,CAAC;AAChD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iCAAiC,CAAC;AAChD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC"}
+35
View File
@@ -0,0 +1,35 @@
/******************************************************************************
* Copyright 2021-2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { URI } from '../utils/uri-utils.js';
import type { LangiumDocuments } from '../workspace/documents.js';
import * as ast from '../languages/generated/ast.js';
import type { LangiumGrammarServices } from './langium-grammar-module.js';
import type { IParserConfig } from '../parser/parser-config.js';
import type { LanguageMetaData } from '../languages/language-meta-data.js';
import type { Module } from '../dependency-injection.js';
import type { LangiumServices, LangiumSharedServices } from '../lsp/lsp-services.js';
export declare function hasDataTypeReturn(rule: ast.ParserRule): boolean;
export declare function isStringGrammarType(type: ast.AbstractType | ast.TypeDefinition): boolean;
export declare function getTypeNameWithoutError(type?: ast.AbstractType | ast.Action): string | undefined;
export declare function resolveImportUri(imp: ast.GrammarImport): URI | undefined;
export declare function resolveImport(documents: LangiumDocuments, imp: ast.GrammarImport): ast.Grammar | undefined;
export declare function resolveTransitiveImports(documents: LangiumDocuments, grammar: ast.Grammar): ast.Grammar[];
export declare function resolveTransitiveImports(documents: LangiumDocuments, importNode: ast.GrammarImport): ast.Grammar[];
export declare function extractAssignments(element: ast.AbstractElement): ast.Assignment[];
export declare function isPrimitiveGrammarType(type: string): boolean;
/**
* Create an instance of the language services for the given grammar. This function is very
* useful when the grammar is defined on-the-fly, for example in tests of the Langium framework.
*/
export declare function createServicesForGrammar<L extends LangiumServices = LangiumServices, S extends LangiumSharedServices = LangiumSharedServices>(config: {
grammar: string | ast.Grammar;
grammarServices?: LangiumGrammarServices;
parserConfig?: IParserConfig;
languageMetaData?: LanguageMetaData;
module?: Module<L, unknown>;
sharedModule?: Module<S, unknown>;
}): Promise<L>;
//# sourceMappingURL=internal-grammar-util.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"internal-grammar-util.d.ts","sourceRoot":"","sources":["../../src/grammar/internal-grammar-util.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,KAAK,GAAG,MAAM,+BAA+B,CAAC;AAGrD,OAAO,KAAK,EAAE,sBAAsB,EAAC,MAAM,6BAA6B,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,KAAK,EAAE,MAAM,EAAC,MAAM,4BAA4B,CAAC;AAGxD,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAMrF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,GAAG,CAAC,UAAU,GAAG,OAAO,CAG/D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,cAAc,GAAG,OAAO,CAExF;AAmCD,wBAAgB,uBAAuB,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,SAAS,CAShG;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,aAAa,GAAG,GAAG,GAAG,SAAS,CAUxE;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,gBAAgB,EAAE,GAAG,EAAE,GAAG,CAAC,aAAa,GAAG,GAAG,CAAC,OAAO,GAAG,SAAS,CAc1G;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,gBAAgB,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,EAAE,CAAA;AAC1G,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,gBAAgB,EAAE,UAAU,EAAE,GAAG,CAAC,aAAa,GAAG,GAAG,CAAC,OAAO,EAAE,CAAA;AA2CnH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,UAAU,EAAE,CAYjF;AAID,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,EAAE,CAAC,SAAS,qBAAqB,GAAG,qBAAqB,EAAE,MAAM,EAAE;IACzJ,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC;IAC9B,eAAe,CAAC,EAAE,sBAAsB,CAAC;IACzC,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;CACpC,GAAG,OAAO,CAAC,CAAC,CAAC,CAkCb"}
+198
View File
@@ -0,0 +1,198 @@
/******************************************************************************
* Copyright 2021-2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { URI } from '../utils/uri-utils.js';
import * as ast from '../languages/generated/ast.js';
import { getDocument } from '../utils/ast-utils.js';
import { UriUtils } from '../utils/uri-utils.js';
import { createLangiumGrammarServices } from './langium-grammar-module.js';
import { inject } from '../dependency-injection.js';
import { createDefaultModule, createDefaultSharedModule } from '../lsp/default-lsp-module.js';
import { EmptyFileSystem } from '../workspace/file-system-provider.js';
import { interpretAstReflection } from './ast-reflection-interpreter.js';
import { getTypeName, isDataType } from '../utils/grammar-utils.js';
export function hasDataTypeReturn(rule) {
const returnType = rule.returnType?.ref;
return rule.dataType !== undefined || (ast.isType(returnType) && isDataType(returnType));
}
export function isStringGrammarType(type) {
return isStringTypeInternal(type, new Set());
}
function isStringTypeInternal(type, visited) {
if (visited.has(type)) {
return true;
}
else {
visited.add(type);
}
if (ast.isParserRule(type)) {
if (type.dataType) {
return type.dataType === 'string';
}
if (type.returnType?.ref) {
return isStringTypeInternal(type.returnType.ref, visited);
}
}
else if (ast.isType(type)) {
return isStringTypeInternal(type.type, visited);
}
else if (ast.isArrayType(type)) {
return false;
}
else if (ast.isReferenceType(type)) {
return false;
}
else if (ast.isUnionType(type)) {
return type.types.every(e => isStringTypeInternal(e, visited));
}
else if (ast.isSimpleType(type)) {
if (type.primitiveType === 'string') {
return true;
}
else if (type.stringType) {
return true;
}
else if (type.typeRef?.ref) {
return isStringTypeInternal(type.typeRef.ref, visited);
}
}
return false;
}
export function getTypeNameWithoutError(type) {
if (!type) {
return undefined;
}
try {
return getTypeName(type);
}
catch {
return undefined;
}
}
export function resolveImportUri(imp) {
if (imp.path === undefined || imp.path.length === 0) {
return undefined;
}
const dirUri = UriUtils.dirname(getDocument(imp).uri);
let grammarPath = imp.path;
if (!grammarPath.endsWith('.langium')) {
grammarPath += '.langium';
}
return UriUtils.resolvePath(dirUri, grammarPath);
}
export function resolveImport(documents, imp) {
const resolvedUri = resolveImportUri(imp);
if (!resolvedUri) {
return undefined;
}
const resolvedDocument = documents.getDocument(resolvedUri);
if (!resolvedDocument) {
return undefined;
}
const node = resolvedDocument.parseResult.value;
if (ast.isGrammar(node)) {
return node;
}
return undefined;
}
export function resolveTransitiveImports(documents, grammarOrImport) {
if (ast.isGrammarImport(grammarOrImport)) {
const resolvedGrammar = resolveImport(documents, grammarOrImport);
if (resolvedGrammar) {
const transitiveGrammars = resolveTransitiveImportsInternal(documents, resolvedGrammar);
transitiveGrammars.push(resolvedGrammar);
return transitiveGrammars;
}
return [];
}
else {
return resolveTransitiveImportsInternal(documents, grammarOrImport);
}
}
/**
* Resolves all transitively imported grammars of the given grammar.
* In case of grammars importing each other in circular way, each grammar is remembered only once.
* The initial grammar will never be part of the result.
* @param documents the service to get all available Langium documents
* @param grammar the grammar to transitively resolve its imported grammars
* @param initialGrammar Even if the initial grammar transitively imports itself in circular way again, the initial grammar will not be part of the result!
* @param visited since grammars might import each other in circular way, this set remembers the already visited gramar URIs to prevent loops
* @param grammars the result set of already imported and resolved grammars
* @returns the collected `grammars` in a new array
*/
function resolveTransitiveImportsInternal(documents, grammar, initialGrammar = grammar, visited = new Set(), grammars = new Set()) {
const doc = getDocument(grammar);
if (initialGrammar !== grammar) {
grammars.add(grammar);
}
if (!visited.has(doc.uri)) {
visited.add(doc.uri);
for (const imp of grammar.imports) {
const importedGrammar = resolveImport(documents, imp);
if (importedGrammar) {
resolveTransitiveImportsInternal(documents, importedGrammar, initialGrammar, visited, grammars);
}
}
}
return Array.from(grammars);
}
export function extractAssignments(element) {
if (ast.isAssignment(element)) {
return [element];
}
else if (ast.isAlternatives(element) || ast.isGroup(element) || ast.isUnorderedGroup(element)) {
return element.elements.flatMap(e => extractAssignments(e));
}
else if (ast.isRuleCall(element) && element.rule.ref) {
if (ast.isInfixRule(element.rule.ref)) {
return [];
}
return extractAssignments(element.rule.ref.definition);
}
return [];
}
const primitiveTypes = ['string', 'number', 'boolean', 'Date', 'bigint'];
export function isPrimitiveGrammarType(type) {
return primitiveTypes.includes(type);
}
/**
* Create an instance of the language services for the given grammar. This function is very
* useful when the grammar is defined on-the-fly, for example in tests of the Langium framework.
*/
export async function createServicesForGrammar(config) {
const grammarServices = config.grammarServices ?? createLangiumGrammarServices(EmptyFileSystem).grammar;
const uri = URI.parse('memory:/grammar.langium');
const factory = grammarServices.shared.workspace.LangiumDocumentFactory;
const grammarDocument = typeof config.grammar === 'string'
? factory.fromString(config.grammar, uri)
: getDocument(config.grammar);
const grammarNode = grammarDocument.parseResult.value;
const documentBuilder = grammarServices.shared.workspace.DocumentBuilder;
await documentBuilder.build([grammarDocument], { validation: false });
const parserConfig = config.parserConfig ?? {
skipValidations: false
};
const languageMetaData = config.languageMetaData ?? {
caseInsensitive: false,
fileExtensions: ['.txt'],
languageId: grammarNode.name ?? 'UNKNOWN',
mode: 'development'
};
const generatedSharedModule = {
AstReflection: () => interpretAstReflection(grammarNode),
};
const generatedModule = {
Grammar: () => grammarNode,
LanguageMetaData: () => languageMetaData,
parser: {
ParserConfig: () => parserConfig
}
};
const shared = inject(createDefaultSharedModule(EmptyFileSystem), generatedSharedModule, config.sharedModule);
const services = inject(createDefaultModule({ shared }), generatedModule, config.module);
shared.ServiceRegistry.register(services);
return services;
}
//# sourceMappingURL=internal-grammar-util.js.map
File diff suppressed because one or more lines are too long
+34
View File
@@ -0,0 +1,34 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { Module } from '../dependency-injection.js';
import type { DeepPartial } from '../services.js';
import type { LangiumServices, LangiumSharedServices, PartialLangiumServices, PartialLangiumSharedServices } from '../lsp/lsp-services.js';
import { type DefaultSharedModuleContext } from '../lsp/default-lsp-module.js';
import { LangiumGrammarValidator } from './validation/validator.js';
import { LangiumGrammarValidationResourcesCollector } from './validation/validation-resources-collector.js';
import { LangiumGrammarTypesValidator } from './validation/types-validator.js';
export type LangiumGrammarAddedServices = {
validation: {
LangiumGrammarValidator: LangiumGrammarValidator;
ValidationResourcesCollector: LangiumGrammarValidationResourcesCollector;
LangiumGrammarTypesValidator: LangiumGrammarTypesValidator;
};
};
export type LangiumGrammarServices = LangiumServices & LangiumGrammarAddedServices;
export declare const LangiumGrammarModule: Module<LangiumGrammarServices, PartialLangiumServices & LangiumGrammarAddedServices>;
/**
* Creates Langium grammar services, enriched with LSP functionality
*
* @param context Shared module context, used to create additional shared modules
* @param sharedModule Existing shared module to inject together with new shared services
* @param module Additional/modified service implementations for the language services
* @returns Shared services enriched with LSP services + Grammar services, per usual
*/
export declare function createLangiumGrammarServices(context: DefaultSharedModuleContext, sharedModule?: Module<LangiumSharedServices, PartialLangiumSharedServices>, module?: Module<LangiumGrammarServices, DeepPartial<LangiumServices & LangiumGrammarAddedServices>>): {
shared: LangiumSharedServices;
grammar: LangiumGrammarServices;
};
//# sourceMappingURL=langium-grammar-module.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"langium-grammar-module.d.ts","sourceRoot":"","sources":["../../src/grammar/langium-grammar-module.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAI3I,OAAO,EAAE,KAAK,0BAA0B,EAAkD,MAAM,8BAA8B,CAAC;AAI/H,OAAO,EAAE,uBAAuB,EAA4B,MAAM,2BAA2B,CAAC;AAU9F,OAAO,EAAE,0CAA0C,EAAE,MAAM,gDAAgD,CAAC;AAC5G,OAAO,EAAE,4BAA4B,EAAgC,MAAM,iCAAiC,CAAC;AAG7G,MAAM,MAAM,2BAA2B,GAAG;IACtC,UAAU,EAAE;QACR,uBAAuB,EAAE,uBAAuB,CAAC;QACjD,4BAA4B,EAAE,0CAA0C,CAAC;QACzE,4BAA4B,EAAE,4BAA4B,CAAC;KAC9D,CAAA;CACJ,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG,eAAe,GAAG,2BAA2B,CAAC;AAEnF,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,sBAAsB,EAAE,sBAAsB,GAAG,2BAA2B,CAsBrH,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,0BAA0B,EAC5E,YAAY,CAAC,EAAE,MAAM,CAAC,qBAAqB,EAAE,4BAA4B,CAAC,EAC1E,MAAM,CAAC,EAAE,MAAM,CAAC,sBAAsB,EAAE,WAAW,CAAC,eAAe,GAAG,2BAA2B,CAAC,CAAC,GAAG;IACtG,MAAM,EAAE,qBAAqB,CAAC;IAC9B,OAAO,EAAE,sBAAsB,CAAA;CAClC,CAyBA"}
+77
View File
@@ -0,0 +1,77 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { LangiumGrammarTypeHierarchyProvider } from './lsp/grammar-type-hierarchy.js';
import { createDefaultModule, createDefaultSharedModule } from '../lsp/default-lsp-module.js';
import { inject } from '../dependency-injection.js';
import { LangiumGrammarGeneratedModule, LangiumGrammarGeneratedSharedModule } from './generated/module.js';
import { LangiumGrammarScopeComputation, LangiumGrammarScopeProvider } from './references/grammar-scope.js';
import { LangiumGrammarValidator, registerValidationChecks } from './validation/validator.js';
import { LangiumGrammarCodeActionProvider } from './lsp/grammar-code-actions.js';
import { LangiumGrammarCompletionProvider } from './lsp/grammar-completion-provider.js';
import { LangiumGrammarFoldingRangeProvider } from './lsp/grammar-folding-ranges.js';
import { LangiumGrammarFormatter } from './lsp/grammar-formatter.js';
import { LangiumGrammarSemanticTokenProvider } from './lsp/grammar-semantic-tokens.js';
import { LangiumGrammarNameProvider } from './references/grammar-naming.js';
import { LangiumGrammarReferences } from './references/grammar-references.js';
import { LangiumGrammarDefinitionProvider } from './lsp/grammar-definition.js';
import { LangiumGrammarCallHierarchyProvider } from './lsp/grammar-call-hierarchy.js';
import { LangiumGrammarValidationResourcesCollector } from './validation/validation-resources-collector.js';
import { LangiumGrammarTypesValidator, registerTypeValidationChecks } from './validation/types-validator.js';
import { DocumentState } from '../workspace/documents.js';
export const LangiumGrammarModule = {
validation: {
LangiumGrammarValidator: (services) => new LangiumGrammarValidator(services),
ValidationResourcesCollector: (services) => new LangiumGrammarValidationResourcesCollector(services),
LangiumGrammarTypesValidator: () => new LangiumGrammarTypesValidator(),
},
lsp: {
FoldingRangeProvider: (services) => new LangiumGrammarFoldingRangeProvider(services),
CodeActionProvider: (services) => new LangiumGrammarCodeActionProvider(services),
SemanticTokenProvider: (services) => new LangiumGrammarSemanticTokenProvider(services),
Formatter: () => new LangiumGrammarFormatter(),
DefinitionProvider: (services) => new LangiumGrammarDefinitionProvider(services),
CallHierarchyProvider: (services) => new LangiumGrammarCallHierarchyProvider(services),
TypeHierarchyProvider: (services) => new LangiumGrammarTypeHierarchyProvider(services),
CompletionProvider: (services) => new LangiumGrammarCompletionProvider(services)
},
references: {
ScopeComputation: (services) => new LangiumGrammarScopeComputation(services),
ScopeProvider: (services) => new LangiumGrammarScopeProvider(services),
References: (services) => new LangiumGrammarReferences(services),
NameProvider: () => new LangiumGrammarNameProvider()
}
};
/**
* Creates Langium grammar services, enriched with LSP functionality
*
* @param context Shared module context, used to create additional shared modules
* @param sharedModule Existing shared module to inject together with new shared services
* @param module Additional/modified service implementations for the language services
* @returns Shared services enriched with LSP services + Grammar services, per usual
*/
export function createLangiumGrammarServices(context, sharedModule, module) {
const shared = inject(createDefaultSharedModule(context), LangiumGrammarGeneratedSharedModule, sharedModule);
const grammar = inject(createDefaultModule({ shared }), LangiumGrammarGeneratedModule, LangiumGrammarModule, module);
addTypeCollectionPhase(shared, grammar);
shared.ServiceRegistry.register(grammar);
registerValidationChecks(grammar);
registerTypeValidationChecks(grammar);
if (!context.connection) {
// We don't run inside a language server
// Therefore, initialize the configuration provider instantly
shared.workspace.ConfigurationProvider.initialized({});
}
return { shared, grammar };
}
function addTypeCollectionPhase(sharedServices, grammarServices) {
const documentBuilder = sharedServices.workspace.DocumentBuilder;
documentBuilder.onDocumentPhase(DocumentState.IndexedReferences, async (document) => {
const typeCollector = grammarServices.validation.ValidationResourcesCollector;
const grammar = document.parseResult.value;
document.validationResources = typeCollector.collectValidationResources(grammar);
});
}
//# sourceMappingURL=langium-grammar-module.js.map
@@ -0,0 +1 @@
{"version":3,"file":"langium-grammar-module.js","sourceRoot":"","sources":["../../src/grammar/langium-grammar-module.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { CallHierarchyIncomingCall, CallHierarchyOutgoingCall } from 'vscode-languageserver';
import type { AstNode } from '../../syntax-tree.js';
import type { Stream } from '../../utils/stream.js';
import type { ReferenceDescription } from '../../workspace/ast-descriptions.js';
import { AbstractCallHierarchyProvider } from '../../lsp/call-hierarchy-provider.js';
export declare class LangiumGrammarCallHierarchyProvider extends AbstractCallHierarchyProvider {
protected getIncomingCalls(node: AstNode, references: Stream<ReferenceDescription>): CallHierarchyIncomingCall[] | undefined;
protected getOutgoingCalls(node: AstNode): CallHierarchyOutgoingCall[] | undefined;
}
//# sourceMappingURL=grammar-call-hierarchy.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"grammar-call-hierarchy.d.ts","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-call-hierarchy.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,yBAAyB,EAAE,yBAAyB,EAAS,MAAM,uBAAuB,CAAC;AACzG,OAAO,KAAK,EAAE,OAAO,EAAW,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAEhF,OAAO,EAAE,6BAA6B,EAAE,MAAM,sCAAsC,CAAC;AAKrF,qBAAa,mCAAoC,SAAQ,6BAA6B;IAElF,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,oBAAoB,CAAC,GAAG,yBAAyB,EAAE,GAAG,SAAS;IAiD5H,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,yBAAyB,EAAE,GAAG,SAAS;CAmErF"}
+128
View File
@@ -0,0 +1,128 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { SymbolKind } from 'vscode-languageserver';
import { AbstractCallHierarchyProvider } from '../../lsp/call-hierarchy-provider.js';
import { getContainerOfType, getDocument, streamAllContents } from '../../utils/ast-utils.js';
import { findLeafNodeAtOffset } from '../../utils/cst-utils.js';
import { isAbstractParserRule, isInfixRule, isParserRule, isRuleCall } from '../../languages/generated/ast.js';
export class LangiumGrammarCallHierarchyProvider extends AbstractCallHierarchyProvider {
getIncomingCalls(node, references) {
if (!isAbstractParserRule(node)) {
return undefined;
}
// This map is used to group incoming calls to avoid duplicates.
const uniqueRules = new Map();
references.forEach(ref => {
const doc = this.documents.getDocument(ref.sourceUri);
if (!doc) {
return;
}
const rootNode = doc.parseResult.value;
if (!rootNode.$cstNode) {
return;
}
const targetNode = findLeafNodeAtOffset(rootNode.$cstNode, ref.segment.offset);
if (!targetNode) {
return;
}
const parserRule = getContainerOfType(targetNode.astNode, isAbstractParserRule);
if (!parserRule || !parserRule.$cstNode) {
return;
}
const nameNode = this.nameProvider.getNameNode(parserRule);
if (!nameNode) {
return;
}
const refDocUri = ref.sourceUri.toString();
const ruleId = refDocUri + '@' + nameNode.text;
uniqueRules.has(ruleId) ?
uniqueRules.set(ruleId, { parserRule: parserRule.$cstNode, nameNode, targetNodes: [...uniqueRules.get(ruleId).targetNodes, targetNode], docUri: refDocUri })
: uniqueRules.set(ruleId, { parserRule: parserRule.$cstNode, nameNode, targetNodes: [targetNode], docUri: refDocUri });
});
if (uniqueRules.size === 0) {
return undefined;
}
return Array.from(uniqueRules.values()).map(rule => ({
from: {
kind: SymbolKind.Method,
name: rule.nameNode.text,
range: rule.parserRule.range,
selectionRange: rule.nameNode.range,
uri: rule.docUri
},
fromRanges: rule.targetNodes.map(node => node.range)
}));
}
getOutgoingCalls(node) {
if (isParserRule(node)) {
const ruleCalls = streamAllContents(node).filter(isRuleCall).toArray();
// This map is used to group outgoing calls to avoid duplicates.
const uniqueRules = new Map();
ruleCalls.forEach(ruleCall => {
const cstNode = ruleCall.$cstNode;
if (!cstNode) {
return;
}
const refCstNode = ruleCall.rule.ref?.$cstNode;
if (!refCstNode) {
return;
}
const refNameNode = this.nameProvider.getNameNode(refCstNode.astNode);
if (!refNameNode) {
return;
}
const refDocUri = getDocument(refCstNode.astNode).uri.toString();
const ruleId = refDocUri + '@' + refNameNode.text;
uniqueRules.has(ruleId) ?
uniqueRules.set(ruleId, { refCstNode: refCstNode, to: refNameNode, from: [...uniqueRules.get(ruleId).from, cstNode.range], docUri: refDocUri })
: uniqueRules.set(ruleId, { refCstNode: refCstNode, to: refNameNode, from: [cstNode.range], docUri: refDocUri });
});
if (uniqueRules.size === 0) {
return undefined;
}
return Array.from(uniqueRules.values()).map(rule => ({
to: {
kind: SymbolKind.Method,
name: rule.to.text,
range: rule.refCstNode.range,
selectionRange: rule.to.range,
uri: rule.docUri
},
fromRanges: rule.from
}));
}
else if (isInfixRule(node)) {
const ruleCall = node.call;
const cstNode = ruleCall.$cstNode;
if (!cstNode) {
return undefined;
}
const refCstNode = ruleCall.rule.ref?.$cstNode;
if (!refCstNode) {
return undefined;
}
const refNameNode = this.nameProvider.getNameNode(refCstNode.astNode);
if (!refNameNode) {
return undefined;
}
const refDocUri = getDocument(refCstNode.astNode).uri.toString();
return [{
to: {
kind: SymbolKind.Method,
name: refNameNode.text,
range: refCstNode.range,
selectionRange: refNameNode.range,
uri: refDocUri
},
fromRanges: [cstNode.range]
}];
}
else {
return undefined;
}
}
}
//# sourceMappingURL=grammar-call-hierarchy.js.map
@@ -0,0 +1 @@
{"version":3,"file":"grammar-call-hierarchy.js","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-call-hierarchy.ts"],"names":[],"mappings":"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"}
+42
View File
@@ -0,0 +1,42 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { CodeActionParams } from 'vscode-languageserver-protocol';
import type { CodeAction, Command } from 'vscode-languageserver-types';
import * as ast from '../../languages/generated/ast.js';
import type { CodeActionProvider } from '../../lsp/code-action.js';
import type { LangiumServices } from '../../lsp/lsp-services.js';
import type { AstReflection } from '../../syntax-tree.js';
import type { MaybePromise } from '../../utils/promise-utils.js';
import type { LangiumDocument } from '../../workspace/documents.js';
import type { IndexManager } from '../../workspace/index-manager.js';
export declare class LangiumGrammarCodeActionProvider implements CodeActionProvider {
protected readonly reflection: AstReflection;
protected readonly indexManager: IndexManager;
constructor(services: LangiumServices);
getCodeActions(document: LangiumDocument<ast.Grammar>, params: CodeActionParams): MaybePromise<Array<Command | CodeAction>>;
private createCodeActions;
/**
* Adds missing returns for parser rule
*/
private fixMissingReturns;
private fixInvalidReturnsInfers;
private fixMissingInfer;
private fixMissingCrossRefTerminal;
private fixSuperfluousInfer;
private isRuleReplaceable;
private replaceRule;
private isDefinitionReplaceable;
private replaceDefinition;
private replaceParserRuleByTypeDeclaration;
private fixUnnecessaryFileExtension;
private makeUpperCase;
private addEntryKeyword;
private fixRegexTokens;
private fixCrossRefSyntax;
private addNewRule;
private lookInGlobalScope;
}
//# sourceMappingURL=grammar-code-actions.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"grammar-code-actions.d.ts","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-code-actions.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAIhF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAY,MAAM,6BAA6B,CAAC;AACjF,OAAO,KAAK,GAAG,MAAM,kCAAkC,CAAC;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAA4B,MAAM,sBAAsB,CAAC;AAGpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAOjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAGrE,qBAAa,gCAAiC,YAAW,kBAAkB;IAEvE,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;IAC7C,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;gBAElC,QAAQ,EAAE,eAAe;IAKrC,cAAc,CAAC,QAAQ,EAAE,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,gBAAgB,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,GAAG,UAAU,CAAC,CAAC;IAS3H,OAAO,CAAC,iBAAiB;IAmDzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,uBAAuB;IAqB/B,OAAO,CAAC,eAAe;IAuBvB,OAAO,CAAC,0BAA0B;IAwBlC,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,kCAAkC;IA8B1C,OAAO,CAAC,2BAA2B;IAwBnC,OAAO,CAAC,aAAa;IAwBrB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,cAAc;IA4BtB,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,UAAU;IA6BlB,OAAO,CAAC,iBAAiB;CA0E5B"}
+458
View File
@@ -0,0 +1,458 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { CodeActionKind } from 'vscode-languageserver';
import * as ast from '../../languages/generated/ast.js';
import { getContainerOfType } from '../../utils/ast-utils.js';
import { findLeafNodeAtOffset } from '../../utils/cst-utils.js';
import { escapeRegExp } from '../../utils/regexp-utils.js';
import { UriUtils } from '../../utils/uri-utils.js';
import { DocumentValidator } from '../../validation/document-validator.js';
import { IssueCodes } from '../validation/validator.js';
export class LangiumGrammarCodeActionProvider {
constructor(services) {
this.reflection = services.shared.AstReflection;
this.indexManager = services.shared.workspace.IndexManager;
}
getCodeActions(document, params) {
const result = [];
const acceptor = (ca) => ca && result.push(ca);
for (const diagnostic of params.context.diagnostics) {
this.createCodeActions(diagnostic, document, acceptor);
}
return result;
}
createCodeActions(diagnostic, document, accept) {
switch (diagnostic.data?.code) {
case IssueCodes.GrammarNameUppercase:
case IssueCodes.RuleNameUppercase:
accept(this.makeUpperCase(diagnostic, document));
break;
case IssueCodes.UseRegexTokens:
accept(this.fixRegexTokens(diagnostic, document));
break;
case IssueCodes.EntryRuleTokenSyntax:
accept(this.addEntryKeyword(diagnostic, document));
break;
case IssueCodes.CrossRefTokenSyntax:
accept(this.fixCrossRefSyntax(diagnostic, document));
break;
case IssueCodes.ParserRuleToTypeDecl:
accept(this.replaceParserRuleByTypeDeclaration(diagnostic, document));
break;
case IssueCodes.UnnecessaryFileExtension:
accept(this.fixUnnecessaryFileExtension(diagnostic, document));
break;
case IssueCodes.MissingReturns:
accept(this.fixMissingReturns(diagnostic, document));
break;
case IssueCodes.InvalidInfers:
case IssueCodes.InvalidReturns:
accept(this.fixInvalidReturnsInfers(diagnostic, document));
break;
case IssueCodes.MissingInfer:
accept(this.fixMissingInfer(diagnostic, document));
break;
case IssueCodes.MissingCrossRefTerminal:
accept(this.fixMissingCrossRefTerminal(diagnostic, document));
break;
case IssueCodes.SuperfluousInfer:
accept(this.fixSuperfluousInfer(diagnostic, document));
break;
case DocumentValidator.LinkingError: {
const data = diagnostic.data;
if (data && data.containerType === 'RuleCall' && data.property === 'rule') {
accept(this.addNewRule(diagnostic, data, document));
}
if (data) {
this.lookInGlobalScope(diagnostic, data, document).forEach(accept);
}
break;
}
}
return undefined;
}
/**
* Adds missing returns for parser rule
*/
fixMissingReturns(diagnostic, document) {
const text = document.textDocument.getText(diagnostic.range);
if (text) {
return {
title: `Add explicit return type for parser rule ${text}`,
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
edit: {
changes: {
[document.textDocument.uri]: [{
range: diagnostic.range,
newText: `${text} returns ${text}` // suggestion adds missing 'return'
}]
}
}
};
}
return undefined;
}
fixInvalidReturnsInfers(diagnostic, document) {
const data = diagnostic.data;
if (data && data.actionSegment) {
const text = document.textDocument.getText(data.actionSegment.range);
return {
title: `Correct ${text} usage`,
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
edit: {
changes: {
[document.textDocument.uri]: [{
range: data.actionSegment.range,
newText: text === 'infers' ? 'returns' : 'infers'
}]
}
}
};
}
return undefined;
}
fixMissingInfer(diagnostic, document) {
const data = diagnostic.data;
if (data && data.actionSegment) {
return {
title: "Correct 'infer' usage",
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
edit: {
changes: {
[document.textDocument.uri]: [{
range: {
start: data.actionSegment.range.end,
end: data.actionSegment.range.end
},
newText: 'infer '
}]
}
}
};
}
return undefined;
}
fixMissingCrossRefTerminal(diagnostic, document) {
const grammar = document.parseResult.value;
const idTerminal = grammar.rules.find(rule => ast.isTerminalRule(rule) && rule.name === 'ID');
if (idTerminal) {
return {
title: 'Use ID token to resolve cross-reference',
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
edit: {
changes: {
[document.textDocument.uri]: [{
range: {
start: diagnostic.range.end,
end: diagnostic.range.end
},
newText: ':ID'
}]
}
}
};
}
return undefined;
}
fixSuperfluousInfer(diagnostic, document) {
const data = diagnostic.data;
if (data && data.actionRange) {
return {
title: "Remove the 'infer' keyword",
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
edit: {
changes: {
[document.textDocument.uri]: [{
range: data.actionRange,
newText: ''
}]
}
}
};
}
return undefined;
}
isRuleReplaceable(rule) {
/** at the moment, only "pure" parser rules are supported:
* - supported are only Alternatives (recursively) and "infers"
* - "returns" is not relevant, since cross-references would not refer to the parser rule, but to its "return type" instead
*/
return !rule.fragment && !rule.entry && rule.parameters.length === 0 && !rule.returnType && !rule.dataType;
}
replaceRule(rule) {
const type = rule.inferredType ?? rule;
return type.name;
}
isDefinitionReplaceable(node) {
if (ast.isRuleCall(node)) {
return node.arguments.length === 0 && ast.isParserRule(node.rule.ref) && this.isRuleReplaceable(node.rule.ref);
}
if (ast.isAlternatives(node)) {
return node.elements.every(child => this.isDefinitionReplaceable(child));
}
return false;
}
replaceDefinition(node) {
if (ast.isRuleCall(node) && node.rule.ref) {
return node.rule.ref.name;
}
if (ast.isAlternatives(node)) {
return node.elements.map(child => this.replaceDefinition(child)).join(' | ');
}
throw new Error('missing code for ' + node);
}
replaceParserRuleByTypeDeclaration(diagnostic, document) {
const rootCst = document.parseResult.value.$cstNode;
if (rootCst) {
const offset = document.textDocument.offsetAt(diagnostic.range.start);
const cstNode = findLeafNodeAtOffset(rootCst, offset);
const rule = getContainerOfType(cstNode?.astNode, ast.isParserRule);
if (rule && rule.$cstNode) {
const isReplaceable = this.isRuleReplaceable(rule) && this.isDefinitionReplaceable(rule.definition);
if (isReplaceable) {
const newText = `type ${this.replaceRule(rule)} = ${this.replaceDefinition(rule.definition)};`;
return {
title: 'Replace with type declaration',
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
isPreferred: true,
edit: {
changes: {
[document.textDocument.uri]: [{
range: diagnostic.range,
newText
}]
}
}
};
}
}
}
return undefined;
}
fixUnnecessaryFileExtension(diagnostic, document) {
const end = { ...diagnostic.range.end };
end.character -= 1;
const start = { ...end };
start.character -= '.langium'.length;
return {
title: 'Remove file extension',
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
isPreferred: true,
edit: {
changes: {
[document.textDocument.uri]: [{
range: {
start,
end
},
newText: ''
}]
}
}
};
}
makeUpperCase(diagnostic, document) {
const range = {
start: diagnostic.range.start,
end: {
line: diagnostic.range.start.line,
character: diagnostic.range.start.character + 1
}
};
return {
title: 'First letter to upper case',
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
isPreferred: true,
edit: {
changes: {
[document.textDocument.uri]: [{
range,
newText: document.textDocument.getText(range).toUpperCase()
}]
}
}
};
}
addEntryKeyword(diagnostic, document) {
return {
title: 'Add entry keyword',
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
isPreferred: true,
edit: {
changes: {
[document.textDocument.uri]: [{
range: { start: diagnostic.range.start, end: diagnostic.range.start },
newText: 'entry '
}]
}
}
};
}
fixRegexTokens(diagnostic, document) {
const offset = document.textDocument.offsetAt(diagnostic.range.start);
const rootCst = document.parseResult.value.$cstNode;
if (rootCst) {
const cstNode = findLeafNodeAtOffset(rootCst, offset);
const container = getContainerOfType(cstNode?.astNode, ast.isCharacterRange);
if (container && container.right && container.$cstNode) {
const left = container.left.value;
const right = container.right.value;
return {
title: 'Refactor into regular expression',
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
isPreferred: true,
edit: {
changes: {
[document.textDocument.uri]: [{
range: container.$cstNode.range,
newText: `/[${escapeRegExp(left)}-${escapeRegExp(right)}]/`
}]
}
}
};
}
}
return undefined;
}
fixCrossRefSyntax(diagnostic, document) {
return {
title: "Replace '|' with ':'",
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
isPreferred: true,
edit: {
changes: {
[document.textDocument.uri]: [{
range: diagnostic.range,
newText: ':'
}]
}
}
};
}
addNewRule(diagnostic, data, document) {
const offset = document.textDocument.offsetAt(diagnostic.range.start);
const rootCst = document.parseResult.value.$cstNode;
if (rootCst) {
const cstNode = findLeafNodeAtOffset(rootCst, offset);
const container = getContainerOfType(cstNode?.astNode, ast.isParserRule);
if (container && container.$cstNode) {
return {
title: `Add new rule '${data.refText}'`,
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
isPreferred: false,
edit: {
changes: {
[document.textDocument.uri]: [{
range: {
start: container.$cstNode.range.end,
end: container.$cstNode.range.end
},
newText: '\n\n' + data.refText + ':\n /* TODO implement rule */ {infer ' + data.refText + '};'
}]
}
}
};
}
}
return undefined;
}
lookInGlobalScope(diagnostic, data, document) {
const refInfo = {
container: {
$type: data.containerType
},
property: data.property,
reference: {
$refText: data.refText
}
};
const referenceType = this.reflection.getReferenceType(refInfo);
const candidates = this.indexManager.allElements(referenceType).filter(e => e.name === data.refText);
const result = [];
let shortestPathIndex = -1;
let shortestPathLength = -1;
for (const candidate of candidates) {
if (UriUtils.equals(candidate.documentUri, document.uri)) {
continue;
}
// Find an import path and a position to insert the import
const importPath = getRelativeImport(document.uri, candidate.documentUri);
let position;
let suffix = '';
const grammar = document.parseResult.value;
const nextImport = grammar.imports.find(imp => imp.path && importPath < imp.path);
if (nextImport) {
// Insert the new import alphabetically
position = nextImport.$cstNode?.range.start;
}
else if (grammar.imports.length > 0) {
// Put the new import after the last import
const rangeEnd = grammar.imports[grammar.imports.length - 1].$cstNode.range.end;
if (rangeEnd) {
position = { line: rangeEnd.line + 1, character: 0 };
}
}
else if (grammar.rules.length > 0) {
// Put the new import before the first rule
position = grammar.rules[0].$cstNode?.range.start;
suffix = '\n';
}
if (position) {
if (shortestPathIndex < 0 || importPath.length < shortestPathLength) {
shortestPathIndex = result.length;
shortestPathLength = importPath.length;
}
// Add an import declaration for the candidate in the global scope
result.push({
title: `Add import to '${importPath}'`,
kind: CodeActionKind.QuickFix,
diagnostics: [diagnostic],
isPreferred: false,
edit: {
changes: {
[document.textDocument.uri]: [{
range: {
start: position,
end: position
},
newText: `import '${importPath}'\n${suffix}`
}]
}
}
});
}
}
// Mark the code action with the shortest import path as preferred
if (shortestPathIndex >= 0) {
result[shortestPathIndex].isPreferred = true;
}
return result;
}
}
function getRelativeImport(source, target) {
const sourceDir = UriUtils.dirname(source);
let relativePath = UriUtils.relative(sourceDir, target);
if (!relativePath.startsWith('./') && !relativePath.startsWith('../')) {
relativePath = './' + relativePath;
}
if (relativePath.endsWith('.langium')) {
relativePath = relativePath.substring(0, relativePath.length - '.langium'.length);
}
return relativePath;
}
//# sourceMappingURL=grammar-code-actions.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,18 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { NextFeature } from '../../lsp/completion/follow-element-computation.js';
import { DefaultCompletionProvider, type CompletionAcceptor, type CompletionContext } from '../../lsp/completion/completion-provider.js';
import type { MaybePromise } from '../../utils/promise-utils.js';
import type { AbstractElement } from '../../languages/generated/ast.js';
import type { LangiumServices } from '../../lsp/lsp-services.js';
export declare class LangiumGrammarCompletionProvider extends DefaultCompletionProvider {
private readonly documents;
constructor(services: LangiumServices);
protected completionFor(context: CompletionContext, next: NextFeature<AbstractElement>, acceptor: CompletionAcceptor): MaybePromise<void>;
private completeImportPath;
private getAllFiles;
}
//# sourceMappingURL=grammar-completion-provider.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"grammar-completion-provider.d.ts","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-completion-provider.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAIhF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oDAAoD,CAAC;AACtF,OAAO,EAAE,yBAAyB,EAAE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AACzI,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAGjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAGxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE,qBAAa,gCAAiC,SAAQ,yBAAyB;IAE3E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;gBAEvC,QAAQ,EAAE,eAAe;cAKlB,aAAa,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,WAAW,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,kBAAkB,GAAG,YAAY,CAAC,IAAI,CAAC;IASlJ,OAAO,CAAC,kBAAkB;IAmC1B,OAAO,CAAC,WAAW;CAmBtB"}
@@ -0,0 +1,78 @@
/******************************************************************************
* Copyright 2023 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { CompletionItemKind } from 'vscode-languageserver-types';
import { DefaultCompletionProvider } from '../../lsp/completion/completion-provider.js';
import { getContainerOfType } from '../../utils/ast-utils.js';
import { isAssignment } from '../../languages/generated/ast.js';
import { UriUtils } from '../../utils/uri-utils.js';
export class LangiumGrammarCompletionProvider extends DefaultCompletionProvider {
constructor(services) {
super(services);
this.documents = () => services.shared.workspace.LangiumDocuments;
}
completionFor(context, next, acceptor) {
const assignment = getContainerOfType(next.feature, isAssignment);
if (assignment?.feature === 'path') {
this.completeImportPath(context, acceptor);
}
else {
return super.completionFor(context, next, acceptor);
}
}
completeImportPath(context, acceptor) {
const text = context.textDocument.getText();
const existingText = text.substring(context.tokenOffset, context.offset);
let allPaths = this.getAllFiles(context.document);
let range = {
start: context.position,
end: context.position
};
if (existingText.length > 0) {
const existingPath = existingText.substring(1);
allPaths = allPaths.filter(path => path.startsWith(existingPath));
// Completely replace the current token
const start = context.textDocument.positionAt(context.tokenOffset + 1);
const end = context.textDocument.positionAt(context.tokenEndOffset - 1);
range = {
start,
end
};
}
for (const path of allPaths) {
// Only insert quotes if there is no `path` token yet.
const delimiter = existingText.length > 0 ? '' : '"';
const completionValue = `${delimiter}${path}${delimiter}`;
acceptor(context, {
label: path,
textEdit: {
newText: completionValue,
range
},
kind: CompletionItemKind.File,
sortText: '0'
});
}
}
getAllFiles(document) {
const documents = this.documents().all;
const uri = document.uri.toString();
const dirname = UriUtils.dirname(document.uri).toString();
const paths = [];
for (const doc of documents) {
if (!UriUtils.equals(doc.uri, uri)) {
const docUri = doc.uri.toString();
const uriWithoutExt = docUri.substring(0, docUri.length - UriUtils.extname(doc.uri).length);
let relativePath = UriUtils.relative(dirname, uriWithoutExt);
if (!relativePath.startsWith('.')) {
relativePath = `./${relativePath}`;
}
paths.push(relativePath);
}
}
return paths;
}
}
//# sourceMappingURL=grammar-completion-provider.js.map
@@ -0,0 +1 @@
{"version":3,"file":"grammar-completion-provider.js","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-completion-provider.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAGhF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,EAAE,yBAAyB,EAAmD,MAAM,6CAA6C,CAAC;AAEzI,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAG9D,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGpD,MAAM,OAAO,gCAAiC,SAAQ,yBAAyB;IAI3E,YAAY,QAAyB;QACjC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAChB,IAAI,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC;IACtE,CAAC;IAEkB,aAAa,CAAC,OAA0B,EAAE,IAAkC,EAAE,QAA4B;QACzH,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QAClE,IAAI,UAAU,EAAE,OAAO,KAAK,MAAM,EAAE,CAAC;YACjC,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACJ,OAAO,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QACxD,CAAC;IACL,CAAC;IAEO,kBAAkB,CAAC,OAA0B,EAAE,QAA4B;QAC/E,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;QAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACzE,IAAI,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAClD,IAAI,KAAK,GAAU;YACf,KAAK,EAAE,OAAO,CAAC,QAAQ;YACvB,GAAG,EAAE,OAAO,CAAC,QAAQ;SACxB,CAAC;QACF,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,YAAY,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAC/C,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;YAClE,uCAAuC;YACvC,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;YACvE,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC;YACxE,KAAK,GAAG;gBACJ,KAAK;gBACL,GAAG;aACN,CAAC;QACN,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC1B,sDAAsD;YACtD,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;YACrD,MAAM,eAAe,GAAG,GAAG,SAAS,GAAG,IAAI,GAAG,SAAS,EAAE,CAAC;YAC1D,QAAQ,CAAC,OAAO,EAAE;gBACd,KAAK,EAAE,IAAI;gBACX,QAAQ,EAAE;oBACN,OAAO,EAAE,eAAe;oBACxB,KAAK;iBACR;gBACD,IAAI,EAAE,kBAAkB,CAAC,IAAI;gBAC7B,QAAQ,EAAE,GAAG;aAChB,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAEO,WAAW,CAAC,QAAyB;QACzC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC;QACvC,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC1D,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;YAC1B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;gBACjC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;gBAClC,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;gBAC5F,IAAI,YAAY,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;gBAC7D,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBAChC,YAAY,GAAG,KAAK,YAAY,EAAE,CAAC;gBACvC,CAAC;gBACD,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC7B,CAAC;QACL,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;CAEJ"}
+20
View File
@@ -0,0 +1,20 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { DefinitionParams } from 'vscode-languageserver';
import type { LangiumServices } from '../../lsp/lsp-services.js';
import type { AstNode, LeafCstNode } from '../../syntax-tree.js';
import type { MaybePromise } from '../../utils/promise-utils.js';
import type { LangiumDocuments } from '../../workspace/documents.js';
import type { Grammar } from '../../languages/generated/ast.js';
import { LocationLink } from 'vscode-languageserver';
import { DefaultDefinitionProvider } from '../../lsp/index.js';
export declare class LangiumGrammarDefinitionProvider extends DefaultDefinitionProvider {
protected documents: LangiumDocuments;
constructor(services: LangiumServices);
protected collectLocationLinks(sourceCstNode: LeafCstNode, _params: DefinitionParams): MaybePromise<LocationLink[] | undefined>;
protected findTargetObject(importedGrammar: Grammar): AstNode | undefined;
}
//# sourceMappingURL=grammar-definition.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"grammar-definition.d.ts","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-definition.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAc,MAAM,sBAAsB,CAAC;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAS,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAM/D,qBAAa,gCAAiC,SAAQ,yBAAyB;IAE3E,SAAS,CAAC,SAAS,EAAE,gBAAgB,CAAC;gBAE1B,QAAQ,EAAE,eAAe;cAKlB,oBAAoB,CAAC,aAAa,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAAC,YAAY,EAAE,GAAG,SAAS,CAAC;IAsBxI,SAAS,CAAC,gBAAgB,CAAC,eAAe,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS;CAO5E"}
+41
View File
@@ -0,0 +1,41 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { LocationLink, Range } from 'vscode-languageserver';
import { DefaultDefinitionProvider } from '../../lsp/index.js';
import { streamContents } from '../../utils/ast-utils.js';
import { findAssignment } from '../../utils/grammar-utils.js';
import { isGrammarImport } from '../../languages/generated/ast.js';
import { resolveImport } from '../internal-grammar-util.js';
export class LangiumGrammarDefinitionProvider extends DefaultDefinitionProvider {
constructor(services) {
super(services);
this.documents = services.shared.workspace.LangiumDocuments;
}
collectLocationLinks(sourceCstNode, _params) {
const pathFeature = 'path';
if (isGrammarImport(sourceCstNode.astNode) && findAssignment(sourceCstNode)?.feature === pathFeature) {
const importedGrammar = resolveImport(this.documents, sourceCstNode.astNode);
if (importedGrammar?.$document) {
const targetObject = this.findTargetObject(importedGrammar) ?? importedGrammar;
const selectionRange = this.nameProvider.getNameNode(targetObject)?.range ?? Range.create(0, 0, 0, 0);
const previewRange = targetObject.$cstNode?.range ?? Range.create(0, 0, 0, 0);
return [
LocationLink.create(importedGrammar.$document.uri.toString(), previewRange, selectionRange, sourceCstNode.range)
];
}
return undefined;
}
return super.collectLocationLinks(sourceCstNode, _params);
}
findTargetObject(importedGrammar) {
// Jump to grammar name or the first element
if (importedGrammar.isDeclared) {
return importedGrammar;
}
return streamContents(importedGrammar).head();
}
}
//# sourceMappingURL=grammar-definition.js.map
@@ -0,0 +1 @@
{"version":3,"file":"grammar-definition.js","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-definition.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAQhF,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,OAAO,gCAAiC,SAAQ,yBAAyB;IAI3E,YAAY,QAAyB;QACjC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAChB,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC;IAChE,CAAC;IAEkB,oBAAoB,CAAC,aAA0B,EAAE,OAAyB;QACzF,MAAM,WAAW,GAA8B,MAAM,CAAC;QACtD,IAAI,eAAe,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,cAAc,CAAC,aAAa,CAAC,EAAE,OAAO,KAAK,WAAW,EAAE,CAAC;YACnG,MAAM,eAAe,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;YAC7E,IAAI,eAAe,EAAE,SAAS,EAAE,CAAC;gBAC7B,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,IAAI,eAAe,CAAC;gBAC/E,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBACtG,MAAM,YAAY,GAAG,YAAY,CAAC,QAAQ,EAAE,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC9E,OAAO;oBACH,YAAY,CAAC,MAAM,CACf,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,EACxC,YAAY,EACZ,cAAc,EACd,aAAa,CAAC,KAAK,CACtB;iBACJ,CAAC;YACN,CAAC;YACD,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,OAAO,KAAK,CAAC,oBAAoB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC;IAES,gBAAgB,CAAC,eAAwB;QAC/C,4CAA4C;QAC5C,IAAI,eAAe,CAAC,UAAU,EAAE,CAAC;YAC7B,OAAO,eAAe,CAAC;QAC3B,CAAC;QACD,OAAO,cAAc,CAAC,eAAe,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,CAAC;CACJ"}
@@ -0,0 +1,14 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { AstNode } from '../../syntax-tree.js';
import { DefaultFoldingRangeProvider } from '../../lsp/folding-range-provider.js';
/**
* A specialized folding range provider for the grammar language
*/
export declare class LangiumGrammarFoldingRangeProvider extends DefaultFoldingRangeProvider {
shouldProcessContent(node: AstNode): boolean;
}
//# sourceMappingURL=grammar-folding-ranges.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"grammar-folding-ranges.d.ts","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-folding-ranges.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAGlF;;GAEG;AACH,qBAAa,kCAAmC,SAAQ,2BAA2B;IAEtE,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;CAIxD"}
+17
View File
@@ -0,0 +1,17 @@
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { DefaultFoldingRangeProvider } from '../../lsp/folding-range-provider.js';
import { isParserRule } from '../../languages/generated/ast.js';
/**
* A specialized folding range provider for the grammar language
*/
export class LangiumGrammarFoldingRangeProvider extends DefaultFoldingRangeProvider {
shouldProcessContent(node) {
// Exclude parser rules from folding
return !isParserRule(node);
}
}
//# sourceMappingURL=grammar-folding-ranges.js.map
@@ -0,0 +1 @@
{"version":3,"file":"grammar-folding-ranges.js","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-folding-ranges.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAGhF,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAEhE;;GAEG;AACH,MAAM,OAAO,kCAAmC,SAAQ,2BAA2B;IAEtE,oBAAoB,CAAC,IAAa;QACvC,oCAAoC;QACpC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;CACJ"}
+11
View File
@@ -0,0 +1,11 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { AstNode } from '../../syntax-tree.js';
import { AbstractFormatter } from '../../lsp/formatter.js';
export declare class LangiumGrammarFormatter extends AbstractFormatter {
protected format(node: AstNode): void;
}
//# sourceMappingURL=grammar-formatter.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"grammar-formatter.d.ts","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-formatter.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAc,MAAM,wBAAwB,CAAC;AAKvE,qBAAa,uBAAwB,SAAQ,iBAAiB;IAE1D,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;CA0FxC"}

Some files were not shown because too many files have changed in this diff Show More