diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2025-08-10 17:24:44 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2025-08-10 17:24:44 +0800 |
| commit | ab8535cc578672f844419d25fc1cc8e8790f1aa8 (patch) | |
| tree | 756609ca8202815e42d924c010a43e6ea44a23be /components/Searchable.qml | |
| parent | a9da23c79d9a6ef963bb153afcf9707753cbded9 (diff) | |
interal(query): move to Searchable module
from https://github.com/caelestia-dots/shell
license is GPL, this project don't have a license yet, will use GPL
Diffstat (limited to 'components/Searchable.qml')
| -rw-r--r-- | components/Searchable.qml | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/components/Searchable.qml b/components/Searchable.qml new file mode 100644 index 0000000..3275321 --- /dev/null +++ b/components/Searchable.qml @@ -0,0 +1,108 @@ +import Quickshell +import QtQuick + +import "../utils/fzf.js" as Fzf +import "../utils/fuzzysort.js" as Fuzzy +import "../utils/levendist.js" as Levendist + +Singleton { + required property var list + property string key: "name" + + enum SearchAlgorithm { + Levendist, + Fzf, + Fuzzysort + } + property int algorithm: Searchable.SearchAlgorithm.Levendist + property int scoreThreshold: 50 + property var extraOpts: ({}) + property list<string> keys: [key] + property list<real> weights: [1] + property var queryCache: ({}) + + readonly property var fzf: algorithm === Searchable.SearchAlgorithm.Fzf ? new Fzf.Finder(list, Object.assign({ + selector + }, extraOpts)) : null + + readonly property list<var> fuzzyPrepped: algorithm === Searchable.SearchAlgorithm.Fuzzysort ? list.map(e => { + const obj = { + _item: e + }; + for (const k of keys) + obj[k] = Fuzzy.prepare(e[k]); + return obj; + }) : [] + + function transformSearch(search: string): string { + return search; + } + + function selector(item: var): string { + return item[key]; + } + + function levendistQuery(search: string): list<var> { + // console.log(list); + if (queryCache[search]) + return queryCache[search]; + + let source = list; + let previousLists = Object.keys(queryCache).filter(key => key && search.startsWith(key)); + if (previousLists.length > 0) { + var longest = previousLists.reduce((a, b) => a.length >= b.length ? a : b); + source = queryCache[longest]; + } + + const p = [], f = []; + for (const s of source) { + const t = s[key].toLowerCase(); + if (t.startsWith(search)) { + p.push(s); + continue; + } + let pos = 0; + for (const c of search) { + pos = t.indexOf(c, pos); + if (pos < 0) { + pos = 0; + break; + } + pos++; + } + if (pos) + f.push({ + s, + score: Levendist.distance(search, t) + }); + } + const results = p.concat(f.filter(item => item.score <= scoreThreshold).sort((a, b) => a.score - b.score).map(({ + s + }) => s)); + + queryCache[search] = results; + return results; + } + + function query(search: string): list<var> { + search = transformSearch(search); + if (!search) + return [...list]; + + if (algorithm === Searchable.SearchAlgorithm.Levendist) + return levendistQuery(search.toLowerCase()); + + if (algorithm === Searchable.SearchAlgorithm.Fuzzysort) + return Fuzzy.go(search, fuzzyPrepped, Object.assign({ + all: true, + keys, + scoreFn: r => weights.reduce((a, w, i) => a + r[i].score * w, 0) + }, extraOpts)).map(r => r.obj._item); + + return fzf.find(search).sort((a, b) => { + if (a.score === b.score) + return selector(a.item).trim().length - selector(b.item).trim().length; + return b.score - a.score; + }).map(r => r.item); + } +} |
