mirror of
https://github.com/microsoft/monaco-editor.git
synced 2025-12-22 11:35:40 +01:00
Add standalone monaco-json-interpolation package
Extract the JSON interpolation language as a standalone npm package that
works with the official Monaco Editor as a peer dependency.
Features:
- Zero dependencies on monaco-editor internals
- Simple registration API: register() and getDefaults()
- Variable context for custom completions and hover
- Monarch tokenizer with nextEmbedded for JavaScript
- TypeScript types included
- ESM and CommonJS builds via tsup
Usage:
```typescript
import { register, getDefaults } from 'monaco-json-interpolation';
register();
getDefaults().setVariableContext({
getVariables: () => [{ name: 'env', value: 'prod' }]
});
```
This commit is contained in:
parent
4540e05e5a
commit
6039262df7
6 changed files with 835 additions and 0 deletions
157
packages/monaco-json-interpolation/README.md
Normal file
157
packages/monaco-json-interpolation/README.md
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
# monaco-json-interpolation
|
||||||
|
|
||||||
|
A Monaco Editor add-on that provides JSON language support with `${...}` variable interpolation.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Syntax Highlighting**: Full JSON syntax highlighting with embedded JavaScript inside `${...}`
|
||||||
|
- **Variable Completions**: Autocomplete for your custom variables inside interpolations
|
||||||
|
- **Hover Information**: See variable types, descriptions, and current values on hover
|
||||||
|
- **JSONC Support**: Comments (`//`, `/* */`) and trailing commas are allowed
|
||||||
|
- **Folding**: Code folding for objects and arrays
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install monaco-json-interpolation
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Setup
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import * as monaco from 'monaco-editor';
|
||||||
|
import { register, getDefaults } from 'monaco-json-interpolation';
|
||||||
|
|
||||||
|
// Register the language (call once at startup)
|
||||||
|
register();
|
||||||
|
|
||||||
|
// Create an editor with the new language
|
||||||
|
const editor = monaco.editor.create(document.getElementById('container'), {
|
||||||
|
value: '{\n "message": "Hello, ${name}!"\n}',
|
||||||
|
language: 'json-interpolation'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Providing Variables
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { getDefaults } from 'monaco-json-interpolation';
|
||||||
|
|
||||||
|
// Set up variable context for completions and hover
|
||||||
|
getDefaults().setVariableContext({
|
||||||
|
getVariables: () => [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
value: 'World',
|
||||||
|
description: 'The name to greet'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'env',
|
||||||
|
type: 'string',
|
||||||
|
value: 'production',
|
||||||
|
description: 'Current environment'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'config',
|
||||||
|
type: 'object',
|
||||||
|
value: { debug: false, port: 3000 },
|
||||||
|
description: 'Application configuration'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dynamic Variables
|
||||||
|
|
||||||
|
You can also provide variables asynchronously:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
getDefaults().setVariableContext({
|
||||||
|
getVariables: async () => {
|
||||||
|
const response = await fetch('/api/variables');
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### `register(): LanguageServiceDefaults`
|
||||||
|
|
||||||
|
Registers the `json-interpolation` language with Monaco Editor. Should be called once before creating editors. Returns the language service defaults for configuration.
|
||||||
|
|
||||||
|
### `getDefaults(): LanguageServiceDefaults`
|
||||||
|
|
||||||
|
Gets the language service defaults. Automatically registers the language if not already registered.
|
||||||
|
|
||||||
|
### `LanguageServiceDefaults`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface LanguageServiceDefaults {
|
||||||
|
readonly languageId: string;
|
||||||
|
readonly variableContext: VariableContextProvider | null;
|
||||||
|
|
||||||
|
setVariableContext(provider: VariableContextProvider | null): void;
|
||||||
|
setDiagnosticsOptions(options: DiagnosticsOptions): void;
|
||||||
|
setModeConfiguration(modeConfiguration: ModeConfiguration): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `VariableDefinition`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface VariableDefinition {
|
||||||
|
name: string; // Variable name (without $)
|
||||||
|
type?: string; // Type for display (e.g., 'string', 'number')
|
||||||
|
value?: unknown; // Current value (shown in hover)
|
||||||
|
description?: string; // Description for hover/completion
|
||||||
|
detail?: string; // Additional detail text
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `VariableContextProvider`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface VariableContextProvider {
|
||||||
|
getVariables(): VariableDefinition[] | Promise<VariableDefinition[]>;
|
||||||
|
resolveVariable?(name: string): unknown | Promise<unknown>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import * as monaco from 'monaco-editor';
|
||||||
|
import jsonInterpolation from 'monaco-json-interpolation';
|
||||||
|
|
||||||
|
// Register and configure
|
||||||
|
const defaults = jsonInterpolation.register();
|
||||||
|
|
||||||
|
defaults.setVariableContext({
|
||||||
|
getVariables: () => [
|
||||||
|
{ name: 'API_URL', type: 'string', value: 'https://api.example.com' },
|
||||||
|
{ name: 'VERSION', type: 'string', value: '1.0.0' },
|
||||||
|
{ name: 'DEBUG', type: 'boolean', value: false }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create editor
|
||||||
|
const editor = monaco.editor.create(document.getElementById('editor'), {
|
||||||
|
value: `{
|
||||||
|
"endpoint": "\${API_URL}/users",
|
||||||
|
"version": "\${VERSION}",
|
||||||
|
"settings": {
|
||||||
|
"debug": \${DEBUG}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
language: 'json-interpolation',
|
||||||
|
theme: 'vs-dark'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
43
packages/monaco-json-interpolation/package.json
Normal file
43
packages/monaco-json-interpolation/package.json
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"name": "monaco-json-interpolation",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Monaco Editor language support for JSON with ${...} variable interpolation",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"module": "dist/index.mjs",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"import": "./dist/index.mjs",
|
||||||
|
"require": "./dist/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
||||||
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"prepublishOnly": "npm run build"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"monaco",
|
||||||
|
"monaco-editor",
|
||||||
|
"json",
|
||||||
|
"interpolation",
|
||||||
|
"template",
|
||||||
|
"variables"
|
||||||
|
],
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"monaco-editor": ">=0.30.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"monaco-editor": "^0.52.0",
|
||||||
|
"tsup": "^8.0.0",
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
334
packages/monaco-json-interpolation/src/index.ts
Normal file
334
packages/monaco-json-interpolation/src/index.ts
Normal file
|
|
@ -0,0 +1,334 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Monaco JSON Interpolation
|
||||||
|
* Standalone add-on for Monaco Editor providing JSON with ${...} variable interpolation
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as monaco from 'monaco-editor';
|
||||||
|
import { conf, language } from './tokenizer';
|
||||||
|
|
||||||
|
// Re-export types
|
||||||
|
export * from './types';
|
||||||
|
|
||||||
|
// --- Language Registration ---
|
||||||
|
|
||||||
|
const LANGUAGE_ID = 'json-interpolation';
|
||||||
|
|
||||||
|
let isRegistered = false;
|
||||||
|
let currentDefaults: LanguageServiceDefaultsImpl | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the json-interpolation language with Monaco Editor.
|
||||||
|
* This should be called once before creating any editors with this language.
|
||||||
|
*/
|
||||||
|
export function register(): LanguageServiceDefaults {
|
||||||
|
if (isRegistered && currentDefaults) {
|
||||||
|
return currentDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the language
|
||||||
|
monaco.languages.register({
|
||||||
|
id: LANGUAGE_ID,
|
||||||
|
extensions: ['.jsonc', '.json5'],
|
||||||
|
aliases: ['JSON with Interpolation', 'json-interpolation'],
|
||||||
|
mimetypes: ['application/json-interpolation']
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the Monarch tokenizer
|
||||||
|
monaco.languages.setMonarchTokensProvider(LANGUAGE_ID, language);
|
||||||
|
|
||||||
|
// Set the language configuration
|
||||||
|
monaco.languages.setLanguageConfiguration(LANGUAGE_ID, conf);
|
||||||
|
|
||||||
|
// Create defaults
|
||||||
|
currentDefaults = new LanguageServiceDefaultsImpl();
|
||||||
|
|
||||||
|
// Register providers
|
||||||
|
registerProviders(currentDefaults);
|
||||||
|
|
||||||
|
isRegistered = true;
|
||||||
|
return currentDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the language service defaults for configuring the language.
|
||||||
|
* Automatically registers the language if not already registered.
|
||||||
|
*/
|
||||||
|
export function getDefaults(): LanguageServiceDefaults {
|
||||||
|
if (!currentDefaults) {
|
||||||
|
return register();
|
||||||
|
}
|
||||||
|
return currentDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Types ---
|
||||||
|
|
||||||
|
import type {
|
||||||
|
VariableDefinition,
|
||||||
|
VariableContextProvider,
|
||||||
|
LanguageServiceDefaults,
|
||||||
|
DiagnosticsOptions,
|
||||||
|
ModeConfiguration
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
// --- Implementation ---
|
||||||
|
|
||||||
|
class LanguageServiceDefaultsImpl implements LanguageServiceDefaults {
|
||||||
|
private _onDidChange = new monaco.Emitter<LanguageServiceDefaults>();
|
||||||
|
private _diagnosticsOptions: DiagnosticsOptions;
|
||||||
|
private _modeConfiguration: ModeConfiguration;
|
||||||
|
private _variableContext: VariableContextProvider | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._diagnosticsOptions = {
|
||||||
|
validate: true,
|
||||||
|
allowComments: true,
|
||||||
|
allowTrailingCommas: true,
|
||||||
|
schemas: [],
|
||||||
|
schemaValidation: 'warning',
|
||||||
|
comments: 'ignore',
|
||||||
|
trailingCommas: 'ignore'
|
||||||
|
};
|
||||||
|
this._modeConfiguration = {
|
||||||
|
completionItems: true,
|
||||||
|
hovers: true,
|
||||||
|
documentSymbols: true,
|
||||||
|
tokens: true,
|
||||||
|
foldingRanges: true,
|
||||||
|
diagnostics: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get onDidChange(): monaco.IEvent<LanguageServiceDefaults> {
|
||||||
|
return this._onDidChange.event;
|
||||||
|
}
|
||||||
|
|
||||||
|
get languageId(): string {
|
||||||
|
return LANGUAGE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
get modeConfiguration(): ModeConfiguration {
|
||||||
|
return this._modeConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
get diagnosticsOptions(): DiagnosticsOptions {
|
||||||
|
return this._diagnosticsOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
get variableContext(): VariableContextProvider | null {
|
||||||
|
return this._variableContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDiagnosticsOptions(options: DiagnosticsOptions): void {
|
||||||
|
this._diagnosticsOptions = options || {};
|
||||||
|
this._onDidChange.fire(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
setModeConfiguration(modeConfiguration: ModeConfiguration): void {
|
||||||
|
this._modeConfiguration = modeConfiguration || {};
|
||||||
|
this._onDidChange.fire(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
setVariableContext(provider: VariableContextProvider | null): void {
|
||||||
|
this._variableContext = provider;
|
||||||
|
this._onDidChange.fire(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Providers ---
|
||||||
|
|
||||||
|
function registerProviders(defaults: LanguageServiceDefaultsImpl): void {
|
||||||
|
// Variable completion provider
|
||||||
|
monaco.languages.registerCompletionItemProvider(LANGUAGE_ID, {
|
||||||
|
triggerCharacters: ['$', '{'],
|
||||||
|
|
||||||
|
async provideCompletionItems(
|
||||||
|
model: monaco.editor.ITextModel,
|
||||||
|
position: monaco.Position
|
||||||
|
): Promise<monaco.languages.CompletionList | null> {
|
||||||
|
const variableContext = defaults.variableContext;
|
||||||
|
if (!variableContext) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we're inside an interpolation ${...}
|
||||||
|
const textUntilPosition = model.getValueInRange({
|
||||||
|
startLineNumber: 1,
|
||||||
|
startColumn: 1,
|
||||||
|
endLineNumber: position.lineNumber,
|
||||||
|
endColumn: position.column
|
||||||
|
});
|
||||||
|
|
||||||
|
const lastInterpolationStart = textUntilPosition.lastIndexOf('${');
|
||||||
|
if (lastInterpolationStart === -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const afterInterpolationStart = textUntilPosition.substring(lastInterpolationStart);
|
||||||
|
if (afterInterpolationStart.includes('}')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const variables = await variableContext.getVariables();
|
||||||
|
const wordInfo = model.getWordUntilPosition(position);
|
||||||
|
const range = {
|
||||||
|
startLineNumber: position.lineNumber,
|
||||||
|
startColumn: wordInfo.startColumn,
|
||||||
|
endLineNumber: position.lineNumber,
|
||||||
|
endColumn: wordInfo.endColumn
|
||||||
|
};
|
||||||
|
|
||||||
|
const suggestions: monaco.languages.CompletionItem[] = variables.map((variable) => ({
|
||||||
|
label: variable.name,
|
||||||
|
kind: monaco.languages.CompletionItemKind.Variable,
|
||||||
|
detail: variable.detail || variable.type,
|
||||||
|
documentation: formatDocumentation(variable),
|
||||||
|
insertText: variable.name,
|
||||||
|
range: range
|
||||||
|
}));
|
||||||
|
|
||||||
|
return { suggestions };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Variable hover provider
|
||||||
|
monaco.languages.registerHoverProvider(LANGUAGE_ID, {
|
||||||
|
async provideHover(
|
||||||
|
model: monaco.editor.ITextModel,
|
||||||
|
position: monaco.Position
|
||||||
|
): Promise<monaco.languages.Hover | null> {
|
||||||
|
const variableContext = defaults.variableContext;
|
||||||
|
if (!variableContext) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const line = model.getLineContent(position.lineNumber);
|
||||||
|
const offset = position.column - 1;
|
||||||
|
|
||||||
|
// Find interpolation boundaries
|
||||||
|
let inInterpolation = false;
|
||||||
|
let interpStart = -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < line.length - 1; i++) {
|
||||||
|
if (line[i] === '$' && line[i + 1] === '{') {
|
||||||
|
if (i < offset) {
|
||||||
|
inInterpolation = true;
|
||||||
|
interpStart = i + 2;
|
||||||
|
}
|
||||||
|
} else if (line[i] === '}' && inInterpolation) {
|
||||||
|
if (i >= offset) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
inInterpolation = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inInterpolation || interpStart === -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wordInfo = model.getWordAtPosition(position);
|
||||||
|
if (!wordInfo) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const variableName = wordInfo.word;
|
||||||
|
const variables = await variableContext.getVariables();
|
||||||
|
const variable = variables.find((v) => v.name === variableName);
|
||||||
|
|
||||||
|
if (!variable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contents: monaco.IMarkdownString[] = [];
|
||||||
|
|
||||||
|
if (variable.type) {
|
||||||
|
contents.push({
|
||||||
|
value: `\`\`\`typescript\n(variable) ${variable.name}: ${variable.type}\n\`\`\``
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
contents.push({
|
||||||
|
value: `\`\`\`typescript\n(variable) ${variable.name}\n\`\`\``
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variable.description) {
|
||||||
|
contents.push({ value: variable.description });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variable.value !== undefined) {
|
||||||
|
contents.push({
|
||||||
|
value: `**Current value:**\n\`\`\`json\n${JSON.stringify(variable.value, null, 2)}\n\`\`\``
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
contents,
|
||||||
|
range: {
|
||||||
|
startLineNumber: position.lineNumber,
|
||||||
|
startColumn: wordInfo.startColumn,
|
||||||
|
endLineNumber: position.lineNumber,
|
||||||
|
endColumn: wordInfo.endColumn
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Folding range provider (basic JSON-like folding)
|
||||||
|
monaco.languages.registerFoldingRangeProvider(LANGUAGE_ID, {
|
||||||
|
provideFoldingRanges(
|
||||||
|
model: monaco.editor.ITextModel
|
||||||
|
): monaco.languages.FoldingRange[] {
|
||||||
|
const ranges: monaco.languages.FoldingRange[] = [];
|
||||||
|
const stack: { char: string; line: number }[] = [];
|
||||||
|
|
||||||
|
for (let i = 1; i <= model.getLineCount(); i++) {
|
||||||
|
const line = model.getLineContent(i);
|
||||||
|
for (const char of line) {
|
||||||
|
if (char === '{' || char === '[') {
|
||||||
|
stack.push({ char, line: i });
|
||||||
|
} else if (char === '}' || char === ']') {
|
||||||
|
const open = stack.pop();
|
||||||
|
if (open && open.line < i) {
|
||||||
|
ranges.push({
|
||||||
|
start: open.line,
|
||||||
|
end: i,
|
||||||
|
kind: monaco.languages.FoldingRangeKind.Region
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ranges;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDocumentation(
|
||||||
|
variable: VariableDefinition
|
||||||
|
): string | monaco.IMarkdownString {
|
||||||
|
let doc = '';
|
||||||
|
|
||||||
|
if (variable.description) {
|
||||||
|
doc += variable.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variable.value !== undefined) {
|
||||||
|
if (doc) {
|
||||||
|
doc += '\n\n';
|
||||||
|
}
|
||||||
|
doc += `**Current value:** \`${JSON.stringify(variable.value)}\``;
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc ? { value: doc } : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Convenience export ---
|
||||||
|
|
||||||
|
export const jsonInterpolation = {
|
||||||
|
register,
|
||||||
|
getDefaults,
|
||||||
|
LANGUAGE_ID
|
||||||
|
};
|
||||||
|
|
||||||
|
export default jsonInterpolation;
|
||||||
127
packages/monaco-json-interpolation/src/tokenizer.ts
Normal file
127
packages/monaco-json-interpolation/src/tokenizer.ts
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* JSON with Interpolation - Monarch Tokenizer
|
||||||
|
* Supports ${...} interpolation with embedded JavaScript highlighting
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import type * as monaco from 'monaco-editor';
|
||||||
|
|
||||||
|
export const conf: monaco.languages.LanguageConfiguration = {
|
||||||
|
wordPattern: /(-?\d*\.\d\w*)|([^\[\{\]\}\:\"\,\s]+)/g,
|
||||||
|
|
||||||
|
comments: {
|
||||||
|
lineComment: '//',
|
||||||
|
blockComment: ['/*', '*/']
|
||||||
|
},
|
||||||
|
|
||||||
|
brackets: [
|
||||||
|
['{', '}'],
|
||||||
|
['[', ']'],
|
||||||
|
['${', '}']
|
||||||
|
],
|
||||||
|
|
||||||
|
autoClosingPairs: [
|
||||||
|
{ open: '{', close: '}' },
|
||||||
|
{ open: '[', close: ']' },
|
||||||
|
{ open: '"', close: '"', notIn: ['string'] },
|
||||||
|
{ open: '${', close: '}' }
|
||||||
|
],
|
||||||
|
|
||||||
|
surroundingPairs: [
|
||||||
|
{ open: '{', close: '}' },
|
||||||
|
{ open: '[', close: ']' },
|
||||||
|
{ open: '"', close: '"' }
|
||||||
|
],
|
||||||
|
|
||||||
|
folding: {
|
||||||
|
markers: {
|
||||||
|
start: /^\s*\/\/\s*#?region\b/,
|
||||||
|
end: /^\s*\/\/\s*#?endregion\b/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const language: monaco.languages.IMonarchLanguage = {
|
||||||
|
defaultToken: '',
|
||||||
|
tokenPostfix: '.json-interpolation',
|
||||||
|
|
||||||
|
escapes: /\\(?:["\\/bfnrt]|u[0-9A-Fa-f]{4})/,
|
||||||
|
|
||||||
|
tokenizer: {
|
||||||
|
root: [
|
||||||
|
[/\s+/, ''],
|
||||||
|
[/\/\/.*$/, 'comment'],
|
||||||
|
[/\/\*/, 'comment', '@comment'],
|
||||||
|
[/[{]/, 'delimiter.bracket', '@object'],
|
||||||
|
[/\[/, 'delimiter.array', '@array']
|
||||||
|
],
|
||||||
|
|
||||||
|
object: [
|
||||||
|
[/\s+/, ''],
|
||||||
|
[/\/\/.*$/, 'comment'],
|
||||||
|
[/\/\*/, 'comment', '@comment'],
|
||||||
|
[/"/, 'string.key', '@propertyName'],
|
||||||
|
[/:/, 'delimiter.colon'],
|
||||||
|
[/,/, 'delimiter.comma'],
|
||||||
|
{ include: '@value' },
|
||||||
|
[/\}/, 'delimiter.bracket', '@pop']
|
||||||
|
],
|
||||||
|
|
||||||
|
propertyName: [
|
||||||
|
[/[^"\\]+/, 'string.key'],
|
||||||
|
[/@escapes/, 'string.escape'],
|
||||||
|
[/\\./, 'string.escape.invalid'],
|
||||||
|
[/"/, 'string.key', '@pop']
|
||||||
|
],
|
||||||
|
|
||||||
|
array: [
|
||||||
|
[/\s+/, ''],
|
||||||
|
[/\/\/.*$/, 'comment'],
|
||||||
|
[/\/\*/, 'comment', '@comment'],
|
||||||
|
[/,/, 'delimiter.comma'],
|
||||||
|
{ include: '@value' },
|
||||||
|
[/\]/, 'delimiter.array', '@pop']
|
||||||
|
],
|
||||||
|
|
||||||
|
value: [
|
||||||
|
[/"/, 'string.value', '@string'],
|
||||||
|
[/-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/, 'number'],
|
||||||
|
[/true|false/, 'keyword'],
|
||||||
|
[/null/, 'keyword'],
|
||||||
|
[/\{/, 'delimiter.bracket', '@object'],
|
||||||
|
[/\[/, 'delimiter.array', '@array']
|
||||||
|
],
|
||||||
|
|
||||||
|
string: [
|
||||||
|
[
|
||||||
|
/\$\{/,
|
||||||
|
{
|
||||||
|
token: 'delimiter.bracket.interpolation',
|
||||||
|
next: '@interpolation',
|
||||||
|
nextEmbedded: 'javascript'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[/[^"\\$]+/, 'string.value'],
|
||||||
|
[/@escapes/, 'string.escape'],
|
||||||
|
[/\\./, 'string.escape.invalid'],
|
||||||
|
[/\$(?!\{)/, 'string.value'],
|
||||||
|
[/"/, 'string.value', '@pop']
|
||||||
|
],
|
||||||
|
|
||||||
|
interpolation: [
|
||||||
|
[
|
||||||
|
/\}/,
|
||||||
|
{
|
||||||
|
token: 'delimiter.bracket.interpolation',
|
||||||
|
next: '@pop',
|
||||||
|
nextEmbedded: '@pop'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
comment: [
|
||||||
|
[/[^/*]+/, 'comment'],
|
||||||
|
[/\*\//, 'comment', '@pop'],
|
||||||
|
[/[/*]/, 'comment']
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
148
packages/monaco-json-interpolation/src/types.ts
Normal file
148
packages/monaco-json-interpolation/src/types.ts
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Monaco JSON Interpolation - Type Definitions
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import type * as monaco from 'monaco-editor';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a variable that can be used in interpolation
|
||||||
|
*/
|
||||||
|
export interface VariableDefinition {
|
||||||
|
/**
|
||||||
|
* The name of the variable (without $ prefix)
|
||||||
|
*/
|
||||||
|
readonly name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the variable for display purposes
|
||||||
|
*/
|
||||||
|
readonly type?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description shown in hover and completion
|
||||||
|
*/
|
||||||
|
readonly description?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current value of the variable (for hover preview)
|
||||||
|
*/
|
||||||
|
readonly value?: unknown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional detail text shown in completion item
|
||||||
|
*/
|
||||||
|
readonly detail?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context provider for interpolation variables
|
||||||
|
*/
|
||||||
|
export interface VariableContextProvider {
|
||||||
|
/**
|
||||||
|
* Get all available variables
|
||||||
|
*/
|
||||||
|
getVariables(): VariableDefinition[] | Promise<VariableDefinition[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a variable by name (optional, for nested property access)
|
||||||
|
*/
|
||||||
|
resolveVariable?(name: string): unknown | Promise<unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Diagnostics configuration options
|
||||||
|
*/
|
||||||
|
export interface DiagnosticsOptions {
|
||||||
|
/**
|
||||||
|
* If set, the validator will be enabled
|
||||||
|
*/
|
||||||
|
readonly validate?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, comments are tolerated
|
||||||
|
*/
|
||||||
|
readonly allowComments?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, trailing commas are tolerated
|
||||||
|
*/
|
||||||
|
readonly allowTrailingCommas?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of known schemas
|
||||||
|
*/
|
||||||
|
readonly schemas?: {
|
||||||
|
readonly uri: string;
|
||||||
|
readonly fileMatch?: string[];
|
||||||
|
readonly schema?: unknown;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The severity of problems from schema validation
|
||||||
|
*/
|
||||||
|
readonly schemaValidation?: 'error' | 'warning' | 'ignore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The severity of trailing commas
|
||||||
|
*/
|
||||||
|
readonly trailingCommas?: 'error' | 'warning' | 'ignore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The severity of comments
|
||||||
|
*/
|
||||||
|
readonly comments?: 'error' | 'warning' | 'ignore';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mode configuration options
|
||||||
|
*/
|
||||||
|
export interface ModeConfiguration {
|
||||||
|
/**
|
||||||
|
* Defines whether the built-in completionItemProvider is enabled
|
||||||
|
*/
|
||||||
|
readonly completionItems?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines whether the built-in hoverProvider is enabled
|
||||||
|
*/
|
||||||
|
readonly hovers?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines whether the built-in documentSymbolProvider is enabled
|
||||||
|
*/
|
||||||
|
readonly documentSymbols?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines whether the built-in tokens provider is enabled
|
||||||
|
*/
|
||||||
|
readonly tokens?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines whether the built-in foldingRange provider is enabled
|
||||||
|
*/
|
||||||
|
readonly foldingRanges?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines whether the built-in diagnostic provider is enabled
|
||||||
|
*/
|
||||||
|
readonly diagnostics?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Language service configuration defaults
|
||||||
|
*/
|
||||||
|
export interface LanguageServiceDefaults {
|
||||||
|
readonly languageId: string;
|
||||||
|
readonly onDidChange: monaco.IEvent<LanguageServiceDefaults>;
|
||||||
|
readonly diagnosticsOptions: DiagnosticsOptions;
|
||||||
|
readonly modeConfiguration: ModeConfiguration;
|
||||||
|
readonly variableContext: VariableContextProvider | null;
|
||||||
|
|
||||||
|
setDiagnosticsOptions(options: DiagnosticsOptions): void;
|
||||||
|
setModeConfiguration(modeConfiguration: ModeConfiguration): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the variable context provider for interpolation completions and hover
|
||||||
|
*/
|
||||||
|
setVariableContext(provider: VariableContextProvider | null): void;
|
||||||
|
}
|
||||||
26
packages/monaco-json-interpolation/tsconfig.json
Normal file
26
packages/monaco-json-interpolation/tsconfig.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2020", "DOM"],
|
||||||
|
"declaration": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": false,
|
||||||
|
"inlineSourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"outDir": "./dist"
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue