checkpoint resize diagnostics and debug profile
This commit is contained in:
1 parent
49c421dc8b
commit
4610610d12
6 files changed
+69
-31
No files matched your search
+18
@@ -22,6 +22,24 @@ debug = 0
|
||||
strip = "debuginfo"
|
||||
incremental = false
|
||||
|
||||
# Debug 壳仍要承担真实终端负载:GPUI 布局/字体塑形和 VT 解析若以
|
||||
# opt-level 0 运行,窗口拖拽与持续输入都会出现发布版没有的延迟。
|
||||
# 只优化热依赖,主 crate 继续使用增量 debug,避免把日常构建变成 release。
|
||||
[profile.dev]
|
||||
codegen-units = 16
|
||||
debug = "limited"
|
||||
split-debuginfo = "unpacked"
|
||||
|
||||
[profile.dev.package]
|
||||
resvg = { opt-level = 3 }
|
||||
rustybuzz = { opt-level = 3 }
|
||||
taffy = { opt-level = 3 }
|
||||
ttf-parser = { opt-level = 3 }
|
||||
smol = { opt-level = 3 }
|
||||
gpui = { opt-level = 3 }
|
||||
gpui_macros = { opt-level = 3 }
|
||||
nebula_terminal = { opt-level = 3 }
|
||||
|
||||
[workspace.dependencies]
|
||||
mlua = { version = "0.11.4", features = ["lua54", "vendored", "serialize", "send"] }
|
||||
serde_path_to_error = "0.1.20"
|
||||
|
||||
@@ -181,8 +181,9 @@ where
|
||||
|
||||
/// Drain the channel.
|
||||
///
|
||||
/// Returns `false` when a shutdown message was received.
|
||||
fn drain_recv_channel(&mut self, state: &mut State) -> bool {
|
||||
/// Returns `Err(())` when a shutdown message was received; otherwise the
|
||||
/// last resize in this channel batch is returned for an ordered commit.
|
||||
fn drain_recv_channel(&mut self, state: &mut State) -> Result<Option<WindowSize>, ()> {
|
||||
// Resize storms (live window drags) queue faster than
|
||||
// ResizePseudoConsole drains them — the console host performs a full
|
||||
// viewport reflow per call. Within one drain only the newest size
|
||||
@@ -195,16 +196,11 @@ where
|
||||
match msg {
|
||||
Msg::Input(input) => state.write_list.push_back(input),
|
||||
Msg::Resize(window_size) => resize = Some(window_size),
|
||||
Msg::Shutdown => return false,
|
||||
Msg::Shutdown => return Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(window_size) = resize {
|
||||
state.stream.resize(window_size);
|
||||
self.pty.on_resize(window_size);
|
||||
}
|
||||
|
||||
true
|
||||
Ok(resize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -213,7 +209,7 @@ where
|
||||
state: &mut State,
|
||||
buf: &mut [u8],
|
||||
mut writer: Option<&mut X>,
|
||||
) -> io::Result<()>
|
||||
) -> io::Result<usize>
|
||||
where
|
||||
X: Write,
|
||||
{
|
||||
@@ -285,7 +281,7 @@ where
|
||||
self.event_proxy.send_event(Event::Wakeup);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(processed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -374,8 +370,37 @@ where
|
||||
}
|
||||
|
||||
// Handle channel events, if there are any.
|
||||
if !self.drain_recv_channel(&mut state) {
|
||||
break;
|
||||
let pending_resize = match self.drain_recv_channel(&mut state) {
|
||||
Ok(resize) => resize,
|
||||
Err(()) => break,
|
||||
};
|
||||
|
||||
if let Some(window_size) = pending_resize {
|
||||
// A resize is a stream boundary, not a UI-side property.
|
||||
// Drain everything already readable while the grid still
|
||||
// has the old geometry; otherwise old-width absolute CUP
|
||||
// output can be parsed into a new-width grid. This is the
|
||||
// local equivalent of tty7's ordered Size echo.
|
||||
loop {
|
||||
match self.pty_read(&mut state, &mut buf, pipe.as_mut()) {
|
||||
Ok(processed) if processed >= MAX_LOCKED_READ => continue,
|
||||
Ok(_) => break,
|
||||
Err(err) => {
|
||||
error!("PTY read before resize failed: {err}");
|
||||
failure = Some(format!("PTY read before resize failed: {err}"));
|
||||
break 'event_loop;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Match Windows Terminal's order: reflow the client model
|
||||
// first, then ask ConPTY to resize. ResizePseudoConsole is
|
||||
// synchronous; repaint bytes it produces are consumed by
|
||||
// the normal readable-event path below against this grid.
|
||||
self.terminal.lock().resize(window_size);
|
||||
self.pty.on_resize(window_size);
|
||||
state.stream.resize(window_size);
|
||||
self.event_proxy.send_event(Event::Wakeup);
|
||||
}
|
||||
|
||||
for event in events.iter() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Grid resize and reflow.
|
||||
|
||||
use std::cmp::{max, min, Ordering};
|
||||
use std::cmp::{Ordering, max, min};
|
||||
use std::mem;
|
||||
|
||||
use crate::index::{Boundary, Column, Line};
|
||||
@@ -99,8 +99,9 @@ impl<T: GridCell + Default + PartialEq> Grid<T> {
|
||||
/// is scrollback available. Once scrollback is exhausted, new lines are
|
||||
/// simply added to the bottom of the screen.
|
||||
///
|
||||
/// ConPTY does not restore scrollback when growing. It leaves visible rows
|
||||
/// and the cursor pinned, opening blank rows below them instead.
|
||||
/// ConPTY 增高时不会把先前推出视口的行重新拉回:现有内容和光标保持
|
||||
/// 原位,只在底部补空行。这里必须复刻 host 的行坐标,否则下一次
|
||||
/// PSReadLine 绝对定位重绘会落到旧输出中间。
|
||||
fn grow_lines<D>(&mut self, conpty: bool, target: usize)
|
||||
where
|
||||
T: ResetDiscriminant<D>,
|
||||
@@ -192,10 +193,7 @@ impl<T: GridCell + Default + PartialEq> Grid<T> {
|
||||
/// Bottommost viewport row containing a non-empty cell.
|
||||
#[inline]
|
||||
fn bottommost_occupied_line(&self) -> usize {
|
||||
(0..self.lines)
|
||||
.rev()
|
||||
.find(|&line| !self.raw[Line(line as i32)].is_clear())
|
||||
.unwrap_or(0)
|
||||
(0..self.lines).rev().find(|&line| !self.raw[Line(line as i32)].is_clear()).unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Grow number of columns in each row, reflowing if necessary.
|
||||
@@ -402,7 +400,8 @@ impl<T: GridCell + Default + PartialEq> Grid<T> {
|
||||
let mut wrapped = match row.shrink_with_minimum(columns, minimum) {
|
||||
Some(wrapped) if reflow => wrapped,
|
||||
_ => {
|
||||
if reflow && i == cursor_buffer_line && self.cursor.point.column >= columns {
|
||||
if reflow && i == cursor_buffer_line && self.cursor.point.column >= columns
|
||||
{
|
||||
// If there are empty cells before the cursor, we assume it is explicit
|
||||
// whitespace and need to wrap it like normal content.
|
||||
Vec::new()
|
||||
|
||||
@@ -402,10 +402,8 @@ fn display_iterator_can_crop_to_the_bottom_of_the_live_viewport() {
|
||||
}
|
||||
}
|
||||
|
||||
let points: Vec<_> = grid
|
||||
.display_iter_from(Line(2), 2, 2)
|
||||
.map(|cell| (cell.point, *cell.cell))
|
||||
.collect();
|
||||
let points: Vec<_> =
|
||||
grid.display_iter_from(Line(2), 2, 2).map(|cell| (cell.point, *cell.cell)).collect();
|
||||
|
||||
assert_eq!(
|
||||
points,
|
||||
|
||||
@@ -8,8 +8,8 @@ use std::{cmp, mem, ptr, str};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use base64::engine::general_purpose::STANDARD as Base64;
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD as Base64;
|
||||
use bitflags::bitflags;
|
||||
use log::{debug, trace};
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
@@ -2429,11 +2429,7 @@ enum ModeState {
|
||||
|
||||
impl From<bool> for ModeState {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
Self::Set
|
||||
} else {
|
||||
Self::Reset
|
||||
}
|
||||
if value { Self::Set } else { Self::Reset }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -393,6 +393,8 @@ impl OnResize for Conpty {
|
||||
// not take the whole process down — log and let the exit path handle it.
|
||||
let result = unsafe { (self.api.resize)(self.handle, window_size.into()) };
|
||||
if result != S_OK {
|
||||
// stderr 兜底:GPUI 主窗形态没有装 logger,失败不能无声。
|
||||
eprintln!("[nebula:conpty] ResizePseudoConsole failed: HRESULT {result:#x}");
|
||||
warn!("ResizePseudoConsole failed: HRESULT {result:#x}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user