Repository: sublimelsp/LSP

Issue:
LSP-json showing errors momentairly due to pull diagnostics
**Describe the bug**

I've noticed that there is slightly non-ideal behavior when using LSP-json and opening some JSON files.

When opening `tsconfig.json` file for example, the trailing commas would be highlighted as errors momentarily until an associated schema is loaded.

This is related to support for pull diagnostics since we are pulling and receiving diagnostics before the schema is loaded.

This doesn't reproduce in VSCode even though it also uses pull diagnostics. Possibly by sheer luck as it might be delaying pull requests and/or showing diagnosics a bit more aggressively.

https://user-images.githubusercontent.com/153197/232613439-c948932b-274a-44d9-9889-c5f6c9060727.mov

LSP log:

```
:: [23:22:26.084] --> LSP-json initialize (1): {'initializationOptions': {'handledSchemaProtocols': ['https', 'http', 'file'], 'provideFormatter': True, 'customCapabilities': {'rangeFormatting': {'editLimit': 1000}}}, 'capabilities': {'general': {'markdown': {'version': '3.2.2', 'parser': 'Python-Markdown'}, 'regularExpressions': {'engine': 'ECMAScript'}}, 'window': {'showMessage': {'messageActionItem': {'additionalPropertiesSupport': True}}, 'showDocument': {'support': True}, 'workDoneProgress': True}, 'workspace': {'configuration': True, 'symbol': {'tagSupport': {'valueSet': [1]}, 'dynamicRegistration': True, 'symbolKind': {'valueSet': [15, 22, 16, 5, 2, 20, 19, 23, 17, 4, 24, 1, 10, 7, 6, 3, 18, 25, 26, 14, 21, 9, 13, 11, 12, 8]}}, 'didChangeWatchedFiles': {'dynamicRegistration': True}, 'applyEdit': True, 'executeCommand': {}, 'inlayHint': {'refreshSupport': True}, 'diagnostics': {'refreshSupport': True}, 'workspaceEdit': {'failureHandling': 'abort', 'documentChanges': True}, 'semanticTokens': {'refreshSupport': True}, 'workspaceFolders': True, 'didChangeConfiguration': {'dynamicRegistration': True}, 'codeLens': {'refreshSupport': True}}, 'textDocument': {'synchronization': {'didSave': True, 'willSave': True, 'dynamicRegistration': True, 'willSaveWaitUntil': True}, 'typeHierarchy': {'dynamicRegistration': True}, 'rename': {'prepareSupportDefaultBehavior': 1, 'dynamicRegistration': True, 'prepareSupport': True}, 'implementation': {'linkSupport': True, 'dynamicRegistration': True}, 'typeDefinition': {'linkSupport': True, 'dynamicRegistration': True}, 'selectionRange': {'dynamicRegistration': True}, 'definition': {'linkSupport': True, 'dynamicRegistration': True}, 'formatting': {'dynamicRegistration': True}, 'codeAction': {'codeActionLiteralSupport': {'codeActionKind': {'valueSet': ['quickfix', 'refactor', 'refactor.extract', 'refactor.inline', 'refactor.rewrite', 'source.fixAll', 'source.organizeImports']}}, 'dynamicRegistration': True, 'dataSupport': True, 'resolveSupport': {'properties': ['edit']}, 'isPreferredSupport': True}, 'publishDiagnostics': {'tagSupport': {'valueSet': [2, 1]}, 'codeDescriptionSupport': True, 'versionSupport': True, 'relatedInformation': True, 'dataSupport': True}, 'semanticTokens': {'tokenModifiers': ['static', 'async', 'declaration', 'modification', 'deprecated', 'definition', 'abstract', 'documentation', 'readonly', 'defaultLibrary'], 'overlappingTokenSupport': False, 'requests': {'range': True, 'full': {'delta': True}}, 'dynamicRegistration': True, 'tokenTypes': ['string', 'enumMember', 'number', 'class', 'type', 'struct', 'property', 'keyword', 'event', 'typeParameter', 'enum', 'macro', 'method', 'namespace', 'modifier', 'operator', 'comment', 'regexp', 'decorator', 'parameter', 'variable', 'interface', 'function'], 'augmentsSyntaxTokens': True, 'multilineTokenSupport': True, 'formats': ['relative']}, 'documentHighlight': {'dynamicRegistration': True}, 'codeLens': {'dynamicRegistration': True}, 'completion': {'completionItem': {'labelDetailsSupport': True, 'tagSupport': {'valueSet': [1]}, 'insertTextModeSupport': {'valueSet': [2]}, 'deprecatedSupport': True, 'documentationFormat': ['markdown', 'plaintext'], 'resolveSupport': {'properties': ['detail', 'documentation', 'additionalTextEdits']}, 'insertReplaceSupport': True, 'snippetSupport': True}, 'insertTextMode': 2, 'dynamicRegistration': True, 'completionItemKind': {'valueSet': [12, 20, 17, 7, 9, 14, 22, 11, 16, 23, 25, 1, 13, 10, 15, 2, 18, 24, 19, 21, 4, 6, 8, 3, 5]}, 'completionList': {'itemDefaults': ['editRange', 'insertTextFormat', 'data']}}, 'documentLink': {'tooltipSupport': True, 'dynamicRegistration': True}, 'callHierarchy': {'dynamicRegistration': True}, 'signatureHelp': {'contextSupport': True, 'dynamicRegistration': True, 'signatureInformation': {'documentationFormat': ['markdown', 'plaintext'], 'parameterInformation': {'labelOffsetSupport': True}, 'activeParameterSupport': True}}, 'declaration': {'linkSupport': True, 'dynamicRegistration': True}, 'rangeFormatting': {'dynamicRegistration': True}, 'diagnostic': {'relatedDocumentSupport': True, 'dynamicRegistration': True}, 'documentSymbol': {'tagSupport': {'valueSet': [1]}, 'hierarchicalDocumentSymbolSupport': True, 'dynamicRegistration': True, 'symbolKind': {'valueSet': [15, 22, 16, 5, 2, 20, 19, 23, 17, 4, 24, 1, 10, 7, 6, 3, 18, 25, 26, 14, 21, 9, 13, 11, 12, 8]}}, 'inlayHint': {'resolveSupport': {'properties': ['textEdits', 'label.command']}, 'dynamicRegistration': True}, 'hover': {'contentFormat': ['markdown', 'plaintext'], 'dynamicRegistration': True}, 'colorProvider': {'dynamicRegistration': True}, 'references': {'dynamicRegistration': True}}}, 'rootPath': '/usr/local/workspace/github/nuxt-2-playground', 'clientInfo': {'name': 'Sublime Text LSP', 'version': '1.23.0'}, 'processId': 17637, 'workspaceFolders': [{'name': 'nuxt-2-playground', 'uri': 'file:///usr/local/workspace/github/nuxt-2-playground'}], 'rootUri': 'file:///usr/local/workspace/github/nuxt-2-playground'}
:: [23:22:26.193] <<< LSP-json (1) (duration: 109ms): {'capabilities': {'selectionRangeProvider': True, 'documentRangeFormattingProvider': True, 'diagnosticProvider': {'interFileDependencies': False, 'workspaceDiagnostics': False, 'documentSelector': None}, 'documentLinkProvider': {}, 'foldingRangeProvider': True, 'textDocumentSync': {'didClose': {}, 'save': {}, 'didOpen': {}, 'change': {'syncKind': 2}}, 'documentSymbolProvider': True, 'colorProvider': {}, 'completionProvider': {'triggerCharacters': ['"', ':'], 'resolveProvider': False}, 'documentFormattingProvider': True, 'hoverProvider': True}}
:: [23:22:26.205]  -> LSP-json initialized: {}
:: [23:22:26.206]  -> LSP-json workspace/didChangeConfiguration: {'settings': {'jsonc': {'patterns': ['buffer://*', '.babelrc', '.eslintrc', '.eslintrc.json', '.hintrc', '.jsfmtrc', '.jshintrc', '.jsonc', '.swcrc', '/.ember-cli', '/.vscode/*.json', '/babel.config.json', '/devcontainer.json']}, 'json': {'jsonFoldingLimit': 5000, 'validate': {'enable': True}, 'format': {'enable': True}, 'jsoncColorDecoratorLimit': 5000, 'jsonColorDecoratorLimit': 5000, 'resultLimit': 5000, 'jsoncFoldingLimit': 5000}, 'userSchemas': [{'uri': 'file:///Users/rafal/workspace/bin/syncr-schema.json', 'fileMatch': ['/.syncr.json']}]}}
:: [23:22:26.274]  -> LSP-json textDocument/didOpen: {'textDocument': {'text': '{\n  "compilerOptions": {\n    "target": "ES2018",\n    "module": "ESNext",\n    "moduleResolution": "Node",\n    "lib": [\n      "ESNext",\n      "ESNext.AsyncIterable",\n      "DOM"\n    ],\n    "esModuleInterop": true,\n    "allowJs": true,\n    "checkJs": true,\n    "sourceMap": true,\n    "strict": true,\n    "noEmit": true,\n    "experimentalDecorators": true,\n    "baseUrl": ".",\n    "paths": {\n      "~/*": [\n        "./*"\n      ],\n      "@/*": [\n        "./*"\n      ]\n    },\n    "types": [\n      "./auto-import.d.ts",\n      "@nuxt/types",\n      "@nuxt/typescript-build",\n      "@nuxtjs/axios",\n      "@nuxtjs/sentry",\n      "@types/node",\n    ]\n  },\n  "include": [\n    "./auto-import.d.ts",\n    "nuxt.config.ts",\n    "components/**/*",\n    "pages/**/*",\n    "types/**/*",\n  ],\n  "exclude": [\n    "node_modules",\n    ".nuxt",\n    "dist",\n  ],\n  "vueCompilerOptions": {\n    "target": 2.7,\n    "strictTemplates": true,\n    "experimentalAdditionalLanguageModules": ["./generate-types.js"]\n  }\n}\n', 'version': 0, 'languageId': 'json', 'uri': 'file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json'}}
:: [23:22:26.275] --> LSP-json textDocument/documentColor (2): {'textDocument': {'uri': 'file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json'}}
:: [23:22:26.276] --> LSP-json textDocument/diagnostic (3): {'textDocument': {'uri': 'file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json'}}
:: [23:22:26.276] --> LSP-json textDocument/documentLink (4): {'textDocument': {'uri': 'file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json'}}
:: [23:22:26.298] <-- LSP-json workspace/diagnostic/refresh (0): None
:: [23:22:26.298] >>> LSP-json (0) (duration: 0ms): None
:: [23:22:26.299] --> LSP-json textDocument/diagnostic (5): {'textDocument': {'uri': 'file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json'}}
:: [23:22:26.299]  -> LSP-json json/schemaAssociations: <params with 88827 characters>
:: [23:22:26.300] <<< LSP-json (2) (duration: 24ms): []
:: [23:22:26.302] <<< LSP-json (3) (duration: 25ms): {'kind': 'full', 'items': [{'severity': 1, 'range': {'start': {'line': 32, 'character': 19}, 'end': {'line': 32, 'character': 20}}, 'message': 'Trailing comma', 'code': 519, 'source': 'json'}, {'severity': 1, 'range': {'start': {'line': 40, 'character': 16}, 'end': {'line': 40, 'character': 17}}, 'message': 'Trailing comma', 'code': 519, 'source': 'json'}, {'severity': 1, 'range': {'start': {'line': 45, 'character': 10}, 'end': {'line': 45, 'character': 11}}, 'message': 'Trailing comma', 'code': 519, 'source': 'json'}]}
:: [23:22:26.306] <<< LSP-json (4) (duration: 30ms): []
:: [23:22:26.309] <<< LSP-json (5) (duration: 10ms): {'kind': 'full', 'items': [{'severity': 1, 'range': {'start': {'line': 32, 'character': 19}, 'end': {'line': 32, 'character': 20}}, 'message': 'Trailing comma', 'code': 519, 'source': 'json'}, {'severity': 1, 'range': {'start': {'line': 40, 'character': 16}, 'end': {'line': 40, 'character': 17}}, 'message': 'Trailing comma', 'code': 519, 'source': 'json'}, {'severity': 1, 'range': {'start': {'line': 45, 'character': 10}, 'end': {'line': 45, 'character': 11}}, 'message': 'Trailing comma', 'code': 519, 'source': 'json'}]}
:: [23:22:26.311] <-- LSP-json workspace/diagnostic/refresh (1): None
:: [23:22:26.311] >>> LSP-json (1) (duration: 0ms): None
:: [23:22:26.312] --> LSP-json textDocument/diagnostic (6): {'textDocument': {'uri': 'file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json'}}
:: [23:22:26.330] <-- LSP-json vscode/content (2): ['sublime://settings/71']
:: [23:22:26.330] >>> LSP-json (2) (duration: 0ms): '{"properties": {"vueCompilerOptions": {"additionalProperties": false, "properties": {"bypassDefineComponentToExposePropsAndEmitsForJsScriptSetupComponents": {"deprecated": true}, "dataAttributes": {"default": [], "markdownDescription": "A glob matcher array that should always avoid recognized as HTML Attributes and Component props.", "type": "array"}, "experimentalAdditionalLanguageModules": {"markdownDescription": "https://github.com/johnsoncodehk/volar/pull/2267", "type": "array"}, "experimentalAllowTypeNarrowingInInlineHandlers": {"deprecated": true}, "experimentalCompatMode": {"deprecated": true}, "experimentalComponentOptionsWrapper": {"deprecated": true}, "experimentalComponentOptionsWrapperEnable": {"deprecated": true}, "experimentalDisableTemplateSupport": {"deprecated": true}, "experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup": {"deprecated": true}, "experimentalImplicitWrapComponentOptionsWithDefineComponent": {"deprecated": true}, "experimentalImplicitWrapComponentOptionsWithVue2Extend": {"deprecated": true}, "experimentalModelPropName": {"default": {"": {"input": {"type": "radio"}}, "checked": {"input": {"type": "checkbox"}}, "value": {"input": true, "select": true, "textarea": true}}, "markdownDescription": "https://github.com/johnsoncodehk/volar/issues/1969", "type": "object"}, "experimentalResolveNonScopedCssClasses": {"deprecated": true}, "experimentalResolveStyleCssClasses": {"enum": ["scoped", "always", "never"], "markdownDescription": "https://github.com/johnsoncodehk/volar/issues/1038, https://github.com/johnsoncodehk/volar/issues/1121"}, "experimentalRfc436": {"default": false, "markdownDescription": "https://github.com/vuejs/rfcs/discussions/436", "type": "boolean"}, "experimentalRuntimeMode": {"deprecated": true}, "experimentalShamefullySupportOptionsApi": {"deprecated": true}, "experimentalSuppressInvalidJsxElementTypeErrors": {"deprecated": true}, "experimentalSuppressUnknownJsxPropertyErrors": {"deprecated": true}, "experimentalTemplateCompilerOptions": {"deprecated": true}, "experimentalTemplateCompilerOptionsRequirePath": {"deprecated": true}, "experimentalUseElementAccessInTemplate": {"default": false, "markdownDescription": "https://github.com/johnsoncodehk/volar/issues/997", "type": "boolean"}, "experimentalUseScriptLeadingCommentInTemplate": {"deprecated": true}, "extensions": {"default": [".vue"], "markdownDescription": "Valid file extensions that should be considered as regular Vue SFC. Please note that you should not use this option separately for additional file extensions IDE support, see https://github.com/johnsoncodehk/volar/tree/master/extensions/vscode-vue-language-features/README.md#custom-file-extensions.", "type": "array"}, "hooks": {"markdownDescription": "https://github.com/johnsoncodehk/volar/pull/2217", "type": "array"}, "htmlAttributes": {"default": ["aria-*"], "markdownDescription": "A glob matcher array that should always be recognizing as HTML Attributes rather than Component props. Attribute name will never convert to camelize case.", "type": "array"}, "jsxTemplates": {"default": false, "markdownDescription": "Whether to compile template to JSX. (Generics component type checking only working with JSX)", "type": "boolean"}, "macros": {"default": {"defineEmits": ["defineEmits"], "defineExpose": ["defineExpose"], "defineProps": ["defineProps"], "withDefaults": ["withDefaults"]}, "type": "object"}, "narrowingTypesInInlineHandlers": {"markdownDescription": "https://github.com/johnsoncodehk/volar/issues/1249", "type": "boolean"}, "nativeTags": {"default": ["div", "img", "..."], "markdownDescription": "List of valid intrinsic elements.", "type": "array"}, "optionsWrapper": {"default": ["(await import(\'vue\')).defineComponent(", ")"], "markdownDescription": "How to wrap option of `export default { ... }`? Default: `[\\"(await import(\'vue\')).default.extend(\\", \\")\\"]` for target < 2.7, `[\\"(await import(\'vue\')).defineComponent(\\", \\")\\"]` for target >= 2.7.", "type": "array"}, "plugins": {"default": [], "markdownDescription": "Plugins to be used in the SFC compiler.", "type": "array"}, "skipTemplateCodegen": {"markdownDescription": "https://github.com/johnsoncodehk/volar/issues/577", "type": "boolean"}, "strictTemplates": {"default": false, "markdownDescription": "Strict props, component type-checking in templates.", "type": "boolean"}, "target": {"anyOf": [{"enum": [2, 2.7, 3]}], "default": 3, "markdownDescription": "Target version of Vue.", "type": "number"}}, "type": "object"}}}'
:: [23:22:26.330] <-- LSP-json vscode/content (3): ['sublime://schemas/tsconfig']
:: [23:22:26.330] >>> LSP-json (3) (duration: 0ms): '{"$id": "sublime://schemas/tsconfig", "$schema": "http://json-schema.org/draft-07/schema#", "allowComments": true, "allowTrailingCommas": true, "default": {"compilerOptions": {"module": "commonjs"}, "exclude": ["node_modules"]}, "title": "JSON schema for the TypeScript compiler\'s configuration file", "type": "object"}'
:: [23:22:27.070] <<< LSP-json (6) (duration: 757ms): {'kind': 'full', 'items': []}
```

