Changes to make autosuggestion even smarter by specially recognizing the cd command.

This commit is contained in:
ridiculousfish 2012-02-18 18:54:36 -08:00
parent ed89df7e9d
commit 14b3a5be56
8 changed files with 296 additions and 131 deletions

View file

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

View file

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

View file

@ -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.
*/

View file

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

View file

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

109
path.cpp
View file

@ -336,47 +336,46 @@ 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( dir[0] == L'/'|| (wcsncmp( dir, L"./", 2 )==0) )
{
struct stat buf;
if( wstat( dir, &buf ) == 0 )
{
if( S_ISDIR(buf.st_mode) )
{
res = wcsdup(dir);
}
else
{
err = ENOTDIR;
if (wd) {
size_t len = wcslen(wd);
assert(wd[len - 1] == L'/');
}
}
}
else
{
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 = L".";
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() );
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) )
@ -398,13 +397,20 @@ wchar_t *path_allocate_cdpath( const wchar_t *dir )
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;
if( wstat( whole_path, &buf ) == 0 )
const wchar_t *dir = iter->c_str();
if( wstat( dir, &buf ) == 0 )
{
if( S_ISDIR(buf.st_mode) )
{
res = whole_path;
res = wcsdup(dir);
break;
}
else
@ -412,17 +418,6 @@ wchar_t *path_allocate_cdpath( const wchar_t *dir )
err = ENOTDIR;
}
}
else
{
if( lwstat( whole_path, &buf ) == 0 )
{
err = EROTTEN;
}
}
free( whole_path );
}
free( path_cpy );
}
if( !res )
@ -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
View file

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

View file

@ -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)
{
}
};
static int threaded_autosuggest(autosuggestion_context_t *ctx) {
int threaded_autosuggest(void) {
ASSERT_IS_BACKGROUND_THREAD();
while (ctx->searcher.go_backwards()) {
history_item_t item = ctx->searcher.current_item();
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 */
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);
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) {
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)