Expose REPL Settings (#37927)

Casper van Elteren , Marshall Bowers , and Kirill Bulatov created

Closes #37829

This PR introduces and exposes `REPLSettings` to control the number of
lines and columns in the REPL. These settings are integrated into the
existing configuration system, allowing for customization and management
through the standard settings interface.

#### Changes
- Added `REPLSettings` struct with `max_number_of_lines` and
`max_number_of_columns` fields.
- Integrated `REPLSettings` with the settings system by implementing the
`Settings` trait.
- Ensured compatibility with the workspace and existing settings
infrastructure.

Release Notes:

- Add configuration "repl" to settings to configure max lines and
columns for repl.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>

Change summary

Cargo.lock                       |  1 
assets/settings/default.json     |  9 +++++
crates/repl/Cargo.toml           |  1 
crates/repl/src/outputs/plain.rs |  8 +---
crates/repl/src/repl.rs          |  3 +
crates/repl/src/repl_settings.rs | 55 ++++++++++++++++++++++++++++++++++
docs/src/configuring-zed.md      | 17 ++++++++++
7 files changed, 89 insertions(+), 5 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -13623,6 +13623,7 @@ dependencies = [
  "runtimelib",
  "schemars",
  "serde",
+ "serde_derive",
  "serde_json",
  "settings",
  "smol",

assets/settings/default.json 🔗

@@ -1844,6 +1844,15 @@
     //    "typescript": "deno"
     // }
   },
+  // Repl settings.
+  "repl": {
+    // Maximum number of columns to keep in REPL's scrollback buffer.
+    // Clamped with [20, 512] range.
+    "max_columns": 128,
+    // Maximum number of lines to keep in REPL's scrollback buffer.
+    // Clamped with [4, 256] range.
+    "max_lines": 32
+  },
   // Vim settings
   "vim": {
     "default_mode": "normal",

crates/repl/Cargo.toml 🔗

@@ -40,6 +40,7 @@ project.workspace = true
 runtimelib.workspace = true
 schemars.workspace = true
 serde.workspace = true
+serde_derive.workspace = true
 serde_json.workspace = true
 settings.workspace = true
 smol.workspace = true

crates/repl/src/outputs/plain.rs 🔗

@@ -31,6 +31,7 @@ use theme::ThemeSettings;
 use ui::{IntoElement, prelude::*};
 
 use crate::outputs::OutputContent;
+use crate::repl_settings::ReplSettings;
 
 /// The `TerminalOutput` struct handles the parsing and rendering of text input,
 /// simulating a basic terminal environment within REPL output.
@@ -53,9 +54,6 @@ pub struct TerminalOutput {
     handler: alacritty_terminal::Term<VoidListener>,
 }
 
-const DEFAULT_NUM_LINES: usize = 32;
-const DEFAULT_NUM_COLUMNS: usize = 128;
-
 /// Returns the default text style for the terminal output.
 pub fn text_style(window: &mut Window, cx: &mut App) -> TextStyle {
     let settings = ThemeSettings::get_global(cx).clone();
@@ -99,8 +97,8 @@ pub fn terminal_size(window: &mut Window, cx: &mut App) -> terminal::TerminalBou
         .unwrap()
         .width;
 
-    let num_lines = DEFAULT_NUM_LINES;
-    let columns = DEFAULT_NUM_COLUMNS;
+    let num_lines = ReplSettings::get_global(cx).max_lines;
+    let columns = ReplSettings::get_global(cx).max_columns;
 
     // Reversed math from terminal::TerminalSize to get pixel width according to terminal width
     let width = columns as f32 * cell_width;

crates/repl/src/repl.rs 🔗

@@ -5,6 +5,7 @@ pub mod notebook;
 mod outputs;
 mod repl_editor;
 mod repl_sessions_ui;
+mod repl_settings;
 mod repl_store;
 mod session;
 
@@ -22,6 +23,7 @@ pub use crate::repl_editor::*;
 pub use crate::repl_sessions_ui::{
     ClearOutputs, Interrupt, ReplSessionsPage, Restart, Run, Sessions, Shutdown,
 };
+pub use crate::repl_settings::ReplSettings;
 use crate::repl_store::ReplStore;
 pub use crate::session::Session;
 
@@ -31,6 +33,7 @@ pub fn init(fs: Arc<dyn Fs>, cx: &mut App) {
     set_dispatcher(zed_dispatcher(cx));
     JupyterSettings::register(cx);
     ::editor::init_settings(cx);
+    ReplSettings::register(cx);
     repl_sessions_ui::init(cx);
     ReplStore::init(fs, cx);
 }

crates/repl/src/repl_settings.rs 🔗

@@ -0,0 +1,55 @@
+use gpui::App;
+use schemars::JsonSchema;
+use serde_derive::{Deserialize, Serialize};
+use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
+
+/// Settings for configuring REPL display and behavior.
+#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
+#[settings_key(key = "repl")]
+pub struct ReplSettings {
+    /// Maximum number of lines to keep in REPL's scrollback buffer.
+    /// Clamped with [4, 256] range.
+    ///
+    /// Default: 32
+    #[serde(default = "default_max_lines")]
+    pub max_lines: usize,
+    /// Maximum number of columns to keep in REPL's scrollback buffer.
+    /// Clamped with [20, 512] range.
+    ///
+    /// Default: 128
+    #[serde(default = "default_max_columns")]
+    pub max_columns: usize,
+}
+
+impl Settings for ReplSettings {
+    type FileContent = Self;
+
+    fn load(sources: SettingsSources<Self::FileContent>, _cx: &mut App) -> anyhow::Result<Self> {
+        let mut settings: ReplSettings = sources.json_merge()?;
+        settings.max_columns = settings.max_columns.clamp(20, 512);
+        settings.max_lines = settings.max_lines.clamp(4, 256);
+        Ok(settings)
+    }
+
+    fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
+}
+
+const DEFAULT_NUM_LINES: usize = 32;
+const DEFAULT_NUM_COLUMNS: usize = 128;
+
+fn default_max_lines() -> usize {
+    DEFAULT_NUM_LINES
+}
+
+fn default_max_columns() -> usize {
+    DEFAULT_NUM_COLUMNS
+}
+
+impl Default for ReplSettings {
+    fn default() -> Self {
+        ReplSettings {
+            max_lines: DEFAULT_NUM_LINES,
+            max_columns: DEFAULT_NUM_COLUMNS,
+        }
+    }
+}

docs/src/configuring-zed.md 🔗

@@ -4026,6 +4026,23 @@ Example command to set the title: `echo -e "\e]2;New Title\007";`
 }
 ```
 
+## REPL
+
+- Description: Repl settings.
+- Setting: `repl`
+- Default:
+
+```json
+"repl": {
+  // Maximum number of columns to keep in REPL's scrollback buffer.
+  // Clamped with [20, 512] range.
+  "max_columns": 128,
+  // Maximum number of lines to keep in REPL's scrollback buffer.
+  // Clamped with [4, 256] range.
+  "max_lines": 32
+},
+```
+
 ## Theme
 
 - Description: The theme setting can be specified in two forms - either as the name of a theme or as an object containing the `mode`, `dark`, and `light` themes for the Zed UI.