Skip to content

Commit

Permalink
avoid cache cancelled response for CodeLens
Browse files Browse the repository at this point in the history
  • Loading branch information
chemzqm committed Nov 22, 2022
1 parent f723d13 commit 91b13bc
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 47 deletions.
50 changes: 6 additions & 44 deletions src/server/features/baseCodeLensProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,21 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TextDocument } from 'coc.nvim'
import { CodeLensProvider } from 'coc.nvim'
import { CancellationToken, CodeLens, Emitter, Event, Range } from 'vscode-languageserver-protocol'
import { CodeLensProvider, TextDocument } from 'coc.nvim'
import { CancellationToken, CodeLens, Range } from 'vscode-languageserver-protocol'
import * as Proto from '../protocol'
import { CachedResponse } from '../tsServer/cachedResponse'
import { ITypeScriptServiceClient } from '../typescriptService'
import { escapeRegExp } from '../utils/regexp'
import * as typeConverters from '../utils/typeConverters'

export class CachedNavTreeResponse {
private response?: Promise<Proto.NavTreeResponse>
private version = -1
private document = ''

public execute(document: TextDocument, f: () => Promise<Proto.NavTreeResponse>): Promise<Proto.NavTreeResponse> {
if (this.matches(document)) {
return this.response
}

return this.update(document, f())
}

private matches(document: TextDocument): boolean {
return (
this.version === document.version &&
this.document === document.uri.toString()
)
}

private update(
document: TextDocument,
response: Promise<Proto.NavTreeResponse>
): Promise<Proto.NavTreeResponse> {
this.response = response
this.version = document.version
this.document = document.uri.toString()
return response
}
}

export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider {
private onDidChangeCodeLensesEmitter = new Emitter<void>()

public constructor(
protected client: ITypeScriptServiceClient,
private cachedResponse: CachedNavTreeResponse,
private cachedResponse: CachedResponse<Proto.NavTreeResponse>,
protected modeId: string
) {}

public get onDidChangeCodeLenses(): Event<void> {
return this.onDidChangeCodeLensesEmitter.event
}

public async provideCodeLenses(
document: TextDocument,
token: CancellationToken
Expand All @@ -65,12 +28,11 @@ export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider

try {
const response = await this.cachedResponse.execute(document, () =>
this.client.execute('navtree', { file: filepath }, token) as any
this.client.execute('navtree', { file: filepath }, token)
)
if (!response) {
if (response.type !== 'response') {
return []
}

const tree = response.body
const referenceableSpans: Range[] = []
if (tree && tree.childItems) {
Expand Down
5 changes: 2 additions & 3 deletions src/server/languageProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import { CodeActionKind, Diagnostic, DiagnosticSeverity, Disposable, disposeAll, DocumentFilter, languages, TextDocument, Uri, workspace } from 'coc.nvim'
import path from 'path'
import * as fileSchemes from '../utils/fileSchemes'
import { CachedNavTreeResponse } from './features/baseCodeLensProvider'
import CallHierarchyProvider from './features/callHierarchy'
import CompletionItemProvider from './features/completionItemProvider'
import DefinitionProvider from './features/definitionProvider'
Expand All @@ -32,8 +31,8 @@ import { TypeScriptDocumentSemanticTokensProvider } from './features/semanticTok
import SignatureHelpProvider from './features/signatureHelp'
import SmartSelection from './features/smartSelect'
import TagClosing from './features/tagClosing'
import UpdateImportsOnFileRenameHandler from './features/updatePathOnRename'
import { OrganizeImportsCodeActionProvider } from './organizeImports'
import { CachedResponse } from './tsServer/cachedResponse'
import { ClientCapability } from './typescriptService'
import TypeScriptServiceClient from './typescriptServiceClient'
import API from './utils/api'
Expand Down Expand Up @@ -209,7 +208,7 @@ export default class LanguageProvider {
'tsserver', [CodeActionKind.QuickFix]))

if (hasSemantic) {
let cachedResponse = new CachedNavTreeResponse()
let cachedResponse = new CachedResponse()
if (this.client.apiVersion.gte(API.v206) && conf.get<boolean>('referencesCodeLens.enabled')) {
this._register(languages.registerCodeLensProvider(documentSelector.semantic, new ReferencesCodeLensProvider(client, cachedResponse, this.description.id)))
}
Expand Down
48 changes: 48 additions & 0 deletions src/server/tsServer/cachedResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { TextDocument } from 'coc.nvim'
import type * as Proto from '../protocol'
import { ServerResponse } from '../typescriptService'

type Resolve<T extends Proto.Response> = () => Promise<ServerResponse.Response<T>>

/**
* Caches a class of TS Server request based on document.
*/
export class CachedResponse<T extends Proto.Response> {
private response?: Promise<ServerResponse.Response<T>>
private version: number = -1;
private document: string = '';

/**
* Execute a request. May return cached value or resolve the new value
*
* Caller must ensure that all input `resolve` functions return equivalent results (keyed only off of document).
*/
public execute(
document: TextDocument,
resolve: Resolve<T>
): Promise<ServerResponse.Response<T>> {
if (this.response && this.matches(document)) {
// Chain so that on cancellation we fall back to the next resolve
return this.response = this.response.then(result => result.type === 'cancelled' ? resolve() : result)
}
return this.reset(document, resolve)
}

private matches(document: TextDocument): boolean {
return this.version === document.version && this.document === document.uri.toString()
}

private async reset(
document: TextDocument,
resolve: Resolve<T>
): Promise<ServerResponse.Response<T>> {
this.version = document.version
this.document = document.uri.toString()
return this.response = resolve()
}
}

0 comments on commit 91b13bc

Please sign in to comment.