mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-02 05:41:16 -03:00
Piling on more code to make autosuggestion try to guess directories even when they're not in the history
This commit is contained in:
160
highlight.cpp
160
highlight.cpp
@@ -68,10 +68,11 @@ static const wchar_t * const highlight_var[] =
|
|||||||
Tests if the specified string is the prefix of any valid path in the system.
|
Tests if the specified string is the prefix of any valid path in the system.
|
||||||
|
|
||||||
\require_dir Whether the valid path must be a directory
|
\require_dir Whether the valid path must be a directory
|
||||||
|
\out_path If non-null, the path on output
|
||||||
\return zero it this is not a valid prefix, non-zero otherwise
|
\return zero it this is not a valid prefix, non-zero otherwise
|
||||||
*/
|
*/
|
||||||
// PCA DOES_IO
|
// PCA DOES_IO
|
||||||
static bool is_potential_path( const wcstring &cpath, bool require_dir = false )
|
static bool is_potential_path( const wcstring &cpath, wcstring *out_path = NULL, bool require_dir = false )
|
||||||
{
|
{
|
||||||
ASSERT_IS_BACKGROUND_THREAD();
|
ASSERT_IS_BACKGROUND_THREAD();
|
||||||
|
|
||||||
@@ -129,9 +130,11 @@ static bool is_potential_path( const wcstring &cpath, bool require_dir = false )
|
|||||||
if( must_be_full_dir )
|
if( must_be_full_dir )
|
||||||
{
|
{
|
||||||
dir = wopendir( cleaned_path );
|
dir = wopendir( cleaned_path );
|
||||||
res = !!dir;
|
|
||||||
if( dir )
|
if( dir )
|
||||||
{
|
{
|
||||||
|
res = true;
|
||||||
|
if (out_path)
|
||||||
|
*out_path = cleaned_path;
|
||||||
closedir( dir );
|
closedir( dir );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,16 +146,27 @@ static bool is_potential_path( const wcstring &cpath, bool require_dir = false )
|
|||||||
if( dir_name == L"/" && base_name == L"/" )
|
if( dir_name == L"/" && base_name == L"/" )
|
||||||
{
|
{
|
||||||
res = true;
|
res = true;
|
||||||
|
if (out_path)
|
||||||
|
*out_path = cleaned_path;
|
||||||
}
|
}
|
||||||
else if( (dir = wopendir( dir_name)) )
|
else if( (dir = wopendir( dir_name)) )
|
||||||
{
|
{
|
||||||
wcstring ent;
|
wcstring ent;
|
||||||
bool is_dir;
|
bool is_dir;
|
||||||
while (wreaddir(dir, ent, &is_dir))
|
while (wreaddir_resolving(dir, dir_name, ent, &is_dir))
|
||||||
{
|
{
|
||||||
if (string_prefixes_string(base_name, ent) && (! require_dir || is_dir))
|
if (string_prefixes_string(base_name, ent) && (! require_dir || is_dir))
|
||||||
{
|
{
|
||||||
res = true;
|
res = true;
|
||||||
|
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'/');
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -528,6 +542,146 @@ static int has_expand_reserved( const wchar_t *str )
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_directory, wcstring &outString) {
|
||||||
|
if (str.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wcstring cmd;
|
||||||
|
bool had_cmd = false;
|
||||||
|
|
||||||
|
wcstring suggestion;
|
||||||
|
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 );
|
||||||
|
wcstring suggested_path;
|
||||||
|
if (is_potential_path(dir, &suggested_path, true /* require directory */)) {
|
||||||
|
suggestionOK = true;
|
||||||
|
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 );
|
||||||
|
|
||||||
|
if (suggestionOK)
|
||||||
|
outString.swap(suggestion);
|
||||||
|
return suggestionOK;
|
||||||
|
}
|
||||||
|
|
||||||
bool autosuggest_handle_special(const wcstring &str, const wcstring &working_directory, bool *outSuggestionOK) {
|
bool autosuggest_handle_special(const wcstring &str, const wcstring &working_directory, bool *outSuggestionOK) {
|
||||||
ASSERT_IS_BACKGROUND_THREAD();
|
ASSERT_IS_BACKGROUND_THREAD();
|
||||||
assert(outSuggestionOK != NULL);
|
assert(outSuggestionOK != NULL);
|
||||||
|
|||||||
@@ -107,6 +107,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 );
|
rgb_color_t highlight_get_color( int highlight, bool is_background );
|
||||||
|
|
||||||
bool autosuggest_handle_special(const wcstring &str, const wcstring &working_directory, bool *outSuggestionOK);
|
bool autosuggest_handle_special(const wcstring &str, const wcstring &working_directory, bool *outSuggestionOK);
|
||||||
|
bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_directory, wcstring &outString);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
25
reader.cpp
25
reader.cpp
@@ -1269,12 +1269,15 @@ static void run_pager( wchar_t *prefix, int is_quoted, const std::vector<complet
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct autosuggestion_context_t {
|
struct autosuggestion_context_t {
|
||||||
|
wcstring search_string;
|
||||||
|
wcstring autosuggestion;
|
||||||
history_search_t searcher;
|
history_search_t searcher;
|
||||||
file_detection_context_t detector;
|
file_detection_context_t detector;
|
||||||
const wcstring working_directory;
|
const wcstring working_directory;
|
||||||
const env_vars vars;
|
const env_vars vars;
|
||||||
|
|
||||||
autosuggestion_context_t(history_t *history, const wcstring &term) :
|
autosuggestion_context_t(history_t *history, const wcstring &term) :
|
||||||
|
search_string(term),
|
||||||
searcher(*history, term, HISTORY_SEARCH_TYPE_PREFIX),
|
searcher(*history, term, HISTORY_SEARCH_TYPE_PREFIX),
|
||||||
detector(history, term),
|
detector(history, term),
|
||||||
working_directory(get_working_directory()),
|
working_directory(get_working_directory()),
|
||||||
@@ -1299,9 +1302,19 @@ struct autosuggestion_context_t {
|
|||||||
item_ok = detector.paths_are_valid(paths);
|
item_ok = detector.paths_are_valid(paths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item_ok)
|
if (item_ok) {
|
||||||
|
this->autosuggestion = searcher.current_string();
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Since we didn't find a suggestion from history, try other means */
|
||||||
|
wcstring special_suggestion;
|
||||||
|
if (autosuggest_suggest_special(search_string, working_directory, special_suggestion)) {
|
||||||
|
this->autosuggestion = special_suggestion;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1315,9 +1328,15 @@ static bool can_autosuggest(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void autosuggest_completed(autosuggestion_context_t *ctx, int result) {
|
static void autosuggest_completed(autosuggestion_context_t *ctx, int result) {
|
||||||
if (result && can_autosuggest() && ctx->searcher.get_term() == data->command_line) {
|
if (result &&
|
||||||
|
can_autosuggest() &&
|
||||||
|
ctx->search_string == data->command_line &&
|
||||||
|
string_prefixes_string(ctx->search_string, ctx->autosuggestion)) {
|
||||||
/* Autosuggestion is active and the search term has not changed, so we're good to go */
|
/* Autosuggestion is active and the search term has not changed, so we're good to go */
|
||||||
data->autosuggestion = ctx->searcher.current_string();
|
data->autosuggestion = ctx->autosuggestion;
|
||||||
|
sanity_check();
|
||||||
|
reader_repaint();
|
||||||
|
|
||||||
}
|
}
|
||||||
delete ctx;
|
delete ctx;
|
||||||
}
|
}
|
||||||
|
|||||||
35
wutil.cpp
35
wutil.cpp
@@ -82,14 +82,41 @@ void wutil_destroy()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wreaddir(DIR *dir, std::wstring &outPath, bool *is_dir)
|
bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir)
|
||||||
{
|
{
|
||||||
struct dirent *d = readdir( dir );
|
struct dirent *d = readdir( dir );
|
||||||
if ( !d ) return false;
|
if ( !d ) return false;
|
||||||
|
|
||||||
outPath = str2wcstring(d->d_name);
|
out_name = str2wcstring(d->d_name);
|
||||||
if (is_dir)
|
if (out_is_dir) {
|
||||||
*is_dir = (d->d_type == DT_DIR);
|
bool is_dir;
|
||||||
|
if (d->d_type == DT_DIR) {
|
||||||
|
is_dir = true;
|
||||||
|
} else if (d->d_type == DT_LNK) {
|
||||||
|
/* We want to treat symlinks to directories as directories. Use stat to resolve it. */
|
||||||
|
cstring fullpath = wcs2string(dir_path);
|
||||||
|
fullpath.push_back('/');
|
||||||
|
fullpath.append(d->d_name);
|
||||||
|
struct stat buf;
|
||||||
|
if (stat(fullpath.c_str(), &buf) != 0) {
|
||||||
|
is_dir = false;
|
||||||
|
} else {
|
||||||
|
is_dir = !! (S_ISDIR(buf.st_mode));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
is_dir = false;
|
||||||
|
}
|
||||||
|
*out_is_dir = is_dir;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wreaddir(DIR *dir, std::wstring &out_name)
|
||||||
|
{
|
||||||
|
struct dirent *d = readdir( dir );
|
||||||
|
if ( !d ) return false;
|
||||||
|
|
||||||
|
out_name = str2wcstring(d->d_name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
wutil.h
3
wutil.h
@@ -113,7 +113,8 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path);
|
|||||||
/**
|
/**
|
||||||
Wide character version of readdir()
|
Wide character version of readdir()
|
||||||
*/
|
*/
|
||||||
bool wreaddir(DIR *dir, std::wstring &outPath, bool *outIsDirectory = NULL);
|
bool wreaddir(DIR *dir, std::wstring &out_name);
|
||||||
|
bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Wide character version of dirname()
|
Wide character version of dirname()
|
||||||
|
|||||||
Reference in New Issue
Block a user