Compare commits
17 Commits
acp-rewind
...
v0.190.3-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
857897ee10 | ||
|
|
e6de7933bb | ||
|
|
fc077d194c | ||
|
|
4cf76207aa | ||
|
|
1b45289efa | ||
|
|
8eb12a3c3d | ||
|
|
7b4f37353a | ||
|
|
9b8035e8c2 | ||
|
|
f6fdb7094b | ||
|
|
0c90b19fa1 | ||
|
|
cb2885410d | ||
|
|
6535a148b5 | ||
|
|
fc3a5a799e | ||
|
|
393382c448 | ||
|
|
2ad24ef799 | ||
|
|
185e0696ae | ||
|
|
750c6356e7 |
62
.github/workflows/ci.yml
vendored
62
.github/workflows/ci.yml
vendored
@@ -715,6 +715,64 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
freebsd:
|
||||
timeout-minutes: 60
|
||||
runs-on: github-8vcpu-ubuntu-2404
|
||||
if: |
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
|
||||
needs: [linux_tests]
|
||||
name: Build Zed on FreeBSD
|
||||
# env:
|
||||
# MYTOKEN : ${{ secrets.MYTOKEN }}
|
||||
# MYTOKEN2: "value2"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build FreeBSD remote-server
|
||||
id: freebsd-build
|
||||
uses: vmactions/freebsd-vm@c3ae29a132c8ef1924775414107a97cac042aad5 # v1.2.0
|
||||
with:
|
||||
# envs: "MYTOKEN MYTOKEN2"
|
||||
usesh: true
|
||||
release: 13.5
|
||||
copyback: true
|
||||
prepare: |
|
||||
pkg install -y \
|
||||
bash curl jq git \
|
||||
rustup-init cmake-core llvm-devel-lite pkgconf protobuf # ibx11 alsa-lib rust-bindgen-cli
|
||||
run: |
|
||||
freebsd-version
|
||||
sysctl hw.model
|
||||
sysctl hw.ncpu
|
||||
sysctl hw.physmem
|
||||
sysctl hw.usermem
|
||||
git config --global --add safe.directory /home/runner/work/zed/zed
|
||||
rustup-init --profile minimal --default-toolchain none -y
|
||||
. "$HOME/.cargo/env"
|
||||
./script/bundle-freebsd
|
||||
mkdir -p out/
|
||||
mv "target/zed-remote-server-freebsd-x86_64.gz" out/
|
||||
rm -rf target/
|
||||
cargo clean
|
||||
|
||||
- name: Upload Artifact to Workflow - zed-remote-server (run-bundling)
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
|
||||
with:
|
||||
name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-freebsd.gz
|
||||
path: out/zed-remote-server-freebsd-x86_64.gz
|
||||
|
||||
- name: Upload Artifacts to release
|
||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
|
||||
if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) }}
|
||||
with:
|
||||
draft: true
|
||||
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
|
||||
files: |
|
||||
out/zed-remote-server-freebsd-x86_64.gz
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
nix-build:
|
||||
name: Build with Nix
|
||||
uses: ./.github/workflows/nix.yml
|
||||
@@ -729,12 +787,12 @@ jobs:
|
||||
if: |
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
&& endsWith(github.ref, '-pre') && !endsWith(github.ref, '.0-pre')
|
||||
needs: [bundle-mac, bundle-linux-x86_x64, bundle-linux-aarch64]
|
||||
needs: [bundle-mac, bundle-linux-x86_x64, bundle-linux-aarch64, freebsd]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- bundle
|
||||
steps:
|
||||
- name: gh release
|
||||
run: gh release edit $GITHUB_REF_NAME --draft=true
|
||||
run: gh release edit $GITHUB_REF_NAME --draft=false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
56
.github/workflows/release_nightly.yml
vendored
56
.github/workflows/release_nightly.yml
vendored
@@ -167,6 +167,62 @@ jobs:
|
||||
- name: Upload Zed Nightly
|
||||
run: script/upload-nightly linux-targz
|
||||
|
||||
freebsd:
|
||||
timeout-minutes: 60
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: github-8vcpu-ubuntu-2404
|
||||
needs: tests
|
||||
name: Build Zed on FreeBSD
|
||||
# env:
|
||||
# MYTOKEN : ${{ secrets.MYTOKEN }}
|
||||
# MYTOKEN2: "value2"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build FreeBSD remote-server
|
||||
id: freebsd-build
|
||||
uses: vmactions/freebsd-vm@c3ae29a132c8ef1924775414107a97cac042aad5 # v1.2.0
|
||||
with:
|
||||
# envs: "MYTOKEN MYTOKEN2"
|
||||
usesh: true
|
||||
release: 13.5
|
||||
copyback: true
|
||||
prepare: |
|
||||
pkg install -y \
|
||||
bash curl jq git \
|
||||
rustup-init cmake-core llvm-devel-lite pkgconf protobuf # ibx11 alsa-lib rust-bindgen-cli
|
||||
run: |
|
||||
freebsd-version
|
||||
sysctl hw.model
|
||||
sysctl hw.ncpu
|
||||
sysctl hw.physmem
|
||||
sysctl hw.usermem
|
||||
git config --global --add safe.directory /home/runner/work/zed/zed
|
||||
rustup-init --profile minimal --default-toolchain none -y
|
||||
. "$HOME/.cargo/env"
|
||||
./script/bundle-freebsd
|
||||
mkdir -p out/
|
||||
mv "target/zed-remote-server-freebsd-x86_64.gz" out/
|
||||
rm -rf target/
|
||||
cargo clean
|
||||
|
||||
- name: Upload Artifact to Workflow - zed-remote-server (run-bundling)
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
|
||||
with:
|
||||
name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-freebsd.gz
|
||||
path: out/zed-remote-server-freebsd-x86_64.gz
|
||||
|
||||
- name: Upload Artifacts to release
|
||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
|
||||
if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) }}
|
||||
with:
|
||||
draft: true
|
||||
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
|
||||
files: |
|
||||
out/zed-remote-server-freebsd-x86_64.gz
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
bundle-nix:
|
||||
name: Build and cache Nix package
|
||||
needs: tests
|
||||
|
||||
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -16509,9 +16509,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter"
|
||||
version = "0.25.5"
|
||||
version = "0.25.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac5fff5c47490dfdf473b5228039bfacad9d765d9b6939d26bf7cc064c1c7822"
|
||||
checksum = "a7cf18d43cbf0bfca51f657132cc616a5097edc4424d538bae6fa60142eaf9f0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"regex",
|
||||
@@ -16524,9 +16524,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-bash"
|
||||
version = "0.23.3"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "329a4d48623ac337d42b1df84e81a1c9dbb2946907c102ca72db158c1964a52e"
|
||||
checksum = "871b0606e667e98a1237ebdc1b0d7056e0aebfdc3141d12b399865d4cb6ed8a6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter-language",
|
||||
@@ -19708,7 +19708,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.190.0"
|
||||
version = "0.190.3"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"agent",
|
||||
|
||||
@@ -574,8 +574,8 @@ tokio = { version = "1" }
|
||||
tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"] }
|
||||
toml = "0.8"
|
||||
tower-http = "0.4.4"
|
||||
tree-sitter = { version = "0.25.5", features = ["wasm"] }
|
||||
tree-sitter-bash = "0.23"
|
||||
tree-sitter = { version = "0.25.6", features = ["wasm"] }
|
||||
tree-sitter-bash = "0.25.0"
|
||||
tree-sitter-c = "0.23"
|
||||
tree-sitter-cpp = "0.23"
|
||||
tree-sitter-css = "0.23"
|
||||
|
||||
@@ -1525,7 +1525,7 @@
|
||||
"allow_rewrap": "anywhere"
|
||||
},
|
||||
"Ruby": {
|
||||
"language_servers": ["solargraph", "!ruby-lsp", "!rubocop", "..."]
|
||||
"language_servers": ["solargraph", "!ruby-lsp", "!rubocop", "!sorbet", "!steep", "..."]
|
||||
},
|
||||
"SCSS": {
|
||||
"prettier": {
|
||||
|
||||
@@ -767,7 +767,7 @@ impl CompletionProvider for ContextPickerCompletionProvider {
|
||||
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
let source_range = snapshot.anchor_before(state.source_range.start)
|
||||
..snapshot.anchor_before(state.source_range.end);
|
||||
..snapshot.anchor_after(state.source_range.end);
|
||||
|
||||
let thread_store = self.thread_store.clone();
|
||||
let text_thread_store = self.text_thread_store.clone();
|
||||
|
||||
@@ -240,13 +240,14 @@ impl SlashCommandCompletionProvider {
|
||||
|
||||
Ok(vec![project::CompletionResponse {
|
||||
completions,
|
||||
is_incomplete: false,
|
||||
// TODO: Could have slash commands indicate whether their completions are incomplete.
|
||||
is_incomplete: true,
|
||||
}])
|
||||
})
|
||||
} else {
|
||||
Task::ready(Ok(vec![project::CompletionResponse {
|
||||
completions: Vec::new(),
|
||||
is_incomplete: false,
|
||||
is_incomplete: true,
|
||||
}]))
|
||||
}
|
||||
}
|
||||
@@ -275,17 +276,17 @@ impl CompletionProvider for SlashCommandCompletionProvider {
|
||||
position.row,
|
||||
call.arguments.last().map_or(call.name.end, |arg| arg.end) as u32,
|
||||
);
|
||||
let command_range = buffer.anchor_after(command_range_start)
|
||||
let command_range = buffer.anchor_before(command_range_start)
|
||||
..buffer.anchor_after(command_range_end);
|
||||
|
||||
let name = line[call.name.clone()].to_string();
|
||||
let (arguments, last_argument_range) = if let Some(argument) = call.arguments.last()
|
||||
{
|
||||
let last_arg_start =
|
||||
buffer.anchor_after(Point::new(position.row, argument.start as u32));
|
||||
buffer.anchor_before(Point::new(position.row, argument.start as u32));
|
||||
let first_arg_start = call.arguments.first().expect("we have the last element");
|
||||
let first_arg_start =
|
||||
buffer.anchor_after(Point::new(position.row, first_arg_start.start as u32));
|
||||
let first_arg_start = buffer
|
||||
.anchor_before(Point::new(position.row, first_arg_start.start as u32));
|
||||
let arguments = call
|
||||
.arguments
|
||||
.into_iter()
|
||||
@@ -298,7 +299,7 @@ impl CompletionProvider for SlashCommandCompletionProvider {
|
||||
)
|
||||
} else {
|
||||
let start =
|
||||
buffer.anchor_after(Point::new(position.row, call.name.start as u32));
|
||||
buffer.anchor_before(Point::new(position.row, call.name.start as u32));
|
||||
(None, start..buffer_position)
|
||||
};
|
||||
|
||||
|
||||
@@ -5046,7 +5046,13 @@ impl Editor {
|
||||
return;
|
||||
}
|
||||
|
||||
let position = self.selections.newest_anchor().head();
|
||||
let multibuffer_snapshot = self.buffer.read(cx).read(cx);
|
||||
|
||||
let position = self
|
||||
.selections
|
||||
.newest_anchor()
|
||||
.head()
|
||||
.bias_right(&multibuffer_snapshot);
|
||||
if position.diff_base_anchor.is_some() {
|
||||
return;
|
||||
}
|
||||
@@ -5059,8 +5065,9 @@ impl Editor {
|
||||
let buffer_snapshot = buffer.read(cx).snapshot();
|
||||
|
||||
let query: Option<Arc<String>> =
|
||||
Self::completion_query(&self.buffer.read(cx).read(cx), position)
|
||||
.map(|query| query.into());
|
||||
Self::completion_query(&multibuffer_snapshot, position).map(|query| query.into());
|
||||
|
||||
drop(multibuffer_snapshot);
|
||||
|
||||
let provider = match requested_source {
|
||||
Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
|
||||
|
||||
@@ -726,7 +726,7 @@ fn completion_replace_range(snapshot: &BufferSnapshot, anchor: &Anchor) -> Optio
|
||||
|
||||
if end_in_line > start_in_line {
|
||||
let replace_start = snapshot.anchor_before(line_start + start_in_line);
|
||||
let replace_end = snapshot.anchor_before(line_start + end_in_line);
|
||||
let replace_end = snapshot.anchor_after(line_start + end_in_line);
|
||||
Some(replace_start..replace_end)
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -6213,7 +6213,7 @@ impl MultiBufferSnapshot {
|
||||
cursor.seek_to_start_of_current_excerpt();
|
||||
let region = cursor.region()?;
|
||||
let offset = region.range.start;
|
||||
let buffer_offset = region.buffer_range.start;
|
||||
let buffer_offset = start_excerpt.buffer_start_offset();
|
||||
let excerpt_offset = cursor.excerpts.start().clone();
|
||||
Some(MultiBufferExcerpt {
|
||||
diff_transforms: cursor.diff_transforms,
|
||||
|
||||
@@ -2842,6 +2842,22 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
|
||||
.unwrap()
|
||||
+ 1
|
||||
);
|
||||
let reference_ranges = cx.update(|cx| {
|
||||
reference
|
||||
.excerpts
|
||||
.iter()
|
||||
.map(|excerpt| {
|
||||
(
|
||||
excerpt.id,
|
||||
excerpt.range.to_offset(&excerpt.buffer.read(cx).snapshot()),
|
||||
)
|
||||
})
|
||||
.collect::<HashMap<_, _>>()
|
||||
});
|
||||
for i in 0..snapshot.len() {
|
||||
let excerpt = snapshot.excerpt_containing(i..i).unwrap();
|
||||
assert_eq!(excerpt.buffer_range(), reference_ranges[&excerpt.id()]);
|
||||
}
|
||||
|
||||
assert_consistent_line_numbers(&snapshot);
|
||||
assert_position_translation(&snapshot);
|
||||
|
||||
@@ -171,7 +171,8 @@ impl ConflictSet {
|
||||
let mut conflicts = Vec::new();
|
||||
|
||||
let mut line_pos = 0;
|
||||
let mut lines = buffer.text_for_range(0..buffer.len()).lines();
|
||||
let buffer_len = buffer.len();
|
||||
let mut lines = buffer.text_for_range(0..buffer_len).lines();
|
||||
|
||||
let mut conflict_start: Option<usize> = None;
|
||||
let mut ours_start: Option<usize> = None;
|
||||
@@ -212,7 +213,7 @@ impl ConflictSet {
|
||||
&& theirs_start.is_some()
|
||||
{
|
||||
let theirs_end = line_pos;
|
||||
let conflict_end = line_end + 1;
|
||||
let conflict_end = (line_end + 1).min(buffer_len);
|
||||
|
||||
let range = buffer.anchor_after(conflict_start.unwrap())
|
||||
..buffer.anchor_before(conflict_end);
|
||||
@@ -390,6 +391,22 @@ mod tests {
|
||||
assert_eq!(their_text, "This is their version in a nested conflict\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conflict_markers_at_eof() {
|
||||
let test_content = r#"
|
||||
<<<<<<< ours
|
||||
=======
|
||||
This is their version
|
||||
>>>>>>> "#
|
||||
.unindent();
|
||||
let buffer_id = BufferId::new(1).unwrap();
|
||||
let buffer = Buffer::new(0, buffer_id, test_content.to_string());
|
||||
let snapshot = buffer.snapshot();
|
||||
|
||||
let conflict_snapshot = ConflictSet::parse(&snapshot);
|
||||
assert_eq!(conflict_snapshot.conflicts.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conflicts_in_range() {
|
||||
// Create a buffer with conflict markers
|
||||
|
||||
@@ -2174,10 +2174,6 @@ impl Pane {
|
||||
self.pinned_tab_count > ix
|
||||
}
|
||||
|
||||
fn has_pinned_tabs(&self) -> bool {
|
||||
self.pinned_tab_count != 0
|
||||
}
|
||||
|
||||
fn has_unpinned_tabs(&self) -> bool {
|
||||
self.pinned_tab_count < self.items.len()
|
||||
}
|
||||
@@ -2893,6 +2889,7 @@ impl Pane {
|
||||
|| cfg!(not(target_os = "macos")) && window.modifiers().control;
|
||||
|
||||
let from_pane = dragged_tab.pane.clone();
|
||||
let from_ix = dragged_tab.ix;
|
||||
self.workspace
|
||||
.update(cx, |_, cx| {
|
||||
cx.defer_in(window, move |workspace, window, cx| {
|
||||
@@ -2900,8 +2897,11 @@ impl Pane {
|
||||
to_pane = workspace.split_pane(to_pane, split_direction, window, cx);
|
||||
}
|
||||
let database_id = workspace.database_id();
|
||||
let old_ix = from_pane.read(cx).index_for_item_id(item_id);
|
||||
let old_len = to_pane.read(cx).items.len();
|
||||
let was_pinned_in_from_pane = from_pane.read_with(cx, |pane, _| {
|
||||
pane.index_for_item_id(item_id)
|
||||
.is_some_and(|ix| pane.is_tab_pinned(ix))
|
||||
});
|
||||
let to_pane_old_length = to_pane.read(cx).items.len();
|
||||
if is_clone {
|
||||
let Some(item) = from_pane
|
||||
.read(cx)
|
||||
@@ -2919,38 +2919,36 @@ impl Pane {
|
||||
} else {
|
||||
move_item(&from_pane, &to_pane, item_id, ix, true, window, cx);
|
||||
}
|
||||
if to_pane == from_pane {
|
||||
if let Some(old_index) = old_ix {
|
||||
to_pane.update(cx, |this, _| {
|
||||
if old_index < this.pinned_tab_count
|
||||
&& (ix == this.items.len() || ix > this.pinned_tab_count)
|
||||
{
|
||||
this.pinned_tab_count -= 1;
|
||||
} else if this.has_pinned_tabs()
|
||||
&& old_index >= this.pinned_tab_count
|
||||
&& ix < this.pinned_tab_count
|
||||
{
|
||||
this.pinned_tab_count += 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
to_pane.update(cx, |this, _| {
|
||||
if this.items.len() > old_len // Did we not deduplicate on drag?
|
||||
&& this.has_pinned_tabs()
|
||||
&& ix < this.pinned_tab_count
|
||||
to_pane.update(cx, |this, _| {
|
||||
if to_pane == from_pane {
|
||||
let moved_right = ix > from_ix;
|
||||
let ix = if moved_right { ix - 1 } else { ix };
|
||||
let is_pinned_in_to_pane = this.is_tab_pinned(ix);
|
||||
|
||||
if (was_pinned_in_from_pane && is_pinned_in_to_pane)
|
||||
|| (!was_pinned_in_from_pane && !is_pinned_in_to_pane)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if is_pinned_in_to_pane {
|
||||
this.pinned_tab_count += 1;
|
||||
} else {
|
||||
this.pinned_tab_count -= 1;
|
||||
}
|
||||
} else if this.items.len() >= to_pane_old_length {
|
||||
let is_pinned_in_to_pane = this.is_tab_pinned(ix);
|
||||
let item_created_pane = to_pane_old_length == 0;
|
||||
let is_first_position = ix == 0;
|
||||
let was_dropped_at_beginning = item_created_pane || is_first_position;
|
||||
let should_remain_pinned = is_pinned_in_to_pane
|
||||
|| (was_pinned_in_from_pane && was_dropped_at_beginning);
|
||||
|
||||
if should_remain_pinned {
|
||||
this.pinned_tab_count += 1;
|
||||
}
|
||||
});
|
||||
from_pane.update(cx, |this, _| {
|
||||
if let Some(index) = old_ix {
|
||||
if this.pinned_tab_count > index {
|
||||
this.pinned_tab_count -= 1;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
.log_err();
|
||||
@@ -4325,6 +4323,725 @@ mod tests {
|
||||
assert_item_labels(&pane, ["C", "A", "B*"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_unpinned_tab_to_split_creates_pane_with_unpinned_tab(
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A, B. Pin B. Activate A
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
let item_b = add_labeled_item(&pane_a, "B", false, cx);
|
||||
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_b.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.activate_item(ix, true, true, window, cx);
|
||||
});
|
||||
|
||||
// Drag A to create new split
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
pane.drag_split_direction = Some(SplitDirection::Right);
|
||||
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 0, window, cx);
|
||||
});
|
||||
|
||||
// A should be moved to new pane. B should remain pinned, A should not be pinned
|
||||
let (pane_a, pane_b) = workspace.read_with(cx, |workspace, _| {
|
||||
let panes = workspace.panes();
|
||||
(panes[0].clone(), panes[1].clone())
|
||||
});
|
||||
assert_item_labels(&pane_a, ["B*!"], cx);
|
||||
assert_item_labels(&pane_b, ["A*"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_pinned_tab_to_split_creates_pane_with_pinned_tab(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A, B. Pin both. Activate A
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
let item_b = add_labeled_item(&pane_a, "B", false, cx);
|
||||
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
|
||||
let ix = pane.index_for_item_id(item_b.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.activate_item(ix, true, true, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_a, ["A*!", "B!"], cx);
|
||||
|
||||
// Drag A to create new split
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
pane.drag_split_direction = Some(SplitDirection::Right);
|
||||
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 0, window, cx);
|
||||
});
|
||||
|
||||
// A should be moved to new pane. Both A and B should still be pinned
|
||||
let (pane_a, pane_b) = workspace.read_with(cx, |workspace, _| {
|
||||
let panes = workspace.panes();
|
||||
(panes[0].clone(), panes[1].clone())
|
||||
});
|
||||
assert_item_labels(&pane_a, ["B*!"], cx);
|
||||
assert_item_labels(&pane_b, ["A*!"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_pinned_tab_into_existing_panes_pinned_region(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A to pane A and pin
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_a, ["A*!"], cx);
|
||||
|
||||
// Add B to pane B and pin
|
||||
let pane_b = workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace.split_pane(pane_a.clone(), SplitDirection::Right, window, cx)
|
||||
});
|
||||
let item_b = add_labeled_item(&pane_b, "B", false, cx);
|
||||
pane_b.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_b.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_b, ["B*!"], cx);
|
||||
|
||||
// Move A from pane A to pane B's pinned region
|
||||
pane_b.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 0, window, cx);
|
||||
});
|
||||
|
||||
// A should stay pinned
|
||||
assert_item_labels(&pane_a, [], cx);
|
||||
assert_item_labels(&pane_b, ["A*!", "B!"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_pinned_tab_into_existing_panes_unpinned_region(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A to pane A and pin
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_a, ["A*!"], cx);
|
||||
|
||||
// Create pane B with pinned item B
|
||||
let pane_b = workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace.split_pane(pane_a.clone(), SplitDirection::Right, window, cx)
|
||||
});
|
||||
let item_b = add_labeled_item(&pane_b, "B", false, cx);
|
||||
assert_item_labels(&pane_b, ["B*"], cx);
|
||||
|
||||
pane_b.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_b.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_b, ["B*!"], cx);
|
||||
|
||||
// Move A from pane A to pane B's unpinned region
|
||||
pane_b.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 1, window, cx);
|
||||
});
|
||||
|
||||
// A should become pinned
|
||||
assert_item_labels(&pane_a, [], cx);
|
||||
assert_item_labels(&pane_b, ["B!", "A*"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_pinned_tab_into_existing_panes_first_position_with_no_pinned_tabs(
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A to pane A and pin
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_a, ["A*!"], cx);
|
||||
|
||||
// Add B to pane B
|
||||
let pane_b = workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace.split_pane(pane_a.clone(), SplitDirection::Right, window, cx)
|
||||
});
|
||||
add_labeled_item(&pane_b, "B", false, cx);
|
||||
assert_item_labels(&pane_b, ["B*"], cx);
|
||||
|
||||
// Move A from pane A to position 0 in pane B, indicating it should stay pinned
|
||||
pane_b.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 0, window, cx);
|
||||
});
|
||||
|
||||
// A should stay pinned
|
||||
assert_item_labels(&pane_a, [], cx);
|
||||
assert_item_labels(&pane_b, ["A*!", "B"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_pinned_tab_into_existing_pane_at_max_capacity_closes_unpinned_tabs(
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
set_max_tabs(cx, Some(2));
|
||||
|
||||
// Add A, B to pane A. Pin both
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
let item_b = add_labeled_item(&pane_a, "B", false, cx);
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
|
||||
let ix = pane.index_for_item_id(item_b.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_a, ["A!", "B*!"], cx);
|
||||
|
||||
// Add C, D to pane B. Pin both
|
||||
let pane_b = workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace.split_pane(pane_a.clone(), SplitDirection::Right, window, cx)
|
||||
});
|
||||
let item_c = add_labeled_item(&pane_b, "C", false, cx);
|
||||
let item_d = add_labeled_item(&pane_b, "D", false, cx);
|
||||
pane_b.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_c.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
|
||||
let ix = pane.index_for_item_id(item_d.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_b, ["C!", "D*!"], cx);
|
||||
|
||||
// Add a third unpinned item to pane B (exceeds max tabs), but is allowed,
|
||||
// as we allow 1 tab over max if the others are pinned or dirty
|
||||
add_labeled_item(&pane_b, "E", false, cx);
|
||||
assert_item_labels(&pane_b, ["C!", "D!", "E*"], cx);
|
||||
|
||||
// Drag pinned A from pane A to position 0 in pane B
|
||||
pane_b.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 0, window, cx);
|
||||
});
|
||||
|
||||
// E (unpinned) should be closed, leaving 3 pinned items
|
||||
assert_item_labels(&pane_a, ["B*!"], cx);
|
||||
assert_item_labels(&pane_b, ["A*!", "C!", "D!"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_last_pinned_tab_to_same_position_stays_pinned(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A to pane A and pin it
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_a, ["A*!"], cx);
|
||||
|
||||
// Drag pinned A to position 1 (directly to the right) in the same pane
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 1, window, cx);
|
||||
});
|
||||
|
||||
// A should still be pinned and active
|
||||
assert_item_labels(&pane_a, ["A*!"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_pinned_tab_beyond_last_pinned_tab_in_same_pane_stays_pinned(
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A, B to pane A and pin both
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
let item_b = add_labeled_item(&pane_a, "B", false, cx);
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
|
||||
let ix = pane.index_for_item_id(item_b.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_a, ["A!", "B*!"], cx);
|
||||
|
||||
// Drag pinned A right of B in the same pane
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 2, window, cx);
|
||||
});
|
||||
|
||||
// A stays pinned
|
||||
assert_item_labels(&pane_a, ["B!", "A*!"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_pinned_tab_beyond_unpinned_tab_in_same_pane_becomes_unpinned(
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A, B to pane A and pin A
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
add_labeled_item(&pane_a, "B", false, cx);
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_a, ["A!", "B*"], cx);
|
||||
|
||||
// Drag pinned A right of B in the same pane
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 2, window, cx);
|
||||
});
|
||||
|
||||
// A becomes unpinned
|
||||
assert_item_labels(&pane_a, ["B", "A*"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_unpinned_tab_in_front_of_pinned_tab_in_same_pane_becomes_pinned(
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A, B to pane A and pin A
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
let item_b = add_labeled_item(&pane_a, "B", false, cx);
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_a, ["A!", "B*"], cx);
|
||||
|
||||
// Drag pinned B left of A in the same pane
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_b.boxed_clone(),
|
||||
ix: 1,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 0, window, cx);
|
||||
});
|
||||
|
||||
// A becomes unpinned
|
||||
assert_item_labels(&pane_a, ["B*!", "A!"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_unpinned_tab_to_the_pinned_region_stays_pinned(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A, B, C to pane A and pin A
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
add_labeled_item(&pane_a, "B", false, cx);
|
||||
let item_c = add_labeled_item(&pane_a, "C", false, cx);
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_a, ["A!", "B", "C*"], cx);
|
||||
|
||||
// Drag pinned C left of B in the same pane
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_c.boxed_clone(),
|
||||
ix: 2,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 1, window, cx);
|
||||
});
|
||||
|
||||
// A stays pinned, B and C remain unpinned
|
||||
assert_item_labels(&pane_a, ["A!", "C*", "B"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_unpinned_tab_into_existing_panes_pinned_region(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add unpinned item A to pane A
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
assert_item_labels(&pane_a, ["A*"], cx);
|
||||
|
||||
// Create pane B with pinned item B
|
||||
let pane_b = workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace.split_pane(pane_a.clone(), SplitDirection::Right, window, cx)
|
||||
});
|
||||
let item_b = add_labeled_item(&pane_b, "B", false, cx);
|
||||
pane_b.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_b.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_b, ["B*!"], cx);
|
||||
|
||||
// Move A from pane A to pane B's pinned region
|
||||
pane_b.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 0, window, cx);
|
||||
});
|
||||
|
||||
// A should become pinned since it was dropped in the pinned region
|
||||
assert_item_labels(&pane_a, [], cx);
|
||||
assert_item_labels(&pane_b, ["A*!", "B!"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_unpinned_tab_into_existing_panes_unpinned_region(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add unpinned item A to pane A
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
assert_item_labels(&pane_a, ["A*"], cx);
|
||||
|
||||
// Create pane B with one pinned item B
|
||||
let pane_b = workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace.split_pane(pane_a.clone(), SplitDirection::Right, window, cx)
|
||||
});
|
||||
let item_b = add_labeled_item(&pane_b, "B", false, cx);
|
||||
pane_b.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_b.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_b, ["B*!"], cx);
|
||||
|
||||
// Move A from pane A to pane B's unpinned region
|
||||
pane_b.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 1, window, cx);
|
||||
});
|
||||
|
||||
// A should remain unpinned since it was dropped outside the pinned region
|
||||
assert_item_labels(&pane_a, [], cx);
|
||||
assert_item_labels(&pane_b, ["B!", "A*"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_pinned_tab_throughout_entire_range_of_pinned_tabs_both_directions(
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A, B, C and pin all
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
let item_b = add_labeled_item(&pane_a, "B", false, cx);
|
||||
let item_c = add_labeled_item(&pane_a, "C", false, cx);
|
||||
assert_item_labels(&pane_a, ["A", "B", "C*"], cx);
|
||||
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
|
||||
let ix = pane.index_for_item_id(item_b.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
|
||||
let ix = pane.index_for_item_id(item_c.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
});
|
||||
assert_item_labels(&pane_a, ["A!", "B!", "C*!"], cx);
|
||||
|
||||
// Move A to right of B
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 1, window, cx);
|
||||
});
|
||||
|
||||
// A should be after B and all are pinned
|
||||
assert_item_labels(&pane_a, ["B!", "A*!", "C!"], cx);
|
||||
|
||||
// Move A to right of C
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 1,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 2, window, cx);
|
||||
});
|
||||
|
||||
// A should be after C and all are pinned
|
||||
assert_item_labels(&pane_a, ["B!", "C!", "A*!"], cx);
|
||||
|
||||
// Move A to left of C
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 2,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 1, window, cx);
|
||||
});
|
||||
|
||||
// A should be before C and all are pinned
|
||||
assert_item_labels(&pane_a, ["B!", "A*!", "C!"], cx);
|
||||
|
||||
// Move A to left of B
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 1,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 0, window, cx);
|
||||
});
|
||||
|
||||
// A should be before B and all are pinned
|
||||
assert_item_labels(&pane_a, ["A*!", "B!", "C!"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_first_tab_to_last_position(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A, B, C
|
||||
let item_a = add_labeled_item(&pane_a, "A", false, cx);
|
||||
add_labeled_item(&pane_a, "B", false, cx);
|
||||
add_labeled_item(&pane_a, "C", false, cx);
|
||||
assert_item_labels(&pane_a, ["A", "B", "C*"], cx);
|
||||
|
||||
// Move A to the end
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_a.boxed_clone(),
|
||||
ix: 0,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 2, window, cx);
|
||||
});
|
||||
|
||||
// A should be at the end
|
||||
assert_item_labels(&pane_a, ["B", "C", "A*"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_drag_last_tab_to_first_position(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane_a = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// Add A, B, C
|
||||
add_labeled_item(&pane_a, "A", false, cx);
|
||||
add_labeled_item(&pane_a, "B", false, cx);
|
||||
let item_c = add_labeled_item(&pane_a, "C", false, cx);
|
||||
assert_item_labels(&pane_a, ["A", "B", "C*"], cx);
|
||||
|
||||
// Move C to the beginning
|
||||
pane_a.update_in(cx, |pane, window, cx| {
|
||||
let dragged_tab = DraggedTab {
|
||||
pane: pane_a.clone(),
|
||||
item: item_c.boxed_clone(),
|
||||
ix: 2,
|
||||
detail: 0,
|
||||
is_active: true,
|
||||
};
|
||||
pane.handle_tab_drop(&dragged_tab, 0, window, cx);
|
||||
});
|
||||
|
||||
// C should be at the beginning
|
||||
assert_item_labels(&pane_a, ["C*", "A", "B"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_add_item_with_new_item(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "The fast, collaborative code editor."
|
||||
edition.workspace = true
|
||||
name = "zed"
|
||||
version = "0.190.0"
|
||||
version = "0.190.3"
|
||||
publish.workspace = true
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Zed Team <hi@zed.dev>"]
|
||||
|
||||
@@ -1 +1 @@
|
||||
dev
|
||||
preview
|
||||
@@ -5,6 +5,7 @@ components = [ "rustfmt", "clippy" ]
|
||||
targets = [
|
||||
"x86_64-apple-darwin",
|
||||
"aarch64-apple-darwin",
|
||||
"x86_64-unknown-freebsd",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-pc-windows-msvc",
|
||||
"wasm32-wasip2", # extensions
|
||||
|
||||
160
script/bundle-freebsd
Executable file
160
script/bundle-freebsd
Executable file
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euxo pipefail
|
||||
source script/lib/blob-store.sh
|
||||
|
||||
# Function for displaying help info
|
||||
help_info() {
|
||||
echo "
|
||||
Usage: ${0##*/} [options]
|
||||
Build a release .tar.gz for FreeBSD.
|
||||
|
||||
Options:
|
||||
-h Display this help and exit.
|
||||
"
|
||||
}
|
||||
|
||||
while getopts 'h' flag; do
|
||||
case "${flag}" in
|
||||
h)
|
||||
help_info
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
export ZED_BUNDLE=true
|
||||
|
||||
channel=$(<crates/zed/RELEASE_CHANNEL)
|
||||
target_dir="${CARGO_TARGET_DIR:-target}"
|
||||
|
||||
version="$(script/get-crate-version zed)"
|
||||
# Set RELEASE_VERSION so it's compiled into GPUI and it knows about the version.
|
||||
export RELEASE_VERSION="${version}"
|
||||
|
||||
commit=$(git rev-parse HEAD | cut -c 1-7)
|
||||
|
||||
version_info=$(rustc --version --verbose)
|
||||
host_line=$(echo "$version_info" | grep host)
|
||||
target_triple=${host_line#*: }
|
||||
remote_server_triple=${REMOTE_SERVER_TARGET:-"${target_triple}"}
|
||||
|
||||
# musl_triple=${target_triple%-gnu}-musl
|
||||
# rustup_installed=false
|
||||
# if command -v rustup >/dev/null 2>&1; then
|
||||
# rustup_installed=true
|
||||
# fi
|
||||
# Generate the licenses first, so they can be baked into the binaries
|
||||
# script/generate-licenses
|
||||
# if "$rustup_installed"; then
|
||||
# rustup target add "$remote_server_triple"
|
||||
# fi
|
||||
|
||||
# export CC=$(which clang)
|
||||
|
||||
# Build binary in release mode
|
||||
export RUSTFLAGS="${RUSTFLAGS:-} -C link-args=-Wl,--disable-new-dtags,-rpath,\$ORIGIN/../lib"
|
||||
# cargo build --release --target "${target_triple}" --package zed --package cli
|
||||
|
||||
# Build remote_server in separate invocation to prevent feature unification from other crates
|
||||
# from influencing dynamic libraries required by it.
|
||||
# if [[ "$remote_server_triple" == "$musl_triple" ]]; then
|
||||
# export RUSTFLAGS="${RUSTFLAGS:-} -C target-feature=+crt-static"
|
||||
# fi
|
||||
cargo build --release --target "${remote_server_triple}" --package remote_server
|
||||
|
||||
# Strip debug symbols and save them for upload to DigitalOcean
|
||||
# objcopy --only-keep-debug "${target_dir}/${target_triple}/release/zed" "${target_dir}/${target_triple}/release/zed.dbg"
|
||||
# objcopy --only-keep-debug "${target_dir}/${remote_server_triple}/release/remote_server" "${target_dir}/${remote_server_triple}/release/remote_server.dbg"
|
||||
# objcopy --strip-debug "${target_dir}/${target_triple}/release/zed"
|
||||
# objcopy --strip-debug "${target_dir}/${target_triple}/release/cli"
|
||||
# objcopy --strip-debug "${target_dir}/${remote_server_triple}/release/remote_server"
|
||||
|
||||
# gzip -f "${target_dir}/${target_triple}/release/zed.dbg"
|
||||
# gzip -f "${target_dir}/${remote_server_triple}/release/remote_server.dbg"
|
||||
|
||||
# if [[ -n "${DIGITALOCEAN_SPACES_SECRET_KEY:-}" && -n "${DIGITALOCEAN_SPACES_ACCESS_KEY:-}" ]]; then
|
||||
# upload_to_blob_store_public \
|
||||
# "zed-debug-symbols" \
|
||||
# "${target_dir}/${target_triple}/release/zed.dbg.gz" \
|
||||
# "$channel/zed-$version-${target_triple}.dbg.gz"
|
||||
# upload_to_blob_store_public \
|
||||
# "zed-debug-symbols" \
|
||||
# "${target_dir}/${remote_server_triple}/release/remote_server.dbg.gz" \
|
||||
# "$channel/remote_server-$version-${remote_server_triple}.dbg.gz"
|
||||
# fi
|
||||
|
||||
# Ensure that remote_server does not depend on libssl nor libcrypto, as we got rid of these deps.
|
||||
if ldd "${target_dir}/${remote_server_triple}/release/remote_server" | grep -q 'libcrypto\|libssl'; then
|
||||
echo "Error: remote_server still depends on libssl or libcrypto" && exit 1
|
||||
fi
|
||||
|
||||
suffix=""
|
||||
if [ "$channel" != "stable" ]; then
|
||||
suffix="-$channel"
|
||||
fi
|
||||
|
||||
# Move everything that should end up in the final package
|
||||
# into a temp directory.
|
||||
# temp_dir=$(mktemp -d)
|
||||
# zed_dir="${temp_dir}/zed$suffix.app"
|
||||
|
||||
# Binary
|
||||
# mkdir -p "${zed_dir}/bin" "${zed_dir}/libexec"
|
||||
# cp "${target_dir}/${target_triple}/release/zed" "${zed_dir}/libexec/zed-editor"
|
||||
# cp "${target_dir}/${target_triple}/release/cli" "${zed_dir}/bin/zed"
|
||||
|
||||
# Libs
|
||||
# find_libs() {
|
||||
# ldd ${target_dir}/${target_triple}/release/zed |
|
||||
# cut -d' ' -f3 |
|
||||
# grep -v '\<\(libstdc++.so\|libc.so\|libgcc_s.so\|libm.so\|libpthread.so\|libdl.so\|libasound.so\)'
|
||||
# }
|
||||
|
||||
# mkdir -p "${zed_dir}/lib"
|
||||
# rm -rf "${zed_dir}/lib/*"
|
||||
# cp $(find_libs) "${zed_dir}/lib"
|
||||
|
||||
# Icons
|
||||
# mkdir -p "${zed_dir}/share/icons/hicolor/512x512/apps"
|
||||
# cp "crates/zed/resources/app-icon$suffix.png" "${zed_dir}/share/icons/hicolor/512x512/apps/zed.png"
|
||||
# mkdir -p "${zed_dir}/share/icons/hicolor/1024x1024/apps"
|
||||
# cp "crates/zed/resources/app-icon$suffix@2x.png" "${zed_dir}/share/icons/hicolor/1024x1024/apps/zed.png"
|
||||
|
||||
# .desktop
|
||||
# export DO_STARTUP_NOTIFY="true"
|
||||
# export APP_CLI="zed"
|
||||
# export APP_ICON="zed"
|
||||
# export APP_ARGS="%U"
|
||||
# if [[ "$channel" == "preview" ]]; then
|
||||
# export APP_NAME="Zed Preview"
|
||||
# elif [[ "$channel" == "nightly" ]]; then
|
||||
# export APP_NAME="Zed Nightly"
|
||||
# elif [[ "$channel" == "dev" ]]; then
|
||||
# export APP_NAME="Zed Devel"
|
||||
# else
|
||||
# export APP_NAME="Zed"
|
||||
# fi
|
||||
|
||||
# mkdir -p "${zed_dir}/share/applications"
|
||||
# envsubst <"crates/zed/resources/zed.desktop.in" >"${zed_dir}/share/applications/zed$suffix.desktop"
|
||||
|
||||
# Copy generated licenses so they'll end up in archive too
|
||||
# cp "assets/licenses.md" "${zed_dir}/licenses.md"
|
||||
|
||||
# Create archive out of everything that's in the temp directory
|
||||
arch=$(uname -m)
|
||||
# target="freebsd-${arch}"
|
||||
# if [[ "$channel" == "dev" ]]; then
|
||||
# archive="zed-${commit}-${target}.tar.gz"
|
||||
# else
|
||||
# archive="zed-${target}.tar.gz"
|
||||
# fi
|
||||
|
||||
# rm -rf "${archive}"
|
||||
# remove_match="zed(-[a-zA-Z0-9]+)?-linux-$(uname -m)\.tar\.gz"
|
||||
# ls "${target_dir}/release" | grep -E ${remove_match} | xargs -d "\n" -I {} rm -f "${target_dir}/release/{}" || true
|
||||
# tar -czvf "${target_dir}/release/$archive" -C ${temp_dir} "zed$suffix.app"
|
||||
|
||||
gzip -f --stdout --best "${target_dir}/${remote_server_triple}/release/remote_server" \
|
||||
> "${target_dir}/zed-remote-server-freebsd-x86_64.gz"
|
||||
Reference in New Issue
Block a user