From be06f842a23956f65a4c25872d9c6f6950639232 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Fri, 7 Feb 2020 16:33:44 +0100 Subject: [PATCH] Allow to omit indices in index range expansions Missing range limits in, say $PATH[..] default to the first/last element, just like Python/Go/Rust slices. --- CHANGELOG.md | 1 + sphinx_doc_src/index.rst | 5 +++++ src/expand.cpp | 36 +++++++++++++++++++++++++++--------- tests/checks/slices.fish | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c96eefd6..c07424ee7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Syntax changes and new commands ### Scripting improvements +- Range limits in index range expansions like `$x[$start..$end]` may be omitted: `$start` and `$end` default to 1 and -1 (the last item) respectively. ### Interactive improvements diff --git a/sphinx_doc_src/index.rst b/sphinx_doc_src/index.rst index 123a1e7fd..aa682ffeb 100644 --- a/sphinx_doc_src/index.rst +++ b/sphinx_doc_src/index.rst @@ -661,6 +661,8 @@ Sequences of elements can be written with the range operator '``..``'. A range ' If the end is smaller than the start, or the start is larger than the end, range expansion will go in reverse. This is unless exactly one of the given indices is negative, so the direction doesn't change if the list has fewer elements than expected. +A missing starting index in a range defaults to 1. This is allowed if the range is the first index expression of the sequence. Similarly, a missing ending index, defaulting to -1 is allowed for the last index range in the sequence. + Some examples:: @@ -672,6 +674,9 @@ Some examples:: # Uses elements from 2 to 5 # Output is: 2 3 4 5 + echo (seq 10)[7..] + # Prints: 7 8 9 10 + # Use overlapping ranges: echo (seq 10)[2..5 1..3] # Takes elements from 2 to 5 and then elements from 1 to 3 diff --git a/src/expand.cpp b/src/expand.cpp index adc7d3f16..df3a38159 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -188,11 +188,20 @@ static size_t parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector 0) { - return pos; + long tmp; + if (idx.empty() && in[pos] == L'.' && in[pos + 1] == L'.') { + // If we are at the first index expression, a missing start index means the range starts + // at the first item. + tmp = 1; // first index + end = &in[pos]; + } else { + tmp = fish_wcstol(&in[pos], &end); + if (errno > 0) { + // We don't test `*end` as is typically done because we expect it to not be the null + // char. Ignore the case of errno==-1 because it means the end char wasn't the null + // char. + return pos; + } } long i1 = tmp > -1 ? tmp : size + tmp + 1; @@ -201,11 +210,20 @@ static size_t parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector 0) { - return pos; + long tmp1; + // Check if we are at the last index range expression, a missing end index means the + // range spans until the last item. + if (in[pos] == L']') { + tmp1 = -1; // last index + end = &in[pos]; + } else { + tmp1 = fish_wcstol(&in[pos], &end); + // Ignore the case of errno==-1 because it means the end char wasn't the null char. + if (errno > 0) { + return pos; + } } pos = end - in; diff --git a/tests/checks/slices.fish b/tests/checks/slices.fish index 8cfe01416..3579a0837 100644 --- a/tests/checks/slices.fish +++ b/tests/checks/slices.fish @@ -3,6 +3,8 @@ set n 10 set test (seq $n) echo $test[1..$n] # normal range #CHECK: 1 2 3 4 5 6 7 8 9 10 +echo $test[1 .. 2] # spaces are allowed +#CHECK: 1 2 echo $test[$n..1] # inverted range #CHECK: 10 9 8 7 6 5 4 3 2 1 echo $test[2..5 8..6] # several ranges @@ -31,3 +33,36 @@ echo $test[(count $test)..1] #CHECK: 10 9 8 7 6 5 4 3 2 1 echo $test[1..(count $test)] #CHECK: 1 2 3 4 5 6 7 8 9 10 + +echo $test[ .. ] +#CHECK: 1 2 3 4 5 6 7 8 9 10 +echo $test[ ..3] +#CHECK: 1 2 3 +echo $test[8.. ] +#CHECK: 8 9 10 +echo $test[..2 5] +# CHECK: 1 2 5 +echo $test[2 9..] +# CHECK: 2 9 10 + +# missing start, cannot use implied range +echo $test[1..2..] +#CHECKERR: {{.*}}: Invalid index value +#CHECKERR: echo $test[1..2..] +#CHECKERR: ^ +echo $test[..1..2] +#CHECKERR: {{.*}}: Invalid index value +#CHECKERR: echo $test[..1..2] +#CHECKERR: ^ + +set -l empty +echo $test[ $empty..] +#CHECK: +echo $test[.."$empty"] +#CHECK: 1 2 3 4 5 6 7 8 9 10 +echo $test["$empty"..] +#CHECK: 1 2 3 4 5 6 7 8 9 10 +echo $test[ (true)..3] +#CHECK: +echo $test[ (string join \n 1 2 3)..3 ] +#CHECK: 1 2 3 2 3 3