One observation I have is that on `workspace/diagnostic/refresh` we should be probably cancelling pending diagnostic requests although that would likely not help with this particular issue.

Corresponding log from VSCode (slightly trimmed for readability). Note that we can also see trailing-commas being reported as diagnostics in the log but it doesn't appear like this is reflected in the editor. maybe because it happens too fast?:

```
[Trace - 23:26:14] Sending request 'initialize - (0)'.
Params: {...}


[Trace - 23:26:14] Received response 'initialize - (0)' in 75ms.
Result: {
    "capabilities": {
        "textDocumentSync": 2,
        "completionProvider": {
            "resolveProvider": false,
            "triggerCharacters": [
                "\"",
                ":"
            ]
        },
        "hoverProvider": true,
        "documentSymbolProvider": true,
        "documentRangeFormattingProvider": false,
        "documentFormattingProvider": false,
        "colorProvider": {},
        "foldingRangeProvider": true,
        "selectionRangeProvider": true,
        "documentLinkProvider": {},
        "diagnosticProvider": {
            "documentSelector": null,
            "interFileDependencies": false,
            "workspaceDiagnostics": false
        }
    }
}


[Trace - 23:26:14] Sending notification 'initialized'.
Params: {}


[Trace - 23:26:14] Sending notification 'textDocument/didOpen'.
Params: {
    "textDocument": {
        "uri": "file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json",
        "languageId": "jsonc",
        "version": 1,
        "text": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2018\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"lib\": [\n      \"ESNext\",\n      \"ESNext.AsyncIterable\",\n      \"DOM\"\n    ],\n    \"esModuleInterop\": true,\n    \"allowJs\": true,\n    \"checkJs\": true,\n    \"sourceMap\": true,\n    \"strict\": true,\n    \"noEmit\": true,\n    \"experimentalDecorators\": true,\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"~/*\": [\n        \"./*\"\n      ],\n      \"@/*\": [\n        \"./*\"\n      ]\n    },\n    \"types\": [\n      \"./auto-import.d.ts\",\n      \"@nuxt/types\",\n      \"@nuxt/typescript-build\",\n      \"@nuxtjs/axios\",\n      \"@nuxtjs/sentry\",\n      \"@types/node\",\n    ]\n  },\n  \"include\": [\n    \"./auto-import.d.ts\",\n    \"nuxt.config.ts\",\n    \"components/**/*\",\n    \"pages/**/*\",\n    \"types/**/*\",\n  ],\n  \"exclude\": [\n    \"node_modules\",\n    \".nuxt\",\n    \"dist\",\n  ],\n  \"vueCompilerOptions\": {\n    \"target\": 2.7,\n    \"strictTemplates\": true,\n    \"experimentalAdditionalLanguageModules\": [\"./generate-types.js\"]\n  }\n}\n"
    }
}


[Trace - 23:26:14] Sending notification 'workspace/didChangeConfiguration'.
Params: {
    "settings": {
        "http": {
            "proxy": "",
            "proxyStrictSSL": true
        },
        "json": {
            "validate": {
                "enable": true
            },
            "format": {
                "enable": true
            },
            "keepLines": {
                "enable": false
            },
            "schemas": [],
            "resultLimit": 5001,
            "jsonFoldingLimit": 5001,
            "jsoncFoldingLimit": 5001,
            "jsonColorDecoratorLimit": 501,
            "jsoncColorDecoratorLimit": 501
        }
    }
}


[Trace - 23:26:14] Sending request 'textDocument/diagnostic - (1)'.
Params: {
    "textDocument": {
        "uri": "file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json"
    }
}


[Trace - 23:26:14] Sending notification 'json/schemaAssociations'.
Params: [
    [
        ...
        {
            "fileMatch": [
                "/tsconfig.json"
            ],
            "uri": "https://json.schemastore.org/tsconfig"
        },...
    ]
]


[Trace - 23:26:14] Sending request 'json/validate - (2)'.
Params: [
    "file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json"
]


[Trace - 23:26:14] Sending request 'json/languageStatus - (3)'.
Params: [
    "file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json"
]


[Trace - 23:26:14] Received request 'workspace/diagnostic/refresh - (0)'.
[Trace - 23:26:14] Sending notification '$/cancelRequest'.
Params: {
    "id": 1
}


[Trace - 23:26:14] Sending response 'workspace/diagnostic/refresh - (0)'. Processing request took 0ms
No result returned.


[Trace - 23:26:14] Sending request 'textDocument/documentSymbol - (4)'.
Params: {
    "textDocument": {
        "uri": "file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json"
    }
}


[Trace - 23:26:14] Sending request 'textDocument/documentColor - (5)'.
Params: {
    "textDocument": {
        "uri": "file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json"
    }
}


[Trace - 23:26:14] Sending request 'textDocument/documentLink - (6)'.
Params: {
    "textDocument": {
        "uri": "file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json"
    }
}


[Trace - 23:26:14] Received response 'textDocument/diagnostic - (1)' in 3ms.
Result: {
    "kind": "full",
    "items": [
        {
            "range": {
                "start": {
                    "line": 32,
                    "character": 19
                },
                "end": {
                    "line": 32,
                    "character": 20
                }
            },
            "message": "Trailing comma",
            "severity": 2,
            "code": 519,
            "source": "jsonc"
        },
        {
            "range": {
                "start": {
                    "line": 40,
                    "character": 16
                },
                "end": {
                    "line": 40,
                    "character": 17
                }
            },
            "message": "Trailing comma",
            "severity": 2,
            "code": 519,
            "source": "jsonc"
        },
        {
            "range": {
                "start": {
                    "line": 45,
                    "character": 10
                },
                "end": {
                    "line": 45,
                    "character": 11
                }
            },
            "message": "Trailing comma",
            "severity": 2,
            "code": 519,
            "source": "jsonc"
        }
    ]
}


[Trace - 23:26:14] Sending request 'textDocument/diagnostic - (7)'.
Params: {
    "textDocument": {
        "uri": "file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json"
    }
}


[Trace - 23:26:14] Received request 'workspace/diagnostic/refresh - (1)'.
[Trace - 23:26:14] Sending notification '$/cancelRequest'.
Params: {
    "id": 7
}


[Trace - 23:26:14] Sending response 'workspace/diagnostic/refresh - (1)'. Processing request took 0ms
No result returned.


[Trace - 23:26:14] Received request 'workspace/diagnostic/refresh - (2)'.
[Trace - 23:26:14] Sending response 'workspace/diagnostic/refresh - (2)'. Processing request took 0ms
No result returned.


[Trace - 23:26:14] Received request 'vscode/content - (3)'.
Params: [
    "https://json.schemastore.org/tsconfig"
]


[json schema cache] Requesting schema https://json.schemastore.org/tsconfig etag undefined...
[Trace - 23:26:14] Received response 'json/languageStatus - (3)' in 7ms.
Result: {
    "schemas": [
        "https://json.schemastore.org/tsconfig",
        "file:///Applications/Visual Studio Code.app/Contents/Resources/app/extensions/typescript-language-features/schemas/tsconfig.schema.json",
        "file:///Users/rafal/.vscode/extensions/vue.volar-1.3.16/dist/schemas/vue-tsconfig.schema.json",
        "file:///Users/rafal/.vscode/extensions/vue.volar-1.3.16/dist/schemas/vue-tsconfig.deprecated.schema.json"
    ]
}


[Trace - 23:26:14] Received response 'textDocument/documentSymbol - (4)' in 7ms.
Result: [...]


[Trace - 23:26:14] Received response 'textDocument/documentLink - (6)' in 7ms.
Result: []


[Trace - 23:26:14] Received notification 'window/logMessage'.
Params: {
    "type": 4,
    "message": "cancelled"
}


cancelled
[Trace - 23:26:14] Received response 'textDocument/diagnostic - (7)' in 7ms. Request failed: Request cancelled (-32800).
[Trace - 23:26:14] Sending request 'textDocument/diagnostic - (8)'.
Params: {
    "textDocument": {
        "uri": "file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json"
    }
}


[Trace - 23:26:14] Sending request 'textDocument/foldingRange - (9)'.
Params: {
    "textDocument": {
        "uri": "file:///usr/local/workspace/github/nuxt-2-playground/tsconfig.json"
    }
}


[Trace - 23:26:14] Received response 'textDocument/foldingRange - (9)' in 1ms.
Result: [...]


[Trace - 23:26:15] Sending response 'vscode/content - (3)'. Processing request took 777ms
Result: "..."


[Trace - 23:26:15] Received response 'json/validate - (2)' in 801ms.
Result: []


[Trace - 23:26:15] Received response 'textDocument/documentColor - (5)' in 800ms.
Result: []


[Trace - 23:26:15] Received response 'textDocument/diagnostic - (8)' in 792ms.
Result: {
    "kind": "full",
    "items": []
}


[Trace - 23:26:18] Sending request 'json/languageStatus - (10)'.
Params: [
    "output:extension-output-vscode.json-language-features-%231-JSON%20Language%20Server"
]


[Trace - 23:26:18] Received response 'json/languageStatus - (10)' in 2ms.
Result: {
    "schemas": []
}
```



