parent
c4ae2c2fbf
commit
de0fa5c6a2
10 changed files with 207 additions and 255 deletions
|
@ -30,6 +30,8 @@
|
|||
"onLanguage:typescript.jsx",
|
||||
"onLanguage:typescriptreact",
|
||||
"onLanguage:jsx-tags",
|
||||
"onLanguage:jsonc",
|
||||
"onCommand:_typescript.configurePlugin",
|
||||
"onCommand:typescript.reloadProjects",
|
||||
"onCommand:javascript.reloadProjects",
|
||||
"onCommand:javascript.goToProjectConfig",
|
||||
|
|
49
src/index.ts
49
src/index.ts
|
@ -9,40 +9,39 @@ interface API {
|
|||
}
|
||||
|
||||
export async function activate(context: ExtensionContext): Promise<API> {
|
||||
let { subscriptions } = context
|
||||
let { subscriptions, logger } = context
|
||||
const config = workspace.getConfiguration().get<any>('tsserver', {})
|
||||
if (!config.enable) return
|
||||
const pluginManager = new PluginManager()
|
||||
const service = new TsserverService(pluginManager)
|
||||
|
||||
subscriptions.push(
|
||||
(services as any).regist(service)
|
||||
)
|
||||
|
||||
await service.start()
|
||||
|
||||
function registCommand(cmd: Command): void {
|
||||
let { id, execute } = cmd
|
||||
subscriptions.push(commands.registerCommand(id as string, execute, cmd))
|
||||
}
|
||||
|
||||
registCommand(new AutoFixCommand(service.clientHost))
|
||||
registCommand(new ReloadProjectsCommand(service.clientHost))
|
||||
registCommand(new OpenTsServerLogCommand(service.clientHost))
|
||||
registCommand(new TypeScriptGoToProjectConfigCommand(service.clientHost))
|
||||
registCommand(new OrganizeImportsCommand(service.clientHost.serviceClient))
|
||||
registCommand(new ConfigurePluginCommand(pluginManager))
|
||||
registCommand(commands.register({
|
||||
id: 'tsserver.restart',
|
||||
execute: (): void => {
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
service.stop().then(() => {
|
||||
setTimeout(() => {
|
||||
service.restart()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
}))
|
||||
registCommand(new AutoFixCommand(service))
|
||||
registCommand(new ReloadProjectsCommand(service))
|
||||
registCommand(new OpenTsServerLogCommand(service))
|
||||
registCommand(new TypeScriptGoToProjectConfigCommand(service))
|
||||
registCommand(new OrganizeImportsCommand(service))
|
||||
|
||||
service.start().then(() => {
|
||||
subscriptions.push(services.regist(service))
|
||||
registCommand(commands.register({
|
||||
id: 'tsserver.restart',
|
||||
execute: (): void => {
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
service.stop().then(() => {
|
||||
setTimeout(() => {
|
||||
service.restart()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
}))
|
||||
}, e => {
|
||||
logger.error(`Error on service start:`, e)
|
||||
})
|
||||
|
||||
return {
|
||||
configurePlugin: (pluginId: string, configuration: {}): void => {
|
||||
pluginManager.setConfiguration(pluginId, configuration)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Uri as URI, diagnosticManager, workspace, commands } from 'coc.nvim'
|
||||
import { Uri as URI, diagnosticManager, workspace, commands, ServiceStat } from 'coc.nvim'
|
||||
import { CancellationToken, Diagnostic } from 'vscode-languageserver-protocol'
|
||||
import * as Proto from './protocol'
|
||||
import TypeScriptServiceClientHost from './typescriptServiceClientHost'
|
||||
|
@ -7,6 +7,7 @@ import { TextEdit, Range } from 'vscode-languageserver-types'
|
|||
import { installModules } from './utils/modules'
|
||||
import { nodeModules } from './utils/helper'
|
||||
import { PluginManager } from '../utils/plugins'
|
||||
import TsserverService from '../server'
|
||||
|
||||
export interface Command {
|
||||
readonly id: string | string[]
|
||||
|
@ -17,11 +18,12 @@ export class ReloadProjectsCommand implements Command {
|
|||
public readonly id = 'tsserver.reloadProjects'
|
||||
|
||||
public constructor(
|
||||
private readonly client: TypeScriptServiceClientHost
|
||||
private readonly service: TsserverService
|
||||
) { }
|
||||
|
||||
public execute(): void {
|
||||
this.client.reloadProjects()
|
||||
public async execute(): Promise<void> {
|
||||
let client = await this.service.getClientHost()
|
||||
client.reloadProjects()
|
||||
workspace.showMessage('projects reloaded')
|
||||
}
|
||||
}
|
||||
|
@ -30,11 +32,12 @@ export class OpenTsServerLogCommand implements Command {
|
|||
public readonly id = 'tsserver.openTsServerLog'
|
||||
|
||||
public constructor(
|
||||
private readonly client: TypeScriptServiceClientHost
|
||||
private readonly service: TsserverService
|
||||
) { }
|
||||
|
||||
public execute(): void {
|
||||
this.client.serviceClient.openTsServerLogFile() // tslint:disable-line
|
||||
public async execute(): Promise<void> {
|
||||
let client = await this.service.getClientHost()
|
||||
client.serviceClient.openTsServerLogFile() // tslint:disable-line
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,17 +45,18 @@ export class TypeScriptGoToProjectConfigCommand implements Command {
|
|||
public readonly id = 'tsserver.goToProjectConfig'
|
||||
|
||||
public constructor(
|
||||
private readonly client: TypeScriptServiceClientHost
|
||||
private readonly service: TsserverService
|
||||
) { }
|
||||
|
||||
public async execute(): Promise<void> {
|
||||
let client = await this.service.getClientHost()
|
||||
let doc = await workspace.document
|
||||
let { languageId } = doc.textDocument
|
||||
if (this.client.serviceClient.modeIds.indexOf(languageId) == -1) {
|
||||
workspace.showMessage(`Could not determine TypeScript or JavaScript project. Unsupported file type: ${languageId}`, 'warning')
|
||||
if (client.serviceClient.modeIds.indexOf(languageId) == -1) {
|
||||
throw new Error(`Could not determine TypeScript or JavaScript project. Unsupported file type: ${languageId}`)
|
||||
return
|
||||
}
|
||||
await goToProjectConfig(this.client, doc.uri)
|
||||
await goToProjectConfig(client, doc.uri)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,13 +73,11 @@ async function goToProjectConfig(clientHost: TypeScriptServiceClientHost, uri: s
|
|||
workspace.showMessage('Could not determine TypeScript or JavaScript project.', 'warning')
|
||||
return
|
||||
}
|
||||
|
||||
const { configFileName } = res.body
|
||||
if (configFileName && !isImplicitProjectConfigFile(configFileName)) {
|
||||
await workspace.openResource(URI.file(configFileName).toString())
|
||||
return
|
||||
}
|
||||
|
||||
workspace.showMessage('Config file not found', 'warning')
|
||||
}
|
||||
|
||||
|
@ -92,18 +94,22 @@ const autoFixableDiagnosticCodes = new Set<number>([
|
|||
export class AutoFixCommand implements Command {
|
||||
public readonly id = 'tsserver.executeAutofix'
|
||||
|
||||
constructor(private client: TypeScriptServiceClientHost) {
|
||||
constructor(private service: TsserverService) {
|
||||
}
|
||||
|
||||
public async execute(): Promise<void> {
|
||||
let document = await workspace.document
|
||||
let { uri } = document
|
||||
let handles = await this.client.handles(uri)
|
||||
if (!handles) {
|
||||
workspace.showMessage(`Document ${uri} is not handled by tsserver.`, 'warning')
|
||||
if (this.service.state != ServiceStat.Running) {
|
||||
throw new Error('service not running')
|
||||
return
|
||||
}
|
||||
let file = this.client.serviceClient.toPath(document.uri)
|
||||
let client = await this.service.getClientHost()
|
||||
let document = await workspace.document
|
||||
let handles = await client.handles(document.textDocument)
|
||||
if (!handles) {
|
||||
throw new Error(`Document ${document.uri} is not handled by tsserver.`)
|
||||
return
|
||||
}
|
||||
let file = client.serviceClient.toPath(document.uri)
|
||||
let diagnostics = diagnosticManager.getDiagnostics(document.uri)
|
||||
let missingDiagnostics = diagnostics.filter(o => o.code == 2307)
|
||||
if (missingDiagnostics.length) {
|
||||
|
@ -125,7 +131,6 @@ export class AutoFixCommand implements Command {
|
|||
arr.push(curr)
|
||||
return arr
|
||||
}, [] as Diagnostic[])
|
||||
let client = this.client.serviceClient
|
||||
let edits: TextEdit[] = []
|
||||
let command: string
|
||||
let names: string[] = []
|
||||
|
@ -134,7 +139,7 @@ export class AutoFixCommand implements Command {
|
|||
...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range),
|
||||
errorCodes: [+(diagnostic.code!)]
|
||||
}
|
||||
const response = await client.execute('getCodeFixes', args, CancellationToken.None)
|
||||
const response = await client.serviceClient.execute('getCodeFixes', args, CancellationToken.None)
|
||||
if (response.type !== 'response' || !response.body || response.body.length < 1) {
|
||||
if (diagnostic.code == 2304) {
|
||||
let { range } = diagnostic
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Uri, commands } from 'coc.nvim'
|
||||
import { Command } from 'coc.nvim/lib/commands'
|
||||
import { CodeActionProvider } from 'coc.nvim/lib/provider'
|
||||
import { CancellationToken, CodeAction, CodeActionContext, Range } from 'vscode-languageserver-protocol'
|
||||
import { CancellationToken, CodeAction, CodeActionContext, CodeActionKind, Range } from 'vscode-languageserver-protocol'
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument'
|
||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||
import { installModules } from '../utils/modules'
|
||||
|
@ -35,7 +35,7 @@ export default class InstallModuleProvider implements CodeActionProvider {
|
|||
let { diagnostics } = context
|
||||
let diags = diagnostics.filter(s => s.code == 2307)
|
||||
let names = diags.map(o => {
|
||||
let ms = o.message.match(/module\s'(.+)'\./)
|
||||
let ms = o.message.match(/module\s'(.+)'/)
|
||||
return ms ? ms[1] : null
|
||||
})
|
||||
names = names.filter(s => s != null)
|
||||
|
@ -48,7 +48,7 @@ export default class InstallModuleProvider implements CodeActionProvider {
|
|||
command: InstallModuleCommand.ID,
|
||||
arguments: [document.uri, name]
|
||||
}
|
||||
let codeAction = CodeAction.create(title, command)
|
||||
let codeAction = CodeAction.create(title, command, CodeActionKind.QuickFix)
|
||||
actions.push(codeAction)
|
||||
}
|
||||
return actions
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { disposeAll, StatusBarItem, TaskOptions, Uri, workspace } from 'coc.nvim'
|
||||
import { CommandManager } from 'coc.nvim/lib/commands'
|
||||
import Task from 'coc.nvim/lib/model/task'
|
||||
import { disposeAll, commands, StatusBarItem, TaskOptions, Uri, workspace } from 'coc.nvim'
|
||||
import path from 'path'
|
||||
import { Disposable, Location } from 'vscode-languageserver-protocol'
|
||||
import TypeScriptServiceClient from '../typescriptServiceClient'
|
||||
|
@ -26,16 +24,15 @@ export default class WatchProject implements Disposable {
|
|||
public static readonly id: string = 'tsserver.watchBuild'
|
||||
public static readonly startTexts: string[] = ['Starting compilation in watch mode', 'Starting incremental compilation']
|
||||
private statusItem: StatusBarItem
|
||||
private task: Task
|
||||
private task: any
|
||||
private options: TaskOptions
|
||||
|
||||
public constructor(
|
||||
commandManager: CommandManager,
|
||||
private client: TypeScriptServiceClient
|
||||
) {
|
||||
this.statusItem = workspace.createStatusBarItem(1, { progress: true })
|
||||
let task = this.task = workspace.createTask('TSC')
|
||||
this.disposables.push(commandManager.registerCommand(WatchProject.id, async () => {
|
||||
this.disposables.push(commands.registerCommand(WatchProject.id, async () => {
|
||||
let opts = this.options = await this.getOptions()
|
||||
await this.start(opts)
|
||||
}))
|
||||
|
|
|
@ -33,6 +33,24 @@ export default class TsserverService implements IServiceProvider {
|
|||
return workspace.getConfiguration('tsserver')
|
||||
}
|
||||
|
||||
/**
|
||||
* Get running client host.
|
||||
*/
|
||||
public getClientHost(): Promise<TypeScriptServiceClientHost> {
|
||||
if (this.state == ServiceStat.Running) return Promise.resolve(this.clientHost)
|
||||
this.start()
|
||||
return new Promise((resolve, reject) => {
|
||||
let timer = setTimeout(() => {
|
||||
reject(new Error(`Server not started after 5s`))
|
||||
}, 5000)
|
||||
let disposable = this.onServiceReady(() => {
|
||||
clearTimeout(timer)
|
||||
disposable.dispose()
|
||||
resolve(this.clientHost)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public start(): Promise<void> {
|
||||
if (this.clientHost) return
|
||||
this.state = ServiceStat.Starting
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { commands, DiagnosticKind, disposeAll, languages, Uri, workspace } from 'coc.nvim'
|
||||
import { DiagnosticKind, disposeAll, languages, Uri, workspace } from 'coc.nvim'
|
||||
import path from 'path'
|
||||
import { CodeActionKind, Diagnostic, DiagnosticSeverity, Disposable, TextDocument } from 'vscode-languageserver-protocol'
|
||||
import { CachedNavTreeResponse } from './features/baseCodeLensProvider'
|
||||
|
@ -27,8 +27,6 @@ import RenameProvider from './features/rename'
|
|||
import SignatureHelpProvider from './features/signatureHelp'
|
||||
import SmartSelection from './features/smartSelect'
|
||||
import UpdateImportsOnFileRenameHandler from './features/updatePathOnRename'
|
||||
import WatchBuild from './features/watchBuild'
|
||||
import WorkspaceSymbolProvider from './features/workspaceSymbols'
|
||||
import { OrganizeImportsCodeActionProvider } from './organizeImports'
|
||||
import TypeScriptServiceClient from './typescriptServiceClient'
|
||||
import API from './utils/api'
|
||||
|
@ -48,16 +46,14 @@ export default class LanguageProvider {
|
|||
) {
|
||||
workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables)
|
||||
this.configurationChanged()
|
||||
let initialized = false
|
||||
|
||||
let initialized = false
|
||||
client.onTsServerStarted(async () => { // tslint:disable-line
|
||||
if (!initialized) {
|
||||
initialized = true
|
||||
this.registerProviders(client, typingsStatus)
|
||||
} else {
|
||||
this.client.diagnosticsManager.reInitialize()
|
||||
}
|
||||
})
|
||||
}, null, this.disposables)
|
||||
}
|
||||
|
||||
private configurationChanged(): void {
|
||||
|
@ -69,201 +65,89 @@ export default class LanguageProvider {
|
|||
disposeAll(this.disposables)
|
||||
}
|
||||
|
||||
private _register(disposable: Disposable): void {
|
||||
this.disposables.push(disposable)
|
||||
}
|
||||
|
||||
private registerProviders(
|
||||
client: TypeScriptServiceClient,
|
||||
typingsStatus: TypingsStatus
|
||||
): void {
|
||||
let languageIds = this.description.modeIds
|
||||
|
||||
this.disposables.push(
|
||||
languages.registerCompletionItemProvider(
|
||||
`tsserver-${this.description.id}`,
|
||||
'TSC',
|
||||
languageIds,
|
||||
new CompletionItemProvider(
|
||||
client,
|
||||
typingsStatus,
|
||||
this.fileConfigurationManager,
|
||||
this.description.id
|
||||
),
|
||||
let clientId = `tsserver-${this.description.id}`
|
||||
this._register(
|
||||
languages.registerCompletionItemProvider(clientId, 'TSC', languageIds,
|
||||
new CompletionItemProvider(client, typingsStatus, this.fileConfigurationManager, this.description.id),
|
||||
CompletionItemProvider.triggerCharacters
|
||||
)
|
||||
)
|
||||
|
||||
if (this.client.apiVersion.gte(API.v230)) {
|
||||
this.disposables.push(
|
||||
languages.registerCompletionItemProvider(
|
||||
`${this.description.id}-directive`,
|
||||
'TSC',
|
||||
languageIds,
|
||||
new DirectiveCommentCompletionProvider(
|
||||
client,
|
||||
),
|
||||
['@']
|
||||
)
|
||||
)
|
||||
this._register(languages.registerCompletionItemProvider(
|
||||
`${this.description.id}-directive`,
|
||||
'TSC', languageIds, new DirectiveCommentCompletionProvider(client,), ['@']
|
||||
))
|
||||
}
|
||||
|
||||
let definitionProvider = new DefinitionProvider(client)
|
||||
|
||||
this.disposables.push(
|
||||
languages.registerDefinitionProvider(
|
||||
languageIds,
|
||||
definitionProvider
|
||||
)
|
||||
)
|
||||
|
||||
this.disposables.push(
|
||||
languages.registerTypeDefinitionProvider(
|
||||
languageIds,
|
||||
definitionProvider
|
||||
)
|
||||
)
|
||||
|
||||
this.disposables.push(
|
||||
languages.registerImplementationProvider(
|
||||
languageIds,
|
||||
definitionProvider
|
||||
)
|
||||
)
|
||||
|
||||
this.disposables.push(
|
||||
languages.registerReferencesProvider(
|
||||
languageIds,
|
||||
new ReferenceProvider(client)
|
||||
)
|
||||
)
|
||||
|
||||
this.disposables.push(
|
||||
languages.registerHoverProvider(
|
||||
languageIds,
|
||||
new HoverProvider(client))
|
||||
)
|
||||
|
||||
this.disposables.push(
|
||||
languages.registerDocumentHighlightProvider(languageIds, new DocumentHighlight(this.client))
|
||||
)
|
||||
|
||||
this.disposables.push(
|
||||
languages.registerSignatureHelpProvider(
|
||||
languageIds,
|
||||
new SignatureHelpProvider(client),
|
||||
['(', ',', '<', ')'])
|
||||
)
|
||||
|
||||
this.disposables.push(
|
||||
languages.registerDocumentSymbolProvider(
|
||||
languageIds,
|
||||
new DocumentSymbolProvider(client))
|
||||
)
|
||||
|
||||
if (this.description.id == 'typescript') {
|
||||
this.disposables.push(
|
||||
languages.registerWorkspaceSymbolProvider(
|
||||
new WorkspaceSymbolProvider(client, languageIds))
|
||||
)
|
||||
}
|
||||
|
||||
this.disposables.push(
|
||||
languages.registerRenameProvider(
|
||||
languageIds,
|
||||
new RenameProvider(client, this.fileConfigurationManager))
|
||||
)
|
||||
this._register(languages.registerDefinitionProvider(languageIds, definitionProvider))
|
||||
this._register(languages.registerTypeDefinitionProvider(languageIds, definitionProvider))
|
||||
this._register(languages.registerImplementationProvider(languageIds, definitionProvider))
|
||||
this._register(languages.registerReferencesProvider(languageIds, new ReferenceProvider(client)))
|
||||
this._register(languages.registerHoverProvider(languageIds, new HoverProvider(client)))
|
||||
this._register(languages.registerDocumentHighlightProvider(languageIds, new DocumentHighlight(this.client)))
|
||||
this._register(languages.registerSignatureHelpProvider(languageIds, new SignatureHelpProvider(client), ['(', ',', '<', ')']))
|
||||
this._register(languages.registerDocumentSymbolProvider(languageIds, new DocumentSymbolProvider(client)))
|
||||
this._register(languages.registerRenameProvider(languageIds, new RenameProvider(client, this.fileConfigurationManager)))
|
||||
let formatProvider = new FormattingProvider(client, this.fileConfigurationManager)
|
||||
this.disposables.push(
|
||||
languages.registerDocumentFormatProvider(languageIds, formatProvider)
|
||||
)
|
||||
this.disposables.push(
|
||||
languages.registerDocumentRangeFormatProvider(languageIds, formatProvider)
|
||||
)
|
||||
this.disposables.push(
|
||||
languages.registerOnTypeFormattingEditProvider(languageIds, formatProvider, [';', '}', '\n', String.fromCharCode(27)])
|
||||
)
|
||||
|
||||
// this.disposables.push(
|
||||
// new ProjectError(client, commandManager)
|
||||
// )
|
||||
|
||||
if (this.client.apiVersion.gte(API.v280)) {
|
||||
this.disposables.push(
|
||||
languages.registerFoldingRangeProvider(languageIds, new Folding(this.client))
|
||||
)
|
||||
this.disposables.push(
|
||||
languages.registerCodeActionProvider(languageIds,
|
||||
new OrganizeImportsCodeActionProvider(this.client, this.fileConfigurationManager),
|
||||
`tsserver-${this.description.id}`, [CodeActionKind.SourceOrganizeImports])
|
||||
)
|
||||
}
|
||||
this._register(languages.registerDocumentFormatProvider(languageIds, formatProvider))
|
||||
this._register(languages.registerDocumentRangeFormatProvider(languageIds, formatProvider))
|
||||
this._register(languages.registerOnTypeFormattingEditProvider(languageIds, formatProvider, [';', '}', '\n', String.fromCharCode(27)]))
|
||||
this._register(languages.registerCodeActionProvider(languageIds, new InstallModuleProvider(client), 'tsserver'))
|
||||
|
||||
let { fileConfigurationManager } = this
|
||||
let conf = fileConfigurationManager.getLanguageConfiguration(this.id)
|
||||
|
||||
if (this.client.apiVersion.gte(API.v290)
|
||||
&& conf.get<boolean>('updateImportsOnFileMove.enable')) {
|
||||
this.disposables.push(
|
||||
new UpdateImportsOnFileRenameHandler(client, this.fileConfigurationManager, this.id)
|
||||
)
|
||||
if (['javascript', 'typescript'].includes(this.id)) {
|
||||
if (this.client.apiVersion.gte(API.v290) && conf.get<boolean>('updateImportsOnFileMove.enable')) {
|
||||
this._register(new UpdateImportsOnFileRenameHandler(client, this.fileConfigurationManager, this.id))
|
||||
}
|
||||
}
|
||||
|
||||
if (this.client.apiVersion.gte(API.v280)) {
|
||||
this._register(languages.registerFoldingRangeProvider(languageIds, new Folding(this.client)))
|
||||
this._register(
|
||||
languages.registerCodeActionProvider(languageIds,
|
||||
new OrganizeImportsCodeActionProvider(this.client, this.fileConfigurationManager),
|
||||
'tsserver', [CodeActionKind.SourceOrganizeImports])
|
||||
)
|
||||
}
|
||||
if (this.client.apiVersion.gte(API.v240)) {
|
||||
this.disposables.push(
|
||||
this._register(
|
||||
languages.registerCodeActionProvider(
|
||||
languageIds,
|
||||
new RefactorProvider(client, this.fileConfigurationManager),
|
||||
'tsserver',
|
||||
[CodeActionKind.Refactor]))
|
||||
}
|
||||
|
||||
this.disposables.push(
|
||||
this._register(
|
||||
languages.registerCodeActionProvider(
|
||||
languageIds,
|
||||
new InstallModuleProvider(client),
|
||||
'tsserver')
|
||||
)
|
||||
|
||||
this.disposables.push(
|
||||
languageIds, new QuickfixProvider(client, this.fileConfigurationManager),
|
||||
'tsserver', [CodeActionKind.QuickFix]))
|
||||
this._register(
|
||||
languages.registerCodeActionProvider(
|
||||
languageIds,
|
||||
new QuickfixProvider(client, this.fileConfigurationManager),
|
||||
'tsserver',
|
||||
[CodeActionKind.QuickFix]))
|
||||
|
||||
this.disposables.push(
|
||||
languages.registerCodeActionProvider(
|
||||
languageIds,
|
||||
new ImportfixProvider(this.client.bufferSyncSupport),
|
||||
'tsserver',
|
||||
[CodeActionKind.QuickFix]))
|
||||
languageIds, new ImportfixProvider(this.client.bufferSyncSupport),
|
||||
'tsserver', [CodeActionKind.QuickFix]))
|
||||
let cachedResponse = new CachedNavTreeResponse()
|
||||
if (this.client.apiVersion.gte(API.v206)
|
||||
&& conf.get<boolean>('referencesCodeLens.enable')) {
|
||||
this.disposables.push(
|
||||
languages.registerCodeLensProvider(
|
||||
languageIds,
|
||||
new ReferencesCodeLensProvider(client, cachedResponse)))
|
||||
if (this.client.apiVersion.gte(API.v206) && conf.get<boolean>('referencesCodeLens.enable')) {
|
||||
this._register(languages.registerCodeLensProvider(languageIds, new ReferencesCodeLensProvider(client, cachedResponse)))
|
||||
}
|
||||
|
||||
if (this.client.apiVersion.gte(API.v220)
|
||||
&& conf.get<boolean>('implementationsCodeLens.enable')) {
|
||||
this.disposables.push(
|
||||
languages.registerCodeLensProvider(
|
||||
languageIds,
|
||||
new ImplementationsCodeLensProvider(client, cachedResponse)))
|
||||
if (this.client.apiVersion.gte(API.v220) && conf.get<boolean>('implementationsCodeLens.enable')) {
|
||||
this._register(languages.registerCodeLensProvider(languageIds, new ImplementationsCodeLensProvider(client, cachedResponse)))
|
||||
}
|
||||
if (this.client.apiVersion.gte(API.v350)) {
|
||||
this.disposables.push(
|
||||
languages.registerSelectionRangeProvider(languageIds, new SmartSelection(this.client))
|
||||
)
|
||||
this._register(languages.registerSelectionRangeProvider(languageIds, new SmartSelection(this.client)))
|
||||
}
|
||||
|
||||
if (this.description.id == 'typescript') {
|
||||
// this.client.apiVersion
|
||||
this.disposables.push(
|
||||
new WatchBuild(commands, this.client)
|
||||
)
|
||||
}
|
||||
|
||||
// if (this.client.apiVersion.gte(API.v300)) {
|
||||
// this.disposables.push(
|
||||
// this._register(
|
||||
// languages.registerCompletionItemProvider(
|
||||
// `tsserver-${this.description.id}-tag`,
|
||||
// 'TSC',
|
||||
|
|
|
@ -11,17 +11,17 @@ import { standardLanguageDescriptions } from './utils/languageDescription'
|
|||
import * as typeconverts from './utils/typeConverters'
|
||||
import FileConfigurationManager from './features/fileConfigurationManager'
|
||||
import TypeScriptServiceClient from './typescriptServiceClient'
|
||||
import TsserverService from '../server'
|
||||
|
||||
export class OrganizeImportsCommand implements Command {
|
||||
public readonly id: string = 'tsserver.organizeImports'
|
||||
|
||||
constructor(
|
||||
private readonly client: TypeScriptServiceClient
|
||||
private readonly service: TsserverService
|
||||
) {
|
||||
}
|
||||
|
||||
private async getTextEdits(document: TextDocument): Promise<WorkspaceEdit | null> {
|
||||
let client = this.client
|
||||
private async getTextEdits(client: TypeScriptServiceClient, document: TextDocument): Promise<WorkspaceEdit | null> {
|
||||
let file = client.toPath(document.uri)
|
||||
const args: Proto.OrganizeImportsRequestArgs = {
|
||||
scope: {
|
||||
|
@ -31,7 +31,7 @@ export class OrganizeImportsCommand implements Command {
|
|||
}
|
||||
}
|
||||
}
|
||||
const response = await this.client.interruptGetErr(() => this.client.execute('organizeImports', args, CancellationToken.None))
|
||||
const response = await client.interruptGetErr(() => client.execute('organizeImports', args, CancellationToken.None))
|
||||
if (!response || response.type != 'response' || !response.success) {
|
||||
return
|
||||
}
|
||||
|
@ -46,13 +46,18 @@ export class OrganizeImportsCommand implements Command {
|
|||
}
|
||||
|
||||
public async execute(document?: TextDocument): Promise<void> {
|
||||
let client = await this.service.getClientHost()
|
||||
if (!document) {
|
||||
let doc = await workspace.document
|
||||
if (!doc.attached) return
|
||||
if (this.client.modeIds.indexOf(doc.textDocument.languageId) == -1) return
|
||||
if (!doc.attached) {
|
||||
throw new Error(`Document not attached.`)
|
||||
}
|
||||
if (client.serviceClient.modeIds.indexOf(doc.filetype) == -1) {
|
||||
throw new Error(`filetype "${doc.filetype}" not supported by tsserver.`)
|
||||
}
|
||||
document = doc.textDocument
|
||||
}
|
||||
let edit = await this.getTextEdits(document)
|
||||
let edit = await this.getTextEdits(client.serviceClient, document)
|
||||
if (edit) await workspace.applyEdit(edit)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -2,18 +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 { Uri, DiagnosticKind, disposeAll, workspace } from 'coc.nvim'
|
||||
import { Uri, DiagnosticKind, disposeAll, workspace, languages } from 'coc.nvim'
|
||||
import { Range, Diagnostic, DiagnosticSeverity, Disposable, Position, CancellationToken, DiagnosticRelatedInformation } from 'vscode-languageserver-protocol'
|
||||
import LanguageProvider from './languageProvider'
|
||||
import * as Proto from './protocol'
|
||||
import * as PConst from './protocol.const'
|
||||
import FileConfigurationManager from './features/fileConfigurationManager'
|
||||
import TypeScriptServiceClient from './typescriptServiceClient'
|
||||
import { LanguageDescription } from './utils/languageDescription'
|
||||
import { DiagnosticLanguage, LanguageDescription } from './utils/languageDescription'
|
||||
import * as typeConverters from './utils/typeConverters'
|
||||
import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus'
|
||||
import { PluginManager } from '../utils/plugins'
|
||||
import { flatten } from '../utils/arrays'
|
||||
import WatchBuild from './features/watchBuild'
|
||||
import WorkspaceSymbolProvider from './features/workspaceSymbols'
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument'
|
||||
|
||||
// Style check diagnostics that can be reported as warnings
|
||||
const styleCheckDiagnostics = [
|
||||
|
@ -29,7 +32,6 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
|||
private readonly ataProgressReporter: AtaProgressReporter
|
||||
private readonly typingsStatus: TypingsStatus
|
||||
private readonly client: TypeScriptServiceClient
|
||||
private readonly languages: LanguageProvider[] = []
|
||||
private readonly languagePerId = new Map<string, LanguageProvider>()
|
||||
private readonly disposables: Disposable[] = []
|
||||
private readonly fileConfigurationManager: FileConfigurationManager
|
||||
|
@ -62,6 +64,9 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
|||
})
|
||||
}, null, this.disposables)
|
||||
|
||||
// features
|
||||
this.disposables.push(new WatchBuild(this.client))
|
||||
this.disposables.push(languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(this.client, allModeIds)))
|
||||
this.client.onConfigDiagnosticsReceived(diag => {
|
||||
let { body } = diag
|
||||
if (body) {
|
||||
|
@ -95,10 +100,36 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
|||
description,
|
||||
this.typingsStatus
|
||||
)
|
||||
this.languages.push(manager)
|
||||
this.disposables.push(manager)
|
||||
this.languagePerId.set(description.id, manager)
|
||||
}
|
||||
const languageIds = new Set<string>()
|
||||
for (const plugin of pluginManager.plugins) {
|
||||
if (plugin.configNamespace && plugin.languages.length) {
|
||||
this.registerExtensionLanguageProvider({
|
||||
id: plugin.configNamespace,
|
||||
modeIds: Array.from(plugin.languages),
|
||||
diagnosticSource: 'ts-plugin',
|
||||
diagnosticLanguage: DiagnosticLanguage.TypeScript,
|
||||
diagnosticOwner: 'typescript',
|
||||
isExternal: true
|
||||
})
|
||||
} else {
|
||||
for (const language of plugin.languages) {
|
||||
languageIds.add(language)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (languageIds.size) {
|
||||
this.registerExtensionLanguageProvider({
|
||||
id: 'typescript-plugins',
|
||||
modeIds: Array.from(languageIds.values()),
|
||||
diagnosticSource: 'ts-plugin',
|
||||
diagnosticLanguage: DiagnosticLanguage.TypeScript,
|
||||
diagnosticOwner: 'typescript',
|
||||
isExternal: true
|
||||
})
|
||||
}
|
||||
|
||||
this.client.ensureServiceStarted()
|
||||
this.client.onTsServerStarted(() => {
|
||||
|
@ -108,8 +139,17 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
|||
this.configurationChanged()
|
||||
}
|
||||
|
||||
private registerExtensionLanguageProvider(description: LanguageDescription) {
|
||||
const manager = new LanguageProvider(this.client, this.fileConfigurationManager, description, this.typingsStatus)
|
||||
this.languagePerId.set(description.id, manager)
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
disposeAll(this.disposables)
|
||||
for (let language of this.languagePerId.values()) {
|
||||
language.dispose()
|
||||
}
|
||||
this.languagePerId.clear()
|
||||
this.fileConfigurationManager.dispose()
|
||||
this.typingsStatus.dispose()
|
||||
this.ataProgressReporter.dispose()
|
||||
|
@ -124,6 +164,7 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
|||
}
|
||||
|
||||
public reloadProjects(): void {
|
||||
this.client.diagnosticsManager.reInitialize()
|
||||
this.client.execute('reloadProjects', null, CancellationToken.None)
|
||||
this.triggerAllDiagnostics()
|
||||
}
|
||||
|
@ -142,18 +183,18 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
|||
try {
|
||||
let doc = await workspace.loadFile(uri)
|
||||
if (!doc) return undefined
|
||||
return this.languages.find(language => language.handles(uri, doc.textDocument))
|
||||
let languages = Array.from(this.languagePerId.values())
|
||||
return languages.find(language => language.handles(uri, doc.textDocument))
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
public async handles(uri: string): Promise<boolean> {
|
||||
const provider = await this.findLanguage(uri)
|
||||
if (provider) {
|
||||
return true
|
||||
}
|
||||
return this.client.bufferSyncSupport.handles(uri)
|
||||
public async handles(doc: TextDocument): Promise<boolean> {
|
||||
let languages = Array.from(this.languagePerId.values())
|
||||
let idx = languages.findIndex(language => language.handles(doc.uri, doc))
|
||||
if (idx != -1) return true
|
||||
return this.client.bufferSyncSupport.handles(doc.uri)
|
||||
}
|
||||
|
||||
private triggerAllDiagnostics(): void {
|
||||
|
@ -235,7 +276,7 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
|||
return code ? styleCheckDiagnostics.indexOf(code) !== -1 : false
|
||||
}
|
||||
|
||||
private getAllModeIds(descriptions: LanguageDescription[], pluginManager: PluginManager) {
|
||||
private getAllModeIds(descriptions: LanguageDescription[], pluginManager: PluginManager): string[] {
|
||||
const allModeIds = flatten([
|
||||
...descriptions.map(x => x.modeIds),
|
||||
...pluginManager.plugins.map(x => x.languages)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { exec } from 'child_process'
|
||||
import { workspace, Uri } from 'coc.nvim'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { Uri, workspace } from 'coc.nvim'
|
||||
|
||||
export function runCommand(cmd: string, cwd: string, timeout?: number): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
|
@ -72,9 +73,10 @@ export function distinct<T>(array: T[], keyFn?: (t: T) => string): T[] {
|
|||
|
||||
export async function installModules(uri: string, names: string[]): Promise<void> {
|
||||
names = distinct(names)
|
||||
let root = await getRoot()
|
||||
if (!root) {
|
||||
workspace.showMessage(`package.json not found from cwd: ${workspace.cwd}`, 'error')
|
||||
let workspaceFolder = workspace.getWorkspaceFolder(uri)
|
||||
let root = workspaceFolder ? Uri.parse(workspaceFolder.uri).fsPath : undefined
|
||||
if (!root || !fs.existsSync(path.join(root, 'package.json'))) {
|
||||
workspace.showMessage(`package.json not found from workspaceFolder: ${root}`, 'error')
|
||||
return
|
||||
}
|
||||
let arr = names.concat(names.map(s => `@types/${s}`))
|
||||
|
@ -93,11 +95,10 @@ export async function installModules(uri: string, names: string[]): Promise<void
|
|||
let deps = exists.filter(s => devs.indexOf(s) == -1)
|
||||
statusItem.text = `Installing ${exists.join(' ')}`
|
||||
try {
|
||||
await Promise.all([deps, devs].map((names, i) => {
|
||||
let cmd = manager == 'npm' ? `npm i ${names.join(' ')}` : `yarn add ${names.join(' ')} --ignore-scripts --no-default-rc`
|
||||
if (i == 1) cmd = cmd + ' --dev'
|
||||
return runCommand(cmd, root)
|
||||
}))
|
||||
let cmd = manager == 'npm' ? `npm i ${deps.join(' ')}` : `yarn add ${deps.join(' ')}`
|
||||
await runCommand(cmd, root)
|
||||
cmd = manager == 'npm' ? `npm i ${deps.join(' ')} --save-dev` : `yarn add ${deps.join(' ')} --save-dev`
|
||||
await runCommand(cmd, root)
|
||||
} catch (e) {
|
||||
statusItem.dispose()
|
||||
workspace.showMessage(`Install error ${e.message}`, 'error')
|
||||
|
|
Loading…
Reference in a new issue