Compare commits

..

21 Commits

Author SHA1 Message Date
Nathan Sobo
fff6a06226 WIP 2024-09-13 10:51:25 -04:00
Albert Marashi
3aeea93847 typescript: Highlight is predicate keyword & ... spread pattern (#17787)
Release Notes:

- Fixed the `is` and `...` highlights for TypeScript
2024-09-13 08:11:27 -04:00
CharlesChen0823
3613ebd93c editor: Fix an error when cut with vim visual line select (#17591)
Becuause in vim visual mode, we will always select next char, hit
[here](66ef318823/crates/vim/src/visual.rs (L174)),
when using editor method
for `cut` this selection, will hit this error.

Closes #17585 

Release Notes:

- N/A

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-09-12 16:55:59 -04:00
Junkui Zhang
461812d7b6 windows: Use the existing open_target function for platform::open_with_system (#17705)
Release Notes:

- N/A
2024-09-12 16:15:20 -04:00
Junkui Zhang
af819bf661 windows: Implement fs::trash_file and fs::trash_dir (#17711)
https://github.com/user-attachments/assets/43370cee-26a5-4d27-b86f-656127e03b4a



Release Notes:

- N/A
2024-09-12 16:14:53 -04:00
Jason Lee
ee96d69e37 gpui: Fix CJK line wrap for GPUI text render (#17737)
Release Notes:

- N/A

This changes is going to let GPUI render correct text wrapping for CJK
characters. We was done this in PR #11296 for Editor, but this is also
need support for other text renders.

| Before | After |
| --- | --- |
| <img width="488" alt="SCR-20240912-jtvo"
src="https://github.com/user-attachments/assets/d061669c-62ab-4a7e-a724-2df84815d1ed">
| <img width="438" alt="image"
src="https://github.com/user-attachments/assets/ec27fd80-69db-48b6-8ade-694cd65d1843">
|
2024-09-12 15:55:03 -04:00
Piotr Osiewicz
b9b62842f8 lsp: Treat unrooted paths as relative to the worktree root (#17769)
gopls would send us watch patterns like `**/*.mod` and we'd fall back to
watching `/`.

Release Notes:
- Fix file watching for go projects resorting to watching the fs root.

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-09-12 15:23:27 -04:00
Conrad Irwin
4d26f83d23 Revert "settings: Remove auxiliary Content types where possible (#16744)" (#17768)
This breaks setting `{"scrollbar": {"show":"never"}}`
Release Notes:

- N/A
2024-09-12 14:46:08 -04:00
Danilo Leal
3b37db4140 Improve button copy on database load error toast (#17767)
Minimal copywriting improvement as that just happened to me while
working on Zed during ReactConf.

Release Notes:

- N/A
2024-09-12 15:17:59 -03:00
Danilo Leal
bba380e41a docs: Add copywriting tweaks to the Vim page (#17766)
Quick writing refinements as we displayed this docs over at RustConf.
Namely:

- Removal of "here" links
- Making link anchors generally bigger
- Adding commas where suitable
- Capitalizing "Vim" (although "vim mode" is still lowercased)

---

Release Notes:

- N/A
2024-09-12 15:06:38 -03:00
renovate[bot]
6841f7b9d7 Update Python to v3.12.6 (#17728)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [python](https://redirect.github.com/containerbase/python-prebuild) |
dependencies | patch | `3.12.5` -> `3.12.6` |

---

### Release Notes

<details>
<summary>containerbase/python-prebuild (python)</summary>

###
[`v3.12.6`](https://redirect.github.com/containerbase/python-prebuild/releases/tag/3.12.6)

[Compare
Source](https://redirect.github.com/containerbase/python-prebuild/compare/3.12.5...3.12.6)

##### Bug Fixes

-   **deps:** update dependency python to v3.12.6

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC43NC4xIiwidXBkYXRlZEluVmVyIjoiMzguNzQuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-12 11:53:31 -04:00
renovate[bot]
f39c175bd3 Update Rust crate serde_json_lenient to 0.2 (#17732)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[serde_json_lenient](https://redirect.github.com/google/serde_json_lenient)
| workspace.dependencies | minor | `0.1` -> `0.2` |

---

### Release Notes

<details>
<summary>google/serde_json_lenient (serde_json_lenient)</summary>

###
[`v0.2.1`](https://redirect.github.com/google/serde_json_lenient/releases/tag/v0.2.1)

[Compare
Source](https://redirect.github.com/google/serde_json_lenient/compare/v0.2.0...v0.2.1)

- Fixed newline handling
([#&#8203;20](https://redirect.github.com/google/serde_json_lenient/issues/20))

###
[`v0.2.0`](https://redirect.github.com/google/serde_json_lenient/releases/tag/v0.2.0)

[Compare
Source](https://redirect.github.com/google/serde_json_lenient/compare/v0.1.8...v0.2.0)

-   Merged from upstream `serde_json`
- Introduce cfg!(parse_negative_zero_as_int) to create a build-time
option to parse -0 the same as other JSON parsers.
([#&#8203;16](https://redirect.github.com/google/serde_json_lenient/issues/16))
- Add an option to control escaped newlines separately from other
control
([#&#8203;18](https://redirect.github.com/google/serde_json_lenient/issues/18))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC43NC4xIiwidXBkYXRlZEluVmVyIjoiMzguNzQuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-12 11:52:59 -04:00
Marshall Bowers
0043b0d957 editor: Render documentation popovers using UI font (#17761)
This PR updates the documentation popovers to render non-code using the
UI font:

<img width="603" alt="Screenshot 2024-09-12 at 11 10 46 AM"
src="https://github.com/user-attachments/assets/c5be0676-0f0f-4ff6-8a96-76b44684e1cf">

<img width="347" alt="Screenshot 2024-09-12 at 11 21 12 AM"
src="https://github.com/user-attachments/assets/842ba922-9837-45c1-9cf2-846fe8362f99">

Requested by @davidbarsky.

Release Notes:

- Changed documentation popovers to render Markdown prose using the UI
font instead of the buffer font. Code blocks still using the buffer
font.
2024-09-12 11:32:24 -04:00
Conrad Irwin
b341079d8a Fix maxTsServerMemory (#17758)
Release Notes:

- N/A

---------

Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
2024-09-12 11:23:37 -04:00
Thorsten Ball
02d5f320ad lsp: Fix initialization_options being used as workspace configuration (#17757)
Release Notes:

- Fixed user-configured `initialization_options` being passed as
`workspace/Configuration` for the vtsls, TypeScript, and YAML language
servers.

Co-authored-by: Bennet <bennet@zed.dev>
2024-09-12 10:02:45 -04:00
Thorsten Ball
9db68ee6ae lsp: Use project-local settings if available (#17753)
Release Notes:

- Changed built-in language support (Rust, Go, C, YAML, ...) to lookup
language-server specific settings locally in project directory first
before falling back to global value.

---------

Co-authored-by: Bennet <bennet@zed.dev>
2024-09-12 09:47:25 -04:00
Conrad Irwin
092f29d394 Use a bigger prefix for numeric sorting (#17752)
Release Notes:

- Fixed sorting of files with YYYYmmddHHMMSS prefix
2024-09-12 09:11:19 -04:00
Conrad Irwin
25b6e43b0f bump eslint memory usage (#17724)
Release Notes:

- Increased memory limit for eslint to reduce crashes
2024-09-11 16:22:10 -04:00
Thorsten Ball
3a6a29f117 vim: Fix inline completions showing up in normal mode (#17727)
Booleans are hard.

Release Notes:

- Fixed inline completions showing up in Vim normal mode.
2024-09-11 16:13:17 -04:00
Thorsten Ball
9407d86ce6 project: Use login shell to get environment per project (#17717)
This is a follow-up to #17075 to spawn a login shell when getting the
environment for projects.

The reason why we didn't do it before is that we only used the
environment for certain language servers and not a lot of other things,
like tasks.

But with #17075 we now use the project more often and use it as the
_base_ environment for tasks/terminals.

Before the change, terminals and tasks would inherit the Zed process'
environment, including PATH and so on. After the change, we would set
the environment, overwriting the PATH instead of merging. But the
non-login shell environment is a subset of the login-shell environment.


Release Notes:

- Fixed environment variables used per project in terminals/tasks
overwriting the base environment and not making use of a login-shell
environment.
2024-09-11 13:33:42 -04:00
Thorsten Ball
b5c42edf1e lsp: Fix noisy logs when starting language servers (#17713)
We would log every time we'd lookup a language server for a file and
we'd also log "starting language server" even though we were about to
only download it and not start it.


Release Notes:

- N/A
2024-09-11 12:56:39 -04:00
92 changed files with 888 additions and 974 deletions

4
Cargo.lock generated
View File

@@ -9928,9 +9928,9 @@ dependencies = [
[[package]]
name = "serde_json_lenient"
version = "0.1.8"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc61c66b53a4035fcce237ef38043f4b2f0ebf918fd0e69541a5166104065581"
checksum = "a5d0bae483150302560d7cb52e7932f39b69a6fbdd099e48d33ef060a8c9c078"
dependencies = [
"indexmap 2.4.0",
"itoa",

View File

@@ -393,7 +393,7 @@ semver = "1.0"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] }
serde_json_lenient = { version = "0.1", features = [
serde_json_lenient = { version = "0.2", features = [
"preserve_order",
"raw_value",
] }
@@ -483,6 +483,7 @@ version = "0.58"
features = [
"implement",
"Foundation_Numerics",
"Storage",
"System",
"System_Threading",
"UI_ViewManagement",

View File

@@ -162,6 +162,7 @@ impl ContextOperation {
)?,
icon: section.icon_name.parse()?,
label: section.label.into(),
path: section.path.map(Into::into),
})
})
.collect::<Result<Vec<_>>>()?,
@@ -242,6 +243,10 @@ impl ContextOperation {
)),
icon_name: icon_name.to_string(),
label: section.label.to_string(),
path: section
.path
.as_ref()
.map(|p| p.to_string_lossy().to_string()),
}
})
.collect(),
@@ -641,6 +646,7 @@ impl Context {
range,
icon: section.icon,
label: section.label.clone(),
path: section.path.clone(),
})
} else {
None
@@ -1825,6 +1831,7 @@ impl Context {
..buffer.anchor_before(start + section.range.end),
icon: section.icon,
label: section.label,
path: section.path,
})
.collect::<Vec<_>>();
sections.sort_by(|a, b| a.range.cmp(&b.range, buffer));
@@ -2977,6 +2984,7 @@ impl SavedContext {
..buffer.anchor_before(section.range.end),
icon: section.icon,
label: section.label,
path: section.path,
}
})
.collect(),

View File

@@ -1089,6 +1089,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
range: section_start..section_end,
icon: ui::IconName::Ai,
label: "section".into(),
path: None,
});
}

View File

@@ -141,6 +141,7 @@ impl SlashCommand for ContextServerSlashCommand {
.description
.unwrap_or(format!("Result from {}", prompt_name)),
),
path: None,
}],
text: prompt,
run_commands_in_text: false,

View File

@@ -70,6 +70,7 @@ impl SlashCommand for DefaultSlashCommand {
range: 0..text.len(),
icon: IconName::Library,
label: "Default".into(),
path: None,
}],
text,
run_commands_in_text: true,

View File

@@ -182,6 +182,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
range: 0..1,
icon: IconName::Library,
label: "No Diagnostics".into(),
path: None,
}],
text: "\n".to_string(),
run_commands_in_text: true,
@@ -228,6 +229,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
PlaceholderType::File(file_path) => file_path.into(),
PlaceholderType::Diagnostic(_, message) => message.into(),
},
path: None,
})
.collect();

View File

@@ -349,6 +349,7 @@ impl SlashCommand for DocsSlashCommand {
range,
icon: IconName::FileDoc,
label: format!("docs ({provider}): {key}",).into(),
path: None,
})
.collect(),
run_commands_in_text: false,

View File

@@ -161,6 +161,7 @@ impl SlashCommand for FetchSlashCommand {
range,
icon: IconName::AtSign,
label: format!("fetch {}", url).into(),
path: None,
}],
run_commands_in_text: false,
})

View File

@@ -171,7 +171,6 @@ impl SlashCommand for FileSlashCommand {
.collect())
})
}
fn run(
self: Arc<Self>,
arguments: &[String],
@@ -428,7 +427,6 @@ pub fn codeblock_fence_for_path(path: Option<&Path>, row_range: Option<Range<u32
text.push('\n');
text
}
pub fn build_entry_output_section(
range: Range<usize>,
path: Option<&Path>,
@@ -454,6 +452,7 @@ pub fn build_entry_output_section(
range,
icon,
label: label.into(),
path: path.map(|p| p.to_path_buf()),
}
}

View File

@@ -57,6 +57,7 @@ impl SlashCommand for NowSlashCommand {
range,
icon: IconName::CountdownTimer,
label: now.to_rfc2822().into(),
path: None,
}],
run_commands_in_text: false,
}))

View File

@@ -140,6 +140,7 @@ impl SlashCommand for ProjectSlashCommand {
range,
icon: IconName::FileTree,
label: "Project".into(),
path: None,
}],
run_commands_in_text: false,
})

View File

@@ -95,6 +95,7 @@ impl SlashCommand for PromptSlashCommand {
range,
icon: IconName::Library,
label: title,
path: None,
}],
run_commands_in_text: true,
})

View File

@@ -165,6 +165,7 @@ impl SlashCommand for SearchSlashCommand {
range: 0..text.len(),
icon: IconName::MagnifyingGlass,
label: query,
path: None,
});
SlashCommandOutput {

View File

@@ -77,6 +77,7 @@ impl SlashCommand for OutlineSlashCommand {
range: 0..outline_text.len(),
icon: IconName::ListTree,
label: path.to_string_lossy().to_string().into(),
path: None,
}],
text: outline_text,
run_commands_in_text: false,

View File

@@ -91,6 +91,7 @@ impl SlashCommand for TerminalSlashCommand {
range,
icon: IconName::Terminal,
label: "Terminal".into(),
path: None,
}],
run_commands_in_text: false,
}))

View File

