Compare commits
14 Commits
fix-ep-can
...
project-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6f43ba45b | ||
|
|
dc8150d410 | ||
|
|
b9d84e5eef | ||
|
|
1a23115773 | ||
|
|
757c043171 | ||
|
|
57e1bb8106 | ||
|
|
5403e74bbd | ||
|
|
0713ddcabc | ||
|
|
6fbbc89904 | ||
|
|
8aa53612fd | ||
|
|
6a311cad11 | ||
|
|
51e97d343d | ||
|
|
c36b12f3b2 | ||
|
|
7c724c0f10 |
136
.github/workflows/extension_bump.yml
vendored
Normal file
136
.github/workflows/extension_bump.yml
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
# Generated from xtask::workflows::extension_bump
|
||||
# Rebuild with `cargo xtask workflows`.
|
||||
name: extension_bump
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_BACKTRACE: '1'
|
||||
CARGO_INCREMENTAL: '0'
|
||||
ZED_EXTENSION_CLI_SHA: 7cfce605704d41ca247e3f84804bf323f6c6caaf
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
bump-type:
|
||||
description: bump-type
|
||||
type: string
|
||||
default: patch
|
||||
secrets:
|
||||
app-id:
|
||||
description: The app ID used to create the PR
|
||||
required: true
|
||||
app-secret:
|
||||
description: The app secret for the corresponding app ID
|
||||
required: true
|
||||
jobs:
|
||||
check_extension:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
clean: false
|
||||
- id: cache-zed-extension-cli
|
||||
name: extension_tests::cache_zed_extension_cli
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
|
||||
with:
|
||||
path: zed-extension
|
||||
key: zed-extension-${{ env.ZED_EXTENSION_CLI_SHA }}
|
||||
- name: extension_tests::download_zed_extension_cli
|
||||
if: steps.cache-zed-extension-cli.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
wget --quiet "https://zed-extension-cli.nyc3.digitaloceanspaces.com/$ZED_EXTENSION_CLI_SHA/x86_64-unknown-linux-gnu/zed-extension"
|
||||
chmod +x zed-extension
|
||||
shell: bash -euxo pipefail {0}
|
||||
- name: extension_tests::check
|
||||
run: |
|
||||
mkdir -p /tmp/ext-scratch
|
||||
mkdir -p /tmp/ext-output
|
||||
./zed-extension --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output
|
||||
shell: bash -euxo pipefail {0}
|
||||
timeout-minutes: 1
|
||||
check_bump_needed:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
clean: false
|
||||
fetch-depth: 10
|
||||
- id: compare-versions-check
|
||||
name: extension_bump::compare_versions
|
||||
run: |+
|
||||
CURRENT_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)"
|
||||
|
||||
git checkout "$(git log -1 --format=%H)"~1
|
||||
|
||||
PREV_COMMIT_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)"
|
||||
|
||||
[[ "$CURRENT_VERSION" == "$PREV_COMMIT_VERSION" ]] && \
|
||||
echo "needs_bump=true" >> "$GITHUB_OUTPUT" || \
|
||||
echo "needs_bump=false" >> "$GITHUB_OUTPUT"
|
||||
|
||||
shell: bash -euxo pipefail {0}
|
||||
outputs:
|
||||
needs_bump: ${{ steps.compare-versions-check.outputs.needs_bump }}
|
||||
timeout-minutes: 1
|
||||
bump_extension_version:
|
||||
needs:
|
||||
- check_extension
|
||||
- check_bump_needed
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && needs.check_bump_needed.outputs.needs_bump == 'true'
|
||||
runs-on: namespace-profile-8x16-ubuntu-2204
|
||||
steps:
|
||||
- id: generate-token
|
||||
name: extension_bump::generate_token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ secrets.app-id }}
|
||||
private-key: ${{ secrets.app-secret }}
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
clean: false
|
||||
- name: extension_bump::install_bump_2_version
|
||||
run: pip install bump2version
|
||||
shell: bash -euxo pipefail {0}
|
||||
- id: bump-version
|
||||
name: extension_bump::bump_version
|
||||
run: |
|
||||
OLD_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)"
|
||||
|
||||
cat <<EOF > .bumpversion.cfg
|
||||
[bumpversion]
|
||||
current_version = "$OLD_VERSION"
|
||||
|
||||
[bumpversion:file:Cargo.toml]
|
||||
|
||||
[bumpversion:file:extension.toml]
|
||||
|
||||
EOF
|
||||
|
||||
bump2version --verbose ${{ inputs.bump-type }}
|
||||
NEW_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)"
|
||||
cargo update --workspace
|
||||
|
||||
rm .bumpversion.cfg
|
||||
|
||||
echo "old_version=${OLD_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
shell: bash -euxo pipefail {0}
|
||||
- name: extension_bump::create_pull_request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
title: Bump version to ${{ steps.bump-version.outputs.new_version }}
|
||||
body: This PR bumps the version of this extension to v${{ steps.bump-version.outputs.new_version }}
|
||||
commit-message: Bump version to v${{ steps.bump-version.outputs.new_version }}
|
||||
branch: bump-from-${{ steps.bump-version.outputs.old_version }}
|
||||
committer: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
|
||||
base: main
|
||||
delete-branch: true
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
sign-commits: true
|
||||
timeout-minutes: 1
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
|
||||
cancel-in-progress: true
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -21192,7 +21192,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.215.0"
|
||||
version = "0.216.0"
|
||||
dependencies = [
|
||||
"acp_tools",
|
||||
"activity_indicator",
|
||||
|
||||
@@ -528,7 +528,7 @@ impl Render for AcpTools {
|
||||
.with_sizing_behavior(gpui::ListSizingBehavior::Auto)
|
||||
.size_full(),
|
||||
)
|
||||
.vertical_scrollbar_for(connection.list_state.clone(), window, cx)
|
||||
.vertical_scrollbar_for(&connection.list_state, window, cx)
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,4 +424,20 @@ impl ThreadsDatabase {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn delete_threads(&self) -> Task<Result<()>> {
|
||||
let connection = self.connection.clone();
|
||||
|
||||
self.executor.spawn(async move {
|
||||
let connection = connection.lock();
|
||||
|
||||
let mut delete = connection.exec_bound::<()>(indoc! {"
|
||||
DELETE FROM threads
|
||||
"})?;
|
||||
|
||||
delete(())?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +188,15 @@ impl HistoryStore {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn delete_threads(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
|
||||
let database_future = ThreadsDatabase::connect(cx);
|
||||
cx.spawn(async move |this, cx| {
|
||||
let database = database_future.await.map_err(|err| anyhow!(err))?;
|
||||
database.delete_threads().await?;
|
||||
this.update(cx, |this, cx| this.reload(cx))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn delete_text_thread(
|
||||
&mut self,
|
||||
path: Arc<Path>,
|
||||
|
||||
@@ -1423,7 +1423,7 @@ mod tests {
|
||||
rel_path("b/eight.txt"),
|
||||
];
|
||||
|
||||
let slash = PathStyle::local().separator();
|
||||
let slash = PathStyle::local().primary_separator();
|
||||
|
||||
let mut opened_editors = Vec::new();
|
||||
for path in paths {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::acp::AcpThreadView;
|
||||
use crate::{AgentPanel, RemoveSelectedThread};
|
||||
use crate::{AgentPanel, RemoveHistory, RemoveSelectedThread};
|
||||
use agent::{HistoryEntry, HistoryStore};
|
||||
use chrono::{Datelike as _, Local, NaiveDate, TimeDelta};
|
||||
use editor::{Editor, EditorEvent};
|
||||
@@ -12,7 +12,7 @@ use std::{fmt::Display, ops::Range};
|
||||
use text::Bias;
|
||||
use time::{OffsetDateTime, UtcOffset};
|
||||
use ui::{
|
||||
HighlightedLabel, IconButtonShape, ListItem, ListItemSpacing, Tooltip, WithScrollbar,
|
||||
HighlightedLabel, IconButtonShape, ListItem, ListItemSpacing, Tab, Tooltip, WithScrollbar,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@ pub struct AcpThreadHistory {
|
||||
search_query: SharedString,
|
||||
visible_items: Vec<ListItemType>,
|
||||
local_timezone: UtcOffset,
|
||||
confirming_delete_history: bool,
|
||||
_update_task: Task<()>,
|
||||
_subscriptions: Vec<gpui::Subscription>,
|
||||
}
|
||||
@@ -98,6 +99,7 @@ impl AcpThreadHistory {
|
||||
)
|
||||
.unwrap(),
|
||||
search_query: SharedString::default(),
|
||||
confirming_delete_history: false,
|
||||
_subscriptions: vec![search_editor_subscription, history_store_subscription],
|
||||
_update_task: Task::ready(()),
|
||||
};
|
||||
@@ -331,6 +333,24 @@ impl AcpThreadHistory {
|
||||
task.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
fn remove_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.history_store.update(cx, |store, cx| {
|
||||
store.delete_threads(cx).detach_and_log_err(cx)
|
||||
});
|
||||
self.confirming_delete_history = false;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn prompt_delete_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.confirming_delete_history = true;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn cancel_delete_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.confirming_delete_history = false;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn render_list_items(
|
||||
&mut self,
|
||||
range: Range<usize>,
|
||||
@@ -447,6 +467,8 @@ impl Focusable for AcpThreadHistory {
|
||||
|
||||
impl Render for AcpThreadHistory {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let has_no_history = self.history_store.read(cx).is_empty(cx);
|
||||
|
||||
v_flex()
|
||||
.key_context("ThreadHistory")
|
||||
.size_full()
|
||||
@@ -457,9 +479,12 @@ impl Render for AcpThreadHistory {
|
||||
.on_action(cx.listener(Self::select_last))
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
.on_action(cx.listener(Self::remove_selected_thread))
|
||||
.on_action(cx.listener(|this, _: &RemoveHistory, window, cx| {
|
||||
this.remove_history(window, cx);
|
||||
}))
|
||||
.child(
|
||||
h_flex()
|
||||
.h(px(41.)) // Match the toolbar perfectly
|
||||
.h(Tab::container_height(cx))
|
||||
.w_full()
|
||||
.py_1()
|
||||
.px_2()
|
||||
@@ -481,7 +506,7 @@ impl Render for AcpThreadHistory {
|
||||
.overflow_hidden()
|
||||
.flex_grow();
|
||||
|
||||
if self.history_store.read(cx).is_empty(cx) {
|
||||
if has_no_history {
|
||||
view.justify_center().items_center().child(
|
||||
Label::new("You don't have any past threads yet.")
|
||||
.size(LabelSize::Small)
|
||||
@@ -502,16 +527,74 @@ impl Render for AcpThreadHistory {
|
||||
)
|
||||
.p_1()
|
||||
.pr_4()
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.flex_grow(),
|
||||
)
|
||||
.vertical_scrollbar_for(
|
||||
self.scroll_handle.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
|
||||
}
|
||||
})
|
||||
.when(!has_no_history, |this| {
|
||||
this.child(
|
||||
h_flex()
|
||||
.p_2()
|
||||
.border_t_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.when(!self.confirming_delete_history, |this| {
|
||||
this.child(
|
||||
Button::new("delete_history", "Delete All History")
|
||||
.full_width()
|
||||
.style(ButtonStyle::Outlined)
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.prompt_delete_history(window, cx);
|
||||
})),
|
||||
)
|
||||
})
|
||||
.when(self.confirming_delete_history, |this| {
|
||||
this.w_full()
|
||||
.gap_2()
|
||||
.flex_wrap()
|
||||
.justify_between()
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_wrap()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new("Delete all threads?")
|
||||
.size(LabelSize::Small),
|
||||
)
|
||||
.child(
|
||||
Label::new("You won't be able to recover them later.")
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
Button::new("cancel_delete", "Cancel")
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.cancel_delete_history(window, cx);
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Button::new("confirm_delete", "Delete")
|
||||
.style(ButtonStyle::Tinted(ui::TintColor::Error))
|
||||
.color(Color::Error)
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(|_, _, window, cx| {
|
||||
window.dispatch_action(
|
||||
Box::new(RemoveHistory),
|
||||
cx,
|
||||
);
|
||||
})),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3989,7 +3989,7 @@ impl AcpThreadView {
|
||||
let file = buffer.read(cx).file()?;
|
||||
let path = file.path();
|
||||
let path_style = file.path_style(cx);
|
||||
let separator = file.path_style(cx).separator();
|
||||
let separator = file.path_style(cx).primary_separator();
|
||||
|
||||
let file_path = path.parent().and_then(|parent| {
|
||||
if parent.is_empty() {
|
||||
@@ -5896,7 +5896,7 @@ impl Render for AcpThreadView {
|
||||
.flex_grow()
|
||||
.into_any(),
|
||||
)
|
||||
.vertical_scrollbar_for(self.list_state.clone(), window, cx)
|
||||
.vertical_scrollbar_for(&self.list_state, window, cx)
|
||||
.into_any()
|
||||
} else {
|
||||
this.child(self.render_recent_history(cx)).into_any()
|
||||
|
||||
@@ -1209,7 +1209,7 @@ impl Render for AgentConfiguration {
|
||||
.child(self.render_context_servers_section(window, cx))
|
||||
.child(self.render_provider_configuration_section(cx)),
|
||||
)
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx),
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,7 +516,7 @@ impl Render for AddLlmProviderModal {
|
||||
.child(
|
||||
div()
|
||||
.size_full()
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
|
||||
.child(
|
||||
v_flex()
|
||||
.id("modal_content")
|
||||
|
||||
@@ -821,7 +821,6 @@ impl ConfigureContextServerModal {
|
||||
|
||||
impl Render for ConfigureContextServerModal {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let scroll_handle = self.scroll_handle.clone();
|
||||
div()
|
||||
.elevation_3(cx)
|
||||
.w(rems(34.))
|
||||
@@ -849,7 +848,7 @@ impl Render for ConfigureContextServerModal {
|
||||
.id("modal-content")
|
||||
.max_h(vh(0.7, window))
|
||||
.overflow_y_scroll()
|
||||
.track_scroll(&scroll_handle)
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.child(self.render_modal_description(window, cx))
|
||||
.child(self.render_modal_content(cx))
|
||||
.child(match &self.state {
|
||||
@@ -862,7 +861,7 @@ impl Render for ConfigureContextServerModal {
|
||||
}
|
||||
}),
|
||||
)
|
||||
.vertical_scrollbar_for(scroll_handle, window, cx),
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx),
|
||||
),
|
||||
)
|
||||
.footer(self.render_modal_footer(cx)),
|
||||
|
||||
@@ -138,7 +138,7 @@ impl ConfigureContextServerToolsModal {
|
||||
items
|
||||
})),
|
||||
)
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,9 @@ use zed_actions::agent::{OpenClaudeCodeOnboardingModal, ReauthenticateAgent};
|
||||
use crate::ManageProfiles;
|
||||
use crate::ui::{AcpOnboardingModal, ClaudeCodeOnboardingModal};
|
||||
use crate::{
|
||||
AddContextServer, AgentDiffPane, DeleteRecentlyOpenThread, Follow, InlineAssistant,
|
||||
NewTextThread, NewThread, OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory,
|
||||
ResetTrialEndUpsell, ResetTrialUpsell, ToggleNavigationMenu, ToggleNewThreadMenu,
|
||||
ToggleOptionsMenu,
|
||||
AddContextServer, AgentDiffPane, Follow, InlineAssistant, NewTextThread, NewThread,
|
||||
OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ResetTrialEndUpsell, ResetTrialUpsell,
|
||||
ToggleNavigationMenu, ToggleNewThreadMenu, ToggleOptionsMenu,
|
||||
acp::AcpThreadView,
|
||||
agent_configuration::{AgentConfiguration, AssistantConfigurationEvent},
|
||||
slash_command::SlashCommandCompletionProvider,
|
||||
@@ -614,11 +613,14 @@ impl AgentPanel {
|
||||
if let Some(panel) = panel.upgrade() {
|
||||
menu = Self::populate_recently_opened_menu_section(menu, panel, cx);
|
||||
}
|
||||
menu.action("View All", Box::new(OpenHistory))
|
||||
.end_slot_action(DeleteRecentlyOpenThread.boxed_clone())
|
||||
|
||||
menu = menu
|
||||
.action("View All", Box::new(OpenHistory))
|
||||
.fixed_width(px(320.).into())
|
||||
.keep_open_on_confirm(false)
|
||||
.key_context("NavigationMenu")
|
||||
.key_context("NavigationMenu");
|
||||
|
||||
menu
|
||||
});
|
||||
weak_panel
|
||||
.update(cx, |panel, cx| {
|
||||
|
||||
@@ -69,6 +69,8 @@ actions!(
|
||||
CycleModeSelector,
|
||||
/// Expands the message editor to full size.
|
||||
ExpandMessageEditor,
|
||||
/// Removes all thread history.
|
||||
RemoveHistory,
|
||||
/// Opens the conversation history view.
|
||||
OpenHistory,
|
||||
/// Adds a context server to the configuration.
|
||||
|
||||
@@ -64,6 +64,16 @@ async fn check_is_contributor(
|
||||
}));
|
||||
}
|
||||
|
||||
if ZedZippyBot::is_zed_zippy_bot(¶ms) {
|
||||
return Ok(Json(CheckIsContributorResponse {
|
||||
signed_at: Some(
|
||||
ZedZippyBot::created_at()
|
||||
.and_utc()
|
||||
.to_rfc3339_opts(SecondsFormat::Millis, true),
|
||||
),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(Json(CheckIsContributorResponse {
|
||||
signed_at: app
|
||||
.db
|
||||
@@ -103,6 +113,36 @@ impl RenovateBot {
|
||||
}
|
||||
}
|
||||
|
||||
/// The Zed Zippy bot GitHub user (`zed-zippy[bot]`).
|
||||
///
|
||||
/// https://api.github.com/users/zed-zippy[bot]
|
||||
struct ZedZippyBot;
|
||||
|
||||
impl ZedZippyBot {
|
||||
const LOGIN: &'static str = "zed-zippy[bot]";
|
||||
const USER_ID: i32 = 234243425;
|
||||
|
||||
/// Returns the `created_at` timestamp for the Zed Zippy bot user.
|
||||
fn created_at() -> &'static NaiveDateTime {
|
||||
static CREATED_AT: OnceLock<NaiveDateTime> = OnceLock::new();
|
||||
CREATED_AT.get_or_init(|| {
|
||||
chrono::DateTime::parse_from_rfc3339("2025-09-24T17:00:11Z")
|
||||
.expect("failed to parse 'created_at' for 'zed-zippy[bot]'")
|
||||
.naive_utc()
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns whether the given contributor selector corresponds to the Zed Zippy bot user.
|
||||
fn is_zed_zippy_bot(contributor: &ContributorSelector) -> bool {
|
||||
match contributor {
|
||||
ContributorSelector::GitHubLogin { github_login } => github_login == Self::LOGIN,
|
||||
ContributorSelector::GitHubUserId { github_user_id } => {
|
||||
github_user_id == &Self::USER_ID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct AddContributorBody {
|
||||
github_user_id: i32,
|
||||
|
||||
@@ -575,7 +575,7 @@ impl BreakpointList {
|
||||
)
|
||||
.with_horizontal_sizing_behavior(gpui::ListHorizontalSizingBehavior::Unconstrained)
|
||||
.with_width_from_item(self.max_width_index)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.flex_1()
|
||||
}
|
||||
|
||||
@@ -776,7 +776,7 @@ impl Render for BreakpointList {
|
||||
.child(self.render_list(cx))
|
||||
.custom_scrollbars(
|
||||
ui::Scrollbars::new(ScrollAxes::Both)
|
||||
.tracked_scroll_handle(self.scroll_handle.clone())
|
||||
.tracked_scroll_handle(&self.scroll_handle)
|
||||
.with_track_along(ScrollAxes::Both, cx.theme().colors().panel_background)
|
||||
.tracked_entity(cx.entity_id()),
|
||||
window,
|
||||
|
||||
@@ -229,7 +229,7 @@ impl MemoryView {
|
||||
rows
|
||||
},
|
||||
)
|
||||
.track_scroll(view_state.scroll_handle)
|
||||
.track_scroll(&view_state.scroll_handle)
|
||||
.with_horizontal_sizing_behavior(ListHorizontalSizingBehavior::Unconstrained)
|
||||
.on_scroll_wheel(cx.listener(|this, evt: &ScrollWheelEvent, window, _| {
|
||||
let mut view_state = this.view_state();
|
||||
@@ -921,7 +921,7 @@ impl Render for MemoryView {
|
||||
}))
|
||||
.custom_scrollbars(
|
||||
ui::Scrollbars::new(ui::ScrollAxes::Both)
|
||||
.tracked_scroll_handle(self.view_state_handle.clone())
|
||||
.tracked_scroll_handle(&self.view_state_handle)
|
||||
.with_track_along(
|
||||
ui::ScrollAxes::Both,
|
||||
cx.theme().colors().panel_background,
|
||||
|
||||
@@ -253,7 +253,7 @@ impl ModuleList {
|
||||
range.map(|ix| this.render_entry(ix, cx)).collect()
|
||||
}),
|
||||
)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.size_full()
|
||||
}
|
||||
}
|
||||
@@ -279,6 +279,6 @@ impl Render for ModuleList {
|
||||
.size_full()
|
||||
.p_1()
|
||||
.child(self.render_list(window, cx))
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -913,7 +913,7 @@ impl Render for StackFrameList {
|
||||
)
|
||||
})
|
||||
.child(self.render_list(window, cx))
|
||||
.vertical_scrollbar_for(self.list_state.clone(), window, cx)
|
||||
.vertical_scrollbar_for(&self.list_state, window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1557,7 +1557,7 @@ impl Render for VariableList {
|
||||
this.render_entries(range, window, cx)
|
||||
}),
|
||||
)
|
||||
.track_scroll(self.list_handle.clone())
|
||||
.track_scroll(&self.list_handle)
|
||||
.with_width_from_item(self.max_width_index)
|
||||
.with_sizing_behavior(gpui::ListSizingBehavior::Auto)
|
||||
.with_horizontal_sizing_behavior(gpui::ListHorizontalSizingBehavior::Unconstrained)
|
||||
@@ -1574,10 +1574,10 @@ impl Render for VariableList {
|
||||
)
|
||||
.with_priority(1)
|
||||
}))
|
||||
// .vertical_scrollbar_for(self.list_handle.clone(), window, cx)
|
||||
// .vertical_scrollbar_for(&self.list_handle, window, cx)
|
||||
.custom_scrollbars(
|
||||
ui::Scrollbars::new(ScrollAxes::Both)
|
||||
.tracked_scroll_handle(self.list_handle.clone())
|
||||
.tracked_scroll_handle(&self.list_handle)
|
||||
.with_track_along(ScrollAxes::Both, cx.theme().colors().panel_background)
|
||||
.tracked_entity(cx.entity_id()),
|
||||
window,
|
||||
|
||||
@@ -933,7 +933,7 @@ impl CompletionsMenu {
|
||||
)
|
||||
.occlude()
|
||||
.max_h(max_height_in_lines as f32 * window.line_height())
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.with_sizing_behavior(ListSizingBehavior::Infer)
|
||||
.map(|this| {
|
||||
if self.display_options.dynamic_width {
|
||||
@@ -948,7 +948,7 @@ impl CompletionsMenu {
|
||||
div().child(list).custom_scrollbars(
|
||||
Scrollbars::for_settings::<CompletionMenuScrollBarSetting>()
|
||||
.show_along(ScrollAxes::Vertical)
|
||||
.tracked_scroll_handle(self.scroll_handle.clone()),
|
||||
.tracked_scroll_handle(&self.scroll_handle),
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
@@ -1599,7 +1599,7 @@ impl CodeActionsMenu {
|
||||
)
|
||||
.occlude()
|
||||
.max_h(max_height_in_lines as f32 * window.line_height())
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.with_width_from_item(
|
||||
self.actions
|
||||
.iter()
|
||||
|
||||
@@ -28198,3 +28198,87 @@ async fn test_multibuffer_selections_with_folding(cx: &mut TestAppContext) {
|
||||
3
|
||||
"});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_multibuffer_scroll_cursor_top_margin(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (editor, cx) = cx.add_window_view(|window, cx| {
|
||||
let multi_buffer = MultiBuffer::build_multi(
|
||||
[
|
||||
("1\n2\n3\n", vec![Point::row_range(0..3)]),
|
||||
("1\n2\n3\n4\n5\n6\n7\n8\n9\n", vec![Point::row_range(0..9)]),
|
||||
],
|
||||
cx,
|
||||
);
|
||||
Editor::new(EditorMode::full(), multi_buffer, None, window, cx)
|
||||
});
|
||||
|
||||
let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
|
||||
|
||||
cx.assert_excerpts_with_selections(indoc! {"
|
||||
[EXCERPT]
|
||||
ˇ1
|
||||
2
|
||||
3
|
||||
[EXCERPT]
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
"});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.change_selections(None.into(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(19)..MultiBufferOffset(19)]);
|
||||
});
|
||||
});
|
||||
|
||||
cx.assert_excerpts_with_selections(indoc! {"
|
||||
[EXCERPT]
|
||||
1
|
||||
2
|
||||
3
|
||||
[EXCERPT]
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
ˇ7
|
||||
8
|
||||
9
|
||||
"});
|
||||
|
||||
cx.update_editor(|editor, _window, cx| {
|
||||
editor.set_vertical_scroll_margin(0, cx);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
assert_eq!(editor.vertical_scroll_margin(), 0);
|
||||
editor.scroll_cursor_top(&ScrollCursorTop, window, cx);
|
||||
assert_eq!(
|
||||
editor.snapshot(window, cx).scroll_position(),
|
||||
gpui::Point::new(0., 12.0)
|
||||
);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, _window, cx| {
|
||||
editor.set_vertical_scroll_margin(3, cx);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
assert_eq!(editor.vertical_scroll_margin(), 3);
|
||||
editor.scroll_cursor_top(&ScrollCursorTop, window, cx);
|
||||
assert_eq!(
|
||||
editor.snapshot(window, cx).scroll_position(),
|
||||
gpui::Point::new(0., 9.0)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10712,9 +10712,9 @@ impl ScrollbarLayout {
|
||||
show_thumb: bool,
|
||||
axis: ScrollbarAxis,
|
||||
) -> Self {
|
||||
let text_units_per_page = f64::from(viewport_size / glyph_space);
|
||||
let text_units_per_page = viewport_size.to_f64() / glyph_space.to_f64();
|
||||
let visible_range = scroll_position..scroll_position + text_units_per_page;
|
||||
let total_text_units = scroll_range / f64::from(glyph_space);
|
||||
let total_text_units = scroll_range / glyph_space.to_f64();
|
||||
|
||||
let thumb_percentage = text_units_per_page / total_text_units;
|
||||
let thumb_size = Pixels::from(ScrollOffset::from(track_length) * thumb_percentage)
|
||||
|
||||
@@ -914,7 +914,7 @@ impl InfoPopover {
|
||||
)
|
||||
.custom_scrollbars(
|
||||
Scrollbars::for_settings::<EditorSettings>()
|
||||
.tracked_scroll_handle(self.scroll_handle.clone()),
|
||||
.tracked_scroll_handle(&self.scroll_handle),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -1012,7 +1012,7 @@ impl DiagnosticPopover {
|
||||
)
|
||||
.custom_scrollbars(
|
||||
Scrollbars::for_settings::<EditorSettings>()
|
||||
.tracked_scroll_handle(self.scroll_handle.clone()),
|
||||
.tracked_scroll_handle(&self.scroll_handle),
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
|
||||
@@ -71,14 +71,20 @@ impl Editor {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
let display_snapshot = self.display_snapshot(cx);
|
||||
let scroll_margin_rows = self.vertical_scroll_margin() as u32;
|
||||
let new_screen_top = self
|
||||
.selections
|
||||
.newest_display(&self.display_snapshot(cx))
|
||||
.newest_display(&display_snapshot)
|
||||
.head()
|
||||
.row()
|
||||
.0;
|
||||
let new_screen_top = new_screen_top.saturating_sub(scroll_margin_rows);
|
||||
let header_offset = display_snapshot
|
||||
.buffer_snapshot()
|
||||
.show_headers()
|
||||
.then(|| display_snapshot.buffer_header_height())
|
||||
.unwrap_or(0);
|
||||
let new_screen_top = new_screen_top.saturating_sub(scroll_margin_rows + header_offset);
|
||||
self.set_scroll_top_row(DisplayRow(new_screen_top), window, cx);
|
||||
}
|
||||
|
||||
|
||||
@@ -391,7 +391,7 @@ impl SignatureHelpPopover {
|
||||
)
|
||||
}),
|
||||
)
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx);
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx);
|
||||
|
||||
let controls = if self.signatures.len() > 1 {
|
||||
let prev_button = IconButton::new("signature_help_prev", IconName::ChevronUp)
|
||||
|
||||
@@ -1704,12 +1704,12 @@ impl Render for ExtensionsPage {
|
||||
if count == 0 {
|
||||
this.child(self.render_empty_state(cx)).into_any_element()
|
||||
} else {
|
||||
let scroll_handle = self.list.clone();
|
||||
let scroll_handle = &self.list;
|
||||
this.child(
|
||||
uniform_list("entries", count, cx.processor(Self::render_extensions))
|
||||
.flex_grow()
|
||||
.pb_4()
|
||||
.track_scroll(scroll_handle.clone()),
|
||||
.track_scroll(scroll_handle),
|
||||
)
|
||||
.vertical_scrollbar_for(scroll_handle, window, cx)
|
||||
.into_any_element()
|
||||
|
||||
@@ -1060,7 +1060,7 @@ impl FileFinderDelegate {
|
||||
(
|
||||
filename.to_string(),
|
||||
Vec::new(),
|
||||
prefix.display(path_style).to_string() + path_style.separator(),
|
||||
prefix.display(path_style).to_string() + path_style.primary_separator(),
|
||||
Vec::new(),
|
||||
)
|
||||
} else {
|
||||
@@ -1071,7 +1071,7 @@ impl FileFinderDelegate {
|
||||
.map_or(String::new(), |f| f.to_string_lossy().into_owned()),
|
||||
Vec::new(),
|
||||
entry_path.absolute.parent().map_or(String::new(), |path| {
|
||||
path.to_string_lossy().into_owned() + path_style.separator()
|
||||
path.to_string_lossy().into_owned() + path_style.primary_separator()
|
||||
}),
|
||||
Vec::new(),
|
||||
)
|
||||
|
||||
@@ -1598,7 +1598,7 @@ async fn test_history_match_positions(cx: &mut gpui::TestAppContext) {
|
||||
assert_eq!(file_label.highlight_indices(), &[0, 1, 2]);
|
||||
assert_eq!(
|
||||
path_label.text(),
|
||||
format!("test{}", PathStyle::local().separator())
|
||||
format!("test{}", PathStyle::local().primary_separator())
|
||||
);
|
||||
assert_eq!(path_label.highlight_indices(), &[] as &[usize]);
|
||||
});
|
||||
|
||||
@@ -559,7 +559,7 @@ impl PickerDelegate for OpenPathDelegate {
|
||||
parent_path,
|
||||
candidate.path.string,
|
||||
if candidate.is_dir {
|
||||
path_style.separator()
|
||||
path_style.primary_separator()
|
||||
} else {
|
||||
""
|
||||
}
|
||||
@@ -569,7 +569,7 @@ impl PickerDelegate for OpenPathDelegate {
|
||||
parent_path,
|
||||
candidate.path.string,
|
||||
if candidate.is_dir {
|
||||
path_style.separator()
|
||||
path_style.primary_separator()
|
||||
} else {
|
||||
""
|
||||
}
|
||||
@@ -826,7 +826,13 @@ impl PickerDelegate for OpenPathDelegate {
|
||||
}
|
||||
|
||||
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
|
||||
Arc::from(format!("[directory{}]filename.ext", self.path_style.separator()).as_str())
|
||||
Arc::from(
|
||||
format!(
|
||||
"[directory{}]filename.ext",
|
||||
self.path_style.primary_separator()
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
}
|
||||
|
||||
fn separators_after_indices(&self) -> Vec<usize> {
|
||||
|
||||
@@ -107,7 +107,7 @@ pub fn match_fixed_path_set(
|
||||
.display(path_style)
|
||||
.chars()
|
||||
.collect::<Vec<_>>();
|
||||
path_prefix_chars.extend(path_style.separator().chars());
|
||||
path_prefix_chars.extend(path_style.primary_separator().chars());
|
||||
let lowercase_pfx = path_prefix_chars
|
||||
.iter()
|
||||
.map(|c| c.to_ascii_lowercase())
|
||||
|
||||
@@ -3939,7 +3939,7 @@ impl GitPanel {
|
||||
ListHorizontalSizingBehavior::Unconstrained,
|
||||
)
|
||||
.with_width_from_item(self.max_width_item_index)
|
||||
.track_scroll(self.scroll_handle.clone()),
|
||||
.track_scroll(&self.scroll_handle),
|
||||
)
|
||||
.on_mouse_down(
|
||||
MouseButton::Right,
|
||||
@@ -3949,7 +3949,7 @@ impl GitPanel {
|
||||
)
|
||||
.custom_scrollbars(
|
||||
Scrollbars::for_settings::<GitPanelSettings>()
|
||||
.tracked_scroll_handle(self.scroll_handle.clone())
|
||||
.tracked_scroll_handle(&self.scroll_handle)
|
||||
.with_track_along(
|
||||
ScrollAxes::Horizontal,
|
||||
cx.theme().colors().panel_background,
|
||||
@@ -4351,8 +4351,11 @@ impl GitPanel {
|
||||
.when(strikethrough, Label::strikethrough),
|
||||
),
|
||||
(true, false) => this.child(
|
||||
self.entry_label(format!("{dir}{}", path_style.separator()), path_color)
|
||||
.when(strikethrough, Label::strikethrough),
|
||||
self.entry_label(
|
||||
format!("{dir}{}", path_style.primary_separator()),
|
||||
path_color,
|
||||
)
|
||||
.when(strikethrough, Label::strikethrough),
|
||||
),
|
||||
_ => this,
|
||||
}
|
||||
|
||||
@@ -438,7 +438,7 @@ impl Render for DataTable {
|
||||
}),
|
||||
)
|
||||
.size_full()
|
||||
.track_scroll(self.scroll_handle.clone()),
|
||||
.track_scroll(&self.scroll_handle),
|
||||
)
|
||||
.child(self.render_scrollbar(window, cx)),
|
||||
),
|
||||
|
||||
@@ -668,9 +668,9 @@ impl UniformList {
|
||||
}
|
||||
|
||||
/// Track and render scroll state of this list with reference to the given scroll handle.
|
||||
pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
|
||||
pub fn track_scroll(mut self, handle: &UniformListScrollHandle) -> Self {
|
||||
self.interactivity.tracked_scroll_handle = Some(handle.0.borrow().base_handle.clone());
|
||||
self.scroll_handle = Some(handle);
|
||||
self.scroll_handle = Some(handle.clone());
|
||||
self
|
||||
}
|
||||
|
||||
@@ -780,7 +780,7 @@ mod test {
|
||||
.collect()
|
||||
}),
|
||||
)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.h(px(200.0)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -507,11 +507,11 @@ impl Render for SyntaxTreeView {
|
||||
}),
|
||||
)
|
||||
.size_full()
|
||||
.track_scroll(self.list_scroll_handle.clone())
|
||||
.track_scroll(&self.list_scroll_handle)
|
||||
.text_bg(cx.theme().colors().background)
|
||||
.into_any_element(),
|
||||
)
|
||||
.vertical_scrollbar_for(self.list_scroll_handle.clone(), window, cx)
|
||||
.vertical_scrollbar_for(&self.list_scroll_handle, window, cx)
|
||||
.into_any_element()
|
||||
} else {
|
||||
let inner_content = v_flex()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name = "YAML"
|
||||
grammar = "yaml"
|
||||
path_suffixes = ["yml", "yaml", "pixi.lock", "clang-format"]
|
||||
path_suffixes = ["yml", "yaml", "pixi.lock", "clang-format", "clangd"]
|
||||
line_comments = ["# "]
|
||||
autoclose_before = ",]}"
|
||||
brackets = [
|
||||
|
||||
@@ -889,7 +889,7 @@ impl Element for MarkdownElement {
|
||||
{
|
||||
let scrollbars = Scrollbars::new(ScrollAxes::Horizontal)
|
||||
.id(("markdown-code-block-scrollbar", range.start))
|
||||
.tracked_scroll_handle(scroll_handle.clone())
|
||||
.tracked_scroll_handle(scroll_handle)
|
||||
.with_track_along(
|
||||
ScrollAxes::Horizontal,
|
||||
cx.theme().colors().editor_background,
|
||||
|
||||
@@ -611,6 +611,6 @@ impl Render for MarkdownPreviewView {
|
||||
.size_full(),
|
||||
)
|
||||
}))
|
||||
.vertical_scrollbar_for(self.list_state.clone(), window, cx)
|
||||
.vertical_scrollbar_for(&self.list_state, window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,10 +400,10 @@ impl Render for ProfilerWindow {
|
||||
this.autoscroll = false;
|
||||
cx.notify();
|
||||
}))
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.size_full(),
|
||||
)
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx),
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ impl Render for Onboarding {
|
||||
.child(self.render_page(cx))
|
||||
.track_scroll(&self.scroll_handle),
|
||||
)
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx),
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4639,7 +4639,7 @@ impl OutlinePanel {
|
||||
.with_sizing_behavior(ListSizingBehavior::Infer)
|
||||
.with_horizontal_sizing_behavior(ListHorizontalSizingBehavior::Unconstrained)
|
||||
.with_width_from_item(self.max_width_item_index)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.when(show_indent_guides, |list| {
|
||||
list.with_decoration(
|
||||
ui::indent_guides(px(indent_size), IndentGuideColors::panel(cx))
|
||||
@@ -4692,7 +4692,7 @@ impl OutlinePanel {
|
||||
.child(list_contents.size_full().flex_shrink())
|
||||
.custom_scrollbars(
|
||||
Scrollbars::for_settings::<OutlinePanelSettings>()
|
||||
.tracked_scroll_handle(self.scroll_handle.clone())
|
||||
.tracked_scroll_handle(&self.scroll_handle.clone())
|
||||
.with_track_along(
|
||||
ScrollAxes::Horizontal,
|
||||
cx.theme().colors().panel_background,
|
||||
|
||||
@@ -780,7 +780,7 @@ impl<D: PickerDelegate> Picker<D> {
|
||||
})
|
||||
.flex_grow()
|
||||
.py_1()
|
||||
.track_scroll(scroll_handle.clone())
|
||||
.track_scroll(&scroll_handle)
|
||||
.into_any_element(),
|
||||
ElementContainer::List(state) => list(
|
||||
state.clone(),
|
||||
@@ -866,12 +866,12 @@ impl<D: PickerDelegate> Render for Picker<D> {
|
||||
|
||||
this.map(|this| match &self.element_container {
|
||||
ElementContainer::List(state) => this.custom_scrollbars(
|
||||
base_scrollbar_config.tracked_scroll_handle(state.clone()),
|
||||
base_scrollbar_config.tracked_scroll_handle(state),
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
ElementContainer::UniformList(state) => this.custom_scrollbars(
|
||||
base_scrollbar_config.tracked_scroll_handle(state.clone()),
|
||||
base_scrollbar_config.tracked_scroll_handle(state),
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
|
||||
@@ -3222,10 +3222,8 @@ impl RepositorySnapshot {
|
||||
abs_path: &Path,
|
||||
path_style: PathStyle,
|
||||
) -> Option<RepoPath> {
|
||||
abs_path
|
||||
.strip_prefix(&work_directory_abs_path)
|
||||
.ok()
|
||||
.and_then(|path| RepoPath::from_std_path(path, path_style).ok())
|
||||
let rel_path = path_style.strip_prefix(abs_path, work_directory_abs_path)?;
|
||||
Some(RepoPath::from_rel_path(&rel_path))
|
||||
}
|
||||
|
||||
pub fn had_conflict_on_last_merge_head_change(&self, repo_path: &RepoPath) -> bool {
|
||||
|
||||
@@ -927,7 +927,7 @@ impl DirectoryLister {
|
||||
.map(|worktree| worktree.read(cx).abs_path().to_string_lossy().into_owned())
|
||||
.or_else(|| std::env::home_dir().map(|dir| dir.to_string_lossy().into_owned()))
|
||||
.map(|mut s| {
|
||||
s.push_str(path_style.separator());
|
||||
s.push_str(path_style.primary_separator());
|
||||
s
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
|
||||
@@ -168,6 +168,7 @@ impl Search {
|
||||
unnamed_buffers.push(handle)
|
||||
};
|
||||
}
|
||||
let open_buffers = Arc::new(open_buffers);
|
||||
let executor = cx.background_executor().clone();
|
||||
let (tx, rx) = unbounded();
|
||||
let (grab_buffer_snapshot_tx, grab_buffer_snapshot_rx) = unbounded();
|
||||
@@ -296,24 +297,22 @@ impl Search {
|
||||
};
|
||||
|
||||
let should_find_all_matches = !tx.is_closed();
|
||||
let num_cpus = executor.num_cpus();
|
||||
|
||||
let worker_pool = executor.scoped(|scope| {
|
||||
let num_cpus = executor.num_cpus();
|
||||
|
||||
assert!(num_cpus > 0);
|
||||
for _ in 0..executor.num_cpus() - 1 {
|
||||
assert!(num_cpus > 0);
|
||||
let worker_pool = (0..num_cpus - 1)
|
||||
.map(|_| {
|
||||
let worker = Worker {
|
||||
query: &query,
|
||||
open_buffers: &open_buffers,
|
||||
query: query.clone(),
|
||||
open_buffers: open_buffers.clone(),
|
||||
candidates: candidate_searcher.clone(),
|
||||
find_all_matches_rx: find_all_matches_rx.clone(),
|
||||
};
|
||||
scope.spawn(worker.run());
|
||||
}
|
||||
|
||||
drop(find_all_matches_rx);
|
||||
drop(candidate_searcher);
|
||||
});
|
||||
executor.spawn(worker.run()).boxed_local()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
drop(find_all_matches_rx);
|
||||
drop(candidate_searcher);
|
||||
|
||||
let (sorted_matches_tx, sorted_matches_rx) = unbounded();
|
||||
// The caller of `into_handle` decides whether they're interested in all matches (files that matched + all matching ranges) or
|
||||
@@ -348,7 +347,7 @@ impl Search {
|
||||
};
|
||||
|
||||
futures::future::join_all(
|
||||
[worker_pool.boxed_local()]
|
||||
worker_pool
|
||||
.into_iter()
|
||||
.chain(buffer_snapshots)
|
||||
.chain(ensure_matches_are_reported_in_order)
|
||||
@@ -578,9 +577,9 @@ impl Search {
|
||||
}
|
||||
}
|
||||
|
||||
struct Worker<'search> {
|
||||
query: &'search SearchQuery,
|
||||
open_buffers: &'search HashSet<ProjectEntryId>,
|
||||
struct Worker {
|
||||
query: Arc<SearchQuery>,
|
||||
open_buffers: Arc<HashSet<ProjectEntryId>>,
|
||||
candidates: FindSearchCandidates,
|
||||
/// Ok, we're back in background: run full scan & find all matches in a given buffer snapshot.
|
||||
/// Then, when you're done, share them via the channel you were given.
|
||||
@@ -591,7 +590,7 @@ struct Worker<'search> {
|
||||
)>,
|
||||
}
|
||||
|
||||
impl Worker<'_> {
|
||||
impl Worker {
|
||||
async fn run(self) {
|
||||
let (
|
||||
input_paths_rx,
|
||||
@@ -629,7 +628,7 @@ impl Worker<'_> {
|
||||
|
||||
loop {
|
||||
let handler = RequestHandler {
|
||||
query: self.query,
|
||||
query: &self.query,
|
||||
open_entries: &self.open_buffers,
|
||||
fs: fs.as_deref(),
|
||||
confirm_contents_will_match_tx: &confirm_contents_will_match_tx,
|
||||
|
||||
@@ -4837,7 +4837,7 @@ impl ProjectPanel {
|
||||
.collect::<Vec<_>>();
|
||||
let active_index = folded_ancestors.active_index();
|
||||
let components_len = components.len();
|
||||
let delimiter = SharedString::new(path_style.separator());
|
||||
let delimiter = SharedString::new(path_style.primary_separator());
|
||||
for (index, component) in components.iter().enumerate() {
|
||||
if index != 0 {
|
||||
let delimiter_target_index = index - 1;
|
||||
@@ -5765,7 +5765,7 @@ impl Render for ProjectPanel {
|
||||
ListHorizontalSizingBehavior::Unconstrained,
|
||||
)
|
||||
.with_width_from_item(self.state.max_width_item_index)
|
||||
.track_scroll(self.scroll_handle.clone()),
|
||||
.track_scroll(&self.scroll_handle),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
@@ -5908,7 +5908,7 @@ impl Render for ProjectPanel {
|
||||
)
|
||||
.custom_scrollbars(
|
||||
Scrollbars::for_settings::<ProjectPanelSettings>()
|
||||
.tracked_scroll_handle(self.scroll_handle.clone())
|
||||
.tracked_scroll_handle(&self.scroll_handle)
|
||||
.with_track_along(
|
||||
ScrollAxes::Horizontal,
|
||||
cx.theme().colors().panel_background,
|
||||
|
||||
@@ -2160,7 +2160,7 @@ impl RemoteServerProjects {
|
||||
)
|
||||
.size_full(),
|
||||
)
|
||||
.vertical_scrollbar_for(state.scroll_handle, window, cx),
|
||||
.vertical_scrollbar_for(&state.scroll_handle, window, cx),
|
||||
),
|
||||
)
|
||||
.into_any_element()
|
||||
|
||||
@@ -607,7 +607,10 @@ pub fn open_settings_editor(
|
||||
window_background: cx.theme().window_background_appearance(),
|
||||
app_id: Some(app_id.to_owned()),
|
||||
window_decorations: Some(window_decorations),
|
||||
window_min_size: Some(scaled_bounds),
|
||||
window_min_size: Some(gpui::Size {
|
||||
width: px(360.0),
|
||||
height: px(240.0),
|
||||
}),
|
||||
window_bounds: Some(WindowBounds::centered(scaled_bounds, cx)),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -2189,7 +2192,7 @@ impl SettingsWindow {
|
||||
format!(
|
||||
"{}{}{}",
|
||||
directory_name,
|
||||
path_style.separator(),
|
||||
path_style.primary_separator(),
|
||||
path.display(path_style)
|
||||
)
|
||||
}
|
||||
@@ -2452,9 +2455,9 @@ impl SettingsWindow {
|
||||
}),
|
||||
)
|
||||
.size_full()
|
||||
.track_scroll(self.navbar_scroll_handle.clone()),
|
||||
.track_scroll(&self.navbar_scroll_handle),
|
||||
)
|
||||
.vertical_scrollbar_for(self.navbar_scroll_handle.clone(), window, cx),
|
||||
.vertical_scrollbar_for(&self.navbar_scroll_handle, window, cx),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
@@ -3009,10 +3012,10 @@ impl SettingsWindow {
|
||||
window.focus_prev();
|
||||
}))
|
||||
.when(sub_page_stack().is_empty(), |this| {
|
||||
this.vertical_scrollbar_for(self.list_state.clone(), window, cx)
|
||||
this.vertical_scrollbar_for(&self.list_state, window, cx)
|
||||
})
|
||||
.when(!sub_page_stack().is_empty(), |this| {
|
||||
this.vertical_scrollbar_for(self.sub_page_scroll_handle.clone(), window, cx)
|
||||
this.vertical_scrollbar_for(&self.sub_page_scroll_handle, window, cx)
|
||||
})
|
||||
.track_focus(&self.content_focus_handle.focus_handle(cx))
|
||||
.pt_6()
|
||||
|
||||
@@ -1118,7 +1118,7 @@ impl Render for TerminalView {
|
||||
ScrollAxes::Vertical,
|
||||
cx.theme().colors().editor_background,
|
||||
)
|
||||
.tracked_scroll_handle(self.scroll_handle.clone()),
|
||||
.tracked_scroll_handle(&self.scroll_handle),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -876,7 +876,7 @@ impl ToolchainSelectorDelegate {
|
||||
.strip_prefix(&worktree_root)
|
||||
.ok()
|
||||
.and_then(|suffix| suffix.to_str())
|
||||
.map(|suffix| format!(".{}{suffix}", path_style.separator()).into())
|
||||
.map(|suffix| format!(".{}{suffix}", path_style.primary_separator()).into())
|
||||
.unwrap_or(path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -872,7 +872,7 @@ impl<const COLS: usize> RenderOnce for Table<COLS> {
|
||||
interaction_state.as_ref(),
|
||||
|this, state| {
|
||||
this.track_scroll(
|
||||
state.read_with(cx, |s, _| s.scroll_handle.clone()),
|
||||
&state.read_with(cx, |s, _| s.scroll_handle.clone()),
|
||||
)
|
||||
},
|
||||
),
|
||||
@@ -906,7 +906,7 @@ impl<const COLS: usize> RenderOnce for Table<COLS> {
|
||||
.unwrap_or_else(|| Scrollbars::new(super::ScrollAxes::Both));
|
||||
content
|
||||
.custom_scrollbars(
|
||||
scrollbars.tracked_scroll_handle(state.read(cx).scroll_handle.clone()),
|
||||
scrollbars.tracked_scroll_handle(&state.read(cx).scroll_handle),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -150,9 +150,9 @@ pub trait WithScrollbar: Sized {
|
||||
// }
|
||||
|
||||
#[track_caller]
|
||||
fn vertical_scrollbar_for<ScrollHandle: ScrollableHandle>(
|
||||
fn vertical_scrollbar_for<ScrollHandle: ScrollableHandle + Clone>(
|
||||
self,
|
||||
scroll_handle: ScrollHandle,
|
||||
scroll_handle: &ScrollHandle,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Self::Output {
|
||||
@@ -441,7 +441,7 @@ impl<ScrollHandle: ScrollableHandle> Scrollbars<ScrollHandle> {
|
||||
|
||||
pub fn tracked_scroll_handle<TrackedHandle: ScrollableHandle>(
|
||||
self,
|
||||
tracked_scroll_handle: TrackedHandle,
|
||||
tracked_scroll_handle: &TrackedHandle,
|
||||
) -> Scrollbars<TrackedHandle> {
|
||||
let Self {
|
||||
id,
|
||||
@@ -454,7 +454,7 @@ impl<ScrollHandle: ScrollableHandle> Scrollbars<ScrollHandle> {
|
||||
} = self;
|
||||
|
||||
Scrollbars {
|
||||
scrollable_handle: Handle::Tracked(tracked_scroll_handle),
|
||||
scrollable_handle: Handle::Tracked(tracked_scroll_handle.clone()),
|
||||
id,
|
||||
tracked_entity: tracked_entity_id,
|
||||
visibility,
|
||||
@@ -968,7 +968,7 @@ impl ScrollableHandle for ScrollHandle {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ScrollableHandle: 'static + Any + Sized {
|
||||
pub trait ScrollableHandle: 'static + Any + Sized + Clone {
|
||||
fn max_offset(&self) -> Size<Pixels>;
|
||||
fn set_offset(&self, point: Point<Pixels>);
|
||||
fn offset(&self) -> Point<Pixels>;
|
||||
|
||||
@@ -24,8 +24,8 @@ impl TabBar {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn track_scroll(mut self, scroll_handle: ScrollHandle) -> Self {
|
||||
self.scroll_handle = Some(scroll_handle);
|
||||
pub fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
|
||||
self.scroll_handle = Some(scroll_handle.clone());
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use globset::{Glob, GlobSet, GlobSetBuilder};
|
||||
use itertools::Itertools;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
@@ -331,13 +332,20 @@ impl PathStyle {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn separator(&self) -> &'static str {
|
||||
pub fn primary_separator(&self) -> &'static str {
|
||||
match self {
|
||||
PathStyle::Posix => "/",
|
||||
PathStyle::Windows => "\\",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn separators(&self) -> &'static [&'static str] {
|
||||
match self {
|
||||
PathStyle::Posix => &["/"],
|
||||
PathStyle::Windows => &["\\", "/"],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_windows(&self) -> bool {
|
||||
*self == PathStyle::Windows
|
||||
}
|
||||
@@ -353,25 +361,54 @@ impl PathStyle {
|
||||
} else {
|
||||
Some(format!(
|
||||
"{left}{}{right}",
|
||||
if left.ends_with(self.separator()) {
|
||||
if left.ends_with(self.primary_separator()) {
|
||||
""
|
||||
} else {
|
||||
self.separator()
|
||||
self.primary_separator()
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split(self, path_like: &str) -> (Option<&str>, &str) {
|
||||
let Some(pos) = path_like.rfind(self.separator()) else {
|
||||
let Some(pos) = path_like.rfind(self.primary_separator()) else {
|
||||
return (None, path_like);
|
||||
};
|
||||
let filename_start = pos + self.separator().len();
|
||||
let filename_start = pos + self.primary_separator().len();
|
||||
(
|
||||
Some(&path_like[..filename_start]),
|
||||
&path_like[filename_start..],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn strip_prefix<'a>(
|
||||
&self,
|
||||
child: &'a Path,
|
||||
parent: &'a Path,
|
||||
) -> Option<std::borrow::Cow<'a, RelPath>> {
|
||||
let parent = parent.to_str()?;
|
||||
if parent.is_empty() {
|
||||
return RelPath::new(child, *self).ok();
|
||||
}
|
||||
let parent = self
|
||||
.separators()
|
||||
.iter()
|
||||
.find_map(|sep| parent.strip_suffix(sep))
|
||||
.unwrap_or(parent);
|
||||
let child = child.to_str()?;
|
||||
let stripped = child.strip_prefix(parent)?;
|
||||
if let Some(relative) = self
|
||||
.separators()
|
||||
.iter()
|
||||
.find_map(|sep| stripped.strip_prefix(sep))
|
||||
{
|
||||
RelPath::new(relative.as_ref(), *self).ok()
|
||||
} else if stripped.is_empty() {
|
||||
Some(Cow::Borrowed(RelPath::empty()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -788,7 +825,7 @@ impl PathMatcher {
|
||||
|
||||
fn check_with_end_separator(&self, path: &Path) -> bool {
|
||||
let path_str = path.to_string_lossy();
|
||||
let separator = self.path_style.separator();
|
||||
let separator = self.path_style.primary_separator();
|
||||
if path_str.ends_with(separator) {
|
||||
false
|
||||
} else {
|
||||
@@ -1311,6 +1348,8 @@ impl WslPath {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::rel_path::rel_path;
|
||||
|
||||
use super::*;
|
||||
use util_macros::perf;
|
||||
|
||||
@@ -2480,6 +2519,89 @@ mod tests {
|
||||
assert_eq!(strip_path_suffix(base, suffix), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strip_prefix() {
|
||||
let expected = [
|
||||
(
|
||||
PathStyle::Posix,
|
||||
"/a/b/c",
|
||||
"/a/b",
|
||||
Some(rel_path("c").into_arc()),
|
||||
),
|
||||
(
|
||||
PathStyle::Posix,
|
||||
"/a/b/c",
|
||||
"/a/b/",
|
||||
Some(rel_path("c").into_arc()),
|
||||
),
|
||||
(
|
||||
PathStyle::Posix,
|
||||
"/a/b/c",
|
||||
"/",
|
||||
Some(rel_path("a/b/c").into_arc()),
|
||||
),
|
||||
(PathStyle::Posix, "/a/b/c", "", None),
|
||||
(PathStyle::Posix, "/a/b//c", "/a/b/", None),
|
||||
(PathStyle::Posix, "/a/bc", "/a/b", None),
|
||||
(
|
||||
PathStyle::Posix,
|
||||
"/a/b/c",
|
||||
"/a/b/c",
|
||||
Some(rel_path("").into_arc()),
|
||||
),
|
||||
(
|
||||
PathStyle::Windows,
|
||||
"C:\\a\\b\\c",
|
||||
"C:\\a\\b",
|
||||
Some(rel_path("c").into_arc()),
|
||||
),
|
||||
(
|
||||
PathStyle::Windows,
|
||||
"C:\\a\\b\\c",
|
||||
"C:\\a\\b\\",
|
||||
Some(rel_path("c").into_arc()),
|
||||
),
|
||||
(
|
||||
PathStyle::Windows,
|
||||
"C:\\a\\b\\c",
|
||||
"C:\\",
|
||||
Some(rel_path("a/b/c").into_arc()),
|
||||
),
|
||||
(PathStyle::Windows, "C:\\a\\b\\c", "", None),
|
||||
(PathStyle::Windows, "C:\\a\\b\\\\c", "C:\\a\\b\\", None),
|
||||
(PathStyle::Windows, "C:\\a\\bc", "C:\\a\\b", None),
|
||||
(
|
||||
PathStyle::Windows,
|
||||
"C:\\a\\b/c",
|
||||
"C:\\a\\b",
|
||||
Some(rel_path("c").into_arc()),
|
||||
),
|
||||
(
|
||||
PathStyle::Windows,
|
||||
"C:\\a\\b/c",
|
||||
"C:\\a\\b\\",
|
||||
Some(rel_path("c").into_arc()),
|
||||
),
|
||||
(
|
||||
PathStyle::Windows,
|
||||
"C:\\a\\b/c",
|
||||
"C:\\a\\b/",
|
||||
Some(rel_path("c").into_arc()),
|
||||
),
|
||||
];
|
||||
let actual = expected.clone().map(|(style, child, parent, _)| {
|
||||
(
|
||||
style,
|
||||
child,
|
||||
parent,
|
||||
style
|
||||
.strip_prefix(child.as_ref(), parent.as_ref())
|
||||
.map(|rel_path| rel_path.into_arc()),
|
||||
)
|
||||
});
|
||||
pretty_assertions::assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
fn test_wsl_path() {
|
||||
|
||||
@@ -965,7 +965,7 @@ impl VimCommand {
|
||||
}
|
||||
};
|
||||
|
||||
let rel_path = if args.ends_with(PathStyle::local().separator()) {
|
||||
let rel_path = if args.ends_with(PathStyle::local().primary_separator()) {
|
||||
rel_path
|
||||
} else {
|
||||
rel_path
|
||||
@@ -998,7 +998,7 @@ impl VimCommand {
|
||||
.display(PathStyle::local())
|
||||
.to_string();
|
||||
if dir.is_dir {
|
||||
path_string.push_str(PathStyle::local().separator());
|
||||
path_string.push_str(PathStyle::local().primary_separator());
|
||||
}
|
||||
path_string
|
||||
})
|
||||
|
||||
@@ -789,7 +789,7 @@ pub mod simple_message_notification {
|
||||
.track_scroll(&self.scroll_handle.clone())
|
||||
.child((self.build_content)(window, cx)),
|
||||
)
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx),
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx),
|
||||
)
|
||||
.show_close_button(self.show_close_button)
|
||||
.show_suppress_button(self.show_suppress_button)
|
||||
|
||||
@@ -999,7 +999,7 @@ impl Worktree {
|
||||
};
|
||||
|
||||
if worktree_relative_path.components().next().is_some() {
|
||||
full_path_string.push_str(self.path_style.separator());
|
||||
full_path_string.push_str(self.path_style.primary_separator());
|
||||
full_path_string.push_str(&worktree_relative_path.display(self.path_style));
|
||||
}
|
||||
|
||||
@@ -2108,8 +2108,8 @@ impl Snapshot {
|
||||
if path.file_name().is_some() {
|
||||
let mut abs_path = self.abs_path.to_string();
|
||||
for component in path.components() {
|
||||
if !abs_path.ends_with(self.path_style.separator()) {
|
||||
abs_path.push_str(self.path_style.separator());
|
||||
if !abs_path.ends_with(self.path_style.primary_separator()) {
|
||||
abs_path.push_str(self.path_style.primary_separator());
|
||||
}
|
||||
abs_path.push_str(component);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "The fast, collaborative code editor."
|
||||
edition.workspace = true
|
||||
name = "zed"
|
||||
version = "0.215.0"
|
||||
version = "0.216.0"
|
||||
publish.workspace = true
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Zed Team <hi@zed.dev>"]
|
||||
|
||||
@@ -627,7 +627,7 @@ impl Render for ComponentPreview {
|
||||
.collect()
|
||||
}),
|
||||
)
|
||||
.track_scroll(self.nav_scroll_handle.clone())
|
||||
.track_scroll(&self.nav_scroll_handle)
|
||||
.p_2p5()
|
||||
.w(px(231.)) // Matches perfectly with the size of the "Component Preview" tab, if that's the first one in the pane
|
||||
.h_full()
|
||||
|
||||
@@ -11,6 +11,7 @@ C support is available natively in Zed.
|
||||
Clangd out of the box assumes mixed C++/C projects. If you have a C-only project you may wish to instruct clangd to treat all files as C using the `-xc` flag. To do this, create a `.clangd` file in the root of your project with the following:
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/clangd.json
|
||||
CompileFlags:
|
||||
Add: [-xc]
|
||||
```
|
||||
|
||||
@@ -78,6 +78,7 @@ You can pass any number of arguments to clangd. To see a full set of available o
|
||||
By default Zed will use the `clangd` language server for formatting C++ code. The Clangd is the same as the `clang-format` CLI tool. To configure this you can add a `.clang-format` file. For example:
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/clang-format-21.x.json
|
||||
---
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
@@ -106,7 +107,8 @@ You can trigger formatting via {#kb editor::Format} or the `editor: format` acti
|
||||
|
||||
In the root of your project, it is generally common to create a `.clangd` file to set extra configuration.
|
||||
|
||||
```text
|
||||
```yaml
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/clangd.json
|
||||
CompileFlags:
|
||||
Add:
|
||||
- "--include-directory=/path/to/include"
|
||||
|
||||
@@ -4,7 +4,13 @@ How to use our internal tools to profile and keep Zed fast.
|
||||
|
||||
See what the CPU spends the most time on. Strongly recommend you use
|
||||
[samply](https://github.com/mstange/samply). It opens an interactive profile in
|
||||
the browser. See its README on how to install and run.
|
||||
the browser (specifically a local instance of [firefox_profiler](https://profiler.firefox.com/)).
|
||||
|
||||
See [samply](https://github.com/mstange/samply)'s README on how to install and run.
|
||||
|
||||
The profile.json does not contain any symbols. Firefox profiler can add the local symbols to the profile for for. To do that hit the upload local profile button in the top right corner.
|
||||
|
||||
<img width="851" height="613" alt="image" src="https://github.com/user-attachments/assets/cbef2b51-0442-4ee9-bc5c-95f6ccf9be2c" />
|
||||
|
||||
# Task/Async profiling
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ mod after_release;
|
||||
mod cherry_pick;
|
||||
mod compare_perf;
|
||||
mod danger;
|
||||
mod extension_bump;
|
||||
mod extension_tests;
|
||||
mod nix_build;
|
||||
mod release_nightly;
|
||||
@@ -44,6 +45,7 @@ pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> {
|
||||
("run_agent_evals.yml", run_agent_evals::run_agent_evals()),
|
||||
("after_release.yml", after_release::after_release()),
|
||||
("extension_tests.yml", extension_tests::extension_tests()),
|
||||
("extension_bump.yml", extension_bump::extension_bump()),
|
||||
];
|
||||
fs::create_dir_all(dir)
|
||||
.with_context(|| format!("Failed to create directory: {}", dir.display()))?;
|
||||
|
||||
@@ -3,14 +3,14 @@ use gh_workflow::*;
|
||||
use crate::tasks::workflows::{
|
||||
runners,
|
||||
steps::{self, NamedJob, named},
|
||||
vars::{self, Input, StepOutput},
|
||||
vars::{self, StepOutput, WorkflowInput},
|
||||
};
|
||||
|
||||
pub fn cherry_pick() -> Workflow {
|
||||
let branch = Input::string("branch", None);
|
||||
let commit = Input::string("commit", None);
|
||||
let channel = Input::string("channel", None);
|
||||
let pr_number = Input::string("pr_number", None);
|
||||
let branch = WorkflowInput::string("branch", None);
|
||||
let commit = WorkflowInput::string("commit", None);
|
||||
let channel = WorkflowInput::string("channel", None);
|
||||
let pr_number = WorkflowInput::string("pr_number", None);
|
||||
let cherry_pick = run_cherry_pick(&branch, &commit, &channel);
|
||||
named::workflow()
|
||||
.run_name(format!("cherry_pick to {channel} #{pr_number}"))
|
||||
@@ -24,7 +24,11 @@ pub fn cherry_pick() -> Workflow {
|
||||
.add_job(cherry_pick.name, cherry_pick.job)
|
||||
}
|
||||
|
||||
fn run_cherry_pick(branch: &Input, commit: &Input, channel: &Input) -> NamedJob {
|
||||
fn run_cherry_pick(
|
||||
branch: &WorkflowInput,
|
||||
commit: &WorkflowInput,
|
||||
channel: &WorkflowInput,
|
||||
) -> NamedJob {
|
||||
fn authenticate_as_zippy() -> (Step<Use>, StepOutput) {
|
||||
let step = named::uses(
|
||||
"actions",
|
||||
@@ -39,9 +43,9 @@ fn run_cherry_pick(branch: &Input, commit: &Input, channel: &Input) -> NamedJob
|
||||
}
|
||||
|
||||
fn cherry_pick(
|
||||
branch: &Input,
|
||||
commit: &Input,
|
||||
channel: &Input,
|
||||
branch: &WorkflowInput,
|
||||
commit: &WorkflowInput,
|
||||
channel: &WorkflowInput,
|
||||
token: &StepOutput,
|
||||
) -> Step<Run> {
|
||||
named::bash(&format!("./script/cherry-pick {branch} {commit} {channel}"))
|
||||
|
||||
@@ -5,13 +5,13 @@ use crate::tasks::workflows::steps::FluentBuilder;
|
||||
use crate::tasks::workflows::{
|
||||
runners,
|
||||
steps::{self, NamedJob, named},
|
||||
vars::Input,
|
||||
vars::WorkflowInput,
|
||||
};
|
||||
|
||||
pub fn compare_perf() -> Workflow {
|
||||
let head = Input::string("head", None);
|
||||
let base = Input::string("base", None);
|
||||
let crate_name = Input::string("crate_name", Some("".to_owned()));
|
||||
let head = WorkflowInput::string("head", None);
|
||||
let base = WorkflowInput::string("base", None);
|
||||
let crate_name = WorkflowInput::string("crate_name", Some("".to_owned()));
|
||||
let run_perf = run_perf(&base, &head, &crate_name);
|
||||
named::workflow()
|
||||
.on(Event::default().workflow_dispatch(
|
||||
@@ -23,8 +23,12 @@ pub fn compare_perf() -> Workflow {
|
||||
.add_job(run_perf.name, run_perf.job)
|
||||
}
|
||||
|
||||
pub fn run_perf(base: &Input, head: &Input, crate_name: &Input) -> NamedJob {
|
||||
fn cargo_perf_test(ref_name: &Input, crate_name: &Input) -> Step<Run> {
|
||||
pub fn run_perf(
|
||||
base: &WorkflowInput,
|
||||
head: &WorkflowInput,
|
||||
crate_name: &WorkflowInput,
|
||||
) -> NamedJob {
|
||||
fn cargo_perf_test(ref_name: &WorkflowInput, crate_name: &WorkflowInput) -> Step<Run> {
|
||||
named::bash(&format!(
|
||||
"
|
||||
if [ -n \"{crate_name}\" ]; then
|
||||
@@ -39,7 +43,7 @@ pub fn run_perf(base: &Input, head: &Input, crate_name: &Input) -> NamedJob {
|
||||
named::uses("taiki-e", "install-action", "hyperfine")
|
||||
}
|
||||
|
||||
fn compare_runs(head: &Input, base: &Input) -> Step<Run> {
|
||||
fn compare_runs(head: &WorkflowInput, base: &WorkflowInput) -> Step<Run> {
|
||||
named::bash(&format!(
|
||||
"cargo perf-compare --save=results.md {base} {head}"
|
||||
))
|
||||
|
||||
217
tooling/xtask/src/tasks/workflows/extension_bump.rs
Normal file
217
tooling/xtask/src/tasks/workflows/extension_bump.rs
Normal file
@@ -0,0 +1,217 @@
|
||||
use gh_workflow::*;
|
||||
use indoc::indoc;
|
||||
|
||||
use crate::tasks::workflows::{
|
||||
extension_tests::{self},
|
||||
runners,
|
||||
steps::{self, CommonJobConditions, DEFAULT_REPOSITORY_OWNER_GUARD, NamedJob, named},
|
||||
vars::{
|
||||
JobOutput, StepOutput, WorkflowInput, WorkflowSecret, one_workflow_per_non_main_branch,
|
||||
},
|
||||
};
|
||||
|
||||
const BUMPVERSION_CONFIG: &str = indoc! {r#"
|
||||
[bumpversion]
|
||||
current_version = "$OLD_VERSION"
|
||||
|
||||
[bumpversion:file:Cargo.toml]
|
||||
|
||||
[bumpversion:file:extension.toml]
|
||||
"#
|
||||
};
|
||||
|
||||
const VERSION_CHECK: &str = r#"sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml"#;
|
||||
|
||||
// This is used by various extensions repos in the zed-extensions org to bump extension versions.
|
||||
pub(crate) fn extension_bump() -> Workflow {
|
||||
let bump_type = WorkflowInput::string("bump-type", Some("patch".to_owned()));
|
||||
|
||||
let app_id = WorkflowSecret::new("app-id", "The app ID used to create the PR");
|
||||
let app_secret =
|
||||
WorkflowSecret::new("app-secret", "The app secret for the corresponding app ID");
|
||||
|
||||
let test_extension = extension_tests::check_extension();
|
||||
let (check_bump_needed, needs_bump) = check_bump_needed();
|
||||
let bump_version = bump_extension_version(
|
||||
&[&test_extension, &check_bump_needed],
|
||||
&bump_type,
|
||||
needs_bump.as_job_output(&check_bump_needed),
|
||||
&app_id,
|
||||
&app_secret,
|
||||
);
|
||||
|
||||
named::workflow()
|
||||
.add_event(
|
||||
Event::default().workflow_call(
|
||||
WorkflowCall::default()
|
||||
.add_input(bump_type.name, bump_type.call_input())
|
||||
.secrets([
|
||||
(app_id.name.to_owned(), app_id.secret_configuration()),
|
||||
(
|
||||
app_secret.name.to_owned(),
|
||||
app_secret.secret_configuration(),
|
||||
),
|
||||
]),
|
||||
),
|
||||
)
|
||||
.concurrency(one_workflow_per_non_main_branch())
|
||||
.add_env(("CARGO_TERM_COLOR", "always"))
|
||||
.add_env(("RUST_BACKTRACE", 1))
|
||||
.add_env(("CARGO_INCREMENTAL", 0))
|
||||
.add_env((
|
||||
"ZED_EXTENSION_CLI_SHA",
|
||||
extension_tests::ZED_EXTENSION_CLI_SHA,
|
||||
))
|
||||
.add_job(test_extension.name, test_extension.job)
|
||||
.add_job(check_bump_needed.name, check_bump_needed.job)
|
||||
.add_job(bump_version.name, bump_version.job)
|
||||
}
|
||||
|
||||
fn check_bump_needed() -> (NamedJob, StepOutput) {
|
||||
let (compare_versions, version_changed) = compare_versions();
|
||||
|
||||
let job = Job::default()
|
||||
.with_repository_owner_guard()
|
||||
.outputs([(version_changed.name.to_owned(), version_changed.to_string())])
|
||||
.runs_on(runners::LINUX_SMALL)
|
||||
.timeout_minutes(1u32)
|
||||
.add_step(steps::checkout_repo().add_with(("fetch-depth", 10)))
|
||||
.add_step(compare_versions);
|
||||
|
||||
(named::job(job), version_changed)
|
||||
}
|
||||
|
||||
/// Compares the current and previous commit and checks whether versions changed inbetween.
|
||||
fn compare_versions() -> (Step<Run>, StepOutput) {
|
||||
let check_needs_bump = named::bash(format!(
|
||||
indoc! {
|
||||
r#"
|
||||
CURRENT_VERSION="$({})"
|
||||
|
||||
git checkout "$(git log -1 --format=%H)"~1
|
||||
|
||||
PREV_COMMIT_VERSION="$({})"
|
||||
|
||||
[[ "$CURRENT_VERSION" == "$PREV_COMMIT_VERSION" ]] && \
|
||||
echo "needs_bump=true" >> "$GITHUB_OUTPUT" || \
|
||||
echo "needs_bump=false" >> "$GITHUB_OUTPUT"
|
||||
|
||||
"#
|
||||
},
|
||||
VERSION_CHECK, VERSION_CHECK
|
||||
))
|
||||
.id("compare-versions-check");
|
||||
|
||||
let needs_bump = StepOutput::new(&check_needs_bump, "needs_bump");
|
||||
|
||||
(check_needs_bump, needs_bump)
|
||||
}
|
||||
|
||||
fn bump_extension_version(
|
||||
dependencies: &[&NamedJob],
|
||||
bump_type: &WorkflowInput,
|
||||
needs_bump: JobOutput,
|
||||
app_id: &WorkflowSecret,
|
||||
app_secret: &WorkflowSecret,
|
||||
) -> NamedJob {
|
||||
let (generate_token, generated_token) = generate_token(app_id, app_secret);
|
||||
let (bump_version, old_version, new_version) = bump_version(bump_type);
|
||||
|
||||
let job = steps::dependant_job(dependencies)
|
||||
.cond(Expression::new(format!(
|
||||
"{DEFAULT_REPOSITORY_OWNER_GUARD} && {} == 'true'",
|
||||
needs_bump.expr(),
|
||||
)))
|
||||
.runs_on(runners::LINUX_LARGE)
|
||||
.timeout_minutes(1u32)
|
||||
.add_step(generate_token)
|
||||
.add_step(steps::checkout_repo())
|
||||
.add_step(install_bump_2_version())
|
||||
.add_step(bump_version)
|
||||
.add_step(create_pull_request(
|
||||
old_version,
|
||||
new_version,
|
||||
generated_token,
|
||||
));
|
||||
|
||||
named::job(job)
|
||||
}
|
||||
|
||||
fn generate_token(app_id: &WorkflowSecret, app_secret: &WorkflowSecret) -> (Step<Use>, StepOutput) {
|
||||
let step = named::uses("actions", "create-github-app-token", "v2")
|
||||
.id("generate-token")
|
||||
.add_with(
|
||||
Input::default()
|
||||
.add("app-id", app_id.to_string())
|
||||
.add("private-key", app_secret.to_string()),
|
||||
);
|
||||
|
||||
let generated_token = StepOutput::new(&step, "token");
|
||||
|
||||
(step, generated_token)
|
||||
}
|
||||
|
||||
fn install_bump_2_version() -> Step<Run> {
|
||||
named::run(runners::Platform::Linux, "pip install bump2version")
|
||||
}
|
||||
|
||||
fn bump_version(bump_type: &WorkflowInput) -> (Step<Run>, StepOutput, StepOutput) {
|
||||
let step = named::bash(format!(
|
||||
indoc! {r#"
|
||||
OLD_VERSION="$({})"
|
||||
|
||||
cat <<EOF > .bumpversion.cfg
|
||||
{}
|
||||
EOF
|
||||
|
||||
bump2version --verbose {}
|
||||
NEW_VERSION="$({})"
|
||||
cargo update --workspace
|
||||
|
||||
rm .bumpversion.cfg
|
||||
|
||||
echo "old_version=${{OLD_VERSION}}" >> "$GITHUB_OUTPUT"
|
||||
echo "new_version=${{NEW_VERSION}}" >> "$GITHUB_OUTPUT"
|
||||
"#
|
||||
},
|
||||
VERSION_CHECK, BUMPVERSION_CONFIG, bump_type, VERSION_CHECK
|
||||
))
|
||||
.id("bump-version");
|
||||
|
||||
let old_version = StepOutput::new(&step, "old_version");
|
||||
let new_version = StepOutput::new(&step, "new_version");
|
||||
(step, old_version, new_version)
|
||||
}
|
||||
|
||||
fn create_pull_request(
|
||||
old_version: StepOutput,
|
||||
new_version: StepOutput,
|
||||
generated_token: StepOutput,
|
||||
) -> Step<Use> {
|
||||
let formatted_version = format!("v{}", new_version);
|
||||
|
||||
named::uses("peter-evans", "create-pull-request", "v7").with(
|
||||
Input::default()
|
||||
.add("title", format!("Bump version to {}", new_version))
|
||||
.add(
|
||||
"body",
|
||||
format!(
|
||||
"This PR bumps the version of this extension to {}",
|
||||
formatted_version
|
||||
),
|
||||
)
|
||||
.add(
|
||||
"commit-message",
|
||||
format!("Bump version to {}", formatted_version),
|
||||
)
|
||||
.add("branch", format!("bump-from-{}", old_version))
|
||||
.add(
|
||||
"committer",
|
||||
"zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>",
|
||||
)
|
||||
.add("base", "main")
|
||||
.add("delete-branch", true)
|
||||
.add("token", generated_token.to_string())
|
||||
.add("sign-commits", true),
|
||||
)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use crate::tasks::workflows::{
|
||||
};
|
||||
|
||||
const RUN_TESTS_INPUT: &str = "run_tests";
|
||||
const ZED_EXTENSION_CLI_SHA: &str = "7cfce605704d41ca247e3f84804bf323f6c6caaf";
|
||||
pub(crate) const ZED_EXTENSION_CLI_SHA: &str = "7cfce605704d41ca247e3f84804bf323f6c6caaf";
|
||||
|
||||
// This is used by various extensions repos in the zed-extensions org to run automated tests.
|
||||
pub(crate) fn extension_tests() -> Workflow {
|
||||
@@ -77,7 +77,7 @@ fn check_rust() -> NamedJob {
|
||||
named::job(job)
|
||||
}
|
||||
|
||||
fn check_extension() -> NamedJob {
|
||||
pub(crate) fn check_extension() -> NamedJob {
|
||||
let (cache_download, cache_hit) = cache_zed_extension_cli();
|
||||
let job = Job::default()
|
||||
.with_repository_owner_guard()
|
||||
|
||||
@@ -3,12 +3,12 @@ use gh_workflow::{Event, Expression, Job, Run, Schedule, Step, Use, Workflow, Wo
|
||||
use crate::tasks::workflows::{
|
||||
runners::{self, Platform},
|
||||
steps::{self, FluentBuilder as _, NamedJob, named, setup_cargo_config},
|
||||
vars::{self, Input},
|
||||
vars::{self, WorkflowInput},
|
||||
};
|
||||
|
||||
pub(crate) fn run_agent_evals() -> Workflow {
|
||||
let agent_evals = agent_evals();
|
||||
let model_name = Input::string("model_name", None);
|
||||
let model_name = WorkflowInput::string("model_name", None);
|
||||
|
||||
named::workflow()
|
||||
.on(Event::default().workflow_dispatch(
|
||||
@@ -29,8 +29,8 @@ pub(crate) fn run_agent_evals() -> Workflow {
|
||||
}
|
||||
|
||||
pub(crate) fn run_unit_evals() -> Workflow {
|
||||
let model_name = Input::string("model_name", None);
|
||||
let commit_sha = Input::string("commit_sha", None);
|
||||
let model_name = WorkflowInput::string("model_name", None);
|
||||
let commit_sha = WorkflowInput::string("commit_sha", None);
|
||||
|
||||
let unit_evals = named::job(unit_evals(Some(&commit_sha)));
|
||||
|
||||
@@ -117,7 +117,7 @@ fn cron_unit_evals() -> NamedJob {
|
||||
named::job(unit_evals(None).add_step(send_failure_to_slack()))
|
||||
}
|
||||
|
||||
fn unit_evals(commit: Option<&Input>) -> Job {
|
||||
fn unit_evals(commit: Option<&WorkflowInput>) -> Job {
|
||||
let script_step = add_api_keys(steps::script("./script/run-unit-evals"));
|
||||
|
||||
Job::default()
|
||||
|
||||
@@ -142,9 +142,13 @@ pub struct NamedJob {
|
||||
// }
|
||||
// }
|
||||
|
||||
pub(crate) const DEFAULT_REPOSITORY_OWNER_GUARD: &str =
|
||||
"(github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')";
|
||||
|
||||
pub fn repository_owner_guard_expression(trigger_always: bool) -> Expression {
|
||||
Expression::new(format!(
|
||||
"(github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions'){}",
|
||||
"{}{}",
|
||||
DEFAULT_REPOSITORY_OWNER_GUARD,
|
||||
trigger_always.then_some(" && always()").unwrap_or_default()
|
||||
))
|
||||
}
|
||||
@@ -248,8 +252,10 @@ pub mod named {
|
||||
/// Returns a bash-script step with the same name as the enclosing function.
|
||||
/// (You shouldn't inline this function into the workflow definition, you must
|
||||
/// wrap it in a new function.)
|
||||
pub fn bash(script: &str) -> Step<Run> {
|
||||
Step::new(function_name(1)).run(script).shell(BASH_SHELL)
|
||||
pub fn bash(script: impl AsRef<str>) -> Step<Run> {
|
||||
Step::new(function_name(1))
|
||||
.run(script.as_ref())
|
||||
.shell(BASH_SHELL)
|
||||
}
|
||||
|
||||
/// Returns a pwsh-script step with the same name as the enclosing function.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use gh_workflow::{Concurrency, Env, Expression, Step, WorkflowDispatchInput};
|
||||
use gh_workflow::{
|
||||
Concurrency, Env, Expression, Step, WorkflowCallInput, WorkflowCallSecret,
|
||||
WorkflowDispatchInput,
|
||||
};
|
||||
|
||||
use crate::tasks::workflows::{runners::Platform, steps::NamedJob};
|
||||
|
||||
@@ -132,7 +135,7 @@ impl PathCondition {
|
||||
}
|
||||
|
||||
pub(crate) struct StepOutput {
|
||||
name: &'static str,
|
||||
pub name: &'static str,
|
||||
step_id: String,
|
||||
}
|
||||
|
||||
@@ -151,6 +154,13 @@ impl StepOutput {
|
||||
pub fn expr(&self) -> String {
|
||||
format!("steps.{}.outputs.{}", self.step_id, self.name)
|
||||
}
|
||||
|
||||
pub fn as_job_output(self, job: &NamedJob) -> JobOutput {
|
||||
JobOutput {
|
||||
job_name: job.name.clone(),
|
||||
name: self.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for StepOutput {
|
||||
@@ -164,17 +174,43 @@ impl serde::Serialize for StepOutput {
|
||||
|
||||
impl std::fmt::Display for StepOutput {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "${{{{ steps.{}.outputs.{} }}}}", self.step_id, self.name)
|
||||
write!(f, "${{{{ {} }}}}", self.expr())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Input {
|
||||
pub(crate) struct JobOutput {
|
||||
job_name: String,
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
impl JobOutput {
|
||||
pub fn expr(&self) -> String {
|
||||
format!("needs.{}.outputs.{}", self.job_name, self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for JobOutput {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for JobOutput {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "${{{{ {} }}}}", self.expr())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorkflowInput {
|
||||
pub input_type: &'static str,
|
||||
pub name: &'static str,
|
||||
pub default: Option<String>,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
impl WorkflowInput {
|
||||
pub fn string(name: &'static str, default: Option<String>) -> Self {
|
||||
Self {
|
||||
input_type: "string",
|
||||
@@ -191,15 +227,62 @@ impl Input {
|
||||
default: self.default.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_input(&self) -> WorkflowCallInput {
|
||||
WorkflowCallInput {
|
||||
description: self.name.to_owned(),
|
||||
required: self.default.is_none(),
|
||||
input_type: self.input_type.to_owned(),
|
||||
default: self.default.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Input {
|
||||
impl std::fmt::Display for WorkflowInput {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "${{{{ inputs.{} }}}}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Input {
|
||||
impl serde::Serialize for WorkflowInput {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WorkflowSecret {
|
||||
pub name: &'static str,
|
||||
description: String,
|
||||
required: bool,
|
||||
}
|
||||
|
||||
impl WorkflowSecret {
|
||||
pub fn new(name: &'static str, description: impl ToString) -> Self {
|
||||
Self {
|
||||
name,
|
||||
description: description.to_string(),
|
||||
required: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn secret_configuration(&self) -> WorkflowCallSecret {
|
||||
WorkflowCallSecret {
|
||||
description: self.description.clone(),
|
||||
required: self.required,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for WorkflowSecret {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "${{{{ secrets.{} }}}}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for WorkflowSecret {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
|
||||
Reference in New Issue
Block a user