Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package fzf for openSUSE:Factory checked in 
at 2021-11-17 01:14:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/fzf (Old)
 and      /work/SRC/openSUSE:Factory/.fzf.new.1890 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "fzf"

Wed Nov 17 01:14:12 2021 rev:20 rq:931747 version:0.28.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/fzf/fzf.changes  2021-10-15 23:05:09.858148825 
+0200
+++ /work/SRC/openSUSE:Factory/.fzf.new.1890/fzf.changes        2021-11-17 
01:15:22.878191043 +0100
@@ -1,0 +2,9 @@
+Sat Nov 13 19:44:54 UTC 2021 - Dirk M??ller <dmuel...@suse.com>
+
+- update to 0.28.0:
+  * Added `--header-first` option to print header before the prompt line
+  * Added `--scroll-off=LINES` option
+  * Fixed bug where preview window is not updated on `reload`
+  * fzf on Windows will also use `$SHELL` to execute external programs
+
+-------------------------------------------------------------------

Old:
----
  0.27.3.tar.gz

New:
----
  0.28.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ fzf.spec ++++++
--- /var/tmp/diff_new_pack.XVZOEY/_old  2021-11-17 01:15:23.474191071 +0100
+++ /var/tmp/diff_new_pack.XVZOEY/_new  2021-11-17 01:15:23.474191071 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           fzf
-Version:        0.27.3
+Version:        0.28.0
 Release:        0
 Summary:        A command-line fuzzy finder
 License:        MIT

++++++ 0.27.3.tar.gz -> 0.28.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/CHANGELOG.md new/fzf-0.28.0/CHANGELOG.md
--- old/fzf-0.27.3/CHANGELOG.md 2021-10-15 16:59:56.000000000 +0200
+++ new/fzf-0.28.0/CHANGELOG.md 2021-11-03 17:05:07.000000000 +0100
@@ -1,6 +1,24 @@
 CHANGELOG
 =========
 