@@ -100,10 +100,10 @@ pub struct SlashCommandOutput {
pub sections: Vec<SlashCommandOutputSection<usize>>,
pub run_commands_in_text: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SlashCommandOutputSection<T> {
pub range: Range<T>,
pub icon: IconName,
pub label: SharedString,
pub path: Option<std::path::PathBuf>,
}

View File

@@ -116,30 +116,27 @@ impl Drop for MacOsUnmounter {
}
}
/// Whether or not to automatically check for updates.
#[derive(Clone, Copy, JsonSchema, Deserialize, Serialize)]
#[serde(default)]
#[serde(transparent)]
struct AutoUpdateSetting(bool);
impl Default for AutoUpdateSetting {
fn default() -> Self {
Self(true)
}
}
/// Whether or not to automatically check for updates.
///
/// Default: true
#[derive(Clone, Copy, Default, JsonSchema, Deserialize, Serialize)]
#[serde(transparent)]
struct AutoUpdateSettingContent(bool);
impl Settings for AutoUpdateSetting {
const KEY: Option<&'static str> = Some("auto_update");
type FileContent = Self;
type FileContent = Option<AutoUpdateSettingContent>;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
let auto_update = [sources.release_channel, sources.user]
.into_iter()
.find_map(|value| value.copied())
.unwrap_or(*sources.default);
.find_map(|value| value.copied().flatten())
.unwrap_or(sources.default.ok_or_else(Self::missing_default)?);
Ok(auto_update)
Ok(Self(auto_update.0))
}
}

View File

@@ -4,20 +4,30 @@ use schemars::JsonSchema;
use serde_derive::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
/// Configuration of voice calls in Zed.
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema)]
#[serde(default)]
#[derive(Deserialize, Debug)]
pub struct CallSettings {
/// Whether the microphone should be muted when joining a channel or a call.
pub mute_on_join: bool,
/// Whether your current project should be shared when joining an empty channel.
pub share_on_join: bool,
}
/// Configuration of voice calls in Zed.
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct CallSettingsContent {
/// Whether the microphone should be muted when joining a channel or a call.
///
/// Default: false
pub mute_on_join: Option<bool>,
/// Whether your current project should be shared when joining an empty channel.
///
/// Default: true
pub share_on_join: Option<bool>,
}
impl Settings for CallSettings {
const KEY: Option<&'static str> = Some("calls");
type FileContent = Self;
type FileContent = CallSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
sources.json_merge()

View File

@@ -99,26 +99,20 @@ pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(20);
actions!(client, [SignIn, SignOut, Reconnect]);
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
pub struct ClientSettings {
/// The server to connect to. If the environment variable
/// ZED_SERVER_URL is set, it will override this setting.
pub server_url: String,
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct ClientSettingsContent {
server_url: Option<String>,
}
impl Default for ClientSettings {
fn default() -> Self {
Self {
server_url: "https://zed.dev".to_owned(),
}
}
#[derive(Deserialize)]
pub struct ClientSettings {
pub server_url: String,
}
impl Settings for ClientSettings {
const KEY: Option<&'static str> = None;
type FileContent = Self;
type FileContent = ClientSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
let mut result = sources.json_merge::<Self>()?;
@@ -130,37 +124,19 @@ impl Settings for ClientSettings {
}
#[derive(Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
pub struct ProxySettings {
/// Set a proxy to use. The proxy protocol is specified by the URI scheme.
///
/// Supported URI scheme: `http`, `https`, `socks4`, `socks4a`, `socks5`,
/// `socks5h`. `http` will be used when no scheme is specified.
///
/// By default no proxy will be used, or Zed will try get proxy settings from
/// environment variables.
///
/// Examples:
/// - "proxy": "socks5://localhost:10808"
/// - "proxy": "http://127.0.0.1:10809"
#[schemars(example = "Self::example_1")]
#[schemars(example = "Self::example_2")]
pub proxy: Option<String>,
pub struct ProxySettingsContent {
proxy: Option<String>,
}
impl ProxySettings {
fn example_1() -> String {
"http://127.0.0.1:10809".to_owned()
}
fn example_2() -> String {
"socks5://localhost:10808".to_owned()
}
#[derive(Deserialize, Default)]
pub struct ProxySettings {
pub proxy: Option<String>,
}
impl Settings for ProxySettings {
const KEY: Option<&'static str> = None;
type FileContent = Self;
type FileContent = ProxySettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
Ok(Self {

View File

@@ -2261,11 +2261,11 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
cx_a.update(editor::init);
cx_b.update(editor::init);
// Turn inline-blame-off by default so no state is transferred without us explicitly doing so
let inline_blame_off_settings = InlineBlameSettings {
let inline_blame_off_settings = Some(InlineBlameSettings {
enabled: false,
delay_ms: 0,
min_column: 0,
};
delay_ms: None,
min_column: None,
});
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<ProjectSettings>(cx, |settings| {

View File

@@ -1649,7 +1649,7 @@ async fn test_following_into_excluded_file(
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions = vec!["**/.git".to_string()];
settings.file_scan_exclusions = Some(vec!["**/.git".to_string()]);
});
});
});

View File

@@ -1108,7 +1108,7 @@ impl Panel for ChatPanel {
settings::update_settings_file::<ChatPanelSettings>(
self.fs.clone(),
cx,
move |settings, _| settings.dock = position,
move |settings, _| settings.dock = Some(position),
);
}

View File

@@ -113,7 +113,9 @@ impl MessageEditor {
editor.set_show_indent_guides(false, cx);
editor.set_completion_provider(Box::new(MessageEditorCompletionProvider(this)));
editor.set_auto_replace_emoji_shortcode(
MessageEditorSettings::get_global(cx).auto_replace_emoji_shortcode,
MessageEditorSettings::get_global(cx)
.auto_replace_emoji_shortcode
.unwrap_or_default(),
);
});
@@ -128,7 +130,9 @@ impl MessageEditor {
cx.observe_global::<settings::SettingsStore>(|view, cx| {
view.editor.update(cx, |editor, cx| {
editor.set_auto_replace_emoji_shortcode(
MessageEditorSettings::get_global(cx).auto_replace_emoji_shortcode,
MessageEditorSettings::get_global(cx)
.auto_replace_emoji_shortcode
.unwrap_or_default(),
)
})
})

View File

@@ -2813,7 +2813,7 @@ impl Panel for CollabPanel {
settings::update_settings_file::<CollaborationPanelSettings>(
self.fs.clone(),
cx,
move |settings, _| settings.dock = position,
move |settings, _| settings.dock = Some(position),
);
}

View File

@@ -672,7 +672,7 @@ impl Panel for NotificationPanel {
settings::update_settings_file::<NotificationPanelSettings>(
self.fs.clone(),
cx,
move |settings, _| settings.dock = position,
move |settings, _| settings.dock = Some(position),
);
}

View File

@@ -2,84 +2,58 @@ use gpui::Pixels;
use schemars::JsonSchema;
use serde_derive::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
use ui::px;
use workspace::dock::DockPosition;
#[derive(Clone, Deserialize, Debug, JsonSchema, Serialize)]
#[serde(default)]
#[derive(Deserialize, Debug)]
pub struct CollaborationPanelSettings {
/// Whether to show the panel button in the status bar.
pub button: bool,
/// Where to dock the panel.
pub dock: DockPosition,
/// Default width of the panel in pixels.
pub default_width: Pixels,
}
impl Default for CollaborationPanelSettings {
fn default() -> Self {
Self {
button: true,
dock: DockPosition::Left,
default_width: px(240.),
}
}
}
#[derive(Clone, Deserialize, Debug, JsonSchema, Serialize)]
#[serde(default)]
#[derive(Deserialize, Debug)]
pub struct ChatPanelSettings {
/// Whether to show the panel button in the status bar.
pub button: bool,
/// Where to dock the panel.
pub dock: DockPosition,
/// Default width of the panel in pixels.
pub default_width: Pixels,
}
impl Default for ChatPanelSettings {
fn default() -> Self {
Self {
button: true,
dock: DockPosition::Right,
default_width: px(240.),
}
}
}
#[derive(Clone, Deserialize, Debug, JsonSchema, Serialize)]
#[serde(default)]
#[derive(Deserialize, Debug)]
pub struct NotificationPanelSettings {
/// Whether to show the panel button in the status bar.
pub button: bool,
/// Where to dock the panel.
pub dock: DockPosition,
/// Default width of the panel in pixels.
pub default_width: Pixels,
}
impl Default for NotificationPanelSettings {
fn default() -> Self {
Self {
button: true,
dock: DockPosition::Right,
default_width: px(380.),
}
}
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
#[serde(default)]
pub struct PanelSettingsContent {
/// Whether to show the panel button in the status bar.
///
/// Default: true
pub button: Option<bool>,
/// Where to dock the panel.
///
/// Default: left
pub dock: Option<DockPosition>,
/// Default width of the panel in pixels.
///
/// Default: 240
pub default_width: Option<f32>,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct MessageEditorSettings {
/// Whether to automatically replace emoji shortcodes with emoji characters.
/// For example: typing `:wave:` gets replaced with `👋`.
pub auto_replace_emoji_shortcode: bool,
///
/// Default: false
pub auto_replace_emoji_shortcode: Option<bool>,
}
impl Settings for CollaborationPanelSettings {
const KEY: Option<&'static str> = Some("collaboration_panel");
type FileContent = Self;
type FileContent = PanelSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,
@@ -92,7 +66,7 @@ impl Settings for CollaborationPanelSettings {
impl Settings for ChatPanelSettings {
const KEY: Option<&'static str> = Some("chat_panel");
type FileContent = Self;
type FileContent = PanelSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,
@@ -105,7 +79,7 @@ impl Settings for ChatPanelSettings {
impl Settings for NotificationPanelSettings {
const KEY: Option<&'static str> = Some("notification_panel");
type FileContent = Self;
type FileContent = PanelSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,
@@ -118,7 +92,7 @@ impl Settings for NotificationPanelSettings {
impl Settings for MessageEditorSettings {
const KEY: Option<&'static str> = Some("message_editor");
type FileContent = Self;
type FileContent = MessageEditorSettings;
fn load(
sources: SettingsSources<Self::FileContent>,

View File

@@ -4,25 +4,23 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
#[serde(default)]
/// Diagnostics configuration.
#[derive(Deserialize, Debug)]
pub struct ProjectDiagnosticsSettings {
/// Whether to show warnings or not by default.
pub include_warnings: bool,
}
impl Default for ProjectDiagnosticsSettings {
fn default() -> Self {
Self {
include_warnings: true,
}
}
/// Diagnostics configuration.
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct ProjectDiagnosticsSettingsContent {
/// Whether to show warnings or not by default.
///
/// Default: true
include_warnings: Option<bool>,
}
impl Settings for ProjectDiagnosticsSettings {
const KEY: Option<&'static str> = Some("diagnostics");
type FileContent = Self;
type FileContent = ProjectDiagnosticsSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
sources.json_merge()

View File

@@ -4975,9 +4975,10 @@ impl Editor {
let cursor = self.selections.newest_anchor().head();
let (buffer, cursor_buffer_position) =
self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
if !user_requested
&& self.enable_inline_completions
&& !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
&& (!self.enable_inline_completions
|| !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx))
{
self.discard_inline_completion(false, cx);
return None;
@@ -6670,7 +6671,11 @@ impl Editor {
let is_entire_line = selection.is_empty() || self.selections.line_mode;
if is_entire_line {
selection.start = Point::new(selection.start.row, 0);
selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
if !selection.is_empty() && selection.end.column == 0 {
selection.end = cmp::min(max_point, selection.end);
} else {
selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
}
selection.goal = SelectionGoal::None;
}
if is_first {
@@ -10639,7 +10644,7 @@ impl Editor {
let fs = workspace.read(cx).app_state().fs.clone();
let current_show = TabBarSettings::get_global(cx).show;
update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
setting.show = !current_show;
setting.show = Some(!current_show);
});
}
@@ -12562,7 +12567,7 @@ impl EditorSnapshot {
let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
matches!(
ProjectSettings::get_global(cx).git.git_gutter,
GitGutterSetting::TrackedFiles
Some(GitGutterSetting::TrackedFiles)
)
});
let gutter_settings = EditorSettings::get_global(cx).gutter;

View File

@@ -3,105 +3,38 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
#[derive(Deserialize, Clone)]
pub struct EditorSettings {
/// Whether the cursor blinks in the editor.
pub cursor_blink: bool,
/// How to highlight the current line in the editor.
pub current_line_highlight: CurrentLineHighlight,
/// Whether to show the informational hover box when moving the mouse
/// over symbols in the editor.
pub hover_popover_enabled: bool,
/// Whether to pop the completions menu while typing in an editor without
/// explicitly requesting it.
pub show_completions_on_input: bool,
/// Whether to display inline and alongside documentation for items in the
/// completions menu.
pub show_completion_documentation: bool,
/// The debounce delay before re-querying the language server for completion
/// documentation when not included in original completion list.
pub completion_documentation_secondary_query_debounce: u64,
/// Whether to use additional LSP queries to format (and amend) the code after
/// every "trigger" symbol input, defined by LSP server capabilities.
pub use_on_type_format: bool,
/// Toolbar related settings
pub toolbar: Toolbar,
/// Scrollbar related settings
pub scrollbar: Scrollbar,
/// Gutter related settings
pub gutter: Gutter,
/// Whether the editor will scroll beyond the last line.
pub scroll_beyond_last_line: ScrollBeyondLastLine,
/// The number of lines to keep above/below the cursor when auto-scrolling.
pub vertical_scroll_margin: f32,
/// Scroll sensitivity multiplier. This multiplier is applied
/// to both the horizontal and vertical delta values while scrolling.
pub scroll_sensitivity: f32,
/// Whether the line numbers on editors gutter are relative or not.
pub relative_line_numbers: bool,
/// When to populate a new search's query based on the text under the cursor.
pub seed_search_query_from_cursor: SeedQuerySetting,
pub use_smartcase_search: bool,
/// The key to use for adding multiple cursors
pub multi_cursor_modifier: MultiCursorModifier,
/// Hide the values of variables in `private` files, as defined by the
/// private_files setting. This only changes the visual representation,
/// the values are still present in the file and can be selected / copied / pasted
pub redact_private_values: bool,
/// How many lines to expand the multibuffer excerpts by default
pub expand_excerpt_lines: u32,
pub middle_click_paste: bool,
/// What to do when multibuffer is double clicked in some of its excerpts
/// (parts of singleton buffers).
#[serde(default)]
pub double_click_in_multibuffer: DoubleClickInMultibuffer,
/// Whether the editor search results will loop
pub search_wrap: bool,
#[serde(default)]
pub search: SearchSettings,
/// Show method signatures in the editor, when inside parentheses.
pub auto_signature_help: bool,
/// Whether to show the signature help after completion or a bracket pair inserted.
/// If `auto_signature_help` is enabled, this setting will be treated as enabled also.
pub show_signature_help_after_edits: bool,
/// Jupyter REPL settings.
pub jupyter: Jupyter,
}
impl Default for EditorSettings {
fn default() -> Self {
Self {
cursor_blink: true,
current_line_highlight: CurrentLineHighlight::All,
hover_popover_enabled: true,
show_completions_on_input: true,
show_completion_documentation: true,
completion_documentation_secondary_query_debounce: 300,
use_on_type_format: true,
toolbar: Default::default(),
scrollbar: Default::default(),
gutter: Default::default(),
scroll_beyond_last_line: ScrollBeyondLastLine::OnePage,
vertical_scroll_margin: 3.,
scroll_sensitivity: 1.0,
relative_line_numbers: false,
seed_search_query_from_cursor: SeedQuerySetting::Always,
multi_cursor_modifier: MultiCursorModifier::Alt,
redact_private_values: false,
expand_excerpt_lines: 3,
double_click_in_multibuffer: DoubleClickInMultibuffer::Select,
search_wrap: true,
auto_signature_help: false,
show_signature_help_after_edits: true,
jupyter: Default::default(),
use_smartcase_search: false,
middle_click_paste: true,
search: SearchSettings::default(),
}
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum CurrentLineHighlight {
@@ -139,93 +72,48 @@ pub enum DoubleClickInMultibuffer {
Open,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[derive(Debug, Clone, Deserialize)]
pub struct Jupyter {
/// Whether the Jupyter feature is enabled.
///
/// Default: true
pub enabled: bool,
}
impl Default for Jupyter {
fn default() -> Self {
Self { enabled: true }
}
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct JupyterContent {
/// Whether the Jupyter feature is enabled.
///
/// Default: true
pub enabled: Option<bool>,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(default)]
pub struct Toolbar {
/// Whether to display breadcrumbs in the editor toolbar.
pub breadcrumbs: bool,
/// Whether to display quick action buttons in the editor toolbar.
pub quick_actions: bool,
/// Whether to show the selections menu in the editor toolbar
pub selections_menu: bool,
}
impl Default for Toolbar {
fn default() -> Self {
Self {
breadcrumbs: true,
quick_actions: true,
selections_menu: true,
}
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct Scrollbar {
/// When to show the scrollbar in the editor.
pub show: ShowScrollbar,
/// Whether to show git diff indicators in the scrollbar.
pub git_diff: bool,
/// Whether to show buffer search result indicators in the scrollbar.
pub selected_symbol: bool,
/// Whether to show selected symbol occurrences in the scrollbar.
pub search_results: bool,
/// Whether to show diagnostic indicators in the scrollbar.
pub diagnostics: bool,
/// Whether to show cursor positions in the scrollbar.
pub cursors: bool,
}
impl Default for Scrollbar {
fn default() -> Self {
Self {
show: ShowScrollbar::Auto,
git_diff: true,
selected_symbol: true,
search_results: true,
diagnostics: true,
cursors: true,
}
}
}
/// Gutter-related settings.
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(default)]
pub struct Gutter {
/// Whether to show line numbers in the gutter.
pub line_numbers: bool,
/// Whether to show code action buttons in the gutter.
pub code_actions: bool,
/// Whether to show runnable buttons in the gutter.
pub runnables: bool,
/// Whether to show fold buttons in the gutter.
pub folds: bool,
}
impl Default for Gutter {
fn default() -> Self {
Self {
line_numbers: true,
code_actions: true,
runnables: true,
folds: true,
}
}
}
/// When to show the scrollbar in the editor.
///
/// Default: auto
@@ -283,6 +171,188 @@ pub struct SearchSettings {
pub regex: bool,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct EditorSettingsContent {
/// Whether the cursor blinks in the editor.
///
/// Default: true
pub cursor_blink: Option<bool>,
/// How to highlight the current line in the editor.
///
/// Default: all
pub current_line_highlight: Option<CurrentLineHighlight>,
/// Whether to show the informational hover box when moving the mouse
/// over symbols in the editor.
///
/// Default: true
pub hover_popover_enabled: Option<bool>,
/// Whether to pop the completions menu while typing in an editor without
/// explicitly requesting it.
///
/// Default: true
pub show_completions_on_input: Option<bool>,
/// Whether to display inline and alongside documentation for items in the
/// completions menu.
///
/// Default: true
pub show_completion_documentation: Option<bool>,
/// The debounce delay before re-querying the language server for completion
/// documentation when not included in original completion list.
///
/// Default: 300 ms
pub completion_documentation_secondary_query_debounce: Option<u64>,
/// Whether to use additional LSP queries to format (and amend) the code after
/// every "trigger" symbol input, defined by LSP server capabilities.
///
/// Default: true
pub use_on_type_format: Option<bool>,
/// Toolbar related settings
pub toolbar: Option<ToolbarContent>,
/// Scrollbar related settings
pub scrollbar: Option<ScrollbarContent>,
/// Gutter related settings
pub gutter: Option<GutterContent>,
/// Whether the editor will scroll beyond the last line.
///
/// Default: one_page
pub scroll_beyond_last_line: Option<ScrollBeyondLastLine>,
/// The number of lines to keep above/below the cursor when auto-scrolling.
///
/// Default: 3.
pub vertical_scroll_margin: Option<f32>,
/// Scroll sensitivity multiplier. This multiplier is applied
/// to both the horizontal and vertical delta values while scrolling.
///
/// Default: 1.0
pub scroll_sensitivity: Option<f32>,
/// Whether the line numbers on editors gutter are relative or not.
///
/// Default: false
pub relative_line_numbers: Option<bool>,
/// When to populate a new search's query based on the text under the cursor.
///
/// Default: always
pub seed_search_query_from_cursor: Option<SeedQuerySetting>,
pub use_smartcase_search: Option<bool>,
/// The key to use for adding multiple cursors
///
/// Default: alt
pub multi_cursor_modifier: Option<MultiCursorModifier>,
/// Hide the values of variables in `private` files, as defined by the
/// private_files setting. This only changes the visual representation,
/// the values are still present in the file and can be selected / copied / pasted
///
/// Default: false
pub redact_private_values: Option<bool>,
/// How many lines to expand the multibuffer excerpts by default
///
/// Default: 3
pub expand_excerpt_lines: Option<u32>,
/// Whether to enable middle-click paste on Linux
///
/// Default: true
pub middle_click_paste: Option<bool>,
/// What to do when multibuffer is double clicked in some of its excerpts
/// (parts of singleton buffers).
///
/// Default: select
pub double_click_in_multibuffer: Option<DoubleClickInMultibuffer>,
/// Whether the editor search results will loop
///
/// Default: true
pub search_wrap: Option<bool>,
/// Defaults to use when opening a new buffer and project search items.
///
/// Default: nothing is enabled
pub search: Option<SearchSettings>,
/// Whether to automatically show a signature help pop-up or not.
///
/// Default: false
pub auto_signature_help: Option<bool>,
/// Whether to show the signature help pop-up after completions or bracket pairs inserted.
///
/// Default: true
pub show_signature_help_after_edits: Option<bool>,
/// Jupyter REPL settings.
pub jupyter: Option<JupyterContent>,
}
// Toolbar related settings
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct ToolbarContent {
/// Whether to display breadcrumbs in the editor toolbar.
///
/// Default: true
pub breadcrumbs: Option<bool>,
/// Whether to display quick action buttons in the editor toolbar.
///
/// Default: true
pub quick_actions: Option<bool>,
/// Whether to show the selections menu in the editor toolbar
///
/// Default: true
pub selections_menu: Option<bool>,
}
/// Scrollbar related settings
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct ScrollbarContent {
/// When to show the scrollbar in the editor.
///
/// Default: auto
pub show: Option<ShowScrollbar>,
/// Whether to show git diff indicators in the scrollbar.
///
/// Default: true
pub git_diff: Option<bool>,
/// Whether to show buffer search result indicators in the scrollbar.
///
/// Default: true
pub search_results: Option<bool>,
/// Whether to show selected symbol occurrences in the scrollbar.
///
/// Default: true
pub selected_symbol: Option<bool>,
/// Whether to show diagnostic indicators in the scrollbar.
///
/// Default: true
pub diagnostics: Option<bool>,
/// Whether to show cursor positions in the scrollbar.
///
/// Default: true
pub cursors: Option<bool>,
}
/// Gutter related settings
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct GutterContent {
/// Whether to show line numbers in the gutter.
///
/// Default: true
pub line_numbers: Option<bool>,
/// Whether to show code action buttons in the gutter.
///
/// Default: true
pub code_actions: Option<bool>,
/// Whether to show runnable buttons in the gutter.
///
/// Default: true
pub runnables: Option<bool>,
/// Whether to show fold buttons in the gutter.
///
/// Default: true
pub folds: Option<bool>,
}
impl EditorSettings {
pub fn jupyter_enabled(cx: &AppContext) -> bool {
EditorSettings::get_global(cx).jupyter.enabled
@@ -292,7 +362,7 @@ impl EditorSettings {
impl Settings for EditorSettings {
const KEY: Option<&'static str> = None;
type FileContent = Self;
type FileContent = EditorSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,

View File

@@ -1,7 +1,7 @@
use std::sync::Arc;
use gpui::{AppContext, FontFeatures, FontWeight};
use project::project_settings::ProjectSettings;
use project::project_settings::{InlineBlameSettings, ProjectSettings};
use settings::{EditableSettingControl, Settings};
use theme::{FontFamilyCache, ThemeSettings};
use ui::{
@@ -296,7 +296,14 @@ impl EditableSettingControl for InlineGitBlameControl {
value: Self::Value,
_cx: &AppContext,
) {
settings.git.inline_blame.enabled = value;
if let Some(inline_blame) = settings.git.inline_blame.as_mut() {
inline_blame.enabled = value;
} else {
settings.git.inline_blame = Some(InlineBlameSettings {
enabled: false,
..Default::default()
});
}
}
}
@@ -342,7 +349,14 @@ impl EditableSettingControl for LineNumbersControl {
value: Self::Value,
_cx: &AppContext,
) {
settings.gutter.line_numbers = value;
if let Some(gutter) = settings.gutter.as_mut() {
gutter.line_numbers = Some(value);
} else {
settings.gutter = Some(crate::editor_settings::GutterContent {
line_numbers: Some(value),
..Default::default()
});
}
}
}
@@ -388,7 +402,7 @@ impl EditableSettingControl for RelativeLineNumbersControl {
value: Self::Value,
_cx: &AppContext,
) {
settings.relative_line_numbers = value;
settings.relative_line_numbers = Some(value);
}
}

View File

@@ -6964,7 +6964,7 @@ async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = true;
settings.auto_signature_help = Some(true);
});
});
});
@@ -7105,8 +7105,8 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui:
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = false;
settings.show_signature_help_after_edits = false;
settings.auto_signature_help = Some(false);
settings.show_signature_help_after_edits = Some(false);
});
});
});
@@ -7232,8 +7232,8 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui:
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = false;
settings.show_signature_help_after_edits = true;
settings.auto_signature_help = Some(false);
settings.show_signature_help_after_edits = Some(true);
});
});
});
@@ -7274,8 +7274,8 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui:
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = true;
settings.show_signature_help_after_edits = false;
settings.auto_signature_help = Some(true);
settings.show_signature_help_after_edits = Some(false);
});
});
});
@@ -7318,7 +7318,7 @@ async fn test_signature_help(cx: &mut gpui::TestAppContext) {
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = true;
settings.auto_signature_help = Some(true);
});
});
});
@@ -7759,7 +7759,7 @@ async fn test_completion(cx: &mut gpui::TestAppContext) {
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.show_completions_on_input = false;
settings.show_completions_on_input = Some(false);
});
})
});

