Merge branch 'main' into edit-file-tool
This commit is contained in:
commit
d52e0f47b5
30 changed files with 320 additions and 104 deletions
|
@ -1,6 +1,6 @@
|
|||
# syntax = docker/dockerfile:1.2
|
||||
|
||||
FROM rust:1.89-bookworm as builder
|
||||
FROM rust:1.88-bookworm as builder
|
||||
WORKDIR app
|
||||
COPY . .
|
||||
|
||||
|
|
|
@ -596,6 +596,8 @@
|
|||
// when a corresponding project entry becomes active.
|
||||
// Gitignored entries are never auto revealed.
|
||||
"auto_reveal_entries": true,
|
||||
// Whether the project panel should open on startup.
|
||||
"starts_open": true,
|
||||
// Whether to fold directories automatically and show compact folders
|
||||
// (e.g. "a/b/c" ) when a directory has only one subdirectory inside.
|
||||
"auto_fold_dirs": true,
|
||||
|
@ -1171,6 +1173,9 @@
|
|||
// Sets a delay after which the inline blame information is shown.
|
||||
// Delay is restarted with every cursor movement.
|
||||
"delay_ms": 0,
|
||||
// The amount of padding between the end of the source line and the start
|
||||
// of the inline blame in units of em widths.
|
||||
"padding": 7,
|
||||
// Whether or not to display the git commit summary on the same line.
|
||||
"show_commit_summary": false,
|
||||
// The minimum column number to show the inline blame information at
|
||||
|
@ -1233,6 +1238,11 @@
|
|||
// 2. hour24
|
||||
"hour_format": "hour12"
|
||||
},
|
||||
// Status bar-related settings.
|
||||
"status_bar": {
|
||||
// Whether to show the active language button in the status bar.
|
||||
"active_language_button": true
|
||||
},
|
||||
// Settings specific to the terminal
|
||||
"terminal": {
|
||||
// What shell to use when opening a terminal. May take 3 values:
|
||||
|
|
|
@ -8,7 +8,6 @@ use agent_client_protocol as acp;
|
|||
use anyhow::{Context as _, Result};
|
||||
use assistant_tool::ActionLog;
|
||||
use editor::Bias;
|
||||
use futures::future::{Fuse, FusedFuture};
|
||||
use futures::{FutureExt, channel::oneshot, future::BoxFuture};
|
||||
use gpui::{AppContext, Context, Entity, EventEmitter, SharedString, Task};
|
||||
use itertools::Itertools;
|
||||
|
@ -482,7 +481,7 @@ pub struct AcpThread {
|
|||
project: Entity<Project>,
|
||||
action_log: Entity<ActionLog>,
|
||||
shared_buffers: HashMap<Entity<Buffer>, BufferSnapshot>,
|
||||
send_task: Option<Fuse<Task<()>>>,
|
||||
send_task: Option<Task<()>>,
|
||||
connection: Rc<dyn AgentConnection>,
|
||||
session_id: acp::SessionId,
|
||||
}
|
||||
|
@ -572,11 +571,7 @@ impl AcpThread {
|
|||
}
|
||||
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
if self
|
||||
.send_task
|
||||
.as_ref()
|
||||
.map_or(false, |t| !t.is_terminated())
|
||||
{
|
||||
if self.send_task.is_some() {
|
||||
if self.waiting_for_tool_confirmation() {
|
||||
ThreadStatus::WaitingForToolConfirmation
|
||||
} else {
|
||||
|
@ -966,31 +961,29 @@ impl AcpThread {
|
|||
let (tx, rx) = oneshot::channel();
|
||||
let cancel_task = self.cancel(cx);
|
||||
|
||||
self.send_task = Some(
|
||||
cx.spawn(async move |this, cx| {
|
||||
async {
|
||||
cancel_task.await;
|
||||
self.send_task = Some(cx.spawn(async move |this, cx| {
|
||||
async {
|
||||
cancel_task.await;
|
||||
|
||||
let result = this
|
||||
.update(cx, |this, cx| {
|
||||
this.connection.prompt(
|
||||
acp::PromptRequest {
|
||||
prompt: message,
|
||||
session_id: this.session_id.clone(),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await;
|
||||
let result = this
|
||||
.update(cx, |this, cx| {
|
||||
this.connection.prompt(
|
||||
acp::PromptRequest {
|
||||
prompt: message,
|
||||
session_id: this.session_id.clone(),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await;
|
||||
|
||||
tx.send(result).log_err();
|
||||
anyhow::Ok(())
|
||||
}
|
||||
.await
|
||||
.log_err();
|
||||
})
|
||||
.fuse(),
|
||||
);
|
||||
tx.send(result).log_err();
|
||||
|
||||
anyhow::Ok(())
|
||||
}
|
||||
.await
|
||||
.log_err();
|
||||
}));
|
||||
|
||||
cx.spawn(async move |this, cx| match rx.await {
|
||||
Ok(Err(e)) => {
|
||||
|
@ -998,7 +991,23 @@ impl AcpThread {
|
|||
.log_err();
|
||||
Err(e)?
|
||||
}
|
||||
_ => {
|
||||
result => {
|
||||
let cancelled = matches!(
|
||||
result,
|
||||
Ok(Ok(acp::PromptResponse {
|
||||
stop_reason: acp::StopReason::Cancelled
|
||||
}))
|
||||
);
|
||||
|
||||
// We only take the task if the current prompt wasn't cancelled.
|
||||
//
|
||||
// This prompt may have been cancelled because another one was sent
|
||||
// while it was still generating. In these cases, dropping `send_task`
|
||||
// would cause the next generation to be cancelled.
|
||||
if !cancelled {
|
||||
this.update(cx, |this, _cx| this.send_task.take()).ok();
|
||||
}
|
||||
|
||||
this.update(cx, |_, cx| cx.emit(AcpThreadEvent::Stopped))
|
||||
.log_err();
|
||||
Ok(())
|
||||
|
|
|
@ -3101,9 +3101,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
|||
// Turn inline-blame-off by default so no state is transferred without us explicitly doing so
|
||||
let inline_blame_off_settings = Some(InlineBlameSettings {
|
||||
enabled: false,
|
||||
delay_ms: None,
|
||||
min_column: None,
|
||||
show_commit_summary: false,
|
||||
..Default::default()
|
||||
});
|
||||
cx_a.update(|cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
|
|
|
@ -20,6 +20,7 @@ pub struct EditorSettings {
|
|||
pub lsp_highlight_debounce: u64,
|
||||
pub hover_popover_enabled: bool,
|
||||
pub hover_popover_delay: u64,
|
||||
pub status_bar: StatusBar,
|
||||
pub toolbar: Toolbar,
|
||||
pub scrollbar: Scrollbar,
|
||||
pub minimap: Minimap,
|
||||
|
@ -125,6 +126,14 @@ pub struct JupyterContent {
|
|||
pub enabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
pub struct StatusBar {
|
||||
/// Whether to display the active language button in the status bar.
|
||||
///
|
||||
/// Default: true
|
||||
pub active_language_button: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
pub struct Toolbar {
|
||||
pub breadcrumbs: bool,
|
||||
|
@ -440,6 +449,8 @@ pub struct EditorSettingsContent {
|
|||
///
|
||||
/// Default: 300
|
||||
pub hover_popover_delay: Option<u64>,
|
||||
/// Status bar related settings
|
||||
pub status_bar: Option<StatusBarContent>,
|
||||
/// Toolbar related settings
|
||||
pub toolbar: Option<ToolbarContent>,
|
||||
/// Scrollbar related settings
|
||||
|
@ -567,6 +578,15 @@ pub struct EditorSettingsContent {
|
|||
pub lsp_document_colors: Option<DocumentColorsRenderMode>,
|
||||
}
|
||||
|
||||
// Status bar related settings
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
pub struct StatusBarContent {
|
||||
/// Whether to display the active language button in the status bar.
|
||||
///
|
||||
/// Default: true
|
||||
pub active_language_button: Option<bool>,
|
||||
}
|
||||
|
||||
// Toolbar related settings
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
pub struct ToolbarContent {
|
||||
|
|
|
@ -86,8 +86,6 @@ use util::post_inc;
|
|||
use util::{RangeExt, ResultExt, debug_panic};
|
||||
use workspace::{CollaboratorId, Workspace, item::Item, notifications::NotifyTaskExt};
|
||||
|
||||
const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 7.;
|
||||
|
||||
/// Determines what kinds of highlights should be applied to a lines background.
|
||||
#[derive(Clone, Copy, Default)]
|
||||
struct LineHighlightSpec {
|
||||
|
@ -2428,10 +2426,13 @@ impl EditorElement {
|
|||
let editor = self.editor.read(cx);
|
||||
let blame = editor.blame.clone()?;
|
||||
let padding = {
|
||||
const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 6.;
|
||||
const INLINE_ACCEPT_SUGGESTION_EM_WIDTHS: f32 = 14.;
|
||||
|
||||
let mut padding = INLINE_BLAME_PADDING_EM_WIDTHS;
|
||||
let mut padding = ProjectSettings::get_global(cx)
|
||||
.git
|
||||
.inline_blame
|
||||
.unwrap_or_default()
|
||||
.padding as f32;
|
||||
|
||||
if let Some(edit_prediction) = editor.active_edit_prediction.as_ref() {
|
||||
match &edit_prediction.completion {
|
||||
|
@ -2469,7 +2470,7 @@ impl EditorElement {
|
|||
let min_column_in_pixels = ProjectSettings::get_global(cx)
|
||||
.git
|
||||
.inline_blame
|
||||
.and_then(|settings| settings.min_column)
|
||||
.map(|settings| settings.min_column)
|
||||
.map(|col| self.column_pixels(col as usize, window))
|
||||
.unwrap_or(px(0.));
|
||||
let min_start = content_origin.x - scroll_pixel_position.x + min_column_in_pixels;
|
||||
|
@ -8030,12 +8031,20 @@ impl Element for EditorElement {
|
|||
autoscroll_containing_element,
|
||||
needs_horizontal_autoscroll,
|
||||
) = self.editor.update(cx, |editor, cx| {
|
||||
let autoscroll_request = editor.autoscroll_request();
|
||||
let autoscroll_request = editor.scroll_manager.take_autoscroll_request();
|
||||
|
||||
let autoscroll_containing_element =
|
||||
autoscroll_request.is_some() || editor.has_pending_selection();
|
||||
|
||||
let (needs_horizontal_autoscroll, was_scrolled) = editor
|
||||
.autoscroll_vertically(bounds, line_height, max_scroll_top, window, cx);
|
||||
.autoscroll_vertically(
|
||||
bounds,
|
||||
line_height,
|
||||
max_scroll_top,
|
||||
autoscroll_request,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
if was_scrolled.0 {
|
||||
snapshot = editor.snapshot(window, cx);
|
||||
}
|
||||
|
@ -8357,7 +8366,13 @@ impl Element for EditorElement {
|
|||
})
|
||||
.flatten()?;
|
||||
let mut element = render_inline_blame_entry(blame_entry, &style, cx)?;
|
||||
let inline_blame_padding = INLINE_BLAME_PADDING_EM_WIDTHS * em_advance;
|
||||
let inline_blame_padding = ProjectSettings::get_global(cx)
|
||||
.git
|
||||
.inline_blame
|
||||
.unwrap_or_default()
|
||||
.padding
|
||||
as f32
|
||||
* em_advance;
|
||||
Some(
|
||||
element
|
||||
.layout_as_root(AvailableSpace::min_size(), window, cx)
|
||||
|
@ -8425,7 +8440,11 @@ impl Element for EditorElement {
|
|||
Ok(blocks) => blocks,
|
||||
Err(resized_blocks) => {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.resize_blocks(resized_blocks, autoscroll_request, cx)
|
||||
editor.resize_blocks(
|
||||
resized_blocks,
|
||||
autoscroll_request.map(|(autoscroll, _)| autoscroll),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
return self.prepaint(None, _inspector_id, bounds, &mut (), window, cx);
|
||||
}
|
||||
|
@ -8470,6 +8489,7 @@ impl Element for EditorElement {
|
|||
scroll_width,
|
||||
em_advance,
|
||||
&line_layouts,
|
||||
autoscroll_request,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
|
|
|
@ -348,8 +348,8 @@ impl ScrollManager {
|
|||
self.show_scrollbars
|
||||
}
|
||||
|
||||
pub fn autoscroll_request(&self) -> Option<Autoscroll> {
|
||||
self.autoscroll_request.map(|(autoscroll, _)| autoscroll)
|
||||
pub fn take_autoscroll_request(&mut self) -> Option<(Autoscroll, bool)> {
|
||||
self.autoscroll_request.take()
|
||||
}
|
||||
|
||||
pub fn active_scrollbar_state(&self) -> Option<&ActiveScrollbarState> {
|
||||
|
|
|
@ -102,15 +102,12 @@ impl AutoscrollStrategy {
|
|||
pub(crate) struct NeedsHorizontalAutoscroll(pub(crate) bool);
|
||||
|
||||
impl Editor {
|
||||
pub fn autoscroll_request(&self) -> Option<Autoscroll> {
|
||||
self.scroll_manager.autoscroll_request()
|
||||
}
|
||||
|
||||
pub(crate) fn autoscroll_vertically(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
line_height: Pixels,
|
||||
max_scroll_top: f32,
|
||||
autoscroll_request: Option<(Autoscroll, bool)>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) -> (NeedsHorizontalAutoscroll, WasScrolled) {
|
||||
|
@ -137,7 +134,7 @@ impl Editor {
|
|||
WasScrolled(false)
|
||||
};
|
||||
|
||||
let Some((autoscroll, local)) = self.scroll_manager.autoscroll_request.take() else {
|
||||
let Some((autoscroll, local)) = autoscroll_request else {
|
||||
return (NeedsHorizontalAutoscroll(false), editor_was_scrolled);
|
||||
};
|
||||
|
||||
|
@ -284,9 +281,12 @@ impl Editor {
|
|||
scroll_width: Pixels,
|
||||
em_advance: Pixels,
|
||||
layouts: &[LineWithInvisibles],
|
||||
autoscroll_request: Option<(Autoscroll, bool)>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<gpui::Point<f32>> {
|
||||
let (_, local) = autoscroll_request?;
|
||||
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let selections = self.selections.all::<Point>(cx);
|
||||
let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
|
||||
|
@ -335,10 +335,10 @@ impl Editor {
|
|||
|
||||
let was_scrolled = if target_left < scroll_left {
|
||||
scroll_position.x = target_left / em_advance;
|
||||
self.set_scroll_position_internal(scroll_position, true, true, window, cx)
|
||||
self.set_scroll_position_internal(scroll_position, local, true, window, cx)
|
||||
} else if target_right > scroll_right {
|
||||
scroll_position.x = (target_right - viewport_width) / em_advance;
|
||||
self.set_scroll_position_internal(scroll_position, true, true, window, cx)
|
||||
self.set_scroll_position_internal(scroll_position, local, true, window, cx)
|
||||
} else {
|
||||
WasScrolled(false)
|
||||
};
|
||||
|
|
|
@ -402,11 +402,11 @@ impl GitRepository for FakeGitRepository {
|
|||
&self,
|
||||
_paths: Vec<RepoPath>,
|
||||
_env: Arc<HashMap<String, String>>,
|
||||
) -> BoxFuture<'_, Result<()>> {
|
||||
) -> BoxFuture<Result<()>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stash_pop(&self, _env: Arc<HashMap<String, String>>) -> BoxFuture<'_, Result<()>> {
|
||||
fn stash_pop(&self, _env: Arc<HashMap<String, String>>) -> BoxFuture<Result<()>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
|
|
@ -399,9 +399,9 @@ pub trait GitRepository: Send + Sync {
|
|||
&self,
|
||||
paths: Vec<RepoPath>,
|
||||
env: Arc<HashMap<String, String>>,
|
||||
) -> BoxFuture<'_, Result<()>>;
|
||||
) -> BoxFuture<Result<()>>;
|
||||
|
||||
fn stash_pop(&self, env: Arc<HashMap<String, String>>) -> BoxFuture<'_, Result<()>>;
|
||||
fn stash_pop(&self, env: Arc<HashMap<String, String>>) -> BoxFuture<Result<()>>;
|
||||
|
||||
fn push(
|
||||
&self,
|
||||
|
@ -1203,7 +1203,7 @@ impl GitRepository for RealGitRepository {
|
|||
&self,
|
||||
paths: Vec<RepoPath>,
|
||||
env: Arc<HashMap<String, String>>,
|
||||
) -> BoxFuture<'_, Result<()>> {
|
||||
) -> BoxFuture<Result<()>> {
|
||||
let working_directory = self.working_directory();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
|
@ -1227,7 +1227,7 @@ impl GitRepository for RealGitRepository {
|
|||
.boxed()
|
||||
}
|
||||
|
||||
fn stash_pop(&self, env: Arc<HashMap<String, String>>) -> BoxFuture<'_, Result<()>> {
|
||||
fn stash_pop(&self, env: Arc<HashMap<String, String>>) -> BoxFuture<Result<()>> {
|
||||
let working_directory = self.working_directory();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
|
|
|
@ -1,12 +1,22 @@
|
|||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use regex::Regex;
|
||||
use url::Url;
|
||||
|
||||
use git::{
|
||||
BuildCommitPermalinkParams, BuildPermalinkParams, GitHostingProvider, ParsedGitRemote,
|
||||
RemoteUrl,
|
||||
PullRequest, RemoteUrl,
|
||||
};
|
||||
|
||||
fn pull_request_regex() -> &'static Regex {
|
||||
static PULL_REQUEST_REGEX: LazyLock<Regex> = LazyLock::new(|| {
|
||||
// This matches Bitbucket PR reference pattern: (pull request #xxx)
|
||||
Regex::new(r"\(pull request #(\d+)\)").unwrap()
|
||||
});
|
||||
&PULL_REQUEST_REGEX
|
||||
}
|
||||
|
||||
pub struct Bitbucket {
|
||||
name: String,
|
||||
base_url: Url,
|
||||
|
@ -96,6 +106,22 @@ impl GitHostingProvider for Bitbucket {
|
|||
);
|
||||
permalink
|
||||
}
|
||||
|
||||
fn extract_pull_request(&self, remote: &ParsedGitRemote, message: &str) -> Option<PullRequest> {
|
||||
// Check first line of commit message for PR references
|
||||
let first_line = message.lines().next()?;
|
||||
|
||||
// Try to match against our PR patterns
|
||||
let capture = pull_request_regex().captures(first_line)?;
|
||||
let number = capture.get(1)?.as_str().parse::<u32>().ok()?;
|
||||
|
||||
// Construct the PR URL in Bitbucket format
|
||||
let mut url = self.base_url();
|
||||
let path = format!("/{}/{}/pull-requests/{}", remote.owner, remote.repo, number);
|
||||
url.set_path(&path);
|
||||
|
||||
Some(PullRequest { number, url })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -203,4 +229,34 @@ mod tests {
|
|||
"https://bitbucket.org/zed-industries/zed/src/f00b4r/main.rs#lines-24:48";
|
||||
assert_eq!(permalink.to_string(), expected_url.to_string())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bitbucket_pull_requests() {
|
||||
use indoc::indoc;
|
||||
|
||||
let remote = ParsedGitRemote {
|
||||
owner: "zed-industries".into(),
|
||||
repo: "zed".into(),
|
||||
};
|
||||
|
||||
let bitbucket = Bitbucket::public_instance();
|
||||
|
||||
// Test message without PR reference
|
||||
let message = "This does not contain a pull request";
|
||||
assert!(bitbucket.extract_pull_request(&remote, message).is_none());
|
||||
|
||||
// Pull request number at end of first line
|
||||
let message = indoc! {r#"
|
||||
Merged in feature-branch (pull request #123)
|
||||
|
||||
Some detailed description of the changes.
|
||||
"#};
|
||||
|
||||
let pr = bitbucket.extract_pull_request(&remote, message).unwrap();
|
||||
assert_eq!(pr.number, 123);
|
||||
assert_eq!(
|
||||
pr.url.as_str(),
|
||||
"https://bitbucket.org/zed-industries/zed/pull-requests/123"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -461,8 +461,6 @@ fn skip_whitespace(source: &str) -> &str {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::slice;
|
||||
|
||||
use super::*;
|
||||
use crate as gpui;
|
||||
use KeyBindingContextPredicate::*;
|
||||
|
@ -676,11 +674,11 @@ mod tests {
|
|||
assert!(predicate.eval(&contexts));
|
||||
|
||||
assert!(!predicate.eval(&[]));
|
||||
assert!(!predicate.eval(slice::from_ref(&child_context)));
|
||||
assert!(!predicate.eval(&[child_context.clone()]));
|
||||
assert!(!predicate.eval(&[parent_context]));
|
||||
|
||||
let zany_predicate = KeyBindingContextPredicate::parse("child > child").unwrap();
|
||||
assert!(!zany_predicate.eval(slice::from_ref(&child_context)));
|
||||
assert!(!zany_predicate.eval(&[child_context.clone()]));
|
||||
assert!(zany_predicate.eval(&[child_context.clone(), child_context.clone()]));
|
||||
}
|
||||
|
||||
|
@ -692,13 +690,13 @@ mod tests {
|
|||
let parent_context = KeyContext::try_from("parent").unwrap();
|
||||
let child_context = KeyContext::try_from("child").unwrap();
|
||||
|
||||
assert!(not_predicate.eval(slice::from_ref(&workspace_context)));
|
||||
assert!(!not_predicate.eval(slice::from_ref(&editor_context)));
|
||||
assert!(not_predicate.eval(&[workspace_context.clone()]));
|
||||
assert!(!not_predicate.eval(&[editor_context.clone()]));
|
||||
assert!(!not_predicate.eval(&[editor_context.clone(), workspace_context.clone()]));
|
||||
assert!(!not_predicate.eval(&[workspace_context.clone(), editor_context.clone()]));
|
||||
|
||||
let complex_not = KeyBindingContextPredicate::parse("!editor && workspace").unwrap();
|
||||
assert!(complex_not.eval(slice::from_ref(&workspace_context)));
|
||||
assert!(complex_not.eval(&[workspace_context.clone()]));
|
||||
assert!(!complex_not.eval(&[editor_context.clone(), workspace_context.clone()]));
|
||||
|
||||
let not_mode_predicate = KeyBindingContextPredicate::parse("!(mode == full)").unwrap();
|
||||
|
@ -711,18 +709,18 @@ mod tests {
|
|||
assert!(not_mode_predicate.eval(&[other_mode_context]));
|
||||
|
||||
let not_descendant = KeyBindingContextPredicate::parse("!(parent > child)").unwrap();
|
||||
assert!(not_descendant.eval(slice::from_ref(&parent_context)));
|
||||
assert!(not_descendant.eval(slice::from_ref(&child_context)));
|
||||
assert!(not_descendant.eval(&[parent_context.clone()]));
|
||||
assert!(not_descendant.eval(&[child_context.clone()]));
|
||||
assert!(!not_descendant.eval(&[parent_context.clone(), child_context.clone()]));
|
||||
|
||||
let not_descendant = KeyBindingContextPredicate::parse("parent > !child").unwrap();
|
||||
assert!(!not_descendant.eval(slice::from_ref(&parent_context)));
|
||||
assert!(!not_descendant.eval(slice::from_ref(&child_context)));
|
||||
assert!(!not_descendant.eval(&[parent_context.clone()]));
|
||||
assert!(!not_descendant.eval(&[child_context.clone()]));
|
||||
assert!(!not_descendant.eval(&[parent_context.clone(), child_context.clone()]));
|
||||
|
||||
let double_not = KeyBindingContextPredicate::parse("!!editor").unwrap();
|
||||
assert!(double_not.eval(slice::from_ref(&editor_context)));
|
||||
assert!(!double_not.eval(slice::from_ref(&workspace_context)));
|
||||
assert!(double_not.eval(&[editor_context.clone()]));
|
||||
assert!(!double_not.eval(&[workspace_context.clone()]));
|
||||
|
||||
// Test complex descendant cases
|
||||
let workspace_context = KeyContext::try_from("Workspace").unwrap();
|
||||
|
@ -756,9 +754,9 @@ mod tests {
|
|||
|
||||
// !Workspace - shouldn't match when Workspace is in the context
|
||||
let not_workspace = KeyBindingContextPredicate::parse("!Workspace").unwrap();
|
||||
assert!(!not_workspace.eval(slice::from_ref(&workspace_context)));
|
||||
assert!(not_workspace.eval(slice::from_ref(&pane_context)));
|
||||
assert!(not_workspace.eval(slice::from_ref(&editor_context)));
|
||||
assert!(!not_workspace.eval(&[workspace_context.clone()]));
|
||||
assert!(not_workspace.eval(&[pane_context.clone()]));
|
||||
assert!(not_workspace.eval(&[editor_context.clone()]));
|
||||
assert!(!not_workspace.eval(&workspace_pane_editor));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,28 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use windows::Win32::UI::WindowsAndMessaging::HCURSOR;
|
||||
use windows::Win32::{Foundation::HANDLE, UI::WindowsAndMessaging::HCURSOR};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct SafeHandle {
|
||||
raw: HANDLE,
|
||||
}
|
||||
|
||||
unsafe impl Send for SafeHandle {}
|
||||
unsafe impl Sync for SafeHandle {}
|
||||
|
||||
impl From<HANDLE> for SafeHandle {
|
||||
fn from(value: HANDLE) -> Self {
|
||||
SafeHandle { raw: value }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SafeHandle {
|
||||
type Target = HANDLE;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.raw
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct SafeCursor {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use editor::Editor;
|
||||
use editor::{Editor, EditorSettings};
|
||||
use gpui::{
|
||||
Context, Entity, IntoElement, ParentElement, Render, Subscription, WeakEntity, Window, div,
|
||||
};
|
||||
use language::LanguageName;
|
||||
use settings::Settings as _;
|
||||
use ui::{Button, ButtonCommon, Clickable, FluentBuilder, LabelSize, Tooltip};
|
||||
use workspace::{StatusItemView, Workspace, item::ItemHandle};
|
||||
|
||||
|
@ -39,6 +40,13 @@ impl ActiveBufferLanguage {
|
|||
|
||||
impl Render for ActiveBufferLanguage {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
if !EditorSettings::get_global(cx)
|
||||
.status_bar
|
||||
.active_language_button
|
||||
{
|
||||
return div();
|
||||
}
|
||||
|
||||
div().when_some(self.active_language.as_ref(), |el, active_language| {
|
||||
let active_language_text = if let Some(active_language_text) = active_language {
|
||||
active_language_text.to_string()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name = "C++"
|
||||
grammar = "cpp"
|
||||
path_suffixes = ["cc", "hh", "cpp", "h", "hpp", "cxx", "hxx", "c++", "ipp", "inl", "ixx", "cu", "cuh", "C", "H"]
|
||||
path_suffixes = ["cc", "hh", "cpp", "h", "hpp", "cxx", "hxx", "c++", "ipp", "inl", "ino", "ixx", "cu", "cuh", "C", "H"]
|
||||
line_comments = ["// ", "/// ", "//! "]
|
||||
decrease_indent_patterns = [
|
||||
{ pattern = "^\\s*\\{.*\\}?\\s*$", valid_after = ["if", "for", "while", "do", "switch", "else"] },
|
||||
|
|
|
@ -146,6 +146,7 @@
|
|||
"&&="
|
||||
"||="
|
||||
"??="
|
||||
"..."
|
||||
] @operator
|
||||
|
||||
(regex "/" @string.regex)
|
||||
|
|
|
@ -146,6 +146,7 @@
|
|||
"&&="
|
||||
"||="
|
||||
"??="
|
||||
"..."
|
||||
] @operator
|
||||
|
||||
(regex "/" @string.regex)
|
||||
|
|
|
@ -167,6 +167,7 @@
|
|||
"&&="
|
||||
"||="
|
||||
"??="
|
||||
"..."
|
||||
] @operator
|
||||
|
||||
(regex "/" @string.regex)
|
||||
|
|
|
@ -431,10 +431,9 @@ impl GitSettings {
|
|||
|
||||
pub fn inline_blame_delay(&self) -> Option<Duration> {
|
||||
match self.inline_blame {
|
||||
Some(InlineBlameSettings {
|
||||
delay_ms: Some(delay_ms),
|
||||
..
|
||||
}) if delay_ms > 0 => Some(Duration::from_millis(delay_ms)),
|
||||
Some(InlineBlameSettings { delay_ms, .. }) if delay_ms > 0 => {
|
||||
Some(Duration::from_millis(delay_ms))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -470,7 +469,7 @@ pub enum GitGutterSetting {
|
|||
Hide,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct InlineBlameSettings {
|
||||
/// Whether or not to show git blame data inline in
|
||||
|
@ -483,11 +482,19 @@ pub struct InlineBlameSettings {
|
|||
/// after a delay once the cursor stops moving.
|
||||
///
|
||||
/// Default: 0
|
||||
pub delay_ms: Option<u64>,
|
||||
#[serde(default)]
|
||||
pub delay_ms: u64,
|
||||
/// The amount of padding between the end of the source line and the start
|
||||
/// of the inline blame in units of columns.
|
||||
///
|
||||
/// Default: 7
|
||||
#[serde(default = "default_inline_blame_padding")]
|
||||
pub padding: u32,
|
||||
/// The minimum column number to show the inline blame information at
|
||||
///
|
||||
/// Default: 0
|
||||
pub min_column: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub min_column: u32,
|
||||
/// Whether to show commit summary as part of the inline blame.
|
||||
///
|
||||
/// Default: false
|
||||
|
@ -495,6 +502,22 @@ pub struct InlineBlameSettings {
|
|||
pub show_commit_summary: bool,
|
||||
}
|
||||
|
||||
fn default_inline_blame_padding() -> u32 {
|
||||
7
|
||||
}
|
||||
|
||||
impl Default for InlineBlameSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
delay_ms: 0,
|
||||
padding: default_inline_blame_padding(),
|
||||
min_column: 0,
|
||||
show_commit_summary: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
pub struct BinarySettings {
|
||||
pub path: Option<String>,
|
||||
|
|
|
@ -5625,6 +5625,10 @@ impl Panel for ProjectPanel {
|
|||
}
|
||||
|
||||
fn starts_open(&self, _: &Window, cx: &App) -> bool {
|
||||
if !ProjectPanelSettings::get_global(cx).starts_open {
|
||||
return false;
|
||||
}
|
||||
|
||||
let project = &self.project.read(cx);
|
||||
project.visible_worktrees(cx).any(|tree| {
|
||||
tree.read(cx)
|
||||
|
|
|
@ -43,6 +43,7 @@ pub struct ProjectPanelSettings {
|
|||
pub sticky_scroll: bool,
|
||||
pub auto_reveal_entries: bool,
|
||||
pub auto_fold_dirs: bool,
|
||||
pub starts_open: bool,
|
||||
pub scrollbar: ScrollbarSettings,
|
||||
pub show_diagnostics: ShowDiagnostics,
|
||||
pub hide_root: bool,
|
||||
|
@ -139,6 +140,10 @@ pub struct ProjectPanelSettingsContent {
|
|||
///
|
||||
/// Default: true
|
||||
pub auto_fold_dirs: Option<bool>,
|
||||
/// Whether the project panel should open on startup.
|
||||
///
|
||||
/// Default: true
|
||||
pub starts_open: Option<bool>,
|
||||
/// Scrollbar-related settings
|
||||
pub scrollbar: Option<ScrollbarSettingsContent>,
|
||||
/// Which files containing diagnostic errors/warnings to mark in the project panel.
|
||||
|
|
|
@ -136,7 +136,7 @@ impl BatchedTextRun {
|
|||
.shape_line(
|
||||
self.text.clone().into(),
|
||||
self.font_size.to_pixels(window.rem_size()),
|
||||
std::slice::from_ref(&self.style),
|
||||
&[self.style.clone()],
|
||||
Some(dimensions.cell_width),
|
||||
)
|
||||
.paint(pos, dimensions.line_height, window, cx);
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 146 KiB |
|
@ -1275,6 +1275,18 @@ Each option controls displaying of a particular toolbar element. If all elements
|
|||
|
||||
`boolean` values
|
||||
|
||||
## Status Bar
|
||||
|
||||
- Description: Control various elements in the status bar. Note that some items in the status bar have their own settings set elsewhere.
|
||||
- Setting: `status_bar`
|
||||
- Default:
|
||||
|
||||
```json
|
||||
"status_bar": {
|
||||
"active_language_button": true,
|
||||
},
|
||||
```
|
||||
|
||||
## LSP
|
||||
|
||||
- Description: Configuration for language servers.
|
||||
|
@ -1795,7 +1807,6 @@ Example:
|
|||
{
|
||||
"git": {
|
||||
"inline_blame": {
|
||||
"enabled": true,
|
||||
"delay_ms": 500
|
||||
}
|
||||
}
|
||||
|
@ -1808,7 +1819,6 @@ Example:
|
|||
{
|
||||
"git": {
|
||||
"inline_blame": {
|
||||
"enabled": true,
|
||||
"show_commit_summary": true
|
||||
}
|
||||
}
|
||||
|
@ -1821,13 +1831,24 @@ Example:
|
|||
{
|
||||
"git": {
|
||||
"inline_blame": {
|
||||
"enabled": true,
|
||||
"min_column": 80
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. Set the padding between the end of the line and the inline blame hint, in ems:
|
||||
|
||||
```json
|
||||
{
|
||||
"git": {
|
||||
"inline_blame": {
|
||||
"padding": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Hunk Style
|
||||
|
||||
- Description: What styling we should use for the diff hunks.
|
||||
|
@ -3204,7 +3225,8 @@ Run the `theme selector: toggle` action in the command palette to see a current
|
|||
"indent_guides": {
|
||||
"show": "always"
|
||||
},
|
||||
"hide_root": false
|
||||
"hide_root": false,
|
||||
"starts_open": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -223,6 +223,7 @@ TBD: Centered layout related settings
|
|||
"enabled": true, // Show/hide inline blame
|
||||
"delay": 0, // Show after delay (ms)
|
||||
"min_column": 0, // Minimum column to inline display blame
|
||||
"padding": 7, // Padding between code and inline blame (em)
|
||||
"show_commit_summary": false // Show/hide commit summary
|
||||
},
|
||||
"hunk_style": "staged_hollow" // staged_hollow, unstaged_hollow
|
||||
|
@ -305,6 +306,17 @@ TBD: Centered layout related settings
|
|||
}
|
||||
```
|
||||
|
||||
### Status Bar
|
||||
|
||||
```json
|
||||
"status_bar": {
|
||||
// Show/hide a button that displays the active buffer's language.
|
||||
// Clicking the button brings up the language selector.
|
||||
// Defaults to true.
|
||||
"active_language_button": true,
|
||||
},
|
||||
```
|
||||
|
||||
### Multibuffer
|
||||
|
||||
```json
|
||||
|
|
|
@ -9,7 +9,7 @@ repository = "https://github.com/zed-industries/zed"
|
|||
[language_servers.emmet-language-server]
|
||||
name = "Emmet Language Server"
|
||||
language = "HTML"
|
||||
languages = ["HTML", "PHP", "ERB", "HTML/ERB", "JavaScript", "TSX", "CSS", "HEEX", "Elixir"]
|
||||
languages = ["HTML", "PHP", "ERB", "HTML/ERB", "JavaScript", "TSX", "CSS", "HEEX", "Elixir", "Vue.js"]
|
||||
|
||||
[language_servers.emmet-language-server.language_ids]
|
||||
"HTML" = "html"
|
||||
|
@ -21,3 +21,4 @@ languages = ["HTML", "PHP", "ERB", "HTML/ERB", "JavaScript", "TSX", "CSS", "HEEX
|
|||
"CSS" = "css"
|
||||
"HEEX" = "heex"
|
||||
"Elixir" = "heex"
|
||||
"Vue.js" = "vue"
|
||||
|
|
18
flake.lock
generated
18
flake.lock
generated
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"crane": {
|
||||
"locked": {
|
||||
"lastModified": 1754269165,
|
||||
"narHash": "sha256-0tcS8FHd4QjbCVoxN9jI+PjHgA4vc/IjkUSp+N3zy0U=",
|
||||
"lastModified": 1750266157,
|
||||
"narHash": "sha256-tL42YoNg9y30u7zAqtoGDNdTyXTi8EALDeCB13FtbQA=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "444e81206df3f7d92780680e45858e31d2f07a08",
|
||||
"rev": "e37c943371b73ed87faf33f7583860f81f1d5a48",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -33,10 +33,10 @@
|
|||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 315532800,
|
||||
"narHash": "sha256-5VYevX3GccubYeccRGAXvCPA1ktrGmIX1IFC0icX07g=",
|
||||
"rev": "a683adc19ff5228af548c6539dbc3440509bfed3",
|
||||
"narHash": "sha256-j+zO+IHQ7VwEam0pjPExdbLT2rVioyVS3iq4bLO3GEc=",
|
||||
"rev": "61c0f513911459945e2cb8bf333dc849f1b976ff",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre840248.a683adc19ff5/nixexprs.tar.xz"
|
||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre821324.61c0f5139114/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
|
@ -58,11 +58,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1754575663,
|
||||
"narHash": "sha256-afOx8AG0KYtw7mlt6s6ahBBy7eEHZwws3iCRoiuRQS4=",
|
||||
"lastModified": 1750964660,
|
||||
"narHash": "sha256-YQ6EyFetjH1uy5JhdhRdPe6cuNXlYpMAQePFfZj4W7M=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "6db0fb0e9cec2e9729dc52bf4898e6c135bb8a0f",
|
||||
"rev": "04f0fcfb1a50c63529805a798b4b5c21610ff390",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[toolchain]
|
||||
channel = "1.89"
|
||||
channel = "1.88"
|
||||
profile = "minimal"
|
||||
components = [ "rustfmt", "clippy" ]
|
||||
targets = [
|
||||
|
|
|
@ -9,7 +9,12 @@ main() {
|
|||
platform="$(uname -s)"
|
||||
arch="$(uname -m)"
|
||||
channel="${ZED_CHANNEL:-stable}"
|
||||
temp="$(mktemp -d "/tmp/zed-XXXXXX")"
|
||||
# Use TMPDIR if available (for environments with non-standard temp directories)
|
||||
if [ -n "${TMPDIR:-}" ] && [ -d "${TMPDIR}" ]; then
|
||||
temp="$(mktemp -d "$TMPDIR/zed-XXXXXX")"
|
||||
else
|
||||
temp="$(mktemp -d "/tmp/zed-XXXXXX")"
|
||||
fi
|
||||
|
||||
if [ "$platform" = "Darwin" ]; then
|
||||
platform="macos"
|
||||
|
|
|
@ -5,7 +5,7 @@ function export_vars_for_environment {
|
|||
echo "Invalid environment name '${environment}'" >&2
|
||||
exit 1
|
||||
fi
|
||||
export $(cat $env_file)
|
||||
export $(grep -v '^#' $env_file | grep -v '^[[:space:]]*$')
|
||||
}
|
||||
|
||||
function target_zed_kube_cluster {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue