mirror of
https://github.com/danbulant/monaco-yaml
synced 2026-05-24 12:21:53 +00:00
feat: some adjustment to auto completion
This commit is contained in:
parent
56dda31a03
commit
a478bef722
7 changed files with 153 additions and 93 deletions
|
|
@ -40,7 +40,7 @@ export abstract class ASTNodeImpl {
|
|||
|
||||
constructor(parent: ASTNode, offset: number, length?: number) {
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
this.length = length || 0;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
|
|
@ -290,6 +290,30 @@ export function contains(node: ASTNode, offset: number, includeRightBound = fals
|
|||
return offset >= node.offset && offset < (node.offset + node.length) || includeRightBound && offset === (node.offset + node.length);
|
||||
}
|
||||
|
||||
// export function contains(node: ASTNode, offset: number, includeRightBound = false): boolean {
|
||||
// let flag = offset >= node.offset && offset <= (node.offset + node.length);
|
||||
// if (!flag && includeRightBound) {
|
||||
// if (node.parent && node.parent.children && )
|
||||
// const nextSibling = node.parent
|
||||
// }
|
||||
// return flag;
|
||||
// }
|
||||
|
||||
// export function findNodeAtOffset(node: ASTNode, offset: number, includeRightBound = false): ASTNode | undefined {
|
||||
// if (contains(node, offset, includeRightBound)) {
|
||||
// const children = node.children;
|
||||
// if (Array.isArray(children)) {
|
||||
// for (var i = 0; i < children.length && children[i].offset <= offset; i++) {
|
||||
// const item = findNodeAtOffset(children[i], offset, includeRightBound);
|
||||
// if (item) {
|
||||
// return item;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return node;
|
||||
// }
|
||||
// }
|
||||
|
||||
export class JSONDocument {
|
||||
|
||||
constructor(public root: ASTNode, public readonly syntaxErrors: Diagnostic[] = [], public readonly comments: Range[] = []) {
|
||||
|
|
@ -298,6 +322,7 @@ export class JSONDocument {
|
|||
public getNodeFromOffset(offset: number, includeRightBound = false): ASTNode | undefined {
|
||||
if (this.root) {
|
||||
return <ASTNode>Json.findNodeAtOffset(this.root, offset, includeRightBound);
|
||||
//return findNodeAtOffset(this.root, offset, includeRightBound);
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ import * as nls from 'vscode-nls';
|
|||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import * as Yaml from '../../yaml-ast-parser/index'
|
||||
import { Schema, Type } from 'js-yaml';
|
||||
|
||||
import { getLineStartPositions } from '../utils/documentPositionCalculator'
|
||||
import { parseYamlBoolean } from './scalar-type';
|
||||
import { ObjectASTNodeImpl, StringASTNodeImpl, PropertyASTNodeImpl, NullASTNodeImpl, ArrayASTNodeImpl, BooleanASTNodeImpl, NumberASTNodeImpl } from './jsonParser';
|
||||
import { ASTNode, PropertyASTNode, ErrorCode } from '../jsonLanguageTypes';
|
||||
import { SingleYAMLDocument, YAMLDocument } from '../yamlLanguageTypes';
|
||||
import { Schema } from '../../yaml-ast-parser/schema';
|
||||
import { Type } from '../../yaml-ast-parser/type';
|
||||
|
||||
function recursivelyBuildAst(parent: ASTNode, node: Yaml.YAMLNode): ASTNode {
|
||||
|
||||
|
|
@ -47,7 +48,8 @@ function recursivelyBuildAst(parent: ASTNode, node: Yaml.YAMLNode): ASTNode {
|
|||
const keyNode = new StringASTNodeImpl(result, key.startPosition, key.endPosition - key.startPosition);
|
||||
keyNode.value = key.value;
|
||||
|
||||
const valueNode = (instance.value) ? recursivelyBuildAst(result, instance.value) : new NullASTNodeImpl(parent, instance.startPosition)
|
||||
// TODO: calculate the correct NULL range.
|
||||
const valueNode = (instance.value) ? recursivelyBuildAst(result, instance.value) : new NullASTNodeImpl(parent, instance.endPosition);
|
||||
|
||||
result.keyNode = keyNode;
|
||||
result.valueNode = valueNode;
|
||||
|
|
|
|||
|
|
@ -58,11 +58,13 @@ export class YAMLCompletion {
|
|||
|
||||
let currentDoc = matchOffsetToDocument(offset, doc);
|
||||
|
||||
if(currentDoc === null){
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
const currentDocIndex = doc.documents.indexOf(currentDoc);
|
||||
|
||||
let node = currentDoc.getNodeFromOffset(offset, true);
|
||||
if (currentDoc === null) {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
let node = currentDoc.getNodeFromOffsetEndInclusive(offset);
|
||||
if (this.isInComment(document, node ? node.offset : 0, offset)) {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
|
@ -131,12 +133,13 @@ export class YAMLCompletion {
|
|||
}
|
||||
}
|
||||
|
||||
// Support multiple doc per YAML
|
||||
if (schema && schema.schema && schema.schema.schemaSequence && schema.schema.schemaSequence[currentDocIndex]) {
|
||||
schema = new SchemaService.ResolvedSchema(schema.schema.schemaSequence[currentDocIndex]);
|
||||
}
|
||||
|
||||
// proposals for properties
|
||||
if (node && node.type === 'object') {
|
||||
// don't suggest keys when the cursor is just before the opening curly brace
|
||||
if (node.offset === offset) {
|
||||
return result;
|
||||
}
|
||||
// don't suggest properties that are already present
|
||||
let properties = node.properties;
|
||||
properties.forEach(p => {
|
||||
|
|
@ -362,7 +365,6 @@ export class YAMLCompletion {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private getValueCompletions(schema: SchemaService.ResolvedSchema, doc: Parser.JSONDocument, node: ASTNode, offset: number, document: TextDocument, collector: CompletionsCollector, types: { [type: string]: boolean }): void {
|
||||
let offsetForSeparator = offset;
|
||||
let parentKey: string = null;
|
||||
|
|
@ -679,11 +681,11 @@ export class YAMLCompletion {
|
|||
}
|
||||
|
||||
private getInsertTextForValue(value: any, separatorAfter: string): string {
|
||||
var text = JSON.stringify(value, null, '\t');
|
||||
const text = value;
|
||||
if (text === '{}') {
|
||||
return '{$1}' + separatorAfter;
|
||||
return '{\n\t$1\n}' + separatorAfter;
|
||||
} else if (text === '[]') {
|
||||
return '[$1]' + separatorAfter;
|
||||
return '[\n\t$1\n]' + separatorAfter;
|
||||
}
|
||||
return this.getInsertTextForPlainText(text + separatorAfter);
|
||||
}
|
||||
|
|
@ -767,7 +769,7 @@ export class YAMLCompletion {
|
|||
if (!addValue) {
|
||||
return propertyText;
|
||||
}
|
||||
let resultText = propertyText + ': ';
|
||||
let resultText = propertyText + ':';
|
||||
|
||||
let value;
|
||||
let nValueProposals = 0;
|
||||
|
|
@ -804,23 +806,23 @@ export class YAMLCompletion {
|
|||
}
|
||||
switch (type) {
|
||||
case 'boolean':
|
||||
value = '$1';
|
||||
value = ' $1';
|
||||
break;
|
||||
case 'string':
|
||||
value = '"$1"';
|
||||
value = ' $1';
|
||||
break;
|
||||
case 'object':
|
||||
value = '{$1}';
|
||||
value = '\n\t';
|
||||
break;
|
||||
case 'array':
|
||||
value = '[$1]';
|
||||
value = '\n\t- ';
|
||||
break;
|
||||
case 'number':
|
||||
case 'integer':
|
||||
value = '${1:0}';
|
||||
value = ' ${1:0}';
|
||||
break;
|
||||
case 'null':
|
||||
value = '${1:null}';
|
||||
value = ' ${1:null}';
|
||||
break;
|
||||
default:
|
||||
return propertyText;
|
||||
|
|
@ -834,8 +836,8 @@ export class YAMLCompletion {
|
|||
}
|
||||
|
||||
private getCurrentWord(document: TextDocument, offset: number) {
|
||||
var i = offset - 1;
|
||||
var text = document.getText();
|
||||
let i = offset - 1;
|
||||
const text = document.getText();
|
||||
while (i >= 0 && ' \t\n\r\v":{[,]}'.indexOf(text.charAt(i)) === -1) {
|
||||
i--;
|
||||
}
|
||||
|
|
@ -853,7 +855,7 @@ export class YAMLCompletion {
|
|||
case Json.SyntaxKind.EOF:
|
||||
return '';
|
||||
default:
|
||||
return ',';
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,36 +2,65 @@ import { JSONDocument } from './parser/jsonParser';
|
|||
import { ASTNode } from './jsonLanguageTypes';
|
||||
|
||||
export class SingleYAMLDocument extends JSONDocument {
|
||||
public lines;
|
||||
public errors;
|
||||
public warnings;
|
||||
public lines;
|
||||
public errors;
|
||||
public warnings;
|
||||
|
||||
constructor(lines: number[]) {
|
||||
super(null, []);
|
||||
this.lines = lines;
|
||||
this.errors = [];
|
||||
this.warnings = [];
|
||||
}
|
||||
constructor(lines: number[]) {
|
||||
super(null, []);
|
||||
this.lines = lines;
|
||||
this.errors = [];
|
||||
this.warnings = [];
|
||||
}
|
||||
|
||||
public getSchemas(schema, doc, node) {
|
||||
let matchingSchemas = [];
|
||||
doc.validate(schema, matchingSchemas, node.start);
|
||||
return matchingSchemas;
|
||||
}
|
||||
public getSchemas(schema, doc, node) {
|
||||
let matchingSchemas = [];
|
||||
doc.validate(schema, matchingSchemas, node.start);
|
||||
return matchingSchemas;
|
||||
}
|
||||
|
||||
public getNodeFromOffset(offset: number, includeRightBound = false): ASTNode {
|
||||
return super.getNodeFromOffset(offset, includeRightBound);
|
||||
public getNodeFromOffset(offset: number): ASTNode {
|
||||
return super.getNodeFromOffset(offset, true);
|
||||
}
|
||||
|
||||
public getNodeFromOffsetEndInclusive(offset: number): ASTNode {
|
||||
let collector: ASTNode[] = [];
|
||||
let findNode = (node: ASTNode): ASTNode => {
|
||||
if (offset >= node.offset && offset <= node.offset + node.length) {
|
||||
let children = node.children;
|
||||
for (let i = 0; i < children.length && children[i].offset <= offset; i++) {
|
||||
let item = findNode(children[i]);
|
||||
if (item) {
|
||||
collector.push(item);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
let foundNode = findNode(this.root);
|
||||
let currMinDist = Number.MAX_VALUE;
|
||||
let currMinNode = null;
|
||||
for (let possibleNode in collector) {
|
||||
let currNode = collector[possibleNode];
|
||||
let minDist = (currNode.offset + currNode.length - offset) + (offset - currNode.offset);
|
||||
if (minDist < currMinDist) {
|
||||
currMinNode = currNode;
|
||||
currMinDist = minDist;
|
||||
}
|
||||
}
|
||||
return currMinNode || foundNode;
|
||||
}
|
||||
}
|
||||
|
||||
export class YAMLDocument {
|
||||
public documents: SingleYAMLDocument[]
|
||||
public errors;
|
||||
public warnings;
|
||||
public documents: SingleYAMLDocument[]
|
||||
public errors;
|
||||
public warnings;
|
||||
|
||||
constructor(documents: SingleYAMLDocument[]) {
|
||||
this.documents = documents;
|
||||
this.errors = [];
|
||||
this.warnings = [];
|
||||
}
|
||||
constructor(documents: SingleYAMLDocument[]) {
|
||||
this.documents = documents;
|
||||
this.errors = [];
|
||||
this.warnings = [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,18 +75,19 @@ export class Schema {
|
|||
}
|
||||
|
||||
static DEFAULT = null;
|
||||
static create = function createSchema() {
|
||||
var schemas, types;
|
||||
static create = function createSchema(...args: [Schema | Schema[], Type[]] | [Type[]]) {
|
||||
let schemas: Schema | Schema[];
|
||||
let types: Type[];
|
||||
|
||||
switch (arguments.length) {
|
||||
switch (args.length) {
|
||||
case 1:
|
||||
schemas = Schema.DEFAULT;
|
||||
types = arguments[0];
|
||||
types = args[0];
|
||||
break;
|
||||
|
||||
case 2:
|
||||
schemas = arguments[0];
|
||||
types = arguments[1];
|
||||
schemas = args[0];
|
||||
types = args[1];
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -109,4 +110,4 @@ export class Schema {
|
|||
explicit: types
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,15 +26,16 @@ export function setupMode(defaults: LanguageServiceDefaultsImpl): void {
|
|||
|
||||
let languageId = defaults.languageId;
|
||||
|
||||
// TODO:
|
||||
// disposables.push(monaco.languages.registerCompletionItemProvider(languageId, new languageFeatures.CompletionAdapter(worker)));
|
||||
disposables.push(monaco.languages.registerCompletionItemProvider(languageId, new languageFeatures.CompletionAdapter(worker)));
|
||||
disposables.push(monaco.languages.registerHoverProvider(languageId, new languageFeatures.HoverAdapter(worker)));
|
||||
disposables.push(monaco.languages.registerDocumentSymbolProvider(languageId, new languageFeatures.DocumentSymbolAdapter(worker)));
|
||||
disposables.push(monaco.languages.registerColorProvider(languageId, new languageFeatures.DocumentColorAdapter(worker)));
|
||||
disposables.push(monaco.languages.registerDocumentFormattingEditProvider(languageId, new languageFeatures.DocumentFormattingEditProvider(worker)));
|
||||
disposables.push(monaco.languages.registerDocumentRangeFormattingEditProvider(languageId, new languageFeatures.DocumentRangeFormattingEditProvider(worker)));
|
||||
disposables.push(new languageFeatures.DiagnosticsAdapter(languageId, worker, defaults));
|
||||
disposables.push(monaco.languages.setLanguageConfiguration(languageId, richEditConfiguration));
|
||||
disposables.push(monaco.languages.setLanguageConfiguration(languageId, richEditConfiguration));
|
||||
|
||||
// Color adapter should be necessary most of the time:
|
||||
// disposables.push(monaco.languages.registerColorProvider(languageId, new languageFeatures.DocumentColorAdapter(worker)));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -47,41 +47,42 @@
|
|||
'vs/basic-languages/monaco.contribution',
|
||||
'vs/language/yaml/monaco.contribution'
|
||||
], function () {
|
||||
const yaml = `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
namespace: default
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
apps.deployment: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
apps.deployment: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:alpine`;
|
||||
|
||||
var editor = monaco.editor.create(document.getElementById('container'), {
|
||||
value: yaml,
|
||||
language: 'yaml'
|
||||
const yaml = `p1: `;
|
||||
const modelUri = monaco.Uri.parse("a://b/foo.json");
|
||||
const editor = monaco.editor.create(document.getElementById('container'), {
|
||||
language: 'yaml',
|
||||
showFoldingControls: 'always',
|
||||
model: monaco.editor.createModel(yaml, "yaml", modelUri),
|
||||
});
|
||||
|
||||
monaco.languages.yaml.yamlDefaults.setDiagnosticsOptions({
|
||||
enableSchemaRequest: true,
|
||||
validate: true,
|
||||
schemas: [
|
||||
{
|
||||
uri: 'https://raw.githubusercontent.com/garethr/kubernetes-json-schema/master/master/deployment.json',
|
||||
fileMatch: ['*'],
|
||||
},
|
||||
],
|
||||
schemas: [{
|
||||
uri: "http://myserver/foo-schema.json", // id of the first schema
|
||||
fileMatch: [modelUri.toString()], // associate with our model
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
p1: {
|
||||
enum: ["v1", "v2"]
|
||||
},
|
||||
p2: {
|
||||
$ref: "http://myserver/bar-schema.json" // reference the second schema
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
uri: "http://myserver/bar-schema.json", // id of the first schema
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
q1: {
|
||||
enum: ["x1", "x2"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
require(["vs/editor/contrib/quickOpen/quickOpen"], async quickOpen => {
|
||||
|
|
@ -117,7 +118,8 @@ spec:
|
|||
const position = selection.getPosition();
|
||||
const symbol = await _getSymbolForPosition(model, position);
|
||||
|
||||
if (symbol) {
|
||||
console.log(`${symbol.name}: ${symbol.range}`);
|
||||
if (symbol && symbol.range) {
|
||||
const decoration = {
|
||||
range: symbol.range,
|
||||
options: {
|
||||
|
|
@ -130,8 +132,6 @@ spec:
|
|||
oldDecorations,
|
||||
decoration ? [decoration] : [],
|
||||
)
|
||||
|
||||
console.log(`${symbol.name}: ${symbol.range}`);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue