diff --git a/src/common.cpp b/src/common.cpp index a9037ca8f..b4063df79 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1040,6 +1040,7 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring case L'|': case L';': case L'"': + case L'%': case L'~': { bool char_is_normal = (c == L'~' && no_tilde) || (c == L'^' && no_caret) || (c == L'?' && no_qmark); @@ -1398,6 +1399,17 @@ static bool unescape_string_internal(const wchar_t *const input, const size_t in } break; } + case L'%': { + // Note that this only recognizes %self if the string is literally %self. + // %self/foo will NOT match this. + if (unescape_special && input_position == 0 && + !wcscmp(input, PROCESS_EXPAND_SELF_STR)) { + to_append_or_none = PROCESS_EXPAND_SELF; + input_position += + wcslen(PROCESS_EXPAND_SELF_STR) - 1; // skip over 'self' part. + } + break; + } case L'*': { if (unescape_special) { // In general, this is ANY_STRING. But as a hack, if the last appended char diff --git a/src/env.cpp b/src/env.cpp index 69e9d8a03..9c35b9ba4 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -924,7 +924,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { wcstring version = str2wcstring(get_fish_version()); env_set_one(L"version", ENV_GLOBAL, version); - // Set the $fish_pid variable (%self replacement) + // Set the $fish_pid variable. env_set_one(L"fish_pid", ENV_GLOBAL, to_string(getpid())); // Set the $hostname variable diff --git a/src/expand.cpp b/src/expand.cpp index a0e2dd7ef..d4bc6e553 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -56,7 +56,7 @@ /// Characters which make a string unclean if they are the first character of the string. See \c /// expand_is_clean(). -#define UNCLEAN_FIRST L"~" +#define UNCLEAN_FIRST L"~%" /// Unclean characters. See \c expand_is_clean(). #define UNCLEAN L"$*?\\\"'({})" @@ -784,6 +784,13 @@ static void expand_home_directory(wcstring &input) { } } +/// Expand the %self escape. Note this can only come at the beginning of the string. +static void expand_percent_self(wcstring &input) { + if (!input.empty() && input.front() == PROCESS_EXPAND_SELF) { + input.replace(0, 1, to_string(getpid())); + } +} + void expand_tilde(wcstring &input) { // Avoid needless COW behavior by ensuring we use const at. const wcstring &tmp = input; @@ -934,12 +941,13 @@ static expand_error_t expand_stage_braces(wcstring input, std::vector *out, - expand_flags_t flags, parse_error_list_t *errors) { +static expand_error_t expand_stage_home_and_self(wcstring input, std::vector *out, + expand_flags_t flags, parse_error_list_t *errors) { (void)errors; if (!(EXPAND_SKIP_HOME_DIRECTORIES & flags)) { expand_home_directory(input); } + expand_percent_self(input); append_completion(out, std::move(input)); return EXPAND_OK; } @@ -1047,7 +1055,7 @@ expand_error_t expand_string(wcstring input, std::vector *out_comp // Our expansion stages. const expand_stage_t stages[] = {expand_stage_cmdsubst, expand_stage_variables, - expand_stage_braces, expand_stage_home, + expand_stage_braces, expand_stage_home_and_self, expand_stage_wildcards}; // Load up our single initial completion. diff --git a/src/expand.h b/src/expand.h index f92350a1c..b6fac9364 100644 --- a/src/expand.h +++ b/src/expand.h @@ -61,6 +61,8 @@ class completion_t; enum { /// Character representing a home directory. HOME_DIRECTORY = EXPAND_RESERVED_BASE, + /// Character representing process expansion for %self. + PROCESS_EXPAND_SELF, /// Character representing variable expansion. VARIABLE_EXPAND, /// Character representing variable expansion into a single element. @@ -95,6 +97,9 @@ enum expand_error_t { EXPAND_WILDCARD_MATCH }; +/// The string represented by PROCESS_EXPAND_SELF +#define PROCESS_EXPAND_SELF_STR L"%self" + /// Perform various forms of expansion on in, such as tilde expansion (\~USER becomes the users home /// directory), variable expansion (\$VAR_NAME becomes the value of the environment variable /// VAR_NAME), cmdsubst expansion and wildcard expansion. The results are inserted into the list diff --git a/src/highlight.cpp b/src/highlight.cpp index b620f6d3d..ac988d42b 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -121,6 +121,7 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l for (size_t i = 0; i < path_with_magic.size(); i++) { wchar_t c = path_with_magic.at(i); switch (c) { + case PROCESS_EXPAND_SELF: case VARIABLE_EXPAND: case VARIABLE_EXPAND_SINGLE: case BRACE_BEGIN: diff --git a/tests/expansion.err b/tests/expansion.err index d75619e07..65ffe2e28 100644 --- a/tests/expansion.err +++ b/tests/expansion.err @@ -8,6 +8,9 @@ fish: Invalid index value echo ()[d] ^ +#################### +# Percent self + #################### # Catch your breath fish: $) is not a valid variable in fish. diff --git a/tests/expansion.in b/tests/expansion.in index f0f594bbd..a0fdd2138 100644 --- a/tests/expansion.in +++ b/tests/expansion.in @@ -101,6 +101,11 @@ echo $foo[d] echo ()[1] echo ()[d] +logmsg Percent self +echo %selfNOT NOT%self \%self "%self" '%self' +echo %self | string match -qr '^\\d+$' +echo "All digits: $status" + logmsg Catch your breath set paren ')' echo $$paren diff --git a/tests/expansion.out b/tests/expansion.out index 82f3b9ee7..ca7596395 100644 --- a/tests/expansion.out +++ b/tests/expansion.out @@ -63,6 +63,11 @@ 0 +#################### +# Percent self +%selfNOT NOT%self %self %self %self +All digits: 0 + #################### # Catch your breath