git: 1f32ed1ebb79 - main - editors/microsoft-edit: Update to 1.1.0

From: MANTANI Nobutaka <nobutaka_at_FreeBSD.org>
Date: Sat, 31 May 2025 09:52:30 UTC
The branch main has been updated by nobutaka:

URL: https://cgit.FreeBSD.org/ports/commit/?id=1f32ed1ebb79129863646d34f221c748b094ae88

commit 1f32ed1ebb79129863646d34f221c748b094ae88
Author:     MANTANI Nobutaka <nobutaka@FreeBSD.org>
AuthorDate: 2025-05-31 09:50:48 +0000
Commit:     MANTANI Nobutaka <nobutaka@FreeBSD.org>
CommitDate: 2025-05-31 09:51:28 +0000

    editors/microsoft-edit: Update to 1.1.0
---
 editors/microsoft-edit/Makefile                    |   3 +-
 editors/microsoft-edit/distinfo                    |   6 +-
 .../files/patch-assets_manpage_edit.1              |  29 --
 editors/microsoft-edit/files/patch-src_lib.rs      |  10 -
 editors/microsoft-edit/files/patch-src_sys_unix.rs |  97 -----
 editors/microsoft-edit/files/patch-src_tui.rs      | 421 ---------------------
 6 files changed, 4 insertions(+), 562 deletions(-)

diff --git a/editors/microsoft-edit/Makefile b/editors/microsoft-edit/Makefile
index 97ea85cf8af0..9397ddf55815 100644
--- a/editors/microsoft-edit/Makefile
+++ b/editors/microsoft-edit/Makefile
@@ -1,7 +1,6 @@
 PORTNAME=	microsoft-edit
 DISTVERSIONPREFIX=	v
-DISTVERSION=	1.0.0
-PORTREVISION=	1
+DISTVERSION=	1.1.0
 CATEGORIES=	editors
 
 MAINTAINER=	nobutaka@FreeBSD.org
diff --git a/editors/microsoft-edit/distinfo b/editors/microsoft-edit/distinfo
index b13edffb06a3..c059ccc913fa 100644
--- a/editors/microsoft-edit/distinfo
+++ b/editors/microsoft-edit/distinfo
@@ -1,4 +1,4 @@
-TIMESTAMP = 1748176479
+TIMESTAMP = 1748527565
 SHA256 (rust/crates/aho-corasick-1.1.3.crate) = 8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916
 SIZE (rust/crates/aho-corasick-1.1.3.crate) = 183311
 SHA256 (rust/crates/anes-0.1.6.crate) = 4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299
@@ -141,5 +141,5 @@ SHA256 (rust/crates/windows_x86_64_msvc-0.52.6.crate) = 589f6da84c646204747d1270
 SIZE (rust/crates/windows_x86_64_msvc-0.52.6.crate) = 832564
 SHA256 (rust/crates/winres-0.1.12.crate) = b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c
 SIZE (rust/crates/winres-0.1.12.crate) = 19772
