Skip to content

Commit

Permalink
feat(language-service): drag and drop import respects tsconfig path a…
Browse files Browse the repository at this point in the history
…liases (#4184)
  • Loading branch information
johnsoncodehk committed Mar 31, 2024
1 parent 21c1cbd commit 4f162a1
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 12 deletions.
7 changes: 6 additions & 1 deletion packages/language-service/index.ts
Expand Up @@ -30,6 +30,7 @@ import { create as createVueVisualizeHiddenCallbackParamPlugin } from './lib/plu
import { decorateLanguageServiceForVue } from '@vue/typescript-plugin/lib/common';
import { collectExtractProps } from '@vue/typescript-plugin/lib/requests/collectExtractProps';
import { getComponentEvents, getComponentNames, getComponentProps, getElementAttrs, getTemplateContextProps } from '@vue/typescript-plugin/lib/requests/componentInfos';
import { getImportPathForFile } from '@vue/typescript-plugin/lib/requests/getImportPathForFile';
import { getPropertiesAtLocation } from '@vue/typescript-plugin/lib/requests/getPropertiesAtLocation';
import { getQuickInfoAtPosition } from '@vue/typescript-plugin/lib/requests/getQuickInfoAtPosition';

Expand Down Expand Up @@ -78,7 +79,7 @@ export function createVueServicePlugins(
createVueSfcPlugin(),
createVueTwoslashQueriesPlugin(ts, getTsPluginClient),
createVueReferencesCodeLensPlugin(),
createVueDocumentDropPlugin(ts),
createVueDocumentDropPlugin(ts, getTsPluginClient),
createVueAutoDotValuePlugin(ts, getTsPluginClient),
createVueAutoWrapParenthesesPlugin(ts),
createVueAutoAddSpacePlugin(),
Expand Down Expand Up @@ -107,6 +108,7 @@ export function createDefaultGetTsPluginClient(
typescript: ts,
language: context.language,
languageService,
languageServiceHost: context.language.typescript.languageServiceHost,
vueOptions: getVueOptions(context.env),
isTsPlugin: false,
getFileId: context.env.typescript!.fileNameToUri,
Expand All @@ -118,6 +120,9 @@ export function createDefaultGetTsPluginClient(
async getPropertiesAtLocation(...args) {
return await getPropertiesAtLocation.apply(requestContext, args);
},
async getImportPathForFile(...args) {
return await getImportPathForFile.apply(requestContext, args);
},
async getComponentEvents(...args) {
return await getComponentEvents.apply(requestContext, args);
},
Expand Down
34 changes: 28 additions & 6 deletions packages/language-service/lib/plugins/vue-document-drop.ts
Expand Up @@ -3,15 +3,21 @@ import { camelize, capitalize, hyphenate } from '@vue/shared';
import * as path from 'path-browserify';
import type * as vscode from 'vscode-languageserver-protocol';
import { createAddComponentToOptionEdit, getLastImportNode } from '../plugins/vue-extract-file';
import { LanguageServicePlugin, LanguageServicePluginInstance, TagNameCasing } from '../types';
import { LanguageServicePlugin, LanguageServicePluginInstance, ServiceContext, TagNameCasing } from '../types';
import { getUserPreferences } from 'volar-service-typescript/lib/configs/getUserPreferences';

export function create(ts: typeof import('typescript')): LanguageServicePlugin {
export function create(
ts: typeof import('typescript'),
getTsPluginClient?: (context: ServiceContext) => typeof import('@vue/typescript-plugin/lib/client') | undefined,
): LanguageServicePlugin {
return {
name: 'vue-document-drop',
create(context): LanguageServicePluginInstance {

let casing: TagNameCasing = TagNameCasing.Pascal; // TODO

const tsPluginClient = getTsPluginClient?.(context);

return {
async provideDocumentDropEdits(document, _position, dataTransfer) {

Expand Down Expand Up @@ -51,12 +57,28 @@ export function create(ts: typeof import('typescript')): LanguageServicePlugin {
const additionalEdit: vscode.WorkspaceEdit = {};
const code = [...forEachEmbeddedCode(vueVirtualCode)].find(code => code.id === (sfc.scriptSetup ? 'scriptSetupFormat' : 'scriptFormat'))!;
const lastImportNode = getLastImportNode(ts, script.ast);
const incomingFileName = context.env.typescript!.uriToFileName(importUri);

let importPath: string | undefined;

const serviceScript = sourceScript.generated?.languagePlugin.typescript?.getServiceScript(vueVirtualCode);
if (tsPluginClient && serviceScript) {
const tsDocumentUri = context.encodeEmbeddedDocumentUri(sourceScript.id, serviceScript.code.id);
const tsDocument = context.documents.get(tsDocumentUri, serviceScript.code.languageId, serviceScript.code.snapshot);
const preferences = await getUserPreferences(context, tsDocument);
const importPathRequest = await tsPluginClient.getImportPathForFile(vueVirtualCode.fileName, incomingFileName, preferences);
if (importPathRequest) {
importPath = importPathRequest;
}
}

let importPath = path.relative(path.dirname(document.uri), importUri)
|| importUri.substring(importUri.lastIndexOf('/') + 1);
if (!importPath) {
importPath = path.relative(path.dirname(vueVirtualCode.fileName), incomingFileName)
|| importUri.substring(importUri.lastIndexOf('/') + 1);

if (!importPath.startsWith('./') && !importPath.startsWith('../')) {
importPath = './' + importPath;
if (!importPath.startsWith('./') && !importPath.startsWith('../')) {
importPath = './' + importPath;
}
}

additionalEdit.changes ??= {};
Expand Down
2 changes: 1 addition & 1 deletion packages/language-service/package.json
Expand Up @@ -31,7 +31,7 @@
"volar-service-json": "0.0.37",
"volar-service-pug": "0.0.37",
"volar-service-pug-beautify": "0.0.37",
"volar-service-typescript": "0.0.37",
"volar-service-typescript": "0.0.37-patch.1",
"volar-service-typescript-twoslash-queries": "0.0.37",
"vscode-html-languageservice": "^5.1.0",
"vscode-languageserver-textdocument": "^1.0.11",
Expand Down
9 changes: 9 additions & 0 deletions packages/typescript-plugin/lib/client.ts
Expand Up @@ -10,6 +10,15 @@ export function collectExtractProps(
});
}

export async function getImportPathForFile(
...args: Parameters<typeof import('./requests/getImportPathForFile.js')['getImportPathForFile']>
) {
return await sendRequest<ReturnType<typeof import('./requests/getImportPathForFile')['getImportPathForFile']>>({
type: 'getImportPathForFile',
args,
});
}

export async function getPropertiesAtLocation(
...args: Parameters<typeof import('./requests/getPropertiesAtLocation.js')['getPropertiesAtLocation']>
) {
Expand Down
46 changes: 46 additions & 0 deletions packages/typescript-plugin/lib/requests/getImportPathForFile.ts
@@ -0,0 +1,46 @@
import type * as ts from 'typescript';

export function getImportPathForFile(
this: {
typescript: typeof import('typescript');
languageService: ts.LanguageService;
languageServiceHost: ts.LanguageServiceHost;
},
fileName: string,
incomingFileName: string,
preferences: ts.UserPreferences,
) {
const { typescript: ts, languageService, languageServiceHost } = this;
const program = languageService.getProgram();
const incomingFile = program?.getSourceFile(incomingFileName);
const sourceFile = program?.getSourceFile(fileName);
if (!program || !sourceFile || !incomingFile) {
return;
}

const getModuleSpecifiersWithCacheInfo: (
moduleSymbol: ts.Symbol,
checker: ts.TypeChecker,
compilerOptions: ts.CompilerOptions,
importingSourceFile: ts.SourceFile,
host: any,
userPreferences: ts.UserPreferences,
options?: {},
) => {
moduleSpecifiers: readonly string[];
computedWithoutCache: boolean;
} = (ts as any).moduleSpecifiers.getModuleSpecifiersWithCacheInfo;
const resolutionHost = (ts as any).createModuleSpecifierResolutionHost(program, languageServiceHost);
const moduleSpecifiers = getModuleSpecifiersWithCacheInfo(
(incomingFile as any).symbol,
program.getTypeChecker(),
languageServiceHost.getCompilationSettings(),
sourceFile,
resolutionHost,
preferences,
);

for (const moduleSpecifier of moduleSpecifiers.moduleSpecifiers) {
return moduleSpecifier;
}
}
7 changes: 7 additions & 0 deletions packages/typescript-plugin/lib/server.ts
Expand Up @@ -3,6 +3,7 @@ import * as net from 'net';
import type * as ts from 'typescript';
import { collectExtractProps } from './requests/collectExtractProps';
import { getComponentEvents, getComponentNames, getComponentProps, getElementAttrs, getTemplateContextProps } from './requests/componentInfos';
import { getImportPathForFile } from './requests/getImportPathForFile';
import { getPropertiesAtLocation } from './requests/getPropertiesAtLocation';
import { getQuickInfoAtPosition } from './requests/getQuickInfoAtPosition';
import { NamedPipeServer, connect, readPipeTable, updatePipeTable } from './utils';
Expand All @@ -11,6 +12,7 @@ import type { Language, VueCompilerOptions } from '@vue/language-core';
export interface Request {
type: 'containsFile'
| 'collectExtractProps'
| 'getImportPathForFile'
| 'getPropertiesAtLocation'
| 'getQuickInfoAtPosition'
// Component Infos
Expand Down Expand Up @@ -50,6 +52,7 @@ export function startNamedPipeServer(
const requestContext = {
typescript: ts,
languageService: project.info.languageService,
languageServiceHost: project.info.languageServiceHost,
language: project.language,
vueOptions: project.vueOptions,
isTsPlugin: true,
Expand All @@ -59,6 +62,10 @@ export function startNamedPipeServer(
const result = collectExtractProps.apply(requestContext, request.args as any);
connection.write(JSON.stringify(result ?? null));
}
else if (request.type === 'getImportPathForFile') {
const result = getImportPathForFile.apply(requestContext, request.args as any);
connection.write(JSON.stringify(result ?? null));
}
else if (request.type === 'getPropertiesAtLocation') {
const result = getPropertiesAtLocation.apply(requestContext, request.args as any);
connection.write(JSON.stringify(result ?? null));
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4f162a1

Please sign in to comment.