mirror of
https://github.com/microsoft/monaco-editor.git
synced 2025-12-22 13:55:41 +01:00
Add manual test page for json-interpolation language
- Add json-interpolation to dev-setup.js module loading - Create standalone test page that works with CDN Monaco - Test page demonstrates syntax highlighting, completions, hover info, comments, and trailing comma support
This commit is contained in:
parent
8ca12ee61a
commit
9b018b7916
3 changed files with 458 additions and 0 deletions
|
|
@ -105,6 +105,7 @@
|
||||||
'vs/language/css/monaco.contribution',
|
'vs/language/css/monaco.contribution',
|
||||||
'vs/language/html/monaco.contribution',
|
'vs/language/html/monaco.contribution',
|
||||||
'vs/language/json/monaco.contribution',
|
'vs/language/json/monaco.contribution',
|
||||||
|
'vs/language/json-interpolation/monaco.contribution',
|
||||||
'vs/language/typescript/monaco.contribution'
|
'vs/language/typescript/monaco.contribution'
|
||||||
],
|
],
|
||||||
callback
|
callback
|
||||||
|
|
|
||||||
105
test/manual/json-interpolation/index.html
Normal file
105
test/manual/json-interpolation/index.html
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||||
|
<link rel="stylesheet" href="../index.css" />
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 10px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.info h3 {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
}
|
||||||
|
.info ul {
|
||||||
|
margin: 5px 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a class="loading-opts" href="../index2.html">[Back to main]</a>
|
||||||
|
<h2>JSON with Interpolation Test</h2>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<h3>Features to test:</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Syntax highlighting</strong>: Notice ${...} interpolations have JavaScript highlighting</li>
|
||||||
|
<li><strong>Completions</strong>: Type inside ${} to see JavaScript completions</li>
|
||||||
|
<li><strong>Hover</strong>: Hover over variables inside ${} for type info</li>
|
||||||
|
<li><strong>Comments</strong>: // and /* */ comments are supported</li>
|
||||||
|
<li><strong>Trailing commas</strong>: Trailing commas are allowed</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="container"
|
||||||
|
style="width: 800px; height: 400px; border: 1px solid grey"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<script src="../dev-setup.js"></script>
|
||||||
|
<script>
|
||||||
|
loadEditor(function () {
|
||||||
|
// Set up JavaScript extra libs so the JS worker knows about these variables
|
||||||
|
monaco.languages.typescript.javascriptDefaults.setExtraLibs([
|
||||||
|
{
|
||||||
|
content: `
|
||||||
|
declare const name: string;
|
||||||
|
declare const env: string;
|
||||||
|
declare const config: { debug: boolean; port: number };
|
||||||
|
declare const API_URL: string;
|
||||||
|
declare const VERSION: string;
|
||||||
|
|
||||||
|
// Example functions
|
||||||
|
declare function formatDate(date: Date): string;
|
||||||
|
declare function capitalize(str: string): string;
|
||||||
|
`,
|
||||||
|
filePath: 'file:///globals.d.ts'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Sample JSON with interpolation
|
||||||
|
var sampleContent = [
|
||||||
|
'{',
|
||||||
|
' // This is a JSON file with interpolation support',
|
||||||
|
' "greeting": "Hello, ${name}!",',
|
||||||
|
' "environment": "${env}",',
|
||||||
|
' "apiEndpoint": "${API_URL}/users",',
|
||||||
|
' "version": "${VERSION}",',
|
||||||
|
'',
|
||||||
|
' /* Multi-line comment support */',
|
||||||
|
' "settings": {',
|
||||||
|
' "debug": ${config.debug},',
|
||||||
|
' "port": ${config.port}',
|
||||||
|
' },',
|
||||||
|
'',
|
||||||
|
' // Try typing inside ${} to see JavaScript completions',
|
||||||
|
' "computed": "${capitalize(name)}",',
|
||||||
|
'',
|
||||||
|
' // Trailing commas are allowed',
|
||||||
|
' "items": [1, 2, 3,],',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
// Create the editor
|
||||||
|
var editor = monaco.editor.create(document.getElementById('container'), {
|
||||||
|
value: sampleContent,
|
||||||
|
language: 'json-interpolation',
|
||||||
|
theme: 'vs-dark',
|
||||||
|
minimap: { enabled: false },
|
||||||
|
automaticLayout: true,
|
||||||
|
fontSize: 14,
|
||||||
|
tabSize: 2
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('JSON Interpolation editor initialized');
|
||||||
|
console.log('Available languages:', monaco.languages.getLanguages().map(l => l.id));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
352
test/manual/json-interpolation/standalone.html
Normal file
352
test/manual/json-interpolation/standalone.html
Normal file
|
|
@ -0,0 +1,352 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>JSON Interpolation - Standalone Test</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
background: #1e1e1e;
|
||||||
|
color: #d4d4d4;
|
||||||
|
}
|
||||||
|
h2 { margin-bottom: 10px; }
|
||||||
|
.info {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 10px;
|
||||||
|
background: #2d2d2d;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.info h3 { margin: 0 0 10px 0; }
|
||||||
|
.info ul { margin: 5px 0; padding-left: 20px; }
|
||||||
|
#container {
|
||||||
|
width: 800px;
|
||||||
|
height: 400px;
|
||||||
|
border: 1px solid #444;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>JSON with Interpolation - Standalone Test</h2>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<h3>Features to test:</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Syntax highlighting</strong>: Notice ${...} interpolations have JavaScript highlighting</li>
|
||||||
|
<li><strong>Completions</strong>: Type inside ${} to see JavaScript completions for name, env, config, etc.</li>
|
||||||
|
<li><strong>Hover</strong>: Hover over variables inside ${} for type info</li>
|
||||||
|
<li><strong>Comments</strong>: // and /* */ comments are supported</li>
|
||||||
|
<li><strong>Trailing commas</strong>: Trailing commas are allowed</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="container"></div>
|
||||||
|
|
||||||
|
<!-- Load Monaco from CDN -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.52.0/min/vs/loader.js"></script>
|
||||||
|
<script>
|
||||||
|
require.config({
|
||||||
|
paths: { 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.0/min/vs' }
|
||||||
|
});
|
||||||
|
|
||||||
|
require(['vs/editor/editor.main'], function() {
|
||||||
|
// --- Register json-interpolation language ---
|
||||||
|
|
||||||
|
const LANGUAGE_ID = 'json-interpolation';
|
||||||
|
|
||||||
|
// Register the language
|
||||||
|
monaco.languages.register({
|
||||||
|
id: LANGUAGE_ID,
|
||||||
|
extensions: ['.jsonc', '.json5'],
|
||||||
|
aliases: ['JSON with Interpolation', 'json-interpolation'],
|
||||||
|
mimetypes: ['application/json-interpolation']
|
||||||
|
});
|
||||||
|
|
||||||
|
// Language configuration
|
||||||
|
monaco.languages.setLanguageConfiguration(LANGUAGE_ID, {
|
||||||
|
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: '"' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Monarch tokenizer with nextEmbedded for JavaScript
|
||||||
|
monaco.languages.setMonarchTokensProvider(LANGUAGE_ID, {
|
||||||
|
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']
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set up JavaScript extra libs for variable completions inside ${}
|
||||||
|
monaco.languages.typescript.javascriptDefaults.setExtraLibs([
|
||||||
|
{
|
||||||
|
content: `
|
||||||
|
/** The name to greet */
|
||||||
|
declare const name: string;
|
||||||
|
/** Current environment */
|
||||||
|
declare const env: string;
|
||||||
|
/** Application configuration */
|
||||||
|
declare const config: { debug: boolean; port: number };
|
||||||
|
/** Base API URL */
|
||||||
|
declare const API_URL: string;
|
||||||
|
/** Application version */
|
||||||
|
declare const VERSION: string;
|
||||||
|
|
||||||
|
/** Format a date to string */
|
||||||
|
declare function formatDate(date: Date): string;
|
||||||
|
/** Capitalize a string */
|
||||||
|
declare function capitalize(str: string): string;
|
||||||
|
`,
|
||||||
|
filePath: 'file:///globals.d.ts'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// --- Helper to check if inside interpolation using tokenization ---
|
||||||
|
function isInsideInterpolation(model, offset) {
|
||||||
|
const source = model.getValue();
|
||||||
|
const tokenLines = monaco.editor.tokenize(source, LANGUAGE_ID);
|
||||||
|
|
||||||
|
let currentOffset = 0;
|
||||||
|
for (let lineIdx = 0; lineIdx < tokenLines.length; lineIdx++) {
|
||||||
|
const lineTokens = tokenLines[lineIdx];
|
||||||
|
const lineContent = model.getLineContent(lineIdx + 1);
|
||||||
|
|
||||||
|
for (let tokenIdx = 0; tokenIdx < lineTokens.length; tokenIdx++) {
|
||||||
|
const token = lineTokens[tokenIdx];
|
||||||
|
const nextToken = lineTokens[tokenIdx + 1];
|
||||||
|
const tokenStart = currentOffset + token.offset;
|
||||||
|
const tokenEnd = nextToken
|
||||||
|
? currentOffset + nextToken.offset
|
||||||
|
: currentOffset + lineContent.length;
|
||||||
|
|
||||||
|
if (offset >= tokenStart && offset <= tokenEnd) {
|
||||||
|
// Check if in embedded JS (token type doesn't end with our language)
|
||||||
|
return !token.type.endsWith('.json-interpolation') && token.type !== '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentOffset += lineContent.length + 1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Completion provider for JS inside interpolation ---
|
||||||
|
monaco.languages.registerCompletionItemProvider(LANGUAGE_ID, {
|
||||||
|
triggerCharacters: ['"', ':', ' ', '$', '{', '.'],
|
||||||
|
|
||||||
|
async provideCompletionItems(model, position) {
|
||||||
|
const offset = model.getOffsetAt(position);
|
||||||
|
|
||||||
|
if (isInsideInterpolation(model, offset)) {
|
||||||
|
try {
|
||||||
|
const getWorker = await monaco.languages.typescript.getJavaScriptWorker();
|
||||||
|
const worker = await getWorker(model.uri);
|
||||||
|
const info = await worker.getCompletionsAtPosition(model.uri.toString(), offset);
|
||||||
|
|
||||||
|
if (!info || !info.entries) return null;
|
||||||
|
|
||||||
|
const wordInfo = model.getWordUntilPosition(position);
|
||||||
|
const range = {
|
||||||
|
startLineNumber: position.lineNumber,
|
||||||
|
startColumn: wordInfo.startColumn,
|
||||||
|
endLineNumber: position.lineNumber,
|
||||||
|
endColumn: wordInfo.endColumn
|
||||||
|
};
|
||||||
|
|
||||||
|
const suggestions = info.entries.map(entry => ({
|
||||||
|
label: entry.name,
|
||||||
|
kind: monaco.languages.CompletionItemKind.Variable,
|
||||||
|
detail: entry.kindModifiers,
|
||||||
|
sortText: entry.sortText,
|
||||||
|
insertText: entry.insertText || entry.name,
|
||||||
|
range: range
|
||||||
|
}));
|
||||||
|
|
||||||
|
return { suggestions };
|
||||||
|
} catch (e) {
|
||||||
|
console.error('JS completion error:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Hover provider for JS inside interpolation ---
|
||||||
|
monaco.languages.registerHoverProvider(LANGUAGE_ID, {
|
||||||
|
async provideHover(model, position) {
|
||||||
|
const offset = model.getOffsetAt(position);
|
||||||
|
|
||||||
|
if (isInsideInterpolation(model, offset)) {
|
||||||
|
try {
|
||||||
|
const getWorker = await monaco.languages.typescript.getJavaScriptWorker();
|
||||||
|
const worker = await getWorker(model.uri);
|
||||||
|
const info = await worker.getQuickInfoAtPosition(model.uri.toString(), offset);
|
||||||
|
|
||||||
|
if (!info) return null;
|
||||||
|
|
||||||
|
const contents = [];
|
||||||
|
if (info.displayParts) {
|
||||||
|
const displayText = info.displayParts.map(p => p.text).join('');
|
||||||
|
contents.push({ value: '```typescript\n' + displayText + '\n```' });
|
||||||
|
}
|
||||||
|
if (info.documentation && info.documentation.length > 0) {
|
||||||
|
const docText = info.documentation.map(p => p.text).join('');
|
||||||
|
contents.push({ value: docText });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contents.length === 0) return null;
|
||||||
|
|
||||||
|
const wordInfo = model.getWordAtPosition(position);
|
||||||
|
const range = wordInfo ? {
|
||||||
|
startLineNumber: position.lineNumber,
|
||||||
|
startColumn: wordInfo.startColumn,
|
||||||
|
endLineNumber: position.lineNumber,
|
||||||
|
endColumn: wordInfo.endColumn
|
||||||
|
} : undefined;
|
||||||
|
|
||||||
|
return { contents, range };
|
||||||
|
} catch (e) {
|
||||||
|
console.error('JS hover error:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Sample JSON content with interpolation ---
|
||||||
|
const sampleContent = `{
|
||||||
|
// This is a JSON file with interpolation support
|
||||||
|
"greeting": "Hello, \${name}!",
|
||||||
|
"environment": "\${env}",
|
||||||
|
"apiEndpoint": "\${API_URL}/users",
|
||||||
|
"version": "\${VERSION}",
|
||||||
|
|
||||||
|
/* Multi-line comment support */
|
||||||
|
"settings": {
|
||||||
|
"debug": \${config.debug},
|
||||||
|
"port": \${config.port}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Try typing inside \${} to see JavaScript completions
|
||||||
|
"computed": "\${capitalize(name)}",
|
||||||
|
|
||||||
|
// Trailing commas are allowed
|
||||||
|
"items": [1, 2, 3,],
|
||||||
|
}`;
|
||||||
|
|
||||||
|
// Create the editor
|
||||||
|
const editor = monaco.editor.create(document.getElementById('container'), {
|
||||||
|
value: sampleContent,
|
||||||
|
language: LANGUAGE_ID,
|
||||||
|
theme: 'vs-dark',
|
||||||
|
minimap: { enabled: false },
|
||||||
|
automaticLayout: true,
|
||||||
|
fontSize: 14,
|
||||||
|
tabSize: 2
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('JSON Interpolation editor initialized');
|
||||||
|
console.log('Try:');
|
||||||
|
console.log(' - Hover over ${name} to see variable info');
|
||||||
|
console.log(' - Type inside ${} to get JavaScript completions');
|
||||||
|
console.log(' - Notice syntax highlighting for embedded JS');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue