@@ -537,6 +537,12 @@ export function findNodeAtOffset(node: ASTNode, offset: number, includeRightBoun
537537 return undefined ;
538538}
539539
540+ interface IValidationMatch {
541+ schema : JSONSchema ;
542+ validationResult : ValidationResult ;
543+ matchingSchemas : ISchemaCollector ;
544+ }
545+
540546export class JSONDocument {
541547 public isKubernetes : boolean ;
542548 public disableAdditionalProperties : boolean ;
@@ -870,7 +876,7 @@ function validate(
870876 const val = getNodeValue ( node ) ;
871877 let enumValueMatch = false ;
872878 for ( const e of schema . enum ) {
873- if ( val === e || ( callFromAutoComplete && isString ( val ) && isString ( e ) && val && e . startsWith ( val ) ) ) {
879+ if ( val === e || isAutoCompleteEqualMaybe ( callFromAutoComplete , node , val , e ) ) {
874880 enumValueMatch = true ;
875881 break ;
876882 }
@@ -902,10 +908,7 @@ function validate(
902908
903909 if ( isDefined ( schema . const ) ) {
904910 const val = getNodeValue ( node ) ;
905- if (
906- ! equals ( val , schema . const ) &&
907- ! ( callFromAutoComplete && isString ( val ) && isString ( schema . const ) && schema . const . startsWith ( val ) )
908- ) {
911+ if ( ! equals ( val , schema . const ) && ! isAutoCompleteEqualMaybe ( callFromAutoComplete , node , val , schema . const ) ) {
909912 validationResult . problems . push ( {
910913 location : { offset : node . offset , length : node . length } ,
911914 severity : DiagnosticSeverity . Warning ,
@@ -1374,7 +1377,21 @@ function validate(
13741377 ( schema . type === 'object' && schema . additionalProperties === undefined && options . disableAdditionalProperties === true )
13751378 ) {
13761379 if ( unprocessedProperties . length > 0 ) {
1377- 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 ) ;
13781395
13791396 for ( const propertyName of unprocessedProperties ) {
13801397 const child = seenKeys [ propertyName ] ;
@@ -1508,23 +1525,11 @@ function validate(
15081525 node : ASTNode ,
15091526 maxOneMatch ,
15101527 subValidationResult : ValidationResult ,
1511- bestMatch : {
1512- schema : JSONSchema ;
1513- validationResult : ValidationResult ;
1514- matchingSchemas : ISchemaCollector ;
1515- } ,
1528+ bestMatch : IValidationMatch ,
15161529 subSchema ,
15171530 subMatchingSchemas
1518- ) : {
1519- schema : JSONSchema ;
1520- validationResult : ValidationResult ;
1521- matchingSchemas : ISchemaCollector ;
1522- } {
1523- if (
1524- ! maxOneMatch &&
1525- ! subValidationResult . hasProblems ( ) &&
1526- ( ! bestMatch . validationResult . hasProblems ( ) || callFromAutoComplete )
1527- ) {
1531+ ) : IValidationMatch {
1532+ if ( ! maxOneMatch && ! subValidationResult . hasProblems ( ) && ! bestMatch . validationResult . hasProblems ( ) ) {
15281533 // no errors, both are equally good matches
15291534 bestMatch . matchingSchemas . merge ( subMatchingSchemas ) ;
15301535 bestMatch . validationResult . propertiesMatches += subValidationResult . propertiesMatches ;
@@ -1545,19 +1550,30 @@ function validate(
15451550 validationResult : subValidationResult ,
15461551 matchingSchemas : subMatchingSchemas ,
15471552 } ;
1548- } 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+ ) {
15491557 // there's already a best matching but we are as good
1550- bestMatch . matchingSchemas . merge ( subMatchingSchemas ) ;
1551- bestMatch . validationResult . mergeEnumValues ( subValidationResult ) ;
1552- bestMatch . validationResult . mergeWarningGeneric ( subValidationResult , [
1553- ProblemType . missingRequiredPropWarning ,
1554- ProblemType . typeMismatchWarning ,
1555- ProblemType . constWarning ,
1556- ] ) ;
1558+ mergeValidationMatches ( bestMatch , subMatchingSchemas , subValidationResult ) ;
15571559 }
15581560 }
15591561 return bestMatch ;
15601562 }
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+ }
15611577}
15621578
15631579function getSchemaSource ( schema : JSONSchema , originalSchema : JSONSchema ) : string | undefined {
@@ -1595,3 +1611,26 @@ function getSchemaUri(schema: JSONSchema, originalSchema: JSONSchema): string[]
15951611function getWarningMessage ( problemType : ProblemType , args : string [ ] ) : string {
15961612 return localize ( problemType , ProblemTypeMessages [ problemType ] , args . join ( ' | ' ) ) ;
15971613}
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+ }
0 commit comments