Files
ChuSQL/chusql-web/frontend/src/core/sqlHighlight.ts
T

77 lines
2.4 KiB
TypeScript

export const SQL_TOKEN_COLORS = {
keyword: '#569CD6',
string: '#CE9178',
number: '#B5CEA8',
comment: '#6A9955',
} as const;
export const SQL_PLAIN_COLOR = 'rgb(200, 200, 200)';
export type SqlTokenKind = 'plain' | keyof typeof SQL_TOKEN_COLORS;
export interface SqlToken { text: string; kind: SqlTokenKind }
// SQL 词法高亮:与执行器共用同一套 token 配色。
const keywords = new Set([
'select', 'from', 'where', 'insert', 'into', 'values', 'update', 'set', 'delete',
'create', 'table', 'index', 'on', 'drop', 'alter', 'and', 'or', 'not', 'null',
'order', 'by', 'group', 'having', 'limit', 'offset', 'join', 'left', 'right',
'inner', 'outer', 'as', 'distinct', 'primary', 'key', 'int', 'integer', 'str',
'text', 'bool', 'boolean', 'true', 'false',
]);
const wordChar = /[A-Za-z0-9_]/;
// 切分 SQL 为关键字、字符串、数字、注释与普通片段。
export function tokenizeSql(sql: string): SqlToken[] {
const tokens: SqlToken[] = [];
let plain = '';
let index = 0;
const flush = () => { if (plain) { tokens.push({ text: plain, kind: 'plain' }); plain = ''; } };
while (index < sql.length) {
const char = sql[index];
if (char === "'") {
flush();
let end = index + 1;
while (end < sql.length) {
if (sql[end] !== "'") { end += 1; continue; }
if (sql[end + 1] === "'") { end += 2; continue; }
end += 1;
break;
}
tokens.push({ text: sql.slice(index, end), kind: 'string' });
index = end;
continue;
}
if (char === '-' && sql[index + 1] === '-') {
flush();
const end = sql.indexOf('\n', index);
tokens.push({ text: end < 0 ? sql.slice(index) : sql.slice(index, end), kind: 'comment' });
index = end < 0 ? sql.length : end;
continue;
}
if (char >= '0' && char <= '9') {
flush();
let end = index;
while (end < sql.length && (wordChar.test(sql[end]) || sql[end] === '.')) end += 1;
tokens.push({ text: sql.slice(index, end), kind: 'number' });
index = end;
continue;
}
if (wordChar.test(char)) {
let end = index;
while (end < sql.length && wordChar.test(sql[end])) end += 1;
const word = sql.slice(index, end);
if (keywords.has(word.toLowerCase())) { flush(); tokens.push({ text: word, kind: 'keyword' }); }
else plain += word;
index = end;
continue;
}
plain += char;
index += 1;
}
flush();
return tokens;
}