aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Mark Griffiths <mark@thebespokepixel.com>2014-08-01 05:16:02 +0100
committerGravatar Mark Griffiths <mark@thebespokepixel.com>2014-08-01 05:16:02 +0100
commit8ac0fdfea726ff5e4fa8793d5461c6e097e62574 (patch)
treea13557f07d83f0037eca4ae295139f2e9c51dda4
parent6dfd71670ada17b63dd776cb43d37c2fe07c4fee (diff)
parent35ba97cbdf52027e3d2f76f24ab4ac034685f098 (diff)
Merge branch 'master' into documentation-update
Conflicts: doc_src/history.txt doc_src/test.txt
-rw-r--r--.gitignore4
-rw-r--r--Makefile.in21
-rw-r--r--builtin.cpp12
-rw-r--r--builtin_complete.cpp2
-rw-r--r--doc_src/fish_lexicon_filter.in4
-rw-r--r--doc_src/history.txt32
-rw-r--r--doc_src/test.txt47
-rw-r--r--doc_src/user_doc.css.in68
-rw-r--r--env.cpp64
-rw-r--r--fish_tests.cpp66
-rw-r--r--history.cpp115
-rw-r--r--history.h9
-rw-r--r--parse_util.cpp26
-rw-r--r--parse_util.h3
-rw-r--r--reader.cpp69
-rw-r--r--reader.h4
-rw-r--r--screen.cpp24
-rw-r--r--share/completions/node.fish200
-rw-r--r--share/completions/npm.fish141
-rw-r--r--share/functions/fish_prompt.fish2
-rw-r--r--share/functions/fish_vi_prompt.fish2
-rw-r--r--share/functions/umask.fish2
-rw-r--r--share/tools/web_config/fishconfig.css34
-rw-r--r--share/tools/web_config/js/controllers.js17
-rw-r--r--share/tools/web_config/partials/colors.html2
-rw-r--r--share/tools/web_config/partials/prompt.html20
-rw-r--r--share/tools/web_config/sample_prompts/classic.fish2
-rw-r--r--share/tools/web_config/sample_prompts/classic_git.fish2
-rw-r--r--share/tools/web_config/sample_prompts/classic_status.fish2
-rw-r--r--share/tools/web_config/sample_prompts/debian_chroot.fish2
-rw-r--r--share/tools/web_config/sample_prompts/informative.fish4
-rw-r--r--share/tools/web_config/sample_prompts/nim.fish2
-rw-r--r--share/tools/web_config/sample_prompts/user_host_path.fish2
-rwxr-xr-xshare/tools/web_config/webconfig.py117
-rw-r--r--tests/history_sample_corrupt15
35 files changed, 816 insertions, 312 deletions
diff --git a/.gitignore b/.gitignore
index 492c5758..b33a905a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,7 +35,9 @@ tests/foo.txt
FISH-BUILD-VERSION-FILE
version
messages.pot
+
lexicon.txt
+doc_src/user_doc.css
doc_src/fish_lexicon_filter
debug-lexicon.log
@@ -44,3 +46,5 @@ Fish-Shell.sublime-project
.editorconfig
doc_src/.editorconfig
+
+
diff --git a/Makefile.in b/Makefile.in
index 7fd8e3bf..8ec1608b 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -270,7 +270,26 @@ user_doc: $(HDR_FILES_SRC) Doxyfile.user $(HTML_SRC) $(HELP_SRC) doc.h $(HDR_FIL
doc_src/user_doc.css: doc_src/user_doc.css.in doc_src/fish_lexicon_filter
-rm $@
cd doc_src; \
- cat user_doc.css.in >> user_doc.css
+ cat $@.in | awk '{if ($$0 ~ /@normal@/) { system("echo style normal | ./fish_lexicon_filter"); } \
+ else if ($$0 ~ /@autosuggestion@/) { system("echo style autosuggestion | ./fish_lexicon_filter"); } \
+ else{ print $$0;}}' >$@
+
+
+# @autosuggestion@
+# @command@
+# @comment@
+# @cwd@
+# @error@
+# @history_current@
+# @match@
+# @normal@
+# @operator@
+# @param@
+# @quote@
+# @redirection@
+# @search_match@
+# @user@
+# @valid_path@
#
# Source code documentation. Also includes user documentation.
diff --git a/builtin.cpp b/builtin.cpp
index a7b173bb..5ab3ea43 100644
--- a/builtin.cpp
+++ b/builtin.cpp
@@ -3512,6 +3512,7 @@ static int builtin_history(parser_t &parser, wchar_t **argv)
bool search_prefix = false;
bool save_history = false;
bool clear_history = false;
+ bool merge_history = false;
static const struct woption long_options[] =
{
@@ -3521,6 +3522,7 @@ static int builtin_history(parser_t &parser, wchar_t **argv)
{ L"contains", no_argument, 0, 'c' },
{ L"save", no_argument, 0, 'v' },
{ L"clear", no_argument, 0, 'l' },
+ { L"merge", no_argument, 0, 'm' },
{ L"help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 }
};
@@ -3555,6 +3557,9 @@ static int builtin_history(parser_t &parser, wchar_t **argv)
case 'l':
clear_history = true;
break;
+ case 'm':
+ merge_history = true;
+ break;
case 'h':
builtin_print_help(parser, argv[0], stdout_buffer);
return STATUS_BUILTIN_OK;
@@ -3578,12 +3583,17 @@ static int builtin_history(parser_t &parser, wchar_t **argv)
if (argc == 1)
{
wcstring full_history;
- history->get_string_representation(full_history, wcstring(L"\n"));
+ history->get_string_representation(&full_history, wcstring(L"\n"));
stdout_buffer.append(full_history);
stdout_buffer.push_back('\n');
return STATUS_BUILTIN_OK;
}
+ if (merge_history)
+ {
+ history->incorporate_external_changes();
+ }
+
if (search_history)
{
int res = STATUS_BUILTIN_ERROR;
diff --git a/builtin_complete.cpp b/builtin_complete.cpp
index d029e83e..3b7a13a2 100644
--- a/builtin_complete.cpp
+++ b/builtin_complete.cpp
@@ -447,7 +447,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
{
const wcstring condition_string = condition;
parse_error_list_t errors;
- if (parse_util_detect_errors(condition_string, &errors))
+ if (parse_util_detect_errors(condition_string, &errors, false /* do not accept incomplete */))
{
append_format(stderr_buffer,
L"%ls: Condition '%ls' contained a syntax error",
diff --git a/doc_src/fish_lexicon_filter.in b/doc_src/fish_lexicon_filter.in
index 60cde9be..82aafc8b 100644
--- a/doc_src/fish_lexicon_filter.in
+++ b/doc_src/fish_lexicon_filter.in
@@ -232,8 +232,8 @@ b protect
G
#.
# Uncomment the folowing two lines (ss) to log the buffer join.
-s/^.*$/JOIN: &/w debug-lexicon.log
-s/^JOIN: //
+# s/^.*$/JOIN: &/w debug-lexicon.log
+# s/^JOIN: //
#.
# Iterate over alternate lines, matching '<' to '\'
:join
diff --git a/doc_src/history.txt b/doc_src/history.txt
index 25c07315..322831ef 100644
--- a/doc_src/history.txt
+++ b/doc_src/history.txt
@@ -2,7 +2,7 @@
\subsection history-synopsis Synopsis
\fish{syn}
-history [--save|--clear]
+history [--save|--clear|--merge]
history [--search|--delete] [--prefix "prefix string"|--contains "search string"]
\endfish
@@ -11,27 +11,17 @@ history [--search|--delete] [--prefix "prefix string"|--contains "search string"
`history` is used to list, search and delete the history of commands used.
The following options are available:
-
-- `--save` saves all changes in the history file. The shell automatically
-saves the history file; this option is provided for internal use.
-- `--clear` clears the history file. A prompt is displayed before the history
-is erased.
-- `--search` returns history items in keeping with the `--prefix` or
-`--contains` options.
+- `--save` saves all changes in the history file. The shell automatically saves the history file; this option is provided for internal use.
+- `--clear` clears the history file. A prompt is displayed before the history is erased.
+- `--merge` immediately incorporates history changes from other sessions. Ordinarily fish ignores history changes from sessions started after the current one. This command applies those changes immediately.
+- `--search` returns history items in keeping with the `--prefix` or `--contains` options.
- `--delete` deletes history items.
-- `--prefix` searches or deletes items in the history that begin with the
-specified text string.
-- `--contains` searches or deletes items in the history that contain the
-specified text string.
-
-If `--search` is specified without `--contains` or `--prefix`,
-`--contains` will be assumed.
-
-If `--delete` is specified without `--contains` or `--prefix`,
-only a history item which exactly matches the parameter will be erased. No
-prompt will be given. If `--delete` is specified with either of these
-parameters, an interactive prompt will be displayed before any items are
-deleted.
+- `--prefix` searches or deletes items in the history that begin with the specified text string.
+- `--contains` searches or deletes items in the history that contain the specified text string.
+
+If `--search` is specified without `--contains` or `--prefix`, `--contains` will be assumed.
+
+If `--delete` is specified without `--contains` or `--prefix`, only a history item which exactly matches the parameter will be erased. No prompt will be given. If `--delete` is specified with either of these parameters, an interactive prompt will be displayed before any items are deleted.
\subsection history-examples Example
diff --git a/doc_src/test.txt b/doc_src/test.txt
index cfa974b3..b7e0a6a5 100644
--- a/doc_src/test.txt
+++ b/doc_src/test.txt
@@ -7,9 +7,7 @@ test [EXPRESSION]
\subsection test-description Description
-Tests the expression given and sets the exit status to 0 if true,
-and 1 if false. An expression is made up of one or more operators
-and their arguments.
+Tests the expression given and sets the exit status to 0 if true, and 1 if false. An expression is made up of one or more operators and their arguments.
The following operators are available to examine files and directories:
- `-b FILE` returns true if `FILE` is a block device.
@@ -18,11 +16,9 @@ The following operators are available to examine files and directories:
- `-e FILE` returns true if `FILE` exists.
- `-f FILE` returns true if `FILE` is a regular file.
- `-g FILE` returns true if `FILE` has the set-group-ID bit set.
-- `-G FILE` returns true if `FILE` exists and has the same group ID
-as the current user.
+- `-G FILE` returns true if `FILE` exists and has the same group ID as the current user.
- `-L FILE` returns true if `FILE` is a symbolic link.
-- `-O FILE` returns true if `FILE` exists and is owned by the current
-user.
+- `-O FILE` returns true if `FILE` exists and is owned by the current user.
- `-p FILE` returns true if `FILE` is a named pipe.
- `-r FILE` returns true if `FILE` is marked as readable.
- `-s FILE` returns true if the size of `FILE` is greater than zero.
@@ -33,10 +29,8 @@ user.
- `-x FILE` returns true if `FILE` is marked as executable.
The following operators are available to compare and examine text strings:
-- `STRING1 = STRING2` returns true if the strings `STRING1` and
-`STRING2` are identical.
-- `STRING1 != STRING2` returns true if the strings `STRING1` and
-`STRING2` are not identical.
+- `STRING1 = STRING2` returns true if the strings `STRING1` and `STRING2` are identical.
+- `STRING1 != STRING2` returns true if the strings `STRING1` and `STRING2` are not identical.
- `-n STRING` returns true if the length of `STRING` is non-zero.
- `-z STRING` returns true if the length of `STRING` is zero.
@@ -48,22 +42,18 @@ The following operators are available to compare and examine numbers:
- `NUM1 -lt NUM2` returns true if `NUM1` is less than `NUM2`.
- `NUM1 -le NUM2` returns true if `NUM1` is less than or equal to `NUM2`.
-Note that only integers are supported. For more complex mathematical
-operations, including fractions, the `env` program may be useful. Consult the
-documentation for your operating system.
+Note that only integers are supported. For more complex mathematical operations, including fractions, the `env` program may be useful. Consult the documentation for your operating system.
Expressions can be combined using the following operators:
- `COND1 -a COND2` returns true if both `COND1` and `COND2` are true.
- `COND1 -o COND2` returns true if either `COND1` or `COND2` are true.
Expressions can be inverted using the `!` operator:
-- `! EXPRESSION` returns true if `EXPRESSION` is false, and false if
-`EXPRESSION` is true.
+- `! EXPRESSION` returns true if `EXPRESSION` is false, and false if `EXPRESSION` is true.
Expressions can be grouped using parentheses.
- `( EXPRESSION )` returns the value of `EXPRESSION`.
-Note that parentheses will usually require escaping with `\\(` to avoid
-being interpreted as a command substitution.
+Note that parentheses will usually require escaping with `\\(` to avoid being interpreted as a command substitution.
\subsection test-example Examples
@@ -75,17 +65,15 @@ if test -d /tmp
end
\endfish
-If the variable `MANPATH` is defined and not empty, print the contents:
+If the variable `MANPATH` is defined and not empty, print the contents. (If `MANPATH` is not defined, then it will expand to zero arguments, unless quoted.)
\fish
-if test -n $MANPATH
+if test -n "$MANPATH"
echo $MANPATH
end
\endfish
-Parentheses and the `-o` and `-a` operators can be combined to produce
-more complicated expressions. In this example, success is printed if there is
-a `/foo` or `/bar` file as well as a `/baz` or `/bat` file.
+Parentheses and the `-o` and `-a` operators can be combined to produce more complicated expressions. In this example, success is printed if there is a `/foo` or `/bar` file as well as a `/baz` or `/bat` file.
\fish
if test \( -f /foo -o -f /bar \) -a \( -f /baz -o -f /bat \)
@@ -96,14 +84,7 @@ end.
\subsection test-standards Standards
`test` implements a subset of the
-<a href="http://www.unix.com/man-page/POSIX/1/test/">IEEE Std 1003.1-2008
-(POSIX.1) standard</a>. The following exceptions apply:
+<a href="http://www.unix.com/man-page/POSIX/1/test/">IEEE Std 1003.1-2008 (POSIX.1) standard</a>. The following exceptions apply:
- The `<` and `>` operators for comparing strings are not implemented.
-- Because this test is a shell builtin and not a standalone utility, using
- the -c flag on a special file descriptors like standard input and output
- may not return the same result when invoked from within a pipe as one
- would expect when invoking the `test` utility in another shell.
-
- In cases such as this, one can use `command` `test` to explicitly
- use the system's standalone `test` rather than this `builtin` `test`.
-
+- Because this test is a shell builtin and not a standalone utility, using the -c flag on a special file descriptors like standard input and output may not return the same result when invoked from within a pipe as one would expect when invoking the `test` utility in another shell.
+ In cases such as this, one can use `command` `test` to explicitly use the system's standalone `test` rather than this `builtin` `test`.
diff --git a/doc_src/user_doc.css.in b/doc_src/user_doc.css.in
index d2f00642..c43da628 100644
--- a/doc_src/user_doc.css.in
+++ b/doc_src/user_doc.css.in
@@ -131,7 +131,8 @@ h3 {
padding-bottom: 10px;
border-bottom: 1px solid #AAA;
}
-/*Special Formmating for Code and keys*/
+/* Special Formmating */
+/* Keyboard */
.key span {
display:none;
}
@@ -147,14 +148,12 @@ h3 {
font-weight: normal;
white-space: nowrap;
}
+/* Console display */
tt, code, pre, .fish {
font-family: "DejaVu Sans Mono", Menlo, Monaco, "Source Code Pro", "Ubuntu Mono", "Consolas", "Lucida Console", monospace, fixed;
font-weight: 500;
text-shadow: 0 0 0 rgba(0,0,0,1); /* Stronger anti-aliasing */
}
-tt {
- color: red; /*REMOVE THIS*/
-}
code, pre, .line {
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
@@ -166,50 +165,51 @@ h1 > code, h2 > code, h3 > code {
font-family: "DejaVu Sans Mono", Menlo, "Source Code Pro", "Consolas", "Lucida Console", Roboto, Verdana, sans-serif;
font-weight: 700;
}
+/*Default 'light' console*/
.fish {
margin: 1rem;
padding: 0.4rem 1rem;
line-height: 1.9rem;
+ color: #222;
background-color: #fafafa;
border: 1px solid #f0f0f0;
border-radius: 0.4rem;
}
-
-.comment { color: #777; }
-.command { color: #0A0; }
-.function { color: #0A0; }
-.binary { color: #060; }
-.argument { color: #906; }
-.variable { color: #339; }
-.redirect { color: #F00; }
-.operator { color: #990; }
-.file { color: #c97922; }
-.path { color: #c97922; }
-.string { color: #770; }
-.prompt { color: #03C; }
-.suggest { color: cyan ; }
-.error { color: red; font-weight: bold; }
-.cursor { border-bottom: 2px solid green; }
-
-
-/*.keyword, .keywordflow { color: #050; }*/
-/*.stringliteral, .charliteral { color: #226; }*/
-/*.preprocessor, .comment { color: #555; text-shadow: none; }*/
-
.cli-dark {
background-color: #111;
color: #ddd;
- padding: 0.4rem 2rem;
+ padding: 0.4rem 1rem;
border-radius: 0.4rem;
}
-/*
-.cli .command {
- color: #0E0;
- font-weight: bold;
+.normal { @normal@; }
+.comment { @comment@; }
+.command { @command@; }
+.function { @command@; }
+.binary { @command@; }
+.argument { @param@ }
+.variable { @param@ }
+.redirect { @redirection@ }
+.operator { @operator@ }
+.file { @valid_path@ }
+.path { @valid_path@ }
+.string { @quote@ }
+.suggest { @autosuggestion@ }
+.error { @error@ }
+.match { @match@ }
+.search_match { @search_match@ }
+.user { @user@ }
+.cwd { @cwd@ }
+.history { @history_current@ }
+.prompt { color: #222; }
+.cursor { border-bottom: 2px solid green; }
+.underline { text-decoration: underline; }
+/* Console variants */
+.cli-dark {
+ background-color: #111;
+ color: #ddd;
+ padding: 0.4rem 1rem;
+ border-radius: 0.4rem;
}
-.cli .function {
- color: #0C0;
-}*/
/*Menus*/
.menu { margin: 1.4rem 0; line-height: 2.2rem; }
.menu ul { list-style-type: none; }
diff --git a/env.cpp b/env.cpp
index ec0fea30..88d61c85 100644
--- a/env.cpp
+++ b/env.cpp
@@ -415,39 +415,6 @@ wcstring env_get_pwd_slash(void)
return pwd;
}
-/**
- Set up default values for various variables if not defined.
- */
-static void env_set_defaults()
-{
-
- if (env_get_string(L"USER").missing())
- {
- struct passwd *pw = getpwuid(getuid());
- if (pw->pw_name != NULL)
- {
- const wcstring wide_name = str2wcstring(pw->pw_name);
- env_set(L"USER", wide_name.c_str(), ENV_GLOBAL);
- }
- }
-
- if (env_get_string(L"HOME").missing())
- {
- const env_var_t unam = env_get_string(L"USER");
- char *unam_narrow = wcs2str(unam.c_str());
- struct passwd *pw = getpwnam(unam_narrow);
- if (pw->pw_dir != NULL)
- {
- const wcstring dir = str2wcstring(pw->pw_dir);
- env_set(L"HOME", dir.c_str(), ENV_GLOBAL);
- }
- free(unam_narrow);
- }
-
- env_set_pwd();
-
-}
-
// Some variables should not be arrays. This used to be handled by a startup script, but we'd like to get down to 0 forks for startup, so handle it here.
static bool variable_can_be_array(const wcstring &key)
{
@@ -546,11 +513,14 @@ void env_init(const struct config_paths_t *paths /* or NULL */)
/*
Set up the USER variable
*/
- const struct passwd *pw = getpwuid(getuid());
- if (pw && pw->pw_name)
+ if (env_get_string(L"USER").missing_or_empty())
{
- const wcstring uname = str2wcstring(pw->pw_name);
- env_set(L"USER", uname.c_str(), ENV_GLOBAL | ENV_EXPORT);
+ const struct passwd *pw = getpwuid(getuid());
+ if (pw && pw->pw_name)
+ {
+ const wcstring uname = str2wcstring(pw->pw_name);
+ env_set(L"USER", uname.c_str(), ENV_GLOBAL | ENV_EXPORT);
+ }
}
/*
@@ -580,8 +550,22 @@ void env_init(const struct config_paths_t *paths /* or NULL */)
}
env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT);
- /* Set correct defaults for e.g. USER and HOME variables */
- env_set_defaults();
+ /* Set up the HOME variable */
+ if (env_get_string(L"HOME").missing_or_empty())
+ {
+ const env_var_t unam = env_get_string(L"USER");
+ char *unam_narrow = wcs2str(unam.c_str());
+ struct passwd *pw = getpwnam(unam_narrow);
+ if (pw->pw_dir != NULL)
+ {
+ const wcstring dir = str2wcstring(pw->pw_dir);
+ env_set(L"HOME", dir.c_str(), ENV_GLOBAL);
+ }
+ free(unam_narrow);
+ }
+
+ /* Set PWD */
+ env_set_pwd();
/* Set g_log_forks */
env_var_t log_forks = env_get_string(L"fish_log_forks");
@@ -969,7 +953,7 @@ env_var_t env_get_string(const wcstring &key, env_mode_flags_t mode)
history = &history_t::history_with_name(L"fish");
}
if (history)
- history->get_string_representation(result, ARRAY_SEP_STR);
+ history->get_string_representation(&result, ARRAY_SEP_STR);
return result;
}
else if (key == L"COLUMNS")
diff --git a/fish_tests.cpp b/fish_tests.cpp
index b29f7124..08ab8477 100644
--- a/fish_tests.cpp
+++ b/fish_tests.cpp
@@ -1209,6 +1209,14 @@ static void test_escape_sequences(void)
if (escape_code_length(L"\x1b[2J") != 4) err(L"test_escape_sequences failed on line %d\n", __LINE__);
if (escape_code_length(L"\x1b[38;5;123mABC") != strlen("\x1b[38;5;123m")) err(L"test_escape_sequences failed on line %d\n", __LINE__);
if (escape_code_length(L"\x1b@") != 2) err(L"test_escape_sequences failed on line %d\n", __LINE__);
+
+ // iTerm2 escape sequences
+ if (escape_code_length(L"\x1b]50;CurrentDir=/tmp/foo\x07NOT_PART_OF_SEQUENCE") != 25) err(L"test_escape_sequences failed on line %d\n", __LINE__);
+ if (escape_code_length(L"\x1b]50;SetMark\x07NOT_PART_OF_SEQUENCE") != 13) err(L"test_escape_sequences failed on line %d\n", __LINE__);
+ if (escape_code_length(L"\x1b" L"]6;1;bg;red;brightness;255\x07NOT_PART_OF_SEQUENCE") != 28) err(L"test_escape_sequences failed on line %d\n", __LINE__);
+ if (escape_code_length(L"\x1b]Pg4040ff\x1b\\NOT_PART_OF_SEQUENCE") != 12) err(L"test_escape_sequences failed on line %d\n", __LINE__);
+ if (escape_code_length(L"\x1b]blahblahblah\x1b\\") != 16) err(L"test_escape_sequences failed on line %d\n", __LINE__);
+ if (escape_code_length(L"\x1b]blahblahblah\x07") != 15) err(L"test_escape_sequences failed on line %d\n", __LINE__);
}
class lru_node_test_t : public lru_node_t
@@ -2688,7 +2696,8 @@ void history_tests_t::test_history_merge(void)
const size_t count = 3;
const wcstring name = L"merge_test";
history_t *hists[count] = {new history_t(name), new history_t(name), new history_t(name)};
- wcstring texts[count] = {L"History 1", L"History 2", L"History 3"};
+ const wcstring texts[count] = {L"History 1", L"History 2", L"History 3"};
+ const wcstring alt_texts[count] = {L"History Alt 1", L"History Alt 2", L"History Alt 3"};
/* Make sure history is clear */
for (size_t i=0; i < count; i++)
@@ -2730,6 +2739,32 @@ void history_tests_t::test_history_merge(void)
do_test(history_contains(everything, texts[i]));
}
+ /* Tell all histories to merge. Now everybody should have everything. */
+ for (size_t i=0; i < count; i++)
+ {
+ hists[i]->incorporate_external_changes();
+ }
+ /* Add some more per-history items */
+ for (size_t i=0; i < count; i++)
+ {
+ hists[i]->add(alt_texts[i]);
+ }
+ /* Everybody should have old items, but only one history should have each new item */
+ for (size_t i = 0; i < count; i++)
+ {
+ for (size_t j=0; j < count; j++)
+ {
+ /* Old item */
+ do_test(history_contains(hists[i], texts[j]));
+
+ /* New item */
+ bool does_contain = history_contains(hists[i], alt_texts[j]);
+ bool should_contain = (i == j);
+ do_test(should_contain == does_contain);
+ }
+ }
+
+
/* Clean up */
for (size_t i=0; i < 3; i++)
{
@@ -2793,7 +2828,7 @@ static bool history_equals(history_t &hist, const wchar_t * const *strings)
void history_tests_t::test_history_formats(void)
{
const wchar_t *name;
-
+
// Test inferring and reading legacy and bash history formats
name = L"history_sample_fish_1_x";
say(L"Testing %ls", name);
@@ -2886,6 +2921,33 @@ void history_tests_t::test_history_formats(void)
test_history.clear();
fclose(f);
}
+
+ name = L"history_sample_corrupt1";
+ say(L"Testing %ls", name);
+ if (! install_sample_history(name))
+ {
+ err(L"Couldn't open file tests/%ls", name);
+ }
+ else
+ {
+ /* We simply invoke get_string_representation. If we don't die, the test is a success. */
+ history_t &test_history = history_t::history_with_name(name);
+ const wchar_t *expected[] =
+ {
+ L"no_newline_at_end_of_file",
+
+ L"corrupt_prefix",
+
+ L"this_command_is_ok",
+
+ NULL
+ };
+ if (! history_equals(test_history, expected))
+ {
+ err(L"test_history_formats failed for %ls\n", name);
+ }
+ test_history.clear();
+ }
}
void history_tests_t::test_history_speed(void)
diff --git a/history.cpp b/history.cpp
index fdbc2359..ff65c454 100644
--- a/history.cpp
+++ b/history.cpp
@@ -215,10 +215,10 @@ static std::map<wcstring, history_t *> histories;
static wcstring history_filename(const wcstring &name, const wcstring &suffix);
/** Replaces newlines with a literal backslash followed by an n, and replaces backslashes with two backslashes. */
-static void escape_yaml(std::string &str);
+static void escape_yaml(std::string *str);
/** Undoes escape_yaml */
-static void unescape_yaml(std::string &str);
+static void unescape_yaml(std::string *str);
/* We can merge two items if they are the same command. We use the more recent timestamp, more recent identifier, and the longer list of required paths. */
bool history_item_t::merge(const history_item_t &item)
@@ -271,7 +271,7 @@ bool history_item_t::matches_search(const wcstring &term, enum history_search_ty
static void append_yaml_to_buffer(const wcstring &wcmd, time_t timestamp, const path_list_t &required_paths, history_output_buffer_t *buffer)
{
std::string cmd = wcs2string(wcmd);
- escape_yaml(cmd);
+ escape_yaml(&cmd);
buffer->append("- cmd: ", cmd.c_str(), "\n");
char timestamp_str[96];
@@ -285,7 +285,7 @@ static void append_yaml_to_buffer(const wcstring &wcmd, time_t timestamp, const
for (path_list_t::const_iterator iter = required_paths.begin(); iter != required_paths.end(); ++iter)
{
std::string path = wcs2string(*iter);
- escape_yaml(path);
+ escape_yaml(&path);
buffer->append(" - ", path.c_str(), "\n");
}
}
@@ -366,7 +366,7 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length
size_t result = (size_t)(-1);
while (cursor < mmap_length)
{
- const char * const line_start = begin + cursor;
+ const char *line_start = begin + cursor;
/* Advance the cursor to the next line */
const char *newline = (const char *)memchr(line_start, '\n', mmap_length - cursor);
@@ -374,15 +374,14 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length
break;
/* Advance the cursor past this line. +1 is for the newline */
- size_t line_len = newline - line_start;
- cursor += line_len + 1;
+ cursor = newline - begin + 1;
/* Skip lines with a leading space, since these are in the interior of one of our items */
if (line_start[0] == ' ')
continue;
/* Skip very short lines to make one of the checks below easier */
- if (line_len < 3)
+ if (newline - line_start < 3)
continue;
/* Try to be a little YAML compatible. Skip lines with leading %, ---, or ... */
@@ -390,6 +389,23 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length
! memcmp(line_start, "---", 3) ||
! memcmp(line_start, "...", 3))
continue;
+
+
+ /* Hackish: fish 1.x rewriting a fish 2.0 history file can produce lines with lots of leading "- cmd: - cmd: - cmd:". Trim all but one leading "- cmd:". */
+ const char *double_cmd = "- cmd: - cmd: ";
+ const size_t double_cmd_len = strlen(double_cmd);
+ while (newline - line_start > double_cmd_len && ! memcmp(line_start, double_cmd, double_cmd_len))
+ {
+ /* Skip over just one of the - cmd. In the end there will be just one left. */
+ line_start += strlen("- cmd: ");
+ }
+
+ /* Hackish: fish 1.x rewriting a fish 2.0 history file can produce commands like "when: 123456". Ignore those. */
+ const char *cmd_when = "- cmd: when:";
+ const size_t cmd_when_len = strlen(cmd_when);
+ if (newline - line_start >= cmd_when_len && ! memcmp(line_start, cmd_when, cmd_when_len))
+ continue;
+
/* At this point, we know line_start is at the beginning of an item. But maybe we want to skip this item because of timestamps. A 0 cutoff means we don't care; if we do care, then try parsing out a timestamp. */
if (cutoff_timestamp != 0)
@@ -400,15 +416,9 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length
/* Walk over lines that we think are interior. These lines are not null terminated, but are guaranteed to contain a newline. */
bool has_timestamp = false;
- time_t timestamp;
+ time_t timestamp = 0;
const char *interior_line;
- /*
- * Ensure the loop is processed at least once. Otherwise,
- * timestamp is unitialized.
- */
- bool processed_once = false;
-
for (interior_line = next_line(line_start, end - line_start);
interior_line != NULL && ! has_timestamp;
interior_line = next_line(interior_line, end - interior_line))
@@ -423,12 +433,8 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length
/* Try parsing a timestamp from this line. If we succeed, the loop will break. */
has_timestamp = parse_timestamp(interior_line, &timestamp);
-
- processed_once = true;
}
- assert(processed_once);
-
/* Skip this item if the timestamp is past our cutoff. */
if (has_timestamp && timestamp > cutoff_timestamp)
{
@@ -536,7 +542,7 @@ history_t::history_t(const wcstring &pname) :
mmap_start(NULL),
mmap_length(0),
mmap_file_id(kInvalidFileID),
- birth_timestamp(time(NULL)),
+ boundary_timestamp(time(NULL)),
countdown_to_vacuum(-1),
loaded_old(false),
chaos_mode(false)
@@ -606,10 +612,12 @@ void history_t::save_internal_unless_disabled()
void history_t::add(const wcstring &str, history_identifier_t ident)
{
time_t when = time(NULL);
- /* Big hack: do not allow timestamps equal to our birthdate. This is because we include items whose timestamps are equal to our birthdate when reading old history, so we can catch "just closed" items. But this means that we may interpret our own items, that we just wrote, as old items, if we wrote them in the same second as our birthdate.
+ /* Big hack: do not allow timestamps equal to our boundary date. This is because we include items whose timestamps are equal to our boundary when reading old history, so we can catch "just closed" items. But this means that we may interpret our own items, that we just wrote, as old items, if we wrote them in the same second as our birthdate.
*/
- if (when == this->birth_timestamp)
+ if (when == this->boundary_timestamp)
+ {
when++;
+ }
this->add(history_item_t(str, when, ident));
}
@@ -659,7 +667,7 @@ void history_t::set_valid_file_paths(const wcstring_list_t &valid_file_paths, hi
}
}
-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);
@@ -675,8 +683,8 @@ void history_t::get_string_representation(wcstring &result, const wcstring &sepa
continue;
if (! first)
- result.append(separator);
- result.append(iter->str());
+ result->append(separator);
+ result->append(iter->str());
first = false;
}
@@ -692,8 +700,8 @@ void history_t::get_string_representation(wcstring &result, const wcstring &sepa
continue;
if (! first)
- result.append(separator);
- result.append(item.str());
+ result->append(separator);
+ result->append(item.str());
first = false;
}
}
@@ -761,18 +769,18 @@ static size_t trim_leading_spaces(std::string &str)
return i;
}
-static bool extract_prefix_and_unescape_yaml(std::string &key, std::string &value, const std::string &line)
+static bool extract_prefix_and_unescape_yaml(std::string *key, std::string *value, const std::string &line)
{
size_t where = line.find(":");
if (where != std::string::npos)
{
- key.assign(line, 0, where);
+ key->assign(line, 0, where);
// skip a space after the : if necessary
size_t val_start = where + 1;
if (val_start < line.size() && line.at(val_start) == ' ')
val_start++;
- value.assign(line, val_start, line.size() - val_start);
+ value->assign(line, val_start, line.size() - val_start);
unescape_yaml(key);
unescape_yaml(value);
@@ -789,13 +797,15 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len)
size_t indent = 0, cursor = 0;
std::string key, value, line;
-
+
/* Read the "- cmd:" line */
size_t advance = read_line(base, cursor, len, line);
trim_leading_spaces(line);
- if (! extract_prefix_and_unescape_yaml(key, value, line) || key != "- cmd")
+ if (! extract_prefix_and_unescape_yaml(&key, &value, line) || key != "- cmd")
+ {
goto done;
-
+ }
+
cursor += advance;
cmd = str2wcstring(value);
@@ -813,7 +823,7 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len)
if (this_indent == 0 || indent != this_indent)
break;
- if (! extract_prefix_and_unescape_yaml(key, value, line))
+ if (! extract_prefix_and_unescape_yaml(&key, &value, line))
break;
/* We are definitely going to consume this line */
@@ -843,7 +853,7 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len)
/* Skip the leading dash-space and then store this path it */
line.erase(0, 2);
- unescape_yaml(line);
+ unescape_yaml(&line);
paths.push_back(str2wcstring(line));
}
}
@@ -1008,7 +1018,7 @@ void history_t::populate_from_mmap(void)
size_t cursor = 0;
for (;;)
{
- size_t offset = offset_of_next_item(mmap_start, mmap_length, mmap_type, &cursor, birth_timestamp);
+ size_t offset = offset_of_next_item(mmap_start, mmap_length, mmap_type, &cursor, boundary_timestamp);
// If we get back -1, we're done
if (offset == (size_t)(-1))
break;
@@ -1188,31 +1198,31 @@ bool history_search_t::match_already_made(const wcstring &match) const
return false;
}
-static void replace_all(std::string &str, const char *needle, const char *replacement)
+static void replace_all(std::string *str, const char *needle, const char *replacement)
{
size_t needle_len = strlen(needle), replacement_len = strlen(replacement);
size_t offset = 0;
- while ((offset = str.find(needle, offset)) != std::string::npos)
+ while ((offset = str->find(needle, offset)) != std::string::npos)
{
- str.replace(offset, needle_len, replacement);
+ str->replace(offset, needle_len, replacement);
offset += replacement_len;
}
}
-static void escape_yaml(std::string &str)
+static void escape_yaml(std::string *str)
{
replace_all(str, "\\", "\\\\"); //replace one backslash with two
replace_all(str, "\n", "\\n"); //replace newline with backslash + literal n
}
/* This function is called frequently, so it ought to be fast. */
-static void unescape_yaml(std::string &str)
+static void unescape_yaml(std::string *str)
{
- size_t cursor = 0, size = str.size();
+ size_t cursor = 0, size = str->size();
while (cursor < size)
{
// Operate on a const version of str, to avoid needless COWs that at() does.
- const std::string &const_str = str;
+ const std::string &const_str = *str;
// Look for a backslash
size_t backslash = const_str.find('\\', cursor);
@@ -1228,13 +1238,13 @@ static void unescape_yaml(std::string &str)
if (escaped_char == '\\')
{
// Two backslashes in a row. Delete the second one.
- str.erase(backslash + 1, 1);
+ str->erase(backslash + 1, 1);
size--;
}
else if (escaped_char == 'n')
{
// Backslash + n. Replace with a newline.
- str.replace(backslash, 2, "\n");
+ str->replace(backslash, 2, "\n");
size--;
}
// The character at index backslash has now been made whole; start at the next character
@@ -1259,6 +1269,7 @@ static wcstring history_filename(const wcstring &name, const wcstring &suffix)
void history_t::clear_file_state()
{
+ ASSERT_IS_LOCKED(lock);
/* Erase everything we know about our file */
if (mmap_start != NULL && mmap_start != MAP_FAILED)
{
@@ -1692,6 +1703,20 @@ void history_t::populate_from_bash(FILE *stream)
}
}
+void history_t::incorporate_external_changes()
+{
+ /* To incorporate new items, we simply update our timestamp to now, so that items from previous instances get added. We then clear the file state so that we remap the file. Note that this is somehwhat expensive because we will be going back over old items. An optimization would be to preserve old_item_offsets so that they don't have to be recomputed. (However, then items *deleted* in other instances would not show up here). */
+ time_t new_timestamp = time(NULL);
+ scoped_lock locker(lock);
+
+ /* If for some reason the clock went backwards, we don't want to start dropping items; therefore we only do work if time has progressed. This also makes multiple calls cheap. */
+ if (new_timestamp > this->boundary_timestamp)
+ {
+ this->boundary_timestamp = new_timestamp;
+ this->clear_file_state();
+ }
+}
+
void history_init()
{
}
diff --git a/history.h b/history.h
index e6d48682..921f09c5 100644
--- a/history.h
+++ b/history.h
@@ -154,8 +154,8 @@ private:
/** The file ID of the file we mmap'd */
file_id_t mmap_file_id;
- /** Timestamp of when this history was created */
- const time_t birth_timestamp;
+ /** The boundary timestamp distinguishes old items from new items. Items whose timestamps are <= the boundary are considered "old". Items whose timestemps are > the boundary are new, and are ignored by this instance (unless they came from this instance). The timestamp may be adjusted by incorporate_external_changes() */
+ time_t boundary_timestamp;
/** How many items we add until the next vacuum. Initially a random value. */
int countdown_to_vacuum;
@@ -233,8 +233,11 @@ public:
/** Populates from a bash history file */
void populate_from_bash(FILE *f);
+ /** Incorporates the history of other shells into this history */
+ void incorporate_external_changes();
+
/* Gets all the history into a string with ARRAY_SEP_STR. This is intended for the $history environment variable. This may be long! */
- void get_string_representation(wcstring &str, const wcstring &separator);
+ void get_string_representation(wcstring *result, const wcstring &separator);
/** Sets the valid file paths for the history item with the given identifier */
void set_valid_file_paths(const wcstring_list_t &valid_file_paths, history_identifier_t ident);
diff --git a/parse_util.cpp b/parse_util.cpp
index ae7d7e5f..65cf77b2 100644
--- a/parse_util.cpp
+++ b/parse_util.cpp
@@ -1149,7 +1149,7 @@ parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t
tmp.append(paran_end+1);
parse_error_list_t subst_errors;
- err |= parse_util_detect_errors(subst, &subst_errors);
+ err |= parse_util_detect_errors(subst, &subst_errors, false /* do not accept incomplete */);
/* Our command substitution produced error offsets relative to its source. Tweak the offsets of the errors in the command substitution to account for both its offset within the string, and the offset of the node */
size_t error_offset = (paran_begin + 1 - arg_cpy) + node.source_start;
@@ -1212,7 +1212,7 @@ parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t
return err;
}
-parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, parse_error_list_t *out_errors)
+parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, parse_error_list_t *out_errors, bool allow_incomplete)
{
parse_node_tree_t node_tree;
parse_error_list_t parse_errors;
@@ -1227,31 +1227,41 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, pars
bool has_unclosed_block = false;
// Whether there's an unclosed quote, and therefore unfinished
+ // This is only set if allow_incomplete is set
bool has_unclosed_quote = false;
// Parse the input string into a parse tree
// Some errors are detected here
bool parsed = parse_tree_from_string(buff_src, parse_flag_leave_unterminated, &node_tree, &parse_errors);
- for (size_t i=0; i < parse_errors.size(); i++)
+ if (allow_incomplete)
{
- if (parse_errors.at(i).code == parse_error_tokenizer_unterminated_quote)
+ for (size_t i=0; i < parse_errors.size(); i++)
{
- // Remove this error, since we don't consider it a real error
- has_unclosed_quote = true;
- parse_errors.erase(parse_errors.begin() + i);
- i--;
+ if (parse_errors.at(i).code == parse_error_tokenizer_unterminated_quote)
+ {
+ // Remove this error, since we don't consider it a real error
+ has_unclosed_quote = true;
+ parse_errors.erase(parse_errors.begin() + i);
+ i--;
+ }
}
}
+
// #1238: If the only error was unterminated quote, then consider this to have parsed successfully. A better fix would be to have parse_tree_from_string return this information directly (but it would be a shame to munge up its nice bool return).
if (parse_errors.empty() && has_unclosed_quote)
+ {
parsed = true;
+ }
if (! parsed)
{
errored = true;
}
+ // has_unclosed_quote may only be set if allow_incomplete is true
+ assert(! has_unclosed_quote || allow_incomplete);
+
// Expand all commands
// Verify 'or' and 'and' not used inside pipelines
// Verify pipes via parser_is_pipe_forbidden
diff --git a/parse_util.h b/parse_util.h
index c918b9b5..b6c6e44c 100644
--- a/parse_util.h
+++ b/parse_util.h
@@ -178,7 +178,8 @@ wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote)
/** Given a string, parse it as fish code and then return the indents. The return value has the same size as the string */
std::vector<int> parse_util_compute_indents(const wcstring &src);
-parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, parse_error_list_t *out_errors = NULL);
+/** Given a string, detect parse errors in it. If allow_incomplete is set, then if the string is incomplete (e.g. an unclosed quote), an error is not returned and the PARSER_TEST_INCOMPLETE bit is set in the return value. If allow_incomplete is not set, then incomplete strings result in an error. */
+parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, parse_error_list_t *out_errors = NULL, bool allow_incomplete = true);
/**
Test if this argument contains any errors. Detected errors include syntax errors in command substitutions, improperly escaped characters and improper use of the variable expansion operator.
diff --git a/reader.cpp b/reader.cpp
index 8c4fc80c..44a219d5 100644
--- a/reader.cpp
+++ b/reader.cpp
@@ -183,10 +183,14 @@ static pthread_key_t generation_count_key;
static void set_command_line_and_position(editable_line_t *el, const wcstring &new_str, size_t pos);
-void editable_line_t::insert_string(const wcstring &str)
+void editable_line_t::insert_string(const wcstring &str, size_t start, size_t len)
{
- this->text.insert(this->position, str);
- this->position += str.size();
+ // Clamp the range to something valid
+ size_t string_length = str.size();
+ start = mini(start, string_length);
+ len = mini(len, string_length - start);
+ this->text.insert(this->position, str, start, len);
+ this->position += len;
}
/**
@@ -1215,30 +1219,50 @@ static void remove_backward()
/**
Insert the characters of the string into the command line buffer
and print them to the screen using syntax highlighting, etc.
- Optionally also expand abbreviations.
+ Optionally also expand abbreviations, after space characters.
Returns true if the string changed.
*/
-static bool insert_string(editable_line_t *el, const wcstring &str, bool should_expand_abbreviations = false)
+static bool insert_string(editable_line_t *el, const wcstring &str, bool allow_expand_abbreviations = false)
{
size_t len = str.size();
if (len == 0)
return false;
-
- el->insert_string(str);
- update_buff_pos(el, el->position);
- data->command_line_changed(el);
-
+
+ /* Start inserting. If we are expanding abbreviations, we have to do this after every space (see #1434), so look for spaces. We try to do this efficiently (rather than the simpler character at a time) to avoid expensive work in command_line_changed() */
+ size_t cursor = 0;
+ while (cursor < len)
+ {
+ /* Determine the position of the next space (possibly none), and the end of the range we wish to insert */
+ size_t space_triggering_expansion_pos = allow_expand_abbreviations ? str.find(L' ', cursor) : wcstring::npos;
+ bool has_space_triggering_expansion = (space_triggering_expansion_pos != wcstring::npos);
+ size_t range_end = (has_space_triggering_expansion ? space_triggering_expansion_pos + 1 : len);
+
+ /* Insert from the cursor up to but not including the range end */
+ assert(range_end > cursor);
+ el->insert_string(str, cursor, range_end - cursor);
+
+ update_buff_pos(el, el->position);
+ data->command_line_changed(el);
+
+ /* If we got a space, then the last character we inserted was that space. Expand abbreviations. */
+ if (has_space_triggering_expansion && allow_expand_abbreviations)
+ {
+ assert(range_end > 0);
+ assert(str.at(range_end - 1) == L' ');
+ data->expand_abbreviation_as_necessary(1);
+ }
+ cursor = range_end;
+ }
+
if (el == &data->command_line)
{
data->suppress_autosuggestion = false;
-
- if (should_expand_abbreviations)
- data->expand_abbreviation_as_necessary(1);
-
+
/* Syntax highlight. Note we must have that buff_pos > 0 because we just added something nonzero to its length */
assert(el->position > 0);
reader_super_highlight_me_plenty(-1);
}
+
reader_repaint();
return true;
@@ -1248,9 +1272,9 @@ static bool insert_string(editable_line_t *el, const wcstring &str, bool should_
Insert the character into the command line buffer and print it to
the screen using syntax highlighting, etc.
*/
-static bool insert_char(editable_line_t *el, wchar_t c, bool should_expand_abbreviations = false)
+static bool insert_char(editable_line_t *el, wchar_t c, bool allow_expand_abbreviations = false)
{
- return insert_string(el, wcstring(1, c), should_expand_abbreviations);
+ return insert_string(el, wcstring(1, c), allow_expand_abbreviations);
}
@@ -2544,7 +2568,7 @@ int reader_shell_test(const wchar_t *b)
bstr.push_back(L'\n');
parse_error_list_t errors;
- int res = parse_util_detect_errors(bstr, &errors);
+ int res = parse_util_detect_errors(bstr, &errors, true /* do accept incomplete */);
if (res & PARSER_TEST_ERROR)
{
@@ -3121,7 +3145,7 @@ const wchar_t *reader_readline(void)
break;
}
- insert_string(&data->command_line, arr);
+ insert_string(&data->command_line, arr, true);
}
}
@@ -4026,20 +4050,19 @@ const wchar_t *reader_readline(void)
{
if ((!wchar_private(c)) && (((c>31) || (c==L'\n'))&& (c != 127)))
{
- bool should_expand_abbreviations = false;
+ bool allow_expand_abbreviations = false;
if (data->is_navigating_pager_contents())
{
data->pager.set_search_field_shown(true);
}
else
{
- /* Expand abbreviations after space */
- should_expand_abbreviations = (c == L' ');
+ allow_expand_abbreviations = true;
}
/* Regular character */
editable_line_t *el = data->active_edit_line();
- insert_char(data->active_edit_line(), c, should_expand_abbreviations);
+ insert_char(data->active_edit_line(), c, allow_expand_abbreviations);
/* End paging upon inserting into the normal command line */
if (el == &data->command_line)
@@ -4220,7 +4243,7 @@ static int read_ni(int fd, const io_chain_t &io)
}
parse_error_list_t errors;
- if (! parse_util_detect_errors(str, &errors))
+ if (! parse_util_detect_errors(str, &errors, false /* do not accept incomplete */))
{
parser.eval(str, io, TOP);
}
diff --git a/reader.h b/reader.h
index 0a92aa1d..3375851a 100644
--- a/reader.h
+++ b/reader.h
@@ -64,8 +64,8 @@ public:
{
}
- /* Inserts the string at the cursor position */
- void insert_string(const wcstring &str);
+ /* Inserts a substring of str given by start, len at the cursor position */
+ void insert_string(const wcstring &str, size_t start = 0, size_t len = wcstring::npos);
};
/**
diff --git a/screen.cpp b/screen.cpp
index 96fe0f2a..9f1cc6e4 100644
--- a/screen.cpp
+++ b/screen.cpp
@@ -252,6 +252,29 @@ size_t escape_code_length(const wchar_t *code)
}
}
}
+
+ if (! found)
+ {
+ /* iTerm2 escape codes: CSI followed by ], terminated by either BEL or escape + backslash. See https://code.google.com/p/iterm2/wiki/ProprietaryEscapeCodes */
+ if (code[1] == ']')
+ {
+ // Start at 2 to skip over <esc>]
+ size_t cursor = 2;
+ for (; code[cursor] != L'\0'; cursor++)
+ {
+ /* Consume a sequence of characters up to <esc>\ or <bel> */
+ if (code[cursor] == '\x07' || (code[cursor] == '\\' && code[cursor - 1] == '\x1b'))
+ {
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ {
+ resulting_length = cursor + 1;
+ }
+ }
+ }
if (! found)
{
@@ -291,7 +314,6 @@ size_t escape_code_length(const wchar_t *code)
resulting_length = cursor;
}
}
-
if (! found)
{
/* Generic VT100 two byte sequence: <esc> followed by something in the range @ through _ */
diff --git a/share/completions/node.fish b/share/completions/node.fish
new file mode 100644
index 00000000..2eeb8701
--- /dev/null
+++ b/share/completions/node.fish
@@ -0,0 +1,200 @@
+#
+# Command specific completions for the node command.
+# These completions were generated from the commands
+# man page by the make_completions.py script, but has
+# been hand edited since.
+#
+
+# the four main options, each with a short & long flag
+complete -x -c node -s v -n '__fish_not_contain_opt -s p -s i -s e --eval --print --interactive' -l version --description "Print node's version"
+complete -c node -n '__fish_not_contain_opt -s v -s p -s i --version --print --interactive' -r -s e -l eval --description 'Evaluate script'
+complete -c node -n '__fish_not_contain_opt -s v -s e -s i --version --eval --interactive' -r -s p -l print --description 'Print result of --eval'
+complete -c node -n '__fish_not_contain_opt -s v -s p -s e --version --print --eval' -s i -l interactive --description 'Always enter the REPL even if stdin does not appear to be a terminal'
+
+# longer options related to V8, ES5, logging, etc.
+complete -c node -l no-deprecation --description 'Silence deprecation warnings'
+complete -c node -l trace-deprecation --description 'Show stack traces on deprecations'
+complete -c node -l throw-deprecation --description 'Throw errors on deprecations'
+complete -c node -l v8-options --description 'Print v8 command line options'
+complete -c node -l max-stack-size --description 'Set max v8 stack size (bytes)'
+complete -c node -l use_strict --description 'enforce strict mode. type: bool default: false'
+complete -c node -l es5_readonly --description 'activate correct semantics for inheriting readonliness. type: bool default: false'
+complete -c node -l es52_globals --description 'activate new semantics for global var declarations. type: bool default: false'
+complete -c node -l harmony_typeof --description 'enable harmony semantics for typeof. type: bool default: false'
+complete -c node -l harmony_scoping --description 'enable harmony block scoping. type: bool default: false'
+complete -c node -l harmony_modules --description 'enable harmony modules (implies block scoping). type: bool default: false'
+complete -c node -l harmony_proxies --description 'enable harmony proxies. type: bool default: false'
+complete -c node -l harmony_collections --description 'enable harmony collections (sets, maps, and weak maps). type: bool default: false'
+complete -c node -l harmony --description 'enable all harmony features (except typeof). type: bool default: false'
+complete -c node -l packed_arrays --description 'optimizes arrays that have no holes. type: bool default: false'
+complete -c node -l smi_only_arrays --description 'tracks arrays with only smi values. type: bool default: true'
+complete -c node -l clever_optimizations --description 'Optimize object size, Array shift, DOM strings and string +. type: bool default: true'
+complete -c node -l unbox_double_arrays --description 'automatically unbox arrays of doubles. type: bool default: true'
+complete -c node -l string_slices --description 'use string slices. type: bool default: true'
+complete -c node -l crankshaft --description 'use crankshaft. type: bool default: true'
+complete -c node -l hydrogen_filter --description 'optimization filter. type: string default:'
+complete -c node -l use_range --description 'use hydrogen range analysis. type: bool default: true'
+complete -c node -l eliminate_dead_phis --description 'eliminate dead phis. type: bool default: true'
+complete -c node -l use_gvn --description 'use hydrogen global value numbering. type: bool default: true'
+complete -c node -l use_canonicalizing --description 'use hydrogen instruction canonicalizing. type: bool default: true'
+complete -c node -l use_inlining --description 'use function inlining. type: bool default: true'
+complete -c node -l max_inlined_source_size --description 'maximum source size in bytes considered for a single inlining. type: int default: 600'
+complete -c node -l max_inlined_nodes --description 'maximum number of AST nodes considered for a single inlining. type: int default: 196'
+complete -c node -l max_inlined_nodes_cumulative --description 'maximum cumulative number of AST nodes considered for inlining. type: int default: 196'
+complete -c node -l loop_invariant_code_motion --description 'loop invariant code motion. type: bool default: true'
+complete -c node -l collect_megamorphic_maps_from_stub_cache --description 'crankshaft harvests type feedback from stub cache. type: bool default: true'
+complete -c node -l hydrogen_stats --description 'print statistics for hydrogen. type: bool default: false'
+complete -c node -l trace_hydrogen --description 'trace generated hydrogen to file. type: bool default: false'
+complete -c node -l trace_phase --description 'trace generated IR for specified phases. type: string default: Z'
+complete -c node -l trace_inlining --description 'trace inlining decisions. type: bool default: false'
+complete -c node -l trace_alloc --description 'trace register allocator. type: bool default: false'
+complete -c node -l trace_all_uses --description 'trace all use positions. type: bool default: false'
+complete -c node -l trace_range --description 'trace range analysis. type: bool default: false'
+complete -c node -l trace_gvn --description 'trace global value numbering. type: bool default: false'
+complete -c node -l trace_representation --description 'trace representation types. type: bool default: false'
+complete -c node -l stress_pointer_maps --description 'pointer map for every instruction. type: bool default: false'
+complete -c node -l stress_environments --description 'environment for every instruction. type: bool default: false'
+complete -c node -l deopt_every_n_times --description 'deoptimize every n times a deopt point is passed. type: int default: 0'
+complete -c node -l trap_on_deopt --description 'put a break point before deoptimizing. type: bool default: false'
+complete -c node -l deoptimize_uncommon_cases --description 'deoptimize uncommon cases. type: bool default: true'
+complete -c node -l polymorphic_inlining --description 'polymorphic inlining. type: bool default: true'
+complete -c node -l use_osr --description 'use on-stack replacement. type: bool default: true'
+complete -c node -l array_bounds_checks_elimination --description 'perform array bounds checks elimination. type: bool default: false'
+complete -c node -l array_index_dehoisting --description 'perform array index dehoisting. type: bool default: false'
+complete -c node -l trace_osr --description 'trace on-stack replacement. type: bool default: false'
+complete -c node -l stress_runs --description 'number of stress runs. type: int default: 0'
+complete -c node -l optimize_closures --description 'optimize closures. type: bool default: true'
+complete -c node -l inline_construct --description 'inline constructor calls. type: bool default: true'
+complete -c node -l inline_arguments --description 'inline functions with arguments object. type: bool default: true'
+complete -c node -l loop_weight --description 'loop weight for representation inference. type: int default: 1'
+complete -c node -l optimize_for_in --description 'optimize functions containing for-in loops. type: bool default: true'
+complete -c node -l experimental_profiler --description 'enable all profiler experiments. type: bool default: true'
+complete -c node -l watch_ic_patching --description 'profiler considers IC stability. type: bool default: false'
+complete -c node -l frame_count --description 'number of stack frames inspected by the profiler. type: int default: 1'
+complete -c node -l self_optimization --description 'primitive functions trigger their own optimization. type: bool default: false'
+complete -c node -l direct_self_opt --description 'call recompile stub directly when self-optimizing. type: bool default: false'
+complete -c node -l retry_self_opt --description 're-try self-optimization if it failed. type: bool default: false'
+complete -c node -l count_based_interrupts --description 'trigger profiler ticks based on counting instead of timing. type: bool default: false'
+complete -c node -l interrupt_at_exit --description 'insert an interrupt check at function exit. type: bool default: false'
+complete -c node -l weighted_back_edges --description 'weight back edges by jump distance for interrupt triggering. type: bool default: false'
+complete -c node -l interrupt_budget --description 'execution budget before interrupt is triggered. type: int default: 5900'
+complete -c node -l type_info_threshold --description 'percentage of ICs that must have type info to allow optimization. type: int default: 15'
+complete -c node -l self_opt_count --description 'call count before self-optimization. type: int default: 130'
+complete -c node -l trace_opt_verbose --description 'extra verbose compilation tracing. type: bool default: false'
+complete -c node -l debug_code --description 'generate extra code (assertions) for debugging. type: bool default: false'
+complete -c node -l code_comments --description 'emit comments in code disassembly. type: bool default: false'
+complete -c node -l enable_sse2 --description 'enable use of SSE2 instructions if available. type: bool default: true'
+complete -c node -l enable_sse3 --description 'enable use of SSE3 instructions if available. type: bool default: true'
+complete -c node -l enable_sse4_1 --description 'enable use of SSE4'
+complete -c node -l enable_cmov --description 'enable use of CMOV instruction if available. type: bool default: true'
+complete -c node -l enable_rdtsc --description 'enable use of RDTSC instruction if available. type: bool default: true'
+complete -c node -l enable_sahf --description 'enable use of SAHF instruction if available (X64 only). type: bool default: true'
+complete -c node -l enable_vfp3 --description 'enable use of VFP3 instructions if available - this implies enabling ARMv7 instructions (ARM only). type: bool default: true'
+complete -c node -l enable_armv7 --description 'enable use of ARMv7 instructions if available (ARM only). type: bool default: true'
+complete -c node -l enable_fpu --description 'enable use of MIPS FPU instructions if available (MIPS only). type: bool default: true'
+complete -c node -l expose_natives_as --description 'expose natives in global object. type: string default: NULL'
+complete -c node -l expose_debug_as --description 'expose debug in global object. type: string default: NULL'
+complete -c node -l expose_gc --description 'expose gc extension. type: bool default: false'
+complete -c node -l expose_externalize_string --description 'expose externalize string extension. type: bool default: false'
+complete -c node -l stack_trace_limit --description 'number of stack frames to capture. type: int default: 10'
+complete -c node -l builtins_in_stack_traces --description 'show built-in functions in stack traces. type: bool default: false'
+complete -c node -l disable_native_files --description 'disable builtin natives files. type: bool default: false'
+complete -c node -l inline_new --description 'use fast inline allocation. type: bool default: true'
+complete -c node -l stack_trace_on_abort --description 'print a stack trace if an assertion failure occurs. type: bool default: true'
+complete -c node -l trace --description 'trace function calls. type: bool default: false'
+complete -c node -l mask_constants_with_cookie --description 'use random jit cookie to mask large constants. type: bool default: true'
+complete -c node -l lazy --description 'use lazy compilation. type: bool default: true'
+complete -c node -l trace_opt --description 'trace lazy optimization. type: bool default: false'
+complete -c node -l trace_opt_stats --description 'trace lazy optimization statistics. type: bool default: false'
+complete -c node -l opt --description 'use adaptive optimizations. type: bool default: true'
+complete -c node -l always_opt --description 'always try to optimize functions. type: bool default: false'
+complete -c node -l prepare_always_opt --description 'prepare for turning on always opt. type: bool default: false'
+complete -c node -l trace_deopt --description 'trace deoptimization. type: bool default: false'
+complete -c node -l min_preparse_length --description 'minimum length for automatic enable preparsing. type: int default: 1024'
+complete -c node -l always_full_compiler --description 'try to use the dedicated run-once backend for all code. type: bool default: false'
+complete -c node -l trace_bailout --description 'print reasons for falling back to using the classic V8 backend. type: bool default: false'
+complete -c node -l compilation_cache --description 'enable compilation cache. type: bool default: true'
+complete -c node -l cache_prototype_transitions --description 'cache prototype transitions. type: bool default: true'
+complete -c node -l trace_debug_json --description 'trace debugging JSON request/response. type: bool default: false'
+complete -c node -l debugger_auto_break --description 'automatically set the debug break flag when debugger commands are in the queue. type: bool default: true'
+complete -c node -l enable_liveedit --description 'enable liveedit experimental feature. type: bool default: true'
+complete -c node -l break_on_abort --description 'always cause a debug break before aborting. type: bool default: true'
+complete -c node -l stack_size --description 'default size of stack region v8 is allowed to use (in kBytes). type: int default: 984'
+complete -c node -l max_stack_trace_source_length --description 'maximum length of function source code printed in a stack trace'
+complete -c node -l always_inline_smi_code --description 'always inline smi code in non-opt code. type: bool default: false'
+complete -c node -l max_new_space_size --description 'max size of the new generation (in kBytes). type: int default: 0'
+complete -c node -l max_old_space_size --description 'max size of the old generation (in Mbytes). type: int default: 0'
+complete -c node -l max_executable_size --description 'max size of executable memory (in Mbytes). type: int default: 0'
+complete -c node -l gc_global --description 'always perform global GCs. type: bool default: false'
+complete -c node -l gc_interval --description 'garbage collect after <n> allocations. type: int default: -1'
+complete -c node -l trace_gc --description 'print one trace line following each garbage collection. type: bool default: false'
+complete -c node -l trace_gc_nvp --description 'print one detailed trace line in name=value format after each garbage collection. type: bool default: false'
+complete -c node -l print_cumulative_gc_stat --description 'print cumulative GC statistics in name=value format on exit. type: bool default: false'
+complete -c node -l trace_gc_verbose --description 'print more details following each garbage collection. type: bool default: false'
+complete -c node -l trace_fragmentation --description 'report fragmentation for old pointer and data pages. type: bool default: false'
+complete -c node -l collect_maps --description 'garbage collect maps from which no objects can be reached. type: bool default: true'
+complete -c node -l flush_code --description 'flush code that we expect not to use again before full gc. type: bool default: true'
+complete -c node -l incremental_marking --description 'use incremental marking. type: bool default: true'
+complete -c node -l incremental_marking_steps --description 'do incremental marking steps. type: bool default: true'
+complete -c node -l trace_incremental_marking --description 'trace progress of the incremental marking. type: bool default: false'
+complete -c node -l use_idle_notification --description 'Use idle notification to reduce memory footprint'
+complete -c node -l send_idle_notification --description 'Send idle notification between stress runs'
+complete -c node -l use_ic --description 'use inline caching. type: bool default: true'
+complete -c node -l native_code_counters --description 'generate extra code for manipulating stats counters. type: bool default: false'
+complete -c node -l always_compact --description 'Perform compaction on every full GC. type: bool default: false'
+complete -c node -l lazy_sweeping --description 'Use lazy sweeping for old pointer and data spaces. type: bool default: true'
+complete -c node -l never_compact --description 'Never perform compaction on full GC - testing only. type: bool default: false'
+complete -c node -l compact_code_space --description 'Compact code space on full non-incremental collections. type: bool default: true'
+complete -c node -l cleanup_code_caches_at_gc --description 'Flush inline caches prior to mark compact collection and flush code caches in maps during mark compact cycle'
+complete -c node -l random_seed --description 'Default seed for initializing random generator (0, the default, means to use system random)'
+complete -c node -l use_verbose_printer --description 'allows verbose printing. type: bool default: true'
+complete -c node -l allow_natives_syntax --description 'allow natives syntax. type: bool default: false'
+complete -c node -l trace_sim --description 'Trace simulator execution. type: bool default: false'
+complete -c node -l check_icache --description 'Check icache flushes in ARM and MIPS simulator. type: bool default: false'
+complete -c node -l stop_sim_at --description 'Simulator stop after x number of instructions. type: int default: 0'
+complete -c node -l sim_stack_alignment --description 'Stack alignment in bytes in simulator (4 or 8, 8 is default). type: int default: 8'
+complete -c node -l trace_exception --description 'print stack trace when throwing exceptions. type: bool default: false'
+complete -c node -l preallocate_message_memory --description 'preallocate some memory to build stack traces'
+complete -c node -l randomize_hashes --description 'randomize hashes to avoid predictable hash collisions (with snapshots this option cannot override the baked-in seed). type: bool default: true'
+complete -c node -l hash_seed --description 'Fixed seed to use to hash property keys (0 means random) (with snapshots this option cannot override the baked-in seed). type: int default: 0'
+complete -c node -l preemption --description 'activate a 100ms timer that switches between V8 threads. type: bool default: false'
+complete -c node -l regexp_optimization --description 'generate optimized regexp code. type: bool default: true'
+complete -c node -l testing_bool_flag --description 'testing_bool_flag. type: bool default: true'
+complete -c node -l testing_int_flag --description 'testing_int_flag. type: int default: 13'
+complete -c node -l testing_float_flag --description 'float-flag. type: float default: 2'
+complete -c node -l testing_string_flag --description 'string-flag. type: string default: Hello, world!'
+complete -c node -l testing_prng_seed --description 'Seed used for threading test randomness. type: int default: 42'
+complete -c node -l testing_serialization_file --description 'file in which to serialize heap. type: string default: /tmp/serdes'
+complete -c node -l help --description 'Print usage message, including flags, on console. type: bool default: true'
+complete -c node -l dump_counters --description 'Dump counters on exit. type: bool default: false'
+complete -c node -l debugger --description 'Enable JavaScript debugger. type: bool default: false'
+complete -c node -l remote_debugger --description 'Connect JavaScript debugger to the debugger agent in another process. type: bool default: false'
+complete -c node -l debugger_agent --description 'Enable debugger agent. type: bool default: false'
+complete -c node -l debugger_port --description 'Port to use for remote debugging. type: int default: 5858'
+complete -c node -l map_counters --description 'Map counters to a file. type: string default:'
+complete -c node -l js_arguments --description 'Pass all remaining arguments to the script'
+complete -c node -l debug_compile_events --description 'Enable debugger compile events. type: bool default: true'
+complete -c node -l debug_script_collected_events --description 'Enable debugger script collected events. type: bool default: true'
+complete -c node -l gdbjit --description 'enable GDBJIT interface (disables compacting GC). type: bool default: false'
+complete -c node -l gdbjit_full --description 'enable GDBJIT interface for all code objects. type: bool default: false'
+complete -c node -l gdbjit_dump --description 'dump elf objects with debug info to disk. type: bool default: false'
+complete -c node -l gdbjit_dump_filter --description 'dump only objects containing this substring. type: string default:'
+complete -c node -l force_marking_deque_overflows --description 'force overflows of marking deque by reducing its size to 64 words. type: bool default: false'
+complete -c node -l stress_compaction --description 'stress the GC compactor to flush out bugs (implies --force_marking_deque_overflows). type: bool default: false'
+complete -c node -l log --description 'Minimal logging (no API, code, GC, suspect, or handles samples)'
+complete -c node -l log_all --description 'Log all events to the log file'
+complete -c node -l log_runtime --description 'Activate runtime system %Log call'
+complete -c node -l log_api --description 'Log API events to the log file'
+complete -c node -l log_code --description 'Log code events to the log file without profiling'
+complete -c node -l log_gc --description 'Log heap samples on garbage collection for the hp2ps tool'
+complete -c node -l log_handles --description 'Log global handle events'
+complete -c node -l log_snapshot_positions --description 'log positions of (de)serialized objects in the snapshot'
+complete -c node -l log_suspect --description 'Log suspect operations'
+complete -c node -l prof --description 'Log statistical profiling information (implies --log-code)'
+complete -c node -l prof_auto --description 'Used with --prof, starts profiling automatically) type: bool default: true'
+complete -c node -l prof_lazy --description 'Used with --prof, only does sampling and logging when profiler is active (implies --noprof_auto)'
+complete -c node -l prof_browser_mode --description 'Used with --prof, turns on browser-compatible mode for profiling'
+complete -c node -l log_regexp --description 'Log regular expression execution'
+complete -c node -l sliding_state_window --description 'Update sliding state window counters'
+complete -c node -l logfile --description 'Specify the name of the log file'
+complete -c node -l ll_prof --description 'Enable low-level linux profiler'
diff --git a/share/completions/npm.fish b/share/completions/npm.fish
new file mode 100644
index 00000000..2e65719f
--- /dev/null
+++ b/share/completions/npm.fish
@@ -0,0 +1,141 @@
+# NPM (https://npmjs.org) completions for Fish shell
+# top 2 fns taken from
+# https://stackoverflow.com/questions/16657803/creating-autocomplete-script-with-sub-commands
+# see also Fish's large set of completions for examples:
+# https://github.com/fish-shell/fish-shell/tree/master/share/completions
+
+function __fish_npm_needs_command
+ set cmd (commandline -opc)
+
+ if [ (count $cmd) -eq 1 -a $cmd[1] = 'npm' ]
+ return 0
+ end
+
+ return 1
+end
+
+function __fish_npm_using_command
+ set cmd (commandline -opc)
+
+ if [ (count $cmd) -gt 1 ]
+ if [ $argv[1] = $cmd[2] ]
+ return 0
+ end
+ end
+
+ return 1
+end
+
+# return everything that can be used with the npm config get/set commands
+function __fish_npm_settings
+ command npm config ls -l | command grep -o '.* =' | command tr -d '; ' | command tr -d ' ='
+end
+
+# cache
+complete -f -c npm -n '__fish_npm_needs_command' -a 'cache' -d "Manipulates package's cache"
+complete -f -c npm -n '__fish_npm_using_command cache' -a 'add' -d 'Add the specified package to the local cache'
+complete -f -c npm -n '__fish_npm_using_command cache' -a 'clean' -d 'Delete data out of the cache folder'
+complete -f -c npm -n '__fish_npm_using_command cache' -a 'ls' -d 'Show the data in the cache'
+
+# config
+for c in 'c' 'config'
+ complete -f -c npm -n "__fish_npm_needs_command" -a "$c" -d 'Manage the npm configuration files'
+ complete -f -c npm -n "__fish_npm_using_command $c" -a 'set' -d 'Sets the config key to the value'
+ complete -f -c npm -n "__fish_npm_using_command $c" -a 'get' -d 'Echo the config value to stdout'
+ complete -f -c npm -n "__fish_npm_using_command $c" -a 'delete' -d 'Deletes the key from all configuration files'
+ complete -f -c npm -n "__fish_npm_using_command $c" -a 'list' -d 'Show all the config settings'
+ complete -f -c npm -n "__fish_npm_using_command $c" -a 'ls' -d 'Show all the config settings'
+ complete -f -c npm -n "__fish_npm_using_command $c" -a 'edit' -d 'Opens the config file in an editor'
+end
+# get, set also exist as shorthands
+complete -f -c npm -n "__fish_npm_needs_command" -a 'get' -d 'Echo the config value to stdout'
+complete -f -c npm -n "__fish_npm_needs_command" -a 'set' -d 'Sets the config key to the value'
+complete -f -c npm -n "__fish_npm_using_command set" -a '(__fish_npm_settings)'
+complete -f -c npm -n "__fish_npm_using_command get" -a '(__fish_npm_settings)'
+
+# List of NPM commands
+# one quick-&-dirty way to get them: npm | grep ',' | tr ',' '\n'
+set --local npm_cmds 'add-user' 'adduser' 'apihelp' 'author' 'bin' 'bugs' 'c' 'completion' 'config' 'ddp' 'dedupe' 'deprecate' 'docs' 'edit' 'explore' 'faq' 'find' 'find-dupes' 'get' 'help' 'help-search' 'home' 'i' 'info' 'init' 'install' 'isntall' 'issues' 'la' 'link' 'list' 'll' 'ln' 'login' 'ls' 'outdated' 'owner' 'pack' 'prefix' 'prune' 'publish' 'r' 'rb' 'rebuild' 'remove' 'repo' 'restart' 'rm' 'root' 'run-script' 's' 'se' 'search' 'set' 'show' 'shrinkwrap' 'star' 'stars' 'start' 'stop' 'submodule' 't' 'tag' 'test' 'tst' 'un' 'uninstall' 'unlink' 'unpublish' 'unstar' 'up' 'update' 'v' 'version' 'view' 'whoami'
+
+# help
+complete -f -c npm -n '__fish_npm_needs_command' -a 'help' -d 'Get help on npm'
+complete -f -c npm -n '__fish_npm_using_command help' -a "$npm_cmds"
+
+# install
+for c in 'install' 'isntall' 'i'
+ complete -f -c npm -n '__fish_npm_needs_command' -a "$c" -d 'install a package'
+ complete -f -c npm -n "__fish_npm_using_command $c" -l save-dev -d 'Save to devDependencies in package.json'
+ complete -f -c npm -n "__fish_npm_using_command $c" -l save -d 'Save to dependencies in package.json'
+ complete -f -c npm -n "__fish_npm_using_command $c" -s g -l global -d 'Install package globally'
+end
+
+# list
+for c in 'la' 'list' 'll' 'ls'
+ complete -f -c npm -n '__fish_npm_needs_command' -a "$c" -d 'List installed packages'
+ complete -f -c npm -n "__fish_npm_using_command $c" -s g -l global -d 'List packages in the global install prefix instead of in the current project'
+ complete -f -c npm -n "__fish_npm_using_command $c" -l json -d 'Show information in JSON format'
+ complete -f -c npm -n "__fish_npm_using_command $c" -l long -d 'Show extended information'
+ complete -f -c npm -n "__fish_npm_using_command $c" -l parseable -d 'Show parseable output instead of tree view'
+ complete -x -c npm -n "__fish_npm_using_command $c" -l depth -d 'Max display depth of the dependency tree'
+end
+
+# owner
+complete -f -c npm -n '__fish_npm_needs_command' -a 'owner' -d 'Manage package owners'
+complete -f -c npm -n '__fish_npm_using_command owner' -a 'ls' -d 'List package owners'
+complete -f -c npm -n '__fish_npm_using_command owner' -a 'add' -d 'Add a new owner to package'
+complete -f -c npm -n '__fish_npm_using_command owner' -a 'rm' -d 'Remove an owner from package'
+
+# remove
+for c in 'r' 'remove' 'rm' 'un' 'uninstall' 'unlink'
+ complete -f -c npm -n '__fish_npm_needs_command' -a "$c" -d 'remove package'
+ complete -x -c npm -n "__fish_npm_using_command $c" -s g -l global -d 'remove global package'
+ complete -x -c npm -n "__fish_npm_using_command $c" -l save -d 'Package will be removed from your dependencies'
+ complete -x -c npm -n "__fish_npm_using_command $c" -l save-dev -d 'Package will be removed from your devDependencies'
+ complete -x -c npm -n "__fish_npm_using_command $c" -l save-optional -d 'Package will be removed from your optionalDependencies'
+end
+
+# search
+for c in 'find' 's' 'se' 'search'
+ complete -f -c npm -n '__fish_npm_needs_command' -a "$c" -d 'Search for packages'
+ complete -x -c npm -n "__fish_npm_using_command $c" -l long -d 'Display full package descriptions and other long text across multiple lines'
+end
+
+# update
+for c in 'up' 'update'
+ complete -f -c npm -n '__fish_npm_needs_command' -a "$c" -d 'Update package(s)'
+ complete -f -c npm -n "__fish_npm_using_command $c" -s g -l global -d 'Update global package(s)'
+end
+
+# misc shorter explanations
+complete -f -c npm -n '__fish_npm_needs_command' -a 'adduser add-user login' -d 'Add a registry user account'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'bin' -d 'Display npm bin folder'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'bugs issues' -d 'Bugs for a package in a web browser maybe'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'ddp dedupe find-dupes' -d 'Reduce duplication'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'deprecate' -d 'Deprecate a version of a package'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'docs home' -d 'Docs for a package in a web browser maybe'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'edit' -d 'Edit an installed package'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'explore' -d 'Browse an installed package'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'faq' -d 'Frequently Asked Questions'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'help-search' -d 'Search npm help documentation'
+complete -f -c npm -n '__fish_npm_using_command help-search' -l long -d 'Display full package descriptions and other long text across multiple lines'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'info v view' -d 'View registry info'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'link ln' -d 'Symlink a package folder'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'outdated' -d 'Check for outdated packages'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'pack' -d 'Create a tarball from a package'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'prefix' -d 'Display NPM prefix'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'prune' -d 'Remove extraneous packages'
+complete -c npm -n '__fish_npm_needs_command' -a 'publish' -d 'Publish a package'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'rb rebuild' -d 'Rebuild a package'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'root ' -d 'Display npm root'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'run-script run' -d 'Run arbitrary package scripts'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'shrinkwrap' -d 'Lock down dependency versions'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'star' -d 'Mark your favorite packages'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'stars' -d 'View packages marked as favorites'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'start' -d 'Start a package'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'stop' -d 'Stop a package'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'submodule' -d 'Add a package as a git submodule'
+complete -f -c npm -n '__fish_npm_needs_command' -a 't tst test' -d 'Test a package'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'unpublish' -d 'Remove a package from the registry'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'unstar' -d 'Remove star from a package'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'version' -d 'Bump a package version'
+complete -f -c npm -n '__fish_npm_needs_command' -a 'whoami' -d 'Display npm username'
diff --git a/share/functions/fish_prompt.fish b/share/functions/fish_prompt.fish
index f4aab344..aaa48c68 100644
--- a/share/functions/fish_prompt.fish
+++ b/share/functions/fish_prompt.fish
@@ -15,7 +15,7 @@ function fish_prompt --description "Write out the prompt"
switch $USER
- case root
+ case root toor
if not set -q __fish_prompt_cwd
if set -q fish_color_cwd_root
diff --git a/share/functions/fish_vi_prompt.fish b/share/functions/fish_vi_prompt.fish
index cd70a114..420ea3cf 100644
--- a/share/functions/fish_vi_prompt.fish
+++ b/share/functions/fish_vi_prompt.fish
@@ -27,7 +27,7 @@ function fish_vi_prompt --description "Simple vi prompt"
switch $USER
- case root
+ case root toor
if not set -q __fish_prompt_cwd
if set -q fish_color_cwd_root
diff --git a/share/functions/umask.fish b/share/functions/umask.fish
index f65ca655..d68fb4dd 100644
--- a/share/functions/umask.fish
+++ b/share/functions/umask.fish
@@ -1,7 +1,7 @@
function __fish_umask_parse -d "Internal umask function"
# Test if already a valid octal mask, and pad it with zeros
- if echo $argv | sgrep -E '^(0|)[0-7]{1,3}$' >/dev/null
+ if echo $argv | sgrep -E '^0?[0-7]{1,3}$' >/dev/null
set -l char_count (echo $argv| wc -c)
for i in (seq (math 5 - $char_count)); set argv 0$argv; end
echo $argv
diff --git a/share/tools/web_config/fishconfig.css b/share/tools/web_config/fishconfig.css
index f018e4ed..d2eb8fc7 100644
--- a/share/tools/web_config/fishconfig.css
+++ b/share/tools/web_config/fishconfig.css
@@ -349,22 +349,23 @@ body {
cursor: pointer;
}
-.color_scheme_choice_label {
+.color_scheme_choice_label, .prompt_demo_choice_label {
margin-left: 10px;
margin-bottom: 3px;
cursor: pointer;
font-size: 12pt;
white-space: normal;
+ color: #AAA;
}
-.color_scheme_choices_scrollview {
+.color_scheme_choices_scrollview, .prompt_choices_scrollview {
border-top: 1px solid #333;
padding-top: 5px;
overflow: scroll;
- max-height: 18em; /* about two and a half boxes */
+ max-height: 30em; /* about two and a half boxes */
}
-.color_scheme_choices_list {
+.color_scheme_choices_list, .prompt_choices_list {
overflow-y: hidden; /* makes our height account for floats */
padding: 0 10px 15px 10px; /* top right bottom left */
bottom: 0px;
@@ -418,17 +419,26 @@ img.delete_icon {
color: #C8C8C8;
}
-.prompt_demo {
+.prompt_demo, .current_prompt {
font-size: 12pt;
padding: 10px;
- margin: 5px 20px 25px; /* top right bottom left */
+ margin: 5px 5px 25px 5px; /* top right bottom left */
cursor: pointer;
line-height: 1.8em;
- border: solid #777 1px;
+ border: solid #333 1px;
position: relative; /* so that our absolutely positioned elements work */
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.unbordered {
+ border: none;
+ padding-top: 0;
+ padding-bottom: 0;
}
-.save_button, .prompt_save_button, .colors_close_button, .customize_theme_button {
+.save_button, .prompt_save_button, .colors_close_button, .customize_theme_button, .generic_button {
border-radius: 5px;
border: solid rgba(71,71,71,0.5) 1px;
padding: 5px 8px;
@@ -440,7 +450,7 @@ img.delete_icon {
cursor: pointer;
}
-.save_button:hover, .customize_theme_button:hover {
+.save_button:hover, .customize_theme_button:hover, .generic_button:hover {
border-color: rgba(71,71,71,0.9);
}
@@ -456,12 +466,6 @@ img.delete_icon {
font-size: 12pt;
}
-.prompt_demo_choice_label {
- margin: 25px 20px 5px;
- font-size: 12pt;
- white-space: normal;
-}
-
.prompt_demo_text {
white-space: pre;
line-height: 170%;
diff --git a/share/tools/web_config/js/controllers.js b/share/tools/web_config/js/controllers.js
index 796e7729..7db84a35 100644
--- a/share/tools/web_config/js/controllers.js
+++ b/share/tools/web_config/js/controllers.js
@@ -120,6 +120,8 @@ controllers.controller("colorsController", function($scope, $http) {
controllers.controller("promptController", function($scope, $http) {
$scope.selectedPrompt = null;
+ $scope.showSaveButton = true;
+ $scope.savePromptButtonTitle = "Set Prompt";
$scope.fetchSamplePrompts= function() {
$http.get("/sample_prompts/").success(function(data, status, headers, config) {
@@ -131,8 +133,10 @@ controllers.controller("promptController", function($scope, $http) {
}
})};
- $scope.selectPrompt = function(promptt) {
- $scope.selectedPrompt= promptt;
+ $scope.selectPrompt = function(prompt) {
+ $scope.selectedPrompt= prompt;
+
+ $scope.savePromptButtonTitle = "Set Prompt";
}
$scope.setNewPrompt = function(selectedPrompt) {
@@ -143,9 +147,18 @@ controllers.controller("promptController", function($scope, $http) {
$scope.samplePrompts[0].function = selectedPrompt.function;
$scope.samplePrompts[0].font_size = selectedPrompt.font_size;
$scope.selectedPrompt = $scope.samplePrompts[0];
+
+ // Note that we set it
+ $scope.savePromptButtonTitle = "Prompt Set!";
})};
$scope.fetchSamplePrompts();
+
+ $scope.setPrompt = function() {
+ if ($scope.selectedPrompt) {
+ $scope.setNewPrompt($scope.selectedPrompt);
+ }
+ }
});
controllers.controller("functionsController", function($scope, $http) {
diff --git a/share/tools/web_config/partials/colors.html b/share/tools/web_config/partials/colors.html
index 3738ec2b..9a3730cf 100644
--- a/share/tools/web_config/partials/colors.html
+++ b/share/tools/web_config/partials/colors.html
@@ -124,7 +124,7 @@ fish cannot change the background color of your terminal. Refer to your terminal
<div class="color_scheme_choice_container" data-ng-repeat="colorScheme in colorSchemes" ng-click="changeSelectedColorScheme(colorScheme)">
<div class="color_scheme_choice_label">
<!-- This click/clickBubble nonsense is so that we can have a separate URL inside a parent with an onClick handler -->
- <span style="color: #AAA">{{colorScheme.name}}</span><!--a data-bind="if: $data.url, click: function(){return true;}, clickBubble: false, attr: { href: $data.url}"><img class="external_link_img" src="external_link.png"></a-->
+ {{colorScheme.name}}<!--a data-bind="if: $data.url, click: function(){return true;}, clickBubble: false, attr: { href: $data.url}"><img class="external_link_img" src="external_link.png"></a-->
</div>
<div class="colorpicker_text_sample_tight" data-ng-style="{'background-color': colorScheme.preferred_background}">
<span data-ng-style="{'color': colorScheme.command}">/bright/vixens</span>
diff --git a/share/tools/web_config/partials/prompt.html b/share/tools/web_config/partials/prompt.html
index e11da923..fed16141 100644
--- a/share/tools/web_config/partials/prompt.html
+++ b/share/tools/web_config/partials/prompt.html
@@ -1,10 +1,20 @@
-<div style="padding: 0 10px 15px;">
+<!-- The first 'sample' prompt is the current one; the remainders are samples. This ought to be cleaned up. -->
+<div class="current_prompt" style="min-height: 7.5em">
+ <div class="prompt_demo_choice_label">{{ selectedPrompt.name }}</div>
+ <div ng-bind-html-unsafe='selectedPrompt.demo' class="prompt_demo unbordered"></div>
+ <div style="position: absolute; right: 5px; bottom: 5px; color:">
+ <span class="save_button"
+ ng-show="showSaveButton"
+ style="color: #777"
+ ng-click="setPrompt()">{{ savePromptButtonTitle }}</span>
+ </div>
+</div>
+<div style="margin: 10px 0 7px 35px;">Select a prompt below:</div>
+<div class="prompt_choices_scrollview">
+<div class="prompt_choices_list">
<div ng-repeat="prompt in samplePrompts">
<div class="prompt_demo_choice_label">{{ prompt.name }}</div>
<div ng-bind-html-unsafe='prompt.demo' class="prompt_demo" ng-click="selectPrompt(prompt)"></div>
- <div class="prompt_function" ng-show="selectedPrompt == prompt">
- <div class="prompt_function_text">{{ prompt.function }}</div>
- </div>
- <span class="prompt_save_button" ng-click="setNewPrompt(selectedPrompt)" ng-show="selectedPrompt == prompt && selectedPrompt != samplePrompts[0]">Use</span>
</div>
</div>
+</div>
diff --git a/share/tools/web_config/sample_prompts/classic.fish b/share/tools/web_config/sample_prompts/classic.fish
index 9464b778..64c2fb50 100644
--- a/share/tools/web_config/sample_prompts/classic.fish
+++ b/share/tools/web_config/sample_prompts/classic.fish
@@ -12,7 +12,7 @@ function fish_prompt --description "Write out the prompt"
switch $USER
- case root
+ case root toor
if not set -q __fish_prompt_cwd
if set -q fish_color_cwd_root
diff --git a/share/tools/web_config/sample_prompts/classic_git.fish b/share/tools/web_config/sample_prompts/classic_git.fish
index 0d91f5fe..299faa34 100644
--- a/share/tools/web_config/sample_prompts/classic_git.fish
+++ b/share/tools/web_config/sample_prompts/classic_git.fish
@@ -43,7 +43,7 @@ function fish_prompt --description 'Write out the prompt'
switch $USER
- case root
+ case root toor
if not set -q __fish_prompt_cwd
if set -q fish_color_cwd_root
diff --git a/share/tools/web_config/sample_prompts/classic_status.fish b/share/tools/web_config/sample_prompts/classic_status.fish
index a93ff9f2..4b062c5d 100644
--- a/share/tools/web_config/sample_prompts/classic_status.fish
+++ b/share/tools/web_config/sample_prompts/classic_status.fish
@@ -22,7 +22,7 @@ function fish_prompt --description 'Write out the prompt'
set -l user_prompt '>'
switch $USER
# Set our root colors, if we're root :)
- case root
+ case root toor
set user_prompt '#'
if not set -q __fish_prompt_cwd
if set -q fish_color_cwd_root
diff --git a/share/tools/web_config/sample_prompts/debian_chroot.fish b/share/tools/web_config/sample_prompts/debian_chroot.fish
index d800c744..80acfa9a 100644
--- a/share/tools/web_config/sample_prompts/debian_chroot.fish
+++ b/share/tools/web_config/sample_prompts/debian_chroot.fish
@@ -31,7 +31,7 @@ function fish_prompt --description 'Write out the prompt, prepending the Debian
switch $USER
- case root
+ case root toor
if not set -q __fish_prompt_cwd
if set -q fish_color_cwd_root
diff --git a/share/tools/web_config/sample_prompts/informative.fish b/share/tools/web_config/sample_prompts/informative.fish
index 9095b740..b0bc601b 100644
--- a/share/tools/web_config/sample_prompts/informative.fish
+++ b/share/tools/web_config/sample_prompts/informative.fish
@@ -27,7 +27,7 @@ if not set -q __fish_color_blue
switch $USER
-case root
+case root toor
if not set -q __fish_prompt_cwd
if set -q fish_color_cwd_root
@@ -48,4 +48,4 @@ if not set -q __fish_prompt_cwd
printf '[%s] %s%s@%s %s%s %s(%s)%s \f\r> ' (date "+%H:%M:%S") "$__fish_color_blue" $USER $__fish_prompt_hostname "$__fish_prompt_cwd" (pwd) "$__fish_color_status" "$stat" "$__fish_prompt_normal"
end
-end \ No newline at end of file
+end
diff --git a/share/tools/web_config/sample_prompts/nim.fish b/share/tools/web_config/sample_prompts/nim.fish
index 6eab39e6..87837bf8 100644
--- a/share/tools/web_config/sample_prompts/nim.fish
+++ b/share/tools/web_config/sample_prompts/nim.fish
@@ -13,7 +13,7 @@ function fish_prompt
end
set_color -o green
echo -n [
- if [ $USER = root ]
+ if test $USER = root -o $USER = toor
set_color -o red
else
set_color -o yellow
diff --git a/share/tools/web_config/sample_prompts/user_host_path.fish b/share/tools/web_config/sample_prompts/user_host_path.fish
index c125eea0..de917ee7 100644
--- a/share/tools/web_config/sample_prompts/user_host_path.fish
+++ b/share/tools/web_config/sample_prompts/user_host_path.fish
@@ -6,7 +6,7 @@ function fish_prompt -d "Write out the prompt"
set -l pwd (echo -n $PWD | sed "s/^$home_escaped/~/" | sed 's/ /%20/g')
set -l prompt_symbol ''
switch $USER
- case root; set prompt_symbol '#'
+ case root toor; set prompt_symbol '#'
case '*'; set prompt_symbol '$'
end
printf "[%s@%s %s%s%s]%s " $USER (hostname -s) (set_color $fish_color_cwd) $pwd (set_color normal) $prompt_symbol
diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py
index fec78af9..c3daef5f 100755
--- a/share/tools/web_config/webconfig.py
+++ b/share/tools/web_config/webconfig.py
@@ -1,7 +1,10 @@
#!/usr/bin/env python
# Whether we're Python 2
-import sys, os
+import sys
+import multiprocessing.pool
+import os
+import operator
IS_PY2 = sys.version_info[0] == 2
if IS_PY2:
@@ -29,8 +32,6 @@ try:
except ImportError:
import simplejson as json
-from optparse import OptionParser
-
FISH_BIN_PATH = False # will be set later
def run_fish_cmd(text):
from subprocess import PIPE
@@ -287,7 +288,7 @@ class BindingParser:
def set_buffer(self, buffer):
""" Sets code to parse """
- self.buffer = buffer
+ self.buffer = buffer or b''
self.index = 0
def get_char(self):
@@ -489,7 +490,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
result.append([color_name, color_desc, parse_color('')])
# Sort our result (by their keys)
- result.sort()
+ result.sort(key=operator.itemgetter('name'))
return result
@@ -545,29 +546,22 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
bindings = []
binding_parser = BindingParser()
- parser = OptionParser()
- parser.add_option("-k")
- parser.add_option("-M")
- parser.add_option("-m")
-
- # Ignore any parsing errors
- parser.error = lambda x: None
-
for line in out.split('\n'):
- comps = line.split(' ', 1)
+ comps = line.split(' ', 2)
- if len(comps) < 2:
+ if len(comps) < 3:
continue
- # parse arguments passed to bind command
- bind_args_list = comps[1].split(' ', 6)
- (options, args) = parser.parse_args(bind_args_list)
-
- key_name= options.k
- command = args[0]
+ if comps[1] == '-k':
+ key_name, command = comps[2].split(' ', 1)
+ binding_parser.set_buffer(key_name)
+ else:
+ key_name = None
+ command = comps[2]
+ binding_parser.set_buffer(comps[1])
- binding_parser.set_buffer(key_name)
- fish_binding = FishBinding(command=command, binding=key_name, readable_binding=binding_parser.get_readable_binding())
+ readable_binding = binding_parser.get_readable_binding()
+ fish_binding = FishBinding(command, key_name, readable_binding)
bindings.append(fish_binding)
return [ binding.get_json_obj() for binding in bindings ]
@@ -611,22 +605,27 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
out, err = run_fish_cmd(cmd)
return len(err) == 0
- def do_get_prompt(self, command_to_run, prompt_function_text):
+ def do_get_prompt(self, command_to_run, prompt_function_text, extras_dict):
# Return the prompt output by the given command
prompt_demo_ansi, err = run_fish_cmd(command_to_run)
prompt_demo_html = ansi_to_html(prompt_demo_ansi)
prompt_demo_font_size = self.font_size_for_ansi_prompt(prompt_demo_ansi)
- return {'function': prompt_function_text, 'demo': prompt_demo_html, 'font_size': prompt_demo_font_size }
+ result = {'function': prompt_function_text, 'demo': prompt_demo_html, 'font_size': prompt_demo_font_size }
+ if extras_dict:
+ result.update(extras_dict)
+ return result
def do_get_current_prompt(self):
# Return the current prompt
prompt_func, err = run_fish_cmd('functions fish_prompt')
- return self.do_get_prompt('cd "' + initial_wd + '" ; fish_prompt', prompt_func.strip())
+ result = self.do_get_prompt('builtin cd "' + initial_wd + '" ; fish_prompt', prompt_func.strip(), {'name': 'Current'})
+ return result
- def do_get_sample_prompt(self, text):
+ def do_get_sample_prompt(self, text, extras_dict):
# Return the prompt you get from the given text
+ # extras_dict is a dictionary whose values get merged in
cmd = text + "\n cd \"" + initial_wd + "\" \n fish_prompt\n"
- return self.do_get_prompt(cmd, text.strip())
+ return self.do_get_prompt(cmd, text.strip(), extras_dict)
def parse_one_sample_prompt_hash(self, line, result_dict):
# Allow us to skip whitespace, etc.
@@ -644,38 +643,41 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
return line.startswith('#')
- def read_one_sample_prompt(self, fd):
- # Read one sample prompt from fd
- function_lines = []
- result = {}
- parsing_hashes = True
- for line in fd:
- # Parse hashes until parse_one_sample_prompt_hash return False
- if parsing_hashes:
- parsing_hashes = self.parse_one_sample_prompt_hash(line, result)
- # Maybe not we're not parsing hashes, or maybe we already were not
- if not parsing_hashes:
- function_lines.append(line)
- func = ''.join(function_lines).strip()
- result.update(self.do_get_sample_prompt(func))
- return result
+ def read_one_sample_prompt(self, path):
+ try:
+ with open(path) as fd:
+ extras_dict = {}
+ # Read one sample prompt from fd
+ function_lines = []
+ parsing_hashes = True
+ for line in fd:
+ # Parse hashes until parse_one_sample_prompt_hash return False
+ if parsing_hashes:
+ parsing_hashes = self.parse_one_sample_prompt_hash(line, extras_dict)
+ # Maybe not we're not parsing hashes, or maybe we already were not
+ if not parsing_hashes:
+ function_lines.append(line)
+ func = ''.join(function_lines).strip()
+ result = self.do_get_sample_prompt(func, extras_dict)
+ return result
+ except IOError:
+ # Ignore unreadable files, etc.
+ return None
def do_get_sample_prompts_list(self):
- result = []
- # Start with the "Current" meta-sample
- result.append({'name': 'Current'})
- result[0].update(self.do_get_current_prompt())
+ pool = multiprocessing.pool.ThreadPool(processes=8)
+
+ # Kick off the "Current" meta-sample
+ current_metasample_async = pool.apply_async(self.do_get_current_prompt)
# Read all of the prompts in sample_prompts
paths = glob.iglob('sample_prompts/*.fish')
- for path in paths:
- try:
- fd = open(path)
- result.append(self.read_one_sample_prompt(fd))
- fd.close()
- except IOError:
- # Ignore unreadable files, etc
- pass
+ sample_results = pool.map(self.read_one_sample_prompt, paths, 1)
+
+ # Finish up
+ result = []
+ result.append(current_metasample_async.get())
+ result.extend([r for r in sample_results if r])
return result
def font_size_for_ansi_prompt(self, prompt_demo_ansi):
@@ -705,8 +707,6 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
output = self.do_get_history()
# end = time.time()
# print "History: ", end - start
- elif p == '/current_prompt/':
- output = self.do_get_current_prompt()
elif p == '/sample_prompts/':
output = self.do_get_sample_prompts_list()
elif re.match(r"/color/(\w+)/", p):
@@ -761,9 +761,6 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
elif p == '/get_function/':
what = postvars.get('what')
output = [self.do_get_function(what[0])]
- elif p == '/get_sample_prompt/':
- what = postvars.get('what')
- output = [self.do_get_sample_prompt(what[0])]
elif p == '/delete_history_item/':
what = postvars.get('what')
if self.do_delete_history_item(what[0]):
diff --git a/tests/history_sample_corrupt1 b/tests/history_sample_corrupt1
new file mode 100644
index 00000000..3dfe694f
--- /dev/null
+++ b/tests/history_sample_corrupt1
@@ -0,0 +1,5 @@
+- cmd: this_command_is_ok
+- cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: when: 1396531614
+- cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: - cmd: corrupt_prefix
+- cmd: no_newline_at_end_of_file
+ when: 1403531898 \ No newline at end of file