use crate::{
buffer::BufferProperties,
client::ClientHandle,
command::CommandManager,
editor::{Editor, EditorContext, EditorFlow, KeysIterator},
editor_utils::{
readline_poll, LogKind, ReadLinePoll, REGISTER_READLINE_INPUT, REGISTER_READLINE_PROMPT,
},
mode::{ModeKind, ModeState},
platform::{Key, KeyCode},
word_database::WordIndicesIter,
};
pub struct State {
pub on_client_keys: fn(
ctx: &mut EditorContext,
ClientHandle,
&mut KeysIterator,
ReadLinePoll,
) -> Option<EditorFlow>,
continuation: String,
}
impl Default for State {
fn default() -> Self {
Self {
on_client_keys: |_, _, _, _| Some(EditorFlow::Continue),
continuation: String::new(),
}
}
}
impl ModeState for State {
fn on_enter(editor: &mut Editor) {
editor.registers.get_mut(REGISTER_READLINE_INPUT).clear();
}
fn on_exit(editor: &mut Editor) {
editor.mode.plugin_handle = None;
editor.registers.get_mut(REGISTER_READLINE_INPUT).clear();
editor.picker.clear();
}
fn on_keys(
ctx: &mut EditorContext,
client_handle: ClientHandle,
keys: &mut KeysIterator,
) -> Option<EditorFlow> {
let this = &mut ctx.editor.mode.picker_state;
let poll = readline_poll(
ctx.editor.registers.get_mut(REGISTER_READLINE_INPUT),
&mut ctx.platform,
&mut ctx.editor.string_pool,
&ctx.editor.buffered_keys,
keys,
);
if let ReadLinePoll::Pending = poll {
keys.index = keys.index.saturating_sub(1);
match keys.next(&ctx.editor.buffered_keys) {
Key {
code: KeyCode::Char('n'),
shift: false,
control: true,
alt: false,
}
| Key {
code: KeyCode::Down,
shift: false,
control: false,
alt: false,
} => {
ctx.editor.picker.move_cursor(1);
}
Key {
code: KeyCode::Char('p'),
shift: false,
control: true,
alt: false,
}
| Key {
code: KeyCode::Up,
shift: false,
control: false,
alt: false,
} => {
ctx.editor.picker.move_cursor(-1);
}
Key {
code: KeyCode::Char('j'),
shift: false,
control: true,
alt: false,
}
| Key {
code: KeyCode::PageDown,
shift: false,
control: false,
alt: false,
} => {
let picker_height = ctx
.editor
.picker
.len()
.min(ctx.editor.config.picker_max_height as _)
as isize;
ctx.editor.picker.move_cursor(picker_height / 2);
}
Key {
code: KeyCode::Char('k'),
shift: false,
control: true,
alt: false,
}
| Key {
code: KeyCode::PageUp,
shift: false,
control: false,
alt: false,
} => {
let picker_height = ctx
.editor
.picker
.len()
.min(ctx.editor.config.picker_max_height as _)
as isize;
ctx.editor.picker.move_cursor(-picker_height / 2);
}
Key {
code: KeyCode::Char('b'),
shift: false,
control: true,
alt: false,
}
| Key {
code: KeyCode::Home,
shift: false,
control: false,
alt: false,
} => {
let cursor = ctx.editor.picker.cursor().unwrap_or(0) as isize;
ctx.editor.picker.move_cursor(-cursor);
}
Key {
code: KeyCode::Char('e'),
shift: false,
control: true,
alt: false,
}
| Key {
code: KeyCode::End,
shift: false,
control: false,
alt: false,
} => {
let cursor = ctx.editor.picker.cursor().unwrap_or(0) as isize;
let entry_count = ctx.editor.picker.len() as isize;
ctx.editor.picker.move_cursor(entry_count - cursor - 1);
}
_ => {
let readline_input = ctx.editor.registers.get(REGISTER_READLINE_INPUT);
ctx.editor
.picker
.filter(WordIndicesIter::empty(), readline_input);
ctx.editor.picker.move_cursor(0);
}
}
}
let f = this.on_client_keys;
f(ctx, client_handle, keys, poll)
}
}
pub mod opened_buffers {
use super::*;
use std::path::Path;
pub fn enter_mode(ctx: &mut EditorContext) {
fn on_client_keys(
ctx: &mut EditorContext,
client_handle: ClientHandle,
_: &mut KeysIterator,
poll: ReadLinePoll,
) -> Option<EditorFlow> {
match poll {
ReadLinePoll::Pending => return Some(EditorFlow::Continue),
ReadLinePoll::Submitted => (),
ReadLinePoll::Canceled => {
ctx.editor.enter_mode(ModeKind::default());
return Some(EditorFlow::Continue);
}
}
let path = match ctx.editor.picker.current_entry(&ctx.editor.word_database) {
Some((_, entry)) => entry,
_ => {
ctx.editor.enter_mode(ModeKind::default());
return Some(EditorFlow::Continue);
}
};
let path = ctx.editor.string_pool.acquire_with(path);
if let Ok(buffer_view_handle) = ctx.editor.buffer_view_handle_from_path(
client_handle,
Path::new(&path),
BufferProperties::text(),
false,
) {
let client = ctx.clients.get_mut(client_handle);
client.set_buffer_view_handle(Some(buffer_view_handle), &ctx.editor.buffer_views);
}
ctx.editor.string_pool.release(path);
ctx.editor.enter_mode(ModeKind::default());
Some(EditorFlow::Continue)
}
ctx.editor
.registers
.set(REGISTER_READLINE_PROMPT, "buffer:");
ctx.editor.picker.clear();
for path in ctx.editor.buffers.iter().filter_map(|b| b.path.to_str()) {
ctx.editor.picker.add_custom_entry(path);
}
ctx.editor.picker.filter(WordIndicesIter::empty(), "");
ctx.editor.picker.move_cursor(0);
if ctx.editor.picker.len() > 0 {
ctx.editor.mode.picker_state.on_client_keys = on_client_keys;
ctx.editor.enter_mode(ModeKind::Picker);
} else {
ctx.editor
.logger
.write(LogKind::Error)
.str("no buffer opened");
}
}
}
pub mod custom {
use super::*;
pub fn enter_mode(ctx: &mut EditorContext, continuation: &str) {
fn on_client_keys(
ctx: &mut EditorContext,
client_handle: ClientHandle,
_: &mut KeysIterator,
poll: ReadLinePoll,
) -> Option<EditorFlow> {
match poll {
ReadLinePoll::Pending => (),
ReadLinePoll::Submitted => {
if ctx.editor.picker.cursor().is_none() {
ctx.editor.enter_mode(ModeKind::default());
return Some(EditorFlow::Continue);
}
let continuation = &ctx.editor.mode.picker_state.continuation;
let continuation = ctx.editor.string_pool.acquire_with(continuation);
let result = CommandManager::eval(
ctx,
Some(client_handle),
"picker-continuation",
&continuation,
);
let flow = CommandManager::unwrap_eval_result(ctx, result);
ctx.editor.string_pool.release(continuation);
ctx.editor.enter_mode(ModeKind::default());
return Some(flow);
}
ReadLinePoll::Canceled => ctx.editor.enter_mode(ModeKind::default()),
}
Some(EditorFlow::Continue)
}
let state = &mut ctx.editor.mode.picker_state;
state.on_client_keys = on_client_keys;
state.continuation.clear();
state.continuation.push_str(continuation);
ctx.editor.enter_mode(ModeKind::Picker);
}
}