@@ -57,15 +57,39 @@ _ghost_notify() {
5757_ghost_validate_command () {
5858 local cmd_line=" $1 "
5959
60- # Extract the first word (command) - handle quoted strings
60+ # Extract the first word (command) - handling escaped spaces
6161 local first_word
6262
6363 # If the line has no spaces but cursor is not at the end,
6464 # the user is editing in the middle - extract only what's before the cursor
6565 if [[ " $cmd_line " != * ' ' * && $READLINE_POINT -lt ${# cmd_line} ]]; then
6666 first_word=" ${cmd_line: 0: $READLINE_POINT } "
6767 else
68- first_word=" ${cmd_line%% * } "
68+ # Extract first word respecting escaped spaces
69+ first_word=" "
70+ local escaped=false
71+ local i
72+ for (( i= 0 ; i< ${# cmd_line} ; i++ )) ; do
73+ local char=" ${cmd_line: $i : 1} "
74+
75+ if $escaped ; then
76+ first_word+=" $char "
77+ escaped=false
78+ continue
79+ fi
80+
81+ if [[ " $char " == ' \' ]]; then
82+ first_word+=" $char "
83+ escaped=true
84+ continue
85+ fi
86+
87+ if [[ " $char " == ' ' ]]; then
88+ break
89+ fi
90+
91+ first_word+=" $char "
92+ done
6993 fi
7094
7195 # Remove trailing semicolon (;) FIRST - before removing quotes
@@ -81,18 +105,33 @@ _ghost_validate_command() {
81105 [[ -z " $first_word " ]] && return 0
82106 [[ " $first_word " == * " =" * ]] && return 0
83107
84- # Skip validation if it's a function declaration (word followed by parentheses)
85- # Examples: test(), my_func(), func_name()
108+ # Skip validation if it's a function declaration
86109 if [[ " $first_word " =~ ^[a-zA-Z_][a-zA-Z0-9_]* \(\) $ ]]; then
87110 return 0
88111 fi
89112
113+ # Check if the word contains escaped spaces or backslashes (file paths)
114+ local has_escapes=false
115+ [[ " $first_word " == * ' \ ' * || " $first_word " == * ' \' * ]] && has_escapes=true
116+
90117 # Expand tilde (~) to $HOME for path validation
91118 local expanded_word=" $first_word "
92119 if [[ " $first_word " == " ~" * ]]; then
93120 expanded_word=" ${HOME}${first_word: 1} "
94121 fi
95122
123+ # If the word has escapes, it's a file path - skip type/compgen validations
124+ if $has_escapes ; then
125+ # Try to validate as a file path directly
126+ local eval_path
127+ eval_path=$( eval echo " $first_word " 2> /dev/null)
128+ if [[ -n " $eval_path " ]] && (test -e " $eval_path " 2> /dev/null || test -e " $expanded_word " 2> /dev/null); then
129+ return 0
130+ fi
131+ # If the file doesn't exist, it's invalid (don't check type/compgen)
132+ return 1
133+ fi
134+
96135 # 1. Check if it's a keyword/special builtin
97136 case " $first_word " in
98137 if|then|else|elif|fi|case|esac|for|while|until|do|done|in|function|select|time|coproc|declare|typeset|local|export|readonly|unset) return 0 ;;
0 commit comments