refactor(gpu): the rich-text projection delegates too
A THIRD copy of the rule lived in the projection that decides where the GPU actually renders a later tab: manual `stop - column % stop` arithmetic, and a per-character advance calling `UnicodeWidthChar::width` directly. So the previous commit's mutation broke the minimap while leaving the rendering path untouched --- the shared bound could still drift from the columns the GPU draws at. Both now delegate to `pmacs_protocol::columns::advance_char`: the tab width is DERIVED from the shared advance rather than recomputed, and the per-character step is the shared one. The projection's stream semantics stay local, because they are real and distinct: the column runs ACROSS chunks, so adornment text shifts a later tab, and a newline restarts it. That is why the wrapper still exists rather than being replaced outright. Evidence, not assertion: mutating the tab stop in `pmacs_protocol::columns` now breaks BOTH `tab_projection_uses_shared_stops_and_unicode_columns` and `minimap_columns_match_code_tab_and_unicode_widths`. Six adornment rows, including `caret_projection_accounts_for_inline_adornments`, still pass, so the stream behaviour survived the delegation. `TAB_STOP_COLUMNS` and `UnicodeWidthChar` are now unused imports in pmacs-gpu and are dropped --- which is itself the check that no copy of the rule remains in this crate.
This commit is contained in:
parent
9e54cd2c5b
commit
364eb7b095
|
|
@ -46,13 +46,12 @@ use pmacs_protocol::{
|
||||||
MAX_STATUSLINE_FACE_BYTES, MAX_STATUSLINE_PROVIDERS, MAX_STATUSLINE_SEGMENT_BYTES,
|
MAX_STATUSLINE_FACE_BYTES, MAX_STATUSLINE_PROVIDERS, MAX_STATUSLINE_SEGMENT_BYTES,
|
||||||
MAX_STATUSLINE_TOTAL_TEXT_BYTES, MenuPromptRow, MinibufferRow, Modifiers,
|
MAX_STATUSLINE_TOTAL_TEXT_BYTES, MenuPromptRow, MinibufferRow, Modifiers,
|
||||||
MouseButton as ProtocolMouseButton, MouseKind as ProtocolMouseKind, PointerKind,
|
MouseButton as ProtocolMouseButton, MouseKind as ProtocolMouseKind, PointerKind,
|
||||||
SelectionSnapshot, StatuslineSegment, StyleSegment, StyleSpan, TAB_STOP_COLUMNS,
|
SelectionSnapshot, StatuslineSegment, StyleSegment, StyleSpan, TEXT_INPUT_MIN_VERSION,
|
||||||
TEXT_INPUT_MIN_VERSION, TerminalFrame, UnderlineStyle,
|
TerminalFrame, UnderlineStyle,
|
||||||
cell::{Color as CellColor, Style as CellStyle},
|
cell::{Color as CellColor, Style as CellStyle},
|
||||||
is_builtin_pair_char, is_modeline_face_name,
|
is_builtin_pair_char, is_modeline_face_name,
|
||||||
panel::{PANEL_MIN_VERSION, PanelFrame, PanelFramePayload},
|
panel::{PANEL_MIN_VERSION, PanelFrame, PanelFramePayload},
|
||||||
};
|
};
|
||||||
use unicode_width::UnicodeWidthChar;
|
|
||||||
use wgpu::MultisampleState;
|
use wgpu::MultisampleState;
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::event::{ElementState, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent};
|
use winit::event::{ElementState, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent};
|
||||||
|
|
@ -14099,8 +14098,15 @@ fn expand_chunk_tabs(chunks: Vec<RichChunk>) -> Vec<RichChunk> {
|
||||||
source: offset_chunk_source(source, segment_start as u64),
|
source: offset_chunk_source(source, segment_start as u64),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let tab_stop = TAB_STOP_COLUMNS as usize;
|
// The tab's width comes from the SHARED rule, derived
|
||||||
let tab_width = tab_stop - column % tab_stop;
|
// rather than recomputed: a second copy of `stop - column %
|
||||||
|
// stop` here is how the rendered column and B7's bound
|
||||||
|
// drift apart.
|
||||||
|
let tab_width = pmacs_protocol::columns::advance_char(
|
||||||
|
u32::try_from(column).unwrap_or(u32::MAX),
|
||||||
|
'\t',
|
||||||
|
) as usize
|
||||||
|
- column;
|
||||||
expanded.push(RichChunk {
|
expanded.push(RichChunk {
|
||||||
text: " ".repeat(tab_width),
|
text: " ".repeat(tab_width),
|
||||||
color,
|
color,
|
||||||
|
|
@ -14144,12 +14150,23 @@ fn offset_chunk_source(source: ChunkSource, byte_offset: u64) -> ChunkSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advance the projection's running display column across `text`.
|
||||||
|
///
|
||||||
|
/// **Per-character advance delegates to
|
||||||
|
/// [`pmacs_protocol::columns::advance_char`]** so the column this
|
||||||
|
/// projection renders at is the same column B7's bound and the caret
|
||||||
|
/// follow reckon in. The stream semantics stay local and are the reason
|
||||||
|
/// this wrapper exists at all: the column runs across chunks, so
|
||||||
|
/// **adornment text shifts a later tab**, and a newline restarts it.
|
||||||
fn advance_display_column(column: &mut usize, text: &str) {
|
fn advance_display_column(column: &mut usize, text: &str) {
|
||||||
for ch in text.chars() {
|
for ch in text.chars() {
|
||||||
if ch == '\n' {
|
if ch == '\n' {
|
||||||
*column = 0;
|
*column = 0;
|
||||||
} else {
|
} else {
|
||||||
*column += UnicodeWidthChar::width(ch).unwrap_or(0);
|
*column = pmacs_protocol::columns::advance_char(
|
||||||
|
u32::try_from(*column).unwrap_or(u32::MAX),
|
||||||
|
ch,
|
||||||
|
) as usize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue