Compare commits
15 Commits
Remove-ruf
...
v0.214.2-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a55eb87bdb | ||
|
|
325c857409 | ||
|
|
425901cf73 | ||
|
|
f0ef48562b | ||
|
|
81fcb11073 | ||
|
|
de2c00b9f0 | ||
|
|
a9eab53619 | ||
|
|
919330a1ff | ||
|
|
680c66728a | ||
|
|
c210465e5c | ||
|
|
d22d86bf03 | ||
|
|
97009296ea | ||
|
|
9c45875c18 | ||
|
|
52f494d330 | ||
|
|
49eaaff379 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -21233,7 +21233,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.214.0"
|
||||
version = "0.214.2"
|
||||
dependencies = [
|
||||
"acp_tools",
|
||||
"activity_indicator",
|
||||
|
||||
@@ -48,7 +48,7 @@ pub async fn get_buffer_content_or_outline(
|
||||
if outline_items.is_empty() {
|
||||
let text = buffer.read_with(cx, |buffer, _| {
|
||||
let snapshot = buffer.snapshot();
|
||||
let len = snapshot.len().min(1024);
|
||||
let len = snapshot.len().min(snapshot.as_rope().floor_char_boundary(1024));
|
||||
let content = snapshot.text_for_range(0..len).collect::<String>();
|
||||
if let Some(path) = path {
|
||||
format!("# First 1KB of {path} (file too large to show full content, and no outline available)\n\n{content}")
|
||||
@@ -178,7 +178,7 @@ mod tests {
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
let project = Project::test(fs, [], cx).await;
|
||||
|
||||
let content = "A".repeat(100 * 1024); // 100KB
|
||||
let content = "⚡".repeat(100 * 1024); // 100KB
|
||||
let content_len = content.len();
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.create_buffer(true, cx))
|
||||
@@ -194,7 +194,7 @@ mod tests {
|
||||
|
||||
// Should contain some of the actual file content
|
||||
assert!(
|
||||
result.text.contains("AAAAAAAAAA"),
|
||||
result.text.contains("⚡⚡⚡⚡⚡⚡⚡"),
|
||||
"Result did not contain content subset"
|
||||
);
|
||||
|
||||
|
||||
@@ -250,7 +250,6 @@ impl PasswordProxy {
|
||||
.await
|
||||
.with_context(|| format!("creating askpass script at {askpass_script_path:?}"))?;
|
||||
make_file_executable(&askpass_script_path).await?;
|
||||
// todo(shell): There might be no powershell on the system
|
||||
#[cfg(target_os = "windows")]
|
||||
let askpass_helper = format!(
|
||||
"powershell.exe -ExecutionPolicy Bypass -File {}",
|
||||
@@ -375,7 +374,7 @@ fn generate_askpass_script(
|
||||
Ok(format!(
|
||||
r#"
|
||||
$ErrorActionPreference = 'Stop';
|
||||
($args -join [char]0) | & {askpass_program} --askpass={askpass_socket} 2> $null
|
||||
($args -join [char]0) | {askpass_program} --askpass={askpass_socket} 2> $null
|
||||
"#,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -453,6 +453,7 @@ impl Server {
|
||||
.add_request_handler(forward_mutating_project_request::<proto::StashPop>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::StashDrop>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::Commit>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::RunGitHook>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitInit>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::GetRemotes>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::GitShow>)
|
||||
|
||||
@@ -51,11 +51,13 @@ pub async fn init(crash_init: InitCrashHandler) {
|
||||
unsafe { env::set_var("RUST_BACKTRACE", "1") };
|
||||
old_hook(info);
|
||||
// prevent the macOS crash dialog from popping up
|
||||
std::process::exit(1);
|
||||
if cfg!(target_os = "macos") {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}));
|
||||
return;
|
||||
}
|
||||
(Some(true), _) | (None, _) => {
|
||||
_ => {
|
||||
panic::set_hook(Box::new(panic_hook));
|
||||
}
|
||||
}
|
||||
@@ -300,11 +302,18 @@ pub fn panic_hook(info: &PanicHookInfo) {
|
||||
.map(|loc| format!("{}:{}", loc.file(), loc.line()))
|
||||
.unwrap_or_default();
|
||||
|
||||
let current_thread = std::thread::current();
|
||||
let thread_name = current_thread.name().unwrap_or("<unnamed>");
|
||||
|
||||
// wait 500ms for the crash handler process to start up
|
||||
// if it's still not there just write panic info and no minidump
|
||||
let retry_frequency = Duration::from_millis(100);
|
||||
for _ in 0..5 {
|
||||
if let Some(client) = CRASH_HANDLER.get() {
|
||||
let location = info
|
||||
.location()
|
||||
.map_or_else(|| "<unknown>".to_owned(), |location| location.to_string());
|
||||
log::error!("thread '{thread_name}' panicked at {location}:\n{message}...");
|
||||
client
|
||||
.send_message(
|
||||
2,
|
||||
|
||||
@@ -74,7 +74,6 @@ use smallvec::{SmallVec, smallvec};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
borrow::Cow,
|
||||
cell::Cell,
|
||||
cmp::{self, Ordering},
|
||||
fmt::{self, Write},
|
||||
iter, mem,
|
||||
@@ -186,13 +185,6 @@ impl SelectionLayout {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RenderBlocksOutput {
|
||||
blocks: Vec<BlockLayout>,
|
||||
row_block_types: HashMap<DisplayRow, bool>,
|
||||
resized_blocks: Option<HashMap<CustomBlockId, u32>>,
|
||||
}
|
||||
|
||||
pub struct EditorElement {
|
||||
editor: Entity<Editor>,
|
||||
style: EditorStyle,
|
||||
@@ -3675,7 +3667,6 @@ impl EditorElement {
|
||||
latest_selection_anchors: &HashMap<BufferId, Anchor>,
|
||||
is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
|
||||
sticky_header_excerpt_id: Option<ExcerptId>,
|
||||
block_resize_offset: &mut i32,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Option<(AnyElement, Size<Pixels>, DisplayRow, Pixels)> {
|
||||
@@ -3829,10 +3820,7 @@ impl EditorElement {
|
||||
};
|
||||
let mut element_height_in_lines = ((final_size.height / line_height).ceil() as u32).max(1);
|
||||
|
||||
let effective_row_start = block_row_start.0 as i32 + *block_resize_offset;
|
||||
debug_assert!(effective_row_start >= 0);
|
||||
let mut row = DisplayRow(effective_row_start.max(0) as u32);
|
||||
|
||||
let mut row = block_row_start;
|
||||
let mut x_offset = px(0.);
|
||||
let mut is_block = true;
|
||||
|
||||
@@ -3862,7 +3850,6 @@ impl EditorElement {
|
||||
}
|
||||
};
|
||||
if element_height_in_lines != block.height() {
|
||||
*block_resize_offset += element_height_in_lines as i32 - block.height() as i32;
|
||||
resized_blocks.insert(custom_block_id, element_height_in_lines);
|
||||
}
|
||||
}
|
||||
@@ -4267,7 +4254,7 @@ impl EditorElement {
|
||||
sticky_header_excerpt_id: Option<ExcerptId>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> RenderBlocksOutput {
|
||||
) -> Result<(Vec<BlockLayout>, HashMap<DisplayRow, bool>), HashMap<CustomBlockId, u32>> {
|
||||
let (fixed_blocks, non_fixed_blocks) = snapshot
|
||||
.blocks_in_range(rows.clone())
|
||||
.partition::<Vec<_>, _>(|(_, block)| block.style() == BlockStyle::Fixed);
|
||||
@@ -4279,7 +4266,6 @@ impl EditorElement {
|
||||
let mut blocks = Vec::new();
|
||||
let mut resized_blocks = HashMap::default();
|
||||
let mut row_block_types = HashMap::default();
|
||||
let mut block_resize_offset: i32 = 0;
|
||||
|
||||
for (row, block) in fixed_blocks {
|
||||
let block_id = block.id();
|
||||
@@ -4310,7 +4296,6 @@ impl EditorElement {
|
||||
latest_selection_anchors,
|
||||
is_row_soft_wrapped,
|
||||
sticky_header_excerpt_id,
|
||||
&mut block_resize_offset,
|
||||
window,
|
||||
cx,
|
||||
) {
|
||||
@@ -4369,7 +4354,6 @@ impl EditorElement {
|
||||
latest_selection_anchors,
|
||||
is_row_soft_wrapped,
|
||||
sticky_header_excerpt_id,
|
||||
&mut block_resize_offset,
|
||||
window,
|
||||
cx,
|
||||
) {
|
||||
@@ -4426,7 +4410,6 @@ impl EditorElement {
|
||||
latest_selection_anchors,
|
||||
is_row_soft_wrapped,
|
||||
sticky_header_excerpt_id,
|
||||
&mut block_resize_offset,
|
||||
window,
|
||||
cx,
|
||||
) {
|
||||
@@ -4446,12 +4429,9 @@ impl EditorElement {
|
||||
if resized_blocks.is_empty() {
|
||||
*scroll_width =
|
||||
(*scroll_width).max(fixed_block_max_width - editor_margins.gutter.width);
|
||||
}
|
||||
|
||||
RenderBlocksOutput {
|
||||
blocks,
|
||||
row_block_types,
|
||||
resized_blocks: (!resized_blocks.is_empty()).then_some(resized_blocks),
|
||||
Ok((blocks, row_block_types))
|
||||
} else {
|
||||
Err(resized_blocks)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8792,48 +8772,8 @@ impl EditorElement {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EditorRequestLayoutState {
|
||||
// We use prepaint depth to limit the number of times prepaint is
|
||||
// called recursively. We need this so that we can update stale
|
||||
// data for e.g. block heights in block map.
|
||||
prepaint_depth: Rc<Cell<usize>>,
|
||||
}
|
||||
|
||||
impl EditorRequestLayoutState {
|
||||
// In ideal conditions we only need one more subsequent prepaint call for resize to take effect.
|
||||
// i.e. MAX_PREPAINT_DEPTH = 2, but since moving blocks inline (place_near), more lines from
|
||||
// below get exposed, and we end up querying blocks for those lines too in subsequent renders.
|
||||
// Setting MAX_PREPAINT_DEPTH = 3, passes all tests. Just to be on the safe side we set it to 5, so
|
||||
// that subsequent shrinking does not lead to incorrect block placing.
|
||||
const MAX_PREPAINT_DEPTH: usize = 5;
|
||||
|
||||
fn increment_prepaint_depth(&self) -> EditorPrepaintGuard {
|
||||
let depth = self.prepaint_depth.get();
|
||||
self.prepaint_depth.set(depth + 1);
|
||||
EditorPrepaintGuard {
|
||||
prepaint_depth: self.prepaint_depth.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn can_prepaint(&self) -> bool {
|
||||
self.prepaint_depth.get() < Self::MAX_PREPAINT_DEPTH
|
||||
}
|
||||
}
|
||||
|
||||
struct EditorPrepaintGuard {
|
||||
prepaint_depth: Rc<Cell<usize>>,
|
||||
}
|
||||
|
||||
impl Drop for EditorPrepaintGuard {
|
||||
fn drop(&mut self) {
|
||||
let depth = self.prepaint_depth.get();
|
||||
self.prepaint_depth.set(depth.saturating_sub(1));
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for EditorElement {
|
||||
type RequestLayoutState = EditorRequestLayoutState;
|
||||
type RequestLayoutState = ();
|
||||
type PrepaintState = EditorLayout;
|
||||
|
||||
fn id(&self) -> Option<ElementId> {
|
||||
@@ -8850,7 +8790,7 @@ impl Element for EditorElement {
|
||||
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||
) -> (gpui::LayoutId, ()) {
|
||||
let rem_size = self.rem_size(cx);
|
||||
window.with_rem_size(rem_size, |window| {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
@@ -8917,7 +8857,7 @@ impl Element for EditorElement {
|
||||
}
|
||||
};
|
||||
|
||||
(layout_id, EditorRequestLayoutState::default())
|
||||
(layout_id, ())
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -8927,11 +8867,10 @@ impl Element for EditorElement {
|
||||
_: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||
bounds: Bounds<Pixels>,
|
||||
request_layout: &mut Self::RequestLayoutState,
|
||||
_: &mut Self::RequestLayoutState,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Self::PrepaintState {
|
||||
let _prepaint_depth_guard = request_layout.increment_prepaint_depth();
|
||||
let text_style = TextStyleRefinement {
|
||||
font_size: Some(self.style.text.font_size),
|
||||
line_height: Some(self.style.text.line_height),
|
||||
@@ -9455,20 +9394,7 @@ impl Element for EditorElement {
|
||||
// If the fold widths have changed, we need to prepaint
|
||||
// the element again to account for any changes in
|
||||
// wrapping.
|
||||
if request_layout.can_prepaint() {
|
||||
return self.prepaint(
|
||||
None,
|
||||
_inspector_id,
|
||||
bounds,
|
||||
request_layout,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
debug_panic!(
|
||||
"skipping recursive prepaint at max depth. renderer widths may be stale."
|
||||
);
|
||||
}
|
||||
return self.prepaint(None, _inspector_id, bounds, &mut (), window, cx);
|
||||
}
|
||||
|
||||
let longest_line_blame_width = self
|
||||
@@ -9555,35 +9481,20 @@ impl Element for EditorElement {
|
||||
)
|
||||
})
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let RenderBlocksOutput {
|
||||
mut blocks,
|
||||
row_block_types,
|
||||
resized_blocks,
|
||||
} = blocks;
|
||||
if let Some(resized_blocks) = resized_blocks {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.resize_blocks(
|
||||
resized_blocks,
|
||||
autoscroll_request.map(|(autoscroll, _)| autoscroll),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
if request_layout.can_prepaint() {
|
||||
return self.prepaint(
|
||||
None,
|
||||
_inspector_id,
|
||||
bounds,
|
||||
request_layout,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
debug_panic!(
|
||||
"skipping recursive prepaint at max depth. block layout may be stale."
|
||||
);
|
||||
.unwrap_or_else(|| Ok((Vec::default(), HashMap::default())));
|
||||
let (mut blocks, row_block_types) = match blocks {
|
||||
Ok(blocks) => blocks,
|
||||
Err(resized_blocks) => {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.resize_blocks(
|
||||
resized_blocks,
|
||||
autoscroll_request.map(|(autoscroll, _)| autoscroll),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
return self.prepaint(None, _inspector_id, bounds, &mut (), window, cx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let sticky_buffer_header = sticky_header_excerpt.map(|sticky_header_excerpt| {
|
||||
window.with_element_namespace("blocks", |window| {
|
||||
|
||||
@@ -3,7 +3,7 @@ use anyhow::{Context as _, Result, bail};
|
||||
use collections::{HashMap, HashSet};
|
||||
use futures::future::{self, BoxFuture, join_all};
|
||||
use git::{
|
||||
Oid,
|
||||
Oid, RunHook,
|
||||
blame::Blame,
|
||||
repository::{
|
||||
AskPassDelegate, Branch, CommitDetails, CommitOptions, FetchOptions, GitRepository,
|
||||
@@ -532,6 +532,14 @@ impl GitRepository for FakeGitRepository {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn run_hook(
|
||||
&self,
|
||||
_hook: RunHook,
|
||||
_env: Arc<HashMap<String, String>>,
|
||||
) -> BoxFuture<'_, Result<()>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn push(
|
||||
&self,
|
||||
_branch: String,
|
||||
|
||||
@@ -225,3 +225,28 @@ impl From<Oid> for usize {
|
||||
u64::from_ne_bytes(u64_bytes) as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum RunHook {
|
||||
PreCommit,
|
||||
}
|
||||
|
||||
impl RunHook {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::PreCommit => "pre-commit",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_proto(&self) -> i32 {
|
||||
*self as i32
|
||||
}
|
||||
|
||||
pub fn from_proto(value: i32) -> Option<Self> {
|
||||
match value {
|
||||
0 => Some(Self::PreCommit),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::commit::parse_git_diff_name_status;
|
||||
use crate::stash::GitStash;
|
||||
use crate::status::{DiffTreeType, GitStatus, StatusCode, TreeDiff};
|
||||
use crate::{Oid, SHORT_SHA_LENGTH};
|
||||
use crate::{Oid, RunHook, SHORT_SHA_LENGTH};
|
||||
use anyhow::{Context as _, Result, anyhow, bail};
|
||||
use collections::HashMap;
|
||||
use futures::future::BoxFuture;
|
||||
@@ -485,6 +485,12 @@ pub trait GitRepository: Send + Sync {
|
||||
env: Arc<HashMap<String, String>>,
|
||||
) -> BoxFuture<'_, Result<()>>;
|
||||
|
||||
fn run_hook(
|
||||
&self,
|
||||
hook: RunHook,
|
||||
env: Arc<HashMap<String, String>>,
|
||||
) -> BoxFuture<'_, Result<()>>;
|
||||
|
||||
fn commit(
|
||||
&self,
|
||||
message: SharedString,
|
||||
@@ -1643,6 +1649,7 @@ impl GitRepository for RealGitRepository {
|
||||
.args(["commit", "--quiet", "-m"])
|
||||
.arg(&message.to_string())
|
||||
.arg("--cleanup=strip")
|
||||
.arg("--no-verify")
|
||||
.stdout(smol::process::Stdio::piped())
|
||||
.stderr(smol::process::Stdio::piped());
|
||||
|
||||
@@ -2037,6 +2044,26 @@ impl GitRepository for RealGitRepository {
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn run_hook(
|
||||
&self,
|
||||
hook: RunHook,
|
||||
env: Arc<HashMap<String, String>>,
|
||||
) -> BoxFuture<'_, Result<()>> {
|
||||
let working_directory = self.working_directory();
|
||||
let git_binary_path = self.any_git_binary_path.clone();
|
||||
let executor = self.executor.clone();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
let working_directory = working_directory?;
|
||||
let git = GitBinary::new(git_binary_path, working_directory, executor)
|
||||
.envs(HashMap::clone(&env));
|
||||
git.run(&["hook", "run", "--ignore-missing", hook.as_str()])
|
||||
.await?;
|
||||
Ok(())
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
fn git_status_args(path_prefixes: &[RepoPath]) -> Vec<OsString> {
|
||||
|
||||
@@ -373,6 +373,7 @@ impl GitPanel {
|
||||
let is_sort_by_path = GitPanelSettings::get_global(cx).sort_by_path;
|
||||
if is_sort_by_path != was_sort_by_path {
|
||||
this.entries.clear();
|
||||
this.bulk_staging.take();
|
||||
this.update_visible_entries(window, cx);
|
||||
}
|
||||
was_sort_by_path = is_sort_by_path
|
||||
|
||||
@@ -1135,32 +1135,7 @@ impl Platform for MacPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
// Next, check for URL flavors (including file URLs). Some tools only provide a URL
|
||||
// with no plain text entry.
|
||||
{
|
||||
// Try the modern UTType identifiers first.
|
||||
let file_url_type: id = ns_string("public.file-url");
|
||||
let url_type: id = ns_string("public.url");
|
||||
|
||||
let url_data = if msg_send![types, containsObject: file_url_type] {
|
||||
pasteboard.dataForType(file_url_type)
|
||||
} else if msg_send![types, containsObject: url_type] {
|
||||
pasteboard.dataForType(url_type)
|
||||
} else {
|
||||
nil
|
||||
};
|
||||
|
||||
if url_data != nil && !url_data.bytes().is_null() {
|
||||
let bytes = slice::from_raw_parts(
|
||||
url_data.bytes() as *mut u8,
|
||||
url_data.length() as usize,
|
||||
);
|
||||
|
||||
return Some(self.read_string_from_clipboard(&state, bytes));
|
||||
}
|
||||
}
|
||||
|
||||
// If it wasn't a string or URL, try the various supported image types.
|
||||
// If it wasn't a string, try the various supported image types.
|
||||
for format in ImageFormat::iter() {
|
||||
if let Some(item) = try_clipboard_image(pasteboard, format) {
|
||||
return Some(item);
|
||||
@@ -1168,7 +1143,7 @@ impl Platform for MacPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
// If it wasn't a string, URL, or a supported image type, give up.
|
||||
// If it wasn't a string or a supported image type, give up.
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1743,40 +1718,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_url_reads_as_url_string() {
|
||||
let platform = build_platform();
|
||||
|
||||
// Create a file URL for an arbitrary test path and write it to the pasteboard.
|
||||
// This path does not need to exist; we only validate URL→path conversion.
|
||||
let mock_path = "/tmp/zed-clipboard-file-url-test";
|
||||
unsafe {
|
||||
// Build an NSURL from the file path
|
||||
let url: id = msg_send![class!(NSURL), fileURLWithPath: ns_string(mock_path)];
|
||||
let abs: id = msg_send![url, absoluteString];
|
||||
|
||||
// Encode the URL string as UTF-8 bytes
|
||||
let len: usize = msg_send![abs, lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
|
||||
let bytes_ptr = abs.UTF8String() as *const u8;
|
||||
let data = NSData::dataWithBytes_length_(nil, bytes_ptr as *const c_void, len as u64);
|
||||
|
||||
// Write as public.file-url to the unique pasteboard
|
||||
let file_url_type: id = ns_string("public.file-url");
|
||||
platform
|
||||
.0
|
||||
.lock()
|
||||
.pasteboard
|
||||
.setData_forType(data, file_url_type);
|
||||
}
|
||||
|
||||
// Ensure the clipboard read returns the URL string, not a converted path
|
||||
let expected_url = format!("file://{}", mock_path);
|
||||
assert_eq!(
|
||||
platform.read_from_clipboard(),
|
||||
Some(ClipboardItem::new_string(expected_url))
|
||||
);
|
||||
}
|
||||
|
||||
fn build_platform() -> MacPlatform {
|
||||
let platform = MacPlatform::new(false);
|
||||
platform.0.lock().pasteboard = unsafe { NSPasteboard::pasteboardWithUniqueName(nil) };
|
||||
|
||||
@@ -390,12 +390,10 @@ impl Platform for WindowsPlatform {
|
||||
clippy::disallowed_methods,
|
||||
reason = "We are restarting ourselves, using std command thus is fine"
|
||||
)]
|
||||
// todo(shell): There might be no powershell on the system
|
||||
let restart_process =
|
||||
util::command::new_std_command(util::shell::get_windows_system_shell())
|
||||
.arg("-command")
|
||||
.arg(script)
|
||||
.spawn();
|
||||
let restart_process = util::command::new_std_command("powershell.exe")
|
||||
.arg("-command")
|
||||
.arg(script)
|
||||
.spawn();
|
||||
|
||||
match restart_process {
|
||||
Ok(_) => self.quit(),
|
||||
|
||||
@@ -25,7 +25,7 @@ use futures::{
|
||||
stream::FuturesOrdered,
|
||||
};
|
||||
use git::{
|
||||
BuildPermalinkParams, GitHostingProviderRegistry, Oid,
|
||||
BuildPermalinkParams, GitHostingProviderRegistry, Oid, RunHook,
|
||||
blame::Blame,
|
||||
parse_git_remote_url,
|
||||
repository::{
|
||||
@@ -433,6 +433,7 @@ impl GitStore {
|
||||
client.add_entity_request_handler(Self::handle_stash_apply);
|
||||
client.add_entity_request_handler(Self::handle_stash_drop);
|
||||
client.add_entity_request_handler(Self::handle_commit);
|
||||
client.add_entity_request_handler(Self::handle_run_hook);
|
||||
client.add_entity_request_handler(Self::handle_reset);
|
||||
client.add_entity_request_handler(Self::handle_show);
|
||||
client.add_entity_request_handler(Self::handle_load_commit_diff);
|
||||
@@ -1982,6 +1983,22 @@ impl GitStore {
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_run_hook(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::RunGitHook>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let repository_id = RepositoryId::from_proto(envelope.payload.repository_id);
|
||||
let repository_handle = Self::repository_for_request(&this, repository_id, &mut cx)?;
|
||||
let hook = RunHook::from_proto(envelope.payload.hook).context("invalid hook")?;
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, cx| {
|
||||
repository_handle.run_hook(hook, cx)
|
||||
})?
|
||||
.await??;
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_commit(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::Commit>,
|
||||
@@ -4262,19 +4279,49 @@ impl Repository {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run_hook(&mut self, hook: RunHook, _cx: &mut App) -> oneshot::Receiver<Result<()>> {
|
||||
let id = self.id;
|
||||
self.send_job(
|
||||
Some(format!("git hook {}", hook.as_str()).into()),
|
||||
move |git_repo, _cx| async move {
|
||||
match git_repo {
|
||||
RepositoryState::Local {
|
||||
backend,
|
||||
environment,
|
||||
} => backend.run_hook(hook, environment.clone()).await,
|
||||
RepositoryState::Remote { project_id, client } => {
|
||||
client
|
||||
.request(proto::RunGitHook {
|
||||
project_id: project_id.0,
|
||||
repository_id: id.to_proto(),
|
||||
hook: hook.to_proto(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn commit(
|
||||
&mut self,
|
||||
message: SharedString,
|
||||
name_and_email: Option<(SharedString, SharedString)>,
|
||||
options: CommitOptions,
|
||||
askpass: AskPassDelegate,
|
||||
_cx: &mut App,
|
||||
cx: &mut App,
|
||||
) -> oneshot::Receiver<Result<()>> {
|
||||
let id = self.id;
|
||||
let askpass_delegates = self.askpass_delegates.clone();
|
||||
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
|
||||
|
||||
let rx = self.run_hook(RunHook::PreCommit, cx);
|
||||
|
||||
self.send_job(Some("git commit".into()), move |git_repo, _cx| async move {
|
||||
rx.await??;
|
||||
|
||||
match git_repo {
|
||||
RepositoryState::Local {
|
||||
backend,
|
||||
@@ -4999,6 +5046,7 @@ impl Repository {
|
||||
if update.is_last_update {
|
||||
self.snapshot.scan_id = update.scan_id;
|
||||
}
|
||||
self.clear_pending_ops(cx);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -531,3 +531,13 @@ message GitCreateWorktree {
|
||||
string directory = 4;
|
||||
optional string commit = 5;
|
||||
}
|
||||
|
||||
message RunGitHook {
|
||||
enum GitHook {
|
||||
PRE_COMMIT = 0;
|
||||
}
|
||||
|
||||
uint64 project_id = 1;
|
||||
uint64 repository_id = 2;
|
||||
GitHook hook = 3;
|
||||
}
|
||||
|
||||
@@ -437,7 +437,9 @@ message Envelope {
|
||||
OpenImageResponse open_image_response = 392;
|
||||
CreateImageForPeer create_image_for_peer = 393;
|
||||
|
||||
ExternalExtensionAgentsUpdated external_extension_agents_updated = 394; // current max
|
||||
ExternalExtensionAgentsUpdated external_extension_agents_updated = 394;
|
||||
|
||||
RunGitHook run_git_hook = 395; // current max
|
||||
}
|
||||
|
||||
reserved 87 to 88;
|
||||
|
||||
@@ -126,7 +126,7 @@ impl ErrorExt for anyhow::Error {
|
||||
if let Some(rpc_error) = self.downcast_ref::<RpcError>() {
|
||||
rpc_error.cloned()
|
||||
} else {
|
||||
anyhow::anyhow!("{self}")
|
||||
anyhow::anyhow!("{self:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ messages!(
|
||||
(ChannelMessageUpdate, Foreground),
|
||||
(CloseBuffer, Foreground),
|
||||
(Commit, Background),
|
||||
(RunGitHook, Background),
|
||||
(CopyProjectEntry, Foreground),
|
||||
(CreateBufferForPeer, Foreground),
|
||||
(CreateImageForPeer, Foreground),
|
||||
@@ -349,6 +350,7 @@ request_messages!(
|
||||
(Call, Ack),
|
||||
(CancelCall, Ack),
|
||||
(Commit, Ack),
|
||||
(RunGitHook, Ack),
|
||||
(CopyProjectEntry, ProjectEntryResponse),
|
||||
(CreateChannel, CreateChannelResponse),
|
||||
(CreateProjectEntry, ProjectEntryResponse),
|
||||
@@ -547,6 +549,7 @@ entity_messages!(
|
||||
BufferSaved,
|
||||
CloseBuffer,
|
||||
Commit,
|
||||
RunGitHook,
|
||||
GetColorPresentation,
|
||||
CopyProjectEntry,
|
||||
CreateBufferForPeer,
|
||||
|
||||
@@ -1327,7 +1327,7 @@ fn build_command(
|
||||
let working_dir = RemotePathBuf::new(working_dir, ssh_path_style).to_string();
|
||||
|
||||
// shlex will wrap the command in single quotes (''), disabling ~ expansion,
|
||||
// replace with with something that works
|
||||
// replace with something that works
|
||||
const TILDE_PREFIX: &'static str = "~/";
|
||||
if working_dir.starts_with(TILDE_PREFIX) {
|
||||
let working_dir = working_dir.trim_start_matches("~").trim_start_matches("/");
|
||||
|
||||
@@ -251,11 +251,13 @@ impl WslRemoteConnection {
|
||||
let mkdir = self.shell_kind.prepend_command_prefix("mkdir");
|
||||
self.run_wsl_command(&mkdir, &["-p", &parent])
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to create directory when uploading file: {}", e))?;
|
||||
.context("Failed to create directory when uploading file")?;
|
||||
}
|
||||
|
||||
let t0 = Instant::now();
|
||||
let src_stat = fs::metadata(&src_path).await?;
|
||||
let src_stat = fs::metadata(&src_path)
|
||||
.await
|
||||
.with_context(|| format!("source path does not exist: {}", src_path.display()))?;
|
||||
let size = src_stat.len();
|
||||
log::info!(
|
||||
"uploading remote server to WSL {:?} ({}kb)",
|
||||
|
||||
@@ -2,8 +2,6 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{borrow::Cow, fmt, path::Path, sync::LazyLock};
|
||||
|
||||
use crate::command::new_std_command;
|
||||
|
||||
/// Shell configuration to open the terminal with.
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -110,12 +108,16 @@ pub fn get_windows_system_shell() -> String {
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn find_pwsh_in_programfiles(find_alternate: bool, find_preview: bool) -> Option<PathBuf> {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
let env_var = if find_alternate {
|
||||
if cfg!(target_pointer_width = "64") {
|
||||
"ProgramFiles(x86)"
|
||||
} else {
|
||||
"ProgramW6432"
|
||||
}
|
||||
"ProgramFiles(x86)"
|
||||
} else {
|
||||
"ProgramFiles"
|
||||
};
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
let env_var = if find_alternate {
|
||||
"ProgramW6432"
|
||||
} else {
|
||||
"ProgramFiles"
|
||||
};
|
||||
@@ -163,19 +165,23 @@ pub fn get_windows_system_shell() -> String {
|
||||
} else {
|
||||
"Microsoft.PowerShell_"
|
||||
};
|
||||
msix_app_dir.read_dir().ok()?.find_map(|entry| {
|
||||
let entry = entry.ok()?;
|
||||
if !matches!(entry.file_type(), Ok(ft) if ft.is_dir()) {
|
||||
return None;
|
||||
}
|
||||
msix_app_dir
|
||||
.read_dir()
|
||||
.ok()?
|
||||
.filter_map(|entry| {
|
||||
let entry = entry.ok()?;
|
||||
if !matches!(entry.file_type(), Ok(ft) if ft.is_dir()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if !entry.file_name().to_string_lossy().starts_with(prefix) {
|
||||
return None;
|
||||
}
|
||||
if !entry.file_name().to_string_lossy().starts_with(prefix) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let exe_path = entry.path().join("pwsh.exe");
|
||||
exe_path.exists().then_some(exe_path)
|
||||
})
|
||||
let exe_path = entry.path().join("pwsh.exe");
|
||||
exe_path.exists().then_some(exe_path)
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
fn find_pwsh_in_scoop() -> Option<PathBuf> {
|
||||
@@ -184,37 +190,15 @@ pub fn get_windows_system_shell() -> String {
|
||||
pwsh_exe.exists().then_some(pwsh_exe)
|
||||
}
|
||||
|
||||
// check whether the found powershell is executable for us
|
||||
static SYSTEM_SHELL: LazyLock<String> = LazyLock::new(|| {
|
||||
let can_execute_pwsh = |p: &PathBuf| {
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let status = new_std_command(p).arg("-NoProfile").arg("-Help").status();
|
||||
let success = status.as_ref().is_ok_and(|status| status.success());
|
||||
if !success {
|
||||
log::warn!(
|
||||
"Powershell found at `{}` is not executable: {status:?}",
|
||||
p.display()
|
||||
);
|
||||
}
|
||||
success
|
||||
};
|
||||
|
||||
let locations = [
|
||||
|| find_pwsh_in_programfiles(false, false),
|
||||
|| find_pwsh_in_programfiles(true, false),
|
||||
|| find_pwsh_in_msix(false),
|
||||
|| find_pwsh_in_programfiles(false, true),
|
||||
|| find_pwsh_in_msix(true),
|
||||
|| find_pwsh_in_programfiles(true, true),
|
||||
|| find_pwsh_in_scoop(),
|
||||
|| which::which_global("pwsh.exe").ok(),
|
||||
|| which::which_global("powershell.exe").ok(),
|
||||
];
|
||||
locations
|
||||
.into_iter()
|
||||
.filter_map(|f| f())
|
||||
.find(|p| can_execute_pwsh(&p))
|
||||
.map(|p| p.to_string_lossy().trim().to_owned())
|
||||
find_pwsh_in_programfiles(false, false)
|
||||
.or_else(|| find_pwsh_in_programfiles(true, false))
|
||||
.or_else(|| find_pwsh_in_msix(false))
|
||||
.or_else(|| find_pwsh_in_programfiles(false, true))
|
||||
.or_else(|| find_pwsh_in_msix(true))
|
||||
.or_else(|| find_pwsh_in_programfiles(true, true))
|
||||
.or_else(find_pwsh_in_scoop)
|
||||
.map(|p| p.to_string_lossy().into_owned())
|
||||
.inspect(|shell| log::info!("Found powershell in: {}", shell))
|
||||
.unwrap_or_else(|| {
|
||||
log::warn!("Powershell not found, falling back to `cmd`");
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "The fast, collaborative code editor."
|
||||
edition.workspace = true
|
||||
name = "zed"
|
||||
version = "0.214.0"
|
||||
version = "0.214.2"
|
||||
publish.workspace = true
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Zed Team <hi@zed.dev>"]
|
||||
|
||||
@@ -1 +1 @@
|
||||
dev
|
||||
preview
|
||||
@@ -113,6 +113,8 @@ if [[ -n "${MACOS_CERTIFICATE:-}" && -n "${MACOS_CERTIFICATE_PASSWORD:-}" && -n
|
||||
security create-keychain -p "$MACOS_CERTIFICATE_PASSWORD" zed.keychain || echo ""
|
||||
security default-keychain -s zed.keychain
|
||||
security unlock-keychain -p "$MACOS_CERTIFICATE_PASSWORD" zed.keychain
|
||||
# Calling set-keychain-settings without `-t` disables the auto-lock timeout
|
||||
security set-keychain-settings zed.keychain
|
||||
echo "$MACOS_CERTIFICATE" | base64 --decode > /tmp/zed-certificate.p12
|
||||
security import /tmp/zed-certificate.p12 -k zed.keychain -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
|
||||
rm /tmp/zed-certificate.p12
|
||||
|
||||
@@ -204,7 +204,7 @@ function CollectFiles {
|
||||
if($Architecture -eq "aarch64") {
|
||||
New-Item -Type Directory -Path "$innoDir\arm64" -Force
|
||||
Move-Item -Path ".\conpty\build\native\runtimes\arm64\OpenConsole.exe" -Destination "$innoDir\arm64\OpenConsole.exe" -Force
|
||||
Move-Item -Path ".\conpty\runtimes\win10-arm64\native\conpty.dll" -Destination "$innoDir\conpty.dll" -Force
|
||||
Move-Item -Path ".\conpty\runtimes\win-arm64\native\conpty.dll" -Destination "$innoDir\conpty.dll" -Force
|
||||
}
|
||||
else {
|
||||
New-Item -Type Directory -Path "$innoDir\x64" -Force
|
||||
@@ -212,7 +212,7 @@ function CollectFiles {
|
||||
Move-Item -Path ".\AGS_SDK-6.3.0\ags_lib\lib\amd_ags_x64.dll" -Destination "$innoDir\amd_ags_x64.dll" -Force
|
||||
Move-Item -Path ".\conpty\build\native\runtimes\x64\OpenConsole.exe" -Destination "$innoDir\x64\OpenConsole.exe" -Force
|
||||
Move-Item -Path ".\conpty\build\native\runtimes\arm64\OpenConsole.exe" -Destination "$innoDir\arm64\OpenConsole.exe" -Force
|
||||
Move-Item -Path ".\conpty\runtimes\win10-x64\native\conpty.dll" -Destination "$innoDir\conpty.dll" -Force
|
||||
Move-Item -Path ".\conpty\runtimes\win-x64\native\conpty.dll" -Destination "$innoDir\conpty.dll" -Force
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user