Compare commits

...

8 Commits

Author SHA1 Message Date
Jakub Konka
e5788b0a29 git_panel: Fix checkbox flickering by removing debounce timeout 2025-11-13 18:58:31 +01:00
zed-zippy[bot]
70d909449b remote: Add missing quotation in extract_server_binary (#42085) (cherry-pick to stable) (#42607)
Cherry-pick of #42085 to stable

----
Also respect the shell env for various commands again
Should close https://github.com/zed-industries/zed/issues/42027

Release Notes:

- Fixed remote server installation failing on some setups

Co-authored-by: Lukas Wirth <lukas@zed.dev>
2025-11-13 09:34:58 +00:00
zed-zippy[bot]
7e7a791360 diagnostics: Workaround weird panic in update_path_excerpts (#42602) (cherry-pick to stable) (#42605)
Cherry-pick of #42602 to stable

----
Fixes ZED-36P

Patching this over for now until I can figure out the cause of this

Release Notes:

- Fixed panic in diagnostics pane

Co-authored-by: Lukas Wirth <lukas@zed.dev>
2025-11-13 09:25:56 +00:00
zed-zippy[bot]
909cc30cc3 gpui: Remove all (unsound) ManuallyDrop usages, panic on device loss (#42114) (cherry-pick to stable) (#42603)
Cherry-pick of #42114 to stable

----
Given that when we lose our devices unrecoverably we will panic anyways,
might as well do so eagerly which makes it clearer.

Additionally this PR replaces all uses of `ManuallyDrop` with `Option`,
as otherwise we need to do manual bookkeeping of what is and isn't
initialized when we try to recover devices as we can bail out halfway
while recovering. In other words, the code prior to this was fairly
unsound due to freely using `ManuallyDrop::drop`.
 
Fixes ZED-1SS
 
Release Notes:

- N/A *or* Added/Fixed/Improved ...

Co-authored-by: Lukas Wirth <lukas@zed.dev>
2025-11-13 09:13:29 +00:00
Lukas Wirth
c547a06425 diagnostics: Close diagnosticsless buffers on refresh (#42503)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-13 09:33:30 +01:00
Lukas Wirth
47debe9943 editor: Remove buffer and display map fields from SelectionsCollection (#42175)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-13 09:33:20 +01:00
zed-zippy[bot]
7e5f706ddc Avoid re-creating releases when re-running workflows (#42573) (cherry-pick to stable) (#42593)
Cherry-pick of #42573 to stable

----
Closes #ISSUE

Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-13 05:02:37 +00:00
Conrad Irwin
89274d82e7 Use powershell for winget job steps (#42565)
Co-Authored-By: Claude

Release Notes:

- N/A
2025-11-12 13:41:35 -07:00
29 changed files with 738 additions and 706 deletions

View File

@@ -42,14 +42,14 @@ jobs:
- id: set-package-name
name: after_release::publish_winget::set_package_name
run: |
if [ "${{ github.event.release.prerelease }}" == "true" ]; then
PACKAGE_NAME=ZedIndustries.Zed.Preview
else
PACKAGE_NAME=ZedIndustries.Zed
fi
if ("${{ github.event.release.prerelease }}" -eq "true") {
$PACKAGE_NAME = "ZedIndustries.Zed.Preview"
} else {
$PACKAGE_NAME = "ZedIndustries.Zed"
}
echo "PACKAGE_NAME=$PACKAGE_NAME" >> "$GITHUB_OUTPUT"
shell: bash -euxo pipefail {0}
echo "PACKAGE_NAME=$PACKAGE_NAME" >> $env:GITHUB_OUTPUT
shell: pwsh
- name: after_release::publish_winget::winget_releaser
uses: vedantmgoyal9/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f
with:

View File

@@ -182,7 +182,6 @@ impl ProjectDiagnosticsEditor {
project::Event::DiskBasedDiagnosticsFinished { language_server_id } => {
log::debug!("disk based diagnostics finished for server {language_server_id}");
this.close_diagnosticless_buffers(
window,
cx,
this.editor.focus_handle(cx).contains_focused(window, cx)
|| this.focus_handle.contains_focused(window, cx),
@@ -247,10 +246,10 @@ impl ProjectDiagnosticsEditor {
window.focus(&this.focus_handle);
}
}
EditorEvent::Blurred => this.close_diagnosticless_buffers(window, cx, false),
EditorEvent::Saved => this.close_diagnosticless_buffers(window, cx, true),
EditorEvent::Blurred => this.close_diagnosticless_buffers(cx, false),
EditorEvent::Saved => this.close_diagnosticless_buffers(cx, true),
EditorEvent::SelectionsChanged { .. } => {
this.close_diagnosticless_buffers(window, cx, true)
this.close_diagnosticless_buffers(cx, true)
}
_ => {}
}
@@ -298,17 +297,16 @@ impl ProjectDiagnosticsEditor {
/// - have no diagnostics anymore
/// - are saved (not dirty)
/// - and, if `retain_selections` is true, do not have selections within them
fn close_diagnosticless_buffers(
&mut self,
_window: &mut Window,
cx: &mut Context<Self>,
retain_selections: bool,
) {
let buffer_ids = self.multibuffer.read(cx).all_buffer_ids();
let selected_buffers = self.editor.update(cx, |editor, cx| {
fn close_diagnosticless_buffers(&mut self, cx: &mut Context<Self>, retain_selections: bool) {
let snapshot = self
.editor
.update(cx, |editor, cx| editor.display_snapshot(cx));
let buffer = self.multibuffer.read(cx);
let buffer_ids = buffer.all_buffer_ids();
let selected_buffers = self.editor.update(cx, |editor, _| {
editor
.selections
.all_anchors(cx)
.all_anchors(&snapshot)
.iter()
.filter_map(|anchor| anchor.start.buffer_id)
.collect::<HashSet<_>>()
@@ -443,7 +441,7 @@ impl ProjectDiagnosticsEditor {
fn focus_out(&mut self, _: FocusOutEvent, window: &mut Window, cx: &mut Context<Self>) {
if !self.focus_handle.is_focused(window) && !self.editor.focus_handle(cx).is_focused(window)
{
self.close_diagnosticless_buffers(window, cx, false);
self.close_diagnosticless_buffers(cx, false);
}
}
@@ -457,8 +455,7 @@ impl ProjectDiagnosticsEditor {
});
}
});
self.multibuffer
.update(cx, |multibuffer, cx| multibuffer.clear(cx));
self.close_diagnosticless_buffers(cx, false);
self.project.update(cx, |project, cx| {
self.paths_to_update = project
.diagnostic_summaries(false, cx)

View File

@@ -156,7 +156,9 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// Cursor is at the first diagnostic
editor.update(cx, |editor, cx| {
assert_eq!(
editor.selections.display_ranges(cx),
editor
.selections
.display_ranges(&editor.display_snapshot(cx)),
[DisplayPoint::new(DisplayRow(3), 8)..DisplayPoint::new(DisplayRow(3), 8)]
);
});
@@ -232,7 +234,9 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// Cursor keeps its position.
editor.update(cx, |editor, cx| {
assert_eq!(
editor.selections.display_ranges(cx),
editor
.selections
.display_ranges(&editor.display_snapshot(cx)),
[DisplayPoint::new(DisplayRow(8), 8)..DisplayPoint::new(DisplayRow(8), 8)]
);
});

View File

@@ -63,6 +63,14 @@ pub struct BlockSnapshot {
pub(super) excerpt_header_height: u32,
}
impl Deref for BlockSnapshot {
type Target = WrapSnapshot;
fn deref(&self) -> &Self::Target {
&self.wrap_snapshot
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CustomBlockId(pub usize);

View File

@@ -630,6 +630,14 @@ pub struct FoldSnapshot {
pub version: usize,
}
impl Deref for FoldSnapshot {
type Target = InlaySnapshot;
fn deref(&self) -> &Self::Target {
&self.inlay_snapshot
}
}
impl FoldSnapshot {
pub fn buffer(&self) -> &MultiBufferSnapshot {
&self.inlay_snapshot.buffer

View File

@@ -32,6 +32,14 @@ pub struct InlaySnapshot {
pub version: usize,
}
impl std::ops::Deref for InlaySnapshot {
type Target = MultiBufferSnapshot;
fn deref(&self) -> &Self::Target {
&self.buffer
}
}
#[derive(Clone, Debug)]
enum Transform {
Isomorphic(TextSummary),

View File

@@ -167,6 +167,14 @@ pub struct TabSnapshot {
pub version: usize,
}
impl std::ops::Deref for TabSnapshot {
type Target = FoldSnapshot;
fn deref(&self) -> &Self::Target {
&self.fold_snapshot
}
}
impl TabSnapshot {
pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
&self.fold_snapshot.inlay_snapshot.buffer

View File

@@ -43,6 +43,14 @@ pub struct WrapSnapshot {
interpolated: bool,
}
impl std::ops::Deref for WrapSnapshot {
type Target = TabSnapshot;
fn deref(&self) -> &Self::Target {
&self.tab_snapshot
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct Transform {
summary: TransformSummary,

View File

@@ -1827,7 +1827,7 @@ impl Editor {
})
});
let selections = SelectionsCollection::new(display_map.clone(), multi_buffer.clone());
let selections = SelectionsCollection::new();
let blink_manager = cx.new(|cx| {
let mut blink_manager = BlinkManager::new(CURSOR_BLINK_INTERVAL, cx);
@@ -2384,7 +2384,7 @@ impl Editor {
}
pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
self.selections.display_map(cx)
self.display_map.update(cx, |map, cx| map.snapshot(cx))
}
pub fn deploy_mouse_context_menu(
@@ -3283,11 +3283,13 @@ impl Editor {
other: Entity<Editor>,
cx: &mut Context<Self>,
) -> gpui::Subscription {
assert_eq!(self.buffer(), other.read(cx).buffer());
let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
if !other_selections.is_empty() {
self.selections.change_with(cx, |selections| {
selections.select_anchors(other_selections);
});
self.selections
.change_with(&self.display_snapshot(cx), |selections| {
selections.select_anchors(other_selections);
});
}
let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
@@ -3296,7 +3298,8 @@ impl Editor {
if other_selections.is_empty() {
return;
}
this.selections.change_with(cx, |selections| {
let snapshot = this.display_snapshot(cx);
this.selections.change_with(&snapshot, |selections| {
selections.select_anchors(other_selections);
});
}
@@ -3309,9 +3312,12 @@ impl Editor {
return;
}
other.update(cx, |other_editor, cx| {
other_editor.selections.change_with(cx, |selections| {
selections.select_anchors(these_selections);
})
let snapshot = other_editor.display_snapshot(cx);
other_editor
.selections
.change_with(&snapshot, |selections| {
selections.select_anchors(these_selections);
})
});
}
});
@@ -3327,13 +3333,14 @@ impl Editor {
effects: SelectionEffects,
window: &mut Window,
cx: &mut Context<Self>,
change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
) -> R {
let snapshot = self.display_snapshot(cx);
if let Some(state) = &mut self.deferred_selection_effects_state {
state.effects.scroll = effects.scroll.or(state.effects.scroll);
state.effects.completions = effects.completions;
state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
let (changed, result) = self.selections.change_with(cx, change);
let (changed, result) = self.selections.change_with(&snapshot, change);
state.changed |= changed;
return result;
}
@@ -3348,7 +3355,7 @@ impl Editor {
add_selections_state: self.add_selections_state.clone(),
},
};
let (changed, result) = self.selections.change_with(cx, change);
let (changed, result) = self.selections.change_with(&snapshot, change);
state.changed = state.changed || changed;
if self.defer_selection_effects {
self.deferred_selection_effects_state = Some(state);
@@ -16157,7 +16164,7 @@ impl Editor {
.map(|s| s.to_vec())
{
self.change_selections(Default::default(), window, cx, |s| {
let map = s.display_map();
let map = s.display_snapshot();
s.select_display_ranges(selections.iter().map(|a| {
let point = a.to_display_point(&map);
point..point
@@ -16178,7 +16185,7 @@ impl Editor {
.map(|s| s.to_vec())
{
self.change_selections(Default::default(), window, cx, |s| {
let map = s.display_map();
let map = s.display_snapshot();
s.select_display_ranges(selections.iter().map(|a| {
let point = a.to_display_point(&map);
point..point
@@ -17973,14 +17980,15 @@ impl Editor {
cx: &mut Context<Self>,
) {
let old_cursor_position = self.selections.newest_anchor().head();
self.selections.change_with(cx, |s| {
s.select_anchors(selections);
if let Some(pending_selection) = pending_selection {
s.set_pending(pending_selection, SelectMode::Character);
} else {
s.clear_pending();
}
});
self.selections
.change_with(&self.display_snapshot(cx), |s| {
s.select_anchors(selections);
if let Some(pending_selection) = pending_selection {
s.set_pending(pending_selection, SelectMode::Character);
} else {
s.clear_pending();
}
});
self.selections_did_change(
false,
&old_cursor_position,
@@ -20188,7 +20196,7 @@ impl Editor {
let locations = self
.selections
.all_anchors(cx)
.all_anchors(&self.display_snapshot(cx))
.iter()
.map(|selection| {
(

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +1,30 @@
use std::{
cell::Ref,
cmp, fmt, iter, mem,
ops::{Deref, DerefMut, Range, Sub},
sync::Arc,
};
use collections::HashMap;
use gpui::{App, Entity, Pixels};
use itertools::Itertools;
use gpui::Pixels;
use itertools::Itertools as _;
use language::{Bias, Point, Selection, SelectionGoal, TextDimension};
use util::post_inc;
use crate::{
Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode,
ToOffset, ToPoint,
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBufferSnapshot, SelectMode, ToOffset,
ToPoint,
display_map::{DisplaySnapshot, ToDisplayPoint},
movement::TextLayoutDetails,
};
#[derive(Debug, Clone)]
pub struct PendingSelection {
pub selection: Selection<Anchor>,
pub mode: SelectMode,
selection: Selection<Anchor>,
mode: SelectMode,
}
#[derive(Debug, Clone)]
pub struct SelectionsCollection {
display_map: Entity<DisplayMap>,
buffer: Entity<MultiBuffer>,
next_selection_id: usize,
line_mode: bool,
/// The non-pending, non-overlapping selections.
@@ -40,10 +37,8 @@ pub struct SelectionsCollection {
}
impl SelectionsCollection {
pub fn new(display_map: Entity<DisplayMap>, buffer: Entity<MultiBuffer>) -> Self {
pub fn new() -> Self {
Self {
display_map,
buffer,
next_selection_id: 1,
line_mode: false,
disjoint: Arc::default(),
@@ -62,14 +57,6 @@ impl SelectionsCollection {
}
}
pub fn display_map(&self, cx: &mut App) -> DisplaySnapshot {
self.display_map.update(cx, |map, cx| map.snapshot(cx))
}
fn buffer<'a>(&self, cx: &'a App) -> Ref<'a, MultiBufferSnapshot> {
self.buffer.read(cx).read(cx)
}
pub fn clone_state(&mut self, other: &SelectionsCollection) {
self.next_selection_id = other.next_selection_id;
self.line_mode = other.line_mode;
@@ -106,15 +93,14 @@ impl SelectionsCollection {
}
/// Non-overlapping selections using anchors, including the pending selection.
pub fn all_anchors(&self, cx: &mut App) -> Arc<[Selection<Anchor>]> {
pub fn all_anchors(&self, snapshot: &DisplaySnapshot) -> Arc<[Selection<Anchor>]> {
if self.pending.is_none() {
self.disjoint_anchors_arc()
} else {
let all_offset_selections = self.all::<usize>(&self.display_map(cx));
let buffer = self.buffer(cx);
let all_offset_selections = self.all::<usize>(snapshot);
all_offset_selections
.into_iter()
.map(|selection| selection_to_anchor_selection(selection, &buffer))
.map(|selection| selection_to_anchor_selection(selection, snapshot))
.collect()
}
}
@@ -354,16 +340,17 @@ impl SelectionsCollection {
}
#[cfg(any(test, feature = "test-support"))]
pub fn display_ranges(&self, cx: &mut App) -> Vec<Range<DisplayPoint>> {
let display_map = self.display_map(cx);
pub fn display_ranges(&self, display_snapshot: &DisplaySnapshot) -> Vec<Range<DisplayPoint>> {
self.disjoint_anchors_arc()
.iter()
.chain(self.pending_anchor())
.map(|s| {
if s.reversed {
s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map)
s.end.to_display_point(display_snapshot)
..s.start.to_display_point(display_snapshot)
} else {
s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map)
s.start.to_display_point(display_snapshot)
..s.end.to_display_point(display_snapshot)
}
})
.collect()
@@ -414,13 +401,13 @@ impl SelectionsCollection {
pub fn change_with<R>(
&mut self,
cx: &mut App,
change: impl FnOnce(&mut MutableSelectionsCollection) -> R,
snapshot: &DisplaySnapshot,
change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
) -> (bool, R) {
let mut mutable_collection = MutableSelectionsCollection {
snapshot,
collection: self,
selections_changed: false,
cx,
};
let result = change(&mut mutable_collection);
@@ -460,13 +447,13 @@ impl SelectionsCollection {
}
}
pub struct MutableSelectionsCollection<'a> {
pub struct MutableSelectionsCollection<'snap, 'a> {
collection: &'a mut SelectionsCollection,
snapshot: &'snap DisplaySnapshot,
selections_changed: bool,
cx: &'a mut App,
}
impl<'a> fmt::Debug for MutableSelectionsCollection<'a> {
impl<'snap, 'a> fmt::Debug for MutableSelectionsCollection<'snap, 'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MutableSelectionsCollection")
.field("collection", &self.collection)
@@ -475,13 +462,9 @@ impl<'a> fmt::Debug for MutableSelectionsCollection<'a> {
}
}
impl<'a> MutableSelectionsCollection<'a> {
pub fn display_map(&mut self) -> DisplaySnapshot {
self.collection.display_map(self.cx)
}
pub fn buffer(&self) -> Ref<'_, MultiBufferSnapshot> {
self.collection.buffer(self.cx)
impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> {
pub fn display_snapshot(&self) -> DisplaySnapshot {
self.snapshot.clone()
}
pub fn clear_disjoint(&mut self) {
@@ -512,12 +495,11 @@ impl<'a> MutableSelectionsCollection<'a> {
}
pub(crate) fn set_pending_anchor_range(&mut self, range: Range<Anchor>, mode: SelectMode) {
let buffer = self.buffer.read(self.cx).snapshot(self.cx);
self.collection.pending = Some(PendingSelection {
selection: {
let mut start = range.start;
let mut end = range.end;
let reversed = if start.cmp(&end, &buffer).is_gt() {
let reversed = if start.cmp(&end, self.snapshot).is_gt() {
mem::swap(&mut start, &mut end);
true
} else {
@@ -557,7 +539,7 @@ impl<'a> MutableSelectionsCollection<'a> {
return true;
}
if !oldest.start.cmp(&oldest.end, &self.buffer()).is_eq() {
if !oldest.start.cmp(&oldest.end, self.snapshot).is_eq() {
let head = oldest.head();
oldest.start = head;
oldest.end = head;
@@ -573,10 +555,10 @@ impl<'a> MutableSelectionsCollection<'a> {
where
T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub<T, Output = T> + std::marker::Copy,
{
let display_map = self.display_map();
let display_map = self.display_snapshot();
let mut selections = self.collection.all(&display_map);
let mut start = range.start.to_offset(&self.buffer());
let mut end = range.end.to_offset(&self.buffer());
let mut start = range.start.to_offset(self.snapshot);
let mut end = range.end.to_offset(self.snapshot);
let reversed = if start > end {
mem::swap(&mut start, &mut end);
true
@@ -597,10 +579,9 @@ impl<'a> MutableSelectionsCollection<'a> {
where
T: ToOffset + std::marker::Copy + std::fmt::Debug,
{
let buffer = self.buffer.read(self.cx).snapshot(self.cx);
let mut selections = selections
.into_iter()
.map(|selection| selection.map(|it| it.to_offset(&buffer)))
.map(|selection| selection.map(|it| it.to_offset(self.snapshot)))
.map(|mut selection| {
if selection.start > selection.end {
mem::swap(&mut selection.start, &mut selection.end);
@@ -629,14 +610,14 @@ impl<'a> MutableSelectionsCollection<'a> {
self.collection.disjoint = Arc::from_iter(
selections
.into_iter()
.map(|selection| selection_to_anchor_selection(selection, &buffer)),
.map(|selection| selection_to_anchor_selection(selection, self.snapshot)),
);
self.collection.pending = None;
self.selections_changed = true;
}
pub fn select_anchors(&mut self, selections: Vec<Selection<Anchor>>) {
let map = self.display_map();
let map = self.display_snapshot();
let resolved_selections =
resolve_selections_wrapping_blocks::<usize, _>(&selections, &map).collect::<Vec<_>>();
self.select(resolved_selections);
@@ -647,10 +628,9 @@ impl<'a> MutableSelectionsCollection<'a> {
I: IntoIterator<Item = Range<T>>,
T: ToOffset,
{
let buffer = self.buffer.read(self.cx).snapshot(self.cx);
let ranges = ranges
.into_iter()
.map(|range| range.start.to_offset(&buffer)..range.end.to_offset(&buffer));
.map(|range| range.start.to_offset(self.snapshot)..range.end.to_offset(self.snapshot));
self.select_offset_ranges(ranges);
}
@@ -686,13 +666,12 @@ impl<'a> MutableSelectionsCollection<'a> {
where
I: IntoIterator<Item = Range<Anchor>>,
{
let buffer = self.buffer.read(self.cx).snapshot(self.cx);
let selections = ranges
.into_iter()
.map(|range| {
let mut start = range.start;
let mut end = range.end;
let reversed = if start.cmp(&end, &buffer).is_gt() {
let reversed = if start.cmp(&end, self.snapshot).is_gt() {
mem::swap(&mut start, &mut end);
true
} else {
@@ -718,7 +697,6 @@ impl<'a> MutableSelectionsCollection<'a> {
where
T: IntoIterator<Item = Range<DisplayPoint>>,
{
let display_map = self.display_map();
let selections = ranges
.into_iter()
.map(|range| {
@@ -732,8 +710,8 @@ impl<'a> MutableSelectionsCollection<'a> {
};
Selection {
id: post_inc(&mut self.collection.next_selection_id),
start: start.to_point(&display_map),
end: end.to_point(&display_map),
start: start.to_point(self.snapshot),
end: end.to_point(self.snapshot),
reversed,
goal: SelectionGoal::None,
}
@@ -743,7 +721,6 @@ impl<'a> MutableSelectionsCollection<'a> {
}
pub fn reverse_selections(&mut self) {
let map = &self.display_map();
let mut new_selections: Vec<Selection<Point>> = Vec::new();
let disjoint = self.disjoint.clone();
for selection in disjoint
@@ -753,8 +730,14 @@ impl<'a> MutableSelectionsCollection<'a> {
{
new_selections.push(Selection {
id: self.new_selection_id(),
start: selection.start.to_display_point(map).to_point(map),
end: selection.end.to_display_point(map).to_point(map),
start: selection
.start
.to_display_point(self.snapshot)
.to_point(self.snapshot),
end: selection
.end
.to_display_point(self.snapshot)
.to_point(self.snapshot),
reversed: selection.reversed,
goal: selection.goal,
});
@@ -767,7 +750,7 @@ impl<'a> MutableSelectionsCollection<'a> {
mut move_selection: impl FnMut(&DisplaySnapshot, &mut Selection<DisplayPoint>),
) {
let mut changed = false;
let display_map = self.display_map();
let display_map = self.display_snapshot();
let selections = self.collection.all_display(&display_map);
let selections = selections
.into_iter()
@@ -791,22 +774,20 @@ impl<'a> MutableSelectionsCollection<'a> {
mut move_selection: impl FnMut(&MultiBufferSnapshot, &mut Selection<usize>),
) {
let mut changed = false;
let snapshot = self.buffer().clone();
let display_map = self.display_map();
let display_map = self.display_snapshot();
let selections = self
.collection
.all::<usize>(&display_map)
.into_iter()
.map(|selection| {
let mut moved_selection = selection.clone();
move_selection(&snapshot, &mut moved_selection);
move_selection(self.snapshot, &mut moved_selection);
if selection != moved_selection {
changed = true;
}
moved_selection
})
.collect();
drop(snapshot);
if changed {
self.select(selections)
@@ -858,11 +839,10 @@ impl<'a> MutableSelectionsCollection<'a> {
&mut self,
find_replacement_cursors: impl FnOnce(&DisplaySnapshot) -> Vec<DisplayPoint>,
) {
let display_map = self.display_map();
let new_selections = find_replacement_cursors(&display_map)
let new_selections = find_replacement_cursors(self.snapshot)
.into_iter()
.map(|cursor| {
let cursor_point = cursor.to_point(&display_map);
let cursor_point = cursor.to_point(self.snapshot);
Selection {
id: post_inc(&mut self.collection.next_selection_id),
start: cursor_point,
@@ -886,12 +866,11 @@ impl<'a> MutableSelectionsCollection<'a> {
let mut selections_with_lost_position = HashMap::default();
let anchors_with_status = {
let buffer = self.buffer();
let disjoint_anchors = self
.disjoint
.iter()
.flat_map(|selection| [&selection.start, &selection.end]);
buffer.refresh_anchors(disjoint_anchors)
self.snapshot.refresh_anchors(disjoint_anchors)
};
let adjusted_disjoint: Vec<_> = anchors_with_status
.chunks(2)
@@ -919,16 +898,16 @@ impl<'a> MutableSelectionsCollection<'a> {
.collect();
if !adjusted_disjoint.is_empty() {
let map = self.display_map();
let map = self.display_snapshot();
let resolved_selections =
resolve_selections_wrapping_blocks(adjusted_disjoint.iter(), &map).collect();
self.select::<usize>(resolved_selections);
}
if let Some(pending) = pending.as_mut() {
let buffer = self.buffer();
let anchors =
buffer.refresh_anchors([&pending.selection.start, &pending.selection.end]);
let anchors = self
.snapshot
.refresh_anchors([&pending.selection.start, &pending.selection.end]);
let (_, start, kept_start) = anchors[0];
let (_, end, kept_end) = anchors[1];
let kept_head = if pending.selection.reversed {
@@ -951,14 +930,14 @@ impl<'a> MutableSelectionsCollection<'a> {
}
}
impl Deref for MutableSelectionsCollection<'_> {
impl Deref for MutableSelectionsCollection<'_, '_> {
type Target = SelectionsCollection;
fn deref(&self) -> &Self::Target {
self.collection
}
}
impl DerefMut for MutableSelectionsCollection<'_> {
impl DerefMut for MutableSelectionsCollection<'_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.collection
}

View File

@@ -54,7 +54,7 @@ use settings::{Settings, SettingsStore, StatusStyle};
use std::future::Future;
use std::ops::Range;
use std::path::Path;
use std::{collections::HashSet, sync::Arc, time::Duration, usize};
use std::{collections::HashSet, sync::Arc, usize};
use strum::{IntoEnumIterator, VariantNames};
use time::OffsetDateTime;
use ui::{
@@ -176,8 +176,6 @@ fn git_panel_context_menu(
const GIT_PANEL_KEY: &str = "GitPanel";
const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
pub fn register(workspace: &mut Workspace) {
workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
workspace.toggle_panel_focus::<GitPanel>(window, cx);
@@ -2582,7 +2580,6 @@ impl GitPanel {
let handle = cx.entity().downgrade();
self.reopen_commit_buffer(window, cx);
self.update_visible_entries_task = cx.spawn_in(window, async move |_, cx| {
cx.background_executor().timer(UPDATE_DEBOUNCE).await;
if let Some(git_panel) = handle.upgrade() {
git_panel
.update_in(cx, |git_panel, window, cx| {
@@ -5074,11 +5071,10 @@ mod tests {
let panel = workspace.update(cx, GitPanel::new).unwrap();
let handle = cx.update_window_entity(&panel, |panel, _, _| {
cx.update_window_entity(&panel, |panel, _, _| {
std::mem::replace(&mut panel.update_visible_entries_task, Task::ready(()))
});
cx.executor().advance_clock(2 * UPDATE_DEBOUNCE);
handle.await;
})
.await;
let entries = panel.read_with(cx, |panel, _| panel.entries.clone());
pretty_assertions::assert_eq!(
@@ -5100,11 +5096,10 @@ mod tests {
],
);
let handle = cx.update_window_entity(&panel, |panel, _, _| {
cx.update_window_entity(&panel, |panel, _, _| {
std::mem::replace(&mut panel.update_visible_entries_task, Task::ready(()))
});
cx.executor().advance_clock(2 * UPDATE_DEBOUNCE);
handle.await;
})
.await;
let entries = panel.read_with(cx, |panel, _| panel.entries.clone());
pretty_assertions::assert_eq!(
entries,
@@ -5195,11 +5190,10 @@ mod tests {
let panel = workspace.update(cx, GitPanel::new).unwrap();
let handle = cx.update_window_entity(&panel, |panel, _, _| {
cx.update_window_entity(&panel, |panel, _, _| {
std::mem::replace(&mut panel.update_visible_entries_task, Task::ready(()))
});
cx.executor().advance_clock(2 * UPDATE_DEBOUNCE);
handle.await;
})
.await;
let entries = panel.read_with(cx, |panel, _| panel.entries.clone());
#[rustfmt::skip]
@@ -5244,11 +5238,10 @@ mod tests {
cx.executor().run_until_parked();
let handle = cx.update_window_entity(&panel, |panel, _, _| {
cx.update_window_entity(&panel, |panel, _, _| {
std::mem::replace(&mut panel.update_visible_entries_task, Task::ready(()))
});
cx.executor().advance_clock(2 * UPDATE_DEBOUNCE);
handle.await;
})
.await;
let entries = panel.read_with(cx, |panel, _| panel.entries.clone());
#[rustfmt::skip]
@@ -5293,11 +5286,10 @@ mod tests {
cx.executor().run_until_parked();
let handle = cx.update_window_entity(&panel, |panel, _, _| {
cx.update_window_entity(&panel, |panel, _, _| {
std::mem::replace(&mut panel.update_visible_entries_task, Task::ready(()))
});
cx.executor().advance_clock(2 * UPDATE_DEBOUNCE);
handle.await;
})
.await;
let entries = panel.read_with(cx, |panel, _| panel.entries.clone());
#[rustfmt::skip]
@@ -5387,11 +5379,10 @@ mod tests {
let panel = workspace.update(cx, GitPanel::new).unwrap();
let handle = cx.update_window_entity(&panel, |panel, _, _| {
cx.update_window_entity(&panel, |panel, _, _| {
std::mem::replace(&mut panel.update_visible_entries_task, Task::ready(()))
});
cx.executor().advance_clock(2 * UPDATE_DEBOUNCE);
handle.await;
})
.await;
let entries = panel.read_with(cx, |panel, _| panel.entries.clone());
#[rustfmt::skip]
@@ -5460,11 +5451,10 @@ mod tests {
cx.executor().run_until_parked();
let handle = cx.update_window_entity(&panel, |panel, _, _| {
cx.update_window_entity(&panel, |panel, _, _| {
std::mem::replace(&mut panel.update_visible_entries_task, Task::ready(()))
});
cx.executor().advance_clock(2 * UPDATE_DEBOUNCE);
handle.await;
})
.await;
let entries = panel.read_with(cx, |panel, _| panel.entries.clone());
#[rustfmt::skip]
@@ -5519,11 +5509,10 @@ mod tests {
cx.executor().run_until_parked();
let handle = cx.update_window_entity(&panel, |panel, _, _| {
cx.update_window_entity(&panel, |panel, _, _| {
std::mem::replace(&mut panel.update_visible_entries_task, Task::ready(()))
});
cx.executor().advance_clock(2 * UPDATE_DEBOUNCE);
handle.await;
})
.await;
let entries = panel.read_with(cx, |panel, _| panel.entries.clone());
#[rustfmt::skip]
@@ -5649,6 +5638,21 @@ mod tests {
let cx = &mut VisualTestContext::from_window(*workspace, cx);
let panel = workspace.update(cx, GitPanel::new).unwrap();
cx.read(|cx| {
project
.read(cx)
.worktrees(cx)
.next()
.unwrap()
.read(cx)
.as_local()
.unwrap()
.scan_complete()
})
.await;
cx.executor().run_until_parked();
// Enable the `sort_by_path` setting and wait for entries to be updated,
// as there should no longer be separators between Tracked and Untracked
// files.

View File

@@ -211,8 +211,8 @@ impl DirectWriteTextSystem {
})))
}
pub(crate) fn handle_gpu_lost(&self, directx_devices: &DirectXDevices) {
self.0.write().handle_gpu_lost(directx_devices);
pub(crate) fn handle_gpu_lost(&self, directx_devices: &DirectXDevices) -> Result<()> {
self.0.write().handle_gpu_lost(directx_devices)
}
}
@@ -1215,18 +1215,11 @@ impl DirectWriteState {
result
}
fn handle_gpu_lost(&mut self, directx_devices: &DirectXDevices) {
try_to_recover_from_device_lost(
|| GPUState::new(directx_devices).context("Recreating GPU state for DirectWrite"),
|gpu_state| self.components.gpu_state = gpu_state,
|| {
log::error!(
"Failed to recreate GPU state for DirectWrite after multiple attempts."
);
// Do something here?
// At this point, the device loss is considered unrecoverable.
},
);
fn handle_gpu_lost(&mut self, directx_devices: &DirectXDevices) -> Result<()> {
try_to_recover_from_device_lost(|| {
GPUState::new(directx_devices).context("Recreating GPU state for DirectWrite")
})
.map(|gpu_state| self.components.gpu_state = gpu_state)
}
}

View File

@@ -1,4 +1,5 @@
use anyhow::{Context, Result};
use itertools::Itertools;
use util::ResultExt;
use windows::Win32::{
Foundation::HMODULE,
@@ -20,24 +21,18 @@ use windows::Win32::{
};
use windows::core::Interface;
pub(crate) fn try_to_recover_from_device_lost<T>(
mut f: impl FnMut() -> Result<T>,
on_success: impl FnOnce(T),
on_error: impl FnOnce(),
) {
let result = (0..5).find_map(|i| {
if i > 0 {
// Add a small delay before retrying
std::thread::sleep(std::time::Duration::from_millis(100));
}
f().log_err()
});
if let Some(result) = result {
on_success(result);
} else {
on_error();
}
pub(crate) fn try_to_recover_from_device_lost<T>(mut f: impl FnMut() -> Result<T>) -> Result<T> {
(0..5)
.map(|i| {
if i > 0 {
// Add a small delay before retrying
std::thread::sleep(std::time::Duration::from_millis(100 + i * 10));
}
f()
})
.find_or_last(Result::is_ok)
.unwrap()
.context("DirectXRenderer failed to recover from lost device after multiple attempts")
}
#[derive(Clone)]

View File

@@ -1,5 +1,5 @@
use std::{
mem::ManuallyDrop,
slice,
sync::{Arc, OnceLock},
};
@@ -39,12 +39,15 @@ pub(crate) struct FontInfo {
pub(crate) struct DirectXRenderer {
hwnd: HWND,
atlas: Arc<DirectXAtlas>,
devices: ManuallyDrop<DirectXRendererDevices>,
resources: ManuallyDrop<DirectXResources>,
devices: Option<DirectXRendererDevices>,
resources: Option<DirectXResources>,
globals: DirectXGlobalElements,
pipelines: DirectXRenderPipelines,
direct_composition: Option<DirectComposition>,
font_info: &'static FontInfo,
width: u32,
height: u32,
}
/// Direct3D objects
@@ -60,19 +63,17 @@ pub(crate) struct DirectXRendererDevices {
struct DirectXResources {
// Direct3D rendering objects
swap_chain: IDXGISwapChain1,
render_target: ManuallyDrop<ID3D11Texture2D>,
render_target_view: [Option<ID3D11RenderTargetView>; 1],
render_target: Option<ID3D11Texture2D>,
render_target_view: Option<ID3D11RenderTargetView>,
// Path intermediate textures (with MSAA)
path_intermediate_texture: ID3D11Texture2D,
path_intermediate_srv: [Option<ID3D11ShaderResourceView>; 1],
path_intermediate_srv: Option<ID3D11ShaderResourceView>,
path_intermediate_msaa_texture: ID3D11Texture2D,
path_intermediate_msaa_view: [Option<ID3D11RenderTargetView>; 1],
path_intermediate_msaa_view: Option<ID3D11RenderTargetView>,
// Cached window size and viewport
width: u32,
height: u32,
viewport: [D3D11_VIEWPORT; 1],
// Cached viewport
viewport: D3D11_VIEWPORT,
}
struct DirectXRenderPipelines {
@@ -86,8 +87,8 @@ struct DirectXRenderPipelines {
}
struct DirectXGlobalElements {
global_params_buffer: [Option<ID3D11Buffer>; 1],
sampler: [Option<ID3D11SamplerState>; 1],
global_params_buffer: Option<ID3D11Buffer>,
sampler: Option<ID3D11SamplerState>,
}
struct DirectComposition {
@@ -100,7 +101,7 @@ impl DirectXRendererDevices {
pub(crate) fn new(
directx_devices: &DirectXDevices,
disable_direct_composition: bool,
) -> Result<ManuallyDrop<Self>> {
) -> Result<Self> {
let DirectXDevices {
adapter,
dxgi_factory,
@@ -113,13 +114,13 @@ impl DirectXRendererDevices {
Some(device.cast().context("Creating DXGI device")?)
};
Ok(ManuallyDrop::new(Self {
Ok(Self {
adapter: adapter.clone(),
dxgi_factory: dxgi_factory.clone(),
device: device.clone(),
device_context: device_context.clone(),
dxgi_device,
}))
})
}
}
@@ -158,12 +159,14 @@ impl DirectXRenderer {
Ok(DirectXRenderer {
hwnd,
atlas,
devices,
resources,
devices: Some(devices),
resources: Some(resources),
globals,
pipelines,
direct_composition,
font_info: Self::get_font_info(),
width: 1,
height: 1,
})
}
@@ -172,55 +175,49 @@ impl DirectXRenderer {
}
fn pre_draw(&self) -> Result<()> {
let resources = self.resources.as_ref().expect("resources missing");
let device_context = &self
.devices
.as_ref()
.expect("devices missing")
.device_context;
update_buffer(
&self.devices.device_context,
self.globals.global_params_buffer[0].as_ref().unwrap(),
device_context,
self.globals.global_params_buffer.as_ref().unwrap(),
&[GlobalParams {
gamma_ratios: self.font_info.gamma_ratios,
viewport_size: [
self.resources.viewport[0].Width,
self.resources.viewport[0].Height,
],
viewport_size: [resources.viewport.Width, resources.viewport.Height],
grayscale_enhanced_contrast: self.font_info.grayscale_enhanced_contrast,
_pad: 0,
}],
)?;
unsafe {
self.devices.device_context.ClearRenderTargetView(
self.resources.render_target_view[0].as_ref().unwrap(),
&[0.0; 4],
);
self.devices
.device_context
.OMSetRenderTargets(Some(&self.resources.render_target_view), None);
self.devices
.device_context
.RSSetViewports(Some(&self.resources.viewport));
device_context
.ClearRenderTargetView(resources.render_target_view.as_ref().unwrap(), &[0.0; 4]);
device_context
.OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None);
device_context.RSSetViewports(Some(slice::from_ref(&resources.viewport)));
}
Ok(())
}
#[inline]
fn present(&mut self) -> Result<()> {
let result = unsafe { self.resources.swap_chain.Present(0, DXGI_PRESENT(0)) };
let result = unsafe {
self.resources
.as_ref()
.expect("resources missing")
.swap_chain
.Present(0, DXGI_PRESENT(0))
};
result.ok().context("Presenting swap chain failed")
}
pub(crate) fn handle_device_lost(&mut self, directx_devices: &DirectXDevices) {
try_to_recover_from_device_lost(
|| {
self.handle_device_lost_impl(directx_devices)
.context("DirectXRenderer handling device lost")
},
|_| {},
|| {
log::error!(
"DirectXRenderer failed to recover from device lost after multiple attempts"
);
// Do something here?
// At this point, the device loss is considered unrecoverable.
},
);
pub(crate) fn handle_device_lost(&mut self, directx_devices: &DirectXDevices) -> Result<()> {
try_to_recover_from_device_lost(|| {
self.handle_device_lost_impl(directx_devices)
.context("DirectXRenderer handling device lost")
})
}
fn handle_device_lost_impl(&mut self, directx_devices: &DirectXDevices) -> Result<()> {
@@ -228,35 +225,41 @@ impl DirectXRenderer {
unsafe {
#[cfg(debug_assertions)]
report_live_objects(&self.devices.device)
.context("Failed to report live objects after device lost")
.log_err();
if let Some(devices) = &self.devices {
report_live_objects(&devices.device)
.context("Failed to report live objects after device lost")
.log_err();
}
ManuallyDrop::drop(&mut self.resources);
self.devices.device_context.OMSetRenderTargets(None, None);
self.devices.device_context.ClearState();
self.devices.device_context.Flush();
self.resources.take();
if let Some(devices) = &self.devices {
devices.device_context.OMSetRenderTargets(None, None);
devices.device_context.ClearState();
devices.device_context.Flush();
#[cfg(debug_assertions)]
report_live_objects(&devices.device)
.context("Failed to report live objects after device lost")
.log_err();
}
#[cfg(debug_assertions)]
report_live_objects(&self.devices.device)
.context("Failed to report live objects after device lost")
.log_err();
drop(self.direct_composition.take());
ManuallyDrop::drop(&mut self.devices);
self.direct_composition.take();
self.devices.take();
}
let devices = DirectXRendererDevices::new(directx_devices, disable_direct_composition)
.context("Recreating DirectX devices")?;
let resources = DirectXResources::new(
&devices,
self.resources.width,
self.resources.height,
self.width,
self.height,
self.hwnd,
disable_direct_composition,
)?;
let globals = DirectXGlobalElements::new(&devices.device)?;
let pipelines = DirectXRenderPipelines::new(&devices.device)?;
)
.context("Creating DirectX resources")?;
let globals = DirectXGlobalElements::new(&devices.device)
.context("Creating DirectXGlobalElements")?;
let pipelines = DirectXRenderPipelines::new(&devices.device)
.context("Creating DirectXRenderPipelines")?;
let direct_composition = if disable_direct_composition {
None
@@ -269,17 +272,17 @@ impl DirectXRenderer {
self.atlas
.handle_device_lost(&devices.device, &devices.device_context);
self.devices = devices;
self.resources = resources;
unsafe {
devices
.device_context
.OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None);
}
self.devices = Some(devices);
self.resources = Some(resources);
self.globals = globals;
self.pipelines = pipelines;
self.direct_composition = direct_composition;
unsafe {
self.devices
.device_context
.OMSetRenderTargets(Some(&self.resources.render_target_view), None);
}
Ok(())
}
@@ -318,23 +321,25 @@ impl DirectXRenderer {
pub(crate) fn resize(&mut self, new_size: Size<DevicePixels>) -> Result<()> {
let width = new_size.width.0.max(1) as u32;
let height = new_size.height.0.max(1) as u32;
if self.resources.width == width && self.resources.height == height {
if self.width == width && self.height == height {
return Ok(());
}
self.resources.width = width;
self.resources.height = height;
self.width = width;
self.height = height;
// Clear the render target before resizing
unsafe { self.devices.device_context.OMSetRenderTargets(None, None) };
unsafe { ManuallyDrop::drop(&mut self.resources.render_target) };
drop(self.resources.render_target_view[0].take().unwrap());
let devices = self.devices.as_ref().context("devices missing")?;
unsafe { devices.device_context.OMSetRenderTargets(None, None) };
let resources = self.resources.as_mut().context("resources missing")?;
resources.render_target.take();
resources.render_target_view.take();
// Resizing the swap chain requires a call to the underlying DXGI adapter, which can return the device removed error.
// The app might have moved to a monitor that's attached to a different graphics device.
// When a graphics device is removed or reset, the desktop resolution often changes, resulting in a window size change.
// But here we just return the error, because we are handling device lost scenarios elsewhere.
unsafe {
self.resources
resources
.swap_chain
.ResizeBuffers(
BUFFER_COUNT as u32,
@@ -346,12 +351,11 @@ impl DirectXRenderer {
.context("Failed to resize swap chain")?;
}
self.resources
.recreate_resources(&self.devices, width, height)?;
resources.recreate_resources(devices, width, height)?;
unsafe {
self.devices
devices
.device_context
.OMSetRenderTargets(Some(&self.resources.render_target_view), None);
.OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None);
}
Ok(())
@@ -361,15 +365,22 @@ impl DirectXRenderer {
if shadows.is_empty() {
return Ok(());
}
let devices = self.devices.as_ref().context("devices missing")?;
self.pipelines.shadow_pipeline.update_buffer(
&self.devices.device,
&self.devices.device_context,
&devices.device,
&devices.device_context,
shadows,
)?;
self.pipelines.shadow_pipeline.draw(
&self.devices.device_context,
&self.resources.viewport,
&self.globals.global_params_buffer,
&devices.device_context,
slice::from_ref(
&self
.resources
.as_ref()
.context("resources missing")?
.viewport,
),
slice::from_ref(&self.globals.global_params_buffer),
D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
4,
shadows.len() as u32,
@@ -380,15 +391,22 @@ impl DirectXRenderer {
if quads.is_empty() {
return Ok(());
}
let devices = self.devices.as_ref().context("devices missing")?;
self.pipelines.quad_pipeline.update_buffer(
&self.devices.device,
&self.devices.device_context,
&devices.device,
&devices.device_context,
quads,
)?;
self.pipelines.quad_pipeline.draw(
&self.devices.device_context,
&self.resources.viewport,
&self.globals.global_params_buffer,
&devices.device_context,
slice::from_ref(
&self
.resources
.as_ref()
.context("resources missing")?
.viewport,
),
slice::from_ref(&self.globals.global_params_buffer),
D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
4,
quads.len() as u32,
@@ -400,18 +418,19 @@ impl DirectXRenderer {
return Ok(());
}
let devices = self.devices.as_ref().context("devices missing")?;
let resources = self.resources.as_ref().context("resources missing")?;
// Clear intermediate MSAA texture
unsafe {
self.devices.device_context.ClearRenderTargetView(
self.resources.path_intermediate_msaa_view[0]
.as_ref()
.unwrap(),
devices.device_context.ClearRenderTargetView(
resources.path_intermediate_msaa_view.as_ref().unwrap(),
&[0.0; 4],
);
// Set intermediate MSAA texture as render target
self.devices
.device_context
.OMSetRenderTargets(Some(&self.resources.path_intermediate_msaa_view), None);
devices.device_context.OMSetRenderTargets(
Some(slice::from_ref(&resources.path_intermediate_msaa_view)),
None,
);
}
// Collect all vertices and sprites for a single draw call
@@ -427,14 +446,15 @@ impl DirectXRenderer {
}
self.pipelines.path_rasterization_pipeline.update_buffer(
&self.devices.device,
&self.devices.device_context,
&devices.device,
&devices.device_context,
&vertices,
)?;
self.pipelines.path_rasterization_pipeline.draw(
&self.devices.device_context,
&self.resources.viewport,
&self.globals.global_params_buffer,
&devices.device_context,
slice::from_ref(&resources.viewport),
slice::from_ref(&self.globals.global_params_buffer),
D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
vertices.len() as u32,
1,
@@ -442,17 +462,17 @@ impl DirectXRenderer {
// Resolve MSAA to non-MSAA intermediate texture
unsafe {
self.devices.device_context.ResolveSubresource(
&self.resources.path_intermediate_texture,
devices.device_context.ResolveSubresource(
&resources.path_intermediate_texture,
0,
&self.resources.path_intermediate_msaa_texture,
&resources.path_intermediate_msaa_texture,
0,
RENDER_TARGET_FORMAT,
);
// Restore main render target
self.devices
devices
.device_context
.OMSetRenderTargets(Some(&self.resources.render_target_view), None);
.OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None);
}
Ok(())
@@ -485,19 +505,21 @@ impl DirectXRenderer {
vec![PathSprite { bounds }]
};
let devices = self.devices.as_ref().context("devices missing")?;
let resources = self.resources.as_ref().context("resources missing")?;
self.pipelines.path_sprite_pipeline.update_buffer(
&self.devices.device,
&self.devices.device_context,
&devices.device,
&devices.device_context,
&sprites,
)?;
// Draw the sprites with the path texture
self.pipelines.path_sprite_pipeline.draw_with_texture(
&self.devices.device_context,
&self.resources.path_intermediate_srv,
&self.resources.viewport,
&self.globals.global_params_buffer,
&self.globals.sampler,
&devices.device_context,
slice::from_ref(&resources.path_intermediate_srv),
slice::from_ref(&resources.viewport),
slice::from_ref(&self.globals.global_params_buffer),
slice::from_ref(&self.globals.sampler),
sprites.len() as u32,
)
}
@@ -506,15 +528,17 @@ impl DirectXRenderer {
if underlines.is_empty() {
return Ok(());
}
let devices = self.devices.as_ref().context("devices missing")?;
let resources = self.resources.as_ref().context("resources missing")?;
self.pipelines.underline_pipeline.update_buffer(
&self.devices.device,
&self.devices.device_context,
&devices.device,
&devices.device_context,
underlines,
)?;
self.pipelines.underline_pipeline.draw(
&self.devices.device_context,
&self.resources.viewport,
&self.globals.global_params_buffer,
&devices.device_context,
slice::from_ref(&resources.viewport),
slice::from_ref(&self.globals.global_params_buffer),
D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
4,
underlines.len() as u32,
@@ -529,18 +553,20 @@ impl DirectXRenderer {
if sprites.is_empty() {
return Ok(());
}
let devices = self.devices.as_ref().context("devices missing")?;
let resources = self.resources.as_ref().context("resources missing")?;
self.pipelines.mono_sprites.update_buffer(
&self.devices.device,
&self.devices.device_context,
&devices.device,
&devices.device_context,
sprites,
)?;
let texture_view = self.atlas.get_texture_view(texture_id);
self.pipelines.mono_sprites.draw_with_texture(
&self.devices.device_context,
&devices.device_context,
&texture_view,
&self.resources.viewport,
&self.globals.global_params_buffer,
&self.globals.sampler,
slice::from_ref(&resources.viewport),
slice::from_ref(&self.globals.global_params_buffer),
slice::from_ref(&self.globals.sampler),
sprites.len() as u32,
)
}
@@ -553,18 +579,21 @@ impl DirectXRenderer {
if sprites.is_empty() {
return Ok(());
}
let devices = self.devices.as_ref().context("devices missing")?;
let resources = self.resources.as_ref().context("resources missing")?;
self.pipelines.poly_sprites.update_buffer(
&self.devices.device,
&self.devices.device_context,
&devices.device,
&devices.device_context,
sprites,
)?;
let texture_view = self.atlas.get_texture_view(texture_id);
self.pipelines.poly_sprites.draw_with_texture(
&self.devices.device_context,
&devices.device_context,
&texture_view,
&self.resources.viewport,
&self.globals.global_params_buffer,
&self.globals.sampler,
slice::from_ref(&resources.viewport),
slice::from_ref(&self.globals.global_params_buffer),
slice::from_ref(&self.globals.sampler),
sprites.len() as u32,
)
}
@@ -577,7 +606,8 @@ impl DirectXRenderer {
}
pub(crate) fn gpu_specs(&self) -> Result<GpuSpecs> {
let desc = unsafe { self.devices.adapter.GetDesc1() }?;
let devices = self.devices.as_ref().context("devices missing")?;
let desc = unsafe { devices.adapter.GetDesc1() }?;
let is_software_emulated = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != 0;
let device_name = String::from_utf16_lossy(&desc.Description)
.trim_matches(char::from(0))
@@ -592,7 +622,7 @@ impl DirectXRenderer {
0x10DE => nvidia::get_driver_version(),
0x1002 => amd::get_driver_version(),
// For Intel and other vendors, we use the DXGI API to get the driver version.
_ => dxgi::get_driver_version(&self.devices.adapter),
_ => dxgi::get_driver_version(&devices.adapter),
}
.context("Failed to get gpu driver info")
.log_err()
@@ -626,7 +656,7 @@ impl DirectXResources {
height: u32,
hwnd: HWND,
disable_direct_composition: bool,
) -> Result<ManuallyDrop<Self>> {
) -> Result<Self> {
let swap_chain = if disable_direct_composition {
create_swap_chain(&devices.dxgi_factory, &devices.device, hwnd, width, height)?
} else {
@@ -649,18 +679,16 @@ impl DirectXResources {
) = create_resources(devices, &swap_chain, width, height)?;
set_rasterizer_state(&devices.device, &devices.device_context)?;
Ok(ManuallyDrop::new(Self {
Ok(Self {
swap_chain,
render_target,
render_target: Some(render_target),
render_target_view,
path_intermediate_texture,
path_intermediate_msaa_texture,
path_intermediate_msaa_view,
path_intermediate_srv,
viewport,
width,
height,
}))
})
}
#[inline]
@@ -679,7 +707,7 @@ impl DirectXResources {
path_intermediate_msaa_view,
viewport,
) = create_resources(devices, &self.swap_chain, width, height)?;
self.render_target = render_target;
self.render_target = Some(render_target);
self.render_target_view = render_target_view;
self.path_intermediate_texture = path_intermediate_texture;
self.path_intermediate_msaa_texture = path_intermediate_msaa_texture;
@@ -789,7 +817,7 @@ impl DirectXGlobalElements {
};
let mut buffer = None;
device.CreateBuffer(&desc, None, Some(&mut buffer))?;
[buffer]
buffer
};
let sampler = unsafe {
@@ -807,7 +835,7 @@ impl DirectXGlobalElements {
};
let mut output = None;
device.CreateSamplerState(&desc, Some(&mut output))?;
[output]
output
};
Ok(Self {
@@ -832,7 +860,7 @@ struct PipelineState<T> {
fragment: ID3D11PixelShader,
buffer: ID3D11Buffer,
buffer_size: usize,
view: [Option<ID3D11ShaderResourceView>; 1],
view: Option<ID3D11ShaderResourceView>,
blend_state: ID3D11BlendState,
_marker: std::marker::PhantomData<T>,
}
@@ -902,7 +930,7 @@ impl<T> PipelineState<T> {
) -> Result<()> {
set_pipeline_state(
device_context,
&self.view,
slice::from_ref(&self.view),
topology,
viewport,
&self.vertex,
@@ -927,7 +955,7 @@ impl<T> PipelineState<T> {
) -> Result<()> {
set_pipeline_state(
device_context,
&self.view,
slice::from_ref(&self.view),
D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
viewport,
&self.vertex,
@@ -964,18 +992,8 @@ struct PathSprite {
impl Drop for DirectXRenderer {
fn drop(&mut self) {
#[cfg(debug_assertions)]
report_live_objects(&self.devices.device).ok();
unsafe {
ManuallyDrop::drop(&mut self.devices);
ManuallyDrop::drop(&mut self.resources);
}
}
}
impl Drop for DirectXResources {
fn drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.render_target);
if let Some(devices) = &self.devices {
report_live_objects(&devices.device).ok();
}
}
}
@@ -1049,13 +1067,13 @@ fn create_resources(
width: u32,
height: u32,
) -> Result<(
ManuallyDrop<ID3D11Texture2D>,
[Option<ID3D11RenderTargetView>; 1],
ID3D11Texture2D,
[Option<ID3D11ShaderResourceView>; 1],
Option<ID3D11RenderTargetView>,
ID3D11Texture2D,
[Option<ID3D11RenderTargetView>; 1],
[D3D11_VIEWPORT; 1],
Option<ID3D11ShaderResourceView>,
ID3D11Texture2D,
Option<ID3D11RenderTargetView>,
D3D11_VIEWPORT,
)> {
let (render_target, render_target_view) =
create_render_target_and_its_view(swap_chain, &devices.device)?;
@@ -1079,17 +1097,11 @@ fn create_resources(
fn create_render_target_and_its_view(
swap_chain: &IDXGISwapChain1,
device: &ID3D11Device,
) -> Result<(
ManuallyDrop<ID3D11Texture2D>,
[Option<ID3D11RenderTargetView>; 1],
)> {
) -> Result<(ID3D11Texture2D, Option<ID3D11RenderTargetView>)> {
let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
let mut render_target_view = None;
unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? };
Ok((
ManuallyDrop::new(render_target),
[Some(render_target_view.unwrap())],
))
Ok((render_target, render_target_view))
}
#[inline]
@@ -1097,7 +1109,7 @@ fn create_path_intermediate_texture(
device: &ID3D11Device,
width: u32,
height: u32,
) -> Result<(ID3D11Texture2D, [Option<ID3D11ShaderResourceView>; 1])> {
) -> Result<(ID3D11Texture2D, Option<ID3D11ShaderResourceView>)> {
let texture = unsafe {
let mut output = None;
let desc = D3D11_TEXTURE2D_DESC {
@@ -1122,7 +1134,7 @@ fn create_path_intermediate_texture(
let mut shader_resource_view = None;
unsafe { device.CreateShaderResourceView(&texture, None, Some(&mut shader_resource_view))? };
Ok((texture, [Some(shader_resource_view.unwrap())]))
Ok((texture, Some(shader_resource_view.unwrap())))
}
#[inline]
@@ -1130,7 +1142,7 @@ fn create_path_intermediate_msaa_texture_and_view(
device: &ID3D11Device,
width: u32,
height: u32,
) -> Result<(ID3D11Texture2D, [Option<ID3D11RenderTargetView>; 1])> {
) -> Result<(ID3D11Texture2D, Option<ID3D11RenderTargetView>)> {
let msaa_texture = unsafe {
let mut output = None;
let desc = D3D11_TEXTURE2D_DESC {
@@ -1153,15 +1165,11 @@ fn create_path_intermediate_msaa_texture_and_view(
};
let mut msaa_view = None;
unsafe { device.CreateRenderTargetView(&msaa_texture, None, Some(&mut msaa_view))? };
Ok((msaa_texture, [Some(msaa_view.unwrap())]))
Ok((msaa_texture, Some(msaa_view.unwrap())))
}
#[inline]
fn set_viewport(
device_context: &ID3D11DeviceContext,
width: f32,
height: f32,
) -> [D3D11_VIEWPORT; 1] {
fn set_viewport(device_context: &ID3D11DeviceContext, width: f32, height: f32) -> D3D11_VIEWPORT {
let viewport = [D3D11_VIEWPORT {
TopLeftX: 0.0,
TopLeftY: 0.0,
@@ -1171,7 +1179,7 @@ fn set_viewport(
MaxDepth: 1.0,
}];
unsafe { device_context.RSSetViewports(Some(&viewport)) };
viewport
viewport[0]
}
#[inline]
@@ -1299,10 +1307,10 @@ fn create_buffer(
fn create_buffer_view(
device: &ID3D11Device,
buffer: &ID3D11Buffer,
) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
) -> Result<Option<ID3D11ShaderResourceView>> {
let mut view = None;
unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
Ok([view])
Ok(view)
}
#[inline]

View File

@@ -1133,7 +1133,9 @@ impl WindowsWindowInner {
let mut lock = self.state.borrow_mut();
let devices = lparam.0 as *const DirectXDevices;
let devices = unsafe { &*devices };
lock.renderer.handle_device_lost(&devices);
if let Err(err) = lock.renderer.handle_device_lost(&devices) {
panic!("Device lost: {err}");
}
Some(0)
}

View File

@@ -1,7 +1,6 @@
use std::{
cell::RefCell,
ffi::OsStr,
mem::ManuallyDrop,
path::{Path, PathBuf},
rc::{Rc, Weak},
sync::{Arc, atomic::Ordering},
@@ -57,7 +56,7 @@ pub(crate) struct WindowsPlatformState {
jump_list: JumpList,
// NOTE: standard cursor handles don't need to close.
pub(crate) current_cursor: Option<HCURSOR>,
directx_devices: ManuallyDrop<DirectXDevices>,
directx_devices: Option<DirectXDevices>,
}
#[derive(Default)]
@@ -76,7 +75,7 @@ impl WindowsPlatformState {
let callbacks = PlatformCallbacks::default();
let jump_list = JumpList::new();
let current_cursor = load_cursor(CursorStyle::Arrow);
let directx_devices = ManuallyDrop::new(directx_devices);
let directx_devices = Some(directx_devices);
Self {
callbacks,
@@ -190,7 +189,7 @@ impl WindowsPlatform {
main_receiver: self.inner.main_receiver.clone(),
platform_window_handle: self.handle,
disable_direct_composition: self.disable_direct_composition,
directx_devices: (*self.inner.state.borrow().directx_devices).clone(),
directx_devices: self.inner.state.borrow().directx_devices.clone().unwrap(),
}
}
@@ -238,7 +237,7 @@ impl WindowsPlatform {
}
fn begin_vsync_thread(&self) {
let mut directx_device = (*self.inner.state.borrow().directx_devices).clone();
let mut directx_device = self.inner.state.borrow().directx_devices.clone().unwrap();
let platform_window: SafeHwnd = self.handle.into();
let validation_number = self.inner.validation_number;
let all_windows = Arc::downgrade(&self.raw_window_handles);
@@ -250,13 +249,15 @@ impl WindowsPlatform {
loop {
vsync_provider.wait_for_vsync();
if check_device_lost(&directx_device.device) {
handle_gpu_device_lost(
if let Err(err) = handle_gpu_device_lost(
&mut directx_device,
platform_window.as_raw(),
validation_number,
&all_windows,
&text_system,
);
) {
panic!("Device lost: {err}");
}
}
let Some(all_windows) = all_windows.upgrade() else {
break;
@@ -826,10 +827,8 @@ impl WindowsPlatformInner {
let mut lock = self.state.borrow_mut();
let directx_devices = lparam.0 as *const DirectXDevices;
let directx_devices = unsafe { &*directx_devices };
unsafe {
ManuallyDrop::drop(&mut lock.directx_devices);
}
lock.directx_devices = ManuallyDrop::new(directx_devices.clone());
lock.directx_devices.take();
lock.directx_devices = Some(directx_devices.clone());
Some(0)
}
@@ -846,14 +845,6 @@ impl Drop for WindowsPlatform {
}
}
impl Drop for WindowsPlatformState {
fn drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.directx_devices);
}
}
}
pub(crate) struct WindowCreationInfo {
pub(crate) icon: HICON,
pub(crate) executor: ForegroundExecutor,
@@ -1077,37 +1068,28 @@ fn handle_gpu_device_lost(
validation_number: usize,
all_windows: &std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
text_system: &std::sync::Weak<DirectWriteTextSystem>,
) {
) -> Result<()> {
// Here we wait a bit to ensure the system has time to recover from the device lost state.
// If we don't wait, the final drawing result will be blank.
std::thread::sleep(std::time::Duration::from_millis(350));
try_to_recover_from_device_lost(
|| {
DirectXDevices::new()
.context("Failed to recreate new DirectX devices after device lost")
},
|new_devices| *directx_devices = new_devices,
|| {
log::error!("Failed to recover DirectX devices after multiple attempts.");
// Do something here?
// At this point, the device loss is considered unrecoverable.
// std::process::exit(1);
},
);
*directx_devices = try_to_recover_from_device_lost(|| {
DirectXDevices::new().context("Failed to recreate new DirectX devices after device lost")
})?;
log::info!("DirectX devices successfully recreated.");
let lparam = LPARAM(directx_devices as *const _ as _);
unsafe {
SendMessageW(
platform_window,
WM_GPUI_GPU_DEVICE_LOST,
Some(WPARAM(validation_number)),
Some(LPARAM(directx_devices as *const _ as _)),
Some(lparam),
);
}
if let Some(text_system) = text_system.upgrade() {
text_system.handle_gpu_lost(&directx_devices);
text_system.handle_gpu_lost(&directx_devices)?;
}
if let Some(all_windows) = all_windows.upgrade() {
for window in all_windows.read().iter() {
@@ -1116,7 +1098,7 @@ fn handle_gpu_device_lost(
window.as_raw(),
WM_GPUI_GPU_DEVICE_LOST,
Some(WPARAM(validation_number)),
Some(LPARAM(directx_devices as *const _ as _)),
Some(lparam),
);
}
}
@@ -1132,6 +1114,7 @@ fn handle_gpu_device_lost(
}
}
}
Ok(())
}
const PLATFORM_WINDOW_CLASS_NAME: PCWSTR = w!("Zed::PlatformWindow");

View File

@@ -410,6 +410,8 @@ impl MultiBuffer {
}
self.insert_excerpts_with_ids_after(insert_after, buffer, to_insert, cx);
// todo(lw): There is a logic bug somewhere that causes the to_remove vector to be not ordered correctly
to_remove.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
self.remove_excerpts(to_remove, cx);
if excerpt_ids.is_empty() {
self.excerpts_by_path.remove(&path);

View File

@@ -370,7 +370,7 @@ impl RemoteConnection for SshRemoteConnection {
let ssh_proxy_process = match self
.socket
.ssh_command(self.ssh_shell_kind, "env", &proxy_args)
.ssh_command(self.ssh_shell_kind, "env", &proxy_args, false)
// IMPORTANT: we kill this process when we drop the task that uses it.
.kill_on_drop(true)
.spawn()
@@ -578,6 +578,7 @@ impl SshRemoteConnection {
self.ssh_shell_kind,
&dst_path.display(self.path_style()),
&["version"],
true,
)
.await
.is_ok()
@@ -615,13 +616,13 @@ impl SshRemoteConnection {
{
Ok(_) => {
self.extract_server_binary(&dst_path, &tmp_path_gz, delegate, cx)
.await?;
.await
.context("extracting server binary")?;
return Ok(dst_path);
}
Err(e) => {
log::error!(
"Failed to download binary on server, attempting to upload server: {}",
e
"Failed to download binary on server, attempting to upload server: {e:#}",
)
}
}
@@ -629,11 +630,14 @@ impl SshRemoteConnection {
let src_path = delegate
.download_server_binary_locally(self.ssh_platform, release_channel, wanted_version, cx)
.await?;
.await
.context("downloading server binary locally")?;
self.upload_local_server_binary(&src_path, &tmp_path_gz, delegate, cx)
.await?;
.await
.context("uploading server binary")?;
self.extract_server_binary(&dst_path, &tmp_path_gz, delegate, cx)
.await?;
.await
.context("extracting server binary")?;
Ok(dst_path)
}
@@ -651,6 +655,7 @@ impl SshRemoteConnection {
self.ssh_shell_kind,
"mkdir",
&["-p", parent.display(self.path_style()).as_ref()],
true,
)
.await?;
}
@@ -675,6 +680,7 @@ impl SshRemoteConnection {
"-o",
&tmp_path_gz.display(self.path_style()),
],
true,
)
.await
{
@@ -682,7 +688,7 @@ impl SshRemoteConnection {
Err(e) => {
if self
.socket
.run_command(self.ssh_shell_kind, "which", &["curl"])
.run_command(self.ssh_shell_kind, "which", &["curl"], true)
.await
.is_ok()
{
@@ -702,6 +708,7 @@ impl SshRemoteConnection {
"-O",
&tmp_path_gz.display(self.path_style()),
],
true,
)
.await
{
@@ -709,7 +716,7 @@ impl SshRemoteConnection {
Err(e) => {
if self
.socket
.run_command(self.ssh_shell_kind, "which", &["wget"])
.run_command(self.ssh_shell_kind, "which", &["wget"], true)
.await
.is_ok()
{
@@ -738,6 +745,7 @@ impl SshRemoteConnection {
self.ssh_shell_kind,
"mkdir",
&["-p", parent.display(self.path_style()).as_ref()],
true,
)
.await?;
}
@@ -778,14 +786,23 @@ impl SshRemoteConnection {
let dst_path = dst_path.display(self.path_style());
let dst_path = shell_kind.try_quote(&dst_path).context("shell quoting")?;
let script = if let Some(tmp_path) = orig_tmp_path.strip_suffix(".gz") {
let orig_tmp_path = shell_kind
.try_quote(&orig_tmp_path)
.context("shell quoting")?;
let tmp_path = shell_kind.try_quote(&tmp_path).context("shell quoting")?;
format!(
"gunzip -f {orig_tmp_path} && chmod {server_mode} {tmp_path} && mv {tmp_path} {dst_path}",
)
} else {
let orig_tmp_path = shell_kind
.try_quote(&orig_tmp_path)
.context("shell quoting")?;
format!("chmod {server_mode} {orig_tmp_path} && mv {orig_tmp_path} {dst_path}",)
};
let args = shell_kind.args_for_shell(false, script.to_string());
self.socket.run_command(shell_kind, "sh", &args).await?;
self.socket
.run_command(shell_kind, "sh", &args, true)
.await?;
Ok(())
}
@@ -934,6 +951,7 @@ impl SshSocket {
shell_kind: ShellKind,
program: &str,
args: &[impl AsRef<str>],
allow_pseudo_tty: bool,
) -> process::Command {
let mut command = util::command::new_smol_command("ssh");
let program = shell_kind.prepend_command_prefix(program);
@@ -953,9 +971,11 @@ impl SshSocket {
let separator = shell_kind.sequential_commands_separator();
let to_run = format!("cd{separator} {to_run}");
self.ssh_options(&mut command, true)
.arg(self.connection_options.ssh_url())
.arg("-T")
.arg(to_run);
.arg(self.connection_options.ssh_url());
if !allow_pseudo_tty {
command.arg("-T");
}
command.arg(to_run);
log::debug!("ssh {:?}", command);
command
}
@@ -965,8 +985,12 @@ impl SshSocket {
shell_kind: ShellKind,
program: &str,
args: &[impl AsRef<str>],
allow_pseudo_tty: bool,
) -> Result<String> {
let output = self.ssh_command(shell_kind, program, args).output().await?;
let output = self
.ssh_command(shell_kind, program, args, allow_pseudo_tty)
.output()
.await?;
anyhow::ensure!(
output.status.success(),
"failed to run command: {}",
@@ -1039,7 +1063,7 @@ impl SshSocket {
}
async fn platform(&self, shell: ShellKind) -> Result<RemotePlatform> {
let uname = self.run_command(shell, "uname", &["-sm"]).await?;
let uname = self.run_command(shell, "uname", &["-sm"], false).await?;
let Some((os, arch)) = uname.split_once(" ") else {
anyhow::bail!("unknown uname: {uname:?}")
};
@@ -1072,7 +1096,7 @@ impl SshSocket {
async fn shell(&self) -> String {
let default_shell = "sh";
match self
.run_command(ShellKind::Posix, "sh", &["-c", "echo $SHELL"])
.run_command(ShellKind::Posix, "sh", &["-c", "echo $SHELL"], false)
.await
{
Ok(shell) => match shell.trim() {

View File

@@ -1685,7 +1685,9 @@ mod tests {
assert_eq!(search_bar.active_match_index, Some(0));
search_bar.select_next_match(&SelectNextMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43)]
);
});
@@ -1696,7 +1698,9 @@ mod tests {
search_bar.update_in(cx, |search_bar, window, cx| {
search_bar.select_next_match(&SelectNextMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(3), 11)..DisplayPoint::new(DisplayRow(3), 13)]
);
});
@@ -1707,7 +1711,9 @@ mod tests {
search_bar.update_in(cx, |search_bar, window, cx| {
search_bar.select_next_match(&SelectNextMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(3), 56)..DisplayPoint::new(DisplayRow(3), 58)]
);
});
@@ -1718,7 +1724,9 @@ mod tests {
search_bar.update_in(cx, |search_bar, window, cx| {
search_bar.select_next_match(&SelectNextMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43)]
);
});
@@ -1729,7 +1737,9 @@ mod tests {
search_bar.update_in(cx, |search_bar, window, cx| {
search_bar.select_prev_match(&SelectPreviousMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(3), 56)..DisplayPoint::new(DisplayRow(3), 58)]
);
});
@@ -1740,7 +1750,9 @@ mod tests {
search_bar.update_in(cx, |search_bar, window, cx| {
search_bar.select_prev_match(&SelectPreviousMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(3), 11)..DisplayPoint::new(DisplayRow(3), 13)]
);
});
@@ -1751,7 +1763,9 @@ mod tests {
search_bar.update_in(cx, |search_bar, window, cx| {
search_bar.select_prev_match(&SelectPreviousMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43)]
);
});
@@ -1772,7 +1786,9 @@ mod tests {
assert_eq!(search_bar.active_match_index, Some(1));
search_bar.select_prev_match(&SelectPreviousMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43)]
);
});
@@ -1793,7 +1809,9 @@ mod tests {
assert_eq!(search_bar.active_match_index, Some(1));
search_bar.select_next_match(&SelectNextMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(3), 11)..DisplayPoint::new(DisplayRow(3), 13)]
);
});
@@ -1814,7 +1832,9 @@ mod tests {
assert_eq!(search_bar.active_match_index, Some(2));
search_bar.select_prev_match(&SelectPreviousMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(3), 56)..DisplayPoint::new(DisplayRow(3), 58)]
);
});
@@ -1835,7 +1855,9 @@ mod tests {
assert_eq!(search_bar.active_match_index, Some(2));
search_bar.select_next_match(&SelectNextMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43)]
);
});
@@ -1856,7 +1878,9 @@ mod tests {
assert_eq!(search_bar.active_match_index, Some(0));
search_bar.select_prev_match(&SelectPreviousMatch, window, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(3), 56)..DisplayPoint::new(DisplayRow(3), 58)]
);
});
@@ -1989,7 +2013,7 @@ mod tests {
"Initially, the editor should not be focused"
);
let initial_selections = editor.update(cx, |editor, cx| {
let initial_selections = editor.selections.display_ranges(cx);
let initial_selections = editor.selections.display_ranges(&editor.display_snapshot(cx));
assert_eq!(
initial_selections.len(), 1,
"Expected to have only one selection before adding carets to all matches, but got: {initial_selections:?}",
@@ -2008,7 +2032,7 @@ mod tests {
);
search_bar.update(cx, |search_bar, cx| {
let all_selections =
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx));
editor.update(cx, |editor, cx| editor.selections.display_ranges(&editor.display_snapshot(cx)));
assert_eq!(
all_selections.len(),
expected_query_matches_count,
@@ -2032,8 +2056,11 @@ mod tests {
"Should still have editor focused after SelectNextMatch"
);
search_bar.update(cx, |search_bar, cx| {
let all_selections =
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx));
let all_selections = editor.update(cx, |editor, cx| {
editor
.selections
.display_ranges(&editor.display_snapshot(cx))
});
assert_eq!(
all_selections.len(),
1,
@@ -2062,7 +2089,7 @@ mod tests {
);
search_bar.update(cx, |search_bar, cx| {
let all_selections =
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx));
editor.update(cx, |editor, cx| editor.selections.display_ranges(&editor.display_snapshot(cx)));
assert_eq!(
all_selections.len(),
expected_query_matches_count,
@@ -2087,8 +2114,11 @@ mod tests {
);
search_bar.update(cx, |search_bar, cx| {
let all_selections =
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx));
let all_selections = editor.update(cx, |editor, cx| {
editor
.selections
.display_ranges(&editor.display_snapshot(cx))
});
assert_eq!(
all_selections.len(),
1,
@@ -2130,7 +2160,7 @@ mod tests {
);
search_bar.update(cx, |search_bar, cx| {
let all_selections =
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx));
editor.update(cx, |editor, cx| editor.selections.display_ranges(&editor.display_snapshot(cx)));
assert_eq!(
all_selections, last_match_selections,
"Should not select anything new if there are no matches"
@@ -2194,8 +2224,11 @@ mod tests {
search_bar.select_all_matches(&SelectAllMatches, window, cx);
});
search_bar.update(cx, |_, cx| {
let all_selections =
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx));
let all_selections = editor.update(cx, |editor, cx| {
editor
.selections
.display_ranges(&editor.display_snapshot(cx))
});
assert_eq!(
all_selections.len(),
2,
@@ -2220,8 +2253,11 @@ mod tests {
search_bar.select_all_matches(&SelectAllMatches, window, cx);
});
search_bar.update(cx, |_, cx| {
let all_selections =
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx));
let all_selections = editor.update(cx, |editor, cx| {
editor
.selections
.display_ranges(&editor.display_snapshot(cx))
});
assert_eq!(
all_selections.len(),
2,

View File

@@ -2526,7 +2526,7 @@ pub mod tests {
assert_eq!(
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
.update(cx, |editor, cx| editor.selections.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(2), 32)..DisplayPoint::new(DisplayRow(2), 35)]
);
@@ -2537,9 +2537,9 @@ pub mod tests {
.update(cx, |search_view, window, cx| {
assert_eq!(search_view.active_match_index, Some(1));
assert_eq!(
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
search_view.results_editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(2), 37)..DisplayPoint::new(DisplayRow(2), 40)]
);
search_view.select_match(Direction::Next, window, cx);
@@ -2550,9 +2550,9 @@ pub mod tests {
.update(cx, |search_view, window, cx| {
assert_eq!(search_view.active_match_index, Some(2));
assert_eq!(
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
search_view.results_editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 9)]
);
search_view.select_match(Direction::Next, window, cx);
@@ -2563,9 +2563,9 @@ pub mod tests {
.update(cx, |search_view, window, cx| {
assert_eq!(search_view.active_match_index, Some(0));
assert_eq!(
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
search_view.results_editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(2), 32)..DisplayPoint::new(DisplayRow(2), 35)]
);
search_view.select_match(Direction::Prev, window, cx);
@@ -2576,9 +2576,9 @@ pub mod tests {
.update(cx, |search_view, window, cx| {
assert_eq!(search_view.active_match_index, Some(2));
assert_eq!(
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
search_view.results_editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 9)]
);
search_view.select_match(Direction::Prev, window, cx);
@@ -2589,9 +2589,9 @@ pub mod tests {
.update(cx, |search_view, _, cx| {
assert_eq!(search_view.active_match_index, Some(1));
assert_eq!(
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
search_view.results_editor.update(cx, |editor, cx| editor
.selections
.display_ranges(&editor.display_snapshot(cx))),
[DisplayPoint::new(DisplayRow(2), 37)..DisplayPoint::new(DisplayRow(2), 40)]
);
})

View File

@@ -38,7 +38,7 @@ impl Vim {
.map(|s| s.to_vec())
{
editor.change_selections(Default::default(), window, cx, |s| {
let map = s.display_map();
let map = s.display_snapshot();
s.select_display_ranges(selections.iter().map(|a| {
let point = a.to_display_point(&map);
point..point

View File

@@ -682,8 +682,9 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
.disjoint_anchor_ranges()
.collect::<Vec<_>>()
});
let snapshot = editor.buffer().read(cx).snapshot(cx);
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
let end = Point::new(range.end.0, s.buffer().line_len(range.end));
let end = Point::new(range.end.0, snapshot.line_len(range.end));
s.select_ranges([end..Point::new(range.start.0, 0)]);
});
selections

View File

@@ -120,8 +120,8 @@ impl Vim {
editor.edit(edits, cx);
let snapshot = editor.buffer().read(cx).snapshot(cx);
editor.change_selections(Default::default(), window, cx, |s| {
let snapshot = s.buffer().clone();
s.select_ranges(new_selections.into_iter().map(|(anchor, len)| {
let offset = anchor.to_offset(&snapshot);
if action.before {

View File

@@ -1210,7 +1210,7 @@ impl Vim {
s.select_anchor_ranges(vec![pos..pos])
}
let snapshot = s.display_map();
let snapshot = s.display_snapshot();
if let Some(pending) = s.pending_anchor_mut()
&& pending.reversed
&& mode.is_visual()

View File

@@ -179,7 +179,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
vim.update_editor(cx, |_, editor, cx| {
editor.set_clip_at_line_ends(false, cx);
editor.change_selections(Default::default(), window, cx, |s| {
let map = s.display_map();
let map = s.display_snapshot();
let ranges = ranges
.into_iter()
.map(|(start, end, reversed)| {
@@ -304,7 +304,7 @@ impl Vim {
) {
let text_layout_details = editor.text_layout_details(window);
editor.change_selections(Default::default(), window, cx, |s| {
let map = &s.display_map();
let map = &s.display_snapshot();
let mut head = s.newest_anchor().head().to_display_point(map);
let mut tail = s.oldest_anchor().tail().to_display_point(map);

View File

@@ -4071,7 +4071,9 @@ mod tests {
let editor = item.downcast::<Editor>().unwrap();
let (selections, scroll_position) = editor.update(cx, |editor, cx| {
(
editor.selections.display_ranges(cx),
editor
.selections
.display_ranges(&editor.display_snapshot(cx)),
editor.scroll_position(cx),
)
});

View File

@@ -5,4 +5,5 @@ if [[ "$GITHUB_REF_NAME" == *"-pre" ]]; then
preview="-p"
fi
gh release create -t "$GITHUB_REF_NAME" -d "$GITHUB_REF_NAME" -F "$1" $preview
gh release view "$GITHUB_REF_NAME" ||\
gh release create -t "$GITHUB_REF_NAME" -d "$GITHUB_REF_NAME" -F "$1" $preview

View File

@@ -88,14 +88,14 @@ fn post_to_discord(deps: &[&NamedJob]) -> NamedJob {
fn publish_winget() -> NamedJob {
fn set_package_name() -> (Step<Run>, StepOutput) {
let step = named::bash(indoc::indoc! {r#"
if [ "${{ github.event.release.prerelease }}" == "true" ]; then
PACKAGE_NAME=ZedIndustries.Zed.Preview
else
PACKAGE_NAME=ZedIndustries.Zed
fi
let step = named::pwsh(indoc::indoc! {r#"
if ("${{ github.event.release.prerelease }}" -eq "true") {
$PACKAGE_NAME = "ZedIndustries.Zed.Preview"
} else {
$PACKAGE_NAME = "ZedIndustries.Zed"
}
echo "PACKAGE_NAME=$PACKAGE_NAME" >> "$GITHUB_OUTPUT"
echo "PACKAGE_NAME=$PACKAGE_NAME" >> $env:GITHUB_OUTPUT
"#})
.id("set-package-name");