Add connect mode, portals, backlinks and renames (stage 8d, v1)
Part 8d, the last of stage 8, and with it everything on DESIGN.md's v1 list. Connect mode (C): the cursor goes to another node, a dashed arrow in the visual color follows it, and Enter links the two; then the cursor goes back to where C was typed (the user's pick). d removes the line between them (a branch is cut, and the child comes loose), > puts the node under the other one, and <Space> a, l, c, s and t style the line as you watch, making it when it isn't there yet. Everything connect mode changes is one undo group, and Esc takes it back with History::cancel, which leaves nothing to redo. From visual mode every selected node connects, and the line pickers go back to connect mode when they close. Portals show their document live, from a cache of what each document file holds (open documents from their buffers, the others from their files, looked at every second): the root's title, its color and how many nodes it has, or ? when the document isn't there. Enter or gf goes through, as a jump, and the pane's breadcrumb starts with the documents it came through. <Space>p types :portal (the user's pick): a path as :e takes it, or the title of a new document. Backlinks are put right whenever a document is opened or saved; the root shows how many, the Note view lists them, gu goes up to them (the user's pick) and :backlinks lists them. File names follow the root's title: when it changed, the next save renames the file (-2 when the name is taken; a folder's default document keeps its name), and the portals pointing at it, its backlinks in the documents it leads to, the recent documents, the capital marks and the jump lists follow. A portal whose file was moved by hand finds its document in that folder by uid. Portals in a register keep full paths, so a pasted portal leads to the same document from any folder. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
bd819409f4
commit
ebe8a5988e
45 files changed
+2329
-148
No files matched your search
+27
-8
@@ -6,7 +6,7 @@ use ratatui::layout::Position;
|
||||
|
||||
use super::action::{Action, AppAction, DocumentChoice, Message};
|
||||
use super::mode::Mode;
|
||||
use super::{App, calls, commands, visual};
|
||||
use super::{App, calls, commands, connect, visual};
|
||||
use crate::command::parse::{Sources, parse};
|
||||
use crate::config::settings::Setting;
|
||||
use crate::config::theme_file;
|
||||
@@ -26,7 +26,7 @@ impl App {
|
||||
Mode::Command(_) => self.command_line_key(key),
|
||||
Mode::Text(_) => self.text_key(key),
|
||||
Mode::Popup(_) => self.popup_key(key),
|
||||
Mode::Normal | Mode::Visual(_) | Mode::Move(_) => {
|
||||
Mode::Normal | Mode::Visual(_) | Mode::Move(_) | Mode::Connect(_) => {
|
||||
match self.keys.press(key, &self.keymap.tables(self.contexts())) {
|
||||
Outcome::Run(typed) => self.run_typed(typed),
|
||||
Outcome::Unknown(keys) => {
|
||||
@@ -37,10 +37,12 @@ impl App {
|
||||
}
|
||||
}
|
||||
self.end_recording();
|
||||
// Portals show the documents they lead to as they are now.
|
||||
self.refresh_portals(false);
|
||||
}
|
||||
|
||||
/// A command: the focused view has the first say, then visual mode's
|
||||
/// commands (in visual mode), then the shared commands.
|
||||
/// A command: the focused view has the first say, then visual or
|
||||
/// connect mode's commands (in those modes), then the shared commands.
|
||||
pub(super) fn dispatch(&mut self, typed: Typed) {
|
||||
let selected = self.selection().unwrap_or_default();
|
||||
let pane = self.panes.current_mut();
|
||||
@@ -51,6 +53,7 @@ impl App {
|
||||
.command(&typed, &ctx)
|
||||
.or_else(|| match &self.shared.mode {
|
||||
Mode::Visual(visual) => visual::command(&typed, visual, &selected, kind, &ctx),
|
||||
Mode::Connect(connect) => connect::command(&typed, connect, &ctx),
|
||||
_ => None,
|
||||
})
|
||||
.or_else(|| commands::shared(&typed, &ctx));
|
||||
@@ -169,7 +172,21 @@ impl App {
|
||||
/// goes to the document bar or to the pane under it (a drag stays with
|
||||
/// the pane it started in), which takes the focus.
|
||||
pub fn gesture(&mut self, gesture: Gesture) {
|
||||
self.route_gesture(gesture);
|
||||
self.refresh_portals(false);
|
||||
}
|
||||
|
||||
fn route_gesture(&mut self, gesture: Gesture) {
|
||||
let at = |cell: Cell| Position::new(cell.col, cell.row);
|
||||
// In connect mode a click only aims: no editing, no menu, no
|
||||
// selecting.
|
||||
let gesture = match (&self.shared.mode, gesture) {
|
||||
(Mode::Connect(_), Gesture::RightPress { .. }) => return,
|
||||
(Mode::Connect(_), Gesture::Press { at, .. } | Gesture::DoublePress { at }) => {
|
||||
Gesture::Press { at, ctrl: false }
|
||||
}
|
||||
_ => gesture,
|
||||
};
|
||||
// A click drops a half-typed key sequence: a `d` typed before
|
||||
// clicking somewhere else mustn't wait to delete what comes next.
|
||||
if matches!(gesture, Gesture::Press { .. } | Gesture::DoublePress { .. } | Gesture::RightPress { .. })
|
||||
@@ -214,12 +231,14 @@ impl App {
|
||||
self.dragging = Some(target);
|
||||
}
|
||||
if target != self.panes.focused() {
|
||||
// Another pane: a text edited in this one is done first, and
|
||||
// move or visual mode ends.
|
||||
// Another pane: a text edited in this one is done first, move or
|
||||
// visual mode ends, and connect mode is cancelled.
|
||||
let ctx = self.shared.ctx(self.buffer());
|
||||
let mut actions = self.shared.mode.edit().map(|edit| edit.finish(&ctx)).unwrap_or_default();
|
||||
if let Mode::Move(_) | Mode::Visual(_) = self.shared.mode {
|
||||
actions.push(Action::App(AppAction::LeaveMode));
|
||||
match &self.shared.mode {
|
||||
Mode::Move(_) | Mode::Visual(_) => actions.push(Action::App(AppAction::LeaveMode)),
|
||||
Mode::Connect(connecting) => actions.extend(connect::cancel(connecting)),
|
||||
_ => {}
|
||||
}
|
||||
self.update(actions);
|
||||
self.panes.focus(target);
|
||||
|
||||
Reference in new issue
Block a user