Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function initializeLanguageClient(vlsModulePath: string, globalSnippetDir
'prettier',
'stylusSupremacy'
],
fileEvents: vscode.workspace.createFileSystemWatcher('{**/*.js,**/*.ts}', false, false, true)
fileEvents: vscode.workspace.createFileSystemWatcher('{**/*.js,**/*.ts,**/*.json}', false, false, true)
},
initializationOptions: {
config,
Expand Down
2 changes: 1 addition & 1 deletion client/vueMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export async function activate(context: vscode.ExtensionContext) {
);

context.subscriptions.push(
vscode.commands.registerCommand('vetur.chooseTypeScriptRefactoring', (args: any) => {
vscode.commands.registerCommand('vetur.chooseTypeScriptCodeAction', (args: any) => {
client
.sendRequest<vscode.Command | undefined>('requestCodeActionEdits', args)
.then(command => command && vscode.commands.executeCommand(command.command, ...command.arguments!));
Expand Down
7 changes: 4 additions & 3 deletions server/src/embeddedSupport/languageModes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
Position,
FormattingOptions,
SymbolInformation,
CodeAction,
CodeActionContext,
ColorInformation,
Color,
Expand All @@ -28,7 +29,7 @@ import { getCSSMode, getSCSSMode, getLESSMode, getPostCSSMode } from '../modes/s
import { getJavascriptMode } from '../modes/script/javascript';
import { VueHTMLMode } from '../modes/template';
import { getStylusMode } from '../modes/style/stylus';
import { DocumentContext, RefactorAction } from '../types';
import { DocumentContext, RefactorAction, CodeActionReq } from '../types';
import { VueInfoService } from '../services/vueInfoService';
import { DependencyService, State } from '../services/dependencyService';
import { nullMode } from '../modes/nullMode';
Expand All @@ -52,8 +53,8 @@ export interface LanguageMode {
range: Range,
formatParams: FormattingOptions,
context: CodeActionContext
): Command[];
getRefactorEdits?(doc: TextDocument, args: RefactorAction): Command;
): CodeAction[];
getCodeActionEdits?(doc: TextDocument, req: CodeActionReq): Command;
doComplete?(document: TextDocument, position: Position): CompletionList;
doResolve?(document: TextDocument, item: CompletionItem): CompletionItem;
doHover?(document: TextDocument, position: Position): Hover;
Expand Down
261 changes: 201 additions & 60 deletions server/src/modes/script/javascript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import {
Position,
FormattingOptions,
DiagnosticTag,
MarkupContent
MarkupContent,
CodeAction,
CodeActionKind,
CodeActionContext
} from 'vscode-languageserver-types';
import { LanguageMode } from '../../embeddedSupport/languageModes';
import { VueDocumentRegions, LanguageRange } from '../../embeddedSupport/embeddedSupport';
Expand All @@ -38,7 +41,7 @@ import { VLSFormatConfig } from '../../config';
import { VueInfoService } from '../../services/vueInfoService';
import { getComponentInfo } from './componentInfo';
import { DependencyService, T_TypeScript, State } from '../../services/dependencyService';
import { RefactorAction } from '../../types';
import { RefactorAction, CodeActionReq, CodeActionReqKind } from '../../types';
import { IServiceHost } from '../../services/typescriptService/serviceHost';
import { toCompletionItemKind, toSymbolKind } from '../../services/typescriptService/util';

Expand Down Expand Up @@ -110,7 +113,8 @@ export async function getJavascriptMode(
const fileFsPath = getFileFsPath(doc.uri);
const rawScriptDiagnostics = [
...service.getSyntacticDiagnostics(fileFsPath),
...service.getSemanticDiagnostics(fileFsPath)
...service.getSemanticDiagnostics(fileFsPath),
...service.getSuggestionDiagnostics(fileFsPath)
];

return rawScriptDiagnostics.map(diag => {
Expand Down Expand Up @@ -420,6 +424,7 @@ export async function getJavascriptMode(
const fileName = getFileFsPath(scriptDoc.uri);
const start = scriptDoc.offsetAt(range.start);
const end = scriptDoc.offsetAt(range.end);
const textRange = { pos: start, end };
if (!supportedCodeFixCodes) {
supportedCodeFixCodes = new Set(
ts
Expand All @@ -428,46 +433,71 @@ export async function getJavascriptMode(
.filter(x => !isNaN(x))
);
}
const fixableDiagnosticCodes = context.diagnostics.map(d => +d.code!).filter(c => supportedCodeFixCodes.has(c));
if (!fixableDiagnosticCodes) {
return [];
}

const formatSettings: ts.FormatCodeSettings = getFormatCodeSettings(config);

const result: Command[] = [];
const fixes = service.getCodeFixesAtPosition(
const result: CodeAction[] = [];

provideQuickFixCodeActions(
fileName,
start,
end,
fixableDiagnosticCodes,
textRange,
context,
supportedCodeFixCodes,
formatSettings,
/*preferences*/ {}
/*preferences*/ {},
service,
result
);
collectQuickFixCommands(fixes, service, result);

const textRange = { pos: start, end };
const refactorings = service.getApplicableRefactors(fileName, textRange, /*preferences*/ {});
collectRefactoringCommands(refactorings, fileName, formatSettings, textRange, result);
provideOrganizeImports(fileName, textRange, context, formatSettings, /*preferences*/ {}, service, result);
provideRefactoringCommands(fileName, textRange, context, formatSettings, /*preferences*/ {}, service, result);

return result;
},
getRefactorEdits(doc: TextDocument, args: RefactorAction) {
getCodeActionEdits(doc: TextDocument, req: CodeActionReq) {
const { service } = updateCurrentVueTextDocument(doc);
const response = service.getEditsForRefactor(
args.fileName,
args.formatOptions,
args.textRange,
args.refactorName,
args.actionName,
args.preferences
);
if (!response) {
// TODO: What happens when there's no response?
return createApplyCodeActionCommand('', {});

if (req.kind === CodeActionReqKind.RefactorAction) {
const args = req.arguments;
const response = service.getEditsForRefactor(
req.fileName,
req.formatSettings,
req.textRange,
args.refactorName,
args.actionName,
req.preferences
);
if (!response) {
// TODO: What happens when there's no response?
return createApplyCodeActionCommand('', {});
}
const uriMapping = createUriMappingForEdits(response.edits, service);
return createApplyCodeActionCommand('', uriMapping);
}

if (req.kind === CodeActionReqKind.CombinedCodeFix) {
const fix = service.getCombinedCodeFix(
{ type: 'file', fileName: req.fileName },
req.arguments.fixId,
req.formatSettings,
req.preferences
);

const uriMapping = createUriMappingForEdits(fix.changes.slice(), service);
return createApplyCodeActionCommand('', uriMapping);
}

if (req.kind === CodeActionReqKind.OrganizeImports) {
const response = service.organizeImports(
{ type: 'file', fileName: req.fileName },
req.formatSettings,
req.preferences
);

const uriMapping = createUriMappingForEdits(response.slice(), service);
return createApplyCodeActionCommand('', uriMapping);
}
const uriMapping = createUriMappingForEdits(response.edits, service);
return createApplyCodeActionCommand('', uriMapping);

return createApplyCodeActionCommand('', {});
},
format(doc: TextDocument, range: Range, formatParams: FormattingOptions): TextEdit[] {
const { scriptDoc, service } = updateCurrentVueTextDocument(doc);
Expand Down Expand Up @@ -546,60 +576,170 @@ export async function getJavascriptMode(
};
}

function collectRefactoringCommands(
refactorings: ts.ApplicableRefactorInfo[],
function provideRefactoringCommands(
fileName: string,
formatSettings: any,
textRange: { pos: number; end: number },
result: Command[]
context: CodeActionContext,
formatSettings: ts.FormatCodeSettings,
preferences: ts.UserPreferences,
service: ts.LanguageService,
result: CodeAction[]
) {
const actions: RefactorAction[] = [];
if (
context.only &&
!context.only.some(el =>
[
CodeActionKind.Refactor,
CodeActionKind.RefactorExtract,
CodeActionKind.RefactorInline,
CodeActionKind.RefactorRewrite,
CodeActionKind.Source
].includes(el)
)
) {
return;
}

const refactorings = service.getApplicableRefactors(fileName, textRange, /*preferences*/ {});

const actions: CodeActionReq[] = [];
for (const refactoring of refactorings) {
const refactorName = refactoring.name;
if (refactoring.inlineable) {
actions.push({
kind: CodeActionReqKind.RefactorAction,
fileName,
formatOptions: formatSettings,
textRange,
refactorName,
actionName: refactorName,
preferences: {},
description: refactoring.description
formatSettings,
preferences,
arguments: {
refactorName,
actionName: refactorName,
description: refactoring.description
}
});
} else {
actions.push(
...refactoring.actions.map(action => ({
fileName,
formatOptions: formatSettings,
textRange,
refactorName,
actionName: action.name,
preferences: {},
description: action.description
}))
...refactoring.actions.map(
action =>
({
kind: CodeActionReqKind.RefactorAction,
fileName,
textRange,
formatSettings,
preferences,
arguments: {
refactorName,
actionName: action.name,
description: action.description
}
} as CodeActionReq)
)
);
}
}
for (const action of actions) {
result.push({
command: 'vetur.chooseTypeScriptRefactoring',
title: action.description,
arguments: [action]
title: (action.arguments as RefactorAction).description,
kind: CodeActionKind.Refactor,
command: createRequestCodeActionCommand((action.arguments as RefactorAction).description, action)
});
}
}

function collectQuickFixCommands(
fixes: ReadonlyArray<ts.CodeFixAction>,
function provideQuickFixCodeActions(
fileName: string,
textRange: { pos: number; end: number },
context: CodeActionContext,
supportedCodeFixCodes: Set<number>,
formatSettings: ts.FormatCodeSettings,
preferences: ts.UserPreferences,
service: ts.LanguageService,
result: Command[]
result: CodeAction[]
) {
if (context.only && !context.only.includes(CodeActionKind.QuickFix)) {
return;
}

const fixableDiagnosticCodes = context.diagnostics.map(d => +d.code!).filter(c => supportedCodeFixCodes.has(c));
if (!fixableDiagnosticCodes) {
return;
}

const fixes = service.getCodeFixesAtPosition(
fileName,
textRange.pos,
textRange.end,
fixableDiagnosticCodes,
formatSettings,
preferences
);

for (const fix of fixes) {
const uriTextEditMapping = createUriMappingForEdits(fix.changes, service);
result.push(createApplyCodeActionCommand(fix.description, uriTextEditMapping));
result.push({
title: fix.description,
kind: CodeActionKind.QuickFix,
diagnostics: context.diagnostics,
command: createApplyCodeActionCommand(fix.description, createUriMappingForEdits(fix.changes, service))
});
if (fix.fixAllDescription && fix.fixId) {
result.push({
title: fix.fixAllDescription,
kind: CodeActionKind.QuickFix,
diagnostics: context.diagnostics,
command: createRequestCodeActionCommand(fix.fixAllDescription, {
kind: CodeActionReqKind.CombinedCodeFix,
fileName,
textRange,
formatSettings,
preferences,
arguments: {
fixId: fix.fixId
}
})
});
}
}
}

function provideOrganizeImports(
fileName: string,
textRange: { pos: number; end: number },
context: CodeActionContext,
formatSettings: ts.FormatCodeSettings,
preferences: ts.UserPreferences,
service: ts.LanguageService,
result: CodeAction[]
) {
if (
!context.only ||
(!context.only.includes(CodeActionKind.SourceOrganizeImports) && !context.only.includes(CodeActionKind.Source))
) {
return;
}

result.push({
title: 'Organize Imports',
kind: CodeActionKind.SourceOrganizeImports,
command: createRequestCodeActionCommand('Organize Imports', {
kind: CodeActionReqKind.OrganizeImports,
fileName,
textRange,
formatSettings,
preferences,
arguments: {}
})
});
}

function createRequestCodeActionCommand(title: string, action: CodeActionReq): Command {
return {
title,
command: 'vetur.chooseTypeScriptCodeAction',
arguments: [action]
};
}

function createApplyCodeActionCommand(title: string, uriTextEditMapping: Record<string, TextEdit[]>): Command {
return {
title,
Expand Down Expand Up @@ -665,7 +805,8 @@ function getFormatCodeSettings(config: any): ts.FormatCodeSettings {
return {
tabSize: config.vetur.format.options.tabSize,
indentSize: config.vetur.format.options.tabSize,
convertTabsToSpaces: !config.vetur.format.options.useTabs
convertTabsToSpaces: !config.vetur.format.options.useTabs,
insertSpaceAfterCommaDelimiter: true
};
}

Expand Down
Loading