question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Code action response contains `null` as one of the code actions

See original GitHub issue

The response to a textDocument/codeAction request contains null as one of the code actions, which according to LSP specification is not allowed.

LSP specification: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeAction

Log:

:: --> jdtls textDocument/codeAction(55): {'context': {'diagnostics': [], 'triggerKind': 2}, 'textDocument': {'uri': '<REMOVED>'}, 'range': {'end': {'character': 27, 'line': 20}, 'start': {'character': 27, 'line': 20}}}


:: <<< jdtls 55: [{'title': 'Assign statement to new field', 'kind': 'refactor.assign.field', 'data': {'pid': '0', 'rid': '15'}, 'diagnostics': []}, {'title': 'Assign statement to new local variable', 'kind': 'refactor.assign.variable', 'data': {'pid': '1', 'rid': '15'}, 'diagnostics': []}, None, {'title': 'Organize imports', 'kind': 'source.organizeImports', 'data': {'pid': '2', 'rid': '15'}, 'diagnostics': []}, {'title': 'Generate toString()', 'kind': 'source.generate.toString', 'data': {'pid': '3', 'rid': '15'}, 'diagnostics': []}, {'title': "Sort Members for 'DynamicProxyTest.java'", 'kind': 'source.sortMembers', 'data': {'pid': '4', 'rid': '15'}, 'diagnostics': []}, {'title': 'Change modifiers to final where possible', 'kind': 'source.generate.finalModifiers', 'data': {'pid': '5', 'rid': '15'}, 'diagnostics': []}]

See https://github.com/sublimelsp/LSP/issues/2057 for additional information.

Issue Analytics

  • State:closed
  • Created 10 months ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
rgrunbercommented, Nov 25, 2022

So to summarize, I think we need to do the following :

1. Fix the current JDT-LS code to avoid collecting empty code action objects. This should be easy to do.

2. Investigate why quick-assists are considered "default code action kinds" in JDT-LS. Some clients may opt out of them but we still collect them, then fail to convert when we realize the client doesn't want them.

3. While code action kinds seem to be hierarchical, I don't think the "empty kind" is meant to be part of that hierarchy. If so there's no point in declaring anything else as it will always match. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeActionKind

Just to follow up here. So (1) is done now, which resulted in this change being closed. We should no longer emit None/null items (at least from that code path) in code action responses.

For (2), I suspect quick-assists are special in that we might have needed a separate category (eg. quickassist) and didn’t want to place such code actions under source. The problem is that codeActionLiteralSupport can’t be overriden in vscode-languageclient and it may be the same for other clients also. if a language client emitted the empty string ("") or "quickassist" for codeActionKind that will probably work to pick up these quick assists.

For (3), as from (2), I think some changes need to happen, but we’re partly limited by client API. If all clients had access to codeActionLiteralSupport then we could implement the check more appropriately on the language server.

1reaction
rgrunbercommented, Nov 18, 2022

Ok, I’ve debbuged this and know what’s going wrong. I think we can fix this in JDT-LS, but the Sublime extension is also going to need to update some settings it sends to the server to ensure it continues to receive the relevant code actions (in additon to maybe others that are missing).

I’m including in this comment a sample of what I see Sublime sending in initialize as well as what I see VS Code sending for the sake of comparison.