+0.28.0
+------
+- Added `--header-first` option to print header before the prompt line
+  ```sh
+  fzf --header $'Welcome to fzf\n??????????????????????????????????????????' 
--reverse --height 30% --border --header-first
+  ```
+- Added `--scroll-off=LINES` option (similar to `scrolloff` option of Vim)
+    - You can set it to a very large number so that the cursor stays in the
+      middle of the screen while scrolling
+      ```sh
+      fzf --scroll-off=5
+      fzf --scroll-off=999
+      ```
+- Fixed bug where preview window is not updated on `reload` (#2644)
+- fzf on Windows will also use `$SHELL` to execute external programs
+    - See #2638 and #2647
+    - Thanks to @rashil2000, @vovcacik, and @janlazo
+
 0.27.3
 ------
 - Preview window is `hidden` by default when there are `preview` bindings but
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/Makefile new/fzf-0.28.0/Makefile
--- old/fzf-0.27.3/Makefile     2021-10-15 16:59:56.000000000 +0200
+++ new/fzf-0.28.0/Makefile     2021-11-03 17:05:07.000000000 +0100
@@ -102,6 +102,7 @@
        grep -qF $(VERSION) install.ps1
 
        # Make release note out of CHANGELOG.md
+       mkdir -p tmp
        sed -n '/^$(VERSION_REGEX)$$/,/^[0-9]/p' CHANGELOG.md | tail -r | \
                sed '1,/^ *$$/d' | tail -r | sed 1,2d | tee tmp/release-note
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/install new/fzf-0.28.0/install
--- old/fzf-0.27.3/install      2021-10-15 16:59:56.000000000 +0200
+++ new/fzf-0.28.0/install      2021-11-03 17:05:07.000000000 +0100
@@ -2,7 +2,7 @@
 
 set -u
 
-version=0.27.3
+version=0.28.0
 auto_completion=
 key_bindings=
 update_config=2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/install.ps1 new/fzf-0.28.0/install.ps1
--- old/fzf-0.27.3/install.ps1  2021-10-15 16:59:56.000000000 +0200
+++ new/fzf-0.28.0/install.ps1  2021-11-03 17:05:07.000000000 +0100
@@ -1,4 +1,4 @@
-$version="0.27.3"
+$version="0.28.0"
 
 $fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/main.go new/fzf-0.28.0/main.go
--- old/fzf-0.27.3/main.go      2021-10-15 16:59:56.000000000 +0200
+++ new/fzf-0.28.0/main.go      2021-11-03 17:05:07.000000000 +0100
@@ -1,11 +1,11 @@
 package main
 
 import (
-       "github.com/junegunn/fzf/src"
+       fzf "github.com/junegunn/fzf/src"
        "github.com/junegunn/fzf/src/protector"
 )
 
-var version string = "0.27"
+var version string = "0.28"
 var revision string = "devel"
 
 func main() {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/man/man1/fzf-tmux.1 
new/fzf-0.28.0/man/man1/fzf-tmux.1
--- old/fzf-0.27.3/man/man1/fzf-tmux.1  2021-10-15 16:59:56.000000000 +0200
+++ new/fzf-0.28.0/man/man1/fzf-tmux.1  2021-11-03 17:05:07.000000000 +0100
@@ -21,7 +21,7 @@
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ..
-.TH fzf-tmux 1 "Oct 2021" "fzf 0.27.3" "fzf-tmux - open fzf in tmux split pane"
+.TH fzf-tmux 1 "Nov 2021" "fzf 0.28.0" "fzf-tmux - open fzf in tmux split pane"
 
 .SH NAME
 fzf-tmux - open fzf in tmux split pane
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/man/man1/fzf.1 
new/fzf-0.28.0/man/man1/fzf.1
--- old/fzf-0.27.3/man/man1/fzf.1       2021-10-15 16:59:56.000000000 +0200
+++ new/fzf-0.28.0/man/man1/fzf.1       2021-11-03 17:05:07.000000000 +0100
@@ -21,7 +21,7 @@
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ..
-.TH fzf 1 "Oct 2021" "fzf 0.27.3" "fzf - a command-line fuzzy finder"
+.TH fzf 1 "Nov 2021" "fzf 0.28.0" "fzf - a command-line fuzzy finder"
 
 .SH NAME
 fzf - a command-line fuzzy finder
@@ -135,10 +135,14 @@
 Keep the right end of the line visible when it's too long. Effective only when
 the query string is empty.
 .TP
+.BI "--scroll-off=" "LINES"
+Number of screen lines to keep above or below when scrolling to the top or to
+the bottom (default: 0).
+.TP
 .B "--no-hscroll"
 Disable horizontal scroll
 .TP
-.BI "--hscroll-off=" "COL"
+.BI "--hscroll-off=" "COLS"
 Number of screen columns to keep to the right of the highlighted substring
 (default: 10). Setting it to a large value will cause the text to be positioned
 on the center of the screen.
@@ -295,6 +299,9 @@
 The first N lines of the input are treated as the sticky header. When
 \fB--with-nth\fR is set, the lines are transformed just like the other
 lines that follow.
+.TP
+.B "--header-first"
+Print header before the prompt line
 .SS Display
 .TP
 .B "--ansi"
@@ -853,6 +860,7 @@
     \fBpreview-top\fR
     \fBprevious-history\fR          (\fIctrl-p\fR on \fB--history\fR)
     \fBprint-query\fR               (print query and exit)
+    \fBput\fR                       (put the character to the prompt)
     \fBrefresh-preview\fR
     \fBreload(...)\fR               (see below for the details)
     \fBreplace-query\fR             (replace query string with the current 
selection)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/shell/completion.bash 
new/fzf-0.28.0/shell/completion.bash
--- old/fzf-0.27.3/shell/completion.bash        2021-10-15 16:59:56.000000000 
+0200
+++ new/fzf-0.28.0/shell/completion.bash        2021-11-03 17:05:07.000000000 
+0100
@@ -32,7 +32,7 @@
 ###########################################################
 
 # To redraw line after fzf closes (printf '\e[5n')
-bind '"\e[0n": redraw-current-line'
+bind '"\e[0n": redraw-current-line' 2> /dev/null
 
 __fzf_comprun() {
   if [[ "$(type -t _fzf_comprun 2>&1)" = function ]]; then
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/src/options.go 
new/fzf-0.28.0/src/options.go
--- old/fzf-0.27.3/src/options.go       2021-10-15 16:59:56.000000000 +0200
+++ new/fzf-0.28.0/src/options.go       2021-11-03 17:05:07.000000000 +0100
@@ -44,8 +44,10 @@
     --bind=KEYBINDS       Custom key bindings. Refer to the man page.
     --cycle               Enable cyclic scroll
     --keep-right          Keep the right end of the line visible on overflow
+    --scroll-off=LINES    Number of screen lines to keep above or below when
+                          scrolling to the top or to the bottom (default: 0)
     --no-hscroll          Disable horizontal scroll
-    --hscroll-off=COL     Number of screen columns to keep to the right of the
+    --hscroll-off=COLS    Number of screen columns to keep to the right of the
                           highlighted substring (default: 10)
     --filepath-word       Make word-wise movements respect path separators
     --jump-labels=CHARS   Label characters for jump and jump-accept
@@ -67,6 +69,7 @@
     --marker=STR          Multi-select marker (default: '>')
     --header=STR          String to print as header
     --header-lines=N      The first N lines of the input are treated as header
+    --header-first        Print header before the prompt line
 
   Display
     --ansi                Enable processing of ANSI color codes
@@ -200,6 +203,7 @@
        KeepRight   bool
        Hscroll     bool
        HscrollOff  int
+       ScrollOff   int
        FileWord    bool
        InfoStyle   infoStyle
        JumpLabels  string
@@ -222,6 +226,7 @@
        History     *History
        Header      []string
        HeaderLines int
+       HeaderFirst bool
        Margin      [4]sizeSpec
        Padding     [4]sizeSpec
        BorderShape tui.BorderShape
@@ -261,6 +266,7 @@
                KeepRight:   false,
                Hscroll:     true,
                HscrollOff:  10,
+               ScrollOff:   0,
                FileWord:    false,
                InfoStyle:   infoDefault,
                JumpLabels:  defaultJumpLabels,
@@ -283,6 +289,7 @@
                History:     nil,
                Header:      make([]string, 0),
                HeaderLines: 0,
+               HeaderFirst: false,
                Margin:      defaultMargin(),
                Padding:     defaultMargin(),
                Unicode:     true,
@@ -974,6 +981,12 @@
                                appendAction(actEnableSearch)
                        case "disable-search":
                                appendAction(actDisableSearch)
+                       case "put":
+                               if key.Type == tui.Rune && 
unicode.IsGraphic(key.Char) {
+                                       appendAction(actRune)
+                               } else {
+                                       errorExit("unable to put non-printable 
character: " + pair[0])
+                               }
                        default:
                                t := isExecuteAction(specLower)
                                if t == actIgnore {
@@ -1354,6 +1367,8 @@
                        opts.Hscroll = false
                case "--hscroll-off":
                        opts.HscrollOff = nextInt(allArgs, &i, "hscroll offset 
required")
+               case "--scroll-off":
+                       opts.ScrollOff = nextInt(allArgs, &i, "scroll offset 
required")
                case "--filepath-word":
                        opts.FileWord = true
                case "--no-filepath-word":
@@ -1421,6 +1436,10 @@
                case "--header-lines":
                        opts.HeaderLines = atoi(
                                nextString(allArgs, &i, "number of header lines 
required"))
+               case "--header-first":
+                       opts.HeaderFirst = true
+               case "--no-header-first":
+                       opts.HeaderFirst = false
                case "--preview":
                        opts.Preview.command = nextString(allArgs, &i, "preview 
command required")
                case "--no-preview":
@@ -1530,6 +1549,8 @@
                                opts.Tabstop = atoi(value)
                        } else if match, value := optString(arg, 
"--hscroll-off="); match {
                                opts.HscrollOff = atoi(value)
+                       } else if match, value := optString(arg, 
"--scroll-off="); match {
+                               opts.ScrollOff = atoi(value)
                        } else if match, value := optString(arg, 
"--jump-labels="); match {
                                opts.JumpLabels = value
                                validateJumpLabels = true
@@ -1547,6 +1568,10 @@
                errorExit("hscroll offset must be a non-negative integer")
        }
 
+       if opts.ScrollOff < 0 {
+               errorExit("scroll offset must be a non-negative integer")
+       }
+
        if opts.Tabstop < 1 {
                errorExit("tab stop must be a positive integer")
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/src/terminal.go 
new/fzf-0.28.0/src/terminal.go
--- old/fzf-0.27.3/src/terminal.go      2021-10-15 16:59:56.000000000 +0200
+++ new/fzf-0.28.0/src/terminal.go      2021-11-03 17:05:07.000000000 +0100
@@ -121,6 +121,7 @@
        keepRight    bool
        hscroll      bool
        hscrollOff   int
+       scrollOff    int
        wordRubout   string
        wordNext     string
        cx           int
@@ -139,6 +140,8 @@
        printQuery   bool
        history      *History
        cycle        bool
+       headerFirst  bool
+       headerLines  int
        header       []string
        header0      []string
        ansi         bool
@@ -502,6 +505,7 @@
                keepRight:   opts.KeepRight,
                hscroll:     opts.Hscroll,
                hscrollOff:  opts.HscrollOff,
+               scrollOff:   opts.ScrollOff,
                wordRubout:  wordRubout,
                wordNext:    wordNext,
                cx:          len(input),
@@ -527,6 +531,8 @@
                paused:      opts.Phony,
                strong:      strongAttr,
                cycle:       opts.Cycle,
+               headerFirst: opts.HeaderFirst,
+               headerLines: opts.HeaderLines,
                header:      header,
                header0:     header,
                ansi:        opts.Ansi,
@@ -974,12 +980,23 @@
        return before, after
 }
 
+func (t *Terminal) promptLine() int {
+       if t.headerFirst {
+               max := t.window.Height() - 1
+               if !t.noInfoLine() {
+                       max--
+               }
+               return util.Min(len(t.header0)+t.headerLines, max)
+       }
+       return 0
+}
+
 func (t *Terminal) placeCursor() {
-       t.move(0, t.promptLen+t.queryLen[0], false)
+       t.move(t.promptLine(), t.promptLen+t.queryLen[0], false)
 }
 
 func (t *Terminal) printPrompt() {
-       t.move(0, 0, true)
+       t.move(t.promptLine(), 0, true)
        t.prompt()
 
        before, after := t.updatePromptOffset()
@@ -1001,22 +1018,23 @@
 
 func (t *Terminal) printInfo() {
        pos := 0
+       line := t.promptLine()
        switch t.infoStyle {
        case infoDefault:
-               t.move(1, 0, true)
+               t.move(line+1, 0, true)
                if t.reading {
                        duration := int64(spinnerDuration)
                        idx := (time.Now().UnixNano() % (duration * 
int64(len(t.spinner)))) / duration
                        t.window.CPrint(tui.ColSpinner, t.spinner[idx])
                }
-               t.move(1, 2, false)
+               t.move(line+1, 2, false)
                pos = 2
        case infoInline:
                pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
                if pos+len(" < ") > t.window.Width() {
                        return
                }
-               t.move(0, pos, true)
+               t.move(line, pos, true)
                if t.reading {
                        t.window.CPrint(tui.ColSpinner, " < ")
                } else {
@@ -1059,11 +1077,20 @@
                return
        }
        max := t.window.Height()
+       if t.headerFirst {
+               max--
+               if !t.noInfoLine() {
+                       max--
+               }
+       }
        var state *ansiState
        for idx, lineStr := range t.header {
-               line := idx + 2
-               if t.noInfoLine() {
-                       line--
+               line := idx
+               if !t.headerFirst {
+                       line++
+                       if !t.noInfoLine() {
+                               line++
+                       }
                }
                if line >= max {
                        continue
@@ -2642,7 +2669,7 @@
                                                        }
                                                }
                                        } else if me.Down {
-                                               if my == 0 && mx >= 0 {
+                                               if my == t.promptLine() && mx 
>= 0 {
                                                        // Prompt
                                                        t.cx = mx + t.xoffset
                                                } else if my >= min {
@@ -2673,6 +2700,7 @@
                                        command := t.replacePlaceholder(a.a, 
false, string(t.input), list)
                                        newCommand = &command
                                        t.reading = true
+                                       t.version++
                                }
                        case actUnbind:
                                keys := parseKeyChords(a.a, "PANIC")
@@ -2748,9 +2776,26 @@
 
        t.cy = util.Constrain(t.cy, 0, count-1)
 
-       minOffset := t.cy - height + 1
+       minOffset := util.Max(t.cy-height+1, 0)
        maxOffset := util.Max(util.Min(count-height, t.cy), 0)
        t.offset = util.Constrain(t.offset, minOffset, maxOffset)
+       if t.scrollOff == 0 {
+               return
+       }
+
+       scrollOff := util.Min(height/2, t.scrollOff)
+       for {
+               prevOffset := t.offset
+               if t.cy-t.offset < scrollOff {
+                       t.offset = util.Max(minOffset, t.offset-1)
+               }
+               if t.cy-t.offset >= height-scrollOff {
+                       t.offset = util.Min(maxOffset, t.offset+1)
+               }
+               if t.offset == prevOffset {
+                       break
+               }
+       }
 }
 
 func (t *Terminal) vmove(o int, allowCycle bool) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/src/terminal_test.go 
new/fzf-0.28.0/src/terminal_test.go
--- old/fzf-0.27.3/src/terminal_test.go 2021-10-15 16:59:56.000000000 +0200
+++ new/fzf-0.28.0/src/terminal_test.go 2021-11-03 17:05:07.000000000 +0100
@@ -288,6 +288,7 @@
                {give{`grep {} ~/test`, ``, newItems(`~`)}, want{output: `grep 
'~' ~/test`}},
 
                // 2) problematic examples
+               // (not necessarily unexpected)
 
                // paths that need to expand some part of it won't work 
(special characters and variables)
                {give{`cat {}`, ``, newItems(`~/test`)}, want{output: `cat 
'~/test'`}},
@@ -315,6 +316,7 @@
                {give{`rg -- {}`, ``, newItems(`"C:\test.txt"`)}, want{output: 
`rg -- ^"\^"C:\\test.txt\^"^"`}},
 
                // 2) problematic examples
+               // (not necessarily unexpected)
 
                // notepad++'s parser can't handle `-n"12"` generate by fzf, 
expects `-n12`
                {give{`notepad++ -n{1} {2}`, ``, newItems(`12   C:\Work\Test 
Folder\File.txt`)}, want{output: `notepad++ -n^"12^" ^"C:\\Work\\Test 
Folder\\File.txt^"`}},
@@ -327,11 +329,97 @@
 
                // the "file" flag in the pattern won't create *.bat or *.cmd 
file so the command in the output tries to edit the file, instead of executing 
it
                // the temp file contains: `cat "C:\test.txt"`
+               // TODO this should actually work
                {give{`cmd /c {f}`, ``, newItems(`cat "C:\test.txt"`)}, 
want{match: `^cmd /c .*\fzf-preview-[0-9]{9}$`}},
        }
        testCommands(t, tests)
 }
 
+// purpose of this test is to demonstrate some shortcomings of fzf's 
templating system on Windows in Powershell
+func TestPowershellCommands(t *testing.T) {
+       if !util.IsWindows() {
+               t.SkipNow()
+       }
+
+       tests := []testCase{
+               // reference: give{template, query, items}, want{output OR 
match}
+
+               /*
+                       You can read each line in the following table as a 
pipeline that
+                       consist of series of parsers that act upon your input 
(col. 1) and
+                       each cell represents the output value.
+
+                       For example:
+                        - exec.Command("program.exe", `\''`)
+                          - goes to win32 api which will process it 
transparently as it contains no special characters, see [CommandLineToArgvW][].
+                            - powershell command will receive it as is, that 
is two arguments: a literal backslash and empty string in single quotes
+                            - native command run via/from powershell will 
receive only one argument: a literal backslash. Because extra parsing rules 
apply, see [NativeCallsFromPowershell][].
+                              - some?? apps have internal parser, that 
requires one more level of escaping (yes, this is completely 
application-specific, but see terminal_test.go#TestWindowsCommands)
+
+                       Character???   CommandLineToArgvW   Powershell commands 
             Native commands from Powershell   Apps requiring escapes??    | 
Being tested below
+                       ----------   ------------------   
------------------------------   -------------------------------   
-------------------------- | ------------------
+                       "            empty string??        missing argument 
error           ...                               ...                        |
+                       \"           literal "            unbalanced quote 
error           ...                               ...                        |
+                       '\"'         literal '"'          literal "             
           empty string                      empty string (match all)   | yes
+                       '\\\"'       literal '\"'         literal \"            
           literal "                         literal "                  |
+                       ----------   ------------------   
------------------------------   -------------------------------   
-------------------------- | ------------------
+                       \            transparent          transparent           
           transparent                       regex error                |
+                       '\'          transparent          literal \             
           literal \                         regex error                | yes
+                       \\           transparent          transparent           
           transparent                       literal \                  |
+                       '\\'         transparent          literal \\            
           literal \\                        literal \                  |
+                       ----------   ------------------   
------------------------------   -------------------------------   
-------------------------- | ------------------
+                       '            transparent          unbalanced quote 
error           ...                               ...                        |
+                       \'           transparent          literal \ and unb. 
quote error   ...                               ...                        |
+                       \''          transparent          literal \ and empty 
string       literal \                         regex error                | no, 
but given as example above
+                       '''          transparent          unbalanced quote 
error           ...                               ...                        |
+                       ''''         transparent          literal '             
           literal '                         literal '                  | yes
+                       ----------   ------------------   
------------------------------   -------------------------------   
-------------------------- | ------------------
+
+                       ???: charatecter or characters 'x' as an argument to a 
program in go's call: exec.Command("program.exe", `x`)
+                       ??: native commands like grep, git grep, ripgrep
+                       ??: interpreted as a grouping quote, affects argument 
parser and gets removed from the result
+
+                       [CommandLineToArgvW]: 
https://docs.microsoft.com/en-gb/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw#remarks
+                       [NativeCallsFromPowershell]: 
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing?view=powershell-7.1#passing-arguments-that-contain-quote-characters
+               */
+
+               // 1) working examples
+
+               {give{`Get-Content {}`, ``, newItems(`C:\test.txt`)}, 
want{output: `Get-Content 'C:\test.txt'`}},
+               {give{`rg -- "package" {}`, ``, newItems(`.\test.go`)}, 
want{output: `rg -- "package" '.\test.go'`}},
+
+               // example of escaping single quotes
+               {give{`rg -- {}`, ``, newItems(`'foobar'`)}, want{output: `rg 
-- '''foobar'''`}},
+
+               // chaining powershells
+               {give{`powershell -NoProfile -Command {}`, ``, newItems(`cat 
"C:\test.txt"`)}, want{output: `powershell -NoProfile -Command 'cat 
\"C:\test.txt\"'`}},
+
+               // 2) problematic examples
+               // (not necessarily unexpected)
+
+               // looking for a path string will only work with escaped 
backslashes
+               {give{`rg -- {}`, ``, newItems(`C:\test.txt`)}, want{output: 
`rg -- 'C:\test.txt'`}},
+               // looking for a literal double quote will only work with 
triple escaped double quotes
+               {give{`rg -- {}`, ``, newItems(`"C:\test.txt"`)}, want{output: 
`rg -- '\"C:\test.txt\"'`}},
+
+               // Get-Content (i.e. cat alias) is parsing `"` as a part of the 
file path, returns an error:
+               // Get-Content : Cannot find drive. A drive with the name '"C:' 
does not exist.
+               {give{`cat {}`, ``, newItems(`"C:\test.txt"`)}, want{output: 
`cat '\"C:\test.txt\"'`}},
+
+               // the "file" flag in the pattern won't create *.ps1 file so 
the powershell will offload this "unknown" filetype
+               // to explorer, which will prompt user to pick editing program 
for the fzf-preview file
+               // the temp file contains: `cat "C:\test.txt"`
+               // TODO this should actually work
+               {give{`powershell -NoProfile -Command {f}`, ``, newItems(`cat 
"C:\test.txt"`)}, want{match: `^powershell -NoProfile -Command 
.*\fzf-preview-[0-9]{9}$`}},
+       }
+
+       // to force powershell-style escaping we temporarily set environment 
variable that fzf honors
+       shellBackup := os.Getenv("SHELL")
+       os.Setenv("SHELL", "powershell")
+       testCommands(t, tests)
+       os.Setenv("SHELL", shellBackup)
+}
+
 /*
        Test typical valid placeholders and parsing of them.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/src/terminal_windows.go 
new/fzf-0.28.0/src/terminal_windows.go
--- old/fzf-0.27.3/src/terminal_windows.go      2021-10-15 16:59:56.000000000 
+0200
+++ new/fzf-0.28.0/src/terminal_windows.go      2021-11-03 17:05:07.000000000 
+0100
@@ -21,10 +21,25 @@
 }
 
 func quoteEntry(entry string) string {
-       escaped := strings.Replace(entry, `\`, `\\`, -1)
-       escaped = `"` + strings.Replace(escaped, `"`, `\"`, -1) + `"`
-       r, _ := regexp.Compile(`[&|<>()@^%!"]`)
-       return r.ReplaceAllStringFunc(escaped, func(match string) string {
-               return "^" + match
-       })
+       shell := os.Getenv("SHELL")
+       if len(shell) == 0 {
+               shell = "cmd"
+       }
+
+       if strings.Contains(shell, "cmd") {
+               // backslash escaping is done here for applications
+               // (see ripgrep test case in 
terminal_test.go#TestWindowsCommands)
+               escaped := strings.Replace(entry, `\`, `\\`, -1)
+               escaped = `"` + strings.Replace(escaped, `"`, `\"`, -1) + `"`
+               // caret is the escape character for cmd shell
+               r, _ := regexp.Compile(`[&|<>()@^%!"]`)
+               return r.ReplaceAllStringFunc(escaped, func(match string) 
string {
+                       return "^" + match
+               })
+       } else if strings.Contains(shell, "pwsh") || strings.Contains(shell, 
"powershell") {
+               escaped := strings.Replace(entry, `"`, `\"`, -1)
+               return "'" + strings.Replace(escaped, "'", "''", -1) + "'"
+       } else {
+               return "'" + strings.Replace(entry, "'", "'\\''", -1) + "'"
+       }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/src/util/util_windows.go 
new/fzf-0.28.0/src/util/util_windows.go
--- old/fzf-0.27.3/src/util/util_windows.go     2021-10-15 16:59:56.000000000 
+0200
+++ new/fzf-0.28.0/src/util/util_windows.go     2021-11-03 17:05:07.000000000 
+0100
@@ -6,23 +6,57 @@
        "fmt"
        "os"
        "os/exec"
+       "strings"
+       "sync/atomic"
        "syscall"
 )
 
-// ExecCommand executes the given command with cmd
+var shellPath atomic.Value
+
+// ExecCommand executes the given command with $SHELL
 func ExecCommand(command string, setpgid bool) *exec.Cmd {
-       return ExecCommandWith("cmd", command, setpgid)
+       var shell string
+       if cached := shellPath.Load(); cached != nil {
+               shell = cached.(string)
+       } else {
+               shell = os.Getenv("SHELL")
+               if len(shell) == 0 {
+                       shell = "cmd"
+               } else if strings.Contains(shell, "/") {
+                       out, err := exec.Command("cygpath", "-w", 
shell).Output()
+                       if err == nil {
+                               shell = strings.Trim(string(out), "\n")
+                       }
+               }
+               shellPath.Store(shell)
+       }
+       return ExecCommandWith(shell, command, setpgid)
 }
 
-// ExecCommandWith executes the given command with cmd. _shell parameter is
-// ignored on Windows.
+// ExecCommandWith executes the given command with the specified shell
 // FIXME: setpgid is unused. We set it in the Unix implementation so that we
 // can kill preview process with its child processes at once.
-func ExecCommandWith(_shell string, command string, setpgid bool) *exec.Cmd {
-       cmd := exec.Command("cmd")
+// NOTE: For "powershell", we should ideally set output encoding to UTF8,
+// but it is left as is now because no adverse effect has been observed.
+func ExecCommandWith(shell string, command string, setpgid bool) *exec.Cmd {
+       var cmd *exec.Cmd
+       if strings.Contains(shell, "cmd") {
+               cmd = exec.Command(shell)
+               cmd.SysProcAttr = &syscall.SysProcAttr{
+                       HideWindow:    false,
+                       CmdLine:       fmt.Sprintf(` /v:on/s/c "%s"`, command),
+                       CreationFlags: 0,
+               }
+               return cmd
+       }
+
+       if strings.Contains(shell, "pwsh") || strings.Contains(shell, 
"powershell") {
+               cmd = exec.Command(shell, "-NoProfile", "-Command", command)
+       } else {
+               cmd = exec.Command(shell, "-c", command)
+       }
        cmd.SysProcAttr = &syscall.SysProcAttr{
                HideWindow:    false,
-               CmdLine:       fmt.Sprintf(` /v:on/s/c "%s"`, command),
                CreationFlags: 0,
        }
        return cmd
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.27.3/test/test_go.rb 
new/fzf-0.28.0/test/test_go.rb
--- old/fzf-0.27.3/test/test_go.rb      2021-10-15 16:59:56.000000000 +0200
+++ new/fzf-0.28.0/test/test_go.rb      2021-11-03 17:05:07.000000000 +0100
@@ -2069,6 +2069,79 @@
     tmux.send_keys :Up
     tmux.until { |lines| assert_includes lines[1], '[[99]]' }
   end
+
+  def test_reload_should_update_preview
+    tmux.send_keys "seq 3 | #{FZF} --bind 'ctrl-t:reload:echo 4' --preview 
'echo {}' --preview-window 'nohidden'", :Enter
+    tmux.until { |lines| assert_includes lines[1], '1' }
+    tmux.send_keys 'C-t'
+    tmux.until { |lines| assert_includes lines[1], '4' }
+  end
+
+  def test_scroll_off
+    tmux.send_keys "seq 1000 | #{FZF} --scroll-off=3 --bind l:last", :Enter
+    tmux.until { |lines| assert_equal 1000, lines.item_count }
+    height = tmux.until { |lines| lines }.first.to_i
+    tmux.send_keys :PgUp
+    tmux.until do |lines|
+      assert_equal height + 3, lines.first.to_i
+      assert_equal "> #{height}", lines[3].strip
+    end
+    tmux.send_keys :Up
+    tmux.until { |lines| assert_equal "> #{height + 1}", lines[3].strip }
+    tmux.send_keys 'l'
+    tmux.until { |lines| assert_equal '> 1000', lines.first.strip }
+    tmux.send_keys :PgDn
+    tmux.until { |lines| assert_equal "> #{1000 - height + 1}", 
lines.reverse[5].strip }
+    tmux.send_keys :Down
+    tmux.until { |lines| assert_equal "> #{1000 - height}", 
lines.reverse[5].strip }
+  end
+
+  def test_scroll_off_large
+    tmux.send_keys "seq 1000 | #{FZF} --scroll-off=9999", :Enter
+    tmux.until { |lines| assert_equal 1000, lines.item_count }
+    height = tmux.until { |lines| lines }.first.to_i
+    tmux.send_keys :PgUp
+    tmux.until { |lines| assert_equal "> #{height}", lines[height / 2].strip }
+    tmux.send_keys :Up
+    tmux.until { |lines| assert_equal "> #{height + 1}", lines[height / 
2].strip }
+    tmux.send_keys :Up
+    tmux.until { |lines| assert_equal "> #{height + 2}", lines[height / 
2].strip }
+    tmux.send_keys :Down
+    tmux.until { |lines| assert_equal "> #{height + 1}", lines[height / 
2].strip }
+  end
+
+  def test_header_first
+    tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 
--header-first", :Enter
+    tmux.until do |lines|
+      expected = <<~OUTPUT
+        > 4
+          997/997
+        >
+          3
+          2
+          1
+          foobar
+      OUTPUT
+
+      assert_equal expected.chomp, lines.reverse.take(7).reverse.join("\n")
+    end
+  end
+
+  def test_header_first_reverse
+    tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 
--header-first --reverse --inline-info", :Enter
+    tmux.until do |lines|
+      expected = <<~OUTPUT
+          foobar
+          1
+          2
+          3
+        >   < 997/997
+        > 4
+      OUTPUT
+
+      assert_equal expected.chomp, lines.take(6).join("\n")
+    end
+  end
 end
 
 module TestShell

Reply via email to