diff --git a/src/server/commands.ts b/src/server/commands.ts index 2d6bc11..2313759 100644 --- a/src/server/commands.ts +++ b/src/server/commands.ts @@ -1,4 +1,4 @@ -import { diagnosticManager, workspace } from 'coc.nvim' +import { diagnosticManager, workspace, commands } from 'coc.nvim' import { CancellationToken, Diagnostic } from 'vscode-languageserver-protocol' import URI from 'vscode-uri' import * as Proto from './protocol' @@ -6,36 +6,7 @@ import TypeScriptServiceClientHost from './typescriptServiceClientHost' import * as typeConverters from './utils/typeConverters' import { TextEdit, Range } from 'vscode-languageserver-types' import { installModules } from './utils/modules' - -const nodeModules = [ - 'assert', - 'cluster', - 'crypto', - 'dns', - 'domain', - 'events', - 'fs', - 'http', - 'http2', - 'https', - 'inspector', - 'net', - 'os', - 'path', - 'punycode', - 'querystring', - 'readline', - 'repl', - 'stream', - 'string_decoder', - 'tls', - 'tty', - 'url', - 'util', - 'v8', - 'vm', - 'zlib', - 'perf_hooks'] +import { nodeModules } from './utils/helper' export interface Command { readonly id: string | string[] @@ -153,6 +124,8 @@ export class AutoFixCommand implements Command { }, [] as Diagnostic[]) let client = this.client.serviceClient let edits: TextEdit[] = [] + let command: string + let names: string[] = [] for (let diagnostic of diagnostics) { const args: Proto.CodeFixRequestArgs = { ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range), @@ -164,11 +137,13 @@ export class AutoFixCommand implements Command { let { range } = diagnostic let line = document.getline(range.start.line) let name = line.slice(range.start.character, range.end.character) - if (nodeModules.indexOf(name) !== -1) { + if (nodeModules.indexOf(name) !== -1 && names.indexOf(name) == -1) { + names.push(name) edits.push({ range: Range.create(0, 0, 0, 0), newText: `import ${name} from '${name}'\n` }) + command = 'tsserver.organizeImports' } } continue @@ -186,5 +161,6 @@ export class AutoFixCommand implements Command { } } if (edits.length) await document.applyEdits(workspace.nvim, edits) + if (command) await commands.executeCommand(command) } } diff --git a/src/server/features/importFix.ts b/src/server/features/importFix.ts new file mode 100644 index 0000000..c78b5e3 --- /dev/null +++ b/src/server/features/importFix.ts @@ -0,0 +1,62 @@ +import { CodeActionProvider, workspace } from 'coc.nvim' +import BufferSyncSupport from './bufferSyncSupport' +import { TextDocument, Range, CodeActionContext, CancellationToken, CodeAction } from 'vscode-languageserver-protocol' +import { nodeModules } from '../utils/helper' +import { WorkspaceEdit, Command, TextEdit } from 'vscode-languageserver-types' + +export default class ImportFixProvider implements CodeActionProvider { + + constructor( + private readonly bufferSyncSupport: BufferSyncSupport, + ) { + + } + + public async provideCodeActions( + document: TextDocument, + _range: Range, + context: CodeActionContext, + _token: CancellationToken + ): Promise { + + if (this.bufferSyncSupport.hasPendingDiagnostics(document.uri)) { + return [] + } + let diagnostics = context.diagnostics.filter(d => d.code == 2304) + if (!diagnostics.length) return [] + let edits: TextEdit[] = [] + let names: string[] = [] + let doc = workspace.getDocument(document.uri) + let command: string + for (const diagnostic of diagnostics) { + let { range } = diagnostic + let line = doc.getline(range.start.line) + let name = line.slice(range.start.character, range.end.character) + if (names.indexOf(name) !== -1) continue + if (nodeModules.indexOf(name) !== -1) { + names.push(name) + edits.push({ + range: Range.create(0, 0, 0, 0), + newText: `import ${name} from '${name}'\n` + }) + command = 'tsserver.organizeImports' + } + } + let edit: WorkspaceEdit = { + changes: { + [document.uri]: edits + } + } + let cmd: Command = null + if (command) cmd = { + title: `fix import`, + command: 'tsserver.organizeImports' + } + return [{ + title: `Add import ${names.join(', ')}`, + edit, + command: cmd + }] + } + +} diff --git a/src/server/languageProvider.ts b/src/server/languageProvider.ts index e6ee7ff..534a861 100644 --- a/src/server/languageProvider.ts +++ b/src/server/languageProvider.ts @@ -20,6 +20,7 @@ import HoverProvider from './features/hover' import ImplementationsCodeLensProvider from './features/implementationsCodeLens' // import TagCompletionProvider from './features/tagCompletion' import QuickfixProvider from './features/quickfix' +import ImportfixProvider from './features/importFix' import RefactorProvider from './features/refactor' import ReferenceProvider from './features/references' import ReferencesCodeLensProvider from './features/referencesCodeLens' @@ -252,6 +253,13 @@ export default class LanguageProvider { new QuickfixProvider(client, this.diagnosticsManager, this.bufferSyncSupport), 'tsserver', [CodeActionKind.QuickFix])) + + this.disposables.push( + languages.registerCodeActionProvider( + languageIds, + new ImportfixProvider(this.bufferSyncSupport), + 'tsserver', + [CodeActionKind.QuickFix])) let cachedResponse = new CachedNavTreeResponse() if (this.client.apiVersion.gte(API.v206) && conf.get('referencesCodeLens.enable')) { diff --git a/src/server/utils/helper.ts b/src/server/utils/helper.ts new file mode 100644 index 0000000..11a1e29 --- /dev/null +++ b/src/server/utils/helper.ts @@ -0,0 +1,31 @@ + +export const nodeModules = [ + 'assert', + 'cluster', + 'crypto', + 'dns', + 'domain', + 'events', + 'fs', + 'http', + 'http2', + 'https', + 'inspector', + 'net', + 'os', + 'path', + 'punycode', + 'querystring', + 'readline', + 'repl', + 'stream', + 'string_decoder', + 'tls', + 'tty', + 'url', + 'util', + 'v8', + 'vm', + 'zlib', + 'perf_hooks'] +