on master help sign in
Refactor :set to parse by deserializing values (#1799)
2b0835bGokul Soumya
helix-core/src/syntax.rs+1−8
@@ -216,14 +216,7 @@ impl FromStr for AutoPairConfig {
216 // only do bool parsing for runtime setting
216 // only do bool parsing for runtime setting
217 fn from_str(s: &str) -> Result<Self, Self::Err> {
217 fn from_str(s: &str) -> Result<Self, Self::Err> {
218 let enable: bool = s.parse()?;
218 let enable: bool = s.parse()?;
219−
220− let enable = if enable {
221− AutoPairConfig::Enable(true)
222− } else {
223− AutoPairConfig::Enable(false)
224− };
225−
226− Ok(enable)
219+ Ok(AutoPairConfig::Enable(enable))
227 }
220 }
228}
221}
229
222
helix-term/src/commands/typed.rs+16−18
@@ -872,34 +872,32 @@ pub(super) fn goto_line_number(
872 Ok(())
872 Ok(())
873}
873}
874
874
875+/// Change config at runtime. Access nested values by dot syntax, for
876+/// example to disable smart case search, use `:set search.smart-case false`.
875fn setting(
877fn setting(
876 cx: &mut compositor::Context,
878 cx: &mut compositor::Context,
877 args: &[Cow<str>],
879 args: &[Cow<str>],
878 _event: PromptEvent,
880 _event: PromptEvent,
879) -> anyhow::Result<()> {
881) -> anyhow::Result<()> {
880− let runtime_config = &mut cx.editor.config;
881−
882 if args.len() != 2 {
882 if args.len() != 2 {
883 anyhow::bail!("Bad arguments. Usage: `:set key field`");
883 anyhow::bail!("Bad arguments. Usage: `:set key field`");
884 }
884 }
885−
886 let (key, arg) = (&args[0].to_lowercase(), &args[1]);
885 let (key, arg) = (&args[0].to_lowercase(), &args[1]);
887
886
888− match key.as_ref() {
889− "scrolloff" => runtime_config.scrolloff = arg.parse()?,
890− "scroll-lines" => runtime_config.scroll_lines = arg.parse()?,
891− "mouse" => runtime_config.mouse = arg.parse()?,
892− "line-number" => runtime_config.line_number = arg.parse()?,
893− "middle-click_paste" => runtime_config.middle_click_paste = arg.parse()?,
894− "auto-pairs" => runtime_config.auto_pairs = arg.parse()?,
895− "auto-completion" => runtime_config.auto_completion = arg.parse()?,
896− "completion-trigger-len" => runtime_config.completion_trigger_len = arg.parse()?,
897− "auto-info" => runtime_config.auto_info = arg.parse()?,
898− "true-color" => runtime_config.true_color = arg.parse()?,
899− "search.smart-case" => runtime_config.search.smart_case = arg.parse()?,
900− "search.wrap-around" => runtime_config.search.wrap_around = arg.parse()?,
901− _ => anyhow::bail!("Unknown key `{}`.", args[0]),
887+ let key_error = || anyhow::anyhow!("Unknown key `{key}`");
902− }
888+ let field_error = |_| anyhow::anyhow!("Could not parse field `{arg}`");
889+
890+ let mut config = serde_json::to_value(&cx.editor.config).unwrap();
891+ let pointer = format!("/{}", key.replace('.', "/"));
892+ let value = config.pointer_mut(&pointer).ok_or_else(key_error)?;
893+
894+ *value = if value.is_string() {
895+ // JSON strings require quotes, so we can't .parse() directly
896+ serde_json::Value::String(arg.to_string())
897+ } else {
898+ arg.parse().map_err(field_error)?
899+ };
900+ cx.editor.config = serde_json::from_value(config).map_err(field_error)?;
903
901
904 Ok(())
902 Ok(())
905}
903}
helix-view/src/editor.rs+19−3
@@ -38,7 +38,7 @@ use helix_core::{
38use helix_core::{Position, Selection};
38use helix_core::{Position, Selection};
39use helix_dap as dap;
39use helix_dap as dap;
40
40
41−use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize};
41+use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
42
42
43fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result<Duration, D::Error>
43fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result<Duration, D::Error>
44where
44where
@@ -48,6 +48,18 @@ where
48 Ok(Duration::from_millis(millis))
48 Ok(Duration::from_millis(millis))
49}
49}
50
50
51+fn serialize_duration_millis<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
52+where
53+ S: Serializer,
54+{
55+ serializer.serialize_u64(
56+ duration
57+ .as_millis()
58+ .try_into()
59+ .map_err(|_| serde::ser::Error::custom("duration value overflowed u64"))?,
60+ )
61+}
62+
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
63#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
52#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
64#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
53pub struct FilePickerConfig {
65pub struct FilePickerConfig {
@@ -109,8 +121,12 @@ pub struct Config {
109 pub auto_pairs: AutoPairConfig,
121 pub auto_pairs: AutoPairConfig,
110 /// Automatic auto-completion, automatically pop up without user trigger. Defaults to true.
122 /// Automatic auto-completion, automatically pop up without user trigger. Defaults to true.
111 pub auto_completion: bool,
123 pub auto_completion: bool,
112− /// Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. Defaults to 400ms.
124+ /// Time in milliseconds since last keypress before idle timers trigger.
125+ /// Used for autocompletion, set to 0 for instant. Defaults to 400ms.
126+ #[serde(
127+ serialize_with = "serialize_duration_millis",
113− #[serde(skip_serializing, deserialize_with = "deserialize_duration_millis")]
128+ deserialize_with = "deserialize_duration_millis"
129+ )]
114 pub idle_timeout: Duration,
130 pub idle_timeout: Duration,
115 pub completion_trigger_len: u8,
131 pub completion_trigger_len: u8,
116 /// Whether to display infoboxes. Defaults to true.
132 /// Whether to display infoboxes. Defaults to true.