From e71e15135f75228b3566f220d24947a3a04ab78a Mon Sep 17 00:00:00 2001 From: koki-develop Date: Wed, 12 Apr 2023 09:29:36 +0900 Subject: [PATCH 01/17] add WithPreviewWindow --- fzf.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fzf.go b/fzf.go index 16da0f4..7a34f11 100644 --- a/fzf.go +++ b/fzf.go @@ -9,7 +9,8 @@ import ( ) var defaultFindOption = findOption{ - itemPrefixFunc: nil, + itemPrefixFunc: nil, + previewWindowFunc: nil, } // Fuzzy Finder. @@ -103,7 +104,8 @@ func (fzf *FZF) Abort() { type FindOption func(o *findOption) type findOption struct { - itemPrefixFunc func(i int) string + itemPrefixFunc func(i int) string + previewWindowFunc func(i, width, height int) string } // WithItemPrefix sets the prefix function of the item. @@ -112,3 +114,10 @@ func WithItemPrefix(f func(i int) string) FindOption { o.itemPrefixFunc = f } } + +// WithPreviewWindow sets the preview window function of the item. +func WithPreviewWindow(f func(i, width, height int) string) FindOption { + return func(o *findOption) { + o.previewWindowFunc = f + } +} From cbde306972f4e027702b22738a2d2ae9d656093c Mon Sep 17 00:00:00 2001 From: koki-develop Date: Wed, 12 Apr 2023 21:10:19 +0900 Subject: [PATCH 02/17] support preview window --- model.go | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/model.go b/model.go index ae88ce8..c738d8d 100644 --- a/model.go +++ b/model.go @@ -45,6 +45,9 @@ type model struct { windowHeight int windowYPosition int + mainViewWidth int + previewWindowWidth int + // components input textinput.Model } @@ -125,6 +128,17 @@ func (m *model) View() string { defer m.option.hotReloadLocker.Unlock() } + views := []string{ + m.mainView(), + } + if m.findOption.previewWindowFunc != nil { + views = append(views, m.previewWindowView()) + } + + return lipgloss.JoinHorizontal(lipgloss.Top, views...) +} + +func (m *model) mainView() string { var v strings.Builder var windowStyle lipgloss.Style @@ -159,7 +173,7 @@ func (m *model) inputView() string { ItemsCount: m.items.Len(), MatchesCount: len(m.matches), SelectedCount: len(m.choices), - WindowWidth: m.windowWidth, + WindowWidth: m.mainViewWidth, Limit: m.option.limit, NoLimit: m.option.noLimit, })) @@ -172,7 +186,7 @@ func (m *model) inputView() string { ItemsCount: m.items.Len(), MatchesCount: len(m.matches), SelectedCount: len(m.choices), - WindowWidth: m.windowWidth, + WindowWidth: m.mainViewWidth, Limit: m.option.limit, NoLimit: m.option.noLimit, })) @@ -240,7 +254,7 @@ func (m *model) itemView(match Match, cursorLine bool) string { _, _ = v.WriteString(stringLinesToSpace(m.findOption.itemPrefixFunc(match.Index))) } - maxItemWidth := m.windowWidth - lipgloss.Width(v.String()) + maxItemWidth := m.mainViewWidth - lipgloss.Width(v.String()) if maxItemWidth < 1 { return v.String() } @@ -307,6 +321,20 @@ func (m *model) itemView(match Match, cursorLine bool) string { return v.String() } +func (m *model) previewWindowView() string { + v := "" + if len(m.matches) > 0 { + v = m.findOption.previewWindowFunc(m.matches[m.cursorPosition].Index, m.previewWindowWidth, m.windowHeight) + } + + return lipgloss.NewStyle(). + Width(m.mainViewWidth). + Height(m.windowHeight). + BorderStyle(lipgloss.NormalBorder()). + BorderLeft(true). + Render(v) +} + /* * update */ @@ -360,9 +388,9 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // window m.windowWidth = msg.Width m.windowHeight = msg.Height - m.input.Width = m.windowWidth - m.promptWidth m.fixYPosition() m.fixCursor() + m.fixWidth() case watchReloadMsg: // watch reload return m, m.watchReload() @@ -475,6 +503,15 @@ func (m *model) fixYPosition() { } } +func (m *model) fixWidth() { + m.mainViewWidth = m.windowWidth + if m.findOption.previewWindowFunc != nil { + m.mainViewWidth /= 2 + m.previewWindowWidth = m.windowWidth - m.mainViewWidth + } + m.input.Width = m.mainViewWidth - m.promptWidth - 1 +} + func (m *model) forceReload() { m.loadItems(m.items) } From f982de149e510fb6e6c7110e004aceb6b36abd26 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Wed, 12 Apr 2023 21:19:33 +0900 Subject: [PATCH 03/17] refactor mainView() --- model.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/model.go b/model.go index c738d8d..b0a7725 100644 --- a/model.go +++ b/model.go @@ -139,24 +139,21 @@ func (m *model) View() string { } func (m *model) mainView() string { - var v strings.Builder + rows := make([]string, 2) - var windowStyle lipgloss.Style + windowStyle := lipgloss.NewStyle().Height(m.windowHeight) switch m.option.inputPosition { case InputPositionTop: - windowStyle = lipgloss.NewStyle().Height(m.windowHeight).AlignVertical(lipgloss.Top) - _, _ = v.WriteString(m.inputView()) - _, _ = v.WriteRune('\n') - _, _ = v.WriteString(m.itemsView()) - + windowStyle = windowStyle.AlignVertical(lipgloss.Top) + rows[0] = m.inputView() + rows[1] = m.itemsView() case InputPositionBottom: - windowStyle = lipgloss.NewStyle().Height(m.windowHeight).AlignVertical(lipgloss.Bottom) - _, _ = v.WriteString(m.itemsView()) - _, _ = v.WriteRune('\n') - _, _ = v.WriteString(m.inputView()) + windowStyle = windowStyle.AlignVertical(lipgloss.Bottom) + rows[0] = m.itemsView() + rows[1] = m.inputView() } - return windowStyle.Render(v.String()) + return windowStyle.Render(strings.Join(rows, "\n")) } func (m *model) inputView() string { From 00f9184c12c7faec6539e6d7eee0062cade1f165 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Wed, 12 Apr 2023 21:27:51 +0900 Subject: [PATCH 04/17] refactor inputView() --- model.go | 56 +++++++++++++++++++++++--------------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/model.go b/model.go index b0a7725..a3b6aaa 100644 --- a/model.go +++ b/model.go @@ -128,9 +128,7 @@ func (m *model) View() string { defer m.option.hotReloadLocker.Unlock() } - views := []string{ - m.mainView(), - } + views := []string{m.mainView()} if m.findOption.previewWindowFunc != nil { views = append(views, m.previewWindowView()) } @@ -153,47 +151,39 @@ func (m *model) mainView() string { rows[1] = m.inputView() } - return windowStyle.Render(strings.Join(rows, "\n")) + return windowStyle.Render(lipgloss.JoinVertical(lipgloss.Left, rows...)) } func (m *model) inputView() string { - var v strings.Builder + rows := []string{} + + countView := "" + countViewEnabled := m.option.countViewEnabled && m.option.countViewFunc != nil + if countViewEnabled { + countView = m.option.countViewFunc(CountViewMeta{ + ItemsCount: m.items.Len(), + MatchesCount: len(m.matches), + SelectedCount: len(m.choices), + WindowWidth: m.mainViewWidth, + Limit: m.option.limit, + NoLimit: m.option.noLimit, + }) + } switch m.option.inputPosition { case InputPositionTop: - // input - _, _ = v.WriteString(m.input.View()) - // count - if m.option.countViewEnabled { - _, _ = v.WriteRune('\n') - _, _ = v.WriteString(m.option.countViewFunc(CountViewMeta{ - ItemsCount: m.items.Len(), - MatchesCount: len(m.matches), - SelectedCount: len(m.choices), - WindowWidth: m.mainViewWidth, - Limit: m.option.limit, - NoLimit: m.option.noLimit, - })) + rows = append(rows, m.input.View()) + if countViewEnabled { + rows = append(rows, countView) } - case InputPositionBottom: - // count - if m.option.countViewEnabled { - _, _ = v.WriteString(m.option.countViewFunc(CountViewMeta{ - ItemsCount: m.items.Len(), - MatchesCount: len(m.matches), - SelectedCount: len(m.choices), - WindowWidth: m.mainViewWidth, - Limit: m.option.limit, - NoLimit: m.option.noLimit, - })) - _, _ = v.WriteRune('\n') + if countViewEnabled { + rows = append(rows, countView) } - // input - _, _ = v.WriteString(m.input.View()) + rows = append(rows, m.input.View()) } - return v.String() + return lipgloss.JoinVertical(lipgloss.Left, rows...) } func (m *model) inputHeight() int { From e3ceb130588e093f3fc738d0cdd768bf84335464 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Wed, 12 Apr 2023 21:28:39 +0900 Subject: [PATCH 05/17] refactor itemsView() --- model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model.go b/model.go index a3b6aaa..6c5119e 100644 --- a/model.go +++ b/model.go @@ -214,7 +214,7 @@ func (m *model) itemsView() string { } } - return strings.Join(rows, "\n") + return lipgloss.JoinVertical(lipgloss.Left, rows...) } func (m *model) itemView(match Match, cursorLine bool) string { From 98993a62288b636ab0e90262ba1b0f04ca005863 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Wed, 12 Apr 2023 21:41:10 +0900 Subject: [PATCH 06/17] remove redundant filter --- model.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/model.go b/model.go index 6c5119e..c296a90 100644 --- a/model.go +++ b/model.go @@ -395,9 +395,11 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmds = append(cmds, cmd) } if beforeValue != m.input.Value() { - m.filter() - m.fixYPosition() - m.fixCursor() + if len(m.matches) > 0 || !strings.HasPrefix(m.input.Value(), beforeValue) { + m.filter() + m.fixYPosition() + m.fixCursor() + } } return m, tea.Batch(cmds...) From 1403e052fd782ca7ccec0ac452a15c55d327b7d6 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Wed, 12 Apr 2023 21:46:21 +0900 Subject: [PATCH 07/17] mainViewWidth -> previewWindowWidth --- model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model.go b/model.go index c296a90..1f6652d 100644 --- a/model.go +++ b/model.go @@ -315,7 +315,7 @@ func (m *model) previewWindowView() string { } return lipgloss.NewStyle(). - Width(m.mainViewWidth). + Width(m.previewWindowWidth). Height(m.windowHeight). BorderStyle(lipgloss.NormalBorder()). BorderLeft(true). From c605667fb6987fbc5d212b6d30745a0ed7ac7137 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Wed, 12 Apr 2023 21:53:01 +0900 Subject: [PATCH 08/17] fix border --- option.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/option.go b/option.go index ef64d78..7cf1e37 100644 --- a/option.go +++ b/option.go @@ -52,7 +52,7 @@ var defaultOption = option{ if borderw < 0 { borderw = 0 } - _, _ = v.WriteString(strings.Repeat("─", borderw)) + _, _ = v.WriteString(strings.Repeat("-", borderw)) return v.String() }, From b547cd35434a8aded7a4b73089dfeec6bda8498a Mon Sep 17 00:00:00 2001 From: koki-develop Date: Wed, 12 Apr 2023 21:53:12 +0900 Subject: [PATCH 09/17] set main view width --- model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model.go b/model.go index 1f6652d..6aa2ce2 100644 --- a/model.go +++ b/model.go @@ -139,7 +139,7 @@ func (m *model) View() string { func (m *model) mainView() string { rows := make([]string, 2) - windowStyle := lipgloss.NewStyle().Height(m.windowHeight) + windowStyle := lipgloss.NewStyle().Height(m.windowHeight).Width(m.mainViewWidth) switch m.option.inputPosition { case InputPositionTop: windowStyle = windowStyle.AlignVertical(lipgloss.Top) From bb57c9b940ab0761ecb044b8a367b0626845368e Mon Sep 17 00:00:00 2001 From: koki-develop Date: Wed, 12 Apr 2023 21:59:38 +0900 Subject: [PATCH 10/17] add examples/preview-window --- examples/preview-window/main.go | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 examples/preview-window/main.go diff --git a/examples/preview-window/main.go b/examples/preview-window/main.go new file mode 100644 index 0000000..029334e --- /dev/null +++ b/examples/preview-window/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "log" + "os" + + "github.com/koki-develop/go-fzf" +) + +func main() { + files, err := os.ReadDir(".") + if err != nil { + log.Fatal(err) + } + + f, err := fzf.New() + if err != nil { + log.Fatal(err) + } + + idxs, err := f.Find( + files, + func(i int) string { return files[i].Name() }, + fzf.WithPreviewWindow(func(i, width, height int) string { + info, _ := files[i].Info() + return fmt.Sprintf( + "Name: %s\nModTime: %s\nSize: %d bytes", + info.Name(), info.ModTime(), info.Size(), + ) + }), + ) + if err != nil { + log.Fatal(err) + } + + for _, i := range idxs { + fmt.Println(files[i].Name()) + } +} From 65485319c40db7ac8b4e93396c523dd1004ea8fa Mon Sep 17 00:00:00 2001 From: koki-develop Date: Wed, 12 Apr 2023 22:00:37 +0900 Subject: [PATCH 11/17] WindowWidth -> Width --- model.go | 2 +- option.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/model.go b/model.go index 6aa2ce2..329996f 100644 --- a/model.go +++ b/model.go @@ -164,7 +164,7 @@ func (m *model) inputView() string { ItemsCount: m.items.Len(), MatchesCount: len(m.matches), SelectedCount: len(m.choices), - WindowWidth: m.mainViewWidth, + Width: m.mainViewWidth, Limit: m.option.limit, NoLimit: m.option.noLimit, }) diff --git a/option.go b/option.go index 7cf1e37..a739fc6 100644 --- a/option.go +++ b/option.go @@ -48,7 +48,7 @@ var defaultOption = option{ _, _ = v.WriteString(") ") } - borderw := meta.WindowWidth - v.Len() + borderw := meta.Width - v.Len() if borderw < 0 { borderw = 0 } @@ -64,7 +64,7 @@ type CountViewMeta struct { ItemsCount int MatchesCount int SelectedCount int - WindowWidth int + Width int Limit int NoLimit bool } From a7851ded2766098e2625975c8532aeacdb1a00c3 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Thu, 13 Apr 2023 17:48:39 +0900 Subject: [PATCH 12/17] update docs --- docs/library/README.ja.md | 35 +++++++++++++++++++++++++++++++++++ docs/library/README.md | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/docs/library/README.ja.md b/docs/library/README.ja.md index fb9a020..a6d2008 100644 --- a/docs/library/README.ja.md +++ b/docs/library/README.ja.md @@ -157,6 +157,7 @@ if err != nil { - [インプットの位置](#インプットの位置) - [インプットのプレースホルダ](#インプットのプレースホルダ) - [カウントビュー](#カウントビュー) +- [プレビューウィンドウ](#プレビューウィンドウ) - [スタイル](#スタイル) #### プロンプト @@ -251,6 +252,40 @@ if err != nil { [Example](/examples/countview/) +#### プレビューウィンドウ + +`Find()` メソッドに `fzf.PreviewWindow()` 渡すとプレビューウィンドウをレンダリングする関数を設定することができます。 +関数の引数にはカーソル行のアイテムのインデックスとプレビューウィンドウのサイズが渡されます。 + +```go +files, err := os.ReadDir(".") +if err != nil { + // ... +} + +f, err := fzf.New() +if err != nil { + // ... +} + +idxs, err := f.Find( + files, + func(i int) string { return files[i].Name() }, + fzf.WithPreviewWindow(func(i, width, height int) string { + info, _ := files[i].Info() + return fmt.Sprintf( + "Name: %s\nModTime: %s\nSize: %d bytes", + info.Name(), info.ModTime(), info.Size(), + ) + }), +) +if err != nil { + // ... +} +``` + +[Example](/examples/preview-window/) + #### スタイル `fzf.WithStyles()` を使用すると各コンポーネントのスタイルを設定することができます。 diff --git a/docs/library/README.md b/docs/library/README.md index 211780f..ee6a8f5 100644 --- a/docs/library/README.md +++ b/docs/library/README.md @@ -158,6 +158,7 @@ if err != nil { - [Position of input](#position-of-input) - [Placeholder for input](#placeholder-for-input) - [Count View](#count-view) +- [Preview Window](#preview-window) - [Styles](#styles) #### Prompt @@ -253,6 +254,40 @@ if err != nil { [Example](/examples/countview/) +#### Preview Window + +You can set a function to render a preview window by passing `fzf.PreviewWindow()` to the `Find()` method. +The function argument is passed the index of the item at the cursor line and the size of the preview window. + +```go +files, err := os.ReadDir(".") +if err != nil { + // ... +} + +f, err := fzf.New() +if err != nil { + // ... +} + +idxs, err := f.Find( + files, + func(i int) string { return files[i].Name() }, + fzf.WithPreviewWindow(func(i, width, height int) string { + info, _ := files[i].Info() + return fmt.Sprintf( + "Name: %s\nModTime: %s\nSize: %d bytes", + info.Name(), info.ModTime(), info.Size(), + ) + }), +) +if err != nil { + // ... +} +``` + +[Example](/examples/preview-window/) + #### Styles `WithStyles()` can be used to set the style of each component. From 7a8e618b65a82d3e6aeb9419c9f3df16904796e6 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Thu, 13 Apr 2023 17:54:20 +0900 Subject: [PATCH 13/17] create tape --- examples/preview-window/README.md | 1 + tapes/library/preview-window.tape | 43 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 examples/preview-window/README.md create mode 100644 tapes/library/preview-window.tape diff --git a/examples/preview-window/README.md b/examples/preview-window/README.md new file mode 100644 index 0000000..b6cb717 --- /dev/null +++ b/examples/preview-window/README.md @@ -0,0 +1 @@ +![](./demo.gif) diff --git a/tapes/library/preview-window.tape b/tapes/library/preview-window.tape new file mode 100644 index 0000000..29fa83a --- /dev/null +++ b/tapes/library/preview-window.tape @@ -0,0 +1,43 @@ +# configuration +Output ./examples/preview-window/demo.gif +Set Shell "bash" +Set FontSize 32 +Set Width 1400 +Set Height 600 + +# setup +Hide +Type "mkdir ./tmp" Enter +Type "cp ./examples/preview-window/main.go ./tmp/main.go" Enter +Type "cd ./tmp" Enter +Type "echo hello > hello.go" Enter +Type "echo world > world.ts" Enter +Type "echo foo > foo.ts" Enter +Type "echo bar > bar.js" Enter +Ctrl+l +Show + +# --- + +Type "go run ./main.go" Sleep 750ms Enter +Sleep 2s + +Down 2 +Sleep 1s + +Up 2 +Sleep 1s + +Type "world" +Sleep 750ms + +Enter + +Sleep 3s + +# --- + +# cleanup +Hide +Type "cd ../" Enter +Type "\rm -rf ./tmp" Enter From c60783d6a5dbb51e20bc102f82affbe888cebe15 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Thu, 13 Apr 2023 18:01:30 +0900 Subject: [PATCH 14/17] Revert "fix border" This reverts commit c605667fb6987fbc5d212b6d30745a0ed7ac7137. --- option.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/option.go b/option.go index a739fc6..930b1ac 100644 --- a/option.go +++ b/option.go @@ -52,7 +52,7 @@ var defaultOption = option{ if borderw < 0 { borderw = 0 } - _, _ = v.WriteString(strings.Repeat("-", borderw)) + _, _ = v.WriteString(strings.Repeat("─", borderw)) return v.String() }, From 1bef1f922317e707fb37164135584ce364769f37 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Thu, 13 Apr 2023 18:03:12 +0900 Subject: [PATCH 15/17] disable east asian width --- model.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/model.go b/model.go index 329996f..8d0967c 100644 --- a/model.go +++ b/model.go @@ -8,6 +8,7 @@ import ( "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/mattn/go-runewidth" ) var ( @@ -105,6 +106,8 @@ func (m *model) setFindOption(findOption *findOption) { } func (m *model) Init() tea.Cmd { + runewidth.DefaultCondition.EastAsianWidth = false + cmds := []tea.Cmd{ textinput.Blink, tea.EnterAltScreen, From 5b81d35d51642b588bb6f6dce7ee83f0517a54d8 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Thu, 13 Apr 2023 18:04:15 +0900 Subject: [PATCH 16/17] fix truncate suffix --- model.go | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/model.go b/model.go index 8d0967c..2b5e251 100644 --- a/model.go +++ b/model.go @@ -251,7 +251,6 @@ func (m *model) itemView(match Match, cursorLine bool) string { runes := []rune(match.Str) from := 0 - to := len(runes) ellipsis := ".." // truncate string @@ -264,24 +263,15 @@ func (m *model) itemView(match Match, cursorLine bool) string { ellipsis = "" } - if len(match.MatchedIndexes) == 0 { - // truncate end - to = maxItemWidth - len(ellipsis) - } else { + if len(match.MatchedIndexes) > 0 { lastMatchedIndex := match.MatchedIndexes[len(match.MatchedIndexes)-1] - if lastMatchedIndex+8+len(ellipsis) < maxItemWidth { - // truncate end - to = maxItemWidth - len(ellipsis) - } else { + if lastMatchedIndex+8+len(ellipsis) >= maxItemWidth { v.WriteString(m.ellipsisStyle.Render(ellipsis)) if lastMatchedIndex+1+8+len(ellipsis) < len(runes) { - // truncate both start and end from = lastMatchedIndex + 1 - maxItemWidth + 8 + len(ellipsis)*2 - to = from + maxItemWidth - len(ellipsis)*2 } else { - // truncate start from = len(runes) - maxItemWidth + len(ellipsis) } } @@ -289,25 +279,37 @@ func (m *model) itemView(match Match, cursorLine bool) string { } // write item - for ci, c := range runes[from:to] { + truncateSuffix := false + truncateAt := maxItemWidth - 3 + if from != 0 { + truncateAt -= 2 + } + var itemv strings.Builder + for ci, c := range runes[from:] { // matches if intContains(match.MatchedIndexes, ci+from) { if cursorLine { - _, _ = v.WriteString(m.cursorLineMatchesStyle.Render(string(c))) + _, _ = itemv.WriteString(m.cursorLineMatchesStyle.Render(string(c))) } else { - _, _ = v.WriteString(m.matchesStyle.Render(string(c))) + _, _ = itemv.WriteString(m.matchesStyle.Render(string(c))) } } else if cursorLine { - _, _ = v.WriteString(m.cursorLineStyle.Render(string(c))) + _, _ = itemv.WriteString(m.cursorLineStyle.Render(string(c))) } else { - _, _ = v.WriteRune(c) + _, _ = itemv.WriteRune(c) + } + + if lipgloss.Width(itemv.String()) >= truncateAt { + truncateSuffix = true + break } } - if to != len(runes) { - v.WriteString(m.ellipsisStyle.Render(ellipsis)) + if truncateSuffix { + _, _ = itemv.WriteString(m.ellipsisStyle.Render(ellipsis)) } + _, _ = v.WriteString(itemv.String()) return v.String() } From f390da735c3b94f152b857ba41a1e8e2356dff08 Mon Sep 17 00:00:00 2001 From: koki-develop Date: Thu, 13 Apr 2023 21:18:29 +0900 Subject: [PATCH 17/17] fix --- model.go | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/model.go b/model.go index 2b5e251..ec8891a 100644 --- a/model.go +++ b/model.go @@ -280,29 +280,34 @@ func (m *model) itemView(match Match, cursorLine bool) string { // write item truncateSuffix := false - truncateAt := maxItemWidth - 3 - if from != 0 { - truncateAt -= 2 + textWidth := maxItemWidth - 2 + if from > 0 { + textWidth -= 2 } var itemv strings.Builder for ci, c := range runes[from:] { + var s string // matches if intContains(match.MatchedIndexes, ci+from) { if cursorLine { - _, _ = itemv.WriteString(m.cursorLineMatchesStyle.Render(string(c))) + s = m.cursorLineMatchesStyle.Render(string(c)) } else { - _, _ = itemv.WriteString(m.matchesStyle.Render(string(c))) + s = m.matchesStyle.Render(string(c)) } } else if cursorLine { - _, _ = itemv.WriteString(m.cursorLineStyle.Render(string(c))) + s = m.cursorLineStyle.Render(string(c)) } else { - _, _ = itemv.WriteRune(c) + s = string(c) } - if lipgloss.Width(itemv.String()) >= truncateAt { - truncateSuffix = true - break + if lipgloss.Width(s)+lipgloss.Width(itemv.String()) > textWidth { + if ci+from != len(runes) && lipgloss.Width(string(runes[ci+from:])) > 2 { + truncateSuffix = true + break + } } + + _, _ = itemv.WriteString(s) } if truncateSuffix {