Skip to content

Commit 75e7a16

Browse files
chore: pull new changes from 1.19
2 parents 79bbf93 + 1be76fb commit 75e7a16

33 files changed

+691
-247
lines changed

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ jobs:
7575
with:
7676
context: .
7777
file: ./Dockerfile
78-
platforms: linux/amd64
78+
platforms: linux/amd64,linux/arm64
7979
push: true
8080
tags: |
8181
quay.io/redhat-developer/yaml-language-server:latest

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
### 1.18.0
2+
- Feat: Do not suggest propertyNames if doNotSuggest is true [#1045](https://github.com/redhat-developer/yaml-language-server/pull/1045)
3+
- Feat: Exclude not suggest properties from possible properties error [#1051](https://github.com/redhat-developer/yaml-language-server/pull/1051)
4+
- Feat: Fix enum values to be unique [#1028](https://github.com/redhat-developer/yaml-language-server/pull/1028)
5+
- Fix: URL-encoded characters in $ref paths break schema resolution [#1082](https://github.com/redhat-developer/vscode-yaml/issues/1082)
6+
- Fix: Tests fail with Node.JS 23.7.x [#1018](https://github.com/redhat-developer/yaml-language-server/issues/1018)
7+
- Fix: Autocompletion problem when value is null inside anyOf object [#684](https://github.com/redhat-developer/yaml-language-server/issues/684)
8+
- Fix: Enum strings YES / NO are converted to boolean incorrectly [#1036](https://github.com/redhat-developer/yaml-language-server/issues/1036)
9+
- Fix: Autocompletion with escape sequence chars [#1040](https://github.com/redhat-developer/yaml-language-server/pull/1040)
10+
- Fix: multipleOf does not work for floats [#985](https://github.com/redhat-developer/yaml-language-server/issues/985)
11+
12+
Thanks to [Petr Spacek](https://github.com/p-spacek), [Kosta](https://github.com/Kosta-Github) and [Willem Jan](https://github.com/Willem-J-an) for your contributions.
13+
14+
### 1.17.0
15+
- Feat: Supported docker arm64 image [#1027](https://github.com/redhat-developer/yaml-language-server/pull/1027)
16+
- Fix: Reverted ajv-draft04 as it not support HTTPS [#1026](https://github.com/redhat-developer/yaml-language-server/pull/1026)
17+
- Fix: Use replaceAll() instead of replace() when turning a label into a regex [#1078](https://github.com/redhat-developer/vscode-yaml/issues/1078)
18+
119
### 1.16.0
220
- Feat: Add support for draft-04 (2019 and 2020 included) json schemas while supporting draft-07 [#1006](https://github.com/redhat-developer/yaml-language-server/pull/1006)
321
- Feat: quickFix for enum, const, property [#900](https://github.com/redhat-developer/yaml-language-server/pull/900)

package.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "yaml-language-server",
33
"description": "YAML language server",
4-
"version": "1.17.0",
4+
"version": "1.19.0",
55
"author": "Red Hat",
66
"license": "MIT",
77
"contributors": [
@@ -26,19 +26,18 @@
2626
"url": "https://github.com/redhat-developer/yaml-language-server.git"
2727
},
2828
"dependencies": {
29-
"ajv": "^8.11.0",
29+
"ajv": "^8.17.1",
3030
"common-tags": "^1.8.2",
31-
"ajv-draft-04": "^1.0.0",
3231
"lodash": "4.17.21",
33-
"prettier": "^3.0.0",
32+
"prettier": "^3.5.0",
3433
"request-light": "^0.5.7",
3534
"vscode-json-languageservice": "4.1.8",
3635
"vscode-languageserver": "^9.0.0",
3736
"vscode-languageserver-textdocument": "^1.0.1",
3837
"vscode-languageserver-types": "^3.16.0",
3938
"vscode-nls": "^5.0.0",
4039
"vscode-uri": "^3.0.2",
41-
"yaml": "2.2.2"
40+
"yaml": "2.7.1"
4241
},
4342
"devDependencies": {
4443
"@microsoft/eslint-formatter-sarif": "3.0.0",

src/languageserver/handlers/settingsHandlers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ export class SettingsHandler {
107107
this.yamlSettings.yamlFormatterSettings.bracketSpacing = settings.yaml.format.bracketSpacing;
108108
}
109109

110+
if (settings.yaml.format.trailingComma !== undefined) {
111+
this.yamlSettings.yamlFormatterSettings.trailingComma = settings.yaml.format.trailingComma;
112+
}
113+
110114
if (settings.yaml.format.enable !== undefined) {
111115
this.yamlSettings.yamlFormatterSettings.enable = settings.yaml.format.enable;
112116
}

src/languageservice/jsonASTTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export interface NumberASTNode extends BaseASTNode {
5656
export interface BooleanASTNode extends BaseASTNode {
5757
readonly type: 'boolean';
5858
readonly value: boolean;
59+
readonly source: string;
5960
}
6061
export interface NullASTNode extends BaseASTNode {
6162
readonly type: 'null';

src/languageservice/parser/ast-converter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ function convertScalar(node: Scalar, parent: ASTNode): ASTNode {
137137
return result;
138138
}
139139
case 'boolean':
140-
return new BooleanASTNodeImpl(parent, node, node.value, ...toOffsetLength(node.range));
140+
return new BooleanASTNodeImpl(parent, node, node.value, node.source, ...toOffsetLength(node.range));
141141
case 'number': {
142142
const result = new NumberASTNodeImpl(parent, node, ...toOffsetLength(node.range));
143143
result.value = node.value;

src/languageservice/parser/jsonParser07.ts

Lines changed: 75 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { isArrayEqual } from '../utils/arrUtils';
2727
import { Node, Pair } from 'yaml';
2828
import { safeCreateUnicodeRegExp } from '../utils/strings';
2929
import { FilePatternAssociation } from '../services/yamlSchemaService';
30+
import { floatSafeRemainder } from '../utils/math';
3031

3132
const localize = nls.loadMessageBundle();
3233
const MSG_PROPERTY_NOT_ALLOWED = 'Property {0} is not allowed.';
@@ -168,10 +169,12 @@ export class NullASTNodeImpl extends ASTNodeImpl implements NullASTNode {
168169
export class BooleanASTNodeImpl extends ASTNodeImpl implements BooleanASTNode {
169170
public type: 'boolean' = 'boolean' as const;
170171
public value: boolean;
172+
public source: string;
171173

172-
constructor(parent: ASTNode, internalNode: Node, boolValue: boolean, offset: number, length?: number) {
174+
constructor(parent: ASTNode, internalNode: Node, boolValue: boolean, boolSource: string, offset: number, length?: number) {
173175
super(parent, internalNode, offset, length);
174176
this.value = boolValue;
177+
this.source = boolSource;
175178
}
176179
}
177180

@@ -501,8 +504,9 @@ export function getNodeValue(node: ASTNode): any {
501504
case 'null':
502505
case 'string':
503506
case 'number':
504-
case 'boolean':
505507
return node.value;
508+
case 'boolean':
509+
return node.source;
506510
default:
507511
return undefined;
508512
}
@@ -533,6 +537,12 @@ export function findNodeAtOffset(node: ASTNode, offset: number, includeRightBoun
533537
return undefined;
534538
}
535539

540+
interface IValidationMatch {
541+
schema: JSONSchema;
542+
validationResult: ValidationResult;
543+
matchingSchemas: ISchemaCollector;
544+
}
545+
536546
export class JSONDocument {
537547
public isKubernetes: boolean;
538548
public disableAdditionalProperties: boolean;
@@ -866,7 +876,7 @@ function validate(
866876
const val = getNodeValue(node);
867877
let enumValueMatch = false;
868878
for (const e of schema.enum) {
869-
if (equals(val, e) || (callFromAutoComplete && isString(val) && isString(e) && val && e.startsWith(val))) {
879+
if (val === e || isAutoCompleteEqualMaybe(callFromAutoComplete, node, val, e)) {
870880
enumValueMatch = true;
871881
break;
872882
}
@@ -898,10 +908,7 @@ function validate(
898908

899909
if (isDefined(schema.const)) {
900910
const val = getNodeValue(node);
901-
if (
902-
!equals(val, schema.const) &&
903-
!(callFromAutoComplete && isString(val) && isString(schema.const) && schema.const.startsWith(val))
904-
) {
911+
if (!equals(val, schema.const) && !isAutoCompleteEqualMaybe(callFromAutoComplete, node, val, schema.const)) {
905912
validationResult.problems.push({
906913
location: { offset: node.offset, length: node.length },
907914
severity: DiagnosticSeverity.Warning,
@@ -935,7 +942,7 @@ function validate(
935942
const val = node.value;
936943

937944
if (isNumber(schema.multipleOf)) {
938-
if (val % schema.multipleOf !== 0) {
945+
if (floatSafeRemainder(val, schema.multipleOf) !== 0) {
939946
validationResult.problems.push({
940947
location: { offset: node.offset, length: node.length },
941948
severity: DiagnosticSeverity.Warning,
@@ -1370,7 +1377,21 @@ function validate(
13701377
(schema.type === 'object' && schema.additionalProperties === undefined && options.disableAdditionalProperties === true)
13711378
) {
13721379
if (unprocessedProperties.length > 0) {
1373-
const possibleProperties = schema.properties && Object.keys(schema.properties).filter((prop) => !seenKeys[prop]);
1380+
const possibleProperties =
1381+
schema.properties &&
1382+
Object.entries(schema.properties)
1383+
.filter(([key, property]) => {
1384+
// don't include existing properties
1385+
if (seenKeys[key]) {
1386+
return false;
1387+
}
1388+
// don't include properties that are not suggested in completion
1389+
if (property && typeof property === 'object' && (property.doNotSuggest || property.deprecationMessage)) {
1390+
return false;
1391+
}
1392+
return true;
1393+
})
1394+
.map(([key]) => key);
13741395

13751396
for (const propertyName of unprocessedProperties) {
13761397
const child = seenKeys[propertyName];
@@ -1504,23 +1525,11 @@ function validate(
15041525
node: ASTNode,
15051526
maxOneMatch,
15061527
subValidationResult: ValidationResult,
1507-
bestMatch: {
1508-
schema: JSONSchema;
1509-
validationResult: ValidationResult;
1510-
matchingSchemas: ISchemaCollector;
1511-
},
1528+
bestMatch: IValidationMatch,
15121529
subSchema,
15131530
subMatchingSchemas
1514-
): {
1515-
schema: JSONSchema;
1516-
validationResult: ValidationResult;
1517-
matchingSchemas: ISchemaCollector;
1518-
} {
1519-
if (
1520-
!maxOneMatch &&
1521-
!subValidationResult.hasProblems() &&
1522-
(!bestMatch.validationResult.hasProblems() || callFromAutoComplete)
1523-
) {
1531+
): IValidationMatch {
1532+
if (!maxOneMatch && !subValidationResult.hasProblems() && !bestMatch.validationResult.hasProblems()) {
15241533
// no errors, both are equally good matches
15251534
bestMatch.matchingSchemas.merge(subMatchingSchemas);
15261535
bestMatch.validationResult.propertiesMatches += subValidationResult.propertiesMatches;
@@ -1541,19 +1550,30 @@ function validate(
15411550
validationResult: subValidationResult,
15421551
matchingSchemas: subMatchingSchemas,
15431552
};
1544-
} else if (compareResult === 0) {
1553+
} else if (
1554+
compareResult === 0 ||
1555+
((node.value === null || node.type === 'null') && node.length === 0) // node with no value can match any schema potentially
1556+
) {
15451557
// there's already a best matching but we are as good
1546-
bestMatch.matchingSchemas.merge(subMatchingSchemas);
1547-
bestMatch.validationResult.mergeEnumValues(subValidationResult);
1548-
bestMatch.validationResult.mergeWarningGeneric(subValidationResult, [
1549-
ProblemType.missingRequiredPropWarning,
1550-
ProblemType.typeMismatchWarning,
1551-
ProblemType.constWarning,
1552-
]);
1558+
mergeValidationMatches(bestMatch, subMatchingSchemas, subValidationResult);
15531559
}
15541560
}
15551561
return bestMatch;
15561562
}
1563+
1564+
function mergeValidationMatches(
1565+
bestMatch: IValidationMatch,
1566+
subMatchingSchemas: ISchemaCollector,
1567+
subValidationResult: ValidationResult
1568+
): void {
1569+
bestMatch.matchingSchemas.merge(subMatchingSchemas);
1570+
bestMatch.validationResult.mergeEnumValues(subValidationResult);
1571+
bestMatch.validationResult.mergeWarningGeneric(subValidationResult, [
1572+
ProblemType.missingRequiredPropWarning,
1573+
ProblemType.typeMismatchWarning,
1574+
ProblemType.constWarning,
1575+
]);
1576+
}
15571577
}
15581578

15591579
function getSchemaSource(schema: JSONSchema, originalSchema: JSONSchema): string | undefined {
@@ -1591,3 +1611,26 @@ function getSchemaUri(schema: JSONSchema, originalSchema: JSONSchema): string[]
15911611
function getWarningMessage(problemType: ProblemType, args: string[]): string {
15921612
return localize(problemType, ProblemTypeMessages[problemType], args.join(' | '));
15931613
}
1614+
1615+
/**
1616+
* if callFromAutoComplete than compare value from yaml and value from schema (s.const | s.enum[i])
1617+
* allows partial match for autocompletion
1618+
*/
1619+
function isAutoCompleteEqualMaybe(
1620+
callFromAutoComplete: boolean,
1621+
node: ASTNode,
1622+
nodeValue: unknown,
1623+
schemaValue: unknown
1624+
): boolean {
1625+
if (!callFromAutoComplete) {
1626+
return false;
1627+
}
1628+
1629+
// if autocompletion property doesn't have value, then it could be a match
1630+
const isWithoutValue = nodeValue === null && node.length === 0; // allows `prop: ` but ignore `prop: null`
1631+
if (isWithoutValue) {
1632+
return true;
1633+
}
1634+
1635+
return isString(nodeValue) && isString(schemaValue) && schemaValue.startsWith(nodeValue);
1636+
}

src/languageservice/services/validation/unused-anchors.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export class UnusedAnchorsValidator implements AdditionalValidator {
1616
const result = [];
1717
const anchors = new Set<Scalar | YAMLMap | YAMLSeq>();
1818
const usedAnchors = new Set<Node>();
19+
const unIdentifiedAlias = new Set<Node>();
1920
const anchorParent = new Map<Scalar | YAMLMap | YAMLSeq, Node | Pair>();
2021

2122
visit(yamlDoc.internalDocument, (key, node, path) => {
@@ -27,7 +28,11 @@ export class UnusedAnchorsValidator implements AdditionalValidator {
2728
anchorParent.set(node, path[path.length - 1] as Node);
2829
}
2930
if (isAlias(node)) {
30-
usedAnchors.add(node.resolve(yamlDoc.internalDocument));
31+
if (!node.resolve(yamlDoc.internalDocument)) {
32+
unIdentifiedAlias.add(node);
33+
} else {
34+
usedAnchors.add(node.resolve(yamlDoc.internalDocument));
35+
}
3136
}
3237
});
3338

@@ -39,13 +44,29 @@ export class UnusedAnchorsValidator implements AdditionalValidator {
3944
document.positionAt(aToken.offset),
4045
document.positionAt(aToken.offset + aToken.source.length)
4146
);
42-
const warningDiagnostic = Diagnostic.create(range, `Unused anchor "${aToken.source}"`, DiagnosticSeverity.Hint, 0);
47+
const warningDiagnostic = Diagnostic.create(
48+
range,
49+
`Unused anchor "${aToken.source}"`,
50+
DiagnosticSeverity.Information,
51+
0
52+
);
4353
warningDiagnostic.tags = [DiagnosticTag.Unnecessary];
4454
result.push(warningDiagnostic);
4555
}
4656
}
4757
}
4858

59+
unIdentifiedAlias.forEach((node) => {
60+
const nodeRange = node.range;
61+
if (nodeRange) {
62+
const startOffset = nodeRange[0];
63+
const endOffset = nodeRange[1];
64+
const range = Range.create(document.positionAt(startOffset), document.positionAt(endOffset));
65+
const warningDiagnostic = Diagnostic.create(range, `Unresolved alias "${node}"`, DiagnosticSeverity.Information, 0);
66+
warningDiagnostic.tags = [DiagnosticTag.Unnecessary];
67+
result.push(warningDiagnostic);
68+
}
69+
});
4970
return result;
5071
}
5172
private getAnchorNode(parentNode: YamlNode, node: Node): CST.SourceToken | undefined {

src/languageservice/services/yamlCodeActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export class YamlCodeActions {
9797
const textBuff = new TextBuffer(document);
9898
const processedLine: number[] = [];
9999
for (const diag of diagnostics) {
100-
if (diag.message === 'Using tabs can lead to unpredictable results') {
100+
if (diag.message === 'Tabs are not allowed as indentation') {
101101
if (processedLine.includes(diag.range.start.line)) {
102102
continue;
103103
}

0 commit comments

Comments
 (0)