Sublime Java initialize
{
	"workspaceFolders": [
		{
			"name": "junit5-jupiter-starter-gradle",
			"uri": "file:///home/rgrunber/sample-projects/junit5-samples/junit5-jupiter-starter-gradle"
		}
	],
	"rootPath": "/home/rgrunber/sample-projects/junit5-samples/junit5-jupiter-starter-gradle",
	"capabilities": {
		"textDocument": {
			"codeLens": {
				"dynamicRegistration": True
			},
			"documentSymbol": {
				"dynamicRegistration": True,
				"symbolKind": {
					"valueSet": [
						14,
						16,
						11,
						10,
						25,
						17,
						13,
						19,
						21,
						8,
						12,
						7,
						2,
						4,
						5,
						22,
						15,
						20,
						3,
						18,
						1,
						23,
						26,
						9,
						24,
						6
					]
				},
				"tagSupport": {
					"valueSet": [
						1
					]
				},
				"hierarchicalDocumentSymbolSupport": True
			},
			"rename": {
				"dynamicRegistration": True,
				"prepareSupport": True
			},
			"synchronization": {
				"dynamicRegistration": True,
				"willSave": True,
				"didSave": True,
				"willSaveWaitUntil": True
			},
			"inlayHint": {
				"dynamicRegistration": True,
				"resolveSupport": {
					"properties": [
						"textEdits",
						"label.command"
					]
				}
			},
			"references": {
				"dynamicRegistration": True
			},
			"formatting": {
				"dynamicRegistration": True
			},
			"definition": {
				"dynamicRegistration": True,
				"linkSupport": True
			},
			"semanticTokens": {
				"formats": [
					"relative"
				],
				"requests": {
					"full": {
						"delta": True
					},
					"range": True
				},
				"tokenTypes": [
					"decorator",
					"parameter",
					"regexp",
					"interface",
					"operator",
					"variable",
					"keyword",
					"event",
					"function",
					"property",
					"struct",
					"class",
					"type",
					"macro",
					"namespace",
					"modifier",
					"number",
					"typeParameter",
					"enum",
					"enumMember",
					"string",
					"method",
					"comment"
				],
				"augmentsSyntaxTokens": True,
				"overlappingTokenSupport": False,
				"multilineTokenSupport": True,
				"dynamicRegistration": True,
				"tokenModifiers": [
					"readonly",
					"static",
					"documentation",
					"definition",
					"declaration",
					"modification",
					"defaultLibrary",
					"async",
					"abstract",
					"deprecated"
				]
			},
			"documentLink": {
				"dynamicRegistration": True,
				"tooltipSupport": True
			},
			"publishDiagnostics": {
				"relatedInformation": True,
				"versionSupport": True,
				"tagSupport": {
					"valueSet": [
						2,
						1
					]
				},
				"dataSupport": True,
				"codeDescriptionSupport": True
			},
			"completion": {
				"dynamicRegistration": True,
				"completionItemKind": {
					"valueSet": [
						21,
						16,
						13,
						12,
						24,
						6,
						14,
						5,
						15,
						3,
						10,
						18,
						9,
						1,
						11,
						7,
						8,
						23,
						17,
						22,
						25,
						4,
						20,
						2,
						19
					]
				},
				"insertTextMode": 2,
				"completionItem": {
					"snippetSupport": True,
					"documentationFormat": [
						"markdown",
						"plaintext"
					],
					"insertTextModeSupport": {
						"valueSet": [
							2
						]
					},
					"labelDetailsSupport": True,
					"resolveSupport": {
						"properties": [
							"detail",
							"documentation",
							"additionalTextEdits"
						]
					},
					"insertReplaceSupport": True,
					"tagSupport": {
						"valueSet": [
							1
						]
					},
					"deprecatedSupport": True
				}
			},
			"signatureHelp": {
				"dynamicRegistration": True,
				"contextSupport": True,
				"signatureInformation": {
					"activeParameterSupport": True,
					"documentationFormat": [
						"markdown",
						"plaintext"
					],
					"parameterInformation": {
						"labelOffsetSupport": True
					}
				}
			},
			"colorProvider": {
				"dynamicRegistration": True
			},
			"implementation": {
				"dynamicRegistration": True,
				"linkSupport": True
			},
			"hover": {
				"dynamicRegistration": True,
				"contentFormat": [
					"markdown",
					"plaintext"
				]
			},
			"declaration": {
				"dynamicRegistration": True,
				"linkSupport": True
			},
			"codeAction": {
				"disabledSupport": True,
				"codeActionLiteralSupport": {
					"codeActionKind": {
						"valueSet": [
							"quickfix",
							"refactor",
							"refactor.extract",
							"refactor.inline",
							"refactor.rewrite",
							"source.fixAll",
							"source.organizeImports"
						]
					}
				},
				"resolveSupport": {
					"properties": [
						"edit"
					]
				},
				"dynamicRegistration": True,
				"isPreferredSupport": True,
				"dataSupport": True
			},
			"selectionRange": {
				"dynamicRegistration": True
			},
			"typeDefinition": {
				"dynamicRegistration": True,
				"linkSupport": True
			},
			"documentHighlight": {
				"dynamicRegistration": True
			},
			"rangeFormatting": {
				"dynamicRegistration": True
			}
		},
		"general": {
			"regularExpressions": {
				"engine": "ECMAScript"
			},
			"markdown": {
				"version": "3.2.2",
				"parser": "Python-Markdown"
			}
		},
		"workspace": {
			"codeLens": {
				"refreshSupport": True
			},
			"workspaceFolders": True,
			"didChangeConfiguration": {
				"dynamicRegistration": True
			},
			"workspaceEdit": {
				"failureHandling": "abort",
				"documentChanges": True
			},
			"configuration": True,
			"executeCommand": {},
			"inlayHint": {
				"refreshSupport": True
			},
			"symbol": {
				"dynamicRegistration": True,
				"symbolKind": {
					"valueSet": [
						14,
						16,
						11,
						10,
						25,
						17,
						13,
						19,
						21,
						8,
						12,
						7,
						2,
						4,
						5,
						22,
						15,
						20,
						3,
						18,
						1,
						23,
						26,
						9,
						24,
						6
					]
				},
				"tagSupport": {
					"valueSet": [
						1
					]
				}
			},
			"applyEdit": True,
			"semanticTokens": {
				"refreshSupport": True
			}
		},
		"window": {
			"workDoneProgress": True,
			"showMessage": {
				"messageActionItem": {
					"additionalPropertiesSupport": True
				}
			},
			"showDocument": {
				"support": True
			}
		}
	},
	"initializationOptions": {
		"bundles": [
        ...
        ...
		]
	},
	"processId": 90113,
	"clientInfo": {
		"version": "1.20.0",
		"name": "Sublime Text LSP"
	},
	"rootUri": "file:///home/rgrunber/sample-projects/junit5-samples/junit5-jupiter-starter-gradle"
}