View File

@@ -1283,7 +1283,10 @@ impl EditorElement {
.row,
);
let git_gutter_setting = ProjectSettings::get_global(cx).git.git_gutter;
let git_gutter_setting = ProjectSettings::get_global(cx)
.git
.git_gutter
.unwrap_or_default();
let display_hunks = buffer_snapshot
.git_diff_hunks_in_range(buffer_start_row..buffer_end_row)
.map(|hunk| diff_hunk_to_display(&hunk, snapshot))
@@ -1363,10 +1366,12 @@ impl EditorElement {
};
let padded_line_end = line_end + em_width * INLINE_BLAME_PADDING_EM_WIDTHS;
let min_column_in_pixels = self.column_pixels(
ProjectSettings::get_global(cx).git.inline_blame.min_column as usize,
cx,
);
let min_column_in_pixels = ProjectSettings::get_global(cx)
.git
.inline_blame
.and_then(|settings| settings.min_column)
.map(|col| self.column_pixels(col as usize, cx))
.unwrap_or(px(0.));
let min_start = content_origin.x - scroll_pixel_position.x + min_column_in_pixels;
cmp::max(padded_line_end, min_start)
@@ -3326,7 +3331,7 @@ impl EditorElement {
.unwrap_or_else(|| {
matches!(
ProjectSettings::get_global(cx).git.git_gutter,
GitGutterSetting::TrackedFiles
Some(GitGutterSetting::TrackedFiles)
)
});
if show_git_gutter {

View File

@@ -518,19 +518,22 @@ async fn parse_blocks(
let rendered_block = cx
.new_view(|cx| {
let settings = ThemeSettings::get_global(cx);
let ui_font_family = settings.ui_font.family.clone();
let buffer_font_family = settings.buffer_font.family.clone();
let mut base_style = cx.text_style();
base_style.refine(&TextStyleRefinement {
font_family: Some(buffer_font_family.clone()),
let mut base_text_style = cx.text_style();
base_text_style.refine(&TextStyleRefinement {
font_family: Some(ui_font_family.clone()),
color: Some(cx.theme().colors().editor_foreground),
..Default::default()
});
let markdown_style = MarkdownStyle {
base_text_style: base_style,
code_block: StyleRefinement::default().mt(rems(1.)).mb(rems(1.)),
base_text_style,
code_block: StyleRefinement::default().my(rems(1.)).font_buffer(cx),
inline_code: TextStyleRefinement {
background_color: Some(cx.theme().colors().background),
font_family: Some(buffer_font_family),
..Default::default()
},
rule_color: Color::Muted.color(cx),

View File

@@ -6,25 +6,18 @@ use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
use std::sync::Arc;
#[derive(Deserialize, Serialize, Debug, Clone, JsonSchema)]
#[serde(default)]
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)]
pub struct ExtensionSettings {
/// The extensions that should be automatically installed by Zed.
///
/// This is used to make functionality provided by extensions (e.g., language support)
/// available out-of-the-box.
#[serde(default)]
pub auto_install_extensions: HashMap<Arc<str>, bool>,
#[serde(default)]
pub auto_update_extensions: HashMap<Arc<str>, bool>,
}
impl Default for ExtensionSettings {
fn default() -> Self {
Self {
auto_install_extensions: HashMap::from_iter([("html".into(), true)]),
auto_update_extensions: Default::default(),
}
}
}
impl ExtensionSettings {
/// Returns whether the given extension should be auto-installed.
pub fn should_auto_install(&self, extension_id: &str) -> bool {

View File

@@ -1000,7 +1000,7 @@ impl ExtensionsPage {
this.update_settings::<VimModeSetting>(
selection,
cx,
|setting, value| *setting = VimModeSetting(value),
|setting, value| *setting = Some(value),
);
}),
)),

View File

@@ -342,6 +342,24 @@ impl Fs for RealFs {
}
}
#[cfg(target_os = "windows")]
async fn trash_file(&self, path: &Path, _options: RemoveOptions) -> Result<()> {
use windows::{
core::HSTRING,
Storage::{StorageDeleteOption, StorageFile},
};
// todo(windows)
// When new version of `windows-rs` release, make this operation `async`
let path = path.canonicalize()?.to_string_lossy().to_string();
let path_str = path.trim_start_matches("\\\\?\\");
if path_str.is_empty() {
anyhow::bail!("File path is empty!");
}
let file = StorageFile::GetFileFromPathAsync(&HSTRING::from(path_str))?.get()?;
file.DeleteAsync(StorageDeleteOption::Default)?.get()?;
Ok(())
}
#[cfg(target_os = "macos")]
async fn trash_dir(&self, path: &Path, options: RemoveOptions) -> Result<()> {
self.trash_file(path, options).await
@@ -352,6 +370,25 @@ impl Fs for RealFs {
self.trash_file(path, options).await
}
#[cfg(target_os = "windows")]
async fn trash_dir(&self, path: &Path, _options: RemoveOptions) -> Result<()> {
use windows::{
core::HSTRING,
Storage::{StorageDeleteOption, StorageFolder},
};
let path = path.canonicalize()?.to_string_lossy().to_string();
let path_str = path.trim_start_matches("\\\\?\\");
if path_str.is_empty() {
anyhow::bail!("Folder path is empty!");
}
// todo(windows)
// When new version of `windows-rs` release, make this operation `async`
let folder = StorageFolder::GetFolderFromPathAsync(&HSTRING::from(path_str))?.get()?;
folder.DeleteAsync(StorageDeleteOption::Default)?.get()?;
Ok(())
}
async fn open_sync(&self, path: &Path) -> Result<Box<dyn io::Read>> {
Ok(Box::new(std::fs::File::open(path)?))
}

View File

@@ -180,10 +180,18 @@ pub(crate) enum LineIndicatorFormat {
Long,
}
/// Whether or not to automatically check for updates.
///
/// Values: short, long
/// Default: short
#[derive(Clone, Copy, Default, JsonSchema, Deserialize, Serialize)]
#[serde(transparent)]
pub(crate) struct LineIndicatorFormatContent(LineIndicatorFormat);
impl Settings for LineIndicatorFormat {
const KEY: Option<&'static str> = Some("line_indicator_format");
type FileContent = Self;
type FileContent = Option<LineIndicatorFormatContent>;
fn load(
sources: SettingsSources<Self::FileContent>,
@@ -191,9 +199,9 @@ impl Settings for LineIndicatorFormat {
) -> anyhow::Result<Self> {
let format = [sources.release_channel, sources.user]
.into_iter()
.find_map(|value| value.copied())
.unwrap_or(*sources.default);
.find_map(|value| value.copied().flatten())
.unwrap_or(sources.default.ok_or_else(Self::missing_default)?);
Ok(format)
Ok(format.0)
}
}

View File

@@ -4,7 +4,7 @@ struct HelloWorld {}
impl Render for HelloWorld {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
let text = "The longest word in any of the major English language 以及中文的测试 dictionaries is pneumonoultramicroscopicsilicovolcanoconiosis, a word that refers to a lung disease contracted from the inhalation of very fine silica particles, specifically from a volcano; medically, it is the same as silicosis.";
let text = "The longest word 你好世界这段是中文,こんにちはこの段落は日本語です in any of the major English language dictionaries is pneumonoultramicroscopicsilicovolcanoconiosis, a word that refers to a lung disease contracted from the inhalation of very fine silica particles, specifically from a volcano; medically, it is the same as silicosis.";
div()
.id("page")
.size_full()
@@ -40,6 +40,7 @@ impl Render for HelloWorld {
.border_1()
.border_color(gpui::red())
.text_ellipsis()
.w_full()
.child("A short text in normal div"),
),
)

View File

@@ -5,7 +5,6 @@
use core::fmt::Debug;
use derive_more::{Add, AddAssign, Div, DivAssign, Mul, Neg, Sub, SubAssign};
use refineable::Refineable;
use schemars::JsonSchema;
use serde_derive::{Deserialize, Serialize};
use std::{
cmp::{self, PartialOrd},
@@ -2202,7 +2201,6 @@ impl From<Percentage> for Radians {
PartialEq,
Serialize,
Deserialize,
JsonSchema,
)]
#[repr(transparent)]
pub struct Pixels(pub f32);

View File

@@ -401,14 +401,19 @@ impl Platform for WindowsPlatform {
}
fn open_with_system(&self, path: &Path) {
let executor = self.background_executor().clone();
let path = path.to_owned();
executor
let Ok(full_path) = path.canonicalize() else {
log::error!("unable to parse file full path: {}", path.display());
return;
};
self.background_executor()
.spawn(async move {
let _ = std::process::Command::new("cmd")
.args(&["/c", "start", "", path.to_str().expect("path to string")])
.spawn()
.expect("Failed to open file");
let Some(full_path_str) = full_path.to_str() else {
return;
};
if full_path_str.is_empty() {
return;
};
open_target(full_path_str);
})
.detach();
}

View File

@@ -9,6 +9,8 @@ use std::{
sync::Arc,
};
use super::LineWrapper;
/// A laid out and styled line of text
#[derive(Default, Debug)]
pub struct LineLayout {
@@ -152,9 +154,18 @@ impl LineLayout {
continue;
}
if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
last_candidate_ix = Some(boundary);
last_candidate_x = x;
// Here is very similar to `LineWrapper::wrap_line` to determine text wrapping,
// but there are some differences, so we have to duplicate the code here.
if LineWrapper::is_word_char(ch) {
if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
last_candidate_ix = Some(boundary);
last_candidate_x = x;
}
} else {
if ch != ' ' && first_non_whitespace_ix.is_some() {
last_candidate_ix = Some(boundary);
last_candidate_x = x;
}
}
if ch != ' ' && first_non_whitespace_ix.is_none() {

View File

@@ -833,7 +833,7 @@ impl LanguageRegistry {
) -> Option<PendingLanguageServer> {
let server_id = self.state.write().next_language_server_id();
log::info!(
"starting language server {:?}, path: {root_path:?}, id: {server_id}",
"attempting to start language server {:?}, path: {root_path:?}, id: {server_id}",
adapter.name.0
);

View File

@@ -70,10 +70,10 @@ pub struct LanguageSettings {
/// The column at which to soft-wrap lines, for buffers where soft-wrap
/// is enabled.
pub preferred_line_length: u32,
/// Whether to show wrap guides (vertical rulers) in the editor.
/// Setting this to true will show a guide at the 'preferred_line_length' value
/// if softwrap is set to 'preferred_line_length', and will show any
/// additional guides as specified by the 'wrap_guides' setting.
// Whether to show wrap guides (vertical rulers) in the editor.
// Setting this to true will show a guide at the 'preferred_line_length' value
// if softwrap is set to 'preferred_line_length', and will show any
// additional guides as specified by the 'wrap_guides' setting.
pub show_wrap_guides: bool,
/// Character counts at which to show wrap guides (vertical rulers) in the editor.
pub wrap_guides: Vec<usize>,

View File

@@ -5,8 +5,7 @@ use gpui::AsyncAppContext;
use http_client::github::{latest_github_release, GitHubLspBinaryVersion};
pub use language::*;
use lsp::LanguageServerBinary;
use project::project_settings::{BinarySettings, ProjectSettings};
use settings::Settings;
use project::{lsp_store::language_server_settings, project_settings::BinarySettings};
use smol::fs::{self, File};
use std::{any::Any, env::consts, path::PathBuf, sync::Arc};
use util::{fs::remove_matching, maybe, ResultExt};
@@ -29,10 +28,7 @@ impl super::LspAdapter for CLspAdapter {
cx: &AsyncAppContext,
) -> Option<LanguageServerBinary> {
let configured_binary = cx.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(Self::SERVER_NAME)
.and_then(|s| s.binary.clone())
language_server_settings(delegate, Self::SERVER_NAME, cx).and_then(|s| s.binary.clone())
});
match configured_binary {

View File

@@ -5,10 +5,9 @@ use gpui::{AppContext, AsyncAppContext, Task};
use http_client::github::latest_github_release;
pub use language::*;
use lsp::LanguageServerBinary;
use project::project_settings::{BinarySettings, ProjectSettings};
use project::{lsp_store::language_server_settings, project_settings::BinarySettings};
use regex::Regex;
use serde_json::json;
use settings::Settings;
use smol::{fs, process};
use std::{
any::Any,
@@ -71,10 +70,7 @@ impl super::LspAdapter for GoLspAdapter {
cx: &AsyncAppContext,
) -> Option<LanguageServerBinary> {
let configured_binary = cx.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(Self::SERVER_NAME)
.and_then(|s| s.binary.clone())
language_server_settings(delegate, Self::SERVER_NAME, cx).and_then(|s| s.binary.clone())
});
match configured_binary {

View File

@@ -7,13 +7,10 @@ use feature_flags::FeatureFlagAppExt;
use futures::StreamExt;
use gpui::{AppContext, AsyncAppContext};
use http_client::github::{latest_github_release, GitHubLspBinaryVersion};
use language::{
CodeLabel, Language, LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate,
};
use language::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use project::ContextProviderWithTasks;
use rope::Rope;
use serde_json::{json, Value};
use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};
use smol::{
@@ -205,30 +202,6 @@ impl LspAdapter for JsonLspAdapter {
})))
}
async fn label_for_completion(
&self,
item: &lsp::CompletionItem,
language: &Arc<Language>,
) -> Option<CodeLabel> {
let text = if let Some(description) = item
.label_details
.as_ref()
.and_then(|label_details| label_details.description.as_ref())
{
format!("{} {}", item.label, description)
} else if let Some(detail) = &item.detail {
format!("{} {}", item.label, detail)
} else {
item.label.clone()
};
let rope = Rope::from(item.label.as_str());
let runs = language.highlight_text(&rope, 0..item.label.len());
Some(language::CodeLabel {
text,
runs,
filter_range: 0..item.label.len(),
})
}
async fn workspace_configuration(
self: Arc<Self>,
_: &Arc<dyn LspAdapterDelegate>,

View File

@@ -5,9 +5,9 @@ use gpui::AsyncAppContext;
use language::{ContextProvider, LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use project::project_settings::ProjectSettings;
use project::lsp_store::language_server_settings;
use serde_json::Value;
use settings::Settings;
use std::{
any::Any,
borrow::Cow,
@@ -177,13 +177,11 @@ impl LspAdapter for PythonLspAdapter {
async fn workspace_configuration(
self: Arc<Self>,
_: &Arc<dyn LspAdapterDelegate>,
adapter: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Result<Value> {
cx.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(Self::SERVER_NAME)
language_server_settings(adapter.as_ref(), Self::SERVER_NAME, cx)
.and_then(|s| s.settings.clone())
.unwrap_or_default()
})

View File

@@ -7,9 +7,8 @@ use http_client::github::{latest_github_release, GitHubLspBinaryVersion};
pub use language::*;
use language_settings::all_language_settings;
use lsp::LanguageServerBinary;
use project::project_settings::{BinarySettings, ProjectSettings};
use project::{lsp_store::language_server_settings, project_settings::BinarySettings};
use regex::Regex;
use settings::Settings;
use smol::fs::{self, File};
use std::{
any::Any,
@@ -40,10 +39,7 @@ impl LspAdapter for RustLspAdapter {
cx: &AsyncAppContext,
) -> Option<LanguageServerBinary> {
let configured_binary = cx.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(Self::SERVER_NAME)
.and_then(|s| s.binary.clone())
language_server_settings(delegate, Self::SERVER_NAME, cx).and_then(|s| s.binary.clone())
});
match configured_binary {

View File

@@ -6,9 +6,8 @@ use gpui::AsyncAppContext;
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use project::project_settings::ProjectSettings;
use project::lsp_store::language_server_settings;
use serde_json::{json, Value};
use settings::Settings;
use smol::fs;
use std::{
any::Any,
@@ -53,14 +52,12 @@ impl LspAdapter for TailwindLspAdapter {
async fn check_if_user_installed(
&self,
_delegate: &dyn LspAdapterDelegate,
delegate: &dyn LspAdapterDelegate,
cx: &AsyncAppContext,
) -> Option<LanguageServerBinary> {
let configured_binary = cx
.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(Self::SERVER_NAME)
language_server_settings(delegate, Self::SERVER_NAME, cx)
.and_then(|s| s.binary.clone())
})
.ok()??;
@@ -171,13 +168,11 @@ impl LspAdapter for TailwindLspAdapter {
async fn workspace_configuration(
self: Arc<Self>,
_: &Arc<dyn LspAdapterDelegate>,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Result<Value> {
let tailwind_user_settings = cx.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(Self::SERVER_NAME)
language_server_settings(delegate.as_ref(), Self::SERVER_NAME, cx)
.and_then(|s| s.settings.clone())
.unwrap_or_default()
})?;

View File

@@ -181,6 +181,7 @@
"import"
"in"
"instanceof"
"is"
"let"
"new"
"of"

View File

@@ -5,11 +5,11 @@
(template_string)
] @string
[
(jsx_element)
] @element
(jsx_element) @element
[
(jsx_attribute)
(jsx_opening_element)
(jsx_closing_element)
(jsx_self_closing_element)
(jsx_expression)
] @default

View File

@@ -8,10 +8,9 @@ use http_client::github::{build_asset_url, AssetKind, GitHubLspBinaryVersion};
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::{CodeActionKind, LanguageServerBinary};
use node_runtime::NodeRuntime;
use project::project_settings::ProjectSettings;
use project::lsp_store::language_server_settings;
use project::ContextProviderWithTasks;
use serde_json::{json, Value};
use settings::Settings;
use smol::{fs, io::BufReader, stream::StreamExt};
use std::{
any::Any,
@@ -58,7 +57,11 @@ fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
}
fn eslint_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
vec![server_path.into(), "--stdio".into()]
vec![
"--max-old-space-size=8192".into(),
server_path.into(),
"--stdio".into(),
]
}
pub struct TypeScriptLspAdapter {
@@ -232,14 +235,12 @@ impl LspAdapter for TypeScriptLspAdapter {
async fn workspace_configuration(
self: Arc<Self>,
_: &Arc<dyn LspAdapterDelegate>,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Result<Value> {
let override_options = cx.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(Self::SERVER_NAME)
.and_then(|s| s.initialization_options.clone())
language_server_settings(delegate.as_ref(), Self::SERVER_NAME, cx)
.and_then(|s| s.settings.clone())
})?;
if let Some(options) = override_options {
return Ok(options);
@@ -330,9 +331,7 @@ impl LspAdapter for EsLintLspAdapter {
let workspace_root = delegate.worktree_root_path();
let eslint_user_settings = cx.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(Self::SERVER_NAME)
language_server_settings(delegate.as_ref(), Self::SERVER_NAME, cx)
.and_then(|s| s.settings.clone())
.unwrap_or_default()
})?;

View File

@@ -100,6 +100,7 @@
] @punctuation.delimiter
[
"..."
"-"
"--"
"-="
@@ -181,6 +182,7 @@
"import"
"in"
"instanceof"
"is"
"let"
"new"
"of"

View File

@@ -5,16 +5,15 @@ use gpui::AsyncAppContext;
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::{CodeActionKind, LanguageServerBinary};
use node_runtime::NodeRuntime;
use project::project_settings::{BinarySettings, ProjectSettings};
use project::{lsp_store::language_server_settings, project_settings::BinarySettings};
use serde_json::{json, Value};
use settings::{Settings, SettingsLocation};
use std::{
any::Any,
ffi::OsString,
path::{Path, PathBuf},
sync::Arc,
};
use util::{maybe, merge_json_value_into, ResultExt};
use util::{maybe, ResultExt};
fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
vec![server_path.into(), "--stdio".into()]
@@ -75,10 +74,7 @@ impl LspAdapter for VtslsLspAdapter {
cx: &AsyncAppContext,
) -> Option<LanguageServerBinary> {
let configured_binary = cx.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(SERVER_NAME)
.and_then(|s| s.binary.clone())
language_server_settings(delegate, SERVER_NAME, cx).and_then(|s| s.binary.clone())
});
match configured_binary {
@@ -226,9 +222,6 @@ impl LspAdapter for VtslsLspAdapter {
"suggest": {
"completeFunctionCalls": true
},
"tsserver": {
"maxTsServerMemory": 8092
},
"inlayHints": {
"parameterNames": {
"enabled": "all",
@@ -270,33 +263,28 @@ impl LspAdapter for VtslsLspAdapter {
async fn workspace_configuration(
self: Arc<Self>,
adapter: &Arc<dyn LspAdapterDelegate>,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Result<Value> {
let override_options = cx.update(|cx| {
ProjectSettings::get(
Some(SettingsLocation {
worktree_id: adapter.worktree_id(),
path: adapter.worktree_root_path(),
}),
cx,
)
.lsp
.get(SERVER_NAME)
.and_then(|s| s.initialization_options.clone())
language_server_settings(delegate.as_ref(), SERVER_NAME, cx)
.and_then(|s| s.settings.clone())
})?;
if let Some(options) = override_options {
return Ok(options);
}
let mut initialization_options = self
.initialization_options(adapter)
.await
.map(|o| o.unwrap())?;
if let Some(override_options) = override_options {
merge_json_value_into(override_options, &mut initialization_options)
}
Ok(initialization_options)
let config = serde_json::json!({
"tsserver": {
"maxTsServerMemory": 8092
},
});
Ok(serde_json::json!({
"typescript": config,
"javascript": config
}))
}
fn language_ids(&self) -> HashMap<String, String> {

View File

@@ -7,7 +7,7 @@ use language::{
};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use project::project_settings::ProjectSettings;
use project::lsp_store::language_server_settings;
use serde_json::Value;
use settings::{Settings, SettingsLocation};
use smol::fs;
@@ -44,14 +44,12 @@ impl LspAdapter for YamlLspAdapter {
async fn check_if_user_installed(
&self,
_delegate: &dyn LspAdapterDelegate,
delegate: &dyn LspAdapterDelegate,
cx: &AsyncAppContext,
) -> Option<LanguageServerBinary> {
let configured_binary = cx
.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(Self::SERVER_NAME)
language_server_settings(delegate, Self::SERVER_NAME, cx)
.and_then(|s| s.binary.clone())
})
.ok()??;
@@ -147,10 +145,8 @@ impl LspAdapter for YamlLspAdapter {
let mut options = serde_json::json!({"[yaml]": {"editor.tabSize": tab_size}});
let project_options = cx.update(|cx| {
ProjectSettings::get_global(cx)
.lsp
.get(Self::SERVER_NAME)
.and_then(|s| s.initialization_options.clone())
language_server_settings(delegate.as_ref(), Self::SERVER_NAME, cx)
.and_then(|s| s.settings.clone())
})?;
if let Some(override_options) = project_options {
merge_json_value_into(override_options, &mut options);

View File

@@ -272,7 +272,7 @@ impl LanguageServer {
};
log::info!(
"starting language server. binary path: {:?}, working directory: {:?}, args: {:?}",
"starting language server process. binary path: {:?}, working directory: {:?}, args: {:?}",
binary.path,
working_dir,
&binary.arguments

View File

@@ -24,12 +24,12 @@ use editor::{
use file_icons::FileIcons;
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
use gpui::{
actions, anchored, deferred, div, impl_actions, uniform_list, Action, AnyElement, AppContext,
AssetSource, AsyncWindowContext, ClipboardItem, DismissEvent, Div, ElementId, EventEmitter,
FocusHandle, FocusableView, HighlightStyle, InteractiveElement, IntoElement, KeyContext, Model,
MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Render, SharedString, Stateful,
Styled, Subscription, Task, UniformListScrollHandle, View, ViewContext, VisualContext,
WeakView, WindowContext,
actions, anchored, deferred, div, impl_actions, px, uniform_list, Action, AnyElement,
AppContext, AssetSource, AsyncWindowContext, ClipboardItem, DismissEvent, Div, ElementId,
EventEmitter, FocusHandle, FocusableView, HighlightStyle, InteractiveElement, IntoElement,
KeyContext, Model, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Render,
SharedString, Stateful, Styled, Subscription, Task, UniformListScrollHandle, View, ViewContext,
VisualContext, WeakView, WindowContext,
};
use itertools::Itertools;
use language::{BufferId, BufferSnapshot, OffsetRangeExt, OutlineItem};
@@ -1938,7 +1938,7 @@ impl OutlinePanel {
.child(
ListItem::new(item_id)
.indent_level(depth)
.indent_step_size(settings.indent_size)
.indent_step_size(px(settings.indent_size))
.selected(is_active)
.when_some(icon_element, |list_item, icon_element| {
list_item.child(h_flex().child(icon_element))
@@ -3801,7 +3801,7 @@ impl Panel for OutlinePanel {
DockPosition::Left | DockPosition::Bottom => OutlinePanelDockPosition::Left,
DockPosition::Right => OutlinePanelDockPosition::Right,
};
settings.dock = dock;
settings.dock = Some(dock);
},
);
}

View File

@@ -1,5 +1,4 @@
use anyhow;
use gpui::{px, Pixels};
use gpui::Pixels;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
@@ -11,51 +10,66 @@ pub enum OutlinePanelDockPosition {
Right,
}
#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, JsonSchema)]
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
pub struct OutlinePanelSettings {
/// Whether to show the outline panel button in the status bar.
pub button: bool,
/// Customize default width (in pixels) taken by outline panel
pub default_width: Pixels,
/// The position of outline panel
pub dock: OutlinePanelDockPosition,
/// Whether to show file icons in the outline panel.
pub file_icons: bool,
/// Whether to show folder icons or chevrons for directories in the outline panel.
pub folder_icons: bool,
/// Whether to show the git status in the outline panel.
pub git_status: bool,
/// Amount of indentation (in pixels) for nested items.
pub indent_size: Pixels,
/// Whether to reveal it in the outline panel automatically,
/// when a corresponding project entry becomes active.
/// Gitignored entries are never auto revealed.
pub indent_size: f32,
pub auto_reveal_entries: bool,
/// Whether to fold directories automatically
/// when directory has only one directory inside.
pub auto_fold_dirs: bool,
}
impl Default for OutlinePanelSettings {
fn default() -> Self {
Self {
button: true,
default_width: px(240.),
dock: OutlinePanelDockPosition::Left,
file_icons: true,
folder_icons: true,
auto_fold_dirs: true,
auto_reveal_entries: true,
indent_size: px(20.),
git_status: true,
}
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct OutlinePanelSettingsContent {
/// Whether to show the outline panel button in the status bar.
///
/// Default: true
pub button: Option<bool>,
/// Customize default width (in pixels) taken by outline panel
///
/// Default: 240
pub default_width: Option<f32>,
/// The position of outline panel
///
/// Default: left
pub dock: Option<OutlinePanelDockPosition>,
/// Whether to show file icons in the outline panel.
///
/// Default: true
pub file_icons: Option<bool>,
/// Whether to show folder icons or chevrons for directories in the outline panel.
///
/// Default: true
pub folder_icons: Option<bool>,
/// Whether to show the git status in the outline panel.
///
/// Default: true
pub git_status: Option<bool>,
/// Amount of indentation (in pixels) for nested items.
///
/// Default: 20
pub indent_size: Option<f32>,
/// Whether to reveal it in the outline panel automatically,
/// when a corresponding project entry becomes active.
/// Gitignored entries are never auto revealed.
///
/// Default: true
pub auto_reveal_entries: Option<bool>,
/// Whether to fold directories automatically
/// when directory has only one directory inside.
///
/// Default: true
pub auto_fold_dirs: Option<bool>,
}
impl Settings for OutlinePanelSettings {
const KEY: Option<&'static str> = Some("outline_panel");
type FileContent = Self;
type FileContent = OutlinePanelSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,

View File

@@ -1,184 +0,0 @@
use std::time::Instant;
use anyhow::Result;
use gpui::{
div, AppContext, InteractiveElement as _, Render, StatefulInteractiveElement as _,
Subscription, ViewContext, VisualContext,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources, SettingsStore};
use workspace::{
ui::{Label, LabelCommon, LabelSize, Tooltip},
ItemHandle, StatusItemView, Workspace,
};
const SHOW_STARTUP_TIME_DURATION: std::time::Duration = std::time::Duration::from_secs(5);
pub fn init(cx: &mut AppContext) {
PerformanceSettings::register(cx);
let mut enabled = PerformanceSettings::get_global(cx).show_in_status_bar;
let start_time = Instant::now();
let mut _observe_workspaces = toggle_status_bar_items(enabled, start_time, cx);
cx.observe_global::<SettingsStore>(move |cx| {
let new_value = PerformanceSettings::get_global(cx).show_in_status_bar;
if new_value != enabled {
enabled = new_value;
_observe_workspaces = toggle_status_bar_items(enabled, start_time, cx);
}
})
.detach();
}
fn toggle_status_bar_items(
enabled: bool,
start_time: Instant,
cx: &mut AppContext,
) -> Option<Subscription> {
for window in cx.windows() {
if let Some(workspace) = window.downcast::<Workspace>() {
workspace
.update(cx, |workspace, cx| {
toggle_status_bar_item(workspace, enabled, start_time, cx);
})
.ok();
}
}
if enabled {
log::info!("performance metrics display enabled");
Some(cx.observe_new_views::<Workspace>(move |workspace, cx| {
toggle_status_bar_item(workspace, true, start_time, cx);
}))
} else {
log::info!("performance metrics display disabled");
None
}
}
struct PerformanceStatusBarItem {
display_mode: DisplayMode,
}
#[derive(Copy, Clone, Debug)]
enum DisplayMode {
StartupTime,
Fps,
}
impl PerformanceStatusBarItem {
fn new(start_time: Instant, cx: &mut ViewContext<Self>) -> Self {
let now = Instant::now();
let display_mode = if now < start_time + SHOW_STARTUP_TIME_DURATION {
DisplayMode::StartupTime
} else {
DisplayMode::Fps
};
let this = Self { display_mode };
if let DisplayMode::StartupTime = display_mode {
cx.spawn(|this, mut cx| async move {
let now = Instant::now();
let remaining_duration =
(start_time + SHOW_STARTUP_TIME_DURATION).saturating_duration_since(now);
cx.background_executor().timer(remaining_duration).await;
this.update(&mut cx, |this, cx| {
this.display_mode = DisplayMode::Fps;
cx.notify();
})
.ok();
})
.detach();
}
this
}
}
impl Render for PerformanceStatusBarItem {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement {
let text = match self.display_mode {
DisplayMode::StartupTime => cx
.time_to_first_window_draw()
.map_or("Pending".to_string(), |duration| {
format!("{}ms", duration.as_millis())
}),
DisplayMode::Fps => cx.fps().map_or("".to_string(), |fps| {
format!("{:3} FPS", fps.round() as u32)
}),
};
use gpui::ParentElement;
let display_mode = self.display_mode;
div()
.id("performance status")
.child(Label::new(text).size(LabelSize::Small))
.tooltip(move |cx| match display_mode {
DisplayMode::StartupTime => Tooltip::text("Time to first window draw", cx),
DisplayMode::Fps => cx
.new_view(|cx| {
let tooltip = Tooltip::new("Current FPS");
if let Some(time_to_first) = cx.time_to_first_window_draw() {
tooltip.meta(format!(
"Time to first window draw: {}ms",
time_to_first.as_millis()
))
} else {
tooltip
}
})
.into(),
})
}
}
impl StatusItemView for PerformanceStatusBarItem {
fn set_active_pane_item(
&mut self,
_active_pane_item: Option<&dyn ItemHandle>,
_cx: &mut gpui::ViewContext<Self>,
) {
// This is not currently used.
}
}
fn toggle_status_bar_item(
workspace: &mut Workspace,
enabled: bool,
start_time: Instant,
cx: &mut ViewContext<Workspace>,
) {
if enabled {
workspace.status_bar().update(cx, |bar, cx| {
bar.add_right_item(
cx.new_view(|cx| PerformanceStatusBarItem::new(start_time, cx)),
cx,
)
});
} else {
workspace.status_bar().update(cx, |bar, cx| {
bar.remove_items_of_type::<PerformanceStatusBarItem>(cx);
});
}
}
/// Configuration of the display of performance details.
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
#[serde(default)]
pub struct PerformanceSettings {
/// Display the time to first window draw and frame rate in the status bar.
pub show_in_status_bar: bool,
}
impl Settings for PerformanceSettings {
const KEY: Option<&'static str> = Some("performance");
type FileContent = Self;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
sources.json_merge()
}
}

View File

@@ -219,7 +219,7 @@ async fn load_shell_environment(
);
let output = smol::process::Command::new(&shell)
.args(["-i", "-c", &command])
.args(["-l", "-i", "-c", &command])
.envs(direnv_environment)
.output()
.await

View File

@@ -3,7 +3,7 @@ use crate::{
environment::ProjectEnvironment,
lsp_command::{self, *},
lsp_ext_command,
project_settings::ProjectSettings,
project_settings::{LspSettings, ProjectSettings},
relativize_path, resolve_path,
worktree_store::{WorktreeStore, WorktreeStoreEvent},
yarn::YarnPathStore,
@@ -26,7 +26,6 @@ use gpui::{
Task, WeakModel,
};
use http_client::{AsyncBody, Error, HttpClient, Request, Response, Uri};
use itertools::Itertools;
use language::{
language_settings::{
all_language_settings, language_settings, AllLanguageSettings, LanguageSettings,
@@ -3500,7 +3499,7 @@ impl LspStore {
.to_owned();
let path = if Path::new(path).components().next().is_none()
{
Arc::from(Path::new("/"))
Arc::from(Path::new(worktree_root_path))
} else {
PathBuf::from(path).into()
};
@@ -4489,14 +4488,6 @@ impl LspStore {
);
}
log::info!(
"starting language servers for {language}: {adapters}",
adapters = enabled_lsp_adapters
.iter()
.map(|adapter| adapter.name.0.as_ref())
.join(", ")
);
for adapter in &enabled_lsp_adapters {
self.start_language_server(worktree, adapter.clone(), language.clone(), cx);
}
@@ -7044,6 +7035,23 @@ impl HttpClient for BlockedHttpClient {
None
}
}
pub fn language_server_settings<'a, 'b: 'a>(
delegate: &'a dyn LspAdapterDelegate,
language: &str,
cx: &'b AppContext,
) -> Option<&'a LspSettings> {
ProjectSettings::get(
Some(SettingsLocation {
worktree_id: delegate.worktree_id(),
path: delegate.worktree_root_path(),
}),
cx,
)
.lsp
.get(language)
}
#[async_trait]
impl LspAdapterDelegate for ProjectLspAdapterDelegate {
fn show_notification(&self, message: &str, cx: &mut AppContext) {

View File

@@ -20,7 +20,6 @@ use worktree::{PathChange, UpdatedEntriesSet, Worktree, WorktreeId};
use crate::worktree_store::{WorktreeStore, WorktreeStoreEvent};
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
pub struct ProjectSettings {
/// Configuration for language servers.
///
@@ -42,6 +41,7 @@ pub struct ProjectSettings {
pub load_direnv: DirenvSettings,
/// Configuration for session-related features
#[serde(default)]
pub session: SessionSettings,
}
@@ -59,31 +59,36 @@ pub enum DirenvSettings {
}
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
pub struct GitSettings {
/// Whether or not to show the git gutter.
///
/// Default: tracked_files
pub git_gutter: GitGutterSetting,
pub git_gutter: Option<GitGutterSetting>,
pub gutter_debounce: Option<u64>,
/// Whether or not to show git blame data inline in
/// the currently focused line.
///
/// Default: on
pub inline_blame: InlineBlameSettings,
pub inline_blame: Option<InlineBlameSettings>,
}
impl GitSettings {
pub fn inline_blame_enabled(&self) -> bool {
#[allow(unknown_lints, clippy::manual_unwrap_or_default)]
self.inline_blame.enabled
match self.inline_blame {
Some(InlineBlameSettings { enabled, .. }) => enabled,
_ => false,
}
}
pub fn inline_blame_delay(&self) -> Option<Duration> {
self.inline_blame
.delay_ms
.gt(&0)
.then(|| Duration::from_millis(self.inline_blame.delay_ms))
match self.inline_blame {
Some(InlineBlameSettings {
delay_ms: Some(delay_ms),
..
}) if delay_ms > 0 => Some(Duration::from_millis(delay_ms)),
_ => None,
}
}
}
@@ -97,34 +102,28 @@ pub enum GitGutterSetting {
Hide,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
#[serde(default)]
pub struct InlineBlameSettings {
/// Whether or not to show git blame data inline in
/// the currently focused line.
///
/// Default: true
#[serde(default = "true_value")]
pub enabled: bool,
/// Whether to only show the inline blame information
/// after a delay once the cursor stops moving.
///
/// Default: 0
pub delay_ms: u64,
pub delay_ms: Option<u64>,
/// The minimum column number to show the inline blame information at
///
/// Default: 0
pub min_column: u32,
pub min_column: Option<u32>,
}
impl Default for InlineBlameSettings {
fn default() -> Self {
Self {
enabled: true,
delay_ms: 0,
min_column: 0,
}
}
const fn true_value() -> bool {
true
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]

View File

@@ -2289,7 +2289,7 @@ impl ProjectPanel {
.child(
ListItem::new(entry_id.to_proto() as usize)
.indent_level(depth)
.indent_step_size(settings.indent_size)
.indent_step_size(px(settings.indent_size))
.selected(is_marked || is_active)
.when_some(canonical_path, |this, path| {
this.end_slot::<AnyElement>(
@@ -2817,7 +2817,7 @@ impl Render for DraggedProjectEntryView {
this.bg(cx.theme().colors().background).w(self.width).child(
ListItem::new(self.selection.entry_id.to_proto() as usize)
.indent_level(self.details.depth)
.indent_step_size(settings.indent_size)
.indent_step_size(px(settings.indent_size))
.child(if let Some(icon) = &self.details.icon {
div().child(Icon::from_path(icon.clone()))
} else {
@@ -2855,7 +2855,7 @@ impl Panel for ProjectPanel {
DockPosition::Left | DockPosition::Bottom => ProjectPanelDockPosition::Left,
DockPosition::Right => ProjectPanelDockPosition::Right,
};
settings.dock = dock;
settings.dock = Some(dock);
},
);
}
@@ -3029,7 +3029,7 @@ mod tests {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
worktree_settings.file_scan_exclusions =
vec!["**/.git".to_string(), "**/4/**".to_string()];
Some(vec!["**/.git".to_string(), "**/4/**".to_string()]);
});
});
});
@@ -4818,10 +4818,10 @@ mod tests {
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
worktree_settings.file_scan_exclusions = Vec::new();
worktree_settings.file_scan_exclusions = Some(Vec::new());
});
store.update_user_settings::<ProjectPanelSettings>(cx, |project_panel_settings| {
project_panel_settings.auto_reveal_entries = false
project_panel_settings.auto_reveal_entries = Some(false)
});
})
});
@@ -4940,7 +4940,7 @@ mod tests {
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<ProjectPanelSettings>(cx, |project_panel_settings| {
project_panel_settings.auto_reveal_entries = true
project_panel_settings.auto_reveal_entries = Some(true)
});
})
});
@@ -5054,10 +5054,10 @@ mod tests {
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
worktree_settings.file_scan_exclusions = Vec::new();
worktree_settings.file_scan_exclusions = Some(Vec::new());
});
store.update_user_settings::<ProjectPanelSettings>(cx, |project_panel_settings| {
project_panel_settings.auto_reveal_entries = false
project_panel_settings.auto_reveal_entries = Some(false)
});
})
});
@@ -5256,7 +5256,7 @@ mod tests {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
project_settings.file_scan_exclusions =
vec!["excluded_dir".to_string(), "**/.git".to_string()];
Some(vec!["excluded_dir".to_string(), "**/.git".to_string()]);
});
});
});
@@ -5569,10 +5569,10 @@ mod tests {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<ProjectPanelSettings>(cx, |project_panel_settings| {
project_panel_settings.auto_fold_dirs = false;
project_panel_settings.auto_fold_dirs = Some(false);
});
store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
worktree_settings.file_scan_exclusions = Vec::new();
worktree_settings.file_scan_exclusions = Some(Vec::new());
});
});
});
@@ -5591,10 +5591,10 @@ mod tests {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<ProjectPanelSettings>(cx, |project_panel_settings| {
project_panel_settings.auto_fold_dirs = false;
project_panel_settings.auto_fold_dirs = Some(false);
});
store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
worktree_settings.file_scan_exclusions = Vec::new();
worktree_settings.file_scan_exclusions = Some(Vec::new());
});
});
});

View File

@@ -2,7 +2,6 @@ use gpui::Pixels;
use schemars::JsonSchema;
use serde_derive::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
use ui::px;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)]
#[serde(rename_all = "snake_case")]
@@ -11,50 +10,20 @@ pub enum ProjectPanelDockPosition {
Right,
}
#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, JsonSchema)]
#[serde(default)]
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
pub struct ProjectPanelSettings {
/// Whether to show the project panel button in the status bar.
pub button: bool,
/// Customize default width (in pixels) taken by project panel
pub default_width: Pixels,
/// The position of project panel
pub dock: ProjectPanelDockPosition,
/// Whether to show file icons in the project panel.
pub file_icons: bool,
/// Whether to show folder icons or chevrons for directories in the project panel.
pub folder_icons: bool,
/// Whether to show the git status in the project panel.
pub git_status: bool,
/// Amount of indentation (in pixels) for nested items.
pub indent_size: Pixels,
/// Whether to reveal it in the project panel automatically,
/// when a corresponding project entry becomes active.
/// Gitignored entries are never auto revealed.
pub indent_size: f32,
pub auto_reveal_entries: bool,
/// Whether to fold directories automatically
/// when directory has only one directory inside.
pub auto_fold_dirs: bool,
/// Scrollbar-related settings
pub scrollbar: ScrollbarSettings,
}
impl Default for ProjectPanelSettings {
fn default() -> Self {
Self {
button: true,
default_width: px(240.),
dock: ProjectPanelDockPosition::Left,
file_icons: true,
folder_icons: true,
git_status: true,
indent_size: px(20.),
auto_reveal_entries: true,
auto_fold_dirs: true,
scrollbar: Default::default(),
}
}
}
/// When to show the scrollbar in the project panel.
///
/// Default: always
@@ -68,7 +37,7 @@ pub enum ShowScrollbar {
Never,
}
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct ScrollbarSettings {
/// When to show the scrollbar in the project panel.
///
@@ -76,10 +45,63 @@ pub struct ScrollbarSettings {
pub show: ShowScrollbar,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct ScrollbarSettingsContent {
/// When to show the scrollbar in the project panel.
///
/// Default: always
pub show: Option<ShowScrollbar>,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct ProjectPanelSettingsContent {
/// Whether to show the project panel button in the status bar.
///
/// Default: true
pub button: Option<bool>,
/// Customize default width (in pixels) taken by project panel
///
/// Default: 240
pub default_width: Option<f32>,
/// The position of project panel
///
/// Default: left
pub dock: Option<ProjectPanelDockPosition>,
/// Whether to show file icons in the project panel.
///
/// Default: true
pub file_icons: Option<bool>,
/// Whether to show folder icons or chevrons for directories in the project panel.
///
/// Default: true
pub folder_icons: Option<bool>,
/// Whether to show the git status in the project panel.
///
/// Default: true
pub git_status: Option<bool>,
/// Amount of indentation (in pixels) for nested items.
///
/// Default: 20
pub indent_size: Option<f32>,
/// Whether to reveal it in the project panel automatically,
/// when a corresponding project entry becomes active.
/// Gitignored entries are never auto revealed.
///
/// Default: true
pub auto_reveal_entries: Option<bool>,
/// Whether to fold directories automatically
/// when directory has only one directory inside.
///
/// Default: false
pub auto_fold_dirs: Option<bool>,
/// Scrollbar-related settings
pub scrollbar: Option<ScrollbarSettingsContent>,
}
impl Settings for ProjectPanelSettings {
const KEY: Option<&'static str> = Some("project_panel");
type FileContent = Self;
type FileContent = ProjectPanelSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,

View File

@@ -2388,6 +2388,7 @@ message SlashCommandOutputSection {
AnchorRange range = 1;
string icon_name = 2;
string label = 3;
optional string path = 4;
}
message ContextOperation {

View File

@@ -48,6 +48,7 @@ use workspace::{notifications::DetachAndPromptErr, AppState, ModalView, Workspac
use crate::open_dev_server_project;
use crate::ssh_connections::connect_over_ssh;
use crate::ssh_connections::open_ssh_project;
use crate::ssh_connections::RemoteSettingsContent;
use crate::ssh_connections::SshConnection;
use crate::ssh_connections::SshConnectionModal;
use crate::ssh_connections::SshProject;
@@ -1023,7 +1024,7 @@ impl DevServerProjects {
fn update_settings_file(
&mut self,
cx: &mut ViewContext<Self>,
f: impl FnOnce(&mut SshSettings) + Send + Sync + 'static,
f: impl FnOnce(&mut RemoteSettingsContent) + Send + Sync + 'static,
) {
let Some(fs) = self
.workspace

View File

@@ -22,24 +22,8 @@ use ui::{
use util::paths::PathWithPosition;
use workspace::{AppState, ModalView, Workspace};
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
#[derive(Deserialize)]
pub struct SshSettings {
/// ssh_connections is an array of ssh connections.
/// By default this setting is null, which disables the direct ssh connection support.
/// You can configure these from `project: Open Remote` in the command palette.
/// Zed's ssh support will pull configuration from your ~/.ssh too.
/// Examples:
/// [
/// {
/// "host": "example-box",
/// "projects": [
/// {
/// "paths": ["/home/user/code/zed"]
/// }
/// ]
/// }
/// ]
pub ssh_connections: Option<Vec<SshConnection>>,
}
@@ -78,10 +62,15 @@ pub struct SshProject {
pub paths: Vec<String>,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct RemoteSettingsContent {
pub ssh_connections: Option<Vec<SshConnection>>,
}
impl Settings for SshSettings {
const KEY: Option<&'static str> = None;
type FileContent = Self;
type FileContent = RemoteSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
sources.json_merge()

View File

@@ -6,10 +6,8 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
#[derive(Debug, Default)]
pub struct JupyterSettings {
/// Default kernels to select for each language.
pub kernel_selections: HashMap<String, String>,
}
@@ -22,10 +20,26 @@ impl JupyterSettings {
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
pub struct JupyterSettingsContent {
/// Default kernels to select for each language.
///
/// Default: `{}`
pub kernel_selections: Option<HashMap<String, String>>,
}
impl Default for JupyterSettingsContent {
fn default() -> Self {
JupyterSettingsContent {
kernel_selections: Some(HashMap::new()),
}
}
}
impl Settings for JupyterSettings {
const KEY: Option<&'static str> = Some("jupyter");
type FileContent = Self;
type FileContent = JupyterSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,
@@ -37,8 +51,10 @@ impl Settings for JupyterSettings {
let mut settings = JupyterSettings::default();
for value in sources.defaults_and_customizations() {
for (k, v) in &value.kernel_selections {
settings.kernel_selections.insert(k.clone(), v.clone());
if let Some(source) = &value.kernel_selections {
for (k, v) in source {
settings.kernel_selections.insert(k.clone(), v.clone());
}
}
}

View File

@@ -2,26 +2,22 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
#[derive(Clone, Serialize, Deserialize, PartialEq, JsonSchema)]
#[serde(default)]
/// Task-related settings.
#[derive(Serialize, Deserialize, PartialEq, Default)]
pub(crate) struct TaskSettings {
/// Whether to show task status indicator in the status bar. Default: true
pub(crate) show_status_indicator: bool,
}
impl Default for TaskSettings {
fn default() -> Self {
Self {
show_status_indicator: true,
}
}
/// Task-related settings.
#[derive(Serialize, Deserialize, PartialEq, Default, Clone, JsonSchema)]
pub(crate) struct TaskSettingsContent {
/// Whether to show task status indicator in the status bar. Default: true
show_status_indicator: Option<bool>,
}
impl Settings for TaskSettings {
const KEY: Option<&'static str> = Some("task");
type FileContent = Self;
type FileContent = TaskSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,

View File

@@ -644,7 +644,7 @@ impl<T: Ord + Clone> RangeExt<T> for RangeInclusive<T> {
/// This is useful for turning regular alphanumerically sorted sequences as `1-abc, 10, 11-def, .., 2, 21-abc`
/// into `1-abc, 2, 10, 11-def, .., 21-abc`
#[derive(Debug, PartialEq, Eq)]
pub struct NumericPrefixWithSuffix<'a>(Option<u32>, &'a str);
pub struct NumericPrefixWithSuffix<'a>(Option<u64>, &'a str);
impl<'a> NumericPrefixWithSuffix<'a> {
pub fn from_numeric_prefixed_str(str: &'a str) -> Self {

View File

@@ -132,7 +132,7 @@ mod test {
let mut custom_digraphs = HashMap::default();
custom_digraphs.insert("|-".into(), "".into());
custom_digraphs.insert(":)".into(), "👨‍💻".into());
s.custom_digraphs = custom_digraphs;
s.custom_digraphs = Some(custom_digraphs);
});
});

View File

@@ -1184,7 +1184,7 @@ mod test {
let mut cx = VimTestContext::new(cx, true).await;
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_multiline_find = true;
s.use_multiline_find = Some(true);
});
});
@@ -1226,7 +1226,7 @@ mod test {
let mut cx = VimTestContext::new(cx, true).await;
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_multiline_find = true;
s.use_multiline_find = Some(true);
});
});
@@ -1268,7 +1268,7 @@ mod test {
let mut cx = VimTestContext::new(cx, true).await;
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_smartcase_find = true;
s.use_smartcase_find = Some(true);
});
});

View File

@@ -291,7 +291,7 @@ mod test {
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_system_clipboard = UseSystemClipboard::Never
s.use_system_clipboard = Some(UseSystemClipboard::Never)
});
});
@@ -327,7 +327,7 @@ mod test {
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_system_clipboard = UseSystemClipboard::OnYank
s.use_system_clipboard = Some(UseSystemClipboard::OnYank)
});
});
@@ -584,7 +584,7 @@ mod test {
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_system_clipboard = UseSystemClipboard::Never
s.use_system_clipboard = Some(UseSystemClipboard::Never)
});
});
@@ -630,7 +630,7 @@ mod test {
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_system_clipboard = UseSystemClipboard::Never
s.use_system_clipboard = Some(UseSystemClipboard::Never)
});
});
@@ -659,7 +659,7 @@ mod test {
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_system_clipboard = UseSystemClipboard::Never
s.use_system_clipboard = Some(UseSystemClipboard::Never)
});
});
@@ -707,7 +707,7 @@ mod test {
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_system_clipboard = UseSystemClipboard::Never
s.use_system_clipboard = Some(UseSystemClipboard::Never)
});
});

