mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-23 00:01:16 -03:00
Compare commits
23 Commits
OpenBeta_r
...
OpenBeta_r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b187538339 | ||
|
|
93f6aabe12 | ||
|
|
10aa107380 | ||
|
|
53cba2a2e6 | ||
|
|
d871095d0b | ||
|
|
9bf8a5e877 | ||
|
|
3836bfe5a1 | ||
|
|
7698553c3a | ||
|
|
be1d216b34 | ||
|
|
85cbb943b5 | ||
|
|
ec34f2527a | ||
|
|
b877181e17 | ||
|
|
69446be1ee | ||
|
|
cc90f9cf80 | ||
|
|
457e6a5f46 | ||
|
|
e7db18bae2 | ||
|
|
f8e3e853aa | ||
|
|
b7ba252965 | ||
|
|
ae12e1b537 | ||
|
|
9255f625fa | ||
|
|
377d93d1cf | ||
|
|
19d14c7c3a | ||
|
|
6e3a823b7b |
@@ -168,8 +168,14 @@ GENERATED_INTERN_SCRIPT_FILES := builtin_scripts.h builtin_scripts.cpp
|
|||||||
|
|
||||||
# Use a pattern rule so that Make knows to only issue one invocation
|
# Use a pattern rule so that Make knows to only issue one invocation
|
||||||
# per http://www.gnu.org/software/make/manual/make.html#Pattern-Intro
|
# per http://www.gnu.org/software/make/manual/make.html#Pattern-Intro
|
||||||
|
|
||||||
|
# Internalized scripts are currently disabled.
|
||||||
|
# For now, we just generate empty arrays.
|
||||||
|
# To generate them again, you would run this:
|
||||||
|
# ./internalize_scripts.py share/functions/*.fish share/completions/*.fish
|
||||||
|
|
||||||
builtin%scripts.h builtin%scripts.cpp: internalize_scripts.py
|
builtin%scripts.h builtin%scripts.cpp: internalize_scripts.py
|
||||||
./internalize_scripts.py share/functions/*.fish share/completions/*.fish
|
./internalize_scripts.py
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ The classes responsible for autoloading functions and completions.
|
|||||||
static const int kAutoloadStalenessInterval = 15;
|
static const int kAutoloadStalenessInterval = 15;
|
||||||
|
|
||||||
file_access_attempt_t access_file(const wcstring &path, int mode) {
|
file_access_attempt_t access_file(const wcstring &path, int mode) {
|
||||||
|
//printf("Touch %ls\n", path.c_str());
|
||||||
file_access_attempt_t result = {0};
|
file_access_attempt_t result = {0};
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
if (wstat(path, &statbuf)) {
|
if (wstat(path, &statbuf)) {
|
||||||
|
|||||||
3
build_tools/description-pak
Normal file
3
build_tools/description-pak
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
This is the_ridiculous'fish s delightful fork of, fish friendly interactive shell. For more information, visit http://ridiculousfish.com/shell/ .
|
||||||
|
|
||||||
|
This installer will install fish, but will not modify your /etc/shells file or your default shell. I trust you know how to do that yourself if you care to!
|
||||||
@@ -1 +1 @@
|
|||||||
<pkgref spec="1.12" uuid="6A7E1ED5-E40E-4ACE-959B-B9D77555AD39"><config><identifier>com.ridiculousfish.fish-shell.fishShell.fish_pkg.pkg</identifier><version>1.0</version><description></description><post-install type="none"/><requireAuthorization/><installFrom>/private/tmp/fish_pkg</installFrom><installTo>/</installTo><flags><followSymbolicLinks/></flags><packageStore type="internal"></packageStore><mod>installTo</mod><mod>parent</mod></config></pkgref>
|
<pkgref spec="1.12" uuid="6A7E1ED5-E40E-4ACE-959B-B9D77555AD39"><config><identifier>com.ridiculousfish.fish-shell.fishShell.fish_pkg.pkg</identifier><version>1.0</version><description></description><post-install type="none"/><requireAuthorization/><installFrom>/private/tmp/fish_pkg</installFrom><installTo>/</installTo><flags><followSymbolicLinks/></flags><packageStore type="internal"></packageStore><mod>parent</mod><mod>installTo</mod></config></pkgref>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
\
|
\
|
||||||
The path to fish will be added to /etc/shells, but your default shell will not be modified. \
|
The path to fish will be added to /etc/shells, but your default shell will not be modified. \
|
||||||
\
|
\
|
||||||
This release is beta r1.\
|
This release is beta r2.\
|
||||||
}]]></resource><resource mime-type="text/rtf" kind="embedded" type="conclusion"><![CDATA[{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf470
|
}]]></resource><resource mime-type="text/rtf" kind="embedded" type="conclusion"><![CDATA[{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf470
|
||||||
{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Monaco;}
|
{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Monaco;}
|
||||||
{\colortbl;\red255\green255\blue255;}
|
{\colortbl;\red255\green255\blue255;}
|
||||||
|
|||||||
128
builtin.cpp
128
builtin.cpp
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
1). Create a function in builtin.c with the following signature:
|
1). Create a function in builtin.c with the following signature:
|
||||||
|
|
||||||
<tt>static int builtin_NAME( wchar_t ** args )</tt>
|
<tt>static int builtin_NAME( parser_t &parser, wchar_t ** args )</tt>
|
||||||
|
|
||||||
where NAME is the name of the builtin, and args is a zero-terminated list of arguments.
|
where NAME is the name of the builtin, and args is a zero-terminated list of arguments.
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
#include "parser_keywords.h"
|
#include "parser_keywords.h"
|
||||||
#include "expand.h"
|
#include "expand.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
|
#include "history.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The default prompt for the read command
|
The default prompt for the read command
|
||||||
@@ -3590,6 +3590,127 @@ static int builtin_case( parser_t &parser, wchar_t **argv )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
History of commands executed by user
|
||||||
|
*/
|
||||||
|
static int builtin_history( parser_t &parser, wchar_t **argv )
|
||||||
|
{
|
||||||
|
int argc = builtin_count_args(argv);
|
||||||
|
|
||||||
|
bool search_history = false;
|
||||||
|
bool delete_item = false;
|
||||||
|
bool search_prefix = false;
|
||||||
|
bool save_history = false;
|
||||||
|
bool clear_history = false;
|
||||||
|
|
||||||
|
wcstring delete_string;
|
||||||
|
wcstring search_string;
|
||||||
|
|
||||||
|
static const struct woption long_options[] =
|
||||||
|
{
|
||||||
|
{ L"prefix", required_argument, 0, 'p' },
|
||||||
|
{ L"delete", required_argument, 0, 'd' },
|
||||||
|
{ L"search", no_argument, 0, 's' },
|
||||||
|
{ L"contains", required_argument, 0, 'c' },
|
||||||
|
{ L"save", no_argument, 0, 'v' },
|
||||||
|
{ L"clear", no_argument, 0, 'l' },
|
||||||
|
{ 0, 0, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
int opt = 0;
|
||||||
|
int opt_index = 0;
|
||||||
|
woptind = 0;
|
||||||
|
history_t *history = reader_get_history();
|
||||||
|
|
||||||
|
while((opt = wgetopt_long( argc, argv, L"pdsc", long_options, &opt_index )) != -1)
|
||||||
|
{
|
||||||
|
switch(opt)
|
||||||
|
{
|
||||||
|
case 'p':
|
||||||
|
search_prefix = true;
|
||||||
|
search_string = woptarg;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
delete_item = true;
|
||||||
|
delete_string = woptarg;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
search_history = true;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
search_string = woptarg;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
save_history = true;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
clear_history = true;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
append_format(stderr_buffer, BUILTIN_ERR_UNKNOWN, argv[0], argv[woptind-1]);
|
||||||
|
return STATUS_BUILTIN_ERROR;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
append_format(stderr_buffer, BUILTIN_ERR_UNKNOWN, argv[0], argv[woptind-1]);
|
||||||
|
return STATUS_BUILTIN_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc == 1)
|
||||||
|
{
|
||||||
|
wcstring full_history;
|
||||||
|
history->get_string_representation(full_history, wcstring(L"\n"));
|
||||||
|
stdout_buffer.append(full_history);
|
||||||
|
stdout_buffer.push_back('\n');
|
||||||
|
return STATUS_BUILTIN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search_history)
|
||||||
|
{
|
||||||
|
int res = STATUS_BUILTIN_ERROR;
|
||||||
|
|
||||||
|
if (search_string.empty())
|
||||||
|
{
|
||||||
|
append_format(stderr_buffer, BUILTIN_ERR_COMBO2, argv[0], L"Use --search with either --contains or --prefix");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
history_search_t searcher = history_search_t(*history, search_string, search_prefix?HISTORY_SEARCH_TYPE_PREFIX:HISTORY_SEARCH_TYPE_CONTAINS);
|
||||||
|
while (searcher.go_backwards())
|
||||||
|
{
|
||||||
|
stdout_buffer.append(searcher.current_string());
|
||||||
|
stdout_buffer.append(L"\n");
|
||||||
|
res = STATUS_BUILTIN_OK;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delete_item)
|
||||||
|
{
|
||||||
|
if (delete_string[0] == '"' && delete_string[delete_string.length() - 1] == '"')
|
||||||
|
delete_string = delete_string.substr(1, delete_string.length() - 2);
|
||||||
|
|
||||||
|
history->remove(delete_string);
|
||||||
|
return STATUS_BUILTIN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (save_history)
|
||||||
|
{
|
||||||
|
history->save();
|
||||||
|
return STATUS_BUILTIN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clear_history)
|
||||||
|
{
|
||||||
|
history->clear();
|
||||||
|
history->save();
|
||||||
|
return STATUS_BUILTIN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return STATUS_BUILTIN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
END OF BUILTIN COMMANDS
|
END OF BUILTIN COMMANDS
|
||||||
Below are functions for handling the builtin commands.
|
Below are functions for handling the builtin commands.
|
||||||
@@ -3629,7 +3750,8 @@ static const builtin_data_t builtin_datas[]=
|
|||||||
{ L"for", &builtin_for, N_( L"Perform a set of commands multiple times" ) },
|
{ L"for", &builtin_for, N_( L"Perform a set of commands multiple times" ) },
|
||||||
{ L"function", &builtin_function, N_( L"Define a new function" ) },
|
{ L"function", &builtin_function, N_( L"Define a new function" ) },
|
||||||
{ L"functions", &builtin_functions, N_( L"List or remove functions" ) },
|
{ L"functions", &builtin_functions, N_( L"List or remove functions" ) },
|
||||||
{ L"if", &builtin_generic, N_( L"Evaluate block if condition is true" ) },
|
{ L"history", &builtin_history, N_( L"History of commands executed by user" ) },
|
||||||
|
{ L"if", &builtin_generic, N_( L"Evaluate block if condition is true" ) },
|
||||||
{ L"jobs", &builtin_jobs, N_( L"Print currently running jobs" ) },
|
{ L"jobs", &builtin_jobs, N_( L"Print currently running jobs" ) },
|
||||||
{ L"not", &builtin_generic, N_( L"Negate exit status of job" ) },
|
{ L"not", &builtin_generic, N_( L"Negate exit status of job" ) },
|
||||||
{ L"or", &builtin_generic, N_( L"Execute command if previous command failed" ) },
|
{ L"or", &builtin_generic, N_( L"Execute command if previous command failed" ) },
|
||||||
|
|||||||
36
doc_src/history.txt
Normal file
36
doc_src/history.txt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
\section history history - Show and manipulate user's command history
|
||||||
|
|
||||||
|
\subsection history-synopsis Synopsis
|
||||||
|
<pre>
|
||||||
|
history (--save | --clear)
|
||||||
|
history (--search | --delete ) (--prefix "prefix string" | --search "search string")
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
\subsection history-description Description
|
||||||
|
|
||||||
|
history is used to list, search and delete user's command history.
|
||||||
|
|
||||||
|
\subsection history-examples Example
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
history --save
|
||||||
|
Save all changes in history file.
|
||||||
|
|
||||||
|
history --clear
|
||||||
|
Delete all history items.
|
||||||
|
|
||||||
|
history --search --contains "foo"
|
||||||
|
Searches commands containing "foo" string.
|
||||||
|
|
||||||
|
history --search --prefix "foo"
|
||||||
|
Searches for commands with prefix "foo".
|
||||||
|
|
||||||
|
history --delete --contains "foo"
|
||||||
|
Interactively delete commands containing string "foo".
|
||||||
|
|
||||||
|
history --delete --prefix "foo"
|
||||||
|
Interactively delete commands with prefix "foo".
|
||||||
|
|
||||||
|
history --delete "foo"
|
||||||
|
Delete command "foo" from history.
|
||||||
|
<pre>
|
||||||
69
event.cpp
69
event.cpp
@@ -66,7 +66,8 @@ static int active_list=0;
|
|||||||
typedef std::vector<event_t *> event_list_t;
|
typedef std::vector<event_t *> event_list_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
List of event handlers
|
List of event handlers.
|
||||||
|
Note this is inspected by our signal handler, so we must block signals around manipulating it.
|
||||||
*/
|
*/
|
||||||
static event_list_t events;
|
static event_list_t events;
|
||||||
/**
|
/**
|
||||||
@@ -246,8 +247,11 @@ void event_add_handler( const event_t *event )
|
|||||||
{
|
{
|
||||||
signal_handle( e->param1.signal, 1 );
|
signal_handle( e->param1.signal, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
events.push_back(e);
|
// Block around updating the events vector
|
||||||
|
signal_block();
|
||||||
|
events.push_back(e);
|
||||||
|
signal_unblock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void event_remove( event_t *criterion )
|
void event_remove( event_t *criterion )
|
||||||
@@ -296,7 +300,9 @@ void event_remove( event_t *criterion )
|
|||||||
new_list.push_back(n);
|
new_list.push_back(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
signal_block();
|
||||||
events.swap(new_list);
|
events.swap(new_list);
|
||||||
|
signal_unblock();
|
||||||
}
|
}
|
||||||
|
|
||||||
int event_get( event_t *criterion, std::vector<event_t *> *out )
|
int event_get( event_t *criterion, std::vector<event_t *> *out )
|
||||||
@@ -322,6 +328,28 @@ int event_get( event_t *criterion, std::vector<event_t *> *out )
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool event_is_signal_observed(int sig)
|
||||||
|
{
|
||||||
|
/* We are in a signal handler! Don't allocate memory, etc.
|
||||||
|
This does what event_match does, except it doesn't require passing in an event_t.
|
||||||
|
*/
|
||||||
|
size_t i, max = events.size();
|
||||||
|
for (i=0; i < max; i++)
|
||||||
|
{
|
||||||
|
const event_t *event = events[i];
|
||||||
|
if (event->type == EVENT_ANY)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (event->type == EVENT_SIGNAL)
|
||||||
|
{
|
||||||
|
if( event->param1.signal == EVENT_ANY_SIGNAL || event->param1.signal == sig)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Free all events in the kill list
|
Free all events in the kill list
|
||||||
*/
|
*/
|
||||||
@@ -519,26 +547,32 @@ static void event_fire_delayed()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void event_fire_signal(int signal)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
This means we are in a signal handler. We must be very
|
||||||
|
careful not do do anything that could cause a memory
|
||||||
|
allocation or something else that might be bad when in a
|
||||||
|
signal handler.
|
||||||
|
*/
|
||||||
|
if( sig_list[active_list].count < SIG_UNHANDLED_MAX )
|
||||||
|
sig_list[active_list].signal[sig_list[active_list].count++]=signal;
|
||||||
|
else
|
||||||
|
sig_list[active_list].overflow=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void event_fire( event_t *event )
|
void event_fire( event_t *event )
|
||||||
{
|
{
|
||||||
is_event++;
|
|
||||||
|
|
||||||
if( event && (event->type == EVENT_SIGNAL) )
|
if( event && (event->type == EVENT_SIGNAL) )
|
||||||
{
|
{
|
||||||
/*
|
event_fire_signal(event->param1.signal);
|
||||||
This means we are in a signal handler. We must be very
|
|
||||||
careful not do do anything that could cause a memory
|
|
||||||
allocation or something else that might be bad when in a
|
|
||||||
signal handler.
|
|
||||||
*/
|
|
||||||
if( sig_list[active_list].count < SIG_UNHANDLED_MAX )
|
|
||||||
sig_list[active_list].signal[sig_list[active_list].count++]=event->param1.signal;
|
|
||||||
else
|
|
||||||
sig_list[active_list].overflow=1;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
is_event++;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Fire events triggered by signals
|
Fire events triggered by signals
|
||||||
*/
|
*/
|
||||||
@@ -555,9 +589,8 @@ void event_fire( event_t *event )
|
|||||||
event_fire_internal( event );
|
event_fire_internal( event );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is_event--;
|
||||||
}
|
}
|
||||||
is_event--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -569,10 +602,10 @@ void event_destroy()
|
|||||||
{
|
{
|
||||||
|
|
||||||
for_each(events.begin(), events.end(), event_free);
|
for_each(events.begin(), events.end(), event_free);
|
||||||
events.resize(0);
|
events.clear();
|
||||||
|
|
||||||
for_each(killme.begin(), killme.end(), event_free);
|
for_each(killme.begin(), killme.end(), event_free);
|
||||||
killme.resize(0);
|
killme.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void event_free( event_t *e )
|
void event_free( event_t *e )
|
||||||
|
|||||||
9
event.h
9
event.h
@@ -122,6 +122,12 @@ void event_remove( event_t *event );
|
|||||||
*/
|
*/
|
||||||
int event_get( event_t *criterion, std::vector<event_t *> *out );
|
int event_get( event_t *criterion, std::vector<event_t *> *out );
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns whether an event listener is registered for the given signal.
|
||||||
|
This is safe to call from a signal handler.
|
||||||
|
*/
|
||||||
|
bool event_is_signal_observed(int signal);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Fire the specified event. The function_name field of the event must
|
Fire the specified event. The function_name field of the event must
|
||||||
be set to 0. If the event is of type EVENT_SIGNAL, no the event is
|
be set to 0. If the event is of type EVENT_SIGNAL, no the event is
|
||||||
@@ -140,6 +146,9 @@ int event_get( event_t *criterion, std::vector<event_t *> *out );
|
|||||||
*/
|
*/
|
||||||
void event_fire( event_t *event );
|
void event_fire( event_t *event );
|
||||||
|
|
||||||
|
/** Like event_fire, but takes a signal directly. */
|
||||||
|
void event_fire_signal(int signal);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Initialize the event-handling library
|
Initialize the event-handling library
|
||||||
*/
|
*/
|
||||||
|
|||||||
21
expand.cpp
21
expand.cpp
@@ -1341,32 +1341,15 @@ static void expand_tilde_internal( wcstring &input )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static wchar_t * expand_tilde_internal_compat( wchar_t *in )
|
|
||||||
{
|
|
||||||
wcstring tmp = in;
|
|
||||||
expand_tilde_internal(tmp);
|
|
||||||
free(in);
|
|
||||||
return wcsdup(tmp.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void expand_tilde( wcstring &input)
|
void expand_tilde( wcstring &input)
|
||||||
{
|
{
|
||||||
if( input[0] == L'~' )
|
if( ! input.empty() && input.at(0) == L'~' )
|
||||||
{
|
{
|
||||||
input[0] = HOME_DIRECTORY;
|
input.at(0) = HOME_DIRECTORY;
|
||||||
expand_tilde_internal( input );
|
expand_tilde_internal( input );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wchar_t * expand_tilde_compat( wchar_t *input )
|
|
||||||
{
|
|
||||||
if (input[0] == L'~') {
|
|
||||||
input[0] = HOME_DIRECTORY;
|
|
||||||
return expand_tilde_internal_compat(input);
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove any internal separators. Also optionally convert wildcard characters to
|
Remove any internal separators. Also optionally convert wildcard characters to
|
||||||
regular equivalents. This is done to support EXPAND_SKIP_WILDCARDS.
|
regular equivalents. This is done to support EXPAND_SKIP_WILDCARDS.
|
||||||
|
|||||||
1
expand.h
1
expand.h
@@ -162,7 +162,6 @@ wcstring expand_escape_variable( const wcstring &in );
|
|||||||
\param input the string to tilde expand
|
\param input the string to tilde expand
|
||||||
*/
|
*/
|
||||||
void expand_tilde(wcstring &input);
|
void expand_tilde(wcstring &input);
|
||||||
wchar_t * expand_tilde_compat( wchar_t *input );
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -65,8 +65,7 @@ static void read_file( FILE *f, wcstring &b )
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
b.push_back((wchar_t)c);
|
||||||
b.push_back(c);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +74,7 @@ static void read_file( FILE *f, wcstring &b )
|
|||||||
*/
|
*/
|
||||||
static void insert_tabs( wcstring &out, int indent )
|
static void insert_tabs( wcstring &out, int indent )
|
||||||
{
|
{
|
||||||
out.append(L'\t', indent);
|
out.append(indent, L'\t');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -576,7 +576,14 @@ static void test_path()
|
|||||||
if( canon != L"/foo/bar" )
|
if( canon != L"/foo/bar" )
|
||||||
{
|
{
|
||||||
err( L"Bug in canonical PATH code" );
|
err( L"Bug in canonical PATH code" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path = L"/";
|
||||||
|
path_make_canonical(path);
|
||||||
|
if (path != L"/")
|
||||||
|
{
|
||||||
|
err( L"Bug in canonical PATH code" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Test is_potential_path */
|
/** Test is_potential_path */
|
||||||
@@ -856,7 +863,12 @@ void history_tests_t::test_history(void) {
|
|||||||
history_search_t search2(history, L"et");
|
history_search_t search2(history, L"et");
|
||||||
test_history_matches(search2, 1);
|
test_history_matches(search2, 1);
|
||||||
assert(search2.current_string() == L"Beta");
|
assert(search2.current_string() == L"Beta");
|
||||||
|
|
||||||
|
/* Test item removal */
|
||||||
|
history.remove(L"Alpha");
|
||||||
|
history_search_t search3(history, L"Alpha");
|
||||||
|
test_history_matches(search3, 0);
|
||||||
|
|
||||||
/* Test history escaping and unescaping, yaml, etc. */
|
/* Test history escaping and unescaping, yaml, etc. */
|
||||||
std::vector<history_item_t> before, after;
|
std::vector<history_item_t> before, after;
|
||||||
history.clear();
|
history.clear();
|
||||||
|
|||||||
@@ -916,7 +916,7 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int is_cmd = 0;
|
bool is_cmd = false;
|
||||||
int is_subcommand = 0;
|
int is_subcommand = 0;
|
||||||
int mark = tok_get_pos( &tok );
|
int mark = tok_get_pos( &tok );
|
||||||
color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMAND;
|
color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMAND;
|
||||||
@@ -984,11 +984,11 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
|||||||
function, since we don't have to stat
|
function, since we don't have to stat
|
||||||
any files for that
|
any files for that
|
||||||
*/
|
*/
|
||||||
if( use_builtin )
|
if (! is_cmd && use_builtin )
|
||||||
is_cmd = is_cmd || builtin_exists( cmd );
|
is_cmd = builtin_exists( cmd );
|
||||||
|
|
||||||
if( use_function )
|
if (! is_cmd && use_function )
|
||||||
is_cmd = is_cmd || function_exists_no_autoload( cmd, vars );
|
is_cmd = function_exists_no_autoload( cmd, vars );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Moving on to expensive tests
|
Moving on to expensive tests
|
||||||
@@ -997,12 +997,19 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
|||||||
/*
|
/*
|
||||||
Check if this is a regular command
|
Check if this is a regular command
|
||||||
*/
|
*/
|
||||||
if( use_command )
|
if (! is_cmd && use_command )
|
||||||
{
|
{
|
||||||
wcstring tmp;
|
wcstring tmp;
|
||||||
is_cmd = is_cmd || path_get_path_string( cmd, tmp, vars );
|
is_cmd = path_get_path_string( cmd, tmp, vars );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Maybe it is a path for a implicit cd command. */
|
||||||
|
if (! is_cmd)
|
||||||
|
{
|
||||||
|
if (use_builtin || (use_function && function_exists_no_autoload( L"cd", vars)))
|
||||||
|
is_cmd = path_can_be_implicit_cd(cmd, NULL, working_directory.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
if( is_cmd )
|
if( is_cmd )
|
||||||
{
|
{
|
||||||
color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMAND;
|
color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMAND;
|
||||||
@@ -1181,7 +1188,7 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// PCA DOES_IO (calls is_potential_path, path_get_path, maybe others)
|
// PCA This function does I/O, (calls is_potential_path, path_get_path, maybe others) and so ought to only run on a background thread
|
||||||
void highlight_shell( const wcstring &buff, std::vector<int> &color, int pos, wcstring_list_t *error, const env_vars &vars )
|
void highlight_shell( const wcstring &buff, std::vector<int> &color, int pos, wcstring_list_t *error, const env_vars &vars )
|
||||||
{
|
{
|
||||||
ASSERT_IS_BACKGROUND_THREAD();
|
ASSERT_IS_BACKGROUND_THREAD();
|
||||||
|
|||||||
42
history.cpp
42
history.cpp
@@ -393,6 +393,23 @@ void history_t::add(const wcstring &str, const path_list_t &valid_paths)
|
|||||||
this->add(history_item_t(str, time(NULL), valid_paths));
|
this->add(history_item_t(str, time(NULL), valid_paths));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void history_t::remove(const wcstring &str)
|
||||||
|
{
|
||||||
|
history_item_t item_to_delete(str);
|
||||||
|
deleted_items.push_back(item_to_delete);
|
||||||
|
|
||||||
|
for (std::vector<history_item_t>::iterator iter = new_items.begin(); iter != new_items.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (iter->match_contents(item_to_delete))
|
||||||
|
{
|
||||||
|
new_items.erase(iter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
void history_t::get_string_representation(wcstring &result, const wcstring &separator)
|
void history_t::get_string_representation(wcstring &result, const wcstring &separator)
|
||||||
{
|
{
|
||||||
scoped_lock locker(lock);
|
scoped_lock locker(lock);
|
||||||
@@ -400,10 +417,10 @@ void history_t::get_string_representation(wcstring &result, const wcstring &sepa
|
|||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
/* Append new items */
|
/* Append new items */
|
||||||
for (size_t i=0; i < new_items.size(); i++) {
|
for (std::vector<history_item_t>::const_reverse_iterator iter=new_items.rbegin(); iter < new_items.rend(); ++iter) {
|
||||||
if (! first)
|
if (! first)
|
||||||
result.append(separator);
|
result.append(separator);
|
||||||
result.append(new_items.at(i).str());
|
result.append(iter->str());
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,7 +812,7 @@ void history_t::save_internal()
|
|||||||
ASSERT_IS_LOCKED(lock);
|
ASSERT_IS_LOCKED(lock);
|
||||||
|
|
||||||
/* Nothing to do if there's no new items */
|
/* Nothing to do if there's no new items */
|
||||||
if (new_items.empty())
|
if (new_items.empty() && deleted_items.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Compact our new items so we don't have duplicates */
|
/* Compact our new items so we don't have duplicates */
|
||||||
@@ -825,9 +842,11 @@ void history_t::save_internal()
|
|||||||
|
|
||||||
/* Try decoding an old item */
|
/* Try decoding an old item */
|
||||||
const history_item_t old_item = history_t::decode_item(local_mmap_start + offset, local_mmap_size - offset);
|
const history_item_t old_item = history_t::decode_item(local_mmap_start + offset, local_mmap_size - offset);
|
||||||
if (old_item.empty())
|
if (old_item.empty() || is_deleted(old_item))
|
||||||
|
{
|
||||||
|
// debug(0, L"Item is deleted : %s\n", old_item.str().c_str());
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
/* The old item may actually be more recent than our new item, if it came from another session. Insert all new items at the given index with an earlier timestamp. */
|
/* The old item may actually be more recent than our new item, if it came from another session. Insert all new items at the given index with an earlier timestamp. */
|
||||||
for (; new_item_iter != new_items.end(); ++new_item_iter) {
|
for (; new_item_iter != new_items.end(); ++new_item_iter) {
|
||||||
if (new_item_iter->timestamp() < old_item.timestamp()) {
|
if (new_item_iter->timestamp() < old_item.timestamp()) {
|
||||||
@@ -846,7 +865,8 @@ void history_t::save_internal()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Insert any remaining new items */
|
/* Insert any remaining new items */
|
||||||
for (; new_item_iter != new_items.end(); ++new_item_iter) {
|
for (; new_item_iter != new_items.end(); ++new_item_iter)
|
||||||
|
{
|
||||||
lru.add_item(*new_item_iter);
|
lru.add_item(*new_item_iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -903,6 +923,7 @@ void history_t::save(void) {
|
|||||||
void history_t::clear(void) {
|
void history_t::clear(void) {
|
||||||
scoped_lock locker(lock);
|
scoped_lock locker(lock);
|
||||||
new_items.clear();
|
new_items.clear();
|
||||||
|
deleted_items.clear();
|
||||||
unsaved_item_count = 0;
|
unsaved_item_count = 0;
|
||||||
old_item_offsets.clear();
|
old_item_offsets.clear();
|
||||||
wcstring filename = history_filename(name, L"");
|
wcstring filename = history_filename(name, L"");
|
||||||
@@ -1020,3 +1041,12 @@ void history_t::add_with_file_detection(const wcstring &str)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool history_t::is_deleted(const history_item_t &item) const
|
||||||
|
{
|
||||||
|
for (std::vector<history_item_t>::const_iterator iter = deleted_items.begin(); iter != deleted_items.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (iter->match_contents(item)) { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
14
history.h
14
history.h
@@ -64,6 +64,10 @@ class history_item_t {
|
|||||||
creation_timestamp == other.creation_timestamp &&
|
creation_timestamp == other.creation_timestamp &&
|
||||||
required_paths == other.required_paths;
|
required_paths == other.required_paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool match_contents(const history_item_t &other) const {
|
||||||
|
return contents == other.contents;
|
||||||
|
}
|
||||||
|
|
||||||
/* Functions for testing only */
|
/* Functions for testing only */
|
||||||
|
|
||||||
@@ -96,7 +100,10 @@ class history_t {
|
|||||||
|
|
||||||
/** New items. */
|
/** New items. */
|
||||||
std::vector<history_item_t> new_items;
|
std::vector<history_item_t> new_items;
|
||||||
|
|
||||||
|
/** Deleted items. */
|
||||||
|
std::vector<history_item_t> deleted_items;
|
||||||
|
|
||||||
/** How many items we've added without saving */
|
/** How many items we've added without saving */
|
||||||
size_t unsaved_item_count;
|
size_t unsaved_item_count;
|
||||||
|
|
||||||
@@ -137,6 +144,9 @@ class history_t {
|
|||||||
|
|
||||||
/** Add a new history item to the end */
|
/** Add a new history item to the end */
|
||||||
void add(const wcstring &str, const path_list_t &valid_paths = path_list_t());
|
void add(const wcstring &str, const path_list_t &valid_paths = path_list_t());
|
||||||
|
|
||||||
|
/** Remove a history item */
|
||||||
|
void remove(const wcstring &str);
|
||||||
|
|
||||||
/** Add a new history item to the end */
|
/** Add a new history item to the end */
|
||||||
void add_with_file_detection(const wcstring &str);
|
void add_with_file_detection(const wcstring &str);
|
||||||
@@ -152,6 +162,8 @@ class history_t {
|
|||||||
|
|
||||||
/** Return the specified history at the specified index. 0 is the index of the current commandline. (So the most recent item is at index 1.) */
|
/** Return the specified history at the specified index. 0 is the index of the current commandline. (So the most recent item is at index 1.) */
|
||||||
history_item_t item_at_index(size_t idx);
|
history_item_t item_at_index(size_t idx);
|
||||||
|
|
||||||
|
bool is_deleted(const history_item_t &item) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class history_search_t {
|
class history_search_t {
|
||||||
|
|||||||
24
input.cpp
24
input.cpp
@@ -316,9 +316,16 @@ void update_fish_term256(void)
|
|||||||
env_var_t term = env_get_string(L"TERM");
|
env_var_t term = env_get_string(L"TERM");
|
||||||
if (term.missing()) {
|
if (term.missing()) {
|
||||||
support_term256 = false;
|
support_term256 = false;
|
||||||
|
} else if (term.find(L"256color") != wcstring::npos) {
|
||||||
|
/* Explicitly supported */
|
||||||
|
support_term256 = true;
|
||||||
|
} else if (term.find(L"xterm") != wcstring::npos) {
|
||||||
|
// assume that all xterms are 256, except for OS X SnowLeopard
|
||||||
|
env_var_t prog = env_get_string(L"TERM_PROGRAM");
|
||||||
|
support_term256 = (prog != L"Apple_Terminal");
|
||||||
} else {
|
} else {
|
||||||
// assume that all xterms are 256
|
// Don't know, default to false
|
||||||
support_term256 = (term.find(L"256color") != wcstring::npos || term.find(L"xterm") != wcstring::npos);
|
support_term256 = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output_set_supports_term256(support_term256);
|
output_set_supports_term256(support_term256);
|
||||||
@@ -434,8 +441,8 @@ static wint_t input_exec_binding( const input_mapping_t &m, const wcstring &seq
|
|||||||
*/
|
*/
|
||||||
static wint_t input_try_mapping( const input_mapping_t &m)
|
static wint_t input_try_mapping( const input_mapping_t &m)
|
||||||
{
|
{
|
||||||
int j, k;
|
|
||||||
wint_t c=0;
|
wint_t c=0;
|
||||||
|
int j;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check if the actual function code of this mapping is on the stack
|
Check if the actual function code of this mapping is on the stack
|
||||||
@@ -448,15 +455,22 @@ static wint_t input_try_mapping( const input_mapping_t &m)
|
|||||||
input_unreadch( c );
|
input_unreadch( c );
|
||||||
|
|
||||||
const wchar_t *str = m.seq.c_str();
|
const wchar_t *str = m.seq.c_str();
|
||||||
for( j=0; str[j] != L'\0' && str[j] == (c=input_common_readch( j>0 )); j++ )
|
for (j=0; str[j] != L'\0'; j++)
|
||||||
;
|
{
|
||||||
|
bool timed = (j > 0);
|
||||||
|
c = input_common_readch(timed);
|
||||||
|
if (str[j] != c)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if( str[j] == L'\0' )
|
if( str[j] == L'\0' )
|
||||||
{
|
{
|
||||||
|
/* We matched the entire sequence */
|
||||||
return input_exec_binding( m, m.seq );
|
return input_exec_binding( m, m.seq );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
int k;
|
||||||
/*
|
/*
|
||||||
Return the read characters
|
Return the read characters
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
|
||||||
import string, sys, os.path
|
import string, sys, os.path
|
||||||
@@ -47,16 +47,20 @@ class cfunc:
|
|||||||
|
|
||||||
def cdef(self):
|
def cdef(self):
|
||||||
result = ""
|
result = ""
|
||||||
result += "static const char * const %s = \n\t" % self.cfunc_name()
|
result += "static const char * const {0} = \n\t".format(self.cfunc_name())
|
||||||
result += '\n\t'.join(self.lines)
|
result += '\n\t'.join(self.lines)
|
||||||
result += ';\n'
|
result += ';\n'
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def cfunc_name(self):
|
def cfunc_name(self):
|
||||||
# Translate - and . to underscore
|
# Translate - and . to underscore
|
||||||
translator = string.maketrans('-.', '__')
|
try: #Python 2
|
||||||
munged_name = string.translate(self.name, translator)
|
translator = string.maketrans('-.', '__')
|
||||||
return "%s_%s" % (self.type, munged_name)
|
munged_name = string.translate(self.name, translator)
|
||||||
|
except AttributeError: #Python 3
|
||||||
|
translator = "".maketrans('-.', '__')
|
||||||
|
munged_name = self.name.translate(translator)
|
||||||
|
return "{0}_{1}".format(self.type, munged_name)
|
||||||
|
|
||||||
TYPES = ['function', 'completion']
|
TYPES = ['function', 'completion']
|
||||||
type_to_funcs = dict((t, []) for t in TYPES)
|
type_to_funcs = dict((t, []) for t in TYPES)
|
||||||
@@ -71,7 +75,7 @@ for file in sys.argv[1:]:
|
|||||||
# Try to figure out the file type (completion or function)
|
# Try to figure out the file type (completion or function)
|
||||||
matches = [dir in dirname for dir in TYPES]
|
matches = [dir in dirname for dir in TYPES]
|
||||||
if matches.count(True) is not 1:
|
if matches.count(True) is not 1:
|
||||||
print "Cannot determine the type of the file at path %s" % file
|
print("Cannot determine the type of the file at path {0}".format(file))
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
type = TYPES[matches.index(True)]
|
type = TYPES[matches.index(True)]
|
||||||
|
|
||||||
@@ -81,7 +85,7 @@ for file in sys.argv[1:]:
|
|||||||
type_to_funcs[type].append(newfunc)
|
type_to_funcs[type].append(newfunc)
|
||||||
|
|
||||||
# Sort our functions by name
|
# Sort our functions by name
|
||||||
for funcs in type_to_funcs.itervalues():
|
for funcs in type_to_funcs.values():
|
||||||
funcs.sort(key=cfunc.cfunc_name)
|
funcs.sort(key=cfunc.cfunc_name)
|
||||||
|
|
||||||
# Output our header
|
# Output our header
|
||||||
@@ -97,7 +101,7 @@ fd.write('\n')
|
|||||||
for type in TYPES:
|
for type in TYPES:
|
||||||
funcs = type_to_funcs[type]
|
funcs = type_to_funcs[type]
|
||||||
fd.write('\n')
|
fd.write('\n')
|
||||||
fd.write('extern const struct builtin_script_t internal_%s_scripts[%d];' % (type, len(funcs)))
|
fd.write('extern const struct builtin_script_t internal_{0}_scripts[{1}];'.format(type, len(funcs)))
|
||||||
fd.write('\n')
|
fd.write('\n')
|
||||||
fd.close()
|
fd.close()
|
||||||
|
|
||||||
@@ -113,8 +117,8 @@ for type in TYPES:
|
|||||||
# Output the refs
|
# Output the refs
|
||||||
for type in TYPES:
|
for type in TYPES:
|
||||||
funcs = type_to_funcs[type]
|
funcs = type_to_funcs[type]
|
||||||
func_refs = ["{L%s, %s}" % (stringize(func.name), func.cfunc_name()) for func in funcs]
|
func_refs = ["{0}L{1}, {2}{3}".format("{", stringize(func.name), func.cfunc_name(), "}") for func in funcs]
|
||||||
fd.write('const struct builtin_script_t internal_%s_scripts[%d] =\n' % (type, len(funcs)))
|
fd.write('const struct builtin_script_t internal_{0}_scripts[{1}] =\n'.format(type, len(funcs)))
|
||||||
fd.write('{\n\t')
|
fd.write('{\n\t')
|
||||||
fd.write(',\n\t'.join(func_refs))
|
fd.write(',\n\t'.join(func_refs))
|
||||||
fd.write('\n};\n')
|
fd.write('\n};\n')
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import commands
|
try:
|
||||||
|
import commands
|
||||||
|
except ImportError:
|
||||||
|
import subprocess
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# Regexes for performing cleanup
|
# Regexes for performing cleanup
|
||||||
@@ -12,13 +15,13 @@ cl = { re.compile(r"-[ \t]*\n[ \t\r]+" ):"",
|
|||||||
re.compile(r"[ \n\t\r]$"):"" }
|
re.compile(r"[ \n\t\r]$"):"" }
|
||||||
|
|
||||||
def header(cmd):
|
def header(cmd):
|
||||||
print '''#
|
print('''#
|
||||||
# Command specific completions for the %s command.
|
# Command specific completions for the {0} command.
|
||||||
# These completions were generated from the commands
|
# These completions were generated from the commands
|
||||||
# man page by the make_completions.py script, but may
|
# man page by the make_completions.py script, but may
|
||||||
# have been hand edited since.
|
# have been hand edited since.
|
||||||
#
|
#
|
||||||
''' % (cmd)
|
'''.format(cmd))
|
||||||
|
|
||||||
def up_first(s):
|
def up_first(s):
|
||||||
return s[0].upper() + s[1:]
|
return s[0].upper() + s[1:]
|
||||||
@@ -57,13 +60,16 @@ def print_completion( cmd, switch_arr, arg, desc ):
|
|||||||
|
|
||||||
res += " --description '%s'" % (up_first(escape_quotes(clean(desc))))
|
res += " --description '%s'" % (up_first(escape_quotes(clean(desc))))
|
||||||
|
|
||||||
print res
|
print(res)
|
||||||
|
|
||||||
cmd = sys.argv[1]
|
cmd = sys.argv[1]
|
||||||
|
|
||||||
header(cmd)
|
header(cmd)
|
||||||
|
|
||||||
man = commands.getoutput( "man %s | col -b" % cmd )
|
try:
|
||||||
|
man = commands.getoutput( "man %s | col -b".format(cmd))
|
||||||
|
except NameError:
|
||||||
|
man = subprocess.getoutput( "man %s | col -b".format(cmd))
|
||||||
|
|
||||||
remainder = man
|
remainder = man
|
||||||
|
|
||||||
@@ -152,7 +158,7 @@ for c in man:
|
|||||||
desc += c
|
desc += c
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print "Unknown mode", mode
|
print("Unknown mode {0}".format(mode))
|
||||||
|
|
||||||
pc = c
|
pc = c
|
||||||
|
|
||||||
|
|||||||
62
parser.cpp
62
parser.cpp
@@ -371,14 +371,37 @@ parser_t::parser_t(enum parser_type_t type, bool errors) :
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* A pointer to the principal parser (which is a static local) */
|
||||||
|
static parser_t *s_principal_parser = NULL;
|
||||||
|
|
||||||
parser_t &parser_t::principal_parser(void)
|
parser_t &parser_t::principal_parser(void)
|
||||||
{
|
{
|
||||||
ASSERT_IS_NOT_FORKED_CHILD();
|
ASSERT_IS_NOT_FORKED_CHILD();
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
static parser_t parser(PARSER_TYPE_GENERAL, true);
|
static parser_t parser(PARSER_TYPE_GENERAL, true);
|
||||||
|
if (! s_principal_parser)
|
||||||
|
{
|
||||||
|
s_principal_parser = &parser;
|
||||||
|
}
|
||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void parser_t::skip_all_blocks(void)
|
||||||
|
{
|
||||||
|
/* Tell all blocks to skip */
|
||||||
|
if (s_principal_parser)
|
||||||
|
{
|
||||||
|
//write(2, "Cancelling blocks\n", strlen("Cancelling blocks\n"));
|
||||||
|
block_t *c = s_principal_parser->current_block;
|
||||||
|
while( c )
|
||||||
|
{
|
||||||
|
c->skip = true;
|
||||||
|
//fprintf(stderr, " Cancelled %p\n", c);
|
||||||
|
c = c->outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return the current number of block nestings
|
Return the current number of block nestings
|
||||||
*/
|
*/
|
||||||
@@ -1956,10 +1979,28 @@ int parser_t::parse_job( process_t *p,
|
|||||||
p->actual_cmd = path_get_path( args.at(0).completion.c_str() );
|
p->actual_cmd = path_get_path( args.at(0).completion.c_str() );
|
||||||
err = errno;
|
err = errno;
|
||||||
|
|
||||||
/*
|
bool use_implicit_cd = false;
|
||||||
Check if the specified command exists
|
if (p->actual_cmd == NULL)
|
||||||
*/
|
{
|
||||||
if( p->actual_cmd == NULL )
|
/* If the specified command does not exist, try using an implicit cd. */
|
||||||
|
wcstring implicit_cd_path;
|
||||||
|
use_implicit_cd = path_can_be_implicit_cd(args.at(0).completion, &implicit_cd_path);
|
||||||
|
if (use_implicit_cd)
|
||||||
|
{
|
||||||
|
args.clear();
|
||||||
|
args.push_back(completion_t(L"cd"));
|
||||||
|
args.push_back(completion_t(implicit_cd_path));
|
||||||
|
|
||||||
|
/* If we have defined a wrapper around cd, use it, otherwise use the cd builtin */
|
||||||
|
if (use_function && function_exists(L"cd"))
|
||||||
|
p->type = INTERNAL_FUNCTION;
|
||||||
|
else
|
||||||
|
p->type = INTERNAL_BUILTIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the specified command exists */
|
||||||
|
if( p->actual_cmd == NULL && ! use_implicit_cd )
|
||||||
{
|
{
|
||||||
|
|
||||||
int tmp;
|
int tmp;
|
||||||
@@ -2253,7 +2294,7 @@ void parser_t::eval_job( tokenizer *tok )
|
|||||||
|
|
||||||
|
|
||||||
profile_item_t *profile_item = NULL;
|
profile_item_t *profile_item = NULL;
|
||||||
int skip = 0;
|
bool skip = false;
|
||||||
int job_begin_pos, prev_tokenizer_pos;
|
int job_begin_pos, prev_tokenizer_pos;
|
||||||
const bool do_profile = profile;
|
const bool do_profile = profile;
|
||||||
|
|
||||||
@@ -2294,7 +2335,6 @@ void parser_t::eval_job( tokenizer *tok )
|
|||||||
}
|
}
|
||||||
|
|
||||||
j->first_process = new process_t();
|
j->first_process = new process_t();
|
||||||
|
|
||||||
job_begin_pos = tok_get_pos( tok );
|
job_begin_pos = tok_get_pos( tok );
|
||||||
|
|
||||||
if( parse_job( j->first_process, j, tok ) &&
|
if( parse_job( j->first_process, j, tok ) &&
|
||||||
@@ -2319,9 +2359,9 @@ void parser_t::eval_job( tokenizer *tok )
|
|||||||
profile_item->skipped=current_block->skip;
|
profile_item->skipped=current_block->skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
skip |= current_block->skip;
|
skip = skip || current_block->skip;
|
||||||
skip |= job_get_flag( j, JOB_WILDCARD_ERROR );
|
skip = skip || job_get_flag( j, JOB_WILDCARD_ERROR );
|
||||||
skip |= job_get_flag( j, JOB_SKIP );
|
skip = skip || job_get_flag( j, JOB_SKIP );
|
||||||
|
|
||||||
if(!skip )
|
if(!skip )
|
||||||
{
|
{
|
||||||
@@ -2357,7 +2397,9 @@ void parser_t::eval_job( tokenizer *tok )
|
|||||||
{
|
{
|
||||||
case WHILE_TEST_FIRST:
|
case WHILE_TEST_FIRST:
|
||||||
{
|
{
|
||||||
current_block->skip = proc_get_last_status()!= 0;
|
// PCA I added the 'current_block->skip ||' part because we couldn't reliably
|
||||||
|
// control-C out of loops like this: while test 1 -eq 1; end
|
||||||
|
current_block->skip = current_block->skip || proc_get_last_status()!= 0;
|
||||||
current_block->state1<int>()=WHILE_TESTED;
|
current_block->state1<int>()=WHILE_TESTED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
5
parser.h
5
parser.h
@@ -332,6 +332,11 @@ class parser_t {
|
|||||||
/** Get the "principal" parser, whatever that is */
|
/** Get the "principal" parser, whatever that is */
|
||||||
static parser_t &principal_parser();
|
static parser_t &principal_parser();
|
||||||
|
|
||||||
|
/** Indicates that execution of all blocks in the principal parser should stop.
|
||||||
|
This is called from signal handlers!
|
||||||
|
*/
|
||||||
|
static void skip_all_blocks();
|
||||||
|
|
||||||
/** Create a parser of the given type */
|
/** Create a parser of the given type */
|
||||||
parser_t(enum parser_type_t type, bool show_errors);
|
parser_t(enum parser_type_t type, bool show_errors);
|
||||||
|
|
||||||
|
|||||||
31
path.cpp
31
path.cpp
@@ -431,13 +431,38 @@ wchar_t *path_allocate_cdpath( const wcstring &dir, const wchar_t *wd )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd) {
|
bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd)
|
||||||
|
{
|
||||||
wchar_t *tmp = path_allocate_cdpath(in, wd);
|
wchar_t *tmp = path_allocate_cdpath(in, wd);
|
||||||
bool result = (tmp != NULL);
|
bool result = (tmp != NULL);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wchar_t *wd)
|
||||||
|
{
|
||||||
|
wcstring exp_path = path;
|
||||||
|
expand_tilde(exp_path);
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
if (string_prefixes_string(L"/", exp_path) ||
|
||||||
|
string_prefixes_string(L"./", exp_path) ||
|
||||||
|
string_prefixes_string(L"../", exp_path) ||
|
||||||
|
exp_path == L"..")
|
||||||
|
{
|
||||||
|
/* These paths can be implicit cd. Note that a single period cannot (that's used for sourcing files anyways) */
|
||||||
|
wchar_t *cd_path = path_allocate_cdpath(exp_path, wd);
|
||||||
|
if (cd_path)
|
||||||
|
{
|
||||||
|
/* It worked. Return the path if desired */
|
||||||
|
if (out_path)
|
||||||
|
out_path->assign(cd_path);
|
||||||
|
free(cd_path);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool path_get_config(wcstring &path)
|
bool path_get_config(wcstring &path)
|
||||||
{
|
{
|
||||||
@@ -500,8 +525,8 @@ void path_make_canonical( wcstring &path )
|
|||||||
replace_all(path, L"//", L"/");
|
replace_all(path, L"//", L"/");
|
||||||
} while (path.size() != size);
|
} while (path.size() != size);
|
||||||
|
|
||||||
/* Remove trailing slashes */
|
/* Remove trailing slashes, except don't remove the first one */
|
||||||
while (size--) {
|
while (size-- > 1) {
|
||||||
if (path.at(size) != L'/')
|
if (path.at(size) != L'/')
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
3
path.h
3
path.h
@@ -31,6 +31,9 @@ bool path_get_config(wcstring &path);
|
|||||||
*/
|
*/
|
||||||
wchar_t *path_get_path( const wchar_t *cmd );
|
wchar_t *path_get_path( const wchar_t *cmd );
|
||||||
|
|
||||||
|
/** Returns whether the path can be used for an implicit cd command; if so, also returns the path by reference (if desired). This requires it to start with one of the allowed prefixes (., .., ~) and resolve to a directory. */
|
||||||
|
bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path = NULL, const wchar_t *wd = NULL);
|
||||||
|
|
||||||
class env_vars;
|
class env_vars;
|
||||||
bool path_get_path_string(const wcstring &cmd, wcstring &output);
|
bool path_get_path_string(const wcstring &cmd, wcstring &output);
|
||||||
bool path_get_path_string(const wcstring &cmd, wcstring &output, const env_vars &vars);
|
bool path_get_path_string(const wcstring &cmd, wcstring &output, const env_vars &vars);
|
||||||
|
|||||||
17
proc.cpp
17
proc.cpp
@@ -399,11 +399,10 @@ static void mark_process_status( const job_t *j,
|
|||||||
*/
|
*/
|
||||||
static void handle_child_status( pid_t pid, int status )
|
static void handle_child_status( pid_t pid, int status )
|
||||||
{
|
{
|
||||||
int found_proc = 0;
|
bool found_proc = false;
|
||||||
const job_t *j=0;
|
const job_t *j=0;
|
||||||
process_t *p=0;
|
process_t *p=0;
|
||||||
// char mess[MESS_SIZE];
|
// char mess[MESS_SIZE];
|
||||||
found_proc = 0;
|
|
||||||
/*
|
/*
|
||||||
snprintf( mess,
|
snprintf( mess,
|
||||||
MESS_SIZE,
|
MESS_SIZE,
|
||||||
@@ -413,7 +412,7 @@ static void handle_child_status( pid_t pid, int status )
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
job_iterator_t jobs;
|
job_iterator_t jobs;
|
||||||
while ((j = jobs.next()))
|
while (! found_proc && (j = jobs.next()))
|
||||||
{
|
{
|
||||||
process_t *prev=0;
|
process_t *prev=0;
|
||||||
for( p=j->first_process; p; p=p->next )
|
for( p=j->first_process; p; p=p->next )
|
||||||
@@ -442,7 +441,7 @@ static void handle_child_status( pid_t pid, int status )
|
|||||||
kill(prev->pid,SIGPIPE);
|
kill(prev->pid,SIGPIPE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
found_proc = 1;
|
found_proc = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
prev = p;
|
prev = p;
|
||||||
@@ -466,15 +465,10 @@ static void handle_child_status( pid_t pid, int status )
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//PCA INSTANCED_PARSER what is this?
|
/* In an interactive session, tell the principal parser to skip all blocks we're executing so control-C returns control to the user. */
|
||||||
block_t *c = NULL;//parser.current_block;
|
|
||||||
if( p && found_proc )
|
if( p && found_proc )
|
||||||
{
|
{
|
||||||
while( c )
|
parser_t::skip_all_blocks();
|
||||||
{
|
|
||||||
c->skip=1;
|
|
||||||
c=c->outer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -499,6 +493,7 @@ static void handle_child_status( pid_t pid, int status )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This is called from a signal handler */
|
||||||
void job_handle_signal ( int signal, siginfo_t *info, void *con )
|
void job_handle_signal ( int signal, siginfo_t *info, void *con )
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
4
proc.h
4
proc.h
@@ -433,7 +433,9 @@ typedef std::list<job_t *> job_list_t;
|
|||||||
|
|
||||||
bool job_list_is_empty(void);
|
bool job_list_is_empty(void);
|
||||||
|
|
||||||
/** A class to aid iteration over jobs list */
|
/** A class to aid iteration over jobs list.
|
||||||
|
Note this is used from a signal handler, so it must be careful to not allocate memory.
|
||||||
|
*/
|
||||||
class job_iterator_t {
|
class job_iterator_t {
|
||||||
job_list_t * const job_list;
|
job_list_t * const job_list;
|
||||||
job_list_t::iterator current, end;
|
job_list_t::iterator current, end;
|
||||||
|
|||||||
18
reader.cpp
18
reader.cpp
@@ -493,19 +493,12 @@ static void reader_kill( size_t begin_idx, int length, int mode, int newv )
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This is called from a signal handler! */
|
||||||
void reader_handle_int( int sig )
|
void reader_handle_int( int sig )
|
||||||
{
|
{
|
||||||
//PCA INSTANCED_PARSER what is this?
|
|
||||||
block_t *c = NULL;//current_block;
|
|
||||||
|
|
||||||
if( !is_interactive_read )
|
if( !is_interactive_read )
|
||||||
{
|
{
|
||||||
while( c )
|
parser_t::skip_all_blocks();
|
||||||
{
|
|
||||||
c->type=FAKE;
|
|
||||||
c->skip=1;
|
|
||||||
c=c->outer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interrupted = 1;
|
interrupted = 1;
|
||||||
@@ -672,6 +665,13 @@ void reader_init()
|
|||||||
shell_modes.c_cc[VMIN]=1;
|
shell_modes.c_cc[VMIN]=1;
|
||||||
shell_modes.c_cc[VTIME]=0;
|
shell_modes.c_cc[VTIME]=0;
|
||||||
|
|
||||||
|
// PCA disable VDSUSP (typically control-Y), which is a funny job control
|
||||||
|
// function available only on OS X and BSD systems
|
||||||
|
// This lets us use control-Y for yank instead
|
||||||
|
#ifdef VDSUSP
|
||||||
|
shell_modes.c_cc[VDSUSP] = _POSIX_VDISABLE;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Repaint if necessary before each byte is read. This lets us react immediately to universal variable color changes. */
|
/* Repaint if necessary before each byte is read. This lets us react immediately to universal variable color changes. */
|
||||||
input_common_set_poll_callback(reader_repaint_if_needed);
|
input_common_set_poll_callback(reader_repaint_if_needed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,35 @@ li {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head><body>
|
</head><body>
|
||||||
|
<h2>Release Notes for <i>fishfish</i> Beta r2</h2>
|
||||||
|
|
||||||
|
<h3>Bug Fixes</h3>
|
||||||
|
<ul>
|
||||||
|
<li><b>Implicit cd</b> is back, for paths that start with one or two dots, a slash, or a tilde.</li>
|
||||||
|
<li><b>Overrides of default functions should be fixed</b>. The "internalized scripts" feature is disabled for now.</li>
|
||||||
|
<li><b>Disabled delayed suspend</b>. This is a strange job-control feature of BSD systems, including OS X. Disabling it frees up Control Y for other purposes; in particular, for yank, which now works on OS X.</li>
|
||||||
|
<li><b>fish_indent is fixed</b>. In particular, the <span class="mono">funced</span> and <span class="mono">funcsave</span> functions work again.
|
||||||
|
<li>A SIGTERM now ends the whole execution stack again (<a href="https://github.com/ridiculousfish/fishfish/issues/13">resolving this issue</a>).
|
||||||
|
<li>Bumped the __fish_config_interactive version number so the default fish_color_autosuggestion kicks in.
|
||||||
|
<li>fish_config better handles combined term256 and classic colors like "555 yellow".
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>New Features</h3>
|
||||||
|
<ul>
|
||||||
|
<li>A <b>history builtin</b>, and associated interactive function that enables deleting history items. Example usage:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Print all history items beginning with echo: <span class="mono">history --prefix echo</span></li>
|
||||||
|
<li>Print all history items containing foo: <span class="mono">history --contains foo</span></li>
|
||||||
|
<li>Interactively delete some items containing foo: <span class="mono">history --delete --contains foo</span></li>
|
||||||
|
</ul><br>
|
||||||
|
Credit to Siteshwar for implementation. Thanks Siteshwar!
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
<h2>Release Notes for <i>fishfish</i> Beta r1</h2>
|
<h2>Release Notes for <i>fishfish</i> Beta r1</h2>
|
||||||
|
|
||||||
<h3>Scripting</h3>
|
<h3>Scripting</h3>
|
||||||
|
|||||||
5
share/completions/history.fish
Normal file
5
share/completions/history.fish
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
complete -c history -r -l prefix --description "Match history items that start with the given prefix"
|
||||||
|
complete -c history -r -l contains --description "Match history items that contain the given string"
|
||||||
|
complete -c history -l search --description "Print matching history items, which is the default behavior"
|
||||||
|
complete -c history -l delete --description "Interactively delete matching history items"
|
||||||
|
complete -c history -l clear --description "Clear your entire history"
|
||||||
@@ -77,7 +77,7 @@ function __fish_config_interactive -d "Initializations that should be performed
|
|||||||
# If we are starting up for the first time, set various defaults
|
# If we are starting up for the first time, set various defaults
|
||||||
#
|
#
|
||||||
|
|
||||||
if not set -q __fish_init_1_23_0
|
if not set -q __fish_init_1_50_0
|
||||||
if not set -q fish_greeting
|
if not set -q fish_greeting
|
||||||
set -l line1 (printf (_ 'Welcome to fish, the friendly interactive shell') )
|
set -l line1 (printf (_ 'Welcome to fish, the friendly interactive shell') )
|
||||||
set -l line2 (printf (_ 'Type %shelp%s for instructions on how to use fish') (set_color green) (set_color normal))
|
set -l line2 (printf (_ 'Type %shelp%s for instructions on how to use fish') (set_color green) (set_color normal))
|
||||||
@@ -97,15 +97,15 @@ function __fish_config_interactive -d "Initializations that should be performed
|
|||||||
|
|
||||||
# Regular syntax highlighting colors
|
# Regular syntax highlighting colors
|
||||||
set_default fish_color_normal normal
|
set_default fish_color_normal normal
|
||||||
set_default fish_color_command 005fd7
|
set_default fish_color_command 005fd7 purple
|
||||||
set_default fish_color_param 00afff
|
set_default fish_color_param 00afff cyan
|
||||||
set_default fish_color_redirection normal
|
set_default fish_color_redirection normal
|
||||||
set_default fish_color_comment red
|
set_default fish_color_comment red
|
||||||
set_default fish_color_error red --bold
|
set_default fish_color_error red --bold
|
||||||
set_default fish_color_escape cyan
|
set_default fish_color_escape cyan
|
||||||
set_default fish_color_operator cyan
|
set_default fish_color_operator cyan
|
||||||
set_default fish_color_quote brown
|
set_default fish_color_quote brown
|
||||||
set_default fish_color_autosuggestion 555
|
set_default fish_color_autosuggestion 555 yellow
|
||||||
set_default fish_color_valid_path --underline
|
set_default fish_color_valid_path --underline
|
||||||
|
|
||||||
set_default fish_color_cwd green
|
set_default fish_color_cwd green
|
||||||
@@ -226,7 +226,7 @@ function __fish_config_interactive -d "Initializations that should be performed
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
# Ubuntu Feisty places this command in the regular path instead
|
# Ubuntu Feisty places this command in the regular path instead
|
||||||
if which -s command-not-found
|
if which command-not-found > /dev/null 2> /dev/null
|
||||||
function fish_command_not_found_handler --on-event fish_command_not_found
|
function fish_command_not_found_handler --on-event fish_command_not_found
|
||||||
command-not-found $argv
|
command-not-found $argv
|
||||||
end
|
end
|
||||||
|
|||||||
116
share/functions/history.fish
Normal file
116
share/functions/history.fish
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#
|
||||||
|
#Deletes an item from history
|
||||||
|
#
|
||||||
|
|
||||||
|
function history --description "Deletes an item from history"
|
||||||
|
|
||||||
|
set -l argc (count $argv)
|
||||||
|
set -l prefix_args ""
|
||||||
|
set -l contains_args ""
|
||||||
|
|
||||||
|
set -l cmd print
|
||||||
|
|
||||||
|
set -l search_mode none
|
||||||
|
|
||||||
|
if test $argc -gt 0
|
||||||
|
for i in (seq $argc)
|
||||||
|
switch $argv[$i]
|
||||||
|
case --delete
|
||||||
|
set cmd delete
|
||||||
|
case --prefix
|
||||||
|
set search_mode prefix
|
||||||
|
set prefix_args $argv[(math $i + 1)]
|
||||||
|
case --contains
|
||||||
|
set search_mode contains
|
||||||
|
set contains_args $argv[(math $i + 1)]
|
||||||
|
case --clear
|
||||||
|
set cmd clear
|
||||||
|
case --search
|
||||||
|
set cmd print
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
switch $cmd
|
||||||
|
case print
|
||||||
|
# Print matching items
|
||||||
|
# Note this may end up passing --search twice to the builtin,
|
||||||
|
# but that's harmless
|
||||||
|
builtin history --search $argv
|
||||||
|
|
||||||
|
case delete
|
||||||
|
# Interactively delete history
|
||||||
|
set -l found_items ""
|
||||||
|
switch $search_mode
|
||||||
|
case prefix
|
||||||
|
set found_items (builtin history --search --prefix $prefix_args)
|
||||||
|
case contains
|
||||||
|
set found_items (builtin history --search --contains $contains_args)
|
||||||
|
case none
|
||||||
|
builtin history $argv
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
set found_items_count (count $found_items)
|
||||||
|
if test $found_items_count -gt 0
|
||||||
|
echo "[0] cancel"
|
||||||
|
echo "[1] all"
|
||||||
|
echo
|
||||||
|
|
||||||
|
for i in (seq $found_items_count)
|
||||||
|
printf "[%s] %s \n" (math $i + 1) $found_items[$i]
|
||||||
|
end
|
||||||
|
|
||||||
|
read --local --prompt "echo 'Delete which entries? > '" choice
|
||||||
|
set choice (echo $choice | tr " " "\n")
|
||||||
|
|
||||||
|
for i in $choice
|
||||||
|
|
||||||
|
# Skip empty input, for example, if the user just hits return
|
||||||
|
if test -z $i
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
#Following two validations could be embedded with "and" but I find the syntax kind of weird.
|
||||||
|
if not echo $i | grep -E -q "^[0-9]+\$"
|
||||||
|
printf "Invalid input: %s\n" $i
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
if test $i -gt (math $found_items_count + 1)
|
||||||
|
printf "Invalid input : %s\n" $i
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
if test $i = "0"
|
||||||
|
printf "Cancel\n"
|
||||||
|
return
|
||||||
|
else
|
||||||
|
if test $i = "1"
|
||||||
|
for item in $found_items
|
||||||
|
builtin history --delete $item
|
||||||
|
end
|
||||||
|
printf "Deleted all!\n"
|
||||||
|
return
|
||||||
|
else
|
||||||
|
builtin history --delete $found_items[(math $i - 1)]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
case clear
|
||||||
|
# Erase the entire history
|
||||||
|
if test $clear = 1
|
||||||
|
echo "Are you sure you want to clear history ? (y/n)"
|
||||||
|
read ch
|
||||||
|
if test $ch = "y"
|
||||||
|
builtin history $argv
|
||||||
|
echo "History cleared!"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
builtin history $argv
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -37,6 +37,16 @@ def parse_one_color(comp):
|
|||||||
# Unknown
|
# Unknown
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
def better_color(c1, c2):
|
||||||
|
""" Indicate which color is "better", i.e. prefer term256 colors """
|
||||||
|
if not c2: return c1
|
||||||
|
if not c1: return c2
|
||||||
|
if c1 == 'normal': return c2
|
||||||
|
if c2 == 'normal': return c1
|
||||||
|
if c2 in named_colors: return c1
|
||||||
|
if c1 in named_colors: return c2
|
||||||
|
return c1
|
||||||
|
|
||||||
|
|
||||||
def parse_color(color_str):
|
def parse_color(color_str):
|
||||||
""" A basic function to parse a color string, for example, 'red' '--bold' """
|
""" A basic function to parse a color string, for example, 'red' '--bold' """
|
||||||
@@ -53,11 +63,10 @@ def parse_color(color_str):
|
|||||||
underline = True
|
underline = True
|
||||||
elif comp.startswith('--background='):
|
elif comp.startswith('--background='):
|
||||||
# Background color
|
# Background color
|
||||||
background_color = parse_one_color(comp[len('--background='):])
|
background_color = better_color(background_color, parse_one_color(comp[len('--background='):]))
|
||||||
else:
|
else:
|
||||||
# Regular color
|
# Regular color
|
||||||
maybe_color = parse_one_color(comp)
|
color = better_color(color, parse_one_color(comp))
|
||||||
if maybe_color: color = maybe_color
|
|
||||||
|
|
||||||
return [color, background_color, bold, underline]
|
return [color, background_color, bold, underline]
|
||||||
|
|
||||||
|
|||||||
17
signal.cpp
17
signal.cpp
@@ -427,10 +427,9 @@ const wchar_t *signal_get_desc( int sig )
|
|||||||
*/
|
*/
|
||||||
static void default_handler(int signal, siginfo_t *info, void *context)
|
static void default_handler(int signal, siginfo_t *info, void *context)
|
||||||
{
|
{
|
||||||
event_t e = event_t::signal_event(signal);
|
if (event_is_signal_observed(signal))
|
||||||
if( event_get( &e, 0 ) )
|
{
|
||||||
{
|
event_fire_signal(signal);
|
||||||
event_fire( &e );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,16 +448,14 @@ static void handle_winch( int sig, siginfo_t *info, void *context )
|
|||||||
*/
|
*/
|
||||||
static void handle_hup( int sig, siginfo_t *info, void *context )
|
static void handle_hup( int sig, siginfo_t *info, void *context )
|
||||||
{
|
{
|
||||||
event_t e = event_t::signal_event(SIGHUP);
|
if (event_is_signal_observed(SIGHUP))
|
||||||
if( event_get( &e, 0 ) )
|
|
||||||
{
|
{
|
||||||
default_handler( sig, 0, 0 );
|
default_handler(sig, 0, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reader_exit( 1, 1 );
|
reader_exit(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user