Compare commits
10 Commits
mcp-auth
...
v0.205.2-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49f98ca4f6 | ||
|
|
6b8b1e6859 | ||
|
|
de412e4c71 | ||
|
|
3e55f14863 | ||
|
|
900e1adcda | ||
|
|
5229cc75ac | ||
|
|
844f3eeb71 | ||
|
|
ab4687391a | ||
|
|
d5cf9be162 | ||
|
|
3530445baf |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -21105,7 +21105,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.205.0"
|
||||
version = "0.205.2"
|
||||
dependencies = [
|
||||
"acp_tools",
|
||||
"activity_indicator",
|
||||
|
||||
@@ -1781,6 +1781,9 @@ impl AcpThread {
|
||||
reuse_shared_snapshot: bool,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<String>> {
|
||||
// Args are 1-based, move to 0-based
|
||||
let line = line.unwrap_or_default().saturating_sub(1);
|
||||
let limit = limit.unwrap_or(u32::MAX);
|
||||
let project = self.project.clone();
|
||||
let action_log = self.action_log.clone();
|
||||
cx.spawn(async move |this, cx| {
|
||||
@@ -1808,44 +1811,37 @@ impl AcpThread {
|
||||
action_log.update(cx, |action_log, cx| {
|
||||
action_log.buffer_read(buffer.clone(), cx);
|
||||
})?;
|
||||
project.update(cx, |project, cx| {
|
||||
let position = buffer
|
||||
.read(cx)
|
||||
.snapshot()
|
||||
.anchor_before(Point::new(line.unwrap_or_default(), 0));
|
||||
project.set_agent_location(
|
||||
Some(AgentLocation {
|
||||
buffer: buffer.downgrade(),
|
||||
position,
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
})?;
|
||||
|
||||
buffer.update(cx, |buffer, _| buffer.snapshot())?
|
||||
let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot())?;
|
||||
this.update(cx, |this, _| {
|
||||
this.shared_buffers.insert(buffer.clone(), snapshot.clone());
|
||||
})?;
|
||||
snapshot
|
||||
};
|
||||
|
||||
this.update(cx, |this, _| {
|
||||
let text = snapshot.text();
|
||||
this.shared_buffers.insert(buffer.clone(), snapshot);
|
||||
if line.is_none() && limit.is_none() {
|
||||
return Ok(text);
|
||||
}
|
||||
let limit = limit.unwrap_or(u32::MAX) as usize;
|
||||
let Some(line) = line else {
|
||||
return Ok(text.lines().take(limit).collect::<String>());
|
||||
};
|
||||
let max_point = snapshot.max_point();
|
||||
if line >= max_point.row {
|
||||
anyhow::bail!(
|
||||
"Attempting to read beyond the end of the file, line {}:{}",
|
||||
max_point.row + 1,
|
||||
max_point.column
|
||||
);
|
||||
}
|
||||
|
||||
let count = text.lines().count();
|
||||
if count < line as usize {
|
||||
anyhow::bail!("There are only {} lines", count);
|
||||
}
|
||||
Ok(text
|
||||
.lines()
|
||||
.skip(line as usize + 1)
|
||||
.take(limit)
|
||||
.collect::<String>())
|
||||
})?
|
||||
let start = snapshot.anchor_before(Point::new(line, 0));
|
||||
let end = snapshot.anchor_before(Point::new(line.saturating_add(limit), 0));
|
||||
|
||||
project.update(cx, |project, cx| {
|
||||
project.set_agent_location(
|
||||
Some(AgentLocation {
|
||||
buffer: buffer.downgrade(),
|
||||
position: start,
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
})?;
|
||||
|
||||
Ok(snapshot.text_for_range(start..end).collect::<String>())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2391,6 +2387,82 @@ mod tests {
|
||||
request.await.unwrap();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_reading_from_line(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(path!("/tmp"), json!({"foo": "one\ntwo\nthree\nfour\n"}))
|
||||
.await;
|
||||
let project = Project::test(fs.clone(), [], cx).await;
|
||||
project
|
||||
.update(cx, |project, cx| {
|
||||
project.find_or_create_worktree(path!("/tmp/foo"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let connection = Rc::new(FakeAgentConnection::new());
|
||||
|
||||
let thread = cx
|
||||
.update(|cx| connection.new_thread(project, Path::new(path!("/tmp")), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Whole file
|
||||
let content = thread
|
||||
.update(cx, |thread, cx| {
|
||||
thread.read_text_file(path!("/tmp/foo").into(), None, None, false, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(content, "one\ntwo\nthree\nfour\n");
|
||||
|
||||
// Only start line
|
||||
let content = thread
|
||||
.update(cx, |thread, cx| {
|
||||
thread.read_text_file(path!("/tmp/foo").into(), Some(3), None, false, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(content, "three\nfour\n");
|
||||
|
||||
// Only limit
|
||||
let content = thread
|
||||
.update(cx, |thread, cx| {
|
||||
thread.read_text_file(path!("/tmp/foo").into(), None, Some(2), false, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(content, "one\ntwo\n");
|
||||
|
||||
// Range
|
||||
let content = thread
|
||||
.update(cx, |thread, cx| {
|
||||
thread.read_text_file(path!("/tmp/foo").into(), Some(2), Some(2), false, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(content, "two\nthree\n");
|
||||
|
||||
// Invalid
|
||||
let err = thread
|
||||
.update(cx, |thread, cx| {
|
||||
thread.read_text_file(path!("/tmp/foo").into(), Some(5), Some(2), false, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"Attempting to read beyond the end of the file, line 5:0"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_succeeding_canceled_toolcall(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
@@ -1591,7 +1591,7 @@ impl AcpThreadView {
|
||||
task.shell = shell;
|
||||
|
||||
let terminal = terminal_panel.update_in(cx, |terminal_panel, window, cx| {
|
||||
terminal_panel.spawn_task(login.clone(), window, cx)
|
||||
terminal_panel.spawn_task(&login, window, cx)
|
||||
})?;
|
||||
|
||||
let terminal = terminal.await?;
|
||||
|
||||
@@ -506,7 +506,6 @@ mod tests {
|
||||
use super::*;
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use futures::channel::oneshot;
|
||||
use gpui::TestAppContext;
|
||||
use indoc::indoc;
|
||||
use language::{Language, LanguageConfig, LanguageId, LanguageMatcher, tree_sitter_rust};
|
||||
@@ -655,17 +654,10 @@ mod tests {
|
||||
expect_file_decl("a.rs", &decls[1], &project, cx);
|
||||
});
|
||||
|
||||
// Drop the buffer and wait for release
|
||||
let (release_tx, release_rx) = oneshot::channel();
|
||||
cx.update(|cx| {
|
||||
cx.observe_release(&buffer, |_, _| {
|
||||
release_tx.send(()).ok();
|
||||
})
|
||||
.detach();
|
||||
// Need to trigger flush_effects so that the observe_release handler will run.
|
||||
cx.update(|_cx| {
|
||||
drop(buffer);
|
||||
});
|
||||
drop(buffer);
|
||||
cx.run_until_parked();
|
||||
release_rx.await.ok();
|
||||
cx.run_until_parked();
|
||||
|
||||
index.read_with(cx, |index, cx| {
|
||||
|
||||
@@ -55,7 +55,7 @@ use std::{
|
||||
str,
|
||||
sync::{
|
||||
Arc, LazyLock,
|
||||
atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst},
|
||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||
},
|
||||
};
|
||||
use syntax_map::{QueryCursorHandle, SyntaxSnapshot};
|
||||
@@ -168,7 +168,6 @@ pub struct CachedLspAdapter {
|
||||
pub disk_based_diagnostics_progress_token: Option<String>,
|
||||
language_ids: HashMap<LanguageName, String>,
|
||||
pub adapter: Arc<dyn LspAdapter>,
|
||||
pub reinstall_attempt_count: AtomicU64,
|
||||
cached_binary: ServerBinaryCache,
|
||||
}
|
||||
|
||||
@@ -185,7 +184,6 @@ impl Debug for CachedLspAdapter {
|
||||
&self.disk_based_diagnostics_progress_token,
|
||||
)
|
||||
.field("language_ids", &self.language_ids)
|
||||
.field("reinstall_attempt_count", &self.reinstall_attempt_count)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
@@ -204,7 +202,6 @@ impl CachedLspAdapter {
|
||||
language_ids,
|
||||
adapter,
|
||||
cached_binary: Default::default(),
|
||||
reinstall_attempt_count: AtomicU64::new(0),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ pub fn init(languages: Arc<LanguageRegistry>, fs: Arc<dyn Fs>, node: NodeRuntime
|
||||
let ty_lsp_adapter = Arc::new(python::TyLspAdapter::new(fs.clone()));
|
||||
let python_context_provider = Arc::new(python::PythonContextProvider);
|
||||
let python_lsp_adapter = Arc::new(python::PyrightLspAdapter::new(node.clone()));
|
||||
let basedpyright_lsp_adapter = Arc::new(BasedPyrightLspAdapter::new());
|
||||
let basedpyright_lsp_adapter = Arc::new(BasedPyrightLspAdapter::new(node.clone()));
|
||||
let ruff_lsp_adapter = Arc::new(RuffLspAdapter::new(fs.clone()));
|
||||
let python_toolchain_provider = Arc::new(python::PythonToolchainProvider);
|
||||
let rust_context_provider = Arc::new(rust::RustContextProvider);
|
||||
|
||||
@@ -29,7 +29,6 @@ use parking_lot::Mutex;
|
||||
use std::str::FromStr;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
ffi::OsString,
|
||||
fmt::Write,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
@@ -65,9 +64,6 @@ impl ManifestProvider for PyprojectTomlManifestProvider {
|
||||
}
|
||||
}
|
||||
|
||||
const SERVER_PATH: &str = "node_modules/pyright/langserver.index.js";
|
||||
const NODE_MODULE_RELATIVE_SERVER_PATH: &str = "pyright/langserver.index.js";
|
||||
|
||||
enum TestRunner {
|
||||
UNITTEST,
|
||||
PYTEST,
|
||||
@@ -85,10 +81,6 @@ impl FromStr for TestRunner {
|
||||
}
|
||||
}
|
||||
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![server_path.into(), "--stdio".into()]
|
||||
}
|
||||
|
||||
/// Pyright assigns each completion item a `sortText` of the form `XX.YYYY.name`.
|
||||
/// Where `XX` is the sorting category, `YYYY` is based on most recent usage,
|
||||
/// and `name` is the symbol name itself.
|
||||
@@ -108,7 +100,7 @@ pub struct TyLspAdapter {
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl TyLspAdapter {
|
||||
const GITHUB_ASSET_KIND: AssetKind = AssetKind::Gz;
|
||||
const GITHUB_ASSET_KIND: AssetKind = AssetKind::TarGz;
|
||||
const ARCH_SERVER_NAME: &str = "apple-darwin";
|
||||
}
|
||||
|
||||
@@ -224,15 +216,20 @@ impl LspInstaller for TyLspAdapter {
|
||||
digest: expected_digest,
|
||||
} = latest_version;
|
||||
let destination_path = container_dir.join(format!("ty-{name}"));
|
||||
|
||||
async_fs::create_dir_all(&destination_path).await?;
|
||||
|
||||
let server_path = match Self::GITHUB_ASSET_KIND {
|
||||
AssetKind::TarGz | AssetKind::Gz => destination_path.clone(), // Tar and gzip extract in place.
|
||||
AssetKind::Zip => destination_path.clone().join("ty.exe"), // zip contains a .exe
|
||||
AssetKind::TarGz | AssetKind::Gz => destination_path
|
||||
.join(Self::build_asset_name()?.0)
|
||||
.join("ty"),
|
||||
AssetKind::Zip => destination_path.clone().join("ty.exe"),
|
||||
};
|
||||
|
||||
let binary = LanguageServerBinary {
|
||||
path: server_path.clone(),
|
||||
env: None,
|
||||
arguments: Default::default(),
|
||||
arguments: vec!["server".into()],
|
||||
};
|
||||
|
||||
let metadata_path = destination_path.with_extension("metadata");
|
||||
@@ -291,7 +288,7 @@ impl LspInstaller for TyLspAdapter {
|
||||
Ok(LanguageServerBinary {
|
||||
path: server_path,
|
||||
env: None,
|
||||
arguments: Default::default(),
|
||||
arguments: vec!["server".into()],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -313,14 +310,16 @@ impl LspInstaller for TyLspAdapter {
|
||||
|
||||
let path = last.context("no cached binary")?;
|
||||
let path = match TyLspAdapter::GITHUB_ASSET_KIND {
|
||||
AssetKind::TarGz | AssetKind::Gz => path, // Tar and gzip extract in place.
|
||||
AssetKind::Zip => path.join("ty.exe"), // zip contains a .exe
|
||||
AssetKind::TarGz | AssetKind::Gz => {
|
||||
path.join(Self::build_asset_name()?.0).join("ty")
|
||||
}
|
||||
AssetKind::Zip => path.join("ty.exe"),
|
||||
};
|
||||
|
||||
anyhow::Ok(LanguageServerBinary {
|
||||
path,
|
||||
env: None,
|
||||
arguments: Default::default(),
|
||||
arguments: vec!["server".into()],
|
||||
})
|
||||
})
|
||||
.await
|
||||
@@ -334,10 +333,29 @@ pub struct PyrightLspAdapter {
|
||||
|
||||
impl PyrightLspAdapter {
|
||||
const SERVER_NAME: LanguageServerName = LanguageServerName::new_static("pyright");
|
||||
const SERVER_PATH: &str = "node_modules/pyright/langserver.index.js";
|
||||
const NODE_MODULE_RELATIVE_SERVER_PATH: &str = "pyright/langserver.index.js";
|
||||
|
||||
pub fn new(node: NodeRuntime) -> Self {
|
||||
PyrightLspAdapter { node }
|
||||
}
|
||||
|
||||
async fn get_cached_server_binary(
|
||||
container_dir: PathBuf,
|
||||
node: &NodeRuntime,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let server_path = container_dir.join(Self::SERVER_PATH);
|
||||
if server_path.exists() {
|
||||
Some(LanguageServerBinary {
|
||||
path: node.binary_path().await.log_err()?,
|
||||
env: None,
|
||||
arguments: vec![server_path.into(), "--stdio".into()],
|
||||
})
|
||||
} else {
|
||||
log::error!("missing executable in directory {:?}", server_path);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
@@ -550,13 +568,13 @@ impl LspInstaller for PyrightLspAdapter {
|
||||
.await
|
||||
.log_err()??;
|
||||
|
||||
let path = node_modules_path.join(NODE_MODULE_RELATIVE_SERVER_PATH);
|
||||
let path = node_modules_path.join(Self::NODE_MODULE_RELATIVE_SERVER_PATH);
|
||||
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: node,
|
||||
env: Some(env),
|
||||
arguments: server_binary_arguments(&path),
|
||||
arguments: vec![path.into(), "--stdio".into()],
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -567,7 +585,7 @@ impl LspInstaller for PyrightLspAdapter {
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
let server_path = container_dir.join(Self::SERVER_PATH);
|
||||
|
||||
self.node
|
||||
.npm_install_packages(
|
||||
@@ -580,7 +598,7 @@ impl LspInstaller for PyrightLspAdapter {
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: Some(env),
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
arguments: vec![server_path.into(), "--stdio".into()],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -590,7 +608,7 @@ impl LspInstaller for PyrightLspAdapter {
|
||||
container_dir: &PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
let server_path = container_dir.join(Self::SERVER_PATH);
|
||||
|
||||
let should_install_language_server = self
|
||||
.node
|
||||
@@ -609,7 +627,7 @@ impl LspInstaller for PyrightLspAdapter {
|
||||
Some(LanguageServerBinary {
|
||||
path: self.node.binary_path().await.ok()?,
|
||||
env: Some(env),
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
arguments: vec![server_path.into(), "--stdio".into()],
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -619,29 +637,12 @@ impl LspInstaller for PyrightLspAdapter {
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let mut binary = get_cached_server_binary(container_dir, &self.node).await?;
|
||||
let mut binary = Self::get_cached_server_binary(container_dir, &self.node).await?;
|
||||
binary.env = Some(delegate.shell_env().await);
|
||||
Some(binary)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_cached_server_binary(
|
||||
container_dir: PathBuf,
|
||||
node: &NodeRuntime,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
if server_path.exists() {
|
||||
Some(LanguageServerBinary {
|
||||
path: node.binary_path().await.log_err()?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
log::error!("missing executable in directory {:?}", server_path);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct PythonContextProvider;
|
||||
|
||||
const PYTHON_TEST_TARGET_TASK_VARIABLE: VariableName =
|
||||
@@ -1559,7 +1560,7 @@ impl LspInstaller for PyLspAdapter {
|
||||
util::command::new_smol_command(pip_path.as_path())
|
||||
.arg("install")
|
||||
.arg("python-lsp-server")
|
||||
.arg("-U")
|
||||
.arg("--upgrade")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
@@ -1570,7 +1571,7 @@ impl LspInstaller for PyLspAdapter {
|
||||
util::command::new_smol_command(pip_path.as_path())
|
||||
.arg("install")
|
||||
.arg("python-lsp-server[all]")
|
||||
.arg("-U")
|
||||
.arg("--upgrade")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
@@ -1581,7 +1582,7 @@ impl LspInstaller for PyLspAdapter {
|
||||
util::command::new_smol_command(pip_path)
|
||||
.arg("install")
|
||||
.arg("pylsp-mypy")
|
||||
.arg("-U")
|
||||
.arg("--upgrade")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
@@ -1589,6 +1590,10 @@ impl LspInstaller for PyLspAdapter {
|
||||
"pylsp-mypy installation failed"
|
||||
);
|
||||
let pylsp = venv.join(BINARY_DIR).join("pylsp");
|
||||
ensure!(
|
||||
delegate.which(pylsp.as_os_str()).await.is_some(),
|
||||
"pylsp installation was incomplete"
|
||||
);
|
||||
Ok(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
@@ -1603,6 +1608,7 @@ impl LspInstaller for PyLspAdapter {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let venv = self.base_venv(delegate).await.ok()?;
|
||||
let pylsp = venv.join(BINARY_DIR).join("pylsp");
|
||||
delegate.which(pylsp.as_os_str()).await?;
|
||||
Some(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
@@ -1612,62 +1618,34 @@ impl LspInstaller for PyLspAdapter {
|
||||
}
|
||||
|
||||
pub(crate) struct BasedPyrightLspAdapter {
|
||||
python_venv_base: OnceCell<Result<Arc<Path>, String>>,
|
||||
node: NodeRuntime,
|
||||
}
|
||||
|
||||
impl BasedPyrightLspAdapter {
|
||||
const SERVER_NAME: LanguageServerName = LanguageServerName::new_static("basedpyright");
|
||||
const BINARY_NAME: &'static str = "basedpyright-langserver";
|
||||
const SERVER_PATH: &str = "node_modules/basedpyright/langserver.index.js";
|
||||
const NODE_MODULE_RELATIVE_SERVER_PATH: &str = "basedpyright/langserver.index.js";
|
||||
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
python_venv_base: OnceCell::new(),
|
||||
}
|
||||
pub(crate) fn new(node: NodeRuntime) -> Self {
|
||||
BasedPyrightLspAdapter { node }
|
||||
}
|
||||
|
||||
async fn ensure_venv(delegate: &dyn LspAdapterDelegate) -> Result<Arc<Path>> {
|
||||
let python_path = Self::find_base_python(delegate)
|
||||
.await
|
||||
.context("Could not find Python installation for basedpyright")?;
|
||||
let work_dir = delegate
|
||||
.language_server_download_dir(&Self::SERVER_NAME)
|
||||
.await
|
||||
.context("Could not get working directory for basedpyright")?;
|
||||
let mut path = PathBuf::from(work_dir.as_ref());
|
||||
path.push("basedpyright-venv");
|
||||
if !path.exists() {
|
||||
util::command::new_smol_command(python_path)
|
||||
.arg("-m")
|
||||
.arg("venv")
|
||||
.arg("basedpyright-venv")
|
||||
.current_dir(work_dir)
|
||||
.spawn()?
|
||||
.output()
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(path.into())
|
||||
}
|
||||
|
||||
// Find "baseline", user python version from which we'll create our own venv.
|
||||
async fn find_base_python(delegate: &dyn LspAdapterDelegate) -> Option<PathBuf> {
|
||||
for path in ["python3", "python"] {
|
||||
if let Some(path) = delegate.which(path.as_ref()).await {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
async fn base_venv(&self, delegate: &dyn LspAdapterDelegate) -> Result<Arc<Path>, String> {
|
||||
self.python_venv_base
|
||||
.get_or_init(move || async move {
|
||||
Self::ensure_venv(delegate)
|
||||
.await
|
||||
.map_err(|e| format!("{e}"))
|
||||
async fn get_cached_server_binary(
|
||||
container_dir: PathBuf,
|
||||
node: &NodeRuntime,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let server_path = container_dir.join(Self::SERVER_PATH);
|
||||
if server_path.exists() {
|
||||
Some(LanguageServerBinary {
|
||||
path: node.binary_path().await.log_err()?,
|
||||
env: None,
|
||||
arguments: vec![server_path.into(), "--stdio".into()],
|
||||
})
|
||||
.await
|
||||
.clone()
|
||||
} else {
|
||||
log::error!("missing executable in directory {:?}", server_path);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1857,81 +1835,112 @@ impl LspAdapter for BasedPyrightLspAdapter {
|
||||
}
|
||||
|
||||
impl LspInstaller for BasedPyrightLspAdapter {
|
||||
type BinaryVersion = ();
|
||||
type BinaryVersion = String;
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
_: bool,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
) -> Result<String> {
|
||||
self.node
|
||||
.npm_package_latest_version(Self::SERVER_NAME.as_ref())
|
||||
.await
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
toolchain: Option<Toolchain>,
|
||||
_: Option<Toolchain>,
|
||||
_: &AsyncApp,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
if let Some(bin) = delegate.which(Self::BINARY_NAME.as_ref()).await {
|
||||
if let Some(path) = delegate.which(Self::BINARY_NAME.as_ref()).await {
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: bin,
|
||||
path,
|
||||
env: Some(env),
|
||||
arguments: vec!["--stdio".into()],
|
||||
})
|
||||
} else {
|
||||
let path = Path::new(toolchain?.path.as_ref())
|
||||
.parent()?
|
||||
.join(Self::BINARY_NAME);
|
||||
path.exists().then(|| LanguageServerBinary {
|
||||
path,
|
||||
arguments: vec!["--stdio".into()],
|
||||
env: None,
|
||||
// TODO shouldn't this be self.node.binary_path()?
|
||||
let node = delegate.which("node".as_ref()).await?;
|
||||
let (node_modules_path, _) = delegate
|
||||
.npm_package_installed_version(Self::SERVER_NAME.as_ref())
|
||||
.await
|
||||
.log_err()??;
|
||||
|
||||
let path = node_modules_path.join(Self::NODE_MODULE_RELATIVE_SERVER_PATH);
|
||||
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: node,
|
||||
env: Some(env),
|
||||
arguments: vec![path.into(), "--stdio".into()],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_latest_version: (),
|
||||
_container_dir: PathBuf,
|
||||
latest_version: Self::BinaryVersion,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let venv = self.base_venv(delegate).await.map_err(|e| anyhow!(e))?;
|
||||
let pip_path = venv.join(BINARY_DIR).join("pip3");
|
||||
ensure!(
|
||||
util::command::new_smol_command(pip_path.as_path())
|
||||
.arg("install")
|
||||
.arg("basedpyright")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
.success(),
|
||||
"basedpyright installation failed"
|
||||
);
|
||||
let pylsp = venv.join(BINARY_DIR).join(Self::BINARY_NAME);
|
||||
let server_path = container_dir.join(Self::SERVER_PATH);
|
||||
|
||||
self.node
|
||||
.npm_install_packages(
|
||||
&container_dir,
|
||||
&[(Self::SERVER_NAME.as_ref(), latest_version.as_str())],
|
||||
)
|
||||
.await?;
|
||||
|
||||
let env = delegate.shell_env().await;
|
||||
Ok(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
arguments: vec!["--stdio".into()],
|
||||
path: self.node.binary_path().await?,
|
||||
env: Some(env),
|
||||
arguments: vec![server_path.into(), "--stdio".into()],
|
||||
})
|
||||
}
|
||||
|
||||
async fn check_if_version_installed(
|
||||
&self,
|
||||
version: &Self::BinaryVersion,
|
||||
container_dir: &PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let server_path = container_dir.join(Self::SERVER_PATH);
|
||||
|
||||
let should_install_language_server = self
|
||||
.node
|
||||
.should_install_npm_package(
|
||||
Self::SERVER_NAME.as_ref(),
|
||||
&server_path,
|
||||
container_dir,
|
||||
VersionStrategy::Latest(version),
|
||||
)
|
||||
.await;
|
||||
|
||||
if should_install_language_server {
|
||||
None
|
||||
} else {
|
||||
let env = delegate.shell_env().await;
|
||||
Some(LanguageServerBinary {
|
||||
path: self.node.binary_path().await.ok()?,
|
||||
env: Some(env),
|
||||
arguments: vec![server_path.into(), "--stdio".into()],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
_container_dir: PathBuf,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let venv = self.base_venv(delegate).await.ok()?;
|
||||
let pylsp = venv.join(BINARY_DIR).join(Self::BINARY_NAME);
|
||||
Some(LanguageServerBinary {
|
||||
path: pylsp,
|
||||
env: None,
|
||||
arguments: vec!["--stdio".into()],
|
||||
})
|
||||
let mut binary = Self::get_cached_server_binary(container_dir, &self.node).await?;
|
||||
binary.env = Some(delegate.shell_env().await);
|
||||
Some(binary)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ impl AgentServerStore {
|
||||
let subscription = cx.observe_global::<SettingsStore>(|this, cx| {
|
||||
this.agent_servers_settings_changed(cx);
|
||||
});
|
||||
let this = Self {
|
||||
let mut this = Self {
|
||||
state: AgentServerStoreState::Local {
|
||||
node_runtime,
|
||||
fs,
|
||||
@@ -245,14 +245,7 @@ impl AgentServerStore {
|
||||
},
|
||||
external_agents: Default::default(),
|
||||
};
|
||||
cx.spawn(async move |this, cx| {
|
||||
cx.background_executor().timer(Duration::from_secs(1)).await;
|
||||
this.update(cx, |this, cx| {
|
||||
this.agent_servers_settings_changed(cx);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
this.agent_servers_settings_changed(cx);
|
||||
this
|
||||
}
|
||||
|
||||
@@ -305,22 +298,29 @@ impl AgentServerStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shared(&mut self, project_id: u64, client: AnyProtoClient) {
|
||||
pub fn shared(&mut self, project_id: u64, client: AnyProtoClient, cx: &mut Context<Self>) {
|
||||
match &mut self.state {
|
||||
AgentServerStoreState::Local {
|
||||
downstream_client, ..
|
||||
} => {
|
||||
client
|
||||
.send(proto::ExternalAgentsUpdated {
|
||||
project_id,
|
||||
names: self
|
||||
.external_agents
|
||||
*downstream_client = Some((project_id, client.clone()));
|
||||
// Send the current list of external agents downstream, but only after a delay,
|
||||
// to avoid having the message arrive before the downstream project's agent server store
|
||||
// sets up its handlers.
|
||||
cx.spawn(async move |this, cx| {
|
||||
cx.background_executor().timer(Duration::from_secs(1)).await;
|
||||
let names = this.update(cx, |this, _| {
|
||||
this.external_agents
|
||||
.keys()
|
||||
.map(|name| name.to_string())
|
||||
.collect(),
|
||||
})
|
||||
.log_err();
|
||||
*downstream_client = Some((project_id, client));
|
||||
.collect()
|
||||
})?;
|
||||
client
|
||||
.send(proto::ExternalAgentsUpdated { project_id, names })
|
||||
.log_err();
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
AgentServerStoreState::Remote { .. } => {
|
||||
debug_panic!(
|
||||
@@ -721,11 +721,6 @@ struct RemoteExternalAgentServer {
|
||||
new_version_available_tx: Option<watch::Sender<Option<String>>>,
|
||||
}
|
||||
|
||||
// new method: status_updated
|
||||
// does nothing in the all-local case
|
||||
// for RemoteExternalAgentServer, sends on the stored tx
|
||||
// etc.
|
||||
|
||||
impl ExternalAgentServer for RemoteExternalAgentServer {
|
||||
fn get_command(
|
||||
&mut self,
|
||||
|
||||
@@ -257,7 +257,7 @@ impl EventEmitter<ConflictSetUpdate> for ConflictSet {}
|
||||
mod tests {
|
||||
use std::{path::Path, sync::mpsc};
|
||||
|
||||
use crate::{Project, project_settings::ProjectSettings};
|
||||
use crate::Project;
|
||||
|
||||
use super::*;
|
||||
use fs::FakeFs;
|
||||
@@ -484,7 +484,7 @@ mod tests {
|
||||
cx.update(|cx| {
|
||||
settings::init(cx);
|
||||
WorktreeSettings::register(cx);
|
||||
ProjectSettings::register(cx);
|
||||
Project::init_settings(cx);
|
||||
AllLanguageSettings::register(cx);
|
||||
});
|
||||
let initial_text = "
|
||||
@@ -585,7 +585,7 @@ mod tests {
|
||||
cx.update(|cx| {
|
||||
settings::init(cx);
|
||||
WorktreeSettings::register(cx);
|
||||
ProjectSettings::register(cx);
|
||||
Project::init_settings(cx);
|
||||
AllLanguageSettings::register(cx);
|
||||
});
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ impl HeadlessProject {
|
||||
let agent_server_store = cx.new(|cx| {
|
||||
let mut agent_server_store =
|
||||
AgentServerStore::local(node_runtime.clone(), fs.clone(), environment, cx);
|
||||
agent_server_store.shared(REMOTE_SERVER_PROJECT_ID, session.clone());
|
||||
agent_server_store.shared(REMOTE_SERVER_PROJECT_ID, session.clone(), cx);
|
||||
agent_server_store
|
||||
});
|
||||
|
||||
|
||||
@@ -784,7 +784,10 @@ impl<'a> Chunks<'a> {
|
||||
slice_start..slice_end
|
||||
};
|
||||
|
||||
let bitmask = (1u128 << slice_range.end as u128).saturating_sub(1);
|
||||
// slice range has a bounds between 0 and 128 in non test builds
|
||||
// We use a non wrapping sub because we want to overflow in the case where slice_range.end == 128
|
||||
// because that represents a full chunk and the bitmask shouldn't remove anything
|
||||
let bitmask = (1u128.unbounded_shl(slice_range.end as u32)).wrapping_sub(1);
|
||||
|
||||
let chars = (chunk.chars() & bitmask) >> slice_range.start;
|
||||
let tabs = (chunk.tabs & bitmask) >> slice_range.start;
|
||||
|
||||
@@ -532,14 +532,10 @@ impl TerminalBuilder {
|
||||
child_exited: None,
|
||||
};
|
||||
|
||||
if !activation_script.is_empty() && no_task {
|
||||
if cfg!(not(target_os = "windows")) && !activation_script.is_empty() && no_task {
|
||||
for activation_script in activation_script {
|
||||
terminal.input(activation_script.into_bytes());
|
||||
terminal.write_to_pty(if cfg!(windows) {
|
||||
&b"\r\n"[..]
|
||||
} else {
|
||||
&b"\n"[..]
|
||||
});
|
||||
terminal.write_to_pty(b"\n");
|
||||
}
|
||||
terminal.clear();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ use itertools::Itertools;
|
||||
use project::{Fs, Project, ProjectEntryId};
|
||||
use search::{BufferSearchBar, buffer_search::DivRegistrar};
|
||||
use settings::Settings;
|
||||
use task::{RevealStrategy, RevealTarget, SpawnInTerminal, TaskId};
|
||||
use task::{RevealStrategy, RevealTarget, ShellBuilder, SpawnInTerminal, TaskId};
|
||||
use terminal::{
|
||||
Terminal,
|
||||
terminal_settings::{TerminalDockPosition, TerminalSettings},
|
||||
@@ -521,10 +521,42 @@ impl TerminalPanel {
|
||||
|
||||
pub fn spawn_task(
|
||||
&mut self,
|
||||
task: SpawnInTerminal,
|
||||
task: &SpawnInTerminal,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<WeakEntity<Terminal>>> {
|
||||
let remote_client = self
|
||||
.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
let project = workspace.project().read(cx);
|
||||
if project.is_via_collab() {
|
||||
Err(anyhow!("cannot spawn tasks as a guest"))
|
||||
} else {
|
||||
Ok(project.remote_client())
|
||||
}
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let remote_client = match remote_client {
|
||||
Ok(remote_client) => remote_client,
|
||||
Err(e) => return Task::ready(Err(e)),
|
||||
};
|
||||
|
||||
let remote_shell = remote_client
|
||||
.as_ref()
|
||||
.and_then(|remote_client| remote_client.read(cx).shell());
|
||||
|
||||
let builder = ShellBuilder::new(remote_shell.as_deref(), &task.shell);
|
||||
let command_label = builder.command_label(&task.command_label);
|
||||
let (command, args) = builder.build(task.command.clone(), &task.args);
|
||||
|
||||
let task = SpawnInTerminal {
|
||||
command_label,
|
||||
command: Some(command),
|
||||
args,
|
||||
..task.clone()
|
||||
};
|
||||
|
||||
if task.allow_concurrent_runs && task.use_new_terminal {
|
||||
return self.spawn_in_new_terminal(task, window, cx);
|
||||
}
|
||||
@@ -1558,7 +1590,7 @@ impl workspace::TerminalProvider for TerminalProvider {
|
||||
window.spawn(cx, async move |cx| {
|
||||
let terminal = terminal_panel
|
||||
.update_in(cx, |terminal_panel, window, cx| {
|
||||
terminal_panel.spawn_task(task, window, cx)
|
||||
terminal_panel.spawn_task(&task, window, cx)
|
||||
})
|
||||
.ok()?
|
||||
.await;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "The fast, collaborative code editor."
|
||||
edition.workspace = true
|
||||
name = "zed"
|
||||
version = "0.205.0"
|
||||
version = "0.205.2"
|
||||
publish.workspace = true
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Zed Team <hi@zed.dev>"]
|
||||
|
||||
@@ -1 +1 @@
|
||||
dev
|
||||
preview
|
||||
Reference in New Issue
Block a user