on master help sign in
Update settings at runtime (#798)
a306a10Tamo
book/src/generated/typable-cmd.md+1
@@ -41,3 +41,4 @@
41| `:hsplit`, `:hs`, `:sp` | Open the file in a horizontal split. |
41| `:hsplit`, `:hs`, `:sp` | Open the file in a horizontal split. |
42| `:tutor` | Open the tutorial. |
42| `:tutor` | Open the tutorial. |
43| `:goto`, `:g` | Go to line number. |
43| `:goto`, `:g` | Go to line number. |
44+| `:set-option`, `:set` | Set a config option at runtime |
helix-term/src/commands.rs+37
@@ -2637,6 +2637,36 @@ pub mod cmd {
2637 let (view, doc) = current!(cx.editor);
2637 let (view, doc) = current!(cx.editor);
2638
2638
2639 view.ensure_cursor_in_view(doc, line);
2639 view.ensure_cursor_in_view(doc, line);
2640+ Ok(())
2641+ }
2642+
2643+ fn setting(
2644+ cx: &mut compositor::Context,
2645+ args: &[Cow<str>],
2646+ _event: PromptEvent,
2647+ ) -> anyhow::Result<()> {
2648+ let runtime_config = &mut cx.editor.config;
2649+
2650+ if args.len() != 2 {
2651+ anyhow::bail!("Bad arguments. Usage: `:set key field`");
2652+ }
2653+
2654+ let (key, arg) = (&args[0].to_lowercase(), &args[1]);
2655+
2656+ match key.as_ref() {
2657+ "scrolloff" => runtime_config.scrolloff = arg.parse()?,
2658+ "scroll-lines" => runtime_config.scroll_lines = arg.parse()?,
2659+ "mouse" => runtime_config.mouse = arg.parse()?,
2660+ "line-number" => runtime_config.line_number = arg.parse()?,
2661+ "middle-click_paste" => runtime_config.middle_click_paste = arg.parse()?,
2662+ "smart-case" => runtime_config.smart_case = arg.parse()?,
2663+ "auto-pairs" => runtime_config.auto_pairs = arg.parse()?,
2664+ "auto-completion" => runtime_config.auto_completion = arg.parse()?,
2665+ "completion-trigger-len" => runtime_config.completion_trigger_len = arg.parse()?,
2666+ "auto-info" => runtime_config.auto_info = arg.parse()?,
2667+ "true-color" => runtime_config.true_color = arg.parse()?,
2668+ _ => anyhow::bail!("Unknown key `{}`.", args[0]),
2669+ }
2640
2670
2641 Ok(())
2671 Ok(())
2642 }
2672 }
@@ -2928,6 +2958,13 @@ pub mod cmd {
2928 doc: "Go to line number.",
2958 doc: "Go to line number.",
2929 fun: goto_line_number,
2959 fun: goto_line_number,
2930 completer: None,
2960 completer: None,
2961+ },
2962+ TypableCommand {
2963+ name: "set-option",
2964+ aliases: &["set"],
2965+ doc: "Set a config option at runtime",
2966+ fun: setting,
2967+ completer: Some(completers::setting),
2931 }
2968 }
2932 ];
2969 ];
2933
2970
helix-term/src/ui/mod.rs+27
@@ -174,7 +174,9 @@ pub mod completers {
174 use crate::ui::prompt::Completion;
174 use crate::ui::prompt::Completion;
175 use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
175 use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
176 use fuzzy_matcher::FuzzyMatcher;
176 use fuzzy_matcher::FuzzyMatcher;
177+ use helix_view::editor::Config;
177 use helix_view::theme;
178 use helix_view::theme;
179+ use once_cell::sync::Lazy;
178 use std::borrow::Cow;
180 use std::borrow::Cow;
179 use std::cmp::Reverse;
181 use std::cmp::Reverse;
180
182
@@ -208,6 +210,31 @@ pub mod completers {
208 names
210 names
209 }
211 }
210
212
213+ pub fn setting(input: &str) -> Vec<Completion> {
214+ static KEYS: Lazy<Vec<String>> = Lazy::new(|| {
215+ serde_json::to_value(Config::default())
216+ .unwrap()
217+ .as_object()
218+ .unwrap()
219+ .keys()
220+ .cloned()
221+ .collect()
222+ });
223+
224+ let matcher = Matcher::default();
225+
226+ let mut matches: Vec<_> = KEYS
227+ .iter()
228+ .filter_map(|name| matcher.fuzzy_match(name, input).map(|score| (name, score)))
229+ .collect();
230+
231+ matches.sort_unstable_by_key(|(_file, score)| Reverse(*score));
232+ matches
233+ .into_iter()
234+ .map(|(name, _)| ((0..), name.into()))
235+ .collect()
236+ }
237+
211 pub fn filename(input: &str) -> Vec<Completion> {
238 pub fn filename(input: &str) -> Vec<Completion> {
212 filename_impl(input, |entry| {
239 filename_impl(input, |entry| {
213 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
240 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
helix-view/src/editor.rs+16−4
@@ -27,7 +27,7 @@ pub use helix_core::register::Registers;
27use helix_core::syntax;
27use helix_core::syntax;
28use helix_core::{Position, Selection};
28use helix_core::{Position, Selection};
29
29
30−use serde::Deserialize;
30+use serde::{Deserialize, Serialize};
31
31
32fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result<Duration, D::Error>
32fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result<Duration, D::Error>
33where
33where
@@ -37,7 +37,7 @@ where
37 Ok(Duration::from_millis(millis))
37 Ok(Duration::from_millis(millis))
38}
38}
39
39
40−#[derive(Debug, Clone, PartialEq, Deserialize)]
40+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
41#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
41#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
42pub struct FilePickerConfig {
42pub struct FilePickerConfig {
43 /// IgnoreOptions
43 /// IgnoreOptions
@@ -77,7 +77,7 @@ impl Default for FilePickerConfig {
77 }
77 }
78}
78}
79
79
80−#[derive(Debug, Clone, PartialEq, Deserialize)]
80+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
81#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
81#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
82pub struct Config {
82pub struct Config {
83 /// Padding to keep between the edge of the screen and the cursor when scrolling. Defaults to 5.
83 /// Padding to keep between the edge of the screen and the cursor when scrolling. Defaults to 5.
@@ -109,7 +109,7 @@ pub struct Config {
109 pub true_color: bool,
109 pub true_color: bool,
110}
110}
111
111
112−#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
112+#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
113#[serde(rename_all = "kebab-case")]
113#[serde(rename_all = "kebab-case")]
114pub enum LineNumber {
114pub enum LineNumber {
115 /// Show absolute line number
115 /// Show absolute line number
@@ -119,6 +119,18 @@ pub enum LineNumber {
119 Relative,
119 Relative,
120}
120}
121
121
122+impl std::str::FromStr for LineNumber {
123+ type Err = anyhow::Error;
124+
125+ fn from_str(s: &str) -> Result<Self, Self::Err> {
126+ match s.to_lowercase().as_str() {
127+ "absolute" | "abs" => Ok(Self::Absolute),
128+ "relative" | "rel" => Ok(Self::Relative),
129+ _ => anyhow::bail!("Line number can only be `absolute` or `relative`."),
130+ }
131+ }
132+}
133+
122impl Default for Config {
134impl Default for Config {
123 fn default() -> Self {
135 fn default() -> Self {
124 Self {
136 Self {