use super::{App, FormatMode};
use crate::json_ops::JsonOperations;
use serde_json::Value;
impl App {
pub fn open_entry_overlay(&mut self) {
self.start_editing_entry();
}
pub fn start_editing_entry(&mut self) {
let target_idx = if self.selected_entry_index < self.relf_entries.len() {
self.relf_entries[self.selected_entry_index].original_index
} else {
return; };
if let Ok(json_value) = serde_json::from_str::<Value>(&self.json_input) {
if let Some(obj) = json_value.as_object() {
let mut current_idx = 0;
if let Some(outside) = obj.get("outside") {
if let Some(outside_array) = outside.as_array() {
if target_idx < current_idx + outside_array.len() {
let local_idx = target_idx - current_idx;
if let Some(entry_obj) = outside_array[local_idx].as_object() {
let name = entry_obj.get("name").and_then(|v| v.as_str()).unwrap_or("").to_string();
let context = entry_obj.get("context").and_then(|v| v.as_str()).unwrap_or("").to_string();
let url = entry_obj.get("url").and_then(|v| v.as_str()).unwrap_or("").to_string();
let percentage = entry_obj.get("percentage").and_then(|v| v.as_i64());
let name_is_empty = name.is_empty();
let context_is_empty = context.is_empty();
let url_is_empty = url.is_empty();
self.edit_buffer = vec![
if name_is_empty { "name".to_string() } else { name },
if context_is_empty { "context".to_string() } else { context },
if url_is_empty { "url".to_string() } else { url },
if let Some(pct) = percentage { pct.to_string() } else { "percentage".to_string() },
];
self.edit_buffer_is_placeholder = vec![
name_is_empty,
context_is_empty,
url_is_empty,
percentage.is_none(),
];
self.edit_field_index = 0;
self.editing_entry = true;
self.edit_field_editing_mode = false;
self.edit_insert_mode = false;
self.edit_cursor_pos = 0;
return;
}
}
current_idx += outside_array.len();
}
}
if let Some(inside) = obj.get("inside") {
if let Some(inside_array) = inside.as_array() {
if target_idx < current_idx + inside_array.len() {
let local_idx = target_idx - current_idx;
if let Some(entry_obj) = inside_array[local_idx].as_object() {
let date = entry_obj.get("date").and_then(|v| v.as_str()).unwrap_or("").to_string();
let context = entry_obj.get("context").and_then(|v| v.as_str()).unwrap_or("").to_string();
let date_is_empty = date.is_empty();
let context_is_empty = context.is_empty();
self.edit_buffer = vec![
if date_is_empty { "date".to_string() } else { date },
if context_is_empty { "context".to_string() } else { context },
];
self.edit_buffer_is_placeholder = vec![
date_is_empty,
context_is_empty,
];
self.edit_field_index = 0;
self.editing_entry = true;
self.edit_field_editing_mode = false;
self.edit_insert_mode = false;
self.edit_cursor_pos = 0;
return;
}
}
}
}
}
}
}
pub fn save_edited_entry(&mut self) {
if self.edit_buffer.is_empty() {
self.editing_entry = false;
return;
}
let target_idx = if self.selected_entry_index < self.relf_entries.len() {
self.relf_entries[self.selected_entry_index].original_index
} else {
self.editing_entry = false;
return; };
match serde_json::from_str::<Value>(&self.json_input) {
Ok(mut json_value) => {
if let Some(obj) = json_value.as_object_mut() {
let mut current_idx = 0;
let mut found = false;
if let Some(outside) = obj.get_mut("outside") {
if let Some(outside_array) = outside.as_array_mut() {
if target_idx < current_idx + outside_array.len() {
let local_idx = target_idx - current_idx;
if let Some(entry_obj) = outside_array[local_idx].as_object_mut() {
if self.edit_buffer.len() >= 1 && self.edit_buffer_is_placeholder.len() >= 1 {
let name_val = &self.edit_buffer[0];
let is_placeholder = self.edit_buffer_is_placeholder[0];
entry_obj.insert("name".to_string(),
Value::String(if is_placeholder { String::new() } else { name_val.clone() }));
}
if self.edit_buffer.len() >= 2 && self.edit_buffer_is_placeholder.len() >= 2 {
let context_val = &self.edit_buffer[1];
let is_placeholder = self.edit_buffer_is_placeholder[1];
entry_obj.insert("context".to_string(),
Value::String(if is_placeholder { String::new() } else { context_val.clone() }));
}
if self.edit_buffer.len() >= 3 && self.edit_buffer_is_placeholder.len() >= 3 {
let url_val = &self.edit_buffer[2];
let is_placeholder = self.edit_buffer_is_placeholder[2];
entry_obj.insert("url".to_string(),
Value::String(if is_placeholder { String::new() } else { url_val.clone() }));
}
if self.edit_buffer.len() >= 4 && self.edit_buffer_is_placeholder.len() >= 4 {
let pct_val = &self.edit_buffer[3];
let is_placeholder = self.edit_buffer_is_placeholder[3];
if is_placeholder {
entry_obj.insert("percentage".to_string(), Value::Null);
} else if let Ok(pct) = pct_val.trim_end_matches('%').parse::<i64>() {
entry_obj.insert("percentage".to_string(), Value::Number(pct.into()));
}
}
found = true;
}
} else {
current_idx += outside_array.len();
}
}
}
if !found {
if let Some(inside) = obj.get_mut("inside") {
if let Some(inside_array) = inside.as_array_mut() {
let local_idx = target_idx - current_idx;
if local_idx < inside_array.len() {
if let Some(entry_obj) = inside_array[local_idx].as_object_mut() {
if self.edit_buffer.len() >= 1 && self.edit_buffer_is_placeholder.len() >= 1 {
let date_val = &self.edit_buffer[0];
let is_placeholder = self.edit_buffer_is_placeholder[0];
entry_obj.insert("date".to_string(),
Value::String(if is_placeholder { String::new() } else { date_val.clone() }));
}
if self.edit_buffer.len() >= 2 && self.edit_buffer_is_placeholder.len() >= 2 {
let context_val = &self.edit_buffer[1];
let is_placeholder = self.edit_buffer_is_placeholder[1];
entry_obj.insert("context".to_string(),
Value::String(if is_placeholder { String::new() } else { context_val.clone() }));
}
found = true;
}
}
}
}
}
if found {
match serde_json::to_string_pretty(&json_value) {
Ok(formatted) => {
self.json_input = formatted;
self.is_modified = true;
self.convert_json();
self.set_status("Entry updated");
self.save_file();
}
Err(e) => self.set_status(&format!("Error formatting JSON: {}", e)),
}
}
}
}
Err(e) => self.set_status(&format!("Invalid JSON: {}", e)),
}
self.editing_entry = false;
}
pub fn cancel_editing_entry(&mut self) {
self.editing_entry = false;
self.edit_buffer.clear();
self.edit_buffer_is_placeholder.clear();
self.edit_field_index = 0;
self.edit_insert_mode = false;
self.edit_cursor_pos = 0;
self.edit_hscroll = 0;
self.edit_vscroll = 0;
self.view_edit_mode = false;
self.edit_field_editing_mode = false;
self.edit_skip_normal_mode = false;
}
pub fn insert_char(&mut self, c: char) {
if self.format_mode == FormatMode::Edit {
self.save_undo_state();
let mut lines = self.get_json_lines();
if lines.is_empty() {
lines.push(String::new());
self.content_cursor_line = 0;
self.content_cursor_col = 0;
}
if self.content_cursor_line >= lines.len() {
self.content_cursor_line = lines.len().saturating_sub(1);
}
let line = &mut lines[self.content_cursor_line];
let mut chars: Vec<char> = line.chars().collect();
let pos = self.content_cursor_col.min(chars.len());
chars.insert(pos, c);
lines[self.content_cursor_line] = chars.into_iter().collect();
self.content_cursor_col += 1;
self.set_json_from_lines(lines);
self.ensure_cursor_visible();
}
}
pub fn insert_newline(&mut self) {
if self.format_mode == FormatMode::Edit {
self.save_undo_state();
let mut lines = self.get_json_lines();
if lines.is_empty() {
lines.push(String::new());
lines.push(String::new());
self.content_cursor_line = 1;
self.content_cursor_col = 0;
} else {
if self.content_cursor_line >= lines.len() {
self.content_cursor_line = lines.len().saturating_sub(1);
}
let line = lines[self.content_cursor_line].clone();
let split_pos = self.content_cursor_col.min(line.len());
let (left, right) = line.split_at(split_pos);
lines[self.content_cursor_line] = left.to_string();
lines.insert(self.content_cursor_line + 1, right.to_string());
self.content_cursor_line += 1;
self.content_cursor_col = 0;
}
self.set_json_from_lines(lines);
self.ensure_cursor_visible();
}
}
pub fn backspace(&mut self) {
if self.format_mode == FormatMode::Edit {
self.save_undo_state();
let mut lines = self.get_json_lines();
if lines.is_empty() {
return;
}
if self.content_cursor_col > 0 && self.content_cursor_line < lines.len() {
let mut chars: Vec<char> = lines[self.content_cursor_line].chars().collect();
if self.content_cursor_col > 0 && self.content_cursor_col <= chars.len() {
chars.remove(self.content_cursor_col - 1);
lines[self.content_cursor_line] = chars.into_iter().collect();
self.content_cursor_col -= 1;
self.set_json_from_lines(lines);
}
} else if self.content_cursor_col == 0 && self.content_cursor_line > 0 {
let current_line = lines.remove(self.content_cursor_line);
self.content_cursor_line -= 1;
let prev_line_len = lines[self.content_cursor_line].chars().count();
lines[self.content_cursor_line].push_str(¤t_line);
self.content_cursor_col = prev_line_len;
self.set_json_from_lines(lines);
}
}
}
pub fn delete_char(&mut self) {
if self.format_mode == FormatMode::Edit {
self.save_undo_state();
let mut lines = self.get_json_lines();
if lines.is_empty() {
return;
}
if self.content_cursor_line < lines.len() {
let mut chars: Vec<char> = lines[self.content_cursor_line].chars().collect();
if self.content_cursor_col < chars.len() {
chars.remove(self.content_cursor_col);
lines[self.content_cursor_line] = chars.into_iter().collect();
self.set_json_from_lines(lines);
} else if self.content_cursor_line + 1 < lines.len() {
let next_line = lines.remove(self.content_cursor_line + 1);
lines[self.content_cursor_line].push_str(&next_line);
self.set_json_from_lines(lines);
}
}
}
}
pub fn move_cursor_left(&mut self) {
if self.content_cursor_col > 0 {
self.content_cursor_col -= 1;
} else if self.content_cursor_line > 0 {
self.content_cursor_line -= 1;
let lines = self.get_json_lines();
if self.content_cursor_line < lines.len() {
self.content_cursor_col = lines[self.content_cursor_line].chars().count();
}
}
self.ensure_cursor_visible();
}
pub fn move_cursor_right(&mut self) {
let lines = self.get_json_lines();
if lines.is_empty() {
return;
}
if self.content_cursor_line < lines.len() {
let line_len = lines[self.content_cursor_line].chars().count();
if self.content_cursor_col < line_len {
self.content_cursor_col += 1;
} else if self.content_cursor_line + 1 < lines.len() {
self.content_cursor_line += 1;
self.content_cursor_col = 0;
}
}
self.ensure_cursor_visible();
}
pub fn move_cursor_up(&mut self) {
if self.content_cursor_line > 0 {
self.content_cursor_line -= 1;
let lines = self.get_json_lines();
if self.content_cursor_line < lines.len() {
let line_len = lines[self.content_cursor_line].chars().count();
self.content_cursor_col = self.content_cursor_col.min(line_len);
}
}
self.ensure_cursor_visible();
}
pub fn move_cursor_down(&mut self) {
let lines = self.get_json_lines();
if lines.is_empty() {
return;
}
let content_lines = if self.format_mode == FormatMode::Edit {
lines.len()
} else {
self.rendered_content.len()
};
if self.content_cursor_line + 1 < content_lines {
self.content_cursor_line += 1;
let line_len =
if self.format_mode == FormatMode::Edit && self.content_cursor_line < lines.len() {
lines[self.content_cursor_line].chars().count()
} else if self.content_cursor_line < self.rendered_content.len() {
self.rendered_content[self.content_cursor_line]
.chars()
.count()
} else {
0
};
self.content_cursor_col = self.content_cursor_col.min(line_len);
} else {
let virtual_padding = if self.showing_help { 0 } else { 10 };
let max_scroll =
(content_lines as u16 + virtual_padding).saturating_sub(self.get_visible_height());
if self.scroll < max_scroll {
self.scroll += 1;
}
}
self.ensure_cursor_visible();
}
pub fn append_inside(&mut self) {
match JsonOperations::add_inside_entry(&self.json_input) {
Ok((formatted, line, col, message)) => {
self.json_input = formatted;
self.is_modified = true;
self.convert_json();
if self.format_mode == FormatMode::View {
if let Ok(json_value) = serde_json::from_str::<Value>(&self.json_input) {
if let Some(obj) = json_value.as_object() {
let outside_count = obj
.get("outside")
.and_then(|v| v.as_array())
.map(|arr| arr.len())
.unwrap_or(0);
self.selected_entry_index = outside_count;
self.scroll = 0;
}
}
} else {
self.content_cursor_line = line;
self.content_cursor_col = col;
self.ensure_cursor_visible();
}
self.set_status(&message);
}
Err(e) => self.set_status(&format!("Error: {}", e)),
}
}
pub fn append_outside(&mut self) {
match JsonOperations::add_outside_entry(&self.json_input) {
Ok((formatted, line, col, message)) => {
self.json_input = formatted;
self.is_modified = true;
self.convert_json();
if self.format_mode == FormatMode::View {
if let Ok(json_value) = serde_json::from_str::<Value>(&self.json_input) {
if let Some(obj) = json_value.as_object() {
let outside_count = obj
.get("outside")
.and_then(|v| v.as_array())
.map(|arr| arr.len())
.unwrap_or(0);
self.selected_entry_index = outside_count.saturating_sub(1);
self.scroll = 0;
}
}
} else {
self.content_cursor_line = line;
self.content_cursor_col = col;
self.ensure_cursor_visible();
}
self.set_status(&message);
}
Err(e) => self.set_status(&format!("Error: {}", e)),
}
}
pub fn ensure_cursor_visible(&mut self) {
let lines = self.get_json_lines();
if lines.is_empty() {
self.content_cursor_line = 0;
self.content_cursor_col = 0;
return;
}
let content_lines = if self.format_mode == FormatMode::Edit {
lines.len()
} else {
self.rendered_content.len()
};
if self.content_cursor_line >= content_lines {
self.content_cursor_line = content_lines.saturating_sub(1);
}
let line_len =
if self.format_mode == FormatMode::Edit && self.content_cursor_line < lines.len() {
lines[self.content_cursor_line].chars().count()
} else if self.content_cursor_line < self.rendered_content.len() {
self.rendered_content[self.content_cursor_line]
.chars()
.count()
} else {
0
};
if self.content_cursor_col > line_len {
self.content_cursor_col = line_len;
}
let cursor_line = if self.format_mode == FormatMode::Edit {
self.content_cursor_line as u16
} else {
self.calculate_cursor_visual_position().0
};
let visible_height = self.get_visible_height();
let scrolloff = 3u16;
if cursor_line < self.scroll {
self.scroll = cursor_line;
} else if visible_height > 0 && cursor_line >= self.scroll + visible_height {
self.scroll = cursor_line.saturating_sub(visible_height - 1);
} else if visible_height > scrolloff * 2 {
if cursor_line < self.scroll + scrolloff {
self.scroll = cursor_line.saturating_sub(scrolloff);
} else if cursor_line > self.scroll + visible_height - scrolloff - 1 {
self.scroll = cursor_line + scrolloff + 1 - visible_height;
}
}
let virtual_padding = 10;
let max_scroll = (content_lines as u16 + virtual_padding).saturating_sub(visible_height);
if self.scroll > max_scroll {
self.scroll = max_scroll;
}
if self.format_mode == FormatMode::Edit {
if self.content_cursor_line < lines.len() {
let current = &lines[self.content_cursor_line];
let cursor_display_col = self.prefix_display_width(current, self.content_cursor_col) as u16;
let w = self.get_content_width();
let margin = 8u16;
if cursor_display_col < self.hscroll + margin {
self.hscroll = cursor_display_col.saturating_sub(margin);
} else if cursor_display_col >= self.hscroll + w.saturating_sub(margin) {
self.hscroll = cursor_display_col + margin - w + 1;
}
}
}
}
pub fn order_entries(&mut self) {
match JsonOperations::order_entries(&self.json_input) {
Ok((formatted, message)) => {
self.json_input = formatted;
self.is_modified = true;
self.convert_json();
if self.format_mode == FormatMode::View {
self.save_file();
}
self.set_status(&message);
}
Err(e) => self.set_status(&format!("Error: {}", e)),
}
}
pub fn order_by_percentage(&mut self) {
match JsonOperations::order_by_percentage(&self.json_input) {
Ok((formatted, message)) => {
self.json_input = formatted;
self.is_modified = true;
self.convert_json();
if self.format_mode == FormatMode::View {
self.save_file();
}
self.set_status(&message);
}
Err(e) => self.set_status(&format!("Error: {}", e)),
}
}
pub fn order_by_name(&mut self) {
match JsonOperations::order_by_name(&self.json_input) {
Ok((formatted, message)) => {
self.json_input = formatted;
self.is_modified = true;
self.convert_json();
if self.format_mode == FormatMode::View {
self.save_file();
}
self.set_status(&message);
}
Err(e) => self.set_status(&format!("Error: {}", e)),
}
}
pub fn ensure_overlay_cursor_visible(&mut self) {
if !self.edit_field_editing_mode {
return;
}
if self.edit_field_index >= self.edit_buffer.len() {
return;
}
let field = &self.edit_buffer[self.edit_field_index];
let cursor_pos = self.edit_cursor_pos;
let is_context_field = (self.edit_buffer.len() == 2 && self.edit_field_index == 1) ||
(self.edit_buffer.len() == 4 && self.edit_field_index == 1);
if is_context_field && self.view_edit_mode {
let lines: Vec<&str> = field.split('\n').collect();
let mut char_count = 0;
let mut current_line = 0;
for (line_idx, line) in lines.iter().enumerate() {
let line_len = line.chars().count();
let separator_len = if line_idx < lines.len() - 1 { 1 } else { 0 };
if cursor_pos <= char_count + line_len {
current_line = line_idx;
break;
}
char_count += line_len + separator_len;
}
let num_lines = lines.len();
let min_window_height = 5;
let window_height = if num_lines < min_window_height {
num_lines.max(1) as u16
} else {
num_lines.min(20) as u16
};
let margin_v = 1u16;
if (current_line as u16) < self.edit_vscroll + margin_v {
self.edit_vscroll = (current_line as u16).saturating_sub(margin_v);
} else if (current_line as u16) >= self.edit_vscroll + window_height.saturating_sub(margin_v) {
self.edit_vscroll = (current_line as u16) + margin_v - window_height + 1;
}
self.edit_hscroll = 0;
} else {
let cursor_display_col = self.prefix_display_width(field, cursor_pos) as u16;
let window_width = 70u16;
let margin = 4u16;
if cursor_display_col < self.edit_hscroll + margin {
self.edit_hscroll = cursor_display_col.saturating_sub(margin);
} else if cursor_display_col >= self.edit_hscroll + window_width.saturating_sub(margin) {
self.edit_hscroll = cursor_display_col + margin - window_width + 1;
}
}
}
}