Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/ui/app_state/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,23 @@ pub fn calc_grid(
(cols.max(10), rows.max(5))
}

pub fn calc_grid_split(
total_width: f32,
height: f32,
font_size: f32,
status_bar_visible: bool,
banner_visible: bool,
) -> (usize, usize) {
let char_width = font_size * 0.62;
let char_height = font_size * 1.29;
let banner_extra = if banner_visible { font_size * 2.5 } else { 0.0 };
let padding_y = if status_bar_visible { 118.0 } else { 96.0 } + banner_extra;
let pane_content_width = ((total_width - 41.0) / 2.0).max(0.0);
let cols = (pane_content_width / char_width).floor() as usize;
let rows = ((height - padding_y) / char_height).max(0.0).floor() as usize;
(cols.max(10), rows.max(5))
}
Comment thread
pmqueiroz marked this conversation as resolved.

pub fn pixel_to_cell(pos: Point, font_size: f32) -> Option<(usize, usize)> {
const X_ORIGIN: f32 = 20.0;
const Y_ORIGIN: f32 = 88.0;
Expand Down
3 changes: 3 additions & 0 deletions src/ui/app_state/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,8 @@ pub enum Message {
DiagnosticBannerResponse(Result<String, String>),
DiagnosticBannerCommand(String),
SettingsDiagnosticBannerToggled(bool),
SplitPane,
CloseSplitPane,
CloseLeftPane,
NoOp,
}
69 changes: 51 additions & 18 deletions src/ui/app_state/subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ fn handle_key_pressed(
};
}

#[cfg(target_os = "macos")]
let split_mod = modifiers.logo() && modifiers.shift();
#[cfg(not(target_os = "macos"))]
let split_mod = modifiers.control() && modifiers.shift();

if split_mod && let Key::Character(c) = &key {
match c.as_str() {
"t" | "T" => return Some(Message::SplitPane),
"w" | "W" => return Some(Message::CloseSplitPane),
_ => {}
}
}

let kb = config::keybindings();
if matches_kb(&kb.prev_tab, &key, modifiers) {
return Some(Message::PrevTab);
Expand Down Expand Up @@ -268,25 +281,45 @@ impl Nova {
}));

