UNPKG

@tidbcloud/codemirror-extension-sql-parser

Version:

codemiror extension to parser editor content to SQL statements

106 lines (103 loc) 3.26 kB
import { ensureSyntaxTree } from '@codemirror/language'; import { StateEffect, StateField } from '@codemirror/state'; import { EditorView } from '@codemirror/view'; // state effect const statementsEffect = StateEffect.define(); // state field const statementsField = StateField.define({ create() { return []; }, update(value, tr) { for (let effect of tr.effects) { if (effect.is(statementsEffect)) { return effect.value; } } return value; } }); // regex to match use statement // examples: // - use test; // - use test ; // - USE `test`; const useStatementRegex = /^use\s+[`]?([a-zA-Z0-9_-]+)[`]?\s*;$/i; const ddlStatementRegex = /^(create|drop|alter|truncate|rename|comment|grant|revoke)/i; // event listener const statementsParser = () => { let first = true; return EditorView.updateListener.of((v) => { if (first) { first = false; } else { if (!v.docChanged && !v.viewportChanged) return; } const { state } = v; const statements = []; let database = ''; // syntaxTree() only parse the content in the visible area // syntaxTree(state) ensureSyntaxTree(state, v.view.viewport.to, 1 * 1000) ?.cursor() .iterate((node) => { if (node.name === 'Script') { // root node return true; } if (node.name === 'Statement') { let type = 'other'; const content = state.sliceDoc(node.from, node.to); const match = content.match(useStatementRegex); if (match) { database = match[1]; type = 'use'; } else if (content.match(ddlStatementRegex)) { type = 'ddl'; } const lineFrom = state.doc.lineAt(node.from); const lineTo = state.doc.lineAt(node.to); statements.push({ from: node.from, to: node.to, lineFrom: lineFrom.number, lineTo: lineTo.number, content, database, type }); } return true; }); v.view.dispatch({ effects: statementsEffect.of(statements) }); }); }; //------------------- function getSqlStatements(state) { return state.field(statementsField); } function getNearbyStatement(state, pos) { const allStatements = getSqlStatements(state); let target; // find the nearest statement before the pos for (let i = allStatements.length - 1; i >= 0; i--) { const s = allStatements[i]; if (s.to <= pos) { target = s; break; } } // if no statement found, choose the first statement if (!target && allStatements.length > 0) { target = allStatements[0]; } return target; } //------------------- function sqlParser() { return [statementsField, statementsParser()]; } export { getNearbyStatement, getSqlStatements, sqlParser, useStatementRegex };