View File

@@ -294,7 +294,7 @@ mod test {
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<EditorSettings>(cx, |s| {
s.scroll_beyond_last_line = ScrollBeyondLastLine::Off
s.scroll_beyond_last_line = Some(ScrollBeyondLastLine::Off)
});
});

View File

@@ -542,7 +542,7 @@ mod test {
let mut cx = VimTestContext::new(cx, true).await;
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<EditorSettings>(cx, |s| s.search_wrap = false);
store.update_user_settings::<EditorSettings>(cx, |s| s.search_wrap = Some(false));
});
cx.set_state("ˇhi\nhigh\nhi\n", Mode::Normal);
@@ -655,7 +655,7 @@ mod test {
// check that searching with unable search wrap
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<EditorSettings>(cx, |s| s.search_wrap = false);
store.update_user_settings::<EditorSettings>(cx, |s| s.search_wrap = Some(false));
});
cx.set_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
cx.simulate_keystrokes("/ c c enter");

View File

@@ -1300,7 +1300,7 @@ async fn test_command_alias(cx: &mut gpui::TestAppContext) {
store.update_user_settings::<WorkspaceSettings>(cx, |s| {
let mut aliases = HashMap::default();
aliases.insert("Q".to_string(), "upper".to_string());
s.command_aliases = aliases
s.command_aliases = Some(aliases)
});
});

View File

@@ -57,7 +57,7 @@ impl VimTestContext {
pub fn new_with_lsp(mut cx: EditorLspTestContext, enabled: bool) -> VimTestContext {
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = VimModeSetting(enabled));
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
});
settings::KeymapFile::load_asset("keymaps/default-macos.json", cx).unwrap();
if enabled {
@@ -105,7 +105,7 @@ impl VimTestContext {
pub fn enable_vim(&mut self) {
self.cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = VimModeSetting(true));
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(true));
});
})
}
@@ -113,7 +113,7 @@ impl VimTestContext {
pub fn disable_vim(&mut self) {
self.cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = VimModeSetting(false));
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(false));
});
})
}

View File

@@ -46,8 +46,6 @@ use crate::state::ReplayableAction;
/// Whether or not to enable Vim mode.
///
/// Default: false
#[derive(Copy, Clone, Default, Deserialize, Serialize, JsonSchema)]
#[serde(default, transparent)]
pub struct VimModeSetting(pub bool);
/// An Action to Switch between modes
@@ -101,7 +99,7 @@ pub fn init(cx: &mut AppContext) {
let fs = workspace.app_state().fs.clone();
let currently_enabled = Vim::enabled(cx);
update_settings_file::<VimModeSetting>(fs, cx, move |setting, _| {
*setting = VimModeSetting(!currently_enabled);
*setting = Some(!currently_enabled)
})
});
@@ -1070,10 +1068,12 @@ impl Vim {
impl Settings for VimModeSetting {
const KEY: Option<&'static str> = Some("vim_mode");
type FileContent = Self;
type FileContent = Option<bool>;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
Ok(sources.user.copied().unwrap_or(*sources.default))
Ok(Self(sources.user.copied().flatten().unwrap_or(
sources.default.ok_or_else(Self::missing_default)?,
)))
}
}
@@ -1089,8 +1089,7 @@ pub enum UseSystemClipboard {
OnYank,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
#[derive(Deserialize)]
struct VimSettings {
pub toggle_relative_line_numbers: bool,
pub use_system_clipboard: UseSystemClipboard,
@@ -1099,22 +1098,19 @@ struct VimSettings {
pub custom_digraphs: HashMap<String, Arc<str>>,
}
impl Default for VimSettings {
fn default() -> Self {
Self {
toggle_relative_line_numbers: false,
use_system_clipboard: UseSystemClipboard::Always,
use_multiline_find: false,
use_smartcase_find: false,
custom_digraphs: Default::default(),
}
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
struct VimSettingsContent {
pub toggle_relative_line_numbers: Option<bool>,
pub use_system_clipboard: Option<UseSystemClipboard>,
pub use_multiline_find: Option<bool>,
pub use_smartcase_find: Option<bool>,
pub custom_digraphs: Option<HashMap<String, Arc<str>>>,
}
impl Settings for VimSettings {
const KEY: Option<&'static str> = Some("vim");
type FileContent = Self;
type FileContent = VimSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
sources.json_merge()

View File

@@ -177,7 +177,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
.report_setting_event("keymap", base_keymap.to_string());
update_settings_file::<BaseKeymap>(self.fs.clone(), cx, move |setting, _| {
*setting = base_keymap;
*setting = Some(base_keymap)
});
}

View File

@@ -87,15 +87,15 @@ impl BaseKeymap {
impl Settings for BaseKeymap {
const KEY: Option<&'static str> = Some("base_keymap");
type FileContent = Self;
type FileContent = Option<Self>;
fn load(
sources: SettingsSources<Self::FileContent>,
_: &mut gpui::AppContext,
) -> anyhow::Result<Self> {
if let Some(user_value) = sources.user.copied() {
if let Some(Some(user_value)) = sources.user.copied() {
return Ok(user_value);
}
Ok(*sources.default)
sources.default.ok_or_else(Self::missing_default)
}
}

View File

@@ -188,7 +188,7 @@ impl Render for WelcomePage {
this.update_settings::<VimModeSetting>(
selection,
cx,
|setting, value| *setting = VimModeSetting(value),
|setting, value| *setting = Some(value),
);
}),
))

View File

@@ -36,49 +36,20 @@ use util::ResultExt;
pub const LEADER_UPDATE_THROTTLE: Duration = Duration::from_millis(200);
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
#[derive(Deserialize)]
pub struct ItemSettings {
/// Whether to show the Git file status on a tab item.
pub git_status: bool,
/// Position of the close button in a tab.
pub close_position: ClosePosition,
/// Whether to show the file icon for a tab.
pub file_icons: bool,
}
impl Default for ItemSettings {
fn default() -> Self {
Self {
git_status: false,
close_position: ClosePosition::Right,
file_icons: false,
}
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
#[derive(Deserialize)]
pub struct PreviewTabsSettings {
/// Whether to show opened editors as preview tabs.
/// Preview tabs do not stay open, are reused until explicitly set to be kept open opened (via double-click or editing) and show file names in italic.
pub enabled: bool,
/// Whether to open tabs in preview mode when selected from the file finder.
pub enable_preview_from_file_finder: bool,
/// Whether a preview tab gets replaced when code navigation is used to navigate away from the tab.
pub enable_preview_from_code_navigation: bool,
}
impl Default for PreviewTabsSettings {
fn default() -> Self {
Self {
enabled: true,
enable_preview_from_file_finder: false,
enable_preview_from_code_navigation: false,
}
}
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum ClosePosition {
@@ -96,10 +67,43 @@ impl ClosePosition {
}
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct ItemSettingsContent {
/// Whether to show the Git file status on a tab item.
///
/// Default: false
git_status: Option<bool>,
/// Position of the close button in a tab.
///
/// Default: right
close_position: Option<ClosePosition>,
/// Whether to show the file icon for a tab.
///
/// Default: false
file_icons: Option<bool>,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct PreviewTabsSettingsContent {
/// Whether to show opened editors as preview tabs.
/// Preview tabs do not stay open, are reused until explicitly set to be kept open opened (via double-click or editing) and show file names in italic.
///
/// Default: true
enabled: Option<bool>,
/// Whether to open tabs in preview mode when selected from the file finder.
///
/// Default: false
enable_preview_from_file_finder: Option<bool>,
/// Whether a preview tab gets replaced when code navigation is used to navigate away from the tab.
///
/// Default: false
enable_preview_from_code_navigation: Option<bool>,
}
impl Settings for ItemSettings {
const KEY: Option<&'static str> = Some("tabs");
type FileContent = Self;
type FileContent = ItemSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
sources.json_merge()
@@ -109,7 +113,7 @@ impl Settings for ItemSettings {
impl Settings for PreviewTabsSettings {
const KEY: Option<&'static str> = Some("preview_tabs");
type FileContent = Self;
type FileContent = PreviewTabsSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
sources.json_merge()

View File

@@ -4644,7 +4644,7 @@ fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncA
|cx| {
cx.new_view(|_| {
MessageNotification::new("Failed to load the database file.")
.with_click_message("Click to let us know about this error")
.with_click_message("File an issue")
.on_click(|cx| cx.open_url(REPORT_ISSUE_URL))
})
},
@@ -6465,7 +6465,7 @@ mod tests {
item.update(cx, |item, cx| {
SettingsStore::update_global(cx, |settings, cx| {
settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
settings.autosave = AutosaveSetting::OnWindowChange;
settings.autosave = Some(AutosaveSetting::OnWindowChange);
})
});
item.is_dirty = true;
@@ -6485,7 +6485,7 @@ mod tests {
cx.focus_self();
SettingsStore::update_global(cx, |settings, cx| {
settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
settings.autosave = AutosaveSetting::OnFocusChange;
settings.autosave = Some(AutosaveSetting::OnFocusChange);
})
});
item.is_dirty = true;
@@ -6508,7 +6508,7 @@ mod tests {
item.update(cx, |item, cx| {
SettingsStore::update_global(cx, |settings, cx| {
settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
settings.autosave = AutosaveSetting::AfterDelay { milliseconds: 500 };
settings.autosave = Some(AutosaveSetting::AfterDelay { milliseconds: 500 });
})
});
item.is_dirty = true;
@@ -6527,7 +6527,7 @@ mod tests {
item.update(cx, |item, cx| {
SettingsStore::update_global(cx, |settings, cx| {
settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
settings.autosave = AutosaveSetting::OnFocusChange;
settings.autosave = Some(AutosaveSetting::OnFocusChange);
})
});
item.is_dirty = true;

View File

@@ -5,58 +5,22 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
#[derive(Deserialize)]
pub struct WorkspaceSettings {
/// Scale by which to zoom the active pane.
/// When set to 1.0, the active pane has the same size as others,
/// but when set to a larger value, the active pane takes up more space.
pub active_pane_magnification: f32,
/// Direction to split horizontally.
pub pane_split_direction_horizontal: PaneSplitDirectionHorizontal,
/// Direction to split vertically.
pub pane_split_direction_vertical: PaneSplitDirectionVertical,
/// Centered layout related settings.
pub centered_layout: CenteredLayoutSettings,
/// Whether or not to prompt the user to confirm before closing the application.
pub confirm_quit: bool,
/// Whether or not to show the call status icon in the status bar.
pub show_call_status_icon: bool,
/// When to automatically save edited buffers.
pub autosave: AutosaveSetting,
/// Controls previous session restoration in freshly launched Zed instance.
pub restore_on_startup: RestoreOnStartupBehavior,
/// The size of the workspace split drop targets on the outer edges.
/// Given as a fraction that will be multiplied by the smaller dimension of the workspace.
pub drop_target_size: f32,
/// Whether to close the window when using 'close active item' on a workspace with no tabs
pub when_closing_with_no_tabs: CloseWindowWhenNoItems,
/// Whether to use the system provided dialogs for Open and Save As.
/// When set to false, Zed will use the built-in keyboard-first pickers.
pub use_system_path_prompts: bool,
/// Aliases for the command palette. When you type a key in this map,
/// it will be assumed to equal the value.
pub command_aliases: HashMap<String, String>,
}
impl Default for WorkspaceSettings {
fn default() -> Self {
Self {
active_pane_magnification: 1.0,
pane_split_direction_horizontal: PaneSplitDirectionHorizontal::Up,
pane_split_direction_vertical: PaneSplitDirectionVertical::Left,
centered_layout: CenteredLayoutSettings::default(),
confirm_quit: false,
show_call_status_icon: true,
autosave: AutosaveSetting::Off,
restore_on_startup: RestoreOnStartupBehavior::default(),
drop_target_size: 0.2,
when_closing_with_no_tabs: CloseWindowWhenNoItems::default(),
use_system_path_prompts: true,
command_aliases: HashMap::default(),
}
}
}
#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum CloseWindowWhenNoItems {
@@ -91,22 +55,77 @@ pub enum RestoreOnStartupBehavior {
LastSession,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct WorkspaceSettingsContent {
/// Scale by which to zoom the active pane.
/// When set to 1.0, the active pane has the same size as others,
/// but when set to a larger value, the active pane takes up more space.
///
/// Default: `1.0`
pub active_pane_magnification: Option<f32>,
// Direction to split horizontally.
//
// Default: "up"
pub pane_split_direction_horizontal: Option<PaneSplitDirectionHorizontal>,
// Direction to split vertically.
//
// Default: "left"
pub pane_split_direction_vertical: Option<PaneSplitDirectionVertical>,
// Centered layout related settings.
pub centered_layout: Option<CenteredLayoutSettings>,
/// Whether or not to prompt the user to confirm before closing the application.
///
/// Default: false
pub confirm_quit: Option<bool>,
/// Whether or not to show the call status icon in the status bar.
///
/// Default: true
pub show_call_status_icon: Option<bool>,
/// When to automatically save edited buffers.
///
/// Default: off
pub autosave: Option<AutosaveSetting>,
/// Controls previous session restoration in freshly launched Zed instance.
/// Values: none, last_workspace, last_session
/// Default: last_session
pub restore_on_startup: Option<RestoreOnStartupBehavior>,
/// The size of the workspace split drop targets on the outer edges.
/// Given as a fraction that will be multiplied by the smaller dimension of the workspace.
///
/// Default: `0.2` (20% of the smaller dimension of the workspace)
pub drop_target_size: Option<f32>,
/// Whether to close the window when using 'close active item' on a workspace with no tabs
///
/// Default: auto ("on" on macOS, "off" otherwise)
pub when_closing_with_no_tabs: Option<CloseWindowWhenNoItems>,
/// Whether to use the system provided dialogs for Open and Save As.
/// When set to false, Zed will use the built-in keyboard-first pickers.
///
/// Default: true
pub use_system_path_prompts: Option<bool>,
/// Aliases for the command palette. When you type a key in this map,
/// it will be assumed to equal the value.
///
/// Default: true
pub command_aliases: Option<HashMap<String, String>>,
}
#[derive(Deserialize)]
pub struct TabBarSettings {
/// Whether or not to show the tab bar in the editor.
pub show: bool,
/// Whether or not to show the navigation history buttons in the tab bar.
pub show_nav_history_buttons: bool,
}
impl Default for TabBarSettings {
fn default() -> Self {
Self {
show_nav_history_buttons: true,
show: true,
}
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct TabBarSettingsContent {
/// Whether or not to show the tab bar in the editor.
///
/// Default: true
pub show: Option<bool>,
/// Whether or not to show the navigation history buttons in the tab bar.
///
/// Default: true
pub show_nav_history_buttons: Option<bool>,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
@@ -144,26 +163,17 @@ pub struct CenteredLayoutSettings {
///
/// Default: 0.2
pub left_padding: Option<f32>,
/// The relative width of the right padding of the central pane from the
/// workspace when the centered layout is used.
// The relative width of the right padding of the central pane from the
// workspace when the centered layout is used.
///
/// Default: 0.2
pub right_padding: Option<f32>,
}
impl Default for CenteredLayoutSettings {
fn default() -> Self {
Self {
left_padding: Some(0.2),
right_padding: Some(0.2),
}
}
}
impl Settings for WorkspaceSettings {
const KEY: Option<&'static str> = None;
type FileContent = Self;
type FileContent = WorkspaceSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
sources.json_merge()
@@ -173,7 +183,7 @@ impl Settings for WorkspaceSettings {
impl Settings for TabBarSettings {
const KEY: Option<&'static str> = Some("tab_bar");
type FileContent = Self;
type FileContent = TabBarSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
sources.json_merge()

View File

@@ -25,8 +25,7 @@ impl WorktreeSettings {
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct WorktreeSettingsContent {
/// Completely ignore files matching globs from `file_scan_exclusions`
///
@@ -40,42 +39,12 @@ pub struct WorktreeSettingsContent {
/// "**/.classpath",
/// "**/.settings"
/// ]
pub file_scan_exclusions: Vec<String>,
#[serde(default)]
pub file_scan_exclusions: Option<Vec<String>>,
/// Treat the files matching these globs as `.env` files.
/// Default: [ "**/.env*" ]
pub private_files: Vec<String>,
}
impl Default for WorktreeSettingsContent {
fn default() -> Self {
Self {
private_files: [
"**/.env*",
"**/*.pem",
"**/*.key",
"**/*.cert",
"**/*.crt",
"**/secrets.yml",
]
.into_iter()
.map(str::to_owned)
.collect(),
file_scan_exclusions: [
"**/.git",
"**/.svn",
"**/.hg",
"**/CVS",
"**/.DS_Store",
"**/Thumbs.db",
"**/.classpath",
"**/.settings",
]
.into_iter()
.map(str::to_owned)
.collect(),
}
}
pub private_files: Option<Vec<String>>,
}
impl Settings for WorktreeSettings {
@@ -88,8 +57,8 @@ impl Settings for WorktreeSettings {
_: &mut AppContext,
) -> anyhow::Result<Self> {
let result: WorktreeSettingsContent = sources.json_merge()?;
let mut file_scan_exclusions = result.file_scan_exclusions;
let mut private_files = result.private_files;
let mut file_scan_exclusions = result.file_scan_exclusions.unwrap_or_default();
let mut private_files = result.private_files.unwrap_or_default();
file_scan_exclusions.sort();
private_files.sort();
Ok(Self {

View File

@@ -673,7 +673,7 @@ async fn test_rescan_with_gitignore(cx: &mut TestAppContext) {
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
project_settings.file_scan_exclusions = Vec::new();
project_settings.file_scan_exclusions = Some(Vec::new());
});
});
});
@@ -910,7 +910,7 @@ async fn test_file_scan_exclusions(cx: &mut TestAppContext) {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
project_settings.file_scan_exclusions =
vec!["**/foo/**".to_string(), "**/.DS_Store".to_string()];
Some(vec!["**/foo/**".to_string(), "**/.DS_Store".to_string()]);
});
});
});
@@ -945,7 +945,8 @@ async fn test_file_scan_exclusions(cx: &mut TestAppContext) {
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
project_settings.file_scan_exclusions = vec!["**/node_modules/**".to_string()];
project_settings.file_scan_exclusions =
Some(vec!["**/node_modules/**".to_string()]);
});
});
});
@@ -1008,11 +1009,11 @@ async fn test_fs_events_in_exclusions(cx: &mut TestAppContext) {
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
project_settings.file_scan_exclusions = vec![
project_settings.file_scan_exclusions = Some(vec![
"**/.git".to_string(),
"node_modules/".to_string(),
"build_output".to_string(),
];
]);
});
});
});

View File

@@ -1996,7 +1996,7 @@ mod tests {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
project_settings.file_scan_exclusions =
vec!["excluded_dir".to_string(), "**/.git".to_string()];
Some(vec!["excluded_dir".to_string(), "**/.git".to_string()]);
});
});
});

View File

@@ -52,7 +52,7 @@ Prettier will also be used for TypeScript files by default. To disable this:
{
"lsp": {
"vtsls": {
"initialization_options": {
"settings": {
// For TypeScript:
"typescript": { "tsserver": { "maxTsServerMemory": 16184 } },
// For JavaScript:

View File

@@ -12,7 +12,7 @@ So, Zed's vim mode does not replicate Vim one-to-one, but it meshes Vim's modal
## Enabling and disabling vim mode
When you first open Zed, a checkbox will appear on the welcome screen, allowing you to enable vim mode.
When you first open Zed, you'll see a checkbox on the welcome screen that allows you to enable vim mode.
If you missed this, you can toggle vim mode on or off anytime by opening the command palette and using the workspace command `toggle vim mode`.
@@ -83,7 +83,7 @@ ctrl-x ctrl-z Hides all suggestions
:Ext[ensions] Open the extensions window
```
Vim mode uses Zed to define concepts like "brackets" (for the `%` key) and "words" (for motions like `w` and `e`). This does lead to some differences, but they are mostly positive. For example `%` considers `|` to be a bracket in languages like Rust; and `w` considers `$` to be a word-character in languages like Javascript.
Vim mode uses Zed to define concepts like "brackets" (for the `%` key) and "words" (for motions like `w` and `e`). This does lead to some differences, but they are mostly positive. For example `%` considers `|` to be a bracket in languages like Rust; and `w` considers `$` to be a word-character in languages like JavaScript.
Vim mode emulates visual block mode using Zed's multiple cursor support. This again leads to some differences, but is much more powerful.
@@ -129,11 +129,13 @@ For vim-specific shortcuts, you may find the following template a good place to
If you would like to emulate vim's `map` (`nmap` etc.) commands you can bind to the [`workspace::SendKeystrokes`](./key-bindings.md#remapping-keys) action in the correct context.
You can see the bindings that are enabled by default in vim mode [here](https://github.com/zed-industries/zed/blob/main/assets/keymaps/vim.json).
Check out the [bindings that are enabled by default in vim mode](https://github.com/zed-industries/zed/blob/main/assets/keymaps/vim.json).
### Contexts
Zed's keyboard bindings are evaluated only when the `"context"` matches the location you are in on the screen. Locations are nested, so when you're editing you're in the `"Workspace"` location is at the top, containing a `"Pane"` which contains an `"Editor"`. Contexts are matched only on one level at a time. So it is possible to combine `Editor && vim_mode == normal`, but `Workspace && vim_mode == normal` will never match because we set the vim context at the `Editor` level.
Zed's keyboard bindings are evaluated only when the `"context"` matches the location you are in on the screen. Locations are nested, so when you're editing, you're in the `"Workspace"` location, which is at the top, containing a `"Pane"` that contains an `"Editor"`.
Contexts are matched only on one level at a time. So, it is possible to combine `Editor && vim_mode == normal`, but `Workspace && vim_mode == normal` will never match because we set the vim context at the `Editor` level.
Vim mode adds several contexts to the `Editor`:
@@ -164,13 +166,13 @@ If you're using vim mode on Linux or Windows, you may find it overrides keybindi
Vim mode allows you to enable Zeds command palette with `:`. This means that you can use vim's command palette to run any action that Zed supports.
Additionally, vim mode contains a number of aliases for popular vim commands to ensure that muscle memory works. For example `:w<enter>` will save the file.
Additionally, vim mode contains a number of aliases for popular Vim commands to ensure that muscle memory works. For example, `:w<enter>` will save the file.
We do not (yet) emulate the full power of vims command line, in particular, we do not support arguments to commands yet. Please reach out on [GitHub](https://github.com/zed-industries/zed) as you find things that are missing from the command palette.
We do not (yet) emulate the full power of Vims command line, in particular, we do not support arguments to commands yet. Please [file issues on GitHub](https://github.com/zed-industries/zed) as you find things that are missing from the command palette.
As mentioned above, one thing to be aware of is that the regex engine is slightly different from vim's in `:%s/a/b`.
Currently supported vim-specific commands:
Currently supported Vim-specific commands:
```
# window management
@@ -296,7 +298,7 @@ There are also a few Zed settings that you may also enjoy if you use vim mode:
}
```
If you want to navigate between the editor and docks (terminal, project panel, AI assistant, ...) just like you navigate between splits you can use the following key bindings:
If you want to navigate between the editor and docks (terminal, project panel, AI assistant panel, etc...), just like you navigate between splits, you can use the following key bindings:
```json
{
@@ -366,4 +368,4 @@ Notably:
To help with the transition, the command palette will fix parentheses and replace groups for you when you run `:%s//`. So `%s:/\(a\)(b)/\1/` will be converted into a search for "(a)\(b\)" and a replacement of "$1".
For the full syntax supported by Zed's regex engine see the [regex crate documentation](https://docs.rs/regex/latest/regex/#syntax).
For the full syntax supported by Zed's regex engine [see the regex crate documentation](https://docs.rs/regex/latest/regex/#syntax).

View File

@@ -529,5 +529,5 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "3.12.5"
content-hash = "3e6aa4dc758eb933f7e2d1a305d1e397b13a960ac4846ef54c5a11b906b77015"
python-versions = "3.12.6"
content-hash = "7827704e06a8c195297507e0d05e7a7c3843ed299bd353f31570ee4c435c6896"

View File

@@ -8,7 +8,7 @@ readme = "README.md"
[tool.poetry.dependencies]
mypy = "1.6.0"
PyGithub = "1.55"
python = "3.12.5"
python = "3.12.6"
pytz = "2022.1"
typer = "0.9.0"
types-pytz = "2023.3.1.1"