feat(ux): initial layout engine (#50)

* prototype

* refactor(layout): move stuff around

* style(format): make rustfmt happy
This commit is contained in:
Aram Drevekenin 2020-11-15 19:55:56 +01:00 committed by GitHub
parent a2914066bf
commit 47954166a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 424 additions and 5 deletions

1
Cargo.lock generated
View File

@ -457,6 +457,7 @@ dependencies = [
"libc",
"nix",
"serde",
"serde_yaml",
"signal-hook",
"structopt",
"termios",

View File

@ -18,6 +18,7 @@ futures = "0.3.5"
serde = { version = "1.0", features = ["derive"] }
bincode = "1.3.1"
structopt = "0.3"
serde_yaml = "0.8"
[dependencies.async-std]
version = "1.3.0"

134
src/layout.rs Normal file
View File

@ -0,0 +1,134 @@
use crate::terminal_pane::PositionAndSize;
use serde::{Deserialize, Serialize};
fn _debug_log_to_file(message: String) {
use std::fs::OpenOptions;
use std::io::prelude::*;
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("/tmp/mosaic-log.txt")
.unwrap();
file.write_all(message.as_bytes()).unwrap();
file.write_all("\n".as_bytes()).unwrap();
}
fn split_space_to_parts_vertically(
space_to_split: &PositionAndSize,
percentages: Vec<u8>,
) -> Vec<PositionAndSize> {
let mut split_parts = vec![];
let mut current_x_position = space_to_split.x;
let width = space_to_split.columns - (percentages.len() - 1); // minus space for gaps
for percentage in percentages.iter() {
let columns = (width as f32 * (*percentage as f32 / 100.0)) as usize; // TODO: round properly
split_parts.push(PositionAndSize {
x: current_x_position,
y: space_to_split.y,
columns,
rows: space_to_split.rows,
});
current_x_position += columns + 1; // 1 for gap
}
let total_width = split_parts
.iter()
.fold(0, |total_width, part| total_width + part.columns);
if total_width < width {
// we have some extra space left, let's add it to the last part
let last_part_index = split_parts.len() - 1;
let mut last_part = split_parts.get_mut(last_part_index).unwrap();
last_part.columns += width - total_width;
}
split_parts
}
fn split_space_to_parts_horizontally(
space_to_split: &PositionAndSize,
percentages: Vec<u8>,
) -> Vec<PositionAndSize> {
let mut split_parts = vec![];
let mut current_y_position = space_to_split.y;
let height = space_to_split.rows - (percentages.len() - 1); // minus space for gaps
for percentage in percentages.iter() {
let rows = (height as f32 * (*percentage as f32 / 100.0)) as usize; // TODO: round properly
split_parts.push(PositionAndSize {
x: space_to_split.x,
y: current_y_position,
columns: space_to_split.columns,
rows,
});
current_y_position += rows + 1; // 1 for gap
}
let total_height = split_parts
.iter()
.fold(0, |total_height, part| total_height + part.rows);
if total_height < height {
// we have some extra space left, let's add it to the last part
let last_part_index = split_parts.len() - 1;
let mut last_part = split_parts.get_mut(last_part_index).unwrap();
last_part.rows += height - total_height;
}
split_parts
}
fn split_space(space_to_split: &PositionAndSize, layout: &Layout) -> Vec<PositionAndSize> {
let mut pane_positions: Vec<PositionAndSize> = vec![];
let percentages: Vec<u8> = layout
.parts
.iter()
.map(|part| {
let split_size = part.split_size.as_ref().unwrap(); // TODO: if there is no split size, it should get the remaining "free space"
match split_size {
SplitSize::Percent(percent) => *percent,
}
})
.collect();
let split_parts = match layout.direction {
Direction::Vertical => split_space_to_parts_vertically(space_to_split, percentages),
Direction::Horizontal => split_space_to_parts_horizontally(space_to_split, percentages),
};
for (i, part) in layout.parts.iter().enumerate() {
let part_position_and_size = split_parts.get(i).unwrap();
if part.parts.len() > 0 {
let mut part_positions = split_space(&part_position_and_size, part);
pane_positions.append(&mut part_positions);
} else {
pane_positions.push(*part_position_and_size);
}
}
pane_positions
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Direction {
Horizontal,
Vertical,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum SplitSize {
Percent(u8), // 1 to 100
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Layout {
pub direction: Direction,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub parts: Vec<Layout>,
#[serde(skip_serializing_if = "Option::is_none")]
pub split_size: Option<SplitSize>,
}
impl Layout {
pub fn total_panes(&self) -> usize {
let mut total_panes = 0;
total_panes += self.parts.len();
for part in self.parts.iter() {
total_panes += part.total_panes();
}
total_panes
}
pub fn position_panes_in_space(&self, space: &PositionAndSize) -> Vec<PositionAndSize> {
split_space(space, &self)
}
}

View File

@ -2,6 +2,7 @@
mod tests;
mod boundaries;
mod layout;
mod os_input_output;
mod pty_bus;
mod screen;
@ -14,8 +15,10 @@ use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use serde::{Deserialize, Serialize};
use serde_yaml;
use structopt::StructOpt;
use crate::layout::Layout;
use crate::os_input_output::{get_os_input, OsApi};
use crate::pty_bus::{PtyBus, PtyInstruction, VteEvent};
use crate::screen::{Screen, ScreenInstruction};
@ -43,7 +46,9 @@ pub struct Opt {
#[structopt(long)]
/// Maximum panes on screen, caution: opening more panes will close old ones
max_panes: Option<usize>,
#[structopt(short, long)]
/// Path to a layout yaml file
layout: Option<PathBuf>,
#[structopt(short, long)]
debug: bool,
}
@ -140,7 +145,26 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
.name("pty".to_string())
.spawn({
move || {
pty_bus.spawn_terminal_vertically(None);
match opts.layout {
Some(layout_path) => {
use std::fs::File;
let mut layout_file = File::open(&layout_path)
.expect(&format!("cannot find layout {}", layout_path.display()));
let mut layout = String::new();
layout_file.read_to_string(&mut layout).expect(&format!(
"could not read layout {}",
layout_path.display()
));
let layout: Layout = serde_yaml::from_str(&layout).expect(&format!(
"could not parse layout {}",
layout_path.display()
));
pty_bus.spawn_terminals_for_layout(layout);
}
None => {
pty_bus.spawn_terminal_vertically(None);
}
}
loop {
let event = pty_bus
.receive_pty_instructions
@ -230,6 +254,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
ScreenInstruction::ToggleActiveTerminalFullscreen => {
screen.toggle_active_terminal_fullscreen();
}
ScreenInstruction::ApplyLayout((layout, new_pane_pids)) => {
screen.apply_layout(layout, new_pane_pids)
}
ScreenInstruction::Quit => {
break;
}

View File

@ -9,6 +9,7 @@ use ::std::time::{Duration, Instant};
use ::vte;
use std::path::PathBuf;
use crate::layout::Layout;
use crate::os_input_output::OsApi;
use crate::ScreenInstruction;
@ -317,6 +318,29 @@ impl PtyBus {
.send(ScreenInstruction::HorizontalSplit(pid_primary))
.unwrap();
}
pub fn spawn_terminals_for_layout(&mut self, layout: Layout) {
let total_panes = layout.total_panes();
let mut new_pane_pids = vec![];
for _ in 0..total_panes {
let (pid_primary, pid_secondary): (RawFd, RawFd) = self.os_input.spawn_terminal(None);
self.id_to_child_pid.insert(pid_primary, pid_secondary);
new_pane_pids.push(pid_primary);
}
&self
.send_screen_instructions
.send(ScreenInstruction::ApplyLayout((
layout,
new_pane_pids.clone(),
)));
for id in new_pane_pids {
stream_terminal_bytes(
id,
self.send_screen_instructions.clone(),
self.os_input.clone(),
self.debug_to_file,
);
}
}
pub fn close_pane(&mut self, id: RawFd) {
let child_pid = self.id_to_child_pid.get(&id).unwrap();
self.os_input.kill(*child_pid).unwrap();

View File

@ -5,9 +5,10 @@ use std::os::unix::io::RawFd;
use std::sync::mpsc::{Receiver, Sender};
use crate::boundaries::Boundaries;
use crate::layout::Layout;
use crate::os_input_output::OsApi;
use crate::pty_bus::{PtyInstruction, VteEvent};
use crate::terminal_pane::TerminalPane;
use crate::terminal_pane::{PositionAndSize, TerminalPane};
use crate::AppInstruction;
/*
@ -80,6 +81,7 @@ pub enum ScreenInstruction {
CloseFocusedPane,
ToggleActiveTerminalFullscreen,
ClosePane(RawFd),
ApplyLayout((Layout, Vec<RawFd>)),
}
pub struct Screen {
@ -115,6 +117,64 @@ impl Screen {
os_api,
}
}
pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>) {
self.panes_to_hide.clear();
// TODO: this should be an attribute on Screen instead of full_screen_ws
let free_space = PositionAndSize {
x: 0,
y: 0,
rows: self.full_screen_ws.ws_row as usize,
columns: self.full_screen_ws.ws_col as usize,
};
let positions_in_layout = layout.position_panes_in_space(&free_space);
let mut positions_and_size = positions_in_layout.iter();
for (pid, terminal_pane) in self.terminals.iter_mut() {
match positions_and_size.next() {
Some(position_and_size) => {
terminal_pane.reset_size_and_position_override();
terminal_pane.change_size_p(&position_and_size);
self.os_api.set_terminal_size_using_fd(
*pid,
position_and_size.columns as u16,
position_and_size.rows as u16,
);
}
None => {
// we filled the entire layout, no room for this pane
// TODO: handle active terminal
self.panes_to_hide.insert(*pid);
}
}
}
let mut new_pids = new_pids.iter();
for position_and_size in positions_and_size {
// there are still panes left to fill, use the pids we received in this method
let pid = new_pids.next().unwrap(); // if this crashes it means we got less pids than there are panes in this layout
let mut new_terminal = TerminalPane::new(
*pid,
self.full_screen_ws.clone(),
position_and_size.x,
position_and_size.y,
);
new_terminal.change_size_p(position_and_size);
self.os_api.set_terminal_size_using_fd(
new_terminal.pid,
new_terminal.get_columns() as u16,
new_terminal.get_rows() as u16,
);
self.terminals.insert(*pid, new_terminal);
}
for unused_pid in new_pids {
// this is a bit of a hack and happens because we don't have any central location that
// can query the screen as to how many panes it needs to create a layout
// fixing this will require a bit of an architecture change
self.send_pty_instructions
.send(PtyInstruction::ClosePane(*unused_pid))
.unwrap();
}
self.active_terminal = Some(*self.terminals.iter().next().unwrap().0);
self.render();
}
pub fn new_pane(&mut self, pid: RawFd) {
self.close_down_to_max_terminals();
if self.terminals.is_empty() {

View File

@ -17,6 +17,7 @@ pub struct PositionAndSize {
pub columns: usize,
}
#[derive(Debug)]
pub struct TerminalPane {
pub pid: RawFd,
pub scroll: Scroll,
@ -134,6 +135,12 @@ impl TerminalPane {
self.reflow_lines();
self.should_render = true;
}
pub fn change_size_p(&mut self, position_and_size: &PositionAndSize) {
self.position_and_size = *position_and_size;
self.reflow_lines();
self.should_render = true;
}
// TODO: merge these two methods
pub fn change_size(&mut self, ws: &Winsize) {
self.position_and_size.columns = ws.ws_col as usize;
self.position_and_size.rows = ws.ws_row as usize;

View File

@ -143,7 +143,7 @@ impl OsApi for FakeInputOutput {
.push(IoEvent::UnsetRawMode(pid));
}
fn spawn_terminal(&mut self, _file_to_open: Option<PathBuf>) -> (RawFd, RawFd) {
let next_terminal_id = { self.read_buffers.lock().unwrap().keys().len() as RawFd + 1 };
let next_terminal_id = self.stdin_writes.lock().unwrap().keys().len() as RawFd + 1;
self.add_terminal(next_terminal_id);
(next_terminal_id as i32, next_terminal_id + 1000) // secondary number is arbitrary here
}

View File

@ -0,0 +1,16 @@
---
direction: Horizontal
parts:
- direction: Vertical
parts:
- direction: Horizontal
split_size:
Percent: 20
- direction: Horizontal
split_size:
Percent: 80
split_size:
Percent: 80
- direction: Vertical
split_size:
Percent: 20

View File

@ -0,0 +1,51 @@
use ::insta::assert_snapshot;
use ::nix::pty::Winsize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::utils::commands::QUIT;
use crate::tests::utils::get_output_frame_snapshots;
use crate::{start, Opt};
fn get_fake_os_input(fake_win_size: &Winsize) -> FakeInputOutput {
FakeInputOutput::new(fake_win_size.clone())
}
#[test]
pub fn accepts_basic_layout() {
let fake_win_size = Winsize {
ws_col: 121,
ws_row: 20,
ws_xpixel: 0,
ws_ypixel: 0,
};
let mut fake_input_output = get_fake_os_input(&fake_win_size);
fake_input_output.add_terminal_input(&[QUIT]);
use std::path::PathBuf;
let mut opts = Opt::default();
opts.layout = Some(PathBuf::from(
"src/tests/fixtures/layouts/three-panes-with-nesting.yaml",
));
start(Box::new(fake_input_output.clone()), opts);
let output_frames = fake_input_output
.stdout_writer
.output_frames
.lock()
.unwrap();
let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size);
let snapshot_count = snapshots.len();
let first_snapshot = snapshots.get(0).unwrap();
let next_to_last_snapshot = snapshots.get(snapshot_count - 2).unwrap();
let last_snapshot = snapshots.last().unwrap();
// here we only test the first, next to last and last snapshot because there's a race condition
// with the other snapshots. Namely all the terminals are created asynchronously and read in an
// async task, so we have no way to guarantee the order in which their bytes will be read, and
// it doesn't really matter in this context. We just want to see that the layout is initially
// created properly and that in the end it's populated properly with its content
//
// we read the next to last as well as the last, because the last includes the "Bye from
// Mosaic" message, and we also want to make sure things are fine before that
assert_snapshot!(first_snapshot);
assert_snapshot!(next_to_last_snapshot);
assert_snapshot!(last_snapshot);
}

View File

@ -1,6 +1,7 @@
pub mod basic;
pub mod close_pane;
pub mod compatibility;
pub mod layouts;
pub mod resize_down;
pub mod resize_left;
pub mod resize_right;

View File

@ -0,0 +1,24 @@
---
source: src/tests/integration/layouts.rs
expression: next_to_last_snapshot
---
line6-bbbbbbbbbbbbbbbbbb│line6-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line7-bbbbbbbbbbbbbbbbbb│line7-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line8-bbbbbbbbbbbbbbbbbb│line8-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line9-bbbbbbbbbbbbbbbbbb│line9-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line10-bbbbbbbbbbbbbbbbb│line10-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line11-bbbbbbbbbbbbbbbbb│line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line12-bbbbbbbbbbbbbbbbb│line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line13-bbbbbbbbbbbbbbbbb│line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line14-bbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line15-bbbbbbbbbbbbbbbbb│line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line16-bbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line17-bbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line18-bbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line19-bbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
prompt $ █ │prompt $
────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────
line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $

View File

@ -0,0 +1,24 @@
---
source: src/tests/integration/layouts.rs
expression: last_snapshot
---
line7-bbbbbbbbbbbbbbbbbb│line7-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line8-bbbbbbbbbbbbbbbbbb│line8-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line9-bbbbbbbbbbbbbbbbbb│line9-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line10-bbbbbbbbbbbbbbbbb│line10-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line11-bbbbbbbbbbbbbbbbb│line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line12-bbbbbbbbbbbbbbbbb│line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line13-bbbbbbbbbbbbbbbbb│line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line14-bbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line15-bbbbbbbbbbbbbbbbb│line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line16-bbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line17-bbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line18-bbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
line19-bbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
prompt $ │prompt $
────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────
line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $
Bye from Mosaic!█

View File

@ -0,0 +1,24 @@
---
source: src/tests/integration/layouts.rs
expression: first_snapshot
---
█ │
────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────

View File

@ -1,6 +1,6 @@
use crate::tests::tty_inputs::{
COL_10, COL_121, COL_14, COL_15, COL_19, COL_20, COL_24, COL_29, COL_30, COL_34, COL_39,
COL_40, COL_50, COL_60, COL_70, COL_90,
COL_40, COL_50, COL_60, COL_70, COL_90, COL_96,
};
use std::collections::HashMap;
use std::fs;
@ -65,6 +65,7 @@ pub fn get_possible_tty_inputs() -> HashMap<u16, Bytes> {
let col_60_bytes = Bytes::new().content_from_str(&COL_60);
let col_70_bytes = Bytes::new().content_from_str(&COL_70);
let col_90_bytes = Bytes::new().content_from_str(&COL_90);
let col_96_bytes = Bytes::new().content_from_str(&COL_96);
let col_121_bytes = Bytes::new().content_from_str(&COL_121);
possible_inputs.insert(10, col_10_bytes);
possible_inputs.insert(14, col_14_bytes);
@ -81,6 +82,7 @@ pub fn get_possible_tty_inputs() -> HashMap<u16, Bytes> {
possible_inputs.insert(60, col_60_bytes);
possible_inputs.insert(70, col_70_bytes);
possible_inputs.insert(90, col_90_bytes);
possible_inputs.insert(96, col_96_bytes);
possible_inputs.insert(121, col_121_bytes);
possible_inputs
}

View File

@ -364,3 +364,26 @@ pub const COL_90: [&str; 20] = [
"line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"prompt $ ",
];
pub const COL_96: [&str; 20] = [
"line1-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line2-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line3-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line4-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line5-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line6-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line7-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line8-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line9-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line10-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n",
"prompt $ ",
];