Merge pull request #1524 from zed-industries/ime-finalize
IME Changes: Tracking PR
This commit is contained in:
commit
0fef72ac5f
10 changed files with 267 additions and 208 deletions
|
@ -420,6 +420,7 @@
|
||||||
"enter": "terminal::Enter",
|
"enter": "terminal::Enter",
|
||||||
"ctrl-c": "terminal::CtrlC",
|
"ctrl-c": "terminal::CtrlC",
|
||||||
// Useful terminal actions:
|
// Useful terminal actions:
|
||||||
|
"ctrl-cmd-space": "terminal::ShowCharacterPalette",
|
||||||
"cmd-c": "terminal::Copy",
|
"cmd-c": "terminal::Copy",
|
||||||
"cmd-v": "terminal::Paste",
|
"cmd-v": "terminal::Paste",
|
||||||
"cmd-k": "terminal::Clear"
|
"cmd-k": "terminal::Clear"
|
||||||
|
@ -428,6 +429,7 @@
|
||||||
{
|
{
|
||||||
"context": "ModalTerminal",
|
"context": "ModalTerminal",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
"ctrl-cmd-space": "terminal::ShowCharacterPalette",
|
||||||
"shift-escape": "terminal::DeployModal"
|
"shift-escape": "terminal::DeployModal"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,16 +105,13 @@
|
||||||
//Set the cursor blinking behavior in the terminal.
|
//Set the cursor blinking behavior in the terminal.
|
||||||
//May take 4 values:
|
//May take 4 values:
|
||||||
// 1. Never blink the cursor, ignoring the terminal mode
|
// 1. Never blink the cursor, ignoring the terminal mode
|
||||||
// "blinking": "never",
|
|
||||||
// 2. Default the cursor blink to off, but allow the terminal to
|
|
||||||
// turn blinking on
|
|
||||||
// "blinking": "off",
|
// "blinking": "off",
|
||||||
// 3. Default the cursor blink to on, but allow the terminal to
|
// 2. Default the cursor blink to off, but allow the terminal to
|
||||||
// turn blinking off
|
// set blinking
|
||||||
|
// "blinking": "terminal_controlled",
|
||||||
|
// 3. Always blink the cursor, ignoring the terminal mode
|
||||||
// "blinking": "on",
|
// "blinking": "on",
|
||||||
// 4. Always blink the cursor, ignoring the terminal mode
|
"blinking": "terminal_controlled",
|
||||||
// "blinking": "always",
|
|
||||||
"blinking": "on",
|
|
||||||
//Any key-value pairs added to this list will be added to the terminal's
|
//Any key-value pairs added to this list will be added to the terminal's
|
||||||
//enviroment. Use `:` to seperate multiple values.
|
//enviroment. Use `:` to seperate multiple values.
|
||||||
"env": {
|
"env": {
|
||||||
|
|
|
@ -1801,7 +1801,7 @@ impl Cursor {
|
||||||
pub fn paint(&self, origin: Vector2F, cx: &mut PaintContext) {
|
pub fn paint(&self, origin: Vector2F, cx: &mut PaintContext) {
|
||||||
let bounds = match self.shape {
|
let bounds = match self.shape {
|
||||||
CursorShape::Bar => RectF::new(self.origin + origin, vec2f(2.0, self.line_height)),
|
CursorShape::Bar => RectF::new(self.origin + origin, vec2f(2.0, self.line_height)),
|
||||||
CursorShape::Block => RectF::new(
|
CursorShape::Block | CursorShape::Hollow => RectF::new(
|
||||||
self.origin + origin,
|
self.origin + origin,
|
||||||
vec2f(self.block_width, self.line_height),
|
vec2f(self.block_width, self.line_height),
|
||||||
),
|
),
|
||||||
|
@ -1809,59 +1809,32 @@ impl Cursor {
|
||||||
self.origin + origin + Vector2F::new(0.0, self.line_height - 2.0),
|
self.origin + origin + Vector2F::new(0.0, self.line_height - 2.0),
|
||||||
vec2f(self.block_width, 2.0),
|
vec2f(self.block_width, 2.0),
|
||||||
),
|
),
|
||||||
CursorShape::Hollow => RectF::new(
|
|
||||||
self.origin + origin + Vector2F::new(0.0, self.line_height - 1.0),
|
|
||||||
vec2f(self.block_width, 1.0),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Draw text under the hollow block if need be
|
//Draw background or border quad
|
||||||
if matches!(self.shape, CursorShape::Hollow) {
|
if matches!(self.shape, CursorShape::Hollow) {
|
||||||
if let Some(block_text) = &self.block_text {
|
|
||||||
block_text.paint(self.origin + origin, bounds, self.line_height, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.scene.push_quad(Quad {
|
|
||||||
bounds,
|
|
||||||
background: Some(self.color),
|
|
||||||
border: Border::new(0., Color::black()),
|
|
||||||
corner_radius: 0.,
|
|
||||||
});
|
|
||||||
|
|
||||||
if matches!(self.shape, CursorShape::Hollow) {
|
|
||||||
//Top
|
|
||||||
cx.scene.push_quad(Quad {
|
cx.scene.push_quad(Quad {
|
||||||
bounds: RectF::new(
|
bounds,
|
||||||
self.origin + origin + Vector2F::new(0.0, -1.0),
|
background: None,
|
||||||
vec2f(self.block_width + 1., 1.0),
|
border: Border::all(1., self.color),
|
||||||
),
|
|
||||||
background: Some(self.color),
|
|
||||||
border: Border::new(0., Color::black()),
|
|
||||||
corner_radius: 0.,
|
|
||||||
});
|
|
||||||
//Left
|
|
||||||
cx.scene.push_quad(Quad {
|
|
||||||
bounds: RectF::new(self.origin + origin, vec2f(1.0, self.line_height)),
|
|
||||||
background: Some(self.color),
|
|
||||||
border: Border::new(0., Color::black()),
|
|
||||||
corner_radius: 0.,
|
|
||||||
});
|
|
||||||
//Right
|
|
||||||
cx.scene.push_quad(Quad {
|
|
||||||
bounds: RectF::new(
|
|
||||||
self.origin + origin + vec2f(self.block_width, 0.),
|
|
||||||
vec2f(1.0, self.line_height),
|
|
||||||
),
|
|
||||||
background: Some(self.color),
|
|
||||||
border: Border::new(0., Color::black()),
|
|
||||||
corner_radius: 0.,
|
corner_radius: 0.,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if let Some(block_text) = &self.block_text {
|
cx.scene.push_quad(Quad {
|
||||||
block_text.paint(self.origin + origin, bounds, self.line_height, cx);
|
bounds,
|
||||||
}
|
background: Some(self.color),
|
||||||
|
border: Default::default(),
|
||||||
|
corner_radius: 0.,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(block_text) = &self.block_text {
|
||||||
|
block_text.paint(self.origin + origin, bounds, self.line_height, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shape(&self) -> CursorShape {
|
||||||
|
self.shape
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -278,6 +278,18 @@ unsafe fn build_classes() {
|
||||||
|
|
||||||
pub struct Window(Rc<RefCell<WindowState>>);
|
pub struct Window(Rc<RefCell<WindowState>>);
|
||||||
|
|
||||||
|
///Used to track what the IME does when we send it a keystroke.
|
||||||
|
///This is only used to handle the case where the IME mysteriously
|
||||||
|
///swallows certain keys.
|
||||||
|
///
|
||||||
|
///Basically a direct copy of the approach that WezTerm uses in:
|
||||||
|
///github.com/wez/wezterm : d5755f3e : window/src/os/macos/window.rs
|
||||||
|
enum ImeState {
|
||||||
|
Continue,
|
||||||
|
Acted,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
struct WindowState {
|
struct WindowState {
|
||||||
id: usize,
|
id: usize,
|
||||||
native_window: id,
|
native_window: id,
|
||||||
|
@ -299,6 +311,10 @@ struct WindowState {
|
||||||
layer: id,
|
layer: id,
|
||||||
traffic_light_position: Option<Vector2F>,
|
traffic_light_position: Option<Vector2F>,
|
||||||
previous_modifiers_changed_event: Option<Event>,
|
previous_modifiers_changed_event: Option<Event>,
|
||||||
|
//State tracking what the IME did after the last request
|
||||||
|
ime_state: ImeState,
|
||||||
|
//Retains the last IME Text
|
||||||
|
ime_text: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InsertText {
|
struct InsertText {
|
||||||
|
@ -395,6 +411,8 @@ impl Window {
|
||||||
layer,
|
layer,
|
||||||
traffic_light_position: options.traffic_light_position,
|
traffic_light_position: options.traffic_light_position,
|
||||||
previous_modifiers_changed_event: None,
|
previous_modifiers_changed_event: None,
|
||||||
|
ime_state: ImeState::None,
|
||||||
|
ime_text: None,
|
||||||
})));
|
})));
|
||||||
|
|
||||||
(*native_window).set_ivar(
|
(*native_window).set_ivar(
|
||||||
|
@ -764,6 +782,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
|
||||||
let mut window_state_borrow = window_state.as_ref().borrow_mut();
|
let mut window_state_borrow = window_state.as_ref().borrow_mut();
|
||||||
|
|
||||||
let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
|
let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
|
||||||
|
|
||||||
if let Some(event) = event {
|
if let Some(event) = event {
|
||||||
if key_equivalent {
|
if key_equivalent {
|
||||||
window_state_borrow.performed_key_equivalent = true;
|
window_state_borrow.performed_key_equivalent = true;
|
||||||
|
@ -801,7 +820,9 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
|
||||||
|
|
||||||
let mut handled = false;
|
let mut handled = false;
|
||||||
let mut window_state_borrow = window_state.borrow_mut();
|
let mut window_state_borrow = window_state.borrow_mut();
|
||||||
|
let ime_text = window_state_borrow.ime_text.clone();
|
||||||
if let Some((event, insert_text)) = window_state_borrow.pending_key_down.take() {
|
if let Some((event, insert_text)) = window_state_borrow.pending_key_down.take() {
|
||||||
|
let is_held = event.is_held;
|
||||||
if let Some(mut callback) = window_state_borrow.event_callback.take() {
|
if let Some(mut callback) = window_state_borrow.event_callback.take() {
|
||||||
drop(window_state_borrow);
|
drop(window_state_borrow);
|
||||||
|
|
||||||
|
@ -820,6 +841,18 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
|
||||||
input_handler
|
input_handler
|
||||||
.replace_text_in_range(insert.replacement_range, &insert.text)
|
.replace_text_in_range(insert.replacement_range, &insert.text)
|
||||||
});
|
});
|
||||||
|
} else if !is_composing && is_held {
|
||||||
|
if let Some(last_insert_text) = ime_text {
|
||||||
|
//MacOS IME is a bit funky, and even when you've told it there's nothing to
|
||||||
|
//inter it will still swallow certain keys (e.g. 'f', 'j') and not others
|
||||||
|
//(e.g. 'n'). This is a problem for certain kinds of views, like the terminal
|
||||||
|
with_input_handler(this, |input_handler| {
|
||||||
|
if input_handler.selected_text_range().is_none() {
|
||||||
|
handled = true;
|
||||||
|
input_handler.replace_text_in_range(None, &last_insert_text)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1132,7 +1165,6 @@ extern "C" fn first_rect_for_character_range(
|
||||||
let window = get_window_state(this).borrow().native_window;
|
let window = get_window_state(this).borrow().native_window;
|
||||||
NSView::frame(window)
|
NSView::frame(window)
|
||||||
};
|
};
|
||||||
|
|
||||||
with_input_handler(this, |input_handler| {
|
with_input_handler(this, |input_handler| {
|
||||||
input_handler.rect_for_range(range.to_range()?)
|
input_handler.rect_for_range(range.to_range()?)
|
||||||
})
|
})
|
||||||
|
@ -1170,6 +1202,9 @@ extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NS
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let replacement_range = replacement_range.to_range();
|
let replacement_range = replacement_range.to_range();
|
||||||
|
|
||||||
|
window_state.borrow_mut().ime_text = Some(text.to_string());
|
||||||
|
window_state.borrow_mut().ime_state = ImeState::Acted;
|
||||||
|
|
||||||
let is_composing =
|
let is_composing =
|
||||||
with_input_handler(this, |input_handler| input_handler.marked_text_range())
|
with_input_handler(this, |input_handler| input_handler.marked_text_range())
|
||||||
.flatten()
|
.flatten()
|
||||||
|
@ -1198,7 +1233,8 @@ extern "C" fn set_marked_text(
|
||||||
replacement_range: NSRange,
|
replacement_range: NSRange,
|
||||||
) {
|
) {
|
||||||
unsafe {
|
unsafe {
|
||||||
get_window_state(this).borrow_mut().pending_key_down.take();
|
let window_state = get_window_state(this);
|
||||||
|
window_state.borrow_mut().pending_key_down.take();
|
||||||
|
|
||||||
let is_attributed_string: BOOL =
|
let is_attributed_string: BOOL =
|
||||||
msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
|
msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
|
||||||
|
@ -1213,6 +1249,9 @@ extern "C" fn set_marked_text(
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
window_state.borrow_mut().ime_state = ImeState::Acted;
|
||||||
|
window_state.borrow_mut().ime_text = Some(text.to_string());
|
||||||
|
|
||||||
with_input_handler(this, |input_handler| {
|
with_input_handler(this, |input_handler| {
|
||||||
input_handler.replace_and_mark_text_in_range(replacement_range, text, selected_range);
|
input_handler.replace_and_mark_text_in_range(replacement_range, text, selected_range);
|
||||||
});
|
});
|
||||||
|
@ -1220,6 +1259,13 @@ extern "C" fn set_marked_text(
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn unmark_text(this: &Object, _: Sel) {
|
extern "C" fn unmark_text(this: &Object, _: Sel) {
|
||||||
|
unsafe {
|
||||||
|
let state = get_window_state(this);
|
||||||
|
let mut borrow = state.borrow_mut();
|
||||||
|
borrow.ime_state = ImeState::Acted;
|
||||||
|
borrow.ime_text.take();
|
||||||
|
}
|
||||||
|
|
||||||
with_input_handler(this, |input_handler| input_handler.unmark_text());
|
with_input_handler(this, |input_handler| input_handler.unmark_text());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1246,7 +1292,14 @@ extern "C" fn attributed_substring_for_proposed_range(
|
||||||
.unwrap_or(nil)
|
.unwrap_or(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn do_command_by_selector(_: &Object, _: Sel, _: Sel) {}
|
extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) {
|
||||||
|
unsafe {
|
||||||
|
let state = get_window_state(this);
|
||||||
|
let mut borrow = state.borrow_mut();
|
||||||
|
borrow.ime_state = ImeState::Continue;
|
||||||
|
borrow.ime_text.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn synthetic_drag(
|
async fn synthetic_drag(
|
||||||
window_state: Weak<RefCell<WindowState>>,
|
window_state: Weak<RefCell<WindowState>>,
|
||||||
|
|
|
@ -89,15 +89,14 @@ pub struct TerminalSettings {
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum TerminalBlink {
|
pub enum TerminalBlink {
|
||||||
Never,
|
|
||||||
On,
|
|
||||||
Off,
|
Off,
|
||||||
Always,
|
TerminalControlled,
|
||||||
|
On,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TerminalBlink {
|
impl Default for TerminalBlink {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
TerminalBlink::On
|
TerminalBlink::TerminalControlled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,17 @@ This crate is split into two conceptual halves:
|
||||||
- The terminal.rs file and the src/mappings/ folder, these contain the code for interacting with Alacritty and maintaining the pty event loop. Some behavior in this file is constrained by terminal protocols and standards. The Zed init function is also placed here.
|
- The terminal.rs file and the src/mappings/ folder, these contain the code for interacting with Alacritty and maintaining the pty event loop. Some behavior in this file is constrained by terminal protocols and standards. The Zed init function is also placed here.
|
||||||
- Everything else. These other files integrate the `Terminal` struct created in terminal.rs into the rest of GPUI. The main entry point for GPUI is the terminal_view.rs file and the modal.rs file.
|
- Everything else. These other files integrate the `Terminal` struct created in terminal.rs into the rest of GPUI. The main entry point for GPUI is the terminal_view.rs file and the modal.rs file.
|
||||||
|
|
||||||
Terminals are created externally, and so can fail in unexpected ways However, GPUI currently does not have an API for models than can fail to instantiate. `TerminalBuilder` solves this by using Rust's type system to split `Terminal` instantiation into a 2 step process: first attempt to create the file handles with `TerminalBuilder::new()`, check the result, then call `TerminalBuilder::subscribe(cx)` from within a model context.
|
ttys are created externally, and so can fail in unexpected ways. However, GPUI currently does not have an API for models than can fail to instantiate. `TerminalBuilder` solves this by using Rust's type system to split tty instantiation into a 2 step process: first attempt to create the file handles with `TerminalBuilder::new()`, check the result, then call `TerminalBuilder::subscribe(cx)` from within a model context.
|
||||||
The TerminalView struct abstracts over failed and successful terminals, and provides a standardized way of instantiating an always-successful view of a terminal.
|
|
||||||
|
The TerminalView struct abstracts over failed and successful terminals, passing focus through to the associated view and allowing clients to build a terminal without worrying about errors.
|
||||||
|
|
||||||
|
#Input
|
||||||
|
|
||||||
|
There are currently 3 distinct paths for getting keystrokes to the terminal:
|
||||||
|
|
||||||
|
1. Terminal specific characters and bindings. Things like ctrl-a mapping to ASCII control character 1, ANSI escape codes associated with the function keys, etc. These are caught with a raw key-down handler in the element and are processed immediately. This is done with the `try_keystroke()` method on Terminal
|
||||||
|
|
||||||
|
2. GPU Action handlers. GPUI clobbers a few vital keys by adding bindings to them in the global context. These keys are synthesized and then dispatched through the same `try_keystroke()` API as the above mappings
|
||||||
|
|
||||||
|
3. IME text. When the special character mappings fail, we pass the keystroke back to GPUI to hand it to the IME system. This comes back to us in the `View::replace_text_in_range()` method, and we then send that to the terminal directly, bypassing `try_keystroke()`.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use alacritty_terminal::{
|
use alacritty_terminal::{
|
||||||
ansi::{Color as AnsiColor, Color::Named, NamedColor},
|
ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor},
|
||||||
grid::{Dimensions, Scroll},
|
grid::{Dimensions, Scroll},
|
||||||
index::{Column as GridCol, Line as GridLine, Point, Side},
|
index::{Column as GridCol, Line as GridLine, Point, Side},
|
||||||
selection::SelectionRange,
|
selection::SelectionRange,
|
||||||
|
@ -21,7 +21,7 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use settings::{Settings, TerminalBlink};
|
use settings::Settings;
|
||||||
use theme::TerminalStyle;
|
use theme::TerminalStyle;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ pub struct TerminalEl {
|
||||||
view: WeakViewHandle<ConnectedView>,
|
view: WeakViewHandle<ConnectedView>,
|
||||||
modal: bool,
|
modal: bool,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
blink_state: bool,
|
cursor_visible: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TerminalEl {
|
impl TerminalEl {
|
||||||
|
@ -210,14 +210,14 @@ impl TerminalEl {
|
||||||
terminal: WeakModelHandle<Terminal>,
|
terminal: WeakModelHandle<Terminal>,
|
||||||
modal: bool,
|
modal: bool,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
blink_state: bool,
|
cursor_visible: bool,
|
||||||
) -> TerminalEl {
|
) -> TerminalEl {
|
||||||
TerminalEl {
|
TerminalEl {
|
||||||
view,
|
view,
|
||||||
terminal,
|
terminal,
|
||||||
modal,
|
modal,
|
||||||
focused,
|
focused,
|
||||||
blink_state,
|
cursor_visible,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,33 +571,6 @@ impl TerminalEl {
|
||||||
|
|
||||||
(point, side)
|
(point, side)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn should_show_cursor(
|
|
||||||
settings: Option<TerminalBlink>,
|
|
||||||
blinking_on: bool,
|
|
||||||
focused: bool,
|
|
||||||
blink_show: bool,
|
|
||||||
) -> bool {
|
|
||||||
if !focused {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
match settings {
|
|
||||||
Some(setting) => match setting {
|
|
||||||
TerminalBlink::Never => true,
|
|
||||||
TerminalBlink::On | TerminalBlink::Off if blinking_on => blink_show,
|
|
||||||
TerminalBlink::On | TerminalBlink::Off /*if !blinking_on */ => true,
|
|
||||||
TerminalBlink::Always => focused && blink_show,
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
if blinking_on {
|
|
||||||
blink_show
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for TerminalEl {
|
impl Element for TerminalEl {
|
||||||
|
@ -610,7 +583,6 @@ impl Element for TerminalEl {
|
||||||
cx: &mut gpui::LayoutContext,
|
cx: &mut gpui::LayoutContext,
|
||||||
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
||||||
let settings = cx.global::<Settings>();
|
let settings = cx.global::<Settings>();
|
||||||
let blink_settings = settings.terminal_overrides.blinking.clone();
|
|
||||||
let font_cache = cx.font_cache();
|
let font_cache = cx.font_cache();
|
||||||
|
|
||||||
//Setup layout information
|
//Setup layout information
|
||||||
|
@ -629,13 +601,13 @@ impl Element for TerminalEl {
|
||||||
terminal_theme.colors.background
|
terminal_theme.colors.background
|
||||||
};
|
};
|
||||||
|
|
||||||
let (cells, selection, cursor, display_offset, cursor_text, blink_mode) = self
|
let (cells, selection, cursor, display_offset, cursor_text) = self
|
||||||
.terminal
|
.terminal
|
||||||
.upgrade(cx)
|
.upgrade(cx)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.update(cx.app, |terminal, mcx| {
|
.update(cx.app, |terminal, mcx| {
|
||||||
terminal.set_size(dimensions);
|
terminal.set_size(dimensions);
|
||||||
terminal.render_lock(mcx, |content, cursor_text, blink_mode| {
|
terminal.render_lock(mcx, |content, cursor_text| {
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
cells.extend(
|
cells.extend(
|
||||||
content
|
content
|
||||||
|
@ -659,7 +631,6 @@ impl Element for TerminalEl {
|
||||||
content.cursor,
|
content.cursor,
|
||||||
content.display_offset,
|
content.display_offset,
|
||||||
cursor_text,
|
cursor_text,
|
||||||
blink_mode,
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -674,59 +645,57 @@ impl Element for TerminalEl {
|
||||||
selection,
|
selection,
|
||||||
);
|
);
|
||||||
|
|
||||||
//Layout cursor
|
//Layout cursor. Rectangle is used for IME, so we should lay it out even
|
||||||
let cursor = {
|
//if we don't end up showing it.
|
||||||
if !TerminalEl::should_show_cursor(
|
let cursor = if let AlacCursorShape::Hidden = cursor.shape {
|
||||||
blink_settings,
|
None
|
||||||
blink_mode,
|
} else {
|
||||||
self.focused,
|
let cursor_point = DisplayCursor::from(cursor.point, display_offset);
|
||||||
self.blink_state,
|
let cursor_text = {
|
||||||
) {
|
let str_trxt = cursor_text.to_string();
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let cursor_point = DisplayCursor::from(cursor.point, display_offset);
|
|
||||||
let cursor_text = {
|
|
||||||
let str_trxt = cursor_text.to_string();
|
|
||||||
|
|
||||||
let color = if self.focused {
|
let color = if self.focused {
|
||||||
terminal_theme.colors.background
|
terminal_theme.colors.background
|
||||||
} else {
|
} else {
|
||||||
terminal_theme.colors.foreground
|
terminal_theme.colors.foreground
|
||||||
};
|
|
||||||
|
|
||||||
cx.text_layout_cache.layout_str(
|
|
||||||
&str_trxt,
|
|
||||||
text_style.font_size,
|
|
||||||
&[(
|
|
||||||
str_trxt.len(),
|
|
||||||
RunStyle {
|
|
||||||
font_id: text_style.font_id,
|
|
||||||
color,
|
|
||||||
underline: Default::default(),
|
|
||||||
},
|
|
||||||
)],
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TerminalEl::shape_cursor(cursor_point, dimensions, &cursor_text).map(
|
cx.text_layout_cache.layout_str(
|
||||||
move |(cursor_position, block_width)| {
|
&str_trxt,
|
||||||
let (shape, color) = if self.focused {
|
text_style.font_size,
|
||||||
(CursorShape::Block, terminal_theme.colors.cursor)
|
&[(
|
||||||
} else {
|
str_trxt.len(),
|
||||||
(CursorShape::Hollow, terminal_theme.colors.foreground)
|
RunStyle {
|
||||||
};
|
font_id: text_style.font_id,
|
||||||
|
|
||||||
Cursor::new(
|
|
||||||
cursor_position,
|
|
||||||
block_width,
|
|
||||||
dimensions.line_height,
|
|
||||||
color,
|
color,
|
||||||
shape,
|
underline: Default::default(),
|
||||||
Some(cursor_text),
|
},
|
||||||
)
|
)],
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
};
|
||||||
|
|
||||||
|
TerminalEl::shape_cursor(cursor_point, dimensions, &cursor_text).map(
|
||||||
|
move |(cursor_position, block_width)| {
|
||||||
|
let shape = match cursor.shape {
|
||||||
|
AlacCursorShape::Block if !self.focused => CursorShape::Hollow,
|
||||||
|
AlacCursorShape::Block => CursorShape::Block,
|
||||||
|
AlacCursorShape::Underline => CursorShape::Underscore,
|
||||||
|
AlacCursorShape::Beam => CursorShape::Bar,
|
||||||
|
AlacCursorShape::HollowBlock => CursorShape::Hollow,
|
||||||
|
//This case is handled in the if wrapping the whole cursor layout
|
||||||
|
AlacCursorShape::Hidden => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Cursor::new(
|
||||||
|
cursor_position,
|
||||||
|
block_width,
|
||||||
|
dimensions.line_height,
|
||||||
|
terminal_theme.colors.cursor,
|
||||||
|
shape,
|
||||||
|
Some(cursor_text),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
//Done!
|
//Done!
|
||||||
|
@ -817,10 +786,12 @@ impl Element for TerminalEl {
|
||||||
});
|
});
|
||||||
|
|
||||||
//Draw cursor
|
//Draw cursor
|
||||||
if let Some(cursor) = &layout.cursor {
|
if self.cursor_visible {
|
||||||
cx.paint_layer(clip_bounds, |cx| {
|
if let Some(cursor) = &layout.cursor {
|
||||||
cursor.paint(origin, cx);
|
cx.paint_layer(clip_bounds, |cx| {
|
||||||
})
|
cursor.paint(origin, cx);
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use gpui::{
|
||||||
AnyViewHandle, AppContext, Element, ElementBox, ModelHandle, MutableAppContext, View,
|
AnyViewHandle, AppContext, Element, ElementBox, ModelHandle, MutableAppContext, View,
|
||||||
ViewContext, ViewHandle,
|
ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
|
use settings::{Settings, TerminalBlink};
|
||||||
use smol::Timer;
|
use smol::Timer;
|
||||||
use workspace::pane;
|
use workspace::pane;
|
||||||
|
|
||||||
|
@ -29,7 +30,17 @@ pub struct DeployContextMenu {
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
terminal,
|
terminal,
|
||||||
[Up, Down, CtrlC, Escape, Enter, Clear, Copy, Paste,]
|
[
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
CtrlC,
|
||||||
|
Escape,
|
||||||
|
Enter,
|
||||||
|
Clear,
|
||||||
|
Copy,
|
||||||
|
Paste,
|
||||||
|
ShowCharacterPalette
|
||||||
|
]
|
||||||
);
|
);
|
||||||
impl_internal_actions!(project_panel, [DeployContextMenu]);
|
impl_internal_actions!(project_panel, [DeployContextMenu]);
|
||||||
|
|
||||||
|
@ -45,6 +56,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_action(ConnectedView::copy);
|
cx.add_action(ConnectedView::copy);
|
||||||
cx.add_action(ConnectedView::paste);
|
cx.add_action(ConnectedView::paste);
|
||||||
cx.add_action(ConnectedView::clear);
|
cx.add_action(ConnectedView::clear);
|
||||||
|
cx.add_action(ConnectedView::show_character_palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
///A terminal view, maintains the PTY's file handles and communicates with the terminal
|
///A terminal view, maintains the PTY's file handles and communicates with the terminal
|
||||||
|
@ -56,7 +68,8 @@ pub struct ConnectedView {
|
||||||
// Only for styling purposes. Doesn't effect behavior
|
// Only for styling purposes. Doesn't effect behavior
|
||||||
modal: bool,
|
modal: bool,
|
||||||
context_menu: ViewHandle<ContextMenu>,
|
context_menu: ViewHandle<ContextMenu>,
|
||||||
show_cursor: bool,
|
blink_state: bool,
|
||||||
|
blinking_on: bool,
|
||||||
blinking_paused: bool,
|
blinking_paused: bool,
|
||||||
blink_epoch: usize,
|
blink_epoch: usize,
|
||||||
}
|
}
|
||||||
|
@ -80,7 +93,7 @@ impl ConnectedView {
|
||||||
this.has_bell = true;
|
this.has_bell = true;
|
||||||
cx.emit(Event::Wakeup);
|
cx.emit(Event::Wakeup);
|
||||||
}
|
}
|
||||||
|
Event::BlinkChanged => this.blinking_on = !this.blinking_on,
|
||||||
_ => cx.emit(*event),
|
_ => cx.emit(*event),
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
@ -91,7 +104,8 @@ impl ConnectedView {
|
||||||
has_bell: false,
|
has_bell: false,
|
||||||
modal,
|
modal,
|
||||||
context_menu: cx.add_view(ContextMenu::new),
|
context_menu: cx.add_view(ContextMenu::new),
|
||||||
show_cursor: true,
|
blink_state: true,
|
||||||
|
blinking_on: false,
|
||||||
blinking_paused: false,
|
blinking_paused: false,
|
||||||
blink_epoch: 0,
|
blink_epoch: 0,
|
||||||
}
|
}
|
||||||
|
@ -126,19 +140,64 @@ impl ConnectedView {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
|
||||||
|
if !self
|
||||||
|
.terminal
|
||||||
|
.read(cx)
|
||||||
|
.last_mode
|
||||||
|
.contains(TermMode::ALT_SCREEN)
|
||||||
|
{
|
||||||
|
cx.show_character_palette();
|
||||||
|
} else {
|
||||||
|
self.terminal.update(cx, |term, _| {
|
||||||
|
term.try_keystroke(&Keystroke::parse("ctrl-cmd-space").unwrap())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn clear(&mut self, _: &Clear, cx: &mut ViewContext<Self>) {
|
fn clear(&mut self, _: &Clear, cx: &mut ViewContext<Self>) {
|
||||||
self.terminal.update(cx, |term, _| term.clear());
|
self.terminal.update(cx, |term, _| term.clear());
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Following code copied from editor cursor
|
pub fn should_show_cursor(
|
||||||
pub fn blink_show(&self) -> bool {
|
&self,
|
||||||
self.blinking_paused || self.show_cursor
|
focused: bool,
|
||||||
|
cx: &mut gpui::RenderContext<'_, Self>,
|
||||||
|
) -> bool {
|
||||||
|
//Don't blink the cursor when not focused, blinking is disabled, or paused
|
||||||
|
if !focused
|
||||||
|
|| !self.blinking_on
|
||||||
|
|| self.blinking_paused
|
||||||
|
|| self
|
||||||
|
.terminal
|
||||||
|
.read(cx)
|
||||||
|
.last_mode
|
||||||
|
.contains(TermMode::ALT_SCREEN)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let setting = {
|
||||||
|
let settings = cx.global::<Settings>();
|
||||||
|
settings
|
||||||
|
.terminal_overrides
|
||||||
|
.blinking
|
||||||
|
.clone()
|
||||||
|
.unwrap_or(TerminalBlink::TerminalControlled)
|
||||||
|
};
|
||||||
|
|
||||||
|
match setting {
|
||||||
|
//If the user requested to never blink, don't blink it.
|
||||||
|
TerminalBlink::Off => true,
|
||||||
|
//If the terminal is controlling it, check terminal mode
|
||||||
|
TerminalBlink::TerminalControlled | TerminalBlink::On => self.blink_state,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
|
fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
|
||||||
if epoch == self.blink_epoch && !self.blinking_paused {
|
if epoch == self.blink_epoch && !self.blinking_paused {
|
||||||
self.show_cursor = !self.show_cursor;
|
self.blink_state = !self.blink_state;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
let epoch = self.next_blink_epoch();
|
let epoch = self.next_blink_epoch();
|
||||||
|
@ -156,7 +215,7 @@ impl ConnectedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
|
pub fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
self.show_cursor = true;
|
self.blink_state = true;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
let epoch = self.next_blink_epoch();
|
let epoch = self.next_blink_epoch();
|
||||||
|
@ -199,41 +258,41 @@ impl ConnectedView {
|
||||||
///Synthesize the keyboard event corresponding to 'up'
|
///Synthesize the keyboard event corresponding to 'up'
|
||||||
fn up(&mut self, _: &Up, cx: &mut ViewContext<Self>) {
|
fn up(&mut self, _: &Up, cx: &mut ViewContext<Self>) {
|
||||||
self.clear_bel(cx);
|
self.clear_bel(cx);
|
||||||
self.terminal
|
self.terminal.update(cx, |term, _| {
|
||||||
.read(cx)
|
term.try_keystroke(&Keystroke::parse("up").unwrap())
|
||||||
.try_keystroke(&Keystroke::parse("up").unwrap());
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
///Synthesize the keyboard event corresponding to 'down'
|
///Synthesize the keyboard event corresponding to 'down'
|
||||||
fn down(&mut self, _: &Down, cx: &mut ViewContext<Self>) {
|
fn down(&mut self, _: &Down, cx: &mut ViewContext<Self>) {
|
||||||
self.clear_bel(cx);
|
self.clear_bel(cx);
|
||||||
self.terminal
|
self.terminal.update(cx, |term, _| {
|
||||||
.read(cx)
|
term.try_keystroke(&Keystroke::parse("down").unwrap())
|
||||||
.try_keystroke(&Keystroke::parse("down").unwrap());
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
///Synthesize the keyboard event corresponding to 'ctrl-c'
|
///Synthesize the keyboard event corresponding to 'ctrl-c'
|
||||||
fn ctrl_c(&mut self, _: &CtrlC, cx: &mut ViewContext<Self>) {
|
fn ctrl_c(&mut self, _: &CtrlC, cx: &mut ViewContext<Self>) {
|
||||||
self.clear_bel(cx);
|
self.clear_bel(cx);
|
||||||
self.terminal
|
self.terminal.update(cx, |term, _| {
|
||||||
.read(cx)
|
term.try_keystroke(&Keystroke::parse("ctrl-c").unwrap())
|
||||||
.try_keystroke(&Keystroke::parse("ctrl-c").unwrap());
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
///Synthesize the keyboard event corresponding to 'escape'
|
///Synthesize the keyboard event corresponding to 'escape'
|
||||||
fn escape(&mut self, _: &Escape, cx: &mut ViewContext<Self>) {
|
fn escape(&mut self, _: &Escape, cx: &mut ViewContext<Self>) {
|
||||||
self.clear_bel(cx);
|
self.clear_bel(cx);
|
||||||
self.terminal
|
self.terminal.update(cx, |term, _| {
|
||||||
.read(cx)
|
term.try_keystroke(&Keystroke::parse("escape").unwrap())
|
||||||
.try_keystroke(&Keystroke::parse("escape").unwrap());
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
///Synthesize the keyboard event corresponding to 'enter'
|
///Synthesize the keyboard event corresponding to 'enter'
|
||||||
fn enter(&mut self, _: &Enter, cx: &mut ViewContext<Self>) {
|
fn enter(&mut self, _: &Enter, cx: &mut ViewContext<Self>) {
|
||||||
self.clear_bel(cx);
|
self.clear_bel(cx);
|
||||||
self.terminal
|
self.terminal.update(cx, |term, _| {
|
||||||
.read(cx)
|
term.try_keystroke(&Keystroke::parse("enter").unwrap())
|
||||||
.try_keystroke(&Keystroke::parse("enter").unwrap());
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +317,7 @@ impl View for ConnectedView {
|
||||||
terminal_handle,
|
terminal_handle,
|
||||||
self.modal,
|
self.modal,
|
||||||
focused,
|
focused,
|
||||||
self.blink_show(),
|
self.should_show_cursor(focused, cx),
|
||||||
)
|
)
|
||||||
.contained()
|
.contained()
|
||||||
.boxed(),
|
.boxed(),
|
||||||
|
@ -299,8 +358,10 @@ impl View for ConnectedView {
|
||||||
text: &str,
|
text: &str,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
self.terminal
|
self.terminal.update(cx, |terminal, _| {
|
||||||
.update(cx, |terminal, _| terminal.write_to_pty(text.into()));
|
terminal.write_to_pty(text.into());
|
||||||
|
terminal.scroll(alacritty_terminal::grid::Scroll::Bottom);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keymap_context(&self, _: &gpui::AppContext) -> gpui::keymap::Context {
|
fn keymap_context(&self, _: &gpui::AppContext) -> gpui::keymap::Context {
|
||||||
|
|
|
@ -53,7 +53,6 @@ pub fn to_esc_str(keystroke: &Keystroke, mode: &TermMode) -> Option<String> {
|
||||||
// Manual Bindings including modifiers
|
// Manual Bindings including modifiers
|
||||||
let manual_esc_str = match (keystroke.key.as_ref(), &modifiers) {
|
let manual_esc_str = match (keystroke.key.as_ref(), &modifiers) {
|
||||||
//Basic special keys
|
//Basic special keys
|
||||||
("space", Modifiers::None) => Some(" ".to_string()),
|
|
||||||
("tab", Modifiers::None) => Some("\x09".to_string()),
|
("tab", Modifiers::None) => Some("\x09".to_string()),
|
||||||
("escape", Modifiers::None) => Some("\x1b".to_string()),
|
("escape", Modifiers::None) => Some("\x1b".to_string()),
|
||||||
("enter", Modifiers::None) => Some("\x0d".to_string()),
|
("enter", Modifiers::None) => Some("\x0d".to_string()),
|
||||||
|
|
|
@ -62,6 +62,7 @@ pub enum Event {
|
||||||
CloseTerminal,
|
CloseTerminal,
|
||||||
Bell,
|
Bell,
|
||||||
Wakeup,
|
Wakeup,
|
||||||
|
BlinkChanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -294,15 +295,10 @@ impl TerminalBuilder {
|
||||||
let mut term = Term::new(&config, &initial_size, ZedListener(events_tx.clone()));
|
let mut term = Term::new(&config, &initial_size, ZedListener(events_tx.clone()));
|
||||||
|
|
||||||
//Start off blinking if we need to
|
//Start off blinking if we need to
|
||||||
match blink_settings {
|
if let Some(TerminalBlink::On) = blink_settings {
|
||||||
Some(setting) => match setting {
|
term.set_mode(alacritty_terminal::ansi::Mode::BlinkingCursor)
|
||||||
TerminalBlink::On | TerminalBlink::Always => {
|
|
||||||
term.set_mode(alacritty_terminal::ansi::Mode::BlinkingCursor)
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
None => term.set_mode(alacritty_terminal::ansi::Mode::BlinkingCursor),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let term = Arc::new(FairMutex::new(term));
|
let term = Arc::new(FairMutex::new(term));
|
||||||
|
|
||||||
//Setup the pty...
|
//Setup the pty...
|
||||||
|
@ -469,17 +465,17 @@ impl Terminal {
|
||||||
AlacTermEvent::ClipboardStore(_, data) => {
|
AlacTermEvent::ClipboardStore(_, data) => {
|
||||||
cx.write_to_clipboard(ClipboardItem::new(data.to_string()))
|
cx.write_to_clipboard(ClipboardItem::new(data.to_string()))
|
||||||
}
|
}
|
||||||
AlacTermEvent::ClipboardLoad(_, format) => self.notify_pty(format(
|
AlacTermEvent::ClipboardLoad(_, format) => self.write_to_pty(format(
|
||||||
&cx.read_from_clipboard()
|
&cx.read_from_clipboard()
|
||||||
.map(|ci| ci.text().to_string())
|
.map(|ci| ci.text().to_string())
|
||||||
.unwrap_or_else(|| "".to_string()),
|
.unwrap_or_else(|| "".to_string()),
|
||||||
)),
|
)),
|
||||||
AlacTermEvent::PtyWrite(out) => self.notify_pty(out.clone()),
|
AlacTermEvent::PtyWrite(out) => self.write_to_pty(out.clone()),
|
||||||
AlacTermEvent::TextAreaSizeRequest(format) => {
|
AlacTermEvent::TextAreaSizeRequest(format) => {
|
||||||
self.notify_pty(format(self.cur_size.into()))
|
self.write_to_pty(format(self.cur_size.into()))
|
||||||
}
|
}
|
||||||
AlacTermEvent::CursorBlinkingChange => {
|
AlacTermEvent::CursorBlinkingChange => {
|
||||||
//TODO whatever state we need to set to get the cursor blinking
|
cx.emit(Event::BlinkChanged);
|
||||||
}
|
}
|
||||||
AlacTermEvent::Bell => {
|
AlacTermEvent::Bell => {
|
||||||
cx.emit(Event::Bell);
|
cx.emit(Event::Bell);
|
||||||
|
@ -519,7 +515,7 @@ impl Terminal {
|
||||||
let term_style = &cx.global::<Settings>().theme.terminal;
|
let term_style = &cx.global::<Settings>().theme.terminal;
|
||||||
to_alac_rgb(get_color_at_index(index, &term_style.colors))
|
to_alac_rgb(get_color_at_index(index, &term_style.colors))
|
||||||
});
|
});
|
||||||
self.notify_pty(format(color))
|
self.write_to_pty(format(color))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InternalEvent::Resize(new_size) => {
|
InternalEvent::Resize(new_size) => {
|
||||||
|
@ -530,7 +526,7 @@ impl Terminal {
|
||||||
term.resize(*new_size);
|
term.resize(*new_size);
|
||||||
}
|
}
|
||||||
InternalEvent::Clear => {
|
InternalEvent::Clear => {
|
||||||
self.notify_pty("\x0c".to_string());
|
self.write_to_pty("\x0c".to_string());
|
||||||
term.clear_screen(ClearMode::Saved);
|
term.clear_screen(ClearMode::Saved);
|
||||||
}
|
}
|
||||||
InternalEvent::Scroll(scroll) => term.scroll_display(*scroll),
|
InternalEvent::Scroll(scroll) => term.scroll_display(*scroll),
|
||||||
|
@ -550,12 +546,8 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notify_pty(&self, txt: String) {
|
|
||||||
self.pty_tx.notify(txt.into_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
///Write the Input payload to the tty.
|
///Write the Input payload to the tty.
|
||||||
pub fn write_to_pty(&mut self, input: String) {
|
pub fn write_to_pty(&self, input: String) {
|
||||||
self.pty_tx.notify(input.into_bytes());
|
self.pty_tx.notify(input.into_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,10 +560,11 @@ impl Terminal {
|
||||||
self.events.push(InternalEvent::Clear)
|
self.events.push(InternalEvent::Clear)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_keystroke(&self, keystroke: &Keystroke) -> bool {
|
pub fn try_keystroke(&mut self, keystroke: &Keystroke) -> bool {
|
||||||
let esc = to_esc_str(keystroke, &self.last_mode);
|
let esc = to_esc_str(keystroke, &self.last_mode);
|
||||||
if let Some(esc) = esc {
|
if let Some(esc) = esc {
|
||||||
self.notify_pty(esc);
|
self.write_to_pty(esc);
|
||||||
|
self.scroll(Scroll::Bottom);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -581,11 +574,11 @@ impl Terminal {
|
||||||
///Paste text into the terminal
|
///Paste text into the terminal
|
||||||
pub fn paste(&self, text: &str) {
|
pub fn paste(&self, text: &str) {
|
||||||
if self.last_mode.contains(TermMode::BRACKETED_PASTE) {
|
if self.last_mode.contains(TermMode::BRACKETED_PASTE) {
|
||||||
self.notify_pty("\x1b[200~".to_string());
|
self.write_to_pty("\x1b[200~".to_string());
|
||||||
self.notify_pty(text.replace('\x1b', ""));
|
self.write_to_pty(text.replace('\x1b', ""));
|
||||||
self.notify_pty("\x1b[201~".to_string());
|
self.write_to_pty("\x1b[201~".to_string());
|
||||||
} else {
|
} else {
|
||||||
self.notify_pty(text.replace("\r\n", "\r").replace('\n', "\r"));
|
self.write_to_pty(text.replace("\r\n", "\r").replace('\n', "\r"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,7 +588,7 @@ impl Terminal {
|
||||||
|
|
||||||
pub fn render_lock<F, T>(&mut self, cx: &mut ModelContext<Self>, f: F) -> T
|
pub fn render_lock<F, T>(&mut self, cx: &mut ModelContext<Self>, f: F) -> T
|
||||||
where
|
where
|
||||||
F: FnOnce(RenderableContent, char, bool) -> T,
|
F: FnOnce(RenderableContent, char) -> T,
|
||||||
{
|
{
|
||||||
let m = self.term.clone(); //Arc clone
|
let m = self.term.clone(); //Arc clone
|
||||||
let mut term = m.lock();
|
let mut term = m.lock();
|
||||||
|
@ -611,7 +604,7 @@ impl Terminal {
|
||||||
|
|
||||||
let cursor_text = term.grid()[content.cursor.point].c;
|
let cursor_text = term.grid()[content.cursor.point].c;
|
||||||
|
|
||||||
f(content, cursor_text, term.cursor_style().blinking)
|
f(content, cursor_text)
|
||||||
}
|
}
|
||||||
|
|
||||||
///Scroll the terminal
|
///Scroll the terminal
|
||||||
|
@ -621,13 +614,13 @@ impl Terminal {
|
||||||
|
|
||||||
pub fn focus_in(&self) {
|
pub fn focus_in(&self) {
|
||||||
if self.last_mode.contains(TermMode::FOCUS_IN_OUT) {
|
if self.last_mode.contains(TermMode::FOCUS_IN_OUT) {
|
||||||
self.notify_pty("\x1b[I".to_string());
|
self.write_to_pty("\x1b[I".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus_out(&self) {
|
pub fn focus_out(&self) {
|
||||||
if self.last_mode.contains(TermMode::FOCUS_IN_OUT) {
|
if self.last_mode.contains(TermMode::FOCUS_IN_OUT) {
|
||||||
self.notify_pty("\x1b[O".to_string());
|
self.write_to_pty("\x1b[O".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue