Skip to content

Commit

Permalink
Add auto import component in script when completion in template
Browse files Browse the repository at this point in the history
  • Loading branch information
yoyo930021 committed Nov 8, 2020
1 parent 2e0f8db commit 22df49c
Show file tree
Hide file tree
Showing 9 changed files with 317 additions and 11 deletions.
7 changes: 7 additions & 0 deletions server/src/embeddedSupport/languageModes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { VLSFullConfig } from '../config';
import { SassLanguageMode } from '../modes/style/sass/sassLanguageMode';
import { getPugMode } from '../modes/pug';
import { VCancellationToken } from '../utils/cancellationToken';
import { createAutoImportVueService } from '../services/autoImportVueService';

export interface VLSServices {
infoService?: VueInfoService;
Expand Down Expand Up @@ -127,22 +128,28 @@ export class LanguageModes {
return vueDocument.getSingleTypeDocument('script');
});
this.serviceHost = getServiceHost(tsModule, workspacePath, scriptRegionDocuments);
const autoImportVueService = createAutoImportVueService(services.infoService);

const vueHtmlMode = new VueHTMLMode(
tsModule,
this.serviceHost,
this.documentRegions,
workspacePath,
autoImportVueService,
services.infoService
);

autoImportVueService.setGetFilesFn(() =>
this.serviceHost.getFileNames().filter(fileName => fileName.endsWith('.vue'))
);
const jsMode = await getJavascriptMode(
this.serviceHost,
this.documentRegions,
workspacePath,
services.infoService,
services.dependencyService
);
autoImportVueService.setGetJSResolve(jsMode.doResolve!);

this.modes['vue'] = getVueMode(workspacePath, globalSnippetDir);
this.modes['vue-html'] = vueHtmlMode;
Expand Down
20 changes: 16 additions & 4 deletions server/src/modes/script/childComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ interface InternalChildComponent {
defaultExportNode?: ts.Node;
}

