Fixed a slew of bugs involving switching modes.
This commit is contained in:
parent
a193f2c0ca
commit
f98b36833c
|
|
@ -181,7 +181,7 @@ class IcedViewportView: NSView {
|
||||||
|
|
||||||
if cmd && !shift {
|
if cmd && !shift {
|
||||||
switch chars {
|
switch chars {
|
||||||
case "a", "b", "c", "e", "f", "g", "i", "v", "x", "z", "p", "t",
|
case "a", "b", "c", "e", "f", "g", "i", "r", "v", "x", "z", "p", "t",
|
||||||
"=", "+", "-", "0":
|
"=", "+", "-", "0":
|
||||||
keyDown(with: event)
|
keyDown(with: event)
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,8 @@ pub enum Message {
|
||||||
/// blank line with one blank line of padding above and below).
|
/// blank line with one blank line of padding above and below).
|
||||||
FixUp,
|
FixUp,
|
||||||
Evaluate,
|
Evaluate,
|
||||||
|
/// Full-document ordered eval: every module evaluated in sequence.
|
||||||
|
EvalAll,
|
||||||
SmartEval,
|
SmartEval,
|
||||||
ZoomIn,
|
ZoomIn,
|
||||||
ZoomOut,
|
ZoomOut,
|
||||||
|
|
@ -436,6 +438,10 @@ pub struct EditorState {
|
||||||
// ── Images ──
|
// ── Images ──
|
||||||
pub computed_images: Vec<ComputedImage>,
|
pub computed_images: Vec<ComputedImage>,
|
||||||
pub image_cache: HashMap<String, ImageCacheEntry>,
|
pub image_cache: HashMap<String, ImageCacheEntry>,
|
||||||
|
|
||||||
|
/// Previous global cursor line (block start_line + intra-block line).
|
||||||
|
/// Used by `tick()` to detect cursor-line changes and trigger eval.
|
||||||
|
prev_cursor_line: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Per-eval table name→id bookkeeping. `keys` is every alias a table is
|
/// Per-eval table name→id bookkeeping. `keys` is every alias a table is
|
||||||
|
|
@ -551,6 +557,7 @@ impl EditorState {
|
||||||
pending_clipboard: None,
|
pending_clipboard: None,
|
||||||
computed_images: Vec::new(),
|
computed_images: Vec::new(),
|
||||||
image_cache: HashMap::new(),
|
image_cache: HashMap::new(),
|
||||||
|
prev_cursor_line: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1126,6 +1133,10 @@ impl EditorState {
|
||||||
if let Some(sc) = loaded.sidecar {
|
if let Some(sc) = loaded.sidecar {
|
||||||
self.apply_sidecar(&sc);
|
self.apply_sidecar(&sc);
|
||||||
}
|
}
|
||||||
|
// Trigger full eval when loading into Live or View mode.
|
||||||
|
if self.render_mode == RenderMode::Live || self.render_mode == RenderMode::View {
|
||||||
|
self.run_eval_all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save the document to raw file bytes: assign sidecar ids to any tables
|
/// Save the document to raw file bytes: assign sidecar ids to any tables
|
||||||
|
|
@ -1497,6 +1508,22 @@ impl EditorState {
|
||||||
self.eval_dirty = false;
|
self.eval_dirty = false;
|
||||||
self.run_eval();
|
self.run_eval();
|
||||||
}
|
}
|
||||||
|
// Cursor-line-change trigger: when the cursor moves to a different
|
||||||
|
// line (arrow keys, click, etc.) without an edit, re-evaluate.
|
||||||
|
{
|
||||||
|
let block_start = self.layout.get(self.focused_block)
|
||||||
|
.and_then(|id| self.registry.get(id))
|
||||||
|
.map(|b| b.start_line())
|
||||||
|
.unwrap_or(0);
|
||||||
|
let intra = self.content().cursor().position.line;
|
||||||
|
let global_line = block_start + intra;
|
||||||
|
if global_line != self.prev_cursor_line {
|
||||||
|
self.prev_cursor_line = global_line;
|
||||||
|
if !self.eval_dirty {
|
||||||
|
self.run_eval();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Fire the long-press copy at the threshold — if the user is still
|
// Fire the long-press copy at the threshold — if the user is still
|
||||||
// holding past LONG_PRESS_MS without having released, double-clicked,
|
// holding past LONG_PRESS_MS without having released, double-clicked,
|
||||||
// or moved off, drop the result onto the clipboard.
|
// or moved off, drop the result onto the clipboard.
|
||||||
|
|
@ -2127,7 +2154,7 @@ impl EditorState {
|
||||||
/// containing the raw markdown. The single-block view path renders it
|
/// containing the raw markdown. The single-block view path renders it
|
||||||
/// as a full-page text editor. Cmd+A then selects all text naturally.
|
/// as a full-page text editor. Cmd+A then selects all text naturally.
|
||||||
pub fn enter_editor_mode(&mut self) {
|
pub fn enter_editor_mode(&mut self) {
|
||||||
if self.render_mode != RenderMode::Live { return; }
|
if self.render_mode == RenderMode::Editor { return; }
|
||||||
self.push_undo_snapshot();
|
self.push_undo_snapshot();
|
||||||
let full = self.full_text();
|
let full = self.full_text();
|
||||||
self.clear_blocks();
|
self.clear_blocks();
|
||||||
|
|
@ -2138,6 +2165,10 @@ impl EditorState {
|
||||||
self.render_mode = RenderMode::Editor;
|
self.render_mode = RenderMode::Editor;
|
||||||
self.all_blocks_selected = false;
|
self.all_blocks_selected = false;
|
||||||
self.editing = None;
|
self.editing = None;
|
||||||
|
self.eval_results.clear();
|
||||||
|
self.computed_tables.clear();
|
||||||
|
self.computed_trees.clear();
|
||||||
|
self.computed_cells.clear();
|
||||||
// Select all text in the single editor so the user can immediately
|
// Select all text in the single editor so the user can immediately
|
||||||
// delete or type over it.
|
// delete or type over it.
|
||||||
self.content_mut().perform(Action::Move(Motion::DocumentStart));
|
self.content_mut().perform(Action::Move(Motion::DocumentStart));
|
||||||
|
|
@ -2150,7 +2181,7 @@ impl EditorState {
|
||||||
/// Switch back to live mode: reparse the single text block into
|
/// Switch back to live mode: reparse the single text block into
|
||||||
/// structured blocks (headings, tables, HRs, etc.).
|
/// structured blocks (headings, tables, HRs, etc.).
|
||||||
pub fn exit_editor_mode(&mut self) {
|
pub fn exit_editor_mode(&mut self) {
|
||||||
if self.render_mode == RenderMode::Live { return; }
|
if self.render_mode != RenderMode::Editor { return; }
|
||||||
let text = self.content().text();
|
let text = self.content().text();
|
||||||
let lang = self.lang_str();
|
let lang = self.lang_str();
|
||||||
self.replace_blocks(blocks::parse_blocks(&text, &lang));
|
self.replace_blocks(blocks::parse_blocks(&text, &lang));
|
||||||
|
|
@ -2218,8 +2249,12 @@ impl EditorState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find use declarations in the focused block and import those modules
|
// Find use declarations in all text blocks of this module and import those modules
|
||||||
if let Some(block) = self.block_at(block_idx) {
|
let use_block_ids: Vec<crate::selection::BlockId> = my_module
|
||||||
|
.map(|m| m.block_ids.clone())
|
||||||
|
.unwrap_or_default();
|
||||||
|
for &bid in &use_block_ids {
|
||||||
|
if let Some(block) = self.registry.get(&bid) {
|
||||||
if let Some(tb) = block.as_any().downcast_ref::<TextBlock>() {
|
if let Some(tb) = block.as_any().downcast_ref::<TextBlock>() {
|
||||||
let text = tb.content.text();
|
let text = tb.content.text();
|
||||||
let use_decls = interp::extract_use_declarations(&text);
|
let use_decls = interp::extract_use_declarations(&text);
|
||||||
|
|
@ -2246,6 +2281,7 @@ impl EditorState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
eval_interp
|
eval_interp
|
||||||
}
|
}
|
||||||
|
|
@ -2375,6 +2411,33 @@ impl EditorState {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate every module in document order. Each module gets a fresh
|
||||||
|
/// interpreter seeded with root exports (and its own `use` imports).
|
||||||
|
/// Used by Cmd+R, mode switches to Live/View, and file loads.
|
||||||
|
fn run_eval_all(&mut self) {
|
||||||
|
self.rebuild_modules();
|
||||||
|
// Clear all computed layers up front.
|
||||||
|
self.eval_results.clear();
|
||||||
|
self.computed_tables.clear();
|
||||||
|
self.computed_trees.clear();
|
||||||
|
self.computed_cells.clear();
|
||||||
|
|
||||||
|
// Evaluate each module in order by temporarily pointing focused_block
|
||||||
|
// at a text block within it, then calling run_eval() which already
|
||||||
|
// handles cross-module imports (root exports + `use` declarations).
|
||||||
|
let saved = self.focused_block;
|
||||||
|
let modules: Vec<crate::module::Module> = self.modules.clone();
|
||||||
|
for module in &modules {
|
||||||
|
let anchor_idx = module.block_ids.iter()
|
||||||
|
.find_map(|bid| self.layout.iter().position(|id| id == bid));
|
||||||
|
if let Some(idx) = anchor_idx {
|
||||||
|
self.focused_block = idx;
|
||||||
|
self.run_eval();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.focused_block = saved;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn take_pending_focus(&mut self) -> Option<WidgetId> {
|
pub fn take_pending_focus(&mut self) -> Option<WidgetId> {
|
||||||
self.pending_focus.take()
|
self.pending_focus.take()
|
||||||
}
|
}
|
||||||
|
|
@ -2542,6 +2605,7 @@ impl EditorState {
|
||||||
Message::ShowContextMenu { .. } | Message::HideContextMenu => true,
|
Message::ShowContextMenu { .. } | Message::HideContextMenu => true,
|
||||||
Message::CopyLiteral(_) | Message::CopyFocusedTableSelection => true,
|
Message::CopyLiteral(_) | Message::CopyFocusedTableSelection => true,
|
||||||
Message::InlineResultPress { .. } | Message::InlineResultRelease => true,
|
Message::InlineResultPress { .. } | Message::InlineResultRelease => true,
|
||||||
|
Message::EvalAll => true,
|
||||||
Message::EditorAction(action) | Message::BlockAction(_, action) => {
|
Message::EditorAction(action) | Message::BlockAction(_, action) => {
|
||||||
!action.is_edit()
|
!action.is_edit()
|
||||||
}
|
}
|
||||||
|
|
@ -2769,6 +2833,9 @@ impl EditorState {
|
||||||
Message::Evaluate => {
|
Message::Evaluate => {
|
||||||
self.run_eval();
|
self.run_eval();
|
||||||
}
|
}
|
||||||
|
Message::EvalAll => {
|
||||||
|
self.run_eval_all();
|
||||||
|
}
|
||||||
Message::SmartEval => {
|
Message::SmartEval => {
|
||||||
let cursor = self.content().cursor();
|
let cursor = self.content().cursor();
|
||||||
let text = self.content().text();
|
let text = self.content().text();
|
||||||
|
|
@ -3273,9 +3340,24 @@ impl EditorState {
|
||||||
}
|
}
|
||||||
Message::SetRenderMode(mode) => {
|
Message::SetRenderMode(mode) => {
|
||||||
match mode {
|
match mode {
|
||||||
RenderMode::Live => self.exit_editor_mode(),
|
RenderMode::Live => {
|
||||||
|
if self.render_mode == RenderMode::Editor {
|
||||||
|
self.exit_editor_mode();
|
||||||
|
} else if self.render_mode == RenderMode::View {
|
||||||
|
self.render_mode = RenderMode::Live;
|
||||||
|
self.reparse();
|
||||||
|
// Restore keyboard focus to the focused text block.
|
||||||
|
if let Some(tb) = self.text_block_at(self.focused_block) {
|
||||||
|
self.pending_focus = Some(block_editor_id(tb.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.run_eval_all();
|
||||||
|
}
|
||||||
RenderMode::Editor => self.enter_editor_mode(),
|
RenderMode::Editor => self.enter_editor_mode(),
|
||||||
RenderMode::View => self.enter_view_mode(),
|
RenderMode::View => {
|
||||||
|
self.enter_view_mode();
|
||||||
|
self.run_eval_all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::ClearAllBlocks => {
|
Message::ClearAllBlocks => {
|
||||||
|
|
|
||||||
|
|
@ -362,6 +362,10 @@ pub fn render(handle: &mut ViewportHandle) {
|
||||||
messages.push(Message::SmartEval);
|
messages.push(Message::SmartEval);
|
||||||
consumed.push(ev_idx);
|
consumed.push(ev_idx);
|
||||||
}
|
}
|
||||||
|
"r" => {
|
||||||
|
messages.push(Message::EvalAll);
|
||||||
|
consumed.push(ev_idx);
|
||||||
|
}
|
||||||
"z" if modifiers.shift() => {
|
"z" if modifiers.shift() => {
|
||||||
messages.push(Message::Redo);
|
messages.push(Message::Redo);
|
||||||
consumed.push(ev_idx);
|
consumed.push(ev_idx);
|
||||||
|
|
|
||||||
|
|
@ -304,9 +304,9 @@ pub extern "C" fn viewport_send_command(handle: *mut ViewportHandle, command: u3
|
||||||
8 => h.state.update(editor::Message::ZoomOut),
|
8 => h.state.update(editor::Message::ZoomOut),
|
||||||
9 => h.state.update(editor::Message::ZoomReset),
|
9 => h.state.update(editor::Message::ZoomReset),
|
||||||
// 11 = live, 12 = editor, 13 = view
|
// 11 = live, 12 = editor, 13 = view
|
||||||
11 => h.state.exit_editor_mode(),
|
11 => h.state.update(editor::Message::SetRenderMode(editor::RenderMode::Live)),
|
||||||
12 => h.state.enter_editor_mode(),
|
12 => h.state.update(editor::Message::SetRenderMode(editor::RenderMode::Editor)),
|
||||||
13 => h.state.enter_view_mode(),
|
13 => h.state.update(editor::Message::SetRenderMode(editor::RenderMode::View)),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
h.needs_redraw = true;
|
h.needs_redraw = true;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue