@@ -530,6 +530,12 @@ export function findNodeAtOffset(node: ASTNode, offset: number, includeRightBoun
530
530
return undefined ;
531
531
}
532
532
533
+ interface IValidationMatch {
534
+ schema : JSONSchema ;
535
+ validationResult : ValidationResult ;
536
+ matchingSchemas : ISchemaCollector ;
537
+ }
538
+
533
539
export class JSONDocument {
534
540
public isKubernetes : boolean ;
535
541
public disableAdditionalProperties : boolean ;
@@ -863,7 +869,7 @@ function validate(
863
869
const val = getNodeValue ( node ) ;
864
870
let enumValueMatch = false ;
865
871
for ( const e of schema . enum ) {
866
- if ( equals ( val , e ) || ( callFromAutoComplete && isString ( val ) && isString ( e ) && val && e . startsWith ( val ) ) ) {
872
+ if ( equals ( val , e ) || isAutoCompleteEqualMaybe ( callFromAutoComplete , node , val , e ) ) {
867
873
enumValueMatch = true ;
868
874
break ;
869
875
}
@@ -894,10 +900,7 @@ function validate(
894
900
895
901
if ( isDefined ( schema . const ) ) {
896
902
const val = getNodeValue ( node ) ;
897
- if (
898
- ! equals ( val , schema . const ) &&
899
- ! ( callFromAutoComplete && isString ( val ) && isString ( schema . const ) && schema . const . startsWith ( val ) )
900
- ) {
903
+ if ( ! equals ( val , schema . const ) && ! isAutoCompleteEqualMaybe ( callFromAutoComplete , node , val , schema . const ) ) {
901
904
validationResult . problems . push ( {
902
905
location : { offset : node . offset , length : node . length } ,
903
906
severity : DiagnosticSeverity . Warning ,
@@ -1498,23 +1501,11 @@ function validate(
1498
1501
node : ASTNode ,
1499
1502
maxOneMatch ,
1500
1503
subValidationResult : ValidationResult ,
1501
- bestMatch : {
1502
- schema : JSONSchema ;
1503
- validationResult : ValidationResult ;
1504
- matchingSchemas : ISchemaCollector ;
1505
- } ,
1504
+ bestMatch : IValidationMatch ,
1506
1505
subSchema ,
1507
1506
subMatchingSchemas
1508
- ) : {
1509
- schema : JSONSchema ;
1510
- validationResult : ValidationResult ;
1511
- matchingSchemas : ISchemaCollector ;
1512
- } {
1513
- if (
1514
- ! maxOneMatch &&
1515
- ! subValidationResult . hasProblems ( ) &&
1516
- ( ! bestMatch . validationResult . hasProblems ( ) || callFromAutoComplete )
1517
- ) {
1507
+ ) : IValidationMatch {
1508
+ if ( ! maxOneMatch && ! subValidationResult . hasProblems ( ) && ! bestMatch . validationResult . hasProblems ( ) ) {
1518
1509
// no errors, both are equally good matches
1519
1510
bestMatch . matchingSchemas . merge ( subMatchingSchemas ) ;
1520
1511
bestMatch . validationResult . propertiesMatches += subValidationResult . propertiesMatches ;
@@ -1535,19 +1526,30 @@ function validate(
1535
1526
validationResult : subValidationResult ,
1536
1527
matchingSchemas : subMatchingSchemas ,
1537
1528
} ;
1538
- } else if ( compareResult === 0 ) {
1529
+ } else if (
1530
+ compareResult === 0 ||
1531
+ ( ( node . value === null || node . type === 'null' ) && node . length === 0 ) // node with no value can match any schema potentially
1532
+ ) {
1539
1533
// there's already a best matching but we are as good
1540
- bestMatch . matchingSchemas . merge ( subMatchingSchemas ) ;
1541
- bestMatch . validationResult . mergeEnumValues ( subValidationResult ) ;
1542
- bestMatch . validationResult . mergeWarningGeneric ( subValidationResult , [
1543
- ProblemType . missingRequiredPropWarning ,
1544
- ProblemType . typeMismatchWarning ,
1545
- ProblemType . constWarning ,
1546
- ] ) ;
1534
+ mergeValidationMatches ( bestMatch , subMatchingSchemas , subValidationResult ) ;
1547
1535
}
1548
1536
}
1549
1537
return bestMatch ;
1550
1538
}
1539
+
1540
+ function mergeValidationMatches (
1541
+ bestMatch : IValidationMatch ,
1542
+ subMatchingSchemas : ISchemaCollector ,
1543
+ subValidationResult : ValidationResult
1544
+ ) : void {
1545
+ bestMatch . matchingSchemas . merge ( subMatchingSchemas ) ;
1546
+ bestMatch . validationResult . mergeEnumValues ( subValidationResult ) ;
1547
+ bestMatch . validationResult . mergeWarningGeneric ( subValidationResult , [
1548
+ ProblemType . missingRequiredPropWarning ,
1549
+ ProblemType . typeMismatchWarning ,
1550
+ ProblemType . constWarning ,
1551
+ ] ) ;
1552
+ }
1551
1553
}
1552
1554
1553
1555
function getSchemaSource ( schema : JSONSchema , originalSchema : JSONSchema ) : string | undefined {
@@ -1585,3 +1587,26 @@ function getSchemaUri(schema: JSONSchema, originalSchema: JSONSchema): string[]
1585
1587
function getWarningMessage ( problemType : ProblemType , args : string [ ] ) : string {
1586
1588
return localize ( problemType , ProblemTypeMessages [ problemType ] , args . join ( ' | ' ) ) ;
1587
1589
}
1590
+
1591
+ /**
1592
+ * if callFromAutoComplete than compare value from yaml and value from schema (s.const | s.enum[i])
1593
+ * allows partial match for autocompletion
1594
+ */
1595
+ function isAutoCompleteEqualMaybe (
1596
+ callFromAutoComplete : boolean ,
1597
+ node : ASTNode ,
1598
+ nodeValue : unknown ,
1599
+ schemaValue : unknown
1600
+ ) : boolean {
1601
+ if ( ! callFromAutoComplete ) {
1602
+ return false ;
1603
+ }
1604
+
1605
+ // if autocompletion property doesn't have value, then it could be a match
1606
+ const isWithoutValue = nodeValue === null && node . length === 0 ; // allows `prop: ` but ignore `prop: null`
1607
+ if ( isWithoutValue ) {
1608
+ return true ;
1609
+ }
1610
+
1611
+ return isString ( nodeValue ) && isString ( schemaValue ) && schemaValue . startsWith ( nodeValue ) ;
1612
+ }
0 commit comments