📄 syntax.c
字号:
/* * Editor: TDE, the Thomson-Davis Editor * Filename: syntax.c * Author: Jason Hood * Date: August 30, 1997 to September 13 * * All the syntax highlighting functions. * * My implementation of syntax highlighting is a hybrid of Elvis', by Steve * Kirkendall, and Rhide's, by Salvador E. Tropea and Robert Hoehne. * * There are four stages for a file to have syntax highlighting: locate the * language, parse it, set the initial flags, and finally display it. The * languages are in the "tde.shl" (_S_yntax _H_igh_L_ighting) file. If it can't * be found in the current directory, try HOME or the executable's directory * before giving up. The file is then parsed and syntatic features and keywords * (or just words to be highlighted) are noted. That's what I borrowed from * Elvis. When Elvis displays lines, it searches backwards to see if it's in a * comment or string. I didn't really like this idea, so I decided to use * Rhide's method of storing the type of each line. It's only required for the * multi-line stuff - everything else can be calculated on the fly. (The types * are explained in syntax_check()). To display lines requires re-calculating * the type of this line (since it's probably been modified), checking if that * would affect other lines (eg. starting a multi-line comment) and finally * displaying all the affected lines. The actual display is still done in * update_line(), but the checking is done here, as is determining what color * each character of the line will be (the syntax_attr() function). * * I was wondering how to handle C's 'l', 'u' and 'f' suffixes. Initially I was * going to handle it specially, along with Pascal and Ada's '#' character * (character constant for Pascal, based number for Ada). Later I realized that * PERL and Ada can have underscores in their numbers, so I added the "innumber" * command. It's not a perfect solution, but it's better to have invalid numbers * in the integer color, rather than valid numbers in bad. * * 980531: look for the syntax file in the HOME/executable directory and then * the current directory. This allows languages to be defined globally * with local add-ons. * 980716: made the language and keyword strings use FAR pointers; as a * consequence use of my_{malloc,strcmp,free}. * 980813: Allow languages to "inherit" from another language; the current * directory is again scanned first. * 990426: allocated the language name as part of the structure. * 011122: allow two of each comment type, using four characters maximum. */#include "tdestr.h" /* typedefs for global variables */#include "common.h"#include "define.h" /* editor function defs */#include "tdefunc.h" /* prototypes for all functions in tde */#include "syntax.h" /* syntax highlighting definitions */#include "config.h" /* syntax highlighting file processing */#define NO_LANGUAGE -2 /* return codes for scan_syntax */#define LANGUAGE_EXISTS 1/* * Some globals for file parsing. */static int initialized = FALSE; /* have the colors been defined? */static file_infos *syntax_file; /* file containing syntax highlighting */static int rescan = FALSE; /* rescan an already loaded language */extern char *line_in; /* input line buffer */extern char line_out[]; /* output line buffer */extern int line_no; /* current line number */static int error_no = 0; /* errors before this already displayed */extern int process_macro;extern MACRO *mac;extern TREE *branch;extern TREE *cfg_key_tree;static int prompt_line;static int in_macro = FALSE;extern int background;extern MENU_STR *user_menu;extern void make_menu( MENU_STR *, long *, int );static int out_of_memory; /* no room for keywords */static void lang_defaults( syntax_info * );static int language_list( char * );static void find_languages( char *, LIST **, int * );/* * Name: init_syntax * Purpose: see if the file has a recognized language; if so initialise * the syntax highlighting. * Passed: window: pointer to current window * Returns: OK for a recognized language; * NO_LANGUAGE for no recognized language/pattern; * ERROR otherwise. * Notes: if g_option.language is NULL, test the filename for a pattern; * if empty (ie "") then syntax highlighting is disabled; * otherwise load the language specified. If it couldn't be found * set g_option.language to NULL. * * 980526: Modified to suit the changed syntax_select. * 031128: Ignore non-file windows. */int init_syntax( TDE_WIN *window ){LANGUAGE *ll;char fname[PATH_MAX]; /* location of syntax highlighting file */int rc; if (g_option.language && *g_option.language == '\0') return( NO_LANGUAGE ); syntax_file = window->file_info; if (!g_option.language && syntax_file->file_name[0] == '\0') return( NO_LANGUAGE ); /* * See if the language has already been loaded. */ sort.order_array = sort_order.ignore; if (g_option.language && !rescan) { for (ll = g_status.language_list; ll != NULL; ll = ll->next) { if (!my_strcmp( (text_ptr)g_option.language, (text_ptr)ll->name )) break; } if (ll != NULL) { syntax_file->syntax = ll; syntax_init_lines( syntax_file ); window->syntax = TRUE; return( OK ); } } prompt_line = window->bottom_line; out_of_memory = FALSE; rc = NO_LANGUAGE; if (file_exists( SYNTAXFILE ) == 0) rc = scan_syntax( SYNTAXFILE, g_option.language, TRUE ); if (rc == NO_LANGUAGE) { join_strings( fname, g_status.argv[0], SYNTAXFILE ); rc = scan_syntax( fname, g_option.language, FALSE ); } if (rc == OK) syntax_init_lines( syntax_file ); else if (rc == NO_LANGUAGE && g_option.language) { combine_strings( line_out, syntax13a, g_option.language, syntax13b ); error( WARNING, prompt_line, line_out ); if (g_option.language == g_option_all.language) g_option_all.language = NULL; } /* * Don't turn off syntax highlighting if the new language wasn't found. */ if (!window->syntax) window->syntax = (rc == OK) ? TRUE : FALSE; return( rc );}/* * Name: scan_syntax * Purpose: to determine if the file should have syntax highlighting * Passed: name: name of the syntax highlighting file * language: language to find, or NULL to match filename pattern * local: is the syntax file in the current directory? * Returns: OK if the file has syntax highlighting; * NO_LANGUAGE if the language couldn't be identified; * ERROR otherwise (no memory). * * 980526: Modified to suit the changed syntax_select. * 980815: If the file is local, prepend the current directory to each pattern. */int scan_syntax( char *name, char *language, int local ){FILE *fp;char *line; /* pointer to the line read */char lang[MAX_COLS+2]; /* buffer to hold the language found */char parent[MAX_COLS+2]; /* buffer for the parent language */char dir[PATH_MAX]; /* buffer for current directory/pattern */char *pat; /* pointer into above for pattern */int key_no; /* index into key array */register LANGUAGE *ll; /* list of languages already loaded */int rc = NO_LANGUAGE;int load = TRUE;unsigned old_line;int cnt; /* * Open the file in binary mode, since I seek in syntax_init_colors(). */ if ((fp = fopen( name, "rb" )) == NULL) return( NO_LANGUAGE ); sort.order_array = sort_order.ignore; if (local) { get_current_directory( dir ); if (!strcmp( dir, g_status.argv[0] )) { *dir = '\0'; local = FALSE; } } else *dir = '\0'; pat = dir + strlen( dir ); line_no = 0; process_macro = FALSE; while (rc == NO_LANGUAGE) { do key_no = syntax_read_line( fp, &line ); while (key_no != SHL_LANGUAGE && key_no != ERROR); if (key_no == ERROR) break; line = parse_token( line, lang ); if (language != NULL) { if (!my_strcmp( (text_ptr)lang, (text_ptr)language )) rc = OK; else continue; } /* * Search for the parent language */ if (line != NULL) { /* * Skip the optional "from" */ line = parse_token( line, parent ); if (line != NULL) parse_token( line, parent ); } else *parent = '\0'; key_no = syntax_read_line( fp, &line ); if (key_no == SHL_PATTERN) { while (rc == NO_LANGUAGE && line != NULL) { line = parse_token( line, pat ); if (wildcard( dir, syntax_file->file_name )) rc = OK; } } else if (line_no > error_no) { ERRORLINE( syntax2, lang ); /* expecting pattern after language */ error_no = line_no; } } if (rc == OK) { for (ll = g_status.language_list; ll != NULL; ll = ll->next) { if (!my_strcmp( (text_ptr)lang, (text_ptr)ll->name )) break; } if (ll == NULL) { if (*parent) { rescan = FALSE; old_line = line_no; rc = scan_syntax( name, parent, local ); if (rc == NO_LANGUAGE && local) { join_strings( dir, g_status.argv[0], SYNTAXFILE ); rc = scan_syntax( dir, parent, FALSE ); } line_no = old_line; if (rc == OK) { ll = add_language( lang, syntax_file->syntax ); if (ll == NULL) load = FALSE; else syntax_file->syntax = ll; } else if (rc == NO_LANGUAGE) { load = FALSE; combine_strings( line_out, syntax13a, parent, syntax13b ); ERRORLINE( line_out, dir ); } } else { if ((ll = add_language( lang, NULL )) == NULL) { rc = ERROR; load = FALSE; } else syntax_file->syntax = ll; } } else { syntax_file->syntax = ll; load = rescan; } if (load) { cfg_key_tree = &ll->key_tree; user_menu = &ll->menu; if (user_menu->minor_cnt == 0 && ll->parent && ll->parent->menu.minor_cnt != 0) { /* * Make a duplicate of the parent's menu. */ cnt = user_menu->minor_cnt = ll->parent->menu.minor_cnt; cnt *= sizeof(MINOR_STR); user_menu->minor = malloc( cnt ); if (user_menu->minor != NULL) { memcpy( user_menu->minor, ll->parent->menu.minor, cnt ); user_menu->minor->line = NULL; } } /* * A new language is being loaded. If this is the first language * initialize all the colors. */ if (!initialized) { syntax_init_colors( fp ); initialized = TRUE; } if (ll->info->icase == MATCH) sort.order_array = sort_order.match; syntax_parse_file( fp, syntax_file->syntax ); if (user_menu->minor_cnt != 0) { get_minor_counts( user_menu ); make_menu( user_menu, NULL, 1 ); } } } fclose( fp ); return( rc );}/* * Name: syntax_read_line * Purpose: read a line from the syntax file * Passed: fp: file to read * line: pointer to the next token * Returns: the key number of the first token, or ERROR for eof or error. * line points past the first token, or NULL for eof or error. * Notes: global variable line_no is updated. * blank lines and comments are skipped. * unrecognized settings display a warning but continue. * macros ignore lines until another valid syntax feature. * * 980526: if the second last character is '\r', make it '\n'. * 990421: recognize new color parsing. */int syntax_read_line( FILE *fp, char **line ){char token[1042];int key;const char *errmsg; do { errmsg = NULL; do { ++line_no; if ((key = read_line( fp )) == EOF) { *line = NULL; return( ERROR ); }#if !defined( __UNIX__ ) /* * need to remove the CR manually, since I'm reading as binary */ if (line_in[key-1] == '\r') line_in[key-1] = '\0';#endif *line = parse_token( line_in, token ); } while (*token == '\0'); if (process_macro) return( SHL_MACRO ); if (*line == NULL) errmsg = config1; /* setting without value */ else { key = search( token, valid_syntax, SHL_NUM_FEATURES-1 ); if (key != ERROR) in_macro = (key == SHL_MACRO); else if (!in_macro) { key = parse_key( token ); if (key != ERROR) { if (key_func[KEY_IDX( key )] != TwoCharKey) errmsg = syntax4; /* syntax macro requires two-key */ else { key = SHL_MACRO; in_macro = TRUE; } } else { *line = line_in; key = parse_color( line, token, prompt_line, syntax1 ); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -