Compare commits
4 Commits
nucleo
...
settings-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a8c602f45 | ||
|
|
30551ba66e | ||
|
|
d689465e60 | ||
|
|
4a64796d7b |
@@ -4500,7 +4500,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
|
||||
title: "Program",
|
||||
description: "The shell program to use.",
|
||||
field: Box::new(SettingField {
|
||||
json_path: Some("terminal.shell.program"),
|
||||
json_path: Some("terminal.shell"),
|
||||
pick: |settings_content| {
|
||||
match settings_content.terminal.as_ref()?.project.shell.as_ref() {
|
||||
Some(settings::Shell::Program(program)) => Some(program),
|
||||
|
||||
@@ -6,10 +6,10 @@ use editor::{Editor, EditorEvent};
|
||||
use feature_flags::FeatureFlag;
|
||||
use fuzzy::StringMatchCandidate;
|
||||
use gpui::{
|
||||
Action, App, DEFAULT_ADDITIONAL_WINDOW_SIZE, Div, Entity, FocusHandle, Focusable, Global,
|
||||
ListState, ReadGlobal as _, ScrollHandle, Stateful, Subscription, Task, TitlebarOptions,
|
||||
UniformListScrollHandle, Window, WindowBounds, WindowHandle, WindowOptions, actions, div, list,
|
||||
point, prelude::*, px, uniform_list,
|
||||
Action, App, ClipboardItem, DEFAULT_ADDITIONAL_WINDOW_SIZE, Div, Entity, FocusHandle,
|
||||
Focusable, Global, ListState, ReadGlobal as _, ScrollHandle, Stateful, Subscription, Task,
|
||||
TitlebarOptions, UniformListScrollHandle, Window, WindowBounds, WindowHandle, WindowOptions,
|
||||
actions, div, list, point, prelude::*, px, uniform_list,
|
||||
};
|
||||
use heck::ToTitleCase as _;
|
||||
use project::{Project, WorktreeId};
|
||||
@@ -18,13 +18,13 @@ use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use settings::{Settings, SettingsContent, SettingsStore};
|
||||
use std::{
|
||||
any::{Any, TypeId, type_name},
|
||||
any::{type_name, Any, TypeId},
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
num::{NonZero, NonZeroU32},
|
||||
ops::Range,
|
||||
rc::Rc,
|
||||
sync::{Arc, LazyLock, RwLock},
|
||||
sync::{atomic::AtomicI32, Arc, LazyLock, RwLock},
|
||||
};
|
||||
use title_bar::platform_title_bar::PlatformTitleBar;
|
||||
use ui::{
|
||||
@@ -512,43 +512,10 @@ pub fn open_settings_editor(
|
||||
return;
|
||||
}
|
||||
|
||||
settings_window.current_file = SettingsUiFile::User;
|
||||
settings_window.build_ui(window, cx);
|
||||
|
||||
let mut item_info = None;
|
||||
'search: for (nav_entry_index, entry) in settings_window.navbar_entries.iter().enumerate() {
|
||||
if entry.is_root {
|
||||
continue;
|
||||
}
|
||||
let page_index = entry.page_index;
|
||||
let header_index = entry
|
||||
.item_index
|
||||
.expect("non-root entries should have an item index");
|
||||
for item_index in header_index + 1..settings_window.pages[page_index].items.len() {
|
||||
let item = &settings_window.pages[page_index].items[item_index];
|
||||
if let SettingsPageItem::SectionHeader(_) = item {
|
||||
break;
|
||||
}
|
||||
if let SettingsPageItem::SettingItem(item) = item {
|
||||
if item.field.json_path() == Some(path) {
|
||||
if !item.files.contains(USER) {
|
||||
log::error!("Found item {}, but it is not a user setting", path);
|
||||
return;
|
||||
}
|
||||
item_info = Some((item_index, nav_entry_index));
|
||||
break 'search;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some((item_index, navbar_entry_index)) = item_info else {
|
||||
log::error!("Failed to find item for {}", path);
|
||||
return;
|
||||
};
|
||||
|
||||
settings_window.open_navbar_entry_page(navbar_entry_index);
|
||||
window.focus(&settings_window.focus_handle_for_content_element(item_index, cx));
|
||||
settings_window.scroll_to_content_item(item_index, window, cx);
|
||||
settings_window.search_bar.update(cx, |editor, cx| {
|
||||
editor.set_text(format!("#{path}"), window, cx);
|
||||
});
|
||||
settings_window.update_matches(cx);
|
||||
}
|
||||
|
||||
let existing_window = cx
|
||||
@@ -673,13 +640,14 @@ pub struct SettingsWindow {
|
||||
struct SearchIndex {
|
||||
bm25_engine: bm25::SearchEngine<usize>,
|
||||
fuzzy_match_candidates: Vec<StringMatchCandidate>,
|
||||
key_lut: Vec<SearchItemKey>,
|
||||
key_lut: Vec<SearchKeyLUTEntry>,
|
||||
}
|
||||
|
||||
struct SearchItemKey {
|
||||
struct SearchKeyLUTEntry {
|
||||
page_index: usize,
|
||||
header_index: usize,
|
||||
item_index: usize,
|
||||
json_path: Option<&'static str>,
|
||||
}
|
||||
|
||||
struct SubPage {
|
||||
@@ -933,17 +901,47 @@ fn render_settings_item(
|
||||
let (found_in_file, _) = setting_item.field.file_set_in(file.clone(), cx);
|
||||
let file_set_in = SettingsUiFile::from_settings(found_in_file.clone());
|
||||
|
||||
let clipboard_has_link = cx
|
||||
.read_from_clipboard()
|
||||
.and_then(|entry| entry.text())
|
||||
.map_or(false, |maybe_url| {
|
||||
maybe_url.strip_prefix("zed://settings/") == setting_item.field.json_path()
|
||||
});
|
||||
let (link_icon, link_icon_color) = if clipboard_has_link {
|
||||
(IconName::Check, Color::Success)
|
||||
} else {
|
||||
(IconName::Hash, Color::Muted)
|
||||
};
|
||||
|
||||
h_flex()
|
||||
.id(setting_item.title)
|
||||
.relative()
|
||||
.min_w_0()
|
||||
.justify_between()
|
||||
.child(
|
||||
v_flex()
|
||||
.w_1_2()
|
||||
.group("setting-item")
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.gap_1()
|
||||
.ml_neg_8()
|
||||
// .group_hover("setting-item", |s| s.gap_10())
|
||||
.child(
|
||||
IconButton::new("copy-link-btn", link_icon)
|
||||
.icon_color(link_icon_color)
|
||||
.icon_size(IconSize::Small)
|
||||
.shape(IconButtonShape::Square)
|
||||
.tooltip(Tooltip::text("Copy Link"))
|
||||
.when_some(setting_item.field.json_path(), |this, path| {
|
||||
this.on_click(cx.listener(move |_, _, _, cx| {
|
||||
let link = format!("zed://settings/{}", path);
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(link));
|
||||
cx.notify();
|
||||
}))
|
||||
})
|
||||
)
|
||||
.child(Label::new(SharedString::new_static(setting_item.title)))
|
||||
.when_some(
|
||||
setting_item
|
||||
@@ -986,6 +984,38 @@ fn render_settings_item(
|
||||
),
|
||||
)
|
||||
.child(control)
|
||||
// .when(sub_page_stack().is_empty(), |this| {
|
||||
// this.child(
|
||||
// div()
|
||||
// .visible_on_hover("setting-item")
|
||||
// .absolute()
|
||||
// .top_0()
|
||||
// .left_neg_5(
|
||||
// )
|
||||
// .child({
|
||||
// IconButton::new("copy-link-btn", link_icon)
|
||||
// .icon_color(link_icon_color)
|
||||
// .icon_size(IconSize::Small)
|
||||
// .shape(IconButtonShape::Square)
|
||||
// .tooltip(Tooltip::text("Copy Link"))
|
||||
// .when_some(
|
||||
// setting_item.field.json_path(),
|
||||
// |this, path| {
|
||||
// this.on_click(cx.listener(
|
||||
// move |_, _, _, cx| {
|
||||
// let link =
|
||||
// format!("zed://settings/{}", path);
|
||||
// cx.write_to_clipboard(
|
||||
// ClipboardItem::new_string(link),
|
||||
// );
|
||||
// cx.notify();
|
||||
// },
|
||||
// ))
|
||||
// },
|
||||
// )
|
||||
// }),
|
||||
// )
|
||||
// })
|
||||
}
|
||||
|
||||
struct SettingItem {
|
||||
@@ -1462,7 +1492,7 @@ impl SettingsWindow {
|
||||
|
||||
fn update_matches(&mut self, cx: &mut Context<SettingsWindow>) {
|
||||
self.search_task.take();
|
||||
let query = self.search_bar.read(cx).text(cx);
|
||||
let mut query = self.search_bar.read(cx).text(cx);
|
||||
if query.is_empty() || self.search_index.is_none() {
|
||||
for page in &mut self.filter_table {
|
||||
page.fill(true);
|
||||
@@ -1474,6 +1504,14 @@ impl SettingsWindow {
|
||||
return;
|
||||
}
|
||||
|
||||
let is_json_link_query;
|
||||
if query.starts_with("#") {
|
||||
query.remove(0);
|
||||
is_json_link_query = true;
|
||||
} else {
|
||||
is_json_link_query = false;
|
||||
}
|
||||
|
||||
let search_index = self.search_index.as_ref().unwrap().clone();
|
||||
|
||||
fn update_matches_inner(
|
||||
@@ -1487,10 +1525,11 @@ impl SettingsWindow {
|
||||
}
|
||||
|
||||
for match_index in match_indices {
|
||||
let SearchItemKey {
|
||||
let SearchKeyLUTEntry {
|
||||
page_index,
|
||||
header_index,
|
||||
item_index,
|
||||
..
|
||||
} = search_index.key_lut[match_index];
|
||||
let page = &mut this.filter_table[page_index];
|
||||
page[header_index] = true;
|
||||
@@ -1504,6 +1543,29 @@ impl SettingsWindow {
|
||||
}
|
||||
|
||||
self.search_task = Some(cx.spawn(async move |this, cx| {
|
||||
if is_json_link_query {
|
||||
let mut indices = vec![];
|
||||
for (index, SearchKeyLUTEntry { json_path, .. }) in
|
||||
search_index.key_lut.iter().enumerate()
|
||||
{
|
||||
let Some(json_path) = json_path else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(post) = query.strip_prefix(json_path)
|
||||
&& (post.is_empty() || post.starts_with('.'))
|
||||
{
|
||||
indices.push(index);
|
||||
}
|
||||
}
|
||||
if !indices.is_empty() {
|
||||
this.update(cx, |this, cx| {
|
||||
update_matches_inner(this, search_index.as_ref(), indices.into_iter(), cx);
|
||||
})
|
||||
.ok();
|
||||
return;
|
||||
}
|
||||
}
|
||||
let bm25_task = cx.background_spawn({
|
||||
let search_index = search_index.clone();
|
||||
let max_results = search_index.key_lut.len();
|
||||
@@ -1591,7 +1653,7 @@ impl SettingsWindow {
|
||||
}
|
||||
|
||||
fn build_search_index(&mut self) {
|
||||
let mut key_lut: Vec<SearchItemKey> = vec![];
|
||||
let mut key_lut: Vec<SearchKeyLUTEntry> = vec![];
|
||||
let mut documents = Vec::default();
|
||||
let mut fuzzy_match_candidates = Vec::default();
|
||||
|
||||
@@ -1613,11 +1675,16 @@ impl SettingsWindow {
|
||||
let mut header_str = "";
|
||||
for (item_index, item) in page.items.iter().enumerate() {
|
||||
let key_index = key_lut.len();
|
||||
let mut json_path = None;
|
||||
match item {
|
||||
SettingsPageItem::DynamicItem(DynamicItem {
|
||||
discriminant: item, ..
|
||||
})
|
||||
| SettingsPageItem::SettingItem(item) => {
|
||||
json_path = item
|
||||
.field
|
||||
.json_path()
|
||||
.map(|path| path.trim_end_matches('$'));
|
||||
documents.push(bm25::Document {
|
||||
id: key_index,
|
||||
contents: [page.title, header_str, item.title, item.description]
|
||||
@@ -1651,10 +1718,11 @@ impl SettingsWindow {
|
||||
push_candidates(&mut fuzzy_match_candidates, key_index, page.title);
|
||||
push_candidates(&mut fuzzy_match_candidates, key_index, header_str);
|
||||
|
||||
key_lut.push(SearchItemKey {
|
||||
key_lut.push(SearchKeyLUTEntry {
|
||||
page_index,
|
||||
header_index,
|
||||
item_index,
|
||||
json_path,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2744,12 +2812,14 @@ impl SettingsWindow {
|
||||
.track_focus(&self.content_focus_handle.focus_handle(cx))
|
||||
.flex_1()
|
||||
.pt_6()
|
||||
.px_8()
|
||||
// .px_8()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(warning_banner)
|
||||
.child(page_header)
|
||||
.child(
|
||||
div()
|
||||
.px_8()
|
||||
// .debug_bg_red()
|
||||
.size_full()
|
||||
.tab_group()
|
||||
.tab_index(CONTENT_GROUP_TAB_INDEX)
|
||||
|
||||
@@ -853,10 +853,13 @@ fn handle_open_request(request: OpenRequest, app_state: Arc<AppState>, cx: &mut
|
||||
// languages.$(language).tab_size
|
||||
// [ languages $(language) tab_size]
|
||||
workspace::with_active_or_new_workspace(cx, |_workspace, window, cx| {
|
||||
window.dispatch_action(
|
||||
Box::new(zed_actions::OpenSettingsAt { path: setting_path }),
|
||||
cx,
|
||||
);
|
||||
match setting_path {
|
||||
None => window.dispatch_action(Box::new(zed_actions::OpenSettings), cx),
|
||||
Some(setting_path) => window.dispatch_action(
|
||||
Box::new(zed_actions::OpenSettingsAt { path: setting_path }),
|
||||
cx,
|
||||
),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,10 @@ pub enum OpenRequestKind {
|
||||
AgentPanel,
|
||||
DockMenuAction { index: usize },
|
||||
BuiltinJsonSchema { schema_path: String },
|
||||
Setting { setting_path: String },
|
||||
Setting {
|
||||
// None just opens settings without navigating to a specific path
|
||||
setting_path: Option<String> ,
|
||||
},
|
||||
}
|
||||
|
||||
impl OpenRequest {
|
||||
@@ -94,9 +97,14 @@ impl OpenRequest {
|
||||
this.kind = Some(OpenRequestKind::BuiltinJsonSchema {
|
||||
schema_path: schema_path.to_string(),
|
||||
});
|
||||
} else if let Some(setting_path) = url.strip_prefix("zed://settings/") {
|
||||
} else if url == "zed://settings" || url == "zed://settings/" {
|
||||
this.kind = Some(OpenRequestKind::Setting {
|
||||
setting_path: setting_path.to_string(),
|
||||
setting_path: None
|
||||
});
|
||||
}
|
||||
else if let Some(setting_path) = url.strip_prefix("zed://settings/") {
|
||||
this.kind = Some(OpenRequestKind::Setting {
|
||||
setting_path: Some(setting_path.to_string()),
|
||||
});
|
||||
} else if url.starts_with("ssh://") {
|
||||
this.parse_ssh_file_path(&url, cx)?
|
||||
|
||||
Reference in New Issue
Block a user