Only send `completionItem/resolve` if supported (#2624)

* Oni will hang indefinitely if the language server simply chooses to
ignore unsupported requests.
* This can be avoided if the server provides correct capabilities and
we actually check them before making the request.
This commit is contained in:
feltech 2018-10-12 11:49:45 +01:00 committed by Akin
parent a4d877b967
commit a369b79c20
4 changed files with 1927 additions and 34 deletions

View File

@ -10,6 +10,7 @@ import * as Log from "oni-core-logging"
import * as Helpers from "./../../Plugins/Api/LanguageClient/LanguageClientHelpers"
import { LanguageManager } from "./../Language"
import { IServerCapabilities } from "./../Language/ServerCapabilities"
export interface CompletionsRequestContext {
language: string
@ -86,6 +87,12 @@ export class LanguageServiceCompletionsRequestor implements ICompletionsRequesto
filePath: string,
completionItem: types.CompletionItem,
): Promise<types.CompletionItem> {
const caps: IServerCapabilities = await this._languageManager.getCapabilitiesForLanguage(
language,
)
if (!caps.completionProvider.resolveProvider) {
return completionItem
}
let result
try {
result = await this._languageManager.sendLanguageServerRequest(

View File

@ -0,0 +1,60 @@
import * as assert from "assert"
import * as sinon from "sinon"
import { LanguageServiceCompletionsRequestor } from "./../../../src/Services/Completion/CompletionsRequestor"
import { LanguageManager } from "./../../../src/Services/Language"
describe("LanguageServiceCompletionsRequestor", () => {
describe("getCompletionDetails", () => {
let completionItem: any
let completionDetails: any
let languageManager: any
let requestor: any
beforeEach(() => {
completionItem = sinon.stub()
completionDetails = sinon.stub()
languageManager = sinon.createStubInstance(LanguageManager)
languageManager.sendLanguageServerRequest.resolves(completionDetails)
requestor = new LanguageServiceCompletionsRequestor(languageManager)
})
it("doesn't send the request if the server is incapable", async () => {
languageManager.getCapabilitiesForLanguage.resolves({
completionProvider: { resolveProvider: false },
})
const returnedDetails = await requestor.getCompletionDetails(
"mocklang",
"mockpath",
completionItem as any,
)
sinon.assert.calledWithExactly(languageManager.getCapabilitiesForLanguage, "mocklang")
sinon.assert.notCalled(languageManager.sendLanguageServerRequest)
assert.strictEqual(returnedDetails, completionItem)
})
it("requests completion details if server is capable", async () => {
languageManager.getCapabilitiesForLanguage.resolves({
completionProvider: { resolveProvider: true },
})
const returnedDetails = await requestor.getCompletionDetails(
"mocklang",
"mockpath",
completionItem as any,
)
sinon.assert.calledWithExactly(languageManager.getCapabilitiesForLanguage, "mocklang")
sinon.assert.calledWithExactly(
languageManager.sendLanguageServerRequest,
"mocklang",
"mockpath",
"completionItem/resolve",
completionItem,
)
assert.strictEqual(returnedDetails, completionDetails)
})
})
})

View File

@ -928,7 +928,7 @@
"@types/redux-mock-store": "^1.0.0",
"@types/rimraf": "^2.0.2",
"@types/shelljs": "^0.7.7",
"@types/sinon": "1.16.32",
"@types/sinon": "5.0.5",
"@types/webgl2": "^0.0.3",
"autoprefixer": "6.4.0",
"aws-sdk": "^2.202.0",
@ -992,7 +992,7 @@
"remap-istanbul": "^0.10.1",
"reselect": "3.0.1",
"rxjs": "5.5.8",
"sinon": "1.17.6",
"sinon": "5.0.5",
"spectron": "^3.8.0",
"style-loader": "0.18.2",
"sudo-prompt": "7.1.1",

1890
yarn.lock

File diff suppressed because it is too large Load Diff