Skip to content

Commit

Permalink
Basic support <script setup>
Browse files Browse the repository at this point in the history
  • Loading branch information
yoyo930021 committed Feb 19, 2023
1 parent ce241f6 commit d89071c
Show file tree
Hide file tree
Showing 16 changed files with 456 additions and 128 deletions.
35 changes: 31 additions & 4 deletions server/src/embeddedSupport/embeddedSupport.ts
@@ -1,6 +1,6 @@
import { Position, Range } from 'vscode-languageserver-types';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { parseVueDocumentRegions, EmbeddedRegion } from './vueDocumentRegionParser';
import { parseVueDocumentRegions, EmbeddedRegion, RegionAttrs } from './vueDocumentRegionParser';

export type LanguageId =
| 'vue'
Expand All @@ -19,7 +19,7 @@ export type LanguageId =

export interface LanguageRange extends Range {
languageId: LanguageId;
attributeValue?: boolean;
attrs: RegionAttrs;
}

export interface VueDocumentRegions {
Expand Down Expand Up @@ -49,6 +49,7 @@ export interface VueDocumentRegions {
* Get language for determining
*/
getLanguageAtPosition(position: Position): LanguageId;
getLanguageRangeAtPosition(position: Position): LanguageRange | null;

getImportedScripts(): string[];
}
Expand All @@ -72,6 +73,7 @@ export function getVueDocumentRegions(document: TextDocument): VueDocumentRegion

getAllLanguageRanges: () => getAllLanguageRanges(document, regions),
getLanguageAtPosition: (position: Position) => getLanguageAtPosition(document, regions, position),
getLanguageRangeAtPosition: (position: Position) => getLanguageRangeAtPosition(document, regions, position),
getImportedScripts: () => importedScripts
};
}
Expand All @@ -81,7 +83,8 @@ function getAllLanguageRanges(document: TextDocument, regions: EmbeddedRegion[])
return {
languageId: r.languageId,
start: document.positionAt(r.start),
end: document.positionAt(r.end)
end: document.positionAt(r.end),
attrs: r.attrs
};
});
}
Expand All @@ -100,6 +103,29 @@ function getLanguageAtPosition(document: TextDocument, regions: EmbeddedRegion[]
return 'vue';
}

function getLanguageRangeAtPosition(
document: TextDocument,
regions: EmbeddedRegion[],
position: Position
): LanguageRange | null {
const offset = document.offsetAt(position);
for (const region of regions) {
if (region.start <= offset) {
if (offset <= region.end) {
return {
start: document.positionAt(region.start),
end: document.positionAt(region.end),
languageId: region.languageId,
attrs: region.attrs
};
}
} else {
break;
}
}
return null;
}

export function getSingleLanguageDocument(
document: TextDocument,
regions: EmbeddedRegion[],
Expand Down Expand Up @@ -159,7 +185,8 @@ export function getLanguageRangesOfType(
result.push({
start: document.positionAt(r.start),
end: document.positionAt(r.end),
languageId: r.languageId
languageId: r.languageId,
attrs: r.attrs
});
}
}
Expand Down
29 changes: 29 additions & 0 deletions server/src/embeddedSupport/test/embeddedSupport.test.ts
Expand Up @@ -205,3 +205,32 @@ suite('Embedded <template> ', () => {
);
});
});

suite('Embedded region attrs', () => {
const src = `
<template other other-string="other">
</template>
<script setup lang="ts">
export default {
}
</script>
<style lang="scss" scoped>
</style>
<style lang="less" module>
</style>
`;

test('Basic', () => {
const { regions } = parseVueDocumentRegions(TextDocument.create('test://test.vue', 'vue', 0, src));

assert.equal(regions[0].attrs['other'], true);
assert.equal(regions[0].attrs['other-string'], 'other');
assert.equal(regions[1].attrs.setup, true);
assert.equal(regions[1].attrs.lang, 'ts');
assert.equal(regions[2].attrs['lang'], 'scss');
assert.equal(regions[2].attrs['scoped'], true);
assert.equal(regions[3].attrs['lang'], 'less');
assert.equal(regions[3].attrs['module'], true);
});
});
55 changes: 39 additions & 16 deletions server/src/embeddedSupport/vueDocumentRegionParser.ts
Expand Up @@ -4,12 +4,16 @@ import { removeQuotes } from '../utils/strings';
import { LanguageId } from './embeddedSupport';

export type RegionType = 'template' | 'script' | 'style' | 'custom';
export type RegionAttrKey = 'setup' | 'module' | 'scoped' | 'lang';

export type RegionAttrs = Partial<Record<RegionAttrKey, boolean | string>> & Partial<Record<string, boolean | string>>;

export interface EmbeddedRegion {
languageId: LanguageId;
start: number;
end: number;
type: RegionType;
attrs: RegionAttrs;
}

