+16
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"default-module.js","sourceRoot":"","sources":["../src/default-module.ts"],"names":[],"mappings":"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"}
|
||||
+59
@@ -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
@@ -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
@@ -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
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"dependency-injection.js","sourceRoot":"","sources":["../src/dependency-injection.ts"],"names":[],"mappings":"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"}
|
||||
+25
@@ -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
|
||||
+1
@@ -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
@@ -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
|
||||
+1
@@ -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"}
|
||||
+32
@@ -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
|
||||
+1
@@ -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"}
|
||||
+66
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"documentation-provider.js","sourceRoot":"","sources":["../../src/documentation/documentation-provider.ts"],"names":[],"mappings":"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"}
|
||||
+9
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+967
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+386
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+40
@@ -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
@@ -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
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+13
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"node-joiner.d.ts","sourceRoot":"","sources":["../../src/generate/node-joiner.ts"],"names":[],"mappings":"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"}
|
||||
+67
@@ -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
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"node-joiner.js","sourceRoot":"","sources":["../../src/generate/node-joiner.ts"],"names":[],"mappings":"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"}
|
||||
+12
@@ -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
@@ -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
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+280
@@ -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
@@ -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
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+29
@@ -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
@@ -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
@@ -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
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"template-string.js","sourceRoot":"","sources":["../../src/generate/template-string.ts"],"names":[],"mappings":"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"}
|
||||
+12
@@ -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
|
||||
+1
@@ -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
@@ -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
|
||||
+1
@@ -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
@@ -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
@@ -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"}
|
||||
+8
File diff suppressed because one or more lines are too long
+1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
+1
@@ -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
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+34
@@ -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
|
||||
+1
@@ -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
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"langium-grammar-module.js","sourceRoot":"","sources":["../../src/grammar/langium-grammar-module.ts"],"names":[],"mappings":"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"}
|
||||
+15
@@ -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
|
||||
+1
@@ -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
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"grammar-call-hierarchy.js","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-call-hierarchy.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAMhF,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,6BAA6B,EAAE,MAAM,sCAAsC,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC9F,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAE/G,MAAM,OAAO,mCAAoC,SAAQ,6BAA6B;IAExE,gBAAgB,CAAC,IAAa,EAAE,UAAwC;QAC9E,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,gEAAgE;QAChE,MAAM,WAAW,GAAG,IAAI,GAAG,EAA8F,CAAC;QAC1H,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACrB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACtD,IAAI,CAAC,GAAG,EAAE,CAAC;gBACP,OAAO;YACX,CAAC;YACD,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC;YACvC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;gBACrB,OAAO;YACX,CAAC;YACD,MAAM,UAAU,GAAG,oBAAoB,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC/E,IAAI,CAAC,UAAU,EAAE,CAAC;gBACd,OAAO;YACX,CAAC;YACD,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;YAChF,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;gBACtC,OAAO;YACX,CAAC;YACD,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;YAC3D,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACZ,OAAO;YACX,CAAC;YACD,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAG,SAAS,GAAG,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC;YAE/C,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;gBACrB,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC,WAAW,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;gBAC7J,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QAC/H,CAAC,CAAC,CAAC;QACH,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjD,IAAI,EAAE;gBACF,IAAI,EAAE,UAAU,CAAC,MAAM;gBACvB,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;gBACxB,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK;gBAC5B,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK;gBACnC,GAAG,EAAE,IAAI,CAAC,MAAM;aACnB;YACD,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;SACvD,CAAC,CAAC,CAAC;IACR,CAAC;IAES,gBAAgB,CAAC,IAAa;QACpC,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;YACvE,gEAAgE;YAChE,MAAM,WAAW,GAAG,IAAI,GAAG,EAA+E,CAAC;YAC3G,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;gBACzB,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC;gBAClC,IAAI,CAAC,OAAO,EAAE,CAAC;oBACX,OAAO;gBACX,CAAC;gBACD,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC;gBAC/C,IAAI,CAAC,UAAU,EAAE,CAAC;oBACd,OAAO;gBACX,CAAC;gBACD,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;gBACtE,IAAI,CAAC,WAAW,EAAE,CAAC;oBACf,OAAO;gBACX,CAAC;gBACD,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;gBACjE,MAAM,MAAM,GAAG,SAAS,GAAG,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC;gBAElD,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;oBACrB,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;oBAChJ,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YACzH,CAAC,CAAC,CAAC;YACH,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO,SAAS,CAAC;YACrB,CAAC;YACD,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACjD,EAAE,EAAE;oBACA,IAAI,EAAE,UAAU,CAAC,MAAM;oBACvB,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI;oBAClB,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK;oBAC5B,cAAc,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK;oBAC7B,GAAG,EAAE,IAAI,CAAC,MAAM;iBACnB;gBACD,UAAU,EAAE,IAAI,CAAC,IAAI;aACxB,CAAC,CAAC,CAAC;QACR,CAAC;aAAM,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;YAC3B,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC;YAClC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACX,OAAO,SAAS,CAAC;YACrB,CAAC;YACD,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC;YAC/C,IAAI,CAAC,UAAU,EAAE,CAAC;gBACd,OAAO,SAAS,CAAC;YACrB,CAAC;YACD,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACtE,IAAI,CAAC,WAAW,EAAE,CAAC;gBACf,OAAO,SAAS,CAAC;YACrB,CAAC;YACD,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YACjE,OAAO,CAAC;oBACJ,EAAE,EAAE;wBACA,IAAI,EAAE,UAAU,CAAC,MAAM;wBACvB,IAAI,EAAE,WAAW,CAAC,IAAI;wBACtB,KAAK,EAAE,UAAU,CAAC,KAAK;wBACvB,cAAc,EAAE,WAAW,CAAC,KAAK;wBACjC,GAAG,EAAE,SAAS;qBACjB;oBACD,UAAU,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;iBAC9B,CAAC,CAAC;QACP,CAAC;aAAM,CAAC;YACJ,OAAO,SAAS,CAAC;QACrB,CAAC;IACL,CAAC;CACJ"}
|
||||
+42
@@ -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
|
||||
+1
@@ -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
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+18
@@ -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
|
||||
+1
@@ -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"}
|
||||
+78
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"grammar-completion-provider.js","sourceRoot":"","sources":["../../../src/grammar/lsp/grammar-completion-provider.ts"],"names":[],"mappings":"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"}
|
||||
+20
@@ -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
|
||||
+1
@@ -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
@@ -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
|
||||
+1
@@ -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"}
|
||||
+14
@@ -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
|
||||
+1
@@ -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
@@ -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
|
||||
+1
@@ -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
@@ -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
|
||||
+1
@@ -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
Reference in New Issue
Block a user