1use std::{cmp, io};
2
3use crossterm::{
4 cursor,
5 event::DisableMouseCapture,
6 execute,
7 terminal::{LeaveAlternateScreen, disable_raw_mode},
8};
9use general::GENERAL_CONFIG;
10
11use crate::boxes::{errorbox::ErrorBox, help_box::HelpBox, linkbox::LinkBox, searchbox::SearchBox};
12
13pub mod colors;
14pub mod general;
15pub mod keys;
16
17#[derive(Debug, Clone, Copy, PartialEq, Default)]
18pub enum Mode {
19 View,
20 #[default]
21 FileTree,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Default)]
25pub enum Boxes {
26 Error,
27 Search,
28 LinkPreview,
29 #[default]
30 None,
31}
32
33impl From<JumpHistory> for Mode {
34 fn from(jump_history: JumpHistory) -> Self {
35 match jump_history.history.last() {
36 Some(jump) => match jump {
37 Jump::File(_) => Mode::View,
38 Jump::FileTree => Mode::FileTree,
39 },
40 None => Mode::FileTree,
41 }
42 }
43}
44
45#[derive(Default, Clone)]
46pub struct App {
47 pub vertical_scroll: u16,
48 width: u16,
49 pub selected: bool,
50 pub select_index: usize,
51 pub mode: Mode,
52 pub boxes: Boxes,
53 pub history: JumpHistory,
54 pub search_box: SearchBox,
55 pub message_box: ErrorBox,
56 pub help_box: HelpBox,
57 pub link_box: LinkBox,
58}
59
60impl App {
61 pub fn reset(&mut self) {
62 self.vertical_scroll = 0;
63 self.selected = false;
64 self.select_index = 0;
65 self.boxes = Boxes::None;
66 self.help_box.close();
67 }
68
69 pub fn set_width(&mut self, width: u16) -> bool {
70 let temp_width = self.width;
71 self.width = cmp::min(width, GENERAL_CONFIG.width);
72 temp_width != self.width
73 }
74
75 #[must_use]
76 pub fn width(&self) -> u16 {
77 self.width
78 }
79}
80
81pub enum LinkType<'a> {
82 Internal(&'a str),
83 External(&'a str),
84 MarkdownFile(&'a str),
85}
86
87impl<'a> From<&'a str> for LinkType<'a> {
88 fn from(s: &'a str) -> Self {
89 if s.starts_with('#') {
90 return Self::Internal(s);
91 }
92 if s.ends_with("md") || !s.contains('.') {
93 return Self::MarkdownFile(s);
94 }
95 Self::External(s)
96 }
97}
98
99pub fn destruct_terminal() {
100 disable_raw_mode().unwrap();
101 execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture).unwrap();
102 execute!(io::stdout(), cursor::Show).unwrap();
103}
104
105#[derive(Debug, Clone)]
106pub struct JumpHistory {
107 history: Vec<Jump>,
108}
109
110impl JumpHistory {
111 #[must_use]
112 pub fn new() -> Self {
113 Self {
114 history: Vec::new(),
115 }
116 }
117
118 pub fn push(&mut self, jump: Jump) {
119 self.history.push(jump);
120 }
121
122 pub fn pop(&mut self) -> Jump {
123 if let Some(jump) = self.history.pop() {
124 jump
125 } else {
126 Jump::FileTree
127 }
128 }
129}
130
131impl Default for JumpHistory {
132 fn default() -> Self {
133 Self::new()
134 }
135}
136
137#[derive(Debug, Clone, PartialEq)]
138pub enum Jump {
139 File(String),
140 FileTree,
141}
142
143#[cfg(test)]
144#[test]
145fn test_jump_history() {
146 let mut jump_history = JumpHistory::default();
147 jump_history.push(Jump::File("file".to_string()));
148 jump_history.push(Jump::File("file2".to_string()));
149 jump_history.push(Jump::FileTree);
150 assert_eq!(jump_history.pop(), Jump::FileTree);
151 assert_eq!(jump_history.pop(), Jump::File("file2".to_string()));
152 assert_eq!(jump_history.pop(), Jump::File("file".to_string()));
153 assert_eq!(jump_history.pop(), Jump::FileTree);
154 assert_eq!(jump_history.pop(), Jump::FileTree);
155 assert_eq!(jump_history.pop(), Jump::FileTree);
156}