for tab in &self.tabs {
if !tab.pty_alive {
continue;
if tab.pty_alive {
let key = PtyKey {
tab_id: tab.id,
shell_cmd: tab.shell_cmd.clone(),
initial_cols: tab.grid.cols as u16,
initial_rows: tab.grid.rows as u16,
initial_cwd: tab.initial_cwd.clone(),
};
subs.push(Subscription::run_with(key, |k| {
pty_worker(
k.tab_id,
k.initial_cols,
k.initial_rows,
k.shell_cmd.clone(),
k.initial_cwd.clone(),
)
}));
}

if let Some(split) = &tab.split
&& split.pty_alive
{
let split_key = PtyKey {
tab_id: split.id,
shell_cmd: split.shell_cmd.clone(),
initial_cols: split.grid.cols as u16,
initial_rows: split.grid.rows as u16,
initial_cwd: split.initial_cwd.clone(),
};
subs.push(Subscription::run_with(split_key, |k| {
pty_worker(
k.tab_id,
k.initial_cols,
k.initial_rows,
k.shell_cmd.clone(),
k.initial_cwd.clone(),
)
}));
}
let key = PtyKey {
tab_id: tab.id,
shell_cmd: tab.shell_cmd.clone(),
initial_cols: tab.grid.cols as u16,
initial_rows: tab.grid.rows as u16,
initial_cwd: tab.initial_cwd.clone(),
};
subs.push(Subscription::run_with(key, |k| {
pty_worker(
k.tab_id,
k.initial_cols,
k.initial_rows,
k.shell_cmd.clone(),
k.initial_cwd.clone(),
)
}));
}

Subscription::batch(subs)
Expand Down
80 changes: 79 additions & 1 deletion src/ui/app_state/update/input.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#[cfg(target_os = "windows")]
use std::io::Write;
use std::sync::atomic::Ordering;

Expand All @@ -16,6 +17,39 @@ impl Nova {
return iced::Task::none();
}

let Some(active_tab) = self.tabs.get(self.active_index) else {
return iced::Task::none();
};
let active_pane_is_split = active_tab.active_pane_is_split;

if active_pane_is_split {
self.selection_start = None;
self.selection_end = None;
self.click_count = 0;
self.diagnostic_banner = None;
self.ai_pending_diagnostic = None;
if let Some(active_tab) = self.tabs.get_mut(self.active_index)
&& let Some(split) = &mut active_tab.split
{
if bytes == b"\t"
&& let Some(suggestion) = split.grid.suggestion.take()
&& !suggestion.is_empty()
{
if let Some(tx) = &split.pty_tx {
let _ = tx.send_blocking(PtyCommand::Input(suggestion.into_bytes()));
}
split.grid.input_start_col = None;
split.grid.input_start_row = None;
return iced::Task::none();
}
split.scroll_offset = 0;
if let Some(tx) = &split.pty_tx {
let _ = tx.send_blocking(PtyCommand::Input(bytes));
}
}
return iced::Task::none();
}
Comment thread
pmqueiroz marked this conversation as resolved.

if bytes == b"\t"
&& let Some(active_tab) = self.tabs.get_mut(self.active_index)
&& let Some(suggestion) = active_tab.grid.suggestion.take()
Expand Down Expand Up @@ -85,12 +119,56 @@ impl Nova {
}

pub(super) fn handle_pty_output(&mut self, tab_id: usize, bytes: Vec<u8>) -> iced::Task<Message> {
if let Some(tab_idx) = self
.tabs
.iter()
.position(|t| t.split.as_ref().map(|s| s.id) == Some(tab_id))
{
let tab = &mut self.tabs[tab_idx];
let split = tab.split.as_mut().unwrap();
#[cfg(target_os = "windows")]
if std::env::var("NOVA_DEBUG_PTY").is_ok()
&& let Ok(mut f) = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(std::env::temp_dir().join("nova_pty_debug.bin"))
{
let _ = f.write_all(&bytes);
}
Comment thread
pmqueiroz marked this conversation as resolved.
let mut executor = AnsiExecutor {
grid: &mut split.grid,
bell_pending: false,
};
for byte in bytes {
split.ansi_parser.advance(&mut executor, &[byte]);
}
while !split.grid.output_queue.is_empty() {
let response = split.grid.output_queue.remove(0);
if let Some(tx) = &split.pty_tx {
let _ = tx.send_blocking(PtyCommand::Input(response));
}
}
split.grid.control_queue.clear();
let new_pwd = split.grid.pwd.clone();
if new_pwd != split.pwd {
split.pwd = new_pwd;
}
split.scroll_offset = 0;
if let Some(partial) = split.grid.extract_current_input() {
split.grid.suggestion = split.grid.find_best_suggestion(&partial);
} else {
split.grid.suggestion = None;
}
return iced::Task::none();
}
Comment thread
pmqueiroz marked this conversation as resolved.

if let Some(tab) = self.tabs.iter_mut().find(|t| t.id == tab_id) {
#[cfg(target_os = "windows")]
if std::env::var("NOVA_DEBUG_PTY").is_ok()
&& let Ok(mut f) = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open("C:\\Users\\Public\\nova_pty_debug.bin")
.open(std::env::temp_dir().join("nova_pty_debug.bin"))
{
let _ = f.write_all(&bytes);
}
Expand Down
43 changes: 42 additions & 1 deletion src/ui/app_state/update/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,34 @@ impl Nova {
self.active_index = self.tabs.len() - 1;
iced::Task::none()
}
Message::SplitPane => self.handle_split_pane(),
Message::CloseSplitPane => {
self.handle_close_split_pane();
iced::Task::none()
}
Message::CloseLeftPane => {
self.handle_close_left_pane();
iced::Task::none()
}
Message::SwitchTab(index) => {
if index < self.tabs.len() {
self.active_index = index;
}
iced::Task::none()
}
Message::CloseActiveTab => self.update(Message::CloseTab(self.active_index)),
Message::CloseActiveTab => {
if let Some(tab) = self.tabs.get(self.active_index)
&& tab.split.is_some()
{
let msg = if tab.active_pane_is_split {
Message::CloseSplitPane
} else {
Message::CloseLeftPane
};
return self.update(msg);
}
self.update(Message::CloseTab(self.active_index))
}
Message::NextTab => {
if !self.tabs.is_empty() {
self.active_index = (self.active_index + 1) % self.tabs.len();
Expand All @@ -201,13 +222,33 @@ impl Nova {
}
tab.pty_tx = Some(tx);
tab.pty_alive = true;
return iced::Task::none();
}
for tab in &mut self.tabs {
if let Some(split) = &mut tab.split
&& split.id == tab_id
{
split.pty_tx = Some(tx);
split.pty_alive = true;
return iced::Task::none();
}
}
iced::Task::none()
}
Message::PtyExited(tab_id) => {
if let Some(tab) = self.tabs.iter_mut().find(|t| t.id == tab_id) {
tab.pty_alive = false;
tab.pty_tx = None;
return iced::Task::none();
}
for tab in &mut self.tabs {
if let Some(split) = &mut tab.split
&& split.id == tab_id
{
split.pty_alive = false;
split.pty_tx = None;
return iced::Task::none();
}
}
iced::Task::none()
}
Expand Down
17 changes: 17 additions & 0 deletions src/ui/app_state/update/mouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ impl Nova {
}

if button == mouse::Button::Left {
if let Some(tab) = self.tabs.get_mut(self.active_index)
&& tab.split.is_some()
{
tab.active_pane_is_split = self.cursor_position.x > self.window_size.width / 2.0;
}
Comment thread
pmqueiroz marked this conversation as resolved.

let now = std::time::Instant::now();
let threshold = std::time::Duration::from_millis(500);
if cell.is_some()
Expand Down Expand Up @@ -233,6 +239,17 @@ impl Nova {
}

let rows = (delta.abs() * 3.0).round() as usize;
if tab.active_pane_is_split {
if let Some(split) = &mut tab.split {
if delta > 0.0 {
let new_offset = split.scroll_offset.saturating_add(rows);
split.scroll_offset = new_offset.min(split.grid.scrollback.len());
} else {
split.scroll_offset = split.scroll_offset.saturating_sub(rows);
}
}
return;
}
let old_offset = tab.scroll_offset;
if delta > 0.0 {
let new_offset = tab.scroll_offset.saturating_add(rows);
Expand Down
Loading