export function getChildComponents(
export function analyzeComponentsDefine(
tsModule: T_TypeScript,
defaultExportType: ts.Type,
checker: ts.TypeChecker,
tagCasing = 'kebab'
): InternalChildComponent[] | undefined {
): { start: number; end: number; insertPos: number; list: InternalChildComponent[] } | undefined {
let componentsSymbol: ts.Symbol | undefined;

if (!isClassType(tsModule, defaultExportType)) {
Expand All @@ -51,8 +51,9 @@ export function getChildComponents(
if (componentsDeclaration.kind === tsModule.SyntaxKind.ObjectLiteralExpression) {
const componentsType = checker.getTypeOfSymbolAtLocation(componentsSymbol, componentsDeclaration);

let insertPos = componentsDeclaration.getStart() + 1;
const result: InternalChildComponent[] = [];
checker.getPropertiesOfType(componentsType).forEach(s => {
checker.getPropertiesOfType(componentsType).forEach((s, i, arr) => {
if (!s.valueDeclaration) {
return;
}
Expand All @@ -62,6 +63,10 @@ export function getChildComponents(
componentName = kebabCase(s.name);
}

if (i === arr.length - 1) {
insertPos = s.valueDeclaration.getEnd();
}

let objectLiteralSymbol: ts.Symbol | undefined;
if (s.valueDeclaration.kind === tsModule.SyntaxKind.PropertyAssignment) {
objectLiteralSymbol =
Expand All @@ -79,11 +84,13 @@ export function getChildComponents(
if (!definitionSymbol.valueDeclaration) {
return;
}

const sourceFile = definitionSymbol.valueDeclaration.getSourceFile();
const defaultExportNode = getDefaultExportNode(tsModule, sourceFile);
if (!defaultExportNode) {
return;
}

result.push({
name: componentName,
documentation: buildDocumentation(tsModule, definitionSymbol, checker),
Expand All @@ -97,6 +104,11 @@ export function getChildComponents(
}
});

return result;
return {
start: componentsDeclaration.getStart(),
end: componentsDeclaration.getEnd(),
insertPos,
list: result
};
}
}
30 changes: 27 additions & 3 deletions server/src/modes/script/componentInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
MethodInfo,
ChildComponent
} from '../../services/vueInfoService';
import { getChildComponents } from './childComponents';
import { analyzeComponentsDefine } from './childComponents';
import { T_TypeScript } from '../../services/dependencyService';

export function getComponentInfo(
Expand Down Expand Up @@ -36,14 +36,15 @@ export function getComponentInfo(
const vueFileInfo = analyzeDefaultExportExpr(tsModule, defaultExportNode, checker);

const defaultExportType = checker.getTypeAtLocation(defaultExportNode);
const internalChildComponents = getChildComponents(
const componentsDefineInfo = analyzeComponentsDefine(
tsModule,
defaultExportType,
checker,
config.vetur.completion.tagCasing
);

if (internalChildComponents) {
if (componentsDefineInfo) {
const { list: internalChildComponents, ...defineInfo } = componentsDefineInfo;
const childComponents: ChildComponent[] = [];
internalChildComponents.forEach(c => {
childComponents.push({
Expand All @@ -54,6 +55,7 @@ export function getComponentInfo(
});
});
vueFileInfo.componentInfo.childComponents = childComponents;
vueFileInfo.componentInfo.componentsDefine = defineInfo;
}

return vueFileInfo;
Expand All @@ -66,13 +68,15 @@ export function analyzeDefaultExportExpr(
): VueFileInfo {
const defaultExportType = checker.getTypeAtLocation(defaultExportNode);

const newOptionPos = getNewOptionPos(tsModule, defaultExportType, checker);
const props = getProps(tsModule, defaultExportType, checker);
const data = getData(tsModule, defaultExportType, checker);
const computed = getComputed(tsModule, defaultExportType, checker);
const methods = getMethods(tsModule, defaultExportType, checker);

return {
componentInfo: {
insertInOptionAPIPos: newOptionPos,
props,
data,
computed,
Expand All @@ -96,6 +100,26 @@ export function getDefaultExportNode(tsModule: T_TypeScript, sourceFile: ts.Sour
return getNodeFromExportNode(tsModule, exportNode);
}

function getNewOptionPos(tsModule: T_TypeScript, defaultExportType: ts.Type, checker: ts.TypeChecker) {
if (isClassType(tsModule, defaultExportType)) {
const decoratorArgumentType = getClassDecoratorArgumentType(tsModule, defaultExportType, checker);
if (decoratorArgumentType && decoratorArgumentType.symbol.valueDeclaration) {
return (
decoratorArgumentType.symbol.valueDeclaration.pos +
decoratorArgumentType.symbol.valueDeclaration.getFullText().search('{') +
1
);
}
} else {
return (
defaultExportType.symbol?.valueDeclaration?.pos +
defaultExportType.symbol?.valueDeclaration?.getFullText().search('{') +
1
);
}
return undefined;
}

function getProps(tsModule: T_TypeScript, defaultExportType: ts.Type, checker: ts.TypeChecker): PropInfo[] | undefined {
const result: PropInfo[] = markPropBoundToModel(
defaultExportType,
Expand Down
14 changes: 12 additions & 2 deletions server/src/modes/template/htmlMode.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as _ from 'lodash';

import { LanguageModelCache, getLanguageModelCache } from '../../embeddedSupport/languageModelCache';
import { TextDocument, Position, Range, FormattingOptions } from 'vscode-languageserver-types';
import { TextDocument, Position, Range, FormattingOptions, CompletionItem } from 'vscode-languageserver-types';
import { LanguageMode } from '../../embeddedSupport/languageModes';
import { VueDocumentRegions } from '../../embeddedSupport/embeddedSupport';
import { HTMLDocument } from './parser/htmlParser';
Expand All @@ -27,6 +27,7 @@ import { VueVersion } from '../../services/typescriptService/vueVersion';
import { doPropValidation } from './services/vuePropValidation';
import { getFoldingRanges } from './services/htmlFolding';
import { isVCancellationRequested, VCancellationToken } from '../../utils/cancellationToken';
import { AutoImportVueService } from '../../services/autoImportVueService';

export class HTMLMode implements LanguageMode {
private tagProviderSettings: CompletionConfiguration;
Expand All @@ -42,6 +43,7 @@ export class HTMLMode implements LanguageMode {
private workspacePath: string | undefined,
vueVersion: VueVersion,
private vueDocuments: LanguageModelCache<HTMLDocument>,
private autoImportVueService: AutoImportVueService,
private vueInfoService?: VueInfoService
) {
this.tagProviderSettings = getTagProviderSettings(workspacePath);
Expand All @@ -59,6 +61,7 @@ export class HTMLMode implements LanguageMode {
configure(c: VLSFullConfig) {
this.enabledTagProviders = getEnabledTagProviders(this.tagProviderSettings);
this.config = c;
this.autoImportVueService.setGetConfigure(() => c);
}

async doValidation(document: TextDocument, cancellationToken?: VCancellationToken) {
Expand Down Expand Up @@ -93,7 +96,14 @@ export class HTMLMode implements LanguageMode {
tagProviders.push(getComponentInfoTagProvider(info.componentInfo.childComponents));
}

return doComplete(embedded, position, this.vueDocuments.refreshAndGet(embedded), tagProviders, this.config.emmet);
return doComplete(
embedded,
position,
this.vueDocuments.refreshAndGet(embedded),
tagProviders,
this.config.emmet,
this.autoImportVueService.doComplete(document)
);
}
doHover(document: TextDocument, position: Position) {
const embedded = this.embeddedDocuments.refreshAndGet(document);
Expand Down
16 changes: 15 additions & 1 deletion server/src/modes/template/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,35 @@ import { T_TypeScript } from '../../services/dependencyService';
import { HTMLDocument, parseHTMLDocument } from './parser/htmlParser';
import { inferVueVersion } from '../../services/typescriptService/vueVersion';
import { VCancellationToken } from '../../utils/cancellationToken';
import { AutoImportVueService } from '../../services/autoImportVueService';

type DocumentRegionCache = LanguageModelCache<VueDocumentRegions>;

export class VueHTMLMode implements LanguageMode {
private htmlMode: HTMLMode;
private vueInterpolationMode: VueInterpolationMode;
private autoImportVueService: AutoImportVueService;

constructor(
tsModule: T_TypeScript,
serviceHost: IServiceHost,
documentRegions: DocumentRegionCache,
workspacePath: string,
autoImportVueService: AutoImportVueService,
vueInfoService?: VueInfoService
) {
const vueDocuments = getLanguageModelCache<HTMLDocument>(10, 60, document => parseHTMLDocument(document));
const vueVersion = inferVueVersion(tsModule, workspacePath);
this.htmlMode = new HTMLMode(documentRegions, workspacePath, vueVersion, vueDocuments, vueInfoService);
this.htmlMode = new HTMLMode(
documentRegions,
workspacePath,
vueVersion,
vueDocuments,
autoImportVueService,
vueInfoService
);
this.vueInterpolationMode = new VueInterpolationMode(tsModule, serviceHost, vueDocuments, vueInfoService);
this.autoImportVueService = autoImportVueService;
}
getId() {
return 'vue-html';
Expand All @@ -63,6 +74,9 @@ export class VueHTMLMode implements LanguageMode {
};
}
doResolve(document: TextDocument, item: CompletionItem): CompletionItem {
if (this.autoImportVueService.isMyResolve(item)) {
return this.autoImportVueService.doResolve(document, item);
}
return this.vueInterpolationMode.doResolve(document, item);
}
doHover(document: TextDocument, position: Position): Hover {
Expand Down
6 changes: 5 additions & 1 deletion server/src/modes/template/services/htmlCompletion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export function doComplete(
position: Position,
htmlDocument: HTMLDocument,
tagProviders: IHTMLTagProvider[],
emmetConfig: emmet.EmmetConfiguration
emmetConfig: emmet.EmmetConfiguration,
autoImportCompletions?: CompletionItem[]
): CompletionList {
const modifierProvider = getModifierProvider();

Expand Down Expand Up @@ -63,6 +64,9 @@ export function doComplete(
});
});
});
autoImportCompletions?.forEach(item => {
result.items.push(item);
});
return result;
}

Expand Down
Loading

0 comments on commit 22df49c

Please sign in to comment.