From 050ec883d678b992789a4175619864676705a41d Mon Sep 17 00:00:00 2001 From: Pavel Aslanov Date: Sun, 10 Nov 2024 10:46:32 +0000 Subject: [PATCH] [sweep] pass window uid in `Sweep(Select|Bind)` --- sweep-cli/src/main.rs | 2 +- sweep-lib/src/sweep.rs | 54 +++++++++++++++++++++-------- sweep-py/sweep/apps/bash_history.py | 5 +-- sweep-py/sweep/apps/demo.py | 50 ++++---------------------- sweep-py/sweep/apps/mpd.py | 16 +++++---- sweep-py/sweep/sweep.py | 32 +++++++++-------- 6 files changed, 74 insertions(+), 85 deletions(-) diff --git a/sweep-cli/src/main.rs b/sweep-cli/src/main.rs index ca8b6ee..6410c2e 100644 --- a/sweep-cli/src/main.rs +++ b/sweep-cli/src/main.rs @@ -164,7 +164,7 @@ async fn main() -> Result<(), Error> { }); }; while let Some(event) = sweep.next_event().await { - if let SweepEvent::Select(items) = event { + if let SweepEvent::Select { items, .. } = event { if items.is_empty() && !args.no_match_use_input { continue; } diff --git a/sweep-lib/src/sweep.rs b/sweep-lib/src/sweep.rs index 50bc711..8bdf7d1 100644 --- a/sweep-lib/src/sweep.rs +++ b/sweep-lib/src/sweep.rs @@ -128,7 +128,7 @@ where loop { tokio::select! { event = sweep.next_event() => match event { - Some(SweepEvent::Select(entry)) => return Ok(entry), + Some(SweepEvent::Select { items, .. }) => return Ok(items), None => return Ok(Vec::new()), _ => continue, }, @@ -151,7 +151,6 @@ enum SweepRequest { tag: String, desc: String, }, - Terminate, Current(oneshot::Sender>), Marked(oneshot::Sender>), CursorSet { @@ -168,16 +167,25 @@ enum SweepRequest { }, HaystackClear, RankerKeepOrder(Option), + RenderSuppress(bool), + + Terminate, WindowSwitch(Either, WindowId>), WindowPop, - RenderSuppress(bool), } /// Events returned to [Sweep] type #[derive(Clone, Debug)] pub enum SweepEvent { - Select(Vec), - Bind { tag: String, chord: KeyChord }, + Select { + uid: WindowId, + items: Vec, + }, + Bind { + uid: WindowId, + tag: String, + chord: KeyChord, + }, Window(WindowEvent), Resize(TerminalSize), } @@ -379,9 +387,9 @@ where Arc::new({ let send = std::sync::Mutex::new(Some(send)); // one shot is moved on send move |event| { - if let SweepEvent::Select(selected) = event { + if let SweepEvent::Select { items, .. } = event { if let Some(send) = send.with_mut(|send| send.take()) { - _ = send.send(selected); + _ = send.send(items); } Ok(WindowAction::Close { uid: Some(uid.clone()), @@ -775,12 +783,16 @@ where while let Some(event) = sweep.next_event().await { match event { - SweepEvent::Bind { tag, chord } => { - peer.notify_with_value("bind", json!({"tag": tag, "key": chord}))? - } - SweepEvent::Select(items) => { + SweepEvent::Bind { uid, tag, chord } => peer.notify_with_value( + "bind", + json!({"uid": uid, "tag": tag, "key": chord}), + )?, + SweepEvent::Select { uid, items } => { if !items.is_empty() { - peer.notify_with_value("select", json!({"items": items}))? + peer.notify_with_value( + "select", + json!({"uid": uid, "items": items}), + )? } } SweepEvent::Window(window_event) => { @@ -1236,6 +1248,7 @@ where SweepAction::User { tag, chord, .. } => { if !tag.is_empty() { return (self.event_handler)(SweepEvent::Bind { + uid: self.window_uid.clone(), tag: tag.clone(), chord: chord.clone(), }); @@ -1251,11 +1264,17 @@ where .into_iter() .collect() }; - return (self.event_handler)(SweepEvent::Select(selected)); + return (self.event_handler)(SweepEvent::Select { + uid: self.window_uid.clone(), + items: selected, + }); } SweepAction::SelectByIndex(index) => { if let Some(item) = self.haystack.get(*index) { - return (self.event_handler)(SweepEvent::Select(vec![item.clone()])); + return (self.event_handler)(SweepEvent::Select { + uid: self.window_uid.clone(), + items: vec![item.clone()], + }); } } SweepAction::Mark => { @@ -1364,7 +1383,10 @@ where self.term_waker.clone(), None, Arc::new(move |event| { - if let SweepEvent::Select(selected) = event { + if let SweepEvent::Select { + items: selected, .. + } = event + { let name = selected .into_iter() .next() @@ -1525,6 +1547,7 @@ impl Window for SweepWindow { if is_first_key && key == backspace && self.input.get().count() == 0 { if let Some(ref tag) = self.key_empty_backspace { return (self.event_handler)(SweepEvent::Bind { + uid: self.window_uid.clone(), tag: tag.clone(), chord: KeyChord::from_iter([backspace]), }); @@ -1560,6 +1583,7 @@ impl Window for SweepWindow { _ => { let key = Key::new(mouse.name, mouse.mode); (self.event_handler)(SweepEvent::Bind { + uid: self.window_uid.clone(), tag: tag.to_owned(), chord: KeyChord::from_iter([key]), }) diff --git a/sweep-py/sweep/apps/bash_history.py b/sweep-py/sweep/apps/bash_history.py index c9fbfc2..cae6d81 100644 --- a/sweep-py/sweep/apps/bash_history.py +++ b/sweep-py/sweep/apps/bash_history.py @@ -16,10 +16,7 @@ BASH_HISTORY_FILE = "~/.bash_history" DATE_RE = re.compile(r"^#(\d+)$") TERM_ICON = Icon( - path="M20,19V7H4V19H20M20,3A2,2 0 0,1 22,5V19A2,2 0 0,1 20,21H4" - "A2,2 0 0,1 2,19V5C2,3.89 2.9,3 4,3H20M13,17V15H18V17H13M9.58,13" - "L5.57,9H8.4L11.7,12.3C12.09,12.69 12.09,13.33 11.7,13.72L8.42,17" - "H5.59L9.58,13Z", + path="M20,19V7H4V19H20M20,3A2,2 0 0,1 22,5V19A2,2 0 0,1 20,21H4A2,2 0 0,1 2,19V5C2,3.89 2.9,3 4,3H20M13,17V15H18V17H13M9.58,13L5.57,9H8.4L11.7,12.3C12.09,12.69 12.09,13.33 11.7,13.72L8.42,17H5.59L9.58,13Z", view_box=(0, 0, 24, 24), size=(1, 3), fallback=" ", diff --git a/sweep-py/sweep/apps/demo.py b/sweep-py/sweep/apps/demo.py index adebc30..83d0acc 100644 --- a/sweep-py/sweep/apps/demo.py +++ b/sweep-py/sweep/apps/demo.py @@ -31,57 +31,25 @@ from . import sweep_default_cmd ICON_BEER = Icon( - path="M8.5 10A.75.75 0 0 0 7 10v7a.75.75 0 0 0 1.5 0v-7ZM11.5 10a.75.75 0 0 " - "0-1.5 0v7a.75.75 0 0 0 1.5 0v-7ZM14.5 10a.75.75 0 0 0-1.5 0v7a.75.75 0 0 0 " - "1.5 0v-7ZM4 5.25A3.25 3.25 0 0 1 7.25 2h7a3.25 3.25 0 0 1 3.25 3.25V6h1.25" - "A3.25 3.25 0 0 1 22 9.25v5.5A3.25 3.25 0 0 1 18.75 18H17.5v1.75A2.25 2.25 0" - " 0 1 15.25 22h-9A2.25 2.25 0 0 1 4 19.75V5.25ZM16 7.5H5.5v12.25c0 .414.336" - ".75.75.75h9a.75.75 0 0 0 .75-.75V7.5Zm1.5 9h1.25a1.75 1.75 0 0 0 1.75-1.75" - "v-5.5a1.75 1.75 0 0 0-1.75-1.75H17.5v9ZM16 5.25a1.75 1.75 0 0 0-1.75-1.75" - "h-7A1.75 1.75 0 0 0 5.5 5.25V6H16v-.75Z", + path="M8.5 10A.75.75 0 0 0 7 10v7a.75.75 0 0 0 1.5 0v-7ZM11.5 10a.75.75 0 0 0-1.5 0v7a.75.75 0 0 0 1.5 0v-7ZM14.5 10a.75.75 0 0 0-1.5 0v7a.75.75 0 0 0 1.5 0v-7ZM4 5.25A3.25 3.25 0 0 1 7.25 2h7a3.25 3.25 0 0 1 3.25 3.25V6h1.25A3.25 3.25 0 0 1 22 9.25v5.5A3.25 3.25 0 0 1 18.75 18H17.5v1.75A2.25 2.25 0 0 1 15.25 22h-9A2.25 2.25 0 0 1 4 19.75V5.25ZM16 7.5H5.5v12.25c0 .414.336.75.75.75h9a.75.75 0 0 0 .75-.75V7.5Zm1.5 9h1.25a1.75 1.75 0 0 0 1.75-1.75v-5.5a1.75 1.75 0 0 0-1.75-1.75H17.5v9ZM16 5.25a1.75 1.75 0 0 0-1.75-1.75h-7A1.75 1.75 0 0 0 5.5 5.25V6H16v-.75Z", view_box=(0, 0, 24, 24), size=(1, 3), fallback="[P]", ) ICON_COCKTAIL = Icon( - path="M19.873 3.49a.75.75 0 1 0-.246-1.48l-6 1a.75.75 0 0 0-.613.593L12.736 " - "5H5.75a.75.75 0 0 0-.75.75v4a3.25 3.25 0 0 0 3 3.24v.51c0 1.953 1.4 3.579 " - "3.25 3.93v3.07h-2.5a.75.75 0 0 0 0 1.5h6.5a.75.75 0 0 0 0-1.5h-2.5v-3.07A4.001" - " 4.001 0 0 0 16 13.5v-.51a3.25 3.25 0 0 0 3-3.24v-4a.75.75 0 0 0-.75-.75h-3.985" - "l.119-.595 5.49-.915ZM17.5 8h-3.835l.3-1.5H17.5V8Zm-4.135 1.5H17.5v.25a1.75" - " 1.75 0 0 1-1.75 1.75h-.5a.75.75 0 0 0-.75.75v1.25a2.5 2.5 0 0 1-5 0v-1.25" - "a.75.75 0 0 0-.75-.75h-.5A1.75 1.75 0 0 1 6.5 9.75V9.5h5.335l-.82 4.103a.75" - ".75 0 1 0 1.47.294l.88-4.397ZM12.135 8H6.5V6.5h5.935l-.3 1.5Z", + path="M19.873 3.49a.75.75 0 1 0-.246-1.48l-6 1a.75.75 0 0 0-.613.593L12.736 5H5.75a.75.75 0 0 0-.75.75v4a3.25 3.25 0 0 0 3 3.24v.51c0 1.953 1.4 3.579 3.25 3.93v3.07h-2.5a.75.75 0 0 0 0 1.5h6.5a.75.75 0 0 0 0-1.5h-2.5v-3.07A4.001 4.001 0 0 0 16 13.5v-.51a3.25 3.25 0 0 0 3-3.24v-4a.75.75 0 0 0-.75-.75h-3.985l.119-.595 5.49-.915ZM17.5 8h-3.835l.3-1.5H17.5V8Zm-4.135 1.5H17.5v.25a1.75 1.75 0 0 1-1.75 1.75h-.5a.75.75 0 0 0-.75.75v1.25a2.5 2.5 0 0 1-5 0v-1.25a.75.75 0 0 0-.75-.75h-.5A1.75 1.75 0 0 1 6.5 9.75V9.5h5.335l-.82 4.103a.75.75 0 1 0 1.47.294l.88-4.397ZM12.135 8H6.5V6.5h5.935l-.3 1.5Z", view_box=(0, 0, 24, 24), size=(1, 3), fallback="[C] ", ) ICON_BACKPACK = Icon( - path="M12 2a3.75 3.75 0 0 0-3.736 3.424A7.999 7.999 0 0 0 4 12.5v6.25A3.25 3.25" - " 0 0 0 7.25 22h5.56a6.518 6.518 0 0 1-1.078-1.5H7.25a1.75 1.75 0 0 1-1.75-1.75" - "v-3.036H8v1.536a.75.75 0 0 0 1.5 0v-1.536h1.748c.175-.613.438-1.19.774-1.714" - "H5.5v-1.5a6.5 6.5 0 0 1 12.838-1.446 6.455 6.455 0 0 1 1.596.417 8.006 8.006" - " 0 0 0-4.198-6.047A3.75 3.75 0 0 0 12 2Zm0 2.5c-.698 0-1.374.09-2.02.257a2.25" - " 2.25 0 0 1 4.04 0A8.013 8.013 0 0 0 12 4.5ZM14.034 12a6.465 6.465 0 0 1 1.74" - "-.768c.144-.239.226-.517.226-.815A2.417 2.417 0 0 0 13.583 8h-3.166A2.417 " - "2.417 0 0 0 8 10.417C8 11.29 8.709 12 9.583 12h4.451ZM9.5 10.417c0-.507.41-.917" - ".917-.917h3.166c.507 0 .917.41.917.917a.083.083 0 0 1-.083.083H9.583a.083.083" - " 0 0 1-.083-.083ZM23 17.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Zm-5 .5.001 " - "2.503a.5.5 0 1 1-1 0V18h-2.505a.5.5 0 0 1 0-1H17v-2.5a.5.5 0 1 1 1 0V17h2.497" - "a.5.5 0 0 1 0 1H18Z", + path="M12 2a3.75 3.75 0 0 0-3.736 3.424A7.999 7.999 0 0 0 4 12.5v6.25A3.25 3.25 0 0 0 7.25 22h5.56a6.518 6.518 0 0 1-1.078-1.5H7.25a1.75 1.75 0 0 1-1.75-1.75v-3.036H8v1.536a.75.75 0 0 0 1.5 0v-1.536h1.748c.175-.613.438-1.19.774-1.714H5.5v-1.5a6.5 6.5 0 0 1 12.838-1.446 6.455 6.455 0 0 1 1.596.417 8.006 8.006 0 0 0-4.198-6.047A3.75 3.75 0 0 0 12 2Zm0 2.5c-.698 0-1.374.09-2.02.257a2.25 2.25 0 0 1 4.04 0A8.013 8.013 0 0 0 12 4.5ZM14.034 12a6.465 6.465 0 0 1 1.74-.768c.144-.239.226-.517.226-.815A2.417 2.417 0 0 0 13.583 8h-3.166A2.417 2.417 0 0 0 8 10.417C8 11.29 8.709 12 9.583 12h4.451ZM9.5 10.417c0-.507.41-.917.917-.917h3.166c.507 0 .917.41.917.917a.083.083 0 0 1-.083.083H9.583a.083.083 0 0 1-.083-.083ZM23 17.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Zm-5 .5.001 2.503a.5.5 0 1 1-1 0V18h-2.505a.5.5 0 0 1 0-1H17v-2.5a.5.5 0 1 1 1 0V17h2.497a.5.5 0 0 1 0 1H18Z", view_box=(0, 0, 24, 24), size=(1, 3), fallback="[B]", ) ICON_SOFA = Icon( - path="M21 9V7C21 5.35 19.65 4 18 4H14C13.23 4 12.53 4.3 12 4.78" - "C11.47 4.3 10.77 4 10 4H6C4.35 4 3 5.35 3 7V9C1.35 9 0 10.35 0 12V17" - "C0 18.65 1.35 20 3 20V22H5V20H19V22H21V20C22.65 20 24 18.65 24 17V12" - "C24 10.35 22.65 9 21 9M14 6H18C18.55 6 19 6.45 19 7V9.78" - "C18.39 10.33 18 11.12 18 12V14H13V7C13 6.45 13.45 6 14 6M5 7" - "C5 6.45 5.45 6 6 6H10C10.55 6 11 6.45 11 7V14H6V12C6 11.12 5.61 10.33 5 9.78" - "V7M22 17C22 17.55 21.55 18 21 18H3C2.45 18 2 17.55 2 17V12" - "C2 11.45 2.45 11 3 11S4 11.45 4 12V16H20V12C20 11.45 20.45 11 21 11S22 11.45 22 12V17Z", + path="M21 9V7C21 5.35 19.65 4 18 4H14C13.23 4 12.53 4.3 12 4.78C11.47 4.3 10.77 4 10 4H6C4.35 4 3 5.35 3 7V9C1.35 9 0 10.35 0 12V17C0 18.65 1.35 20 3 20V22H5V20H19V22H21V20C22.65 20 24 18.65 24 17V12C24 10.35 22.65 9 21 9M14 6H18C18.55 6 19 6.45 19 7V9.78C18.39 10.33 18 11.12 18 12V14H13V7C13 6.45 13.45 6 14 6M5 7C5 6.45 5.45 6 6 6H10C10.55 6 11 6.45 11 7V14H6V12C6 11.12 5.61 10.33 5 9.78V7M22 17C22 17.55 21.55 18 21 18H3C2.45 18 2 17.55 2 17V12C2 11.45 2.45 11 3 11S4 11.45 4 12V16H20V12C20 11.45 20.45 11 21 11S22 11.45 22 12V17Z", size=(4, 10), view_box=(0, 0, 24, 24), fallback="[S]", @@ -90,13 +58,7 @@ view_box=(0, 0, 128, 128), size=(1, 3), fallback="[P]", - path="M37.73 26.48L90.27 26.48Q96.79 26.48 101.41 31.11Q106.04 35.73 106.04 42.25" - "L106.04 42.25L106.04 79.03Q106.04 85.54 101.41 90.17Q96.79 94.79 90.27 94.79" - "L90.27 94.79L37.73 94.79Q31.21 94.79 26.59 90.17Q21.96 85.54 21.96 79.03" - "L21.96 79.03L21.96 42.25Q21.96 35.73 26.59 31.11Q31.21 26.48 37.73 26.48" - "L37.73 26.48ZM71.99 31.74L37.73 31.74Q33.31 31.74 30.27 34.78" - "Q27.22 37.83 27.22 42.25L27.22 42.25L27.22 79.03Q27.22 83.44 30.27 86.49" - "Q33.31 89.54 37.73 89.54L37.73 89.54L71.99 89.54L71.99 31.74Z", + path="M37.73 26.48L90.27 26.48Q96.79 26.48 101.41 31.11Q106.04 35.73 106.04 42.25L106.04 42.25L106.04 79.03Q106.04 85.54 101.41 90.17Q96.79 94.79 90.27 94.79L90.27 94.79L37.73 94.79Q31.21 94.79 26.59 90.17Q21.96 85.54 21.96 79.03L21.96 79.03L21.96 42.25Q21.96 35.73 26.59 31.11Q31.21 26.48 37.73 26.48L37.73 26.48ZM71.99 31.74L37.73 31.74Q33.31 31.74 30.27 34.78Q27.22 37.83 27.22 42.25L27.22 42.25L27.22 79.03Q27.22 83.44 30.27 86.49Q33.31 89.54 37.73 89.54L37.73 89.54L71.99 89.54L71.99 31.74Z", ) ICON_FOOT = Icon( view_box=(0, 0, 128, 128), @@ -272,7 +234,7 @@ def candidate_clicked(clicked: int) -> Candidate: match event: case SweepSize() | SweepWindow(): continue - case SweepSelect(items) if len(items) == 1: + case SweepSelect(items=items) if len(items) == 1: item = items[0] if isinstance(item, Candidate) and item.extra: match item.extra: diff --git a/sweep-py/sweep/apps/mpd.py b/sweep-py/sweep/apps/mpd.py index 03a5c71..810e478 100644 --- a/sweep-py/sweep/apps/mpd.py +++ b/sweep-py/sweep/apps/mpd.py @@ -17,7 +17,7 @@ from dataclasses import dataclass from datetime import datetime from enum import Enum -from typing import Any, NamedTuple, cast +from typing import Any, NamedTuple, cast, final, override from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence from PIL import Image as PILImage @@ -190,11 +190,13 @@ def __init__(self, file: str) -> None: self.id = None self.current = None + @override def __eq__(self, other: Any) -> bool: if not isinstance(other, Song): return False return self.file == other.file + @override def __hash__(self) -> int: return hash(self.file) @@ -412,12 +414,14 @@ def get_bytes(self) -> bytes: return cast(bytes, self.data) return b"" + @override def __repr__(self) -> str: if self.name == "binary": return f"binary={len(self.data)}" return f"{self.name}={self.data}" +@final class MPD: """MPD Client implementation @@ -725,10 +729,10 @@ class MPDSweepView(Enum): class MPDSweep: def __init__(self, mpd: MPD, sweep: Sweep[Song]) -> None: - self._mpd = mpd - self._sweep = sweep - self._view = MPDSweepView.MAX - self._events_queue = asyncio.Queue[MPDEvent]() + self._mpd: MPD = mpd + self._sweep: Sweep[Song] = sweep + self._view: MPDSweepView = MPDSweepView.MAX + self._events_queue: asyncio.Queue[MPDEvent] = asyncio.Queue() async def run(self) -> None: # fields @@ -973,7 +977,7 @@ async def _playlist_song_move_down(self) -> None: return await self._mpd.move(song, 1) - async def _goto(self, sweep: Sweep[Song], tag: str) -> None: + async def _goto(self, sweep: Sweep[Song], _tag: str) -> None: song = await self._sweep.items_current() if song is None: return None diff --git a/sweep-py/sweep/sweep.py b/sweep-py/sweep/sweep.py index 00cc7de..88bf6bb 100755 --- a/sweep-py/sweep/sweep.py +++ b/sweep-py/sweep/sweep.py @@ -102,9 +102,18 @@ def from_json(obj: Any) -> Size: return Size(height, width) +@dataclass +class SweepSelect[I]: + """Event generated on item(s) select""" + + uid: WindowId + items: list[I] + + class SweepBind(NamedTuple): """Event generated on bound key press""" + uid: WindowId tag: str key: str | None @@ -135,16 +144,6 @@ def from_json(obj: Any) -> SweepSize: return SweepSize(cells, pixels, pixels_per_cell) -@dataclass -class SweepSelect[I]: - """Event generated on item(s) select""" - - items: list[I] - - def __init__(self, items: list[I]): - self.items = items - - @dataclass class SweepWindow: """Fired on window transition""" @@ -671,23 +670,26 @@ async def event_iter() -> AsyncGenerator[SweepEvent[I], None]: continue if event.method == "select": yield SweepSelect( - [ + uid=event.params["uid"], + items=[ self.__item_get(item) for item in event.params.get("items", []) - ] + ], ) elif event.method == "bind": + uid = event.params["uid"] tag = event.params.get("tag", "") handler = self.__binds.get(tag) if handler is None: yield SweepBind( - event.params.get("tag", ""), - event.params.get("key", None), + uid=uid, + tag=tag, + key=event.params.get("key", None), ) else: item = await handler(self, tag) if item is not None: - yield SweepSelect([item]) + yield SweepSelect(uid, items=[item]) elif event.method == "resize": size = SweepSize.from_json(event.params) self.__size = size