2005-09-20 13:26:39 +00:00
/** \file highlight.c
Functions for syntax highlighting
*/
2006-08-11 01:18:35 +00:00
# include "config.h"
2005-09-20 13:26:39 +00:00
# include <stdlib.h>
# include <stdio.h>
# include <sys/stat.h>
# include <unistd.h>
# include <errno.h>
# include <wchar.h>
# include <wctype.h>
# include <termios.h>
# include <signal.h>
2006-02-28 13:17:16 +00:00
# include "fallback.h"
2005-09-20 13:26:39 +00:00
# include "util.h"
2006-02-28 13:17:16 +00:00
2005-09-20 13:26:39 +00:00
# include "wutil.h"
# include "highlight.h"
# include "tokenizer.h"
# include "proc.h"
# include "parser.h"
2006-01-30 16:51:50 +00:00
# include "parse_util.h"
2007-04-22 09:50:26 +00:00
# include "parser_keywords.h"
2005-09-20 13:26:39 +00:00
# include "builtin.h"
# include "function.h"
# include "env.h"
# include "expand.h"
# include "sanity.h"
# include "common.h"
# include "complete.h"
# include "output.h"
2006-07-28 13:52:03 +00:00
# include "wildcard.h"
2006-10-19 11:50:23 +00:00
# include "path.h"
2005-09-20 13:26:39 +00:00
2006-06-20 00:50:10 +00:00
/**
Number of elements in the highlight_var array
*/
2006-06-14 13:22:40 +00:00
# define VAR_COUNT ( sizeof(highlight_var) / sizeof(wchar_t *) )
2012-02-22 01:55:56 +00:00
static void highlight_universal_internal ( const wcstring & buff ,
2012-02-21 19:45:13 +00:00
std : : vector < int > & color ,
2011-12-27 03:18:46 +00:00
int pos ) ;
2005-09-20 13:26:39 +00:00
/**
The environment variables used to specify the color of different tokens .
*/
2012-02-07 04:14:19 +00:00
static const wchar_t * const highlight_var [ ] =
2005-09-20 13:26:39 +00:00
{
L " fish_color_normal " ,
2006-06-14 13:22:40 +00:00
L " fish_color_error " ,
2005-09-20 13:26:39 +00:00
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 " ,
2006-05-26 16:46:38 +00:00
L " fish_color_operator " ,
2006-05-27 12:35:16 +00:00
L " fish_color_escape " ,
2006-06-14 13:22:40 +00:00
L " fish_color_quote " ,
L " fish_color_redirection " ,
2012-02-07 04:14:19 +00:00
L " fish_color_valid_path " ,
L " fish_color_autosuggestion "
} ;
2005-09-20 13:26:39 +00:00
2006-06-20 00:50:10 +00:00
/**
2011-12-27 03:18:46 +00:00
Tests if the specified string is the prefix of any valid path in the system .
2012-02-18 02:08:08 +00:00
\ require_dir Whether the valid path must be a directory
2012-02-20 10:13:31 +00:00
\ out_path If non - null , the path on output
2006-06-20 00:50:10 +00:00
\ return zero it this is not a valid prefix , non - zero otherwise
*/
2011-12-27 03:18:46 +00:00
// PCA DOES_IO
2012-02-20 10:13:31 +00:00
static bool is_potential_path ( const wcstring & cpath , wcstring * out_path = NULL , bool require_dir = false )
2006-06-14 13:22:40 +00:00
{
2011-12-27 03:18:46 +00:00
ASSERT_IS_BACKGROUND_THREAD ( ) ;
const wchar_t * unescaped , * in ;
wcstring cleaned_path ;
2006-06-14 13:22:40 +00:00
int has_magic = 0 ;
2012-01-02 21:40:03 +00:00
bool res = false ;
2011-12-27 03:18:46 +00:00
wcstring path ( cpath ) ;
expand_tilde ( path ) ;
2012-01-02 21:40:03 +00:00
if ( ! unescape_string ( path , 1 ) )
return false ;
2011-12-27 03:18:46 +00:00
unescaped = path . c_str ( ) ;
// debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped );
for ( in = 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 :
{
cleaned_path + = * in ;
break ;
}
}
}
2012-02-18 02:08:08 +00:00
if ( ! has_magic & & ! cleaned_path . empty ( ) )
2011-12-27 03:18:46 +00:00
{
2012-02-18 02:08:08 +00:00
bool must_be_full_dir = cleaned_path [ cleaned_path . length ( ) - 1 ] = = L ' / ' ;
2011-12-27 03:18:46 +00:00
DIR * dir ;
2012-02-18 02:08:08 +00:00
if ( must_be_full_dir )
2011-12-27 03:18:46 +00:00
{
2012-02-18 17:11:22 +00:00
dir = wopendir ( cleaned_path ) ;
2011-12-27 03:18:46 +00:00
if ( dir )
{
2012-02-20 10:13:31 +00:00
res = true ;
if ( out_path )
* out_path = cleaned_path ;
2011-12-27 03:18:46 +00:00
closedir ( dir ) ;
}
}
else
{
wcstring dir_name = wdirname ( cleaned_path ) ;
wcstring base_name = wbasename ( cleaned_path ) ;
if ( dir_name = = L " / " & & base_name = = L " / " )
{
2012-01-02 21:40:03 +00:00
res = true ;
2012-02-20 10:13:31 +00:00
if ( out_path )
* out_path = cleaned_path ;
2011-12-27 03:18:46 +00:00
}
2012-02-18 17:11:22 +00:00
else if ( ( dir = wopendir ( dir_name ) ) )
2011-12-27 03:18:46 +00:00
{
wcstring ent ;
2012-02-18 02:08:08 +00:00
bool is_dir ;
2012-02-20 10:13:31 +00:00
while ( wreaddir_resolving ( dir , dir_name , ent , & is_dir ) )
2011-12-27 03:18:46 +00:00
{
2012-02-18 02:08:08 +00:00
if ( string_prefixes_string ( base_name , ent ) & & ( ! require_dir | | is_dir ) )
2011-12-27 03:18:46 +00:00
{
2012-01-02 21:40:03 +00:00
res = true ;
2012-02-20 10:13:31 +00:00
if ( out_path ) {
out_path - > assign ( dir_name ) ;
out_path - > push_back ( L ' / ' ) ;
out_path - > append ( ent ) ;
path_make_canonical ( * out_path ) ;
/* We actually do want a trailing / for directories, since it makes autosuggestion a bit nicer */
if ( is_dir )
out_path - > push_back ( L ' / ' ) ;
}
2011-12-27 03:18:46 +00:00
break ;
}
}
closedir ( dir ) ;
}
}
}
2006-06-14 13:22:40 +00:00
return res ;
2011-12-27 03:18:46 +00:00
2006-06-14 13:22:40 +00:00
}
2012-02-13 17:52:17 +00:00
rgb_color_t highlight_get_color ( int highlight , bool is_background )
2012-02-12 01:07:56 +00:00
{
size_t i ;
int idx = 0 ;
rgb_color_t result ;
if ( highlight < 0 )
return rgb_color_t : : normal ( ) ;
if ( highlight > ( 1 < < VAR_COUNT ) )
return rgb_color_t : : normal ( ) ;
for ( i = 0 ; i < VAR_COUNT ; i + + )
{
if ( highlight & ( 1 < < i ) )
{
idx = i ;
break ;
}
}
env_var_t val_wstr = env_get_string ( highlight_var [ idx ] ) ;
// debug( 1, L"%d -> %d -> %ls", highlight, idx, val );
if ( val_wstr . missing ( ) )
val_wstr = env_get_string ( highlight_var [ 0 ] ) ;
if ( ! val_wstr . missing ( ) )
result = parse_color ( val_wstr , is_background ) ;
if ( highlight & HIGHLIGHT_VALID_PATH )
{
env_var_t val2_wstr = env_get_string ( L " fish_color_valid_path " ) ;
const wcstring val2 = val2_wstr . missing ( ) ? L " " : val2_wstr . c_str ( ) ;
rgb_color_t result2 = parse_color ( val2 , is_background ) ;
2012-02-13 02:05:59 +00:00
if ( result . is_normal ( ) )
2012-02-12 01:07:56 +00:00
result = result2 ;
else
{
if ( result2 . is_bold ( ) )
result . set_bold ( true ) ;
if ( result2 . is_underline ( ) )
result . set_underline ( true ) ;
}
}
return result ;
}
2006-06-14 13:22:40 +00:00
2005-09-20 13:26:39 +00:00
2006-05-27 00:56:18 +00:00
/**
Highligt operators ( such as $ , ~ , % , as well as escaped characters .
*/
2012-03-01 00:14:03 +00:00
static void highlight_param ( const wcstring & buffstr , std : : vector < int > & colors , int pos , wcstring_list_t * error )
{
const wchar_t * const buff = buffstr . c_str ( ) ;
2012-03-30 16:58:25 +00:00
enum { e_unquoted , e_single_quoted , e_double_quoted } mode = e_unquoted ;
2012-03-01 00:14:03 +00:00
size_t in_pos , len = buffstr . size ( ) ;
2006-05-26 16:46:38 +00:00
int bracket_count = 0 ;
2012-03-01 00:14:03 +00:00
int normal_status = colors . at ( 0 ) ;
2011-12-27 03:18:46 +00:00
2012-03-30 16:58:25 +00:00
for ( in_pos = 0 ; in_pos < len ; in_pos + + )
2006-05-26 16:46:38 +00:00
{
2012-03-01 00:14:03 +00:00
wchar_t c = buffstr . at ( in_pos ) ;
2006-05-26 16:46:38 +00:00
switch ( mode )
{
2012-03-04 04:38:16 +00:00
/*
Mode 0 means unquoted string
*/
2012-03-30 16:58:25 +00:00
case e_unquoted :
2006-05-26 16:46:38 +00:00
{
if ( c = = L ' \\ ' )
{
2012-03-30 16:58:25 +00:00
size_t start_pos = in_pos ;
2006-05-30 00:35:35 +00:00
in_pos + + ;
2011-12-27 03:18:46 +00:00
2006-05-30 00:35:35 +00:00
if ( wcschr ( L " ~% " , buff [ in_pos ] ) )
2006-05-26 16:46:38 +00:00
{
2006-05-30 00:35:35 +00:00
if ( in_pos = = 1 )
2006-05-27 13:40:26 +00:00
{
2012-03-01 00:14:03 +00:00
colors . at ( start_pos ) = HIGHLIGHT_ESCAPE ;
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-27 13:40:26 +00:00
}
2006-05-30 00:35:35 +00:00
}
else if ( buff [ in_pos ] = = L ' , ' )
{
if ( bracket_count )
2006-05-27 13:40:26 +00:00
{
2012-03-01 00:14:03 +00:00
colors . at ( start_pos ) = HIGHLIGHT_ESCAPE ;
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-27 13:40:26 +00:00
}
2006-05-30 00:35:35 +00:00
}
2007-09-25 11:55:14 +00:00
else if ( wcschr ( L " abefnrtv*?$() { } [ ] ' \ " <>^ \\ #;|& " , buff [ in_pos ] ) )
2006-05-30 00:35:35 +00:00
{
2012-03-01 00:14:03 +00:00
colors . at ( start_pos ) = HIGHLIGHT_ESCAPE ;
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-30 00:35:35 +00:00
}
2007-09-25 11:55:14 +00:00
else if ( wcschr ( L " c " , buff [ in_pos ] ) )
2012-03-04 04:38:16 +00:00
{
2012-03-01 00:14:03 +00:00
colors . at ( start_pos ) = HIGHLIGHT_ESCAPE ;
2012-03-04 04:38:16 +00:00
if ( in_pos + 2 < colors . size ( ) )
colors . at ( in_pos + 2 ) = normal_status ;
2007-09-25 11:55:14 +00:00
}
2006-05-30 00:35:35 +00:00
else if ( wcschr ( L " uUxX01234567 " , buff [ in_pos ] ) )
{
int i ;
long long res = 0 ;
int chars = 2 ;
int base = 16 ;
2011-12-27 03:18:46 +00:00
2006-05-30 00:35:35 +00:00
wchar_t max_val = ASCII_MAX ;
2011-12-27 03:18:46 +00:00
2006-05-30 00:35:35 +00:00
switch ( buff [ in_pos ] )
2006-05-26 16:46:38 +00:00
{
2006-05-30 00:35:35 +00:00
case L ' u ' :
{
chars = 4 ;
max_val = UCS2_MAX ;
break ;
}
2012-03-04 04:38:16 +00:00
2006-05-30 00:35:35 +00:00
case L ' U ' :
2006-05-26 16:46:38 +00:00
{
2006-05-30 00:35:35 +00:00
chars = 8 ;
max_val = WCHAR_MAX ;
break ;
2006-05-26 16:46:38 +00:00
}
2012-03-04 04:38:16 +00:00
2006-05-30 00:35:35 +00:00
case L ' x ' :
2006-05-26 16:46:38 +00:00
{
2006-05-30 00:35:35 +00:00
break ;
2006-05-26 16:46:38 +00:00
}
2012-03-04 04:38:16 +00:00
2006-05-30 00:35:35 +00:00
case L ' X ' :
2006-05-26 16:46:38 +00:00
{
2006-05-30 00:35:35 +00:00
max_val = BYTE_MAX ;
break ;
2006-05-26 16:46:38 +00:00
}
2012-03-04 04:38:16 +00:00
2006-05-30 00:35:35 +00:00
default :
{
base = 8 ;
chars = 3 ;
in_pos - - ;
break ;
2011-12-27 03:18:46 +00:00
}
2006-05-30 00:35:35 +00:00
}
2011-12-27 03:18:46 +00:00
2006-05-30 00:35:35 +00:00
for ( i = 0 ; i < chars ; i + + )
{
int d = convert_digit ( buff [ + + in_pos ] , base ) ;
2011-12-27 03:18:46 +00:00
2006-05-30 00:35:35 +00:00
if ( d < 0 )
{
in_pos - - ;
break ;
2006-05-26 16:46:38 +00:00
}
2011-12-27 03:18:46 +00:00
2006-05-30 00:35:35 +00:00
res = ( res * base ) | d ;
}
2012-03-04 04:38:16 +00:00
2006-05-30 00:35:35 +00:00
if ( ( res < = max_val ) )
{
2012-03-01 00:14:03 +00:00
colors . at ( start_pos ) = HIGHLIGHT_ESCAPE ;
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-30 00:35:35 +00:00
}
else
2011-12-27 03:18:46 +00:00
{
2012-03-01 00:14:03 +00:00
colors . at ( start_pos ) = HIGHLIGHT_ERROR ;
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-26 16:46:38 +00:00
}
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
}
2011-12-27 03:18:46 +00:00
else
2006-05-26 16:46:38 +00:00
{
switch ( buff [ in_pos ] ) {
case L ' ~ ' :
case L ' % ' :
{
if ( in_pos = = 0 )
{
2012-03-01 00:14:03 +00:00
colors . at ( in_pos ) = HIGHLIGHT_OPERATOR ;
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-26 16:46:38 +00:00
}
break ;
}
2012-03-04 04:38:16 +00:00
2006-06-01 23:04:38 +00:00
case L ' $ ' :
{
2011-12-27 03:18:46 +00:00
wchar_t n = buff [ in_pos + 1 ] ;
2012-03-01 00:14:03 +00:00
colors . at ( in_pos ) = ( n = = L ' $ ' | | wcsvarchr ( n ) ) ? HIGHLIGHT_OPERATOR : HIGHLIGHT_ERROR ;
colors . at ( in_pos + 1 ) = normal_status ;
2006-06-01 23:04:38 +00:00
break ;
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
case L ' * ' :
case L ' ? ' :
2006-05-27 00:56:18 +00:00
case L ' ( ' :
case L ' ) ' :
2006-05-26 16:46:38 +00:00
{
2012-03-01 00:14:03 +00:00
colors . at ( in_pos ) = HIGHLIGHT_OPERATOR ;
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-26 16:46:38 +00:00
break ;
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
case L ' { ' :
{
2012-03-01 00:14:03 +00:00
colors . at ( in_pos ) = HIGHLIGHT_OPERATOR ;
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-26 16:46:38 +00:00
bracket_count + + ;
2011-12-27 03:18:46 +00:00
break ;
2006-05-26 16:46:38 +00:00
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
case L ' } ' :
{
2012-03-01 00:14:03 +00:00
colors . at ( in_pos ) = HIGHLIGHT_OPERATOR ;
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-26 16:46:38 +00:00
bracket_count - - ;
2011-12-27 03:18:46 +00:00
break ;
2006-05-26 16:46:38 +00:00
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
case L ' , ' :
{
if ( bracket_count )
{
2012-03-01 00:14:03 +00:00
colors . at ( in_pos ) = HIGHLIGHT_OPERATOR ;
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-26 16:46:38 +00:00
}
2012-03-04 04:38:16 +00:00
2011-12-27 03:18:46 +00:00
break ;
2006-05-26 16:46:38 +00:00
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
case L ' \' ' :
{
2012-03-01 00:14:03 +00:00
colors . at ( in_pos ) = HIGHLIGHT_QUOTE ;
2012-03-30 16:58:25 +00:00
mode = e_single_quoted ;
2011-12-27 03:18:46 +00:00
break ;
2006-05-26 16:46:38 +00:00
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
case L ' \" ' :
{
2012-03-01 00:14:03 +00:00
colors . at ( in_pos ) = HIGHLIGHT_QUOTE ;
2012-03-30 16:58:25 +00:00
mode = e_double_quoted ;
2006-05-26 16:46:38 +00:00
break ;
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
}
2011-12-27 03:18:46 +00:00
}
2006-05-26 16:46:38 +00:00
break ;
}
2012-03-04 04:38:16 +00:00
/*
Mode 1 means single quoted string , i . e ' foo '
*/
2012-03-30 16:58:25 +00:00
case e_single_quoted :
2006-05-26 16:46:38 +00:00
{
if ( c = = L ' \\ ' )
{
int start_pos = in_pos ;
switch ( buff [ + + in_pos ] )
{
case ' \\ ' :
case L ' \' ' :
{
2012-03-01 00:14:03 +00:00
colors . at ( start_pos ) = HIGHLIGHT_ESCAPE ;
colors . at ( in_pos + 1 ) = HIGHLIGHT_QUOTE ;
2006-05-26 16:46:38 +00:00
break ;
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
case 0 :
{
return ;
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
}
2011-12-27 03:18:46 +00:00
2006-05-26 16:46:38 +00:00
}
if ( c = = L ' \' ' )
{
2012-03-30 16:58:25 +00:00
mode = e_unquoted ;
2012-03-01 00:14:03 +00:00
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-26 16:46:38 +00:00
}
2011-12-27 03:18:46 +00:00
2006-05-26 16:46:38 +00:00
break ;
}
2012-03-04 04:38:16 +00:00
/*
Mode 2 means double quoted string , i . e . " foo "
*/
2012-03-30 16:58:25 +00:00
case e_double_quoted :
2006-05-26 16:46:38 +00:00
{
switch ( c )
{
case ' " ' :
{
2012-03-30 16:58:25 +00:00
mode = e_unquoted ;
2012-03-01 00:14:03 +00:00
colors . at ( in_pos + 1 ) = normal_status ;
2006-05-26 16:46:38 +00:00
break ;
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
case ' \\ ' :
{
int start_pos = in_pos ;
switch ( buff [ + + in_pos ] )
{
case L ' \0 ' :
{
return ;
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
case ' \\ ' :
case L ' $ ' :
case ' " ' :
{
2012-03-01 00:14:03 +00:00
colors . at ( start_pos ) = HIGHLIGHT_ESCAPE ;
colors . at ( in_pos + 1 ) = HIGHLIGHT_QUOTE ;
2006-05-26 16:46:38 +00:00
break ;
}
}
break ;
}
2012-03-04 04:38:16 +00:00
2006-05-26 16:46:38 +00:00
case ' $ ' :
{
2006-06-01 23:04:38 +00:00
wchar_t n = buff [ in_pos + 1 ] ;
2012-03-01 00:14:03 +00:00
colors . at ( in_pos ) = ( n = = L ' $ ' | | wcsvarchr ( n ) ) ? HIGHLIGHT_OPERATOR : HIGHLIGHT_ERROR ;
colors . at ( in_pos + 1 ) = HIGHLIGHT_QUOTE ;
2006-05-26 16:46:38 +00:00
break ;
}
2012-03-04 04:38:16 +00:00
2011-12-27 03:18:46 +00:00
}
2006-05-26 16:46:38 +00:00
break ;
}
}
}
}
2012-01-30 10:45:55 +00:00
static int has_expand_reserved ( const wchar_t * str )
2008-02-04 23:09:05 +00:00
{
while ( * str )
{
if ( * str > = EXPAND_RESERVED & &
* str < = EXPAND_RESERVED_END )
{
return 1 ;
}
str + + ;
}
return 0 ;
}
2012-03-30 18:16:24 +00:00
/* Attempts to suggest a completion for a command we handle specially, like 'cd'. Returns true if we recognized the command (even if we couldn't think of a suggestion for it) */
2012-02-20 10:13:31 +00:00
bool autosuggest_suggest_special ( const wcstring & str , const wcstring & working_directory , wcstring & outString ) {
if ( str . empty ( ) )
return false ;
wcstring cmd ;
2012-03-30 18:16:24 +00:00
bool had_cmd = false , recognized_cmd = false ;
2012-02-20 10:13:31 +00:00
wcstring suggestion ;
tokenizer tok ;
for ( tok_init ( & tok , str . c_str ( ) , TOK_SQUASH_ERRORS ) ;
tok_has_next ( & tok ) ;
tok_next ( & tok ) )
{
int last_type = tok_last_type ( & tok ) ;
switch ( last_type )
{
case TOK_STRING :
{
if ( had_cmd )
{
2012-03-30 18:16:24 +00:00
recognized_cmd = ( cmd = = L " cd " ) ;
if ( recognized_cmd )
2012-02-20 10:13:31 +00:00
{
wcstring dir = tok_last ( & tok ) ;
wcstring suggested_path ;
2012-04-22 03:08:08 +00:00
2012-02-20 10:13:31 +00:00
if ( is_potential_path ( dir , & suggested_path , true /* require directory */ ) ) {
2012-04-22 03:08:08 +00:00
2012-03-30 18:16:24 +00:00
/* suggested_path needs to actually have dir as a prefix (perhaps with different case). Handle stuff like ./ */
bool wants_dot_slash = string_prefixes_string ( L " ./ " , dir ) ;
bool has_dot_slash = string_prefixes_string ( L " ./ " , suggested_path ) ;
2012-04-22 03:08:08 +00:00
2012-03-30 18:16:24 +00:00
if ( wants_dot_slash & & ! has_dot_slash ) {
suggested_path . insert ( 0 , L " ./ " ) ;
} else if ( ! wants_dot_slash & & has_dot_slash ) {
suggested_path . erase ( 0 , 2 ) ;
}
2012-04-22 03:08:08 +00:00
bool wants_tilde = string_prefixes_string ( L " ~ " , dir ) ;
bool has_tilde = string_prefixes_string ( L " ~ " , suggested_path ) ;
if ( wants_tilde & & ! has_tilde ) {
// The input string has a tilde, the output string does not
// Extract the tilde part, expand it, see if the expansion prefixes the suggestion
// If so, replace it with the tilde part
size_t slash_idx = dir . find ( L ' / ' ) ;
const wcstring tilde_part ( dir , 0 , slash_idx ) ; //note that slash_idx is npos this will return everything
// Expand the tilde
wcstring expanded_tilde = tilde_part ;
expand_tilde ( expanded_tilde ) ;
// Replace it
if ( string_prefixes_string ( expanded_tilde , suggested_path ) ) {
suggested_path . replace ( 0 , expanded_tilde . size ( ) , tilde_part ) ;
}
}
2012-02-20 10:13:31 +00:00
suggestion = str ;
suggestion . erase ( tok_get_pos ( & tok ) ) ;
suggestion . append ( suggested_path ) ;
}
}
}
else
{
/*
Command . First check that the command actually exists .
*/
cmd = tok_last ( & tok ) ;
bool expanded = expand_one ( cmd , EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES ) ;
if ( ! expanded | | has_expand_reserved ( cmd . c_str ( ) ) )
{
}
else
{
int is_subcommand = 0 ;
int mark = tok_get_pos ( & tok ) ;
if ( parser_keywords_is_subcommand ( cmd ) )
{
int sw ;
if ( cmd = = L " builtin " )
{
}
else if ( cmd = = L " command " )
{
}
tok_next ( & tok ) ;
sw = parser_keywords_is_switch ( tok_last ( & tok ) ) ;
if ( ! parser_keywords_is_block ( cmd ) & &
sw = = ARG_SWITCH )
{
}
else
{
if ( sw = = ARG_SKIP )
{
mark = tok_get_pos ( & tok ) ;
}
is_subcommand = 1 ;
}
tok_set_pos ( & tok , mark ) ;
}
if ( ! is_subcommand )
{
had_cmd = true ;
}
}
}
break ;
}
case TOK_REDIRECT_NOCLOB :
case TOK_REDIRECT_OUT :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_FD :
{
if ( ! had_cmd )
{
break ;
}
tok_next ( & tok ) ;
break ;
}
case TOK_PIPE :
case TOK_BACKGROUND :
{
had_cmd = false ;
break ;
}
case TOK_END :
{
had_cmd = false ;
break ;
}
case TOK_COMMENT :
{
break ;
}
case TOK_ERROR :
default :
{
break ;
}
}
}
tok_destroy ( & tok ) ;
2012-03-30 18:16:24 +00:00
if ( recognized_cmd ) {
2012-02-20 10:13:31 +00:00
outString . swap ( suggestion ) ;
2012-03-30 18:16:24 +00:00
}
return recognized_cmd ;
2012-02-20 10:13:31 +00:00
}
2012-02-19 07:26:39 +00:00
bool autosuggest_handle_special ( const wcstring & str , const wcstring & working_directory , bool * outSuggestionOK ) {
2012-02-19 02:54:36 +00:00
ASSERT_IS_BACKGROUND_THREAD ( ) ;
assert ( outSuggestionOK ! = NULL ) ;
if ( str . empty ( ) )
return false ;
wcstring cmd ;
bool had_cmd = false ;
bool handled = false ;
bool suggestionOK = true ;
tokenizer tok ;
for ( tok_init ( & tok , str . c_str ( ) , TOK_SQUASH_ERRORS ) ;
tok_has_next ( & tok ) ;
tok_next ( & tok ) )
{
int last_type = tok_last_type ( & tok ) ;
switch ( last_type )
{
case TOK_STRING :
{
if ( had_cmd )
{
if ( cmd = = L " cd " )
{
wcstring dir = tok_last ( & tok ) ;
if ( expand_one ( dir , EXPAND_SKIP_CMDSUBST ) )
{
/* We can specially handle the cd command */
handled = true ;
bool is_help = string_prefixes_string ( dir , L " --help " ) | | string_prefixes_string ( dir , L " -h " ) ;
2012-02-19 05:56:30 +00:00
if ( is_help ) {
2012-02-19 02:54:36 +00:00
suggestionOK = false ;
2012-02-19 05:56:30 +00:00
} else {
wchar_t * path = path_allocate_cdpath ( dir . c_str ( ) , working_directory . c_str ( ) ) ;
if ( path = = NULL ) {
suggestionOK = false ;
} else if ( paths_are_same_file ( working_directory , path ) ) {
/* Don't suggest the working directory as the path! */
suggestionOK = false ;
} else {
suggestionOK = true ;
}
free ( path ) ;
2012-02-19 02:54:36 +00:00
}
}
}
}
else
{
/*
Command . First check that the command actually exists .
*/
cmd = tok_last ( & tok ) ;
bool expanded = expand_one ( cmd , EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES ) ;
if ( ! expanded | | has_expand_reserved ( cmd . c_str ( ) ) )
{
2005-09-20 13:26:39 +00:00
2012-02-19 02:54:36 +00:00
}
else
{
int is_subcommand = 0 ;
int mark = tok_get_pos ( & tok ) ;
if ( parser_keywords_is_subcommand ( cmd ) )
{
int sw ;
if ( cmd = = L " builtin " )
{
}
else if ( cmd = = L " command " )
{
}
tok_next ( & tok ) ;
sw = parser_keywords_is_switch ( tok_last ( & tok ) ) ;
if ( ! parser_keywords_is_block ( cmd ) & &
sw = = ARG_SWITCH )
{
}
else
{
if ( sw = = ARG_SKIP )
{
mark = tok_get_pos ( & tok ) ;
}
is_subcommand = 1 ;
}
tok_set_pos ( & tok , mark ) ;
}
if ( ! is_subcommand )
{
had_cmd = true ;
}
}
}
break ;
}
case TOK_REDIRECT_NOCLOB :
case TOK_REDIRECT_OUT :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_FD :
{
if ( ! had_cmd )
{
break ;
}
tok_next ( & tok ) ;
break ;
}
case TOK_PIPE :
case TOK_BACKGROUND :
{
had_cmd = false ;
break ;
}
case TOK_END :
{
had_cmd = false ;
break ;
}
case TOK_COMMENT :
{
break ;
}
case TOK_ERROR :
default :
{
break ;
}
}
}
tok_destroy ( & tok ) ;
* outSuggestionOK = suggestionOK ;
return handled ;
}
2011-12-27 03:18:46 +00:00
2012-02-11 01:54:21 +00:00
// This function does I/O
2012-02-21 19:45:13 +00:00
static void tokenize ( const wchar_t * const buff , std : : vector < int > & color , const int pos , wcstring_list_t * error , const env_vars & vars ) {
2011-12-27 03:18:46 +00:00
ASSERT_IS_BACKGROUND_THREAD ( ) ;
2012-02-21 19:45:13 +00:00
2012-01-30 10:45:55 +00:00
wcstring cmd ;
2005-09-20 13:26:39 +00:00
int had_cmd = 0 ;
2012-01-30 10:45:55 +00:00
wcstring last_cmd ;
2006-06-12 14:12:33 +00:00
int len ;
2010-09-18 01:51:16 +00:00
int accept_switches = 1 ;
2011-12-27 03:18:46 +00:00
2006-12-14 01:35:37 +00:00
int use_function = 1 ;
int use_command = 1 ;
2012-02-19 02:54:36 +00:00
int use_builtin = 1 ;
2011-12-27 03:18:46 +00:00
2006-06-21 00:48:36 +00:00
CHECK ( buff , ) ;
2010-09-18 01:51:16 +00:00
2006-06-12 14:12:33 +00:00
len = wcslen ( buff ) ;
2010-09-18 01:51:16 +00:00
2005-09-20 13:26:39 +00:00
if ( ! len )
return ;
2010-09-18 01:51:16 +00:00
2012-02-21 19:45:13 +00:00
std : : fill ( color . begin ( ) , color . end ( ) , - 1 ) ;
2010-09-18 01:51:16 +00:00
2011-12-27 03:18:46 +00:00
tokenizer tok ;
2012-02-17 23:55:54 +00:00
for ( tok_init ( & tok , buff , TOK_SHOW_COMMENTS | TOK_SQUASH_ERRORS ) ;
2011-12-27 03:18:46 +00:00
tok_has_next ( & tok ) ;
tok_next ( & tok ) )
{
2005-09-20 13:26:39 +00:00
int last_type = tok_last_type ( & tok ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
switch ( last_type )
{
case TOK_STRING :
{
if ( had_cmd )
{
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
/*Parameter */
wchar_t * param = tok_last ( & tok ) ;
if ( param [ 0 ] = = L ' - ' )
{
2006-12-04 12:07:07 +00:00
if ( wcscmp ( param , L " -- " ) = = 0 )
{
accept_switches = 0 ;
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_PARAM ;
2006-12-04 12:07:07 +00:00
}
else if ( accept_switches )
{
2012-01-30 10:45:55 +00:00
if ( complete_is_valid_option ( last_cmd . c_str ( ) , param , error , false /* no autoload */ ) )
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_PARAM ;
2006-12-04 12:07:07 +00:00
else
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_ERROR ;
2006-12-04 12:07:07 +00:00
}
2005-09-20 13:26:39 +00:00
else
2006-12-04 12:07:07 +00:00
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_PARAM ;
2006-12-04 12:07:07 +00:00
}
2005-09-20 13:26:39 +00:00
}
else
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_PARAM ;
2011-12-27 03:18:46 +00:00
}
2012-01-30 10:45:55 +00:00
if ( cmd = = L " cd " )
2006-06-15 01:00:38 +00:00
{
2012-02-01 00:50:03 +00:00
wcstring dir = tok_last ( & tok ) ;
if ( expand_one ( dir , EXPAND_SKIP_CMDSUBST ) )
2006-06-15 01:00:38 +00:00
{
2012-02-01 00:50:03 +00:00
int is_help = string_prefixes_string ( dir , L " --help " ) | | string_prefixes_string ( dir , L " -h " ) ;
if ( ! is_help & & ! path_can_get_cdpath ( dir ) )
2006-06-17 10:41:28 +00:00
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_ERROR ;
2006-06-17 10:41:28 +00:00
}
2006-06-15 01:00:38 +00:00
}
}
2011-12-27 03:18:46 +00:00
2012-03-01 00:14:03 +00:00
/* Highlight the parameter. highlight_param wants to write one more color than we have characters (hysterical raisins) so allocate one more in the vector. But don't copy it back. */
const wcstring param_str = param ;
int tok_pos = tok_get_pos ( & tok ) ;
2012-03-31 21:05:14 +00:00
std : : vector < int > : : const_iterator where = color . begin ( ) + tok_pos ;
std : : vector < int > subcolors ( where , where + param_str . size ( ) ) ;
subcolors . push_back ( - 1 ) ;
2012-03-01 00:14:03 +00:00
highlight_param ( param_str , subcolors , pos - tok_pos , error ) ;
2012-03-31 21:05:14 +00:00
/* Copy the subcolors back into our colors array */
2012-03-01 00:14:03 +00:00
std : : copy ( subcolors . begin ( ) , subcolors . begin ( ) + param_str . size ( ) , color . begin ( ) + tok_pos ) ;
2005-09-20 13:26:39 +00:00
}
else
2011-12-27 03:18:46 +00:00
{
2005-09-20 13:26:39 +00:00
/*
2011-12-27 03:18:46 +00:00
Command . First check that the command actually exists .
*/
2012-01-30 10:45:55 +00:00
cmd = tok_last ( & tok ) ;
bool expanded = expand_one ( cmd , EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES ) ;
if ( ! expanded | | has_expand_reserved ( cmd . c_str ( ) ) )
2005-09-20 13:26:39 +00:00
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_ERROR ;
2005-09-20 13:26:39 +00:00
}
else
{
int is_cmd = 0 ;
int is_subcommand = 0 ;
int mark = tok_get_pos ( & tok ) ;
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_COMMAND ;
2011-12-27 03:18:46 +00:00
2012-01-30 17:46:33 +00:00
if ( parser_keywords_is_subcommand ( cmd ) )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
2006-12-14 13:40:25 +00:00
int sw ;
2011-12-27 03:18:46 +00:00
2012-01-30 10:45:55 +00:00
if ( cmd = = L " builtin " )
2006-12-14 01:35:37 +00:00
{
use_function = 0 ;
use_command = 0 ;
use_builtin = 1 ;
}
2012-01-30 10:45:55 +00:00
else if ( cmd = = L " command " )
2006-12-14 01:35:37 +00:00
{
use_command = 1 ;
use_function = 0 ;
use_builtin = 0 ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
tok_next ( & tok ) ;
2011-12-27 03:18:46 +00:00
2007-04-22 09:50:26 +00:00
sw = parser_keywords_is_switch ( tok_last ( & tok ) ) ;
2011-12-27 03:18:46 +00:00
2012-01-30 17:46:33 +00:00
if ( ! parser_keywords_is_block ( cmd ) & &
2011-12-27 03:18:46 +00:00
sw = = ARG_SWITCH )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
/*
The ' builtin ' and ' command ' builtins
are normally followed by another
command , but if they are invoked
with a switch , they aren ' t .
*/
2006-12-14 13:40:25 +00:00
use_command = 1 ;
use_function = 1 ;
use_builtin = 2 ;
2005-09-20 13:26:39 +00:00
}
else
{
2006-12-14 13:40:25 +00:00
if ( sw = = ARG_SKIP )
2006-12-14 01:35:37 +00:00
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_PARAM ;
2006-12-14 01:35:37 +00:00
mark = tok_get_pos ( & tok ) ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
is_subcommand = 1 ;
}
tok_set_pos ( & tok , mark ) ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( ! is_subcommand )
{
/*
2011-12-27 03:18:46 +00:00
OK , this is a command , it has been
successfully expanded and everything
looks ok . Lets check if the command
exists .
*/
2006-06-01 22:42:17 +00:00
/*
2011-12-27 03:18:46 +00:00
First check if it is a builtin or
function , since we don ' t have to stat
any files for that
*/
2006-12-14 01:35:37 +00:00
if ( use_builtin )
2012-02-01 04:22:25 +00:00
is_cmd | = builtin_exists ( cmd ) ;
2011-12-27 03:18:46 +00:00
2006-12-14 01:35:37 +00:00
if ( use_function )
2012-02-19 17:25:15 +00:00
is_cmd | = function_exists_no_autoload ( cmd , vars ) ;
2011-12-27 03:18:46 +00:00
2006-06-01 22:42:17 +00:00
/*
2011-12-27 03:18:46 +00:00
Moving on to expensive tests
*/
2006-06-01 22:42:17 +00:00
/*
2011-12-27 03:18:46 +00:00
Check if this is a regular command
*/
2006-12-14 01:35:37 +00:00
if ( use_command )
2011-12-27 03:18:46 +00:00
{
wcstring tmp ;
is_cmd | = ! ! path_get_path_string ( cmd , tmp , vars ) ;
}
2005-09-20 13:26:39 +00:00
if ( is_cmd )
2011-12-27 03:18:46 +00:00
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_COMMAND ;
2005-09-20 13:26:39 +00:00
}
else
{
2012-02-11 01:54:21 +00:00
if ( error ) {
error - > push_back ( format_string ( L " Unknown command \' %ls \' " , cmd . c_str ( ) ) ) ;
}
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = ( HIGHLIGHT_ERROR ) ;
2005-09-20 13:26:39 +00:00
}
had_cmd = 1 ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( had_cmd )
{
2012-01-30 10:45:55 +00:00
last_cmd = tok_last ( & tok ) ;
2006-06-01 22:42:17 +00:00
}
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
break ;
}
2011-12-27 03:18:46 +00:00
2007-10-26 18:42:32 +00:00
case TOK_REDIRECT_NOCLOB :
2005-09-20 13:26:39 +00:00
case TOK_REDIRECT_OUT :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_FD :
{
if ( ! had_cmd )
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_ERROR ;
2005-09-20 13:26:39 +00:00
if ( error )
2012-02-11 01:54:21 +00:00
error - > push_back ( L " Redirection without a command " ) ;
2005-09-20 13:26:39 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2012-02-01 05:30:09 +00:00
wcstring target_str ;
const wchar_t * target = NULL ;
2011-12-27 03:18:46 +00:00
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_REDIRECTION ;
2005-09-20 13:26:39 +00:00
tok_next ( & tok ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
/*
2011-12-27 03:18:46 +00:00
Check that we are redirecting into a file
*/
2005-09-20 13:26:39 +00:00
switch ( tok_last_type ( & tok ) )
{
case TOK_STRING :
{
2012-02-01 05:30:09 +00:00
target_str = tok_last ( & tok ) ;
if ( expand_one ( target_str , EXPAND_SKIP_CMDSUBST ) ) {
target = target_str . c_str ( ) ;
}
2005-09-20 13:26:39 +00:00
/*
2011-12-27 03:18:46 +00:00
Redirect filename may contain a cmdsubst .
If so , it will be ignored / not flagged .
*/
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
break ;
2005-09-20 13:26:39 +00:00
default :
{
2012-02-22 03:39:29 +00:00
size_t pos = tok_get_pos ( & tok ) ;
if ( pos < color . size ( ) ) {
color . at ( pos ) = HIGHLIGHT_ERROR ;
}
2005-09-20 13:26:39 +00:00
if ( error )
2012-02-11 01:54:21 +00:00
error - > push_back ( L " Invalid redirection " ) ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( target ! = 0 )
{
2012-02-08 10:13:39 +00:00
wcstring dir = target ;
2012-02-14 00:38:35 +00:00
size_t slash_idx = dir . find_last_of ( L ' / ' ) ;
2005-09-20 13:26:39 +00:00
struct stat buff ;
2011-12-27 03:18:46 +00:00
/*
If file is in directory other than ' . ' , check
that the directory exists .
*/
2012-02-08 10:13:39 +00:00
if ( slash_idx ! = wcstring : : npos )
2005-09-20 13:26:39 +00:00
{
2012-02-08 10:13:39 +00:00
dir . resize ( slash_idx ) ;
2012-02-18 17:11:22 +00:00
if ( wstat ( dir , & buff ) = = - 1 )
2005-09-20 13:26:39 +00:00
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_ERROR ;
2005-09-20 13:26:39 +00:00
if ( error )
2012-02-11 01:54:21 +00:00
error - > push_back ( format_string ( L " Directory \' %ls \' does not exist " , dir . c_str ( ) ) ) ;
2011-12-27 03:18:46 +00:00
2006-06-12 14:12:33 +00:00
}
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
/*
2011-12-27 03:18:46 +00:00
If the file is read from or appended to , check
if it exists .
*/
if ( last_type = = TOK_REDIRECT_IN | |
last_type = = TOK_REDIRECT_APPEND )
2005-09-20 13:26:39 +00:00
{
if ( wstat ( target , & buff ) = = - 1 )
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_ERROR ;
2005-09-20 13:26:39 +00:00
if ( error )
2012-02-11 01:54:21 +00:00
error - > push_back ( format_string ( L " File \' %ls \' does not exist " , target ) ) ;
2005-09-20 13:26:39 +00:00
}
}
2007-10-26 18:42:32 +00:00
if ( last_type = = TOK_REDIRECT_NOCLOB )
{
if ( wstat ( target , & buff ) ! = - 1 )
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_ERROR ;
2007-10-26 18:42:32 +00:00
if ( error )
2012-02-11 01:54:21 +00:00
error - > push_back ( format_string ( L " File \' %ls \' exists " , target ) ) ;
2007-10-26 18:42:32 +00:00
}
}
2005-09-20 13:26:39 +00:00
}
break ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
case TOK_PIPE :
case TOK_BACKGROUND :
{
if ( had_cmd )
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_END ;
2005-09-20 13:26:39 +00:00
had_cmd = 0 ;
2006-12-14 01:35:37 +00:00
use_command = 1 ;
use_function = 1 ;
use_builtin = 1 ;
2006-12-04 12:07:07 +00:00
accept_switches = 1 ;
2005-09-20 13:26:39 +00:00
}
else
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_ERROR ;
2005-09-20 13:26:39 +00:00
if ( error )
2012-02-11 01:54:21 +00:00
error - > push_back ( L " No job to put in background " ) ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
case TOK_END :
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_END ;
2005-09-20 13:26:39 +00:00
had_cmd = 0 ;
2006-12-14 01:35:37 +00:00
use_command = 1 ;
use_function = 1 ;
use_builtin = 1 ;
2006-12-04 12:07:07 +00:00
accept_switches = 1 ;
2005-09-20 13:26:39 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
case TOK_COMMENT :
{
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_COMMENT ;
2005-09-20 13:26:39 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
case TOK_ERROR :
default :
{
/*
2011-12-27 03:18:46 +00:00
If the tokenizer reports an error , highlight it as such .
*/
2005-09-20 13:26:39 +00:00
if ( error )
2012-02-11 01:54:21 +00:00
error - > push_back ( tok_last ( & tok ) ) ;
2012-02-21 19:45:13 +00:00
color . at ( tok_get_pos ( & tok ) ) = HIGHLIGHT_ERROR ;
2011-12-27 03:18:46 +00:00
break ;
}
2005-09-20 13:26:39 +00:00
}
}
2011-12-27 03:18:46 +00:00
tok_destroy ( & tok ) ;
}
2005-09-20 13:26:39 +00:00
2011-12-27 03:18:46 +00:00
// PCA DOES_IO (calls is_potential_path, path_get_path, maybe others)
2012-02-22 01:55:56 +00:00
void highlight_shell ( const wcstring & buff , std : : vector < int > & color , int pos , wcstring_list_t * error , const env_vars & vars )
2011-12-27 03:18:46 +00:00
{
ASSERT_IS_BACKGROUND_THREAD ( ) ;
2012-02-22 01:55:56 +00:00
const size_t length = buff . size ( ) ;
assert ( buff . size ( ) = = color . size ( ) ) ;
2012-02-21 19:45:13 +00:00
2012-02-22 01:55:56 +00:00
if ( length = = 0 )
2011-12-27 03:18:46 +00:00
return ;
2012-02-21 19:45:13 +00:00
std : : fill ( color . begin ( ) , color . end ( ) , - 1 ) ;
2011-12-27 03:18:46 +00:00
/* Tokenize the string */
2012-02-22 01:55:56 +00:00
tokenize ( buff . c_str ( ) , color , pos , error , vars ) ;
2010-09-18 01:51:16 +00:00
2005-09-20 13:26:39 +00:00
/*
2006-08-22 14:38:31 +00:00
Locate and syntax highlight cmdsubsts recursively
2005-09-20 13:26:39 +00:00
*/
2005-10-25 11:03:52 +00:00
2012-02-22 01:55:56 +00:00
wchar_t * const subbuff = wcsdup ( buff . c_str ( ) ) ;
2012-02-14 01:15:41 +00:00
wchar_t * subpos = subbuff ;
2005-09-20 13:26:39 +00:00
int done = 0 ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
while ( 1 )
{
wchar_t * begin , * end ;
2012-02-21 19:45:13 +00:00
2012-02-08 10:13:39 +00:00
if ( parse_util_locate_cmdsubst ( subpos , & begin , & end , 1 ) < = 0 )
2005-09-20 13:26:39 +00:00
{
break ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( ! * end )
done = 1 ;
else
* end = 0 ;
2011-12-27 03:18:46 +00:00
2012-02-21 19:45:13 +00:00
//our subcolors start at color + (begin-subbuff)+1
size_t start = begin - subbuff + 1 , len = wcslen ( begin + 1 ) ;
std : : vector < int > subcolors ;
subcolors . resize ( len , - 1 ) ;
highlight_shell ( begin + 1 , subcolors , - 1 , error , vars ) ;
// insert subcolors
std : : copy ( subcolors . begin ( ) , subcolors . end ( ) , color . begin ( ) + start ) ;
// highlight the end of the subcommand
assert ( end > = subbuff ) ;
2012-02-22 01:55:56 +00:00
if ( ( size_t ) ( end - subbuff ) < length ) {
2012-02-21 19:45:13 +00:00
color . at ( end - subbuff ) = HIGHLIGHT_OPERATOR ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( done )
break ;
2011-12-27 03:18:46 +00:00
2010-09-18 01:51:16 +00:00
subpos = end + 1 ;
2005-09-20 13:26:39 +00:00
}
2012-02-14 01:15:41 +00:00
free ( subbuff ) ;
2005-09-20 13:26:39 +00:00
2006-06-17 10:41:28 +00:00
/*
The highlighting code only changes the first element when the
color changes . This fills in the rest .
*/
2012-02-22 01:55:56 +00:00
int last_val = 0 ;
for ( size_t i = 0 ; i < buff . size ( ) ; i + + )
2005-09-20 13:26:39 +00:00
{
2012-02-21 19:45:13 +00:00
if ( color . at ( i ) > = 0 )
last_val = color . at ( i ) ;
2005-09-20 13:26:39 +00:00
else
2012-02-21 19:45:13 +00:00
color . at ( i ) = last_val ;
2005-09-20 13:26:39 +00:00
}
2006-06-14 13:22:40 +00:00
/*
Color potentially valid paths in a special path color if they
are the current token .
2012-02-22 01:55:56 +00:00
For reasons that I don ' t yet understand , it ' s required that pos be allowed to be length ( e . g . when backspacing ) .
2006-06-14 13:22:40 +00:00
*/
2012-02-22 01:55:56 +00:00
if ( pos > = 0 & & ( size_t ) pos < = length )
2006-06-14 13:22:40 +00:00
{
2011-12-27 03:18:46 +00:00
2012-02-22 01:55:56 +00:00
const wchar_t * cbuff = buff . c_str ( ) ;
2012-02-08 10:13:39 +00:00
const wchar_t * tok_begin , * tok_end ;
2012-02-22 01:55:56 +00:00
parse_util_token_extent ( cbuff , pos , & tok_begin , & tok_end , 0 , 0 ) ;
2006-07-03 10:46:47 +00:00
if ( tok_begin & & tok_end )
2006-06-14 13:22:40 +00:00
{
2012-02-08 10:13:39 +00:00
const wcstring token ( tok_begin , tok_end - tok_begin ) ;
2006-06-14 13:22:40 +00:00
if ( is_potential_path ( token ) )
{
2012-02-22 01:55:56 +00:00
for ( ptrdiff_t i = tok_begin - cbuff ; i < ( tok_end - cbuff ) ; i + + )
2006-06-14 13:22:40 +00:00
{
2012-02-18 02:08:08 +00:00
// Don't color HIGHLIGHT_ERROR because it looks dorky. For example, trying to cd into a non-directory would show an underline and also red.
2012-02-22 01:55:56 +00:00
if ( ! ( color . at ( i ) & HIGHLIGHT_ERROR ) ) {
2012-02-21 19:45:13 +00:00
color . at ( i ) | = HIGHLIGHT_VALID_PATH ;
2012-02-22 01:55:56 +00:00
}
2006-06-14 13:22:40 +00:00
}
}
}
}
2011-12-27 03:18:46 +00:00
2010-09-18 01:51:16 +00:00
2011-12-27 03:18:46 +00:00
highlight_universal_internal ( buff , color , pos ) ;
2005-09-20 13:26:39 +00:00
/*
Spaces should not be highlighted at all , since it makes cursor look funky in some terminals
*/
2012-02-22 01:55:56 +00:00
for ( size_t i = 0 ; i < buff . size ( ) ; i + + )
2005-09-20 13:26:39 +00:00
{
2012-02-22 01:55:56 +00:00
if ( iswspace ( buff . at ( i ) ) )
2005-09-20 13:26:39 +00:00
{
2012-02-21 19:45:13 +00:00
color . at ( i ) = 0 ;
2005-09-20 13:26:39 +00:00
}
}
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
/**
Perform quote and parenthesis highlighting on the specified string .
*/
2012-02-22 01:55:56 +00:00
static void highlight_universal_internal ( const wcstring & buffstr ,
2012-02-21 19:45:13 +00:00
std : : vector < int > & color ,
2011-12-27 03:18:46 +00:00
int pos )
{
2012-02-22 01:55:56 +00:00
assert ( buffstr . size ( ) = = color . size ( ) ) ;
if ( ( pos > = 0 ) & & ( ( size_t ) pos < buffstr . size ( ) ) )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
/*
Highlight matching quotes
*/
2012-02-22 01:55:56 +00:00
if ( ( buffstr . at ( pos ) = = L ' \' ' ) | | ( buffstr . at ( pos ) = = L ' \" ' ) )
2005-09-20 13:26:39 +00:00
{
2012-03-03 23:20:30 +00:00
std : : vector < long > lst ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
int level = 0 ;
wchar_t prev_q = 0 ;
2011-12-27 03:18:46 +00:00
2012-02-22 01:55:56 +00:00
const wchar_t * const buff = buffstr . c_str ( ) ;
const wchar_t * str = buff ;
2005-09-20 13:26:39 +00:00
int match_found = 0 ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
while ( * str )
{
switch ( * str )
{
case L ' \\ ' :
str + + ;
break ;
case L ' \" ' :
case L ' \' ' :
if ( level = = 0 )
{
level + + ;
2012-02-08 08:45:07 +00:00
lst . push_back ( ( long ) ( str - buff ) ) ;
2005-09-20 13:26:39 +00:00
prev_q = * str ;
}
else
{
if ( prev_q = = * str )
{
2006-06-01 19:42:31 +00:00
long pos1 , pos2 ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
level - - ;
2012-02-08 08:45:07 +00:00
pos1 = lst . back ( ) ;
2005-09-20 13:26:39 +00:00
pos2 = str - buff ;
if ( pos1 = = pos | | pos2 = = pos )
{
2012-02-21 19:45:13 +00:00
color . at ( pos1 ) | = HIGHLIGHT_MATCH < < 16 ;
color . at ( pos2 ) | = HIGHLIGHT_MATCH < < 16 ;
2005-09-20 13:26:39 +00:00
match_found = 1 ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
prev_q = * str = = L ' \" ' ? L ' \' ' : L ' \" ' ;
}
else
{
level + + ;
2012-02-08 08:45:07 +00:00
lst . push_back ( ( long ) ( str - buff ) ) ;
2005-09-20 13:26:39 +00:00
prev_q = * str ;
}
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
break ;
}
if ( ( * str = = L ' \0 ' ) )
break ;
str + + ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( ! match_found )
2012-02-21 19:45:13 +00:00
color . at ( pos ) = HIGHLIGHT_ERROR < < 16 ;
2005-09-20 13:26:39 +00:00
}
/*
Highlight matching parenthesis
*/
2012-02-22 01:55:56 +00:00
const wchar_t c = buffstr . at ( pos ) ;
if ( wcschr ( L " ()[]{} " , c ) )
2005-09-20 13:26:39 +00:00
{
2012-02-22 01:55:56 +00:00
int step = wcschr ( L " ({[ " , c ) ? 1 : - 1 ;
wchar_t dec_char = * ( wcschr ( L " ()[]{} " , c ) + step ) ;
wchar_t inc_char = c ;
2005-09-20 13:26:39 +00:00
int level = 0 ;
2011-12-27 03:18:46 +00:00
int match_found = 0 ;
2012-02-22 01:55:56 +00:00
for ( long i = pos ; i > = 0 & & ( size_t ) i < buffstr . size ( ) ; i + = step ) {
const wchar_t test_char = buffstr . at ( i ) ;
if ( test_char = = inc_char )
2005-09-20 13:26:39 +00:00
level + + ;
2012-02-22 01:55:56 +00:00
if ( test_char = = dec_char )
2005-09-20 13:26:39 +00:00
level - - ;
if ( level = = 0 )
{
2012-02-22 01:55:56 +00:00
long pos2 = i ;
2012-02-21 19:45:13 +00:00
color . at ( pos ) | = HIGHLIGHT_MATCH < < 16 ;
color . at ( pos2 ) | = HIGHLIGHT_MATCH < < 16 ;
2005-09-20 13:26:39 +00:00
match_found = 1 ;
break ;
}
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( ! match_found )
2006-06-14 13:22:40 +00:00
color [ pos ] = HIGHLIGHT_ERROR < < 16 ;
2005-09-20 13:26:39 +00:00
}
}
}
2012-02-22 01:55:56 +00:00
void highlight_universal ( const wcstring & buff , std : : vector < int > & color , int pos , wcstring_list_t * error , const env_vars & vars )
2005-09-20 13:26:39 +00:00
{
2012-02-22 01:55:56 +00:00
assert ( buff . size ( ) = = color . size ( ) ) ;
std : : fill ( color . begin ( ) , color . end ( ) , 0 ) ;
2011-12-27 03:18:46 +00:00
highlight_universal_internal ( buff , color , pos ) ;
2005-09-20 13:26:39 +00:00
}