feat: add Skim::run_items API

This commit is contained in:
Loric ANDRE 2026-03-08 18:45:53 +01:00
parent 396f2953a9
commit 9c9e15c97b
4 changed files with 77 additions and 1 deletions

12
examples/base.rs Normal file
View file

@ -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(())
}

View file

@ -13,6 +13,7 @@ pub use crate::helper::selector::DefaultSkimSelector;
pub use crate::options::{SkimOptions, SkimOptionsBuilder}; pub use crate::options::{SkimOptions, SkimOptionsBuilder};
pub use crate::output::SkimOutput; pub use crate::output::SkimOutput;
pub use crate::reader::CommandCollector; pub use crate::reader::CommandCollector;
pub use crate::skim_item::Item;
pub use crate::tui::{Event, PreviewCallback, event::Action}; pub use crate::tui::{Event, PreviewCallback, event::Action};
pub use crate::*; pub use crate::*;
pub use kanal::{Receiver, Sender, bounded, unbounded}; pub use kanal::{Receiver, Sender, bounded, unbounded};

View file

@ -11,7 +11,7 @@ use tokio::{runtime::Handle, select, task::block_in_place};
use crate::reader::{Reader, ReaderControl}; use crate::reader::{Reader, ReaderControl};
use crate::tui::{App, Event, Size, Tui, event::Action}; 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 /// Main entry point for running skim
pub struct Skim<Backend = ratatui::backend::CrosstermBackend<BufWriter<Stderr>>> pub struct Skim<Backend = ratatui::backend::CrosstermBackend<BufWriter<Stderr>>>
@ -79,6 +79,28 @@ impl Skim {
Ok(output) Ok(output)
} }
/// Run skim with a Vec of items
pub fn run_items<I, T>(options: SkimOptions, items: I) -> Result<SkimOutput>
where
I: IntoIterator<Item = T>,
T: SkimItem,
{
const BATCH_SIZE: usize = 1024;
let (tx, rx) = crate::prelude::unbounded();
let mut batch: Vec<Arc<dyn SkimItem>> = 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<dyn SkimItem>);
}
tx.send(batch)?;
Self::run_with(options, Some(rx))
}
/// Initialize the TUI with the default crossterm backend, but do not enter it yet /// Initialize the TUI with the default crossterm backend, but do not enter it yet
pub fn init_tui(&mut self) -> Result<()> { pub fn init_tui(&mut self) -> Result<()> {
self.tui = Some(Tui::new_with_height(self.height)?); self.tui = Some(Tui::new_with_height(self.height)?);

View file

@ -97,6 +97,47 @@ impl<T: AsRef<str> + Send + Sync + 'static> SkimItem for T {
} }
} }
/// A basic SkimItem implementation for basic types
pub struct Item<T: SkimItem> {
inner: T,
index: usize,
}
impl<T: SkimItem> SkimItem for Item<T> {
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<T: SkimItem> From<T> for Item<T> {
fn from(value: T) -> Self {
Self { inner: value, index: 0 }
}
}
impl Display for dyn SkimItem { impl Display for dyn SkimItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.text()) f.write_str(&self.text())