-SHA256 (microsoft-edit-v1.0.0_GH0.tar.gz) = 6718d9a96aa9dd2117d9d4afa83b0278ef007bcd42bdb1e8c4e2ad34144246c0
-SIZE (microsoft-edit-v1.0.0_GH0.tar.gz) = 250150
+SHA256 (microsoft-edit-v1.1.0_GH0.tar.gz) = d91205513b245bf4ed1127c35d148cac4f7dafd22b071fe3443d080bbda4b9ef
+SIZE (microsoft-edit-v1.1.0_GH0.tar.gz) = 257739
diff --git a/editors/microsoft-edit/files/patch-assets_manpage_edit.1 b/editors/microsoft-edit/files/patch-assets_manpage_edit.1
deleted file mode 100644
index 0ddc19019708..000000000000
--- a/editors/microsoft-edit/files/patch-assets_manpage_edit.1
+++ /dev/null
@@ -1,29 +0,0 @@
---- assets/manpage/edit.1.orig	2025-05-27 16:45:04 UTC
-+++ assets/manpage/edit.1
-@@ -0,0 +1,26 @@
-+.TH EDIT 1 "version 1.0" "May 2025"
-+.SH NAME
-+edit \- a simple text editor
-+.SH SYNOPSIS
-+\fBedit\fP [\fIOPTIONS\fP]... [\fIARGUMENTS\fP]...
-+.SH DESCRIPTION
-+edit is a simple text editor inspired by MS-DOS edit.
-+.SH EDITING
-+Edit is an interactive mode-less editor. Use Alt-F to access the menus.
-+.SH ARGUMENTS
-+.TP
-+\fIFILE[:LINE[:COLUMN]]\fP
-+The file to open, optionally with line and column (e.g., \fBfoo.txt:123:45\fP).
-+.SH OPTIONS
-+.TP
-+\fB\-h\fP, \fB\-\-help\fP
-+Print the help message.
-+.TP
-+\fB\-v\fP, \fB\-\-version\fP
-+Print the version number.
-+.SH COPYRIGHT
-+Copyright (c) Microsoft Corporation.
-+.br
-+Licensed under the MIT License.
-+.SH SEE ALSO
-+https://github.com/microsoft/edit
diff --git a/editors/microsoft-edit/files/patch-src_lib.rs b/editors/microsoft-edit/files/patch-src_lib.rs
deleted file mode 100644
index f83f9e0d6fcb..000000000000
--- a/editors/microsoft-edit/files/patch-src_lib.rs
+++ /dev/null
@@ -1,10 +0,0 @@
---- src/lib.rs.orig	2025-05-18 18:11:22 UTC
-+++ src/lib.rs
-@@ -5,7 +5,6 @@
-     allocator_api,
-     breakpoint,
-     cold_path,
--    inherent_str_constructors,
-     let_chains,
-     linked_list_cursors,
-     maybe_uninit_fill,
diff --git a/editors/microsoft-edit/files/patch-src_sys_unix.rs b/editors/microsoft-edit/files/patch-src_sys_unix.rs
deleted file mode 100644
index 5bfc46a66640..000000000000
--- a/editors/microsoft-edit/files/patch-src_sys_unix.rs
+++ /dev/null
@@ -1,97 +0,0 @@
---- src/sys/unix.rs.orig	2025-05-18 18:11:22 UTC
-+++ src/sys/unix.rs
-@@ -8,9 +8,9 @@ use std::fs::{self, File};
- 
- use std::ffi::{CStr, c_int, c_void};
- use std::fs::{self, File};
--use std::mem::{self, MaybeUninit};
-+use std::mem::{self, ManuallyDrop, MaybeUninit};
- use std::os::fd::{AsRawFd as _, FromRawFd as _};
--use std::ptr::{self, NonNull, null, null_mut};
-+use std::ptr::{self, NonNull, null_mut};
- use std::{thread, time};
- 
- use crate::arena::{Arena, ArenaString, scratch_arena};
-@@ -195,11 +195,19 @@ pub fn read_stdin(arena: &Arena, mut timeout: time::Du
-                 let beg = time::Instant::now();
- 
-                 let mut pollfd = libc::pollfd { fd: STATE.stdin, events: libc::POLLIN, revents: 0 };
--                let ts = libc::timespec {
--                    tv_sec: timeout.as_secs() as libc::time_t,
--                    tv_nsec: timeout.subsec_nanos() as libc::c_long,
--                };
--                let ret = libc::ppoll(&mut pollfd, 1, &ts, null());
-+                let ret;
-+                #[cfg(target_os = "linux")]
-+                {
-+                    let ts = libc::timespec {
-+                        tv_sec: timeout.as_secs() as libc::time_t,
-+                        tv_nsec: timeout.subsec_nanos() as libc::c_long,
-+                    };
-+                    ret = libc::ppoll(&mut pollfd, 1, &ts, ptr::null());
-+                }
-+                #[cfg(not(target_os = "linux"))]
-+                {
-+                    ret = libc::poll(&mut pollfd, 1, timeout.as_millis() as libc::c_int);
-+                }
-                 if ret < 0 {
-                     return None; // Error? Let's assume it's an EOF.
-                 }
-@@ -225,7 +233,7 @@ pub fn read_stdin(arena: &Arena, mut timeout: time::Du
-                 return None; // EOF
-             }
-             if ret < 0 {
--                match *libc::__errno_location() {
-+                match errno() {
-                     libc::EINTR if STATE.inject_resize => break,
-                     libc::EAGAIN if timeout == time::Duration::ZERO => break,
-                     libc::EINTR | libc::EAGAIN => {}
-@@ -304,7 +312,7 @@ pub fn write_stdout(text: &str) {
-             continue;
-         }
- 
--        let err = unsafe { *libc::__errno_location() };
-+        let err = errno();
-         if err != libc::EINTR {
-             return;
-         }
-@@ -407,7 +415,7 @@ unsafe fn load_library(name: &CStr) -> apperr::Result<
- unsafe fn load_library(name: &CStr) -> apperr::Result<NonNull<c_void>> {
-     unsafe {
-         NonNull::new(libc::dlopen(name.as_ptr(), libc::RTLD_LAZY))
--            .ok_or_else(|| errno_to_apperr(libc::ELIBACC))
-+            .ok_or_else(|| errno_to_apperr(libc::ENOENT))
-     }
- }
- 
-@@ -423,7 +431,7 @@ pub unsafe fn get_proc_address<T>(handle: NonNull<c_vo
-     unsafe {
-         let sym = libc::dlsym(handle.as_ptr(), name.as_ptr());
-         if sym.is_null() {
--            Err(errno_to_apperr(libc::ELIBACC))
-+            Err(errno_to_apperr(libc::ENOENT))
-         } else {
-             Ok(mem::transmute_copy(&sym))
-         }
-@@ -538,6 +546,14 @@ pub fn preferred_languages(arena: &Arena) -> Vec<Arena
- }
- 
- #[inline]
-+fn errno() -> i32 {
-+    // Under `-O -Copt-level=s` the 1.87 compiler fails to fully inline and
-+    // remove the raw_os_error() call. This leaves us with the drop() call.
-+    // ManuallyDrop fixes that and results in a direct `std::sys::os::errno` call.
-+    ManuallyDrop::new(std::io::Error::last_os_error()).raw_os_error().unwrap_or(0)
-+}
-+
-+#[inline]
- pub(crate) fn io_error_to_apperr(err: std::io::Error) -> apperr::Error {
-     errno_to_apperr(err.raw_os_error().unwrap_or(0))
- }
-@@ -565,5 +581,5 @@ fn check_int_return(ret: libc::c_int) -> apperr::Resul
- }
- 
- fn check_int_return(ret: libc::c_int) -> apperr::Result<libc::c_int> {
--    if ret < 0 { Err(errno_to_apperr(unsafe { *libc::__errno_location() })) } else { Ok(ret) }
-+    if ret < 0 { Err(errno_to_apperr(errno())) } else { Ok(ret) }
- }
diff --git a/editors/microsoft-edit/files/patch-src_tui.rs b/editors/microsoft-edit/files/patch-src_tui.rs
deleted file mode 100644
index 14d8bcbac081..000000000000
--- a/editors/microsoft-edit/files/patch-src_tui.rs
+++ /dev/null
@@ -1,421 +0,0 @@
---- src/tui.rs.orig	2025-05-18 18:11:22 UTC
-+++ src/tui.rs
-@@ -640,6 +640,7 @@ impl Tui {
- 
-             tree,
-             last_modal: None,
-+            focused_node: None,
-             next_block_id_mixin: 0,
-             needs_settling: false,
- 
-@@ -653,6 +654,9 @@ impl Tui {
-         // out where is to do a binary search of commenting out code in main.rs.
-         debug_assert!(ctx.tree.current_node.borrow().stack_parent.is_none());
- 
-+        // End the root node.
-+        ctx.block_end();
-+
-         // Ensure that focus doesn't escape the active modal.
-         if let Some(node) = ctx.last_modal
-             && !self.is_subtree_focused(&node.borrow())
-@@ -684,18 +688,8 @@ impl Tui {
-         // Remove any unknown nodes from the focus path.
-         // It's important that we do this after the tree has been swapped out,
-         // so that pop_focusable_node() has access to the newest version of the tree.
--        let focus_path_changed = self.pop_focusable_node(focus_path_pop_min);
--        needs_settling |= focus_path_changed;
-+        needs_settling |= self.pop_focusable_node(focus_path_pop_min);
- 
--        // If some elements went away and the focus path changed above, we ignore tab presses.
--        // It may otherwise lead to weird situations where focus moves unexpectedly.
--        if !focus_path_changed
--            && !ctx.input_consumed
--            && let Some(input) = ctx.input_keyboard
--        {
--            needs_settling |= self.move_focus(input);
--        }
--
-         // `needs_more_settling()` depends on the current value
-         // of `settling_have` and so we increment it first.
-         self.settling_have += 1;
-@@ -1213,117 +1207,6 @@ impl Tui {
-         last_before != last_after
-     }
- 
--    // TODO: Move this into `block_end()` and run it whenever the block is a `focus_well`.
--    // It makes no sense otherwise that all input handling occurs in the controls, except for this.
--    fn move_focus(&mut self, input: InputKey) -> bool {
--        if !matches!(input, vk::TAB | SHIFT_TAB | vk::UP | vk::DOWN | vk::LEFT | vk::RIGHT) {
--            return false;
--        }
--
--        let focused_id = self.focused_node_path.last().cloned().unwrap_or(0);
--        let Some(focused) = self.prev_node_map.get(focused_id) else {
--            debug_assert!(false); // The caller should've cleaned up the focus path.
--            return false;
--        };
--
--        let mut focused_start = focused;
--        let mut root = focused;
--
--        // Figure out if we're inside a focus void (a container that doesn't
--        // allow tabbing inside), and in that case, toss the focus to it.
--        //
--        // Also, figure out the container within which the focus must be contained.
--        // This way, tab/shift-tab only moves within the same window.
--        // The ROOT_ID node has no parent, and the others have a float attribute.
--        // If the root is the focused node, it should of course not move upward.
--        loop {
--            let root_node = root.borrow();
--            if root_node.attributes.focus_well {
--                break;
--            }
--            if root_node.attributes.focus_void {
--                focused_start = root;
--            }
--            root = match root_node.parent {
--                Some(parent) => parent,
--                None => break,
--            }
--        }
--
--        let forward;
--        let min_depth;
--        match input {
--            SHIFT_TAB | vk::TAB => {
--                forward = input == vk::TAB;
--                min_depth = usize::MAX;
--            }
--            vk::UP | vk::DOWN => {
--                forward = input == vk::DOWN;
--                min_depth = usize::MAX;
--            }
--            vk::LEFT | vk::RIGHT => {
--                // Find the cell within a row within a table that we're in.
--                // To do so we'll use a circular buffer of the last 3 nodes while we travel up.
--                let mut buf = [None; 3];
--                let mut idx = buf.len() - 1;
--                let mut node = focused_start;
--
--                loop {
--                    idx = (idx + 1) % buf.len();
--                    buf[idx] = Some(node);
--                    if let NodeContent::Table(..) = &node.borrow().content {
--                        break;
--                    }
--                    if ptr::eq(node, root) {
--                        return false;
--                    }
--                    node = match node.borrow().parent {
--                        Some(parent) => parent,
--                        None => return false,
--                    }
--                }
--
--                // The current `idx` points to the table.
--                // The last item is the row.
--                // The 2nd to last item is the cell.
--                let Some(row) = buf[(idx + 3 - 1) % buf.len()] else {
--                    return false;
--                };
--                let Some(cell) = buf[(idx + 3 - 2) % buf.len()] else {
--                    return false;
--                };
--
--                root = row;
--                focused_start = cell;
--                forward = input == vk::RIGHT;
--                min_depth = root.borrow().depth;
--            }
--            _ => return false,
--        }
--
--        let mut focused_next = focused_start;
--        Tree::visit_all(root, focused_start, forward, |node| {
--            let n = node.borrow();
--            if ptr::eq(node, root) {
--                VisitControl::Continue
--            } else if n.attributes.focusable && !ptr::eq(node, focused_start) {
--                focused_next = node;
--                VisitControl::Stop
--            } else if n.attributes.focus_void || n.depth >= min_depth {
--                VisitControl::SkipChildren
--            } else {
--                VisitControl::Continue
--            }
--        });
--
--        if ptr::eq(focused_next, focused_start) {
--            false
--        } else {
--            Tui::build_node_path(Some(focused_next), &mut self.focused_node_path);
--            true
--        }
--    }
--
-     // Scroll the focused node(s) into view inside scrollviews
-     fn scroll_to_focused(&mut self) -> bool {
-         let focused_id = self.focused_node_path.last().cloned().unwrap_or(0);
-@@ -1375,6 +1258,7 @@ pub struct Context<'a, 'input> {
- 
-     tree: Tree<'a>,
-     last_modal: Option<&'a NodeCell<'a>>,
-+    focused_node: Option<&'a NodeCell<'a>>,
-     next_block_id_mixin: u64,
-     needs_settling: bool,
- 
-@@ -1484,8 +1368,83 @@ impl<'a> Context<'a, '_> {
-     /// Ends the current UI block, returning to its parent container.
-     pub fn block_end(&mut self) {
-         self.tree.pop_stack();
-+        self.block_end_move_focus();
-     }
- 
-+    fn block_end_move_focus(&mut self) {
-+        // At this point, it's more like "focus_well?" instead of "focus_well!".
-+        let focus_well = self.tree.last_node;
-+
-+        // Remember the focused node, if any, because once the code below runs,
-+        // we need it for the `Tree::visit_all` call.
-+        if self.is_focused() {
-+            self.focused_node = Some(focus_well);
-+        }
-+
-+        // Filter down to nodes that are focus wells and contain the focus.
-+        // They're basically the "tab container".
-+        if !focus_well.borrow().attributes.focus_well {
-+            return;
-+        }
-+
-+        // The mere fact that there's a `focused_node` indicates that we're the
-+        // first `block_end()` call that's a focus well and also contains the focus.
-+        let Some(focused) = self.focused_node else {
-+            return;
-+        };
-+
-+        // Filter down to Tab/Shift+Tab inputs.
-+        if self.input_consumed {
-+            return;
-+        }
-+        let Some(input) = self.input_keyboard else {
-+            return;
-+        };
-+        if !matches!(input, SHIFT_TAB | vk::TAB) {
-+            return;
-+        }
-+
-+        let forward = input == vk::TAB;
-+        let mut focused_start = focused;
-+        let mut focused_next = focused;
-+
-+        // We may be in a focus void right now (= doesn't want to be tabbed into),
-+        // so first we must go up the tree until we're outside of it.
-+        loop {
-+            if ptr::eq(focused_start, focus_well) {
-+                // If we hit the root / focus well, we weren't in a focus void,
-+                // and can reset `focused_before` to the current focused node.
-+                focused_start = focused;
-+                break;
-+            }
-+
-+            focused_start = focused_start.borrow().parent.unwrap();
-+            if focused_start.borrow().attributes.focus_void {
-+                break;
-+            }
-+        }
-+
-+        Tree::visit_all(focus_well, focused_start, forward, |node| {
-+            let n = node.borrow();
-+            if n.attributes.focusable && !ptr::eq(node, focused_start) {
-+                focused_next = node;
-+                VisitControl::Stop
-+            } else if n.attributes.focus_void {
-+                VisitControl::SkipChildren
-+            } else {
-+                VisitControl::Continue
-+            }
-+        });
-+
-+        if ptr::eq(focused_next, focused_start) {
-+            return;
-+        }
-+
-+        Tui::build_node_path(Some(focused_next), &mut self.tui.focused_node_path);
-+        self.set_input_consumed();
-+        self.needs_rerender();
-+    }
-+
-     /// Mixes in an extra value to the next UI block's ID for uniqueness.
-     /// Use this when you build a list of items with the same classname.
-     pub fn next_block_id_mixin(&mut self, id: u64) {
-@@ -1796,6 +1755,8 @@ impl<'a> Context<'a, '_> {
-                 debug_assert!(matches!(parent.content, NodeContent::Table(_)));
- 
-                 self.block_end();
-+                self.table_end_row();
-+
-                 self.next_block_id_mixin(parent.child_count as u64);
-             }
-         }
-@@ -1803,6 +1764,10 @@ impl<'a> Context<'a, '_> {
-         self.block_begin("row");
-     }
- 
-+    fn table_end_row(&mut self) {
-+        self.table_row_move_focus();
-+    }
-+
-     /// Ends the current table block.
-     pub fn table_end(&mut self) {
-         let current_node = self.tree.current_node.borrow();
-@@ -1811,11 +1776,136 @@ impl<'a> Context<'a, '_> {
-         // current_node will refer to the table. Otherwise, it'll refer to the current row.
-         if !matches!(current_node.content, NodeContent::Table(_)) {
-             self.block_end();
-+            self.table_end_row();
-         }
- 
-         self.block_end(); // table
-+        self.table_end_move_focus();
-     }
- 
-+    fn table_row_move_focus(&mut self) {
-+        // Filter down to table rows that are focused.
-+        if !self.contains_focus() {
-+            return;
-+        }
-+
-+        // Filter down to left/right inputs.
-+        if self.input_consumed {
-+            return;
-+        }
-+        let Some(input) = self.input_keyboard else {
-+            return;
-+        };
-+        if !matches!(input, vk::LEFT | vk::RIGHT) {
-+            return;
-+        }
-+
-+        let row = self.tree.last_node;
-+        let Some(&focused_cell_id) = self.tui.focused_node_path.get(row.borrow().depth + 1) else {
-+            return;
-+        };
-+
-+        let mut prev_next = NodeSiblings { prev: None, next: None };
-+        let mut focused = None;
-+
-+        // Iterate through the cells in the row, looking for the focused cell.
-+        // Take note of the previous and next focusable cells around the focused one.
-+        for cell in Tree::iterate_siblings(row.borrow().children.first) {
-+            let n = cell.borrow();
-+            if n.id == focused_cell_id {
-+                focused = Some(cell);
-+            } else if n.attributes.focusable {
-+                if focused.is_none() {
-+                    prev_next.prev = Some(cell);
-+                } else {
-+                    prev_next.next = Some(cell);
-+                    break;
-+                }
-+            }
-+        }
-+
-+        if focused.is_none() {
-+            return;
-+        }
-+
-+        let forward = input == vk::RIGHT;
-+        let children_idx = if forward { NodeChildren::FIRST } else { NodeChildren::LAST };
-+        let siblings_idx = if forward { NodeSiblings::NEXT } else { NodeSiblings::PREV };
-+        let Some(focused_next) =
-+            prev_next.get(siblings_idx).or_else(|| row.borrow().children.get(children_idx))
-+        else {
-+            return;
-+        };
-+
-+        Tui::build_node_path(Some(focused_next), &mut self.tui.focused_node_path);
-+        self.set_input_consumed();
-+        self.needs_rerender();
-+    }
-+
-+    fn table_end_move_focus(&mut self) {
-+        // Filter down to table rows that are focused.
-+        if !self.contains_focus() {
-+            return;
-+        }
-+
-+        // Filter down to up/down inputs.
-+        if self.input_consumed {
-+            return;
-+        }
-+        let Some(input) = self.input_keyboard else {
-+            return;
-+        };
-+        if !matches!(input, vk::UP | vk::DOWN) {
-+            return;
-+        }
-+
-+        let table = self.tree.last_node;
-+        if table.borrow().child_count <= 1 {
-+            // If there's just one row, we can't move focus up or down.
-+            return;
-+        }
-+
-+        let Some(&focused_row_id) = self.tui.focused_node_path.get(table.borrow().depth + 1) else {
-+            return;
-+        };
-+
-+        let mut prev_next = NodeSiblings { prev: None, next: None };
-+        let mut focused = None;
-+
-+        // Iterate through the row in the table, looking for the focused row.
-+        // Take note of the previous and next focusable rows around the focused one.
-+        for cell in Tree::iterate_siblings(table.borrow().children.first) {
-+            let n = cell.borrow();
-+            if n.id == focused_row_id {
-+                focused = Some(cell);
-+            } else if n.attributes.focusable {
-+                if focused.is_none() {
-+                    prev_next.prev = Some(cell);
-+                } else {
-+                    prev_next.next = Some(cell);
-+                    break;
-+                }
-+            }
-+        }
-+
-+        if focused.is_none() {
-+            return;
-+        }
-+
-+        let forward = input == vk::DOWN;
-+        let children_idx = if forward { NodeChildren::FIRST } else { NodeChildren::LAST };
-+        let siblings_idx = if forward { NodeSiblings::NEXT } else { NodeSiblings::PREV };
-+        let Some(focused_next) =
-+            prev_next.get(siblings_idx).or_else(|| table.borrow().children.get(children_idx))
-+        else {
-+            return;
-+        };
-+
-+        Tui::build_node_path(Some(focused_next), &mut self.tui.focused_node_path);
-+        self.set_input_consumed();
-+        self.needs_rerender();
-+    }
-+
-     /// Creates a simple text label.
-     pub fn label(&mut self, classname: &'static str, text: &str) {
-         self.styled_label_begin(classname);
-@@ -3312,9 +3402,10 @@ impl<'a> Tree<'a> {
-     /// Completes the current node and moves focus to the parent.
-     fn pop_stack(&mut self) {
-         let current_node = self.current_node.borrow();
--        let stack_parent = current_node.stack_parent.unwrap();
--        self.last_node = self.current_node;
--        self.current_node = stack_parent;
-+        if let Some(stack_parent) = current_node.stack_parent {
-+            self.last_node = self.current_node;
-+            self.current_node = stack_parent;
-+        }
-     }
- 
-     fn iterate_siblings(