From a7777b3c118d7cd08868cb688e281b290898a31e Mon Sep 17 00:00:00 2001
From: Michael Davis <mcarsondavis@gmail.com>
Date: Wed, 24 Apr 2024 09:22:22 -0400
Subject: [PATCH] Accept 'IntoIterator<Item = Column<T, D>>' for picker columns

This allows us to replace any `vec![..]`s of columns where all columns
are static with static slices `[..]`.
---
 helix-term/src/commands.rs       | 10 +++++-----
 helix-term/src/commands/dap.rs   |  6 +++---
 helix-term/src/commands/lsp.rs   |  6 +++---
 helix-term/src/commands/typed.rs |  2 +-
 helix-term/src/ui/mod.rs         |  2 +-
 helix-term/src/ui/picker.rs      | 26 ++++++++++++++++++--------
 6 files changed, 31 insertions(+), 21 deletions(-)

diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 84769594..097c3493 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -2265,7 +2265,7 @@ fn global_search(cx: &mut Context) {
         file_picker_config: config.file_picker.clone(),
     };
 
-    let columns = vec![
+    let columns = [
         PickerColumn::new("path", |item: &FileResult, _| {
             let path = helix_stdx::path::get_relative_path(&item.path);
             format!("{}:{}", path.to_string_lossy(), item.line_num + 1).into()
@@ -2886,7 +2886,7 @@ fn buffer_picker(cx: &mut Context) {
     // mru
     items.sort_unstable_by_key(|item| std::cmp::Reverse(item.focused_at));
 
-    let columns = vec![
+    let columns = [
         PickerColumn::new("id", |meta: &BufferMeta, _| meta.id.to_string().into()),
         PickerColumn::new("flags", |meta: &BufferMeta, _| {
             let mut flags = String::new();
@@ -2960,7 +2960,7 @@ fn jumplist_picker(cx: &mut Context) {
         }
     };
 
-    let columns = vec![
+    let columns = [
         ui::PickerColumn::new("id", |item: &JumpMeta, _| item.id.to_string().into()),
         ui::PickerColumn::new("path", |item: &JumpMeta, _| {
             let path = item
@@ -3039,7 +3039,7 @@ fn changed_file_picker(cx: &mut Context) {
     let deleted = cx.editor.theme.get("diff.minus");
     let renamed = cx.editor.theme.get("diff.delta.moved");
 
-    let columns = vec![
+    let columns = [
         PickerColumn::new("change", |change: &FileChange, data: &FileChangeData| {
             match change {
                 FileChange::Untracked { .. } => Span::styled("+ untracked", data.style_untracked),
@@ -3130,7 +3130,7 @@ pub fn command_palette(cx: &mut Context) {
                     }),
             );
 
-            let columns = vec![
+            let columns = [
                 ui::PickerColumn::new("name", |item, _| match item {
                     MappableCommand::Typable { name, .. } => format!(":{name}").into(),
                     MappableCommand::Static { name, .. } => (*name).into(),
diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs
index 39c79a66..0b754bc2 100644
--- a/helix-term/src/commands/dap.rs
+++ b/helix-term/src/commands/dap.rs
@@ -41,7 +41,7 @@ fn thread_picker(
             let debugger = debugger!(editor);
 
             let thread_states = debugger.thread_states.clone();
-            let columns = vec![
+            let columns = [
                 ui::PickerColumn::new("name", |item: &Thread, _| item.name.as_str().into()),
                 ui::PickerColumn::new("state", |item: &Thread, thread_states: &ThreadStates| {
                     thread_states
@@ -250,7 +250,7 @@ pub fn dap_launch(cx: &mut Context) {
 
     let templates = config.templates.clone();
 
-    let columns = vec![ui::PickerColumn::new(
+    let columns = [ui::PickerColumn::new(
         "template",
         |item: &DebugTemplate, _| item.name.as_str().into(),
     )];
@@ -725,7 +725,7 @@ pub fn dap_switch_stack_frame(cx: &mut Context) {
 
     let frames = debugger.stack_frames[&thread_id].clone();
 
-    let columns = vec![ui::PickerColumn::new("frame", |item: &StackFrame, _| {
+    let columns = [ui::PickerColumn::new("frame", |item: &StackFrame, _| {
         item.name.as_str().into() // TODO: include thread_states in the label
     })];
     let picker = Picker::new(columns, 0, frames, (), move |cx, frame, _action| {
diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs
index 601c58eb..103d1df2 100644
--- a/helix-term/src/commands/lsp.rs
+++ b/helix-term/src/commands/lsp.rs
@@ -371,7 +371,7 @@ pub fn symbol_picker(cx: &mut Context) {
             symbols.append(&mut lsp_items);
         }
         let call = move |_editor: &mut Editor, compositor: &mut Compositor| {
-            let columns = vec![
+            let columns = [
                 ui::PickerColumn::new("kind", |item: &SymbolInformationItem, _| {
                     display_symbol_kind(item.symbol.kind).into()
                 }),
@@ -478,7 +478,7 @@ pub fn workspace_symbol_picker(cx: &mut Context) {
         }
         .boxed()
     };
-    let columns = vec![
+    let columns = [
         ui::PickerColumn::new("kind", |item: &SymbolInformationItem, _| {
             display_symbol_kind(item.symbol.kind).into()
         }),
@@ -855,7 +855,7 @@ fn goto_impl(
         }
         [] => unreachable!("`locations` should be non-empty for `goto_impl`"),
         _locations => {
-            let columns = vec![ui::PickerColumn::new(
+            let columns = [ui::PickerColumn::new(
                 "location",
                 |item: &lsp::Location, cwdir: &std::path::PathBuf| {
                     // The preallocation here will overallocate a few characters since it will account for the
diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs
index f65f9657..1b828b3f 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -1404,7 +1404,7 @@ fn lsp_workspace_command(
         let callback = async move {
             let call: job::Callback = Callback::EditorCompositor(Box::new(
                 move |_editor: &mut Editor, compositor: &mut Compositor| {
-                    let columns = vec![ui::PickerColumn::new(
+                    let columns = [ui::PickerColumn::new(
                         "title",
                         |(_ls_id, command): &(_, helix_lsp::lsp::Command), _| {
                             command.title.as_str().into()
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index e1ecf0a6..fae64062 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -219,7 +219,7 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
     });
     log::debug!("file_picker init {:?}", Instant::now().duration_since(now));
 
-    let columns = vec![PickerColumn::new(
+    let columns = [PickerColumn::new(
         "path",
         |item: &PathBuf, root: &PathBuf| {
             item.strip_prefix(root)
diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs
index c65e839c..86943165 100644
--- a/helix-term/src/ui/picker.rs
+++ b/helix-term/src/ui/picker.rs
@@ -275,7 +275,11 @@ pub struct Picker<T: 'static + Send + Sync, D: 'static> {
 }
 
 impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
-    pub fn stream(columns: Vec<Column<T, D>>, editor_data: D) -> (Nucleo<T>, Injector<T, D>) {
+    pub fn stream(
+        columns: impl IntoIterator<Item = Column<T, D>>,
+        editor_data: D,
+    ) -> (Nucleo<T>, Injector<T, D>) {
+        let columns: Arc<[_]> = columns.into_iter().collect();
         let matcher_columns = columns.iter().filter(|col| col.filter).count() as u32;
         assert!(matcher_columns > 0);
         let matcher = Nucleo::new(
@@ -286,7 +290,7 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
         );
         let streamer = Injector {
             dst: matcher.injector(),
-            columns: columns.into(),
+            columns,
             editor_data: Arc::new(editor_data),
             version: 0,
             picker_version: Arc::new(AtomicUsize::new(0)),
@@ -295,13 +299,19 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
         (matcher, streamer)
     }
 
-    pub fn new(
-        columns: Vec<Column<T, D>>,
+    pub fn new<C, O, F>(
+        columns: C,
         primary_column: usize,
-        options: impl IntoIterator<Item = T>,
+        options: O,
         editor_data: D,
-        callback_fn: impl Fn(&mut Context, &T, Action) + 'static,
-    ) -> Self {
+        callback_fn: F,
+    ) -> Self
+    where
+        C: IntoIterator<Item = Column<T, D>>,
+        O: IntoIterator<Item = T>,
+        F: Fn(&mut Context, &T, Action) + 'static,
+    {
+        let columns: Arc<[_]> = columns.into_iter().collect();
         let matcher_columns = columns.iter().filter(|col| col.filter).count() as u32;
         assert!(matcher_columns > 0);
         let matcher = Nucleo::new(
@@ -316,7 +326,7 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
         }
         Self::with(
             matcher,
-            columns.into(),
+            columns,
             primary_column,
             Arc::new(editor_data),
             Arc::new(AtomicUsize::new(0)),