Discussion:
A relevant aspect in the VSCode logs is that the diagnostics with the trailing commas arrive only after the corresponding request (1) was cancelled (which is in complience with the LSP specs, i.e. it can either send an error response or ignore the cancel notification and send the regular response). Without having checked the VSCode source code, I could imagine that it just ignores the responses for such cancelled requests. In fact, if we would also cancel our diagnostics request(s) on `workspace/diagnostic/refresh`, then we would do the same and ignore possible responses (by default):
https://github.com/sublimelsp/LSP/blob/7275f88e7f40228dd22ed233436ce1ddb448048d/plugin/core/sessions.py#L2028-L2032

So maybe it would already be sufficient to implement cancelling on `workspace/diagnostic/refresh`, for this problem to disappear. However, in the Sublime LSP logs, there is another response (5) containing the trailing commas diagnostics, and this one arrives immediately before another `workspace/diagnostic/refresh`. Therefore this second one would not have been cancelled, and so it might not work in this case. But perhaps this was just caused by the server caching the response, because our second request was sent even before the server responded to our first request.

If this issue doesn't go away even with cancelling, maybe we need to add a small delay for sending our new diagnostics requests after  `workspace/diagnostic/refresh`. Note how the server sends this request twice (IDs 0 and 1 in the Sublime LSP logs), but the diagnostics response is only as expected (empty diagnostics) after the second time.

