Browse Source

Sync: 2026-08-01 12:15:45

Gabriel Capella 1 day ago
parent
commit
55d694014e
2 changed files with 54 additions and 23 deletions
  1. 5 0
      dots/.config/fuzzel/fuzzel.ini
  2. 49 23
      dots/.config/sway/scripts/fuzzel-run-or-search.sh

+ 5 - 0
dots/.config/fuzzel/fuzzel.ini

@@ -18,3 +18,8 @@ selection-text=ffffffff
 selection-match=7cafc2ff
 border=323232ff
 
+[key-bindings]
+# dmenu mode only: exits with code 10, which fuzzel-run-or-search.sh reads as
+# "web-search this, whatever it looks like". Enter keeps its normal meaning.
+custom-1=Mod1+Return
+

+ 49 - 23
dots/.config/sway/scripts/fuzzel-run-or-search.sh

@@ -1,7 +1,13 @@
 #!/bin/bash
-# Run a command, or fall back to a web search if it isn't one.
-# fuzzel --dmenu prints the typed string as-is when nothing matches,
-# which is what makes the search fallback work.
+# Run a command, or search the web for it.
+#
+#   Enter      run it if it is an executable, otherwise search for it
+#   Alt+Enter  always search, even if it looks like a command
+#
+# The second one exists because deciding by "is the first word a binary?" gets
+# it wrong for ordinary phrases: test, who, which, sort, time and file are all
+# real executables. Alt+Enter is bound to fuzzel's custom-1, which is dmenu-only
+# and exits with code 10 (see [key-bindings] in fuzzel.ini).
 
 browser_app_id="google-chrome"
 search_url="https://duckduckgo.com/?q="
@@ -19,27 +25,47 @@ list_path_bins() {
     done | sort -u
 }
 
-result=$(list_path_bins | fuzzel --dmenu)
-[[ -z $result ]] && exit 0
+search_web() {
+    local query
+    query=$(jq -rn --arg q "$1" '$q|@uri')
+    xdg-open "$search_url$query" &
+
+    # Wait for the browser window instead of guessing at a fixed sleep: a cold
+    # start takes well over half a second, while an already-running Chrome is
+    # ready almost immediately.
+    local _
+    for _ in $(seq 40); do
+        if swaymsg -t get_tree |
+            jq -e --arg id "$browser_app_id" \
+                'recurse(.nodes[]?, .floating_nodes[]?) | select(.app_id == $id)' >/dev/null; then
+            swaymsg "[app_id=\"$browser_app_id\"] focus" >/dev/null
+            return 0
+        fi
+        sleep 0.05
+    done
+}
 
-# type -P looks only at PATH, so builtins and keywords correctly fall through
-# to the search branch instead of failing silently in exec.
-if type -P "${result%% *}" >/dev/null 2>&1; then
-    exec $result
-fi
+result=$(list_path_bins | fuzzel --dmenu)
+status=$?
 
-query=$(jq -rn --arg q "$result" '$q|@uri')
-xdg-open "$search_url$query" &
+[[ -z $result ]] && exit 0
 
-# Wait for the browser window instead of guessing at a fixed sleep: a cold
-# start takes well over half a second, while an already-running Chrome is
-# ready almost immediately.
-for _ in $(seq 40); do
-    if swaymsg -t get_tree |
-        jq -e --arg id "$browser_app_id" \
-            'recurse(.nodes[]?, .floating_nodes[]?) | select(.app_id == $id)' >/dev/null; then
-        swaymsg "[app_id=\"$browser_app_id\"] focus" >/dev/null
+case $status in
+    # Alt+Enter: search regardless of what the text looks like.
+    10)
+        search_web "$result"
+        ;;
+    # Enter: run it if it really is an executable. type -P looks only at PATH,
+    # so builtins and keywords fall through to the search instead of failing
+    # silently in exec.
+    0)
+        if type -P "${result%% *}" >/dev/null 2>&1; then
+            exec $result
+        fi
+        search_web "$result"
+        ;;
+    # Escape, or fuzzel failed to start.
+    *)
         exit 0
-    fi
-    sleep 0.05
-done
+        ;;
+esac