|
|
@ -6,7 +6,6 @@ pub use dap::*;
|
|
|
|
use helix_stdx::rope::{self, RopeSliceExt};
|
|
|
|
use helix_stdx::rope::{self, RopeSliceExt};
|
|
|
|
use helix_vcs::Hunk;
|
|
|
|
use helix_vcs::Hunk;
|
|
|
|
pub use lsp::*;
|
|
|
|
pub use lsp::*;
|
|
|
|
use tui::widgets::Row;
|
|
|
|
|
|
|
|
pub use typed::*;
|
|
|
|
pub use typed::*;
|
|
|
|
|
|
|
|
|
|
|
|
use helix_core::{
|
|
|
|
use helix_core::{
|
|
|
@ -53,8 +52,7 @@ use crate::{
|
|
|
|
compositor::{self, Component, Compositor},
|
|
|
|
compositor::{self, Component, Compositor},
|
|
|
|
filter_picker_entry,
|
|
|
|
filter_picker_entry,
|
|
|
|
job::Callback,
|
|
|
|
job::Callback,
|
|
|
|
keymap::ReverseKeymap,
|
|
|
|
ui::{self, overlay::overlaid, Picker, PickerColumn, Popup, Prompt, PromptEvent},
|
|
|
|
ui::{self, overlay::overlaid, Picker, Popup, Prompt, PromptEvent},
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
use crate::job::{self, Jobs};
|
|
|
|
use crate::job::{self, Jobs};
|
|
|
@ -2200,32 +2198,15 @@ fn global_search(cx: &mut Context) {
|
|
|
|
path: PathBuf,
|
|
|
|
path: PathBuf,
|
|
|
|
/// 0 indexed lines
|
|
|
|
/// 0 indexed lines
|
|
|
|
line_num: usize,
|
|
|
|
line_num: usize,
|
|
|
|
|
|
|
|
line_content: String,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl FileResult {
|
|
|
|
impl FileResult {
|
|
|
|
fn new(path: &Path, line_num: usize) -> Self {
|
|
|
|
fn new(path: &Path, line_num: usize, line_content: String) -> Self {
|
|
|
|
Self {
|
|
|
|
Self {
|
|
|
|
path: path.to_path_buf(),
|
|
|
|
path: path.to_path_buf(),
|
|
|
|
line_num,
|
|
|
|
line_num,
|
|
|
|
}
|
|
|
|
line_content,
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl ui::menu::Item for FileResult {
|
|
|
|
|
|
|
|
type Data = Option<PathBuf>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn format(&self, current_path: &Self::Data) -> Row {
|
|
|
|
|
|
|
|
let relative_path = helix_stdx::path::get_relative_path(&self.path)
|
|
|
|
|
|
|
|
.to_string_lossy()
|
|
|
|
|
|
|
|
.into_owned();
|
|
|
|
|
|
|
|
if current_path
|
|
|
|
|
|
|
|
.as_ref()
|
|
|
|
|
|
|
|
.map(|p| p == &self.path)
|
|
|
|
|
|
|
|
.unwrap_or(false)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
format!("{} (*)", relative_path).into()
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
relative_path.into()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -2271,8 +2252,28 @@ fn global_search(cx: &mut Context) {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
let columns = vec![
|
|
|
|
let columns = vec![];
|
|
|
|
PickerColumn::new(
|
|
|
|
|
|
|
|
"path",
|
|
|
|
|
|
|
|
|item: &FileResult, current_path: &Option<PathBuf>| {
|
|
|
|
|
|
|
|
let relative_path = helix_stdx::path::get_relative_path(&item.path)
|
|
|
|
|
|
|
|
.to_string_lossy()
|
|
|
|
|
|
|
|
.into_owned();
|
|
|
|
|
|
|
|
if current_path
|
|
|
|
|
|
|
|
.as_ref()
|
|
|
|
|
|
|
|
.map(|p| p == &item.path)
|
|
|
|
|
|
|
|
.unwrap_or(false)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
format!("{} (*)", relative_path).into()
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
relative_path.into()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
PickerColumn::new("contents", |item: &FileResult, _| {
|
|
|
|
|
|
|
|
item.line_content.as_str().into()
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
];
|
|
|
|
let (picker, injector) = Picker::stream(columns, current_path);
|
|
|
|
let (picker, injector) = Picker::stream(columns, current_path);
|
|
|
|
|
|
|
|
|
|
|
|
let dedup_symlinks = file_picker_config.deduplicate_links;
|
|
|
|
let dedup_symlinks = file_picker_config.deduplicate_links;
|
|
|
@ -2299,77 +2300,79 @@ fn global_search(cx: &mut Context) {
|
|
|
|
.max_depth(file_picker_config.max_depth)
|
|
|
|
.max_depth(file_picker_config.max_depth)
|
|
|
|
.filter_entry(move |entry| {
|
|
|
|
.filter_entry(move |entry| {
|
|
|
|
filter_picker_entry(entry, &absolute_root, dedup_symlinks)
|
|
|
|
filter_picker_entry(entry, &absolute_root, dedup_symlinks)
|
|
|
|
});
|
|
|
|
})
|
|
|
|
|
|
|
|
.add_custom_ignore_filename(helix_loader::config_dir().join("ignore"))
|
|
|
|
|
|
|
|
.add_custom_ignore_filename(".helix/ignore")
|
|
|
|
|
|
|
|
.build_parallel()
|
|
|
|
|
|
|
|
.run(|| {
|
|
|
|
|
|
|
|
let mut searcher = searcher.clone();
|
|
|
|
|
|
|
|
let matcher = matcher.clone();
|
|
|
|
|
|
|
|
let injector = injector_.clone();
|
|
|
|
|
|
|
|
let documents = &documents;
|
|
|
|
|
|
|
|
Box::new(move |entry: Result<DirEntry, ignore::Error>| -> WalkState {
|
|
|
|
|
|
|
|
let entry = match entry {
|
|
|
|
|
|
|
|
Ok(entry) => entry,
|
|
|
|
|
|
|
|
Err(_) => return WalkState::Continue,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
walk_builder
|
|
|
|
match entry.file_type() {
|
|
|
|
.add_custom_ignore_filename(helix_loader::config_dir().join("ignore"));
|
|
|
|
Some(entry) if entry.is_file() => {}
|
|
|
|
walk_builder.add_custom_ignore_filename(".helix/ignore");
|
|
|
|
// skip everything else
|
|
|
|
|
|
|
|
_ => return WalkState::Continue,
|
|
|
|
walk_builder.build_parallel().run(|| {
|
|
|
|
};
|
|
|
|
let mut searcher = searcher.clone();
|
|
|
|
|
|
|
|
let matcher = matcher.clone();
|
|
|
|
let mut stop = false;
|
|
|
|
let injector = injector_.clone();
|
|
|
|
let sink = sinks::UTF8(|line_num, line_content| {
|
|
|
|
let documents = &documents;
|
|
|
|
stop = injector
|
|
|
|
Box::new(move |entry: Result<DirEntry, ignore::Error>| -> WalkState {
|
|
|
|
.push(FileResult::new(
|
|
|
|
let entry = match entry {
|
|
|
|
entry.path(),
|
|
|
|
Ok(entry) => entry,
|
|
|
|
line_num as usize - 1,
|
|
|
|
Err(_) => return WalkState::Continue,
|
|
|
|
line_content.to_string(),
|
|
|
|
};
|
|
|
|
))
|
|
|
|
|
|
|
|
.is_err();
|
|
|
|
match entry.file_type() {
|
|
|
|
|
|
|
|
Some(entry) if entry.is_file() => {}
|
|
|
|
Ok(!stop)
|
|
|
|
// skip everything else
|
|
|
|
});
|
|
|
|
_ => return WalkState::Continue,
|
|
|
|
let doc = documents.iter().find(|&(doc_path, _)| {
|
|
|
|
};
|
|
|
|
doc_path
|
|
|
|
|
|
|
|
.as_ref()
|
|
|
|
let mut stop = false;
|
|
|
|
.map_or(false, |doc_path| doc_path == entry.path())
|
|
|
|
let sink = sinks::UTF8(|line_num, _| {
|
|
|
|
});
|
|
|
|
stop = injector
|
|
|
|
|
|
|
|
.push(FileResult::new(entry.path(), line_num as usize - 1))
|
|
|
|
let result = if let Some((_, doc)) = doc {
|
|
|
|
.is_err();
|
|
|
|
// there is already a buffer for this file
|
|
|
|
|
|
|
|
// search the buffer instead of the file because it's faster
|
|
|
|
Ok(!stop)
|
|
|
|
// and captures new edits without requiring a save
|
|
|
|
});
|
|
|
|
if searcher.multi_line_with_matcher(&matcher) {
|
|
|
|
let doc = documents.iter().find(|&(doc_path, _)| {
|
|
|
|
// in this case a continous buffer is required
|
|
|
|
doc_path
|
|
|
|
// convert the rope to a string
|
|
|
|
.as_ref()
|
|
|
|
let text = doc.to_string();
|
|
|
|
.map_or(false, |doc_path| doc_path == entry.path())
|
|
|
|
searcher.search_slice(&matcher, text.as_bytes(), sink)
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
|
|
|
|
searcher.search_reader(
|
|
|
|
let result = if let Some((_, doc)) = doc {
|
|
|
|
&matcher,
|
|
|
|
// there is already a buffer for this file
|
|
|
|
RopeReader::new(doc.slice(..)),
|
|
|
|
// search the buffer instead of the file because it's faster
|
|
|
|
sink,
|
|
|
|
// and captures new edits without requiring a save
|
|
|
|
)
|
|
|
|
if searcher.multi_line_with_matcher(&matcher) {
|
|
|
|
}
|
|
|
|
// in this case a continous buffer is required
|
|
|
|
|
|
|
|
// convert the rope to a string
|
|
|
|
|
|
|
|
let text = doc.to_string();
|
|
|
|
|
|
|
|
searcher.search_slice(&matcher, text.as_bytes(), sink)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
searcher.search_reader(
|
|
|
|
searcher.search_path(&matcher, entry.path(), sink)
|
|
|
|
&matcher,
|
|
|
|
};
|
|
|
|
RopeReader::new(doc.slice(..)),
|
|
|
|
|
|
|
|
sink,
|
|
|
|
if let Err(err) = result {
|
|
|
|
)
|
|
|
|
log::error!(
|
|
|
|
|
|
|
|
"Global search error: {}, {}",
|
|
|
|
|
|
|
|
entry.path().display(),
|
|
|
|
|
|
|
|
err
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if stop {
|
|
|
|
searcher.search_path(&matcher, entry.path(), sink)
|
|
|
|
WalkState::Quit
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
|
|
|
|
WalkState::Continue
|
|
|
|
if let Err(err) = result {
|
|
|
|
}
|
|
|
|
log::error!(
|
|
|
|
})
|
|
|
|
"Global search error: {}, {}",
|
|
|
|
});
|
|
|
|
entry.path().display(),
|
|
|
|
|
|
|
|
err
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if stop {
|
|
|
|
|
|
|
|
WalkState::Quit
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
WalkState::Continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
cx.jobs.callback(async move {
|
|
|
|
cx.jobs.callback(async move {
|
|
|
@ -2378,7 +2381,7 @@ fn global_search(cx: &mut Context) {
|
|
|
|
picker,
|
|
|
|
picker,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
injector,
|
|
|
|
injector,
|
|
|
|
move |cx, FileResult { path, line_num }, action| {
|
|
|
|
move |cx, FileResult { path, line_num, .. }, action| {
|
|
|
|
let doc = match cx.editor.open(path, action) {
|
|
|
|
let doc = match cx.editor.open(path, action) {
|
|
|
|
Ok(id) => doc_mut!(cx.editor, &id),
|
|
|
|
Ok(id) => doc_mut!(cx.editor, &id),
|
|
|
|
Err(e) => {
|
|
|
|
Err(e) => {
|
|
|
@ -2410,7 +2413,7 @@ fn global_search(cx: &mut Context) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.with_preview(
|
|
|
|
.with_preview(
|
|
|
|
|_editor, FileResult { path, line_num }| {
|
|
|
|
|_editor, FileResult { path, line_num, .. }| {
|
|
|
|
Some((path.clone().into(), Some((*line_num, *line_num))))
|
|
|
|
Some((path.clone().into(), Some((*line_num, *line_num))))
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
);
|
|
|
@ -2848,31 +2851,6 @@ fn buffer_picker(cx: &mut Context) {
|
|
|
|
focused_at: std::time::Instant,
|
|
|
|
focused_at: std::time::Instant,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl ui::menu::Item for BufferMeta {
|
|
|
|
|
|
|
|
type Data = ();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn format(&self, _data: &Self::Data) -> Row {
|
|
|
|
|
|
|
|
let path = self
|
|
|
|
|
|
|
|
.path
|
|
|
|
|
|
|
|
.as_deref()
|
|
|
|
|
|
|
|
.map(helix_stdx::path::get_relative_path);
|
|
|
|
|
|
|
|
let path = match path.as_deref().and_then(Path::to_str) {
|
|
|
|
|
|
|
|
Some(path) => path,
|
|
|
|
|
|
|
|
None => SCRATCH_BUFFER_NAME,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut flags = String::new();
|
|
|
|
|
|
|
|
if self.is_modified {
|
|
|
|
|
|
|
|
flags.push('+');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.is_current {
|
|
|
|
|
|
|
|
flags.push('*');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Row::new([self.id.to_string(), flags, path.to_string()])
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let new_meta = |doc: &Document| BufferMeta {
|
|
|
|
let new_meta = |doc: &Document| BufferMeta {
|
|
|
|
id: doc.id(),
|
|
|
|
id: doc.id(),
|
|
|
|
path: doc.path().cloned(),
|
|
|
|
path: doc.path().cloned(),
|
|
|
@ -2891,8 +2869,31 @@ fn buffer_picker(cx: &mut Context) {
|
|
|
|
// mru
|
|
|
|
// mru
|
|
|
|
items.sort_unstable_by_key(|item| std::cmp::Reverse(item.focused_at));
|
|
|
|
items.sort_unstable_by_key(|item| std::cmp::Reverse(item.focused_at));
|
|
|
|
|
|
|
|
|
|
|
|
let columns = vec![];
|
|
|
|
let columns = vec![
|
|
|
|
let picker = Picker::new(columns, 0, items, (), |cx, meta, action| {
|
|
|
|
PickerColumn::new("id", |meta: &BufferMeta, _| meta.id.to_string().into()),
|
|
|
|
|
|
|
|
PickerColumn::new("flags", |meta: &BufferMeta, _| {
|
|
|
|
|
|
|
|
let mut flags = String::new();
|
|
|
|
|
|
|
|
if meta.is_modified {
|
|
|
|
|
|
|
|
flags.push('+');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if meta.is_current {
|
|
|
|
|
|
|
|
flags.push('*');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
flags.into()
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
PickerColumn::new("path", |meta: &BufferMeta, _| {
|
|
|
|
|
|
|
|
let path = meta
|
|
|
|
|
|
|
|
.path
|
|
|
|
|
|
|
|
.as_deref()
|
|
|
|
|
|
|
|
.map(helix_stdx::path::get_relative_path);
|
|
|
|
|
|
|
|
path.as_deref()
|
|
|
|
|
|
|
|
.and_then(Path::to_str)
|
|
|
|
|
|
|
|
.unwrap_or(SCRATCH_BUFFER_NAME)
|
|
|
|
|
|
|
|
.to_string()
|
|
|
|
|
|
|
|
.into()
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
let picker = Picker::new(columns, 2, items, (), |cx, meta, action| {
|
|
|
|
cx.editor.switch(meta.id, action);
|
|
|
|
cx.editor.switch(meta.id, action);
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.with_preview(|editor, meta| {
|
|
|
|
.with_preview(|editor, meta| {
|
|
|
@ -2916,33 +2917,6 @@ fn jumplist_picker(cx: &mut Context) {
|
|
|
|
is_current: bool,
|
|
|
|
is_current: bool,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl ui::menu::Item for JumpMeta {
|
|
|
|
|
|
|
|
type Data = ();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn format(&self, _data: &Self::Data) -> Row {
|
|
|
|
|
|
|
|
let path = self
|
|
|
|
|
|
|
|
.path
|
|
|
|
|
|
|
|
.as_deref()
|
|
|
|
|
|
|
|
.map(helix_stdx::path::get_relative_path);
|
|
|
|
|
|
|
|
let path = match path.as_deref().and_then(Path::to_str) {
|
|
|
|
|
|
|
|
Some(path) => path,
|
|
|
|
|
|
|
|
None => SCRATCH_BUFFER_NAME,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut flags = Vec::new();
|
|
|
|
|
|
|
|
if self.is_current {
|
|
|
|
|
|
|
|
flags.push("*");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let flag = if flags.is_empty() {
|
|
|
|
|
|
|
|
"".into()
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
format!(" ({})", flags.join(""))
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
format!("{} {}{} {}", self.id, path, flag, self.text).into()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (view, _) in cx.editor.tree.views_mut() {
|
|
|
|
for (view, _) in cx.editor.tree.views_mut() {
|
|
|
|
for doc_id in view.jumps.iter().map(|e| e.0).collect::<Vec<_>>().iter() {
|
|
|
|
for doc_id in view.jumps.iter().map(|e| e.0).collect::<Vec<_>>().iter() {
|
|
|
|
let doc = doc_mut!(cx.editor, doc_id);
|
|
|
|
let doc = doc_mut!(cx.editor, doc_id);
|
|
|
@ -2969,10 +2943,37 @@ fn jumplist_picker(cx: &mut Context) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let columns = vec![];
|
|
|
|
let columns = vec![
|
|
|
|
|
|
|
|
ui::PickerColumn::new("id", |item: &JumpMeta, _| item.id.to_string().into()),
|
|
|
|
|
|
|
|
ui::PickerColumn::new("path", |item: &JumpMeta, _| {
|
|
|
|
|
|
|
|
let path = item
|
|
|
|
|
|
|
|
.path
|
|
|
|
|
|
|
|
.as_deref()
|
|
|
|
|
|
|
|
.map(helix_stdx::path::get_relative_path);
|
|
|
|
|
|
|
|
path.as_deref()
|
|
|
|
|
|
|
|
.and_then(Path::to_str)
|
|
|
|
|
|
|
|
.unwrap_or(SCRATCH_BUFFER_NAME)
|
|
|
|
|
|
|
|
.to_string()
|
|
|
|
|
|
|
|
.into()
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
ui::PickerColumn::new("flags", |item: &JumpMeta, _| {
|
|
|
|
|
|
|
|
let mut flags = Vec::new();
|
|
|
|
|
|
|
|
if item.is_current {
|
|
|
|
|
|
|
|
flags.push("*");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if flags.is_empty() {
|
|
|
|
|
|
|
|
"".into()
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
format!(" ({})", flags.join("")).into()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
ui::PickerColumn::new("contents", |item: &JumpMeta, _| item.text.as_str().into()),
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
let picker = Picker::new(
|
|
|
|
let picker = Picker::new(
|
|
|
|
columns,
|
|
|
|
columns,
|
|
|
|
0,
|
|
|
|
1, // path
|
|
|
|
cx.editor
|
|
|
|
cx.editor
|
|
|
|
.tree
|
|
|
|
.tree
|
|
|
|
.views()
|
|
|
|
.views()
|
|
|
@ -3001,35 +3002,6 @@ fn jumplist_picker(cx: &mut Context) {
|
|
|
|
cx.push_layer(Box::new(overlaid(picker)));
|
|
|
|
cx.push_layer(Box::new(overlaid(picker)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl ui::menu::Item for MappableCommand {
|
|
|
|
|
|
|
|
type Data = ReverseKeymap;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn format(&self, keymap: &Self::Data) -> Row {
|
|
|
|
|
|
|
|
let fmt_binding = |bindings: &Vec<Vec<KeyEvent>>| -> String {
|
|
|
|
|
|
|
|
bindings.iter().fold(String::new(), |mut acc, bind| {
|
|
|
|
|
|
|
|
if !acc.is_empty() {
|
|
|
|
|
|
|
|
acc.push(' ');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for key in bind {
|
|
|
|
|
|
|
|
acc.push_str(&key.key_sequence_format());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
acc
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match self {
|
|
|
|
|
|
|
|
MappableCommand::Typable { doc, name, .. } => match keymap.get(name as &String) {
|
|
|
|
|
|
|
|
Some(bindings) => format!("{} ({}) [:{}]", doc, fmt_binding(bindings), name).into(),
|
|
|
|
|
|
|
|
None => format!("{} [:{}]", doc, name).into(),
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
MappableCommand::Static { doc, name, .. } => match keymap.get(*name) {
|
|
|
|
|
|
|
|
Some(bindings) => format!("{} ({}) [{}]", doc, fmt_binding(bindings), name).into(),
|
|
|
|
|
|
|
|
None => format!("{} [{}]", doc, name).into(),
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn command_palette(cx: &mut Context) {
|
|
|
|
pub fn command_palette(cx: &mut Context) {
|
|
|
|
let register = cx.register;
|
|
|
|
let register = cx.register;
|
|
|
|
let count = cx.count;
|
|
|
|
let count = cx.count;
|
|
|
@ -3049,7 +3021,34 @@ pub fn command_palette(cx: &mut Context) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
let columns = vec![];
|
|
|
|
let columns = vec![
|
|
|
|
|
|
|
|
ui::PickerColumn::new("name", |item, _| match item {
|
|
|
|
|
|
|
|
MappableCommand::Typable { name, .. } => format!(":{name}").into(),
|
|
|
|
|
|
|
|
MappableCommand::Static { name, .. } => (*name).into(),
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
ui::PickerColumn::new(
|
|
|
|
|
|
|
|
"bindings",
|
|
|
|
|
|
|
|
|item: &MappableCommand, keymap: &crate::keymap::ReverseKeymap| {
|
|
|
|
|
|
|
|
keymap
|
|
|
|
|
|
|
|
.get(item.name())
|
|
|
|
|
|
|
|
.map(|bindings| {
|
|
|
|
|
|
|
|
bindings.iter().fold(String::new(), |mut acc, bind| {
|
|
|
|
|
|
|
|
if !acc.is_empty() {
|
|
|
|
|
|
|
|
acc.push(' ');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for key in bind {
|
|
|
|
|
|
|
|
acc.push_str(&key.key_sequence_format());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
acc
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.unwrap_or_default()
|
|
|
|
|
|
|
|
.into()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
ui::PickerColumn::new("doc", |item: &MappableCommand, _| item.doc().into()),
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
let picker = Picker::new(columns, 0, commands, keymap, move |cx, command, _action| {
|
|
|
|
let picker = Picker::new(columns, 0, commands, keymap, move |cx, command, _action| {
|
|
|
|
let mut ctx = Context {
|
|
|
|
let mut ctx = Context {
|
|
|
|
register,
|
|
|
|
register,
|
|
|
|