@@ -28,6 +28,7 @@ mod highlight;
2828mod markdown;
2929mod mouse;
3030mod ui;
31+mod view;
3132mod wrap;
3233
3334pub use action::Action;
@@ -38,6 +39,7 @@ pub fn node_menu_index(a: Action) -> usize {
3839 action::NODE_MENU.iter().position(|i| *i == Some(a)).expect("in the node menu")
3940}
4041pub use ui::Hit;
42+pub use view::View;
4143
4244// ------------------------------------------------------------ state
4345
@@ -64,6 +66,10 @@ pub struct FlatRow {
6466 pub via_embed: bool,
6567}
6668
69+/// The status bar's greeting, and a shorter one where it doesn't fit.
70+pub(crate) const HINT: &str = "double-click to zoom · right-click for actions · drag to move · ? help";
71+pub(crate) const HINT_SHORT: &str = "right-click for actions · ? help";
72+
6773pub struct App {
6874 pub(crate) vault: Vault,
6975 mode: Mode,
@@ -97,6 +103,8 @@ pub struct App {
97103 edit_keys: editor::Keys,
98104 edit_clip: editor::Clip,
99105 edit_last_key: Instant,
106+ /// The pane that had focus when the editor opened, focused again after.
107+ edit_return: Focus,
100108 // property editor (§10.6)
101109 props_target: Option<NRef>,
102110 props_rows: Vec<(String, String, bool)>,
@@ -169,7 +177,7 @@ impl App {
169177 wrap: true,
170178 show_reading: false,
171179 register: String::new(),
172− status: "click to select · double-click to zoom · right-click for actions · drag to move · ? help".into(),
180+ status: HINT.into(),
173181 status_time: Instant::now(),
174182 undo: Vec::new(),
175183 redo: Vec::new(),
@@ -186,6 +194,7 @@ impl App {
186194 .unwrap_or_default(),
187195 edit_clip: editor::Clip::default(),
188196 edit_last_key: Instant::now(),
197+ edit_return: Focus::Outline,
189198 props_target: None,
190199 props_rows: Vec::new(),
191200 props_sel: 0,
@@ -662,6 +671,46 @@ impl App {
662671 self.open_editor_on(target);
663672 }
664673
674+ /// The view settings to remember (§10.1).
675+ pub fn view(&self) -> View {
676+ View {
677+ show_reading: self.show_reading,
678+ wrap: self.wrap,
679+ hide_done: self.hide_done,
680+ keys: Some(self.edit_keys),
681+ outline_width: self.ui.outline_width,
682+ zoom: self.zoom_root.map(|z| self.vault.key_of(z)),
683+ folded: self.folded.clone(),
684+ }
685+ }
686+
687+ /// Restore remembered view settings; `keep_keys` when `--keys` or
688+ /// `$FOLD_KEYS` chose the keymap for this run.
689+ pub fn apply_view(&mut self, v: View, keep_keys: bool) {
690+ self.show_reading = v.show_reading;
691+ self.wrap = v.wrap;
692+ self.hide_done = v.hide_done;
693+ if let (false, Some(k)) = (keep_keys, v.keys) {
694+ self.edit_keys = k;
695+ }
696+ self.ui.outline_width = v.outline_width;
697+ self.folded = v.folded;
698+ self.zoom_root = v.zoom.and_then(|k| self.vault.find_by_key(&k)).filter(|&r| self.vault.tree.node(r).kind != Kind::Root);
699+ self.cursor = 0;
700+ self.clamp_cursor();
701+ }
702+
703+ /// Whether a toggle is on, for buttons and the palette.
704+ pub fn action_on(&self, a: Action) -> Option<bool> {
705+ match a {
706+ Action::ReadingPane => Some(self.show_reading),
707+ Action::HideDone => Some(self.hide_done),
708+ Action::Wrap => Some(self.wrap),
709+ Action::RawMode => Some(self.raw_mode),
710+ _ => None,
711+ }
712+ }
713+
665714 /// Whether the reading pane is on screen: shown, or holding the editor.
666715 pub fn reading_visible(&self) -> bool {
667716 self.show_reading || self.mode == Mode::Edit
@@ -671,6 +720,9 @@ impl App {
671720 fn open_editor_on(&mut self, target: NRef) {
672721 let buf = fold_core::edit::open_editor(&self.vault, target);
673722 self.editor = Some(editor::Editor::new(buf, self.edit_keys, self.edit_clip.clone()));
723+ if self.mode != Mode::Edit {
724+ self.edit_return = self.focus;
725+ }
674726 self.mode = Mode::Edit;
675727 self.focus = Focus::Reading;
676728 self.edit_last_key = Instant::now();
@@ -708,6 +760,7 @@ impl App {
708760 self.edit_clip = ed.clip;
709761 }
710762 self.mode = Mode::Normal;
763+ self.focus = self.edit_return;
711764 }
712765
713766 /// Whether the editor holds changes not yet written.
@@ -1133,7 +1186,7 @@ impl App {
11331186 match self.mode {
11341187 Mode::Normal => {
11351188 let starts_seq = match key.code {
1136− KeyCode::Char('z') => self.focus == Focus::Outline,
1189+ KeyCode::Char('z') => true,
11371190 KeyCode::Char('g') => true,
11381191 KeyCode::Char('[') | KeyCode::Char(']') => self.focus == Focus::Reading,
11391192 _ => false,
@@ -1169,6 +1222,7 @@ impl App {
11691222 self.edit_clip = ed.clip;
11701223 }
11711224 self.mode = Mode::Normal;
1225+ self.focus = self.edit_return;
11721226 let _ = self.vault.reload();
11731227 self.clamp_cursor();
11741228 self.say("changes discarded");
@@ -1481,6 +1535,15 @@ impl App {
14811535 KeyCode::Char('n') => self.next_match(&doc, 1),
14821536 KeyCode::Char('N') => self.next_match(&doc, -1),
14831537 KeyCode::Char('q') => self.quit = true,
1538+ // what isn't the pane's own works as it does in the outline
1539+ KeyCode::Char('m') => {
1540+ self.action_target = self.read_node();
1541+ self.run_action(Action::NodeMenu);
1542+ }
1543+ KeyCode::Char(':' | '?' | 'c' | 'C' | 'u' | 'U') => {
1544+ let rows = self.rows();
1545+ self.key_outline(key, rows);
1546+ }
14841547 _ => {}
14851548 }
14861549 }
@@ -2031,6 +2094,10 @@ pub fn run(dir: &Path, keys: Option<&str>) -> anyhow::Result<()> {
20312094 .ok_or_else(|| anyhow::anyhow!("unknown keymap {:?}: use normal, vim or helix", k))?;
20322095 app.edit_keys = parsed;
20332096 }
2097+ let keys_chosen = keys.is_some() || std::env::var_os("FOLD_KEYS").is_some();
2098+ if let Some(v) = view::load(dir) {
2099+ app.apply_view(v, keys_chosen);
2100+ }
20342101 app.start_watcher();
20352102 // a sync-conflict file present at startup starts the merge flow (§12.2)
20362103 if let Ok(files) = app.vault.conflict_files() {
@@ -2051,6 +2118,7 @@ pub fn run(dir: &Path, keys: Option<&str>) -> anyhow::Result<()> {
20512118 let backend = CrosstermBackend::new(std::io::stdout());
20522119 let mut terminal = Terminal::new(backend)?;
20532120 let res = run_loop(&mut terminal, &mut app);
2121+ let _ = view::save(dir, &app.view());
20542122 disable_raw_mode()?;
20552123 std::io::stdout().execute(DisableBracketedPaste)?;
20562124 std::io::stdout().execute(DisableMouseCapture)?;