| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706 |
- import type {
- TokenType,
- Token
- } from "./type";
- const keywordsByLanguage: Record<string, string[]> = {
- "csharp": [
- "protected",
- "namespace",
- "interface",
- "internal",
- "readonly",
- "override",
- "abstract",
- "continue",
- "private",
- "virtual",
- "decimal",
- "finally",
- "public",
- "static",
- "sealed",
- "double",
- "ushort",
- "string",
- "object",
- "switch",
- "return",
- "typeof",
- "using",
- "float",
- "sbyte",
- "short",
- "ulong",
- "class",
- "while",
- "break",
- "catch",
- "throw",
- "false",
- "bool",
- "byte",
- "char",
- "long",
- "uint",
- "this",
- "base",
- "else",
- "case",
- "null",
- "true",
- "void",
- "get",
- "set",
- "int",
- "new",
- "for",
- "try",
- "if",
- "do"
- ],
- "typescript": [
- "implements",
- "instanceof",
- "interface",
- "undefined",
- "namespace",
- "continue",
- "function",
- "extends",
- "default",
- "finally",
- "unknown",
- "boolean",
- "switch",
- "return",
- "import",
- "export",
- "typeof",
- "number",
- "string",
- "symbol",
- "object",
- "while",
- "break",
- "const",
- "class",
- "async",
- "await",
- "catch",
- "throw",
- "super",
- "false",
- "never",
- "else",
- "case",
- "type",
- "from",
- "this",
- "void",
- "null",
- "true",
- "enum",
- "for",
- "let",
- "var",
- "try",
- "new",
- "any",
- "if",
- "do"
- ],
- "tsx": [
- "implements",
- "instanceof",
- "interface",
- "undefined",
- "namespace",
- "continue",
- "function",
- "extends",
- "default",
- "finally",
- "unknown",
- "boolean",
- "switch",
- "return",
- "import",
- "export",
- "typeof",
- "number",
- "string",
- "symbol",
- "object",
- "while",
- "break",
- "const",
- "class",
- "async",
- "await",
- "catch",
- "throw",
- "super",
- "false",
- "never",
- "else",
- "case",
- "type",
- "from",
- "this",
- "void",
- "null",
- "true",
- "enum",
- "for",
- "let",
- "var",
- "try",
- "new",
- "any",
- "if",
- "do"
- ],
- "javascript": [
- "implements",
- "instanceof",
- "interface",
- "undefined",
- "continue",
- "function",
- "extends",
- "default",
- "finally",
- "switch",
- "return",
- "import",
- "export",
- "typeof",
- "while",
- "break",
- "const",
- "class",
- "async",
- "await",
- "catch",
- "throw",
- "super",
- "false",
- "else",
- "case",
- "type",
- "from",
- "this",
- "void",
- "null",
- "true",
- "for",
- "let",
- "var",
- "try",
- "new",
- "if",
- "do"
- ],
- "jsx": [
- "implements",
- "instanceof",
- "interface",
- "undefined",
- "continue",
- "function",
- "extends",
- "default",
- "finally",
- "switch",
- "return",
- "import",
- "export",
- "typeof",
- "while",
- "break",
- "const",
- "class",
- "async",
- "await",
- "catch",
- "throw",
- "super",
- "false",
- "else",
- "case",
- "type",
- "from",
- "this",
- "void",
- "null",
- "true",
- "for",
- "let",
- "var",
- "try",
- "new",
- "if",
- "do"
- ],
- "sql": [
- "REFERENCES",
- "PRIMARY",
- "FOREIGN",
- "BETWEEN",
- "SELECT",
- "INSERT",
- "VALUES",
- "UPDATE",
- "DELETE",
- "CREATE",
- "HAVING",
- "OFFSET",
- "EXISTS",
- "WHERE",
- "TABLE",
- "ALTER",
- "INDEX",
- "INNER",
- "RIGHT",
- "OUTER",
- "GROUP",
- "ORDER",
- "LIMIT",
- "FROM",
- "INTO",
- "DROP",
- "JOIN",
- "LEFT",
- "FULL",
- "NULL",
- "LIKE",
- "SET",
- "KEY",
- "AND",
- "NOT",
- "ON",
- "BY",
- "AS",
- "OR",
- "IS",
- "IN"
- ],
- "bash": [
- "install",
- "remove",
- "docker",
- "export",
- "source",
- "return",
- "start",
- "build",
- "while",
- "yarn",
- "pnpm",
- "test",
- "lint",
- "echo",
- "sudo",
- "brew",
- "bash",
- "then",
- "done",
- "esac",
- "else",
- "elif",
- "case",
- "npm",
- "npx",
- "bun",
- "add",
- "run",
- "dev",
- "apt",
- "git",
- "zsh",
- "for",
- "cd",
- "ls",
- "sh",
- "fi",
- "if",
- "do",
- "in"
- ],
- "python": [
- "nonlocal",
- "continue",
- "finally",
- "lambda",
- "global",
- "import",
- "except",
- "return",
- "assert",
- "False",
- "yield",
- "class",
- "raise",
- "while",
- "break",
- "async",
- "await",
- "elif",
- "pass",
- "None",
- "True",
- "with",
- "from",
- "else",
- "def",
- "and",
- "not",
- "del",
- "try",
- "for",
- "or",
- "is",
- "in",
- "as",
- "if"
- ]
- };
- const allKeywordsList = [
- "implements",
- "instanceof",
- "REFERENCES",
- "protected",
- "namespace",
- "interface",
- "undefined",
- "internal",
- "readonly",
- "override",
- "abstract",
- "continue",
- "function",
- "nonlocal",
- "private",
- "virtual",
- "decimal",
- "finally",
- "extends",
- "default",
- "unknown",
- "boolean",
- "PRIMARY",
- "FOREIGN",
- "BETWEEN",
- "install",
- "public",
- "static",
- "sealed",
- "double",
- "ushort",
- "string",
- "object",
- "switch",
- "return",
- "typeof",
- "import",
- "export",
- "number",
- "symbol",
- "SELECT",
- "INSERT",
- "VALUES",
- "UPDATE",
- "DELETE",
- "CREATE",
- "HAVING",
- "OFFSET",
- "EXISTS",
- "remove",
- "docker",
- "source",
- "lambda",
- "global",
- "except",
- "assert",
- "using",
- "float",
- "sbyte",
- "short",
- "ulong",
- "class",
- "while",
- "break",
- "catch",
- "throw",
- "false",
- "const",
- "async",
- "await",
- "super",
- "never",
- "WHERE",
- "TABLE",
- "ALTER",
- "INDEX",
- "INNER",
- "RIGHT",
- "OUTER",
- "GROUP",
- "ORDER",
- "LIMIT",
- "start",
- "build",
- "False",
- "yield",
- "raise",
- "bool",
- "byte",
- "char",
- "long",
- "uint",
- "this",
- "base",
- "else",
- "case",
- "null",
- "true",
- "void",
- "type",
- "from",
- "enum",
- "FROM",
- "INTO",
- "DROP",
- "JOIN",
- "LEFT",
- "FULL",
- "NULL",
- "LIKE",
- "yarn",
- "pnpm",
- "test",
- "lint",
- "echo",
- "sudo",
- "brew",
- "bash",
- "then",
- "done",
- "esac",
- "elif",
- "pass",
- "None",
- "True",
- "with",
- "get",
- "set",
- "int",
- "new",
- "for",
- "try",
- "let",
- "var",
- "any",
- "SET",
- "KEY",
- "AND",
- "NOT",
- "npm",
- "npx",
- "bun",
- "add",
- "run",
- "dev",
- "apt",
- "git",
- "zsh",
- "def",
- "and",
- "not",
- "del",
- "if",
- "do",
- "ON",
- "BY",
- "AS",
- "OR",
- "IS",
- "IN",
- "cd",
- "ls",
- "sh",
- "fi",
- "in",
- "or",
- "is",
- "as"
- ];
- const tokenRegexCache: Record<string, RegExp> = {};
- const getTokenRegex = (language?: string): RegExp => {
- const cacheKey = language || "default";
- const cachedRegex = tokenRegexCache[cacheKey];
- if (cachedRegex) {
- return cachedRegex;
- }
- let keywordList = allKeywordsList;
- if (language) {
- const langKeywords = keywordsByLanguage[language.toLowerCase()];
- if (langKeywords) {
- keywordList = langKeywords;
- }
- }
- const keywordsRegexStr = "(?<!\\.)\\b(?:" + keywordList.join("|") + ")\\b";
- const keywordControlRegexStr = "(?<!\\.)\\b(?:return|if|else|for|while|try|catch|finally|switch|case|break|continue|throw|yield)\\b";
- let commentRegexStr = "//.*|/\\*[\\s\\S]*?\\*/|#.*|<!--[\\s\\S]*?-->";
- if (language) {
- switch (language.toLowerCase()) {
- case "bash":
- case "python":
- commentRegexStr = "#.*";
- break;
- case "sql":
- commentRegexStr = "--.*|/\\*[\\s\\S]*?\\*/";
- break;
- case "html":
- case "xml":
- commentRegexStr = "<!--[\\s\\S]*?-->";
- break;
- case "javascript":
- case "typescript":
- case "jsx":
- case "tsx":
- case "csharp":
- commentRegexStr = "//.*|/\\*[\\s\\S]*?\\*/";
- break;
- }
- }
- let bashTextRegexStr = "";
- if (language && language.toLowerCase() === "bash") {
- bashTextRegexStr = "(?<text>(?:[a-zA-Z0-9_@]+[.:/-]+)+[a-zA-Z0-9_@]+)|";
- }
- const regexStr = "(?<comment>" + commentRegexStr + ")|" +
- '(?<string>(["\\\'`])(?:(?=(\\\\?))\\4.)*?\\3)|' +
- bashTextRegexStr +
- "(?<keywordControl>" + keywordControlRegexStr + ")|" +
- "(?<keyword>" + keywordsRegexStr + ")|" +
- "(?<htmlTag>(?<=<\\/?)[a-z][a-zA-Z0-9-]*\\b)|" +
- "(?<component>\\b[A-Z][a-zA-Z0-9_$]*\\b)|" +
- "(?<function>\\b[a-zA-Z_$][a-zA-Z0-9_$]*(?=\\s*\\()|\\b[a-zA-Z_$][a-zA-Z0-9_$]*(?=\\s*[:=]\\s*(?:async\\s+)?(?:\\([^)]*\\)|[a-zA-Z_$][a-zA-Z0-9_$]*)\\s*=>)|\\b[a-zA-Z_$][a-zA-Z0-9_$]*(?=\\s*[:=]\\s*function\\b))|" +
- "(?<property>\\b[a-zA-Z_$][a-zA-Z0-9_$]*(?=\\s*[:=]))|" +
- "(?<variable>\\b[a-zA-Z_$][a-zA-Z0-9_$]*\\b)|" +
- "(?<number>\\b\\d+(?:\\.\\d+)?\\b)|" +
- "(?<arrow>=>)|" +
- "(?<punctuationTag>[<>/])|" +
- "(?<operator>[+\\-*=!&|%^~]+)|" +
- "(?<punctuation>[{}()\\[\\].,;:])";
- const regex = new RegExp(regexStr, "g");
- tokenRegexCache[cacheKey] = regex;
- return regex;
- };
- export const parseCode = (code: string, language?: string): Token[] => {
- const tokens: Token[] = [];
- let lastIndex = 0;
- const tokenRegex = getTokenRegex(language);
- tokenRegex.lastIndex = 0;
- let match: RegExpExecArray | null;
- while ((match = tokenRegex.exec(code)) !== null) {
- if (match.index > lastIndex) {
- tokens.push({
- type: "text",
- value: code.substring(lastIndex, match.index)
- });
- }
- const groups = match.groups || {};
- const type = Object.keys(groups).find(key => groups[key] !== undefined) as TokenType;
- if (type) {
- tokens.push({
- type,
- value: groups[type] as string
- });
- }
- lastIndex = match.index + match[0].length;
- }
- if (lastIndex < code.length) {
- tokens.push({
- type: "text",
- value: code.substring(lastIndex)
- });
- }
- return tokens;
- };
- export const splitTokensIntoLines = (tokens: Token[]): Token[][] => {
- const lines: Token[][] = [];
- let currentLine: Token[] = [];
- tokens.forEach((token) => {
- if (token.value.includes("\n")) {
- const parts = token.value.split("\n");
- parts.forEach((part, index) => {
- if (part.length > 0) {
- currentLine.push({
- ...token,
- value: part
- });
- }
- if (index < parts.length - 1) {
- lines.push(currentLine);
- currentLine = [];
- }
- });
- } else {
- currentLine.push(token);
- }
- });
- lines.push(currentLine);
- return lines;
- };
- export const getLineIndentation = (lineTokens: Token[]): number => {
- let indentation = 0;
- for (const token of lineTokens) {
- if (token.type === "text") {
- const match = token.value.match(/^(\s+)/);
- if(match) {
- const matchLen = (match[1] as string).length;
- indentation += matchLen;
- if(matchLen !== token.value.length) {
- break;
- }
- } else if (token.value.length > 0) {
- break;
- }
- } else {
- break;
- }
- }
- return indentation;
- };
|