Skip to content

Commit

Permalink
feat: add option to disable file move edit
Browse files Browse the repository at this point in the history
close #1181, close #1273
  • Loading branch information
johnsoncodehk committed Jul 16, 2022
1 parent 72a2913 commit fd4d125
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 123 deletions.
5 changes: 5 additions & 0 deletions extensions/vscode-vue-language-features/package.json
Expand Up @@ -439,6 +439,11 @@
"template",
"customBlocks"
]
},
"volar.updateImportsOnFileMove.enabled": {
"type": "boolean",
"default": true,
"description": "Enabled update imports on file move."
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion packages/vue-language-server/src/common.ts
Expand Up @@ -95,7 +95,7 @@ export function createLanguageServer(
);

(await import('./features/customFeatures')).register(connection, documents, projects);
(await import('./features/languageFeatures')).register(ts, connection, documents, projects, options.languageFeatures, params, languageConfigs);
(await import('./features/languageFeatures')).register(connection, projects, options.languageFeatures, params);
(await import('./registers/registerlanguageFeatures')).register(options.languageFeatures!, vue.getSemanticTokenLegend(), result.capabilities, languageConfigs);
}

Expand Down
73 changes: 10 additions & 63 deletions packages/vue-language-server/src/features/languageFeatures.ts
@@ -1,20 +1,13 @@
import * as shared from '@volar/shared';
import * as vue from '@volar/vue-language-service';
import { TextDocument } from 'vscode-languageserver-textdocument';
import * as vscode from 'vscode-languageserver';
import type { Projects } from '../projects';
import { fileRenamings, renameFileContentCache, getScriptText } from '../project';
import { getDocumentSafely } from '../utils';
import { LanguageConfigs } from '../common';

