use crate::editor::mode::EditorStateRef;
use reedline::{
Completer, Editor, IdeMenu, ListMenu, Menu, MenuEvent, Painter, Suggestion, UndoBehavior,
};
pub struct FunctionAwareMenu {
inner: IdeMenu,
editor_state: Option<EditorStateRef>,
}
impl FunctionAwareMenu {
pub fn new(inner: IdeMenu) -> Self {
Self {
inner,
editor_state: None,
}
}
pub fn with_editor_state(mut self, state: EditorStateRef) -> Self {
self.editor_state = Some(state);
self
}
fn sync_editor_state(&self, editor: &Editor) {
if let Some(state_ref) = &self.editor_state
&& let Ok(mut state) = state_ref.lock()
{
let buffer = editor.get_buffer();
let cursor_pos = editor.line_buffer().insertion_point();
state.buffer = buffer.to_string();
state.buffer_len = buffer.chars().count();
state.cursor_pos = buffer[..cursor_pos].chars().count();
state.uncertain = false;
}
}
}
impl Menu for FunctionAwareMenu {
fn name(&self) -> &str {
self.inner.name()
}
fn indicator(&self) -> &str {
self.inner.indicator()
}
fn is_active(&self) -> bool {
self.inner.is_active()
}
fn set_active(&mut self, active: bool) {
self.inner.set_active(active);
}
fn clear_input(&mut self) {
self.inner.clear_input();
}
fn on_activate(&mut self) {
self.inner.on_activate();
}
fn handle_menu_event(&mut self, event: &MenuEvent) {
self.inner.handle_menu_event(event);
}
fn menu_event(&mut self, event: MenuEvent) {
self.inner.menu_event(event);
}
fn can_quick_complete(&self) -> bool {
self.inner.can_quick_complete()
}
fn can_partially_complete(
&mut self,
values_updated: bool,
editor: &mut Editor,
completer: &mut dyn Completer,
) -> bool {
let result = self
.inner
.can_partially_complete(values_updated, editor, completer);
if result {
self.sync_editor_state(editor);
}
result
}
fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer) {
self.inner.update_values(editor, completer);
}
fn update_working_details(
&mut self,
editor: &mut Editor,
completer: &mut dyn Completer,
painter: &Painter,
) {
self.inner
.update_working_details(editor, completer, painter);
}
fn replace_in_buffer(&self, editor: &mut Editor) {
let before_len = editor.get_buffer().len();
self.inner.replace_in_buffer(editor);
let buffer = editor.get_buffer();
let cursor_pos = editor.line_buffer().insertion_point();
let is_function_completion = cursor_pos >= 2
&& buffer.len() > before_len
&& buffer.get(cursor_pos - 2..cursor_pos) == Some("()");
if is_function_completion {
editor.edit_buffer(
|line_buffer| {
line_buffer.move_left();
},
UndoBehavior::MoveCursor,
);
}
self.sync_editor_state(editor);
}
fn min_rows(&self) -> u16 {
self.inner.min_rows()
}
fn get_values(&self) -> &[Suggestion] {
self.inner.get_values()
}
fn reset_position(&mut self) {
self.inner.reset_position();
}
fn results_are_provisional(&self) -> bool {
self.inner.results_are_provisional()
}
fn is_awaiting_first_answer(&self) -> bool {
self.inner.is_awaiting_first_answer()
}
fn menu_required_lines(&self, terminal_columns: u16) -> u16 {
self.inner.menu_required_lines(terminal_columns)
}
fn menu_string(&self, available_lines: u16, use_ansi_coloring: bool) -> String {
self.inner.menu_string(available_lines, use_ansi_coloring)
}
fn set_cursor_pos(&mut self, pos: (u16, u16)) {
self.inner.set_cursor_pos(pos);
}
}
pub struct StateSyncHistoryMenu {
inner: ListMenu,
editor_state: Option<EditorStateRef>,
}
impl StateSyncHistoryMenu {
pub fn new(inner: ListMenu) -> Self {
Self {
inner,
editor_state: None,
}
}
pub fn with_editor_state(mut self, state: EditorStateRef) -> Self {
self.editor_state = Some(state);
self
}
fn sync_editor_state(&self, editor: &Editor) {
if let Some(state_ref) = &self.editor_state
&& let Ok(mut state) = state_ref.lock()
{
let buffer = editor.get_buffer();
let cursor_pos = editor.line_buffer().insertion_point();
state.buffer = buffer.to_string();
state.buffer_len = buffer.chars().count();
state.cursor_pos = buffer[..cursor_pos].chars().count();
state.uncertain = false;
}
}
}
impl Menu for StateSyncHistoryMenu {
fn name(&self) -> &str {
self.inner.name()
}
fn indicator(&self) -> &str {
self.inner.indicator()
}
fn is_active(&self) -> bool {
self.inner.is_active()
}
fn set_active(&mut self, active: bool) {
self.inner.set_active(active);
}
fn clear_input(&mut self) {
self.inner.clear_input();
}
fn handle_menu_event(&mut self, event: &MenuEvent) {
self.inner.handle_menu_event(event);
}
fn menu_event(&mut self, event: MenuEvent) {
self.inner.menu_event(event);
}
fn can_quick_complete(&self) -> bool {
self.inner.can_quick_complete()
}
fn can_partially_complete(
&mut self,
values_updated: bool,
editor: &mut Editor,
completer: &mut dyn Completer,
) -> bool {
let result = self
.inner
.can_partially_complete(values_updated, editor, completer);
if result {
self.sync_editor_state(editor);
}
result
}
fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer) {
self.inner.update_values(editor, completer);
}
fn update_working_details(
&mut self,
editor: &mut Editor,
completer: &mut dyn Completer,
painter: &Painter,
) {
self.inner
.update_working_details(editor, completer, painter);
}
fn replace_in_buffer(&self, editor: &mut Editor) {
self.inner.replace_in_buffer(editor);
editor.edit_buffer(
|line_buffer| {
line_buffer.clear_to_end();
},
UndoBehavior::CreateUndoPoint,
);
self.sync_editor_state(editor);
}
fn min_rows(&self) -> u16 {
self.inner.min_rows()
}
fn get_values(&self) -> &[Suggestion] {
self.inner.get_values()
}
fn reset_position(&mut self) {
self.inner.reset_position();
}
fn menu_required_lines(&self, terminal_columns: u16) -> u16 {
self.inner.menu_required_lines(terminal_columns)
}
fn menu_string(&self, available_lines: u16, use_ansi_coloring: bool) -> String {
self.inner.menu_string(available_lines, use_ansi_coloring)
}
fn set_cursor_pos(&mut self, pos: (u16, u16)) {
self.inner.set_cursor_pos(pos);
}
}