const defaultScriptLang = 'javascript';
Expand All @@ -22,6 +26,7 @@ export function parseVueDocumentRegions(document: TextDocument) {
let lastTagName = '';
let lastAttributeName = '';
let languageIdFromType: LanguageId | '' = '';
let attrs: Partial<Record<string, boolean | string>> = {};
const importedScripts: string[] = [];
let stakes = 0;

Expand All @@ -35,7 +40,8 @@ export function parseVueDocumentRegions(document: TextDocument) {
: defaultCSSLang,
start: scanner.getTokenOffset(),
end: scanner.getTokenEnd(),
type: 'style'
type: 'style',
attrs
});
languageIdFromType = '';
break;
Expand All @@ -44,7 +50,8 @@ export function parseVueDocumentRegions(document: TextDocument) {
languageId: languageIdFromType ? languageIdFromType : defaultScriptLang,
start: scanner.getTokenOffset(),
end: scanner.getTokenEnd(),
type: 'script'
type: 'script',
attrs
});
languageIdFromType = '';
break;
Expand All @@ -67,23 +74,24 @@ export function parseVueDocumentRegions(document: TextDocument) {
break;
case HtmlTokenType.AttributeName:
lastAttributeName = scanner.getTokenText();
attrs[lastAttributeName] = true;
break;
case HtmlTokenType.AttributeValue:
const attrValue = removeQuotes(scanner.getTokenText());
if (lastAttributeName === 'lang') {
languageIdFromType = getLanguageIdFromLangAttr(scanner.getTokenText());
languageIdFromType = getLanguageIdFromLangAttr(attrValue);
} else {
if (lastAttributeName === 'src' && lastTagName.toLowerCase() === 'script') {
let value = scanner.getTokenText();
if (value[0] === "'" || value[0] === '"') {
value = value.slice(1, value.length - 1);
}
const value = attrValue;
importedScripts.push(value);
}
}
attrs[lastAttributeName] = attrValue;
lastAttributeName = '';
break;
case HtmlTokenType.StartTagSelfClose:
case HtmlTokenType.EndTagClose:
attrs = {};
stakes--;
lastAttributeName = '';
languageIdFromType = '';
Expand All @@ -104,6 +112,7 @@ function scanTemplateRegion(scanner: Scanner, text: string): EmbeddedRegion | nu
let token = -1;
let start = 0;
let end: number;
const attrs: Partial<Record<string, boolean | string>> = {};

// Scan until finding matching template EndTag
// Also record immediate next StartTagClose to find start
Expand Down Expand Up @@ -138,9 +147,14 @@ function scanTemplateRegion(scanner: Scanner, text: string): EmbeddedRegion | nu
if (start === 0) {
if (token === HtmlTokenType.AttributeName) {
lastAttributeName = scanner.getTokenText();
attrs[lastAttributeName] = true;
} else if (token === HtmlTokenType.AttributeValue) {
const attrValue = removeQuotes(scanner.getTokenText());
if (lastAttributeName === 'lang') {
languageId = getLanguageIdFromLangAttr(scanner.getTokenText());
languageId = getLanguageIdFromLangAttr(attrValue);
}
if (lastAttributeName) {
attrs[lastAttributeName] = attrValue;
}
lastAttributeName = null;
} else if (token === HtmlTokenType.StartTagClose) {
Expand Down Expand Up @@ -168,7 +182,8 @@ function scanTemplateRegion(scanner: Scanner, text: string): EmbeddedRegion | nu
languageId,
start,
end: offset,
type: 'template'
type: 'template',
attrs
};
}
}
Expand All @@ -185,7 +200,8 @@ function scanTemplateRegion(scanner: Scanner, text: string): EmbeddedRegion | nu
languageId,
start,
end,
type: 'template'
type: 'template',
attrs
};
}

Expand All @@ -195,11 +211,12 @@ function scanCustomRegion(tagName: string, scanner: Scanner, text: string): Embe
let token = -1;
let start = 0;
let end: number;
const attrs: Partial<Record<string, boolean | string>> = {};

// Scan until finding matching template EndTag
// Also record immediate next StartTagClose to find start
let unClosedTag = 1;
let lastAttributeName = null;
let lastAttributeName: string | null = null;
while (unClosedTag !== 0) {
token = scanner.scan();

Expand All @@ -210,9 +227,14 @@ function scanCustomRegion(tagName: string, scanner: Scanner, text: string): Embe
if (start === 0) {
if (token === HtmlTokenType.AttributeName) {
lastAttributeName = scanner.getTokenText();
attrs[lastAttributeName] = true;
} else if (token === HtmlTokenType.AttributeValue) {
const attrValue = removeQuotes(scanner.getTokenText());
if (lastAttributeName === 'lang') {
languageId = getLanguageIdFromLangAttr(scanner.getTokenText());
languageId = getLanguageIdFromLangAttr(attrValue);
}
if (lastAttributeName) {
attrs[lastAttributeName] = attrValue;
}
lastAttributeName = null;
} else if (token === HtmlTokenType.StartTagClose) {
Expand Down Expand Up @@ -240,7 +262,8 @@ function scanCustomRegion(tagName: string, scanner: Scanner, text: string): Embe
languageId,
start,
end: offset,
type: 'custom'
type: 'custom',
attrs
};
}
}
Expand All @@ -257,12 +280,12 @@ function scanCustomRegion(tagName: string, scanner: Scanner, text: string): Embe
languageId,
start,
end,
type: 'custom'
type: 'custom',
attrs
};
}

function getLanguageIdFromLangAttr(lang: string): LanguageId {
let languageIdFromType = removeQuotes(lang);
function getLanguageIdFromLangAttr(languageIdFromType: string): LanguageId {
if (languageIdFromType === 'jade') {
languageIdFromType = 'pug';
}
Expand Down
2 changes: 1 addition & 1 deletion server/src/index.ts
Expand Up @@ -141,7 +141,7 @@ export {
printSourceMap,
stringifySourceMapNodes
} from './services/typescriptService/sourceMap';
export { createTemplateDiagnosticFilter } from './services/typescriptService/templateDiagnosticFilter';
export { createTemplateDiagnosticFilter as createTemplateDiagnosticFilter } from './services/typescriptService/diagnosticFilter';
export {
getTemplateTransformFunctions,
renderHelperName,
Expand Down

0 comments on commit d89071c

Please sign in to comment.