export function register(
ts: typeof import('typescript/lib/tsserverlibrary'),
connection: vscode.Connection,
documents: vscode.TextDocuments<TextDocument>,
projects: Projects,
features: NonNullable<shared.ServerInitializationOptions['languageFeatures']>,
params: vscode.InitializeParams,
languageConfigs: LanguageConfigs,
) {
connection.onCompletion(async handler => {
return worker(handler.textDocument.uri, async vueLs => {
Expand Down Expand Up @@ -289,66 +282,20 @@ export function register(
});
connection.workspace.onWillRenameFiles(async handler => {

const hasTsFile = handler.files.some(file =>
languageConfigs.definitelyExts.some(ext => file.oldUri.endsWith(ext))
|| file.newUri.endsWith('.ts')
|| file.newUri.endsWith('.tsx')
);
const config: 'prompt' | 'always' | 'never' | null | undefined = await connection.workspace.getConfiguration(hasTsFile ? 'typescript.updateImportsOnFileMove.enabled' : 'javascript.updateImportsOnFileMove.enabled');

if (config === 'always') {
const renaming = new Promise<void>(async resolve => {
for (const file of handler.files) {
const renameFileContent = getScriptText(documents, shared.uriToFsPath(file.oldUri), ts.sys);
if (renameFileContent) {
renameFileContentCache.set(file.oldUri, renameFileContent);
}
}
await shared.sleep(0);
const edit = await worker();
if (edit) {
if (edit.documentChanges) {
for (const change of edit.documentChanges) {
if (vscode.TextDocumentEdit.is(change)) {
for (const file of handler.files) {
if (change.textDocument.uri === file.oldUri) {
change.textDocument.uri = file.newUri;
change.textDocument.version = getDocumentSafely(documents, file.newUri)?.version ?? change.textDocument.version;
}
}
}
}
}
connection.workspace.applyEdit(edit);
}
resolve();
});
fileRenamings.add(renaming);
(async () => {
await renaming;
fileRenamings.delete(renaming);
renameFileContentCache.clear();
})();
const config = await connection.workspace.getConfiguration('volar.updateImportsOnFileMove.enabled');
if (!config) {
return null;
}

if (config === 'prompt')
return await worker();

return null;

async function worker() {
const edits = (await Promise.all(handler.files
.map(async file => {
const vueLs = await getLanguageService(file.oldUri);
return vueLs?.getEditsForFileRename(file.oldUri, file.newUri);
}))).filter(shared.notEmpty);
if (edits.length) {
const result = edits[0];
vue.mergeWorkspaceEdits(result, ...edits.slice(1));
return result;
}
if (handler.files.length !== 1) {
return null;
}

const file = handler.files[0];

return await worker(file.oldUri, vueLs => {
return vueLs.getEditsForFileRename(file.oldUri, file.newUri) ?? null;
}) ?? null;
});
connection.onRequest(shared.AutoInsertRequest.type, async handler => {
return worker(handler.textDocument.uri, vueLs => {
Expand Down
9 changes: 0 additions & 9 deletions packages/vue-language-server/src/project.ts
Expand Up @@ -10,8 +10,6 @@ import { LanguageConfigs, loadCustomPlugins, RuntimeEnvironment } from './common
import { tsShared } from '@volar/vue-typescript';

export interface Project extends ReturnType<typeof createProject> { }
export const fileRenamings = new Set<Promise<void>>();
export const renameFileContentCache = new Map<string, string>();

export async function createProject(
runtimeEnv: RuntimeEnvironment,
Expand All @@ -26,8 +24,6 @@ export async function createProject(
lsConfigs: ReturnType<typeof createLsConfigs> | undefined,
) {

await Promise.all([...fileRenamings]);

const projectSys: typeof ts.sys = {
...ts.sys,
readFile: (path, encoding) => ts.sys.readFile(resolveAbsolutePath(path), encoding),
Expand Down Expand Up @@ -135,8 +131,6 @@ export async function createProject(
}
async function onWorkspaceFilesChanged(changes: vscode.FileEvent[]) {

await Promise.all([...fileRenamings]);

for (const change of changes) {

const script = scripts.uriGet(change.uri);
Expand Down Expand Up @@ -166,8 +160,6 @@ export async function createProject(
}
async function onDocumentUpdated(document: TextDocument) {

await Promise.all([...fileRenamings]);

const script = scripts.uriGet(document.uri);
if (script) {
script.version = document.version;
Expand Down Expand Up @@ -284,5 +276,4 @@ export function getScriptText(
if (sys.fileExists(fileName)) {
return sys.readFile(fileName, 'utf8');
}
return renameFileContentCache.get(shared.fsPathToUri(fileName));
}
80 changes: 30 additions & 50 deletions packages/vue-language-service/src/languageService.ts
Expand Up @@ -93,7 +93,6 @@ export function createLanguageService(
const vueDocuments = parseVueDocuments(vueLsCtx, tsLs);
const documentContext = getDocumentContext();

const blockingRequests = new Set<Promise<any>>();
const documents = new WeakMap<ts.IScriptSnapshot, TextDocument>();
const documentVersions = new Map<string, number>();

Expand Down Expand Up @@ -223,34 +222,34 @@ export function createLanguageService(
const findTypeDefinition_internal = definition.register(context, 'findTypeDefinition', data => !!data.capabilities.definitions, data => !!data.capabilities.definitions);

return {
doValidation: defineApi(diagnostics.register(context), false),
findReferences: defineApi(references.register(context)),
findFileReferences: defineApi(fileReferences.register(context)),
findDefinition: defineApi(definition.register(context, 'findDefinition', data => !!data.capabilities.definitions, data => !!data.capabilities.definitions)),
findTypeDefinition: defineApi(definition.register(context, 'findTypeDefinition', data => !!data.capabilities.definitions, data => !!data.capabilities.definitions)),
findImplementations: defineApi(definition.register(context, 'findImplementations', data => !!data.capabilities.references, data => false)),
prepareRename: defineApi(renamePrepare.register(context)),
doRename: defineApi(rename.register(context)),
getEditsForFileRename: defineApi(fileRename.register(context)),
getSemanticTokens: defineApi(semanticTokens.register(context)),
doHover: defineApi(hover.register(context)),
doComplete: defineApi(completions.register(context)),
doCodeActions: defineApi(codeActions.register(context)),
doCodeActionResolve: defineApi(codeActionResolve.register(context)),
doCompletionResolve: defineApi(completionResolve.register(context)),
getSignatureHelp: defineApi(signatureHelp.register(context)),
doCodeLens: defineApi(codeLens.register(context)),
doCodeLensResolve: defineApi(codeLensResolve.register(context)),
findDocumentHighlights: defineApi(documentHighlight.register(context)),
findDocumentLinks: defineApi(documentLink.register(context)),
findWorkspaceSymbols: defineApi(workspaceSymbol.register(context)),
doAutoInsert: defineApi(autoInsert.register(context)),
doExecuteCommand: defineApi(executeCommand.register(context)),
getInlayHints: defineApi(inlayHints.register(context)),
doValidation: diagnostics.register(context),
findReferences: references.register(context),
findFileReferences: fileReferences.register(context),
findDefinition: definition.register(context, 'findDefinition', data => !!data.capabilities.definitions, data => !!data.capabilities.definitions),
findTypeDefinition: definition.register(context, 'findTypeDefinition', data => !!data.capabilities.definitions, data => !!data.capabilities.definitions),
findImplementations: definition.register(context, 'findImplementations', data => !!data.capabilities.references, data => false),
prepareRename: renamePrepare.register(context),
doRename: rename.register(context),
getEditsForFileRename: fileRename.register(context),
getSemanticTokens: semanticTokens.register(context),
doHover: hover.register(context),
doComplete: completions.register(context),
doCodeActions: codeActions.register(context),
doCodeActionResolve: codeActionResolve.register(context),
doCompletionResolve: completionResolve.register(context),
getSignatureHelp: signatureHelp.register(context),
doCodeLens: codeLens.register(context),
doCodeLensResolve: codeLensResolve.register(context),
findDocumentHighlights: documentHighlight.register(context),
findDocumentLinks: documentLink.register(context),
findWorkspaceSymbols: workspaceSymbol.register(context),
doAutoInsert: autoInsert.register(context),
doExecuteCommand: executeCommand.register(context),
getInlayHints: inlayHints.register(context),
callHierarchy: {
doPrepare: defineApi(_callHierarchy.doPrepare),
getIncomingCalls: defineApi(_callHierarchy.getIncomingCalls),
getOutgoingCalls: defineApi(_callHierarchy.getOutgoingCalls),
doPrepare: _callHierarchy.doPrepare,
getIncomingCalls: _callHierarchy.getIncomingCalls,
getOutgoingCalls: _callHierarchy.getOutgoingCalls,
},
dispose: () => {
vueLsCtx.typescriptLanguageService.dispose();
Expand All @@ -260,9 +259,9 @@ export function createLanguageService(
vueRuntimeContext: vueLsCtx,
rootPath: vueLsHost.getCurrentDirectory(),
context,
getContext: defineApi(() => context),
// getD3: defineApi(d3.register(context), true), // unused for now
detectTagNameCase: defineApi(tagNameCase.register(context)),
getContext: () => context,
// getD3: d3.register(context), true), // unused for nw
detectTagNameCase: tagNameCase.register(context),
},
};

Expand Down Expand Up @@ -399,23 +398,4 @@ export function createLanguageService(
} : _languageSupportPlugin;
return languageSupportPlugin;
}
function defineApi<T extends (...args: any) => any>(
api: T,
blockNewRequest = true,
): (...args: Parameters<T>) => Promise<ReturnType<T>> {
const handler = {
async apply(target: (...args: any) => any, thisArg: any, argumentsList: Parameters<T>) {
for (const runningRequest of blockingRequests) {
await runningRequest;
}
const runner = target.apply(thisArg, argumentsList);
if (blockNewRequest && runner instanceof Promise) {
blockingRequests.add(runner);
runner.then(() => blockingRequests.delete(runner));
}
return runner;
}
};
return new Proxy<T>(api, handler);
}
}

0 comments on commit fd4d125

Please sign in to comment.