2020-05-26 15:51:08 +00:00
|
|
|
package ui
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-11-17 02:17:11 +00:00
|
|
|
"log"
|
2020-05-26 16:36:14 +00:00
|
|
|
"strings"
|
2020-05-26 15:51:08 +00:00
|
|
|
|
2021-06-14 20:52:00 +00:00
|
|
|
"github.com/charmbracelet/lipgloss"
|
2020-12-14 23:42:33 +00:00
|
|
|
"github.com/muesli/reflow/ansi"
|
|
|
|
"github.com/muesli/reflow/truncate"
|
2020-11-16 20:25:23 +00:00
|
|
|
"github.com/sahilm/fuzzy"
|
2020-05-26 15:51:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2020-08-25 13:56:17 +00:00
|
|
|
verticalLine = "│"
|
|
|
|
noMemoTitle = "No Memo"
|
|
|
|
fileListingStashIcon = "• "
|
2020-05-26 15:51:08 +00:00
|
|
|
)
|
|
|
|
|
2020-05-26 19:10:45 +00:00
|
|
|
func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) {
|
2020-08-25 17:32:49 +00:00
|
|
|
var (
|
2020-12-14 23:42:33 +00:00
|
|
|
truncateTo = uint(m.common.width - stashViewHorizontalPadding*2)
|
2020-08-25 17:32:49 +00:00
|
|
|
gutter string
|
|
|
|
title = md.Note
|
2020-11-25 16:40:24 +00:00
|
|
|
date = md.relativeTime()
|
2020-08-25 17:32:49 +00:00
|
|
|
icon = ""
|
|
|
|
)
|
2020-05-26 15:51:08 +00:00
|
|
|
|
2020-12-16 20:12:48 +00:00
|
|
|
switch md.docType {
|
2020-11-28 02:52:51 +00:00
|
|
|
case NewsDoc:
|
2020-05-26 15:51:08 +00:00
|
|
|
if title == "" {
|
|
|
|
title = "News"
|
|
|
|
} else {
|
2020-12-14 23:42:33 +00:00
|
|
|
title = truncate.StringWithTail(title, truncateTo, ellipsis)
|
2020-05-26 15:51:08 +00:00
|
|
|
}
|
2020-11-28 02:52:51 +00:00
|
|
|
case StashedDoc, ConvertedDoc:
|
2020-08-25 13:56:17 +00:00
|
|
|
icon = fileListingStashIcon
|
2020-05-26 15:51:08 +00:00
|
|
|
if title == "" {
|
|
|
|
title = noMemoTitle
|
|
|
|
}
|
2020-12-14 23:42:33 +00:00
|
|
|
title = truncate.StringWithTail(title, truncateTo-uint(ansi.PrintableRuneWidth(icon)), ellipsis)
|
2020-07-15 00:25:51 +00:00
|
|
|
default:
|
2020-12-14 23:42:33 +00:00
|
|
|
title = truncate.StringWithTail(title, truncateTo, ellipsis)
|
2020-05-26 15:51:08 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 23:38:10 +00:00
|
|
|
isSelected := index == m.cursor()
|
2020-11-26 01:15:21 +00:00
|
|
|
isFiltering := m.filterState == filtering
|
2020-12-17 23:31:08 +00:00
|
|
|
singleFilteredItem := isFiltering && len(m.getVisibleMarkdowns()) == 1
|
2020-11-10 23:48:34 +00:00
|
|
|
|
2020-12-17 23:31:08 +00:00
|
|
|
// If there are multiple items being filtered don't highlight a selected
|
2020-11-10 23:48:34 +00:00
|
|
|
// item in the results. If we've filtered down to one item, however,
|
|
|
|
// highlight that first item since pressing return will open it.
|
2020-11-26 01:15:21 +00:00
|
|
|
if isSelected && !isFiltering || singleFilteredItem {
|
2020-11-04 01:15:30 +00:00
|
|
|
// Selected item
|
|
|
|
|
2020-11-26 03:49:49 +00:00
|
|
|
switch m.selectionState {
|
|
|
|
case selectionPromptingDelete:
|
2020-07-15 16:07:15 +00:00
|
|
|
gutter = faintRedFg(verticalLine)
|
|
|
|
icon = faintRedFg(icon)
|
|
|
|
title = redFg(title)
|
|
|
|
date = faintRedFg(date)
|
2020-11-26 03:49:49 +00:00
|
|
|
case selectionSettingNote:
|
2020-07-15 16:07:15 +00:00
|
|
|
gutter = dullYellowFg(verticalLine)
|
2020-07-15 00:25:51 +00:00
|
|
|
icon = ""
|
2020-11-13 21:17:50 +00:00
|
|
|
title = m.noteInput.View()
|
2020-07-15 16:07:15 +00:00
|
|
|
date = dullYellowFg(date)
|
2020-05-26 15:51:08 +00:00
|
|
|
default:
|
2020-12-17 20:45:08 +00:00
|
|
|
if m.common.latestFileStashed == md.stashID &&
|
2022-01-30 00:42:38 +00:00
|
|
|
m.statusMessage == stashingStatusMessage {
|
2020-12-17 20:45:08 +00:00
|
|
|
gutter = greenFg(verticalLine)
|
|
|
|
icon = dimGreenFg(icon)
|
|
|
|
title = greenFg(title)
|
2020-12-17 23:31:08 +00:00
|
|
|
date = semiDimGreenFg(date)
|
2020-11-13 23:34:59 +00:00
|
|
|
} else {
|
2020-12-17 20:45:08 +00:00
|
|
|
gutter = dullFuchsiaFg(verticalLine)
|
|
|
|
icon = dullFuchsiaFg(icon)
|
2020-12-17 23:31:08 +00:00
|
|
|
if m.currentSection().key == filterSection &&
|
|
|
|
m.filterState == filterApplied || singleFilteredItem {
|
2021-06-14 20:52:00 +00:00
|
|
|
s := lipgloss.NewStyle().Foreground(fuschia)
|
|
|
|
title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true))
|
2020-12-17 20:45:08 +00:00
|
|
|
} else {
|
|
|
|
title = fuchsiaFg(title)
|
|
|
|
}
|
|
|
|
date = dullFuchsiaFg(date)
|
2020-11-13 23:34:59 +00:00
|
|
|
}
|
2020-05-26 15:51:08 +00:00
|
|
|
}
|
|
|
|
} else {
|
2020-11-04 01:15:30 +00:00
|
|
|
// Regular (non-selected) items
|
|
|
|
|
2020-12-17 23:31:08 +00:00
|
|
|
gutter = " "
|
|
|
|
|
2020-12-17 20:45:08 +00:00
|
|
|
if m.common.latestFileStashed == md.stashID &&
|
2022-01-30 00:42:38 +00:00
|
|
|
m.statusMessage == stashingStatusMessage {
|
2020-12-17 20:45:08 +00:00
|
|
|
icon = dimGreenFg(icon)
|
|
|
|
title = greenFg(title)
|
2020-12-17 23:31:08 +00:00
|
|
|
date = semiDimGreenFg(date)
|
2020-12-17 20:45:08 +00:00
|
|
|
} else if md.docType == NewsDoc {
|
2020-11-26 01:15:21 +00:00
|
|
|
if isFiltering && m.filterInput.Value() == "" {
|
2020-11-04 01:15:30 +00:00
|
|
|
title = dimIndigoFg(title)
|
|
|
|
date = dimSubtleIndigoFg(date)
|
|
|
|
} else {
|
2021-06-14 20:52:00 +00:00
|
|
|
s := lipgloss.NewStyle().Foreground(indigo)
|
|
|
|
title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true))
|
2020-11-04 01:15:30 +00:00
|
|
|
date = subtleIndigoFg(date)
|
|
|
|
}
|
2020-11-26 01:15:21 +00:00
|
|
|
} else if isFiltering && m.filterInput.Value() == "" {
|
2020-11-04 01:15:30 +00:00
|
|
|
icon = dimGreenFg(icon)
|
|
|
|
if title == noMemoTitle {
|
2020-12-01 01:32:48 +00:00
|
|
|
title = dimBrightGrayFg(title)
|
2020-10-26 14:52:54 +00:00
|
|
|
} else {
|
2020-11-04 01:15:30 +00:00
|
|
|
title = dimNormalFg(title)
|
2020-10-26 14:52:54 +00:00
|
|
|
}
|
2020-12-01 01:32:48 +00:00
|
|
|
date = dimBrightGrayFg(date)
|
2020-05-26 15:51:08 +00:00
|
|
|
} else {
|
2020-07-15 16:07:15 +00:00
|
|
|
icon = greenFg(icon)
|
2020-05-26 15:51:08 +00:00
|
|
|
if title == noMemoTitle {
|
2020-12-01 01:32:48 +00:00
|
|
|
title = brightGrayFg(title)
|
2020-11-04 01:15:30 +00:00
|
|
|
} else {
|
2021-06-14 20:52:00 +00:00
|
|
|
s := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#1a1a1a", Dark: "#dddddd"})
|
|
|
|
title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true))
|
2020-05-26 15:51:08 +00:00
|
|
|
}
|
2020-12-01 01:32:48 +00:00
|
|
|
date = brightGrayFg(date)
|
2020-05-26 15:51:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-15 00:25:51 +00:00
|
|
|
fmt.Fprintf(b, "%s %s%s\n", gutter, icon, title)
|
2020-05-26 16:36:14 +00:00
|
|
|
fmt.Fprintf(b, "%s %s", gutter, date)
|
2020-05-26 15:51:08 +00:00
|
|
|
}
|
2020-11-13 23:34:59 +00:00
|
|
|
|
2021-06-14 20:52:00 +00:00
|
|
|
func styleFilteredText(haystack, needles string, defaultStyle, matchedStyle lipgloss.Style) string {
|
2020-11-13 23:34:59 +00:00
|
|
|
b := strings.Builder{}
|
2020-11-16 20:25:23 +00:00
|
|
|
|
2020-11-17 02:17:11 +00:00
|
|
|
normalizedHay, err := normalize(haystack)
|
|
|
|
if err != nil && debug {
|
|
|
|
log.Printf("error normalizing '%s': %v", haystack, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
matches := fuzzy.Find(needles, []string{normalizedHay})
|
2020-11-16 20:25:23 +00:00
|
|
|
if len(matches) == 0 {
|
2021-06-14 20:52:00 +00:00
|
|
|
return defaultStyle.Render(haystack)
|
2020-11-16 20:25:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m := matches[0] // only one match exists
|
2020-11-17 02:17:11 +00:00
|
|
|
for i, rune := range []rune(haystack) {
|
2020-11-16 20:25:23 +00:00
|
|
|
styled := false
|
|
|
|
for _, mi := range m.MatchedIndexes {
|
|
|
|
if i == mi {
|
2021-06-14 20:52:00 +00:00
|
|
|
b.WriteString(matchedStyle.Render(string(rune)))
|
2020-11-16 20:25:23 +00:00
|
|
|
styled = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !styled {
|
2021-06-14 20:52:00 +00:00
|
|
|
b.WriteString(defaultStyle.Render(string(rune)))
|
2020-11-13 23:34:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return b.String()
|
|
|
|
}
|