1066c0bc7d
Search uses existing parse grammar ?prefix — type `?query #tag` in capture bar to filter entities client-side. Substring match on body+title+description with AND tag filtering. Esc clears search. Absorb via m key on fluid entities — opens source picker showing all other entities, enter merges source into target. Uses existing store.Absorb() backend.
56 lines
1.0 KiB
Go
56 lines
1.0 KiB
Go
package tui
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/lerko/nib/internal/db"
|
|
)
|
|
|
|
func filterEntities(entities []*db.Entity, query string, tags []string) []*db.Entity {
|
|
if query == "" && len(tags) == 0 {
|
|
return entities
|
|
}
|
|
|
|
query = strings.ToLower(query)
|
|
lowerTags := make([]string, len(tags))
|
|
for i, t := range tags {
|
|
lowerTags[i] = strings.ToLower(t)
|
|
}
|
|
|
|
var result []*db.Entity
|
|
for _, e := range entities {
|
|
if !matchesSearch(e, query, lowerTags) {
|
|
continue
|
|
}
|
|
result = append(result, e)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func matchesSearch(e *db.Entity, query string, tags []string) bool {
|
|
if len(tags) > 0 {
|
|
eTags := make(map[string]bool, len(e.Tags))
|
|
for _, t := range e.Tags {
|
|
eTags[strings.ToLower(t)] = true
|
|
}
|
|
for _, t := range tags {
|
|
if !eTags[t] {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
if query == "" {
|
|
return true
|
|
}
|
|
|
|
haystack := strings.ToLower(e.Body)
|
|
if e.Title != nil {
|
|
haystack += " " + strings.ToLower(*e.Title)
|
|
}
|
|
if e.Description != nil {
|
|
haystack += " " + strings.ToLower(*e.Description)
|
|
}
|
|
return strings.Contains(haystack, query)
|
|
}
|