VS Code Java initialize
{
    "processId": 103318,
    "clientInfo": {
        "name": "Visual Studio Code",
        "version": "1.73.1"
    },
    "locale": "en-gb",
    "rootPath": "/home/rgrunber/sample-projects/junit5-samples/junit5-jupiter-starter-gradle",
    "rootUri": "file:///home/rgrunber/sample-projects/junit5-samples/junit5-jupiter-starter-gradle",
    "capabilities": {
        "workspace": {
            "applyEdit": true,
            "workspaceEdit": {
                "documentChanges": true,
                "resourceOperations": [
                    "create",
                    "rename",
                    "delete"
                ],
                "failureHandling": "textOnlyTransactional",
                "normalizesLineEndings": true,
                "changeAnnotationSupport": {
                    "groupsOnLabel": true
                }
            },
            "didChangeConfiguration": {
                "dynamicRegistration": true
            },
            "didChangeWatchedFiles": {
                "dynamicRegistration": true
            },
            "symbol": {
                "dynamicRegistration": true,
                "symbolKind": {
                    "valueSet": [
                        1,
                        2,
                        3,
                        4,
                        5,
                        6,
                        7,
                        8,
                        9,
                        10,
                        11,
                        12,
                        13,
                        14,
                        15,
                        16,
                        17,
                        18,
                        19,
                        20,
                        21,
                        22,
                        23,
                        24,
                        25,
                        26
                    ]
                },
                "tagSupport": {
                    "valueSet": [
                        1
                    ]
                }
            },
            "codeLens": {
                "refreshSupport": true
            },
            "executeCommand": {
                "dynamicRegistration": true
            },
            "configuration": true,
            "workspaceFolders": true,
            "semanticTokens": {
                "refreshSupport": true
            },
            "fileOperations": {
                "dynamicRegistration": true,
                "didCreate": true,
                "didRename": true,
                "didDelete": true,
                "willCreate": true,
                "willRename": true,
                "willDelete": true
            }
        },
        "textDocument": {
            "publishDiagnostics": {
                "relatedInformation": true,
                "versionSupport": false,
                "tagSupport": {
                    "valueSet": [
                        1,
                        2
                    ]
                },
                "codeDescriptionSupport": true,
                "dataSupport": true
            },
            "synchronization": {
                "dynamicRegistration": true,
                "willSave": true,
                "willSaveWaitUntil": true,
                "didSave": true
            },
            "completion": {
                "dynamicRegistration": true,
                "contextSupport": true,
                "completionItem": {
                    "snippetSupport": true,
                    "commitCharactersSupport": true,
                    "documentationFormat": [
                        "markdown",
                        "plaintext"
                    ],
                    "deprecatedSupport": true,
                    "preselectSupport": true,
                    "tagSupport": {
                        "valueSet": [
                            1
                        ]
                    },
                    "insertReplaceSupport": true,
                    "resolveSupport": {
                        "properties": [
                            "documentation",
                            "detail",
                            "additionalTextEdits"
                        ]
                    },
                    "insertTextModeSupport": {
                        "valueSet": [
                            1,
                            2
                        ]
                    },
                    "labelDetailsSupport": true
                },
                "insertTextMode": 2,
                "completionItemKind": {
                    "valueSet": [
                        1,
                        2,
                        3,
                        4,
                        5,
                        6,
                        7,
                        8,
                        9,
                        10,
                        11,
                        12,
                        13,
                        14,
                        15,
                        16,
                        17,
                        18,
                        19,
                        20,
                        21,
                        22,
                        23,
                        24,
                        25
                    ]
                }
            },
            "hover": {
                "dynamicRegistration": true,
                "contentFormat": [
                    "markdown",
                    "plaintext"
                ]
            },
            "signatureHelp": {
                "dynamicRegistration": true,
                "signatureInformation": {
                    "documentationFormat": [
                        "markdown",
                        "plaintext"
                    ],
                    "parameterInformation": {
                        "labelOffsetSupport": true
                    },
                    "activeParameterSupport": true
                },
                "contextSupport": true
            },
            "definition": {
                "dynamicRegistration": true,
                "linkSupport": true
            },
            "references": {
                "dynamicRegistration": true
            },
            "documentHighlight": {
                "dynamicRegistration": true
            },
            "documentSymbol": {
                "dynamicRegistration": true,
                "symbolKind": {
                    "valueSet": [
                        1,
                        2,
                        3,
                        4,
                        5,
                        6,
                        7,
                        8,
                        9,
                        10,
                        11,
                        12,
                        13,
                        14,
                        15,
                        16,
                        17,
                        18,
                        19,
                        20,
                        21,
                        22,
                        23,
                        24,
                        25,
                        26
                    ]
                },
                "hierarchicalDocumentSymbolSupport": true,
                "tagSupport": {
                    "valueSet": [
                        1
                    ]
                },
                "labelSupport": true
            },
            "codeAction": {
                "dynamicRegistration": true,
                "isPreferredSupport": true,
                "disabledSupport": true,
                "dataSupport": true,
                "resolveSupport": {
                    "properties": [
                        "edit"
                    ]
                },
                "codeActionLiteralSupport": {
                    "codeActionKind": {
                        "valueSet": [
                            "",
                            "quickfix",
                            "refactor",
                            "refactor.extract",
                            "refactor.inline",
                            "refactor.rewrite",
                            "source",
                            "source.organizeImports"
                        ]
                    }
                },
                "honorsChangeAnnotations": false
            },
            "codeLens": {
                "dynamicRegistration": true
            },
            "formatting": {
                "dynamicRegistration": true
            },
            "rangeFormatting": {
                "dynamicRegistration": true
            },
            "onTypeFormatting": {
                "dynamicRegistration": true
            },
            "rename": {
                "dynamicRegistration": true,
                "prepareSupport": true,
                "prepareSupportDefaultBehavior": 1,
                "honorsChangeAnnotations": true
            },
            "documentLink": {
                "dynamicRegistration": true,
                "tooltipSupport": true
            },
            "typeDefinition": {
                "dynamicRegistration": true,
                "linkSupport": true
            },
            "implementation": {
                "dynamicRegistration": true,
                "linkSupport": true
            },
            "colorProvider": {
                "dynamicRegistration": true
            },
            "foldingRange": {
                "dynamicRegistration": true,
                "rangeLimit": 5000,
                "lineFoldingOnly": true
            },
            "declaration": {
                "dynamicRegistration": true,
                "linkSupport": true
            },
            "selectionRange": {
                "dynamicRegistration": true
            },
            "callHierarchy": {
                "dynamicRegistration": true
            },
            "semanticTokens": {
                "dynamicRegistration": true,
                "tokenTypes": [
                    "namespace",
                    "type",
                    "class",
                    "enum",
                    "interface",
                    "struct",
                    "typeParameter",
                    "parameter",
                    "variable",
                    "property",
                    "enumMember",
                    "event",
                    "function",
                    "method",
                    "macro",
                    "keyword",
                    "modifier",
                    "comment",
                    "string",
                    "number",
                    "regexp",
                    "operator"
                ],
                "tokenModifiers": [
                    "declaration",
                    "definition",
                    "readonly",
                    "static",
                    "deprecated",
                    "abstract",
                    "async",
                    "modification",
                    "documentation",
                    "defaultLibrary"
                ],
                "formats": [
                    "relative"
                ],
                "requests": {
                    "range": true,
                    "full": {
                        "delta": true
                    }
                },
                "multilineTokenSupport": false,
                "overlappingTokenSupport": false
            },
            "linkedEditingRange": {
                "dynamicRegistration": true
            }
        },
        "window": {
            "showMessage": {
                "messageActionItem": {
                    "additionalPropertiesSupport": true
                }
            },
            "showDocument": {
                "support": true
            },
            "workDoneProgress": true
        },
        "general": {
            "staleRequestSupport": {
                "cancel": true,
                "retryOnContentModified": [
                    "textDocument/semanticTokens/full",
                    "textDocument/semanticTokens/range",
                    "textDocument/semanticTokens/full/delta"
                ]
            },
            "regularExpressions": {
                "engine": "ECMAScript",
                "version": "ES2020"
            },
            "markdown": {
                "parser": "marked",
                "version": "1.1.0"
            }
        }
    },
    "initializationOptions": {
        "bundles": [
        ...
        ...
        ],
        "workspaceFolders": [
            "file:///home/rgrunber/sample-projects/junit5-samples/junit5-jupiter-starter-gradle"
        ],
        "settings": {
        ...
        ...
        },
        "extendedClientCapabilities": {
            "progressReportProvider": true,
            "classFileContentsSupport": true,
            "overrideMethodsPromptSupport": true,
            "hashCodeEqualsPromptSupport": true,
            "advancedOrganizeImportsSupport": true,
            "generateToStringPromptSupport": true,
            "advancedGenerateAccessorsSupport": true,
            "generateConstructorsPromptSupport": true,
            "generateDelegateMethodsPromptSupport": true,
            "advancedExtractRefactoringSupport": true,
            "inferSelectionSupport": [
                "extractMethod",
                "extractVariable",
                "extractField"
            ],
            "moveRefactoringSupport": true,
            "clientHoverProvider": true,
            "clientDocumentSymbolProvider": true,
            "gradleChecksumWrapperPromptSupport": true,
            "resolveAdditionalTextEditsSupport": true,
            "advancedIntroduceParameterRefactoringSupport": true,
            "actionableRuntimeNotificationSupport": true,
            "shouldLanguageServerExitOnShutdown": true,
            "onCompletionItemSelectedCommand": "editor.action.triggerParameterHints"
        },
        "triggerFiles": [
            "file:///home/rgrunber/sample-projects/junit5-samples/junit5-jupiter-starter-gradle/src/main/java/com/example/project/Calculator.java"
        ]
    },
    "trace": "verbose",
    "workspaceFolders": [
        {
            "uri": "file:///home/rgrunber/sample-projects/junit5-samples/junit5-jupiter-starter-gradle",
            "name": "junit5-jupiter-starter-gradle"
        }
    ]
}

