support referencesCodeLens.showOnAllFunctions

Make codeLens same as VScode
This commit is contained in:
Qiming Zhao 2022-01-13 00:29:12 +08:00
parent 4fa163e554
commit d10bab9072
No known key found for this signature in database
GPG key ID: 9722CD0E8D4DCB8C
7 changed files with 151 additions and 127 deletions

View file

@ -145,6 +145,7 @@ for guide of coc.nvim's configuration.
implementations, default: `true`
- `typescript.referencesCodeLens.enable`:Enable codeLens for references,
default: `true`
- `typescript.referencesCodeLens.showOnAllFunctions`: Enable/disable references CodeLens on all functions in typescript files. Default: `false`
- `typescript.preferences.importModuleSpecifier` default: `"auto"`
- `typescript.preferences.importModuleSpecifierEnding` default: `"auto"`
- `typescript.preferences.quoteStyle` default: `"single"`
@ -209,6 +210,7 @@ for guide of coc.nvim's configuration.
- `javascript.updateImportsOnFileMove.enable` default: `true`
- `javascript.implementationsCodeLens.enable` default: `true`
- `javascript.referencesCodeLens.enable` default: `true`
- `javascript.referencesCodeLens.showOnAllFunctions`: Enable/disable references CodeLens on all functions in JavaScript files default: `false`
- `javascript.preferences.importModuleSpecifier` default: `"auto"`
- `javascript.preferences.importModuleSpecifierEnding` default: `"auto"`
- `javascript.preferences.quoteStyle` default: `"single"`

View file

@ -293,6 +293,12 @@
"default": true,
"description": "Enable codeLens for references"
},
"typescript.referencesCodeLens.showOnAllFunctions": {
"type": "boolean",
"default": false,
"description": "Enable/disable references CodeLens on all functions in typescript files.",
"scope": "window"
},
"typescript.preferences.importModuleSpecifier": {
"type": "string",
"default": "shortest",
@ -547,6 +553,12 @@
"type": "boolean",
"default": true
},
"javascript.referencesCodeLens.showOnAllFunctions": {
"type": "boolean",
"default": false,
"description": "Enable/disable references CodeLens on all functions in JavaScript files.",
"scope": "window"
},
"javascript.preferences.importModuleSpecifier": {
"type": "string",
"default": "shortest",

View file

@ -46,7 +46,8 @@ export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider
public constructor(
protected client: ITypeScriptServiceClient,
private cachedResponse: CachedNavTreeResponse
private cachedResponse: CachedNavTreeResponse,
protected modeId: string
) {}
public get onDidChangeCodeLenses(): Event<void> {
@ -116,38 +117,31 @@ export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider
)
}
}
protected getSymbolRange(
document: TextDocument,
item: Proto.NavigationTree
): Range | null {
if (!item) {
return null
}
}
// TS 3.0+ provides a span for just the symbol
if ((item as any).nameSpan) {
return typeConverters.Range.fromTextSpan((item as any).nameSpan)
}
export function getSymbolRange(
document: TextDocument,
item: Proto.NavigationTree
): Range | null {
if (item.nameSpan) {
return typeConverters.Range.fromTextSpan(item.nameSpan)
}
// In older versions, we have to calculate this manually. See #23924
const span = item.spans && item.spans[0]
if (!span) {
return null
}
// In older versions, we have to calculate this manually. See #23924
const span = item.spans && item.spans[0]
if (!span) {
return null
}
const range = typeConverters.Range.fromTextSpan(span)
const text = document.getText(range)
const range = typeConverters.Range.fromTextSpan(span)
const text = document.getText(range)
const identifierMatch = new RegExp(
`^(.*?(\\b|\\W))${escapeRegExp(item.text || '')}(\\b|\\W)`,
'gm'
)
const match = identifierMatch.exec(text)
const prefixLength = match ? match.index + match[1].length : 0
const startOffset = document.offsetAt(range.start) + prefixLength
return {
start: document.positionAt(startOffset),
end: document.positionAt(startOffset + item.text.length)
}
const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${escapeRegExp(item.text || '')}(\\b|\\W)`, 'gm')
const match = identifierMatch.exec(text)
const prefixLength = match ? match.index + match[1].length : 0
const startOffset = document.offsetAt(range.start) + prefixLength
return {
start: document.positionAt(startOffset),
end: document.positionAt(startOffset + item.text.length)
}
}

View file

@ -7,7 +7,7 @@ import { TextDocument } from 'coc.nvim'
import * as Proto from '../protocol'
import * as PConst from '../protocol.const'
import * as typeConverters from '../utils/typeConverters'
import { TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'
import { TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider'
export default class TypeScriptImplementationsCodeLensProvider extends TypeScriptBaseCodeLensProvider {
public async resolveCodeLens(
@ -21,43 +21,39 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
filepath,
codeLens.range.start
)
try {
const response = await this.client.execute('implementation', args, token, { lowPriority: true })
if (response && response.type == 'response' && response.body) {
const locations = response.body
.map(reference => {
return {
uri: this.client.toResource(reference.file),
range: {
start: typeConverters.Position.fromLocation(reference.start),
end: {
line: reference.start.line,
character: 0
}
}
}
})
// Exclude original from implementations
.filter(
location => !(
location.uri.toString() === uri &&
location.range.start.line === codeLens.range.start.line &&
location.range.start.character ===
codeLens.range.start.character
)
)
codeLens.command = this.getCommand(locations, codeLens)
return codeLens
const response = await this.client.execute('implementation', args, token, { lowPriority: true })
if (response.type !== 'response' || !response.body) {
codeLens.command = {
title: response.type === 'cancelled'
? 'cancelled'
: 'could not determine implementation',
command: ''
}
} catch {
// noop
}
codeLens.command = {
title: '0 implementations',
command: ''
return codeLens
}
const locations = response.body
.map(reference => {
return {
uri: this.client.toResource(reference.file),
range: {
start: typeConverters.Position.fromLocation(reference.start),
end: {
line: reference.start.line,
character: 0
}
}
}
})
// Exclude original from implementations
.filter(
location => !(
location.uri.toString() === uri &&
location.range.start.line === codeLens.range.start.line &&
location.range.start.character ===
codeLens.range.start.character
)
)
codeLens.command = this.getCommand(locations, codeLens)
return codeLens
}
@ -84,7 +80,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
): Range | null {
switch (item.kind) {
case PConst.Kind.interface:
return super.getSymbolRange(document, item)
return getSymbolRange(document, item)
case PConst.Kind.class:
case PConst.Kind.method:
@ -92,7 +88,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
case PConst.Kind.memberGetAccessor:
case PConst.Kind.memberSetAccessor:
if (item.kindModifiers.match(/\babstract\b/g)) {
return super.getSymbolRange(document, item)
return getSymbolRange(document, item)
}
break
}

View file

@ -2,15 +2,16 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken, CodeLens, Range } from 'vscode-languageserver-protocol'
import { TextDocument } from 'coc.nvim'
import { CancellationToken, Position, Range } from 'vscode-languageserver-protocol'
import { TextDocument, workspace, CodeLens } from 'coc.nvim'
import { ExecutionTarget } from '../typescriptService'
import * as Proto from '../protocol'
import * as PConst from '../protocol.const'
import * as typeConverters from '../utils/typeConverters'
import { TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'
import { TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider'
export default class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvider {
public resolveCodeLens(
public async resolveCodeLens(
codeLens: CodeLens,
token: CancellationToken
): Promise<CodeLens> {
@ -20,47 +21,35 @@ export default class TypeScriptReferencesCodeLensProvider extends TypeScriptBase
filepath,
codeLens.range.start
)
return this.client
.execute('references', args, token, {
lowPriority: true
})
.then(response => {
if (!response || response.type != 'response' || !response.body) {
throw codeLens
}
let response = await this.client.execute('references', args, token, {
lowPriority: true,
executionTarget: ExecutionTarget.Semantic
})
if (!response || response.type != 'response' || !response.body) {
codeLens.command = {
title: response.type === 'cancelled'
? 'cancelled'
: 'could not determine references',
command: ''
}
return codeLens
}
const locations = response.body.refs
.map(reference =>
typeConverters.Location.fromTextSpan(
this.client.toResource(reference.file),
reference
)
)
.filter(
location =>
// Exclude original definition from references
!(
location.uri.toString() === uri &&
location.range.start.line === codeLens.range.start.line &&
location.range.start.character ===
codeLens.range.start.character
)
)
const locations = response.body.refs
.filter(reference => !reference.isDefinition)
.map(reference =>
typeConverters.Location.fromTextSpan(
this.client.toResource(reference.file),
reference
)
)
codeLens.command = {
title: locations.length === 1 ? '1 reference' : `${locations.length} references`,
command: locations.length ? 'editor.action.showReferences' : '',
arguments: [uri, codeLens.range.start, locations]
}
return codeLens
})
.catch(() => {
codeLens.command = {
title: '0 references',
command: ''
}
return codeLens
})
codeLens.command = {
title: locations.length === 1 ? '1 reference' : `${locations.length} references`,
command: locations.length ? 'editor.action.showReferences' : '',
arguments: [uri, codeLens.range.start, locations]
}
return codeLens
}
protected extractSymbol(
@ -69,37 +58,68 @@ export default class TypeScriptReferencesCodeLensProvider extends TypeScriptBase
parent: Proto.NavigationTree | null
): Range | null {
if (parent && parent.kind === PConst.Kind.enum) {
return super.getSymbolRange(document, item)
return getSymbolRange(document, item)
}
switch (item.kind) {
case PConst.Kind.function: {
const showOnAllFunctions = workspace.getConfiguration(this.modeId).get<boolean>('referencesCodeLens.showOnAllFunctions')
if (showOnAllFunctions) {
return getSymbolRange(document, item)
}
}
// fallthrough
case PConst.Kind.const:
case PConst.Kind.let:
case PConst.Kind.variable:
case PConst.Kind.function:
// Only show references for exported variables
if (!item.kindModifiers.match(/\bexport\b/)) {
break
if (/\bexport\b/.test(item.kindModifiers)) {
return getSymbolRange(document, item)
}
// fallthrough
break
case PConst.Kind.class:
if (item.text === '<class>') {
break
}
// fallthrough
return getSymbolRange(document, item)
case PConst.Kind.method:
case PConst.Kind.memberVariable:
case PConst.Kind.memberGetAccessor:
case PConst.Kind.memberSetAccessor:
case PConst.Kind.constructorImplementation:
case PConst.Kind.interface:
case PConst.Kind.type:
case PConst.Kind.enum:
return super.getSymbolRange(document, item)
return getSymbolRange(document, item)
case PConst.Kind.method:
case PConst.Kind.memberGetAccessor:
case PConst.Kind.memberSetAccessor:
case PConst.Kind.constructorImplementation:
case PConst.Kind.memberVariable:
// Don't show if child and parent have same start
// For https://github.com/microsoft/vscode/issues/90396
if (parent &&
comparePosition(typeConverters.Position.fromLocation(parent.spans[0].start), typeConverters.Position.fromLocation(item.spans[0].start)) == 0
) {
return null
}
// Only show if parent is a class type object (not a literal)
switch (parent?.kind) {
case PConst.Kind.class:
case PConst.Kind.interface:
case PConst.Kind.type:
return getSymbolRange(document, item)
}
break
}
return null
}
}
export function comparePosition(position: Position, other: Position): number {
if (position.line > other.line) return 1
if (other.line == position.line && position.character > other.character) return 1
if (other.line == position.line && position.character == other.character) return 0
return -1
}

View file

@ -149,10 +149,10 @@ export default class LanguageProvider {
'tsserver', [CodeActionKind.QuickFix]))
let cachedResponse = new CachedNavTreeResponse()
if (this.client.apiVersion.gte(API.v206) && conf.get<boolean>('referencesCodeLens.enable')) {
this._register(languages.registerCodeLensProvider(languageIds, new ReferencesCodeLensProvider(client, cachedResponse)))
this._register(languages.registerCodeLensProvider(languageIds, new ReferencesCodeLensProvider(client, cachedResponse, this.description.id)))
}
if (this.client.apiVersion.gte(API.v220) && conf.get<boolean>('implementationsCodeLens.enable')) {
this._register(languages.registerCodeLensProvider(languageIds, new ImplementationsCodeLensProvider(client, cachedResponse)))
this._register(languages.registerCodeLensProvider(languageIds, new ImplementationsCodeLensProvider(client, cachedResponse, this.description.id)))
}
if (this.client.apiVersion.gte(API.v350)) {
this._register(languages.registerSelectionRangeProvider(languageIds, new SmartSelection(this.client)))

View file

@ -34,7 +34,7 @@ export interface TypeScriptServerPlugin {
readonly languages: string[]
}
export enum ExectuionTarget {
export enum ExecutionTarget {
Semantic,
Syntax
}
@ -43,7 +43,7 @@ export type ExecConfig = {
readonly lowPriority?: boolean
readonly nonRecoverable?: boolean
readonly cancelOnResourceChange?: string
readonly executionTarget?: ExectuionTarget
readonly executionTarget?: ExecutionTarget
}
export interface TypeScriptRequestTypes {