/* SQL Mode By Ted Zuschlag January 1997 This is a hack based on epsilon's c_mode. The code coloring is 98% effective. (See function, is_sql_keyword, below.) Where possible, I simply re-used existing C functions. Otherwise, I created lookalike SQL functions. This SQL_MODE was designed for Oracle's enhanced ANSI SQL, called SQL*PLUS, and their procedural extension of SQL, called PL/SQL. (See functions, is_sql_keyword, and tag_plsql_script below.) SQL sentences are delimited by ; and / . SQL comments are either /* * / just 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. Arbitrarily, I decided to also tag the keywords, DECLARE, EXCEPTION, and REMARK. A different user may wish to omit the latter. Such tagging occurs for file extensions, *.sql, *.sqw, *.osp, *.oss . (*.OSP stands for Oracle Stored Procedure. Thanks to author Steven Feuerstein for the idea!) 2) File extensions *.LST, use a different 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 SQL*PLUS session, and save as SCHEMA.LST (or whatever). Then have epsilon TAG_FILES on SCHEMA.LST. Tagged, will be the the user specified when he dumped the 'complete database schema'. In other words, tagged will be a 'wall chart' of the 'complete database schema'. Revision history: June 14 1997 Lugaru fixed a bug in the use of major_mode. */ #include "eel.h" #include "c.h" #include "colcode.h" 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; { if (strstr("|alter|index|table|add|modify|" "comment|on|table|column|commit|synonym|sysdate|" "create|or|replace|asc|desc|as|is|at|view|" "delete|from|where|and|drop|" "insert|into|values|group by|between|in|like|not|null|" "rename|select|all|distinct|group|order|by|of|for|truncate|" "having|update|set|union|intersect|current|CURRENT|RETURN|return" "start|PROCEDURE|FUNCTION|BEGIN|END|IF|THEN|ELSE|ELSIF|" "CREATE|OR|AS|IS|REPLACE|PACKAGE|BODY|PRAGMA|SUBTYPE|" "CURSOR|OPEN|FETCH|INTO|CLOSE|IN|OUT|NULL|LOOP|", 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, "REMARK "); strcpy (comment_end, " "); strcpy (comment_start, "^REMARK +"); strcpy (comment_pattern, "^REMARK.+"); comment_column = 0; tab_size = 3; want_backups = 1; indent_with_tabs = 1; 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_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 )")) { re_search(1, "([A-Z0-9_]+)"); grab(start = find_group(1, 1), find_group(1, 0), func); add_tag(func, start); }; case_fold = 1; point = 0; while (re_search(1, "^[ \t]*(%<%<)")) { re_search(1, "([A-Z0-9_]+)"); grab(start = find_group(1, 1), find_group(1, 0), func); add_tag(func, start); }; case_fold = 1; point = 0; while (re_search(1, "^[ \t]*(DECLARE|EXCEPTION)")) { grab(start = find_group(1, 1), find_group(1, 0), func); add_tag(func, start); }; case_fold = 1; point = 0; while (re_search(1, "^[ \t]*(REMARK )")) { 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_oss() /* tag pl/sql proc and func calls */ { tag_plsql_script(); } tag_suffix_lst() /* tag pl/sql proc and func calls */ { 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; }