When a language server sends an initialize request to the server, the most obvious thing to send, are the settings, but there’s also a lot of capabilities that are sent to determine what kinds of things the server can safely send back (eg. completions, code actions, hover, etc.)

The part I’d like to focus on is :

Sublime

    "codeActionLiteralSupport": {
        "codeActionKind": {
            "valueSet": [
                "quickfix",
                "refactor",
                "refactor.extract",
                "refactor.inline",
                "refactor.rewrite",
                "source.fixAll",
                "source.organizeImports"
            ]
        }
    }

VS Code

    "codeActionLiteralSupport": {
        "codeActionKind": {
            "valueSet": [
                "",
                "quickfix",
                "refactor",
                "refactor.extract",
                "refactor.inline",
                "refactor.rewrite",
                "source",
                "source.organizeImports"
            ]
        }
    }

https://github.com/eclipse/eclipse.jdt.ls/blob/e64c205d6b2c3edbf6a9f5df3b4c216ba35d3c87/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java#L281-L298

If you look closely, you’ll see Add Javadoc for 'add' has a kind of quickassist, which the Sublime LSP client does not register with the language server. This results in an empty union (eg. null) ending up in the list of code actions. The “empty” code action kind that VS Code sends always matches every code action, which seems like another bug, but ultimately ensures the code action is processed. You can see the differences here :

