Piling on more code to make autosuggestion try to guess directories even when they're not in the history

This commit is contained in:
ridiculousfish
2012-02-20 02:13:31 -08:00
parent 52daf6cf41
commit d5c382bb1a
5 changed files with 213 additions and 11 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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()