Merge remote-tracking branch 'upstream/master'

pull/2131/head
MenkeTechnologies 5 months ago
commit e5b7033c5d

@ -11,4 +11,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@v3
- name: 'Dependency Review'
uses: actions/dependency-review-action@v3
uses: actions/dependency-review-action@v4

@ -7,4 +7,4 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: crate-ci/typos@v1.16.4
- uses: crate-ci/typos@v1.17.2

@ -29,6 +29,7 @@ Advanced fzf examples
* [Branches](#branches)
* [Commit hashes](#commit-hashes)
* [Color themes](#color-themes)
* [fzf Theme Playground](#fzf-theme-playground)
* [Generating fzf color theme from Vim color schemes](#generating-fzf-color-theme-from-vim-color-schemes)
<!-- vim-markdown-toc -->
@ -221,17 +222,17 @@ To make a key binding behave differently each time it is pressed, we need:
2. and a way to dynamically perform different actions depending on the state.
The following example shows how to 1. store the current mode in the prompt
string, 2. and use this information (`{fzf:prompt}`) to determine which
string, 2. and use this information (`$FZF_PROMPT`) to determine which
actions to perform using the `transform` action.
```sh
fd --type file |
fzf --prompt 'Files> ' \
--header 'CTRL-T: Switch between Files/Directories' \
--bind 'ctrl-t:transform:[[ ! {fzf:prompt} =~ Files ]] &&
--bind 'ctrl-t:transform:[[ ! $FZF_PROMPT =~ Files ]] &&
echo "change-prompt(Files> )+reload(fd --type file)" ||
echo "change-prompt(Directories> )+reload(fd --type directory)"' \
--preview '[[ {fzf:prompt} =~ Files ]] && bat --color=always {} || tree -C {}'
--preview '[[ $FZF_PROMPT =~ Files ]] && bat --color=always {} || tree -C {}'
```
Ripgrep integration
@ -632,6 +633,12 @@ export FZF_DEFAULT_OPTS='--color=bg+:#293739,bg:#1B1D1E,border:#808080,spinner:#
![molokai](https://user-images.githubusercontent.com/700826/113475085-8619f300-94ae-11eb-85e4-2766fc3246bf.png)
### fzf Theme Playground
[fzf Theme Playground](https://vitormv.github.io/fzf-themes/) created by
[Vitor Mello](https://github.com/vitormv) is a webpage where you can
interactively create fzf themes.
### Generating fzf color theme from Vim color schemes
The Vim plugin of fzf can generate `--color` option from the current color

@ -34,8 +34,8 @@ make release
Third-party libraries used
--------------------------
- [mattn/go-runewidth](https://github.com/mattn/go-runewidth)
- Licensed under [MIT](http://mattn.mit-license.org)
- [rivo/uniseg](https://github.com/rivo/uniseg)
- Licensed under [MIT](https://raw.githubusercontent.com/rivo/uniseg/master/LICENSE.txt)
- [mattn/go-shellwords](https://github.com/mattn/go-shellwords)
- Licensed under [MIT](http://mattn.mit-license.org)
- [mattn/go-isatty](https://github.com/mattn/go-isatty)

@ -1,9 +1,11 @@
CHANGELOG
=========
0.46.0 (WIP)
0.46.0
------
- Added `result` event that is triggered when the filtering for the current query is complete and the result list is ready.
- Added two new events
- `result` - triggered when the filtering for the current query is complete and the result list is ready
- `resize` - triggered when the terminal size is changed
- fzf now exports the following environment variables to the child processes
| Variable | Description |
| --- | --- |
@ -17,8 +19,8 @@ CHANGELOG
| `FZF_ACTION` | The name of the last action performed |
- This allows you to write sophisticated transformations like so
```sh
# Dynamically resize preview window
seq 10000 | fzf --bind 'result:transform:
# Script to dynamically resize the preview window
transformer='
# 1 line for info, another for prompt, and 2 more lines for preview window border
lines=$(( FZF_LINES - FZF_MATCH_COUNT - 4 ))
if [[ $FZF_MATCH_COUNT -eq 0 ]]; then
@ -28,9 +30,15 @@ CHANGELOG
elif [[ $FZF_PREVIEW_LINES -ne 3 ]]; then
echo "change-preview-window:3"
fi
' --preview 'seq {} 10000' --preview-window up
'
seq 10000 | fzf --preview 'seq {} 10000' --preview-window up \
--bind "result:transform:$transformer" \
--bind "resize:transform:$transformer"
```
- And we're phasing out `{fzf:prompt}` and `{fzf:action}`
- Changed [mattn/go-runewidth](https://github.com/mattn/go-runewidth) dependency to [rivo/uniseg](https://github.com/rivo/uniseg) for accurate results
- Set `--ambidouble` if your terminal displays ambiguous width characters (e.g. box-drawing characters for borders) as 2 columns
- `RUNEWIDTH_EASTASIAN=1` is still respected for backward compatibility, but it's recommended that you use this new option instead
- Bug fixes
0.45.0

File diff suppressed because one or more lines are too long

@ -2,10 +2,9 @@ module github.com/junegunn/fzf
require (
github.com/gdamore/tcell/v2 v2.5.4
github.com/junegunn/go-runewidth v0.0.15-0.20240119074001-7d2ea235ec54
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2
github.com/mattn/go-isatty v0.0.17
github.com/mattn/go-shellwords v1.0.12
github.com/rivo/uniseg v0.4.4
github.com/saracen/walker v0.1.3
golang.org/x/sys v0.16.0
golang.org/x/term v0.16.0
@ -15,6 +14,7 @@ require (
github.com/gdamore/encoding v1.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/text v0.5.0 // indirect
)

@ -2,8 +2,8 @@ github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdk
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/k=
github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw=
github.com/junegunn/go-runewidth v0.0.15-0.20240119074001-7d2ea235ec54 h1:eBbXiL4VPihi5C8DhESYtax8HYfgCDUkzVYigADhkzU=
github.com/junegunn/go-runewidth v0.0.15-0.20240119074001-7d2ea235ec54/go.mod h1:Mq6NazeZhIIQPMFoInCi35AktcN/MuW2elHsDK5N52w=
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2 h1:oEwPBh29BPu1MaTsz2dM9bDrkOgKBoYFC0u6uY2izWo=
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2/go.mod h1:ywqF55XaSE3/uS2tkJqVFKiE0oIYAXRvU2N7DU4y3XQ=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=

@ -2,7 +2,7 @@
set -u
version=0.45.0
version=0.46.0
auto_completion=
key_bindings=
update_config=2

@ -1,4 +1,4 @@
$version="0.45.0"
$version="0.46.0"
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition

@ -5,7 +5,7 @@ import (
"github.com/junegunn/fzf/src/protector"
)
var version string = "0.45"
var version string = "0.46"
var revision string = "devel"
func main() {

@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
..
.TH fzf-tmux 1 "Jan 2024" "fzf 0.45.0" "fzf-tmux - open fzf in tmux split pane"
.TH fzf-tmux 1 "Jan 2024" "fzf 0.46.0" "fzf-tmux - open fzf in tmux split pane"
.SH NAME
fzf-tmux - open fzf in tmux split pane

@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
..
.TH fzf 1 "Jan 2024" "fzf 0.45.0" "fzf - a command-line fuzzy finder"
.TH fzf 1 "Jan 2024" "fzf 0.46.0" "fzf - a command-line fuzzy finder"
.SH NAME
fzf - a command-line fuzzy finder
@ -260,9 +260,8 @@ Draw border around the finder
.br
If you use a terminal emulator where each box-drawing character takes
2 columns, try setting \fBRUNEWIDTH_EASTASIAN\fR environment variable to
\fB0\fR or \fB1\fR. If the border is still not properly rendered, set
\fB--no-unicode\fR.
2 columns, try setting \fB--ambidouble\fR. If the border is still not properly
rendered, set \fB--no-unicode\fR.
.TP
.BI "--border-label" [=LABEL]
@ -313,6 +312,11 @@ the label. Label is printed on the top border line by default, add
Use ASCII characters instead of Unicode drawing characters to draw borders,
the spinner and the horizontal separator.
.TP
.B "--ambidouble"
Set this option if your terminal displays ambiguous width characters (e.g.
box-drawing characters for borders) as 2 columns.
.TP
.BI "--margin=" MARGIN
Comma-separated expression for margins around the finder.
@ -827,12 +831,14 @@ e.g.
\fB# Start HTTP server on port 6266
fzf --listen 6266
# Get program state in JSON format (experimental)
curl localhost:6266
# Send action to the server
curl -XPOST localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )'
# Get program state in JSON format (experimental)
# * Make sure NOT to access this endpoint from execute/transform actions
# as it will result in a timeout
curl localhost:6266
# Start HTTP server on port 6266 with remote connections allowed
# * Listening on non-localhost address requires using an API key
export FZF_API_KEY="$(head -c 32 /dev/urandom | base64)"
@ -1106,6 +1112,13 @@ e.g.
\fB# Change the prompt to "loaded" when the input stream is complete
(seq 10; sleep 1; seq 11 20) | fzf --prompt 'Loading> ' --bind 'load:change-prompt:Loaded> '\fR
.RE
\fIresize\fR
.RS
Triggered when the terminal size is changed.
e.g.
\fBfzf --bind 'resize:transform-header:echo Resized: ${FZF_COLUMNS}x${FZF_LINES}'\fR
.RE
\fIresult\fR
.RS
Triggered when the filtering for the current query is complete and the result list is ready.

@ -11,8 +11,8 @@ import (
"github.com/junegunn/fzf/src/algo"
"github.com/junegunn/fzf/src/tui"
"github.com/junegunn/fzf/src/util"
"github.com/junegunn/uniseg"
"github.com/junegunn/go-runewidth"
"github.com/mattn/go-shellwords"
)
@ -337,6 +337,7 @@ type Options struct {
BorderLabel labelOpts
PreviewLabel labelOpts
Unicode bool
Ambidouble bool
Tabstop int
ListenAddr *listenAddress
Unsafe bool
@ -406,6 +407,7 @@ func defaultOptions() *Options {
Margin: defaultMargin(),
Padding: defaultMargin(),
Unicode: true,
Ambidouble: os.Getenv("RUNEWIDTH_EASTASIAN") == "1",
Tabstop: 8,
BorderLabel: labelOpts{},
PreviewLabel: labelOpts{},
@ -652,6 +654,8 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E
add(tui.Focus)
case "result":
add(tui.Result)
case "resize":
add(tui.Resize)
case "one":
add(tui.One)
case "zero":
@ -1593,8 +1597,6 @@ func parseOptions(opts *Options, allArgs []string) {
}
}
validateJumpLabels := false
validatePointer := false
validateMarker := false
for i := 0; i < len(allArgs); i++ {
arg := allArgs[i]
switch arg {
@ -1774,10 +1776,8 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Prompt = nextString(allArgs, &i, "prompt string required")
case "--pointer":
opts.Pointer = firstLine(nextString(allArgs, &i, "pointer sign string required"))
validatePointer = true
case "--marker":
opts.Marker = firstLine(nextString(allArgs, &i, "selected sign string required"))
validateMarker = true
case "--sync":
opts.Sync = true
case "--no-sync":
@ -1845,6 +1845,10 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Unicode = false
case "--unicode":
opts.Unicode = true
case "--ambidouble":
opts.Ambidouble = true
case "--no-ambidouble":
opts.Ambidouble = false
case "--margin":
opts.Margin = parseMargin(
"margin",
@ -1903,10 +1907,8 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Prompt = value
} else if match, value := optString(arg, "--pointer="); match {
opts.Pointer = firstLine(value)
validatePointer = true
} else if match, value := optString(arg, "--marker="); match {
opts.Marker = firstLine(value)
validateMarker = true
} else if match, value := optString(arg, "-n", "--nth="); match {
opts.Nth = splitNth(value)
} else if match, value := optString(arg, "--with-nth="); match {
@ -2013,31 +2015,31 @@ func parseOptions(opts *Options, allArgs []string) {
}
}
}
if validatePointer {
if err := validateSign(opts.Pointer, "pointer"); err != nil {
errorExit(err.Error())
}
}
if validateMarker {
if err := validateSign(opts.Marker, "marker"); err != nil {
errorExit(err.Error())
}
}
}
func validateSign(sign string, signOptName string) error {
if sign == "" {
return fmt.Errorf("%v cannot be empty", signOptName)
}
if runewidth.StringWidth(sign) > 2 {
if uniseg.StringWidth(sign) > 2 {
return fmt.Errorf("%v display width should be up to 2", signOptName)
}
return nil
}
func postProcessOptions(opts *Options) {
if opts.Ambidouble {
uniseg.EastAsianAmbiguousWidth = 2
}
if err := validateSign(opts.Pointer, "pointer"); err != nil {
errorExit(err.Error())
}
if err := validateSign(opts.Marker, "marker"); err != nil {
errorExit(err.Error())
}
if !opts.Version && !tui.IsLightRendererSupported() && opts.Height.size > 0 {
errorExit("--height option is currently not supported on this platform")
}
@ -2048,7 +2050,7 @@ func postProcessOptions(opts *Options) {
errorExit("--scrollbar should be given one or two characters")
}
for _, r := range runes {
if runewidth.RuneWidth(r) != 1 {
if uniseg.StringWidth(string(r)) != 1 {
errorExit("scrollbar display width should be 1")
}
}

@ -138,7 +138,9 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme,
for i := off[0]; i < off[1]; i++ {
// Negative of 1-based index of itemColors
// - The extra -1 means highlighted
cols[i] = cols[i]*-1 - 1
if cols[i] >= 0 {
cols[i] = cols[i]*-1 - 1
}
}
}

@ -120,7 +120,7 @@ func TestColorOffset(t *testing.T) {
// ++++++++ ++++++++++
// --++++++++-- --++++++++++---
offsets := []Offset{{5, 15}, {25, 35}}
offsets := []Offset{{5, 15}, {10, 12}, {25, 35}}
item := Result{
item: &Item{
colors: &[]ansiOffset{

@ -30,7 +30,9 @@ const (
httpOk = "HTTP/1.1 200 OK" + crlf
httpBadRequest = "HTTP/1.1 400 Bad Request" + crlf
httpUnauthorized = "HTTP/1.1 401 Unauthorized" + crlf
httpUnavailable = "HTTP/1.1 503 Service Unavailable" + crlf
httpReadTimeout = 10 * time.Second
jsonContentType = "Content-Type: application/json" + crlf
maxContentLength = 1024 * 1024
)
@ -141,7 +143,7 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
return answer(httpBadRequest, message)
}
good := func(message string) string {
return answer(httpOk+"Content-Type: application/json"+crlf, message)
return answer(httpOk+jsonContentType, message)
}
conn.SetReadDeadline(time.Now().Add(httpReadTimeout))
scanner := bufio.NewScanner(conn)
@ -165,8 +167,16 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
getMatch := getRegex.FindStringSubmatch(text)
if len(getMatch) > 0 {
server.actionChannel <- []*action{{t: actResponse, a: getMatch[1]}}
response := <-server.responseChannel
return good(response)
select {
case response := <-server.responseChannel:
return good(response)
case <-time.After(2 * time.Second):
go func() {
// Drain the channel
<-server.responseChannel
}()
return answer(httpUnavailable+jsonContentType, `{"error":"timeout"}`)
}
} else if !strings.HasPrefix(text, "POST / HTTP") {
return bad("invalid request method")
}

@ -17,8 +17,7 @@ import (
"syscall"
"time"
"github.com/junegunn/go-runewidth"
"github.com/rivo/uniseg"
"github.com/junegunn/uniseg"
"github.com/junegunn/fzf/src/tui"
"github.com/junegunn/fzf/src/util"
@ -254,6 +253,7 @@ type Terminal struct {
hasResultActions bool
hasFocusActions bool
hasLoadActions bool
hasResizeActions bool
triggerLoad bool
reading bool
running bool
@ -534,7 +534,6 @@ func defaultKeymap() map[tui.Event][]*action {
}
add(tui.Invalid, actInvalid)
add(tui.Resize, actClearScreen)
add(tui.CtrlA, actBeginningOfLine)
add(tui.CtrlB, actBackwardChar)
add(tui.CtrlC, actAbort)
@ -774,7 +773,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
killChan: make(chan int),
serverInputChan: make(chan []*action, 10),
serverOutputChan: make(chan string),
eventChan: make(chan tui.Event, 5), // (load + result + zero|one) | (focus) | (GetChar)
eventChan: make(chan tui.Event, 6), // (load + result + zero|one) | (focus) | (resize) | (GetChar)
tui: renderer,
initFunc: func() { renderer.Init() },
executing: util.NewAtomicBool(false),
@ -798,7 +797,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
t.separator, t.separatorLen = t.ansiLabelPrinter(bar, &tui.ColSeparator, true)
}
if t.unicode {
t.borderWidth = runewidth.RuneWidth('│')
t.borderWidth = uniseg.StringWidth("│")
}
if opts.Scrollbar == nil {
if t.unicode && t.borderWidth == 1 {
@ -818,6 +817,11 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
}
}
var resizeActions []*action
resizeActions, t.hasResizeActions = t.keymap[tui.Resize.AsEvent()]
if t.tui.ShouldEmitResizeEvent() {
t.keymap[tui.Resize.AsEvent()] = append(toActions(actClearScreen), resizeActions...)
}
_, t.hasResultActions = t.keymap[tui.Result.AsEvent()]
_, t.hasFocusActions = t.keymap[tui.Focus.AsEvent()]
_, t.hasLoadActions = t.keymap[tui.Load.AsEvent()]
@ -2852,14 +2856,16 @@ func (t *Terminal) Loop() {
}
}()
resizeChan := make(chan os.Signal, 1)
notifyOnResize(resizeChan) // Non-portable
go func() {
for {
<-resizeChan
t.reqBox.Set(reqResize, nil)
}
}()
if !t.tui.ShouldEmitResizeEvent() {
resizeChan := make(chan os.Signal, 1)
notifyOnResize(resizeChan) // Non-portable
go func() {
for {
<-resizeChan
t.reqBox.Set(reqResize, nil)
}
}()
}
t.mutex.Lock()
t.initFunc()
@ -3130,6 +3136,9 @@ func (t *Terminal) Loop() {
if wasHidden && t.hasPreviewWindow() {
refreshPreview(t.previewOpts.command)
}
if req == reqResize && t.hasResizeActions {
t.eventChan <- tui.Resize.AsEvent()
}
case reqClose:
exit(func() int {
if t.output() {
@ -3212,7 +3221,11 @@ func (t *Terminal) Loop() {
}
select {
case event = <-t.eventChan:
needBarrier = !event.Is(tui.Load, tui.Result, tui.Focus, tui.One, tui.Zero)
if t.tui.ShouldEmitResizeEvent() {
needBarrier = !event.Is(tui.Load, tui.Result, tui.Focus, tui.One, tui.Zero)
} else {
needBarrier = !event.Is(tui.Load, tui.Result, tui.Focus, tui.One, tui.Zero, tui.Resize)
}
case serverActions := <-t.serverInputChan:
event = tui.Invalid.AsEvent()
if t.listenAddr == nil || t.listenAddr.IsLocal() || t.listenUnsafe {

@ -36,6 +36,7 @@ func (r *FullscreenRenderer) Resume(bool, bool) {}
func (r *FullscreenRenderer) PassThrough(string) {}
func (r *FullscreenRenderer) Clear() {}
func (r *FullscreenRenderer) NeedScrollbarRedraw() bool { return false }
func (r *FullscreenRenderer) ShouldEmitResizeEvent() bool { return false }
func (r *FullscreenRenderer) Refresh() {}
func (r *FullscreenRenderer) Close() {}
func (r *FullscreenRenderer) Size() TermSize { return TermSize{} }

@ -10,8 +10,7 @@ import (
"time"
"unicode/utf8"
"github.com/junegunn/go-runewidth"
"github.com/rivo/uniseg"
"github.com/junegunn/uniseg"
"golang.org/x/term"
)
@ -695,6 +694,10 @@ func (r *LightRenderer) NeedScrollbarRedraw() bool {
return false
}
func (r *LightRenderer) ShouldEmitResizeEvent() bool {
return false
}
func (r *LightRenderer) RefreshWindows(windows []Window) {
r.flush()
}
@ -804,7 +807,7 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
if w.preview {
color = ColPreviewBorder
}
hw := runewidth.RuneWidth(w.border.top)
hw := runeWidth(w.border.top)
if top {
w.Move(0, 0)
w.CPrint(color, repeat(w.border.top, w.width/hw))
@ -842,13 +845,13 @@ func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
if w.preview {
color = ColPreviewBorder
}
hw := runewidth.RuneWidth(w.border.top)
tcw := runewidth.RuneWidth(w.border.topLeft) + runewidth.RuneWidth(w.border.topRight)
bcw := runewidth.RuneWidth(w.border.bottomLeft) + runewidth.RuneWidth(w.border.bottomRight)
hw := runeWidth(w.border.top)
tcw := runeWidth(w.border.topLeft) + runeWidth(w.border.topRight)
bcw := runeWidth(w.border.bottomLeft) + runeWidth(w.border.bottomRight)
rem := (w.width - tcw) % hw
w.CPrint(color, string(w.border.topLeft)+repeat(w.border.top, (w.width-tcw)/hw)+repeat(' ', rem)+string(w.border.topRight))
if !onlyHorizontal {
vw := runewidth.RuneWidth(w.border.left)
vw := runeWidth(w.border.left)
for y := 1; y < w.height-1; y++ {
w.Move(y, 0)
w.CPrint(color, string(w.border.left))
@ -1020,7 +1023,7 @@ func wrapLine(input string, prefixLength int, max int, tabstop int) []wrappedLin
} else if rs[0] == '\r' {
w++
} else {
w = runewidth.StringWidth(str)
w = uniseg.StringWidth(str)
}
width += w

@ -10,8 +10,7 @@ import (
"github.com/gdamore/tcell/v2/encoding"
"github.com/junegunn/fzf/src/util"
"github.com/junegunn/go-runewidth"
"github.com/rivo/uniseg"
"github.com/junegunn/uniseg"
)
func HasFullscreenRenderer() bool {
@ -203,6 +202,10 @@ func (r *FullscreenRenderer) NeedScrollbarRedraw() bool {
return true
}
func (r *FullscreenRenderer) ShouldEmitResizeEvent() bool {
return true
}
func (r *FullscreenRenderer) Refresh() {
// noop
}
@ -738,7 +741,7 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
style = w.normal.style()
}
hw := runewidth.RuneWidth(w.borderStyle.top)
hw := runeWidth(w.borderStyle.top)
switch shape {
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble, BorderHorizontal, BorderTop:
max := right - 2*hw
@ -773,7 +776,7 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
}
switch shape {
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble, BorderVertical, BorderRight:
vw := runewidth.RuneWidth(w.borderStyle.right)
vw := runeWidth(w.borderStyle.right)
for y := top; y < bot; y++ {
_screen.SetContent(right-vw, y, w.borderStyle.right, nil, style)
}
@ -782,8 +785,8 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
switch shape {
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble:
_screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)
_screen.SetContent(right-runewidth.RuneWidth(w.borderStyle.topRight), top, w.borderStyle.topRight, nil, style)
_screen.SetContent(right-runeWidth(w.borderStyle.topRight), top, w.borderStyle.topRight, nil, style)
_screen.SetContent(left, bot-1, w.borderStyle.bottomLeft, nil, style)
_screen.SetContent(right-runewidth.RuneWidth(w.borderStyle.bottomRight), bot-1, w.borderStyle.bottomRight, nil, style)
_screen.SetContent(right-runeWidth(w.borderStyle.bottomRight), bot-1, w.borderStyle.bottomRight, nil, style)
}
}

@ -5,6 +5,8 @@ import (
"os"
"strconv"
"time"
"github.com/junegunn/uniseg"
)
// Types of user action
@ -492,6 +494,7 @@ type Renderer interface {
Close()
PassThrough(string)
NeedScrollbarRedraw() bool
ShouldEmitResizeEvent() bool
GetChar() Event
@ -812,3 +815,7 @@ func initPalette(theme *ColorTheme) {
ColPreviewScrollbar = pair(theme.PreviewScrollbar, theme.PreviewBg)
ColPreviewSpinner = pair(theme.Spinner, theme.PreviewBg)
}
func runeWidth(r rune) int {
return uniseg.StringWidth(string(r))
}

@ -6,14 +6,13 @@ import (
"strings"
"time"
"github.com/junegunn/go-runewidth"
"github.com/junegunn/uniseg"
"github.com/mattn/go-isatty"
"github.com/rivo/uniseg"
)
// StringWidth returns string width where each CR/LF character takes 1 column
func StringWidth(s string) int {
return runewidth.StringWidth(s) + strings.Count(s, "\n") + strings.Count(s, "\r")
return uniseg.StringWidth(s) + strings.Count(s, "\n") + strings.Count(s, "\r")
}
// RunesWidth returns runes width
@ -165,7 +164,7 @@ func RepeatToFill(str string, length int, limit int) string {
output := strings.Repeat(str, times)
if rest > 0 {
for _, r := range str {
rest -= runewidth.RuneWidth(r)
rest -= uniseg.StringWidth(string(r))
if rest < 0 {
break
}

Loading…
Cancel
Save