/* SQL Mode By Ted Zuschlag Revised March 2000. Original: January 1997. This SQL_MODE is a clone of epsilon's c_mode. The code coloring is 99% effective. You can tweak the list of reserved SQL keywords. See function, is_sql_keyword, below. This SQL_MODE was designed for Oracle's enhanced ANSI SQL, called SQL*PLUS, and their beautiful scripting language, PL/SQL. (Note Oracle-isms in the functions, is_sql_keyword, and tag_plsql_script below.) FYI, SQL sentences are delimited by ; and / . SQL comments are either /* * /, as in C, or a leading -- . SQL*PLUS also permits the REMARK keyword to designate comment lines. Quote delimiters are the single quote. I used Epsilon's tagging system in two different ways. 1) PL/SQL uses PROCEDURES and FUNCTIONS, and I tag these. Such tagging occurs for file extensions like, *.sq[lw123], *.osp|oss, *.ddl. (*.OSP stands for Oracle Stored Procedure. Thanks to author Steven Feuerstein for the idea!) 2) File extensions *.LST, use a variant tagging function. It goes like this-- SQL*PLUS offers the DESCRIBE command, whose purpose is to describe the layout (i.e. columns) of a table. Repeated use of DESCRIBE could be used to list a 'complete database schema'. Capture this output, and save as SCHEMA.LST (or whatever). Then have epsilon TAG_FILES in SCHEMA.LST. Tagged, will be the . When a source file shows a table named 'EMPLOYEE', epsilon commands like pluck-tag will hyper-link to the table layout of EMPLOYEE in SCHEMA.LST. Revision history: June 14 1997 Lugaru fixed a bug in the use of major_mode. */ #include "eel.h" #include "c.h" #include "colcode.h" char _sql_mode_name[] = "SQL"; color_class text color_scheme "standard-color" green on black; color_sql_from_here(safe) // Move backward to the nearest line guaranteed { // to start outside any colored region, return pos. int pos, limit; // We know safe is ok value to return. limit = point - safe; if (color_look_back && color_look_back < limit) limit = color_look_back; to_begin_line(); pos = point; while ((in_c_comment(limit) & IN_OLD_COMMENT) && search(-1, "/*")) { to_begin_line(); // move back out of an old-style comment pos = point; } point = pos; while (character(point - 2) == '\\') { // don't stay on contin line point--; to_begin_line(); } return point; } color_sql_range(from, to) // recolor just this section { // last colored region may go past to int t = -1, talk, s; char pat[200]; if (from >= to) return to; save_var point, matchstart, matchend; sql_init_color(from, to); point = from; talk = (to - from > 2000); // show status during long delays save_var case_fold = 0; strcpy(pat, "/<*>|--|[\"']"); if (!minimal_coloring) strcat(pat, "|[A-Za-z_][A-Za-z0-9_]*" "|-?%.?[0-9]([A-Za-z0-9._]|[Ee]-)*"); while (point < to) { if (!re_search(1, pat)) { t = size(); break; } t = matchstart; switch (character(point - 1)) { // check last char case '-': // found // one-line comment nl_forward(); set_character_color(t, point, color_class c_comment); break; case '*': // found /* starting comment search(1, "*/"); set_character_color(t, point, color_class c_comment); break; case '"': // found a string literal point = t; re_search(1, "\"([^\"\\\n]|\\(.|\n))*[\"\n]"); set_character_color(t, point, color_class c_number); if (get_character_color(point, (int *) 0, &s) == color_class c_number && s > to) // fix up after sql_init_color(point, to = s); // quoted "'s break; case '\'': // found a char const point = t; re_search(1, "\'([^\'\\\n]|\\(.|\n))*[\'\n]"); set_character_color(t, point, color_class c_charconst); break; default: // found identifier, kywd, or number set_character_color(t, point, sql_keyword_color(t)); break; } if (talk) note("Coloring SQL program: %d%% complete...", (point - from) * 100 / (to - from)); } sql_init_color(to, t); if (talk) note(""); return point; } sql_init_color(from, to) { if (from < to) set_character_color(from, to, minimal_coloring ? color_class c_function : color_class c_punctuation); } sql_keyword_color(from) // return color for "identifier" from here to point { // (something with alpha or digits) char buf[500]; if (point - from > sizeof(buf) - 10) save_var point = from + sizeof(buf) - 10; buf[0] = '|'; // get identifier, between | chars grab(from, point, buf + 1); if (index("0123456789-.", buf[1])) return sql_number_color(buf + 1); strcpy(buf + point - from + 1, "|"); if (is_sql_keyword(buf)) return color_class c_function; /* if (color_class c_function != color_class c_identifier && paren_follows()) return color_class c_function; return color_class c_identifier; see also colcode.e */ return color_class c_number; } is_sql_keyword(p) // is text in p (must be surrounded by |'s) a keyword? char *p; { case_fold = 1; if (strstr("|alter|index|table|add|modify|format|heading|notfound|found|rowcount|" "comment|on|column|commit|synonym|sysdate|btitle|title|spool|" "create|or|replace|asc|desc|as|is|at|view|sequence|role|synonym|tablespace|" "delete|from|where|and|drop|trigger|grant|rollback|storage|" "insert|into|values|group by|between|in|like|not|null|connect|" "rename|select|all|distinct|group|order|by|of|for|truncate|rename|" "having|update|set|union|intersect|minus|current|return|exists|" "start|PROCEDURE|FUNCTION|DECLARE|BEGIN|END|IF|THEN|ELSE|ELSIF|DEFAULT|" "REPLACE|PACKAGE|BODY|PRAGMA|TYPE|SUBTYPE|RECORD|number|date|FALSE|TRUE|" "raise|REF|CURSOR|OPEN|FETCH|INTO|CLOSE|IN|OUT|NULL|LOOP|BOOLEAN|varchar2|" "exception|constant|natural|positive|binary_integer|execute|rowtype|exit|when|", p)) return 1; return 0; } paren_follows() // is there whitespace followed by a "(" at point in buffer? { // if so, we assume it's a function name for (;; point++) { switch (curchar()) { case ' ': case '\t': continue; case '(': return 1; default: return 0; } } } sql_number_color(s) // return color for number in s char *s; // redefine to distinguish different bases { s = s; // silence eel's warning return color_class c_number; } /* char _sql_mode_name = "SQL"; */ command sql_mode() { major_mode = _sql_mode_name; mouse_goes_to_tag = 1; strcpy (comment_begin, "/*"); strcpy (comment_end, "*/"); strcpy (comment_start, "(/<*>|-- )[ ]*"); strcpy (comment_pattern, "--.*$|/<*>(.|)*<*>/"); comment_column = 0; tab_size = 3; want_backups = 1; indent_with_tabs = 0; recolor_range = color_sql_range; // set up coloring rules recolor_from_here = color_sql_from_here; if (want_code_coloring) // maybe turn on coloring when_setting_want_code_coloring(); make_mode(); /* color_look_back = 0; want_code_coloring = 1; */ /* mode_keys = sql_tab; */ /* over_mode = 1; */ /* display_column = 0; scroll mode */ } suffix_sql() { sql_mode(); } suffix_sqw() { sql_mode(); } suffix_sqx() { sql_mode(); } suffix_gqs() { sql_mode(); } suffix_osp() { sql_mode(); } suffix_oss() { sql_mode(); } suffix_ops() { sql_mode(); } suffix_opb() { sql_mode(); } suffix_ddl() { sql_mode(); } suffix_cmt() { sql_mode(); } suffix_sq1() { sql_mode(); } suffix_sq2() { sql_mode(); } suffix_sq3() { sql_mode(); } tag_plsql_script() { char func[70]; int start, opoint = point, ofold = case_fold; case_fold = 1; point = 0; while (re_search(1, "^[ \t]*(PROCEDURE |FUNCTION |TYPE )")) { re_search(1, "([A-Z0-9_]+)"); grab(start = find_group(1, 1), find_group(1, 0), func); add_tag(func, start); }; case_fold = ofold; point = opoint; } tag_suffix_sql() /* tag pl/sql proc and func calls */ { tag_plsql_script(); } tag_suffix_sqw() /* tag pl/sql proc and func calls */ { tag_plsql_script(); } tag_suffix_osp() /* tag pl/sql proc and func calls */ { tag_plsql_script(); } tag_suffix_ops() /* tag pl/sql proc and func calls */ { tag_plsql_script(); } tag_suffix_opb() /* tag pl/sql proc and func calls */ { tag_plsql_script(); } tag_suffix_oss() /* tag pl/sql proc and func calls */ { tag_plsql_script(); } tag_suffix_lst() /* tag output (*.lst) of SQL's describe command */ { char func[70]; int start, opoint = point, ofold = case_fold; case_fold = 1; point = 0; while (re_search(1, "^[A-Za-z0-9: ]+[ \t]*(desc )")) { re_search(1, "([A-Za-z0-9_]+)"); grab(start = find_group(1, 1), find_group(1, 0), func); add_tag(func, start); }; case_fold = ofold; point = opoint; }