mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 13:21:38 +03:00
track the full current dir URL for OSC 7
Matching against the current dir when it includes a host and a path seems like a handy way to automate selecting appropriate theme/color/profile settings, so I'd like to make sure that we have the full URL content available for that. Refs: https://github.com/wez/wezterm/issues/115 Refs: https://github.com/wez/wezterm/issues/117
This commit is contained in:
parent
18bbd2ac6f
commit
4b455288dd
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2875,6 +2875,7 @@ dependencies = [
|
||||
"unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"varbincode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -56,6 +56,7 @@ toml = "0.4"
|
||||
unicode-normalization = "0.1"
|
||||
unicode-segmentation = "1.5"
|
||||
unicode-width = "0.1"
|
||||
url = "2"
|
||||
varbincode = "0.1"
|
||||
walkdir = "2"
|
||||
window = { path = "window", features=["opengl", "wayland"]}
|
||||
|
@ -1065,6 +1065,10 @@ impl TermWindow {
|
||||
None,
|
||||
),
|
||||
};
|
||||
let cwd = match cwd {
|
||||
Some(url) if url.scheme() == "file" => Some(url.path().to_string()),
|
||||
Some(_) | None => None,
|
||||
};
|
||||
let tab = domain.spawn(size, None, cwd, self.mux_window_id)?;
|
||||
let tab_id = tab.tab_id();
|
||||
|
||||
|
@ -7,6 +7,7 @@ use std::cell::{RefCell, RefMut};
|
||||
use std::sync::Arc;
|
||||
use term::color::ColorPalette;
|
||||
use term::{Clipboard, KeyCode, KeyModifiers, MouseEvent, Terminal, TerminalHost};
|
||||
use url::Url;
|
||||
|
||||
pub struct LocalTab {
|
||||
tab_id: TabId,
|
||||
@ -94,8 +95,8 @@ impl Tab for LocalTab {
|
||||
self.terminal.borrow().is_mouse_grabbed()
|
||||
}
|
||||
|
||||
fn get_current_working_dir(&self) -> Option<String> {
|
||||
self.terminal.borrow().get_current_dir().map(String::from)
|
||||
fn get_current_working_dir(&self) -> Option<Url> {
|
||||
self.terminal.borrow().get_current_dir().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ use std::cell::RefMut;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use term::color::ColorPalette;
|
||||
use term::{Clipboard, KeyCode, KeyModifiers, MouseEvent, TerminalHost};
|
||||
use url::Url;
|
||||
|
||||
static TAB_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(0);
|
||||
pub type TabId = usize;
|
||||
@ -69,7 +70,7 @@ pub trait Tab: Downcast {
|
||||
|
||||
fn set_clipboard(&self, _clipboard: &Arc<dyn Clipboard>) {}
|
||||
|
||||
fn get_current_working_dir(&self) -> Option<String>;
|
||||
fn get_current_working_dir(&self) -> Option<Url>;
|
||||
|
||||
fn trickle_paste(&self, text: String) -> anyhow::Result<()> {
|
||||
if text.len() <= PASTE_CHUNK_SIZE {
|
||||
|
@ -117,6 +117,7 @@ fn client_thread(
|
||||
let mut next_serial = 1u64;
|
||||
let mut promises = HashMap::new();
|
||||
let mut read_buffer = Vec::with_capacity(1024);
|
||||
|
||||
loop {
|
||||
loop {
|
||||
match rx.try_recv() {
|
||||
@ -131,7 +132,13 @@ fn client_thread(
|
||||
}
|
||||
},
|
||||
Err(TryRecvError::Empty) => break,
|
||||
Err(TryRecvError::Disconnected) => bail!("Client was destroyed"),
|
||||
Err(TryRecvError::Disconnected) => {
|
||||
for (_, mut promise) in promises.into_iter() {
|
||||
promise.result(Err(anyhow!("Client was destroyed")));
|
||||
}
|
||||
log::error!("Client Disconnected");
|
||||
bail!("Client was destroyed");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -149,20 +156,33 @@ fn client_thread(
|
||||
reconnectable.stream().set_non_blocking(true)?;
|
||||
let res = Pdu::try_read_and_decode(reconnectable.stream(), &mut read_buffer);
|
||||
reconnectable.stream().set_non_blocking(false)?;
|
||||
if let Some(decoded) = res? {
|
||||
log::trace!("decoded serial {}", decoded.serial);
|
||||
if decoded.serial == 0 {
|
||||
process_unilateral(local_domain_id, decoded)?;
|
||||
} else if let Some(mut promise) = promises.remove(&decoded.serial) {
|
||||
promise.result(Ok(decoded.pdu));
|
||||
} else {
|
||||
log::error!(
|
||||
"got serial {} without a corresponding promise",
|
||||
decoded.serial
|
||||
);
|
||||
match res {
|
||||
Ok(None) => {
|
||||
/* no data available right now; try again later! */
|
||||
break;
|
||||
}
|
||||
Ok(Some(decoded)) => {
|
||||
log::trace!("decoded serial {}", decoded.serial);
|
||||
if decoded.serial == 0 {
|
||||
process_unilateral(local_domain_id, decoded)?;
|
||||
} else if let Some(mut promise) = promises.remove(&decoded.serial) {
|
||||
promise.result(Ok(decoded.pdu));
|
||||
} else {
|
||||
log::error!(
|
||||
"got serial {} without a corresponding promise",
|
||||
decoded.serial
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
let reason = format!("Error while decoding response pdu: {}", err);
|
||||
log::error!("{}", reason);
|
||||
for (_, mut promise) in promises.into_iter() {
|
||||
promise.result(Err(anyhow!("{}", reason)));
|
||||
}
|
||||
bail!(reason);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -547,6 +567,9 @@ impl Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::error!("client_thread returned without any error condition");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Future::with_executor(executor(), move || {
|
||||
|
@ -27,6 +27,7 @@ use std::sync::Arc;
|
||||
use term::StableRowIndex;
|
||||
use termwiz::hyperlink::Hyperlink;
|
||||
use termwiz::surface::Line;
|
||||
use url::Url;
|
||||
use varbincode;
|
||||
|
||||
/// Returns the encoded length of the leb128 representation of value
|
||||
@ -237,7 +238,7 @@ macro_rules! pdu {
|
||||
/// The overall version of the codec.
|
||||
/// This must be bumped when backwards incompatible changes
|
||||
/// are made to the types and protocol.
|
||||
pub const CODEC_VERSION: usize = 1;
|
||||
pub const CODEC_VERSION: usize = 2;
|
||||
|
||||
// Defines the Pdu enum.
|
||||
// Each struct has an explicit identifying number.
|
||||
@ -365,13 +366,45 @@ pub struct Pong {}
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct ListTabs {}
|
||||
|
||||
#[derive(Deserialize, Clone, Serialize, PartialEq, Debug)]
|
||||
#[serde(try_from = "String", into = "String")]
|
||||
pub struct SerdeUrl {
|
||||
pub url: Url,
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<String> for SerdeUrl {
|
||||
type Error = url::ParseError;
|
||||
fn try_from(s: String) -> Result<SerdeUrl, url::ParseError> {
|
||||
let url = Url::parse(&s)?;
|
||||
Ok(SerdeUrl { url })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Url> for SerdeUrl {
|
||||
fn from(url: Url) -> SerdeUrl {
|
||||
SerdeUrl { url }
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Url> for SerdeUrl {
|
||||
fn into(self) -> Url {
|
||||
self.url
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for SerdeUrl {
|
||||
fn into(self) -> String {
|
||||
self.url.as_str().into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct WindowAndTabEntry {
|
||||
pub window_id: WindowId,
|
||||
pub tab_id: TabId,
|
||||
pub title: String,
|
||||
pub size: PtySize,
|
||||
pub working_dir: Option<String>,
|
||||
pub working_dir: Option<SerdeUrl>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
@ -444,7 +477,7 @@ pub struct GetTabRenderChangesResponse {
|
||||
pub dimensions: RenderableDimensions,
|
||||
pub dirty_lines: Vec<Range<StableRowIndex>>,
|
||||
pub title: String,
|
||||
pub working_dir: Option<String>,
|
||||
pub working_dir: Option<SerdeUrl>,
|
||||
/// Lines that the server thought we'd almost certainly
|
||||
/// want to fetch as soon as we received this response
|
||||
pub bonus_lines: SerializedLines,
|
||||
|
@ -28,6 +28,7 @@ use std::thread;
|
||||
use std::time::Instant;
|
||||
use term::terminal::Clipboard;
|
||||
use term::StableRowIndex;
|
||||
use url::Url;
|
||||
|
||||
struct LocalListener {
|
||||
listener: UnixListener,
|
||||
@ -386,7 +387,7 @@ pub struct ClientSession<S: ReadAndWrite> {
|
||||
struct PerTab {
|
||||
cursor_position: StableCursorPosition,
|
||||
title: String,
|
||||
working_dir: Option<String>,
|
||||
working_dir: Option<Url>,
|
||||
dimensions: RenderableDimensions,
|
||||
dirty_lines: RangeSet<StableRowIndex>,
|
||||
mouse_grabbed: bool,
|
||||
@ -471,7 +472,7 @@ impl PerTab {
|
||||
cursor_position,
|
||||
title,
|
||||
bonus_lines,
|
||||
working_dir,
|
||||
working_dir: working_dir.map(Into::into),
|
||||
})
|
||||
}
|
||||
|
||||
@ -620,17 +621,20 @@ impl<S: ReadAndWrite> ClientSession<S> {
|
||||
self.stream.set_non_blocking(true)?;
|
||||
let res = Pdu::try_read_and_decode(&mut self.stream, &mut read_buffer);
|
||||
self.stream.set_non_blocking(false)?;
|
||||
if let Some(decoded) = res? {
|
||||
self.process_one(decoded)?;
|
||||
} else {
|
||||
break;
|
||||
match res {
|
||||
Ok(Some(decoded)) => self.process_one(decoded),
|
||||
Ok(None) => break,
|
||||
Err(err) => {
|
||||
log::error!("Error decoding: {}", err);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_one(&mut self, decoded: DecodedPdu) -> anyhow::Result<()> {
|
||||
fn process_one(&mut self, decoded: DecodedPdu) {
|
||||
let start = Instant::now();
|
||||
let sender = self.to_write_tx.clone();
|
||||
let serial = decoded.serial;
|
||||
@ -644,7 +648,6 @@ impl<S: ReadAndWrite> ClientSession<S> {
|
||||
log::trace!("{} processing time {:?}", serial, start.elapsed());
|
||||
sender.send(DecodedPdu { pdu, serial })
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_pdu(&mut self, pdu: Pdu) -> Future<Pdu> {
|
||||
@ -668,7 +671,7 @@ impl<S: ReadAndWrite> ClientSession<S> {
|
||||
pixel_height: 0,
|
||||
pixel_width: 0,
|
||||
},
|
||||
working_dir,
|
||||
working_dir: working_dir.map(Into::into),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ use term::{
|
||||
StableRowIndex, TerminalHost,
|
||||
};
|
||||
use termwiz::input::KeyEvent;
|
||||
use url::Url;
|
||||
|
||||
struct MouseState {
|
||||
future: Option<Future<()>>,
|
||||
@ -319,7 +320,7 @@ impl Tab for ClientTab {
|
||||
*self.mouse_grabbed.borrow()
|
||||
}
|
||||
|
||||
fn get_current_working_dir(&self) -> Option<String> {
|
||||
fn get_current_working_dir(&self) -> Option<Url> {
|
||||
self.renderable.borrow().inner.borrow().working_dir.clone()
|
||||
}
|
||||
}
|
||||
@ -367,7 +368,7 @@ struct RenderableInner {
|
||||
|
||||
lines: LruCache<StableRowIndex, LineEntry>,
|
||||
title: String,
|
||||
working_dir: Option<String>,
|
||||
working_dir: Option<Url>,
|
||||
|
||||
fetch_limiter: RateLimiter,
|
||||
}
|
||||
@ -397,7 +398,7 @@ impl RenderableInner {
|
||||
self.cursor_position = delta.cursor_position;
|
||||
self.dimensions = delta.dimensions;
|
||||
self.title = delta.title;
|
||||
self.working_dir = delta.working_dir;
|
||||
self.working_dir = delta.working_dir.map(Into::into);
|
||||
|
||||
let config = configuration();
|
||||
for (stable_row, line) in delta.bonus_lines.lines() {
|
||||
|
@ -31,6 +31,7 @@ use termwiz::input::{InputEvent, KeyEvent};
|
||||
use termwiz::lineedit::*;
|
||||
use termwiz::surface::{Change, SequenceNo, Surface};
|
||||
use termwiz::terminal::{ScreenSize, Terminal, TerminalWaker};
|
||||
use url::Url;
|
||||
|
||||
struct RenderableInner {
|
||||
surface: Surface,
|
||||
@ -286,7 +287,7 @@ impl Tab for TermWizTerminalTab {
|
||||
true
|
||||
}
|
||||
|
||||
fn get_current_working_dir(&self) -> Option<String> {
|
||||
fn get_current_working_dir(&self) -> Option<Url> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -219,7 +219,7 @@ pub struct TerminalState {
|
||||
|
||||
clipboard: Option<Arc<dyn Clipboard>>,
|
||||
|
||||
current_dir: Option<String>,
|
||||
current_dir: Option<Url>,
|
||||
}
|
||||
|
||||
fn encode_modifiers(mods: KeyModifiers) -> u8 {
|
||||
@ -300,8 +300,8 @@ impl TerminalState {
|
||||
&self.title
|
||||
}
|
||||
|
||||
pub fn get_current_dir(&self) -> Option<&str> {
|
||||
self.current_dir.as_ref().map(String::as_str)
|
||||
pub fn get_current_dir(&self) -> Option<&Url> {
|
||||
self.current_dir.as_ref()
|
||||
}
|
||||
|
||||
/// Returns a copy of the palette.
|
||||
@ -1724,11 +1724,7 @@ impl<'a> Performer<'a> {
|
||||
error!("Application sends SystemNotification: {}", message);
|
||||
}
|
||||
OperatingSystemCommand::CurrentWorkingDirectory(url) => {
|
||||
let dir = match Url::parse(&url) {
|
||||
Ok(url) if url.scheme() == "file" => Some(url.path().to_string()),
|
||||
Ok(_) | Err(_) => None,
|
||||
};
|
||||
self.current_dir = dir;
|
||||
self.current_dir = Url::parse(&url).ok();
|
||||
}
|
||||
OperatingSystemCommand::ChangeColorNumber(specs) => {
|
||||
error!("ChangeColorNumber: {:?}", specs);
|
||||
|
Loading…
Reference in New Issue
Block a user