/** \file highlight.c Functions for syntax highlighting */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "fallback.h" #include "util.h" #include "wutil.h" #include "highlight.h" #include "tokenizer.h" #include "proc.h" #include "parser.h" #include "parse_util.h" #include "parser_keywords.h" #include "builtin.h" #include "function.h" #include "env.h" #include "expand.h" #include "sanity.h" #include "common.h" #include "complete.h" #include "output.h" #include "halloc.h" #include "halloc_util.h" #include "wildcard.h" #include "path.h" /** Number of elements in the highlight_var array */ #define VAR_COUNT ( sizeof(highlight_var)/sizeof(wchar_t *) ) static void highlight_universal_internal( wchar_t * buff, int *color, int pos, array_list_t *error ); /** The environment variables used to specify the color of different tokens. */ static wchar_t *highlight_var[] = { L"fish_color_normal", L"fish_color_error", L"fish_color_command", L"fish_color_end", L"fish_color_param", L"fish_color_comment", L"fish_color_match", L"fish_color_search_match", L"fish_color_operator", L"fish_color_escape", L"fish_color_quote", L"fish_color_redirection", L"fish_color_valid_path" } ; /** Tests if the specified string is the prefix of any valid path in the system. \return zero it this is not a valid prefix, non-zero otherwise */ static int is_potential_path( const wchar_t *path ) { wchar_t *tilde, *unescaped; wchar_t *in, *out; int has_magic = 0; int res = 0; void *context = halloc( 0, 0 ); tilde = expand_tilde( wcsdup(path) ); if( tilde ) { halloc_register( context, tilde ); unescaped = unescape( tilde, 1 ); if( unescaped ) { // debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped ); halloc_register( context, unescaped ); for( in = out = unescaped; *in; in++ ) { switch( *in ) { case PROCESS_EXPAND: case VARIABLE_EXPAND: case VARIABLE_EXPAND_SINGLE: case BRACKET_BEGIN: case BRACKET_END: case BRACKET_SEP: case ANY_CHAR: case ANY_STRING: case ANY_STRING_RECURSIVE: { has_magic = 1; break; } case INTERNAL_SEPARATOR: { break; } default: { *(out++) = *in; break; } } } *out = 0; if( !has_magic && wcslen( unescaped ) ) { int must_be_dir = 0; DIR *dir; must_be_dir = unescaped[wcslen(unescaped)-1] == L'/'; if( must_be_dir ) { dir = wopendir( unescaped ); res = !!dir; if( dir ) { closedir( dir ); } } else { wchar_t *dir_name, *base; struct wdirent *ent; dir_name = wdirname( halloc_wcsdup(context, unescaped) ); base = wbasename( halloc_wcsdup(context, unescaped) ); if( (wcscmp( dir_name, L"/" ) == 0 ) && (wcscmp( base, L"/" ) == 0 ) ) { res = 1; } else if( (dir = wopendir( dir_name )) ) { while( (ent = wreaddir( dir )) ) { if( wcsncmp( ent->d_name, base, wcslen(base) ) == 0 ) { res = 1; break; } } closedir( dir ); } } } } } halloc_free( context ); return res; } int highlight_get_color( int highlight ) { int i; int idx=0; int result = 0; if( highlight < 0 ) return FISH_COLOR_NORMAL; if( highlight >= (1< %d -> %ls", highlight, idx, val ); if( val == 0 ) val = env_get( highlight_var[0]); if( val ) result = output_color_code( val ); if( highlight & HIGHLIGHT_VALID_PATH ) { wchar_t *val2 = env_get( L"fish_color_valid_path" ); int result2 = output_color_code( val2 ); if( result == FISH_COLOR_NORMAL ) result = result2; else { if( result2 & FISH_COLOR_BOLD ) result |= FISH_COLOR_BOLD; if( result2 & FISH_COLOR_UNDERLINE ) result |= FISH_COLOR_UNDERLINE; } } return result; } /** Highligt operators (such as $, ~, %, as well as escaped characters. */ static void highlight_param( const wchar_t * buff, int *color, int pos, array_list_t *error ) { int mode = 0; int in_pos, len = wcslen( buff ); int bracket_count=0; wchar_t c; int normal_status = *color; for( in_pos=0; in_pos^ \\#;|&", buff[in_pos] ) ) { color[start_pos]=HIGHLIGHT_ESCAPE; color[in_pos+1]=normal_status; } else if( wcschr( L"uUxX01234567", buff[in_pos] ) ) { int i; long long res=0; int chars=2; int base=16; int byte = 0; wchar_t max_val = ASCII_MAX; switch( buff[in_pos] ) { case L'u': { chars=4; max_val = UCS2_MAX; break; } case L'U': { chars=8; max_val = WCHAR_MAX; break; } case L'x': { break; } case L'X': { byte=1; max_val = BYTE_MAX; break; } default: { base=8; chars=3; in_pos--; break; } } for( i=0; i= 0 ) last_val = color[i]; else color[i] = last_val; } /* Color potentially valid paths in a special path color if they are the current token. */ if( pos >= 0 && pos <= len ) { wchar_t *tok_begin, *tok_end; wchar_t *token; parse_util_token_extent( buff, pos, &tok_begin, &tok_end, 0, 0 ); if( tok_begin && tok_end ) { token = halloc_wcsndup( context, tok_begin, tok_end-tok_begin ); if( is_potential_path( token ) ) { for( i=tok_begin-buff; i < (tok_end-buff); i++ ) { color[i] |= HIGHLIGHT_VALID_PATH; } } } } highlight_universal_internal( buff, color, pos, error ); /* Spaces should not be highlighted at all, since it makes cursor look funky in some terminals */ for( i=0; buff[i]; i++ ) { if( iswspace(buff[i]) ) { color[i]=0; } } halloc_free( context ); } /** Perform quote and parenthesis highlighting on the specified string. */ static void highlight_universal_internal( wchar_t * buff, int *color, int pos, array_list_t *error ) { if( (pos >= 0) && (pos < wcslen(buff)) ) { /* Highlight matching quotes */ if( (buff[pos] == L'\'') || (buff[pos] == L'\"') ) { array_list_t l; al_init( &l ); int level=0; wchar_t prev_q=0; wchar_t *str=buff; int match_found=0; while(*str) { switch( *str ) { case L'\\': str++; break; case L'\"': case L'\'': if( level == 0 ) { level++; al_push_long( &l, (long)(str-buff) ); prev_q = *str; } else { if( prev_q == *str ) { long pos1, pos2; level--; pos1 = al_pop_long( &l ); pos2 = str-buff; if( pos1==pos || pos2==pos ) { color[pos1]|=HIGHLIGHT_MATCH<<16; color[pos2]|=HIGHLIGHT_MATCH<<16; match_found = 1; } prev_q = *str==L'\"'?L'\'':L'\"'; } else { level++; al_push_long( &l, (long)(str-buff) ); prev_q = *str; } } break; } if( (*str == L'\0')) break; str++; } al_destroy( &l ); if( !match_found ) color[pos] = HIGHLIGHT_ERROR<<16; } /* Highlight matching parenthesis */ if( wcschr( L"()[]{}", buff[pos] ) ) { int step = wcschr(L"({[", buff[pos])?1:-1; wchar_t dec_char = *(wcschr( L"()[]{}", buff[pos] ) + step); wchar_t inc_char = buff[pos]; int level = 0; wchar_t *str = &buff[pos]; int match_found=0; while( (str >= buff) && *str) { if( *str == inc_char ) level++; if( *str == dec_char ) level--; if( level == 0 ) { int pos2 = str-buff; color[pos]|=HIGHLIGHT_MATCH<<16; color[pos2]|=HIGHLIGHT_MATCH<<16; match_found=1; break; } str+= step; } if( !match_found ) color[pos] = HIGHLIGHT_ERROR<<16; } } } void highlight_universal( wchar_t * buff, int *color, int pos, array_list_t *error ) { int i; for( i=0; buff[i]; i++ ) color[i] = 0; highlight_universal_internal( buff, color, pos, error ); }