Settings refactor (#38367)

Co-Authored-By: Ben K <ben@zed.dev>
Co-Authored-By: Anthony <anthony@zed.dev>
Co-Authored-By: Mikayla <mikayla@zed.dev>

Release Notes:

- settings: Major internal changes to settings. The primary user-facing
effect is that some settings which did not make sense in project
settings files are no-longer read from there. (For example the inline
blame settings)

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Anthony <anthony@zed.dev>
This commit is contained in:
Conrad Irwin
2025-09-18 16:47:23 +00:00
committed by GitHub
co-authored by Ben Kunkle Mikayla Maki Anthony
parent 0a9023bce0
commit fcdab160f9
219 changed files with 11697 additions and 11894 deletions
Generated
+196 -132
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -150,7 +150,6 @@ members = [
"crates/session",
"crates/settings",
"crates/settings_profile_selector",
"crates/settings_ui",
"crates/settings_ui_macros",
"crates/snippet",
"crates/snippet_provider",
@@ -630,6 +629,7 @@ serde_json_lenient = { version = "0.2", features = [
serde_path_to_error = "0.1.17"
serde_repr = "0.1"
serde_urlencoded = "0.7"
serde_with = "3.4.0"
sha2 = "0.10"
shellexpand = "2.1.0"
shlex = "1.3.0"
+70 -13
View File
@@ -311,7 +311,7 @@
// bracket, brace, single or double quote characters.
// For example, when you select text and type (, Zed will surround the text with ().
"use_auto_surround": true,
/// Whether indentation should be adjusted based on the context whilst typing.
// Whether indentation should be adjusted based on the context whilst typing.
"auto_indent": true,
// Whether indentation of pasted content should be adjusted based on the context.
"auto_indent_on_paste": true,
@@ -408,6 +408,21 @@
// Whether to show the menus in the titlebar.
"show_menus": false
},
"audio": {
// Opt into the new audio system.
"experimental.rodio_audio": false,
// Requires 'rodio_audio: true'
//
// Use the new audio systems automatic gain control for your microphone.
// This affects how loud you sound to others.
"experimental.control_input_volume": false,
// Requires 'rodio_audio: true'
//
// Use the new audio systems automatic gain control on everyone in the
// call. This makes call members who are too quite louder and those who are
// too loud quieter. This only affects how things sound for you.
"experimental.control_output_volume": false
},
// Scrollbar related settings
"scrollbar": {
// When to show the scrollbar in the editor.
@@ -588,6 +603,7 @@
// Toggle certain types of hints on and off, all switched on by default.
"show_type_hints": true,
"show_parameter_hints": true,
"show_value_hints": true,
// Corresponds to null/None LSP hint type value.
"show_other_hints": true,
// Whether to show a background for inlay hints.
@@ -796,7 +812,7 @@
"agent": {
// Whether the agent is enabled.
"enabled": true,
/// What completion mode to start new threads in, if available. Can be 'normal' or 'burn'.
// What completion mode to start new threads in, if available. Can be 'normal' or 'burn'.
"preferred_completion_mode": "normal",
// Whether to show the agent panel button in the status bar.
"button": true,
@@ -806,6 +822,8 @@
"default_width": 640,
// Default height when the agent panel is docked to the bottom.
"default_height": 320,
// The view to use by default (thread, or text_thread)
"default_view": "thread",
// The default model to use when creating new threads.
"default_model": {
// The provider to use.
@@ -907,14 +925,18 @@
// Default: false
"play_sound_when_agent_done": false,
/// Whether to have edit cards in the agent panel expanded, showing a preview of the full diff.
///
/// Default: true
// Whether to have edit cards in the agent panel expanded, showing a preview of the full diff.
//
// Default: true
"expand_edit_card": true,
/// Whether to have terminal cards in the agent panel expanded, showing the whole command output.
///
/// Default: true
// Whether to have terminal cards in the agent panel expanded, showing the whole command output.
//
// Default: true
"expand_terminal_card": true,
// Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages in the agent panel.
//
// Default: false
"use_modifier_to_send": false,
// Minimum number of lines to display in the agent message editor.
//
// Default: 4
@@ -931,6 +953,7 @@
//
// This is typically customized on a per-language basis.
"language_servers": ["..."],
// When to automatically save edited buffers. This setting can
// take four values.
//
@@ -1262,7 +1285,13 @@
// },
// Whether edit predictions are enabled when editing text threads.
// This setting has no effect if globally disabled.
"enabled_in_text_threads": true
"enabled_in_text_threads": true,
"copilot": {
"enterprise_uri": null,
"proxy": null,
"proxy_no_verify": null
}
},
// Settings specific to journaling
"journal": {
@@ -1765,6 +1794,7 @@
"anthropic": {
"api_url": "https://api.anthropic.com"
},
"bedrock": {},
"google": {
"api_url": "https://generativelanguage.googleapis.com"
},
@@ -1786,14 +1816,30 @@
},
"mistral": {
"api_url": "https://api.mistral.ai/v1"
}
},
"vercel": {
"api_url": "https://api.v0.dev/v1"
},
"x_ai": {
"api_url": "https://api.x.ai/v1"
},
"zed.dev": {}
},
"session": {
// Whether or not to restore unsaved buffers on restart.
//
// If this is true, user won't be prompted whether to save/discard
// dirty files when closing the application.
//
// Default: true
"restore_unsaved_buffers": true
},
// Zed's Prettier integration settings.
// Allows to enable/disable formatting with Prettier
// and configure default Prettier, used when no project-level Prettier installation is found.
"prettier": {
// // Whether to consider prettier formatter or not when attempting to format a file.
// "allowed": false,
"allowed": false
//
// // Use regular Prettier json configuration.
// // If Prettier is allowed, Zed will use this for its Prettier instance for any applicable file, if
@@ -1826,6 +1872,10 @@
// }
// }
},
// DAP Specific settings.
"dap": {
// Specify the DAP name as a key here.
},
// Common language server settings.
"global_lsp_settings": {
// Whether to show the LSP servers button in the status bar.
@@ -1833,7 +1883,8 @@
},
// Jupyter settings
"jupyter": {
"enabled": true
"enabled": true,
"kernel_selections": {}
// Specify the language name as the key and the kernel name as the value.
// "kernel_selections": {
// "python": "conda-base"
@@ -1964,5 +2015,11 @@
// }
// }
// }
"profiles": []
"profiles": [],
// A map of log scopes to the desired log level.
// Useful for filtering out noisy logs or enabling more verbose logging.
//
// Example: {"log": {"client": "warn"}}
"log": {}
}
+2 -2
View File
@@ -49,10 +49,10 @@ impl AgentProfile {
.unwrap_or_default(),
};
update_settings_file::<AgentSettings>(fs, cx, {
update_settings_file(fs, cx, {
let id = id.clone();
move |settings, _cx| {
settings.create_profile(id, profile_settings).log_err();
profile_settings.save_to_settings(id, settings).log_err();
}
});
+2 -2
View File
@@ -3272,7 +3272,7 @@ mod tests {
// Test-specific constants
const TEST_RATE_LIMIT_RETRY_SECS: u64 = 30;
use agent_settings::{AgentProfileId, AgentSettings, LanguageModelParameters};
use agent_settings::{AgentProfileId, AgentSettings};
use assistant_tool::ToolRegistry;
use assistant_tools;
use futures::StreamExt;
@@ -3289,7 +3289,7 @@ mod tests {
use project::{FakeFs, Project};
use prompt_store::PromptBuilder;
use serde_json::json;
use settings::{Settings, SettingsStore};
use settings::{LanguageModelParameters, Settings, SettingsStore};
use std::sync::Arc;
use std::time::Duration;
use theme::ThemeSettings;
+12 -9
View File
@@ -6,7 +6,6 @@ use crate::{HistoryStore, TerminalHandle, ThreadEnvironment, TitleUpdated, Token
use acp_thread::{AcpThread, AgentModelSelector};
use action_log::ActionLog;
use agent_client_protocol as acp;
use agent_settings::AgentSettings;
use anyhow::{Context as _, Result, anyhow};
use collections::{HashSet, IndexMap};
use fs::Fs;
@@ -21,7 +20,7 @@ use project::{Project, ProjectItem, ProjectPath, Worktree};
use prompt_store::{
ProjectContext, PromptId, PromptStore, RulesFileContext, UserRulesContext, WorktreeContext,
};
use settings::update_settings_file;
use settings::{LanguageModelSelection, update_settings_file};
use std::any::Any;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
@@ -873,13 +872,17 @@ impl AgentModelSelector for NativeAgentConnection {
thread.set_model(model.clone(), cx);
});
update_settings_file::<AgentSettings>(
self.0.read(cx).fs.clone(),
cx,
move |settings, _cx| {
settings.set_model(model);
},
);
update_settings_file(self.0.read(cx).fs.clone(), cx, move |settings, _cx| {
let provider = model.provider_id().0.to_string();
let model = model.id().0.to_string();
settings
.agent
.get_or_insert_default()
.set_model(LanguageModelSelection {
provider: provider.into(),
model,
});
});
Task::ready(Ok(()))
}
+5 -2
View File
@@ -2477,8 +2477,11 @@ impl ToolCallEventStream {
"always_allow" => {
if let Some(fs) = fs.clone() {
cx.update(|cx| {
update_settings_file::<AgentSettings>(fs, cx, |settings, _| {
settings.set_always_allow_tool_actions(true);
update_settings_file(fs, cx, |settings, _| {
settings
.agent
.get_or_insert_default()
.set_always_allow_tool_actions(true);
});
})?;
}
+23 -26
View File
@@ -791,14 +791,11 @@ mod tests {
// First, test with format_on_save enabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<language::language_settings::AllLanguageSettings>(
cx,
|settings| {
settings.defaults.format_on_save = Some(FormatOnSave::On);
settings.defaults.formatter =
Some(language::language_settings::SelectedFormatter::Auto);
},
);
store.update_user_settings(cx, |settings| {
settings.project.all_languages.defaults.format_on_save = Some(FormatOnSave::On);
settings.project.all_languages.defaults.formatter =
Some(language::language_settings::SelectedFormatter::Auto);
});
});
});
@@ -853,12 +850,10 @@ mod tests {
// Next, test with format_on_save disabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<language::language_settings::AllLanguageSettings>(
cx,
|settings| {
settings.defaults.format_on_save = Some(FormatOnSave::Off);
},
);
store.update_user_settings(cx, |settings| {
settings.project.all_languages.defaults.format_on_save =
Some(FormatOnSave::Off);
});
});
});
@@ -935,12 +930,13 @@ mod tests {
// First, test with remove_trailing_whitespace_on_save enabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<language::language_settings::AllLanguageSettings>(
cx,
|settings| {
settings.defaults.remove_trailing_whitespace_on_save = Some(true);
},
);
store.update_user_settings(cx, |settings| {
settings
.project
.all_languages
.defaults
.remove_trailing_whitespace_on_save = Some(true);
});
});
});
@@ -991,12 +987,13 @@ mod tests {
// Next, test with remove_trailing_whitespace_on_save disabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<language::language_settings::AllLanguageSettings>(
cx,
|settings| {
settings.defaults.remove_trailing_whitespace_on_save = Some(false);
},
);
store.update_user_settings(cx, |settings| {
settings
.project
.all_languages
.defaults
.remove_trailing_whitespace_on_save = Some(false);
});
});
});
+7 -8
View File
@@ -308,7 +308,7 @@ mod tests {
use super::*;
use gpui::{TestAppContext, UpdateGlobal};
use language::{Language, LanguageConfig, LanguageMatcher};
use project::{FakeFs, Project, WorktreeSettings};
use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use unindent::Unindent;
@@ -827,15 +827,14 @@ mod tests {
cx.update(|cx| {
use gpui::UpdateGlobal;
use project::WorktreeSettings;
use settings::SettingsStore;
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions = Some(vec![
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
]);
settings.private_files = Some(vec![
settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -1062,10 +1061,10 @@ mod tests {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions =
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
settings.private_files = Some(vec!["**/.env".to_string()]);
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
@@ -214,7 +214,7 @@ mod tests {
use super::*;
use gpui::{TestAppContext, UpdateGlobal};
use indoc::indoc;
use project::{FakeFs, Project, WorktreeSettings};
use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use util::path;
@@ -421,13 +421,13 @@ mod tests {
// Configure settings explicitly
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions = Some(vec![
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
"**/.hidden_subdir".to_string(),
]);
settings.private_files = Some(vec![
settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -565,10 +565,10 @@ mod tests {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions =
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
settings.private_files = Some(vec!["**/.env".to_string()]);
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
+6 -7
View File
@@ -586,15 +586,14 @@ mod test {
cx.update(|cx| {
use gpui::UpdateGlobal;
use project::WorktreeSettings;
use settings::SettingsStore;
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions = Some(vec![
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
]);
settings.private_files = Some(vec![
settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -802,10 +801,10 @@ mod test {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions =
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
settings.private_files = Some(vec!["**/.env".to_string()]);
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
+7 -2
View File
@@ -45,8 +45,13 @@ impl AgentServer for ClaudeCode {
}
fn set_default_mode(&self, mode_id: Option<acp::SessionModeId>, fs: Arc<dyn Fs>, cx: &mut App) {
update_settings_file::<AllAgentServersSettings>(fs, cx, |settings, _| {
settings.claude.get_or_insert_default().default_mode = mode_id.map(|m| m.to_string())
update_settings_file(fs, cx, |settings, _| {
settings
.agent_servers
.get_or_insert_default()
.claude
.get_or_insert_default()
.default_mode = mode_id.map(|m| m.to_string())
});
}
+8 -2
View File
@@ -49,8 +49,14 @@ impl crate::AgentServer for CustomAgentServer {
fn set_default_mode(&self, mode_id: Option<acp::SessionModeId>, fs: Arc<dyn Fs>, cx: &mut App) {
let name = self.name();
update_settings_file::<AllAgentServersSettings>(fs, cx, move |settings, _| {
settings.custom.get_mut(&name).unwrap().default_mode = mode_id.map(|m| m.to_string())
update_settings_file(fs, cx, move |settings, _| {
settings
.agent_servers
.get_or_insert_default()
.custom
.get_mut(&name)
.unwrap()
.default_mode = mode_id.map(|m| m.to_string())
});
}
+70 -27
View File
@@ -1,15 +1,17 @@
use std::sync::Arc;
use anyhow::{Result, bail};
use collections::IndexMap;
use convert_case::{Case, Casing as _};
use fs::Fs;
use gpui::{App, SharedString};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings as _, update_settings_file};
use settings::{
AgentProfileContent, ContextServerPresetContent, Settings as _, SettingsContent,
update_settings_file,
};
use util::ResultExt as _;
use crate::AgentSettings;
use crate::{AgentProfileId, AgentSettings};
pub mod builtin_profiles {
use super::AgentProfileId;
@@ -23,27 +25,6 @@ pub mod builtin_profiles {
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AgentProfileId(pub Arc<str>);
impl AgentProfileId {
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for AgentProfileId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Default for AgentProfileId {
fn default() -> Self {
Self("write".into())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AgentProfile {
id: AgentProfileId,
@@ -87,10 +68,10 @@ impl AgentProfile {
.unwrap_or_default(),
};
update_settings_file::<AgentSettings>(fs, cx, {
update_settings_file(fs, cx, {
let id = id.clone();
move |settings, _cx| {
settings.create_profile(id, profile_settings).log_err();
profile_settings.save_to_settings(id, settings).log_err();
}
});
@@ -129,9 +110,71 @@ impl AgentProfileSettings {
.get(server_id)
.is_some_and(|preset| preset.tools.get(tool_name) == Some(&true))
}
pub fn save_to_settings(
&self,
profile_id: AgentProfileId,
content: &mut SettingsContent,
) -> Result<()> {
let profiles = content
.agent
.get_or_insert_default()
.profiles
.get_or_insert_default();
if profiles.contains_key(&profile_id.0) {
bail!("profile with ID '{profile_id}' already exists");
}
profiles.insert(
profile_id.0,
AgentProfileContent {
name: self.name.clone().into(),
tools: self.tools.clone(),
enable_all_context_servers: Some(self.enable_all_context_servers),
context_servers: self
.context_servers
.clone()
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
},
);
Ok(())
}
}
impl From<AgentProfileContent> for AgentProfileSettings {
fn from(content: AgentProfileContent) -> Self {
Self {
name: content.name.into(),
tools: content.tools,
enable_all_context_servers: content.enable_all_context_servers.unwrap_or_default(),
context_servers: content
.context_servers
.into_iter()
.map(|(server_id, preset)| (server_id, preset.into()))
.collect(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ContextServerPreset {
pub tools: IndexMap<Arc<str>, bool>,
}
impl From<settings::ContextServerPresetContent> for ContextServerPreset {
fn from(content: settings::ContextServerPresetContent) -> Self {
Self {
tools: content.tools,
}
}
}
+139 -447
View File
@@ -2,14 +2,16 @@ mod agent_profile;
use std::sync::Arc;
use anyhow::{Result, bail};
use collections::IndexMap;
use gpui::{App, Pixels, SharedString};
use gpui::{App, Pixels, px};
use language_model::LanguageModel;
use schemars::{JsonSchema, json_schema};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
use std::borrow::Cow;
use settings::{
DefaultAgentView, DockPosition, LanguageModelParameters, LanguageModelSelection,
NotifyWhenAgentWaiting, Settings, SettingsContent,
};
use util::MergeFrom;
pub use crate::agent_profile::*;
@@ -22,37 +24,11 @@ pub fn init(cx: &mut App) {
AgentSettings::register(cx);
}
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AgentDockPosition {
Left,
#[default]
Right,
Bottom,
}
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum DefaultView {
#[default]
Thread,
TextThread,
}
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum NotifyWhenAgentWaiting {
#[default]
PrimaryScreen,
AllScreens,
Never,
}
#[derive(Default, Clone, Debug)]
#[derive(Clone, Debug)]
pub struct AgentSettings {
pub enabled: bool,
pub button: bool,
pub dock: AgentDockPosition,
pub dock: DockPosition,
pub default_width: Pixels,
pub default_height: Pixels,
pub default_model: Option<LanguageModelSelection>,
@@ -60,9 +36,8 @@ pub struct AgentSettings {
pub commit_message_model: Option<LanguageModelSelection>,
pub thread_summary_model: Option<LanguageModelSelection>,
pub inline_alternatives: Vec<LanguageModelSelection>,
pub using_outdated_settings_version: bool,
pub default_profile: AgentProfileId,
pub default_view: DefaultView,
pub default_view: DefaultAgentView,
pub profiles: IndexMap<AgentProfileId, AgentProfileSettings>,
pub always_allow_tool_actions: bool,
pub notify_when_agent_waiting: NotifyWhenAgentWaiting,
@@ -81,11 +56,20 @@ pub struct AgentSettings {
impl AgentSettings {
pub fn temperature_for_model(model: &Arc<dyn LanguageModel>, cx: &App) -> Option<f32> {
let settings = Self::get_global(cx);
settings
.model_parameters
.iter()
.rfind(|setting| setting.matches(model))
.and_then(|m| m.temperature)
for setting in settings.model_parameters.iter().rev() {
if let Some(provider) = &setting.provider
&& provider.0 != model.provider_id().0
{
continue;
}
if let Some(setting_model) = &setting.model
&& *setting_model != model.id().0
{
continue;
}
return setting.temperature;
}
return None;
}
pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
@@ -114,223 +98,6 @@ impl AgentSettings {
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct LanguageModelParameters {
pub provider: Option<LanguageModelProviderSetting>,
pub model: Option<SharedString>,
pub temperature: Option<f32>,
}
impl LanguageModelParameters {
pub fn matches(&self, model: &Arc<dyn LanguageModel>) -> bool {
if let Some(provider) = &self.provider
&& provider.0 != model.provider_id().0
{
return false;
}
if let Some(setting_model) = &self.model
&& *setting_model != model.id().0
{
return false;
}
true
}
}
impl AgentSettingsContent {
pub fn set_dock(&mut self, dock: AgentDockPosition) {
self.dock = Some(dock);
}
pub fn set_model(&mut self, language_model: Arc<dyn LanguageModel>) {
let model = language_model.id().0.to_string();
let provider = language_model.provider_id().0.to_string();
self.default_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
self.inline_assistant_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_commit_message_model(&mut self, provider: String, model: String) {
self.commit_message_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_thread_summary_model(&mut self, provider: String, model: String) {
self.thread_summary_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_always_allow_tool_actions(&mut self, allow: bool) {
self.always_allow_tool_actions = Some(allow);
}
pub fn set_play_sound_when_agent_done(&mut self, allow: bool) {
self.play_sound_when_agent_done = Some(allow);
}
pub fn set_single_file_review(&mut self, allow: bool) {
self.single_file_review = Some(allow);
}
pub fn set_use_modifier_to_send(&mut self, always_use: bool) {
self.use_modifier_to_send = Some(always_use);
}
pub fn set_profile(&mut self, profile_id: AgentProfileId) {
self.default_profile = Some(profile_id);
}
pub fn create_profile(
&mut self,
profile_id: AgentProfileId,
profile_settings: AgentProfileSettings,
) -> Result<()> {
let profiles = self.profiles.get_or_insert_default();
if profiles.contains_key(&profile_id) {
bail!("profile with ID '{profile_id}' already exists");
}
profiles.insert(
profile_id,
AgentProfileContent {
name: profile_settings.name.into(),
tools: profile_settings.tools,
enable_all_context_servers: Some(profile_settings.enable_all_context_servers),
context_servers: profile_settings
.context_servers
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
},
);
Ok(())
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, Default, SettingsUi, SettingsKey)]
#[settings_key(key = "agent", fallback_key = "assistant")]
pub struct AgentSettingsContent {
/// Whether the Agent is enabled.
///
/// Default: true
enabled: Option<bool>,
/// Whether to show the agent panel button in the status bar.
///
/// Default: true
button: Option<bool>,
/// Where to dock the agent panel.
///
/// Default: right
dock: Option<AgentDockPosition>,
/// Default width in pixels when the agent panel is docked to the left or right.
///
/// Default: 640
default_width: Option<f32>,
/// Default height in pixels when the agent panel is docked to the bottom.
///
/// Default: 320
default_height: Option<f32>,
/// The default model to use when creating new chats and for other features when a specific model is not specified.
default_model: Option<LanguageModelSelection>,
/// Model to use for the inline assistant. Defaults to default_model when not specified.
inline_assistant_model: Option<LanguageModelSelection>,
/// Model to use for generating git commit messages. Defaults to default_model when not specified.
commit_message_model: Option<LanguageModelSelection>,
/// Model to use for generating thread summaries. Defaults to default_model when not specified.
thread_summary_model: Option<LanguageModelSelection>,
/// Additional models with which to generate alternatives when performing inline assists.
inline_alternatives: Option<Vec<LanguageModelSelection>>,
/// The default profile to use in the Agent.
///
/// Default: write
default_profile: Option<AgentProfileId>,
/// Which view type to show by default in the agent panel.
///
/// Default: "thread"
default_view: Option<DefaultView>,
/// The available agent profiles.
pub profiles: Option<IndexMap<AgentProfileId, AgentProfileContent>>,
/// Whenever a tool action would normally wait for your confirmation
/// that you allow it, always choose to allow it.
///
/// This setting has no effect on external agents that support permission modes, such as Claude Code.
///
/// Set `agent_servers.claude.default_mode` to `bypassPermissions`, to disable all permission requests when using Claude Code.
///
/// Default: false
always_allow_tool_actions: Option<bool>,
/// Where to show a popup notification when the agent is waiting for user input.
///
/// Default: "primary_screen"
notify_when_agent_waiting: Option<NotifyWhenAgentWaiting>,
/// Whether to play a sound when the agent has either completed its response, or needs user input.
///
/// Default: false
play_sound_when_agent_done: Option<bool>,
/// Whether to stream edits from the agent as they are received.
///
/// Default: false
stream_edits: Option<bool>,
/// Whether to display agent edits in single-file editors in addition to the review multibuffer pane.
///
/// Default: true
single_file_review: Option<bool>,
/// Additional parameters for language model requests. When making a request
/// to a model, parameters will be taken from the last entry in this list
/// that matches the model's provider and name. In each entry, both provider
/// and model are optional, so that you can specify parameters for either
/// one.
///
/// Default: []
#[serde(default)]
model_parameters: Vec<LanguageModelParameters>,
/// What completion mode to enable for new threads
///
/// Default: normal
preferred_completion_mode: Option<CompletionMode>,
/// Whether to show thumb buttons for feedback in the agent panel.
///
/// Default: true
enable_feedback: Option<bool>,
/// Whether to have edit cards in the agent panel expanded, showing a preview of the full diff.
///
/// Default: true
expand_edit_card: Option<bool>,
/// Whether to have terminal cards in the agent panel expanded, showing the whole command output.
///
/// Default: true
expand_terminal_card: Option<bool>,
/// Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages in the agent panel.
///
/// Default: false
use_modifier_to_send: Option<bool>,
/// Minimum number of lines of height the agent message editor should have.
///
/// Default: 4
message_editor_min_lines: Option<usize>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)]
#[serde(rename_all = "snake_case")]
pub enum CompletionMode {
@@ -349,215 +116,140 @@ impl From<CompletionMode> for cloud_llm_client::CompletionMode {
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct LanguageModelSelection {
pub provider: LanguageModelProviderSetting,
pub model: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct LanguageModelProviderSetting(pub String);
impl JsonSchema for LanguageModelProviderSetting {
fn schema_name() -> Cow<'static, str> {
"LanguageModelProviderSetting".into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
// list the builtin providers as a subset so that we still auto complete them in the settings
json_schema!({
"anyOf": [
{
"type": "string",
"enum": [
"amazon-bedrock",
"anthropic",
"copilot_chat",
"deepseek",
"google",
"lmstudio",
"mistral",
"ollama",
"openai",
"openrouter",
"vercel",
"x_ai",
"zed.dev"
]
},
{
"type": "string",
}
]
})
impl From<settings::CompletionMode> for CompletionMode {
fn from(value: settings::CompletionMode) -> Self {
match value {
settings::CompletionMode::Normal => CompletionMode::Normal,
settings::CompletionMode::Burn => CompletionMode::Burn,
}
}
}
impl From<String> for LanguageModelProviderSetting {
fn from(provider: String) -> Self {
Self(provider)
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AgentProfileId(pub Arc<str>);
impl AgentProfileId {
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for LanguageModelProviderSetting {
fn from(provider: &str) -> Self {
Self(provider.to_string())
impl std::fmt::Display for AgentProfileId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AgentProfileContent {
pub name: Arc<str>,
#[serde(default)]
pub tools: IndexMap<Arc<str>, bool>,
/// Whether all context servers are enabled by default.
pub enable_all_context_servers: Option<bool>,
#[serde(default)]
pub context_servers: IndexMap<Arc<str>, ContextServerPresetContent>,
}
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct ContextServerPresetContent {
pub tools: IndexMap<Arc<str>, bool>,
impl Default for AgentProfileId {
fn default() -> Self {
Self("write".into())
}
}
impl Settings for AgentSettings {
const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]);
type FileContent = AgentSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,
_: &mut gpui::App,
) -> anyhow::Result<Self> {
let mut settings = AgentSettings::default();
for value in sources.defaults_and_customizations() {
merge(&mut settings.enabled, value.enabled);
merge(&mut settings.button, value.button);
merge(&mut settings.dock, value.dock);
merge(
&mut settings.default_width,
value.default_width.map(Into::into),
);
merge(
&mut settings.default_height,
value.default_height.map(Into::into),
);
settings.default_model = value
.default_model
.clone()
.or(settings.default_model.take());
settings.inline_assistant_model = value
.inline_assistant_model
.clone()
.or(settings.inline_assistant_model.take());
settings.commit_message_model = value
.clone()
.commit_message_model
.or(settings.commit_message_model.take());
settings.thread_summary_model = value
.clone()
.thread_summary_model
.or(settings.thread_summary_model.take());
merge(
&mut settings.inline_alternatives,
value.inline_alternatives.clone(),
);
merge(
&mut settings.notify_when_agent_waiting,
value.notify_when_agent_waiting,
);
merge(
&mut settings.play_sound_when_agent_done,
value.play_sound_when_agent_done,
);
merge(&mut settings.stream_edits, value.stream_edits);
merge(&mut settings.single_file_review, value.single_file_review);
merge(&mut settings.default_profile, value.default_profile.clone());
merge(&mut settings.default_view, value.default_view);
merge(
&mut settings.preferred_completion_mode,
value.preferred_completion_mode,
);
merge(&mut settings.enable_feedback, value.enable_feedback);
merge(&mut settings.expand_edit_card, value.expand_edit_card);
merge(
&mut settings.expand_terminal_card,
value.expand_terminal_card,
);
merge(
&mut settings.use_modifier_to_send,
value.use_modifier_to_send,
);
merge(
&mut settings.message_editor_min_lines,
value.message_editor_min_lines,
);
settings
.model_parameters
.extend_from_slice(&value.model_parameters);
if let Some(profiles) = value.profiles.as_ref() {
settings
.profiles
.extend(profiles.into_iter().map(|(id, profile)| {
(
id.clone(),
AgentProfileSettings {
name: profile.name.clone().into(),
tools: profile.tools.clone(),
enable_all_context_servers: profile
.enable_all_context_servers
.unwrap_or_default(),
context_servers: profile
.context_servers
.iter()
.map(|(context_server_id, preset)| {
(
context_server_id.clone(),
ContextServerPreset {
tools: preset.tools.clone(),
},
)
})
.collect(),
},
)
}));
}
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
let agent = content.agent.clone().unwrap();
Self {
enabled: agent.enabled.unwrap(),
button: agent.button.unwrap(),
dock: agent.dock.unwrap(),
default_width: px(agent.default_width.unwrap()),
default_height: px(agent.default_height.unwrap()),
default_model: Some(agent.default_model.unwrap()),
inline_assistant_model: agent.inline_assistant_model,
commit_message_model: agent.commit_message_model,
thread_summary_model: agent.thread_summary_model,
inline_alternatives: agent.inline_alternatives.unwrap_or_default(),
default_profile: AgentProfileId(agent.default_profile.unwrap()),
default_view: agent.default_view.unwrap(),
profiles: agent
.profiles
.unwrap()
.into_iter()
.map(|(key, val)| (AgentProfileId(key), val.into()))
.collect(),
always_allow_tool_actions: agent.always_allow_tool_actions.unwrap(),
notify_when_agent_waiting: agent.notify_when_agent_waiting.unwrap(),
play_sound_when_agent_done: agent.play_sound_when_agent_done.unwrap(),
stream_edits: agent.stream_edits.unwrap(),
single_file_review: agent.single_file_review.unwrap(),
model_parameters: agent.model_parameters,
preferred_completion_mode: agent.preferred_completion_mode.unwrap().into(),
enable_feedback: agent.enable_feedback.unwrap(),
expand_edit_card: agent.expand_edit_card.unwrap(),
expand_terminal_card: agent.expand_terminal_card.unwrap(),
use_modifier_to_send: agent.use_modifier_to_send.unwrap(),
message_editor_min_lines: agent.message_editor_min_lines.unwrap(),
}
debug_assert!(
!sources.default.always_allow_tool_actions.unwrap_or(false),
"For security, agent.always_allow_tool_actions should always be false in default.json. If it's true, that is a bug that should be fixed!"
);
// For security reasons, only trust the user's global settings for whether to always allow tool actions.
// If this could be overridden locally, an attacker could (e.g. by committing to source control and
// convincing you to switch branches) modify your project-local settings to disable the agent's safety checks.
settings.always_allow_tool_actions = sources
.user
.and_then(|setting| setting.always_allow_tool_actions)
.unwrap_or(false);
Ok(settings)
}
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
let Some(value) = &content.agent else { return };
self.enabled.merge_from(&value.enabled);
self.button.merge_from(&value.button);
self.dock.merge_from(&value.dock);
self.default_width
.merge_from(&value.default_width.map(Into::into));
self.default_height
.merge_from(&value.default_height.map(Into::into));
self.default_model = value.default_model.clone().or(self.default_model.take());
self.inline_assistant_model = value
.inline_assistant_model
.clone()
.or(self.inline_assistant_model.take());
self.commit_message_model = value
.clone()
.commit_message_model
.or(self.commit_message_model.take());
self.thread_summary_model = value
.clone()
.thread_summary_model
.or(self.thread_summary_model.take());
self.inline_alternatives
.merge_from(&value.inline_alternatives.clone());
self.default_profile
.merge_from(&value.default_profile.clone().map(AgentProfileId));
self.default_view.merge_from(&value.default_view);
self.always_allow_tool_actions
.merge_from(&value.always_allow_tool_actions);
self.notify_when_agent_waiting
.merge_from(&value.notify_when_agent_waiting);
self.play_sound_when_agent_done
.merge_from(&value.play_sound_when_agent_done);
self.stream_edits.merge_from(&value.stream_edits);
self.single_file_review
.merge_from(&value.single_file_review);
self.preferred_completion_mode
.merge_from(&value.preferred_completion_mode.map(Into::into));
self.enable_feedback.merge_from(&value.enable_feedback);
self.expand_edit_card.merge_from(&value.expand_edit_card);
self.expand_terminal_card
.merge_from(&value.expand_terminal_card);
self.use_modifier_to_send
.merge_from(&value.use_modifier_to_send);
self.model_parameters
.extend_from_slice(&value.model_parameters);
self.message_editor_min_lines
.merge_from(&value.message_editor_min_lines);
if let Some(profiles) = value.profiles.as_ref() {
self.profiles.extend(
profiles
.into_iter()
.map(|(id, profile)| (AgentProfileId(id.clone()), profile.clone().into())),
);
}
}
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
if let Some(b) = vscode
.read_value("chat.agent.enabled")
.and_then(|b| b.as_bool())
{
current.enabled = Some(b);
current.button = Some(b);
current.agent.get_or_insert_default().enabled = Some(b);
current.agent.get_or_insert_default().button = Some(b);
}
}
}
fn merge<T>(target: &mut T, value: Option<T>) {
if let Some(value) = value {
*target = value;
}
}
+2 -2
View File
@@ -7,7 +7,7 @@ use acp_thread::{AgentConnection, Plan};
use action_log::ActionLog;
use agent_client_protocol::{self as acp, PromptCapabilities};
use agent_servers::{AgentServer, AgentServerDelegate};
use agent_settings::{AgentProfileId, AgentSettings, CompletionMode, NotifyWhenAgentWaiting};
use agent_settings::{AgentProfileId, AgentSettings, CompletionMode};
use agent2::{DbThreadMetadata, HistoryEntry, HistoryEntryId, HistoryStore, NativeAgentServer};
use anyhow::{Result, anyhow, bail};
use arrayvec::ArrayVec;
@@ -35,7 +35,7 @@ use markdown::{HeadingLevelStyles, Markdown, MarkdownElement, MarkdownStyle};
use project::{Project, ProjectEntryId};
use prompt_store::{PromptId, PromptStore};
use rope::Point;
use settings::{Settings as _, SettingsStore};
use settings::{NotifyWhenAgentWaiting, Settings as _, SettingsStore};
use std::cell::RefCell;
use std::path::Path;
use std::sync::Arc;
+81 -92
View File
@@ -26,12 +26,8 @@ use language_model::{
};
use notifications::status_toast::{StatusToast, ToastIcon};
use project::{
agent_server_store::{
AgentServerCommand, AgentServerStore, AllAgentServersSettings, CLAUDE_CODE_NAME,
CustomAgentServerSettings, GEMINI_NAME,
},
agent_server_store::{AgentServerStore, CLAUDE_CODE_NAME, GEMINI_NAME},
context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore},
project_settings::{ContextServerSettings, ProjectSettings},
};
use settings::{Settings, SettingsStore, update_settings_file};
use ui::{
@@ -419,8 +415,8 @@ impl AgentConfiguration {
always_allow_tool_actions,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AgentSettings>(fs.clone(), cx, move |settings, _| {
settings.set_always_allow_tool_actions(allow);
update_settings_file(fs.clone(), cx, move |settings, _| {
settings.agent.get_or_insert_default().set_always_allow_tool_actions(allow);
});
},
)
@@ -437,8 +433,11 @@ impl AgentConfiguration {
single_file_review,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AgentSettings>(fs.clone(), cx, move |settings, _| {
settings.set_single_file_review(allow);
update_settings_file(fs.clone(), cx, move |settings, _| {
settings
.agent
.get_or_insert_default()
.set_single_file_review(allow);
});
},
)
@@ -457,8 +456,8 @@ impl AgentConfiguration {
play_sound_when_agent_done,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AgentSettings>(fs.clone(), cx, move |settings, _| {
settings.set_play_sound_when_agent_done(allow);
update_settings_file(fs.clone(), cx, move |settings, _| {
settings.agent.get_or_insert_default().set_play_sound_when_agent_done(allow);
});
},
)
@@ -477,8 +476,8 @@ impl AgentConfiguration {
use_modifier_to_send,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AgentSettings>(fs.clone(), cx, move |settings, _| {
settings.set_use_modifier_to_send(allow);
update_settings_file(fs.clone(), cx, move |settings, _| {
settings.agent.get_or_insert_default().set_use_modifier_to_send(allow);
});
},
)
@@ -862,14 +861,14 @@ impl AgentConfiguration {
async move |cx| {
uninstall_extension_task.await?;
cx.update(|cx| {
update_settings_file::<ProjectSettings>(
update_settings_file(
fs.clone(),
cx,
{
let context_server_id =
context_server_id.clone();
move |settings, _| {
settings
settings.project
.context_servers
.remove(&context_server_id.0);
}
@@ -944,67 +943,53 @@ impl AgentConfiguration {
.flex_none()
.child(context_server_configuration_menu)
.child(
Switch::new("context-server-switch", is_running.into())
.color(SwitchColor::Accent)
.on_click({
let context_server_manager =
self.context_server_store.clone();
let fs = self.fs.clone();
Switch::new("context-server-switch", is_running.into())
.color(SwitchColor::Accent)
.on_click({
let context_server_manager = self.context_server_store.clone();
let fs = self.fs.clone();
move |state, _window, cx| {
let is_enabled = match state {
ToggleState::Unselected
| ToggleState::Indeterminate => {
context_server_manager.update(
cx,
|this, cx| {
this.stop_server(
&context_server_id,
cx,
)
.log_err();
},
);
false
}
ToggleState::Selected => {
context_server_manager.update(
cx,
|this, cx| {
if let Some(server) =
this.get_server(&context_server_id)
{
this.start_server(server, cx);
}
},
);
true
}
};
update_settings_file::<ProjectSettings>(
fs.clone(),
cx,
{
let context_server_id =
context_server_id.clone();
move |settings, _| {
settings
.context_servers
.entry(context_server_id.0)
.or_insert_with(|| {
ContextServerSettings::Extension {
enabled: is_enabled,
settings: serde_json::json!({}),
}
})
.set_enabled(is_enabled);
move |state, _window, cx| {
let is_enabled = match state {
ToggleState::Unselected
| ToggleState::Indeterminate => {
context_server_manager.update(cx, |this, cx| {
this.stop_server(&context_server_id, cx)
.log_err();
});
false
}
ToggleState::Selected => {
context_server_manager.update(cx, |this, cx| {
if let Some(server) =
this.get_server(&context_server_id)
{
this.start_server(server, cx);
}
},
);
}
}),
),
});
true
}
};
update_settings_file(fs.clone(), cx, {
let context_server_id = context_server_id.clone();
move |settings, _| {
settings
.project
.context_servers
.entry(context_server_id.0)
.or_insert_with(|| {
settings::ContextServerSettingsContent::Extension {
enabled: is_enabled,
settings: serde_json::json!({}),
}
})
.set_enabled(is_enabled);
}
});
}
}),
),
),
)
.map(|parent| {
@@ -1236,15 +1221,12 @@ fn show_unable_to_uninstall_extension_with_context_server(
let context_server_id = context_server_id.clone();
async move |_workspace_handle, cx| {
cx.update(|cx| {
update_settings_file::<ProjectSettings>(
fs,
cx,
move |settings, _| {
settings
.context_servers
.remove(&context_server_id.0);
},
);
update_settings_file(fs, cx, move |settings, _| {
settings
.project
.context_servers
.remove(&context_server_id.0);
});
})?;
anyhow::Ok(())
}
@@ -1282,7 +1264,7 @@ async fn open_new_agent_servers_entry_in_settings_editor(
let settings = cx.global::<SettingsStore>();
let mut unique_server_name = None;
let edits = settings.edits_for_update::<AllAgentServersSettings>(&text, |file| {
let edits = settings.edits_for_update(&text, |settings| {
let server_name: Option<SharedString> = (0..u8::MAX)
.map(|i| {
if i == 0 {
@@ -1291,20 +1273,27 @@ async fn open_new_agent_servers_entry_in_settings_editor(
format!("your_agent_{}", i).into()
}
})
.find(|name| !file.custom.contains_key(name));
.find(|name| {
!settings
.agent_servers
.as_ref()
.is_some_and(|agent_servers| agent_servers.custom.contains_key(name))
});
if let Some(server_name) = server_name {
unique_server_name = Some(server_name.clone());
file.custom.insert(
server_name,
CustomAgentServerSettings {
command: AgentServerCommand {
settings
.agent_servers
.get_or_insert_default()
.custom
.insert(
server_name,
settings::CustomAgentServerSettings {
path: "path_to_executable".into(),
args: vec![],
env: Some(HashMap::default()),
default_mode: None,
},
default_mode: None,
},
);
);
}
});
@@ -5,11 +5,8 @@ use collections::HashSet;
use fs::Fs;
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Render, Task};
use language_model::LanguageModelRegistry;
use language_models::{
AllLanguageModelSettings, OpenAiCompatibleSettingsContent,
provider::open_ai_compatible::{AvailableModel, ModelCapabilities},
};
use settings::update_settings_file;
use language_models::provider::open_ai_compatible::{AvailableModel, ModelCapabilities};
use settings::{OpenAiCompatibleSettingsContent, update_settings_file};
use ui::{
Banner, Checkbox, KeyBinding, Modal, ModalFooter, ModalHeader, Section, ToggleState, prelude::*,
};
@@ -238,14 +235,19 @@ fn save_provider_to_settings(
task.await
.map_err(|_| "Failed to write API key to keychain")?;
cx.update(|cx| {
update_settings_file::<AllLanguageModelSettings>(fs, cx, |settings, _cx| {
settings.openai_compatible.get_or_insert_default().insert(
provider_name,
OpenAiCompatibleSettingsContent {
api_url,
available_models: models,
},
);
update_settings_file(fs, cx, |settings, _cx| {
settings
.language_models
.get_or_insert_default()
.openai_compatible
.get_or_insert_default()
.insert(
provider_name,
OpenAiCompatibleSettingsContent {
api_url,
available_models: models,
},
);
});
})
.ok();
@@ -422,18 +422,17 @@ impl ConfigureContextServerModal {
workspace.update(cx, |workspace, cx| {
let fs = workspace.app_state().fs.clone();
let original_server_id = self.original_server_id.clone();
update_settings_file::<ProjectSettings>(
fs.clone(),
cx,
move |project_settings, _| {
if let Some(original_id) = original_server_id {
if original_id != id {
project_settings.context_servers.remove(&original_id.0);
}
update_settings_file(fs.clone(), cx, move |current, _| {
if let Some(original_id) = original_server_id {
if original_id != id {
current.project.context_servers.remove(&original_id.0);
}
project_settings.context_servers.insert(id.0, settings);
},
);
}
current
.project
.context_servers
.insert(id.0, settings.into());
});
});
} else if let Some(existing_server) = existing_server {
self.context_server_store
@@ -1,14 +1,11 @@
use std::{collections::BTreeMap, sync::Arc};
use agent_settings::{
AgentProfileContent, AgentProfileId, AgentProfileSettings, AgentSettings, AgentSettingsContent,
ContextServerPresetContent,
};
use agent_settings::{AgentProfileId, AgentProfileSettings};
use assistant_tool::{ToolSource, ToolWorkingSet};
use fs::Fs;
use gpui::{App, Context, DismissEvent, Entity, EventEmitter, Focusable, Task, WeakEntity, Window};
use picker::{Picker, PickerDelegate};
use settings::update_settings_file;
use settings::{AgentProfileContent, ContextServerPresetContent, update_settings_file};
use ui::{ListItem, ListItemSpacing, prelude::*};
use util::ResultExt as _;
@@ -266,15 +263,19 @@ impl PickerDelegate for ToolPickerDelegate {
is_enabled
};
update_settings_file::<AgentSettings>(self.fs.clone(), cx, {
update_settings_file(self.fs.clone(), cx, {
let profile_id = self.profile_id.clone();
let default_profile = self.profile_settings.clone();
let server_id = server_id.clone();
let tool_name = tool_name.clone();
move |settings: &mut AgentSettingsContent, _cx| {
let profiles = settings.profiles.get_or_insert_default();
move |settings, _cx| {
let profiles = settings
.agent
.get_or_insert_default()
.profiles
.get_or_insert_default();
let profile = profiles
.entry(profile_id)
.entry(profile_id.0)
.or_insert_with(|| AgentProfileContent {
name: default_profile.name.into(),
tools: default_profile.tools,
+6 -9
View File
@@ -2,7 +2,6 @@ use crate::{
ModelUsageContext,
language_model_selector::{LanguageModelSelector, language_model_selector},
};
use agent_settings::AgentSettings;
use fs::Fs;
use gpui::{Entity, FocusHandle, SharedString};
use picker::popover_menu::PickerPopoverMenu;
@@ -39,14 +38,12 @@ impl AgentModelSelector {
let model_id = model.id().0.to_string();
match &model_usage_context {
ModelUsageContext::InlineAssistant => {
update_settings_file::<AgentSettings>(
fs.clone(),
cx,
move |settings, _cx| {
settings
.set_inline_assistant_model(provider.clone(), model_id);
},
);
update_settings_file(fs.clone(), cx, move |settings, _cx| {
settings
.agent
.get_or_insert_default()
.set_inline_assistant_model(provider.clone(), model_id);
});
}
}
},
+31 -31
View File
@@ -10,6 +10,9 @@ use project::agent_server_store::{
AgentServerCommand, AllAgentServersSettings, CLAUDE_CODE_NAME, GEMINI_NAME,
};
use serde::{Deserialize, Serialize};
use settings::{
DefaultAgentView as DefaultView, LanguageModelProviderSetting, LanguageModelSelection,
};
use zed_actions::OpenBrowser;
use zed_actions::agent::{OpenClaudeCodeOnboardingModal, ReauthenticateAgent};
@@ -33,7 +36,7 @@ use agent::{
history_store::{HistoryEntryId, HistoryStore},
thread_store::{TextThreadStore, ThreadStore},
};
use agent_settings::{AgentDockPosition, AgentSettings, DefaultView};
use agent_settings::AgentSettings;
use ai_onboarding::AgentPanelOnboarding;
use anyhow::{Result, anyhow};
use assistant_context::{AssistantContext, ContextEvent, ContextSummary};
@@ -1058,17 +1061,14 @@ impl AgentPanel {
match self.active_view.which_font_size_used() {
WhichFontSize::AgentFont => {
if persist {
update_settings_file::<ThemeSettings>(
self.fs.clone(),
cx,
move |settings, cx| {
let agent_font_size =
ThemeSettings::get_global(cx).agent_font_size(cx) + delta;
let _ = settings
.agent_font_size
.insert(Some(theme::clamp_font_size(agent_font_size).into()));
},
);
update_settings_file(self.fs.clone(), cx, move |settings, cx| {
let agent_font_size =
ThemeSettings::get_global(cx).agent_font_size(cx) + delta;
let _ = settings
.theme
.agent_font_size
.insert(Some(theme::clamp_font_size(agent_font_size).into()));
});
} else {
theme::adjust_agent_font_size(cx, |size| size + delta);
}
@@ -1089,8 +1089,8 @@ impl AgentPanel {
cx: &mut Context<Self>,
) {
if action.persist {
update_settings_file::<ThemeSettings>(self.fs.clone(), cx, move |settings, _| {
settings.agent_font_size = None;
update_settings_file(self.fs.clone(), cx, move |settings, _| {
settings.theme.agent_font_size = None;
});
} else {
theme::reset_agent_font_size(cx);
@@ -1175,11 +1175,17 @@ impl AgentPanel {
.is_none_or(|model| model.provider.id() != provider.id())
&& let Some(model) = provider.default_model(cx)
{
update_settings_file::<AgentSettings>(
self.fs.clone(),
cx,
move |settings, _| settings.set_model(model),
);
update_settings_file(self.fs.clone(), cx, move |settings, _| {
let provider = model.provider_id().0.to_string();
let model = model.id().0.to_string();
settings
.agent
.get_or_insert_default()
.set_model(LanguageModelSelection {
provider: LanguageModelProviderSetting(provider),
model,
})
});
}
self.new_thread(&NewThread::default(), window, cx);
@@ -1424,11 +1430,7 @@ impl Focusable for AgentPanel {
}
fn agent_panel_dock_position(cx: &App) -> DockPosition {
match AgentSettings::get_global(cx).dock {
AgentDockPosition::Left => DockPosition::Left,
AgentDockPosition::Bottom => DockPosition::Bottom,
AgentDockPosition::Right => DockPosition::Right,
}
AgentSettings::get_global(cx).dock.into()
}
impl EventEmitter<PanelEvent> for AgentPanel {}
@@ -1447,13 +1449,11 @@ impl Panel for AgentPanel {
}
fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context<Self>) {
settings::update_settings_file::<AgentSettings>(self.fs.clone(), cx, move |settings, _| {
let dock = match position {
DockPosition::Left => AgentDockPosition::Left,
DockPosition::Bottom => AgentDockPosition::Bottom,
DockPosition::Right => AgentDockPosition::Right,
};
settings.set_dock(dock);
settings::update_settings_file(self.fs.clone(), cx, move |settings, _| {
settings
.agent
.get_or_insert_default()
.set_dock(position.into());
});
}
+2 -2
View File
@@ -23,7 +23,7 @@ use std::rc::Rc;
use std::sync::Arc;
use agent::ThreadId;
use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
use agent_settings::{AgentProfileId, AgentSettings};
use assistant_slash_command::SlashCommandRegistry;
use client::Client;
use command_palette_hooks::CommandPaletteFilter;
@@ -39,7 +39,7 @@ use project::agent_server_store::AgentServerCommand;
use prompt_store::PromptBuilder;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings as _, SettingsStore};
use settings::{LanguageModelSelection, Settings as _, SettingsStore};
use std::any::TypeId;
use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal};
@@ -5,7 +5,6 @@ use extension::ExtensionManifest;
use fs::Fs;
use gpui::WeakEntity;
use language::LanguageRegistry;
use project::project_settings::ProjectSettings;
use settings::update_settings_file;
use ui::prelude::*;
use util::ResultExt;
@@ -69,8 +68,9 @@ fn remove_context_server_settings(
fs: Arc<dyn Fs>,
cx: &mut App,
) {
update_settings_file::<ProjectSettings>(fs, cx, move |settings, _| {
update_settings_file(fs, cx, move |settings, _| {
settings
.project
.context_servers
.retain(|server_id, _| !context_server_ids.contains(server_id));
});
+11 -9
View File
@@ -1,11 +1,10 @@
use crate::{ManageProfiles, ToggleProfileSelector};
use agent_settings::{
AgentDockPosition, AgentProfile, AgentProfileId, AgentSettings, AvailableProfiles,
builtin_profiles,
AgentProfile, AgentProfileId, AgentSettings, AvailableProfiles, builtin_profiles,
};
use fs::Fs;
use gpui::{Action, Entity, FocusHandle, Subscription, prelude::*};
use settings::{Settings as _, SettingsStore, update_settings_file};
use settings::{DockPosition, Settings as _, SettingsStore, update_settings_file};
use std::sync::Arc;
use ui::{
ContextMenu, ContextMenuEntry, DocumentationEdge, DocumentationSide, PopoverMenu,
@@ -142,10 +141,13 @@ impl ProfileSelector {
let fs = self.fs.clone();
let provider = self.provider.clone();
move |_window, cx| {
update_settings_file::<AgentSettings>(fs.clone(), cx, {
update_settings_file(fs.clone(), cx, {
let profile_id = profile_id.clone();
move |settings, _cx| {
settings.set_profile(profile_id);
settings
.agent
.get_or_insert_default()
.set_profile(profile_id.0);
}
});
@@ -216,10 +218,10 @@ impl Render for ProfileSelector {
}
}
fn documentation_side(position: AgentDockPosition) -> DocumentationSide {
fn documentation_side(position: DockPosition) -> DocumentationSide {
match position {
AgentDockPosition::Left => DocumentationSide::Right,
AgentDockPosition::Bottom => DocumentationSide::Left,
AgentDockPosition::Right => DocumentationSide::Left,
DockPosition::Left => DocumentationSide::Right,
DockPosition::Bottom => DocumentationSide::Left,
DockPosition::Right => DocumentationSide::Left,
}
}
+15 -7
View File
@@ -3,7 +3,7 @@ use crate::{
language_model_selector::{LanguageModelSelector, language_model_selector},
ui::BurnModeTooltip,
};
use agent_settings::{AgentSettings, CompletionMode};
use agent_settings::CompletionMode;
use anyhow::Result;
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection, SlashCommandWorkingSet};
use assistant_slash_commands::{DefaultSlashCommand, FileSlashCommand, selections_creases};
@@ -41,7 +41,10 @@ use project::{Project, Worktree};
use project::{ProjectPath, lsp_store::LocalLspAdapterDelegate};
use rope::Point;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore, update_settings_file};
use settings::{
LanguageModelProviderSetting, LanguageModelSelection, Settings, SettingsStore,
update_settings_file,
};
use std::{
any::TypeId,
cmp,
@@ -294,11 +297,16 @@ impl TextThreadEditor {
language_model_selector(
|cx| LanguageModelRegistry::read_global(cx).default_model(),
move |model, cx| {
update_settings_file::<AgentSettings>(
fs.clone(),
cx,
move |settings, _| settings.set_model(model.clone()),
);
update_settings_file(fs.clone(), cx, move |settings, _| {
let provider = model.provider_id().0.to_string();
let model = model.id().0.to_string();
settings.agent.get_or_insert_default().set_model(
LanguageModelSelection {
provider: LanguageModelProviderSetting(provider),
model,
},
)
});
},
window,
cx,
+1
View File
@@ -23,6 +23,7 @@ http_client.workspace = true
schemars = { workspace = true, optional = true }
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
strum.workspace = true
thiserror.workspace = true
workspace-hack.workspace = true
+19
View File
@@ -8,6 +8,7 @@ use futures::{AsyncBufReadExt, AsyncReadExt, StreamExt, io::BufReader, stream::B
use http_client::http::{self, HeaderMap, HeaderValue};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest, StatusCode};
use serde::{Deserialize, Serialize};
pub use settings::{AnthropicAvailableModel as AvailableModel, ModelMode};
use strum::{EnumIter, EnumString};
use thiserror::Error;
@@ -31,6 +32,24 @@ pub enum AnthropicModelMode {
},
}
impl From<ModelMode> for AnthropicModelMode {
fn from(value: ModelMode) -> Self {
match value {
ModelMode::Default => AnthropicModelMode::Default,
ModelMode::Thinking { budget_tokens } => AnthropicModelMode::Thinking { budget_tokens },
}
}
}
impl From<AnthropicModelMode> for ModelMode {
fn from(value: AnthropicModelMode) -> Self {
match value {
AnthropicModelMode::Default => ModelMode::Default,
AnthropicModelMode::Thinking { budget_tokens } => ModelMode::Thinking { budget_tokens },
}
}
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)]
pub enum Model {
+24 -27
View File
@@ -1445,8 +1445,8 @@ mod tests {
fn init_test_with_config(cx: &mut TestAppContext, data_dir: &Path) {
cx.update(|cx| {
// Set custom data directory (config will be under data_dir/config)
paths::set_custom_data_dir(data_dir.to_str().unwrap());
// Set custom data directory (config will be under data_dir/config)
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
@@ -1537,14 +1537,11 @@ mod tests {
// First, test with format_on_save enabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<language::language_settings::AllLanguageSettings>(
cx,
|settings| {
settings.defaults.format_on_save = Some(FormatOnSave::On);
settings.defaults.formatter =
Some(language::language_settings::SelectedFormatter::Auto);
},
);
store.update_user_settings(cx, |settings| {
settings.project.all_languages.defaults.format_on_save = Some(FormatOnSave::On);
settings.project.all_languages.defaults.formatter =
Some(language::language_settings::SelectedFormatter::Auto);
});
});
});
@@ -1603,12 +1600,10 @@ mod tests {
// Next, test with format_on_save disabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<language::language_settings::AllLanguageSettings>(
cx,
|settings| {
settings.defaults.format_on_save = Some(FormatOnSave::Off);
},
);
store.update_user_settings(cx, |settings| {
settings.project.all_languages.defaults.format_on_save =
Some(FormatOnSave::Off);
});
});
});
@@ -1679,12 +1674,13 @@ mod tests {
// First, test with remove_trailing_whitespace_on_save enabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<language::language_settings::AllLanguageSettings>(
cx,
|settings| {
settings.defaults.remove_trailing_whitespace_on_save = Some(true);
},
);
store.update_user_settings(cx, |settings| {
settings
.project
.all_languages
.defaults
.remove_trailing_whitespace_on_save = Some(true);
});
});
});
@@ -1741,12 +1737,13 @@ mod tests {
// Next, test with remove_trailing_whitespace_on_save disabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<language::language_settings::AllLanguageSettings>(
cx,
|settings| {
settings.defaults.remove_trailing_whitespace_on_save = Some(false);
},
);
store.update_user_settings(cx, |settings| {
settings
.project
.all_languages
.defaults
.remove_trailing_whitespace_on_save = Some(false);
});
});
});
+7 -8
View File
@@ -314,7 +314,7 @@ mod tests {
use gpui::{AppContext, TestAppContext, UpdateGlobal};
use language::{Language, LanguageConfig, LanguageMatcher};
use language_model::fake_provider::FakeLanguageModel;
use project::{FakeFs, Project, WorktreeSettings};
use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use unindent::Unindent;
@@ -849,15 +849,14 @@ mod tests {
cx.update(|cx| {
use gpui::UpdateGlobal;
use project::WorktreeSettings;
use settings::SettingsStore;
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions = Some(vec![
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
]);
settings.private_files = Some(vec![
settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -1158,10 +1157,10 @@ mod tests {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions =
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
settings.private_files = Some(vec!["**/.env".to_string()]);
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
@@ -230,7 +230,7 @@ mod tests {
use gpui::{AppContext, TestAppContext, UpdateGlobal};
use indoc::indoc;
use language_model::fake_provider::FakeLanguageModel;
use project::{FakeFs, Project, WorktreeSettings};
use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use util::path;
@@ -507,13 +507,13 @@ mod tests {
// Configure settings explicitly
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions = Some(vec![
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
"**/.hidden_subdir".to_string(),
]);
settings.private_files = Some(vec![
settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -698,10 +698,10 @@ mod tests {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions =
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
settings.private_files = Some(vec!["**/.env".to_string()]);
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
+7 -8
View File
@@ -299,7 +299,7 @@ mod test {
use gpui::{AppContext, TestAppContext, UpdateGlobal};
use language::{Language, LanguageConfig, LanguageMatcher};
use language_model::fake_provider::FakeLanguageModel;
use project::{FakeFs, Project, WorktreeSettings};
use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use util::path;
@@ -677,15 +677,14 @@ mod test {
cx.update(|cx| {
use gpui::UpdateGlobal;
use project::WorktreeSettings;
use settings::SettingsStore;
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions = Some(vec![
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
]);
settings.private_files = Some(vec![
settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -968,10 +967,10 @@ mod test {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions =
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
settings.private_files = Some(vec!["**/.env".to_string()]);
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
-1
View File
@@ -21,7 +21,6 @@ gpui.workspace = true
log.workspace = true
parking_lot.workspace = true
rodio = { workspace = true, features = [ "wav", "playback", "wav_output" ] }
schemars.workspace = true
serde.workspace = true
settings.workspace = true
smol.workspace = true
+27 -36
View File
@@ -1,62 +1,53 @@
use std::sync::atomic::{AtomicBool, Ordering};
use anyhow::Result;
use gpui::App;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsStore, SettingsUi};
use settings::{Settings, SettingsStore};
use util::MergeFrom as _;
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)]
#[derive(Clone, Debug)]
pub struct AudioSettings {
/// Opt into the new audio system.
#[serde(rename = "experimental.rodio_audio", default)]
pub rodio_audio: bool, // default is false
/// Requires 'rodio_audio: true'
///
/// Use the new audio systems automatic gain control for your microphone.
/// This affects how loud you sound to others.
#[serde(rename = "experimental.control_input_volume", default)]
pub control_input_volume: bool,
/// Requires 'rodio_audio: true'
///
/// Use the new audio systems automatic gain control on everyone in the
/// call. This makes call members who are too quite louder and those who are
/// too loud quieter. This only affects how things sound for you.
#[serde(rename = "experimental.control_output_volume", default)]
pub control_output_volume: bool,
}
/// Configuration of audio in Zed.
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
#[serde(default)]
#[settings_key(key = "audio")]
pub struct AudioSettingsContent {
/// Opt into the new audio system.
#[serde(rename = "experimental.rodio_audio", default)]
pub rodio_audio: bool, // default is false
/// Requires 'rodio_audio: true'
///
/// Use the new audio systems automatic gain control for your microphone.
/// This affects how loud you sound to others.
#[serde(rename = "experimental.control_input_volume", default)]
pub control_input_volume: bool,
/// Requires 'rodio_audio: true'
///
/// Use the new audio systems automatic gain control on everyone in the
/// call. This makes call members who are too quite louder and those who are
/// too loud quieter. This only affects how things sound for you.
#[serde(rename = "experimental.control_output_volume", default)]
pub control_output_volume: bool,
}
/// Configuration of audio in Zed
impl Settings for AudioSettings {
type FileContent = AudioSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _cx: &mut App) -> Result<Self> {
sources.json_merge()
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
let audio = &content.audio.as_ref().unwrap();
AudioSettings {
control_input_volume: audio.control_input_volume.unwrap(),
control_output_volume: audio.control_output_volume.unwrap(),
rodio_audio: audio.rodio_audio.unwrap(),
}
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
let Some(audio) = content.audio.as_ref() else {
return;
};
self.control_input_volume
.merge_from(&audio.control_input_volume);
self.control_output_volume
.merge_from(&audio.control_output_volume);
self.rodio_audio.merge_from(&audio.rodio_audio);
}
fn import_from_vscode(
_vscode: &settings::VsCodeSettings,
_current: &mut settings::SettingsContent,
) {
}
}
/// See docs on [LIVE_SETTINGS]
-1
View File
@@ -21,7 +21,6 @@ http_client.workspace = true
log.workspace = true
paths.workspace = true
release_channel.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
+15 -36
View File
@@ -8,9 +8,8 @@ use gpui::{
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use paths::remote_servers_dir;
use release_channel::{AppCommitSha, ReleaseChannel};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsStore, SettingsUi};
use settings::{Settings, SettingsContent, SettingsStore};
use smol::{fs, io::AsyncReadExt};
use smol::{fs::File, process::Command};
use std::{
@@ -113,47 +112,27 @@ impl Drop for MacOsUnmounter {
}
}
#[derive(Clone, Copy, Debug)]
struct AutoUpdateSetting(bool);
/// Whether or not to automatically check for updates.
///
/// Default: true
#[derive(Clone, Copy, Default, JsonSchema, Deserialize, Serialize, SettingsUi, SettingsKey)]
#[settings_key(None)]
#[settings_ui(group = "Auto Update")]
struct AutoUpdateSettingContent {
pub auto_update: Option<bool>,
}
impl Settings for AutoUpdateSetting {
type FileContent = AutoUpdateSettingContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
let auto_update = [
sources.server,
sources.release_channel,
sources.operating_system,
sources.user,
]
.into_iter()
.find_map(|value| value.and_then(|val| val.auto_update))
.or(sources.default.auto_update)
.ok_or_else(Self::missing_default)?;
Ok(Self(auto_update))
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
debug_assert_eq!(content.auto_update.unwrap(), true);
Self(content.auto_update.unwrap())
}
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
let mut cur = &mut Some(*current);
vscode.enum_setting("update.mode", &mut cur, |s| match s {
"none" | "manual" => Some(AutoUpdateSettingContent {
auto_update: Some(false),
}),
_ => Some(AutoUpdateSettingContent {
auto_update: Some(true),
}),
});
*current = cur.unwrap();
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
if let Some(auto_update) = content.auto_update {
self.0 = auto_update;
}
}
fn import_from_vscode(_: &settings::VsCodeSettings, _: &mut SettingsContent) {
// We could match on vscode's update.mode here, but
// I think it's more important to have more people updating zed by default.
}
}
@@ -1002,7 +981,7 @@ mod tests {
#[gpui::test]
fn test_auto_update_defaults_to_true(cx: &mut TestAppContext) {
cx.update(|cx| {
let mut store = SettingsStore::new(cx);
let mut store = SettingsStore::new(cx, &settings::default_settings());
store
.set_default_settings(&default_settings(), cx)
.expect("Unable to set default settings");
-1
View File
@@ -35,7 +35,6 @@ language.workspace = true
log.workspace = true
postage.workspace = true
project.workspace = true
schemars.workspace = true
serde.workspace = true
settings.workspace = true
telemetry.workspace = true
+21 -25
View File
@@ -1,36 +1,32 @@
use anyhow::Result;
use gpui::App;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
use settings::Settings;
use util::MergeFrom;
#[derive(Deserialize, Debug)]
#[derive(Debug)]
pub struct CallSettings {
pub mute_on_join: bool,
pub share_on_join: bool,
}
/// Configuration of voice calls in Zed.
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
#[settings_key(key = "calls")]
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: false
pub share_on_join: Option<bool>,
}
impl Settings for CallSettings {
type FileContent = CallSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
sources.json_merge()
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
let call = content.calls.clone().unwrap();
CallSettings {
mute_on_join: call.mute_on_join.unwrap(),
share_on_join: call.share_on_join.unwrap(),
}
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
if let Some(call) = content.calls.clone() {
self.mute_on_join.merge_from(&call.mute_on_join);
self.share_on_join.merge_from(&call.share_on_join);
}
}
fn import_from_vscode(
_vscode: &settings::VsCodeSettings,
_current: &mut settings::SettingsContent,
) {
}
}
+57 -49
View File
@@ -31,7 +31,7 @@ use release_channel::{AppVersion, ReleaseChannel};
use rpc::proto::{AnyTypedEnvelope, EnvelopedMessage, PeerId, RequestMessage};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
use settings::{Settings, SettingsContent, SettingsKey, SettingsUi};
use std::{
any::TypeId,
convert::TryFrom,
@@ -50,7 +50,7 @@ use telemetry::Telemetry;
use thiserror::Error;
use tokio::net::TcpStream;
use url::Url;
use util::{ConnectionResult, ResultExt};
use util::{ConnectionResult, MergeFrom, ResultExt};
pub use rpc::*;
pub use telemetry_events::Event;
@@ -96,29 +96,33 @@ actions!(
]
);
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
#[settings_key(None)]
pub struct ClientSettingsContent {
server_url: Option<String>,
}
#[derive(Deserialize)]
pub struct ClientSettings {
pub server_url: String,
}
impl Settings for ClientSettings {
type FileContent = ClientSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
let mut result = sources.json_merge::<Self>()?;
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
if let Some(server_url) = &*ZED_SERVER_URL {
result.server_url.clone_from(server_url)
return Self {
server_url: server_url.clone(),
};
}
Self {
server_url: content.server_url.clone().unwrap(),
}
Ok(result)
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
if ZED_SERVER_URL.is_some() {
return;
}
if let Some(server_url) = content.server_url.clone() {
self.server_url = server_url;
}
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut SettingsContent) {}
}
#[derive(Default, Clone, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
@@ -147,19 +151,19 @@ impl ProxySettings {
}
impl Settings for ProxySettings {
type FileContent = ProxySettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
Ok(Self {
proxy: sources
.user
.or(sources.server)
.and_then(|value| value.proxy.clone())
.or(sources.default.proxy.clone()),
})
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
Self {
proxy: content.proxy.clone(),
}
}
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
if let Some(proxy) = content.proxy.clone() {
self.proxy = Some(proxy)
}
}
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
vscode.string_setting("http.proxy", &mut current.proxy);
}
}
@@ -538,37 +542,41 @@ pub struct TelemetrySettings {
pub metrics: bool,
}
/// Control what info is collected by Zed.
#[derive(Default, Clone, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
#[settings_key(key = "telemetry")]
pub struct TelemetrySettingsContent {
/// Send debug info like crash reports.
///
/// Default: true
pub diagnostics: Option<bool>,
/// Send anonymized usage data like what languages you're using Zed with.
///
/// Default: true
pub metrics: Option<bool>,
}
impl settings::Settings for TelemetrySettings {
type FileContent = TelemetrySettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
sources.json_merge()
fn from_defaults(content: &SettingsContent, _cx: &mut App) -> Self {
Self {
diagnostics: content.telemetry.as_ref().unwrap().diagnostics.unwrap(),
metrics: content.telemetry.as_ref().unwrap().metrics.unwrap(),
}
}
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
vscode.enum_setting("telemetry.telemetryLevel", &mut current.metrics, |s| {
fn refine(&mut self, content: &SettingsContent, _cx: &mut App) {
let Some(telemetry) = &content.telemetry else {
return;
};
self.diagnostics.merge_from(&telemetry.diagnostics);
self.metrics.merge_from(&telemetry.metrics);
}
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
let mut telemetry = settings::TelemetrySettingsContent::default();
vscode.enum_setting("telemetry.telemetryLevel", &mut telemetry.metrics, |s| {
Some(s == "all")
});
vscode.enum_setting("telemetry.telemetryLevel", &mut current.diagnostics, |s| {
Some(matches!(s, "all" | "error" | "crash"))
});
vscode.enum_setting(
"telemetry.telemetryLevel",
&mut telemetry.diagnostics,
|s| Some(matches!(s, "all" | "error" | "crash")),
);
// we could translate telemetry.telemetryLevel, but just because users didn't want
// to send microsoft telemetry doesn't mean they don't want to send it to zed. their
// all/error/crash/off correspond to combinations of our "diagnostics" and "metrics".
if let Some(diagnostics) = telemetry.diagnostics {
current.telemetry.get_or_insert_default().diagnostics = Some(diagnostics)
}
if let Some(metrics) = telemetry.metrics {
current.telemetry.get_or_insert_default().metrics = Some(metrics)
}
}
}
+70 -70
View File
@@ -4,7 +4,7 @@ use crate::{
};
use call::ActiveCall;
use editor::{
DocumentColorsRenderMode, Editor, EditorSettings, RowInfo, SelectionEffects,
DocumentColorsRenderMode, Editor, RowInfo, SelectionEffects,
actions::{
ConfirmCodeAction, ConfirmCompletion, ConfirmRename, ContextMenuFirst,
ExpandMacroRecursively, MoveToEnd, Redo, Rename, SelectAll, ToggleCodeActions, Undo,
@@ -18,20 +18,16 @@ use fs::Fs;
use futures::{SinkExt, StreamExt, channel::mpsc, lock::Mutex};
use gpui::{App, Rgba, TestAppContext, UpdateGlobal, VisualContext, VisualTestContext};
use indoc::indoc;
use language::{
FakeLspAdapter,
language_settings::{AllLanguageSettings, InlayHintSettings},
};
use language::FakeLspAdapter;
use lsp::LSP_REQUEST_TIMEOUT;
use project::{
ProjectPath, SERVER_PROGRESS_THROTTLE_TIMEOUT,
lsp_store::lsp_ext_command::{ExpandedMacro, LspExtExpandMacro},
project_settings::{InlineBlameSettings, ProjectSettings},
};
use recent_projects::disconnected_overlay::DisconnectedOverlay;
use rpc::RECEIVE_TIMEOUT;
use serde_json::json;
use settings::SettingsStore;
use settings::{InlayHintSettingsContent, InlineBlameSettings, SettingsStore};
use std::{
collections::BTreeSet,
ops::{Deref as _, Range},
@@ -1789,35 +1785,37 @@ async fn test_mutual_editor_inlay_hint_cache_update(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true,
show_value_hints: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: false,
show_other_hints: true,
show_background: false,
toggle_on_modifiers_press: None,
})
store.update_user_settings(cx, |settings| {
settings.project.all_languages.defaults.inlay_hints =
Some(InlayHintSettingsContent {
enabled: Some(true),
show_value_hints: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(false),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: false,
show_other_hints: true,
show_background: false,
toggle_on_modifiers_press: None,
})
store.update_user_settings(cx, |settings| {
settings.project.all_languages.defaults.inlay_hints =
Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(false),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
});
});
@@ -2039,35 +2037,37 @@ async fn test_inlay_hint_refresh_is_forwarded(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: false,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: false,
show_parameter_hints: false,
show_other_hints: false,
show_background: false,
toggle_on_modifiers_press: None,
})
store.update_user_settings(cx, |settings| {
settings.project.all_languages.defaults.inlay_hints =
Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(false),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(false),
show_parameter_hints: Some(false),
show_other_hints: Some(false),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
toggle_on_modifiers_press: None,
})
store.update_user_settings(cx, |settings| {
settings.project.all_languages.defaults.inlay_hints =
Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
});
});
@@ -2241,15 +2241,15 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<EditorSettings>(cx, |settings| {
settings.lsp_document_colors = Some(DocumentColorsRenderMode::None);
store.update_user_settings(cx, |settings| {
settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::None);
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<EditorSettings>(cx, |settings| {
settings.lsp_document_colors = Some(DocumentColorsRenderMode::Inlay);
store.update_user_settings(cx, |settings| {
settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::Inlay);
});
});
});
@@ -2422,8 +2422,8 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo
cx_b.update(|_, cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<EditorSettings>(cx, |settings| {
settings.lsp_document_colors = Some(DocumentColorsRenderMode::Background);
store.update_user_settings(cx, |settings| {
settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::Background);
});
});
});
@@ -2450,8 +2450,8 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo
cx_b.update(|_, cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<EditorSettings>(cx, |settings| {
settings.lsp_document_colors = Some(DocumentColorsRenderMode::Inlay);
store.update_user_settings(cx, |settings| {
settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::Inlay);
});
});
});
@@ -2478,8 +2478,8 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo
cx_a.update(|_, cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<EditorSettings>(cx, |settings| {
settings.lsp_document_colors = Some(DocumentColorsRenderMode::Border);
store.update_user_settings(cx, |settings| {
settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::Border);
});
});
});
@@ -3306,20 +3306,20 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
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 = Some(InlineBlameSettings {
enabled: false,
enabled: Some(false),
..Default::default()
});
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<ProjectSettings>(cx, |settings| {
settings.git.inline_blame = inline_blame_off_settings;
store.update_user_settings(cx, |settings| {
settings.git.get_or_insert_default().inline_blame = inline_blame_off_settings;
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<ProjectSettings>(cx, |settings| {
settings.git.inline_blame = inline_blame_off_settings;
store.update_user_settings(cx, |settings| {
settings.git.get_or_insert_default().inline_blame = inline_blame_off_settings;
});
});
});
+3 -3
View File
@@ -12,7 +12,6 @@ use gpui::{
VisualContext, VisualTestContext, point,
};
use language::Capability;
use project::WorktreeSettings;
use rpc::proto::PeerId;
use serde_json::json;
use settings::SettingsStore;
@@ -1710,8 +1709,9 @@ async fn test_following_into_excluded_file(
for cx in [&mut cx_a, &mut cx_b] {
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
settings.file_scan_exclusions = Some(vec!["**/.git".to_string()]);
store.update_user_settings(cx, |settings| {
settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string()]);
});
});
});
+39 -28
View File
@@ -22,9 +22,7 @@ use gpui::{
use language::{
Diagnostic, DiagnosticEntry, DiagnosticSourceKind, FakeLspAdapter, Language, LanguageConfig,
LanguageMatcher, LineEnding, OffsetRangeExt, Point, Rope,
language_settings::{
AllLanguageSettings, Formatter, FormatterList, PrettierSettings, SelectedFormatter,
},
language_settings::{Formatter, FormatterList, SelectedFormatter},
tree_sitter_rust, tree_sitter_typescript,
};
use lsp::{LanguageServerId, OneOf};
@@ -38,7 +36,7 @@ use project::{
use prompt_store::PromptBuilder;
use rand::prelude::*;
use serde_json::json;
use settings::SettingsStore;
use settings::{PrettierSettingsContent, SettingsStore};
use std::{
cell::{Cell, RefCell},
env, future, mem,
@@ -3507,10 +3505,14 @@ async fn test_local_settings(
assert_eq!(
store
.local_settings(worktree_b.read(cx).id())
.map(|(path, content)| (
path,
content.all_languages.defaults.tab_size.map(Into::into)
))
.collect::<Vec<_>>(),
&[
(Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
(Path::new("").into(), Some(2)),
(Path::new("a").into(), Some(8)),
]
)
});
@@ -3526,10 +3528,14 @@ async fn test_local_settings(
assert_eq!(
store
.local_settings(worktree_b.read(cx).id())
.map(|(path, content)| (
path,
content.all_languages.defaults.tab_size.map(Into::into)
))
.collect::<Vec<_>>(),
&[
(Path::new("").into(), r#"{}"#.to_string()),
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
(Path::new("").into(), None),
(Path::new("a").into(), Some(8)),
]
)
});
@@ -3555,10 +3561,14 @@ async fn test_local_settings(
assert_eq!(
store
.local_settings(worktree_b.read(cx).id())
.map(|(path, content)| (
path,
content.all_languages.defaults.tab_size.map(Into::into)
))
.collect::<Vec<_>>(),
&[
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
(Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
(Path::new("a").into(), Some(8)),
(Path::new("b").into(), Some(4)),
]
)
});
@@ -3587,8 +3597,9 @@ async fn test_local_settings(
assert_eq!(
store
.local_settings(worktree_b.read(cx).id())
.map(|(path, content)| (path, content.all_languages.defaults.hard_tabs))
.collect::<Vec<_>>(),
&[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
&[(Path::new("a").into(), Some(true))],
)
});
}
@@ -4598,15 +4609,15 @@ async fn test_formatting_buffer(
// host's configuration is honored as opposed to using the guest's settings.
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
file.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Single(
Formatter::External {
store.update_user_settings(cx, |file| {
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::List(
FormatterList::Single(Formatter::External {
command: "awk".into(),
arguments: Some(
vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
),
},
)));
}),
));
});
});
});
@@ -4694,24 +4705,24 @@ async fn test_prettier_formatting_buffer(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
file.defaults.formatter = Some(SelectedFormatter::Auto);
file.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
store.update_user_settings(cx, |file| {
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::Auto);
file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
allowed: Some(true),
..Default::default()
});
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
file.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Single(
Formatter::LanguageServer { name: None },
)));
file.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
store.update_user_settings(cx, |file| {
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::List(
FormatterList::Single(Formatter::LanguageServer { name: None }),
));
file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
allowed: Some(true),
..Default::default()
});
});
});
@@ -14,10 +14,7 @@ use gpui::{
use http_client::BlockedHttpClient;
use language::{
FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, LanguageRegistry,
language_settings::{
AllLanguageSettings, Formatter, FormatterList, PrettierSettings, SelectedFormatter,
language_settings,
},
language_settings::{Formatter, FormatterList, SelectedFormatter, language_settings},
tree_sitter_typescript,
};
use node_runtime::NodeRuntime;
@@ -30,7 +27,7 @@ use remote::RemoteClient;
use remote_server::{HeadlessAppState, HeadlessProject};
use rpc::proto;
use serde_json::json;
use settings::SettingsStore;
use settings::{PrettierSettingsContent, SettingsStore};
use std::{
path::Path,
sync::{Arc, atomic::AtomicUsize},
@@ -499,24 +496,24 @@ async fn test_ssh_collaboration_formatting_with_prettier(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
file.defaults.formatter = Some(SelectedFormatter::Auto);
file.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
store.update_user_settings(cx, |file| {
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::Auto);
file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
allowed: Some(true),
..Default::default()
});
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
file.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Single(
Formatter::LanguageServer { name: None },
)));
file.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
store.update_user_settings(cx, |file| {
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::List(
FormatterList::Single(Formatter::LanguageServer { name: None }),
));
file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
allowed: Some(true),
..Default::default()
});
});
});
@@ -556,11 +553,11 @@ async fn test_ssh_collaboration_formatting_with_prettier(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
file.defaults.formatter = Some(SelectedFormatter::Auto);
file.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
store.update_user_settings(cx, |file| {
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::Auto);
file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
allowed: Some(true),
..Default::default()
});
});
});
-1
View File
@@ -47,7 +47,6 @@ picker.workspace = true
project.workspace = true
release_channel.workspace = true
rpc.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
+3 -5
View File
@@ -2993,11 +2993,9 @@ impl Panel for CollabPanel {
_window: &mut Window,
cx: &mut Context<Self>,
) {
settings::update_settings_file::<CollaborationPanelSettings>(
self.fs.clone(),
cx,
move |settings, _| settings.dock = Some(position),
);
settings::update_settings_file(self.fs.clone(), cx, move |settings, _| {
settings.collaboration_panel.get_or_insert_default().dock = Some(position.into())
});
}
fn size(&self, _window: &Window, cx: &App) -> Pixels {
-2
View File
@@ -11,7 +11,6 @@ use gpui::{
App, Pixels, PlatformDisplay, Size, WindowBackgroundAppearance, WindowBounds,
WindowDecorations, WindowKind, WindowOptions, point,
};
use panel_settings::MessageEditorSettings;
pub use panel_settings::{CollaborationPanelSettings, NotificationPanelSettings};
use release_channel::ReleaseChannel;
use settings::Settings;
@@ -21,7 +20,6 @@ use workspace::AppState;
pub fn init(app_state: &Arc<AppState>, cx: &mut App) {
CollaborationPanelSettings::register(cx);
NotificationPanelSettings::register(cx);
MessageEditorSettings::register(cx);
channel_view::init(cx);
collab_panel::init(cx);
+3 -5
View File
@@ -621,11 +621,9 @@ impl Panel for NotificationPanel {
}
fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context<Self>) {
settings::update_settings_file::<NotificationPanelSettings>(
self.fs.clone(),
cx,
move |settings, _| settings.dock = Some(position),
);
settings::update_settings_file(self.fs.clone(), cx, move |settings, _| {
settings.notification_panel.get_or_insert_default().dock = Some(position.into())
});
}
fn size(&self, _: &Window, cx: &App) -> Pixels {
+45 -75
View File
@@ -1,102 +1,72 @@
use gpui::Pixels;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
use settings::Settings;
use ui::px;
use util::MergeFrom as _;
use workspace::dock::DockPosition;
#[derive(Deserialize, Debug)]
#[derive(Debug)]
pub struct CollaborationPanelSettings {
pub button: bool,
pub dock: DockPosition,
pub default_width: Pixels,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
#[settings_key(key = "collaboration_panel")]
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(Deserialize, Debug)]
#[derive(Debug)]
pub struct NotificationPanelSettings {
pub button: bool,
pub dock: DockPosition,
pub default_width: Pixels,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
#[settings_key(key = "notification_panel")]
pub struct NotificationPanelSettingsContent {
/// Whether to show the panel button in the status bar.
///
/// Default: true
pub button: Option<bool>,
/// Where to dock the panel.
///
/// Default: right
pub dock: Option<DockPosition>,
/// Default width of the panel in pixels.
///
/// Default: 300
pub default_width: Option<f32>,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
#[settings_key(key = "message_editor")]
pub struct MessageEditorSettings {
/// Whether to automatically replace emoji shortcodes with emoji characters.
/// For example: typing `:wave:` gets replaced with `👋`.
///
/// Default: false
pub auto_replace_emoji_shortcode: Option<bool>,
}
impl Settings for CollaborationPanelSettings {
type FileContent = PanelSettingsContent;
fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
let panel = content.collaboration_panel.as_ref().unwrap();
fn load(
sources: SettingsSources<Self::FileContent>,
_: &mut gpui::App,
) -> anyhow::Result<Self> {
sources.json_merge()
Self {
button: panel.button.unwrap(),
dock: panel.dock.unwrap().into(),
default_width: panel.default_width.map(px).unwrap(),
}
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut ui::App) {
if let Some(panel) = content.collaboration_panel.as_ref() {
self.button.merge_from(&panel.button);
self.default_width
.merge_from(&panel.default_width.map(Pixels::from));
self.dock.merge_from(&panel.dock.map(Into::into));
}
}
fn import_from_vscode(
_vscode: &settings::VsCodeSettings,
_content: &mut settings::SettingsContent,
) {
}
}
impl Settings for NotificationPanelSettings {
type FileContent = NotificationPanelSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,
_: &mut gpui::App,
) -> anyhow::Result<Self> {
sources.json_merge()
fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
let panel = content.notification_panel.as_ref().unwrap();
return Self {
button: panel.button.unwrap(),
dock: panel.dock.unwrap().into(),
default_width: panel.default_width.map(px).unwrap(),
};
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
}
impl Settings for MessageEditorSettings {
type FileContent = MessageEditorSettings;
fn load(
sources: SettingsSources<Self::FileContent>,
_: &mut gpui::App,
) -> anyhow::Result<Self> {
sources.json_merge()
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut ui::App) {
let Some(panel) = content.notification_panel.as_ref() else {
return;
};
self.button.merge_from(&panel.button);
self.dock.merge_from(&panel.dock.map(Into::into));
self.default_width.merge_from(&panel.default_width.map(px));
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
fn import_from_vscode(
_vscode: &settings::VsCodeSettings,
_current: &mut settings::SettingsContent,
) {
}
}
+2 -1
View File
@@ -25,8 +25,9 @@ net.workspace = true
parking_lot.workspace = true
postage.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
serde.workspace = true
settings.workspace = true
smol.workspace = true
tempfile.workspace = true
url = { workspace = true, features = ["serde"] }
+1 -30
View File
@@ -12,12 +12,9 @@ use std::{fmt::Display, path::PathBuf};
use anyhow::Result;
use client::Client;
use collections::HashMap;
use gpui::AsyncApp;
use parking_lot::RwLock;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use util::redact::should_redact;
pub use settings::ContextServerCommand;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ContextServerId(pub Arc<str>);
@@ -28,32 +25,6 @@ impl Display for ContextServerId {
}
}
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema)]
pub struct ContextServerCommand {
#[serde(rename = "command")]
pub path: PathBuf,
pub args: Vec<String>,
pub env: Option<HashMap<String, String>>,
/// Timeout for tool calls in milliseconds. Defaults to 60000 (60 seconds) if not specified.
pub timeout: Option<u64>,
}
impl std::fmt::Debug for ContextServerCommand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let filtered_env = self.env.as_ref().map(|env| {
env.iter()
.map(|(k, v)| (k, if should_redact(k) { "[REDACTED]" } else { v }))
.collect::<Vec<_>>()
});
f.debug_struct("ContextServerCommand")
.field("path", &self.path)
.field("args", &self.args)
.field("env", &filtered_env)
.finish()
}
}
enum ContextServerTransport {
Stdio(ContextServerCommand, Option<PathBuf>),
Custom(Arc<dyn crate::transport::Transport>),
@@ -281,14 +281,11 @@ mod tests {
use indoc::indoc;
use language::{
Point,
language_settings::{
AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings, LspInsertMode,
WordsCompletionMode,
},
language_settings::{CompletionSettingsContent, LspInsertMode, WordsCompletionMode},
};
use project::Project;
use serde_json::json;
use settings::SettingsStore;
use settings::{AllLanguageSettingsContent, SettingsStore};
use std::future::Future;
use util::{
path,
@@ -299,12 +296,11 @@ mod tests {
async fn test_copilot(executor: BackgroundExecutor, cx: &mut TestAppContext) {
// flaky
init_test(cx, |settings| {
settings.defaults.completions = Some(CompletionSettings {
words: WordsCompletionMode::Disabled,
words_min_length: 0,
lsp: true,
lsp_fetch_timeout_ms: 0,
lsp_insert_mode: LspInsertMode::Insert,
settings.defaults.completions = Some(CompletionSettingsContent {
words: Some(WordsCompletionMode::Disabled),
words_min_length: Some(0),
lsp_insert_mode: Some(LspInsertMode::Insert),
..Default::default()
});
});
@@ -532,12 +528,11 @@ mod tests {
) {
// flaky
init_test(cx, |settings| {
settings.defaults.completions = Some(CompletionSettings {
words: WordsCompletionMode::Disabled,
words_min_length: 0,
lsp: true,
lsp_fetch_timeout_ms: 0,
lsp_insert_mode: LspInsertMode::Insert,
settings.defaults.completions = Some(CompletionSettingsContent {
words: Some(WordsCompletionMode::Disabled),
words_min_length: Some(0),
lsp_insert_mode: Some(LspInsertMode::Insert),
..Default::default()
});
});
@@ -1128,7 +1123,7 @@ mod tests {
Project::init_settings(cx);
workspace::init_settings(cx);
SettingsStore::update_global(cx, |store: &mut SettingsStore, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, f);
store.update_user_settings(cx, |settings| f(&mut settings.project.all_languages));
});
});
}
+46 -40
View File
@@ -1,28 +1,12 @@
use dap_types::SteppingGranularity;
use gpui::{App, Global};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
use gpui::App;
use settings::{Settings, SettingsContent};
use util::MergeFrom;
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, SettingsUi)]
#[serde(rename_all = "snake_case")]
pub enum DebugPanelDockPosition {
Left,
Bottom,
Right,
}
#[derive(Serialize, Deserialize, JsonSchema, Clone, SettingsUi, SettingsKey)]
#[serde(default)]
// todo(settings_ui) @ben: I'm pretty sure not having the fields be optional here is a bug,
// it means the defaults will override previously set values if a single key is missing
#[settings_ui(group = "Debugger")]
#[settings_key(key = "debugger")]
pub struct DebuggerSettings {
/// Determines the stepping granularity.
///
/// Default: line
#[settings_ui(skip)]
pub stepping_granularity: SteppingGranularity,
/// Whether the breakpoints should be reused across Zed sessions.
///
@@ -47,31 +31,53 @@ pub struct DebuggerSettings {
/// The dock position of the debug panel
///
/// Default: Bottom
pub dock: DebugPanelDockPosition,
}
impl Default for DebuggerSettings {
fn default() -> Self {
Self {
button: true,
save_breakpoints: true,
stepping_granularity: SteppingGranularity::Line,
timeout: 2000,
log_dap_communications: true,
format_dap_log_messages: true,
dock: DebugPanelDockPosition::Bottom,
}
}
pub dock: settings::DockPosition,
}
impl Settings for DebuggerSettings {
type FileContent = Self;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
sources.json_merge()
fn from_defaults(content: &SettingsContent, _cx: &mut App) -> Self {
let content = content.debugger.clone().unwrap();
Self {
stepping_granularity: dap_granularity_from_settings(
content.stepping_granularity.unwrap(),
),
save_breakpoints: content.save_breakpoints.unwrap(),
button: content.button.unwrap(),
timeout: content.timeout.unwrap(),
log_dap_communications: content.log_dap_communications.unwrap(),
format_dap_log_messages: content.format_dap_log_messages.unwrap(),
dock: content.dock.unwrap(),
}
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
fn refine(&mut self, content: &SettingsContent, _cx: &mut App) {
let Some(content) = &content.debugger else {
return;
};
self.stepping_granularity.merge_from(
&content
.stepping_granularity
.map(dap_granularity_from_settings),
);
self.save_breakpoints.merge_from(&content.save_breakpoints);
self.button.merge_from(&content.button);
self.timeout.merge_from(&content.timeout);
self.log_dap_communications
.merge_from(&content.log_dap_communications);
self.format_dap_log_messages
.merge_from(&content.format_dap_log_messages);
self.dock.merge_from(&content.dock);
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut SettingsContent) {}
}
impl Global for DebuggerSettings {}
fn dap_granularity_from_settings(
granularity: settings::SteppingGranularity,
) -> dap_types::SteppingGranularity {
match granularity {
settings::SteppingGranularity::Instruction => dap_types::SteppingGranularity::Instruction,
settings::SteppingGranularity::Line => dap_types::SteppingGranularity::Line,
settings::SteppingGranularity::Statement => dap_types::SteppingGranularity::Statement,
}
}
+4 -18
View File
@@ -12,7 +12,6 @@ use crate::{
use anyhow::{Context as _, Result, anyhow};
use collections::IndexMap;
use dap::adapters::DebugAdapterName;
use dap::debugger_settings::DebugPanelDockPosition;
use dap::{DapRegistry, StartDebuggingRequestArguments};
use dap::{client::SessionId, debugger_settings::DebuggerSettings};
use editor::Editor;
@@ -1400,11 +1399,7 @@ impl Panel for DebugPanel {
}
fn position(&self, _window: &Window, cx: &App) -> DockPosition {
match DebuggerSettings::get_global(cx).dock {
DebugPanelDockPosition::Left => DockPosition::Left,
DebugPanelDockPosition::Bottom => DockPosition::Bottom,
DebugPanelDockPosition::Right => DockPosition::Right,
}
DebuggerSettings::get_global(cx).dock.into()
}
fn position_is_valid(&self, _: DockPosition) -> bool {
@@ -1426,18 +1421,9 @@ impl Panel for DebugPanel {
});
}
settings::update_settings_file::<DebuggerSettings>(
self.fs.clone(),
cx,
move |settings, _| {
let dock = match position {
DockPosition::Left => DebugPanelDockPosition::Left,
DockPosition::Bottom => DebugPanelDockPosition::Bottom,
DockPosition::Right => DebugPanelDockPosition::Right,
};
settings.dock = dock;
},
);
settings::update_settings_file(self.fs.clone(), cx, move |settings, _| {
settings.debugger.get_or_insert_default().dock = Some(position.into());
});
}
fn size(&self, _window: &Window, _: &App) -> Pixels {
@@ -910,8 +910,10 @@ async fn open_disabled_globs_setting_in_editor(
let settings = cx.global::<SettingsStore>();
// Ensure that we always have "edit_predictions { "disabled_globs": [] }"
let edits = settings.edits_for_update::<AllLanguageSettings>(&text, |file| {
file.edit_predictions
let edits = settings.edits_for_update(&text, |file| {
file.project
.all_languages
.edit_predictions
.get_or_insert_with(Default::default)
.disabled_globs
.get_or_insert_with(Vec::new);
@@ -948,9 +950,12 @@ async fn open_disabled_globs_setting_in_editor(
}
fn set_completion_provider(fs: Arc<dyn Fs>, cx: &mut App, provider: EditPredictionProvider) {
update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
file.features
.get_or_insert(Default::default())
update_settings_file(fs, cx, move |settings, _| {
settings
.project
.all_languages
.features
.get_or_insert_default()
.edit_prediction_provider = Some(provider);
});
}
@@ -962,18 +967,24 @@ fn toggle_show_edit_predictions_for_language(
) {
let show_edit_predictions =
all_language_settings(None, cx).show_edit_predictions(Some(&language), cx);
update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
file.languages
update_settings_file(fs, cx, move |settings, _| {
settings
.project
.all_languages
.languages
.0
.entry(language.name())
.entry(language.name().0)
.or_default()
.show_edit_predictions = Some(!show_edit_predictions);
});
}
fn hide_copilot(fs: Arc<dyn Fs>, cx: &mut App) {
update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
file.features
update_settings_file(fs, cx, move |settings, _| {
settings
.project
.all_languages
.features
.get_or_insert(Default::default())
.edit_prediction_provider = Some(EditPredictionProvider::None);
});
@@ -984,13 +995,14 @@ fn toggle_edit_prediction_mode(fs: Arc<dyn Fs>, mode: EditPredictionsMode, cx: &
let current_mode = settings.edit_predictions_mode();
if current_mode != mode {
update_settings_file::<AllLanguageSettings>(fs, cx, move |settings, _cx| {
if let Some(edit_predictions) = settings.edit_predictions.as_mut() {
edit_predictions.mode = mode;
update_settings_file(fs, cx, move |settings, _cx| {
if let Some(edit_predictions) = settings.project.all_languages.edit_predictions.as_mut()
{
edit_predictions.mode = Some(mode);
} else {
settings.edit_predictions =
Some(language_settings::EditPredictionSettingsContent {
mode,
settings.project.all_languages.edit_predictions =
Some(settings::EditPredictionSettingsContent {
mode: Some(mode),
..Default::default()
});
}
+2 -1
View File
@@ -1,9 +1,10 @@
use crate::{code_context_menus::CompletionsMenu, editor_settings::SnippetSortOrder};
use crate::code_context_menus::CompletionsMenu;
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::TestAppContext;
use language::CodeLabel;
use lsp::{CompletionItem, CompletionItemKind, LanguageServerId};
use project::{Completion, CompletionSource};
use settings::SnippetSortOrder;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use text::Anchor;
+1 -1
View File
@@ -32,7 +32,6 @@ use ui::{Color, IntoElement, ListItem, Pixels, Popover, Styled, prelude::*};
use util::ResultExt;
use crate::CodeActionSource;
use crate::editor_settings::SnippetSortOrder;
use crate::hover_popover::{hover_markdown_style, open_markdown_url};
use crate::{
CodeActionProvider, CompletionId, CompletionItemKind, CompletionProvider, DisplayRow, Editor,
@@ -40,6 +39,7 @@ use crate::{
actions::{ConfirmCodeAction, ConfirmCompletion},
split_words, styled_runs_for_code_label,
};
use settings::SnippetSortOrder;
pub const MENU_GAP: Pixels = px(4.);
pub const MENU_ASIDE_X_PADDING: Pixels = px(16.);
+14 -8
View File
@@ -1529,12 +1529,11 @@ pub mod tests {
use language::{
Buffer, Diagnostic, DiagnosticEntry, DiagnosticSet, Language, LanguageConfig,
LanguageMatcher,
language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
};
use lsp::LanguageServerId;
use project::Project;
use rand::{Rng, prelude::*};
use settings::SettingsStore;
use settings::{SettingsContent, SettingsStore};
use smol::stream::StreamExt;
use std::{env, sync::Arc};
use text::PointUtf16;
@@ -1564,7 +1563,9 @@ pub mod tests {
log::info!("wrap width: {:?}", wrap_width);
cx.update(|cx| {
init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size));
init_test(cx, |s| {
s.project.all_languages.defaults.tab_size = NonZeroU32::new(tab_size)
});
});
let buffer = cx.update(|cx| {
@@ -1623,8 +1624,9 @@ pub mod tests {
log::info!("setting tab size to {:?}", tab_size);
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
s.defaults.tab_size = NonZeroU32::new(tab_size);
store.update_user_settings(cx, |s| {
s.project.all_languages.defaults.tab_size =
NonZeroU32::new(tab_size);
});
});
});
@@ -2084,7 +2086,11 @@ pub mod tests {
);
language.set_theme(&theme);
cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
cx.update(|cx| {
init_test(cx, |s| {
s.project.all_languages.defaults.tab_size = Some(2.try_into().unwrap())
})
});
let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
@@ -2910,7 +2916,7 @@ pub mod tests {
chunks
}
fn init_test(cx: &mut App, f: impl Fn(&mut AllLanguageSettingsContent)) {
fn init_test(cx: &mut App, f: impl Fn(&mut SettingsContent)) {
let settings = SettingsStore::test(cx);
cx.set_global(settings);
workspace::init_settings(cx);
@@ -2919,7 +2925,7 @@ pub mod tests {
Project::init_settings(cx);
theme::init(LoadThemes::JustBase, cx);
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, f);
store.update_user_settings(cx, f);
});
}
}
+13 -13
View File
@@ -160,9 +160,7 @@ use project::{
},
git_store::{GitStoreEvent, RepositoryEvent},
lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
project_settings::{
DiagnosticSeverity, GitGutterSetting, GoToDiagnosticSeverityFilter, ProjectSettings,
},
project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
};
use rand::seq::SliceRandom;
use rpc::{ErrorCode, ErrorExt, proto::PeerId};
@@ -171,7 +169,7 @@ use selections_collection::{
MutableSelectionsCollection, SelectionsCollection, resolve_selections,
};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsLocation, SettingsStore, update_settings_file};
use settings::{GitGutterSetting, Settings, SettingsLocation, SettingsStore, update_settings_file};
use smallvec::{SmallVec, smallvec};
use snippet::Snippet;
use std::{
@@ -2189,7 +2187,7 @@ impl Editor {
show_selection_menu: None,
show_git_blame_inline_delay_task: None,
git_blame_inline_enabled: full_mode
&& ProjectSettings::get_global(cx).git.inline_blame_enabled(),
&& ProjectSettings::get_global(cx).git.inline_blame.enabled,
render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
serialize_dirty_buffers: !is_minimap
&& ProjectSettings::get_global(cx)
@@ -5591,8 +5589,9 @@ impl Editor {
.language_at(buffer_position)
.map(|language| language.name());
let completion_settings =
language_settings(language.clone(), buffer_snapshot.file(), cx).completions;
let completion_settings = language_settings(language.clone(), buffer_snapshot.file(), cx)
.completions
.clone();
let show_completion_documentation = buffer_snapshot
.settings_at(buffer_position, cx)
@@ -19008,8 +19007,8 @@ 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 = Some(!current_show);
update_settings_file(fs, cx, move |setting, _| {
setting.tab_bar.get_or_insert_default().show = Some(!current_show);
});
}
@@ -20723,7 +20722,7 @@ impl Editor {
if self.mode.is_full() {
let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
let inline_blame_enabled = project_settings.git.inline_blame_enabled();
let inline_blame_enabled = project_settings.git.inline_blame.enabled;
if self.show_inline_diagnostics != show_inline_diagnostics {
self.show_inline_diagnostics = show_inline_diagnostics;
self.refresh_inline_diagnostics(false, window, cx);
@@ -21679,11 +21678,12 @@ impl Editor {
}
}
// todo(settings_refactor) this should not be!
fn vim_enabled(cx: &App) -> bool {
cx.global::<SettingsStore>()
.raw_user_settings()
.get("vim_mode")
== Some(&serde_json::Value::Bool(true))
.and_then(|settings| settings.content.vim_mode)
== Some(true)
}
fn process_completion_for_edit(
@@ -23123,7 +23123,7 @@ impl EditorSnapshot {
let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
matches!(
ProjectSettings::get_global(cx).git.git_gutter,
Some(GitGutterSetting::TrackedFiles)
GitGutterSetting::TrackedFiles
)
});
let gutter_settings = EditorSettings::get_global(cx).gutter;
File diff suppressed because it is too large Load Diff
+23 -66
View File
@@ -1,8 +1,8 @@
use std::sync::Arc;
use gpui::{App, FontFeatures, FontWeight};
use project::project_settings::{InlineBlameSettings, ProjectSettings};
use settings::{EditableSettingControl, Settings};
use project::project_settings::ProjectSettings;
use settings::{EditableSettingControl, Settings, SettingsContent};
use theme::{FontFamilyCache, FontFamilyName, ThemeSettings};
use ui::{
CheckboxWithLabel, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer, SettingsGroup,
@@ -59,7 +59,6 @@ struct BufferFontFamilyControl;
impl EditableSettingControl for BufferFontFamilyControl {
type Value = SharedString;
type Settings = ThemeSettings;
fn name(&self) -> SharedString {
"Buffer Font Family".into()
@@ -70,12 +69,8 @@ impl EditableSettingControl for BufferFontFamilyControl {
settings.buffer_font.family.clone()
}
fn apply(
settings: &mut <Self::Settings as Settings>::FileContent,
value: Self::Value,
_cx: &App,
) {
settings.buffer_font_family = Some(FontFamilyName(value.into()));
fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) {
settings.theme.buffer_font_family = Some(FontFamilyName(value.into()));
}
}
@@ -118,7 +113,6 @@ struct BufferFontSizeControl;
impl EditableSettingControl for BufferFontSizeControl {
type Value = Pixels;
type Settings = ThemeSettings;
fn name(&self) -> SharedString {
"Buffer Font Size".into()
@@ -128,12 +122,8 @@ impl EditableSettingControl for BufferFontSizeControl {
ThemeSettings::get_global(cx).buffer_font_size(cx)
}
fn apply(
settings: &mut <Self::Settings as Settings>::FileContent,
value: Self::Value,
_cx: &App,
) {
settings.buffer_font_size = Some(value.into());
fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) {
settings.theme.buffer_font_size = Some(value.into());
}
}
@@ -162,7 +152,6 @@ struct BufferFontWeightControl;
impl EditableSettingControl for BufferFontWeightControl {
type Value = FontWeight;
type Settings = ThemeSettings;
fn name(&self) -> SharedString {
"Buffer Font Weight".into()
@@ -173,12 +162,8 @@ impl EditableSettingControl for BufferFontWeightControl {
settings.buffer_font.weight
}
fn apply(
settings: &mut <Self::Settings as Settings>::FileContent,
value: Self::Value,
_cx: &App,
) {
settings.buffer_font_weight = Some(value.0);
fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) {
settings.theme.buffer_font_weight = Some(value.0);
}
}
@@ -215,7 +200,6 @@ struct BufferFontLigaturesControl;
impl EditableSettingControl for BufferFontLigaturesControl {
type Value = bool;
type Settings = ThemeSettings;
fn name(&self) -> SharedString {
"Buffer Font Ligatures".into()
@@ -230,14 +214,11 @@ impl EditableSettingControl for BufferFontLigaturesControl {
.unwrap_or(true)
}
fn apply(
settings: &mut <Self::Settings as Settings>::FileContent,
value: Self::Value,
_cx: &App,
) {
fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) {
let value = if value { 1 } else { 0 };
let mut features = settings
.theme
.buffer_font_features
.as_ref()
.map(|features| features.tag_value_list().to_vec())
@@ -249,7 +230,7 @@ impl EditableSettingControl for BufferFontLigaturesControl {
features.push(("calt".into(), value));
}
settings.buffer_font_features = Some(FontFeatures(Arc::new(features)));
settings.theme.buffer_font_features = Some(FontFeatures(Arc::new(features)));
}
}
@@ -279,7 +260,6 @@ struct InlineGitBlameControl;
impl EditableSettingControl for InlineGitBlameControl {
type Value = bool;
type Settings = ProjectSettings;
fn name(&self) -> SharedString {
"Inline Git Blame".into()
@@ -287,22 +267,16 @@ impl EditableSettingControl for InlineGitBlameControl {
fn read(cx: &App) -> Self::Value {
let settings = ProjectSettings::get_global(cx);
settings.git.inline_blame_enabled()
settings.git.inline_blame.enabled
}
fn apply(
settings: &mut <Self::Settings as Settings>::FileContent,
value: Self::Value,
_cx: &App,
) {
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()
});
}
fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) {
settings
.git
.get_or_insert_default()
.inline_blame
.get_or_insert_default()
.enabled = Some(value)
}
}
@@ -332,7 +306,6 @@ struct LineNumbersControl;
impl EditableSettingControl for LineNumbersControl {
type Value = bool;
type Settings = EditorSettings;
fn name(&self) -> SharedString {
"Line Numbers".into()
@@ -343,19 +316,8 @@ impl EditableSettingControl for LineNumbersControl {
settings.gutter.line_numbers
}
fn apply(
settings: &mut <Self::Settings as Settings>::FileContent,
value: Self::Value,
_cx: &App,
) {
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()
});
}
fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) {
settings.editor.gutter.get_or_insert_default().line_numbers = Some(value);
}
}
@@ -385,7 +347,6 @@ struct RelativeLineNumbersControl;
impl EditableSettingControl for RelativeLineNumbersControl {
type Value = bool;
type Settings = EditorSettings;
fn name(&self) -> SharedString {
"Relative Line Numbers".into()
@@ -396,12 +357,8 @@ impl EditableSettingControl for RelativeLineNumbersControl {
settings.relative_line_numbers
}
fn apply(
settings: &mut <Self::Settings as Settings>::FileContent,
value: Self::Value,
_cx: &App,
) {
settings.relative_line_numbers = Some(value);
fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) {
settings.editor.relative_line_numbers = Some(value);
}
}
+81 -94
View File
@@ -22,15 +22,15 @@ use indoc::indoc;
use language::{
BracketPairConfig,
Capability::ReadWrite,
DiagnosticSourceKind, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
LanguageName, Override, Point,
DiagnosticSourceKind, FakeLspAdapter, IndentGuideSettings, LanguageConfig,
LanguageConfigOverride, LanguageMatcher, LanguageName, Override, Point,
language_settings::{
AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings, FormatterList,
LanguageSettingsContent, LspInsertMode, PrettierSettings, SelectedFormatter,
CompletionSettingsContent, FormatterList, LanguageSettingsContent, LspInsertMode,
SelectedFormatter,
},
tree_sitter_python,
};
use language_settings::{Formatter, IndentGuideSettings};
use language_settings::Formatter;
use lsp::CompletionParams;
use multi_buffer::{IndentGuide, PathKey};
use parking_lot::Mutex;
@@ -38,9 +38,13 @@ use pretty_assertions::{assert_eq, assert_ne};
use project::{
FakeFs,
debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
project_settings::{LspSettings, ProjectSettings},
project_settings::LspSettings,
};
use serde_json::{self, json};
use settings::{
AllLanguageSettingsContent, IndentGuideBackgroundColoring, IndentGuideColoring,
ProjectSettingsContent,
};
use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
use std::{
iter,
@@ -11699,10 +11703,7 @@ async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
update_test_language_settings(cx, |settings| {
// Enable Prettier formatting for the same buffer, and ensure
// LSP is called instead of Prettier.
settings.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
});
settings.defaults.prettier.get_or_insert_default().allowed = Some(true);
});
let mut fake_servers = language_registry.register_fake_lsp(
"Rust",
@@ -12088,10 +12089,7 @@ async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
)));
update_test_language_settings(cx, |settings| {
settings.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
});
settings.defaults.prettier.get_or_insert_default().allowed = Some(true);
});
let mut fake_servers = language_registry.register_fake_lsp(
"TypeScript",
@@ -12402,8 +12400,8 @@ 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 = Some(true);
settings.update_user_settings(cx, |settings| {
settings.editor.auto_signature_help = Some(true);
});
});
});
@@ -12542,9 +12540,9 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestA
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = Some(false);
settings.show_signature_help_after_edits = Some(false);
settings.update_user_settings(cx, |settings| {
settings.editor.auto_signature_help = Some(false);
settings.editor.show_signature_help_after_edits = Some(false);
});
});
});
@@ -12669,9 +12667,9 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestA
// Ensure that signature_help is called when enabled afte edits
cx.update(|_, cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = Some(false);
settings.show_signature_help_after_edits = Some(true);
settings.update_user_settings(cx, |settings| {
settings.editor.auto_signature_help = Some(false);
settings.editor.show_signature_help_after_edits = Some(true);
});
});
});
@@ -12711,9 +12709,9 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestA
// Ensure that signature_help is called when auto signature help override is enabled
cx.update(|_, cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = Some(true);
settings.show_signature_help_after_edits = Some(false);
settings.update_user_settings(cx, |settings| {
settings.editor.auto_signature_help = Some(true);
settings.editor.show_signature_help_after_edits = Some(false);
});
});
});
@@ -12755,8 +12753,8 @@ async fn test_signature_help(cx: &mut TestAppContext) {
init_test(cx, |_| {});
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = Some(true);
settings.update_user_settings(cx, |settings| {
settings.editor.auto_signature_help = Some(true);
});
});
});
@@ -13346,12 +13344,11 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
);
update_test_language_settings(&mut cx, |settings| {
settings.defaults.completions = Some(CompletionSettings {
lsp_insert_mode,
words: WordsCompletionMode::Disabled,
words_min_length: 0,
lsp: true,
lsp_fetch_timeout_ms: 0,
settings.defaults.completions = Some(CompletionSettingsContent {
lsp_insert_mode: Some(lsp_insert_mode),
words: Some(WordsCompletionMode::Disabled),
words_min_length: Some(0),
..Default::default()
});
});
@@ -13406,13 +13403,12 @@ async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext)
let expected_with_replace_mode = "SubscriptionErrorˇ";
update_test_language_settings(&mut cx, |settings| {
settings.defaults.completions = Some(CompletionSettings {
words: WordsCompletionMode::Disabled,
words_min_length: 0,
settings.defaults.completions = Some(CompletionSettingsContent {
words: Some(WordsCompletionMode::Disabled),
words_min_length: Some(0),
// set the opposite here to ensure that the action is overriding the default behavior
lsp_insert_mode: LspInsertMode::Insert,
lsp: true,
lsp_fetch_timeout_ms: 0,
lsp_insert_mode: Some(LspInsertMode::Insert),
..Default::default()
});
});
@@ -13443,13 +13439,12 @@ async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext)
apply_additional_edits.await.unwrap();
update_test_language_settings(&mut cx, |settings| {
settings.defaults.completions = Some(CompletionSettings {
words: WordsCompletionMode::Disabled,
words_min_length: 0,
settings.defaults.completions = Some(CompletionSettingsContent {
words: Some(WordsCompletionMode::Disabled),
words_min_length: Some(0),
// set the opposite here to ensure that the action is overriding the default behavior
lsp_insert_mode: LspInsertMode::Replace,
lsp: true,
lsp_fetch_timeout_ms: 0,
lsp_insert_mode: Some(LspInsertMode::Replace),
..Default::default()
});
});
@@ -14185,12 +14180,11 @@ async fn test_completion_reuse(cx: &mut TestAppContext) {
async fn test_word_completion(cx: &mut TestAppContext) {
let lsp_fetch_timeout_ms = 10;
init_test(cx, |language_settings| {
language_settings.defaults.completions = Some(CompletionSettings {
words: WordsCompletionMode::Fallback,
words_min_length: 0,
lsp: true,
lsp_fetch_timeout_ms: 10,
lsp_insert_mode: LspInsertMode::Insert,
language_settings.defaults.completions = Some(CompletionSettingsContent {
words_min_length: Some(0),
lsp_fetch_timeout_ms: Some(10),
lsp_insert_mode: Some(LspInsertMode::Insert),
..Default::default()
});
});
@@ -14282,12 +14276,11 @@ async fn test_word_completion(cx: &mut TestAppContext) {
#[gpui::test]
async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
init_test(cx, |language_settings| {
language_settings.defaults.completions = Some(CompletionSettings {
words: WordsCompletionMode::Enabled,
words_min_length: 0,
lsp: true,
lsp_fetch_timeout_ms: 0,
lsp_insert_mode: LspInsertMode::Insert,
language_settings.defaults.completions = Some(CompletionSettingsContent {
words: Some(WordsCompletionMode::Enabled),
words_min_length: Some(0),
lsp_insert_mode: Some(LspInsertMode::Insert),
..Default::default()
});
});
@@ -14346,12 +14339,11 @@ async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext
#[gpui::test]
async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
init_test(cx, |language_settings| {
language_settings.defaults.completions = Some(CompletionSettings {
words: WordsCompletionMode::Disabled,
words_min_length: 0,
lsp: true,
lsp_fetch_timeout_ms: 0,
lsp_insert_mode: LspInsertMode::Insert,
language_settings.defaults.completions = Some(CompletionSettingsContent {
words: Some(WordsCompletionMode::Disabled),
words_min_length: Some(0),
lsp_insert_mode: Some(LspInsertMode::Insert),
..Default::default()
});
});
@@ -14420,12 +14412,11 @@ async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
#[gpui::test]
async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
init_test(cx, |language_settings| {
language_settings.defaults.completions = Some(CompletionSettings {
words: WordsCompletionMode::Fallback,
words_min_length: 0,
lsp: false,
lsp_fetch_timeout_ms: 0,
lsp_insert_mode: LspInsertMode::Insert,
language_settings.defaults.completions = Some(CompletionSettingsContent {
words_min_length: Some(0),
lsp: Some(false),
lsp_insert_mode: Some(LspInsertMode::Insert),
..Default::default()
});
});
@@ -14483,12 +14474,11 @@ async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
#[gpui::test]
async fn test_word_completions_do_not_show_before_threshold(cx: &mut TestAppContext) {
init_test(cx, |language_settings| {
language_settings.defaults.completions = Some(CompletionSettings {
words: WordsCompletionMode::Enabled,
words_min_length: 3,
lsp: true,
lsp_fetch_timeout_ms: 0,
lsp_insert_mode: LspInsertMode::Insert,
language_settings.defaults.completions = Some(CompletionSettingsContent {
words: Some(WordsCompletionMode::Enabled),
words_min_length: Some(3),
lsp_insert_mode: Some(LspInsertMode::Insert),
..Default::default()
});
});
@@ -14553,12 +14543,11 @@ async fn test_word_completions_do_not_show_before_threshold(cx: &mut TestAppCont
#[gpui::test]
async fn test_word_completions_disabled(cx: &mut TestAppContext) {
init_test(cx, |language_settings| {
language_settings.defaults.completions = Some(CompletionSettings {
words: WordsCompletionMode::Enabled,
words_min_length: 0,
lsp: true,
lsp_fetch_timeout_ms: 0,
lsp_insert_mode: LspInsertMode::Insert,
language_settings.defaults.completions = Some(CompletionSettingsContent {
words: Some(WordsCompletionMode::Enabled),
words_min_length: Some(0),
lsp_insert_mode: Some(LspInsertMode::Insert),
..Default::default()
});
});
@@ -17067,7 +17056,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppCon
let _fake_server = fake_servers.next().await.unwrap();
update_test_language_settings(cx, |language_settings| {
language_settings.languages.0.insert(
language_name.clone(),
language_name.clone().0,
LanguageSettingsContent {
tab_size: NonZeroU32::new(8),
..Default::default()
@@ -17991,10 +17980,7 @@ async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
)));
update_test_language_settings(cx, |settings| {
settings.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
});
settings.defaults.prettier.get_or_insert_default().allowed = Some(true);
});
let test_plugin = "test_plugin";
@@ -19982,7 +19968,8 @@ fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -
enabled: true,
line_width: 1,
active_line_width: 1,
..Default::default()
coloring: IndentGuideColoring::default(),
background_coloring: IndentGuideBackgroundColoring::default(),
},
}
}
@@ -23672,8 +23659,8 @@ println!("5");
});
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<WorkspaceSettings>(cx, |s| {
s.restore_on_file_reopen = Some(false);
store.update_user_settings(cx, |s| {
s.workspace.restore_on_file_reopen = Some(false);
});
});
editor.update_in(cx, |editor, window, cx| {
@@ -23697,8 +23684,8 @@ println!("5");
assert!(pane.active_item().is_none());
});
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<WorkspaceSettings>(cx, |s| {
s.restore_on_file_reopen = Some(true);
store.update_user_settings(cx, |s| {
s.workspace.restore_on_file_reopen = Some(true);
});
});
@@ -25120,18 +25107,18 @@ pub(crate) fn update_test_language_settings(
) {
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, f);
store.update_user_settings(cx, |settings| f(&mut settings.project.all_languages));
});
});
}
pub(crate) fn update_test_project_settings(
cx: &mut TestAppContext,
f: impl Fn(&mut ProjectSettings),
f: impl Fn(&mut ProjectSettingsContent),
) {
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<ProjectSettings>(cx, f);
store.update_user_settings(cx, |settings| f(&mut settings.project));
});
});
}
+21 -33
View File
@@ -51,9 +51,7 @@ use gpui::{
transparent_black,
};
use itertools::Itertools;
use language::language_settings::{
IndentGuideBackgroundColoring, IndentGuideColoring, IndentGuideSettings, ShowWhitespaceSetting,
};
use language::{IndentGuideSettings, language_settings::ShowWhitespaceSetting};
use markdown::Markdown;
use multi_buffer::{
Anchor, ExcerptId, ExcerptInfo, ExpandExcerptDirection, ExpandInfo, MultiBufferPoint,
@@ -63,9 +61,12 @@ use multi_buffer::{
use project::{
Entry, ProjectPath,
debugger::breakpoint_store::{Breakpoint, BreakpointSessionState},
project_settings::{GitGutterSetting, GitHunkStyleSetting, ProjectSettings},
project_settings::ProjectSettings,
};
use settings::{
GitGutterSetting, GitHunkStyleSetting, IndentGuideBackgroundColoring, IndentGuideColoring,
Settings,
};
use settings::Settings;
use smallvec::{SmallVec, smallvec};
use std::{
any::TypeId,
@@ -2099,10 +2100,7 @@ impl EditorElement {
.display_diff_hunks_for_rows(display_rows, folded_buffers)
.map(|hunk| (hunk, None))
.collect::<Vec<_>>();
let git_gutter_setting = ProjectSettings::get_global(cx)
.git
.git_gutter
.unwrap_or_default();
let git_gutter_setting = ProjectSettings::get_global(cx).git.git_gutter;
if let GitGutterSetting::TrackedFiles = git_gutter_setting {
for (hunk, hitbox) in &mut display_hunks {
if matches!(hunk, DisplayDiffHunk::Unfolded { .. }) {
@@ -2454,11 +2452,7 @@ impl EditorElement {
let padding = {
const INLINE_ACCEPT_SUGGESTION_EM_WIDTHS: f32 = 14.;
let mut padding = ProjectSettings::get_global(cx)
.git
.inline_blame
.unwrap_or_default()
.padding as f32;
let mut padding = ProjectSettings::get_global(cx).git.inline_blame.padding as f32;
if let Some(edit_prediction) = editor.active_edit_prediction.as_ref()
&& let EditPrediction::Edit {
@@ -2492,12 +2486,10 @@ impl EditorElement {
let padded_line_end = line_end + padding;
let min_column_in_pixels = ProjectSettings::get_global(cx)
.git
.inline_blame
.map(|settings| settings.min_column)
.map(|col| self.column_pixels(col as usize, window))
.unwrap_or(px(0.));
let min_column_in_pixels = self.column_pixels(
ProjectSettings::get_global(cx).git.inline_blame.min_column as usize,
window,
);
let min_start = content_origin.x - scroll_pixel_position.x + min_column_in_pixels;
cmp::max(padded_line_end, min_start)
@@ -5673,7 +5665,7 @@ impl EditorElement {
for indent_guide in indent_guides {
let indent_accent_colors = cx.theme().accents().color_for_index(indent_guide.depth);
let settings = indent_guide.settings;
let settings = &indent_guide.settings;
// TODO fixed for now, expose them through themes later
const INDENT_AWARE_ALPHA: f32 = 0.2;
@@ -6008,7 +6000,7 @@ impl EditorElement {
.unwrap_or_else(|| {
matches!(
ProjectSettings::get_global(cx).git.git_gutter,
Some(GitGutterSetting::TrackedFiles)
GitGutterSetting::TrackedFiles
)
});
if show_git_gutter {
@@ -7303,10 +7295,10 @@ impl EditorElement {
fn diff_hunk_hollow(status: DiffHunkStatus, cx: &mut App) -> bool {
let unstaged = status.has_secondary_hunk();
let unstaged_hollow = ProjectSettings::get_global(cx)
.git
.hunk_style
.is_some_and(|style| matches!(style, GitHunkStyleSetting::UnstagedHollow));
let unstaged_hollow = matches!(
ProjectSettings::get_global(cx).git.hunk_style,
GitHunkStyleSetting::UnstagedHollow
);
unstaged == unstaged_hollow
}
@@ -8843,13 +8835,9 @@ impl Element for EditorElement {
})
.flatten()?;
let mut element = render_inline_blame_entry(blame_entry, style, cx)?;
let inline_blame_padding = ProjectSettings::get_global(cx)
.git
.inline_blame
.unwrap_or_default()
.padding
as f32
* em_advance;
let inline_blame_padding =
ProjectSettings::get_global(cx).git.inline_blame.padding as f32
* em_advance;
Some(
element
.layout_as_root(AvailableSpace::min_size(), window, cx)
+10 -10
View File
@@ -931,8 +931,8 @@ mod tests {
use futures::StreamExt;
use gpui::Modifiers;
use indoc::indoc;
use language::language_settings::InlayHintSettings;
use lsp::request::{GotoDefinition, GotoTypeDefinition};
use settings::InlayHintSettingsContent;
use util::{assert_set_eq, path};
use workspace::item::Item;
@@ -1280,15 +1280,15 @@ mod tests {
#[gpui::test]
async fn test_inlay_hover_links(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true,
show_value_hints: false,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
enabled: Some(true),
show_value_hints: Some(false),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
+10 -10
View File
@@ -1004,8 +1004,8 @@ mod tests {
use collections::BTreeSet;
use gpui::App;
use indoc::indoc;
use language::language_settings::InlayHintSettings;
use markdown::parser::MarkdownEvent;
use settings::InlayHintSettingsContent;
use smol::stream::StreamExt;
use std::sync::atomic;
use std::sync::atomic::AtomicUsize;
@@ -1551,15 +1551,15 @@ mod tests {
#[gpui::test]
async fn test_hover_inlay_label_parts(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
+166 -151
View File
@@ -20,12 +20,14 @@ use anyhow::Context as _;
use clock::Global;
use futures::future;
use gpui::{AppContext as _, AsyncApp, Context, Entity, Task, Window};
use language::{Buffer, BufferSnapshot, language_settings::InlayHintKind};
use language::{
Buffer, BufferSnapshot,
language_settings::{InlayHintKind, InlayHintSettings},
};
use parking_lot::RwLock;
use project::{InlayHint, ResolveState};
use collections::{HashMap, HashSet, hash_map};
use language::language_settings::InlayHintSettings;
use smol::lock::Semaphore;
use sum_tree::Bias;
use text::{BufferId, ToOffset, ToPoint};
@@ -1301,13 +1303,13 @@ pub mod tests {
use futures::StreamExt;
use gpui::{AppContext as _, Context, SemanticVersion, TestAppContext, WindowHandle};
use itertools::Itertools as _;
use language::{Capability, FakeLspAdapter, language_settings::AllLanguageSettingsContent};
use language::{Capability, FakeLspAdapter};
use language::{Language, LanguageConfig, LanguageMatcher};
use lsp::FakeLanguageServer;
use parking_lot::Mutex;
use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use settings::{AllLanguageSettingsContent, InlayHintSettingsContent, SettingsStore};
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
use text::Point;
use util::path;
@@ -1318,15 +1320,17 @@ pub mod tests {
async fn test_basic_cache_update_with_duplicate_hints(cx: &mut gpui::TestAppContext) {
let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
show_other_hints: allowed_hint_kinds.contains(&None),
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(allowed_hint_kinds.contains(&Some(InlayHintKind::Type))),
show_parameter_hints: Some(
allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
),
show_other_hints: Some(allowed_hint_kinds.contains(&None)),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -1428,15 +1432,15 @@ pub mod tests {
#[gpui::test]
async fn test_cache_update_on_lsp_completion_tasks(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -1535,15 +1539,15 @@ pub mod tests {
#[gpui::test]
async fn test_no_hint_updates_for_unrelated_language_files(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -1765,15 +1769,17 @@ pub mod tests {
async fn test_hint_setting_changes(cx: &mut gpui::TestAppContext) {
let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
show_other_hints: allowed_hint_kinds.contains(&None),
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(allowed_hint_kinds.contains(&Some(InlayHintKind::Type))),
show_parameter_hints: Some(
allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
),
show_other_hints: Some(allowed_hint_kinds.contains(&None)),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -1926,16 +1932,19 @@ pub mod tests {
),
] {
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
show_parameter_hints: new_allowed_hint_kinds
.contains(&Some(InlayHintKind::Parameter)),
show_other_hints: new_allowed_hint_kinds.contains(&None),
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(
new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
),
show_parameter_hints: Some(
new_allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
),
show_other_hints: Some(new_allowed_hint_kinds.contains(&None)),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -1970,16 +1979,19 @@ pub mod tests {
let another_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Type)]);
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: false,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
show_parameter_hints: another_allowed_hint_kinds
.contains(&Some(InlayHintKind::Parameter)),
show_other_hints: another_allowed_hint_kinds.contains(&None),
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(false),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(
another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
),
show_parameter_hints: Some(
another_allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
),
show_other_hints: Some(another_allowed_hint_kinds.contains(&None)),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -2027,16 +2039,19 @@ pub mod tests {
let final_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Parameter)]);
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
show_parameter_hints: final_allowed_hint_kinds
.contains(&Some(InlayHintKind::Parameter)),
show_other_hints: final_allowed_hint_kinds.contains(&None),
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(
final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
),
show_parameter_hints: Some(
final_allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
),
show_other_hints: Some(final_allowed_hint_kinds.contains(&None)),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -2102,15 +2117,15 @@ pub mod tests {
#[gpui::test]
async fn test_hint_request_cancellation(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -2239,15 +2254,15 @@ pub mod tests {
#[gpui::test(iterations = 10)]
async fn test_large_buffer_inlay_requests_split(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -2540,15 +2555,15 @@ pub mod tests {
#[gpui::test]
async fn test_multiple_excerpts_large_multibuffer(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -2864,15 +2879,15 @@ pub mod tests {
#[gpui::test]
async fn test_excerpts_removed(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: false,
show_parameter_hints: false,
show_other_hints: false,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(false),
show_parameter_hints: Some(false),
show_other_hints: Some(false),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -3041,15 +3056,15 @@ pub mod tests {
.unwrap();
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -3074,15 +3089,15 @@ pub mod tests {
#[gpui::test]
async fn test_inside_char_boundary_range_hints(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -3167,15 +3182,15 @@ pub mod tests {
#[gpui::test]
async fn test_toggle_inlay_hints(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: false,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(false),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -3244,15 +3259,15 @@ pub mod tests {
.unwrap();
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
@@ -3305,15 +3320,15 @@ pub mod tests {
#[gpui::test]
async fn test_inlays_at_the_same_place(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
show_value_hints: true,
enabled: true,
edit_debounce_ms: 0,
scroll_debounce_ms: 0,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
settings.defaults.inlay_hints = Some(InlayHintSettingsContent {
show_value_hints: Some(true),
enabled: Some(true),
edit_debounce_ms: Some(0),
scroll_debounce_ms: Some(0),
show_type_hints: Some(true),
show_parameter_hints: Some(true),
show_other_hints: Some(true),
show_background: Some(false),
toggle_on_modifiers_press: None,
})
});
+12 -5
View File
@@ -328,7 +328,7 @@ pub(crate) fn refresh_enabled_in_any_buffer(
snapshot.file(),
cx,
);
if language_settings.jsx_tag_auto_close.enabled {
if language_settings.jsx_tag_auto_close {
found_enabled = true;
}
}
@@ -406,7 +406,7 @@ pub(crate) fn handle_from(
};
let language_settings = snapshot.settings_at(edit.new.end, cx);
if !language_settings.jsx_tag_auto_close.enabled {
if !language_settings.jsx_tag_auto_close {
continue;
}
@@ -620,14 +620,17 @@ mod jsx_tag_autoclose_tests {
use super::*;
use gpui::{AppContext as _, TestAppContext};
use language::language_settings::JsxTagAutoCloseSettings;
use languages::language;
use multi_buffer::ExcerptRange;
use text::Selection;
async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
init_test(cx, |settings| {
settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
settings
.defaults
.jsx_tag_auto_close
.get_or_insert_default()
.enabled = Some(true);
});
let mut cx = EditorTestContext::new(cx).await;
@@ -789,7 +792,11 @@ mod jsx_tag_autoclose_tests {
#[gpui::test]
async fn test_multibuffer(cx: &mut TestAppContext) {
init_test(cx, |settings| {
settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
settings
.defaults
.jsx_tag_auto_close
.get_or_insert_default()
.enabled = Some(true);
});
let buffer_a = cx.new(|cx| {
+1 -4
View File
@@ -340,10 +340,7 @@ pub fn init(cx: &mut App) -> Arc<AgentAppState> {
release_channel::init(app_version, cx);
gpui_tokio::init(cx);
let mut settings_store = SettingsStore::new(cx);
settings_store
.set_default_settings(settings::default_settings().as_ref(), cx)
.unwrap();
let settings_store = SettingsStore::new(cx, &settings::default_settings());
cx.set_global(settings_store);
client::init_settings(cx);
-1
View File
@@ -37,7 +37,6 @@ paths.workspace = true
project.workspace = true
remote.workspace = true
release_channel.workspace = true
schemars.workspace = true
semantic_version.workspace = true
serde.workspace = true
serde_json.workspace = true
+18 -18
View File
@@ -1,13 +1,9 @@
use anyhow::Result;
use collections::HashMap;
use gpui::App;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
use settings::Settings;
use std::sync::Arc;
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, SettingsUi, SettingsKey)]
#[settings_key(None)]
#[derive(Debug, Default, Clone)]
pub struct ExtensionSettings {
/// The extensions that should be automatically installed by Zed.
///
@@ -15,9 +11,7 @@ pub struct ExtensionSettings {
/// available out-of-the-box.
///
/// Default: { "html": true }
#[serde(default)]
pub auto_install_extensions: HashMap<Arc<str>, bool>,
#[serde(default)]
pub auto_update_extensions: HashMap<Arc<str>, bool>,
}
@@ -39,18 +33,24 @@ impl ExtensionSettings {
}
impl Settings for ExtensionSettings {
type FileContent = Self;
fn load(sources: SettingsSources<Self::FileContent>, _cx: &mut App) -> Result<Self> {
SettingsSources::<Self::FileContent>::json_merge_with(
[sources.default]
.into_iter()
.chain(sources.user)
.chain(sources.server),
)
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
Self {
auto_install_extensions: content.extension.auto_install_extensions.clone(),
auto_update_extensions: content.extension.auto_update_extensions.clone(),
}
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
self.auto_install_extensions
.extend(content.extension.auto_install_extensions.clone());
self.auto_update_extensions
.extend(content.extension.auto_update_extensions.clone());
}
fn import_from_vscode(
_vscode: &settings::VsCodeSettings,
_current: &mut settings::SettingsContent,
) {
// settingsSync.ignoredExtensions controls autoupdate for vscode extensions, but we
// don't have a mapping to zed-extensions. there's also extensions.autoCheckUpdates
// and extensions.autoUpdate which are global switches, we don't support those yet
@@ -2,7 +2,7 @@ use std::str::FromStr;
use std::sync::Arc;
use client::ExtensionMetadata;
use extension_host::{ExtensionSettings, ExtensionStore};
use extension_host::ExtensionStore;
use fs::Fs;
use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
use gpui::{App, DismissEvent, Entity, EventEmitter, Focusable, Task, WeakEntity, prelude::*};
@@ -183,10 +183,13 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate {
let extension_id = extension_version.id.clone();
let version = extension_version.manifest.version.clone();
update_settings_file::<ExtensionSettings>(self.fs.clone(), cx, {
update_settings_file(self.fs.clone(), cx, {
let extension_id = extension_id.clone();
move |settings, _| {
settings.auto_update_extensions.insert(extension_id, false);
settings
.extension
.auto_update_extensions
.insert(extension_id, false);
}
});
+7 -9
View File
@@ -20,7 +20,7 @@ use gpui::{
use num_format::{Locale, ToFormattedString};
use project::DirectoryLister;
use release_channel::ReleaseChannel;
use settings::Settings;
use settings::{Settings, SettingsContent};
use strum::IntoEnumIterator as _;
use theme::ThemeSettings;
use ui::{
@@ -1269,17 +1269,17 @@ impl ExtensionsPage {
Label::new(message)
}
fn update_settings<T: Settings>(
fn update_settings(
&mut self,
selection: &ToggleState,
cx: &mut Context<Self>,
callback: impl 'static + Send + Fn(&mut T::FileContent, bool),
callback: impl 'static + Send + Fn(&mut SettingsContent, bool),
) {
if let Some(workspace) = self.workspace.upgrade() {
let fs = workspace.read(cx).app_state().fs.clone();
let selection = *selection;
settings::update_settings_file::<T>(fs, cx, move |settings, _| {
settings::update_settings_file(fs, cx, move |settings, _| {
let value = match selection {
ToggleState::Unselected => false,
ToggleState::Selected => true,
@@ -1340,11 +1340,9 @@ impl ExtensionsPage {
},
cx.listener(move |this, selection, _, cx| {
telemetry::event!("Vim Mode Toggled", source = "Feature Upsell");
this.update_settings::<VimModeSetting>(
selection,
cx,
|setting, value| setting.vim_mode = Some(value),
);
this.update_settings(selection, cx, |setting, value| {
setting.vim_mode = Some(value)
});
}),
)),
Feature::LanguageBash => FeatureUpsell::new("Shell support is built-in to Zed!")
+6 -6
View File
@@ -347,16 +347,16 @@ impl FileFinder {
})
}
pub fn modal_max_width(width_setting: Option<FileFinderWidth>, window: &mut Window) -> Pixels {
pub fn modal_max_width(width_setting: FileFinderWidth, window: &mut Window) -> Pixels {
let window_width = window.viewport_size().width;
let small_width = rems(34.).to_pixels(window.rem_size());
match width_setting {
None | Some(FileFinderWidth::Small) => small_width,
Some(FileFinderWidth::Full) => window_width,
Some(FileFinderWidth::XLarge) => (window_width - Pixels(512.)).max(small_width),
Some(FileFinderWidth::Large) => (window_width - Pixels(768.)).max(small_width),
Some(FileFinderWidth::Medium) => (window_width - Pixels(1024.)).max(small_width),
FileFinderWidth::Small => small_width,
FileFinderWidth::Full => window_width,
FileFinderWidth::XLarge => (window_width - Pixels(512.)).max(small_width),
FileFinderWidth::Large => (window_width - Pixels(768.)).max(small_width),
FileFinderWidth::Medium => (window_width - Pixels(1024.)).max(small_width),
}
}
}
+36 -38
View File
@@ -1,55 +1,41 @@
use anyhow::Result;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
use settings::Settings;
use util::MergeFrom;
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
pub struct FileFinderSettings {
pub file_icons: bool,
pub modal_max_width: Option<FileFinderWidth>,
pub modal_max_width: FileFinderWidth,
pub skip_focus_for_active_in_search: bool,
pub include_ignored: Option<bool>,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
#[settings_key(key = "file_finder")]
pub struct FileFinderSettingsContent {
/// Whether to show file icons in the file finder.
///
/// Default: true
pub file_icons: Option<bool>,
/// Determines how much space the file finder can take up in relation to the available window width.
///
/// Default: small
pub modal_max_width: Option<FileFinderWidth>,
/// Determines whether the file finder should skip focus for the active file in search results.
///
/// Default: true
pub skip_focus_for_active_in_search: Option<bool>,
/// Determines whether to show the git status in the file finder
///
/// Default: true
pub git_status: Option<bool>,
/// Whether to use gitignored files when searching.
/// Only the file Zed had indexed will be used, not necessary all the gitignored files.
///
/// Can accept 3 values:
/// * `Some(true)`: Use all gitignored files
/// * `Some(false)`: Use only the files Zed had indexed
/// * `None`: Be smart and search for ignored when called from a gitignored worktree
///
/// Default: None
pub include_ignored: Option<Option<bool>>,
}
impl Settings for FileFinderSettings {
type FileContent = FileFinderSettingsContent;
fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
let file_finder = content.file_finder.as_ref().unwrap();
fn load(sources: SettingsSources<Self::FileContent>, _: &mut gpui::App) -> Result<Self> {
sources.json_merge()
Self {
file_icons: file_finder.file_icons.unwrap(),
modal_max_width: file_finder.modal_max_width.unwrap().into(),
skip_focus_for_active_in_search: file_finder.skip_focus_for_active_in_search.unwrap(),
include_ignored: file_finder.include_ignored.flatten(),
}
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut ui::App) {
let Some(file_finder) = content.file_finder.as_ref() else {
return;
};
self.file_icons.merge_from(&file_finder.file_icons);
self.modal_max_width
.merge_from(&file_finder.modal_max_width.map(Into::into));
self.skip_focus_for_active_in_search
.merge_from(&file_finder.skip_focus_for_active_in_search);
self.include_ignored
.merge_from(&file_finder.include_ignored);
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
@@ -62,3 +48,15 @@ pub enum FileFinderWidth {
XLarge,
Full,
}
impl From<settings::FileFinderWidthContent> for FileFinderWidth {
fn from(content: settings::FileFinderWidthContent) -> Self {
match content {
settings::FileFinderWidthContent::Small => FileFinderWidth::Small,
settings::FileFinderWidthContent::Medium => FileFinderWidth::Medium,
settings::FileFinderWidthContent::Large => FileFinderWidth::Large,
settings::FileFinderWidthContent::XLarge => FileFinderWidth::XLarge,
settings::FileFinderWidthContent::Full => FileFinderWidth::Full,
}
}
}
-1
View File
@@ -19,7 +19,6 @@ git.workspace = true
gpui.workspace = true
http_client.workspace = true
regex.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
+13 -36
View File
@@ -1,11 +1,8 @@
use std::sync::Arc;
use anyhow::Result;
use git::GitHostingProviderRegistry;
use gpui::App;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsStore, SettingsUi};
use settings::{GitHostingProviderConfig, GitHostingProviderKind, Settings, SettingsStore};
use url::Url;
use util::ResultExt as _;
@@ -55,43 +52,23 @@ fn update_git_hosting_providers_from_settings(cx: &mut App) {
provider_registry.set_setting_providers(iter);
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum GitHostingProviderKind {
Github,
Gitlab,
Bitbucket,
}
/// A custom Git hosting provider.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct GitHostingProviderConfig {
/// The type of the provider.
///
/// Must be one of `github`, `gitlab`, or `bitbucket`.
pub provider: GitHostingProviderKind,
/// The base URL for the provider (e.g., "https://code.corp.big.com").
pub base_url: String,
/// The display name for the provider (e.g., "BigCorp GitHub").
pub name: String,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
#[settings_key(None)]
#[derive(Debug, Clone)]
pub struct GitHostingProviderSettings {
/// The list of custom Git hosting providers.
#[serde(default)]
pub git_hosting_providers: Vec<GitHostingProviderConfig>,
}
impl Settings for GitHostingProviderSettings {
type FileContent = Self;
fn load(sources: settings::SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
sources.json_merge()
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
Self {
git_hosting_providers: content.project.git_hosting_providers.clone().unwrap(),
}
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
if let Some(more) = &content.project.git_hosting_providers {
self.git_hosting_providers.extend_from_slice(&more.clone());
}
}
fn import_from_vscode(_: &settings::VsCodeSettings, _: &mut settings::SettingsContent) {}
}
+2 -1
View File
@@ -139,7 +139,8 @@ impl BlameRenderer for GitBlameRenderer {
let author = blame_entry.author.as_deref().unwrap_or_default();
let summary_enabled = ProjectSettings::get_global(cx)
.git
.show_inline_commit_summary();
.inline_blame
.show_commit_summary;
let text = match blame_entry.summary.as_ref() {
Some(summary) if summary_enabled => {
-1
View File
@@ -564,7 +564,6 @@ impl PickerDelegate for BranchListDelegate {
let show_author_name = ProjectSettings::get_global(cx)
.git
.branch_picker
.unwrap_or_default()
.show_author_name;
subject.map_or("no commits found".into(), |subject| {
+7 -9
View File
@@ -2,7 +2,6 @@ use crate::askpass_modal::AskPassModal;
use crate::commit_modal::CommitModal;
use crate::commit_tooltip::CommitTooltip;
use crate::commit_view::CommitView;
use crate::git_panel_settings::StatusStyle;
use crate::project_diff::{self, Diff, ProjectDiff};
use crate::remote_output::{self, RemoteAction, SuccessMessage};
use crate::{branch_picker, picker_prompt, render_remote_button};
@@ -51,7 +50,7 @@ use project::{
git_store::{GitStoreEvent, Repository, RepositoryEvent, RepositoryId},
};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use settings::{Settings, SettingsStore, StatusStyle};
use std::future::Future;
use std::ops::Range;
use std::path::{Path, PathBuf};
@@ -2439,8 +2438,9 @@ impl GitPanel {
let workspace = workspace.read(cx);
let fs = workspace.app_state().fs.clone();
cx.update_global::<SettingsStore, _>(|store, _cx| {
store.update_settings_file::<GitPanelSettings>(fs, move |settings, _cx| {
settings.sort_by_path = Some(!current_setting);
store.update_settings_file(fs, move |settings, _cx| {
settings.git_panel.get_or_insert_default().sort_by_path =
Some(!current_setting);
});
});
}
@@ -4356,11 +4356,9 @@ impl Panel for GitPanel {
}
fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context<Self>) {
settings::update_settings_file::<GitPanelSettings>(
self.fs.clone(),
cx,
move |settings, _| settings.dock = Some(position),
);
settings::update_settings_file(self.fs.clone(), cx, move |settings, _| {
settings.git_panel.get_or_insert_default().dock = Some(position.into())
});
}
fn size(&self, _: &Window, cx: &App) -> Pixels {
+50 -73
View File
@@ -2,8 +2,12 @@ use editor::EditorSettings;
use gpui::Pixels;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
use ui::scrollbars::{ScrollbarVisibility, ShowScrollbar};
use settings::{Settings, SettingsContent, StatusStyle};
use ui::{
px,
scrollbars::{ScrollbarVisibility, ShowScrollbar},
};
use util::MergeFrom;
use workspace::dock::DockPosition;
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
@@ -19,67 +23,7 @@ pub struct ScrollbarSettings {
pub show: Option<ShowScrollbar>,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
// Style of the git status indicator in the panel.
//
// Default: icon
pub enum StatusStyleContent {
Icon,
LabelColor,
}
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum StatusStyle {
#[default]
Icon,
LabelColor,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
#[settings_key(key = "git_panel")]
pub struct GitPanelSettingsContent {
/// 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: 360
pub default_width: Option<f32>,
/// How entry statuses are displayed.
///
/// Default: icon
pub status_style: Option<StatusStyle>,
/// How and when the scrollbar should be displayed.
///
/// Default: inherits editor scrollbar settings
pub scrollbar: Option<ScrollbarSettings>,
/// What the default branch name should be when
/// `init.defaultBranch` is not set in git
///
/// Default: main
pub fallback_branch_name: Option<String>,
/// Whether to sort entries in the panel by path
/// or by status (the default).
///
/// Default: false
pub sort_by_path: Option<bool>,
/// Whether to collapse untracked files in the diff panel.
///
/// Default: false
pub collapse_untracked_diff: Option<bool>,
}
#[derive(Deserialize, Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct GitPanelSettings {
pub button: bool,
pub dock: DockPosition,
@@ -108,17 +52,50 @@ impl ScrollbarVisibility for GitPanelSettings {
}
impl Settings for GitPanelSettings {
type FileContent = GitPanelSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,
_: &mut gpui::App,
) -> anyhow::Result<Self> {
sources.json_merge()
fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
let git_panel = content.git_panel.clone().unwrap();
Self {
button: git_panel.button.unwrap(),
dock: git_panel.dock.unwrap().into(),
default_width: px(git_panel.default_width.unwrap()),
status_style: git_panel.status_style.unwrap(),
scrollbar: ScrollbarSettings {
show: git_panel.scrollbar.unwrap().show.map(Into::into),
},
fallback_branch_name: git_panel.fallback_branch_name.unwrap(),
sort_by_path: git_panel.sort_by_path.unwrap(),
collapse_untracked_diff: git_panel.collapse_untracked_diff.unwrap(),
}
}
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
vscode.bool_setting("git.enabled", &mut current.button);
vscode.string_setting("git.defaultBranchName", &mut current.fallback_branch_name);
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut ui::App) {
let Some(git_panel) = &content.git_panel else {
return;
};
self.button.merge_from(&git_panel.button);
self.dock.merge_from(&git_panel.dock.map(Into::into));
self.default_width
.merge_from(&git_panel.default_width.map(px));
self.status_style.merge_from(&git_panel.status_style);
self.fallback_branch_name
.merge_from(&git_panel.fallback_branch_name);
self.sort_by_path.merge_from(&git_panel.sort_by_path);
self.collapse_untracked_diff
.merge_from(&git_panel.collapse_untracked_diff);
if let Some(show) = git_panel.scrollbar.as_ref().and_then(|s| s.show) {
self.scrollbar.show = Some(show.into())
}
}
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
if let Some(git_enabled) = vscode.read_bool("git.enabled") {
current.git_panel.get_or_insert_default().button = Some(git_enabled);
}
if let Some(default_branch) = vscode.read_string("git.defaultBranchName") {
current
.git_panel
.get_or_insert_default()
.fallback_branch_name = Some(default_branch.to_string());
}
}
}
-2
View File
@@ -13,12 +13,10 @@ path = "src/go_to_line.rs"
doctest = false
[dependencies]
anyhow.workspace = true
editor.workspace = true
gpui.workspace = true
language.workspace = true
menu.workspace = true
schemars.workspace = true
serde.workspace = true
settings.workspace = true
text.workspace = true
+16 -32
View File
@@ -1,15 +1,13 @@
use editor::{Editor, EditorSettings, MultiBufferSnapshot};
use gpui::{App, Entity, FocusHandle, Focusable, Subscription, Task, WeakEntity};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
use settings::Settings;
use std::{fmt::Write, num::NonZeroU32, time::Duration};
use text::{Point, Selection};
use ui::{
Button, ButtonCommon, Clickable, Context, FluentBuilder, IntoElement, LabelSize, ParentElement,
Render, Tooltip, Window, div,
};
use util::paths::FILE_ROW_COLUMN_DELIMITER;
use util::{MergeFrom, paths::FILE_ROW_COLUMN_DELIMITER};
use workspace::{StatusItemView, Workspace, item::ItemHandle};
#[derive(Copy, Clone, Debug, Default, PartialOrd, PartialEq)]
@@ -293,41 +291,27 @@ impl StatusItemView for CursorPosition {
}
}
#[derive(Clone, Copy, Default, PartialEq, Debug, JsonSchema, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum LineIndicatorFormat {
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum LineIndicatorFormat {
Short,
#[default]
Long,
}
#[derive(
Clone, Copy, Default, Debug, JsonSchema, Deserialize, Serialize, SettingsUi, SettingsKey,
)]
#[settings_key(None)]
pub(crate) struct LineIndicatorFormatContent {
line_indicator_format: Option<LineIndicatorFormat>,
impl From<settings::LineIndicatorFormat> for LineIndicatorFormat {
fn from(format: settings::LineIndicatorFormat) -> Self {
match format {
settings::LineIndicatorFormat::Short => LineIndicatorFormat::Short,
settings::LineIndicatorFormat::Long => LineIndicatorFormat::Long,
}
}
}
impl Settings for LineIndicatorFormat {
type FileContent = LineIndicatorFormatContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
let format = [
sources.release_channel,
sources.profile,
sources.user,
sources.operating_system,
Some(sources.default),
]
.into_iter()
.flatten()
.filter_map(|val| val.line_indicator_format)
.next()
.ok_or_else(Self::missing_default)?;
Ok(format)
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
content.line_indicator_format.unwrap().into()
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
self.merge_from(&content.line_indicator_format.map(Into::into));
}
}
+1
View File
@@ -21,5 +21,6 @@ http_client.workspace = true
schemars = { workspace = true, optional = true }
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
strum.workspace = true
workspace-hack.workspace = true
+1 -10
View File
@@ -4,6 +4,7 @@ use anyhow::{Result, anyhow, bail};
use futures::{AsyncBufReadExt, AsyncReadExt, StreamExt, io::BufReader, stream::BoxStream};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub use settings::ModelMode as GoogleModelMode;
pub const API_URL: &str = "https://generativelanguage.googleapis.com";
@@ -295,16 +296,6 @@ pub struct ThinkingConfig {
pub thinking_budget: u32,
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub enum GoogleModelMode {
#[default]
Default,
Thinking {
budget_tokens: Option<u32>,
},
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GenerationConfig {
-1
View File
@@ -24,7 +24,6 @@ gpui.workspace = true
language.workspace = true
log.workspace = true
project.workspace = true
schemars.workspace = true
serde.workspace = true
settings.workspace = true
theme.workspace = true
@@ -1,40 +1,30 @@
use gpui::App;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
pub use settings::ImageFileSizeUnit;
use settings::Settings;
use util::MergeFrom;
/// The settings for the image viewer.
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Default, SettingsUi, SettingsKey)]
#[settings_key(key = "image_viewer")]
#[derive(Clone, Debug, Default)]
pub struct ImageViewerSettings {
/// The unit to use for displaying image file sizes.
///
/// Default: "binary"
#[serde(default)]
pub unit: ImageFileSizeUnit,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "snake_case")]
pub enum ImageFileSizeUnit {
/// Displays file size in binary units (e.g., KiB, MiB).
#[default]
Binary,
/// Displays file size in decimal units (e.g., KB, MB).
Decimal,
}
impl Settings for ImageViewerSettings {
type FileContent = Self;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
SettingsSources::<Self::FileContent>::json_merge_with(
[sources.default]
.into_iter()
.chain(sources.user)
.chain(sources.server),
)
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
Self {
unit: content.image_viewer.clone().unwrap().unit.unwrap(),
}
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
self.unit.merge_from(
&content
.image_viewer
.as_ref()
.and_then(|image_viewer| image_viewer.unit),
);
}
}
+1 -1
View File
@@ -18,12 +18,12 @@ chrono.workspace = true
editor.workspace = true
gpui.workspace = true
log.workspace = true
schemars.workspace = true
serde.workspace = true
settings.workspace = true
shellexpand.workspace = true
workspace.workspace = true
workspace-hack.workspace = true
util.workspace = true
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }
+26 -36
View File
@@ -1,16 +1,15 @@
use anyhow::Result;
use chrono::{Datelike, Local, NaiveTime, Timelike};
use editor::scroll::Autoscroll;
use editor::{Editor, SelectionEffects};
use gpui::{App, AppContext as _, Context, Window, actions};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
pub use settings::HourFormat;
use settings::Settings;
use std::{
fs::OpenOptions,
path::{Path, PathBuf},
sync::Arc,
};
use util::MergeFrom;
use workspace::{AppState, OpenVisible, Workspace};
actions!(
@@ -22,44 +21,35 @@ actions!(
);
/// Settings specific to journaling
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
#[settings_key(key = "journal")]
#[derive(Clone, Debug)]
pub struct JournalSettings {
/// The path of the directory where journal entries are stored.
///
/// Default: `~`
pub path: Option<String>,
pub path: String,
/// What format to display the hours in.
///
/// Default: hour12
pub hour_format: Option<HourFormat>,
}
impl Default for JournalSettings {
fn default() -> Self {
Self {
path: Some("~".into()),
hour_format: Some(Default::default()),
}
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum HourFormat {
#[default]
Hour12,
Hour24,
pub hour_format: HourFormat,
}
impl settings::Settings for JournalSettings {
type FileContent = Self;
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
let journal = content.journal.clone().unwrap();
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
sources.json_merge()
Self {
path: journal.path.unwrap(),
hour_format: journal.hour_format.unwrap(),
}
}
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
let Some(journal) = content.journal.as_ref() else {
return;
};
self.path.merge_from(&journal.path);
self.hour_format.merge_from(&journal.hour_format);
}
}
pub fn init(_: Arc<AppState>, cx: &mut App) {
@@ -77,7 +67,7 @@ pub fn init(_: Arc<AppState>, cx: &mut App) {
pub fn new_journal_entry(workspace: &Workspace, window: &mut Window, cx: &mut App) {
let settings = JournalSettings::get_global(cx);
let journal_dir = match journal_dir(settings.path.as_ref().unwrap()) {
let journal_dir = match journal_dir(&settings.path) {
Some(journal_dir) => journal_dir,
None => {
log::error!("Can't determine journal directory");
@@ -199,13 +189,13 @@ fn journal_dir(path: &str) -> Option<PathBuf> {
.map(|dir| Path::new(&dir.to_string()).to_path_buf().join("journal"))
}
fn heading_entry(now: NaiveTime, hour_format: &Option<HourFormat>) -> String {
fn heading_entry(now: NaiveTime, hour_format: &HourFormat) -> String {
match hour_format {
Some(HourFormat::Hour24) => {
HourFormat::Hour24 => {
let hour = now.hour();
format!("# {}:{:02}", hour, now.minute())
}
_ => {
HourFormat::Hour12 => {
let (pm, hour) = now.hour12();
let am_or_pm = if pm { "PM" } else { "AM" };
format!("# {}:{:02} {}", hour, now.minute(), am_or_pm)
@@ -221,7 +211,7 @@ mod tests {
#[test]
fn test_heading_entry_defaults_to_hour_12() {
let naive_time = NaiveTime::from_hms_milli_opt(15, 0, 0, 0).unwrap();
let actual_heading_entry = heading_entry(naive_time, &None);
let actual_heading_entry = heading_entry(naive_time, &HourFormat::Hour12);
let expected_heading_entry = "# 3:00 PM";
assert_eq!(actual_heading_entry, expected_heading_entry);
@@ -230,7 +220,7 @@ mod tests {
#[test]
fn test_heading_entry_is_hour_12() {
let naive_time = NaiveTime::from_hms_milli_opt(15, 0, 0, 0).unwrap();
let actual_heading_entry = heading_entry(naive_time, &Some(HourFormat::Hour12));
let actual_heading_entry = heading_entry(naive_time, &HourFormat::Hour12);
let expected_heading_entry = "# 3:00 PM";
assert_eq!(actual_heading_entry, expected_heading_entry);
@@ -239,7 +229,7 @@ mod tests {
#[test]
fn test_heading_entry_is_hour_24() {
let naive_time = NaiveTime::from_hms_milli_opt(15, 0, 0, 0).unwrap();
let actual_heading_entry = heading_entry(naive_time, &Some(HourFormat::Hour24));
let actual_heading_entry = heading_entry(naive_time, &HourFormat::Hour24);
let expected_heading_entry = "# 15:00";
assert_eq!(actual_heading_entry, expected_heading_entry);
+13 -6
View File
@@ -30,10 +30,9 @@ use gpui::{
use lsp::{LanguageServerId, NumberOrString};
use parking_lot::Mutex;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use settings::{SettingsUi, WorktreeId};
use settings::WorktreeId;
use smallvec::SmallVec;
use smol::future::yield_now;
use std::{
@@ -174,10 +173,7 @@ pub enum IndentKind {
}
/// The shape of a selection cursor.
#[derive(
Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi,
)]
#[serde(rename_all = "snake_case")]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum CursorShape {
/// A vertical bar
#[default]
@@ -190,6 +186,17 @@ pub enum CursorShape {
Hollow,
}
impl From<settings::CursorShape> for CursorShape {
fn from(shape: settings::CursorShape) -> Self {
match shape {
settings::CursorShape::Bar => CursorShape::Bar,
settings::CursorShape::Block => CursorShape::Block,
settings::CursorShape::Underline => CursorShape::Underline,
settings::CursorShape::Hollow => CursorShape::Hollow,
}
}
}
#[derive(Clone, Debug)]
struct SelectionSet {
line_mode: bool,
+2 -4
View File
@@ -1,8 +1,5 @@
use super::*;
use crate::Buffer;
use crate::language_settings::{
AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent,
};
use clock::ReplicaId;
use collections::BTreeMap;
use futures::FutureExt as _;
@@ -13,6 +10,7 @@ use proto::deserialize_operation;
use rand::prelude::*;
use regex::RegexBuilder;
use settings::SettingsStore;
use settings::{AllLanguageSettingsContent, LanguageSettingsContent};
use std::collections::BTreeSet;
use std::{
env,
@@ -3849,7 +3847,7 @@ fn init_settings(cx: &mut App, f: fn(&mut AllLanguageSettingsContent)) {
cx.set_global(settings_store);
crate::init(cx);
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<AllLanguageSettings>(cx, f);
settings.update_user_settings(cx, |content| f(&mut content.project.all_languages));
});
}
+1 -1
View File
@@ -22,8 +22,8 @@ mod toolchain;
#[cfg(test)]
pub mod buffer_tests;
pub use crate::language_settings::EditPredictionsMode;
use crate::language_settings::SoftWrap;
pub use crate::language_settings::{EditPredictionsMode, IndentGuideSettings};
use anyhow::{Context as _, Result};
use async_trait::async_trait;
use collections::{HashMap, HashSet, IndexSet};
+3 -6
View File
@@ -1,14 +1,11 @@
use crate::{
CachedLspAdapter, File, Language, LanguageConfig, LanguageId, LanguageMatcher,
LanguageServerName, LspAdapter, ManifestName, PLAIN_TEXT, ToolchainLister,
language_settings::{
AllLanguageSettingsContent, LanguageSettingsContent, all_language_settings,
},
task_context::ContextProvider,
with_parser,
language_settings::all_language_settings, task_context::ContextProvider, with_parser,
};
use anyhow::{Context as _, Result, anyhow};
use collections::{FxHashMap, HashMap, HashSet, hash_map};
use settings::{AllLanguageSettingsContent, LanguageSettingsContent};
use futures::{
Future,
@@ -1175,7 +1172,7 @@ impl LanguageRegistryState {
language.set_theme(theme.syntax());
}
self.language_settings.languages.0.insert(
language.name(),
language.name().0,
LanguageSettingsContent {
tab_size: language.config.tab_size,
hard_tabs: language.config.hard_tabs,
File diff suppressed because it is too large Load Diff
+1
View File
@@ -35,6 +35,7 @@ proto.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
smol.workspace = true
telemetry_events.workspace = true
thiserror.workspace = true
+1 -8
View File
@@ -21,6 +21,7 @@ use open_router::OpenRouterError;
use parking_lot::Mutex;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
pub use settings::LanguageModelCacheConfiguration;
use std::ops::{Add, Sub};
use std::str::FromStr;
use std::sync::Arc;
@@ -62,14 +63,6 @@ pub fn init_settings(cx: &mut App) {
registry::init(cx);
}
/// Configuration for caching language model messages.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct LanguageModelCacheConfiguration {
pub max_cache_anchors: usize,
pub should_speculate: bool,
pub min_total_token: u64,
}
/// A completion event from a language model.
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum LanguageModelCompletionEvent {
@@ -18,8 +18,6 @@ use language_model::{
LanguageModelToolResultContent, MessageContent, RateLimiter, Role,
};
use language_model::{LanguageModelCompletionEvent, LanguageModelToolUse, StopReason};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use std::pin::Pin;
use std::str::FromStr;
@@ -30,6 +28,8 @@ use ui::{Icon, IconName, List, Tooltip, prelude::*};
use util::{ResultExt, truncate_and_trailoff};
use zed_env_vars::{EnvVar, env_var};
pub use settings::AnthropicAvailableModel as AvailableModel;
const PROVIDER_ID: LanguageModelProviderId = language_model::ANTHROPIC_PROVIDER_ID;
const PROVIDER_NAME: LanguageModelProviderName = language_model::ANTHROPIC_PROVIDER_NAME;
@@ -40,55 +40,6 @@ pub struct AnthropicSettings {
pub available_models: Vec<AvailableModel>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct AvailableModel {
/// The model's name in the Anthropic API. e.g. claude-3-5-sonnet-latest, claude-3-opus-20240229, etc
pub name: String,
/// The model's name in Zed's UI, such as in the model selector dropdown menu in the assistant panel.
pub display_name: Option<String>,
/// The model's context window size.
pub max_tokens: u64,
/// A model `name` to substitute when calling tools, in case the primary model doesn't support tool calling.
pub tool_override: Option<String>,
/// Configuration of Anthropic's caching API.
pub cache_configuration: Option<LanguageModelCacheConfiguration>,
pub max_output_tokens: Option<u64>,
pub default_temperature: Option<f32>,
#[serde(default)]
pub extra_beta_headers: Vec<String>,
/// The model's mode (e.g. thinking)
pub mode: Option<ModelMode>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum ModelMode {
#[default]
Default,
Thinking {
/// The maximum number of tokens to use for reasoning. Must be lower than the model's `max_output_tokens`.
budget_tokens: Option<u32>,
},
}
impl From<ModelMode> for AnthropicModelMode {
fn from(value: ModelMode) -> Self {
match value {
ModelMode::Default => AnthropicModelMode::Default,
ModelMode::Thinking { budget_tokens } => AnthropicModelMode::Thinking { budget_tokens },
}
}
}
impl From<AnthropicModelMode> for ModelMode {
fn from(value: AnthropicModelMode) -> Self {
match value {
AnthropicModelMode::Default => ModelMode::Default,
AnthropicModelMode::Thinking { budget_tokens } => ModelMode::Thinking { budget_tokens },
}
}
}
pub struct AnthropicLanguageModelProvider {
http_client: Arc<dyn HttpClient>,
state: gpui::Entity<State>,
@@ -237,7 +188,7 @@ impl LanguageModelProvider for AnthropicLanguageModelProvider {
max_output_tokens: model.max_output_tokens,
default_temperature: model.default_temperature,
extra_beta_headers: model.extra_beta_headers.clone(),
mode: model.mode.clone().unwrap_or_default().into(),
mode: model.mode.unwrap_or_default().into(),
},
);
}
+9 -10
View File
@@ -42,7 +42,7 @@ use language_model::{
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use settings::{Settings, SettingsStore};
use settings::{BedrockAvailableModel as AvailableModel, Settings, SettingsStore};
use smol::lock::OnceCell;
use strum::{EnumIter, IntoEnumIterator, IntoStaticStr};
use theme::ThemeSettings;
@@ -83,15 +83,14 @@ pub enum BedrockAuthMethod {
Automatic,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct AvailableModel {
pub name: String,
pub display_name: Option<String>,
pub max_tokens: u64,
pub cache_configuration: Option<LanguageModelCacheConfiguration>,
pub max_output_tokens: Option<u64>,
pub default_temperature: Option<f32>,
pub mode: Option<ModelMode>,
impl From<settings::BedrockAuthMethodContent> for BedrockAuthMethod {
fn from(value: settings::BedrockAuthMethodContent) -> Self {
match value {
settings::BedrockAuthMethodContent::SingleSignOn => BedrockAuthMethod::SingleSignOn,
settings::BedrockAuthMethodContent::Automatic => BedrockAuthMethod::Automatic,
settings::BedrockAuthMethodContent::NamedProfile => BedrockAuthMethod::NamedProfile,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
+2 -36
View File
@@ -32,6 +32,8 @@ use release_channel::AppVersion;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use settings::SettingsStore;
pub use settings::ZedDotDevAvailableModel as AvailableModel;
pub use settings::ZedDotDevAvailableProvider as AvailableProvider;
use smol::io::{AsyncReadExt, BufReader};
use std::pin::Pin;
use std::str::FromStr as _;
@@ -52,42 +54,6 @@ const PROVIDER_NAME: LanguageModelProviderName = language_model::ZED_CLOUD_PROVI
pub struct ZedDotDevSettings {
pub available_models: Vec<AvailableModel>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum AvailableProvider {
Anthropic,
OpenAi,
Google,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct AvailableModel {
/// The provider of the language model.
pub provider: AvailableProvider,
/// The model's name in the provider's API. e.g. claude-3-5-sonnet-20240620
pub name: String,
/// The name displayed in the UI, such as in the assistant panel model dropdown menu.
pub display_name: Option<String>,
/// The size of the context window, indicating the maximum number of tokens the model can process.
pub max_tokens: usize,
/// The maximum number of output tokens allowed by the model.
pub max_output_tokens: Option<u64>,
/// The maximum number of completion tokens allowed by the model (o1-* only)
pub max_completion_tokens: Option<u64>,
/// Override this model with a different Anthropic model for tool calls.
pub tool_override: Option<String>,
/// Indicates whether this custom model supports caching.
pub cache_configuration: Option<LanguageModelCacheConfiguration>,
/// The default temperature to use for this model.
pub default_temperature: Option<f32>,
/// Any extra beta headers to provide when using the model.
#[serde(default)]
pub extra_beta_headers: Vec<String>,
/// The model's mode (e.g. thinking)
pub mode: Option<ModelMode>,
}
#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum ModelMode {
@@ -16,8 +16,7 @@ use language_model::{
LanguageModelToolChoice, LanguageModelToolResultContent, LanguageModelToolUse, MessageContent,
RateLimiter, Role, StopReason, TokenUsage,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub use settings::DeepseekAvailableModel as AvailableModel;
use settings::{Settings, SettingsStore};
use std::pin::Pin;
use std::str::FromStr;
@@ -47,15 +46,6 @@ pub struct DeepSeekSettings {
pub api_url: String,
pub available_models: Vec<AvailableModel>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct AvailableModel {
pub name: String,
pub display_name: Option<String>,
pub max_tokens: u64,
pub max_output_tokens: Option<u64>,
}
pub struct DeepSeekLanguageModelProvider {
http_client: Arc<dyn HttpClient>,
state: Entity<State>,
+2 -27
View File
@@ -24,6 +24,7 @@ use language_model::{
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub use settings::GoogleAvailableModel as AvailableModel;
use settings::{Settings, SettingsStore};
use std::pin::Pin;
use std::sync::{
@@ -60,32 +61,6 @@ pub enum ModelMode {
},
}
impl From<ModelMode> for GoogleModelMode {
fn from(value: ModelMode) -> Self {
match value {
ModelMode::Default => GoogleModelMode::Default,
ModelMode::Thinking { budget_tokens } => GoogleModelMode::Thinking { budget_tokens },
}
}
}
impl From<GoogleModelMode> for ModelMode {
fn from(value: GoogleModelMode) -> Self {
match value {
GoogleModelMode::Default => ModelMode::Default,
GoogleModelMode::Thinking { budget_tokens } => ModelMode::Thinking { budget_tokens },
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct AvailableModel {
name: String,
display_name: Option<String>,
max_tokens: u64,
mode: Option<ModelMode>,
}
pub struct GoogleLanguageModelProvider {
http_client: Arc<dyn HttpClient>,
state: gpui::Entity<State>,
@@ -234,7 +209,7 @@ impl LanguageModelProvider for GoogleLanguageModelProvider {
name: model.name.clone(),
display_name: model.display_name.clone(),
max_tokens: model.max_tokens,
mode: model.mode.unwrap_or_default().into(),
mode: model.mode.unwrap_or_default(),
},
);
}
@@ -15,8 +15,7 @@ use language_model::{
LanguageModelRequest, RateLimiter, Role,
};
use lmstudio::{ModelType, get_models};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub use settings::LmStudioAvailableModel as AvailableModel;
use settings::{Settings, SettingsStore};
use std::pin::Pin;
use std::str::FromStr;
@@ -40,15 +39,6 @@ pub struct LmStudioSettings {
pub available_models: Vec<AvailableModel>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct AvailableModel {
pub name: String,
pub display_name: Option<String>,
pub max_tokens: u64,
pub supports_tool_calls: bool,
pub supports_images: bool,
}
pub struct LmStudioLanguageModelProvider {
http_client: Arc<dyn HttpClient>,
state: gpui::Entity<State>,

Some files were not shown because too many files have changed in this diff Show More