Merge pull request #1524 from zed-industries/ime-finalize

IME Changes: Tracking PR
This commit is contained in:
Mikayla Maki 2022-08-18 13:27:30 -07:00 committed by GitHub
commit 0fef72ac5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 267 additions and 208 deletions

View file

@ -420,6 +420,7 @@
"enter": "terminal::Enter",
"ctrl-c": "terminal::CtrlC",
// Useful terminal actions:
"ctrl-cmd-space": "terminal::ShowCharacterPalette",
"cmd-c": "terminal::Copy",
"cmd-v": "terminal::Paste",
"cmd-k": "terminal::Clear"
@ -428,6 +429,7 @@
{
"context": "ModalTerminal",
"bindings": {
"ctrl-cmd-space": "terminal::ShowCharacterPalette",
"shift-escape": "terminal::DeployModal"
}
}

View file

@ -105,16 +105,13 @@
//Set the cursor blinking behavior in the terminal.
//May take 4 values:
// 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",
// 3. Default the cursor blink to on, but allow the terminal to
// turn blinking off
// 2. Default the cursor blink to off, but allow the terminal to
// set blinking
// "blinking": "terminal_controlled",
// 3. Always blink the cursor, ignoring the terminal mode
// "blinking": "on",
// 4. Always blink the cursor, ignoring the terminal mode
// "blinking": "always",
"blinking": "on",
"blinking": "terminal_controlled",
//Any key-value pairs added to this list will be added to the terminal's
//enviroment. Use `:` to seperate multiple values.
"env": {

View file

@ -1801,7 +1801,7 @@ impl Cursor {
pub fn paint(&self, origin: Vector2F, cx: &mut PaintContext) {
let bounds = match self.shape {
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,
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),
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 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 {
bounds: RectF::new(
self.origin + origin + Vector2F::new(0.0, -1.0),
vec2f(self.block_width + 1., 1.0),
),
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()),
bounds,
background: None,
border: Border::all(1., self.color),
corner_radius: 0.,
});
} else {
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: 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
}
}

View file

@ -278,6 +278,18 @@ unsafe fn build_classes() {
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 {
id: usize,
native_window: id,
@ -299,6 +311,10 @@ struct WindowState {
layer: id,
traffic_light_position: Option<Vector2F>,
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 {
@ -395,6 +411,8 @@ impl Window {
layer,
traffic_light_position: options.traffic_light_position,
previous_modifiers_changed_event: None,
ime_state: ImeState::None,
ime_text: None,
})));
(*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 event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
if let Some(event) = event {
if key_equivalent {
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 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() {
let is_held = event.is_held;
if let Some(mut callback) = window_state_borrow.event_callback.take() {
drop(window_state_borrow);
@ -820,6 +841,18 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
input_handler
.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;
NSView::frame(window)
};
with_input_handler(this, |input_handler| {
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();
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 =
with_input_handler(this, |input_handler| input_handler.marked_text_range())
.flatten()
@ -1198,7 +1233,8 @@ extern "C" fn set_marked_text(
replacement_range: NSRange,
) {
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 =
msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
@ -1213,6 +1249,9 @@ extern "C" fn set_marked_text(
.to_str()
.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| {
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) {
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());
}
@ -1246,7 +1292,14 @@ extern "C" fn attributed_substring_for_proposed_range(
.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(
window_state: Weak<RefCell<WindowState>>,

View file

@ -89,15 +89,14 @@ pub struct TerminalSettings {
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum TerminalBlink {
Never,
On,
Off,
Always,
TerminalControlled,
On,
}
impl Default for TerminalBlink {
fn default() -> Self {
TerminalBlink::On
TerminalBlink::TerminalControlled
}
}

View file

@ -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.
- 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.
The TerminalView struct abstracts over failed and successful terminals, and provides a standardized way of instantiating an always-successful view of a terminal.
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, 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()`.

View file

@ -1,5 +1,5 @@
use alacritty_terminal::{
ansi::{Color as AnsiColor, Color::Named, NamedColor},
ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor},
grid::{Dimensions, Scroll},
index::{Column as GridCol, Line as GridLine, Point, Side},
selection::SelectionRange,
@ -21,7 +21,7 @@ use gpui::{
};
use itertools::Itertools;
use ordered_float::OrderedFloat;
use settings::{Settings, TerminalBlink};
use settings::Settings;
use theme::TerminalStyle;
use util::ResultExt;
@ -201,7 +201,7 @@ pub struct TerminalEl {
view: WeakViewHandle<ConnectedView>,
modal: bool,
focused: bool,
blink_state: bool,
cursor_visible: bool,
}
impl TerminalEl {
@ -210,14 +210,14 @@ impl TerminalEl {
terminal: WeakModelHandle<Terminal>,
modal: bool,
focused: bool,
blink_state: bool,
cursor_visible: bool,
) -> TerminalEl {
TerminalEl {
view,
terminal,
modal,
focused,
blink_state,
cursor_visible,
}
}
@ -571,33 +571,6 @@ impl TerminalEl {
(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 {
@ -610,7 +583,6 @@ impl Element for TerminalEl {
cx: &mut gpui::LayoutContext,
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
let settings = cx.global::<Settings>();
let blink_settings = settings.terminal_overrides.blinking.clone();
let font_cache = cx.font_cache();
//Setup layout information
@ -629,13 +601,13 @@ impl Element for TerminalEl {
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
.upgrade(cx)
.unwrap()
.update(cx.app, |terminal, mcx| {
terminal.set_size(dimensions);
terminal.render_lock(mcx, |content, cursor_text, blink_mode| {
terminal.render_lock(mcx, |content, cursor_text| {
let mut cells = vec![];
cells.extend(
content
@ -659,7 +631,6 @@ impl Element for TerminalEl {
content.cursor,
content.display_offset,
cursor_text,
blink_mode,
)
})
});
@ -674,59 +645,57 @@ impl Element for TerminalEl {
selection,
);
//Layout cursor
let cursor = {
if !TerminalEl::should_show_cursor(
blink_settings,
blink_mode,
self.focused,
self.blink_state,
) {
None
} else {
let cursor_point = DisplayCursor::from(cursor.point, display_offset);
let cursor_text = {
let str_trxt = cursor_text.to_string();
//Layout cursor. Rectangle is used for IME, so we should lay it out even
//if we don't end up showing it.
let cursor = if let AlacCursorShape::Hidden = cursor.shape {
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 {
terminal_theme.colors.background
} else {
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(),
},
)],
)
let color = if self.focused {
terminal_theme.colors.background
} else {
terminal_theme.colors.foreground
};
TerminalEl::shape_cursor(cursor_point, dimensions, &cursor_text).map(
move |(cursor_position, block_width)| {
let (shape, color) = if self.focused {
(CursorShape::Block, terminal_theme.colors.cursor)
} else {
(CursorShape::Hollow, terminal_theme.colors.foreground)
};
Cursor::new(
cursor_position,
block_width,
dimensions.line_height,
cx.text_layout_cache.layout_str(
&str_trxt,
text_style.font_size,
&[(
str_trxt.len(),
RunStyle {
font_id: text_style.font_id,
color,
shape,
Some(cursor_text),
)
},
underline: Default::default(),
},
)],
)
}
};
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!
@ -817,10 +786,12 @@ impl Element for TerminalEl {
});
//Draw cursor
if let Some(cursor) = &layout.cursor {
cx.paint_layer(clip_bounds, |cx| {
cursor.paint(origin, cx);
})
if self.cursor_visible {
if let Some(cursor) = &layout.cursor {
cx.paint_layer(clip_bounds, |cx| {
cursor.paint(origin, cx);
})
}
}
});
}

View file

@ -11,6 +11,7 @@ use gpui::{
AnyViewHandle, AppContext, Element, ElementBox, ModelHandle, MutableAppContext, View,
ViewContext, ViewHandle,
};
use settings::{Settings, TerminalBlink};
use smol::Timer;
use workspace::pane;
@ -29,7 +30,17 @@ pub struct DeployContextMenu {
actions!(
terminal,
[Up, Down, CtrlC, Escape, Enter, Clear, Copy, Paste,]
[
Up,
Down,
CtrlC,
Escape,
Enter,
Clear,
Copy,
Paste,
ShowCharacterPalette
]
);
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::paste);
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
@ -56,7 +68,8 @@ pub struct ConnectedView {
// Only for styling purposes. Doesn't effect behavior
modal: bool,
context_menu: ViewHandle<ContextMenu>,
show_cursor: bool,
blink_state: bool,
blinking_on: bool,
blinking_paused: bool,
blink_epoch: usize,
}
@ -80,7 +93,7 @@ impl ConnectedView {
this.has_bell = true;
cx.emit(Event::Wakeup);
}
Event::BlinkChanged => this.blinking_on = !this.blinking_on,
_ => cx.emit(*event),
})
.detach();
@ -91,7 +104,8 @@ impl ConnectedView {
has_bell: false,
modal,
context_menu: cx.add_view(ContextMenu::new),
show_cursor: true,
blink_state: true,
blinking_on: false,
blinking_paused: false,
blink_epoch: 0,
}
@ -126,19 +140,64 @@ impl ConnectedView {
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>) {
self.terminal.update(cx, |term, _| term.clear());
cx.notify();
}
//Following code copied from editor cursor
pub fn blink_show(&self) -> bool {
self.blinking_paused || self.show_cursor
pub fn should_show_cursor(
&self,
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>) {
if epoch == self.blink_epoch && !self.blinking_paused {
self.show_cursor = !self.show_cursor;
self.blink_state = !self.blink_state;
cx.notify();
let epoch = self.next_blink_epoch();
@ -156,7 +215,7 @@ impl ConnectedView {
}
pub fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
self.show_cursor = true;
self.blink_state = true;
cx.notify();
let epoch = self.next_blink_epoch();
@ -199,41 +258,41 @@ impl ConnectedView {
///Synthesize the keyboard event corresponding to 'up'
fn up(&mut self, _: &Up, cx: &mut ViewContext<Self>) {
self.clear_bel(cx);
self.terminal
.read(cx)
.try_keystroke(&Keystroke::parse("up").unwrap());
self.terminal.update(cx, |term, _| {
term.try_keystroke(&Keystroke::parse("up").unwrap())
});
}
///Synthesize the keyboard event corresponding to 'down'
fn down(&mut self, _: &Down, cx: &mut ViewContext<Self>) {
self.clear_bel(cx);
self.terminal
.read(cx)
.try_keystroke(&Keystroke::parse("down").unwrap());
self.terminal.update(cx, |term, _| {
term.try_keystroke(&Keystroke::parse("down").unwrap())
});
}
///Synthesize the keyboard event corresponding to 'ctrl-c'
fn ctrl_c(&mut self, _: &CtrlC, cx: &mut ViewContext<Self>) {
self.clear_bel(cx);
self.terminal
.read(cx)
.try_keystroke(&Keystroke::parse("ctrl-c").unwrap());
self.terminal.update(cx, |term, _| {
term.try_keystroke(&Keystroke::parse("ctrl-c").unwrap())
});
}
///Synthesize the keyboard event corresponding to 'escape'
fn escape(&mut self, _: &Escape, cx: &mut ViewContext<Self>) {
self.clear_bel(cx);
self.terminal
.read(cx)
.try_keystroke(&Keystroke::parse("escape").unwrap());
self.terminal.update(cx, |term, _| {
term.try_keystroke(&Keystroke::parse("escape").unwrap())
});
}
///Synthesize the keyboard event corresponding to 'enter'
fn enter(&mut self, _: &Enter, cx: &mut ViewContext<Self>) {
self.clear_bel(cx);
self.terminal
.read(cx)
.try_keystroke(&Keystroke::parse("enter").unwrap());
self.terminal.update(cx, |term, _| {
term.try_keystroke(&Keystroke::parse("enter").unwrap())
});
}
}
@ -258,7 +317,7 @@ impl View for ConnectedView {
terminal_handle,
self.modal,
focused,
self.blink_show(),
self.should_show_cursor(focused, cx),
)
.contained()
.boxed(),
@ -299,8 +358,10 @@ impl View for ConnectedView {
text: &str,
cx: &mut ViewContext<Self>,
) {
self.terminal
.update(cx, |terminal, _| terminal.write_to_pty(text.into()));
self.terminal.update(cx, |terminal, _| {
terminal.write_to_pty(text.into());
terminal.scroll(alacritty_terminal::grid::Scroll::Bottom);
});
}
fn keymap_context(&self, _: &gpui::AppContext) -> gpui::keymap::Context {

View file

@ -53,7 +53,6 @@ pub fn to_esc_str(keystroke: &Keystroke, mode: &TermMode) -> Option<String> {
// Manual Bindings including modifiers
let manual_esc_str = match (keystroke.key.as_ref(), &modifiers) {
//Basic special keys
("space", Modifiers::None) => Some(" ".to_string()),
("tab", Modifiers::None) => Some("\x09".to_string()),
("escape", Modifiers::None) => Some("\x1b".to_string()),
("enter", Modifiers::None) => Some("\x0d".to_string()),

View file

@ -62,6 +62,7 @@ pub enum Event {
CloseTerminal,
Bell,
Wakeup,
BlinkChanged,
}
#[derive(Clone, Debug)]
@ -294,15 +295,10 @@ impl TerminalBuilder {
let mut term = Term::new(&config, &initial_size, ZedListener(events_tx.clone()));
//Start off blinking if we need to
match blink_settings {
Some(setting) => match setting {
TerminalBlink::On | TerminalBlink::Always => {
term.set_mode(alacritty_terminal::ansi::Mode::BlinkingCursor)
}
_ => {}
},
None => term.set_mode(alacritty_terminal::ansi::Mode::BlinkingCursor),
if let Some(TerminalBlink::On) = blink_settings {
term.set_mode(alacritty_terminal::ansi::Mode::BlinkingCursor)
}
let term = Arc::new(FairMutex::new(term));
//Setup the pty...
@ -469,17 +465,17 @@ impl Terminal {
AlacTermEvent::ClipboardStore(_, data) => {
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()
.map(|ci| ci.text().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) => {
self.notify_pty(format(self.cur_size.into()))
self.write_to_pty(format(self.cur_size.into()))
}
AlacTermEvent::CursorBlinkingChange => {
//TODO whatever state we need to set to get the cursor blinking
cx.emit(Event::BlinkChanged);
}
AlacTermEvent::Bell => {
cx.emit(Event::Bell);
@ -519,7 +515,7 @@ impl Terminal {
let term_style = &cx.global::<Settings>().theme.terminal;
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) => {
@ -530,7 +526,7 @@ impl Terminal {
term.resize(*new_size);
}
InternalEvent::Clear => {
self.notify_pty("\x0c".to_string());
self.write_to_pty("\x0c".to_string());
term.clear_screen(ClearMode::Saved);
}
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.
pub fn write_to_pty(&mut self, input: String) {
pub fn write_to_pty(&self, input: String) {
self.pty_tx.notify(input.into_bytes());
}
@ -568,10 +560,11 @@ impl Terminal {
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);
if let Some(esc) = esc {
self.notify_pty(esc);
self.write_to_pty(esc);
self.scroll(Scroll::Bottom);
true
} else {
false
@ -581,11 +574,11 @@ impl Terminal {
///Paste text into the terminal
pub fn paste(&self, text: &str) {
if self.last_mode.contains(TermMode::BRACKETED_PASTE) {
self.notify_pty("\x1b[200~".to_string());
self.notify_pty(text.replace('\x1b', ""));
self.notify_pty("\x1b[201~".to_string());
self.write_to_pty("\x1b[200~".to_string());
self.write_to_pty(text.replace('\x1b', ""));
self.write_to_pty("\x1b[201~".to_string());
} 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
where
F: FnOnce(RenderableContent, char, bool) -> T,
F: FnOnce(RenderableContent, char) -> T,
{
let m = self.term.clone(); //Arc clone
let mut term = m.lock();
@ -611,7 +604,7 @@ impl Terminal {
let cursor_text = term.grid()[content.cursor.point].c;
f(content, cursor_text, term.cursor_style().blinking)
f(content, cursor_text)
}
///Scroll the terminal
@ -621,13 +614,13 @@ impl Terminal {
pub fn focus_in(&self) {
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) {
if self.last_mode.contains(TermMode::FOCUS_IN_OUT) {
self.notify_pty("\x1b[O".to_string());
self.write_to_pty("\x1b[O".to_string());
}
}