mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-12 13:08:49 +00:00
Changes to make autosuggestion even smarter by specially recognizing the cd command.
This commit is contained in:
parent
ed89df7e9d
commit
14b3a5be56
8 changed files with 296 additions and 131 deletions
|
@ -225,7 +225,7 @@
|
|||
/* Begin PBXLegacyTarget section */
|
||||
D0A084F713B3AC130099B651 /* FishsFish */ = {
|
||||
isa = PBXLegacyTarget;
|
||||
buildArgumentsString = "-k ${ACTION}";
|
||||
buildArgumentsString = "-k ${ACTION} -j 3";
|
||||
buildConfigurationList = D0A084FA13B3AC130099B651 /* Build configuration list for PBXLegacyTarget "FishsFish" */;
|
||||
buildPhases = (
|
||||
);
|
||||
|
|
|
@ -1826,12 +1826,14 @@ int create_directory( const wcstring &d )
|
|||
return ok?0:-1;
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
void bugreport()
|
||||
{
|
||||
debug( 1,
|
||||
_( L"This is a bug. "
|
||||
_( L"This is a bug. Break on bugreport to debug."
|
||||
L"If you can reproduce it, please send a bug report to %s." ),
|
||||
PACKAGE_BUGREPORT );
|
||||
while (1) sleep(10000);
|
||||
}
|
||||
|
||||
|
||||
|
|
146
highlight.cpp
146
highlight.cpp
|
@ -528,7 +528,150 @@ static int has_expand_reserved( const wchar_t *str )
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool autosuggest_handle_special(const wcstring &str, const env_vars &vars, const wcstring &working_directory, bool *outSuggestionOK) {
|
||||
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");
|
||||
if (! is_help && ! path_can_get_cdpath(dir, working_directory.c_str())) {
|
||||
suggestionOK = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 );
|
||||
|
||||
*outSuggestionOK = suggestionOK;
|
||||
return handled;
|
||||
}
|
||||
|
||||
// This function does I/O
|
||||
static void tokenize( const wchar_t * const buff, int * const color, const int pos, wcstring_list_t *error, const env_vars &vars) {
|
||||
|
@ -563,7 +706,6 @@ static void tokenize( const wchar_t * const buff, int * const color, const int p
|
|||
tok_next( &tok ) )
|
||||
{
|
||||
int last_type = tok_last_type( &tok );
|
||||
int prev_argc=0;
|
||||
|
||||
switch( last_type )
|
||||
{
|
||||
|
@ -620,8 +762,6 @@ static void tokenize( const wchar_t * const buff, int * const color, const int p
|
|||
}
|
||||
else
|
||||
{
|
||||
prev_argc=0;
|
||||
|
||||
/*
|
||||
Command. First check that the command actually exists.
|
||||
*/
|
||||
|
|
|
@ -106,4 +106,7 @@ void highlight_universal( const wchar_t *buff, int *color, int pos, wcstring_lis
|
|||
*/
|
||||
rgb_color_t highlight_get_color( int highlight, bool is_background );
|
||||
|
||||
bool autosuggest_handle_special(const wcstring &str, const env_vars &vars, const wcstring &working_directory, bool *outSuggestionOK);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
35
history.cpp
35
history.cpp
|
@ -695,26 +695,7 @@ int file_detection_context_t::perform_file_detection(bool test_all) {
|
|||
valid_paths.clear();
|
||||
int result = 1;
|
||||
for (path_list_t::const_iterator iter = potential_paths.begin(); iter != potential_paths.end(); iter++) {
|
||||
wcstring path = *iter;
|
||||
|
||||
bool path_is_valid;
|
||||
/* Some special paths are always valid */
|
||||
if (path.empty()) {
|
||||
path_is_valid = false;
|
||||
} else if (path == L"." || path == L"./") {
|
||||
path_is_valid = true;
|
||||
} else if (path == L".." || path == L"../") {
|
||||
path_is_valid = (! working_directory.empty() && working_directory != L"/");
|
||||
} else {
|
||||
/* Maybe append the working directory. Note that we know path is not empty here. */
|
||||
if (path.at(0) != '/') {
|
||||
path.insert(0, working_directory);
|
||||
}
|
||||
path_is_valid = (0 == waccess(path, F_OK));
|
||||
}
|
||||
|
||||
|
||||
if (path_is_valid) {
|
||||
if (path_is_valid(*iter, working_directory)) {
|
||||
/* Push the original (possibly relative) path */
|
||||
valid_paths.push_front(*iter);
|
||||
} else {
|
||||
|
@ -736,17 +717,9 @@ bool file_detection_context_t::paths_are_valid(const path_list_t &paths) {
|
|||
file_detection_context_t::file_detection_context_t(history_t *hist, const wcstring &cmd) :
|
||||
history(hist),
|
||||
command(cmd),
|
||||
when(time(NULL)) {
|
||||
/* Stash the working directory. TODO: We should be respecting CDPATH here*/
|
||||
wchar_t dir_path[4096];
|
||||
const wchar_t *cwd = wgetcwd( dir_path, 4096 );
|
||||
if (cwd) {
|
||||
wcstring wd = cwd;
|
||||
/* Make sure the working directory ends with a slash */
|
||||
if (! wd.empty() && wd.at(wd.size() - 1) != L'/')
|
||||
wd.push_back(L'/');
|
||||
working_directory.swap(wd);
|
||||
}
|
||||
when(time(NULL)),
|
||||
working_directory(get_working_directory())
|
||||
{
|
||||
}
|
||||
|
||||
static int threaded_perform_file_detection(file_detection_context_t *ctx) {
|
||||
|
|
175
path.cpp
175
path.cpp
|
@ -336,96 +336,91 @@ bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env
|
|||
}
|
||||
|
||||
|
||||
wchar_t *path_allocate_cdpath( const wchar_t *dir )
|
||||
wchar_t *path_allocate_cdpath( const wchar_t *dir, const wchar_t *wd )
|
||||
{
|
||||
wchar_t *res = 0;
|
||||
wchar_t *res = NULL;
|
||||
int err = ENOENT;
|
||||
if( !dir )
|
||||
return 0;
|
||||
|
||||
if (wd) {
|
||||
size_t len = wcslen(wd);
|
||||
assert(wd[len - 1] == L'/');
|
||||
}
|
||||
|
||||
if( dir[0] == L'/'|| (wcsncmp( dir, L"./", 2 )==0) )
|
||||
{
|
||||
wcstring_list_t paths;
|
||||
|
||||
if (dir[0] == L'/') {
|
||||
/* Absolute path */
|
||||
paths.push_back(dir);
|
||||
} else if (wcsncmp(dir, L"./", 2) == 0 ||
|
||||
wcsncmp(dir, L"../", 3) == 0 ||
|
||||
wcscmp(dir, L".") == 0 ||
|
||||
wcscmp(dir, L"..") == 0) {
|
||||
/* Path is relative to the working directory */
|
||||
wcstring path;
|
||||
if (wd)
|
||||
path.append(wd);
|
||||
path.append(dir);
|
||||
paths.push_back(path);
|
||||
} else {
|
||||
wchar_t *path_cpy;
|
||||
const wchar_t *nxt_path;
|
||||
wchar_t *state;
|
||||
wchar_t *whole_path;
|
||||
|
||||
env_var_t path = env_get_string(L"CDPATH");
|
||||
if (path.missing())
|
||||
path = wd ? wd : L".";
|
||||
|
||||
nxt_path = path.c_str();
|
||||
path_cpy = wcsdup( path.c_str() );
|
||||
|
||||
for( nxt_path = wcstok( path_cpy, ARRAY_SEP_STR, &state );
|
||||
nxt_path != 0;
|
||||
nxt_path = wcstok( 0, ARRAY_SEP_STR, &state) )
|
||||
{
|
||||
wchar_t *expanded_path = expand_tilde_compat( wcsdup(nxt_path) );
|
||||
|
||||
// debug( 2, L"woot %ls\n", expanded_path );
|
||||
|
||||
int path_len = wcslen( expanded_path );
|
||||
if( path_len == 0 )
|
||||
{
|
||||
free(expanded_path );
|
||||
continue;
|
||||
}
|
||||
|
||||
whole_path =
|
||||
wcsdupcat( expanded_path,
|
||||
( expanded_path[path_len-1] != L'/' )?L"/":L"",
|
||||
dir );
|
||||
|
||||
free(expanded_path );
|
||||
paths.push_back(whole_path);
|
||||
free( whole_path );
|
||||
}
|
||||
free( path_cpy );
|
||||
}
|
||||
|
||||
for (wcstring_list_t::const_iterator iter = paths.begin(); iter != paths.end(); iter++) {
|
||||
struct stat buf;
|
||||
const wchar_t *dir = iter->c_str();
|
||||
if( wstat( dir, &buf ) == 0 )
|
||||
{
|
||||
if( S_ISDIR(buf.st_mode) )
|
||||
{
|
||||
res = wcsdup(dir);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = ENOTDIR;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t *path_cpy;
|
||||
const wchar_t *nxt_path;
|
||||
wchar_t *state;
|
||||
wchar_t *whole_path;
|
||||
}
|
||||
|
||||
env_var_t path = L".";
|
||||
|
||||
nxt_path = path.c_str();
|
||||
path_cpy = wcsdup( path.c_str() );
|
||||
|
||||
if( !path_cpy )
|
||||
{
|
||||
DIE_MEM();
|
||||
}
|
||||
|
||||
for( nxt_path = wcstok( path_cpy, ARRAY_SEP_STR, &state );
|
||||
nxt_path != 0;
|
||||
nxt_path = wcstok( 0, ARRAY_SEP_STR, &state) )
|
||||
{
|
||||
wchar_t *expanded_path = expand_tilde_compat( wcsdup(nxt_path) );
|
||||
|
||||
// debug( 2, L"woot %ls\n", expanded_path );
|
||||
|
||||
int path_len = wcslen( expanded_path );
|
||||
if( path_len == 0 )
|
||||
{
|
||||
free(expanded_path );
|
||||
continue;
|
||||
}
|
||||
|
||||
whole_path =
|
||||
wcsdupcat( expanded_path,
|
||||
( expanded_path[path_len-1] != L'/' )?L"/":L"",
|
||||
dir );
|
||||
|
||||
free(expanded_path );
|
||||
|
||||
struct stat buf;
|
||||
if( wstat( whole_path, &buf ) == 0 )
|
||||
{
|
||||
if( S_ISDIR(buf.st_mode) )
|
||||
{
|
||||
res = whole_path;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = ENOTDIR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( lwstat( whole_path, &buf ) == 0 )
|
||||
{
|
||||
err = EROTTEN;
|
||||
}
|
||||
}
|
||||
|
||||
free( whole_path );
|
||||
}
|
||||
free( path_cpy );
|
||||
}
|
||||
|
||||
if( !res )
|
||||
if( !res )
|
||||
{
|
||||
errno = err;
|
||||
}
|
||||
|
@ -434,8 +429,8 @@ wchar_t *path_allocate_cdpath( const wchar_t *dir )
|
|||
}
|
||||
|
||||
|
||||
bool path_can_get_cdpath(const wcstring &in) {
|
||||
wchar_t *tmp = path_allocate_cdpath(in.c_str());
|
||||
bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd) {
|
||||
wchar_t *tmp = path_allocate_cdpath(in.c_str(), wd);
|
||||
bool result = (tmp != NULL);
|
||||
free(tmp);
|
||||
return result;
|
||||
|
@ -512,3 +507,37 @@ void path_make_canonical( wcstring &path )
|
|||
path.resize(size+1);
|
||||
}
|
||||
|
||||
bool path_is_valid(const wcstring &path, const wcstring &working_directory)
|
||||
{
|
||||
bool path_is_valid;
|
||||
/* Some special paths are always valid */
|
||||
if (path.empty()) {
|
||||
path_is_valid = false;
|
||||
} else if (path == L"." || path == L"./") {
|
||||
path_is_valid = true;
|
||||
} else if (path == L".." || path == L"../") {
|
||||
path_is_valid = (! working_directory.empty() && working_directory != L"/");
|
||||
} else if (path.at(0) != '/') {
|
||||
/* Prepend the working directory. Note that we know path is not empty here. */
|
||||
wcstring tmp = working_directory;
|
||||
tmp.append(path);
|
||||
path_is_valid = (0 == waccess(tmp.c_str(), F_OK));
|
||||
} else {
|
||||
/* Simple check */
|
||||
path_is_valid = (0 == waccess(path.c_str(), F_OK));
|
||||
}
|
||||
return path_is_valid;
|
||||
}
|
||||
|
||||
wcstring get_working_directory(void) {
|
||||
wcstring wd = L"./";
|
||||
wchar_t dir_path[4096];
|
||||
const wchar_t *cwd = wgetcwd( dir_path, 4096 );
|
||||
if (cwd) {
|
||||
wd = cwd;
|
||||
/* Make sure the working directory ends with a slash */
|
||||
if (! wd.empty() && wd.at(wd.size() - 1) != L'/')
|
||||
wd.push_back(L'/');
|
||||
}
|
||||
return wd;
|
||||
}
|
||||
|
|
8
path.h
8
path.h
|
@ -49,11 +49,12 @@ bool path_get_path_string(const wcstring &cmd, wcstring &output, const env_vars
|
|||
will be returned.
|
||||
|
||||
\param in The name of the directory.
|
||||
\param wd The working directory, or NULL to use the default. The working directory should have a slash appended at the end.
|
||||
\return 0 if the command can not be found, the path of the command otherwise. The path should be free'd with free().
|
||||
*/
|
||||
|
||||
wchar_t *path_allocate_cdpath( const wchar_t *in );
|
||||
bool path_can_get_cdpath(const wcstring &in);
|
||||
wchar_t *path_allocate_cdpath( const wchar_t *in, const wchar_t *wd = NULL);
|
||||
bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd = NULL);
|
||||
bool path_get_cdpath_string(const wcstring &in, wcstring &out, const env_vars &vars);
|
||||
|
||||
/**
|
||||
|
@ -62,5 +63,8 @@ bool path_get_cdpath_string(const wcstring &in, wcstring &out, const env_vars &v
|
|||
*/
|
||||
void path_make_canonical( wcstring &path );
|
||||
|
||||
bool path_is_valid(const wcstring &path, const wcstring &working_directory);
|
||||
|
||||
wcstring get_working_directory(void);
|
||||
|
||||
#endif
|
||||
|
|
48
reader.cpp
48
reader.cpp
|
@ -103,6 +103,7 @@ commence.
|
|||
#include "screen.h"
|
||||
#include "iothread.h"
|
||||
#include "intern.h"
|
||||
#include "path.h"
|
||||
|
||||
#include "parse_util.h"
|
||||
|
||||
|
@ -1270,31 +1271,43 @@ static void run_pager( wchar_t *prefix, int is_quoted, const std::vector<complet
|
|||
struct autosuggestion_context_t {
|
||||
history_search_t searcher;
|
||||
file_detection_context_t detector;
|
||||
const wcstring working_directory;
|
||||
const env_vars vars;
|
||||
|
||||
autosuggestion_context_t(history_t *history, const wcstring &term) :
|
||||
searcher(*history, term, HISTORY_SEARCH_TYPE_PREFIX),
|
||||
detector(history, term)
|
||||
detector(history, term),
|
||||
working_directory(get_working_directory()),
|
||||
vars(env_vars::highlighting_keys)
|
||||
{
|
||||
}
|
||||
|
||||
int threaded_autosuggest(void) {
|
||||
ASSERT_IS_BACKGROUND_THREAD();
|
||||
while (searcher.go_backwards()) {
|
||||
history_item_t item = searcher.current_item();
|
||||
bool item_ok = false;
|
||||
if (autosuggest_handle_special(item.str(), vars, working_directory, &item_ok)) {
|
||||
/* The command autosuggestion was handled specially, so we're done */
|
||||
} else {
|
||||
/* See if the item has any required paths */
|
||||
const path_list_t &paths = item.get_required_paths();
|
||||
if (paths.empty()) {
|
||||
item_ok = true;
|
||||
} else {
|
||||
detector.potential_paths = paths;
|
||||
item_ok = detector.paths_are_valid(paths);
|
||||
}
|
||||
}
|
||||
if (item_ok)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
static int threaded_autosuggest(autosuggestion_context_t *ctx) {
|
||||
ASSERT_IS_BACKGROUND_THREAD();
|
||||
while (ctx->searcher.go_backwards()) {
|
||||
history_item_t item = ctx->searcher.current_item();
|
||||
/* See if the item has any required paths */
|
||||
bool item_ok;
|
||||
const path_list_t &paths = item.get_required_paths();
|
||||
if (paths.empty()) {
|
||||
item_ok = true;
|
||||
} else {
|
||||
ctx->detector.potential_paths = paths;
|
||||
item_ok = ctx->detector.paths_are_valid(paths);
|
||||
}
|
||||
if (item_ok)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return ctx->threaded_autosuggest();
|
||||
}
|
||||
|
||||
static bool can_autosuggest(void) {
|
||||
|
@ -2587,6 +2600,7 @@ static int read_i()
|
|||
|
||||
data->buff_pos=0;
|
||||
data->command_line.clear();
|
||||
data->check_size();
|
||||
reader_run_command( parser, tmp );
|
||||
free( (void *)tmp );
|
||||
if( data->end_loop)
|
||||
|
|
Loading…
Reference in a new issue