Compare commits
2 Commits
improve-ve
...
fix/deprec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
131ed8add1 | ||
|
|
1f296334e5 |
2
.github/workflows/run_tests.yml
vendored
2
.github/workflows/run_tests.yml
vendored
@@ -84,7 +84,7 @@ jobs:
|
||||
run: ./script/check-keymaps
|
||||
shell: bash -euxo pipefail {0}
|
||||
- name: run_tests::check_style::check_for_typos
|
||||
uses: crate-ci/typos@2d0ce569feab1f8752f1dde43cc2f2aa53236e06
|
||||
uses: crate-ci/typos@80c8a4945eec0f6d464eaf9e65ed98ef085283d1
|
||||
with:
|
||||
config: ./typos.toml
|
||||
- name: steps::cargo_fmt
|
||||
|
||||
@@ -49,8 +49,7 @@
|
||||
"ctrl-cmd-f": "zed::ToggleFullScreen",
|
||||
"ctrl-cmd-z": "edit_prediction::RateCompletions",
|
||||
"ctrl-cmd-i": "edit_prediction::ToggleMenu",
|
||||
"ctrl-cmd-l": "lsp_tool::ToggleMenu",
|
||||
"ctrl-cmd-c": "editor::DisplayCursorNames"
|
||||
"ctrl-cmd-l": "lsp_tool::ToggleMenu"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -590,7 +589,8 @@
|
||||
"cmd-.": "editor::ToggleCodeActions",
|
||||
"cmd-k r": "editor::RevealInFileManager",
|
||||
"cmd-k p": "editor::CopyPath",
|
||||
"cmd-\\": "pane::SplitRight"
|
||||
"cmd-\\": "pane::SplitRight",
|
||||
"ctrl-cmd-c": "editor::DisplayCursorNames"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -730,8 +730,7 @@
|
||||
"context": "Workspace && debugger_running",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"f5": "zed::NoAction",
|
||||
"f11": "debugger::StepInto"
|
||||
"f5": "zed::NoAction"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -36,12 +36,12 @@
|
||||
"shift-f5": "debugger::Stop",
|
||||
"ctrl-shift-f5": "debugger::RerunSession",
|
||||
"f6": "debugger::Pause",
|
||||
"f10": "debugger::StepOver",
|
||||
"f7": "debugger::StepOver",
|
||||
"ctrl-f11": "debugger::StepInto",
|
||||
"shift-f11": "debugger::StepOut",
|
||||
"f11": "zed::ToggleFullScreen",
|
||||
"ctrl-shift-i": "edit_prediction::ToggleMenu",
|
||||
"shift-alt-l": "lsp_tool::ToggleMenu",
|
||||
"ctrl-shift-alt-c": "editor::DisplayCursorNames"
|
||||
"shift-alt-l": "lsp_tool::ToggleMenu"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -117,7 +117,7 @@
|
||||
"alt-g m": "git::OpenModifiedFiles",
|
||||
"menu": "editor::OpenContextMenu",
|
||||
"shift-f10": "editor::OpenContextMenu",
|
||||
"ctrl-alt-e": "editor::ToggleEditPrediction",
|
||||
"ctrl-shift-e": "editor::ToggleEditPrediction",
|
||||
"f9": "editor::ToggleBreakpoint",
|
||||
"shift-f9": "editor::EditLogBreakpoint"
|
||||
}
|
||||
@@ -215,7 +215,7 @@
|
||||
"context": "ContextEditor > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-i": "assistant::Assist",
|
||||
"ctrl-enter": "assistant::Assist",
|
||||
"ctrl-s": "workspace::Save",
|
||||
"ctrl-shift-,": "assistant::InsertIntoEditor",
|
||||
"shift-enter": "assistant::Split",
|
||||
@@ -500,7 +500,10 @@
|
||||
"ctrl-shift-l": "editor::SelectAllMatches", // Select all occurrences of current selection
|
||||
"ctrl-f2": "editor::SelectAllMatches", // Select all occurrences of current word
|
||||
"ctrl-d": ["editor::SelectNext", { "replace_newest": false }], // editor.action.addSelectionToNextFindMatch / find_under_expand
|
||||
"ctrl-shift-down": ["editor::SelectNext", { "replace_newest": false }], // editor.action.addSelectionToNextFindMatch
|
||||
"ctrl-shift-up": ["editor::SelectPrevious", { "replace_newest": false }], // editor.action.addSelectionToPreviousFindMatch
|
||||
"ctrl-k ctrl-d": ["editor::SelectNext", { "replace_newest": true }], // editor.action.moveSelectionToNextFindMatch / find_under_expand_skip
|
||||
"ctrl-k ctrl-shift-d": ["editor::SelectPrevious", { "replace_newest": true }], // editor.action.moveSelectionToPreviousFindMatch
|
||||
"ctrl-k ctrl-i": "editor::Hover",
|
||||
"ctrl-k ctrl-b": "editor::BlameHover",
|
||||
"ctrl-/": ["editor::ToggleComments", { "advance_downwards": false }],
|
||||
@@ -509,8 +512,12 @@
|
||||
"f2": "editor::Rename",
|
||||
"f12": "editor::GoToDefinition",
|
||||
"alt-f12": "editor::GoToDefinitionSplit",
|
||||
"ctrl-shift-f10": "editor::GoToDefinitionSplit",
|
||||
"ctrl-f12": "editor::GoToImplementation",
|
||||
"shift-f12": "editor::GoToTypeDefinition",
|
||||
"ctrl-alt-f12": "editor::GoToTypeDefinitionSplit",
|
||||
"shift-alt-f12": "editor::FindAllReferences",
|
||||
"ctrl-m": "editor::MoveToEnclosingBracket", // from jetbrains
|
||||
"ctrl-shift-\\": "editor::MoveToEnclosingBracket",
|
||||
"ctrl-shift-[": "editor::Fold",
|
||||
"ctrl-shift-]": "editor::UnfoldLines",
|
||||
@@ -534,6 +541,7 @@
|
||||
"ctrl-k r": "editor::RevealInFileManager",
|
||||
"ctrl-k p": "editor::CopyPath",
|
||||
"ctrl-\\": "pane::SplitRight",
|
||||
"ctrl-shift-alt-c": "editor::DisplayCursorNames",
|
||||
"alt-.": "editor::GoToHunk",
|
||||
"alt-,": "editor::GoToPreviousHunk"
|
||||
}
|
||||
@@ -1116,7 +1124,7 @@
|
||||
"shift-insert": "terminal::Paste",
|
||||
"ctrl-v": "terminal::Paste",
|
||||
"ctrl-shift-v": "terminal::Paste",
|
||||
"ctrl-i": "assistant::InlineAssist",
|
||||
"ctrl-enter": "assistant::InlineAssist",
|
||||
"alt-b": ["terminal::SendText", "\u001bb"],
|
||||
"alt-f": ["terminal::SendText", "\u001bf"],
|
||||
"alt-.": ["terminal::SendText", "\u001b."],
|
||||
|
||||
@@ -96,11 +96,11 @@
|
||||
"terminal.ansi.bright_white": "#fafafaff",
|
||||
"terminal.ansi.dim_white": "#575d65ff",
|
||||
"link_text.hover": "#74ade8ff",
|
||||
"version_control.added": "#2EA04833",
|
||||
"version_control.added": "#27a657ff",
|
||||
"version_control.modified": "#d3b020ff",
|
||||
"version_control.word_added": "#2EA0483D",
|
||||
"version_control.word_deleted": "#78081BB3",
|
||||
"version_control.deleted": "#78081B66",
|
||||
"version_control.word_added": "#2EA04859",
|
||||
"version_control.word_deleted": "#78081BCC",
|
||||
"version_control.deleted": "#e06c76ff",
|
||||
"version_control.conflict_marker.ours": "#a1c1811a",
|
||||
"version_control.conflict_marker.theirs": "#74ade81a",
|
||||
"conflict": "#dec184ff",
|
||||
@@ -499,11 +499,11 @@
|
||||
"terminal.ansi.bright_white": "#ffffffff",
|
||||
"terminal.ansi.dim_white": "#aaaaaaff",
|
||||
"link_text.hover": "#5c78e2ff",
|
||||
"version_control.added": "#2EA04833",
|
||||
"version_control.added": "#27a657ff",
|
||||
"version_control.modified": "#d3b020ff",
|
||||
"version_control.word_added": "#2EA0483D",
|
||||
"version_control.word_deleted": "#F851493D",
|
||||
"version_control.deleted": "#F8514929",
|
||||
"version_control.word_added": "#2EA04859",
|
||||
"version_control.word_deleted": "#F85149CC",
|
||||
"version_control.deleted": "#e06c76ff",
|
||||
"conflict": "#a48819ff",
|
||||
"conflict.background": "#faf2e6ff",
|
||||
"conflict.border": "#f4e7d1ff",
|
||||
|
||||
@@ -524,16 +524,6 @@ impl Room {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn room_id(&self) -> impl Future<Output = Option<String>> + 'static {
|
||||
let room = self.live_kit.as_ref().map(|lk| lk.room.clone());
|
||||
async move {
|
||||
let room = room?;
|
||||
let sid = room.sid().await;
|
||||
let name = room.name();
|
||||
Some(format!("{} (sid: {sid})", name))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn status(&self) -> RoomStatus {
|
||||
self.status
|
||||
}
|
||||
|
||||
@@ -206,16 +206,11 @@ pub struct AcceptEditPredictionBody {
|
||||
pub request_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RejectEditPredictionsBody {
|
||||
pub rejections: Vec<EditPredictionRejection>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct RejectEditPredictionsBodyRef<'a> {
|
||||
pub rejections: &'a [EditPredictionRejection],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct EditPredictionRejection {
|
||||
pub request_id: String,
|
||||
|
||||
@@ -37,7 +37,7 @@ use ui::{
|
||||
};
|
||||
use util::{ResultExt, TryFutureExt, maybe};
|
||||
use workspace::{
|
||||
CopyRoomId, Deafen, LeaveCall, Mute, OpenChannelNotes, ScreenShare, ShareProject, Workspace,
|
||||
Deafen, LeaveCall, Mute, OpenChannelNotes, ScreenShare, ShareProject, Workspace,
|
||||
dock::{DockPosition, Panel, PanelEvent},
|
||||
notifications::{DetachAndPromptErr, NotifyResultExt},
|
||||
};
|
||||
@@ -128,32 +128,6 @@ pub fn init(cx: &mut App) {
|
||||
workspace.register_action(|_, _: &LeaveCall, window, cx| {
|
||||
CollabPanel::leave_call(window, cx);
|
||||
});
|
||||
workspace.register_action(|workspace, _: &CopyRoomId, window, cx| {
|
||||
use workspace::notifications::{NotificationId, NotifyTaskExt as _};
|
||||
|
||||
struct RoomIdCopiedToast;
|
||||
|
||||
if let Some(room) = ActiveCall::global(cx).read(cx).room() {
|
||||
let romo_id_fut = room.read(cx).room_id();
|
||||
cx.spawn(async move |workspace, cx| {
|
||||
let room_id = romo_id_fut.await.context("Failed to get livekit room")?;
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(room_id));
|
||||
workspace.show_toast(
|
||||
workspace::Toast::new(
|
||||
NotificationId::unique::<RoomIdCopiedToast>(),
|
||||
"Room ID copied to clipboard",
|
||||
)
|
||||
.autohide(),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
})
|
||||
.detach_and_notify_err(window, cx);
|
||||
} else {
|
||||
workspace.show_error(&"There’s no active call; join one first.", cx);
|
||||
}
|
||||
});
|
||||
workspace.register_action(|workspace, _: &ShareProject, window, cx| {
|
||||
let project = workspace.project().clone();
|
||||
println!("{project:?}");
|
||||
|
||||
@@ -18,14 +18,14 @@ use gpui::{
|
||||
use language::{Anchor, Buffer, CharScopeContext, CodeLabel, TextBufferSnapshot, ToOffset};
|
||||
use menu::{Confirm, SelectNext, SelectPrevious};
|
||||
use project::{
|
||||
CompletionDisplayOptions, CompletionResponse,
|
||||
Completion, CompletionDisplayOptions, CompletionResponse,
|
||||
debugger::session::{CompletionsQuery, OutputToken, Session},
|
||||
lsp_store::CompletionDocumentation,
|
||||
search_history::{SearchHistory, SearchHistoryCursor},
|
||||
};
|
||||
use settings::Settings;
|
||||
use std::fmt::Write;
|
||||
use std::{ops::Range, rc::Rc, usize};
|
||||
use std::{cell::RefCell, ops::Range, rc::Rc, usize};
|
||||
use theme::{Theme, ThemeSettings};
|
||||
use ui::{ContextMenu, Divider, PopoverMenu, SplitButton, Tooltip, prelude::*};
|
||||
use util::ResultExt;
|
||||
@@ -553,6 +553,17 @@ impl CompletionProvider for ConsoleQueryBarCompletionProvider {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_additional_edits_for_completion(
|
||||
&self,
|
||||
_buffer: Entity<Buffer>,
|
||||
_completions: Rc<RefCell<Box<[Completion]>>>,
|
||||
_completion_index: usize,
|
||||
_push_to_history: bool,
|
||||
_cx: &mut Context<Editor>,
|
||||
) -> gpui::Task<anyhow::Result<Option<language::Transaction>>> {
|
||||
Task::ready(Ok(None))
|
||||
}
|
||||
|
||||
fn is_completion_trigger(
|
||||
&self,
|
||||
buffer: &Entity<Buffer>,
|
||||
|
||||
@@ -146,8 +146,8 @@ use persistence::DB;
|
||||
use project::{
|
||||
BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
|
||||
CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
|
||||
InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
|
||||
ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
|
||||
InvalidationStrategy, Location, LocationLink, PrepareRenameResponse, Project, ProjectItem,
|
||||
ProjectPath, ProjectTransaction, TaskSourceKind,
|
||||
debugger::{
|
||||
breakpoint_store::{
|
||||
Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
|
||||
@@ -6151,43 +6151,9 @@ impl Editor {
|
||||
}
|
||||
|
||||
let provider = self.completion_provider.as_ref()?;
|
||||
|
||||
let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
|
||||
let command = lsp_store.as_ref().and_then(|lsp_store| {
|
||||
let CompletionSource::Lsp {
|
||||
lsp_completion,
|
||||
server_id,
|
||||
..
|
||||
} = &completion.source
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
let lsp_command = lsp_completion.command.as_ref()?;
|
||||
let available_commands = lsp_store
|
||||
.read(cx)
|
||||
.lsp_server_capabilities
|
||||
.get(server_id)
|
||||
.and_then(|server_capabilities| {
|
||||
server_capabilities
|
||||
.execute_command_provider
|
||||
.as_ref()
|
||||
.map(|options| options.commands.as_slice())
|
||||
})?;
|
||||
if available_commands.contains(&lsp_command.command) {
|
||||
Some(CodeAction {
|
||||
server_id: *server_id,
|
||||
range: language::Anchor::MIN..language::Anchor::MIN,
|
||||
lsp_action: LspAction::Command(lsp_command.clone()),
|
||||
resolved: false,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
drop(completion);
|
||||
let apply_edits = provider.apply_additional_edits_for_completion(
|
||||
buffer_handle.clone(),
|
||||
buffer_handle,
|
||||
completions_menu.completions.clone(),
|
||||
candidate_id,
|
||||
true,
|
||||
@@ -6201,29 +6167,8 @@ impl Editor {
|
||||
self.show_signature_help(&ShowSignatureHelp, window, cx);
|
||||
}
|
||||
|
||||
Some(cx.spawn_in(window, async move |editor, cx| {
|
||||
Some(cx.foreground_executor().spawn(async move {
|
||||
apply_edits.await?;
|
||||
|
||||
if let Some((lsp_store, command)) = lsp_store.zip(command) {
|
||||
let title = command.lsp_action.title().to_owned();
|
||||
let project_transaction = lsp_store
|
||||
.update(cx, |lsp_store, cx| {
|
||||
lsp_store.apply_code_action(buffer_handle, command, false, cx)
|
||||
})?
|
||||
.await
|
||||
.context("applying post-completion command")?;
|
||||
if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
|
||||
Self::open_project_transaction(
|
||||
&editor,
|
||||
workspace.downgrade(),
|
||||
project_transaction,
|
||||
title,
|
||||
cx,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -14755,180 +14755,6 @@ async fn test_completion(cx: &mut TestAppContext) {
|
||||
apply_additional_edits.await.unwrap();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_completion_can_run_commands(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/a"),
|
||||
json!({
|
||||
"main.rs": "",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(rust_lang());
|
||||
let command_calls = Arc::new(AtomicUsize::new(0));
|
||||
let registered_command = "_the/command";
|
||||
|
||||
let closure_command_calls = command_calls.clone();
|
||||
let mut fake_servers = language_registry.register_fake_lsp(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
|
||||
..lsp::CompletionOptions::default()
|
||||
}),
|
||||
execute_command_provider: Some(lsp::ExecuteCommandOptions {
|
||||
commands: vec![registered_command.to_owned()],
|
||||
..lsp::ExecuteCommandOptions::default()
|
||||
}),
|
||||
..lsp::ServerCapabilities::default()
|
||||
},
|
||||
initializer: Some(Box::new(move |fake_server| {
|
||||
fake_server.set_request_handler::<lsp::request::Completion, _, _>(
|
||||
move |params, _| async move {
|
||||
Ok(Some(lsp::CompletionResponse::Array(vec![
|
||||
lsp::CompletionItem {
|
||||
label: "registered_command".to_owned(),
|
||||
text_edit: gen_text_edit(¶ms, ""),
|
||||
command: Some(lsp::Command {
|
||||
title: registered_command.to_owned(),
|
||||
command: "_the/command".to_owned(),
|
||||
arguments: Some(vec![serde_json::Value::Bool(true)]),
|
||||
}),
|
||||
..lsp::CompletionItem::default()
|
||||
},
|
||||
lsp::CompletionItem {
|
||||
label: "unregistered_command".to_owned(),
|
||||
text_edit: gen_text_edit(¶ms, ""),
|
||||
command: Some(lsp::Command {
|
||||
title: "????????????".to_owned(),
|
||||
command: "????????????".to_owned(),
|
||||
arguments: Some(vec![serde_json::Value::Null]),
|
||||
}),
|
||||
..lsp::CompletionItem::default()
|
||||
},
|
||||
])))
|
||||
},
|
||||
);
|
||||
fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
|
||||
let command_calls = closure_command_calls.clone();
|
||||
move |params, _| {
|
||||
assert_eq!(params.command, registered_command);
|
||||
let command_calls = command_calls.clone();
|
||||
async move {
|
||||
command_calls.fetch_add(1, atomic::Ordering::Release);
|
||||
Ok(Some(json!(null)))
|
||||
}
|
||||
}
|
||||
});
|
||||
})),
|
||||
..FakeLspAdapter::default()
|
||||
},
|
||||
);
|
||||
let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
||||
let editor = workspace
|
||||
.update(cx, |workspace, window, cx| {
|
||||
workspace.open_abs_path(
|
||||
PathBuf::from(path!("/a/main.rs")),
|
||||
OpenOptions::default(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap()
|
||||
.await
|
||||
.unwrap()
|
||||
.downcast::<Editor>()
|
||||
.unwrap();
|
||||
let _fake_server = fake_servers.next().await.unwrap();
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
cx.focus_self(window);
|
||||
editor.move_to_end(&MoveToEnd, window, cx);
|
||||
editor.handle_input(".", window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
editor.update(cx, |editor, _| {
|
||||
assert!(editor.context_menu_visible());
|
||||
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
|
||||
{
|
||||
let completion_labels = menu
|
||||
.completions
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|c| c.label.text.clone())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
completion_labels,
|
||||
&["registered_command", "unregistered_command",],
|
||||
);
|
||||
} else {
|
||||
panic!("expected completion menu to be open");
|
||||
}
|
||||
});
|
||||
|
||||
editor
|
||||
.update_in(cx, |editor, window, cx| {
|
||||
editor
|
||||
.confirm_completion(&ConfirmCompletion::default(), window, cx)
|
||||
.unwrap()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cx.run_until_parked();
|
||||
assert_eq!(
|
||||
command_calls.load(atomic::Ordering::Acquire),
|
||||
1,
|
||||
"For completion with a registered command, Zed should send a command execution request",
|
||||
);
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
cx.focus_self(window);
|
||||
editor.handle_input(".", window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
editor.update(cx, |editor, _| {
|
||||
assert!(editor.context_menu_visible());
|
||||
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
|
||||
{
|
||||
let completion_labels = menu
|
||||
.completions
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|c| c.label.text.clone())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
completion_labels,
|
||||
&["registered_command", "unregistered_command",],
|
||||
);
|
||||
} else {
|
||||
panic!("expected completion menu to be open");
|
||||
}
|
||||
});
|
||||
editor
|
||||
.update_in(cx, |editor, window, cx| {
|
||||
editor.context_menu_next(&Default::default(), window, cx);
|
||||
editor
|
||||
.confirm_completion(&ConfirmCompletion::default(), window, cx)
|
||||
.unwrap()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cx.run_until_parked();
|
||||
assert_eq!(
|
||||
command_calls.load(atomic::Ordering::Acquire),
|
||||
1,
|
||||
"For completion with an unregistered command, Zed should not send a command execution request",
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_completion_reuse(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
@@ -9213,34 +9213,18 @@ impl Element for EditorElement {
|
||||
continue;
|
||||
};
|
||||
|
||||
let background_color = dbg!(match diff_status.kind {
|
||||
DiffHunkStatusKind::Added =>
|
||||
cx.theme().colors().version_control_added,
|
||||
DiffHunkStatusKind::Deleted =>
|
||||
cx.theme().colors().version_control_deleted,
|
||||
DiffHunkStatusKind::Modified => {
|
||||
debug_panic!("modified diff status for row info");
|
||||
continue;
|
||||
}
|
||||
});
|
||||
let background_color = match diff_status.kind {
|
||||
DiffHunkStatusKind::Added =>
|
||||
cx.theme().colors().version_control_added,
|
||||
DiffHunkStatusKind::Deleted =>
|
||||
cx.theme().colors().version_control_deleted,
|
||||
DiffHunkStatusKind::Modified => {
|
||||
debug_panic!("modified diff status for row info");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Potential fix with new tokens:
|
||||
// diff.added = "#2EA048"
|
||||
// diff.added_word = @diff.added/24%
|
||||
// diff.added_background = @diff.added/20%
|
||||
//
|
||||
//
|
||||
// Ideal State:
|
||||
// version_control.added = "#2EA048"
|
||||
// version_control.added_word = @version_control.added/24%
|
||||
// version_control.added_background = @version_control.added/20%
|
||||
// version_control.added_icon = "something"
|
||||
|
||||
// let hunk_opacity = if background_color.a < 1.0 {
|
||||
// background_color.a // Use existing alpha
|
||||
// } else {
|
||||
// if is_light { 0.16 } else { 0.20 }
|
||||
// };
|
||||
let hunk_opacity = if is_light { 0.16 } else { 0.12 };
|
||||
|
||||
let hollow_highlight = LineHighlight {
|
||||
background: (background_color.opacity(if is_light {
|
||||
@@ -9259,7 +9243,7 @@ impl Element for EditorElement {
|
||||
};
|
||||
|
||||
let filled_highlight = LineHighlight {
|
||||
background: solid_background(background_color),
|
||||
background: solid_background(background_color.opacity(hunk_opacity)),
|
||||
border: None,
|
||||
include_gutter: true,
|
||||
type_id: None,
|
||||
|
||||
@@ -232,14 +232,12 @@ impl From<Oid> for usize {
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum RunHook {
|
||||
PreCommit,
|
||||
PrePush,
|
||||
}
|
||||
|
||||
impl RunHook {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::PreCommit => "pre-commit",
|
||||
Self::PrePush => "pre-push",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +248,6 @@ impl RunHook {
|
||||
pub fn from_proto(value: i32) -> Option<Self> {
|
||||
match value {
|
||||
0 => Some(Self::PreCommit),
|
||||
1 => Some(Self::PrePush),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,7 +748,7 @@ impl Size<Length> {
|
||||
/// assert_eq!(bounds.origin, origin);
|
||||
/// assert_eq!(bounds.size, size);
|
||||
/// ```
|
||||
#[derive(Refineable, Copy, Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||
#[refineable(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Bounds<T: Clone + Debug + Default + PartialEq> {
|
||||
@@ -1676,6 +1676,8 @@ impl Bounds<DevicePixels> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Clone + Debug + Default + PartialEq> Copy for Bounds<T> {}
|
||||
|
||||
/// Represents the edges of a box in a 2D space, such as padding or margin.
|
||||
///
|
||||
/// Each field represents the size of the edge on one side of the box: `top`, `right`, `bottom`, and `left`.
|
||||
|
||||
@@ -51,7 +51,7 @@ impl WindowsWindowInner {
|
||||
WM_NCCALCSIZE => self.handle_calc_client_size(handle, wparam, lparam),
|
||||
WM_DPICHANGED => self.handle_dpi_changed_msg(handle, wparam, lparam),
|
||||
WM_DISPLAYCHANGE => self.handle_display_change_msg(handle),
|
||||
WM_NCHITTEST => self.handle_hit_test_msg(handle, lparam),
|
||||
WM_NCHITTEST => self.handle_hit_test_msg(handle, msg, wparam, lparam),
|
||||
WM_PAINT => self.handle_paint_msg(handle),
|
||||
WM_CLOSE => self.handle_close_msg(),
|
||||
WM_DESTROY => self.handle_destroy_msg(handle),
|
||||
@@ -116,16 +116,17 @@ impl WindowsWindowInner {
|
||||
}
|
||||
|
||||
fn handle_move_msg(&self, handle: HWND, lparam: LPARAM) -> Option<isize> {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let origin = logical_point(
|
||||
lparam.signed_loword() as f32,
|
||||
lparam.signed_hiword() as f32,
|
||||
self.state.scale_factor.get(),
|
||||
lock.scale_factor,
|
||||
);
|
||||
self.state.origin.set(origin);
|
||||
let size = self.state.logical_size.get();
|
||||
lock.origin = origin;
|
||||
let size = lock.logical_size;
|
||||
let center_x = origin.x.0 + size.width.0 / 2.;
|
||||
let center_y = origin.y.0 + size.height.0 / 2.;
|
||||
let monitor_bounds = self.state.display.get().bounds();
|
||||
let monitor_bounds = lock.display.bounds();
|
||||
if center_x < monitor_bounds.left().0
|
||||
|| center_x > monitor_bounds.right().0
|
||||
|| center_y < monitor_bounds.top().0
|
||||
@@ -135,42 +136,42 @@ impl WindowsWindowInner {
|
||||
let monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONULL) };
|
||||
// minimize the window can trigger this event too, in this case,
|
||||
// monitor is invalid, we do nothing.
|
||||
if !monitor.is_invalid() && self.state.display.get().handle != monitor {
|
||||
if !monitor.is_invalid() && lock.display.handle != monitor {
|
||||
// we will get the same monitor if we only have one
|
||||
self.state
|
||||
.display
|
||||
.set(WindowsDisplay::new_with_handle(monitor).log_err()?);
|
||||
lock.display = WindowsDisplay::new_with_handle(monitor).log_err()?;
|
||||
}
|
||||
}
|
||||
if let Some(mut callback) = self.state.callbacks.moved.take() {
|
||||
if let Some(mut callback) = lock.callbacks.moved.take() {
|
||||
drop(lock);
|
||||
callback();
|
||||
self.state.callbacks.moved.set(Some(callback));
|
||||
self.state.borrow_mut().callbacks.moved = Some(callback);
|
||||
}
|
||||
Some(0)
|
||||
}
|
||||
|
||||
fn handle_get_min_max_info_msg(&self, lparam: LPARAM) -> Option<isize> {
|
||||
let min_size = self.state.min_size?;
|
||||
let scale_factor = self.state.scale_factor.get();
|
||||
let boarder_offset = &self.state.border_offset;
|
||||
|
||||
let lock = self.state.borrow();
|
||||
let min_size = lock.min_size?;
|
||||
let scale_factor = lock.scale_factor;
|
||||
let boarder_offset = lock.border_offset;
|
||||
drop(lock);
|
||||
unsafe {
|
||||
let minmax_info = &mut *(lparam.0 as *mut MINMAXINFO);
|
||||
minmax_info.ptMinTrackSize.x =
|
||||
min_size.width.scale(scale_factor).0 as i32 + boarder_offset.width_offset.get();
|
||||
min_size.width.scale(scale_factor).0 as i32 + boarder_offset.width_offset;
|
||||
minmax_info.ptMinTrackSize.y =
|
||||
min_size.height.scale(scale_factor).0 as i32 + boarder_offset.height_offset.get();
|
||||
min_size.height.scale(scale_factor).0 as i32 + boarder_offset.height_offset;
|
||||
}
|
||||
Some(0)
|
||||
}
|
||||
|
||||
fn handle_size_msg(&self, wparam: WPARAM, lparam: LPARAM) -> Option<isize> {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
|
||||
// Don't resize the renderer when the window is minimized, but record that it was minimized so
|
||||
// that on restore the swap chain can be recreated via `update_drawable_size_even_if_unchanged`.
|
||||
if wparam.0 == SIZE_MINIMIZED as usize {
|
||||
self.state
|
||||
.restore_from_minimized
|
||||
.set(self.state.callbacks.request_frame.take());
|
||||
lock.restore_from_minimized = lock.callbacks.request_frame.take();
|
||||
return Some(0);
|
||||
}
|
||||
|
||||
@@ -178,16 +179,14 @@ impl WindowsWindowInner {
|
||||
let height = lparam.hiword().max(1) as i32;
|
||||
let new_size = size(DevicePixels(width), DevicePixels(height));
|
||||
|
||||
let scale_factor = self.state.scale_factor.get();
|
||||
let scale_factor = lock.scale_factor;
|
||||
let mut should_resize_renderer = false;
|
||||
if let Some(restore_from_minimized) = self.state.restore_from_minimized.take() {
|
||||
self.state
|
||||
.callbacks
|
||||
.request_frame
|
||||
.set(Some(restore_from_minimized));
|
||||
if lock.restore_from_minimized.is_some() {
|
||||
lock.callbacks.request_frame = lock.restore_from_minimized.take();
|
||||
} else {
|
||||
should_resize_renderer = true;
|
||||
}
|
||||
drop(lock);
|
||||
|
||||
self.handle_size_change(new_size, scale_factor, should_resize_renderer);
|
||||
Some(0)
|
||||
@@ -200,19 +199,17 @@ impl WindowsWindowInner {
|
||||
should_resize_renderer: bool,
|
||||
) {
|
||||
let new_logical_size = device_size.to_pixels(scale_factor);
|
||||
|
||||
self.state.logical_size.set(new_logical_size);
|
||||
if should_resize_renderer
|
||||
&& let Err(e) = self.state.renderer.borrow_mut().resize(device_size)
|
||||
{
|
||||
let mut lock = self.state.borrow_mut();
|
||||
lock.logical_size = new_logical_size;
|
||||
if should_resize_renderer && let Err(e) = lock.renderer.resize(device_size) {
|
||||
log::error!("Failed to resize renderer, invalidating devices: {}", e);
|
||||
self.state
|
||||
.invalidate_devices
|
||||
lock.invalidate_devices
|
||||
.store(true, std::sync::atomic::Ordering::Release);
|
||||
}
|
||||
if let Some(mut callback) = self.state.callbacks.resize.take() {
|
||||
if let Some(mut callback) = lock.callbacks.resize.take() {
|
||||
drop(lock);
|
||||
callback(new_logical_size, scale_factor);
|
||||
self.state.callbacks.resize.set(Some(callback));
|
||||
self.state.borrow_mut().callbacks.resize = Some(callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,14 +254,17 @@ impl WindowsWindowInner {
|
||||
}
|
||||
|
||||
fn handle_close_msg(&self) -> Option<isize> {
|
||||
let mut callback = self.state.callbacks.should_close.take()?;
|
||||
let mut callback = self.state.borrow_mut().callbacks.should_close.take()?;
|
||||
let should_close = callback();
|
||||
self.state.callbacks.should_close.set(Some(callback));
|
||||
self.state.borrow_mut().callbacks.should_close = Some(callback);
|
||||
if should_close { None } else { Some(0) }
|
||||
}
|
||||
|
||||
fn handle_destroy_msg(&self, handle: HWND) -> Option<isize> {
|
||||
let callback = { self.state.callbacks.close.take() };
|
||||
let callback = {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
lock.callbacks.close.take()
|
||||
};
|
||||
if let Some(callback) = callback {
|
||||
callback();
|
||||
}
|
||||
@@ -283,10 +283,12 @@ impl WindowsWindowInner {
|
||||
fn handle_mouse_move_msg(&self, handle: HWND, lparam: LPARAM, wparam: WPARAM) -> Option<isize> {
|
||||
self.start_tracking_mouse(handle, TME_LEAVE);
|
||||
|
||||
let Some(mut func) = self.state.callbacks.input.take() else {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let Some(mut func) = lock.callbacks.input.take() else {
|
||||
return Some(1);
|
||||
};
|
||||
let scale_factor = self.state.scale_factor.get();
|
||||
let scale_factor = lock.scale_factor;
|
||||
drop(lock);
|
||||
|
||||
let pressed_button = match MODIFIERKEYS_FLAGS(wparam.loword() as u32) {
|
||||
flags if flags.contains(MK_LBUTTON) => Some(MouseButton::Left),
|
||||
@@ -308,32 +310,32 @@ impl WindowsWindowInner {
|
||||
modifiers: current_modifiers(),
|
||||
});
|
||||
let handled = !func(input).propagate;
|
||||
self.state.callbacks.input.set(Some(func));
|
||||
self.state.borrow_mut().callbacks.input = Some(func);
|
||||
|
||||
if handled { Some(0) } else { Some(1) }
|
||||
}
|
||||
|
||||
fn handle_mouse_leave_msg(&self) -> Option<isize> {
|
||||
self.state.hovered.set(false);
|
||||
if let Some(mut callback) = self.state.callbacks.hovered_status_change.take() {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
lock.hovered = false;
|
||||
if let Some(mut callback) = lock.callbacks.hovered_status_change.take() {
|
||||
drop(lock);
|
||||
callback(false);
|
||||
self.state
|
||||
.callbacks
|
||||
.hovered_status_change
|
||||
.set(Some(callback));
|
||||
self.state.borrow_mut().callbacks.hovered_status_change = Some(callback);
|
||||
}
|
||||
|
||||
Some(0)
|
||||
}
|
||||
|
||||
fn handle_syskeyup_msg(&self, wparam: WPARAM, lparam: LPARAM) -> Option<isize> {
|
||||
let input = handle_key_event(wparam, lparam, &self.state, |keystroke, _| {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let input = handle_key_event(wparam, lparam, &mut lock, |keystroke, _| {
|
||||
PlatformInput::KeyUp(KeyUpEvent { keystroke })
|
||||
})?;
|
||||
let mut func = self.state.callbacks.input.take()?;
|
||||
|
||||
let mut func = lock.callbacks.input.take()?;
|
||||
drop(lock);
|
||||
func(input);
|
||||
self.state.callbacks.input.set(Some(func));
|
||||
self.state.borrow_mut().callbacks.input = Some(func);
|
||||
|
||||
// Always return 0 to indicate that the message was handled, so we could properly handle `ModifiersChanged` event.
|
||||
Some(0)
|
||||
@@ -342,10 +344,11 @@ impl WindowsWindowInner {
|
||||
// It's a known bug that you can't trigger `ctrl-shift-0`. See:
|
||||
// https://superuser.com/questions/1455762/ctrl-shift-number-key-combination-has-stopped-working-for-a-few-numbers
|
||||
fn handle_keydown_msg(&self, wparam: WPARAM, lparam: LPARAM) -> Option<isize> {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let Some(input) = handle_key_event(
|
||||
wparam,
|
||||
lparam,
|
||||
&self.state,
|
||||
&mut lock,
|
||||
|keystroke, prefer_character_input| {
|
||||
PlatformInput::KeyDown(KeyDownEvent {
|
||||
keystroke,
|
||||
@@ -356,31 +359,34 @@ impl WindowsWindowInner {
|
||||
) else {
|
||||
return Some(1);
|
||||
};
|
||||
drop(lock);
|
||||
|
||||
let Some(mut func) = self.state.callbacks.input.take() else {
|
||||
let Some(mut func) = self.state.borrow_mut().callbacks.input.take() else {
|
||||
return Some(1);
|
||||
};
|
||||
|
||||
let handled = !func(input).propagate;
|
||||
|
||||
self.state.callbacks.input.set(Some(func));
|
||||
self.state.borrow_mut().callbacks.input = Some(func);
|
||||
|
||||
if handled { Some(0) } else { Some(1) }
|
||||
}
|
||||
|
||||
fn handle_keyup_msg(&self, wparam: WPARAM, lparam: LPARAM) -> Option<isize> {
|
||||
let Some(input) = handle_key_event(wparam, lparam, &self.state, |keystroke, _| {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let Some(input) = handle_key_event(wparam, lparam, &mut lock, |keystroke, _| {
|
||||
PlatformInput::KeyUp(KeyUpEvent { keystroke })
|
||||
}) else {
|
||||
return Some(1);
|
||||
};
|
||||
|
||||
let Some(mut func) = self.state.callbacks.input.take() else {
|
||||
let Some(mut func) = lock.callbacks.input.take() else {
|
||||
return Some(1);
|
||||
};
|
||||
drop(lock);
|
||||
|
||||
let handled = !func(input).propagate;
|
||||
self.state.callbacks.input.set(Some(func));
|
||||
self.state.borrow_mut().callbacks.input = Some(func);
|
||||
|
||||
if handled { Some(0) } else { Some(1) }
|
||||
}
|
||||
@@ -401,15 +407,16 @@ impl WindowsWindowInner {
|
||||
lparam: LPARAM,
|
||||
) -> Option<isize> {
|
||||
unsafe { SetCapture(handle) };
|
||||
|
||||
let Some(mut func) = self.state.callbacks.input.take() else {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let Some(mut func) = lock.callbacks.input.take() else {
|
||||
return Some(1);
|
||||
};
|
||||
let x = lparam.signed_loword();
|
||||
let y = lparam.signed_hiword();
|
||||
let physical_point = point(DevicePixels(x as i32), DevicePixels(y as i32));
|
||||
let click_count = self.state.click_state.update(button, physical_point);
|
||||
let scale_factor = self.state.scale_factor.get();
|
||||
let click_count = lock.click_state.update(button, physical_point);
|
||||
let scale_factor = lock.scale_factor;
|
||||
drop(lock);
|
||||
|
||||
let input = PlatformInput::MouseDown(MouseDownEvent {
|
||||
button,
|
||||
@@ -419,7 +426,7 @@ impl WindowsWindowInner {
|
||||
first_mouse: false,
|
||||
});
|
||||
let handled = !func(input).propagate;
|
||||
self.state.callbacks.input.set(Some(func));
|
||||
self.state.borrow_mut().callbacks.input = Some(func);
|
||||
|
||||
if handled { Some(0) } else { Some(1) }
|
||||
}
|
||||
@@ -431,14 +438,15 @@ impl WindowsWindowInner {
|
||||
lparam: LPARAM,
|
||||
) -> Option<isize> {
|
||||
unsafe { ReleaseCapture().log_err() };
|
||||
|
||||
let Some(mut func) = self.state.callbacks.input.take() else {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let Some(mut func) = lock.callbacks.input.take() else {
|
||||
return Some(1);
|
||||
};
|
||||
let x = lparam.signed_loword() as f32;
|
||||
let y = lparam.signed_hiword() as f32;
|
||||
let click_count = self.state.click_state.current_count.get();
|
||||
let scale_factor = self.state.scale_factor.get();
|
||||
let click_count = lock.click_state.current_count;
|
||||
let scale_factor = lock.scale_factor;
|
||||
drop(lock);
|
||||
|
||||
let input = PlatformInput::MouseUp(MouseUpEvent {
|
||||
button,
|
||||
@@ -447,7 +455,7 @@ impl WindowsWindowInner {
|
||||
click_count,
|
||||
});
|
||||
let handled = !func(input).propagate;
|
||||
self.state.callbacks.input.set(Some(func));
|
||||
self.state.borrow_mut().callbacks.input = Some(func);
|
||||
|
||||
if handled { Some(0) } else { Some(1) }
|
||||
}
|
||||
@@ -474,23 +482,24 @@ impl WindowsWindowInner {
|
||||
lparam: LPARAM,
|
||||
) -> Option<isize> {
|
||||
let modifiers = current_modifiers();
|
||||
|
||||
let Some(mut func) = self.state.callbacks.input.take() else {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let Some(mut func) = lock.callbacks.input.take() else {
|
||||
return Some(1);
|
||||
};
|
||||
let scale_factor = self.state.scale_factor.get();
|
||||
let scale_factor = lock.scale_factor;
|
||||
let wheel_scroll_amount = match modifiers.shift {
|
||||
true => self
|
||||
.system_settings()
|
||||
.mouse_wheel_settings
|
||||
.wheel_scroll_chars
|
||||
.get(),
|
||||
false => self
|
||||
.system_settings()
|
||||
.mouse_wheel_settings
|
||||
.wheel_scroll_lines
|
||||
.get(),
|
||||
true => {
|
||||
self.system_settings()
|
||||
.mouse_wheel_settings
|
||||
.wheel_scroll_chars
|
||||
}
|
||||
false => {
|
||||
self.system_settings()
|
||||
.mouse_wheel_settings
|
||||
.wheel_scroll_lines
|
||||
}
|
||||
};
|
||||
drop(lock);
|
||||
|
||||
let wheel_distance =
|
||||
(wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_amount as f32;
|
||||
@@ -515,7 +524,7 @@ impl WindowsWindowInner {
|
||||
touch_phase: TouchPhase::Moved,
|
||||
});
|
||||
let handled = !func(input).propagate;
|
||||
self.state.callbacks.input.set(Some(func));
|
||||
self.state.borrow_mut().callbacks.input = Some(func);
|
||||
|
||||
if handled { Some(0) } else { Some(1) }
|
||||
}
|
||||
@@ -526,15 +535,16 @@ impl WindowsWindowInner {
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> Option<isize> {
|
||||
let Some(mut func) = self.state.callbacks.input.take() else {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let Some(mut func) = lock.callbacks.input.take() else {
|
||||
return Some(1);
|
||||
};
|
||||
let scale_factor = self.state.scale_factor.get();
|
||||
let scale_factor = lock.scale_factor;
|
||||
let wheel_scroll_chars = self
|
||||
.system_settings()
|
||||
.mouse_wheel_settings
|
||||
.wheel_scroll_chars
|
||||
.get();
|
||||
.wheel_scroll_chars;
|
||||
drop(lock);
|
||||
|
||||
let wheel_distance =
|
||||
(-wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_chars as f32;
|
||||
@@ -553,7 +563,7 @@ impl WindowsWindowInner {
|
||||
touch_phase: TouchPhase::Moved,
|
||||
});
|
||||
let handled = !func(event).propagate;
|
||||
self.state.callbacks.input.set(Some(func));
|
||||
self.state.borrow_mut().callbacks.input = Some(func);
|
||||
|
||||
if handled { Some(0) } else { Some(1) }
|
||||
}
|
||||
@@ -647,11 +657,11 @@ impl WindowsWindowInner {
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> Option<isize> {
|
||||
if !self.hide_title_bar || self.state.is_fullscreen() || wparam.0 == 0 {
|
||||
if !self.hide_title_bar || self.state.borrow().is_fullscreen() || wparam.0 == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let is_maximized = self.state.is_maximized();
|
||||
let is_maximized = self.state.borrow().is_maximized();
|
||||
let insets = get_client_area_insets(handle, is_maximized, self.windows_version);
|
||||
// wparam is TRUE so lparam points to an NCCALCSIZE_PARAMS structure
|
||||
let mut params = lparam.0 as *mut NCCALCSIZE_PARAMS;
|
||||
@@ -666,7 +676,7 @@ impl WindowsWindowInner {
|
||||
// used by Chrome. However, it may result in one row of pixels being obscured
|
||||
// in our client area. But as Chrome says, "there seems to be no better solution."
|
||||
if is_maximized
|
||||
&& let Some(taskbar_position) = self.system_settings().auto_hide_taskbar_position.get()
|
||||
&& let Some(ref taskbar_position) = self.system_settings().auto_hide_taskbar_position
|
||||
{
|
||||
// For the auto-hide taskbar, adjust in by 1 pixel on taskbar edge,
|
||||
// so the window isn't treated as a "fullscreen app", which would cause
|
||||
@@ -695,9 +705,11 @@ impl WindowsWindowInner {
|
||||
let this = self.clone();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
if let Some(mut func) = this.state.callbacks.active_status_change.take() {
|
||||
let mut lock = this.state.borrow_mut();
|
||||
if let Some(mut func) = lock.callbacks.active_status_change.take() {
|
||||
drop(lock);
|
||||
func(activated);
|
||||
this.state.callbacks.active_status_change.set(Some(func));
|
||||
this.state.borrow_mut().callbacks.active_status_change = Some(func);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
@@ -721,11 +733,12 @@ impl WindowsWindowInner {
|
||||
lparam: LPARAM,
|
||||
) -> Option<isize> {
|
||||
let new_dpi = wparam.loword() as f32;
|
||||
|
||||
let is_maximized = self.state.is_maximized();
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let is_maximized = lock.is_maximized();
|
||||
let new_scale_factor = new_dpi / USER_DEFAULT_SCREEN_DPI as f32;
|
||||
self.state.scale_factor.set(new_scale_factor);
|
||||
self.state.border_offset.update(handle).log_err();
|
||||
lock.scale_factor = new_scale_factor;
|
||||
lock.border_offset.update(handle).log_err();
|
||||
drop(lock);
|
||||
|
||||
if is_maximized {
|
||||
// Get the monitor and its work area at the new DPI
|
||||
@@ -799,7 +812,7 @@ impl WindowsWindowInner {
|
||||
// Because WM_DPICHANGED, WM_MOVE, WM_SIZE will come first, window reposition and resize
|
||||
// are handled there.
|
||||
// So we only care about if monitor is disconnected.
|
||||
let previous_monitor = self.state.display.get();
|
||||
let previous_monitor = self.state.borrow().display;
|
||||
if WindowsDisplay::is_connected(previous_monitor.handle) {
|
||||
// we are fine, other display changed
|
||||
return None;
|
||||
@@ -817,78 +830,86 @@ impl WindowsWindowInner {
|
||||
return None;
|
||||
}
|
||||
let new_display = WindowsDisplay::new_with_handle(new_monitor).log_err()?;
|
||||
self.state.display.set(new_display);
|
||||
self.state.borrow_mut().display = new_display;
|
||||
Some(0)
|
||||
}
|
||||
|
||||
fn handle_hit_test_msg(&self, handle: HWND, lparam: LPARAM) -> Option<isize> {
|
||||
if !self.is_movable || self.state.is_fullscreen() {
|
||||
fn handle_hit_test_msg(
|
||||
&self,
|
||||
handle: HWND,
|
||||
msg: u32,
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> Option<isize> {
|
||||
if !self.is_movable || self.state.borrow().is_fullscreen() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let callback = self.state.callbacks.hit_test_window_control.take();
|
||||
let drag_area = if let Some(mut callback) = callback {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
if let Some(mut callback) = lock.callbacks.hit_test_window_control.take() {
|
||||
drop(lock);
|
||||
let area = callback();
|
||||
self.state
|
||||
.callbacks
|
||||
.hit_test_window_control
|
||||
.set(Some(callback));
|
||||
self.state.borrow_mut().callbacks.hit_test_window_control = Some(callback);
|
||||
if let Some(area) = area {
|
||||
match area {
|
||||
return match area {
|
||||
WindowControlArea::Drag => Some(HTCAPTION as _),
|
||||
WindowControlArea::Close => return Some(HTCLOSE as _),
|
||||
WindowControlArea::Max => return Some(HTMAXBUTTON as _),
|
||||
WindowControlArea::Min => return Some(HTMINBUTTON as _),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
WindowControlArea::Close => Some(HTCLOSE as _),
|
||||
WindowControlArea::Max => Some(HTMAXBUTTON as _),
|
||||
WindowControlArea::Min => Some(HTMINBUTTON as _),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
drop(lock);
|
||||
}
|
||||
|
||||
if !self.hide_title_bar {
|
||||
// If the OS draws the title bar, we don't need to handle hit test messages.
|
||||
return drag_area;
|
||||
return None;
|
||||
}
|
||||
|
||||
// default handler for resize areas
|
||||
let hit = unsafe { DefWindowProcW(handle, msg, wparam, lparam) };
|
||||
if matches!(
|
||||
hit.0 as u32,
|
||||
HTNOWHERE
|
||||
| HTRIGHT
|
||||
| HTLEFT
|
||||
| HTTOPLEFT
|
||||
| HTTOP
|
||||
| HTTOPRIGHT
|
||||
| HTBOTTOMRIGHT
|
||||
| HTBOTTOM
|
||||
| HTBOTTOMLEFT
|
||||
) {
|
||||
return Some(hit.0);
|
||||
}
|
||||
|
||||
if self.state.borrow().is_fullscreen() {
|
||||
return Some(HTCLIENT as _);
|
||||
}
|
||||
|
||||
let dpi = unsafe { GetDpiForWindow(handle) };
|
||||
// We do not use the OS title bar, so the default `DefWindowProcW` will only register a 1px edge for resizes
|
||||
// We need to calculate the frame thickness ourselves and do the hit test manually.
|
||||
let frame_y = get_frame_thicknessx(dpi);
|
||||
let frame_x = get_frame_thicknessy(dpi);
|
||||
let frame_y = unsafe { GetSystemMetricsForDpi(SM_CYFRAME, dpi) };
|
||||
|
||||
let mut cursor_point = POINT {
|
||||
x: lparam.signed_loword().into(),
|
||||
y: lparam.signed_hiword().into(),
|
||||
};
|
||||
|
||||
unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
|
||||
if !self.state.is_maximized() && 0 <= cursor_point.y && cursor_point.y <= frame_y {
|
||||
// x-axis actually goes from -frame_x to 0
|
||||
return Some(if cursor_point.x <= 0 {
|
||||
HTTOPLEFT
|
||||
} else {
|
||||
let mut rect = Default::default();
|
||||
unsafe { GetWindowRect(handle, &mut rect) }.log_err();
|
||||
// right and bottom bounds of RECT are exclusive, thus `-1`
|
||||
let right = rect.right - rect.left - 1;
|
||||
// the bounds include the padding frames, so accomodate for both of them
|
||||
if right - 2 * frame_x <= cursor_point.x {
|
||||
HTTOPRIGHT
|
||||
} else {
|
||||
HTTOP
|
||||
}
|
||||
} as _);
|
||||
if !self.state.borrow().is_maximized() && cursor_point.y >= 0 && cursor_point.y <= frame_y {
|
||||
return Some(HTTOP as _);
|
||||
}
|
||||
|
||||
drag_area
|
||||
Some(HTCLIENT as _)
|
||||
}
|
||||
|
||||
fn handle_nc_mouse_move_msg(&self, handle: HWND, lparam: LPARAM) -> Option<isize> {
|
||||
self.start_tracking_mouse(handle, TME_LEAVE | TME_NONCLIENT);
|
||||
|
||||
let mut func = self.state.callbacks.input.take()?;
|
||||
let scale_factor = self.state.scale_factor.get();
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let mut func = lock.callbacks.input.take()?;
|
||||
let scale_factor = lock.scale_factor;
|
||||
drop(lock);
|
||||
|
||||
let mut cursor_point = POINT {
|
||||
x: lparam.signed_loword().into(),
|
||||
@@ -901,7 +922,7 @@ impl WindowsWindowInner {
|
||||
modifiers: current_modifiers(),
|
||||
});
|
||||
let handled = !func(input).propagate;
|
||||
self.state.callbacks.input.set(Some(func));
|
||||
self.state.borrow_mut().callbacks.input = Some(func);
|
||||
|
||||
if handled { Some(0) } else { None }
|
||||
}
|
||||
@@ -913,15 +934,17 @@ impl WindowsWindowInner {
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> Option<isize> {
|
||||
if let Some(mut func) = self.state.callbacks.input.take() {
|
||||
let scale_factor = self.state.scale_factor.get();
|
||||
let mut lock = self.state.borrow_mut();
|
||||
if let Some(mut func) = lock.callbacks.input.take() {
|
||||
let scale_factor = lock.scale_factor;
|
||||
let mut cursor_point = POINT {
|
||||
x: lparam.signed_loword().into(),
|
||||
y: lparam.signed_hiword().into(),
|
||||
};
|
||||
unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
|
||||
let physical_point = point(DevicePixels(cursor_point.x), DevicePixels(cursor_point.y));
|
||||
let click_count = self.state.click_state.update(button, physical_point);
|
||||
let click_count = lock.click_state.update(button, physical_point);
|
||||
drop(lock);
|
||||
|
||||
let input = PlatformInput::MouseDown(MouseDownEvent {
|
||||
button,
|
||||
@@ -932,20 +955,21 @@ impl WindowsWindowInner {
|
||||
});
|
||||
let result = func(input);
|
||||
let handled = !result.propagate || result.default_prevented;
|
||||
self.state.callbacks.input.set(Some(func));
|
||||
self.state.borrow_mut().callbacks.input = Some(func);
|
||||
|
||||
if handled {
|
||||
return Some(0);
|
||||
}
|
||||
} else {
|
||||
drop(lock);
|
||||
};
|
||||
|
||||
// Since these are handled in handle_nc_mouse_up_msg we must prevent the default window proc
|
||||
if button == MouseButton::Left {
|
||||
match wparam.0 as u32 {
|
||||
HTMINBUTTON => self.state.nc_button_pressed.set(Some(HTMINBUTTON)),
|
||||
HTMAXBUTTON => self.state.nc_button_pressed.set(Some(HTMAXBUTTON)),
|
||||
HTCLOSE => self.state.nc_button_pressed.set(Some(HTCLOSE)),
|
||||
HTMINBUTTON => self.state.borrow_mut().nc_button_pressed = Some(HTMINBUTTON),
|
||||
HTMAXBUTTON => self.state.borrow_mut().nc_button_pressed = Some(HTMAXBUTTON),
|
||||
HTCLOSE => self.state.borrow_mut().nc_button_pressed = Some(HTCLOSE),
|
||||
_ => return None,
|
||||
};
|
||||
Some(0)
|
||||
@@ -961,8 +985,10 @@ impl WindowsWindowInner {
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> Option<isize> {
|
||||
if let Some(mut func) = self.state.callbacks.input.take() {
|
||||
let scale_factor = self.state.scale_factor.get();
|
||||
let mut lock = self.state.borrow_mut();
|
||||
if let Some(mut func) = lock.callbacks.input.take() {
|
||||
let scale_factor = lock.scale_factor;
|
||||
drop(lock);
|
||||
|
||||
let mut cursor_point = POINT {
|
||||
x: lparam.signed_loword().into(),
|
||||
@@ -976,15 +1002,16 @@ impl WindowsWindowInner {
|
||||
click_count: 1,
|
||||
});
|
||||
let handled = !func(input).propagate;
|
||||
self.state.callbacks.input.set(Some(func));
|
||||
self.state.borrow_mut().callbacks.input = Some(func);
|
||||
|
||||
if handled {
|
||||
return Some(0);
|
||||
}
|
||||
} else {
|
||||
drop(lock);
|
||||
}
|
||||
|
||||
let last_pressed = self.state.nc_button_pressed.take();
|
||||
let last_pressed = self.state.borrow_mut().nc_button_pressed.take();
|
||||
if button == MouseButton::Left
|
||||
&& let Some(last_pressed) = last_pressed
|
||||
{
|
||||
@@ -994,7 +1021,7 @@ impl WindowsWindowInner {
|
||||
true
|
||||
}
|
||||
(HTMAXBUTTON, HTMAXBUTTON) => {
|
||||
if self.state.is_maximized() {
|
||||
if self.state.borrow().is_maximized() {
|
||||
unsafe { ShowWindowAsync(handle, SW_NORMAL).ok().log_err() };
|
||||
} else {
|
||||
unsafe { ShowWindowAsync(handle, SW_MAXIMIZE).ok().log_err() };
|
||||
@@ -1019,16 +1046,17 @@ impl WindowsWindowInner {
|
||||
}
|
||||
|
||||
fn handle_cursor_changed(&self, lparam: LPARAM) -> Option<isize> {
|
||||
let had_cursor = self.state.current_cursor.get().is_some();
|
||||
let mut state = self.state.borrow_mut();
|
||||
let had_cursor = state.current_cursor.is_some();
|
||||
|
||||
self.state.current_cursor.set(if lparam.0 == 0 {
|
||||
state.current_cursor = if lparam.0 == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(HCURSOR(lparam.0 as _))
|
||||
});
|
||||
};
|
||||
|
||||
if had_cursor != self.state.current_cursor.get().is_some() {
|
||||
unsafe { SetCursor(self.state.current_cursor.get()) };
|
||||
if had_cursor != state.current_cursor.is_some() {
|
||||
unsafe { SetCursor(state.current_cursor) };
|
||||
}
|
||||
|
||||
Some(0)
|
||||
@@ -1051,9 +1079,9 @@ impl WindowsWindowInner {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
SetCursor(self.state.current_cursor.get());
|
||||
SetCursor(self.state.borrow().current_cursor);
|
||||
};
|
||||
Some(0)
|
||||
Some(1)
|
||||
}
|
||||
|
||||
fn handle_system_settings_changed(
|
||||
@@ -1063,12 +1091,13 @@ impl WindowsWindowInner {
|
||||
lparam: LPARAM,
|
||||
) -> Option<isize> {
|
||||
if wparam.0 != 0 {
|
||||
let display = self.state.display.get();
|
||||
self.state.click_state.system_update(wparam.0);
|
||||
self.state.border_offset.update(handle).log_err();
|
||||
// system settings may emit a window message which wants to take the refcell self.state, so drop it
|
||||
|
||||
self.system_settings().update(display, wparam.0);
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let display = lock.display;
|
||||
lock.click_state.system_update(wparam.0);
|
||||
lock.border_offset.update(handle).log_err();
|
||||
// system settings may emit a window message which wants to take the refcell lock, so drop it
|
||||
drop(lock);
|
||||
self.system_settings_mut().update(display, wparam.0);
|
||||
} else {
|
||||
self.handle_system_theme_changed(handle, lparam)?;
|
||||
};
|
||||
@@ -1091,13 +1120,13 @@ impl WindowsWindowInner {
|
||||
let new_appearance = system_appearance()
|
||||
.context("unable to get system appearance when handling ImmersiveColorSet")
|
||||
.log_err()?;
|
||||
|
||||
if new_appearance != self.state.appearance.get() {
|
||||
self.state.appearance.set(new_appearance);
|
||||
let mut callback = self.state.callbacks.appearance_changed.take()?;
|
||||
|
||||
let mut lock = self.state.borrow_mut();
|
||||
if new_appearance != lock.appearance {
|
||||
lock.appearance = new_appearance;
|
||||
let mut callback = lock.callbacks.appearance_changed.take()?;
|
||||
drop(lock);
|
||||
callback();
|
||||
self.state.callbacks.appearance_changed.set(Some(callback));
|
||||
self.state.borrow_mut().callbacks.appearance_changed = Some(callback);
|
||||
configure_dwm_dark_mode(handle, new_appearance);
|
||||
}
|
||||
}
|
||||
@@ -1126,14 +1155,10 @@ impl WindowsWindowInner {
|
||||
}
|
||||
|
||||
fn handle_device_lost(&self, lparam: LPARAM) -> Option<isize> {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let devices = lparam.0 as *const DirectXDevices;
|
||||
let devices = unsafe { &*devices };
|
||||
if let Err(err) = self
|
||||
.state
|
||||
.renderer
|
||||
.borrow_mut()
|
||||
.handle_device_lost(&devices)
|
||||
{
|
||||
if let Err(err) = lock.renderer.handle_device_lost(&devices) {
|
||||
panic!("Device lost: {err}");
|
||||
}
|
||||
Some(0)
|
||||
@@ -1141,18 +1166,18 @@ impl WindowsWindowInner {
|
||||
|
||||
#[inline]
|
||||
fn draw_window(&self, handle: HWND, force_render: bool) -> Option<isize> {
|
||||
let mut request_frame = self.state.callbacks.request_frame.take()?;
|
||||
let mut request_frame = self.state.borrow_mut().callbacks.request_frame.take()?;
|
||||
|
||||
// we are instructing gpui to force render a frame, this will
|
||||
// re-populate all the gpu textures for us so we can resume drawing in
|
||||
// case we disabled drawing earlier due to a device loss
|
||||
self.state.renderer.borrow_mut().mark_drawable();
|
||||
self.state.borrow_mut().renderer.mark_drawable();
|
||||
request_frame(RequestFrameOptions {
|
||||
require_presentation: false,
|
||||
force_render,
|
||||
});
|
||||
|
||||
self.state.callbacks.request_frame.set(Some(request_frame));
|
||||
self.state.borrow_mut().callbacks.request_frame = Some(request_frame);
|
||||
unsafe { ValidateRect(Some(handle), None).ok().log_err() };
|
||||
|
||||
Some(0)
|
||||
@@ -1161,16 +1186,16 @@ impl WindowsWindowInner {
|
||||
#[inline]
|
||||
fn parse_char_message(&self, wparam: WPARAM) -> Option<String> {
|
||||
let code_point = wparam.loword();
|
||||
|
||||
let mut lock = self.state.borrow_mut();
|
||||
// https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G2630
|
||||
match code_point {
|
||||
0xD800..=0xDBFF => {
|
||||
// High surrogate, wait for low surrogate
|
||||
self.state.pending_surrogate.set(Some(code_point));
|
||||
lock.pending_surrogate = Some(code_point);
|
||||
None
|
||||
}
|
||||
0xDC00..=0xDFFF => {
|
||||
if let Some(high_surrogate) = self.state.pending_surrogate.take() {
|
||||
if let Some(high_surrogate) = lock.pending_surrogate.take() {
|
||||
// Low surrogate, combine with pending high surrogate
|
||||
String::from_utf16(&[high_surrogate, code_point]).ok()
|
||||
} else {
|
||||
@@ -1182,7 +1207,7 @@ impl WindowsWindowInner {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.state.pending_surrogate.set(None);
|
||||
lock.pending_surrogate = None;
|
||||
char::from_u32(code_point as u32)
|
||||
.filter(|c| !c.is_control())
|
||||
.map(|c| c.to_string())
|
||||
@@ -1191,8 +1216,9 @@ impl WindowsWindowInner {
|
||||
}
|
||||
|
||||
fn start_tracking_mouse(&self, handle: HWND, flags: TRACKMOUSEEVENT_FLAGS) {
|
||||
if !self.state.hovered.get() {
|
||||
self.state.hovered.set(true);
|
||||
let mut lock = self.state.borrow_mut();
|
||||
if !lock.hovered {
|
||||
lock.hovered = true;
|
||||
unsafe {
|
||||
TrackMouseEvent(&mut TRACKMOUSEEVENT {
|
||||
cbSize: std::mem::size_of::<TRACKMOUSEEVENT>() as u32,
|
||||
@@ -1202,12 +1228,10 @@ impl WindowsWindowInner {
|
||||
})
|
||||
.log_err()
|
||||
};
|
||||
if let Some(mut callback) = self.state.callbacks.hovered_status_change.take() {
|
||||
if let Some(mut callback) = lock.callbacks.hovered_status_change.take() {
|
||||
drop(lock);
|
||||
callback(true);
|
||||
self.state
|
||||
.callbacks
|
||||
.hovered_status_change
|
||||
.set(Some(callback));
|
||||
self.state.borrow_mut().callbacks.hovered_status_change = Some(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1216,9 +1240,9 @@ impl WindowsWindowInner {
|
||||
where
|
||||
F: FnOnce(&mut PlatformInputHandler) -> R,
|
||||
{
|
||||
let mut input_handler = self.state.input_handler.take()?;
|
||||
let mut input_handler = self.state.borrow_mut().input_handler.take()?;
|
||||
let result = f(&mut input_handler);
|
||||
self.state.input_handler.set(Some(input_handler));
|
||||
self.state.borrow_mut().input_handler = Some(input_handler);
|
||||
Some(result)
|
||||
}
|
||||
|
||||
@@ -1226,11 +1250,12 @@ impl WindowsWindowInner {
|
||||
where
|
||||
F: FnOnce(&mut PlatformInputHandler, f32) -> Option<R>,
|
||||
{
|
||||
let mut input_handler = self.state.input_handler.take()?;
|
||||
let scale_factor = self.state.scale_factor.get();
|
||||
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let mut input_handler = lock.input_handler.take()?;
|
||||
let scale_factor = lock.scale_factor;
|
||||
drop(lock);
|
||||
let result = f(&mut input_handler, scale_factor);
|
||||
self.state.input_handler.set(Some(input_handler));
|
||||
self.state.borrow_mut().input_handler = Some(input_handler);
|
||||
result
|
||||
}
|
||||
}
|
||||
@@ -1238,7 +1263,7 @@ impl WindowsWindowInner {
|
||||
fn handle_key_event<F>(
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
state: &WindowsWindowState,
|
||||
state: &mut WindowsWindowState,
|
||||
f: F,
|
||||
) -> Option<PlatformInput>
|
||||
where
|
||||
@@ -1251,12 +1276,11 @@ where
|
||||
VK_SHIFT | VK_CONTROL | VK_MENU | VK_LMENU | VK_RMENU | VK_LWIN | VK_RWIN => {
|
||||
if state
|
||||
.last_reported_modifiers
|
||||
.get()
|
||||
.is_some_and(|prev_modifiers| prev_modifiers == modifiers)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
state.last_reported_modifiers.set(Some(modifiers));
|
||||
state.last_reported_modifiers = Some(modifiers);
|
||||
Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent {
|
||||
modifiers,
|
||||
capslock: current_capslock(),
|
||||
@@ -1267,12 +1291,11 @@ where
|
||||
let capslock = current_capslock();
|
||||
if state
|
||||
.last_reported_capslock
|
||||
.get()
|
||||
.is_some_and(|prev_capslock| prev_capslock == capslock)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
state.last_reported_capslock.set(Some(capslock));
|
||||
state.last_reported_capslock = Some(capslock);
|
||||
Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent {
|
||||
modifiers,
|
||||
capslock,
|
||||
@@ -1504,7 +1527,7 @@ fn get_client_area_insets(
|
||||
// The top inset is calculated using an empirical formula that I derived through various
|
||||
// tests. Without this, the top 1-2 rows of pixels in our window would be obscured.
|
||||
let dpi = unsafe { GetDpiForWindow(handle) };
|
||||
let frame_thickness = get_frame_thicknessx(dpi);
|
||||
let frame_thickness = get_frame_thickness(dpi);
|
||||
let top_insets = if is_maximized {
|
||||
frame_thickness
|
||||
} else {
|
||||
@@ -1525,18 +1548,12 @@ fn get_client_area_insets(
|
||||
// borders on Windows:
|
||||
// - SM_CXSIZEFRAME: The resize handle.
|
||||
// - SM_CXPADDEDBORDER: Additional border space that isn't part of the resize handle.
|
||||
fn get_frame_thicknessx(dpi: u32) -> i32 {
|
||||
fn get_frame_thickness(dpi: u32) -> i32 {
|
||||
let resize_frame_thickness = unsafe { GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) };
|
||||
let padding_thickness = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) };
|
||||
resize_frame_thickness + padding_thickness
|
||||
}
|
||||
|
||||
fn get_frame_thicknessy(dpi: u32) -> i32 {
|
||||
let resize_frame_thickness = unsafe { GetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) };
|
||||
let padding_thickness = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) };
|
||||
resize_frame_thickness + padding_thickness
|
||||
}
|
||||
|
||||
fn notify_frame_changed(handle: HWND) {
|
||||
unsafe {
|
||||
SetWindowPos(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
cell::RefCell,
|
||||
ffi::OsStr,
|
||||
path::{Path, PathBuf},
|
||||
rc::{Rc, Weak},
|
||||
@@ -47,7 +47,7 @@ pub(crate) struct WindowsPlatform {
|
||||
}
|
||||
|
||||
struct WindowsPlatformInner {
|
||||
state: WindowsPlatformState,
|
||||
state: RefCell<WindowsPlatformState>,
|
||||
raw_window_handles: std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
|
||||
// The below members will never change throughout the entire lifecycle of the app.
|
||||
validation_number: usize,
|
||||
@@ -57,22 +57,22 @@ struct WindowsPlatformInner {
|
||||
|
||||
pub(crate) struct WindowsPlatformState {
|
||||
callbacks: PlatformCallbacks,
|
||||
menus: RefCell<Vec<OwnedMenu>>,
|
||||
jump_list: RefCell<JumpList>,
|
||||
menus: Vec<OwnedMenu>,
|
||||
jump_list: JumpList,
|
||||
// NOTE: standard cursor handles don't need to close.
|
||||
pub(crate) current_cursor: Cell<Option<HCURSOR>>,
|
||||
directx_devices: RefCell<Option<DirectXDevices>>,
|
||||
pub(crate) current_cursor: Option<HCURSOR>,
|
||||
directx_devices: Option<DirectXDevices>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PlatformCallbacks {
|
||||
open_urls: Cell<Option<Box<dyn FnMut(Vec<String>)>>>,
|
||||
quit: Cell<Option<Box<dyn FnMut()>>>,
|
||||
reopen: Cell<Option<Box<dyn FnMut()>>>,
|
||||
app_menu_action: Cell<Option<Box<dyn FnMut(&dyn Action)>>>,
|
||||
will_open_app_menu: Cell<Option<Box<dyn FnMut()>>>,
|
||||
validate_app_menu_command: Cell<Option<Box<dyn FnMut(&dyn Action) -> bool>>>,
|
||||
keyboard_layout_change: Cell<Option<Box<dyn FnMut()>>>,
|
||||
open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
|
||||
quit: Option<Box<dyn FnMut()>>,
|
||||
reopen: Option<Box<dyn FnMut()>>,
|
||||
app_menu_action: Option<Box<dyn FnMut(&dyn Action)>>,
|
||||
will_open_app_menu: Option<Box<dyn FnMut()>>,
|
||||
validate_app_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
|
||||
keyboard_layout_change: Option<Box<dyn FnMut()>>,
|
||||
}
|
||||
|
||||
impl WindowsPlatformState {
|
||||
@@ -84,10 +84,10 @@ impl WindowsPlatformState {
|
||||
|
||||
Self {
|
||||
callbacks,
|
||||
jump_list: RefCell::new(jump_list),
|
||||
current_cursor: Cell::new(current_cursor),
|
||||
directx_devices: RefCell::new(directx_devices),
|
||||
menus: RefCell::new(Vec::new()),
|
||||
jump_list,
|
||||
current_cursor,
|
||||
directx_devices,
|
||||
menus: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,14 +194,14 @@ impl WindowsPlatform {
|
||||
WindowCreationInfo {
|
||||
icon: self.icon,
|
||||
executor: self.foreground_executor.clone(),
|
||||
current_cursor: self.inner.state.current_cursor.get(),
|
||||
current_cursor: self.inner.state.borrow().current_cursor,
|
||||
windows_version: self.windows_version,
|
||||
drop_target_helper: self.drop_target_helper.clone(),
|
||||
validation_number: self.inner.validation_number,
|
||||
main_receiver: self.inner.main_receiver.clone(),
|
||||
platform_window_handle: self.handle,
|
||||
disable_direct_composition: self.disable_direct_composition,
|
||||
directx_devices: self.inner.state.directx_devices.borrow().clone().unwrap(),
|
||||
directx_devices: self.inner.state.borrow().directx_devices.clone().unwrap(),
|
||||
invalidate_devices: self.invalidate_devices.clone(),
|
||||
}
|
||||
}
|
||||
@@ -213,8 +213,9 @@ impl WindowsPlatform {
|
||||
actions.push(dock_menu);
|
||||
}
|
||||
});
|
||||
self.inner.state.jump_list.borrow_mut().dock_menus = actions;
|
||||
update_jump_list(&self.inner.state.jump_list.borrow()).log_err();
|
||||
let mut lock = self.inner.state.borrow_mut();
|
||||
lock.jump_list.dock_menus = actions;
|
||||
update_jump_list(&lock.jump_list).log_err();
|
||||
}
|
||||
|
||||
fn update_jump_list(
|
||||
@@ -228,10 +229,12 @@ impl WindowsPlatform {
|
||||
actions.push(dock_menu);
|
||||
}
|
||||
});
|
||||
let mut jump_list = self.inner.state.jump_list.borrow_mut();
|
||||
jump_list.dock_menus = actions;
|
||||
jump_list.recent_workspaces = entries;
|
||||
update_jump_list(&jump_list).log_err().unwrap_or_default()
|
||||
let mut lock = self.inner.state.borrow_mut();
|
||||
lock.jump_list.dock_menus = actions;
|
||||
lock.jump_list.recent_workspaces = entries;
|
||||
update_jump_list(&lock.jump_list)
|
||||
.log_err()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn find_current_active_window(&self) -> Option<HWND> {
|
||||
@@ -247,7 +250,7 @@ impl WindowsPlatform {
|
||||
}
|
||||
|
||||
fn begin_vsync_thread(&self) {
|
||||
let mut directx_device = self.inner.state.directx_devices.borrow().clone().unwrap();
|
||||
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);
|
||||
@@ -331,9 +334,9 @@ impl Platform for WindowsPlatform {
|
||||
fn on_keyboard_layout_change(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner
|
||||
.state
|
||||
.borrow_mut()
|
||||
.callbacks
|
||||
.keyboard_layout_change
|
||||
.set(Some(callback));
|
||||
.keyboard_layout_change = Some(callback);
|
||||
}
|
||||
|
||||
fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
|
||||
@@ -351,7 +354,7 @@ impl Platform for WindowsPlatform {
|
||||
}
|
||||
|
||||
self.inner
|
||||
.with_callback(|callbacks| &callbacks.quit, |callback| callback());
|
||||
.with_callback(|callbacks| &mut callbacks.quit, |callback| callback());
|
||||
}
|
||||
|
||||
fn quit(&self) {
|
||||
@@ -470,7 +473,7 @@ impl Platform for WindowsPlatform {
|
||||
}
|
||||
|
||||
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
|
||||
self.inner.state.callbacks.open_urls.set(Some(callback));
|
||||
self.inner.state.borrow_mut().callbacks.open_urls = Some(callback);
|
||||
}
|
||||
|
||||
fn prompt_for_paths(
|
||||
@@ -540,19 +543,19 @@ impl Platform for WindowsPlatform {
|
||||
}
|
||||
|
||||
fn on_quit(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner.state.callbacks.quit.set(Some(callback));
|
||||
self.inner.state.borrow_mut().callbacks.quit = Some(callback);
|
||||
}
|
||||
|
||||
fn on_reopen(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner.state.callbacks.reopen.set(Some(callback));
|
||||
self.inner.state.borrow_mut().callbacks.reopen = Some(callback);
|
||||
}
|
||||
|
||||
fn set_menus(&self, menus: Vec<Menu>, _keymap: &Keymap) {
|
||||
*self.inner.state.menus.borrow_mut() = menus.into_iter().map(|menu| menu.owned()).collect();
|
||||
self.inner.state.borrow_mut().menus = menus.into_iter().map(|menu| menu.owned()).collect();
|
||||
}
|
||||
|
||||
fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
|
||||
Some(self.inner.state.menus.borrow().clone())
|
||||
Some(self.inner.state.borrow().menus.clone())
|
||||
}
|
||||
|
||||
fn set_dock_menu(&self, menus: Vec<MenuItem>, _keymap: &Keymap) {
|
||||
@@ -560,27 +563,19 @@ impl Platform for WindowsPlatform {
|
||||
}
|
||||
|
||||
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
|
||||
self.inner
|
||||
.state
|
||||
.callbacks
|
||||
.app_menu_action
|
||||
.set(Some(callback));
|
||||
self.inner.state.borrow_mut().callbacks.app_menu_action = Some(callback);
|
||||
}
|
||||
|
||||
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner
|
||||
.state
|
||||
.callbacks
|
||||
.will_open_app_menu
|
||||
.set(Some(callback));
|
||||
self.inner.state.borrow_mut().callbacks.will_open_app_menu = Some(callback);
|
||||
}
|
||||
|
||||
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
|
||||
self.inner
|
||||
.state
|
||||
.borrow_mut()
|
||||
.callbacks
|
||||
.validate_app_menu_command
|
||||
.set(Some(callback));
|
||||
.validate_app_menu_command = Some(callback);
|
||||
}
|
||||
|
||||
fn app_path(&self) -> Result<PathBuf> {
|
||||
@@ -594,13 +589,13 @@ impl Platform for WindowsPlatform {
|
||||
|
||||
fn set_cursor_style(&self, style: CursorStyle) {
|
||||
let hcursor = load_cursor(style);
|
||||
if self.inner.state.current_cursor.get().map(|c| c.0) != hcursor.map(|c| c.0) {
|
||||
if self.inner.state.borrow_mut().current_cursor.map(|c| c.0) != hcursor.map(|c| c.0) {
|
||||
self.post_message(
|
||||
WM_GPUI_CURSOR_STYLE_CHANGED,
|
||||
WPARAM(0),
|
||||
LPARAM(hcursor.map_or(0, |c| c.0 as isize)),
|
||||
);
|
||||
self.inner.state.current_cursor.set(hcursor);
|
||||
self.inner.state.borrow_mut().current_cursor = hcursor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,12 +721,12 @@ impl Platform for WindowsPlatform {
|
||||
|
||||
impl WindowsPlatformInner {
|
||||
fn new(context: &mut PlatformWindowCreateContext) -> Result<Rc<Self>> {
|
||||
let state = WindowsPlatformState::new(
|
||||
let state = RefCell::new(WindowsPlatformState::new(
|
||||
context
|
||||
.directx_devices
|
||||
.take()
|
||||
.context("missing directx devices")?,
|
||||
);
|
||||
));
|
||||
Ok(Rc::new(Self {
|
||||
state,
|
||||
raw_window_handles: context.raw_window_handles.clone(),
|
||||
@@ -751,13 +746,13 @@ impl WindowsPlatformInner {
|
||||
/// Calls `project` to project to the corresponding callback field, removes it from callbacks, calls `f` with the callback and then puts the callback back.
|
||||
fn with_callback<T>(
|
||||
&self,
|
||||
project: impl Fn(&PlatformCallbacks) -> &Cell<Option<T>>,
|
||||
project: impl Fn(&mut PlatformCallbacks) -> &mut Option<T>,
|
||||
f: impl FnOnce(&mut T),
|
||||
) {
|
||||
let callback = project(&self.state.callbacks).take();
|
||||
let callback = project(&mut self.state.borrow_mut().callbacks).take();
|
||||
if let Some(mut callback) = callback {
|
||||
f(&mut callback);
|
||||
project(&self.state.callbacks).set(Some(callback));
|
||||
*project(&mut self.state.borrow_mut().callbacks) = Some(callback)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,8 +877,8 @@ impl WindowsPlatformInner {
|
||||
fn handle_dock_action_event(&self, action_idx: usize) -> Option<isize> {
|
||||
let Some(action) = self
|
||||
.state
|
||||
.borrow_mut()
|
||||
.jump_list
|
||||
.borrow()
|
||||
.dock_menus
|
||||
.get(action_idx)
|
||||
.map(|dock_menu| dock_menu.action.boxed_clone())
|
||||
@@ -892,7 +887,7 @@ impl WindowsPlatformInner {
|
||||
return Some(1);
|
||||
};
|
||||
self.with_callback(
|
||||
|callbacks| &callbacks.app_menu_action,
|
||||
|callbacks| &mut callbacks.app_menu_action,
|
||||
|callback| callback(&*action),
|
||||
);
|
||||
Some(0)
|
||||
@@ -900,7 +895,7 @@ impl WindowsPlatformInner {
|
||||
|
||||
fn handle_keyboard_layout_change(&self) -> Option<isize> {
|
||||
self.with_callback(
|
||||
|callbacks| &callbacks.keyboard_layout_change,
|
||||
|callbacks| &mut callbacks.keyboard_layout_change,
|
||||
|callback| callback(),
|
||||
);
|
||||
Some(0)
|
||||
@@ -909,8 +904,9 @@ impl WindowsPlatformInner {
|
||||
fn handle_device_lost(&self, lparam: LPARAM) -> Option<isize> {
|
||||
let directx_devices = lparam.0 as *const DirectXDevices;
|
||||
let directx_devices = unsafe { &*directx_devices };
|
||||
self.state.directx_devices.borrow_mut().take();
|
||||
*self.state.directx_devices.borrow_mut() = Some(directx_devices.clone());
|
||||
let mut lock = self.state.borrow_mut();
|
||||
lock.directx_devices.take();
|
||||
lock.directx_devices = Some(directx_devices.clone());
|
||||
|
||||
Some(0)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use std::{
|
||||
cell::Cell,
|
||||
ffi::{c_uint, c_void},
|
||||
};
|
||||
use std::ffi::{c_uint, c_void};
|
||||
|
||||
use ::util::ResultExt;
|
||||
use windows::Win32::UI::{
|
||||
@@ -18,18 +15,18 @@ use super::WindowsDisplay;
|
||||
|
||||
/// Windows settings pulled from SystemParametersInfo
|
||||
/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
|
||||
#[derive(Default, Debug, Clone)]
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
pub(crate) struct WindowsSystemSettings {
|
||||
pub(crate) mouse_wheel_settings: MouseWheelSettings,
|
||||
pub(crate) auto_hide_taskbar_position: Cell<Option<AutoHideTaskbarPosition>>,
|
||||
pub(crate) auto_hide_taskbar_position: Option<AutoHideTaskbarPosition>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
pub(crate) struct MouseWheelSettings {
|
||||
/// SEE: SPI_GETWHEELSCROLLCHARS
|
||||
pub(crate) wheel_scroll_chars: Cell<u32>,
|
||||
pub(crate) wheel_scroll_chars: u32,
|
||||
/// SEE: SPI_GETWHEELSCROLLLINES
|
||||
pub(crate) wheel_scroll_lines: Cell<u32>,
|
||||
pub(crate) wheel_scroll_lines: u32,
|
||||
}
|
||||
|
||||
impl WindowsSystemSettings {
|
||||
@@ -39,13 +36,12 @@ impl WindowsSystemSettings {
|
||||
settings
|
||||
}
|
||||
|
||||
fn init(&self, display: WindowsDisplay) {
|
||||
fn init(&mut self, display: WindowsDisplay) {
|
||||
self.mouse_wheel_settings.update();
|
||||
self.auto_hide_taskbar_position
|
||||
.set(AutoHideTaskbarPosition::new(display).log_err().flatten());
|
||||
self.auto_hide_taskbar_position = AutoHideTaskbarPosition::new(display).log_err().flatten();
|
||||
}
|
||||
|
||||
pub(crate) fn update(&self, display: WindowsDisplay, wparam: usize) {
|
||||
pub(crate) fn update(&mut self, display: WindowsDisplay, wparam: usize) {
|
||||
match wparam {
|
||||
// SPI_SETWORKAREA
|
||||
47 => self.update_taskbar_position(display),
|
||||
@@ -55,23 +51,22 @@ impl WindowsSystemSettings {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_mouse_wheel_settings(&self) {
|
||||
fn update_mouse_wheel_settings(&mut self) {
|
||||
self.mouse_wheel_settings.update();
|
||||
}
|
||||
|
||||
fn update_taskbar_position(&self, display: WindowsDisplay) {
|
||||
self.auto_hide_taskbar_position
|
||||
.set(AutoHideTaskbarPosition::new(display).log_err().flatten());
|
||||
fn update_taskbar_position(&mut self, display: WindowsDisplay) {
|
||||
self.auto_hide_taskbar_position = AutoHideTaskbarPosition::new(display).log_err().flatten();
|
||||
}
|
||||
}
|
||||
|
||||
impl MouseWheelSettings {
|
||||
fn update(&self) {
|
||||
fn update(&mut self) {
|
||||
self.update_wheel_scroll_chars();
|
||||
self.update_wheel_scroll_lines();
|
||||
}
|
||||
|
||||
fn update_wheel_scroll_chars(&self) {
|
||||
fn update_wheel_scroll_chars(&mut self) {
|
||||
let mut value = c_uint::default();
|
||||
let result = unsafe {
|
||||
SystemParametersInfoW(
|
||||
@@ -82,12 +77,12 @@ impl MouseWheelSettings {
|
||||
)
|
||||
};
|
||||
|
||||
if result.log_err() != None && self.wheel_scroll_chars.get() != value {
|
||||
self.wheel_scroll_chars.set(value);
|
||||
if result.log_err() != None && self.wheel_scroll_chars != value {
|
||||
self.wheel_scroll_chars = value;
|
||||
}
|
||||
}
|
||||
|
||||
fn update_wheel_scroll_lines(&self) {
|
||||
fn update_wheel_scroll_lines(&mut self) {
|
||||
let mut value = c_uint::default();
|
||||
let result = unsafe {
|
||||
SystemParametersInfoW(
|
||||
@@ -98,8 +93,8 @@ impl MouseWheelSettings {
|
||||
)
|
||||
};
|
||||
|
||||
if result.log_err() != None && self.wheel_scroll_lines.get() != value {
|
||||
self.wheel_scroll_lines.set(value);
|
||||
if result.log_err() != None && self.wheel_scroll_lines != value {
|
||||
self.wheel_scroll_lines = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
cell::RefCell,
|
||||
num::NonZeroIsize,
|
||||
path::PathBuf,
|
||||
rc::{Rc, Weak},
|
||||
@@ -30,51 +30,43 @@ use crate::*;
|
||||
|
||||
pub(crate) struct WindowsWindow(pub Rc<WindowsWindowInner>);
|
||||
|
||||
impl std::ops::Deref for WindowsWindow {
|
||||
type Target = WindowsWindowInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WindowsWindowState {
|
||||
pub origin: Cell<Point<Pixels>>,
|
||||
pub logical_size: Cell<Size<Pixels>>,
|
||||
pub origin: Point<Pixels>,
|
||||
pub logical_size: Size<Pixels>,
|
||||
pub min_size: Option<Size<Pixels>>,
|
||||
pub fullscreen_restore_bounds: Cell<Bounds<Pixels>>,
|
||||
pub fullscreen_restore_bounds: Bounds<Pixels>,
|
||||
pub border_offset: WindowBorderOffset,
|
||||
pub appearance: Cell<WindowAppearance>,
|
||||
pub scale_factor: Cell<f32>,
|
||||
pub restore_from_minimized: Cell<Option<Box<dyn FnMut(RequestFrameOptions)>>>,
|
||||
pub appearance: WindowAppearance,
|
||||
pub scale_factor: f32,
|
||||
pub restore_from_minimized: Option<Box<dyn FnMut(RequestFrameOptions)>>,
|
||||
|
||||
pub callbacks: Callbacks,
|
||||
pub input_handler: Cell<Option<PlatformInputHandler>>,
|
||||
pub pending_surrogate: Cell<Option<u16>>,
|
||||
pub last_reported_modifiers: Cell<Option<Modifiers>>,
|
||||
pub last_reported_capslock: Cell<Option<Capslock>>,
|
||||
pub hovered: Cell<bool>,
|
||||
pub input_handler: Option<PlatformInputHandler>,
|
||||
pub pending_surrogate: Option<u16>,
|
||||
pub last_reported_modifiers: Option<Modifiers>,
|
||||
pub last_reported_capslock: Option<Capslock>,
|
||||
pub hovered: bool,
|
||||
|
||||
pub renderer: RefCell<DirectXRenderer>,
|
||||
pub renderer: DirectXRenderer,
|
||||
|
||||
pub click_state: ClickState,
|
||||
pub current_cursor: Cell<Option<HCURSOR>>,
|
||||
pub nc_button_pressed: Cell<Option<u32>>,
|
||||
pub current_cursor: Option<HCURSOR>,
|
||||
pub nc_button_pressed: Option<u32>,
|
||||
|
||||
pub display: Cell<WindowsDisplay>,
|
||||
pub display: WindowsDisplay,
|
||||
/// Flag to instruct the `VSyncProvider` thread to invalidate the directx devices
|
||||
/// as resizing them has failed, causing us to have lost at least the render target.
|
||||
pub invalidate_devices: Arc<AtomicBool>,
|
||||
fullscreen: Cell<Option<StyleAndBounds>>,
|
||||
initial_placement: Cell<Option<WindowOpenStatus>>,
|
||||
fullscreen: Option<StyleAndBounds>,
|
||||
initial_placement: Option<WindowOpenStatus>,
|
||||
hwnd: HWND,
|
||||
}
|
||||
|
||||
pub(crate) struct WindowsWindowInner {
|
||||
hwnd: HWND,
|
||||
drop_target_helper: IDropTargetHelper,
|
||||
pub(crate) state: WindowsWindowState,
|
||||
system_settings: WindowsSystemSettings,
|
||||
pub(crate) state: RefCell<WindowsWindowState>,
|
||||
system_settings: RefCell<WindowsSystemSettings>,
|
||||
pub(crate) handle: AnyWindowHandle,
|
||||
pub(crate) hide_title_bar: bool,
|
||||
pub(crate) is_movable: bool,
|
||||
@@ -129,27 +121,27 @@ impl WindowsWindowState {
|
||||
let initial_placement = None;
|
||||
|
||||
Ok(Self {
|
||||
origin: Cell::new(origin),
|
||||
logical_size: Cell::new(logical_size),
|
||||
fullscreen_restore_bounds: Cell::new(fullscreen_restore_bounds),
|
||||
origin,
|
||||
logical_size,
|
||||
fullscreen_restore_bounds,
|
||||
border_offset,
|
||||
appearance: Cell::new(appearance),
|
||||
scale_factor: Cell::new(scale_factor),
|
||||
restore_from_minimized: Cell::new(restore_from_minimized),
|
||||
appearance,
|
||||
scale_factor,
|
||||
restore_from_minimized,
|
||||
min_size,
|
||||
callbacks,
|
||||
input_handler: Cell::new(input_handler),
|
||||
pending_surrogate: Cell::new(pending_surrogate),
|
||||
last_reported_modifiers: Cell::new(last_reported_modifiers),
|
||||
last_reported_capslock: Cell::new(last_reported_capslock),
|
||||
hovered: Cell::new(hovered),
|
||||
renderer: RefCell::new(renderer),
|
||||
input_handler,
|
||||
pending_surrogate,
|
||||
last_reported_modifiers,
|
||||
last_reported_capslock,
|
||||
hovered,
|
||||
renderer,
|
||||
click_state,
|
||||
current_cursor: Cell::new(current_cursor),
|
||||
nc_button_pressed: Cell::new(nc_button_pressed),
|
||||
display: Cell::new(display),
|
||||
fullscreen: Cell::new(fullscreen),
|
||||
initial_placement: Cell::new(initial_placement),
|
||||
current_cursor,
|
||||
nc_button_pressed,
|
||||
display,
|
||||
fullscreen,
|
||||
initial_placement,
|
||||
hwnd,
|
||||
invalidate_devices,
|
||||
})
|
||||
@@ -157,7 +149,7 @@ impl WindowsWindowState {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_fullscreen(&self) -> bool {
|
||||
self.fullscreen.get().is_some()
|
||||
self.fullscreen.is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn is_maximized(&self) -> bool {
|
||||
@@ -166,8 +158,8 @@ impl WindowsWindowState {
|
||||
|
||||
fn bounds(&self) -> Bounds<Pixels> {
|
||||
Bounds {
|
||||
origin: self.origin.get(),
|
||||
size: self.logical_size.get(),
|
||||
origin: self.origin,
|
||||
size: self.logical_size,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,8 +178,8 @@ impl WindowsWindowState {
|
||||
(
|
||||
calculate_client_rect(
|
||||
placement.rcNormalPosition,
|
||||
&self.border_offset,
|
||||
self.scale_factor.get(),
|
||||
self.border_offset,
|
||||
self.scale_factor,
|
||||
),
|
||||
placement.showCmd == SW_SHOWMAXIMIZED.0 as u32,
|
||||
)
|
||||
@@ -197,7 +189,7 @@ impl WindowsWindowState {
|
||||
let (bounds, maximized) = self.calculate_window_bounds();
|
||||
|
||||
if self.is_fullscreen() {
|
||||
WindowBounds::Fullscreen(self.fullscreen_restore_bounds.get())
|
||||
WindowBounds::Fullscreen(self.fullscreen_restore_bounds)
|
||||
} else if maximized {
|
||||
WindowBounds::Maximized(bounds)
|
||||
} else {
|
||||
@@ -210,13 +202,13 @@ impl WindowsWindowState {
|
||||
/// Currently, GPUI uses the logical size of the app to handle mouse interactions (such as
|
||||
/// whether the mouse collides with other elements of GPUI).
|
||||
fn content_size(&self) -> Size<Pixels> {
|
||||
self.logical_size.get()
|
||||
self.logical_size
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowsWindowInner {
|
||||
fn new(context: &mut WindowCreateContext, hwnd: HWND, cs: &CREATESTRUCTW) -> Result<Rc<Self>> {
|
||||
let state = WindowsWindowState::new(
|
||||
let state = RefCell::new(WindowsWindowState::new(
|
||||
hwnd,
|
||||
&context.directx_devices,
|
||||
cs,
|
||||
@@ -226,7 +218,7 @@ impl WindowsWindowInner {
|
||||
context.appearance,
|
||||
context.disable_direct_composition,
|
||||
context.invalidate_devices.clone(),
|
||||
)?;
|
||||
)?);
|
||||
|
||||
Ok(Rc::new(Self {
|
||||
hwnd,
|
||||
@@ -240,7 +232,7 @@ impl WindowsWindowInner {
|
||||
validation_number: context.validation_number,
|
||||
main_receiver: context.main_receiver.clone(),
|
||||
platform_window_handle: context.platform_window_handle,
|
||||
system_settings: WindowsSystemSettings::new(context.display),
|
||||
system_settings: RefCell::new(WindowsSystemSettings::new(context.display)),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -248,17 +240,19 @@ impl WindowsWindowInner {
|
||||
let this = self.clone();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
let mut lock = this.state.borrow_mut();
|
||||
let StyleAndBounds {
|
||||
style,
|
||||
x,
|
||||
y,
|
||||
cx,
|
||||
cy,
|
||||
} = match this.state.fullscreen.take() {
|
||||
} = match lock.fullscreen.take() {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
let (window_bounds, _) = this.state.calculate_window_bounds();
|
||||
this.state.fullscreen_restore_bounds.set(window_bounds);
|
||||
let (window_bounds, _) = lock.calculate_window_bounds();
|
||||
lock.fullscreen_restore_bounds = window_bounds;
|
||||
drop(lock);
|
||||
|
||||
let style =
|
||||
WINDOW_STYLE(unsafe { get_window_long(this.hwnd, GWL_STYLE) } as _);
|
||||
@@ -266,20 +260,22 @@ impl WindowsWindowInner {
|
||||
unsafe { GetWindowRect(this.hwnd, &mut rc) }
|
||||
.context("failed to get window rect")
|
||||
.log_err();
|
||||
let _ = this.state.fullscreen.set(Some(StyleAndBounds {
|
||||
|
||||
lock = this.state.borrow_mut();
|
||||
let _ = lock.fullscreen.insert(StyleAndBounds {
|
||||
style,
|
||||
x: rc.left,
|
||||
y: rc.top,
|
||||
cx: rc.right - rc.left,
|
||||
cy: rc.bottom - rc.top,
|
||||
}));
|
||||
});
|
||||
let style = style
|
||||
& !(WS_THICKFRAME
|
||||
| WS_SYSMENU
|
||||
| WS_MAXIMIZEBOX
|
||||
| WS_MINIMIZEBOX
|
||||
| WS_CAPTION);
|
||||
let physical_bounds = this.state.display.get().physical_bounds();
|
||||
let physical_bounds = lock.display.physical_bounds();
|
||||
StyleAndBounds {
|
||||
style,
|
||||
x: physical_bounds.left().0,
|
||||
@@ -289,6 +285,7 @@ impl WindowsWindowInner {
|
||||
}
|
||||
}
|
||||
};
|
||||
drop(lock);
|
||||
unsafe { set_window_long(this.hwnd, GWL_STYLE, style.0 as isize) };
|
||||
unsafe {
|
||||
SetWindowPos(
|
||||
@@ -307,7 +304,7 @@ impl WindowsWindowInner {
|
||||
}
|
||||
|
||||
fn set_window_placement(self: &Rc<Self>) -> Result<()> {
|
||||
let Some(open_status) = self.state.initial_placement.take() else {
|
||||
let Some(open_status) = self.state.borrow_mut().initial_placement.take() else {
|
||||
return Ok(());
|
||||
};
|
||||
match open_status.state {
|
||||
@@ -331,23 +328,27 @@ impl WindowsWindowInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn system_settings(&self) -> &WindowsSystemSettings {
|
||||
&self.system_settings
|
||||
pub(crate) fn system_settings(&self) -> std::cell::Ref<'_, WindowsSystemSettings> {
|
||||
self.system_settings.borrow()
|
||||
}
|
||||
|
||||
pub(crate) fn system_settings_mut(&self) -> std::cell::RefMut<'_, WindowsSystemSettings> {
|
||||
self.system_settings.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Callbacks {
|
||||
pub(crate) request_frame: Cell<Option<Box<dyn FnMut(RequestFrameOptions)>>>,
|
||||
pub(crate) input: Cell<Option<Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>>>,
|
||||
pub(crate) active_status_change: Cell<Option<Box<dyn FnMut(bool)>>>,
|
||||
pub(crate) hovered_status_change: Cell<Option<Box<dyn FnMut(bool)>>>,
|
||||
pub(crate) resize: Cell<Option<Box<dyn FnMut(Size<Pixels>, f32)>>>,
|
||||
pub(crate) moved: Cell<Option<Box<dyn FnMut()>>>,
|
||||
pub(crate) should_close: Cell<Option<Box<dyn FnMut() -> bool>>>,
|
||||
pub(crate) close: Cell<Option<Box<dyn FnOnce()>>>,
|
||||
pub(crate) hit_test_window_control: Cell<Option<Box<dyn FnMut() -> Option<WindowControlArea>>>>,
|
||||
pub(crate) appearance_changed: Cell<Option<Box<dyn FnMut()>>>,
|
||||
pub(crate) request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,
|
||||
pub(crate) input: Option<Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>>,
|
||||
pub(crate) active_status_change: Option<Box<dyn FnMut(bool)>>,
|
||||
pub(crate) hovered_status_change: Option<Box<dyn FnMut(bool)>>,
|
||||
pub(crate) resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
|
||||
pub(crate) moved: Option<Box<dyn FnMut()>>,
|
||||
pub(crate) should_close: Option<Box<dyn FnMut() -> bool>>,
|
||||
pub(crate) close: Option<Box<dyn FnOnce()>>,
|
||||
pub(crate) hit_test_window_control: Option<Box<dyn FnMut() -> Option<WindowControlArea>>>,
|
||||
pub(crate) appearance_changed: Option<Box<dyn FnMut()>>,
|
||||
}
|
||||
|
||||
struct WindowCreateContext {
|
||||
@@ -475,21 +476,21 @@ impl WindowsWindow {
|
||||
|
||||
register_drag_drop(&this)?;
|
||||
configure_dwm_dark_mode(hwnd, appearance);
|
||||
this.state.border_offset.update(hwnd)?;
|
||||
this.state.borrow_mut().border_offset.update(hwnd)?;
|
||||
let placement = retrieve_window_placement(
|
||||
hwnd,
|
||||
display,
|
||||
params.bounds,
|
||||
this.state.scale_factor.get(),
|
||||
&this.state.border_offset,
|
||||
this.state.borrow().scale_factor,
|
||||
this.state.borrow().border_offset,
|
||||
)?;
|
||||
if params.show {
|
||||
unsafe { SetWindowPlacement(hwnd, &placement)? };
|
||||
} else {
|
||||
this.state.initial_placement.set(Some(WindowOpenStatus {
|
||||
this.state.borrow_mut().initial_placement = Some(WindowOpenStatus {
|
||||
placement,
|
||||
state: WindowOpenState::Windowed,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Self(this))
|
||||
@@ -532,15 +533,15 @@ impl Drop for WindowsWindow {
|
||||
|
||||
impl PlatformWindow for WindowsWindow {
|
||||
fn bounds(&self) -> Bounds<Pixels> {
|
||||
self.state.bounds()
|
||||
self.0.state.borrow().bounds()
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
self.state.is_maximized()
|
||||
self.0.state.borrow().is_maximized()
|
||||
}
|
||||
|
||||
fn window_bounds(&self) -> WindowBounds {
|
||||
self.state.window_bounds()
|
||||
self.0.state.borrow().window_bounds()
|
||||
}
|
||||
|
||||
/// get the logical size of the app's drawable area.
|
||||
@@ -548,14 +549,14 @@ impl PlatformWindow for WindowsWindow {
|
||||
/// Currently, GPUI uses the logical size of the app to handle mouse interactions (such as
|
||||
/// whether the mouse collides with other elements of GPUI).
|
||||
fn content_size(&self) -> Size<Pixels> {
|
||||
self.state.content_size()
|
||||
self.0.state.borrow().content_size()
|
||||
}
|
||||
|
||||
fn resize(&mut self, size: Size<Pixels>) {
|
||||
let hwnd = self.0.hwnd;
|
||||
let bounds =
|
||||
crate::bounds(self.bounds().origin, size).to_device_pixels(self.scale_factor());
|
||||
let rect = calculate_window_rect(bounds, &self.state.border_offset);
|
||||
let rect = calculate_window_rect(bounds, self.0.state.borrow().border_offset);
|
||||
|
||||
self.0
|
||||
.executor
|
||||
@@ -578,15 +579,15 @@ impl PlatformWindow for WindowsWindow {
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f32 {
|
||||
self.state.scale_factor.get()
|
||||
self.0.state.borrow().scale_factor
|
||||
}
|
||||
|
||||
fn appearance(&self) -> WindowAppearance {
|
||||
self.state.appearance.get()
|
||||
self.0.state.borrow().appearance
|
||||
}
|
||||
|
||||
fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
||||
Some(Rc::new(self.state.display.get()))
|
||||
Some(Rc::new(self.0.state.borrow().display))
|
||||
}
|
||||
|
||||
fn mouse_position(&self) -> Point<Pixels> {
|
||||
@@ -611,11 +612,11 @@ impl PlatformWindow for WindowsWindow {
|
||||
}
|
||||
|
||||
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
|
||||
self.state.input_handler.set(Some(input_handler));
|
||||
self.0.state.borrow_mut().input_handler = Some(input_handler);
|
||||
}
|
||||
|
||||
fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
|
||||
self.state.input_handler.take()
|
||||
self.0.state.borrow_mut().input_handler.take()
|
||||
}
|
||||
|
||||
fn prompt(
|
||||
@@ -761,7 +762,7 @@ impl PlatformWindow for WindowsWindow {
|
||||
}
|
||||
|
||||
fn is_hovered(&self) -> bool {
|
||||
self.state.hovered.get()
|
||||
self.0.state.borrow().hovered
|
||||
}
|
||||
|
||||
fn set_title(&mut self, title: &str) {
|
||||
@@ -804,9 +805,8 @@ impl PlatformWindow for WindowsWindow {
|
||||
unsafe {
|
||||
if IsWindowVisible(self.0.hwnd).as_bool() {
|
||||
ShowWindowAsync(self.0.hwnd, SW_MAXIMIZE).ok().log_err();
|
||||
} else if let Some(mut status) = self.state.initial_placement.take() {
|
||||
} else if let Some(status) = self.0.state.borrow_mut().initial_placement.as_mut() {
|
||||
status.state = WindowOpenState::Maximized;
|
||||
self.state.initial_placement.set(Some(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -814,78 +814,61 @@ impl PlatformWindow for WindowsWindow {
|
||||
fn toggle_fullscreen(&self) {
|
||||
if unsafe { IsWindowVisible(self.0.hwnd).as_bool() } {
|
||||
self.0.toggle_fullscreen();
|
||||
} else if let Some(mut status) = self.state.initial_placement.take() {
|
||||
} else if let Some(status) = self.0.state.borrow_mut().initial_placement.as_mut() {
|
||||
status.state = WindowOpenState::Fullscreen;
|
||||
self.state.initial_placement.set(Some(status));
|
||||
}
|
||||
}
|
||||
|
||||
fn is_fullscreen(&self) -> bool {
|
||||
self.state.is_fullscreen()
|
||||
self.0.state.borrow().is_fullscreen()
|
||||
}
|
||||
|
||||
fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>) {
|
||||
self.state.callbacks.request_frame.set(Some(callback));
|
||||
self.0.state.borrow_mut().callbacks.request_frame = Some(callback);
|
||||
}
|
||||
|
||||
fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> DispatchEventResult>) {
|
||||
self.state.callbacks.input.set(Some(callback));
|
||||
self.0.state.borrow_mut().callbacks.input = Some(callback);
|
||||
}
|
||||
|
||||
fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
|
||||
self.0
|
||||
.state
|
||||
.callbacks
|
||||
.active_status_change
|
||||
.set(Some(callback));
|
||||
self.0.state.borrow_mut().callbacks.active_status_change = Some(callback);
|
||||
}
|
||||
|
||||
fn on_hover_status_change(&self, callback: Box<dyn FnMut(bool)>) {
|
||||
self.0
|
||||
.state
|
||||
.callbacks
|
||||
.hovered_status_change
|
||||
.set(Some(callback));
|
||||
self.0.state.borrow_mut().callbacks.hovered_status_change = Some(callback);
|
||||
}
|
||||
|
||||
fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
|
||||
self.state.callbacks.resize.set(Some(callback));
|
||||
self.0.state.borrow_mut().callbacks.resize = Some(callback);
|
||||
}
|
||||
|
||||
fn on_moved(&self, callback: Box<dyn FnMut()>) {
|
||||
self.state.callbacks.moved.set(Some(callback));
|
||||
self.0.state.borrow_mut().callbacks.moved = Some(callback);
|
||||
}
|
||||
|
||||
fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
|
||||
self.state.callbacks.should_close.set(Some(callback));
|
||||
self.0.state.borrow_mut().callbacks.should_close = Some(callback);
|
||||
}
|
||||
|
||||
fn on_close(&self, callback: Box<dyn FnOnce()>) {
|
||||
self.state.callbacks.close.set(Some(callback));
|
||||
self.0.state.borrow_mut().callbacks.close = Some(callback);
|
||||
}
|
||||
|
||||
fn on_hit_test_window_control(&self, callback: Box<dyn FnMut() -> Option<WindowControlArea>>) {
|
||||
self.0
|
||||
.state
|
||||
.callbacks
|
||||
.hit_test_window_control
|
||||
.set(Some(callback));
|
||||
self.0.state.borrow_mut().callbacks.hit_test_window_control = Some(callback);
|
||||
}
|
||||
|
||||
fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
|
||||
self.0
|
||||
.state
|
||||
.callbacks
|
||||
.appearance_changed
|
||||
.set(Some(callback));
|
||||
self.0.state.borrow_mut().callbacks.appearance_changed = Some(callback);
|
||||
}
|
||||
|
||||
fn draw(&self, scene: &Scene) {
|
||||
self.state.renderer.borrow_mut().draw(scene).log_err();
|
||||
self.0.state.borrow_mut().renderer.draw(scene).log_err();
|
||||
}
|
||||
|
||||
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
|
||||
self.state.renderer.borrow().sprite_atlas()
|
||||
self.0.state.borrow().renderer.sprite_atlas()
|
||||
}
|
||||
|
||||
fn get_raw_handle(&self) -> HWND {
|
||||
@@ -893,7 +876,7 @@ impl PlatformWindow for WindowsWindow {
|
||||
}
|
||||
|
||||
fn gpu_specs(&self) -> Option<GpuSpecs> {
|
||||
self.state.renderer.borrow().gpu_specs().log_err()
|
||||
self.0.state.borrow().renderer.gpu_specs().log_err()
|
||||
}
|
||||
|
||||
fn update_ime_position(&self, _bounds: Bounds<Pixels>) {
|
||||
@@ -906,9 +889,11 @@ struct WindowsDragDropHandler(pub Rc<WindowsWindowInner>);
|
||||
|
||||
impl WindowsDragDropHandler {
|
||||
fn handle_drag_drop(&self, input: PlatformInput) {
|
||||
if let Some(mut func) = self.0.state.callbacks.input.take() {
|
||||
let mut lock = self.0.state.borrow_mut();
|
||||
if let Some(mut func) = lock.callbacks.input.take() {
|
||||
drop(lock);
|
||||
func(input);
|
||||
self.0.state.callbacks.input.set(Some(func));
|
||||
self.0.state.borrow_mut().callbacks.input = Some(func);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -952,7 +937,7 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl {
|
||||
ScreenToClient(self.0.hwnd, &mut cursor_position)
|
||||
.ok()
|
||||
.log_err();
|
||||
let scale_factor = self.0.state.scale_factor.get();
|
||||
let scale_factor = self.0.state.borrow().scale_factor;
|
||||
let input = PlatformInput::FileDrop(FileDropEvent::Entered {
|
||||
position: logical_point(
|
||||
cursor_position.x as f32,
|
||||
@@ -990,7 +975,7 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl {
|
||||
.ok()
|
||||
.log_err();
|
||||
}
|
||||
let scale_factor = self.0.state.scale_factor.get();
|
||||
let scale_factor = self.0.state.borrow().scale_factor;
|
||||
let input = PlatformInput::FileDrop(FileDropEvent::Pending {
|
||||
position: logical_point(
|
||||
cursor_position.x as f32,
|
||||
@@ -1032,7 +1017,7 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl {
|
||||
.ok()
|
||||
.log_err();
|
||||
}
|
||||
let scale_factor = self.0.state.scale_factor.get();
|
||||
let scale_factor = self.0.state.borrow().scale_factor;
|
||||
let input = PlatformInput::FileDrop(FileDropEvent::Submit {
|
||||
position: logical_point(
|
||||
cursor_position.x as f32,
|
||||
@@ -1046,15 +1031,15 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct ClickState {
|
||||
button: Cell<MouseButton>,
|
||||
last_click: Cell<Instant>,
|
||||
last_position: Cell<Point<DevicePixels>>,
|
||||
double_click_spatial_tolerance_width: Cell<i32>,
|
||||
double_click_spatial_tolerance_height: Cell<i32>,
|
||||
double_click_interval: Cell<Duration>,
|
||||
pub(crate) current_count: Cell<usize>,
|
||||
button: MouseButton,
|
||||
last_click: Instant,
|
||||
last_position: Point<DevicePixels>,
|
||||
double_click_spatial_tolerance_width: i32,
|
||||
double_click_spatial_tolerance_height: i32,
|
||||
double_click_interval: Duration,
|
||||
pub(crate) current_count: usize,
|
||||
}
|
||||
|
||||
impl ClickState {
|
||||
@@ -1064,59 +1049,61 @@ impl ClickState {
|
||||
let double_click_interval = Duration::from_millis(unsafe { GetDoubleClickTime() } as u64);
|
||||
|
||||
ClickState {
|
||||
button: Cell::new(MouseButton::Left),
|
||||
last_click: Cell::new(Instant::now()),
|
||||
last_position: Cell::new(Point::default()),
|
||||
double_click_spatial_tolerance_width: Cell::new(double_click_spatial_tolerance_width),
|
||||
double_click_spatial_tolerance_height: Cell::new(double_click_spatial_tolerance_height),
|
||||
double_click_interval: Cell::new(double_click_interval),
|
||||
current_count: Cell::new(0),
|
||||
button: MouseButton::Left,
|
||||
last_click: Instant::now(),
|
||||
last_position: Point::default(),
|
||||
double_click_spatial_tolerance_width,
|
||||
double_click_spatial_tolerance_height,
|
||||
double_click_interval,
|
||||
current_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// update self and return the needed click count
|
||||
pub fn update(&self, button: MouseButton, new_position: Point<DevicePixels>) -> usize {
|
||||
if self.button.get() == button && self.is_double_click(new_position) {
|
||||
self.current_count.update(|it| it + 1);
|
||||
pub fn update(&mut self, button: MouseButton, new_position: Point<DevicePixels>) -> usize {
|
||||
if self.button == button && self.is_double_click(new_position) {
|
||||
self.current_count += 1;
|
||||
} else {
|
||||
self.current_count.set(1);
|
||||
self.current_count = 1;
|
||||
}
|
||||
self.last_click.set(Instant::now());
|
||||
self.last_position.set(new_position);
|
||||
self.button.set(button);
|
||||
self.last_click = Instant::now();
|
||||
self.last_position = new_position;
|
||||
self.button = button;
|
||||
|
||||
self.current_count.get()
|
||||
self.current_count
|
||||
}
|
||||
|
||||
pub fn system_update(&self, wparam: usize) {
|
||||
pub fn system_update(&mut self, wparam: usize) {
|
||||
match wparam {
|
||||
// SPI_SETDOUBLECLKWIDTH
|
||||
29 => self
|
||||
.double_click_spatial_tolerance_width
|
||||
.set(unsafe { GetSystemMetrics(SM_CXDOUBLECLK) }),
|
||||
29 => {
|
||||
self.double_click_spatial_tolerance_width =
|
||||
unsafe { GetSystemMetrics(SM_CXDOUBLECLK) }
|
||||
}
|
||||
// SPI_SETDOUBLECLKHEIGHT
|
||||
30 => self
|
||||
.double_click_spatial_tolerance_height
|
||||
.set(unsafe { GetSystemMetrics(SM_CYDOUBLECLK) }),
|
||||
30 => {
|
||||
self.double_click_spatial_tolerance_height =
|
||||
unsafe { GetSystemMetrics(SM_CYDOUBLECLK) }
|
||||
}
|
||||
// SPI_SETDOUBLECLICKTIME
|
||||
32 => self
|
||||
.double_click_interval
|
||||
.set(Duration::from_millis(unsafe { GetDoubleClickTime() } as u64)),
|
||||
32 => {
|
||||
self.double_click_interval =
|
||||
Duration::from_millis(unsafe { GetDoubleClickTime() } as u64)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_double_click(&self, new_position: Point<DevicePixels>) -> bool {
|
||||
let diff = self.last_position.get() - new_position;
|
||||
let diff = self.last_position - new_position;
|
||||
|
||||
self.last_click.get().elapsed() < self.double_click_interval.get()
|
||||
&& diff.x.0.abs() <= self.double_click_spatial_tolerance_width.get()
|
||||
&& diff.y.0.abs() <= self.double_click_spatial_tolerance_height.get()
|
||||
self.last_click.elapsed() < self.double_click_interval
|
||||
&& diff.x.0.abs() <= self.double_click_spatial_tolerance_width
|
||||
&& diff.y.0.abs() <= self.double_click_spatial_tolerance_height
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct StyleAndBounds {
|
||||
style: WINDOW_STYLE,
|
||||
x: i32,
|
||||
@@ -1142,14 +1129,14 @@ struct AccentPolicy {
|
||||
|
||||
type Color = (u8, u8, u8, u8);
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub(crate) struct WindowBorderOffset {
|
||||
pub(crate) width_offset: Cell<i32>,
|
||||
pub(crate) height_offset: Cell<i32>,
|
||||
pub(crate) width_offset: i32,
|
||||
pub(crate) height_offset: i32,
|
||||
}
|
||||
|
||||
impl WindowBorderOffset {
|
||||
pub(crate) fn update(&self, hwnd: HWND) -> anyhow::Result<()> {
|
||||
pub(crate) fn update(&mut self, hwnd: HWND) -> anyhow::Result<()> {
|
||||
let window_rect = unsafe {
|
||||
let mut rect = std::mem::zeroed();
|
||||
GetWindowRect(hwnd, &mut rect)?;
|
||||
@@ -1160,21 +1147,19 @@ impl WindowBorderOffset {
|
||||
GetClientRect(hwnd, &mut rect)?;
|
||||
rect
|
||||
};
|
||||
self.width_offset
|
||||
.set((window_rect.right - window_rect.left) - (client_rect.right - client_rect.left));
|
||||
self.height_offset
|
||||
.set((window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top));
|
||||
self.width_offset =
|
||||
(window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
|
||||
self.height_offset =
|
||||
(window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct WindowOpenStatus {
|
||||
placement: WINDOWPLACEMENT,
|
||||
state: WindowOpenState,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum WindowOpenState {
|
||||
Maximized,
|
||||
Fullscreen,
|
||||
@@ -1284,7 +1269,7 @@ fn register_drag_drop(window: &Rc<WindowsWindowInner>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn calculate_window_rect(bounds: Bounds<DevicePixels>, border_offset: &WindowBorderOffset) -> RECT {
|
||||
fn calculate_window_rect(bounds: Bounds<DevicePixels>, border_offset: WindowBorderOffset) -> RECT {
|
||||
// NOTE:
|
||||
// The reason we're not using `AdjustWindowRectEx()` here is
|
||||
// that the size reported by this function is incorrect.
|
||||
@@ -1298,10 +1283,10 @@ fn calculate_window_rect(bounds: Bounds<DevicePixels>, border_offset: &WindowBor
|
||||
right: bounds.right().0,
|
||||
bottom: bounds.bottom().0,
|
||||
};
|
||||
let left_offset = border_offset.width_offset.get() / 2;
|
||||
let top_offset = border_offset.height_offset.get() / 2;
|
||||
let right_offset = border_offset.width_offset.get() - left_offset;
|
||||
let bottom_offset = border_offset.height_offset.get() - top_offset;
|
||||
let left_offset = border_offset.width_offset / 2;
|
||||
let top_offset = border_offset.height_offset / 2;
|
||||
let right_offset = border_offset.width_offset - left_offset;
|
||||
let bottom_offset = border_offset.height_offset - top_offset;
|
||||
rect.left -= left_offset;
|
||||
rect.top -= top_offset;
|
||||
rect.right += right_offset;
|
||||
@@ -1311,13 +1296,13 @@ fn calculate_window_rect(bounds: Bounds<DevicePixels>, border_offset: &WindowBor
|
||||
|
||||
fn calculate_client_rect(
|
||||
rect: RECT,
|
||||
border_offset: &WindowBorderOffset,
|
||||
border_offset: WindowBorderOffset,
|
||||
scale_factor: f32,
|
||||
) -> Bounds<Pixels> {
|
||||
let left_offset = border_offset.width_offset.get() / 2;
|
||||
let top_offset = border_offset.height_offset.get() / 2;
|
||||
let right_offset = border_offset.width_offset.get() - left_offset;
|
||||
let bottom_offset = border_offset.height_offset.get() - top_offset;
|
||||
let left_offset = border_offset.width_offset / 2;
|
||||
let top_offset = border_offset.height_offset / 2;
|
||||
let right_offset = border_offset.width_offset - left_offset;
|
||||
let bottom_offset = border_offset.height_offset - top_offset;
|
||||
let left = rect.left + left_offset;
|
||||
let top = rect.top + top_offset;
|
||||
let right = rect.right - right_offset;
|
||||
@@ -1334,7 +1319,7 @@ fn retrieve_window_placement(
|
||||
display: WindowsDisplay,
|
||||
initial_bounds: Bounds<Pixels>,
|
||||
scale_factor: f32,
|
||||
border_offset: &WindowBorderOffset,
|
||||
border_offset: WindowBorderOffset,
|
||||
) -> Result<WINDOWPLACEMENT> {
|
||||
let mut placement = WINDOWPLACEMENT {
|
||||
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,
|
||||
@@ -1444,9 +1429,7 @@ mod tests {
|
||||
state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(0))),
|
||||
2
|
||||
);
|
||||
state
|
||||
.last_click
|
||||
.update(|it| it - Duration::from_millis(700));
|
||||
state.last_click -= Duration::from_millis(700);
|
||||
assert_eq!(
|
||||
state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(0))),
|
||||
1
|
||||
|
||||
@@ -596,7 +596,7 @@ pub enum HitboxBehavior {
|
||||
/// ```
|
||||
///
|
||||
/// This has effects beyond event handling - any use of hitbox checking, such as hover
|
||||
/// styles and tooltips. These other behaviors are the main point of this mechanism. An
|
||||
/// styles and tooltops. These other behaviors are the main point of this mechanism. An
|
||||
/// alternative might be to not affect mouse event handling - but this would allow
|
||||
/// inconsistent UI where clicks and moves interact with elements that are not considered to
|
||||
/// be hovered.
|
||||
@@ -624,7 +624,7 @@ pub enum HitboxBehavior {
|
||||
/// desired, then a `cx.stop_propagation()` handler like the one above can be used.
|
||||
///
|
||||
/// This has effects beyond event handling - this affects any use of `is_hovered`, such as
|
||||
/// hover styles and tooltips. These other behaviors are the main point of this mechanism.
|
||||
/// hover styles and tooltops. These other behaviors are the main point of this mechanism.
|
||||
/// An alternative might be to not affect mouse event handling - but this would allow
|
||||
/// inconsistent UI where clicks and moves interact with elements that are not considered to
|
||||
/// be hovered.
|
||||
|
||||
@@ -140,13 +140,7 @@ impl LspAdapter for TailwindLspAdapter {
|
||||
) -> Result<Option<serde_json::Value>> {
|
||||
Ok(Some(json!({
|
||||
"provideFormatter": true,
|
||||
"userLanguages": {
|
||||
"html": "html",
|
||||
"css": "css",
|
||||
"javascript": "javascript",
|
||||
"typescript": "typescript",
|
||||
"typescriptreact": "typescriptreact",
|
||||
},
|
||||
|
||||
})))
|
||||
}
|
||||
|
||||
@@ -167,8 +161,18 @@ impl LspAdapter for TailwindLspAdapter {
|
||||
tailwind_user_settings["emmetCompletions"] = Value::Bool(true);
|
||||
}
|
||||
|
||||
if tailwind_user_settings.get("includeLanguages").is_none() {
|
||||
tailwind_user_settings["includeLanguages"] = json!({
|
||||
"html": "html",
|
||||
"css": "css",
|
||||
"javascript": "javascript",
|
||||
"typescript": "typescript",
|
||||
"typescriptreact": "typescriptreact",
|
||||
});
|
||||
}
|
||||
|
||||
Ok(json!({
|
||||
"tailwindCSS": tailwind_user_settings,
|
||||
"tailwindCSS": tailwind_user_settings
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,3 @@
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment")
|
||||
)
|
||||
|
||||
; GitHub actions: JavaScript for workflow scripting (inline and block)
|
||||
(block_mapping
|
||||
(block_mapping_pair
|
||||
key: (flow_node) @_uses (#eq? @_uses "uses")
|
||||
value: (flow_node) @_actions_ghs (#match? @_actions_ghs "^actions/github-script"))
|
||||
(block_mapping_pair
|
||||
key: (flow_node) @_with (#eq? @_with "with")
|
||||
value: (block_node
|
||||
(block_mapping
|
||||
(block_mapping_pair
|
||||
key: (flow_node) @_run (#eq? @_run "script")
|
||||
value: [
|
||||
(flow_node (plain_scalar (string_scalar) @injection.content))
|
||||
(block_node (block_scalar) @injection.content)
|
||||
]
|
||||
(#set! injection.language "javascript")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -98,14 +98,6 @@ impl Room {
|
||||
self.room.connection_state()
|
||||
}
|
||||
|
||||
pub fn name(&self) -> String {
|
||||
self.room.name()
|
||||
}
|
||||
|
||||
pub async fn sid(&self) -> String {
|
||||
self.room.sid().await.to_string()
|
||||
}
|
||||
|
||||
pub async fn publish_local_microphone_track(
|
||||
&self,
|
||||
user_name: String,
|
||||
|
||||
@@ -714,14 +714,6 @@ impl Room {
|
||||
self.0.lock().token.clone()
|
||||
}
|
||||
|
||||
pub fn name(&self) -> String {
|
||||
"test_room".to_string()
|
||||
}
|
||||
|
||||
pub async fn sid(&self) -> String {
|
||||
"RM_test_session".to_string()
|
||||
}
|
||||
|
||||
pub fn play_remote_audio_track(
|
||||
&self,
|
||||
_track: &RemoteAudioTrack,
|
||||
|
||||
@@ -667,7 +667,7 @@ impl LanguageServer {
|
||||
|
||||
#[allow(deprecated)]
|
||||
InitializeParams {
|
||||
process_id: Some(std::process::id()),
|
||||
process_id: None,
|
||||
root_path: None,
|
||||
root_uri: Some(self.root_uri.clone()),
|
||||
initialization_options: None,
|
||||
|
||||
@@ -4648,11 +4648,9 @@ impl Repository {
|
||||
});
|
||||
|
||||
let this = cx.weak_entity();
|
||||
let rx = self.run_hook(RunHook::PrePush, cx);
|
||||
self.send_job(
|
||||
Some(format!("git push {} {} {}", args, remote, branch).into()),
|
||||
move |git_repo, mut cx| async move {
|
||||
rx.await??;
|
||||
match git_repo {
|
||||
RepositoryState::Local(LocalRepositoryState {
|
||||
backend,
|
||||
|
||||
@@ -9733,7 +9733,7 @@ async fn test_ignored_dirs_events(cx: &mut gpui::TestAppContext) {
|
||||
("project/target/debug/deps".to_string(), PathChange::Added),
|
||||
("project/target/debug/deps".to_string(), PathChange::Removed),
|
||||
],
|
||||
"Due to `debug` directory being tracked, it should get updates for entries inside it.
|
||||
"Due to `debug` directory being tracket, it should get updates for entries inside it.
|
||||
No updates for more nested directories should happen as those are ignored",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -565,7 +565,6 @@ message GitCreateWorktree {
|
||||
message RunGitHook {
|
||||
enum GitHook {
|
||||
PRE_COMMIT = 0;
|
||||
PRE_PUSH = 1;
|
||||
}
|
||||
|
||||
uint64 project_id = 1;
|
||||
|
||||
@@ -20,6 +20,7 @@ use std::{
|
||||
time::Instant,
|
||||
};
|
||||
use util::{
|
||||
ResultExt as _,
|
||||
paths::{PathStyle, RemotePathBuf},
|
||||
rel_path::RelPath,
|
||||
shell::ShellKind,
|
||||
@@ -48,6 +49,7 @@ pub(crate) struct WslRemoteConnection {
|
||||
shell_kind: ShellKind,
|
||||
default_system_shell: String,
|
||||
connection_options: WslConnectionOptions,
|
||||
can_exec: bool,
|
||||
}
|
||||
|
||||
impl WslRemoteConnection {
|
||||
@@ -71,6 +73,7 @@ impl WslRemoteConnection {
|
||||
shell: String::new(),
|
||||
shell_kind: ShellKind::Posix,
|
||||
default_system_shell: String::from("/bin/sh"),
|
||||
can_exec: true,
|
||||
};
|
||||
delegate.set_status(Some("Detecting WSL environment"), cx);
|
||||
this.shell = this
|
||||
@@ -79,6 +82,8 @@ impl WslRemoteConnection {
|
||||
.context("failed detecting shell")?;
|
||||
log::info!("Remote shell discovered: {}", this.shell);
|
||||
this.shell_kind = ShellKind::new(&this.shell, false);
|
||||
this.can_exec = this.detect_can_exec().await;
|
||||
log::info!("Remote can exec: {}", this.can_exec);
|
||||
this.platform = this
|
||||
.detect_platform()
|
||||
.await
|
||||
@@ -94,9 +99,27 @@ impl WslRemoteConnection {
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
async fn detect_can_exec(&self) -> bool {
|
||||
let options = &self.connection_options;
|
||||
let program = self.shell_kind.prepend_command_prefix("uname");
|
||||
let args = &["-m"];
|
||||
let output = wsl_command_impl(options, &program, args, true)
|
||||
.output()
|
||||
.await;
|
||||
|
||||
if !output.is_ok_and(|output| output.status.success()) {
|
||||
run_wsl_command_impl(options, &program, args, false)
|
||||
.await
|
||||
.context("failed detecting exec status")
|
||||
.log_err();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
async fn detect_platform(&self) -> Result<RemotePlatform> {
|
||||
let program = self.shell_kind.prepend_command_prefix("uname");
|
||||
let arch_str = self.run_wsl_command_with_output(&program, &["-m"]).await?;
|
||||
let arch_str = self.run_wsl_command(&program, &["-m"]).await?;
|
||||
let arch_str = arch_str.trim().to_string();
|
||||
let arch = match arch_str.as_str() {
|
||||
"x86_64" => "x86_64",
|
||||
@@ -108,25 +131,18 @@ impl WslRemoteConnection {
|
||||
|
||||
async fn detect_shell(&self) -> Result<String> {
|
||||
Ok(self
|
||||
.run_wsl_command_with_output("sh", &["-c", "echo $SHELL"])
|
||||
.run_wsl_command("sh", &["-c", "echo $SHELL"])
|
||||
.await
|
||||
.inspect_err(|err| log::error!("Failed to detect remote shell: {err}"))
|
||||
.ok()
|
||||
.unwrap_or_else(|| "/bin/sh".to_string()))
|
||||
}
|
||||
|
||||
async fn windows_path_to_wsl_path(&self, source: &Path) -> Result<String> {
|
||||
windows_path_to_wsl_path_impl(&self.connection_options, source).await
|
||||
windows_path_to_wsl_path_impl(&self.connection_options, source, self.can_exec).await
|
||||
}
|
||||
|
||||
async fn run_wsl_command_with_output(&self, program: &str, args: &[&str]) -> Result<String> {
|
||||
run_wsl_command_with_output_impl(&self.connection_options, program, args).await
|
||||
}
|
||||
|
||||
async fn run_wsl_command(&self, program: &str, args: &[&str]) -> Result<()> {
|
||||
run_wsl_command_impl(&self.connection_options, program, args, false)
|
||||
.await
|
||||
.map(|_| ())
|
||||
async fn run_wsl_command(&self, program: &str, args: &[&str]) -> Result<String> {
|
||||
run_wsl_command_impl(&self.connection_options, program, args, self.can_exec).await
|
||||
}
|
||||
|
||||
async fn ensure_server_binary(
|
||||
@@ -327,7 +343,7 @@ impl RemoteConnection for WslRemoteConnection {
|
||||
}
|
||||
|
||||
let proxy_process =
|
||||
match wsl_command_impl(&self.connection_options, "env", &proxy_args, false)
|
||||
match wsl_command_impl(&self.connection_options, "env", &proxy_args, self.can_exec)
|
||||
.kill_on_drop(true)
|
||||
.spawn()
|
||||
{
|
||||
@@ -354,14 +370,15 @@ impl RemoteConnection for WslRemoteConnection {
|
||||
) -> Task<Result<()>> {
|
||||
cx.background_spawn({
|
||||
let options = self.connection_options.clone();
|
||||
let can_exec = self.can_exec;
|
||||
async move {
|
||||
let wsl_src = windows_path_to_wsl_path_impl(&options, &src_path).await?;
|
||||
let wsl_src = windows_path_to_wsl_path_impl(&options, &src_path, can_exec).await?;
|
||||
|
||||
run_wsl_command_impl(
|
||||
&options,
|
||||
"cp",
|
||||
&["-r", &wsl_src, &dest_path.to_string()],
|
||||
true,
|
||||
can_exec,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
@@ -503,26 +520,13 @@ async fn sanitize_path(path: &Path) -> Result<String> {
|
||||
Ok(sanitized.replace('\\', "/"))
|
||||
}
|
||||
|
||||
async fn run_wsl_command_with_output_impl(
|
||||
options: &WslConnectionOptions,
|
||||
program: &str,
|
||||
args: &[&str],
|
||||
) -> Result<String> {
|
||||
match run_wsl_command_impl(options, program, args, true).await {
|
||||
Ok(res) => Ok(res),
|
||||
Err(exec_err) => match run_wsl_command_impl(options, program, args, false).await {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => Err(e.context(exec_err)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn windows_path_to_wsl_path_impl(
|
||||
options: &WslConnectionOptions,
|
||||
source: &Path,
|
||||
exec: bool,
|
||||
) -> Result<String> {
|
||||
let source = sanitize_path(source).await?;
|
||||
run_wsl_command_with_output_impl(options, "wslpath", &["-u", &source]).await
|
||||
run_wsl_command_impl(options, "wslpath", &["-u", &source], exec).await
|
||||
}
|
||||
|
||||
async fn run_wsl_command_impl(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/// Trait for recursively merging settings structures.
|
||||
///
|
||||
/// When Zed starts it loads settings from `default.json` to initialize
|
||||
/// When Zed starts it loads settinsg from `default.json` to initialize
|
||||
/// everything. These may be further refined by loading the user's settings,
|
||||
/// and any settings profiles; and then further refined by loading any
|
||||
/// local project settings.
|
||||
|
||||
@@ -8,68 +8,37 @@ pub(crate) fn neutral() -> ColorScaleSet {
|
||||
sand()
|
||||
}
|
||||
|
||||
const LIGHT_ADDED_COLOR: Hsla = Hsla {
|
||||
const ADDED_COLOR: Hsla = Hsla {
|
||||
h: 134. / 360.,
|
||||
s: 0.55,
|
||||
l: 0.40,
|
||||
a: 1.0,
|
||||
};
|
||||
const LIGHT_WORD_ADDED_COLOR: Hsla = Hsla {
|
||||
const WORD_ADDED_COLOR: Hsla = Hsla {
|
||||
h: 134. / 360.,
|
||||
s: 0.55,
|
||||
l: 0.40,
|
||||
a: 0.35,
|
||||
};
|
||||
const LIGHT_MODIFIED_COLOR: Hsla = Hsla {
|
||||
const MODIFIED_COLOR: Hsla = Hsla {
|
||||
h: 48. / 360.,
|
||||
s: 0.76,
|
||||
l: 0.47,
|
||||
a: 1.0,
|
||||
};
|
||||
const LIGHT_REMOVED_COLOR: Hsla = Hsla {
|
||||
const REMOVED_COLOR: Hsla = Hsla {
|
||||
h: 350. / 360.,
|
||||
s: 0.88,
|
||||
l: 0.25,
|
||||
a: 1.0,
|
||||
};
|
||||
const LIGHT_WORD_DELETED_COLOR: Hsla = Hsla {
|
||||
const WORD_DELETED_COLOR: Hsla = Hsla {
|
||||
h: 350. / 360.,
|
||||
s: 0.88,
|
||||
l: 0.25,
|
||||
a: 0.80,
|
||||
};
|
||||
|
||||
pub(crate) const DARK_ADDED_COLOR: Hsla = Hsla {
|
||||
h: 1.0,
|
||||
s: 0.55,
|
||||
l: 0.30,
|
||||
a: 0.20,
|
||||
};
|
||||
pub(crate) const DARK_WORD_ADDED_COLOR: Hsla = Hsla {
|
||||
h: 135. / 360.,
|
||||
s: 0.55,
|
||||
l: 0.30,
|
||||
a: 0.24,
|
||||
};
|
||||
pub(crate) const DARK_MODIFIED_COLOR: Hsla = Hsla {
|
||||
h: 218. / 360.,
|
||||
s: 0.66,
|
||||
l: 0.36,
|
||||
a: 0.20,
|
||||
};
|
||||
pub(crate) const DARK_REMOVED_COLOR: Hsla = Hsla {
|
||||
h: 356. / 360.,
|
||||
s: 0.87,
|
||||
l: 0.35,
|
||||
a: 0.32,
|
||||
};
|
||||
pub(crate) const DARK_WORD_DELETED_COLOR: Hsla = Hsla {
|
||||
h: 356. / 360.,
|
||||
s: 0.87,
|
||||
l: 0.35,
|
||||
a: 0.50,
|
||||
};
|
||||
|
||||
/// The default colors for the theme.
|
||||
///
|
||||
/// Themes that do not specify all colors are refined off of these defaults.
|
||||
@@ -189,14 +158,14 @@ impl ThemeColors {
|
||||
terminal_ansi_dim_cyan: cyan().light().step_10(),
|
||||
terminal_ansi_dim_white: neutral().light().step_11(),
|
||||
link_text_hover: orange().light().step_10(),
|
||||
version_control_added: LIGHT_ADDED_COLOR,
|
||||
version_control_deleted: LIGHT_REMOVED_COLOR,
|
||||
version_control_modified: LIGHT_MODIFIED_COLOR,
|
||||
version_control_renamed: LIGHT_MODIFIED_COLOR,
|
||||
version_control_added: ADDED_COLOR,
|
||||
version_control_deleted: REMOVED_COLOR,
|
||||
version_control_modified: MODIFIED_COLOR,
|
||||
version_control_renamed: MODIFIED_COLOR,
|
||||
version_control_conflict: orange().light().step_12(),
|
||||
version_control_ignored: gray().light().step_12(),
|
||||
version_control_word_added: LIGHT_WORD_ADDED_COLOR,
|
||||
version_control_word_deleted: LIGHT_WORD_DELETED_COLOR,
|
||||
version_control_word_added: WORD_ADDED_COLOR,
|
||||
version_control_word_deleted: WORD_DELETED_COLOR,
|
||||
version_control_conflict_marker_ours: green().light().step_10().alpha(0.5),
|
||||
version_control_conflict_marker_theirs: blue().light().step_10().alpha(0.5),
|
||||
vim_normal_background: system.transparent,
|
||||
@@ -326,14 +295,14 @@ impl ThemeColors {
|
||||
terminal_ansi_bright_white: neutral().dark().step_11(),
|
||||
terminal_ansi_dim_white: neutral().dark().step_10(),
|
||||
link_text_hover: orange().dark().step_10(),
|
||||
version_control_added: DARK_ADDED_COLOR,
|
||||
version_control_deleted: DARK_REMOVED_COLOR,
|
||||
version_control_modified: DARK_MODIFIED_COLOR,
|
||||
version_control_renamed: DARK_MODIFIED_COLOR,
|
||||
version_control_added: ADDED_COLOR,
|
||||
version_control_deleted: REMOVED_COLOR,
|
||||
version_control_modified: MODIFIED_COLOR,
|
||||
version_control_renamed: MODIFIED_COLOR,
|
||||
version_control_conflict: orange().dark().step_12(),
|
||||
version_control_ignored: gray().dark().step_12(),
|
||||
version_control_word_added: DARK_WORD_ADDED_COLOR,
|
||||
version_control_word_deleted: DARK_WORD_DELETED_COLOR,
|
||||
version_control_word_added: WORD_ADDED_COLOR,
|
||||
version_control_word_deleted: WORD_DELETED_COLOR,
|
||||
version_control_conflict_marker_ours: green().dark().step_10().alpha(0.5),
|
||||
version_control_conflict_marker_theirs: blue().dark().step_10().alpha(0.5),
|
||||
vim_normal_background: system.transparent,
|
||||
|
||||
@@ -3,8 +3,7 @@ use std::sync::Arc;
|
||||
use gpui::{FontStyle, FontWeight, HighlightStyle, Hsla, WindowBackgroundAppearance, hsla};
|
||||
|
||||
use crate::{
|
||||
AccentColors, Appearance, DARK_ADDED_COLOR, DARK_MODIFIED_COLOR, DARK_REMOVED_COLOR,
|
||||
DARK_WORD_ADDED_COLOR, DARK_WORD_DELETED_COLOR, DEFAULT_DARK_THEME, PlayerColors, StatusColors,
|
||||
AccentColors, Appearance, DEFAULT_DARK_THEME, PlayerColors, StatusColors,
|
||||
StatusColorsRefinement, SyntaxTheme, SystemColors, Theme, ThemeColors, ThemeColorsRefinement,
|
||||
ThemeFamily, ThemeStyles, default_color_scales,
|
||||
};
|
||||
@@ -71,6 +70,37 @@ pub(crate) fn zed_default_dark() -> Theme {
|
||||
let teal = hsla(187. / 360., 47. / 100., 55. / 100., 1.0);
|
||||
let yellow = hsla(39. / 360., 67. / 100., 69. / 100., 1.0);
|
||||
|
||||
const ADDED_COLOR: Hsla = Hsla {
|
||||
h: 134. / 360.,
|
||||
s: 0.55,
|
||||
l: 0.40,
|
||||
a: 1.0,
|
||||
};
|
||||
const WORD_ADDED_COLOR: Hsla = Hsla {
|
||||
h: 134. / 360.,
|
||||
s: 0.55,
|
||||
l: 0.40,
|
||||
a: 0.35,
|
||||
};
|
||||
const MODIFIED_COLOR: Hsla = Hsla {
|
||||
h: 48. / 360.,
|
||||
s: 0.76,
|
||||
l: 0.47,
|
||||
a: 1.0,
|
||||
};
|
||||
const REMOVED_COLOR: Hsla = Hsla {
|
||||
h: 350. / 360.,
|
||||
s: 0.88,
|
||||
l: 0.25,
|
||||
a: 1.0,
|
||||
};
|
||||
const WORD_DELETED_COLOR: Hsla = Hsla {
|
||||
h: 350. / 360.,
|
||||
s: 0.88,
|
||||
l: 0.25,
|
||||
a: 0.80,
|
||||
};
|
||||
|
||||
let player = PlayerColors::dark();
|
||||
Theme {
|
||||
id: "one_dark".to_string(),
|
||||
@@ -207,14 +237,14 @@ pub(crate) fn zed_default_dark() -> Theme {
|
||||
minimap_thumb_border: hsla(228. / 360., 8. / 100., 25. / 100., 1.),
|
||||
editor_foreground: hsla(218. / 360., 14. / 100., 71. / 100., 1.),
|
||||
link_text_hover: blue,
|
||||
version_control_added: DARK_ADDED_COLOR,
|
||||
version_control_deleted: DARK_REMOVED_COLOR,
|
||||
version_control_modified: DARK_MODIFIED_COLOR,
|
||||
version_control_renamed: DARK_MODIFIED_COLOR,
|
||||
version_control_added: ADDED_COLOR,
|
||||
version_control_deleted: REMOVED_COLOR,
|
||||
version_control_modified: MODIFIED_COLOR,
|
||||
version_control_renamed: MODIFIED_COLOR,
|
||||
version_control_conflict: crate::orange().light().step_12(),
|
||||
version_control_ignored: crate::gray().light().step_12(),
|
||||
version_control_word_added: DARK_WORD_ADDED_COLOR,
|
||||
version_control_word_deleted: DARK_WORD_DELETED_COLOR,
|
||||
version_control_word_added: WORD_ADDED_COLOR,
|
||||
version_control_word_deleted: WORD_DELETED_COLOR,
|
||||
version_control_conflict_marker_ours: crate::green().light().step_12().alpha(0.5),
|
||||
version_control_conflict_marker_theirs: crate::blue().light().step_12().alpha(0.5),
|
||||
|
||||
|
||||
@@ -287,7 +287,6 @@ pub fn theme_colors_refinement(
|
||||
.panel_background
|
||||
.as_ref()
|
||||
.and_then(|color| try_parse_color(color).ok());
|
||||
|
||||
ThemeColorsRefinement {
|
||||
border,
|
||||
border_variant: this
|
||||
@@ -714,7 +713,6 @@ pub fn theme_colors_refinement(
|
||||
.as_ref()
|
||||
.and_then(|color| try_parse_color(color).ok())
|
||||
// Fall back to `created`, for backwards compatibility.
|
||||
// todo!
|
||||
.or(status_colors.created),
|
||||
version_control_deleted: this
|
||||
.version_control_deleted
|
||||
|
||||
@@ -247,10 +247,6 @@ impl ThemeFamily {
|
||||
};
|
||||
let mut theme_colors_refinement =
|
||||
theme_colors_refinement(&theme.style.colors, &status_colors_refinement);
|
||||
// todo! We could pass in refined_theme_colors to apply default version control colors
|
||||
// instead of falling back to the status colors created ... etc
|
||||
//
|
||||
// That would probably break backwards compatibility though
|
||||
apply_theme_color_defaults(&mut theme_colors_refinement, &refined_player_colors);
|
||||
refined_theme_colors.refine(&theme_colors_refinement);
|
||||
|
||||
|
||||
@@ -2724,11 +2724,11 @@ impl Pane {
|
||||
.map(|this| {
|
||||
if is_active {
|
||||
let focus_handle = focus_handle.clone();
|
||||
this.tooltip(move |window, cx| {
|
||||
this.tooltip(move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
end_slot_tooltip_text,
|
||||
end_slot_action,
|
||||
&window.focused(cx).unwrap_or_else(|| focus_handle.clone()),
|
||||
&focus_handle,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
@@ -3031,14 +3031,7 @@ impl Pane {
|
||||
.disabled(!self.can_navigate_backward())
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Go Back",
|
||||
&GoBack,
|
||||
&window.focused(cx).unwrap_or_else(|| focus_handle.clone()),
|
||||
cx,
|
||||
)
|
||||
}
|
||||
move |_window, cx| Tooltip::for_action_in("Go Back", &GoBack, &focus_handle, cx)
|
||||
});
|
||||
|
||||
let navigate_forward = IconButton::new("navigate_forward", IconName::ArrowRight)
|
||||
@@ -3054,13 +3047,8 @@ impl Pane {
|
||||
.disabled(!self.can_navigate_forward())
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Go Forward",
|
||||
&GoForward,
|
||||
&window.focused(cx).unwrap_or_else(|| focus_handle.clone()),
|
||||
cx,
|
||||
)
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in("Go Forward", &GoForward, &focus_handle, cx)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -7236,9 +7236,7 @@ actions!(
|
||||
/// Shares the current project with collaborators.
|
||||
ShareProject,
|
||||
/// Shares your screen with collaborators.
|
||||
ScreenShare,
|
||||
/// Copies the current room name and session id for debugging purposes.
|
||||
CopyRoomId,
|
||||
ScreenShare
|
||||
]
|
||||
);
|
||||
actions!(
|
||||
|
||||
@@ -54,8 +54,8 @@ fn main() {
|
||||
}
|
||||
|
||||
if cfg!(target_arch = "x86_64") {
|
||||
println!("cargo::rerun-if-changed=resources\\windows\\bin\\x64\\conpty.dll");
|
||||
println!("cargo::rerun-if-changed=resources\\windows\\bin\\x64\\OpenConsole.exe");
|
||||
println!("cargo::rerun-if-changed=\\..\\..\\..\\conpty.dll");
|
||||
println!("cargo::rerun-if-changed=\\..\\..\\..\\OpenConsole.exe");
|
||||
let conpty_target = std::env::var("OUT_DIR").unwrap() + "\\..\\..\\..\\conpty.dll";
|
||||
match std::fs::copy("resources/windows/bin/x64/conpty.dll", &conpty_target) {
|
||||
Ok(_) => println!("Copied conpty.dll to {conpty_target}"),
|
||||
|
||||
@@ -132,8 +132,12 @@ impl EditPredictionProvider for ZetaEditPredictionProvider {
|
||||
}
|
||||
|
||||
fn discard(&mut self, cx: &mut Context<Self>) {
|
||||
self.zeta.update(cx, |zeta, _cx| {
|
||||
zeta.reject_current_prediction(EditPredictionRejectReason::Discarded, &self.project);
|
||||
self.zeta.update(cx, |zeta, cx| {
|
||||
zeta.reject_current_prediction(
|
||||
EditPredictionRejectReason::Discarded,
|
||||
&self.project,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -169,10 +173,11 @@ impl EditPredictionProvider for ZetaEditPredictionProvider {
|
||||
let snapshot = buffer.snapshot();
|
||||
|
||||
let Some(edits) = prediction.interpolate(&snapshot) else {
|
||||
self.zeta.update(cx, |zeta, _cx| {
|
||||
self.zeta.update(cx, |zeta, cx| {
|
||||
zeta.reject_current_prediction(
|
||||
EditPredictionRejectReason::InterpolatedEmpty,
|
||||
&self.project,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
return None;
|
||||
|
||||
@@ -5,7 +5,7 @@ use cloud_llm_client::predict_edits_v3::{self, Event, PromptFormat, Signature};
|
||||
use cloud_llm_client::{
|
||||
AcceptEditPredictionBody, EXPIRED_LLM_TOKEN_HEADER_NAME, EditPredictionRejectReason,
|
||||
EditPredictionRejection, MAX_EDIT_PREDICTION_REJECTIONS_PER_REQUEST,
|
||||
MINIMUM_REQUIRED_VERSION_HEADER_NAME, PredictEditsRequestTrigger, RejectEditPredictionsBodyRef,
|
||||
MINIMUM_REQUIRED_VERSION_HEADER_NAME, PredictEditsRequestTrigger, RejectEditPredictionsBody,
|
||||
ZED_VERSION_HEADER_NAME,
|
||||
};
|
||||
use cloud_zeta2_prompt::retrieval_prompt::{SearchToolInput, SearchToolQuery};
|
||||
@@ -19,10 +19,8 @@ use edit_prediction_context::{
|
||||
SyntaxIndex, SyntaxIndexState,
|
||||
};
|
||||
use feature_flags::{FeatureFlag, FeatureFlagAppExt as _, PredictEditsRateCompletionsFeatureFlag};
|
||||
use futures::channel::mpsc::UnboundedReceiver;
|
||||
use futures::channel::{mpsc, oneshot};
|
||||
use futures::{AsyncReadExt as _, FutureExt as _, StreamExt as _, select_biased};
|
||||
use gpui::BackgroundExecutor;
|
||||
use futures::{AsyncReadExt as _, FutureExt as _, StreamExt as _};
|
||||
use gpui::{
|
||||
App, AsyncApp, Entity, EntityId, Global, SharedString, Subscription, Task, WeakEntity, actions,
|
||||
http_client::{self, AsyncBody, Method},
|
||||
@@ -102,7 +100,6 @@ actions!(
|
||||
const EVENT_COUNT_MAX: usize = 6;
|
||||
const CHANGE_GROUPING_LINE_SPAN: u32 = 8;
|
||||
const ZED_PREDICT_DATA_COLLECTION_CHOICE: &str = "zed_predict_data_collection_choice";
|
||||
const REJECT_REQUEST_DEBOUNCE: Duration = Duration::from_secs(15);
|
||||
|
||||
pub struct SweepFeatureFlag;
|
||||
|
||||
@@ -198,7 +195,9 @@ pub struct Zeta {
|
||||
edit_prediction_model: ZetaEditPredictionModel,
|
||||
pub sweep_ai: SweepAi,
|
||||
data_collection_choice: DataCollectionChoice,
|
||||
reject_predictions_tx: mpsc::UnboundedSender<EditPredictionRejection>,
|
||||
rejected_predictions: Vec<EditPredictionRejection>,
|
||||
reject_predictions_tx: mpsc::UnboundedSender<()>,
|
||||
reject_predictions_debounce_task: Option<Task<()>>,
|
||||
shown_predictions: VecDeque<EditPrediction>,
|
||||
rated_predictions: HashSet<EditPredictionId>,
|
||||
}
|
||||
@@ -326,8 +325,13 @@ impl ZetaProject {
|
||||
return;
|
||||
};
|
||||
|
||||
this.update(cx, |this, _cx| {
|
||||
this.reject_prediction(prediction_id, EditPredictionRejectReason::Canceled, false);
|
||||
this.update(cx, |this, cx| {
|
||||
this.reject_prediction(
|
||||
prediction_id,
|
||||
EditPredictionRejectReason::Canceled,
|
||||
false,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
@@ -500,24 +504,14 @@ impl Zeta {
|
||||
let refresh_llm_token_listener = RefreshLlmTokenListener::global(cx);
|
||||
let data_collection_choice = Self::load_data_collection_choice();
|
||||
|
||||
let llm_token = LlmApiToken::default();
|
||||
|
||||
let (reject_tx, reject_rx) = mpsc::unbounded();
|
||||
cx.background_spawn({
|
||||
let client = client.clone();
|
||||
let llm_token = llm_token.clone();
|
||||
let app_version = AppVersion::global(cx);
|
||||
let background_executor = cx.background_executor().clone();
|
||||
async move {
|
||||
Self::handle_rejected_predictions(
|
||||
reject_rx,
|
||||
client,
|
||||
llm_token,
|
||||
app_version,
|
||||
background_executor,
|
||||
)
|
||||
.await
|
||||
let (reject_tx, mut reject_rx) = mpsc::unbounded();
|
||||
cx.spawn(async move |this, cx| {
|
||||
while let Some(()) = reject_rx.next().await {
|
||||
this.update(cx, |this, cx| this.flush_rejected_predictions(cx))?
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach();
|
||||
|
||||
@@ -526,7 +520,7 @@ impl Zeta {
|
||||
client,
|
||||
user_store,
|
||||
options: DEFAULT_OPTIONS,
|
||||
llm_token,
|
||||
llm_token: LlmApiToken::default(),
|
||||
_llm_token_subscription: cx.subscribe(
|
||||
&refresh_llm_token_listener,
|
||||
|this, _listener, _event, cx| {
|
||||
@@ -546,6 +540,8 @@ impl Zeta {
|
||||
edit_prediction_model: ZetaEditPredictionModel::Zeta2,
|
||||
sweep_ai: SweepAi::new(cx),
|
||||
data_collection_choice,
|
||||
rejected_predictions: Vec::new(),
|
||||
reject_predictions_debounce_task: None,
|
||||
reject_predictions_tx: reject_tx,
|
||||
rated_predictions: Default::default(),
|
||||
shown_predictions: Default::default(),
|
||||
@@ -905,73 +901,64 @@ impl Zeta {
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
async fn handle_rejected_predictions(
|
||||
rx: UnboundedReceiver<EditPredictionRejection>,
|
||||
client: Arc<Client>,
|
||||
llm_token: LlmApiToken,
|
||||
app_version: Version,
|
||||
background_executor: BackgroundExecutor,
|
||||
) {
|
||||
let mut rx = std::pin::pin!(rx.peekable());
|
||||
let mut batched = Vec::new();
|
||||
fn flush_rejected_predictions(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
|
||||
match self.edit_prediction_model {
|
||||
ZetaEditPredictionModel::Zeta1 | ZetaEditPredictionModel::Zeta2 => {}
|
||||
ZetaEditPredictionModel::Sweep => return Task::ready(anyhow::Ok(())),
|
||||
}
|
||||
|
||||
while let Some(rejection) = rx.next().await {
|
||||
batched.push(rejection);
|
||||
let client = self.client.clone();
|
||||
let llm_token = self.llm_token.clone();
|
||||
let app_version = AppVersion::global(cx);
|
||||
let last_rejection = self.rejected_predictions.last().cloned();
|
||||
let Some(last_rejection) = last_rejection else {
|
||||
return Task::ready(anyhow::Ok(()));
|
||||
};
|
||||
|
||||
if batched.len() < MAX_EDIT_PREDICTION_REJECTIONS_PER_REQUEST / 2 {
|
||||
select_biased! {
|
||||
next = rx.as_mut().peek().fuse() => {
|
||||
if next.is_some() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
() = background_executor.timer(REJECT_REQUEST_DEBOUNCE).fuse() => {},
|
||||
}
|
||||
}
|
||||
let body = serde_json::to_string(&RejectEditPredictionsBody {
|
||||
rejections: self.rejected_predictions.clone(),
|
||||
})
|
||||
.ok();
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
let url = client
|
||||
.http_client()
|
||||
.build_zed_llm_url("/predict_edits/reject", &[])
|
||||
.unwrap();
|
||||
.build_zed_llm_url("/predict_edits/reject", &[])?;
|
||||
|
||||
let flush_count = batched
|
||||
.len()
|
||||
// in case items have accumulated after failure
|
||||
.min(MAX_EDIT_PREDICTION_REJECTIONS_PER_REQUEST);
|
||||
let start = batched.len() - flush_count;
|
||||
|
||||
let body = RejectEditPredictionsBodyRef {
|
||||
rejections: &batched[start..],
|
||||
};
|
||||
|
||||
let result = Self::send_api_request::<()>(
|
||||
|builder| {
|
||||
let req = builder
|
||||
.uri(url.as_ref())
|
||||
.body(serde_json::to_string(&body)?.into());
|
||||
anyhow::Ok(req?)
|
||||
cx.background_spawn(Self::send_api_request::<()>(
|
||||
move |builder| {
|
||||
let req = builder.uri(url.as_ref()).body(body.clone().into());
|
||||
Ok(req?)
|
||||
},
|
||||
client.clone(),
|
||||
llm_token.clone(),
|
||||
app_version.clone(),
|
||||
)
|
||||
.await;
|
||||
client,
|
||||
llm_token,
|
||||
app_version,
|
||||
))
|
||||
.await
|
||||
.context("Failed to reject edit predictions")?;
|
||||
|
||||
if result.log_err().is_some() {
|
||||
batched.drain(start..);
|
||||
}
|
||||
}
|
||||
this.update(cx, |this, _| {
|
||||
if let Some(ix) = this
|
||||
.rejected_predictions
|
||||
.iter()
|
||||
.position(|rejection| rejection.request_id == last_rejection.request_id)
|
||||
{
|
||||
this.rejected_predictions.drain(..ix + 1);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn reject_current_prediction(
|
||||
&mut self,
|
||||
reason: EditPredictionRejectReason,
|
||||
project: &Entity<Project>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if let Some(project_state) = self.projects.get_mut(&project.entity_id()) {
|
||||
project_state.pending_predictions.clear();
|
||||
if let Some(prediction) = project_state.current_prediction.take() {
|
||||
self.reject_prediction(prediction.prediction.id, reason, prediction.was_shown);
|
||||
self.reject_prediction(prediction.prediction.id, reason, prediction.was_shown, cx);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -997,14 +984,26 @@ impl Zeta {
|
||||
prediction_id: EditPredictionId,
|
||||
reason: EditPredictionRejectReason,
|
||||
was_shown: bool,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.reject_predictions_tx
|
||||
.unbounded_send(EditPredictionRejection {
|
||||
request_id: prediction_id.to_string(),
|
||||
reason,
|
||||
was_shown,
|
||||
})
|
||||
.log_err();
|
||||
self.rejected_predictions.push(EditPredictionRejection {
|
||||
request_id: prediction_id.to_string(),
|
||||
reason,
|
||||
was_shown,
|
||||
});
|
||||
|
||||
let reached_request_limit =
|
||||
self.rejected_predictions.len() >= MAX_EDIT_PREDICTION_REJECTIONS_PER_REQUEST / 2;
|
||||
let reject_tx = self.reject_predictions_tx.clone();
|
||||
self.reject_predictions_debounce_task = Some(cx.spawn(async move |_this, cx| {
|
||||
const REJECT_REQUEST_DEBOUNCE: Duration = Duration::from_secs(15);
|
||||
if !reached_request_limit {
|
||||
cx.background_executor()
|
||||
.timer(REJECT_REQUEST_DEBOUNCE)
|
||||
.await;
|
||||
}
|
||||
reject_tx.unbounded_send(()).log_err();
|
||||
}));
|
||||
}
|
||||
|
||||
fn is_refreshing(&self, project: &Entity<Project>) -> bool {
|
||||
@@ -1212,6 +1211,7 @@ impl Zeta {
|
||||
this.reject_current_prediction(
|
||||
EditPredictionRejectReason::Replaced,
|
||||
&project,
|
||||
cx,
|
||||
);
|
||||
|
||||
Some(new_prediction)
|
||||
@@ -1220,6 +1220,7 @@ impl Zeta {
|
||||
new_prediction.prediction.id,
|
||||
EditPredictionRejectReason::CurrentPreferred,
|
||||
false,
|
||||
cx,
|
||||
);
|
||||
None
|
||||
}
|
||||
@@ -1228,7 +1229,7 @@ impl Zeta {
|
||||
}
|
||||
}
|
||||
Err(reject_reason) => {
|
||||
this.reject_prediction(prediction_result.id, reject_reason, false);
|
||||
this.reject_prediction(prediction_result.id, reject_reason, false, cx);
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -2905,7 +2906,7 @@ fn feature_gate_predict_edits_actions(cx: &mut App) {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{path::Path, sync::Arc, time::Duration};
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use client::UserStore;
|
||||
use clock::FakeSystemClock;
|
||||
@@ -2932,7 +2933,7 @@ mod tests {
|
||||
use util::path;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{BufferEditPrediction, EditPredictionId, REJECT_REQUEST_DEBOUNCE, Zeta};
|
||||
use crate::{BufferEditPrediction, Zeta};
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_current_state(cx: &mut TestAppContext) {
|
||||
@@ -3034,8 +3035,8 @@ mod tests {
|
||||
.unwrap();
|
||||
refresh_task.await.unwrap();
|
||||
|
||||
zeta.update(cx, |zeta, _cx| {
|
||||
zeta.reject_current_prediction(EditPredictionRejectReason::Discarded, &project);
|
||||
zeta.update(cx, |zeta, cx| {
|
||||
zeta.reject_current_prediction(EditPredictionRejectReason::Discarded, &project, cx);
|
||||
});
|
||||
|
||||
// Prediction for another file
|
||||
@@ -3544,17 +3545,14 @@ mod tests {
|
||||
let snapshot = buffer.read_with(cx, |buffer, _cx| buffer.snapshot());
|
||||
let position = snapshot.anchor_before(language::Point::new(1, 3));
|
||||
|
||||
// start two refresh tasks
|
||||
zeta.update(cx, |zeta, cx| {
|
||||
// start two refresh tasks
|
||||
zeta.refresh_prediction_from_buffer(project.clone(), buffer.clone(), position, cx);
|
||||
|
||||
zeta.refresh_prediction_from_buffer(project.clone(), buffer.clone(), position, cx);
|
||||
});
|
||||
|
||||
let (_, respond_first) = requests.predict.next().await.unwrap();
|
||||
|
||||
zeta.update(cx, |zeta, cx| {
|
||||
zeta.refresh_prediction_from_buffer(project.clone(), buffer.clone(), position, cx);
|
||||
});
|
||||
|
||||
let (_, respond_second) = requests.predict.next().await.unwrap();
|
||||
|
||||
// wait for throttle
|
||||
@@ -3633,22 +3631,18 @@ mod tests {
|
||||
let snapshot = buffer.read_with(cx, |buffer, _cx| buffer.snapshot());
|
||||
let position = snapshot.anchor_before(language::Point::new(1, 3));
|
||||
|
||||
// start two refresh tasks
|
||||
zeta.update(cx, |zeta, cx| {
|
||||
// start two refresh tasks
|
||||
zeta.refresh_prediction_from_buffer(project.clone(), buffer.clone(), position, cx);
|
||||
zeta.refresh_prediction_from_buffer(project.clone(), buffer.clone(), position, cx);
|
||||
});
|
||||
|
||||
let (_, respond_first) = requests.predict.next().await.unwrap();
|
||||
|
||||
zeta.update(cx, |zeta, cx| {
|
||||
zeta.refresh_prediction_from_buffer(project.clone(), buffer.clone(), position, cx);
|
||||
});
|
||||
|
||||
let (_, respond_second) = requests.predict.next().await.unwrap();
|
||||
|
||||
// wait for throttle, so requests are sent
|
||||
cx.run_until_parked();
|
||||
|
||||
let (_, respond_first) = requests.predict.next().await.unwrap();
|
||||
let (_, respond_second) = requests.predict.next().await.unwrap();
|
||||
|
||||
zeta.update(cx, |zeta, cx| {
|
||||
// start a third request
|
||||
zeta.refresh_prediction_from_buffer(project.clone(), buffer.clone(), position, cx);
|
||||
@@ -3742,118 +3736,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_rejections_flushing(cx: &mut TestAppContext) {
|
||||
let (zeta, mut requests) = init_test(cx);
|
||||
|
||||
zeta.update(cx, |zeta, _cx| {
|
||||
zeta.reject_prediction(
|
||||
EditPredictionId("test-1".into()),
|
||||
EditPredictionRejectReason::Discarded,
|
||||
false,
|
||||
);
|
||||
zeta.reject_prediction(
|
||||
EditPredictionId("test-2".into()),
|
||||
EditPredictionRejectReason::Canceled,
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
cx.executor().advance_clock(REJECT_REQUEST_DEBOUNCE);
|
||||
cx.run_until_parked();
|
||||
|
||||
let (reject_request, respond_tx) = requests.reject.next().await.unwrap();
|
||||
respond_tx.send(()).unwrap();
|
||||
|
||||
// batched
|
||||
assert_eq!(reject_request.rejections.len(), 2);
|
||||
assert_eq!(
|
||||
reject_request.rejections[0],
|
||||
EditPredictionRejection {
|
||||
request_id: "test-1".to_string(),
|
||||
reason: EditPredictionRejectReason::Discarded,
|
||||
was_shown: false
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
reject_request.rejections[1],
|
||||
EditPredictionRejection {
|
||||
request_id: "test-2".to_string(),
|
||||
reason: EditPredictionRejectReason::Canceled,
|
||||
was_shown: true
|
||||
}
|
||||
);
|
||||
|
||||
// Reaching batch size limit sends without debounce
|
||||
zeta.update(cx, |zeta, _cx| {
|
||||
for i in 0..70 {
|
||||
zeta.reject_prediction(
|
||||
EditPredictionId(format!("batch-{}", i).into()),
|
||||
EditPredictionRejectReason::Discarded,
|
||||
false,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// First MAX/2 items are sent immediately
|
||||
cx.run_until_parked();
|
||||
let (reject_request, respond_tx) = requests.reject.next().await.unwrap();
|
||||
respond_tx.send(()).unwrap();
|
||||
|
||||
assert_eq!(reject_request.rejections.len(), 50);
|
||||
assert_eq!(reject_request.rejections[0].request_id, "batch-0");
|
||||
assert_eq!(reject_request.rejections[49].request_id, "batch-49");
|
||||
|
||||
// Remaining items are debounced with the next batch
|
||||
cx.executor().advance_clock(Duration::from_secs(15));
|
||||
cx.run_until_parked();
|
||||
|
||||
let (reject_request, respond_tx) = requests.reject.next().await.unwrap();
|
||||
respond_tx.send(()).unwrap();
|
||||
|
||||
assert_eq!(reject_request.rejections.len(), 20);
|
||||
assert_eq!(reject_request.rejections[0].request_id, "batch-50");
|
||||
assert_eq!(reject_request.rejections[19].request_id, "batch-69");
|
||||
|
||||
// Request failure
|
||||
zeta.update(cx, |zeta, _cx| {
|
||||
zeta.reject_prediction(
|
||||
EditPredictionId("retry-1".into()),
|
||||
EditPredictionRejectReason::Discarded,
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
cx.executor().advance_clock(REJECT_REQUEST_DEBOUNCE);
|
||||
cx.run_until_parked();
|
||||
|
||||
let (reject_request, _respond_tx) = requests.reject.next().await.unwrap();
|
||||
assert_eq!(reject_request.rejections.len(), 1);
|
||||
assert_eq!(reject_request.rejections[0].request_id, "retry-1");
|
||||
// Simulate failure
|
||||
drop(_respond_tx);
|
||||
|
||||
// Add another rejection
|
||||
zeta.update(cx, |zeta, _cx| {
|
||||
zeta.reject_prediction(
|
||||
EditPredictionId("retry-2".into()),
|
||||
EditPredictionRejectReason::Discarded,
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
cx.executor().advance_clock(REJECT_REQUEST_DEBOUNCE);
|
||||
cx.run_until_parked();
|
||||
|
||||
// Retry should include both the failed item and the new one
|
||||
let (reject_request, respond_tx) = requests.reject.next().await.unwrap();
|
||||
respond_tx.send(()).unwrap();
|
||||
|
||||
assert_eq!(reject_request.rejections.len(), 2);
|
||||
assert_eq!(reject_request.rejections[0].request_id, "retry-1");
|
||||
assert_eq!(reject_request.rejections[1].request_id, "retry-2");
|
||||
}
|
||||
|
||||
// Skipped until we start including diagnostics in prompt
|
||||
// #[gpui::test]
|
||||
// async fn test_request_diagnostics(cx: &mut TestAppContext) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CARGO_ABOUT_VERSION="0.8.2"
|
||||
CARGO_ABOUT_VERSION="0.8"
|
||||
OUTPUT_FILE="${1:-$(pwd)/assets/licenses.md}"
|
||||
TEMPLATE_FILE="script/licenses/template.md.hbs"
|
||||
|
||||
@@ -28,10 +28,10 @@ echo -n "" >"$OUTPUT_FILE"
|
||||
} >>"$OUTPUT_FILE"
|
||||
|
||||
if ! cargo about --version | grep "cargo-about $CARGO_ABOUT_VERSION" &>/dev/null; then
|
||||
echo "Installing cargo-about@$CARGO_ABOUT_VERSION..."
|
||||
cargo install "cargo-about@$CARGO_ABOUT_VERSION"
|
||||
echo "Installing cargo-about@^$CARGO_ABOUT_VERSION..."
|
||||
cargo install "cargo-about@^$CARGO_ABOUT_VERSION"
|
||||
else
|
||||
echo "cargo-about@$CARGO_ABOUT_VERSION is already installed."
|
||||
echo "cargo-about@^$CARGO_ABOUT_VERSION is already installed."
|
||||
fi
|
||||
|
||||
echo "Generating cargo licenses"
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CARGO_ABOUT_VERSION="0.8.2"
|
||||
CARGO_ABOUT_VERSION="0.8"
|
||||
OUTPUT_FILE="${1:-$(pwd)/assets/licenses.csv}"
|
||||
TEMPLATE_FILE="script/licenses/template.csv.hbs"
|
||||
|
||||
if ! cargo about --version | grep "cargo-about $CARGO_ABOUT_VERSION" 2>&1 > /dev/null; then
|
||||
echo "Installing cargo-about@$CARGO_ABOUT_VERSION..."
|
||||
cargo install "cargo-about@$CARGO_ABOUT_VERSION"
|
||||
echo "Installing cargo-about@^$CARGO_ABOUT_VERSION..."
|
||||
cargo install "cargo-about@^$CARGO_ABOUT_VERSION"
|
||||
else
|
||||
echo "cargo-about@$CARGO_ABOUT_VERSION is already installed."
|
||||
echo "cargo-about@^$CARGO_ABOUT_VERSION is already installed."
|
||||
fi
|
||||
|
||||
echo "Generating cargo licenses"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
$CARGO_ABOUT_VERSION="0.8.2"
|
||||
$CARGO_ABOUT_VERSION="0.8"
|
||||
$outputFile=$args[0] ? $args[0] : "$(Get-Location)/assets/licenses.md"
|
||||
$templateFile="script/licenses/template.md.hbs"
|
||||
|
||||
@@ -14,10 +14,10 @@ New-Item -Path "$outputFile" -ItemType File -Value "" -Force
|
||||
|
||||
$versionOutput = cargo about --version
|
||||
if (-not ($versionOutput -match "cargo-about $CARGO_ABOUT_VERSION")) {
|
||||
Write-Host "Installing cargo-about@$CARGO_ABOUT_VERSION..."
|
||||
cargo install "cargo-about@$CARGO_ABOUT_VERSION"
|
||||
Write-Host "Installing cargo-about@^$CARGO_ABOUT_VERSION..."
|
||||
cargo install "cargo-about@^$CARGO_ABOUT_VERSION"
|
||||
} else {
|
||||
Write-Host "cargo-about@$CARGO_ABOUT_VERSION" is already installed
|
||||
Write-Host "cargo-about@^$CARGO_ABOUT_VERSION" is already installed
|
||||
}
|
||||
|
||||
Write-Host "Generating cargo licenses"
|
||||
|
||||
@@ -226,8 +226,8 @@ fn check_style() -> NamedJob {
|
||||
named::uses(
|
||||
"crate-ci",
|
||||
"typos",
|
||||
"2d0ce569feab1f8752f1dde43cc2f2aa53236e06",
|
||||
) // v1.40.0
|
||||
"80c8a4945eec0f6d464eaf9e65ed98ef085283d1",
|
||||
) // v1.38.1
|
||||
.with(("config", "./typos.toml"))
|
||||
}
|
||||
named::job(
|
||||
|
||||
Reference in New Issue
Block a user