2022-12-09 00:10:22 +00:00
|
|
|
mod persistence;
|
|
|
|
pub mod terminal_container_view;
|
|
|
|
pub mod terminal_element;
|
|
|
|
|
2022-12-08 23:18:24 +00:00
|
|
|
use std::{ops::RangeInclusive, time::Duration};
|
2022-08-04 18:22:09 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
use context_menu::{ContextMenu, ContextMenuItem};
|
2022-07-26 00:24:21 +00:00
|
|
|
use gpui::{
|
2022-08-22 23:49:01 +00:00
|
|
|
actions,
|
2022-09-10 00:29:52 +00:00
|
|
|
elements::{AnchorCorner, ChildView, ParentElement, Stack},
|
2022-08-22 23:49:01 +00:00
|
|
|
geometry::vector::Vector2F,
|
2022-09-27 03:01:05 +00:00
|
|
|
impl_actions, impl_internal_actions,
|
2022-08-22 23:49:01 +00:00
|
|
|
keymap::Keystroke,
|
2022-09-01 00:41:53 +00:00
|
|
|
AnyViewHandle, AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, Task,
|
|
|
|
View, ViewContext, ViewHandle,
|
2022-07-26 00:24:21 +00:00
|
|
|
};
|
2022-09-27 03:01:05 +00:00
|
|
|
use serde::Deserialize;
|
2022-08-22 23:49:01 +00:00
|
|
|
use settings::{Settings, TerminalBlink};
|
|
|
|
use smol::Timer;
|
2022-12-08 18:48:28 +00:00
|
|
|
use terminal::{
|
|
|
|
alacritty_terminal::{
|
|
|
|
index::Point,
|
|
|
|
term::{search::RegexSearch, TermMode},
|
|
|
|
},
|
2022-12-09 03:05:26 +00:00
|
|
|
Event, Terminal,
|
2022-12-08 18:48:28 +00:00
|
|
|
};
|
2022-09-27 03:39:40 +00:00
|
|
|
use util::ResultExt;
|
2022-12-08 23:18:24 +00:00
|
|
|
use workspace::{pane, ItemId, WorkspaceId};
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-12-09 03:05:26 +00:00
|
|
|
use crate::{persistence::TERMINAL_DB, terminal_element::TerminalElement};
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
///Event to transmit the scroll from the element to the view
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
pub struct ScrollTerminal(pub i32);
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
#[derive(Clone, PartialEq)]
|
|
|
|
pub struct DeployContextMenu {
|
|
|
|
pub position: Vector2F,
|
2022-08-04 18:22:09 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 03:01:05 +00:00
|
|
|
#[derive(Clone, Default, Deserialize, PartialEq)]
|
|
|
|
pub struct SendText(String);
|
|
|
|
|
2022-09-27 03:39:40 +00:00
|
|
|
#[derive(Clone, Default, Deserialize, PartialEq)]
|
|
|
|
pub struct SendKeystroke(String);
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
actions!(
|
|
|
|
terminal,
|
2022-10-17 21:53:52 +00:00
|
|
|
[Clear, Copy, Paste, ShowCharacterPalette, SearchTest]
|
2022-08-22 23:49:01 +00:00
|
|
|
);
|
2022-09-27 03:01:05 +00:00
|
|
|
|
2022-09-27 03:39:40 +00:00
|
|
|
impl_actions!(terminal, [SendText, SendKeystroke]);
|
2022-09-27 03:01:05 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
impl_internal_actions!(project_panel, [DeployContextMenu]);
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
pub fn init(cx: &mut MutableAppContext) {
|
2022-12-09 00:10:22 +00:00
|
|
|
terminal_container_view::init(cx);
|
2022-08-22 23:49:01 +00:00
|
|
|
//Useful terminal views
|
2022-09-27 03:01:05 +00:00
|
|
|
cx.add_action(TerminalView::send_text);
|
2022-09-27 03:39:40 +00:00
|
|
|
cx.add_action(TerminalView::send_keystroke);
|
2022-08-22 23:49:01 +00:00
|
|
|
cx.add_action(TerminalView::deploy_context_menu);
|
|
|
|
cx.add_action(TerminalView::copy);
|
|
|
|
cx.add_action(TerminalView::paste);
|
|
|
|
cx.add_action(TerminalView::clear);
|
|
|
|
cx.add_action(TerminalView::show_character_palette);
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
///A terminal view, maintains the PTY's file handles and communicates with the terminal
|
2022-07-26 00:24:21 +00:00
|
|
|
pub struct TerminalView {
|
2022-08-22 23:49:01 +00:00
|
|
|
terminal: ModelHandle<Terminal>,
|
|
|
|
has_new_content: bool,
|
|
|
|
//Currently using iTerm bell, show bell emoji in tab until input is received
|
|
|
|
has_bell: bool,
|
|
|
|
// Only for styling purposes. Doesn't effect behavior
|
2022-07-26 00:24:21 +00:00
|
|
|
modal: bool,
|
2022-08-22 23:49:01 +00:00
|
|
|
context_menu: ViewHandle<ContextMenu>,
|
|
|
|
blink_state: bool,
|
|
|
|
blinking_on: bool,
|
|
|
|
blinking_paused: bool,
|
|
|
|
blink_epoch: usize,
|
2022-12-08 23:18:24 +00:00
|
|
|
workspace_id: WorkspaceId,
|
|
|
|
item_id: ItemId,
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Entity for TerminalView {
|
|
|
|
type Event = Event;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TerminalView {
|
|
|
|
pub fn from_terminal(
|
|
|
|
terminal: ModelHandle<Terminal>,
|
|
|
|
modal: bool,
|
2022-12-08 23:18:24 +00:00
|
|
|
workspace_id: WorkspaceId,
|
|
|
|
item_id: ItemId,
|
2022-07-26 00:24:21 +00:00
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
) -> Self {
|
2022-08-22 23:49:01 +00:00
|
|
|
cx.observe(&terminal, |_, _, cx| cx.notify()).detach();
|
|
|
|
cx.subscribe(&terminal, |this, _, event, cx| match event {
|
|
|
|
Event::Wakeup => {
|
|
|
|
if !cx.is_self_focused() {
|
|
|
|
this.has_new_content = true;
|
|
|
|
cx.notify();
|
|
|
|
}
|
2022-09-03 20:46:36 +00:00
|
|
|
cx.emit(Event::Wakeup);
|
2022-08-22 23:49:01 +00:00
|
|
|
}
|
|
|
|
Event::Bell => {
|
|
|
|
this.has_bell = true;
|
|
|
|
cx.emit(Event::Wakeup);
|
|
|
|
}
|
|
|
|
Event::BlinkChanged => this.blinking_on = !this.blinking_on,
|
2022-12-08 18:48:28 +00:00
|
|
|
Event::TitleChanged => {
|
2022-12-08 23:18:24 +00:00
|
|
|
if let Some(foreground_info) = &this.terminal().read(cx).foreground_process_info {
|
|
|
|
let cwd = foreground_info.cwd.clone();
|
|
|
|
|
|
|
|
let item_id = this.item_id;
|
|
|
|
let workspace_id = this.workspace_id;
|
|
|
|
cx.background()
|
|
|
|
.spawn(async move {
|
|
|
|
TERMINAL_DB
|
|
|
|
.save_working_directory(item_id, workspace_id, cwd)
|
|
|
|
.await
|
|
|
|
.log_err();
|
|
|
|
})
|
|
|
|
.detach();
|
|
|
|
}
|
2022-12-08 18:48:28 +00:00
|
|
|
}
|
2022-08-22 23:49:01 +00:00
|
|
|
_ => cx.emit(*event),
|
|
|
|
})
|
|
|
|
.detach();
|
|
|
|
|
|
|
|
Self {
|
|
|
|
terminal,
|
|
|
|
has_new_content: true,
|
|
|
|
has_bell: false,
|
2022-07-26 00:24:21 +00:00
|
|
|
modal,
|
2022-08-22 23:49:01 +00:00
|
|
|
context_menu: cx.add_view(ContextMenu::new),
|
|
|
|
blink_state: true,
|
|
|
|
blinking_on: false,
|
|
|
|
blinking_paused: false,
|
|
|
|
blink_epoch: 0,
|
2022-12-08 23:18:24 +00:00
|
|
|
workspace_id,
|
|
|
|
item_id,
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
pub fn handle(&self) -> ModelHandle<Terminal> {
|
|
|
|
self.terminal.clone()
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
pub fn has_new_content(&self) -> bool {
|
|
|
|
self.has_new_content
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
pub fn has_bell(&self) -> bool {
|
|
|
|
self.has_bell
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
pub fn clear_bel(&mut self, cx: &mut ViewContext<TerminalView>) {
|
|
|
|
self.has_bell = false;
|
|
|
|
cx.emit(Event::Wakeup);
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
pub fn deploy_context_menu(&mut self, action: &DeployContextMenu, cx: &mut ViewContext<Self>) {
|
|
|
|
let menu_entries = vec![
|
2022-10-14 20:52:30 +00:00
|
|
|
ContextMenuItem::item("Clear", Clear),
|
|
|
|
ContextMenuItem::item("Close", pane::CloseActiveItem),
|
2022-08-22 23:49:01 +00:00
|
|
|
];
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-09-10 00:29:52 +00:00
|
|
|
self.context_menu.update(cx, |menu, cx| {
|
|
|
|
menu.show(action.position, AnchorCorner::TopLeft, menu_entries, cx)
|
|
|
|
});
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
cx.notify();
|
|
|
|
}
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
|
|
|
|
if !self
|
|
|
|
.terminal
|
|
|
|
.read(cx)
|
2022-09-01 18:43:27 +00:00
|
|
|
.last_content
|
|
|
|
.mode
|
2022-08-22 23:49:01 +00:00
|
|
|
.contains(TermMode::ALT_SCREEN)
|
|
|
|
{
|
|
|
|
cx.show_character_palette();
|
|
|
|
} else {
|
2022-09-13 06:22:55 +00:00
|
|
|
self.terminal.update(cx, |term, cx| {
|
|
|
|
term.try_keystroke(
|
|
|
|
&Keystroke::parse("ctrl-cmd-space").unwrap(),
|
|
|
|
cx.global::<Settings>()
|
|
|
|
.terminal_overrides
|
|
|
|
.option_as_meta
|
|
|
|
.unwrap_or(false),
|
|
|
|
)
|
2022-08-22 23:49:01 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
fn clear(&mut self, _: &Clear, cx: &mut ViewContext<Self>) {
|
|
|
|
self.terminal.update(cx, |term, _| term.clear());
|
|
|
|
cx.notify();
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
pub fn should_show_cursor(
|
2022-07-26 00:24:21 +00:00
|
|
|
&self,
|
2022-08-22 23:49:01 +00:00
|
|
|
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)
|
2022-09-01 18:43:27 +00:00
|
|
|
.last_content
|
|
|
|
.mode
|
2022-08-22 23:49:01 +00:00
|
|
|
.contains(TermMode::ALT_SCREEN)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
let setting = {
|
|
|
|
let settings = cx.global::<Settings>();
|
|
|
|
settings
|
|
|
|
.terminal_overrides
|
|
|
|
.blinking
|
|
|
|
.clone()
|
|
|
|
.unwrap_or(TerminalBlink::TerminalControlled)
|
|
|
|
};
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
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,
|
|
|
|
}
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
|
|
|
|
if epoch == self.blink_epoch && !self.blinking_paused {
|
|
|
|
self.blink_state = !self.blink_state;
|
|
|
|
cx.notify();
|
|
|
|
|
|
|
|
let epoch = self.next_blink_epoch();
|
|
|
|
cx.spawn(|this, mut cx| {
|
|
|
|
let this = this.downgrade();
|
|
|
|
async move {
|
|
|
|
Timer::after(CURSOR_BLINK_INTERVAL).await;
|
|
|
|
if let Some(this) = this.upgrade(&cx) {
|
|
|
|
this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.detach();
|
|
|
|
}
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
pub fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
|
|
|
|
self.blink_state = true;
|
|
|
|
cx.notify();
|
|
|
|
|
|
|
|
let epoch = self.next_blink_epoch();
|
|
|
|
cx.spawn(|this, mut cx| {
|
|
|
|
let this = this.downgrade();
|
|
|
|
async move {
|
|
|
|
Timer::after(CURSOR_BLINK_INTERVAL).await;
|
|
|
|
if let Some(this) = this.upgrade(&cx) {
|
|
|
|
this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.detach();
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-09-01 00:41:53 +00:00
|
|
|
pub fn find_matches(
|
|
|
|
&mut self,
|
|
|
|
query: project::search::SearchQuery,
|
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
) -> Task<Vec<RangeInclusive<Point>>> {
|
2022-12-08 18:48:28 +00:00
|
|
|
let searcher = regex_search_for_query(query);
|
|
|
|
|
|
|
|
if let Some(searcher) = searcher {
|
|
|
|
self.terminal
|
|
|
|
.update(cx, |term, cx| term.find_matches(searcher, cx))
|
|
|
|
} else {
|
|
|
|
cx.background().spawn(async { Vec::new() })
|
|
|
|
}
|
2022-09-01 00:41:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn terminal(&self) -> &ModelHandle<Terminal> {
|
|
|
|
&self.terminal
|
|
|
|
}
|
|
|
|
|
2022-12-08 23:18:24 +00:00
|
|
|
pub fn added_to_workspace(&mut self, new_id: WorkspaceId, cx: &mut ViewContext<Self>) {
|
|
|
|
cx.background()
|
|
|
|
.spawn(TERMINAL_DB.update_workspace_id(new_id, self.workspace_id, self.item_id))
|
|
|
|
.detach();
|
|
|
|
self.workspace_id = new_id;
|
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
fn next_blink_epoch(&mut self) -> usize {
|
|
|
|
self.blink_epoch += 1;
|
|
|
|
self.blink_epoch
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
|
|
|
|
if epoch == self.blink_epoch {
|
|
|
|
self.blinking_paused = false;
|
|
|
|
self.blink_cursors(epoch, cx);
|
|
|
|
}
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
///Attempt to paste the clipboard into the terminal
|
|
|
|
fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
|
|
|
|
self.terminal.update(cx, |term, _| term.copy())
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
///Attempt to paste the clipboard into the terminal
|
|
|
|
fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
|
|
|
|
if let Some(item) = cx.read_from_clipboard() {
|
|
|
|
self.terminal
|
|
|
|
.update(cx, |terminal, _cx| terminal.paste(item.text()));
|
|
|
|
}
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 03:01:05 +00:00
|
|
|
fn send_text(&mut self, text: &SendText, cx: &mut ViewContext<Self>) {
|
2022-08-22 23:49:01 +00:00
|
|
|
self.clear_bel(cx);
|
|
|
|
self.terminal.update(cx, |term, _| {
|
2022-09-27 03:01:05 +00:00
|
|
|
term.input(text.0.to_string());
|
2022-08-22 23:49:01 +00:00
|
|
|
});
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 03:39:40 +00:00
|
|
|
fn send_keystroke(&mut self, text: &SendKeystroke, cx: &mut ViewContext<Self>) {
|
|
|
|
if let Some(keystroke) = Keystroke::parse(&text.0).log_err() {
|
|
|
|
self.clear_bel(cx);
|
|
|
|
self.terminal.update(cx, |term, cx| {
|
|
|
|
term.try_keystroke(
|
|
|
|
&keystroke,
|
|
|
|
cx.global::<Settings>()
|
|
|
|
.terminal_overrides
|
|
|
|
.option_as_meta
|
|
|
|
.unwrap_or(false),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-08 18:48:28 +00:00
|
|
|
pub fn regex_search_for_query(query: project::search::SearchQuery) -> Option<RegexSearch> {
|
|
|
|
let searcher = match query {
|
|
|
|
project::search::SearchQuery::Text { query, .. } => RegexSearch::new(&query),
|
|
|
|
project::search::SearchQuery::Regex { query, .. } => RegexSearch::new(&query),
|
|
|
|
};
|
|
|
|
searcher.ok()
|
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
impl View for TerminalView {
|
|
|
|
fn ui_name() -> &'static str {
|
|
|
|
"Terminal"
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
|
|
|
|
let terminal_handle = self.terminal.clone().downgrade();
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
let self_id = cx.view_id();
|
|
|
|
let focused = cx
|
|
|
|
.focused_view_id(cx.window_id())
|
|
|
|
.filter(|view_id| *view_id == self_id)
|
|
|
|
.is_some();
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
Stack::new()
|
|
|
|
.with_child(
|
|
|
|
TerminalElement::new(
|
|
|
|
cx.handle(),
|
|
|
|
terminal_handle,
|
|
|
|
focused,
|
|
|
|
self.should_show_cursor(focused, cx),
|
|
|
|
)
|
|
|
|
.contained()
|
|
|
|
.boxed(),
|
|
|
|
)
|
2022-10-13 13:40:21 +00:00
|
|
|
.with_child(ChildView::new(&self.context_menu, cx).boxed())
|
2022-08-22 23:49:01 +00:00
|
|
|
.boxed()
|
|
|
|
}
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-10-16 17:46:31 +00:00
|
|
|
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
2022-08-22 23:49:01 +00:00
|
|
|
self.has_new_content = false;
|
|
|
|
self.terminal.read(cx).focus_in();
|
|
|
|
self.blink_cursors(self.blink_epoch, cx);
|
|
|
|
cx.notify();
|
|
|
|
}
|
2022-07-26 00:24:21 +00:00
|
|
|
|
2022-10-16 17:46:31 +00:00
|
|
|
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
2022-09-24 15:32:06 +00:00
|
|
|
self.terminal.update(cx, |terminal, _| {
|
|
|
|
terminal.focus_out();
|
|
|
|
});
|
2022-08-22 23:49:01 +00:00
|
|
|
cx.notify();
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-10-16 18:55:02 +00:00
|
|
|
fn key_down(&mut self, event: &gpui::KeyDownEvent, cx: &mut ViewContext<Self>) -> bool {
|
|
|
|
self.clear_bel(cx);
|
|
|
|
self.pause_cursor_blinking(cx);
|
|
|
|
|
|
|
|
self.terminal.update(cx, |term, cx| {
|
|
|
|
term.try_keystroke(
|
|
|
|
&event.keystroke,
|
|
|
|
cx.global::<Settings>()
|
|
|
|
.terminal_overrides
|
|
|
|
.option_as_meta
|
|
|
|
.unwrap_or(false),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
//IME stuff
|
|
|
|
fn selected_text_range(&self, cx: &AppContext) -> Option<std::ops::Range<usize>> {
|
|
|
|
if self
|
|
|
|
.terminal
|
|
|
|
.read(cx)
|
2022-09-01 18:43:27 +00:00
|
|
|
.last_content
|
|
|
|
.mode
|
2022-08-22 23:49:01 +00:00
|
|
|
.contains(TermMode::ALT_SCREEN)
|
|
|
|
{
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(0..0)
|
|
|
|
}
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
fn replace_text_in_range(
|
|
|
|
&mut self,
|
|
|
|
_: Option<std::ops::Range<usize>>,
|
|
|
|
text: &str,
|
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
) {
|
|
|
|
self.terminal.update(cx, |terminal, _| {
|
|
|
|
terminal.input(text.into());
|
2022-07-26 00:24:21 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-08-22 23:49:01 +00:00
|
|
|
fn keymap_context(&self, cx: &gpui::AppContext) -> gpui::keymap::Context {
|
|
|
|
let mut context = Self::default_keymap_context();
|
|
|
|
if self.modal {
|
|
|
|
context.set.insert("ModalTerminal".into());
|
|
|
|
}
|
2022-09-01 18:43:27 +00:00
|
|
|
let mode = self.terminal.read(cx).last_content.mode;
|
2022-08-22 23:49:01 +00:00
|
|
|
context.map.insert(
|
|
|
|
"screen".to_string(),
|
|
|
|
(if mode.contains(TermMode::ALT_SCREEN) {
|
|
|
|
"alt"
|
|
|
|
} else {
|
|
|
|
"normal"
|
|
|
|
})
|
|
|
|
.to_string(),
|
|
|
|
);
|
|
|
|
|
|
|
|
if mode.contains(TermMode::APP_CURSOR) {
|
|
|
|
context.set.insert("DECCKM".to_string());
|
|
|
|
}
|
|
|
|
if mode.contains(TermMode::APP_KEYPAD) {
|
|
|
|
context.set.insert("DECPAM".to_string());
|
|
|
|
}
|
|
|
|
//Note the ! here
|
|
|
|
if !mode.contains(TermMode::APP_KEYPAD) {
|
|
|
|
context.set.insert("DECPNM".to_string());
|
|
|
|
}
|
|
|
|
if mode.contains(TermMode::SHOW_CURSOR) {
|
|
|
|
context.set.insert("DECTCEM".to_string());
|
|
|
|
}
|
|
|
|
if mode.contains(TermMode::LINE_WRAP) {
|
|
|
|
context.set.insert("DECAWM".to_string());
|
|
|
|
}
|
|
|
|
if mode.contains(TermMode::ORIGIN) {
|
|
|
|
context.set.insert("DECOM".to_string());
|
|
|
|
}
|
|
|
|
if mode.contains(TermMode::INSERT) {
|
|
|
|
context.set.insert("IRM".to_string());
|
|
|
|
}
|
|
|
|
//LNM is apparently the name for this. https://vt100.net/docs/vt510-rm/LNM.html
|
|
|
|
if mode.contains(TermMode::LINE_FEED_NEW_LINE) {
|
|
|
|
context.set.insert("LNM".to_string());
|
|
|
|
}
|
|
|
|
if mode.contains(TermMode::FOCUS_IN_OUT) {
|
|
|
|
context.set.insert("report_focus".to_string());
|
|
|
|
}
|
|
|
|
if mode.contains(TermMode::ALTERNATE_SCROLL) {
|
|
|
|
context.set.insert("alternate_scroll".to_string());
|
|
|
|
}
|
|
|
|
if mode.contains(TermMode::BRACKETED_PASTE) {
|
|
|
|
context.set.insert("bracketed_paste".to_string());
|
|
|
|
}
|
|
|
|
if mode.intersects(TermMode::MOUSE_MODE) {
|
|
|
|
context.set.insert("any_mouse_reporting".to_string());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let mouse_reporting = if mode.contains(TermMode::MOUSE_REPORT_CLICK) {
|
|
|
|
"click"
|
|
|
|
} else if mode.contains(TermMode::MOUSE_DRAG) {
|
|
|
|
"drag"
|
|
|
|
} else if mode.contains(TermMode::MOUSE_MOTION) {
|
|
|
|
"motion"
|
|
|
|
} else {
|
|
|
|
"off"
|
|
|
|
};
|
|
|
|
context
|
|
|
|
.map
|
|
|
|
.insert("mouse_reporting".to_string(), mouse_reporting.to_string());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let format = if mode.contains(TermMode::SGR_MOUSE) {
|
|
|
|
"sgr"
|
|
|
|
} else if mode.contains(TermMode::UTF8_MOUSE) {
|
|
|
|
"utf8"
|
|
|
|
} else {
|
|
|
|
"normal"
|
|
|
|
};
|
|
|
|
context
|
|
|
|
.map
|
|
|
|
.insert("mouse_format".to_string(), format.to_string());
|
|
|
|
}
|
|
|
|
context
|
2022-07-26 00:24:21 +00:00
|
|
|
}
|
|
|
|
}
|