Skip to content

Commit

Permalink
Merge branch 'main' into feat/add-title-source-hover-settings
Browse files Browse the repository at this point in the history
  • Loading branch information
msivasubramaniaan authored Dec 18, 2024
2 parents 6344c54 + 413846c commit f0001ca
Show file tree
Hide file tree
Showing 14 changed files with 335 additions and 61 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
### 1.15.0
- Fix: some small type issues [#918](https://github.com/redhat-developer/yaml-language-server/pull/918)
- Add: volar-service-yaml to clients [#920](https://github.com/redhat-developer/yaml-language-server/pull/920)
- Fix: Remove ide-yaml from known clients [#921](https://github.com/redhat-developer/yaml-language-server/pull/921)
- Fix: schema loading performance [#923](https://github.com/redhat-developer/yaml-language-server/pull/923)
- Fix: undefined error in mergingResult.problemArgs [#924](https://github.com/redhat-developer/yaml-language-server/pull/924)
- Add: unify string insert text for array and property [#934](https://github.com/redhat-developer/yaml-language-server/pull/934)
- Add: Improve some special cases for selection ranges [#939](https://github.com/redhat-developer/yaml-language-server/pull/939)
- Fix: show all enums on hover [#942](https://github.com/redhat-developer/yaml-language-server/pull/942)
- Fix: update README syntax highlighting [#945](https://github.com/redhat-developer/yaml-language-server/pull/945)
- Fix: render examples as yaml on hover [#947](https://github.com/redhat-developer/yaml-language-server/pull/947)
- Fix: snippets in additionalProperties [#951](https://github.com/redhat-developer/yaml-language-server/pull/951)
- Fix: crash when url is undefined [#954](https://github.com/redhat-developer/yaml-language-server/pull/954)
- Fix: Add null check for customTags [#955](https://github.com/redhat-developer/yaml-language-server/pull/955)

Thanks to [Remco Haszing](https://github.com/remcohaszing), [Petr Spacek](https://github.com/p-spacek), [Tony](https://github.com/Legend-Master), [Gustav Eikaas](https://github.com/GustavEikaas), [Skip Baney](https://github.com/twelvelabs) and [Pierre Prinetti](https://github.com/pierreprinetti) for your contributions.

### 1.14.0
- Fix: Request textDocument/hover failed with message: Invalid regular expression: /(?s).*/: Invalid group [#874](https://github.com/redhat-developer/yaml-language-server/issues/874)
- Fix: nested anyof const [#888](https://github.com/redhat-developer/yaml-language-server/pull/888)
Expand Down
4 changes: 2 additions & 2 deletions src/languageserver/handlers/settingsHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class SettingsHandler {

if (settings.yaml.schemaStore) {
this.yamlSettings.schemaStoreEnabled = settings.yaml.schemaStore.enable;
if (settings.yaml.schemaStore.url.length !== 0) {
if (settings.yaml.schemaStore.url?.length !== 0) {
this.yamlSettings.schemaStoreUrl = settings.yaml.schemaStore.url;
}
}
Expand Down Expand Up @@ -180,7 +180,7 @@ export class SettingsHandler {
private async setSchemaStoreSettingsIfNotSet(): Promise<void> {
const schemaStoreIsSet = this.yamlSettings.schemaStoreSettings.length !== 0;
let schemaStoreUrl = '';
if (this.yamlSettings.schemaStoreUrl.length !== 0) {
if (this.yamlSettings.schemaStoreUrl?.length !== 0) {
schemaStoreUrl = this.yamlSettings.schemaStoreUrl;
} else {
schemaStoreUrl = JSON_SCHEMASTORE_URL;
Expand Down
3 changes: 3 additions & 0 deletions src/languageservice/parser/jsonParser07.ts
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,7 @@ function validate(
),
source: getSchemaSource(schema, originalSchema),
schemaUri: getSchemaUri(schema, originalSchema),
data: { values: schema.enum },
});
}
}
Expand All @@ -907,6 +908,7 @@ function validate(
source: getSchemaSource(schema, originalSchema),
schemaUri: getSchemaUri(schema, originalSchema),
problemArgs: [JSON.stringify(schema.const)],
data: { values: [schema.const] },
});
validationResult.enumValueMatch = false;
} else {
Expand Down Expand Up @@ -1385,6 +1387,7 @@ function validate(
length: propertyNode.keyNode.length,
},
severity: DiagnosticSeverity.Warning,
code: ErrorCode.PropertyExpected,
message: schema.errorMessage || localize('DisallowedExtraPropWarning', MSG_PROPERTY_NOT_ALLOWED, propertyName),
source: getSchemaSource(schema, originalSchema),
schemaUri: getSchemaUri(schema, originalSchema),
Expand Down
57 changes: 52 additions & 5 deletions src/languageservice/services/yamlCodeActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ import { FlowStyleRewriter } from '../utils/flow-style-rewriter';
import { ASTNode } from '../jsonASTTypes';
import * as _ from 'lodash';
import { SourceToken } from 'yaml/dist/parse/cst';
import { ErrorCode } from 'vscode-json-languageservice';

interface YamlDiagnosticData {
schemaUri: string[];
values?: string[];
properties?: string[];
}
export class YamlCodeActions {
private indentation = ' ';
Expand All @@ -54,6 +57,7 @@ export class YamlCodeActions {
result.push(...this.getUnusedAnchorsDelete(params.context.diagnostics, document));
result.push(...this.getConvertToBlockStyleActions(params.context.diagnostics, document));
result.push(...this.getKeyOrderActions(params.context.diagnostics, document));
result.push(...this.getQuickFixForPropertyOrValueMismatch(params.context.diagnostics, document));

return result;
}
Expand Down Expand Up @@ -221,7 +225,7 @@ export class YamlCodeActions {
const results: CodeAction[] = [];
for (const diagnostic of diagnostics) {
if (diagnostic.code === 'flowMap' || diagnostic.code === 'flowSeq') {
const node = getNodeforDiagnostic(document, diagnostic);
const node = getNodeForDiagnostic(document, diagnostic);
if (isMap(node.internalNode) || isSeq(node.internalNode)) {
const blockTypeDescription = isMap(node.internalNode) ? 'map' : 'sequence';
const rewriter = new FlowStyleRewriter(this.indentation);
Expand All @@ -242,7 +246,7 @@ export class YamlCodeActions {
const results: CodeAction[] = [];
for (const diagnostic of diagnostics) {
if (diagnostic?.code === 'mapKeyOrder') {
let node = getNodeforDiagnostic(document, diagnostic);
let node = getNodeForDiagnostic(document, diagnostic);
while (node && node.type !== 'object') {
node = node.parent;
}
Expand Down Expand Up @@ -292,8 +296,8 @@ export class YamlCodeActions {
item.value.end.splice(newLineIndex, 1);
}
} else if (item.value?.type === 'block-scalar') {
const nwline = item.value.props.find((p) => p.type === 'newline');
if (!nwline) {
const newline = item.value.props.find((p) => p.type === 'newline');
if (!newline) {
item.value.props.push({ type: 'newline', indent: 0, offset: item.value.offset, source: '\n' } as SourceToken);
}
}
Expand All @@ -312,9 +316,52 @@ export class YamlCodeActions {
}
return results;
}

/**
* Check if diagnostic contains info for quick fix
* Supports Enum/Const/Property mismatch
*/
private getPossibleQuickFixValues(diagnostic: Diagnostic): string[] | undefined {
if (typeof diagnostic.data !== 'object') {
return;
}
if (
diagnostic.code === ErrorCode.EnumValueMismatch &&
'values' in diagnostic.data &&
Array.isArray((diagnostic.data as YamlDiagnosticData).values)
) {
return (diagnostic.data as YamlDiagnosticData).values;
} else if (
diagnostic.code === ErrorCode.PropertyExpected &&
'properties' in diagnostic.data &&
Array.isArray((diagnostic.data as YamlDiagnosticData).properties)
) {
return (diagnostic.data as YamlDiagnosticData).properties;
}
}

private getQuickFixForPropertyOrValueMismatch(diagnostics: Diagnostic[], document: TextDocument): CodeAction[] {
const results: CodeAction[] = [];
for (const diagnostic of diagnostics) {
const values = this.getPossibleQuickFixValues(diagnostic);
if (!values?.length) {
continue;
}
for (const value of values) {
results.push(
CodeAction.create(
value,
createWorkspaceEdit(document.uri, [TextEdit.replace(diagnostic.range, value)]),
CodeActionKind.QuickFix
)
);
}
}
return results;
}
}

function getNodeforDiagnostic(document: TextDocument, diagnostic: Diagnostic): ASTNode {
function getNodeForDiagnostic(document: TextDocument, diagnostic: Diagnostic): ASTNode {
const yamlDocuments = yamlDocumentsCache.getYamlDocument(document);
const startOffset = document.offsetAt(diagnostic.range.start);
const yamlDoc = matchOffsetToDocument(startOffset, yamlDocuments);
Expand Down
7 changes: 4 additions & 3 deletions src/languageservice/services/yamlCompletion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ export class YamlCompletion {
proposed,
};

if (this.customTags.length > 0) {
if (this.customTags && this.customTags.length > 0) {
this.getCustomTagValueCompletions(collector);
}

Expand Down Expand Up @@ -925,7 +925,8 @@ export class YamlCompletion {
if (propertySchema) {
this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types, 'value');
}
} else if (s.schema.additionalProperties) {
}
if (s.schema.additionalProperties) {
this.addSchemaValueCompletions(s.schema.additionalProperties, separatorAfter, collector, types, 'value');
}
}
Expand Down Expand Up @@ -1205,7 +1206,7 @@ export class YamlCompletion {
insertText = `\${${insertIndex++}:0}`;
break;
case 'string':
insertText = `\${${insertIndex++}:""}`;
insertText = `\${${insertIndex++}}`;
break;
case 'object':
{
Expand Down
115 changes: 75 additions & 40 deletions src/languageservice/services/yamlSelectionRanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,81 +3,72 @@ import { yamlDocumentsCache } from '../parser/yaml-documents';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { ASTNode } from 'vscode-json-languageservice';

export function getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[] | undefined {
if (!document) {
return;
}
export function getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[] {
const doc = yamlDocumentsCache.getYamlDocument(document);
return positions.map((position) => {
const ranges = getRanges(position);
let current: SelectionRange;
let current: SelectionRange | undefined;
for (const range of ranges) {
current = SelectionRange.create(range, current);
}
if (!current) {
current = SelectionRange.create({
start: position,
end: position,
});
}
return current;
return current ?? SelectionRange.create({ start: position, end: position });
});

function getRanges(position: Position): Range[] {
const offset = document.offsetAt(position);
const result: Range[] = [];
for (const ymlDoc of doc.documents) {
let currentNode: ASTNode;
let firstNodeOffset: number;
let isFirstNode = true;
let currentNode: ASTNode | undefined;
let overrideStartOffset: number | undefined;
ymlDoc.visit((node) => {
const endOffset = node.offset + node.length;
// Skip if end offset doesn't even reach cursor position
if (endOffset < offset) {
return true;
}
let startOffset = node.offset;
// Recheck start offset with the trimmed one in case of this
// key:
// - value
// ↑
if (startOffset > offset) {
const nodePosition = document.positionAt(startOffset);
if (nodePosition.line !== position.line) {
return true;
}
const lineBeginning = { line: nodePosition.line, character: 0 };
const text = document.getText({
start: lineBeginning,
end: nodePosition,
});
if (text.trim().length !== 0) {
// Skip if we're ending at new line
// times:
// - second: 1
// millisecond: 10
// | - second: 2
// ↑ millisecond: 0
// (| is actually part of { second: 1, millisecond: 10 })
// \r\n doesn't matter here
if (getTextFromOffsets(endOffset - 1, endOffset) === '\n') {
if (endOffset - 1 < offset) {
return true;
}
startOffset = document.offsetAt(lineBeginning);
if (startOffset > offset) {
}

let startOffset = node.offset;
if (startOffset > offset) {
// Recheck start offset for some special cases
const newOffset = getStartOffsetForSpecialCases(node, position);
if (!newOffset || newOffset > offset) {
return true;
}
startOffset = newOffset;
}

// Allow equal for children to override
if (!currentNode || startOffset >= currentNode.offset) {
currentNode = node;
firstNodeOffset = startOffset;
overrideStartOffset = startOffset;
}
return true;
});
while (currentNode) {
const startOffset = isFirstNode ? firstNodeOffset : currentNode.offset;
const startOffset = overrideStartOffset ?? currentNode.offset;
const endOffset = currentNode.offset + currentNode.length;
const range = {
start: document.positionAt(startOffset),
end: document.positionAt(endOffset),
};
const text = document.getText(range);
const trimmedText = text.trimEnd();
const trimmedLength = text.length - trimmedText.length;
if (trimmedLength > 0) {
range.end = document.positionAt(endOffset - trimmedLength);
const trimmedText = trimEndNewLine(text);
const trimmedEndOffset = startOffset + trimmedText.length;
if (trimmedEndOffset >= offset) {
range.end = document.positionAt(trimmedEndOffset);
}
// Add a jump between '' "" {} []
const isSurroundedBy = (startCharacter: string, endCharacter?: string): boolean => {
Expand All @@ -95,7 +86,7 @@ export function getSelectionRanges(document: TextDocument, positions: Position[]
}
result.push(range);
currentNode = currentNode.parent;
isFirstNode = false;
overrideStartOffset = undefined;
}
// A position can't be in multiple documents
if (result.length > 0) {
Expand All @@ -104,4 +95,48 @@ export function getSelectionRanges(document: TextDocument, positions: Position[]
}
return result.reverse();
}

function getStartOffsetForSpecialCases(node: ASTNode, position: Position): number | undefined {
const nodeStartPosition = document.positionAt(node.offset);
if (nodeStartPosition.line !== position.line) {
return;
}

if (node.parent?.type === 'array') {
// array:
// - value
// ↑
if (getTextFromOffsets(node.offset - 2, node.offset) === '- ') {
return node.offset - 2;
}
}

if (node.type === 'array' || node.type === 'object') {
// array:
// - value
// ↑
const lineBeginning = { line: nodeStartPosition.line, character: 0 };
const text = document.getText({ start: lineBeginning, end: nodeStartPosition });
if (text.trim().length === 0) {
return document.offsetAt(lineBeginning);
}
}
}

function getTextFromOffsets(startOffset: number, endOffset: number): string {
return document.getText({
start: document.positionAt(startOffset),
end: document.positionAt(endOffset),
});
}
}

function trimEndNewLine(str: string): string {
if (str.endsWith('\r\n')) {
return str.substring(0, str.length - 2);
}
if (str.endsWith('\n')) {
return str.substring(0, str.length - 1);
}
return str;
}
2 changes: 1 addition & 1 deletion src/languageservice/yamlLanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export interface LanguageService {
deleteSchemaContent: (schemaDeletions: SchemaDeletions) => void;
deleteSchemasWhole: (schemaDeletions: SchemaDeletionsAll) => void;
getFoldingRanges: (document: TextDocument, context: FoldingRangesContext) => FoldingRange[] | null;
getSelectionRanges: (document: TextDocument, positions: Position[]) => SelectionRange[] | undefined;
getSelectionRanges: (document: TextDocument, positions: Position[]) => SelectionRange[];
getCodeAction: (document: TextDocument, params: CodeActionParams) => CodeAction[] | undefined;
getCodeLens: (document: TextDocument) => PromiseLike<CodeLens[] | undefined> | CodeLens[] | undefined;
resolveCodeLens: (param: CodeLens) => PromiseLike<CodeLens> | CodeLens;
Expand Down
7 changes: 6 additions & 1 deletion test/autoCompletion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,7 @@ describe('Auto Completion Tests', () => {
const completion = parseSetup(content, content.lastIndexOf('Ba') + 2); // pos: 3+2
completion
.then(function (result) {
assert.strictEqual('fooBar:\n - ${1:""}', result.items[0].insertText);
assert.strictEqual('fooBar:\n - ${1}', result.items[0].insertText);
})
.then(done, done);
});
Expand Down Expand Up @@ -3126,5 +3126,10 @@ describe('Auto Completion Tests', () => {
expect(result.items.map((i) => i.label)).to.have.members(['fruit', 'vegetable']);
});
});
it('Should function when settings are undefined', async () => {
languageService.configure({ completion: true });
const content = '';
await parseSetup(content, 0);
});
});
});
Loading

0 comments on commit f0001ca

Please sign in to comment.