Resolving patch:
diff --git a/plugin/session_buffer.py b/plugin/session_buffer.py
--- a/plugin/session_buffer.py
+++ b/plugin/session_buffer.py
@@ -137,6 +137,7 @@ def __init__(self, session_view: SessionViewProtocol, buffer_id: int, uri: Docum
         self.diagnostics_are_visible = False
         self.document_diagnostic_result_id = None  # type: Optional[str]
         self.document_diagnostic_needs_refresh = False
+        self.document_diagnostic_pending_response = None  # type: Optional[int]
         self.last_text_change_time = 0.0
         self.diagnostics_debouncer_async = DebouncerNonThreadSafe(async_thread=True)
         self.color_phantoms = sublime.PhantomSet(view, "lsp_color")
@@ -454,19 +455,27 @@ def do_document_diagnostic_async(self, view: sublime.View, version: Optional[int
         if version is None:
             version = view.change_count()
         if self.has_capability("diagnosticProvider"):
+            if self.document_diagnostic_pending_response:
+                self.session.cancel_request(self.document_diagnostic_pending_response)
             params = {'textDocument': text_document_identifier(view)}  # type: DocumentDiagnosticParams
             identifier = self.get_capability("diagnosticProvider.identifier")
             if identifier:
                 params['identifier'] = identifier
             if self.document_diagnostic_result_id:
                 params['previousResultId'] = self.document_diagnostic_result_id
-            self.session.send_request_async(
+            self.document_diagnostic_pending_response = self.session.send_request_async(
                 Request.documentDiagnostic(params, view),
-                self._if_view_unchanged(self.on_document_diagnostic, version),
-                self._if_view_unchanged(self._on_document_diagnostic_error, version)
+                partial(self._on_document_diagnostic_async, version),
+                partial(self._on_document_diagnostic_error_async, version)
             )
 
-    def on_document_diagnostic(self, view: Optional[sublime.View], response: DocumentDiagnosticReport) -> None:
+    def _on_document_diagnostic_async(self, version: int, response: DocumentDiagnosticReport) -> None:
+        self.document_diagnostic_pending_response = None
+        self._if_view_unchanged(self._apply_document_diagnostic_async, version)(response)
+
+    def _apply_document_diagnostic_async(
+        self, view: Optional[sublime.View], response: DocumentDiagnosticReport
+    ) -> None:
         self.document_diagnostic_result_id = response.get('resultId')
         if is_full_document_diagnostic_report(response):
             self.session.m_textDocument_publishDiagnostics(
@@ -474,14 +483,15 @@ def on_document_diagnostic(self, view: Optional[sublime.View], response: Documen
         for uri, diagnostic_report in response.get('relatedDocuments', {}):
             sb = self.session.get_session_buffer_for_uri_async(uri)
             if sb:
-                cast(SessionBuffer, sb).on_document_diagnostic(None, cast(DocumentDiagnosticReport, diagnostic_report))
+                cast(SessionBuffer, sb)._apply_document_diagnostic_async(
+                    None, cast(DocumentDiagnosticReport, diagnostic_report))
 
-    def _on_document_diagnostic_error(self, view: sublime.View, error: Error) -> None:
+    def _on_document_diagnostic_error_async(self, version: int, error: Error) -> None:
+        self.document_diagnostic_pending_response = None
         if error.code == LSPErrorCodes.ServerCancelled and is_diagnostic_server_cancellation_data(error.data) and \
                 error.data['retriggerRequest']:
             # Retrigger the request after a short delay, but only if there were no additional changes to the buffer (in
             # that case the request will be retriggered automatically anyway)
-            version = view.change_count()
             sublime.set_timeout_async(
                 lambda: self._if_view_unchanged(self.do_document_diagnostic_async, version)(version), 500)
 


Test patch: