Skip to content

Commit

Permalink
fix(language-client): fix connection not disposed
Browse files Browse the repository at this point in the history
  • Loading branch information
chemzqm committed Oct 15, 2022
1 parent 25ba812 commit 3da02fa
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 58 deletions.
6 changes: 3 additions & 3 deletions src/__tests__/client/configuration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ describe('pull configuration feature', () => {
return config
}
await client.sendNotification('pull1')
await helper.wait(50)
expect(config).toBeDefined()
expect(config.length).toBe(3)
await helper.waitValue(() => {
return config?.length
}, 3)
expect(config[1]).toBeNull()
expect(config[0].proxy).toBeDefined()
expect(config[2]).toBeNull()
Expand Down
112 changes: 58 additions & 54 deletions src/__tests__/client/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,54 +485,6 @@ describe('Client integration', () => {
await client.stop()
})

it('should handle error on initialize', async () => {
async function startServer(handler: InitializationFailedHandler | undefined, key = 'throwError'): Promise<lsclient.LanguageClient> {
let clientOptions: lsclient.LanguageClientOptions = {
initializationFailedHandler: handler,
initializationOptions: {
[key]: true
}
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.ipc,
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
await client.start()
return client
}
let n = 0
try {
let client = await startServer(() => {
n++
return n == 1
})
await client.stop()
} catch (e) {
// ignore
}
try {
let client = await startServer(undefined)
await client.stop()
} catch (e) {
// ignore
}
try {
let client = await startServer(undefined, 'normalThrow')
await client.stop()
} catch (e) {
// ignore
}
try {
let client = await startServer(undefined, 'utf8')
void client.stop()
await client.stop()
} catch (e) {
// ignore
}
})

it('should separate diagnostics', async () => {
async function startServer(disable?: boolean, handleDiagnostics?: (uri: string, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => void): Promise<lsclient.LanguageClient> {
let clientOptions: lsclient.LanguageClientOptions = {
Expand All @@ -554,15 +506,16 @@ describe('Client integration', () => {
}
let client = await startServer()
await client.sendNotification('diagnostics')
await helper.wait(30)
let collection = client.diagnostics
let res = collection.get('lsptest:/2')
expect(res.length).toBe(2)
await helper.waitValue(() => {
let collection = client.diagnostics
let res = collection.get('lsptest:/2')
return res.length
}, 2)
await client.stop()
client = await startServer(true)
await client.sendNotification('diagnostics')
await helper.wait(30)
collection = client.diagnostics
let collection = client.diagnostics
expect(collection).toBeUndefined()
await client.stop()
let called = false
Expand Down Expand Up @@ -594,7 +547,6 @@ describe('Client integration', () => {
res = p
})
await client.start()
await helper.wait(10)
await client.sendNotification('edits')
await helper.wait(50)
expect(res).toBeDefined()
Expand Down Expand Up @@ -623,6 +575,58 @@ describe('Client integration', () => {
expect(res).toEqual({ applied: true })
await client.stop()
})

it('should handle error on initialize', async () => {
let client: lsclient.LanguageClient
async function startServer(handler: InitializationFailedHandler | undefined, key = 'throwError'): Promise<lsclient.LanguageClient> {
let clientOptions: lsclient.LanguageClientOptions = {
initializationFailedHandler: handler,
initializationOptions: {
[key]: true
}
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.ipc,
}
client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
await client.start()
return client
}
let n = 0
let fn = async () => {
await startServer(() => {
n++
return n == 1
})
}
await expect(fn()).rejects.toThrow(Error)
await helper.waitValue(() => {
return n
}, 5)
fn = async () => {
await startServer(undefined, 'normalThrow')
}
await expect(fn()).rejects.toThrow(Error)
fn = async () => {
await startServer(undefined, 'utf8')
}
await expect(fn()).rejects.toThrow(Error)
fn = async () => {
await client.stop()
}
await expect(fn()).rejects.toThrow(Error)
let spy = jest.spyOn(window, 'showErrorMessage').mockImplementation(() => {
return undefined
})
fn = async () => {
await startServer(undefined)
}
await expect(fn()).rejects.toThrow(Error)
spy.mockRestore()
await helper.wait(100)
})
})

describe('SettingMonitor', () => {
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/client/server/eventServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ documents.listen(connection)
connection.onInitialize((params) => {
options = params.initializationOptions || {}
if (options.throwError) {
setTimeout(() => {
process.exit()
}, 10)
return new ResponseError(1, 'message', {retry: true})
}
if (options.normalThrow) {
setTimeout(() => {
process.exit()
}, 10)
throw new Error('message')
}
if (options.utf8) {
Expand Down
6 changes: 5 additions & 1 deletion src/__tests__/client/textSynchronization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,11 @@ describe('TextDocumentSynchronization', () => {
await client.sendNotification('registerDocumentSync')
await nvim.command('edit x.vim')
let doc = await workspace.document
await helper.wait(30)

let feature = client.getFeature(DidChangeTextDocumentNotification.method)
await helper.waitValue(() => {
return feature.getProvider(doc.textDocument) != null
}, true)
let provider = feature.getProvider(doc.textDocument)
let changes: TextDocumentContentChange[] = [{
range: Range.create(0, 0, 0, 0),
Expand All @@ -191,6 +194,7 @@ describe('TextDocumentSynchronization', () => {
await provider.send({ contentChanges: changes, textDocument: { uri: doc.uri, version: doc.version }, bufnr: doc.bufnr } as any)
let res = await client.sendRequest('getLastChange') as any
expect(res.text).toBe('\n')
await client.stop()
})
})

Expand Down
10 changes: 10 additions & 0 deletions src/language-client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,10 @@ export abstract class BaseLanguageClient implements FeatureClient<Middleware, La
try {
const result = await connection.initialize(initParams)
if (result.capabilities.positionEncoding !== undefined && result.capabilities.positionEncoding !== PositionEncodingKind.UTF16) {
await connection.shutdown()
await connection.exit()
connection.end()
connection.dispose()
throw new Error(`Unsupported position encoding (${result.capabilities.positionEncoding}) received from server ${this.name}`)
}

Expand Down Expand Up @@ -1086,6 +1090,12 @@ export abstract class BaseLanguageClient implements FeatureClient<Middleware, La
if (this._clientOptions.initializationFailedHandler) {
cb(this._clientOptions.initializationFailedHandler(error))
} else if (error instanceof ResponseError && error.data && error.data.retry) {
if (this._connection) {
let connection = this._connection
connection.end()
this._connection.dispose()
this._connection = null
}
void window.showErrorMessage(error.message, { title: 'Retry', id: 'retry' }).then(item => {
cb(item && item.id === 'retry')
})
Expand Down

0 comments on commit 3da02fa

Please sign in to comment.