https://github.com/microsoft/vscode-languageserver-node/blob/release/client/7.1.0-next.5/client/src/common/client.ts#L2018 . https://github.com/sublimelsp/LSP/blob/main/plugin/core/sessions.py#L324

So to summarize, I think we need to do the following :

  1. Fix the current JDT-LS code to avoid collecting empty code action objects. This should be easy to do.
  2. Investigate why quick-assists are considered “default code action kinds” in JDT-LS. Some clients may opt out of them but we still collect them, then fail to convert when we realize the client doesn’t want them.
  3. While code action kinds seem to be hierarchical, I don’t think the “empty kind” is meant to be part of that hierarchy. If so there’s no point in declaring anything else as it will always match. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeActionKind
Read more comments on GitHub >

github_iconTop Results From Across the Web

Cannot parse response.content when contains null as value
Work correctly when Jenkins returns a valid Json. ACTUAL RESULTS. Jenkinsapi raised exception. USEFUL INFORMATION. The codes are here, in jenkinsbase.py#L85.
Read more >
ASP.NET Web API and Status Code For Null Response
Response can be null when an exception occurs; and the NullObjectActionFilter class shown above can obscure error HTTP status codes.
Read more >
Flow failing due to 'Null' value - Power Platform Community
Solved: I have a flow setup to create a Trello card upon creation of a Sharepoint list item. One of the items I'm...
Read more >
UpdateItem - Amazon DynamoDB - AWS Documentation
Edits an existing item's attributes, or adds a new item to the table if it does not already exist. You can put, delete,...
Read more >
Null API Responses and HTTP 204 Results in ASP.NET Core
x has a behavior that results in API results that return null from the controller action returning a HTTP 204 - No Content...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found