Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fe9d6dccd6 | ||
![]() |
35e50c8c4a | ||
![]() |
19023312c1 | ||
![]() |
56dbd2e031 | ||
![]() |
121249c7c4 | ||
![]() |
8d0b6a3e58 |
7 changed files with 352 additions and 633 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -8184,7 +8184,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.68.0"
|
version = "0.68.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activity_indicator",
|
"activity_indicator",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,19 +1,44 @@
|
||||||
|
use crate::MutableAppContext;
|
||||||
|
use collections::{BTreeMap, HashMap, HashSet};
|
||||||
|
use parking_lot::Mutex;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{hash::Hash, sync::Weak};
|
use std::{hash::Hash, sync::Weak};
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
pub struct CallbackCollection<K: Clone + Hash + Eq, F> {
|
||||||
|
internal: Arc<Mutex<Mapping<K, F>>>,
|
||||||
use collections::{btree_map, BTreeMap, HashMap};
|
|
||||||
|
|
||||||
use crate::MutableAppContext;
|
|
||||||
|
|
||||||
pub type Mapping<K, F> = Mutex<HashMap<K, BTreeMap<usize, Option<F>>>>;
|
|
||||||
|
|
||||||
pub struct CallbackCollection<K: Hash + Eq, F> {
|
|
||||||
internal: Arc<Mapping<K, F>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Hash + Eq, F> Clone for CallbackCollection<K, F> {
|
pub struct Subscription<K: Clone + Hash + Eq, F> {
|
||||||
|
key: K,
|
||||||
|
id: usize,
|
||||||
|
mapping: Option<Weak<Mutex<Mapping<K, F>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Mapping<K, F> {
|
||||||
|
callbacks: HashMap<K, BTreeMap<usize, F>>,
|
||||||
|
dropped_subscriptions: HashMap<K, HashSet<usize>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Hash + Eq, F> Mapping<K, F> {
|
||||||
|
fn clear_dropped_state(&mut self, key: &K, subscription_id: usize) -> bool {
|
||||||
|
if let Some(subscriptions) = self.dropped_subscriptions.get_mut(&key) {
|
||||||
|
subscriptions.remove(&subscription_id)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, F> Default for Mapping<K, F> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
callbacks: Default::default(),
|
||||||
|
dropped_subscriptions: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Clone + Hash + Eq, F> Clone for CallbackCollection<K, F> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
internal: self.internal.clone(),
|
internal: self.internal.clone(),
|
||||||
|
@ -21,7 +46,7 @@ impl<K: Hash + Eq, F> Clone for CallbackCollection<K, F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Hash + Eq + Copy, F> Default for CallbackCollection<K, F> {
|
impl<K: Clone + Hash + Eq + Copy, F> Default for CallbackCollection<K, F> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
CallbackCollection {
|
CallbackCollection {
|
||||||
internal: Arc::new(Mutex::new(Default::default())),
|
internal: Arc::new(Mutex::new(Default::default())),
|
||||||
|
@ -29,78 +54,114 @@ impl<K: Hash + Eq + Copy, F> Default for CallbackCollection<K, F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Hash + Eq + Copy, F> CallbackCollection<K, F> {
|
impl<K: Clone + Hash + Eq + Copy, F> CallbackCollection<K, F> {
|
||||||
pub fn downgrade(&self) -> Weak<Mapping<K, F>> {
|
|
||||||
Arc::downgrade(&self.internal)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.internal.lock().is_empty()
|
self.internal.lock().callbacks.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_callback(&mut self, id: K, subscription_id: usize, callback: F) {
|
pub fn subscribe(&mut self, key: K, subscription_id: usize) -> Subscription<K, F> {
|
||||||
self.internal
|
Subscription {
|
||||||
.lock()
|
key,
|
||||||
.entry(id)
|
id: subscription_id,
|
||||||
.or_default()
|
mapping: Some(Arc::downgrade(&self.internal)),
|
||||||
.insert(subscription_id, Some(callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(&mut self, id: K) {
|
|
||||||
self.internal.lock().remove(&id);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_or_remove_callback(&mut self, id: K, subscription_id: usize, callback: F) {
|
|
||||||
match self
|
|
||||||
.internal
|
|
||||||
.lock()
|
|
||||||
.entry(id)
|
|
||||||
.or_default()
|
|
||||||
.entry(subscription_id)
|
|
||||||
{
|
|
||||||
btree_map::Entry::Vacant(entry) => {
|
|
||||||
entry.insert(Some(callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
btree_map::Entry::Occupied(entry) => {
|
|
||||||
// TODO: This seems like it should never be called because no code
|
|
||||||
// should ever attempt to remove an existing callback
|
|
||||||
debug_assert!(entry.get().is_none());
|
|
||||||
entry.remove();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emit_and_cleanup<C: FnMut(&mut F, &mut MutableAppContext) -> bool>(
|
pub fn add_callback(&mut self, key: K, subscription_id: usize, callback: F) {
|
||||||
|
let mut this = self.internal.lock();
|
||||||
|
|
||||||
|
// If this callback's subscription was dropped before the callback was
|
||||||
|
// added, then just drop the callback.
|
||||||
|
if this.clear_dropped_state(&key, subscription_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.callbacks
|
||||||
|
.entry(key)
|
||||||
|
.or_default()
|
||||||
|
.insert(subscription_id, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, key: K) {
|
||||||
|
// Drop these callbacks after releasing the lock, in case one of them
|
||||||
|
// owns a subscription to this callback collection.
|
||||||
|
let mut this = self.internal.lock();
|
||||||
|
let callbacks = this.callbacks.remove(&key);
|
||||||
|
this.dropped_subscriptions.remove(&key);
|
||||||
|
drop(this);
|
||||||
|
drop(callbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit<C: FnMut(&mut F, &mut MutableAppContext) -> bool>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: K,
|
key: K,
|
||||||
cx: &mut MutableAppContext,
|
cx: &mut MutableAppContext,
|
||||||
mut call_callback: C,
|
mut call_callback: C,
|
||||||
) {
|
) {
|
||||||
let callbacks = self.internal.lock().remove(&id);
|
let callbacks = self.internal.lock().callbacks.remove(&key);
|
||||||
if let Some(callbacks) = callbacks {
|
if let Some(callbacks) = callbacks {
|
||||||
for (subscription_id, callback) in callbacks {
|
for (subscription_id, mut callback) in callbacks {
|
||||||
if let Some(mut callback) = callback {
|
// If this callback's subscription was dropped while invoking an
|
||||||
let alive = call_callback(&mut callback, cx);
|
// earlier callback, then just drop the callback.
|
||||||
if alive {
|
let mut this = self.internal.lock();
|
||||||
match self
|
if this.clear_dropped_state(&key, subscription_id) {
|
||||||
.internal
|
continue;
|
||||||
.lock()
|
|
||||||
.entry(id)
|
|
||||||
.or_default()
|
|
||||||
.entry(subscription_id)
|
|
||||||
{
|
|
||||||
btree_map::Entry::Vacant(entry) => {
|
|
||||||
entry.insert(Some(callback));
|
|
||||||
}
|
|
||||||
btree_map::Entry::Occupied(entry) => {
|
|
||||||
entry.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop(this);
|
||||||
|
let alive = call_callback(&mut callback, cx);
|
||||||
|
|
||||||
|
// If this callback's subscription was dropped while invoking the callback
|
||||||
|
// itself, or if the callback returns false, then just drop the callback.
|
||||||
|
let mut this = self.internal.lock();
|
||||||
|
if this.clear_dropped_state(&key, subscription_id) || !alive {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.callbacks
|
||||||
|
.entry(key)
|
||||||
|
.or_default()
|
||||||
|
.insert(subscription_id, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<K: Clone + Hash + Eq, F> Subscription<K, F> {
|
||||||
|
pub fn id(&self) -> usize {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn detach(&mut self) {
|
||||||
|
self.mapping.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Clone + Hash + Eq, F> Drop for Subscription<K, F> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(mapping) = self.mapping.as_ref().and_then(|mapping| mapping.upgrade()) {
|
||||||
|
let mut mapping = mapping.lock();
|
||||||
|
|
||||||
|
// If the callback is present in the mapping, then just remove it.
|
||||||
|
if let Some(callbacks) = mapping.callbacks.get_mut(&self.key) {
|
||||||
|
let callback = callbacks.remove(&self.id);
|
||||||
|
if callback.is_some() {
|
||||||
|
drop(mapping);
|
||||||
|
drop(callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this subscription's callback is not present, then either it has been
|
||||||
|
// temporarily removed during emit, or it has not yet been added. Record
|
||||||
|
// that this subscription has been dropped so that the callback can be
|
||||||
|
// removed later.
|
||||||
|
mapping
|
||||||
|
.dropped_subscriptions
|
||||||
|
.entry(self.key.clone())
|
||||||
|
.or_default()
|
||||||
|
.insert(self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
||||||
description = "The fast, collaborative code editor."
|
description = "The fast, collaborative code editor."
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.68.0"
|
version = "0.68.2"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "zed"
|
name = "zed"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
dev
|
stable
|
|
@ -37,6 +37,8 @@ pub(crate) struct GithubReleaseAsset {
|
||||||
|
|
||||||
pub async fn npm_package_latest_version(name: &str) -> Result<String> {
|
pub async fn npm_package_latest_version(name: &str) -> Result<String> {
|
||||||
let output = smol::process::Command::new("npm")
|
let output = smol::process::Command::new("npm")
|
||||||
|
.args(["-fetch-retry-mintimeout", "2000"])
|
||||||
|
.args(["-fetch-retry-maxtimeout", "5000"])
|
||||||
.args(["info", name, "--json"])
|
.args(["info", name, "--json"])
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await
|
||||||
|
@ -60,6 +62,8 @@ pub async fn npm_install_packages(
|
||||||
directory: &Path,
|
directory: &Path,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let output = smol::process::Command::new("npm")
|
let output = smol::process::Command::new("npm")
|
||||||
|
.args(["-fetch-retry-mintimeout", "2000"])
|
||||||
|
.args(["-fetch-retry-maxtimeout", "5000"])
|
||||||
.arg("install")
|
.arg("install")
|
||||||
.arg("--prefix")
|
.arg("--prefix")
|
||||||
.arg(directory)
|
.arg(directory)
|
||||||
|
|
|
@ -12,7 +12,8 @@ use util::ResultExt;
|
||||||
pub struct TypeScriptLspAdapter;
|
pub struct TypeScriptLspAdapter;
|
||||||
|
|
||||||
impl TypeScriptLspAdapter {
|
impl TypeScriptLspAdapter {
|
||||||
const BIN_PATH: &'static str = "node_modules/typescript-language-server/lib/cli.js";
|
const OLD_BIN_PATH: &'static str = "node_modules/typescript-language-server/lib/cli.js";
|
||||||
|
const NEW_BIN_PATH: &'static str = "node_modules/typescript-language-server/lib/cli.mjs";
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Versions {
|
struct Versions {
|
||||||
|
@ -57,7 +58,7 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||||
fs::create_dir_all(&version_dir)
|
fs::create_dir_all(&version_dir)
|
||||||
.await
|
.await
|
||||||
.context("failed to create version directory")?;
|
.context("failed to create version directory")?;
|
||||||
let binary_path = version_dir.join(Self::BIN_PATH);
|
let binary_path = version_dir.join(Self::NEW_BIN_PATH);
|
||||||
|
|
||||||
if fs::metadata(&binary_path).await.is_err() {
|
if fs::metadata(&binary_path).await.is_err() {
|
||||||
npm_install_packages(
|
npm_install_packages(
|
||||||
|
@ -98,9 +99,12 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
|
let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
|
||||||
let bin_path = last_version_dir.join(Self::BIN_PATH);
|
let old_bin_path = last_version_dir.join(Self::OLD_BIN_PATH);
|
||||||
if bin_path.exists() {
|
let new_bin_path = last_version_dir.join(Self::NEW_BIN_PATH);
|
||||||
Ok(bin_path)
|
if new_bin_path.exists() {
|
||||||
|
Ok(new_bin_path)
|
||||||
|
} else if old_bin_path.exists() {
|
||||||
|
Ok(old_bin_path)
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!(
|
Err(anyhow!(
|
||||||
"missing executable in directory {:?}",
|
"missing executable in directory {:?}",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue