diff --git a/builtin.cpp b/builtin.cpp index 331f96308..3b40be3c4 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -1660,7 +1660,7 @@ static int builtin_echo(parser_t &parser, wchar_t **argv) wc = L'\b'; break; case L'e': - wc = L'\e'; + wc = L'\x1B'; break; case L'f': wc = L'\f'; @@ -2372,8 +2372,9 @@ static int builtin_read(parser_t &parser, wchar_t **argv) reader_set_highlight_function(&highlight_shell); reader_set_test_function(&reader_shell_test); } - /* No autosuggestions in builtin_read */ + /* No autosuggestions or abbreviations in builtin_read */ reader_set_allow_autosuggesting(false); + reader_set_expand_abbreviations(false); reader_set_exit_on_interrupt(true); reader_set_buffer(commandline, wcslen(commandline)); diff --git a/builtin_printf.cpp b/builtin_printf.cpp index e164f8193..efe4a2118 100644 --- a/builtin_printf.cpp +++ b/builtin_printf.cpp @@ -26,6 +26,7 @@ \a = alert (bell) \b = backspace \c = produce no further output + \e = escape \f = form feed \n = new line \r = carriage return @@ -319,6 +320,9 @@ void builtin_printf_state_t::print_esc_char(wchar_t c) case L'c': /* Cancel the rest of the output. */ this->early_exit = true; break; + case L'e': /* Escape */ + this->append_output(L'\x1B'); + break; case L'f': /* Form feed. */ this->append_output(L'\f'); break; @@ -358,7 +362,7 @@ long builtin_printf_state_t::print_esc(const wchar_t *escstart, bool octal_0) esc_value = esc_value * 16 + hex_to_bin(*p); if (esc_length == 0) this->fatal_error(_(L"missing hexadecimal number in escape")); - this->append_format_output(L"%lc", esc_value); + this->append_output(esc_value); } else if (is_octal_digit(*p)) { @@ -367,10 +371,12 @@ long builtin_printf_state_t::print_esc(const wchar_t *escstart, bool octal_0) extension to POSIX that is compatible with Bash 2.05b. */ for (esc_length = 0, p += octal_0 && *p == L'0'; esc_length < 3 && is_octal_digit(*p); ++esc_length, ++p) esc_value = esc_value * 8 + octal_to_bin(*p); - this->append_format_output(L"%c", esc_value); + this->append_output(esc_value); } - else if (*p && wcschr(L"\"\\abcfnrtv", *p)) + else if (*p && wcschr(L"\"\\abcefnrtv", *p)) + { print_esc_char(*p++); + } else if (*p == L'u' || *p == L'U') { wchar_t esc_char = *p; @@ -575,6 +581,16 @@ void builtin_printf_state_t::print_direc(const wchar_t *start, size_t length, wc } } +/* For each character in str, set the corresponding boolean in the array to the given flag */ +static inline void modify_allowed_format_specifiers(bool ok[UCHAR_MAX + 1], const char *str, bool flag) +{ + for (const char *c = str; *c != '\0'; c++) + { + unsigned char idx = static_cast(*c); + ok[idx] = flag; + } +} + /* Print the text in FORMAT, using ARGV (with ARGC elements) for arguments to any `%' directives. Return the number of elements of ARGV used. */ @@ -616,10 +632,8 @@ int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wch } break; } - - ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E'] = - ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o'] = - ok['s'] = ok['u'] = ok['x'] = ok['X'] = true; + + modify_allowed_format_specifiers(ok, "aAcdeEfFgGiosuxX", true); for (;; f++, direc_length++) { @@ -627,18 +641,17 @@ int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wch { case L'I': case L'\'': - ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] = - ok['o'] = ok['s'] = ok['x'] = ok['X'] = false; + modify_allowed_format_specifiers(ok, "aAceEosxX", false); break; case '-': case '+': case ' ': break; case L'#': - ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = false; + modify_allowed_format_specifiers(ok, "cdisu", false); break; case '0': - ok['c'] = ok['s'] = false; + modify_allowed_format_specifiers(ok, "cs", false); break; default: goto no_more_flag_characters; @@ -679,7 +692,7 @@ no_more_flag_characters: { ++f; ++direc_length; - ok['c'] = false; + modify_allowed_format_specifiers(ok, "c", false); if (*f == L'*') { ++f; diff --git a/env.cpp b/env.cpp index 398425c22..ddb5d2c4e 100644 --- a/env.cpp +++ b/env.cpp @@ -137,7 +137,6 @@ struct env_node_t class variable_entry_t { - bool exportv; /**< Whether the variable should be exported */ wcstring value; /**< Value of the variable */ }; diff --git a/expand.cpp b/expand.cpp index 6dfa9e448..dc83c385d 100644 --- a/expand.cpp +++ b/expand.cpp @@ -1336,10 +1336,7 @@ static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector< const wchar_t * const in = input.c_str(); int parse_ret; - switch (parse_ret = parse_util_locate_cmdsubst(in, - ¶n_begin, - ¶n_end, - 0)) + switch (parse_ret = parse_util_locate_cmdsubst(in, ¶n_begin, ¶n_end, false)) { case -1: parser.error(SYNTAX_ERROR, @@ -1628,10 +1625,7 @@ int expand_string(const wcstring &input, std::vector &output, expa { wchar_t *begin, *end; - if (parse_util_locate_cmdsubst(input.c_str(), - &begin, - &end, - 1) != 0) + if (parse_util_locate_cmdsubst(input.c_str(), &begin, &end, true) != 0) { parser.error(CMDSUBST_ERROR, -1, L"Command substitutions not allowed"); return EXPAND_ERROR; @@ -1939,3 +1933,42 @@ bool fish_openSUSE_dbus_hack_hack_hack_hack(std::vector *args) } return result; } + +bool expand_abbreviation(const wcstring &src, wcstring *output) +{ + if (src.empty()) + return false; + + /* Get the abbreviations. Return false if we have none */ + env_var_t var = env_get_string(USER_ABBREVIATIONS_VARIABLE_NAME); + if (var.missing_or_empty()) + return false; + + bool result = false; + wcstring line; + wcstokenizer tokenizer(var, ARRAY_SEP_STR); + while (tokenizer.next(line)) + { + /* Line is expected to be of the form 'foo=bar'. Parse out the first =. Be forgiving about spaces, but silently skip on failure (no equals, or equals at the end or beginning). Try to avoid copying any strings until we are sure this is a match. */ + size_t equals = line.find(L'='); + if (equals == wcstring::npos || equals == 0 || equals + 1 == line.size()) + continue; + + /* Find the character just past the end of the command. Walk backwards, skipping spaces. */ + size_t cmd_end = equals; + while (cmd_end > 0 && iswspace(line.at(cmd_end - 1))) + cmd_end--; + + /* See if this command matches */ + if (line.compare(0, cmd_end, src) == 0) + { + /* Success. Set output to everythign past the end of the string. */ + if (output != NULL) + output->assign(line, equals + 1, wcstring::npos); + + result = true; + break; + } + } + return result; +} diff --git a/expand.h b/expand.h index 1968e0ee5..51e149dfe 100644 --- a/expand.h +++ b/expand.h @@ -206,6 +206,10 @@ void expand_variable_error(parser_t &parser, const wchar_t *token, size_t token_ */ std::vector expand_get_all_process_names(void); +/** Abbreviation support. Expand src as an abbreviation, returning true if one was found, false if not. If result is not-null, returns the abbreviation by reference. */ +#define USER_ABBREVIATIONS_VARIABLE_NAME L"fish_user_abbreviations" +bool expand_abbreviation(const wcstring &src, wcstring *output); + /* Terrible hacks */ bool fish_xdm_login_hack_hack_hack_hack(std::vector *cmds, int argc, const char * const *argv); bool fish_openSUSE_dbus_hack_hack_hack_hack(std::vector *args); diff --git a/fish.cpp b/fish.cpp index abefa8985..8001f3764 100644 --- a/fish.cpp +++ b/fish.cpp @@ -382,36 +382,6 @@ static int fish_parse_opt(int argc, char **argv, std::vector *out_c return my_optind; } -/** - Calls a bunch of init functions, parses the init files and then - parses commands from stdin or files, depending on arguments -*/ - -static wcstring full_escape(const wchar_t *in) -{ - wcstring out; - for (; *in; in++) - { - if (*in < 32) - { - append_format(out, L"\\x%.2x", *in); - } - else if (*in < 128) - { - out.push_back(*in); - } - else if (*in < 65536) - { - append_format(out, L"\\u%.4x", *in); - } - else - { - append_format(out, L"\\U%.8x", *in); - } - } - return out; -} - extern int g_fork_count; int main(int argc, char **argv) { diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index 6f6c83ba1..93293e50c 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -1209,6 +1209,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.7; SDKROOT = macosx; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/osx/**"; + WARNING_CFLAGS = "-Wall"; }; name = "Release_C++11"; }; @@ -1361,6 +1362,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/osx/**"; + WARNING_CFLAGS = "-Wall"; }; name = Debug; }; @@ -1385,6 +1387,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.6; SDKROOT = macosx; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/osx/**"; + WARNING_CFLAGS = "-Wall"; }; name = Release; }; diff --git a/fish_tests.cpp b/fish_tests.cpp index 85ead75ac..739b47b02 100644 --- a/fish_tests.cpp +++ b/fish_tests.cpp @@ -62,6 +62,7 @@ #include "highlight.h" #include "parse_tree.h" #include "parse_exec.h" +#include "parse_util.h" /** The number of tests to run @@ -529,6 +530,28 @@ static void test_parser() } } +static void test_utils() +{ + say(L"Testing utils"); + const wchar_t *a = L"echo (echo (echo hi"; + + const wchar_t *begin = NULL, *end = NULL; + parse_util_cmdsubst_extent(a, 0, &begin, &end); + if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + parse_util_cmdsubst_extent(a, 1, &begin, &end); + if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + parse_util_cmdsubst_extent(a, 2, &begin, &end); + if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + parse_util_cmdsubst_extent(a, 3, &begin, &end); + if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + + parse_util_cmdsubst_extent(a, 8, &begin, &end); + if (begin != a + wcslen(L"echo (")) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + + parse_util_cmdsubst_extent(a, 17, &begin, &end); + if (begin != a + wcslen(L"echo (echo (")) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); +} + class lru_node_test_t : public lru_node_t { public: @@ -680,6 +703,65 @@ static void test_fuzzy_match(void) if (string_fuzzy_match_string(L"BB", L"ALPHA!").type != fuzzy_match_none) err(L"test_fuzzy_match failed on line %ld", __LINE__); } +static void test_abbreviations(void) +{ + say(L"Testing abbreviations"); + + const wchar_t *abbreviations = + L"gc=git checkout" ARRAY_SEP_STR + L"foo=" ARRAY_SEP_STR + L"gc=something else" ARRAY_SEP_STR + L"=" ARRAY_SEP_STR + L"=foo" ARRAY_SEP_STR + L"foo" ARRAY_SEP_STR + L"foo=bar"; + + env_push(true); + + int ret = env_set(USER_ABBREVIATIONS_VARIABLE_NAME, abbreviations, ENV_LOCAL); + if (ret != 0) err(L"Unable to set abbreviation variable"); + + wcstring result; + if (expand_abbreviation(L"", &result)) err(L"Unexpected success with empty abbreviation"); + if (expand_abbreviation(L"nothing", &result)) err(L"Unexpected success with missing abbreviation"); + + if (! expand_abbreviation(L"gc", &result)) err(L"Unexpected failure with gc abbreviation"); + if (result != L"git checkout") err(L"Wrong abbreviation result for gc"); + result.clear(); + + if (! expand_abbreviation(L"foo", &result)) err(L"Unexpected failure with foo abbreviation"); + if (result != L"bar") err(L"Wrong abbreviation result for foo"); + + bool expanded; + expanded = reader_expand_abbreviation_in_command(L"just a command", 3, &result); + if (expanded) err(L"Command wrongly expanded on line %ld", (long)__LINE__); + expanded = reader_expand_abbreviation_in_command(L"gc somebranch", 0, &result); + if (! expanded) err(L"Command not expanded on line %ld", (long)__LINE__); + + expanded = reader_expand_abbreviation_in_command(L"gc somebranch", wcslen(L"gc"), &result); + if (! expanded) err(L"gc not expanded"); + if (result != L"git checkout somebranch") err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); + + expanded = reader_expand_abbreviation_in_command(L"echo hi ; gc somebranch", wcslen(L"echo hi ; g"), &result); + if (! expanded) err(L"gc not expanded on line %ld", (long)__LINE__); + if (result != L"echo hi ; git checkout somebranch") err(L"gc incorrectly expanded on line %ld", (long)__LINE__); + + expanded = reader_expand_abbreviation_in_command(L"echo (echo (echo (echo (gc ", wcslen(L"echo (echo (echo (echo (gc"), &result); + if (! expanded) err(L"gc not expanded on line %ld", (long)__LINE__); + if (result != L"echo (echo (echo (echo (git checkout ") err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); + + /* if commands should be expanded */ + expanded = reader_expand_abbreviation_in_command(L"if gc", wcslen(L"if gc"), &result); + if (! expanded) err(L"gc not expanded on line %ld", (long)__LINE__); + if (result != L"if git checkout") err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); + + /* others should not be */ + expanded = reader_expand_abbreviation_in_command(L"of gc", wcslen(L"of gc"), &result); + if (expanded) err(L"gc incorrectly expanded on line %ld", (long)__LINE__); + + env_pop(); +} + /** Test path functions */ static void test_path() { @@ -1771,9 +1853,11 @@ int main(int argc, char **argv) test_tok(); test_fork(); test_parser(); + test_utils(); test_lru(); test_expand(); test_fuzzy_match(); + test_abbreviations(); test_test(); test_path(); test_word_motion(); diff --git a/highlight.cpp b/highlight.cpp index 8ea0989e6..606604386 100644 --- a/highlight.cpp +++ b/highlight.cpp @@ -1106,6 +1106,9 @@ static void tokenize(const wchar_t * const buff, std::vector &color, const if (! is_cmd && use_function) is_cmd = function_exists_no_autoload(cmd, vars); + if (! is_cmd) + is_cmd = expand_abbreviation(cmd, NULL); + /* Moving on to expensive tests */ @@ -1319,7 +1322,7 @@ void highlight_shell(const wcstring &buff, std::vector &color, size_t pos, std::fill(color.begin(), color.end(), -1); - /* Do something sucky and get the current working directory on this background thread. This should really be passed in. Note that we also need this as a vector (of one directory). */ + /* Do something sucky and get the current working directory on this background thread. This should really be passed in. */ const wcstring working_directory = env_get_pwd_slash(); /* Tokenize the string */ @@ -1335,7 +1338,7 @@ void highlight_shell(const wcstring &buff, std::vector &color, size_t pos, { wchar_t *begin, *end; - if (parse_util_locate_cmdsubst(subpos, &begin, &end, 1) <= 0) + if (parse_util_locate_cmdsubst(subpos, &begin, &end, true) <= 0) { break; } diff --git a/parse_util.cpp b/parse_util.cpp index a7614f0c2..5e6f4459b 100644 --- a/parse_util.cpp +++ b/parse_util.cpp @@ -153,10 +153,7 @@ size_t parse_util_get_offset(const wcstring &str, int line, long line_offset) } -int parse_util_locate_cmdsubst(const wchar_t *in, - wchar_t **begin, - wchar_t **end, - int allow_incomplete) +int parse_util_locate_cmdsubst(const wchar_t *in, wchar_t **begin, wchar_t **end, bool allow_incomplete) { wchar_t *pos; wchar_t prev=0; @@ -243,73 +240,57 @@ int parse_util_locate_cmdsubst(const wchar_t *in, return 1; } - -void parse_util_cmdsubst_extent(const wchar_t *buff, - size_t cursor_pos, - const wchar_t **a, - const wchar_t **b) +void parse_util_cmdsubst_extent(const wchar_t *buff, size_t cursor_pos, const wchar_t **a, const wchar_t **b) { - wchar_t *begin, *end; - wchar_t *pos; - const wchar_t *cursor = buff + cursor_pos; + const wchar_t * const cursor = buff + cursor_pos; CHECK(buff,); - - if (a) + + const size_t bufflen = wcslen(buff); + assert(cursor_pos <= bufflen); + + /* ap and bp are the beginning and end of the tightest command substitition found so far */ + const wchar_t *ap = buff, *bp = buff + bufflen; + const wchar_t *pos = buff; + for (;;) { - *a = (wchar_t *)buff; - } - - if (b) - { - *b = (wchar_t *)buff+wcslen(buff); - } - - pos = (wchar_t *)buff; - - while (1) - { - if (parse_util_locate_cmdsubst(pos, - &begin, - &end, - 1) <= 0) + wchar_t *begin = NULL, *end = NULL; + if (parse_util_locate_cmdsubst(pos, &begin, &end, true) <= 0) { - /* - No subshell found - */ + /* No subshell found, all done */ break; } - - if (!end) + + /* Intrepret NULL to mean the end */ + if (end == NULL) { - end = (wchar_t *)buff + wcslen(buff); + end = const_cast(buff) + bufflen; } - - if ((begin < cursor) && (end >= cursor)) + + if (begin < cursor && end >= cursor) { + /* This command substitution surrounds the cursor, so it's a tighter fit */ begin++; - - if (a) - { - *a = begin; - } - - if (b) - { - *b = end; - } - - break; + ap = begin; + bp = end; + pos = begin + 1; } - - if (!*end) + else if (begin >= cursor) { + /* This command substitution starts at or after the cursor. Since it was the first command substitution in the string, we're done. */ break; } - - pos = end+1; + else + { + /* This command substitution ends before the cursor. Skip it. */ + assert(end < cursor); + pos = end + 1; + assert(pos <= buff + bufflen); + } } - + + if (a != NULL) *a = ap; + if (b != NULL) *b = bp; } /** @@ -432,7 +413,6 @@ void parse_util_token_extent(const wchar_t *buff, { const wchar_t *begin, *end; long pos; - wchar_t *buffcpy; const wchar_t *a = NULL, *b = NULL, *pa = NULL, *pb = NULL; @@ -459,14 +439,9 @@ void parse_util_token_extent(const wchar_t *buff, assert(end >= begin); assert(end <= (buff+wcslen(buff))); - buffcpy = wcsndup(begin, end-begin); + const wcstring buffcpy = wcstring(begin, end-begin); - if (!buffcpy) - { - DIE_MEM(); - } - - tokenizer_t tok(buffcpy, TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS); + tokenizer_t tok(buffcpy.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS); for (; tok_has_next(&tok); tok_next(&tok)) { size_t tok_begin = tok_get_pos(&tok); @@ -512,8 +487,6 @@ void parse_util_token_extent(const wchar_t *buff, } } - free(buffcpy); - if (tok_begin) { *tok_begin = a; @@ -568,7 +541,6 @@ void parse_util_set_argv(const wchar_t * const *argv, const wcstring_list_t &nam { const wchar_t * const *arg; size_t i; - for (i=0, arg=argv; i < named_arguments.size(); i++) { env_set(named_arguments.at(i).c_str(), *arg, ENV_LOCAL); @@ -576,10 +548,7 @@ void parse_util_set_argv(const wchar_t * const *argv, const wcstring_list_t &nam if (*arg) arg++; } - - } - } wchar_t *parse_util_unescape_wildcards(const wchar_t *str) @@ -679,7 +648,7 @@ static wchar_t get_quote(const wchar_t *cmd, size_t len) { const wchar_t *end = quote_end(&cmd[i]); //fwprintf( stderr, L"Jump %d\n", end-cmd ); - if ((end == 0) || (!*end) || (end-cmd > len)) + if ((end == 0) || (!*end) || (end > cmd + len)) { res = cmd[i]; break; diff --git a/parse_util.h b/parse_util.h index b8e370f76..24147e180 100644 --- a/parse_util.h +++ b/parse_util.h @@ -18,14 +18,14 @@ \param in the string to search for subshells \param begin the starting paranthesis of the subshell \param end the ending paranthesis of the subshell - \param flags set this variable to ACCEPT_INCOMPLETE if in tab_completion mode + \param accept_incomplete whether to permit missing closing parenthesis \return -1 on syntax error, 0 if no subshells exist and 1 on sucess */ int parse_util_locate_cmdsubst(const wchar_t *in, wchar_t **begin, wchar_t **end, - int flags); + bool accept_incomplete); /** Find the beginning and end of the command substitution under the diff --git a/parser.cpp b/parser.cpp index d2b0f0690..f9d4ba5ac 100644 --- a/parser.cpp +++ b/parser.cpp @@ -2025,6 +2025,7 @@ int parser_t::parse_job(process_t *p, for this, used by other shells like bash and zsh). */ + if (wcschr(cmd, L'=')) { wchar_t *cpy = wcsdup(cmd); @@ -2074,9 +2075,15 @@ int parser_t::parse_job(process_t *p, } else { - debug(0, - _(L"Unknown command '%ls'"), - cmd?cmd:L"UNKNOWN"); + /* + Handle unrecognized commands with standard + command not found handler that can make better + error messages + */ + + wcstring_list_t event_args; + event_args.push_back(args.at(0).completion); + event_fire_generic(L"fish_command_not_found", &event_args); } tmp = current_tokenizer_pos; @@ -2088,9 +2095,6 @@ int parser_t::parse_job(process_t *p, job_set_flag(j, JOB_SKIP, 1); - wcstring_list_t event_args; - event_args.push_back(args.at(0).completion); - event_fire_generic(L"fish_command_not_found", &event_args); proc_set_last_status(err==ENOENT?STATUS_UNKNOWN_COMMAND:STATUS_NOT_EXECUTABLE); } } @@ -2714,7 +2718,7 @@ int parser_t::parser_test_argument(const wchar_t *arg, wcstring *out, const wcha switch (parse_util_locate_cmdsubst(arg_cpy, ¶n_begin, ¶n_end, - 0)) + false)) { case -1: err=1; diff --git a/reader.cpp b/reader.cpp index 9b2eea14e..8febb7994 100644 --- a/reader.cpp +++ b/reader.cpp @@ -97,8 +97,8 @@ commence. #include "iothread.h" #include "intern.h" #include "path.h" - #include "parse_util.h" +#include "parser_keywords.h" /** Maximum length of prefix string when printing completion @@ -182,6 +182,8 @@ static pthread_key_t generation_count_key; /* A color is an int */ typedef int color_t; +static void set_command_line_and_position(const wcstring &new_str, size_t pos); + /** A struct describing the state of the interactive reader. These states can be stacked, in case reader_readline() calls are @@ -203,6 +205,9 @@ public: /** When backspacing, we temporarily suppress autosuggestions */ bool suppress_autosuggestion; + /** Whether abbreviations are expanded */ + bool expand_abbreviations; + /** The representation of the current screen contents */ screen_t screen; @@ -244,6 +249,9 @@ public: /** Do what we need to do whenever our command line changes */ void command_line_changed(void); + /** Expand abbreviations at the current cursor position. */ + bool expand_abbreviation_as_necessary(); + /** The current position of the cursor in buff. */ size_t buff_pos; @@ -326,6 +334,7 @@ public: reader_data_t() : allow_autosuggestion(0), suppress_autosuggestion(0), + expand_abbreviations(0), history(0), token_history_pos(0), search_pos(0), @@ -635,6 +644,159 @@ void reader_data_t::command_line_changed() s_generation_count++; } +/* Expand abbreviations at the given cursor position. Does NOT inspect 'data'. */ +bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t cursor_pos, wcstring *output) +{ + /* See if we are at "command position". Get the surrounding command substitution, and get the extent of the first token. */ + const wchar_t * const buff = cmdline.c_str(); + const wchar_t *cmdsub_begin = NULL, *cmdsub_end = NULL; + parse_util_cmdsubst_extent(buff, cursor_pos, &cmdsub_begin, &cmdsub_end); + assert(cmdsub_begin != NULL && cmdsub_begin >= buff); + assert(cmdsub_end != NULL && cmdsub_end >= cmdsub_begin); + + /* Determine the offset of this command substitution */ + const size_t subcmd_offset = cmdsub_begin - buff; + + const wcstring subcmd = wcstring(cmdsub_begin, cmdsub_end - cmdsub_begin); + const wchar_t *subcmd_cstr = subcmd.c_str(); + + /* Get the token before the cursor */ + const wchar_t *subcmd_tok_begin = NULL, *subcmd_tok_end = NULL; + assert(cursor_pos >= subcmd_offset); + size_t subcmd_cursor_pos = cursor_pos - subcmd_offset; + parse_util_token_extent(subcmd_cstr, subcmd_cursor_pos, &subcmd_tok_begin, &subcmd_tok_end, NULL, NULL); + + /* Compute the offset of the token before the cursor within the subcmd */ + assert(subcmd_tok_begin >= subcmd_cstr); + assert(subcmd_tok_end >= subcmd_tok_begin); + const size_t subcmd_tok_begin_offset = subcmd_tok_begin - subcmd_cstr; + const size_t subcmd_tok_length = subcmd_tok_end - subcmd_tok_begin; + + /* Now parse the subcmd, looking for commands */ + bool had_cmd = false, previous_token_is_cmd = false; + tokenizer_t tok(subcmd_cstr, TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS); + for (; tok_has_next(&tok); tok_next(&tok)) + { + size_t tok_pos = static_cast(tok_get_pos(&tok)); + if (tok_pos > subcmd_tok_begin_offset) + { + /* We've passed the token we're interested in */ + break; + } + + int last_type = tok_last_type(&tok); + + switch (last_type) + { + case TOK_STRING: + { + if (had_cmd) + { + /* Parameter to the command. */ + } + else + { + const wcstring potential_cmd = tok_last(&tok); + if (parser_keywords_is_subcommand(potential_cmd)) + { + if (potential_cmd == L"command" || potential_cmd == L"builtin") + { + /* 'command' and 'builtin' defeat abbreviation expansion. Skip this command. */ + had_cmd = true; + } + else + { + /* Other subcommand. Pretend it doesn't exist so that we can expand the following command */ + had_cmd = false; + } + } + else + { + /* It's a normal command */ + had_cmd = true; + if (tok_pos == subcmd_tok_begin_offset) + { + /* This is the token we care about! */ + previous_token_is_cmd = true; + } + } + } + break; + } + + case TOK_REDIRECT_NOCLOB: + case TOK_REDIRECT_OUT: + case TOK_REDIRECT_IN: + case TOK_REDIRECT_APPEND: + case TOK_REDIRECT_FD: + { + if (!had_cmd) + { + break; + } + tok_next(&tok); + break; + } + + case TOK_PIPE: + case TOK_BACKGROUND: + case TOK_END: + { + had_cmd = false; + break; + } + + case TOK_COMMENT: + case TOK_ERROR: + default: + { + break; + } + } + } + + bool result = false; + if (previous_token_is_cmd) + { + /* The token is a command. Try expanding it as an abbreviation. */ + const wcstring token = wcstring(subcmd, subcmd_tok_begin_offset, subcmd_tok_length); + wcstring abbreviation; + if (expand_abbreviation(token, &abbreviation)) + { + /* There was an abbreviation! Replace the token in the full command. Maintain the relative position of the cursor. */ + if (output != NULL) + { + size_t cmd_tok_begin_offset = subcmd_tok_begin_offset + subcmd_offset; + output->assign(cmdline); + output->replace(cmd_tok_begin_offset, subcmd_tok_length, abbreviation); + } + result = true; + } + } + return result; +} + +/* Expand abbreviations. This may change the command line but does NOT repaint it. This is to allow the caller to coalesce repaints. */ +bool reader_data_t::expand_abbreviation_as_necessary() +{ + bool result = false; + if (this->expand_abbreviations) + { + /* Try expanding abbreviations */ + wcstring new_cmdline; + if (reader_expand_abbreviation_in_command(this->command_line, this->buff_pos, &new_cmdline)) + { + /* We expanded an abbreviation! The cursor moves by the difference in the command line lengths. */ + size_t new_buff_pos = this->buff_pos + new_cmdline.size() - this->command_line.size(); + + this->command_line.swap(new_cmdline); + data->buff_pos = new_buff_pos; + data->command_line_changed(); + result = true; + } + } + return result; +} /** Sorts and remove any duplicate completions in the list. */ static void sort_and_make_unique(std::vector &l) @@ -1980,8 +2142,7 @@ void reader_sanity_check() } /** - Set the specified string from the history as the current buffer. Do - not modify prefix_width. + Set the specified string as the current buffer. */ static void set_command_line_and_position(const wcstring &new_str, size_t pos) { @@ -2462,6 +2623,11 @@ void reader_set_allow_autosuggesting(bool flag) data->allow_autosuggestion = flag; } +void reader_set_expand_abbreviations(bool flag) +{ + data->expand_abbreviations = flag; +} + void reader_set_complete_function(complete_function_t f) { data->complete_func = f; @@ -2712,6 +2878,7 @@ static int read_i(void) reader_set_highlight_function(&highlight_shell); reader_set_test_function(&reader_shell_test); reader_set_allow_autosuggesting(true); + reader_set_expand_abbreviations(true); reader_import_history_if_necessary(); parser_t &parser = parser_t::principal_parser(); @@ -2851,7 +3018,6 @@ const wchar_t *reader_readline(void) data->search_buff.clear(); data->search_mode = NO_SEARCH; - exec_prompt(); reader_super_highlight_me_plenty(data->buff_pos); @@ -3261,7 +3427,21 @@ const wchar_t *reader_readline(void) } } - switch (data->test_func(data->command_line.c_str())) + /* See if this command is valid */ + int command_test_result = data->test_func(data->command_line.c_str()); + if (command_test_result == 0 || command_test_result == PARSER_TEST_INCOMPLETE) + { + /* This command is valid, but an abbreviation may make it invalid. If so, we will have to test again. */ + bool abbreviation_expanded = data->expand_abbreviation_as_necessary(); + if (abbreviation_expanded) + { + /* It's our reponsibility to rehighlight and repaint. But everything we do below triggers a repaint. */ + reader_super_highlight_me_plenty(data->buff_pos); + command_test_result = data->test_func(data->command_line.c_str()); + } + } + + switch (command_test_result) { case 0: @@ -3597,9 +3777,14 @@ const wchar_t *reader_readline(void) /* Other, if a normal character, we add it to the command */ default: { - if ((!wchar_private(c)) && (((c>31) || (c==L'\n'))&& (c != 127))) { + /* Expand abbreviations on space */ + if (c == L' ') + { + data->expand_abbreviation_as_necessary(); + } + /* Regular character */ insert_char(c); } @@ -3633,10 +3818,7 @@ const wchar_t *reader_readline(void) } writestr(L"\n"); - /* - if( comp ) - halloc_free( comp ); - */ + if (!reader_exit_forced()) { if (tcsetattr(0,TCSANOW,&old_modes)) /* return to previous mode */ diff --git a/reader.h b/reader.h index dfe8d1ed5..0ead2aa40 100644 --- a/reader.h +++ b/reader.h @@ -204,6 +204,10 @@ void reader_set_right_prompt(const wcstring &prompt); /** Sets whether autosuggesting is allowed. */ void reader_set_allow_autosuggesting(bool flag); +/** Sets whether abbreviation expansion is performed. */ +void reader_set_expand_abbreviations(bool flag); + + /** Sets whether the reader should exit on ^C. */ void reader_set_exit_on_interrupt(bool flag); @@ -243,6 +247,9 @@ int reader_search_mode(); /* Given a command line and an autosuggestion, return the string that gets shown to the user. Exposed for testing purposes only. */ wcstring combine_command_and_autosuggestion(const wcstring &cmdline, const wcstring &autosuggestion); +/* Expand abbreviations at the given cursor position. Exposed for testing purposes only. */ +bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t cursor_pos, wcstring *output); + /* Apply a completion string. Exposed for testing only. */ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flags_t flags, const wcstring &command_line, size_t *inout_cursor_pos, bool append_only); diff --git a/share/completions/canto.fish b/share/completions/canto.fish new file mode 100644 index 000000000..7c487fb75 --- /dev/null +++ b/share/completions/canto.fish @@ -0,0 +1,35 @@ + +function __fish_canto_using_command + set cmd (commandline -opc) + if [ (count $cmd) -gt 1 ] + if [ $argv[1] = $cmd[2] ] + return 0 + end + if [ count $argv -gt 2 ] + if [ $argv[2] = $cmd[2] ] + return 0 + end + end + end + return 1 +end + + +complete -f -c canto -s h -l help -d 'Show Help' +complete -f -c canto -s v -l version -d 'Show version' +complete -f -c canto -s u -l update -d 'Update before running' +complete -f -c canto -s l -l list -d 'List feeds' +complete -f -c canto -s a -l checkall -d 'Show number of new items' + +complete -f -c canto -s n -l checknew -d 'Show number of new items for feed' +complete -f -c canto -n '__fish_canto_using_command -l --checknew' -d 'Feed' -a '(command canto -l)' + +complete -c canto -s o -l opml -d 'Print conf as OPML' +complete -c cnato -s i -l import -d 'Import from OPML' +complete -f -c canto -s r -l url -d 'Add feed' + +complete -c canto -s D -l dir -d 'Set configuration directory' +complete -c canto -s C -l conf -d 'Set configuration file' +complete -c canto -s L -l log -d 'Set client log file' +complete -c canto -s F -l fdir -d 'Set feed directory' +complete -c canto -s S -l sdir -d 'Set script directory' diff --git a/share/completions/darcs.fish b/share/completions/darcs.fish index 65fc9179a..9a826da0f 100644 --- a/share/completions/darcs.fish +++ b/share/completions/darcs.fish @@ -41,7 +41,7 @@ complete -c darcs -n '__fish_use_subcommand' -x -a obliterate --description 'Del complete -c darcs -n '__fish_use_subcommand' -x -a rollback --description 'Record a new patch reversing some recorded changes' complete -c darcs -n '__fish_use_subcommand' -x -a push --description 'Copy and apply patches from this repository to another one' complete -c darcs -n '__fish_use_subcommand' -x -a send --description 'Send by email a bundle of one or more patches' -complete -c darcs -n '__fish_use_subcommand' -x -a apply --description 'Apply a patch bundle created by `darcs send'' +complete -c darcs -n '__fish_use_subcommand' -x -a apply --description 'Apply a patch bundle created by `darcs send\'' complete -c darcs -n '__fish_use_subcommand' -x -a get --description 'Create a local copy of a repository' complete -c darcs -n '__fish_use_subcommand' -x -a put --description 'Makes a copy of the repository' complete -c darcs -n '__fish_use_subcommand' -x -a initialize --description 'Make the current directory a repository' diff --git a/share/functions/__fish_complete_ftp.fish b/share/functions/__fish_complete_ftp.fish index bc69acaf3..92ff0b088 100644 --- a/share/functions/__fish_complete_ftp.fish +++ b/share/functions/__fish_complete_ftp.fish @@ -1,22 +1,14 @@ function __fish_complete_ftp -d 'Complete ftp, pftp' --argument-names ftp complete -c $ftp -xa "(__fish_print_hostnames)" -d 'Hostname' -tname' - complete -c $ftp -s 4 -d 'Use only IPv4 to contact any host' - host' - complete -c $ftp -s 6 -d 'Use IPv6 only' - only' - complete -c $ftp -s p -d 'Use passive mode for data transfers' -sfers' - complete -c $ftp -s A -d 'Use active mode for data transfers' -sfers' - complete -c $ftp -s i -d 'Turn off interactive prompting during multiple file transfers' + complete -c $ftp -s 4 -d 'Use only IPv4 to contact any host' + complete -c $ftp -s 6 -d 'Use IPv6 only' + complete -c $ftp -s p -d 'Use passive mode for data transfers' + complete -c $ftp -s A -d 'Use active mode for data transfers' + complete -c $ftp -s i -d 'Turn off interactive prompting during multiple file transfers.' complete -c $ftp -s n -d 'Restrain ftp from attempting "auto-login" upon initial connection' complete -c $ftp -s e -d 'Disable command editing and history support' complete -c $ftp -s g -d 'Disable file name globbing' -Disable file name globbing' -m -d 'Do not explicitly bind data and control channels to same interface' -channels to same interface' -v -d 'Verbose. Show all server responses and data transfer stats' -es and data transfer stats' -d -d 'Enable debugging' + complete -c $ftp -s m -d 'Do not explicitly bind data and control channels to same interface' + complete -c $ftp -s v -d 'Verbose. Show all server responses and data transfer stats' + complete -c $ftp -s d -d 'Enable debugging' end diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 814fff198..d7961861f 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -218,21 +218,23 @@ function __fish_config_interactive -d "Initializations that should be performed function fish_command_not_found_setup --on-event fish_command_not_found # Remove fish_command_not_found_setup so we only execute this once functions --erase fish_command_not_found_setup - + # First check in /usr/lib, this is where modern Ubuntus place this command if test -f /usr/lib/command-not-found function fish_command_not_found_handler --on-event fish_command_not_found /usr/lib/command-not-found $argv end - fish_command_not_found_handler $argv + # Ubuntu Feisty places this command in the regular path instead + else if type -p command-not-found > /dev/null 2> /dev/null + function fish_command_not_found_handler --on-event fish_command_not_found + command-not-found $argv + end + # Use standard fish command not found handler otherwise else - # Ubuntu Feisty places this command in the regular path instead - if type -p command-not-found > /dev/null 2> /dev/null - function fish_command_not_found_handler --on-event fish_command_not_found - command-not-found $argv - end - fish_command_not_found_handler $argv + function fish_command_not_found_handler --on-event fish_command_not_found + echo fish: Unknown command "'$argv'" >&2 end end + fish_command_not_found_handler $argv end end diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index 5c528beae..ea4df57a4 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -75,6 +75,8 @@ # 'upstream_equal', 'upstream_behind', 'upstream_ahead', and # 'upstream_diverged'. +set -g ___fish_git_prompt_status_order stagedstate invalidstate dirtystate untrackedfiles + function __fish_git_prompt_show_upstream --description "Helper function for __fish_git_prompt" # Ask git-config for some config options set -l svn_remote @@ -82,6 +84,7 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi set -l upstream git set -l legacy set -l verbose + set -l informative set -l svn_url_pattern set -l show_upstream $__fish_git_prompt_showupstream git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showUpstream)$' ^/dev/null | tr '\0\n' '\n ' | while read -l key value @@ -109,6 +112,8 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi set upstream $option case verbose set verbose 1 + case informative + set informative 1 case legacy set legacy 1 end @@ -176,30 +181,42 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi end # calculate the result - if test -z "$verbose" - switch "$count" - case '' # no upstream - case "0 0" # equal to upstream - echo $___fish_git_prompt_char_upstream_equal - case "0 *" # ahead of upstream - echo $___fish_git_prompt_char_upstream_ahead - case "* 0" # behind upstream - echo $___fish_git_prompt_char_upstream_behind - case '*' # diverged from upstream - echo $___fish_git_prompt_char_upstream_diverged - end - else + if test -n "$verbose" echo $count | read -l behind ahead switch "$count" case '' # no upstream case "0 0" # equal to upstream - echo " $___fish_git_prompt_char_upstream_equal" + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_equal" case "0 *" # ahead of upstream - echo " $___fish_git_prompt_char_upstream_ahead$ahead" + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_ahead$ahead" case "* 0" # behind upstream - echo " $___fish_git_prompt_char_upstream_behind$behind" + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_behind$behind" case '*' # diverged from upstream - echo " $__fish_git_prompt_char_upstream_diverged$ahead-$behind" + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_diverged$ahead-$behind" + end + else if test -n informative + echo $count | read -l behind ahead + switch "$count" + case '' # no upstream + case "0 0" # equal to upstream + case "0 *" # ahead of upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_ahead$ahead" + case "* 0" # behind upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_behind$behind" + case '*' # diverged from upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_ahead$ahead$___fish_git_prompt_char_upstream_behind$behind" + end + else + switch "$count" + case '' # no upstream + case "0 0" # equal to upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_equal" + case "0 *" # ahead of upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_ahead$ahead" + case "* 0" # behind upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_behind$behind" + case '*' # diverged from upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_diverged$ahead-$behind" end end end @@ -216,32 +233,39 @@ function __fish_git_prompt --description "Prompt function for Git" set -l u #untracked set -l c (__fish_git_prompt_current_branch_bare) set -l p #upstream + set -l informative_status __fish_git_prompt_validate_chars if test "true" = (git rev-parse --is-inside-work-tree ^/dev/null) - if test -n "$__fish_git_prompt_showdirtystate" - set -l config (git config --bool bash.showDirtyState) - if test "$config" != "false" - set w (__fish_git_prompt_dirty) - set i (__fish_git_prompt_staged) + + if test -n "$__fish_git_prompt_show_informative_status" + set informative_status "|"(__fish_git_prompt_informative_status) + else + if test -n "$__fish_git_prompt_showdirtystate" + set -l config (git config --bool bash.showDirtyState) + if test "$config" != "false" + set w (__fish_git_prompt_dirty) + set i (__fish_git_prompt_staged) + end end - end - if test -n "$__fish_git_prompt_showstashstate" - git rev-parse --verify refs/stash >/dev/null ^&1; and set s $___fish_git_prompt_char_stashstate - end + if test -n "$__fish_git_prompt_showstashstate" + git rev-parse --verify refs/stash >/dev/null ^&1; and set s $___fish_git_prompt_char_stashstate + end - if test -n "$__fish_git_prompt_showuntrackedfiles" - set -l files (git ls-files --others --exclude-standard) - if test -n "$files" - set u $___fish_git_prompt_char_untrackedfiles + if test -n "$__fish_git_prompt_showuntrackedfiles" + set -l files (git ls-files --others --exclude-standard) + if test -n "$files" + set u $___fish_git_prompt_char_untrackedfiles + end end end if test -n "$__fish_git_prompt_showupstream" set p (__fish_git_prompt_show_upstream) end + end __fish_git_prompt_validate_colors @@ -282,7 +306,7 @@ function __fish_git_prompt --description "Prompt function for Git" set format " (%s)" end - printf "%s$format%s" "$___fish_git_prompt_color_prefix" "$___fish_git_prompt_color_prefix_done$c$b$f$r$p$___fish_git_prompt_color_suffix" "$___git_ps_color_suffix_done" + printf "%s$format%s" "$___fish_git_prompt_color_prefix" "$___fish_git_prompt_color_prefix_done$c$b$f$r$p$informative_status$___fish_git_prompt_color_suffix" "$___git_ps_color_suffix_done" end ### helper functions @@ -310,6 +334,46 @@ function __fish_git_prompt_dirty --description "__fish_git_prompt helper, tells echo $dirty end +function __fish_git_prompt_informative_status + + set -l changedFiles (git diff --name-status | cut -c 1-2) + set -l stagedFiles (git diff --staged --name-status | cut -c 1-2) + + set -l dirtystate (math (count $changedFiles) - (count (echo $changedFiles | grep "U"))) + set -l invalidstate (count (echo $stagedFiles | grep "U")) + set -l stagedstate (math (count $stagedFiles) - $invalidstate) + set -l untrackedfiles (count (git ls-files --others --exclude-standard)) + + set -l info + + if [ (math $dirtystate + $invalidstate + $stagedstate + $untrackedfiles) = 0 ] + set info $___fish_git_prompt_color_cleanstate$___fish_git_prompt_char_cleanstate$___fish_git_prompt_color_cleanstate_done + else + for i in $___fish_git_prompt_status_order + if [ $$i != "0" ] + set -l color_var ___fish_git_prompt_color_$i + set -l color_done_var ___fish_git_prompt_color_$i + set -l symbol_var ___fish_git_prompt_char_$i + + set -l color $$color_var + set -l color_done $$color_done_var + set -l symbol $$symbol_var + + set -l count + + if not set -q __fish_git_prompt_hide_$i + set count $$i + end + + set info "$info$color$symbol$count$color_done" + end + end + end + + echo $info + +end + function __fish_git_prompt_current_branch_bare --description "__fish_git_prompt helper, tells wheter or not the current branch is bare" set -l bare @@ -388,145 +452,81 @@ function __fish_git_prompt_git_dir --description "__fish_git_prompt helper, retu echo (git rev-parse --git-dir ^/dev/null) end +function __fish_git_prompt_set_char + set -l user_variable_name "$argv[1]" + set -l char $argv[2] + set -l user_variable $$user_variable_name + + set -l variable _$user_variable_name + set -l variable_done "$variable"_done + + if not set -q $variable + set -g $variable (set -q $user_variable_name; and echo $user_variable; or echo $char) + end + +end + function __fish_git_prompt_validate_chars --description "__fish_git_prompt helper, checks char variables" - if not set -q ___fish_git_prompt_char_dirtystate - set -g ___fish_git_prompt_char_dirtystate (set -q __fish_git_prompt_char_dirtystate; and echo $__fish_git_prompt_char_dirtystate; or echo '*') + + __fish_git_prompt_set_char __fish_git_prompt_char_cleanstate '.' + __fish_git_prompt_set_char __fish_git_prompt_char_dirtystate '*' + __fish_git_prompt_set_char __fish_git_prompt_char_stagedstate '+' + __fish_git_prompt_set_char __fish_git_prompt_char_invalidstate '#' + __fish_git_prompt_set_char __fish_git_prompt_char_stashstate '$' + __fish_git_prompt_set_char __fish_git_prompt_char_untrackedfiles '%' + __fish_git_prompt_set_char __fish_git_prompt_char_upstream_equal '=' + __fish_git_prompt_set_char __fish_git_prompt_char_upstream_behind '<' + __fish_git_prompt_set_char __fish_git_prompt_char_upstream_ahead '>' + __fish_git_prompt_set_char __fish_git_prompt_char_upstream_diverged '<>' + __fish_git_prompt_set_char __fish_git_prompt_char_upstream_prefix ' ' + +end + +function __fish_git_prompt_set_color + set -l user_variable_name "$argv[1]" + set -l user_variable $$user_variable_name + set -l user_variable_bright + + if test (count $user_variable) -eq 2 + set user_variable_bright $user_variable[2] + set user_variable $user_variable[1] end - if not set -q ___fish_git_prompt_char_stagedstate - set -g ___fish_git_prompt_char_stagedstate (set -q __fish_git_prompt_char_stagedstate; and echo $__fish_git_prompt_char_stagedstate; or echo '+') - end - if not set -q ___fish_git_prompt_char_invalidstate - set -g ___fish_git_prompt_char_invalidstate (set -q __fish_git_prompt_char_invalidstate; and echo $__fish_git_prompt_char_invalidstate; or echo '#') - end - if not set -q ___fish_git_prompt_char_stashstate - set -g ___fish_git_prompt_char_stashstate (set -q __fish_git_prompt_char_stashstate; and echo $__fish_git_prompt_char_stashstate; or echo '$') - end - if not set -q ___fish_git_prompt_char_untrackedfiles - set -g ___fish_git_prompt_char_untrackedfiles (set -q __fish_git_prompt_char_untrackedfiles; and echo $__fish_git_prompt_char_untrackedfiles; or echo '%') - end - if not set -q ___fish_git_prompt_char_upstream_equal - set -g ___fish_git_prompt_char_upstream_equal (set -q __fish_git_prompt_char_upstream_equal; and echo $__fish_git_prompt_char_upstream_equal; or echo '=') - end - if not set -q ___fish_git_prompt_char_upstream_behind - set -g ___fish_git_prompt_char_upstream_behind (set -q __fish_git_prompt_char_upstream_behind; and echo $__fish_git_prompt_char_upstream_behind; or echo '<') - end - if not set -q ___fish_git_prompt_char_upstream_ahead - set -g ___fish_git_prompt_char_upstream_ahead (set -q __fish_git_prompt_char_upstream_ahead; and echo $__fish_git_prompt_char_upstream_ahead; or echo '>') - end - if not set -q ___fish_git_prompt_char_upstream_diverged - set -g ___fish_git_prompt_char_upstream_diverged (set -q __fish_git_prompt_char_upstream_diverged; and echo $__fish_git_prompt_char_upstream_diverged; or echo '<>') + + set -l variable _$user_variable_name + set -l variable_done "$variable"_done + + if not set -q $variable + if test -n "$user_variable" + if test -n "$user_variable_bright" + set -g $variable (set_color -o $user_variable) + else + set -g $variable (set_color $user_variable) + end + set -g $variable_done (set_color normal) + else + set -g $variable '' + set -g $variable_done '' + end end + end function __fish_git_prompt_validate_colors --description "__fish_git_prompt helper, checks color variables" - if not set -q ___fish_git_prompt_color - if test -n "$__fish_git_prompt_color" - set -g ___fish_git_prompt_color (set_color $__fish_git_prompt_color) - set -g ___fish_git_prompt_color_done (set_color normal) - else - set -g ___fish_git_prompt_color '' - set -g ___fish_git_prompt_color_done '' - end - end - if not set -q ___fish_git_prompt_color_prefix - if test -n "$__fish_git_prompt_color_prefix" - set -g ___fish_git_prompt_color_prefix (set_color $__fish_git_prompt_color_prefix) - set -g ___fish_git_prompt_color_prefix_done (set_color normal) - else - set -g ___fish_git_prompt_color_prefix $___fish_git_prompt_color - set -g ___fish_git_prompt_color_prefix_done $___fish_git_prompt_color_done - end - end - if not set -q ___fish_git_prompt_color_suffix - if test -n "$__fish_git_prompt_color_suffix" - set -g ___fish_git_prompt_color_suffix (set_color $__fish_git_prompt_color_suffix) - set -g ___fish_git_prompt_color_suffix_done (set_color normal) - else - set -g ___fish_git_prompt_color_suffix $___fish_git_prompt_color - set -g ___fish_git_prompt_color_suffix_done $___fish_git_prompt_color_done - end - end - if not set -q ___fish_git_prompt_color_bare - if test -n "$__fish_git_prompt_color_bare" - set -g ___fish_git_prompt_color_bare (set_color $__fish_git_prompt_color_bare) - set -g ___fish_git_prompt_color_bare_done (set_color normal) - else - set -g ___fish_git_prompt_color_bare $___fish_git_prompt_color - set -g ___fish_git_prompt_color_bare_done $___fish_git_prompt_color_done - end - end - if not set -q ___fish_git_prompt_color_merging - if test -n "$__fish_git_prompt_color_merging" - set -g ___fish_git_prompt_color_merging (set_color $__fish_git_prompt_color_merging) - set -g ___fish_git_prompt_color_merging_done (set_color normal) - else - set -g ___fish_git_prompt_color_merging $___fish_git_prompt_color - set -g ___fish_git_prompt_color_merging_done $___fish_git_prompt_color_done - end - end - if not set -q ___fish_git_prompt_color_branch - if test -n "$__fish_git_prompt_color_branch" - set -g ___fish_git_prompt_color_branch (set_color $__fish_git_prompt_color_branch) - set -g ___fish_git_prompt_color_branch_done (set_color normal) - else - set -g ___fish_git_prompt_color_branch $___fish_git_prompt_color - set -g ___fish_git_prompt_color_branch_done $___fish_git_prompt_color_done - end - end - if not set -q ___fish_git_prompt_color_dirtystate - if test -n "$__fish_git_prompt_color_dirtystate" - set -g ___fish_git_prompt_color_dirtystate (set_color $__fish_git_prompt_color_dirtystate) - set -g ___fish_git_prompt_color_dirtystate_done (set_color normal) - else - set -g ___fish_git_prompt_color_dirtystate $___fish_git_prompt_color - set -g ___fish_git_prompt_color_dirtystate_done $___fish_git_prompt_color_done - end - end - if not set -q ___fish_git_prompt_color_stagedstate - if test -n "$__fish_git_prompt_color_stagedstate" - set -g ___fish_git_prompt_color_stagedstate (set_color $__fish_git_prompt_color_stagedstate) - set -g ___fish_git_prompt_color_stagedstate_done (set_color normal) - else - set -g ___fish_git_prompt_color_stagedstate $___fish_git_prompt_color - set -g ___fish_git_prompt_color_stagedstate_done $___fish_git_prompt_color_done - end - end - if not set -q ___fish_git_prompt_color_invalidstate - if test -n "$__fish_git_prompt_color_invalidstate" - set -g ___fish_git_prompt_color_invalidstate (set_color $__fish_git_prompt_color_invalidstate) - set -g ___fish_git_prompt_color_invalidstate_done (set_color normal) - else - set -g ___fish_git_prompt_color_invalidstate $___fish_git_prompt_color - set -g ___fish_git_prompt_color_invalidstate_done $___fish_git_prompt_color_done - end - end - if not set -q ___fish_git_prompt_color_stashstate - if test -n "$__fish_git_prompt_color_stashstate" - set -g ___fish_git_prompt_color_stashstate (set_color $__fish_git_prompt_color_stashstate) - set -g ___fish_git_prompt_color_stashstate_done (set_color normal) - else - set -g ___fish_git_prompt_color_stashstate $___fish_git_prompt_color - set -g ___fish_git_prompt_color_stashstate_done $___fish_git_prompt_color_done - end - end - if not set -q ___fish_git_prompt_color_untrackedfiles - if test -n "$__fish_git_prompt_color_untrackedfiles" - set -g ___fish_git_prompt_color_untrackedfiles (set_color $__fish_git_prompt_color_untrackedfiles) - set -g ___fish_git_prompt_color_untrackedfiles_done (set_color normal) - else - set -g ___fish_git_prompt_color_untrackedfiles $___fish_git_prompt_color - set -g ___fish_git_prompt_color_untrackedfiles_done $___fish_git_prompt_color_done - end - end - if not set -q ___fish_git_prompt_color_upstream - if test -n "$__fish_git_prompt_color_upstream" - set -g ___fish_git_prompt_color_upstream (set_color $__fish_git_prompt_color_upstream) - set -g ___fish_git_prompt_color_upstream_done (set_color normal) - else - set -g ___fish_git_prompt_color_upstream $___fish_git_prompt_color - set -g ___fish_git_prompt_color_upstream_done $___fish_git_prompt_color_done - end - end + + __fish_git_prompt_set_color __fish_git_prompt_color + __fish_git_prompt_set_color __fish_git_prompt_color_prefix + __fish_git_prompt_set_color __fish_git_prompt_color_suffix + __fish_git_prompt_set_color __fish_git_prompt_color_bare + __fish_git_prompt_set_color __fish_git_prompt_color_merging + __fish_git_prompt_set_color __fish_git_prompt_color_branch + __fish_git_prompt_set_color __fish_git_prompt_color_cleanstate + __fish_git_prompt_set_color __fish_git_prompt_color_dirtystate + __fish_git_prompt_set_color __fish_git_prompt_color_stagedstate + __fish_git_prompt_set_color __fish_git_prompt_color_invalidstate + __fish_git_prompt_set_color __fish_git_prompt_color_stashstate + __fish_git_prompt_set_color __fish_git_prompt_color_untrackedfiles + __fish_git_prompt_set_color __fish_git_prompt_color_upstream + end set -l varargs diff --git a/share/tools/create_manpage_completions.py b/share/tools/create_manpage_completions.py index 8d7aa59fd..24650ac6e 100755 --- a/share/tools/create_manpage_completions.py +++ b/share/tools/create_manpage_completions.py @@ -59,19 +59,19 @@ def flush_diagnostics(where): # This maps commands to lists of completions already_output_completions = {} -def compileAndSearch(regex, input): +def compile_and_search(regex, input): options_section_regex = re.compile(regex , re.DOTALL) options_section_matched = re.search( options_section_regex, input) return options_section_matched -def unquoteDoubleQuotes(data): +def unquote_double_quotes(data): if (len(data) < 2): return data if data[0] == '"' and data[len(data)-1] == '"': data = data[1:len(data)-1] return data -def unquoteSingleQuotes(data): +def unquote_single_quotes(data): if (len(data) < 2): return data if data[0] == '`' and data[len(data)-1] == '\'': @@ -188,11 +188,7 @@ def built_command(options, description): output_complete_command(escaped_cmd, fish_options, truncated_description, built_command_output) - - -def removeGroffFormatting(data): -# data = data.replace("\fI","") -# data = data.replace("\fP","") +def remove_groff_formatting(data): data = data.replace("\\fI","") data = data.replace("\\fP","") data = data.replace("\\f1","") @@ -217,26 +213,26 @@ def removeGroffFormatting(data): return data class ManParser: - def isMyType(self, manpage): + def is_my_type(self, manpage): return False - def parseManPage(self, manpage): + def parse_man_page(self, manpage): return False def name(self): return "no-name" class Type1ManParser(ManParser): - def isMyType(self, manpage): + def is_my_type(self, manpage): # print manpage - options_section_matched = compileAndSearch("\.SH \"OPTIONS\"(.*?)", manpage) + options_section_matched = compile_and_search("\.SH \"OPTIONS\"(.*?)", manpage) if options_section_matched == None: return False else: return True - def parseManPage(self, manpage): + def parse_man_page(self, manpage): options_section_regex = re.compile( "\.SH \"OPTIONS\"(.*?)(\.SH|\Z)", re.DOTALL) options_section_matched = re.search( options_section_regex, manpage) @@ -256,27 +252,22 @@ class Type1ManParser(ManParser): return False while (options_matched != None): - # print len(options_matched.groups()) - # print options_matched.group() data = options_matched.group(1) last_dotpp_index = data.rfind(".PP") if (last_dotpp_index != -1): data = data[last_dotpp_index+3:] - data = removeGroffFormatting(data) + data = remove_groff_formatting(data) data = data.split(".RS 4") - # print data if (len (data) > 1): #and len(data[1]) <= 300): optionName = data[0].strip() if ( optionName.find("-") == -1): add_diagnostic(optionName + " doesn't contain - ") -# return False else: - optionName = unquoteDoubleQuotes(optionName) - optionName = unquoteSingleQuotes(optionName) + optionName = unquote_double_quotes(optionName) + optionName = unquote_single_quotes(optionName) optionDescription = data[1].strip().replace("\n"," ") -# print >> sys.stderr, "Option: ", optionName," Description: ", optionDescription , '\n' built_command(optionName, optionDescription) else: @@ -295,7 +286,7 @@ class Type1ManParser(ManParser): return False while options_matched != None: data = options_matched.group(2) - data = removeGroffFormatting(data) + data = remove_groff_formatting(data) data = data.strip() data = data.split("\n",1) if (len(data)>1 and len(data[1].strip())>0): # and len(data[1])<400): @@ -303,10 +294,9 @@ class Type1ManParser(ManParser): if ( optionName.find("-") == -1): add_diagnostic(optionName + "doesn't contains -") else: - optionName = unquoteDoubleQuotes(optionName) - optionName = unquoteSingleQuotes(optionName) + optionName = unquote_double_quotes(optionName) + optionName = unquote_single_quotes(optionName) optionDescription = data[1].strip().replace("\n"," ") -# print "Option: ", optionName," Description: ", optionDescription , '\n' built_command(optionName, optionDescription) else: add_diagnostic('Unable to split option from description') @@ -330,27 +320,21 @@ class Type1ManParser(ManParser): while options_matched != None: data = options_matched.group(1) -# print "Data is : ", data - data = removeGroffFormatting(data) + data = remove_groff_formatting(data) data = data.strip() data = data.split("\n",1) if (len(data)>1 and len(data[1].strip())>0): # and len(data[1])<400): -# print "Data[0] is: ", data[0] - -# data = re.sub(trailing_num_regex, "", data) optionName = re.sub(trailing_num_regex, "", data[0].strip()) if ('-' not in optionName): add_diagnostic(optionName + " doesn't contain -") else: optionName = optionName.strip() - optionName = unquoteDoubleQuotes(optionName) - optionName = unquoteSingleQuotes(optionName) + optionName = unquote_double_quotes(optionName) + optionName = unquote_single_quotes(optionName) optionDescription = data[1].strip().replace("\n"," ") -# print "Option: ", optionName," Description: ", optionDescription , '\n' built_command(optionName, optionDescription) else: -# print data add_diagnostic('Unable to split option from description') return False @@ -363,31 +347,21 @@ class Type1ManParser(ManParser): class Type2ManParser(ManParser): - def isMyType(self, manpage): - options_section_matched = compileAndSearch("\.SH OPTIONS(.*?)", manpage) + def is_my_type(self, manpage): + options_section_matched = compile_and_search("\.SH OPTIONS(.*?)", manpage) if options_section_matched == None: return False else: return True - def parseManPage(self, manpage): + def parse_man_page(self, manpage): options_section_regex = re.compile( "\.SH OPTIONS(.*?)(\.SH|\Z)", re.DOTALL) options_section_matched = re.search( options_section_regex, manpage) -# if (options_section_matched == None): -# print "Falling Back" -# options_section_regex = re.compile( "\.SH OPTIONS(.*?)$", re.DOTALL) -# options_section_matched = re.search( options_section_regex, manpage) -# print manpage options_section = options_section_matched.group(1) -# print options_section - # print options_section - # sys.exit(1) -# options_parts_regex = re.compile("\.TP(.*?)\.TP", re.DOTALL) options_parts_regex = re.compile("\.[I|T]P( \d+(\.\d)?i?)?(.*?)\.[I|T]P", re.DOTALL) -# options_parts_regex = re.compile("\.TP(.*?)[(\.TP)|(\.SH)]", re.DOTALL) options_matched = re.search(options_parts_regex, options_section) add_diagnostic('Command is ' + CMDNAME) @@ -396,56 +370,46 @@ class Type2ManParser(ManParser): return False while (options_matched != None): - # print len(options_matched.groups()) data = options_matched.group(3) - data = removeGroffFormatting(data) + data = remove_groff_formatting(data) data = data.strip() data = data.split("\n",1) -# print >> sys.stderr, data if (len(data)>1 and len(data[1].strip())>0): # and len(data[1])<400): optionName = data[0].strip() if '-' not in optionName: add_diagnostic(optionName + " doesn't contain -") else: - optionName = unquoteDoubleQuotes(optionName) - optionName = unquoteSingleQuotes(optionName) + optionName = unquote_double_quotes(optionName) + optionName = unquote_single_quotes(optionName) optionDescription = data[1].strip().replace("\n"," ") -# print "Option: ", optionName," Description: ", optionDescription , '\n' built_command(optionName, optionDescription) else: - # print >> sys.stderr, data add_diagnostic('Unable to split option from description') -# return False - options_section = options_section[options_matched.end()-3:] options_matched = re.search(options_parts_regex, options_section) - - def name(self): return "Type2" class Type3ManParser(ManParser): - def isMyType(self, manpage): - options_section_matched = compileAndSearch("\.SH DESCRIPTION(.*?)", manpage) + def is_my_type(self, manpage): + options_section_matched = compile_and_search("\.SH DESCRIPTION(.*?)", manpage) if options_section_matched == None: return False else: return True - def parseManPage(self, manpage): + def parse_man_page(self, manpage): options_section_regex = re.compile( "\.SH DESCRIPTION(.*?)(\.SH|\Z)", re.DOTALL) options_section_matched = re.search( options_section_regex, manpage) options_section = options_section_matched.group(1) - # print options_section - # sys.exit(1) options_parts_regex = re.compile("\.TP(.*?)\.TP", re.DOTALL) options_matched = re.search(options_parts_regex, options_section) add_diagnostic('Command is ' + CMDNAME) @@ -455,10 +419,9 @@ class Type3ManParser(ManParser): return False while (options_matched != None): -# print len(options_matched.groups()) data = options_matched.group(1) - data = removeGroffFormatting(data) + data = remove_groff_formatting(data) data = data.strip() data = data.split("\n",1) @@ -467,10 +430,9 @@ class Type3ManParser(ManParser): if ( optionName.find("-") == -1): add_diagnostic(optionName + "doesn't contain -") else: - optionName = unquoteDoubleQuotes(optionName) - optionName = unquoteSingleQuotes(optionName) + optionName = unquote_double_quotes(optionName) + optionName = unquote_single_quotes(optionName) optionDescription = data[1].strip().replace("\n"," ") -# print >> sys.stderr, "Option: ", optionName," Description: ", optionDescription , '\n' built_command(optionName, optionDescription) else: @@ -481,27 +443,24 @@ class Type3ManParser(ManParser): options_matched = re.search(options_parts_regex, options_section) - def name(self): return "Type3" class Type4ManParser(ManParser): - def isMyType(self, manpage): - options_section_matched = compileAndSearch("\.SH FUNCTION LETTERS(.*?)", manpage) + def is_my_type(self, manpage): + options_section_matched = compile_and_search("\.SH FUNCTION LETTERS(.*?)", manpage) if options_section_matched == None: return False else: return True - def parseManPage(self, manpage): + def parse_man_page(self, manpage): options_section_regex = re.compile( "\.SH FUNCTION LETTERS(.*?)(\.SH|\Z)", re.DOTALL) options_section_matched = re.search( options_section_regex, manpage) options_section = options_section_matched.group(1) - # print options_section - # sys.exit(1) options_parts_regex = re.compile("\.TP(.*?)\.TP", re.DOTALL) options_matched = re.search(options_parts_regex, options_section) add_diagnostic('Command is ' + CMDNAME) @@ -511,10 +470,9 @@ class Type4ManParser(ManParser): return False while (options_matched != None): - # print len(options_matched.groups()) data = options_matched.group(1) - data = removeGroffFormatting(data) + data = remove_groff_formatting(data) data = data.strip() data = data.split("\n",1) @@ -523,10 +481,9 @@ class Type4ManParser(ManParser): if ( optionName.find("-") == -1): add_diagnostic(optionName + " doesn't contain - ") else: - optionName = unquoteDoubleQuotes(optionName) - optionName = unquoteSingleQuotes(optionName) + optionName = unquote_double_quotes(optionName) + optionName = unquote_single_quotes(optionName) optionDescription = data[1].strip().replace("\n"," ") -# print "Option: ", optionName," Description: ", optionDescription , '\n' built_command(optionName, optionDescription) else: @@ -542,8 +499,8 @@ class Type4ManParser(ManParser): return "Type4" class TypeDarwinManParser(ManParser): - def isMyType(self, manpage): - options_section_matched = compileAndSearch("\.S[hH] DESCRIPTION", manpage) + def is_my_type(self, manpage): + options_section_matched = compile_and_search("\.S[hH] DESCRIPTION", manpage) return options_section_matched != None def trim_groff(self, line): @@ -571,9 +528,6 @@ class TypeDarwinManParser(ManParser): result = result + 1 line = line[3:] return result - - - # Replace some groff escapes. There's a lot we don't bother to handle. def groff_replace_escapes(self, line): @@ -586,7 +540,7 @@ class TypeDarwinManParser(ManParser): def is_option(self, line): return line.startswith('.It Fl') - def parseManPage(self, manpage): + def parse_man_page(self, manpage): got_something = False lines = manpage.splitlines() # Discard lines until we get to ".sh Description" @@ -626,9 +580,6 @@ class TypeDarwinManParser(ManParser): desc_lines.append(line) desc = ' '.join(desc_lines) - # print "name: ", name - # print "desc: ", desc - if name == '-': # Skip double -- arguments continue @@ -647,7 +598,7 @@ class TypeDarwinManParser(ManParser): class TypeDeroffManParser(ManParser): - def isMyType(self, manpage): + def is_my_type(self, manpage): return True # We're optimists def is_option(self, line): @@ -656,7 +607,7 @@ class TypeDeroffManParser(ManParser): def could_be_description(self, line): return len(line) > 0 and not line.startswith('-') - def parseManPage(self, manpage): + def parse_man_page(self, manpage): d = Deroffer() d.deroff(manpage) output = d.get_output() @@ -697,7 +648,6 @@ class TypeDeroffManParser(ManParser): return got_something - def name(self): return "Deroffing man parser" @@ -800,7 +750,7 @@ def parse_manpage_at_path(manpage_path, output_directory): parsers = [TypeDeroffManParser()] else: parsers = [Type1ManParser(), Type2ManParser(), Type4ManParser(), Type3ManParser(), TypeDarwinManParser(), TypeDeroffManParser()] - parsersToTry = [p for p in parsers if p.isMyType(manpage)] + parsersToTry = [p for p in parsers if p.is_my_type(manpage)] success = False if not parsersToTry: @@ -810,7 +760,7 @@ def parse_manpage_at_path(manpage_path, output_directory): parser_name = parser.name() add_diagnostic('Trying parser ' + parser_name) diagnostic_indent += 1 - success = parser.parseManPage(manpage) + success = parser.parse_man_page(manpage) diagnostic_indent -= 1 # Make sure empty files aren't reported as success if not built_command_output: diff --git a/share/tools/web_config/sample_prompts/informative_git.fish b/share/tools/web_config/sample_prompts/informative_git.fish new file mode 100644 index 000000000..466b40515 --- /dev/null +++ b/share/tools/web_config/sample_prompts/informative_git.fish @@ -0,0 +1,47 @@ +# name: Informative Git Prompt +# author: Mariusz Smykula + +set -g __fish_git_prompt_show_informative_status 1 +set -g __fish_git_prompt_hide_untrackedfiles 1 + +set -g __fish_git_prompt_color_branch magenta bold +set -g __fish_git_prompt_showupstream "informative" +set -g __fish_git_prompt_char_upstream_ahead "↑" +set -g __fish_git_prompt_char_upstream_behind "↓" +set -g __fish_git_prompt_char_upstream_prefix "" + +set -g __fish_git_prompt_char_stagedstate "●" +set -g __fish_git_prompt_char_dirtystate "✚" +set -g __fish_git_prompt_char_untrackedfiles "…" +set -g __fish_git_prompt_char_conflictedstate "✖" +set -g __fish_git_prompt_char_cleanstate "✔" + +set -g __fish_git_prompt_color_dirtystate blue +set -g __fish_git_prompt_color_stagedstate yellow +set -g __fish_git_prompt_color_invalidstate red +set -g __fish_git_prompt_color_untrackedfiles $fish_color_normal +set -g __fish_git_prompt_color_cleanstate green bold + + +function fish_prompt --description 'Write out the prompt' + + set -l last_status $status + + if not set -q __fish_prompt_normal + set -g __fish_prompt_normal (set_color normal) + end + + # PWD + set_color $fish_color_cwd + echo -n (prompt_pwd) + set_color normal + + printf '%s ' (__fish_git_prompt) + + if not test $last_status -eq 0 + set_color $fish_color_error + end + + echo -n '$ ' + +end diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 8b5b24f92..4f07fd9d7 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -75,7 +75,6 @@ def better_color(c1, c2): if c1 in named_colors: return c2 return c1 - def parse_color(color_str): """ A basic function to parse a color string, for example, 'red' '--bold' """ comps = color_str.split(' ') @@ -98,7 +97,6 @@ def parse_color(color_str): return [color, background_color, bold, underline] - def parse_bool(val): val = val.lower() if val.startswith('f') or val.startswith('0'): return False @@ -229,11 +227,11 @@ def ansi_to_html(val): # Clean up empty spans, the nasty way idx = len(result) - 1 while idx >= 1: - if result[idx] == '' and result[idx-1].startswith('' and result[idx-1].startswith('