vtcode_core/terminal_setup/features/
multiline.rs1use crate::terminal_setup::detector::TerminalType;
7use anyhow::Result;
8
9pub fn generate_config(terminal_type: TerminalType) -> Result<String> {
11 let config = match terminal_type {
12 TerminalType::Ghostty => generate_ghostty_config(),
13 TerminalType::Kitty => generate_kitty_config(),
14 TerminalType::Alacritty => generate_alacritty_config(),
15 TerminalType::WezTerm => generate_wezterm_config(),
16 TerminalType::TerminalApp => generate_terminal_app_instructions(),
17 TerminalType::Xterm => generate_xterm_instructions(),
18 TerminalType::Zed => generate_zed_config(),
19 TerminalType::Warp => {
20 String::new()
22 }
23 TerminalType::ITerm2 => generate_iterm2_instructions(),
24 TerminalType::VSCode => generate_vscode_instructions(),
25 TerminalType::WindowsTerminal => generate_windows_terminal_config(),
26 TerminalType::Hyper => generate_hyper_config(),
27 TerminalType::Tabby => generate_tabby_config(),
28 TerminalType::Unknown => {
29 anyhow::bail!("Cannot generate multiline config for unknown terminal")
30 }
31 };
32
33 Ok(config)
34}
35
36fn generate_wezterm_config() -> String {
38 r#"keys = {
39 { key = "Enter", mods = "SHIFT", action = wezterm.action.SendString("\n") },
40}
41"#
42 .to_string()
43}
44
45fn generate_terminal_app_instructions() -> String {
47 r#"Terminal.app uses profile key mappings.
48Add Shift+Enter mapping to send \n in your active profile."#
49 .to_string()
50}
51
52fn generate_xterm_instructions() -> String {
54 r#"xterm multiline can be configured through X resources or window manager key mapping.
55Ensure Shift+Enter sends a newline sequence."#
56 .to_string()
57}
58
59fn generate_ghostty_config() -> String {
61 "keybind = shift+enter=text:\\n".to_string()
62}
63
64fn generate_kitty_config() -> String {
66 "map shift+enter send_text all \\n".to_string()
67}
68
69fn generate_alacritty_config() -> String {
71 r#"[[keyboard.bindings]]
72key = "Return"
73mods = "Shift"
74chars = "\n"
75"#
76 .to_string()
77}
78
79fn generate_zed_config() -> String {
81 r#"{
82 "context": "Terminal",
83 "bindings": {
84 "shift-enter": ["terminal::SendText", "\n"]
85 }
86}
87"#
88 .to_string()
89}
90
91fn generate_windows_terminal_config() -> String {
93 r#"{
94 "actions": [
95 {
96 "command": {
97 "action": "sendInput",
98 "input": "\n"
99 },
100 "keys": "shift+enter"
101 }
102 ]
103}
104"#
105 .to_string()
106}
107
108fn generate_hyper_config() -> String {
110 r#"// In your .hyper.js config:
111module.exports = {
112 config: {
113 // ... other config
114 },
115 keymaps: {
116 'window:devtools': 'cmd+alt+i',
117 'window:reload': 'cmd+shift+r',
118 'tab:new': 'cmd+t',
119 'shift+enter': 'editor:newline'
120 }
121}
122"#
123 .to_string()
124}
125
126fn generate_tabby_config() -> String {
128 r#"hotkeys:
129 multiline-input:
130 - Shift-Enter
131terminal:
132 sendInputOnEnter: true
133"#
134 .to_string()
135}
136
137fn generate_iterm2_instructions() -> String {
139 r#"iTerm2 Manual Setup Instructions:
140
1411. Open iTerm2 Preferences (Cmd+,)
1422. Go to Profiles → Keys
1433. Click the "+" button to add a new key mapping
1444. Press Shift+Enter when prompted
1455. Set Action to "Send Text"
1466. Enter "\n" (without quotes) in the text field
1477. Click OK to save
148
149This will bind Shift+Enter to insert a newline character.
150"#
151 .to_string()
152}
153
154fn generate_vscode_instructions() -> String {
156 r#"VS Code Terminal Manual Setup:
157
158Add this to your keybindings.json (Cmd+K Cmd+S to open):
159
160{
161 "key": "shift+enter",
162 "command": "workbench.action.terminal.sendSequence",
163 "when": "terminalFocus",
164 "args": { "text": "\n" }
165}
166
167Or use the UI:
1681. Open Command Palette (Cmd+Shift+P)
1692. Search for "Preferences: Open Keyboard Shortcuts (JSON)"
1703. Add the keybinding above to the array
171"#
172 .to_string()
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn test_generate_ghostty_config() {
181 let config = generate_config(TerminalType::Ghostty).unwrap();
182 assert!(config.contains("keybind"));
183 assert!(config.contains("shift+enter"));
184 }
185
186 #[test]
187 fn test_generate_kitty_config() {
188 let config = generate_config(TerminalType::Kitty).unwrap();
189 assert!(config.contains("map shift+enter"));
190 assert!(config.contains("send_text"));
191 }
192
193 #[test]
194 fn test_generate_alacritty_config() {
195 let config = generate_config(TerminalType::Alacritty).unwrap();
196 assert!(config.contains("keyboard.bindings"));
197 assert!(config.contains("Return"));
198 assert!(config.contains("Shift"));
199 }
200
201 #[test]
202 fn test_generate_windows_terminal_config() {
203 let config = generate_config(TerminalType::WindowsTerminal).unwrap();
204 assert!(config.contains("sendInput"));
205 assert!(config.contains("shift+enter"));
206 }
207
208 #[test]
209 fn test_warp_no_config_needed() {
210 let config = generate_config(TerminalType::Warp).unwrap();
211 assert!(config.is_empty());
212 }
213
214 #[test]
215 fn test_iterm2_instructions() {
216 let config = generate_config(TerminalType::ITerm2).unwrap();
217 assert!(config.contains("Manual Setup"));
218 assert!(config.contains("Preferences"));
219 }
220
221 #[test]
222 fn test_unknown_terminal_error() {
223 let result = generate_config(TerminalType::Unknown);
224 result.unwrap_err();
225 }
226 #[test]
227 fn zed_binding_sends_literal_newline_only_in_terminal_context() {
228 let value: serde_json::Value = serde_json::from_str(&generate_config(TerminalType::Zed).unwrap()).unwrap();
229 assert_eq!(value["context"], "Terminal");
230 assert_eq!(value["bindings"]["shift-enter"], serde_json::json!(["terminal::SendText", "\n"]));
231 assert_eq!(value["bindings"].as_object().unwrap().len(), 1);
232 }
233}