feat: fzf-like search instead of fuzzy

Also add license for fuzzysort lib
This commit is contained in:
2 * r + 2 * t 2025-07-19 14:25:39 +10:00
parent 4320904a39
commit ce26c8a754
8 changed files with 1402 additions and 67 deletions

View file

@ -1,18 +1,17 @@
pragma Singleton
import "root:/utils/scripts/fuzzysort.js" as Fuzzy
import qs.services
import qs.config
import qs.utils
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
Searcher {
id: root
property string qalcResult
readonly property list<Action> list: [
readonly property list<Action> actions: [
Action {
name: qsTr("Calculator")
desc: qsTr("Do simple math equations (powered by Qalc)")
@ -134,24 +133,16 @@ Singleton {
}
]
readonly property list<var> preppedActions: list.filter(a => !a.disabled).map(a => ({
name: Fuzzy.prepare(a.name),
desc: Fuzzy.prepare(a.desc),
action: a
}))
function fuzzyQuery(search: string): var {
return Fuzzy.go(search.slice(Config.launcher.actionPrefix.length), preppedActions, {
all: true,
keys: ["name", "desc"],
scoreFn: r => r[0].score > 0 ? r[0].score * 0.9 + r[1].score * 0.1 : 0
}).map(r => r.obj.action);
function transformSearch(search: string): string {
return search.slice(Config.launcher.actionPrefix.length);
}
function autocomplete(list: AppList, text: string): void {
list.search.text = `${Config.launcher.actionPrefix}${text} `;
}
list: actions.filter(a => !a.disabled)
component Action: QtObject {
required property string name
required property string desc

View file

@ -23,14 +23,14 @@ StyledListView {
if (isCalc)
return [0];
if (isScheme)
return Schemes.fuzzyQuery(text);
return Schemes.query(text);
if (isVariant)
return M3Variants.fuzzyQuery(text);
return M3Variants.query(text);
if (isAction)
return Actions.fuzzyQuery(text);
return Actions.query(text);
if (text.startsWith(Config.launcher.actionPrefix))
text = search.text.slice(Config.launcher.actionPrefix.length);
return Apps.fuzzyQuery(text);
return Apps.query(text);
}
model: ScriptModel {

View file

@ -1,14 +1,17 @@
pragma Singleton
import "root:/utils/scripts/fuzzysort.js" as Fuzzy
import qs.config
import qs.utils
import Quickshell
import QtQuick
Singleton {
Searcher {
id: root
readonly property list<Variant> list: [
function transformSearch(search: string): var {
return search.slice(`${Config.launcher.actionPrefix}variant `.length);
}
list: [
Variant {
variant: "vibrant"
icon: "sentiment_very_dissatisfied"
@ -65,18 +68,6 @@ Singleton {
}
]
readonly property list<var> preppedVariants: list.map(v => ({
name: Fuzzy.prepare(v.variant),
variant: v
}))
function fuzzyQuery(search: string): var {
return Fuzzy.go(search.slice(`${Config.launcher.actionPrefix}variant `.length), preppedVariants, {
all: true,
key: "name"
}).map(r => r.obj.variant);
}
component Variant: QtObject {
required property string variant
required property string icon

View file

@ -1,28 +1,19 @@
pragma Singleton
import "root:/utils/scripts/fuzzysort.js" as Fuzzy
import qs.config
import qs.utils
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
Searcher {
id: root
readonly property list<var> preppedSchemes: schemes.instances.map(s => ({
name: Fuzzy.prepare(s.name),
flavour: Fuzzy.prepare(s.flavour),
scheme: s
}))
function fuzzyQuery(search: string): var {
return Fuzzy.go(search.slice(`${Config.launcher.actionPrefix}scheme `.length), preppedSchemes, {
all: true,
keys: ["name", "flavour"],
scoreFn: r => r[0].score > 0 ? r[0].score * 0.9 + r[1].score * 0.1 : 0
}).map(r => r.obj.scheme);
function transformSearch(search: string): var {
return search.slice(`${Config.launcher.actionPrefix}scheme `.length);
}
list: schemes.instances
Variants {
id: schemes

View file

@ -1,25 +1,12 @@
pragma Singleton
import "root:/utils/scripts/fuzzysort.js" as Fuzzy
import qs.utils
import Quickshell
Singleton {
Searcher {
id: root
readonly property list<DesktopEntry> list: DesktopEntries.applications.values.filter(a => !a.noDisplay).sort((a, b) => a.name.localeCompare(b.name))
readonly property list<var> preppedApps: list.map(a => ({
name: Fuzzy.prepare(a.name),
comment: Fuzzy.prepare(a.comment),
entry: a
}))
function fuzzyQuery(search: string): var { // Idk why list<DesktopEntry> doesn't work
return Fuzzy.go(search, preppedApps, {
all: true,
keys: ["name", "comment"],
scoreFn: r => r[0].score > 0 ? r[0].score * 0.9 + r[1].score * 0.1 : 0
}).map(r => r.obj.entry);
}
list: DesktopEntries.applications.values.filter(a => !a.noDisplay).sort((a, b) => a.name.localeCompare(b.name))
function launch(entry: DesktopEntry): void {
if (entry.runInTerminal)

42
utils/Searcher.qml Normal file
View file

@ -0,0 +1,42 @@
import Quickshell
import "scripts/fzf.js" as Fzf
import "scripts/fuzzysort.js" as Fuzzy
import QtQuick
Singleton {
required property list<QtObject> list
property string key: "name"
property bool useFuzzy: false
property var extraOpts: ({})
readonly property var fzf: useFuzzy ? [] : new Fzf.Finder(list, Object.assign({
selector: e => e[key]
}, extraOpts))
readonly property list<var> fuzzyPrepped: useFuzzy ? list.map(e => ({
[key]: e[key],
_item: e
})) : []
function transformSearch(search: string): string {
return search;
}
function query(search: string): list<var> {
search = transformSearch(search);
if (!search)
return [...list];
if (useFuzzy)
return Fuzzy.go(search, fuzzyPrepped, Object.assign({
all: true,
key
}, extraOpts)).map(r => r.obj._item);
return fzf.find(search).sort((a, b) => {
if (a.score === b.score)
return a.item[key].trim().length - b.item[key].trim().length;
return b.score - a.score;
}).map(r => r.item);
}
}

View file

@ -1,5 +1,31 @@
.pragma library
/*
https://github.com/farzher/fuzzysort
MIT License
Copyright (c) 2018 Stephen Kamenar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
var single = (search, target) => {
if(!search || !target) return NULL

1307
utils/scripts/fzf.js Normal file

File diff suppressed because it is too large Load diff