Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: optimize code fold #4202

Merged
merged 2 commits into from
Dec 1, 2024
Merged
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
236 changes: 128 additions & 108 deletions apps/docs/components/docs/components/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ export type TransformTokensTypes = TransformTokens[0][0] & {

const startFlag = ["{", "["];
const endFlag = ["}", "]"];
const specialStartFlag = ["("];
const specialEndFlag = [")"];
const defaultFoldFlagList = ["cn", "HTMLAttributes"];
const defaultShowFlagList = ["Component", "forwardRef", "App", "Example", "AddForm", "SignupForm"];
const isElementStartRegex = /^\s*</;
const isElementEndRegex = /^\s*<\//;

/**
* Transform tokens from `prism-react-renderer` to wrap them in folder structure
Expand All @@ -26,156 +24,141 @@ const defaultShowFlagList = ["Component", "forwardRef", "App", "Example", "AddFo
export function transformTokens(tokens: TransformTokens, folderLine = 10) {
const result: TransformTokens = [];
let lastIndex = 0;
let isShowFolder = false;
let fold = false;
let startElementName = "";

tokens.forEach((token, index) => {
if (index < lastIndex) {
return;
}
token.forEach((t) => {
(t as TransformTokensTypes).index = index;
});
result.push(token);

let startToken: TransformTokens[0][0] = null as any;
let mergedStartFlagList = [...startFlag];
const lineContent = getLineContent(token);
const {isStartTag, isEndTag} = checkIsElement(lineContent);

token.forEach((t) => {
if (defaultFoldFlagList.some((text) => t.content.includes(text))) {
// If cn then need to judge whether it is import token
if (t.content.includes("cn") && token.some((t) => t.content === "import")) {
return;
}
// If it has startElementName means it is within the element range
if (startElementName) {
if (isEndTag) {
// Judge whether it is the end tag of the element then reset startElementName
const {endElementName} = getElementName(lineContent);

// If HTMLAttributes then need to judge whether it have start flag
if (
t.content.includes("HTMLAttributes") &&
!token.some((t) => startFlag.includes(t.content))
) {
return;
if (endElementName === startElementName) {
startElementName = "";
}

fold = true;
mergedStartFlagList.push(...specialStartFlag);
}

if (mergedStartFlagList.includes(t.content)) {
startToken = t;
return;
} else if (isStartTag) {
const {startElementName: elementName, endElementName} = getElementName(lineContent);

if (!endElementName) {
startElementName = elementName;

return;
}
}

if (defaultShowFlagList.some((text) => t.content.includes(text))) {
isShowFolder = true;
let startToken: TransformTokens[0][0] = null as any;

token.forEach((t) => {
if (startFlag.includes(t.content)) {
startToken = t;
}
});

const mergedOptions = fold
? {
specialEndFlag,
specialStartFlag,
}
: undefined;
const isFolder = checkIsFolder(token, mergedOptions);
const isFolder = checkIsFolder(token);

if (isFolder && startToken) {
const endIndex = findEndIndex(tokens, index + 1, mergedOptions);

// Greater than or equal to folderLine then will folder otherwise it will show directly
if (endIndex !== -1 && (endIndex - index >= folderLine || isShowFolder || fold)) {
lastIndex = endIndex;
const folder = tokens.slice(index + 1, endIndex);
const endToken = tokens[endIndex];
const ellipsisToken: TransformTokensTypes = {
types: ["ellipsis"],
content: "",
class: "custom-folder ellipsis-token",
};
const copyContent: TransformTokensTypes = {
types: ["copy"],
content: "",
folderContent: folder,
class: "custom-folder copy-token",
};

endToken.forEach((t, _, arr) => {
let className = "";

className += "custom-folder";
if (t.content.trim() === "" && (arr.length === 3 || arr.length === 4)) {
// Add length check to sure it's added to } token
className += " empty-token";
}
(t as TransformTokensTypes).class = className;
});

startToken.types = ["folderStart"];
(startToken as TransformTokensTypes).folderContent = folder;
(startToken as TransformTokensTypes).summaryContent = [
...token,
ellipsisToken,
copyContent,
...endToken,
];
(startToken as TransformTokensTypes).index = index;
if (isShowFolder && !fold) {
(startToken as TransformTokensTypes).open = true;
}

result.push([startToken]);

isShowFolder = false;
fold = false;
const nextLineContent = tokens.slice(index + 1, index + 2).reduce((acc, line) => {
return acc + getLineContent(line);
}, "");
const isNextLineObjectFolder = checkIsObjectContent(nextLineContent);
const isArrayFolder = lineContent.trim().endsWith("[");

if (isNextLineObjectFolder || isArrayFolder) {
const endIndex = findEndIndex(tokens, index + 1);

// Greater than or equal to folderLine then will folder otherwise it will show directly
if (endIndex !== -1 && endIndex - index >= folderLine) {
lastIndex = endIndex;
const folder = tokens.slice(index + 1, endIndex);
const endToken = tokens[endIndex];

(endToken[0] as TransformTokensTypes).class = "first-custom-folder";

const ellipsisToken: TransformTokensTypes = {
types: ["ellipsis"],
content: "",
class: "custom-folder ellipsis-token",
};
const copyContent: TransformTokensTypes = {
types: ["copy"],
content: "",
folderContent: folder,
class: "custom-folder copy-token",
};

endToken.forEach((t, _, arr) => {
let className = (t as TransformTokensTypes).class || "";

className += " custom-folder";
if (t.content.trim() === "" && (arr.length === 3 || arr.length === 4)) {
// Add length check to sure it's added to } token
className += " empty-token";
}
(t as TransformTokensTypes).class = className;
});

startToken.types = ["folderStart"];
(startToken as TransformTokensTypes).folderContent = folder;
(startToken as TransformTokensTypes).summaryContent = [
...token,
ellipsisToken,
copyContent,
...endToken,
];
(startToken as TransformTokensTypes).index = index;
// isShowFolder && ((startToken as TransformTokensTypes).open = true);

winchesHe marked this conversation as resolved.
Show resolved Hide resolved
result.splice(result.length - 1, 1, [startToken]);

return;
return;
}
}
}
token.forEach((t) => {
(t as TransformTokensTypes).index = index;
});
result.push(token);
});

return result;
}

interface SpecialOptions {
specialStartFlag?: string[];
specialEndFlag?: string[];
}

function checkIsFolder(
token: TransformTokens[0],
{specialStartFlag, specialEndFlag}: SpecialOptions = {},
) {
function checkIsFolder(token: TransformTokens[0]) {
const stack: string[] = [];
const mergedStartFlagList = specialStartFlag ? [...startFlag, ...specialStartFlag] : startFlag;
const mergedEndFlagList = specialEndFlag ? [...endFlag, ...specialEndFlag] : endFlag;

for (const t of token) {
if (mergedStartFlagList.includes(t.content)) {
if (startFlag.includes(t.content)) {
stack.push(t.content);
} else if (mergedEndFlagList.includes(t.content)) {
} else if (endFlag.includes(t.content)) {
stack.pop();
}
}

return stack.length !== 0;
}

function findEndIndex(
tokens: TransformTokens,
startIndex: number,
{specialStartFlag, specialEndFlag}: SpecialOptions = {},
) {
function findEndIndex(tokens: TransformTokens, startIndex: number) {
const stack: string[] = ["flag"];
const mergedStartFlagList = specialStartFlag ? [...startFlag, ...specialStartFlag] : startFlag;
const mergedEndFlagList = specialEndFlag ? [...endFlag, ...specialEndFlag] : endFlag;

for (let i = startIndex; i < tokens.length; i++) {
const token = tokens[i];

for (const line of token) {
const transformLine = line.content.replace(/\$/g, "");

if (mergedStartFlagList.includes(transformLine)) {
if (startFlag.includes(transformLine)) {
stack.push("flag");
} else if (mergedEndFlagList.includes(transformLine)) {
} else if (endFlag.includes(transformLine)) {
winchesHe marked this conversation as resolved.
Show resolved Hide resolved
stack.pop();
}

Expand All @@ -187,3 +170,40 @@ function findEndIndex(

return -1;
}

function checkIsElement(lineContent: string) {
return {
isStartTag: isElementStartRegex.test(lineContent),
isEndTag: isElementEndRegex.test(lineContent),
};
}

function getElementName(lineContent: string) {
const startElementName = lineContent.match(/^\s*<([a-zA-Z.]+)/);
const endElementName = lineContent.match(/^\s*<\/([a-zA-Z.]+)>/);

return {
startElementName: startElementName?.[1] || (lineContent.includes("<>") ? "<>" : ""),
endElementName: endElementName?.[1] || (lineContent.includes("</>") ? "</>" : ""),
};
}

function getLineContent(token: TransformTokens[0]) {
return token.reduce((acc, t) => acc + t.content, "");
}

function checkIsObjectContent(lineContent: string) {
lineContent = lineContent.trim();
// first: match { a }
// second: match { a: b }
// third: match { a (b) }
// fourth: match /** */
const isObjectContent = /^([\w]+,?$)|([\w\[.\]]+:)|([\w]+\s?\(.*?\)$)|(^\/\*\*)/.test(
lineContent,
);
const hasEqual = /\s=\s/.test(lineContent);
const hasFunction = lineContent.includes("function");
const hasVariable = /var|let|const/.test(lineContent);

return isObjectContent && !hasEqual && !hasFunction && !hasVariable;
}
Loading