mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-05 16:21:15 -03:00
Merge branch 'master' into documentation-update
Conflicts (FIXED): .gitignore doc_src/complete.txt doc_src/function.txt
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
*~
|
||||
*.exe
|
||||
|
||||
.DS_Store
|
||||
Makefile
|
||||
autom4te.cache/
|
||||
build/
|
||||
@@ -39,15 +40,10 @@ messages.pot
|
||||
|
||||
lexicon.txt
|
||||
lexicon_filter
|
||||
lexicon-debug.log
|
||||
lexicon.log
|
||||
|
||||
Fish-Shell.sublime-workspace
|
||||
Fish-Shell.sublime-project
|
||||
.editorconfig
|
||||
doc_src/.editorconfig
|
||||
|
||||
|
||||
|
||||
lexicon.log
|
||||
|
||||
.DS_Store
|
||||
|
||||
13
builtin.cpp
13
builtin.cpp
@@ -1809,6 +1809,8 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
|
||||
bool shadows = true;
|
||||
|
||||
woptind=0;
|
||||
|
||||
wcstring_list_t wrap_targets;
|
||||
|
||||
const struct woption long_options[] =
|
||||
{
|
||||
@@ -1818,6 +1820,7 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
|
||||
{ L"on-process-exit", required_argument, 0, 'p' },
|
||||
{ L"on-variable", required_argument, 0, 'v' },
|
||||
{ L"on-event", required_argument, 0, 'e' },
|
||||
{ L"wraps", required_argument, 0, 'w' },
|
||||
{ L"help", no_argument, 0, 'h' },
|
||||
{ L"argument-names", no_argument, 0, 'a' },
|
||||
{ L"no-scope-shadowing", no_argument, 0, 'S' },
|
||||
@@ -1979,6 +1982,10 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
|
||||
case 'S':
|
||||
shadows = 0;
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
wrap_targets.push_back(woptarg);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
@@ -2086,6 +2093,12 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
|
||||
d.definition = contents.c_str();
|
||||
|
||||
function_add(d, parser, definition_line_offset);
|
||||
|
||||
// Handle wrap targets
|
||||
for (size_t w=0; w < wrap_targets.size(); w++)
|
||||
{
|
||||
complete_add_wrapper(name, wrap_targets.at(w));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
23
builtin.h
23
builtin.h
@@ -164,12 +164,25 @@ void builtin_pop_io(parser_t &parser);
|
||||
wcstring builtin_get_desc(const wcstring &b);
|
||||
|
||||
|
||||
/**
|
||||
Slightly kludgy function used with 'complete -C' in order to make
|
||||
the commandline builtin operate on the string to complete instead
|
||||
of operating on whatever is to be completed.
|
||||
|
||||
/** Support for setting and removing transient command lines.
|
||||
This is used by 'complete -C' in order to make
|
||||
the commandline builtin operate on the string to complete instead
|
||||
of operating on whatever is to be completed. It's also used by
|
||||
completion wrappers, to allow a command to appear as the command
|
||||
being wrapped for the purposes of completion.
|
||||
|
||||
Instantiating an instance of builtin_commandline_scoped_transient_t
|
||||
pushes the command as the new transient commandline. The destructor removes it.
|
||||
It will assert if construction/destruction does not happen in a stack-like (LIFO) order.
|
||||
*/
|
||||
const wchar_t *builtin_complete_get_temporary_buffer();
|
||||
class builtin_commandline_scoped_transient_t
|
||||
{
|
||||
size_t token;
|
||||
public:
|
||||
builtin_commandline_scoped_transient_t(const wcstring &cmd);
|
||||
~builtin_commandline_scoped_transient_t();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,6 +79,51 @@ static size_t get_cursor_pos()
|
||||
return current_cursor_pos;
|
||||
}
|
||||
|
||||
static pthread_mutex_t transient_commandline_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static wcstring_list_t *get_transient_stack()
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
ASSERT_IS_LOCKED(transient_commandline_lock);
|
||||
// A pointer is a little more efficient than an object as a static because we can elide the thread-safe initialization
|
||||
static wcstring_list_t *result = NULL;
|
||||
if (! result)
|
||||
{
|
||||
result = new wcstring_list_t();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool get_top_transient(wcstring *out_result)
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
bool result = false;
|
||||
scoped_lock locker(transient_commandline_lock);
|
||||
const wcstring_list_t *stack = get_transient_stack();
|
||||
if (! stack->empty())
|
||||
{
|
||||
out_result->assign(stack->back());
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
builtin_commandline_scoped_transient_t::builtin_commandline_scoped_transient_t(const wcstring &cmd)
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
scoped_lock locker(transient_commandline_lock);
|
||||
wcstring_list_t *stack = get_transient_stack();
|
||||
stack->push_back(cmd);
|
||||
this->token = stack->size();
|
||||
}
|
||||
|
||||
builtin_commandline_scoped_transient_t::~builtin_commandline_scoped_transient_t()
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
scoped_lock locker(transient_commandline_lock);
|
||||
wcstring_list_t *stack = get_transient_stack();
|
||||
assert(this->token == stack->size());
|
||||
stack->pop_back();
|
||||
}
|
||||
|
||||
/**
|
||||
Replace/append/insert the selection with/at/after the specified string.
|
||||
@@ -216,11 +261,15 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
|
||||
int search_mode = 0;
|
||||
int paging_mode = 0;
|
||||
const wchar_t *begin = NULL, *end = NULL;
|
||||
|
||||
current_buffer = (wchar_t *)builtin_complete_get_temporary_buffer();
|
||||
if (current_buffer)
|
||||
|
||||
scoped_push<const wchar_t *> saved_current_buffer(¤t_buffer);
|
||||
scoped_push<size_t> saved_current_cursor_pos(¤t_cursor_pos);
|
||||
|
||||
wcstring transient_commandline;
|
||||
if (get_top_transient(&transient_commandline))
|
||||
{
|
||||
current_cursor_pos = wcslen(current_buffer);
|
||||
current_buffer = transient_commandline.c_str();
|
||||
current_cursor_pos = transient_commandline.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -24,12 +24,6 @@ Functions used for implementing the complete builtin.
|
||||
#include "parser.h"
|
||||
#include "reader.h"
|
||||
|
||||
|
||||
/**
|
||||
Internal storage for the builtin_complete_get_temporary_buffer() function.
|
||||
*/
|
||||
static const wchar_t *temporary_buffer;
|
||||
|
||||
/*
|
||||
builtin_complete_* are a set of rather silly looping functions that
|
||||
make sure that all the proper combinations of complete_add or
|
||||
@@ -270,13 +264,6 @@ static void builtin_complete_remove(const wcstring_list_t &cmd,
|
||||
|
||||
}
|
||||
|
||||
|
||||
const wchar_t *builtin_complete_get_temporary_buffer()
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
return temporary_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
The complete builtin. Used for specifying programmable
|
||||
tab-completions. Calls the functions in complete.c for any heavy
|
||||
@@ -300,6 +287,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||
|
||||
wcstring_list_t cmd;
|
||||
wcstring_list_t path;
|
||||
wcstring_list_t wrap_targets;
|
||||
|
||||
static int recursion_level=0;
|
||||
|
||||
@@ -326,6 +314,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||
{ L"unauthoritative", no_argument, 0, 'u' },
|
||||
{ L"authoritative", no_argument, 0, 'A' },
|
||||
{ L"condition", required_argument, 0, 'n' },
|
||||
{ L"wraps", required_argument, 0, 'w' },
|
||||
{ L"do-complete", optional_argument, 0, 'C' },
|
||||
{ L"help", no_argument, 0, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
@@ -422,6 +411,10 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||
case 'n':
|
||||
condition = woptarg;
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
wrap_targets.push_back(woptarg);
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
do_complete = true;
|
||||
@@ -495,9 +488,9 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||
const wchar_t *token;
|
||||
|
||||
parse_util_token_extent(do_complete_param.c_str(), do_complete_param.size(), &token, 0, 0, 0);
|
||||
|
||||
const wchar_t *prev_temporary_buffer = temporary_buffer;
|
||||
temporary_buffer = do_complete_param.c_str();
|
||||
|
||||
/* Create a scoped transient command line, so that bulitin_commandline will see our argument, not the reader buffer */
|
||||
builtin_commandline_scoped_transient_t temp_buffer(do_complete_param);
|
||||
|
||||
if (recursion_level < 1)
|
||||
{
|
||||
@@ -536,9 +529,6 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||
|
||||
recursion_level--;
|
||||
}
|
||||
|
||||
temporary_buffer = prev_temporary_buffer;
|
||||
|
||||
}
|
||||
else if (woptind != argc)
|
||||
{
|
||||
@@ -558,7 +548,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||
else
|
||||
{
|
||||
int flags = COMPLETE_AUTO_SPACE;
|
||||
|
||||
|
||||
if (remove)
|
||||
{
|
||||
builtin_complete_remove(cmd,
|
||||
@@ -566,6 +556,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||
short_opt.c_str(),
|
||||
gnu_opt,
|
||||
old_opt);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -581,7 +572,18 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||
desc,
|
||||
flags);
|
||||
}
|
||||
|
||||
|
||||
// Handle wrap targets (probably empty)
|
||||
// We only wrap commands, not paths
|
||||
for (size_t w=0; w < wrap_targets.size(); w++)
|
||||
{
|
||||
const wcstring &wrap_target = wrap_targets.at(w);
|
||||
for (size_t i=0; i < cmd.size(); i++)
|
||||
{
|
||||
|
||||
(remove ? complete_remove_wrapper : complete_add_wrapper)(cmd.at(i), wrap_target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
15
common.cpp
15
common.cpp
@@ -1019,7 +1019,22 @@ static void escape_string_internal(const wchar_t *orig_in, size_t in_len, wcstri
|
||||
out += *in;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Experimental fix for #1614
|
||||
// The hope is that any time these appear in a string, they came from wildcard expansion
|
||||
case ANY_CHAR:
|
||||
out += L'?';
|
||||
break;
|
||||
|
||||
case ANY_STRING:
|
||||
out += L'*';
|
||||
break;
|
||||
|
||||
case ANY_STRING_RECURSIVE:
|
||||
out += L"**";
|
||||
break;
|
||||
|
||||
case L'&':
|
||||
case L'$':
|
||||
case L' ':
|
||||
|
||||
160
complete.cpp
160
complete.cpp
@@ -1516,7 +1516,7 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop
|
||||
|
||||
if ((o->short_opt == L'\0') && (o->long_opt[0]==L'\0'))
|
||||
{
|
||||
use_files &= ((o->result_mode & NO_FILES)==0);
|
||||
use_files = use_files && ((o->result_mode & NO_FILES)==0);
|
||||
complete_from_args(str, o->comp, o->localized_desc(), o->flags);
|
||||
}
|
||||
|
||||
@@ -1997,10 +1997,34 @@ void complete(const wcstring &cmd_with_subcmds, std::vector<completion_t> &comps
|
||||
unescape_string(previous_argument, &previous_argument_unescape, UNESCAPE_DEFAULT) &&
|
||||
unescape_string(current_argument, ¤t_argument_unescape, UNESCAPE_INCOMPLETE))
|
||||
{
|
||||
do_file = completer.complete_param(current_command_unescape,
|
||||
// Have to walk over the command and its entire wrap chain
|
||||
// If any command disables do_file, then they all do
|
||||
do_file = true;
|
||||
const wcstring_list_t wrap_chain = complete_get_wrap_chain(current_command_unescape);
|
||||
for (size_t i=0; i < wrap_chain.size(); i++)
|
||||
{
|
||||
// Hackish, this. The first command in the chain is always the given command. For every command past the first, we need to create a transient commandline for builtin_commandline. But not for COMPLETION_REQUEST_AUTOSUGGESTION, which may occur on background threads.
|
||||
builtin_commandline_scoped_transient_t *transient_cmd = NULL;
|
||||
if (i == 0)
|
||||
{
|
||||
assert(wrap_chain.at(i) == current_command_unescape);
|
||||
}
|
||||
else if (! (flags & COMPLETION_REQUEST_AUTOSUGGESTION))
|
||||
{
|
||||
assert(cmd_node != NULL);
|
||||
wcstring faux_cmdline = cmd;
|
||||
faux_cmdline.replace(cmd_node->source_start, cmd_node->source_length, wrap_chain.at(i));
|
||||
transient_cmd = new builtin_commandline_scoped_transient_t(faux_cmdline);
|
||||
}
|
||||
if (! completer.complete_param(wrap_chain.at(i),
|
||||
previous_argument_unescape,
|
||||
current_argument_unescape,
|
||||
!had_ddash);
|
||||
!had_ddash))
|
||||
{
|
||||
do_file = false;
|
||||
}
|
||||
delete transient_cmd; //may be null
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have found no command specific completions at all, fall back to using file completions. */
|
||||
@@ -2072,7 +2096,7 @@ void complete_print(wcstring &out)
|
||||
|
||||
append_switch(out,
|
||||
e->cmd_is_path ? L"path" : L"command",
|
||||
e->cmd);
|
||||
escape_string(e->cmd, ESCAPE_ALL));
|
||||
|
||||
|
||||
if (o->short_opt != 0)
|
||||
@@ -2102,4 +2126,132 @@ void complete_print(wcstring &out)
|
||||
out.append(L"\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Append wraps. This is a wonky interface where even values are the commands, and odd values are the targets that they wrap. */
|
||||
const wcstring_list_t wrap_pairs = complete_get_wrap_pairs();
|
||||
assert(wrap_pairs.size() % 2 == 0);
|
||||
for (size_t i=0; i < wrap_pairs.size(); )
|
||||
{
|
||||
const wcstring &cmd = wrap_pairs.at(i++);
|
||||
const wcstring &target = wrap_pairs.at(i++);
|
||||
append_format(out, L"complete --command %ls --wraps %ls\n", cmd.c_str(), target.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Completion "wrapper" support. The map goes from wrapping-command to wrapped-command-list */
|
||||
static pthread_mutex_t wrapper_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
typedef std::map<wcstring, wcstring_list_t> wrapper_map_t;
|
||||
static wrapper_map_t &wrap_map()
|
||||
{
|
||||
ASSERT_IS_LOCKED(wrapper_lock);
|
||||
// A pointer is a little more efficient than an object as a static because we can elide the thread-safe initialization
|
||||
static wrapper_map_t *wrapper_map = NULL;
|
||||
if (wrapper_map == NULL)
|
||||
{
|
||||
wrapper_map = new wrapper_map_t();
|
||||
}
|
||||
return *wrapper_map;
|
||||
}
|
||||
|
||||
/* Add a new target that is wrapped by command. Example: sgrep (command) wraps grep (target). */
|
||||
bool complete_add_wrapper(const wcstring &command, const wcstring &new_target)
|
||||
{
|
||||
if (command.empty() || new_target.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
scoped_lock locker(wrapper_lock);
|
||||
wrapper_map_t &wraps = wrap_map();
|
||||
wcstring_list_t *targets = &wraps[command];
|
||||
// If it's already present, we do nothing
|
||||
if (std::find(targets->begin(), targets->end(), new_target) == targets->end())
|
||||
{
|
||||
targets->push_back(new_target);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool complete_remove_wrapper(const wcstring &command, const wcstring &target_to_remove)
|
||||
{
|
||||
if (command.empty() || target_to_remove.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
scoped_lock locker(wrapper_lock);
|
||||
wrapper_map_t &wraps = wrap_map();
|
||||
bool result = false;
|
||||
wrapper_map_t::iterator current_targets_iter = wraps.find(command);
|
||||
if (current_targets_iter != wraps.end())
|
||||
{
|
||||
wcstring_list_t *targets = ¤t_targets_iter->second;
|
||||
wcstring_list_t::iterator where = std::find(targets->begin(), targets->end(), target_to_remove);
|
||||
if (where != targets->end())
|
||||
{
|
||||
targets->erase(where);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
wcstring_list_t complete_get_wrap_chain(const wcstring &command)
|
||||
{
|
||||
if (command.empty())
|
||||
{
|
||||
return wcstring_list_t();
|
||||
}
|
||||
scoped_lock locker(wrapper_lock);
|
||||
const wrapper_map_t &wraps = wrap_map();
|
||||
|
||||
wcstring_list_t result;
|
||||
std::set<wcstring> visited; // set of visited commands
|
||||
wcstring_list_t to_visit(1, command); // stack of remaining-to-visit commands
|
||||
|
||||
wcstring target;
|
||||
while (! to_visit.empty())
|
||||
{
|
||||
// Grab the next command to visit, put it in target
|
||||
target.swap(to_visit.back());
|
||||
to_visit.pop_back();
|
||||
|
||||
// Try inserting into visited. If it was already present, we skip it; this is how we avoid loops.
|
||||
if (! visited.insert(target).second)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Insert the target in the result. Note this is the command itself, if this is the first iteration of the loop.
|
||||
result.push_back(target);
|
||||
|
||||
// Enqueue its children
|
||||
wrapper_map_t::const_iterator target_children_iter = wraps.find(target);
|
||||
if (target_children_iter != wraps.end())
|
||||
{
|
||||
const wcstring_list_t &children = target_children_iter->second;
|
||||
to_visit.insert(to_visit.end(), children.begin(), children.end());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
wcstring_list_t complete_get_wrap_pairs()
|
||||
{
|
||||
wcstring_list_t result;
|
||||
scoped_lock locker(wrapper_lock);
|
||||
const wrapper_map_t &wraps = wrap_map();
|
||||
for (wrapper_map_t::const_iterator outer = wraps.begin(); outer != wraps.end(); ++outer)
|
||||
{
|
||||
const wcstring &cmd = outer->first;
|
||||
const wcstring_list_t &targets = outer->second;
|
||||
for (size_t i=0; i < targets.size(); i++)
|
||||
{
|
||||
result.push_back(cmd);
|
||||
result.push_back(targets.at(i));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -267,4 +267,12 @@ void append_completion(std::vector<completion_t> &completions, const wcstring &c
|
||||
/* Function used for testing */
|
||||
void complete_set_variable_names(const wcstring_list_t *names);
|
||||
|
||||
/* Support for "wrap targets." A wrap target is a command that completes liek another command. The target chain is the sequence of wraps (A wraps B wraps C...). Any loops in the chain are silently ignored. */
|
||||
bool complete_add_wrapper(const wcstring &command, const wcstring &wrap_target);
|
||||
bool complete_remove_wrapper(const wcstring &command, const wcstring &wrap_target);
|
||||
wcstring_list_t complete_get_wrap_chain(const wcstring &command);
|
||||
|
||||
/* Wonky interface: returns all wraps. Even-values are the commands, odd values are the targets. */
|
||||
wcstring_list_t complete_get_wrap_pairs();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
\subsection complete-synopsis Synopsis
|
||||
\fish{synopsis}
|
||||
complete ( -c | --command | -p | --path) COMMAND
|
||||
[( -s | --short-option) SHORT_OPTION]
|
||||
[( -l | --long-option | -o | --old-option) LONG_OPTION]
|
||||
[( -a | --arguments) OPTION_ARGUMENTS]
|
||||
[( -d | --description) DESCRIPTION]
|
||||
complete (-c | --command | -p | --path) COMMAND
|
||||
[(-s | --short-option) SHORT_OPTION]
|
||||
[(-l | --long-option | -o | --old-option) LONG_OPTION]
|
||||
[(-a | --arguments) OPTION_ARGUMENTS]
|
||||
[(-w | --wraps) WRAPPED_COMMAND]
|
||||
[(-d | --description) DESCRIPTION]
|
||||
\endfish
|
||||
|
||||
|
||||
@@ -16,21 +17,23 @@ For an introduction to specifying completions, see <a
|
||||
href='index.html#completion-own'>Writing your own completions</a> in
|
||||
the fish manual.
|
||||
|
||||
- `COMMAND` is the name of the command for which to add a completion
|
||||
- `SHORT_OPTION` is a one character option for the command
|
||||
- `LONG_OPTION` is a multi character option for the command
|
||||
- `OPTION_ARGUMENTS` is parameter containing a space-separated list of possible option-arguments, which may contain subshells
|
||||
- `DESCRIPTION` is a description of what the option and/or option arguments do
|
||||
- `-C STRING` or `--do-complete=STRING` makes complete try to find all possible completions for the specified string
|
||||
- `-e` or `--erase` implies that the specified completion should be deleted
|
||||
- `-f` or `--no-files` specifies that the option specified by this completion may not be followed by a filename
|
||||
<<<<<<< HEAD
|
||||
- `COMMAND` is the name of the command for which to add a completion.
|
||||
- `SHORT_OPTION` is a one character option for the command.
|
||||
- `LONG_OPTION` is a multi character option for the command.
|
||||
- `OPTION_ARGUMENTS` is parameter containing a space-separated list of possible option-arguments, which may contain subshells.
|
||||
- `DESCRIPTION` is a description of what the option and/or option arguments do.
|
||||
- `-C STRING` or `--do-complete=STRING` makes complete try to find all possible completions for the specified string.
|
||||
- `-w WRAPPED_COMMAND` or `--wraps=WRAPPED_COMMAND` causes the specified command to inherit completions from the wrapped command.
|
||||
- `-e` or `--erase` implies that the specified completion should be deleted.
|
||||
- `-f` or `--no-files` specifies that the option specified by this completion may not be followed by a filename.
|
||||
- `-n` or `--condition` specifies a shell command that must return 0 if the completion is to be used. This makes it possible to specify completions that should only be used in some cases.
|
||||
- `-o` or `--old-option` implies that the command uses old long style options with only one dash
|
||||
- `-p` or `--path` implies that the string `COMMAND` is the full path of the command
|
||||
- `-r` or `--require-parameter` specifies that the option specified by this completion always must have an option argument, i.e. may not be followed by another option
|
||||
- `-u` or `--unauthoritative` implies that there may be more options than the ones specified, and that fish should not assume that options not listed are spelling errors
|
||||
- `-A` or `--authoritative` implies that there may be no more options than the ones specified, and that fish should assume that options not listed are spelling errors
|
||||
- `-x` or `--exclusive` implies both `-r` and `-f`
|
||||
- `-o` or `--old-option` implies that the command uses old long style options with only one dash.
|
||||
- `-p` or `--path` implies that the string `COMMAND` is the full path of the command.
|
||||
- `-r` or `--require-parameter` specifies that the option specified by this completion always must have an option argument, i.e. may not be followed by another option.
|
||||
- `-u` or `--unauthoritative` implies that there may be more options than the ones specified, and that fish should not assume that options not listed are spelling errors.
|
||||
- `-A` or `--authoritative` implies that there may be no more options than the ones specified, and that fish should assume that options not listed are spelling errors.
|
||||
- `-x` or `--exclusive` implies both `-r` and `-f`.
|
||||
|
||||
Command specific tab-completions in `fish` are based on the notion
|
||||
of options and arguments. An option is a parameter which begins with a
|
||||
@@ -48,6 +51,14 @@ switches may all be used multiple times to specify multiple commands
|
||||
which have the same completion or multiple switches accepted by a
|
||||
command.
|
||||
|
||||
The \c -w or \c --wraps options causes the specified command to inherit
|
||||
completions from another command. The inheriting command is said to
|
||||
"wrap" the inherited command. The wrapping command may have its own
|
||||
completions in addition to inherited ones. A command may wrap multiple
|
||||
commands, and wrapping is transitive: if A wraps B, and B wraps C,
|
||||
then A automatically inherits all of C's completions. Wrapping can
|
||||
be removed using the \c -e or \c --erase options.
|
||||
|
||||
When erasing completions, it is possible to either erase all
|
||||
completions for a specific command by specifying `complete -e -c
|
||||
COMMAND`, or by specifying a specific completion option to delete
|
||||
@@ -84,3 +95,10 @@ This can be written as:
|
||||
where `__fish_contains_opt` is a function that checks the commandline
|
||||
buffer for the presence of a specified set of options.
|
||||
|
||||
To implement an alias, use the `-w` or `--wraps` option:
|
||||
|
||||
`complete -c hub -w git`
|
||||
|
||||
Now hub inherits all of the completions from git. Note this can
|
||||
also be specified in a function declaration.
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ The following options are available:
|
||||
|
||||
- `-a NAMES` or `--argument-names NAMES` assigns the value of successive command-line arguments to the names given in NAMES.
|
||||
- `-d DESCRIPTION` or `--description=DESCRIPTION` is a description of what the function does, suitable as a completion description.
|
||||
- `-w WRAPPED_COMMAND` or `--wraps=WRAPPED_COMMAND` causes the function to inherit completions from the given wrapped command. See the documentation for <a href="#complete">`complete`</a> for more information.
|
||||
- `-e` or `--on-event EVENT_NAME` tells fish to run this function when the specified named event is emitted. Fish internally generates named events e.g. when showing the prompt.
|
||||
- `-j PID` or `--on-job-exit PID` tells fish to run this function when the job with group ID PID exits. Instead of PID, the string 'caller' can be specified. This is only legal when in a command substitution, and will result in the handler being triggered by the exit of the job which created this command substitution.
|
||||
- `-p PID` or `--on-process-exit PID` tells fish to run this function when the fish child process with process ID PID exits.
|
||||
@@ -58,6 +59,5 @@ function mkdir -d "Create a directory and set CWD"
|
||||
end
|
||||
\endfish
|
||||
|
||||
will run the mkdir command, and if it is successful, change the
|
||||
current working directory to the one just created.
|
||||
This will run the `mkdir` command, and if it is successful, change the current working directory to the one just created.
|
||||
|
||||
|
||||
@@ -143,20 +143,44 @@ static void err(const wchar_t *blah, ...)
|
||||
va_list va;
|
||||
va_start(va, blah);
|
||||
err_count++;
|
||||
|
||||
// Xcode's term doesn't support color (even though TERM claims it does)
|
||||
bool colorize = ! getenv("RUNNING_IN_XCODE");
|
||||
|
||||
// show errors in red
|
||||
fputs("\x1b[31m", stdout);
|
||||
if (colorize)
|
||||
{
|
||||
fputs("\x1b[31m", stdout);
|
||||
}
|
||||
|
||||
wprintf(L"Error: ");
|
||||
vwprintf(blah, va);
|
||||
va_end(va);
|
||||
|
||||
// return to normal color
|
||||
fputs("\x1b[0m", stdout);
|
||||
if (colorize)
|
||||
{
|
||||
fputs("\x1b[0m", stdout);
|
||||
}
|
||||
|
||||
wprintf(L"\n");
|
||||
}
|
||||
|
||||
// Joins a wcstring_list_t via commas
|
||||
static wcstring comma_join(const wcstring_list_t &lst)
|
||||
{
|
||||
wcstring result;
|
||||
for (size_t i=0; i < lst.size(); i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
result.push_back(L',');
|
||||
}
|
||||
result.append(lst.at(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#define do_test(e) do { if (! (e)) err(L"Test failed on line %lu: %s", __LINE__, #e); } while (0)
|
||||
|
||||
/* Test sane escapes */
|
||||
@@ -1915,6 +1939,18 @@ static void test_complete(void)
|
||||
do_test(completions.empty());
|
||||
|
||||
complete_set_variable_names(NULL);
|
||||
|
||||
/* Test wraps */
|
||||
do_test(comma_join(complete_get_wrap_chain(L"wrapper1")) == L"wrapper1");
|
||||
complete_add_wrapper(L"wrapper1", L"wrapper2");
|
||||
do_test(comma_join(complete_get_wrap_chain(L"wrapper1")) == L"wrapper1,wrapper2");
|
||||
complete_add_wrapper(L"wrapper2", L"wrapper3");
|
||||
do_test(comma_join(complete_get_wrap_chain(L"wrapper1")) == L"wrapper1,wrapper2,wrapper3");
|
||||
complete_add_wrapper(L"wrapper3", L"wrapper1"); //loop!
|
||||
do_test(comma_join(complete_get_wrap_chain(L"wrapper1")) == L"wrapper1,wrapper2,wrapper3");
|
||||
complete_remove_wrapper(L"wrapper1", L"wrapper2");
|
||||
do_test(comma_join(complete_get_wrap_chain(L"wrapper1")) == L"wrapper1");
|
||||
do_test(comma_join(complete_get_wrap_chain(L"wrapper2")) == L"wrapper2,wrapper3,wrapper1");
|
||||
}
|
||||
|
||||
static void test_1_completion(wcstring line, const wcstring &completion, complete_flags_t flags, bool append_only, wcstring expected, long source_line)
|
||||
|
||||
@@ -464,6 +464,13 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
|
||||
{
|
||||
rendering->remaining_to_disclose = 0;
|
||||
}
|
||||
|
||||
/* If we have only one row remaining to disclose, then squelch the comment row. This prevents us from consuming a line to show "...and 1 more row" */
|
||||
if (! this->fully_disclosed && rendering->remaining_to_disclose == 1)
|
||||
{
|
||||
term_height += 1;
|
||||
rendering->remaining_to_disclose = 0;
|
||||
}
|
||||
|
||||
int pref_tot_width=0;
|
||||
int min_tot_width = 0;
|
||||
|
||||
@@ -403,6 +403,9 @@ public:
|
||||
/* Sets the command line contents, without clearing the pager */
|
||||
static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos);
|
||||
|
||||
/* Clears the pager */
|
||||
static void clear_pager();
|
||||
|
||||
/**
|
||||
The current interactive reading context
|
||||
*/
|
||||
@@ -1564,6 +1567,9 @@ static void accept_autosuggestion(bool full)
|
||||
{
|
||||
if (! data->autosuggestion.empty())
|
||||
{
|
||||
/* Accepting an autosuggestion clears the pager */
|
||||
clear_pager();
|
||||
|
||||
/* Accept the autosuggestion */
|
||||
if (full)
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ complete -x -c ssh -d Hostname -a "
|
||||
"
|
||||
|
||||
complete -x -c ssh -d User -a "
|
||||
(__fish_print_users)@
|
||||
(__fish_print_users | sgrep -v '^_')@
|
||||
"
|
||||
complete -c ssh --description "Command to run" -x -a '(__fish_complete_subcommand --fcs-skip=2)'
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
function __fish_print_users --description "Print a list of local users"
|
||||
if test -x /usr/bin/getent
|
||||
getent passwd | cut -d : -f 1
|
||||
else if test -x /usr/bin/dscl # OS X support
|
||||
dscl . -list /Users
|
||||
else
|
||||
sgrep -ve '^#' /etc/passwd | cut -d : -f 1
|
||||
end
|
||||
|
||||
@@ -48,5 +48,5 @@ function alias --description "Legacy function for creating shellscript functions
|
||||
end
|
||||
end
|
||||
|
||||
eval "function $name; $prefix $body \$argv; end"
|
||||
eval "function $name --wraps $body; $prefix $body \$argv; end"
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user