From 9c9e15c97b13b85ba9f05abd8b3220422dddd358 Mon Sep 17 00:00:00 2001 From: Loric ANDRE Date: Sun, 8 Mar 2026 18:45:53 +0100 Subject: [PATCH] feat: add Skim::run_items API --- examples/base.rs | 12 ++++++++++++ src/prelude.rs | 1 + src/skim.rs | 24 +++++++++++++++++++++++- src/skim_item.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 examples/base.rs diff --git a/examples/base.rs b/examples/base.rs new file mode 100644 index 00000000..597f89cf --- /dev/null +++ b/examples/base.rs @@ -0,0 +1,12 @@ +use skim::prelude::*; + +fn main() -> color_eyre::Result<()> { + let opts = SkimOptionsBuilder::default().multi(true).reverse(true).build()?; + let res = Skim::run_items(opts, ["hello", "world"])?; + + for item in res.selected_items { + println!("Selected {} (id {})", item.output(), item.get_index()); + } + + Ok(()) +} diff --git a/src/prelude.rs b/src/prelude.rs index bc175d6a..a83b126c 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -13,6 +13,7 @@ pub use crate::helper::selector::DefaultSkimSelector; pub use crate::options::{SkimOptions, SkimOptionsBuilder}; pub use crate::output::SkimOutput; pub use crate::reader::CommandCollector; +pub use crate::skim_item::Item; pub use crate::tui::{Event, PreviewCallback, event::Action}; pub use crate::*; pub use kanal::{Receiver, Sender, bounded, unbounded}; diff --git a/src/skim.rs b/src/skim.rs index dc6257f5..2c85d580 100644 --- a/src/skim.rs +++ b/src/skim.rs @@ -11,7 +11,7 @@ use tokio::{runtime::Handle, select, task::block_in_place}; use crate::reader::{Reader, ReaderControl}; use crate::tui::{App, Event, Size, Tui, event::Action}; -use crate::{SkimItemReceiver, SkimOptions, SkimOutput}; +use crate::{SkimItem, SkimItemReceiver, SkimOptions, SkimOutput}; /// Main entry point for running skim pub struct Skim>> @@ -79,6 +79,28 @@ impl Skim { Ok(output) } + /// Run skim with a Vec of items + pub fn run_items(options: SkimOptions, items: I) -> Result + where + I: IntoIterator, + T: SkimItem, + { + const BATCH_SIZE: usize = 1024; + let (tx, rx) = crate::prelude::unbounded(); + let mut batch: Vec> = Vec::with_capacity(BATCH_SIZE); + for (idx, raw_item) in items.into_iter().enumerate() { + if batch.len() == 1024 { + tx.send(batch)?; + batch = Vec::with_capacity(BATCH_SIZE); + } + let mut item = crate::prelude::Item::from(raw_item); + item.set_index(idx); + batch.push(Arc::new(item) as Arc); + } + tx.send(batch)?; + Self::run_with(options, Some(rx)) + } + /// Initialize the TUI with the default crossterm backend, but do not enter it yet pub fn init_tui(&mut self) -> Result<()> { self.tui = Some(Tui::new_with_height(self.height)?); diff --git a/src/skim_item.rs b/src/skim_item.rs index e9783b43..8fac4843 100644 --- a/src/skim_item.rs +++ b/src/skim_item.rs @@ -97,6 +97,47 @@ impl + Send + Sync + 'static> SkimItem for T { } } +/// A basic SkimItem implementation for basic types +pub struct Item { + inner: T, + index: usize, +} + +impl SkimItem for Item { + fn text(&self) -> Cow<'_, str> { + self.inner.text() + } + fn display<'a>(&'a self, context: DisplayContext) -> Line<'a> { + self.inner.display(context) + } + + fn preview(&self, context: PreviewContext) -> ItemPreview { + self.inner.preview(context) + } + + fn output(&self) -> Cow<'_, str> { + self.inner.output() + } + + fn get_matching_ranges(&self) -> Option<&[(usize, usize)]> { + self.inner.get_matching_ranges() + } + + fn get_index(&self) -> usize { + self.index + } + + fn set_index(&mut self, index: usize) { + self.index = index; + } +} + +impl From for Item { + fn from(value: T) -> Self { + Self { inner: value, index: 0 } + } +} + impl Display for dyn SkimItem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&self.text())