/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
cli/tools/pm/interactive_picker.rs
170 строк
5 KB
Bartek Iwańczuk
chore: upgrade Rust toolchain to 1.95.0 (#33294)
07 май 2026, 09:44
Не верифицирован
07 май 2026, 09:44
269f316
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. use std::collections::HashSet; use std::io; use console_static_text::TextItem; use crossterm::ExecutableCommand; use crossterm::cursor; use crossterm::event::KeyCode; use crossterm::event::KeyEvent; use crossterm::event::KeyEventKind; use crossterm::event::KeyModifiers; use crossterm::terminal; use deno_core::anyhow; use deno_terminal::colors; use unicode_width::UnicodeWidthStr; use crate::util::console::HideCursorGuard; use crate::util::console::RawMode; use crate::util::console::new_console_static_text; pub fn select_items<T, TRender>( instructions_line: &str, items: &[T], initial_checked: HashSet<usize>, mut render_item: TRender, ) -> anyhow::Result<Option<HashSet<usize>>> where TRender: FnMut(usize, bool, bool, &T) -> anyhow::Result<TextItem<'static>>, { if items.is_empty() { return Ok(Some(HashSet::new())); } let mut stderr = io::stderr(); let raw_mode = RawMode::enable()?; let mut static_text = new_console_static_text(); static_text.keep_cursor_zero_column(true); let (_, start_row) = cursor::position().unwrap_or_default(); let (_, rows) = terminal::size()?; if rows - start_row < (items.len() + 2) as u16 { let pad = ((items.len() + 2) as u16) - (rows - start_row); stderr.execute(terminal::ScrollUp(pad.min(rows)))?; stderr.execute(cursor::MoveUp(pad.min(rows)))?; } let mut currently_selected = 0; let mut checked = initial_checked; let hide_cursor_guard = HideCursorGuard::hide()?; let instructions_width = format!("? {}", instructions_line).width(); let mut do_it = false; let mut scroll_offset = 0; loop { let mut rendered_items = Vec::with_capacity(items.len() + 1); rendered_items.push(TextItem::new_owned(format!( "{} {}", colors::intense_blue("?"), instructions_line ))); for (idx, item) in items.iter().enumerate() { rendered_items.push(render_item( idx, idx == currently_selected, checked.contains(&idx), item, )?); } let size = static_text.console_size(); let first_line_rows = size .cols .map(|cols| (instructions_width / cols as usize) + 1) .unwrap_or(1); if let Some(rows) = size.rows && rendered_items.len() + first_line_rows >= rows as usize { let adj = if scroll_offset == 0 { first_line_rows.saturating_sub(1) } else { 0 }; if currently_selected < scroll_offset { scroll_offset = currently_selected; } else if currently_selected + 1 >= scroll_offset + (rows as usize).saturating_sub(adj) { scroll_offset = (currently_selected + 1).saturating_sub(rows as usize) + 1; } let adj = if scroll_offset == 0 { first_line_rows.saturating_sub(1) } else { 0 }; let mut new_items = Vec::with_capacity(rows as usize); scroll_offset = scroll_offset.clamp(0, rendered_items.len() - 1); new_items.extend( rendered_items.drain( scroll_offset ..(scroll_offset + (rows as usize).saturating_sub(adj)) .min(rendered_items.len()), ), ); rendered_items = new_items; } static_text.eprint_items(rendered_items.iter()); let event = crossterm::event::read()?; #[allow(clippy::single_match, reason = "more clear")] match event { crossterm::event::Event::Key(KeyEvent { kind: KeyEventKind::Press, code, modifiers, .. }) => match (code, modifiers) { (KeyCode::Char('c'), KeyModifiers::CONTROL) => break, (KeyCode::Up | KeyCode::Char('k'), KeyModifiers::NONE) => { currently_selected = if currently_selected == 0 { items.len() - 1 } else { currently_selected - 1 }; } (KeyCode::Down | KeyCode::Char('j'), KeyModifiers::NONE) => { currently_selected = (currently_selected + 1) % items.len(); } (KeyCode::Char(' '), _) if !checked.insert(currently_selected) => { checked.remove(¤tly_selected); } (KeyCode::Char('a'), _) => { if (0..items.len()).all(|idx| checked.contains(&idx)) { checked.clear(); } else { checked.extend(0..items.len()); } } (KeyCode::Char('i'), _) => { for idx in 0..items.len() { if checked.contains(&idx) { checked.remove(&idx); } else { checked.insert(idx); } } } (KeyCode::Enter, _) => { do_it = true; break; } _ => {} }, _ => {} } } static_text.eprint_clear(); hide_cursor_guard.show()?; raw_mode.disable()?; if do_it { Ok(Some(checked)) } else { Ok(None) } }