From de09997fdb18c0314a55099a2d4f2d7285ffa643 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Mon, 16 Nov 2020 21:17:11 -0500 Subject: [PATCH] Normalize strings during filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes it easier to match characters with diacritics (i.e. you can type 'o' and match an 'รถ'). --- go.mod | 1 + ui/stash.go | 28 ++++++++++++++++++++++++---- ui/stashitem.go | 10 ++++++++-- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 3edc76d..a2e7086 100644 --- a/go.mod +++ b/go.mod @@ -24,4 +24,5 @@ require ( golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc // indirect golang.org/x/sys v0.0.0-20201020230747-6e5568b54d1a + golang.org/x/text v0.3.2 ) diff --git a/ui/stash.go b/ui/stash.go index 2325ac6..f59df57 100644 --- a/ui/stash.go +++ b/ui/stash.go @@ -9,6 +9,7 @@ import ( "sort" "strings" "time" + "unicode" "github.com/charmbracelet/bubbles/paginator" "github.com/charmbracelet/bubbles/spinner" @@ -21,6 +22,8 @@ import ( "github.com/muesli/reflow/ansi" te "github.com/muesli/termenv" "github.com/sahilm/fuzzy" + "golang.org/x/text/transform" + "golang.org/x/text/unicode/norm" ) const ( @@ -231,13 +234,16 @@ func (m *stashModel) getNotes() []*markdown { targets := []string{} for _, t := range m.markdowns { - note := "" - if t.markdownType == newsMarkdown { - note = "News: " + t.Note - } else { + note, err := normalize(t.Note) + if err != nil && debug { + log.Printf("error normalizing '%s': %v", t.Note, err) note = t.Note } + if t.markdownType == newsMarkdown { + note = "News: " + note + } + targets = append(targets, note) } @@ -1081,6 +1087,20 @@ func deleteStashedItem(cc *charm.Client, id int) tea.Cmd { // ETC +// Normalize text to aid in the filtering process. in particular, we remove +// diacritics. +func normalize(in string) (string, error) { + t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC) + out, _, err := transform.String(t, in) + return out, err +} + +// Returns whether a given rune is a nonspacing mark (Mn is the key for +// nonspacing marks) +func isMn(r rune) bool { + return unicode.Is(unicode.Mn, r) +} + // wrapMarkdowns wraps a *charm.Markdown with a *markdown in order to add some // extra metadata. func wrapMarkdowns(t markdownType, md []*charm.Markdown) (m []*markdown) { diff --git a/ui/stashitem.go b/ui/stashitem.go index 0f15b3c..f74ed17 100644 --- a/ui/stashitem.go +++ b/ui/stashitem.go @@ -2,6 +2,7 @@ package ui import ( "fmt" + "log" "strings" "github.com/charmbracelet/charm/ui/common" @@ -122,13 +123,18 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { func styleFilteredText(haystack, needles string, defaultStyle, matchedStyle termenv.Style) string { b := strings.Builder{} - matches := fuzzy.Find(needles, []string{haystack}) + normalizedHay, err := normalize(haystack) + if err != nil && debug { + log.Printf("error normalizing '%s': %v", haystack, err) + } + + matches := fuzzy.Find(needles, []string{normalizedHay}) if len(matches) == 0 { return defaultStyle.Styled(haystack) } m := matches[0] // only one match exists - for i, rune := range haystack { + for i, rune := range []rune(haystack) { styled := false for _, mi := range m.MatchedIndexes { if i == mi {