Add the ability to edit remote directories over SSH (#14530)
This is a first step towards allowing you to edit remote projects
directly over SSH. We'll start with a pretty bare-bones feature set, and
incrementally add further features.
### Todo
Distribution
* [x] Build nightly releases of `zed-remote-server` binaries
* [x] linux (arm + x86)
* [x] mac (arm + x86)
* [x] Build stable + preview releases of `zed-remote-server`
* [x] download and cache remote server binaries as needed when opening
ssh project
* [x] ensure server has the latest version of the binary
Auth
* [x] allow specifying password at the command line
* [x] auth via ssh keys
* [x] UI password prompt
Features
* [x] upload remote server binary to server automatically
* [x] opening directories
* [x] tracking file system updates
* [x] opening, editing, saving buffers
* [ ] file operations (rename, delete, create)
* [ ] git diffs
* [ ] project search
Release Notes:
- N/A
---------
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
co-authored by
Piotr Osiewicz
parent
7733bf686b
commit
b9a53ffa0b
@@ -12,10 +12,9 @@ use core_graphics::{
|
||||
event::{CGEvent, CGEventFlags, CGKeyCode},
|
||||
event_source::{CGEventSource, CGEventSourceStateID},
|
||||
};
|
||||
use ctor::ctor;
|
||||
use metal::foreign_types::ForeignType as _;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use std::{borrow::Cow, mem, ptr};
|
||||
use std::{borrow::Cow, mem, ptr, sync::Once};
|
||||
|
||||
const BACKSPACE_KEY: u16 = 0x7f;
|
||||
const SPACE_KEY: u16 = b' ' as u16;
|
||||
@@ -25,13 +24,22 @@ const ESCAPE_KEY: u16 = 0x1b;
|
||||
const TAB_KEY: u16 = 0x09;
|
||||
const SHIFT_TAB_KEY: u16 = 0x19;
|
||||
|
||||
static mut EVENT_SOURCE: core_graphics::sys::CGEventSourceRef = ptr::null_mut();
|
||||
fn synthesize_keyboard_event(code: CGKeyCode) -> CGEvent {
|
||||
static mut EVENT_SOURCE: core_graphics::sys::CGEventSourceRef = ptr::null_mut();
|
||||
static INIT_EVENT_SOURCE: Once = Once::new();
|
||||
|
||||
#[ctor]
|
||||
unsafe fn build_event_source() {
|
||||
let source = CGEventSource::new(CGEventSourceStateID::Private).unwrap();
|
||||
EVENT_SOURCE = source.as_ptr();
|
||||
INIT_EVENT_SOURCE.call_once(|| {
|
||||
let source = CGEventSource::new(CGEventSourceStateID::Private).unwrap();
|
||||
unsafe {
|
||||
EVENT_SOURCE = source.as_ptr();
|
||||
};
|
||||
mem::forget(source);
|
||||
});
|
||||
|
||||
let source = unsafe { core_graphics::event_source::CGEventSource::from_ptr(EVENT_SOURCE) };
|
||||
let event = CGEvent::new_keyboard_event(source.clone(), code, true).unwrap();
|
||||
mem::forget(source);
|
||||
event
|
||||
}
|
||||
|
||||
pub fn key_to_native(key: &str) -> Cow<str> {
|
||||
@@ -335,9 +343,7 @@ fn chars_for_modified_key(code: CGKeyCode, cmd: bool, shift: bool) -> String {
|
||||
// always returns an empty string with certain keyboards, e.g. Japanese. Synthesizing
|
||||
// an event with the given flags instead lets us access `characters`, which always
|
||||
// returns a valid string.
|
||||
let source = unsafe { core_graphics::event_source::CGEventSource::from_ptr(EVENT_SOURCE) };
|
||||
let event = CGEvent::new_keyboard_event(source.clone(), code, true).unwrap();
|
||||
mem::forget(source);
|
||||
let event = synthesize_keyboard_event(code);
|
||||
|
||||
let mut flags = CGEventFlags::empty();
|
||||
if cmd {
|
||||
|
||||
@@ -24,6 +24,7 @@ use core_foundation::{
|
||||
boolean::CFBoolean,
|
||||
data::CFData,
|
||||
dictionary::{CFDictionary, CFDictionaryRef, CFMutableDictionary},
|
||||
runloop::CFRunLoopRun,
|
||||
string::{CFString, CFStringRef},
|
||||
};
|
||||
use ctor::ctor;
|
||||
@@ -139,6 +140,7 @@ pub(crate) struct MacPlatformState {
|
||||
foreground_executor: ForegroundExecutor,
|
||||
text_system: Arc<MacTextSystem>,
|
||||
renderer_context: renderer::Context,
|
||||
headless: bool,
|
||||
pasteboard: id,
|
||||
text_hash_pasteboard_type: id,
|
||||
metadata_pasteboard_type: id,
|
||||
@@ -155,15 +157,16 @@ pub(crate) struct MacPlatformState {
|
||||
|
||||
impl Default for MacPlatform {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl MacPlatform {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub(crate) fn new(headless: bool) -> Self {
|
||||
let dispatcher = Arc::new(MacDispatcher::new());
|
||||
Self(Mutex::new(MacPlatformState {
|
||||
background_executor: BackgroundExecutor::new(dispatcher.clone()),
|
||||
headless,
|
||||
foreground_executor: ForegroundExecutor::new(dispatcher),
|
||||
text_system: Arc::new(MacTextSystem::new()),
|
||||
renderer_context: renderer::Context::default(),
|
||||
@@ -394,7 +397,15 @@ impl Platform for MacPlatform {
|
||||
}
|
||||
|
||||
fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
|
||||
self.0.lock().finish_launching = Some(on_finish_launching);
|
||||
let mut state = self.0.lock();
|
||||
if state.headless {
|
||||
drop(state);
|
||||
on_finish_launching();
|
||||
unsafe { CFRunLoopRun() };
|
||||
} else {
|
||||
state.finish_launching = Some(on_finish_launching);
|
||||
drop(state);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let app: id = msg_send![APP_CLASS, sharedApplication];
|
||||
@@ -1238,7 +1249,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn build_platform() -> MacPlatform {
|
||||
let platform = MacPlatform::new();
|
||||
let platform = MacPlatform::new(false);
|
||||
platform.0.lock().pasteboard = unsafe { NSPasteboard::pasteboardWithUniqueName(nil) };
|
||||
platform
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user