Compare commits
14 Commits
fix-git-ht
...
v0.146.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c8d9377fa | ||
|
|
0d1f77811f | ||
|
|
3b5aa8dace | ||
|
|
738552b253 | ||
|
|
796adf4adb | ||
|
|
8368e4c7b4 | ||
|
|
6300a672b2 | ||
|
|
e80c0f5c22 | ||
|
|
064d7ba445 | ||
|
|
4708951322 | ||
|
|
054e083a78 | ||
|
|
66a41c9a95 | ||
|
|
3a8acc94e2 | ||
|
|
2291137fb7 |
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@@ -40,10 +40,15 @@ jobs:
|
||||
|
||||
- name: Check spelling
|
||||
run: |
|
||||
if ! which typos > /dev/null; then
|
||||
cargo install typos-cli
|
||||
if ! cargo install --list | grep "typos-cli v$TYPOS_CLI_VERSION" > /dev/null; then
|
||||
echo "Installing typos-cli@$TYPOS_CLI_VERSION..."
|
||||
cargo install "typos-cli@$TYPOS_CLI_VERSION"
|
||||
else
|
||||
echo "typos-cli@$TYPOS_CLI_VERSION is already installed."
|
||||
fi
|
||||
typos
|
||||
env:
|
||||
TYPOS_CLI_VERSION: "1.23.3"
|
||||
|
||||
- name: Run style checks
|
||||
uses: ./.github/actions/check_style
|
||||
|
||||
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -8657,6 +8657,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"async-dispatcher",
|
||||
"base64 0.13.1",
|
||||
"client",
|
||||
"collections",
|
||||
"command_palette_hooks",
|
||||
"editor",
|
||||
@@ -8916,9 +8917,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "runtimelib"
|
||||
version = "0.12.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a4a788465cf51b7ac8f36e4e4ca3dd26013dcddd5ba8376f98752278244294"
|
||||
checksum = "0c3d817764e3971867351e6103955b17d808f5330e9ef63aaaaab55bf8c664c1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-dispatcher",
|
||||
@@ -13596,7 +13597,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.146.0"
|
||||
version = "0.146.3"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
|
||||
@@ -362,7 +362,7 @@ refineable = { path = "./crates/refineable" }
|
||||
regex = "1.5"
|
||||
repair_json = "0.1.0"
|
||||
rsa = "0.9.6"
|
||||
runtimelib = { version = "0.12", default-features = false, features = [
|
||||
runtimelib = { version = "0.14", default-features = false, features = [
|
||||
"async-dispatcher-runtime",
|
||||
] }
|
||||
rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
"backspace": "editor::Backspace",
|
||||
"shift-backspace": "editor::Backspace",
|
||||
"delete": "editor::Delete",
|
||||
"ctrl-d": "editor::Delete",
|
||||
"tab": "editor::Tab",
|
||||
"shift-tab": "editor::TabPrev",
|
||||
"ctrl-k": "editor::CutToEndOfLine",
|
||||
@@ -250,13 +249,6 @@
|
||||
"ctrl-alt-shift-x": "search::ToggleRegex"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Terminal",
|
||||
"bindings": {
|
||||
"ctrl-w": ["terminal::SendKeystroke", "ctrl-w"],
|
||||
"ctrl-e": ["terminal::SendKeystroke", "ctrl-e"]
|
||||
}
|
||||
},
|
||||
// Bindings from VS Code
|
||||
{
|
||||
"context": "Editor",
|
||||
@@ -274,6 +266,7 @@
|
||||
"alt-shift-left": "editor::SelectSmallerSyntaxNode", // Shrink Selection
|
||||
"ctrl-shift-l": "editor::SelectAllMatches", // Select all occurrences of current selection
|
||||
"ctrl-f2": "editor::SelectAllMatches", // Select all occurrences of current word
|
||||
"ctrl-d": ["editor::SelectNext", { "replace_newest": false }],
|
||||
"ctrl-shift-down": ["editor::SelectNext", { "replace_newest": false }], // Add selection to Next Find Match
|
||||
"ctrl-shift-up": ["editor::SelectPrevious", { "replace_newest": false }],
|
||||
"ctrl-k ctrl-d": ["editor::SelectNext", { "replace_newest": true }],
|
||||
@@ -465,12 +458,16 @@
|
||||
{
|
||||
"bindings": {
|
||||
"ctrl-alt-shift-f": "workspace::FollowNextCollaborator",
|
||||
// TODO: Move this to a dock open action
|
||||
"ctrl-shift-c": "collab_panel::ToggleFocus",
|
||||
"ctrl-alt-i": "zed::DebugElements",
|
||||
"ctrl-:": "editor::ToggleInlayHints"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "!Terminal",
|
||||
"bindings": {
|
||||
"ctrl-shift-c": "collab_panel::ToggleFocus"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && mode == full",
|
||||
"bindings": {
|
||||
@@ -604,12 +601,14 @@
|
||||
"context": "Terminal",
|
||||
"bindings": {
|
||||
"ctrl-alt-space": "terminal::ShowCharacterPalette",
|
||||
"shift-ctrl-c": "terminal::Copy",
|
||||
"ctrl-shift-c": "terminal::Copy",
|
||||
"ctrl-insert": "terminal::Copy",
|
||||
"ctrl-a": "editor::SelectAll",
|
||||
"shift-ctrl-v": "terminal::Paste",
|
||||
// "ctrl-a": "editor::SelectAll", // conflicts with readline
|
||||
"ctrl-shift-v": "terminal::Paste",
|
||||
"shift-insert": "terminal::Paste",
|
||||
"ctrl-enter": "assistant::InlineAssist",
|
||||
"ctrl-w": ["terminal::SendKeystroke", "ctrl-w"],
|
||||
"ctrl-e": ["terminal::SendKeystroke", "ctrl-e"],
|
||||
"up": ["terminal::SendKeystroke", "up"],
|
||||
"pageup": ["terminal::SendKeystroke", "pageup"],
|
||||
"down": ["terminal::SendKeystroke", "down"],
|
||||
|
||||
@@ -55,6 +55,8 @@ struct UpdateRequestBody {
|
||||
installation_id: Option<Arc<str>>,
|
||||
release_channel: Option<&'static str>,
|
||||
telemetry: bool,
|
||||
is_staff: Option<bool>,
|
||||
destination: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
@@ -575,18 +577,27 @@ async fn download_remote_server_binary(
|
||||
cx: &AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
let mut target_file = File::create(&target_path).await?;
|
||||
let (installation_id, release_channel, telemetry) = cx.update(|cx| {
|
||||
let installation_id = Client::global(cx).telemetry().installation_id();
|
||||
let (installation_id, release_channel, telemetry_enabled, is_staff) = cx.update(|cx| {
|
||||
let telemetry = Client::global(cx).telemetry().clone();
|
||||
let is_staff = telemetry.is_staff();
|
||||
let installation_id = telemetry.installation_id();
|
||||
let release_channel =
|
||||
ReleaseChannel::try_global(cx).map(|release_channel| release_channel.display_name());
|
||||
let telemetry = TelemetrySettings::get_global(cx).metrics;
|
||||
let telemetry_enabled = TelemetrySettings::get_global(cx).metrics;
|
||||
|
||||
(installation_id, release_channel, telemetry)
|
||||
(
|
||||
installation_id,
|
||||
release_channel,
|
||||
telemetry_enabled,
|
||||
is_staff,
|
||||
)
|
||||
})?;
|
||||
let request_body = AsyncBody::from(serde_json::to_string(&UpdateRequestBody {
|
||||
installation_id,
|
||||
release_channel,
|
||||
telemetry,
|
||||
telemetry: telemetry_enabled,
|
||||
is_staff,
|
||||
destination: "remote",
|
||||
})?);
|
||||
|
||||
let mut response = client.get(&release.url, request_body, true).await?;
|
||||
@@ -602,19 +613,28 @@ async fn download_release(
|
||||
) -> Result<()> {
|
||||
let mut target_file = File::create(&target_path).await?;
|
||||
|
||||
let (installation_id, release_channel, telemetry) = cx.update(|cx| {
|
||||
let installation_id = Client::global(cx).telemetry().installation_id();
|
||||
let (installation_id, release_channel, telemetry_enabled, is_staff) = cx.update(|cx| {
|
||||
let telemetry = Client::global(cx).telemetry().clone();
|
||||
let is_staff = telemetry.is_staff();
|
||||
let installation_id = telemetry.installation_id();
|
||||
let release_channel =
|
||||
ReleaseChannel::try_global(cx).map(|release_channel| release_channel.display_name());
|
||||
let telemetry = TelemetrySettings::get_global(cx).metrics;
|
||||
let telemetry_enabled = TelemetrySettings::get_global(cx).metrics;
|
||||
|
||||
(installation_id, release_channel, telemetry)
|
||||
(
|
||||
installation_id,
|
||||
release_channel,
|
||||
telemetry_enabled,
|
||||
is_staff,
|
||||
)
|
||||
})?;
|
||||
|
||||
let request_body = AsyncBody::from(serde_json::to_string(&UpdateRequestBody {
|
||||
installation_id,
|
||||
release_channel,
|
||||
telemetry,
|
||||
telemetry: telemetry_enabled,
|
||||
is_staff,
|
||||
destination: "local",
|
||||
})?);
|
||||
|
||||
let mut response = client.get(&release.url, request_body, true).await?;
|
||||
|
||||
@@ -18,7 +18,7 @@ use sysinfo::{CpuRefreshKind, Pid, ProcessRefreshKind, RefreshKind, System};
|
||||
use telemetry_events::{
|
||||
ActionEvent, AppEvent, AssistantEvent, AssistantKind, CallEvent, CpuEvent, EditEvent,
|
||||
EditorEvent, Event, EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent,
|
||||
MemoryEvent, SettingEvent,
|
||||
MemoryEvent, ReplEvent, SettingEvent,
|
||||
};
|
||||
use tempfile::NamedTempFile;
|
||||
#[cfg(not(debug_assertions))]
|
||||
@@ -531,6 +531,21 @@ impl Telemetry {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_repl_event(
|
||||
self: &Arc<Self>,
|
||||
kernel_language: String,
|
||||
kernel_status: String,
|
||||
repl_session_id: String,
|
||||
) {
|
||||
let event = Event::Repl(ReplEvent {
|
||||
kernel_language,
|
||||
kernel_status,
|
||||
repl_session_id,
|
||||
});
|
||||
|
||||
self.report_event(event)
|
||||
}
|
||||
|
||||
fn report_event(self: &Arc<Self>, event: Event) {
|
||||
let mut state = self.state.lock();
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ use sha2::{Digest, Sha256};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use telemetry_events::{
|
||||
ActionEvent, AppEvent, AssistantEvent, CallEvent, CpuEvent, EditEvent, EditorEvent, Event,
|
||||
EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent, MemoryEvent,
|
||||
EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent, MemoryEvent, ReplEvent,
|
||||
SettingEvent,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
@@ -518,6 +518,13 @@ pub async fn post_events(
|
||||
checksum_matched,
|
||||
))
|
||||
}
|
||||
Event::Repl(event) => to_upload.repl_events.push(ReplEventRow::from_event(
|
||||
event.clone(),
|
||||
&wrapper,
|
||||
&request_body,
|
||||
first_event_at,
|
||||
checksum_matched,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,6 +549,7 @@ struct ToUpload {
|
||||
extension_events: Vec<ExtensionEventRow>,
|
||||
edit_events: Vec<EditEventRow>,
|
||||
action_events: Vec<ActionEventRow>,
|
||||
repl_events: Vec<ReplEventRow>,
|
||||
}
|
||||
|
||||
impl ToUpload {
|
||||
@@ -617,6 +625,11 @@ impl ToUpload {
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table '{ACTION_EVENTS_TABLE}'"))?;
|
||||
|
||||
const REPL_EVENTS_TABLE: &str = "repl_events";
|
||||
Self::upload_to_table(REPL_EVENTS_TABLE, &self.repl_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table '{REPL_EVENTS_TABLE}'"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -625,22 +638,24 @@ impl ToUpload {
|
||||
rows: &[T],
|
||||
clickhouse_client: &clickhouse::Client,
|
||||
) -> anyhow::Result<()> {
|
||||
if !rows.is_empty() {
|
||||
let mut insert = clickhouse_client.insert(table)?;
|
||||
|
||||
for event in rows {
|
||||
insert.write(event).await?;
|
||||
}
|
||||
|
||||
insert.end().await?;
|
||||
|
||||
let event_count = rows.len();
|
||||
log::info!(
|
||||
"wrote {event_count} {event_specifier} to '{table}'",
|
||||
event_specifier = if event_count == 1 { "event" } else { "events" }
|
||||
);
|
||||
if rows.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut insert = clickhouse_client.insert(table)?;
|
||||
|
||||
for event in rows {
|
||||
insert.write(event).await?;
|
||||
}
|
||||
|
||||
insert.end().await?;
|
||||
|
||||
let event_count = rows.len();
|
||||
log::info!(
|
||||
"wrote {event_count} {event_specifier} to '{table}'",
|
||||
event_specifier = if event_count == 1 { "event" } else { "events" }
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1189,6 +1204,62 @@ impl ExtensionEventRow {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct ReplEventRow {
|
||||
// AppInfoBase
|
||||
app_version: String,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
checksum_matched: bool,
|
||||
release_channel: String,
|
||||
os_name: String,
|
||||
os_version: String,
|
||||
|
||||
// ClientEventBase
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
time: i64,
|
||||
|
||||
// ReplEventRow
|
||||
kernel_language: String,
|
||||
kernel_status: String,
|
||||
repl_session_id: String,
|
||||
}
|
||||
|
||||
impl ReplEventRow {
|
||||
fn from_event(
|
||||
event: ReplEvent,
|
||||
wrapper: &EventWrapper,
|
||||
body: &EventRequestBody,
|
||||
first_event_at: chrono::DateTime<chrono::Utc>,
|
||||
checksum_matched: bool,
|
||||
) -> Self {
|
||||
let semver = body.semver();
|
||||
let time =
|
||||
first_event_at + chrono::Duration::milliseconds(wrapper.milliseconds_since_first_event);
|
||||
|
||||
Self {
|
||||
app_version: body.app_version.clone(),
|
||||
major: semver.map(|v| v.major() as i32),
|
||||
minor: semver.map(|v| v.minor() as i32),
|
||||
patch: semver.map(|v| v.patch() as i32),
|
||||
checksum_matched,
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
kernel_language: event.kernel_language,
|
||||
kernel_status: event.kernel_status,
|
||||
repl_session_id: event.repl_session_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct EditEventRow {
|
||||
// AppInfoBase
|
||||
|
||||
@@ -22,15 +22,6 @@ enum ElementContainer {
|
||||
|
||||
actions!(picker, [ConfirmCompletion]);
|
||||
|
||||
// How long to give the command palette to return if a user
|
||||
// types j<enter> quickly.
|
||||
// Longer in debug builds to reduce flaky test on linux.
|
||||
#[cfg(debug_assertions)]
|
||||
static FINALIZE_TIMEOUT: Duration = Duration::from_millis(32);
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
static FINALIZE_TIMEOUT: Duration = Duration::from_millis(16);
|
||||
|
||||
/// ConfirmInput is an alternative editor action which - instead of selecting active picker entry - treats pickers editor input literally,
|
||||
/// performing some kind of action on it.
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
@@ -333,7 +324,7 @@ impl<D: PickerDelegate> Picker<D> {
|
||||
if self.pending_update_matches.is_some()
|
||||
&& !self
|
||||
.delegate
|
||||
.finalize_update_matches(self.query(cx), FINALIZE_TIMEOUT, cx)
|
||||
.finalize_update_matches(self.query(cx), Duration::from_millis(16), cx)
|
||||
{
|
||||
self.confirm_on_update = Some(false)
|
||||
} else {
|
||||
@@ -346,7 +337,7 @@ impl<D: PickerDelegate> Picker<D> {
|
||||
if self.pending_update_matches.is_some()
|
||||
&& !self
|
||||
.delegate
|
||||
.finalize_update_matches(self.query(cx), FINALIZE_TIMEOUT, cx)
|
||||
.finalize_update_matches(self.query(cx), Duration::from_millis(16), cx)
|
||||
{
|
||||
self.confirm_on_update = Some(true)
|
||||
} else {
|
||||
|
||||
@@ -17,6 +17,7 @@ alacritty_terminal.workspace = true
|
||||
anyhow.workspace = true
|
||||
async-dispatcher.workspace = true
|
||||
base64.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
command_palette_hooks.workspace = true
|
||||
editor.workspace = true
|
||||
|
||||
@@ -19,6 +19,7 @@ use std::{
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KernelSpecification {
|
||||
@@ -222,15 +223,23 @@ impl RunningKernel {
|
||||
|
||||
let process = cmd
|
||||
.current_dir(&working_directory)
|
||||
// .stdout(Stdio::null())
|
||||
// .stderr(Stdio::null())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.kill_on_drop(true)
|
||||
.spawn()
|
||||
.context("failed to start the kernel process")?;
|
||||
|
||||
let mut iopub_socket = connection_info.create_client_iopub_connection("").await?;
|
||||
let mut shell_socket = connection_info.create_client_shell_connection().await?;
|
||||
let mut control_socket = connection_info.create_client_control_connection().await?;
|
||||
let session_id = Uuid::new_v4().to_string();
|
||||
|
||||
let mut iopub_socket = connection_info
|
||||
.create_client_iopub_connection("", &session_id)
|
||||
.await?;
|
||||
let mut shell_socket = connection_info
|
||||
.create_client_shell_connection(&session_id)
|
||||
.await?;
|
||||
let mut control_socket = connection_info
|
||||
.create_client_control_connection(&session_id)
|
||||
.await?;
|
||||
|
||||
let (mut iopub, iosub) = futures::channel::mpsc::channel(100);
|
||||
|
||||
|
||||
@@ -24,13 +24,14 @@ pub use crate::repl_sessions_ui::{
|
||||
};
|
||||
use crate::repl_store::ReplStore;
|
||||
pub use crate::session::Session;
|
||||
use client::telemetry::Telemetry;
|
||||
|
||||
pub fn init(fs: Arc<dyn Fs>, cx: &mut AppContext) {
|
||||
pub fn init(fs: Arc<dyn Fs>, telemetry: Arc<Telemetry>, cx: &mut AppContext) {
|
||||
set_dispatcher(zed_dispatcher(cx));
|
||||
JupyterSettings::register(cx);
|
||||
::editor::init_settings(cx);
|
||||
repl_sessions_ui::init(cx);
|
||||
ReplStore::init(fs, cx);
|
||||
ReplStore::init(fs, telemetry, cx);
|
||||
}
|
||||
|
||||
fn zed_dispatcher(cx: &mut AppContext) -> impl Dispatcher {
|
||||
|
||||
@@ -41,12 +41,15 @@ pub fn run(editor: WeakView<Editor>, cx: &mut WindowContext) -> Result<()> {
|
||||
})?;
|
||||
|
||||
let fs = store.read(cx).fs().clone();
|
||||
let telemetry = store.read(cx).telemetry().clone();
|
||||
|
||||
let session = if let Some(session) = store.read(cx).get_session(editor.entity_id()).cloned()
|
||||
{
|
||||
session
|
||||
} else {
|
||||
let weak_editor = editor.downgrade();
|
||||
let session = cx.new_view(|cx| Session::new(weak_editor, fs, kernel_specification, cx));
|
||||
let session = cx
|
||||
.new_view(|cx| Session::new(weak_editor, fs, telemetry, kernel_specification, cx));
|
||||
|
||||
editor.update(cx, |_editor, cx| {
|
||||
cx.notify();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use client::telemetry::Telemetry;
|
||||
use collections::HashMap;
|
||||
use command_palette_hooks::CommandPaletteFilter;
|
||||
use gpui::{
|
||||
@@ -22,14 +23,15 @@ pub struct ReplStore {
|
||||
enabled: bool,
|
||||
sessions: HashMap<EntityId, View<Session>>,
|
||||
kernel_specifications: Vec<KernelSpecification>,
|
||||
telemetry: Arc<Telemetry>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
||||
impl ReplStore {
|
||||
const NAMESPACE: &'static str = "repl";
|
||||
|
||||
pub(crate) fn init(fs: Arc<dyn Fs>, cx: &mut AppContext) {
|
||||
let store = cx.new_model(move |cx| Self::new(fs, cx));
|
||||
pub(crate) fn init(fs: Arc<dyn Fs>, telemetry: Arc<Telemetry>, cx: &mut AppContext) {
|
||||
let store = cx.new_model(move |cx| Self::new(fs, telemetry, cx));
|
||||
|
||||
store
|
||||
.update(cx, |store, cx| store.refresh_kernelspecs(cx))
|
||||
@@ -42,13 +44,14 @@ impl ReplStore {
|
||||
cx.global::<GlobalReplStore>().0.clone()
|
||||
}
|
||||
|
||||
pub fn new(fs: Arc<dyn Fs>, cx: &mut ModelContext<Self>) -> Self {
|
||||
pub fn new(fs: Arc<dyn Fs>, telemetry: Arc<Telemetry>, cx: &mut ModelContext<Self>) -> Self {
|
||||
let subscriptions = vec![cx.observe_global::<SettingsStore>(move |this, cx| {
|
||||
this.set_enabled(JupyterSettings::enabled(cx), cx);
|
||||
})];
|
||||
|
||||
let this = Self {
|
||||
fs,
|
||||
telemetry,
|
||||
enabled: JupyterSettings::enabled(cx),
|
||||
sessions: HashMap::default(),
|
||||
kernel_specifications: Vec::new(),
|
||||
@@ -62,6 +65,10 @@ impl ReplStore {
|
||||
&self.fs
|
||||
}
|
||||
|
||||
pub fn telemetry(&self) -> &Arc<Telemetry> {
|
||||
&self.telemetry
|
||||
}
|
||||
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.enabled
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use crate::components::KernelListItem;
|
||||
use crate::KernelStatus;
|
||||
use crate::{
|
||||
kernels::{Kernel, KernelSpecification, RunningKernel},
|
||||
outputs::{ExecutionStatus, ExecutionView, LineHeight as _},
|
||||
};
|
||||
use client::telemetry::Telemetry;
|
||||
use collections::{HashMap, HashSet};
|
||||
use editor::{
|
||||
display_map::{
|
||||
@@ -12,7 +14,8 @@ use editor::{
|
||||
scroll::Autoscroll,
|
||||
Anchor, AnchorRangeExt as _, Editor, MultiBuffer, ToPoint,
|
||||
};
|
||||
use futures::{FutureExt as _, StreamExt as _};
|
||||
use futures::io::BufReader;
|
||||
use futures::{AsyncBufReadExt as _, FutureExt as _, StreamExt as _};
|
||||
use gpui::{
|
||||
div, prelude::*, EntityId, EventEmitter, Model, Render, Subscription, Task, View, ViewContext,
|
||||
WeakView,
|
||||
@@ -34,6 +37,7 @@ pub struct Session {
|
||||
blocks: HashMap<String, EditorBlock>,
|
||||
messaging_task: Task<()>,
|
||||
pub kernel_specification: KernelSpecification,
|
||||
telemetry: Arc<Telemetry>,
|
||||
_buffer_subscription: Subscription,
|
||||
}
|
||||
|
||||
@@ -205,9 +209,18 @@ impl Session {
|
||||
pub fn new(
|
||||
editor: WeakView<Editor>,
|
||||
fs: Arc<dyn Fs>,
|
||||
telemetry: Arc<Telemetry>,
|
||||
kernel_specification: KernelSpecification,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let kernel_language = kernel_specification.kernelspec.language.clone();
|
||||
|
||||
telemetry.report_repl_event(
|
||||
kernel_language.clone(),
|
||||
KernelStatus::Starting.to_string(),
|
||||
cx.entity_id().to_string(),
|
||||
);
|
||||
|
||||
let entity_id = editor.entity_id();
|
||||
let working_directory = editor
|
||||
.upgrade()
|
||||
@@ -227,11 +240,39 @@ impl Session {
|
||||
|
||||
match kernel {
|
||||
Ok((mut kernel, mut messages_rx)) => {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.update(&mut cx, |session, cx| {
|
||||
// At this point we can create a new kind of kernel that has the process and our long running background tasks
|
||||
|
||||
let stderr = kernel.process.stderr.take();
|
||||
|
||||
cx.spawn(|_session, mut _cx| async move {
|
||||
if let None = stderr {
|
||||
return;
|
||||
}
|
||||
let reader = BufReader::new(stderr.unwrap());
|
||||
let mut lines = reader.lines();
|
||||
while let Some(Ok(line)) = lines.next().await {
|
||||
log::error!("kernel: {}", line);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let stdout = kernel.process.stderr.take();
|
||||
|
||||
cx.spawn(|_session, mut _cx| async move {
|
||||
if let None = stdout {
|
||||
return;
|
||||
}
|
||||
let reader = BufReader::new(stdout.unwrap());
|
||||
let mut lines = reader.lines();
|
||||
while let Some(Ok(line)) = lines.next().await {
|
||||
log::info!("kernel: {}", line);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let status = kernel.process.status();
|
||||
this.kernel = Kernel::RunningKernel(kernel);
|
||||
session.kernel(Kernel::RunningKernel(kernel), cx);
|
||||
|
||||
cx.spawn(|session, mut cx| async move {
|
||||
let error_message = match status.await {
|
||||
@@ -252,8 +293,10 @@ impl Session {
|
||||
|
||||
session
|
||||
.update(&mut cx, |session, cx| {
|
||||
session.kernel =
|
||||
Kernel::ErroredLaunch(error_message.clone());
|
||||
session.kernel(
|
||||
Kernel::ErroredLaunch(error_message.clone()),
|
||||
cx,
|
||||
);
|
||||
|
||||
session.blocks.values().for_each(|block| {
|
||||
block.execution_view.update(
|
||||
@@ -282,7 +325,7 @@ impl Session {
|
||||
})
|
||||
.detach();
|
||||
|
||||
this.messaging_task = cx.spawn(|session, mut cx| async move {
|
||||
session.messaging_task = cx.spawn(|session, mut cx| async move {
|
||||
while let Some(message) = messages_rx.next().await {
|
||||
session
|
||||
.update(&mut cx, |session, cx| {
|
||||
@@ -291,12 +334,24 @@ impl Session {
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
|
||||
// todo!(kyle): send kernelinforequest once our shell channel read/writes are split
|
||||
// cx.spawn(|this, mut cx| async move {
|
||||
// cx.background_executor()
|
||||
// .timer(Duration::from_millis(120))
|
||||
// .await;
|
||||
// this.update(&mut cx, |this, cx| {
|
||||
// this.send(KernelInfoRequest {}.into(), cx).ok();
|
||||
// })
|
||||
// .ok();
|
||||
// })
|
||||
// .detach();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
Err(err) => {
|
||||
this.update(&mut cx, |this, _cx| {
|
||||
this.kernel = Kernel::ErroredLaunch(err.to_string());
|
||||
this.update(&mut cx, |session, cx| {
|
||||
session.kernel(Kernel::ErroredLaunch(err.to_string()), cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
@@ -319,6 +374,7 @@ impl Session {
|
||||
blocks: HashMap::default(),
|
||||
kernel_specification,
|
||||
_buffer_subscription: subscription,
|
||||
telemetry,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -474,8 +530,8 @@ impl Session {
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
task.await;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.send(message, cx).ok();
|
||||
this.update(&mut cx, |session, cx| {
|
||||
session.send(message, cx).ok();
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
@@ -501,6 +557,13 @@ impl Session {
|
||||
match &message.content {
|
||||
JupyterMessageContent::Status(status) => {
|
||||
self.kernel.set_execution_state(&status.execution_state);
|
||||
|
||||
self.telemetry.report_repl_event(
|
||||
self.kernel_specification.kernelspec.language.clone(),
|
||||
KernelStatus::from(&self.kernel).to_string(),
|
||||
cx.entity_id().to_string(),
|
||||
);
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
JupyterMessageContent::KernelInfoReply(reply) => {
|
||||
@@ -528,6 +591,23 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kernel(&mut self, kernel: Kernel, cx: &mut ViewContext<Self>) {
|
||||
if let Kernel::Shutdown = kernel {
|
||||
cx.emit(SessionEvent::Shutdown(self.editor.clone()));
|
||||
}
|
||||
|
||||
let kernel_status = KernelStatus::from(&kernel).to_string();
|
||||
let kernel_language = self.kernel_specification.kernelspec.language.clone();
|
||||
|
||||
self.telemetry.report_repl_event(
|
||||
kernel_language,
|
||||
kernel_status,
|
||||
cx.entity_id().to_string(),
|
||||
);
|
||||
|
||||
self.kernel = kernel;
|
||||
}
|
||||
|
||||
pub fn shutdown(&mut self, cx: &mut ViewContext<Self>) {
|
||||
let kernel = std::mem::replace(&mut self.kernel, Kernel::ShuttingDown);
|
||||
|
||||
@@ -544,10 +624,9 @@ impl Session {
|
||||
|
||||
kernel.process.kill().ok();
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
cx.emit(SessionEvent::Shutdown(this.editor.clone()));
|
||||
this.clear_outputs(cx);
|
||||
this.kernel = Kernel::Shutdown;
|
||||
this.update(&mut cx, |session, cx| {
|
||||
session.clear_outputs(cx);
|
||||
session.kernel(Kernel::Shutdown, cx);
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
|
||||
@@ -65,6 +65,7 @@ pub enum Event {
|
||||
Extension(ExtensionEvent),
|
||||
Edit(EditEvent),
|
||||
Action(ActionEvent),
|
||||
Repl(ReplEvent),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@@ -148,6 +149,13 @@ pub struct AppEvent {
|
||||
pub operation: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ReplEvent {
|
||||
pub kernel_language: String,
|
||||
pub kernel_status: String,
|
||||
pub repl_session_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct BacktraceFrame {
|
||||
pub ip: usize,
|
||||
|
||||
@@ -916,6 +916,8 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
|
||||
cx.assert_state("const afterˇ = 2; console.log(after)", Mode::Normal)
|
||||
}
|
||||
|
||||
// TODO: this test is flaky on our linux CI machines
|
||||
#[cfg(target_os = "macos")]
|
||||
#[gpui::test]
|
||||
async fn test_remap(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
@@ -956,6 +958,8 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
|
||||
cx.simulate_keystrokes("g x");
|
||||
cx.assert_state("1234fooˇ56789", Mode::Normal);
|
||||
|
||||
cx.executor().allow_parking();
|
||||
|
||||
// test command
|
||||
cx.update(|cx| {
|
||||
cx.bind_keys([KeyBinding::new(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.146.0"
|
||||
version = "0.146.3"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Zed Team <hi@zed.dev>"]
|
||||
|
||||
@@ -1 +1 @@
|
||||
dev
|
||||
stable
|
||||
@@ -169,7 +169,11 @@ fn init_common(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||
supermaven::init(app_state.client.clone(), cx);
|
||||
inline_completion_registry::init(app_state.client.telemetry().clone(), cx);
|
||||
assistant::init(app_state.fs.clone(), app_state.client.clone(), cx);
|
||||
repl::init(app_state.fs.clone(), cx);
|
||||
repl::init(
|
||||
app_state.fs.clone(),
|
||||
app_state.client.telemetry().clone(),
|
||||
cx,
|
||||
);
|
||||
extension::init(
|
||||
app_state.fs.clone(),
|
||||
app_state.client.clone(),
|
||||
|
||||
@@ -3459,7 +3459,11 @@ mod tests {
|
||||
terminal_view::init(cx);
|
||||
language_model::init(app_state.client.clone(), cx);
|
||||
assistant::init(app_state.fs.clone(), app_state.client.clone(), cx);
|
||||
repl::init(app_state.fs.clone(), cx);
|
||||
repl::init(
|
||||
app_state.fs.clone(),
|
||||
app_state.client.telemetry().clone(),
|
||||
cx,
|
||||
);
|
||||
tasks_ui::init(cx);
|
||||
initialize_workspace(app_state.clone(), cx);
|
||||
app_state
|
||||
|
||||
@@ -59,7 +59,7 @@ async function main() {
|
||||
const releaseNotesHeader = /^\s*Release Notes:(.+)/ims;
|
||||
|
||||
let releaseNotes = pullRequest.body || "";
|
||||
let contributor = pullRequest.user.login || "";
|
||||
let contributor = pullRequest.user?.login ?? "Unable to identify";
|
||||
const captures = releaseNotesHeader.exec(releaseNotes);
|
||||
const notes = captures ? captures[1] : "MISSING";
|
||||
const skippableNoteRegex = /^\s*-?\s*n\/?a\s*/ims;
|
||||
|
||||
Reference in New Issue
Block a user