Skip to content

Commit 370fbed

Browse files
Add ⌘K Clear to Start for the integrated terminal
When the terminal is focused, ⌘K clears the screen and scrollback, like Terminal.app. There's a setting to turn it off; it stays on by default. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent cec6287 commit 370fbed

5 files changed

Lines changed: 130 additions & 0 deletions

File tree

CodeEdit/Features/Settings/Pages/TerminalSettings/Models/TerminalSettings.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ extension SettingsData {
1818
[
1919
"Shell",
2020
"Use \"Option\" key as \"Meta\"",
21+
"Clear to Start with ⌘K",
2122
"Use text editor font",
2223
"Font",
2324
"Font Size",
@@ -39,6 +40,11 @@ extension SettingsData {
3940
/// If true, the terminal treats the `Option` key as the `Meta` key
4041
var optionAsMeta: Bool = false
4142

43+
/// If true, pressing ⌘K while the terminal is focused clears the viewport and scrollback.
44+
///
45+
/// Matches Terminal.app / VS Code "Clear to Start" behavior. Enabled by default.
46+
var clearToStartOnCommandK: Bool = true
47+
4248
/// The selected shell to use.
4349
var shell: TerminalShell = .system
4450

@@ -68,6 +74,10 @@ extension SettingsData {
6874
let container = try decoder.container(keyedBy: CodingKeys.self)
6975
self.darkAppearance = try container.decodeIfPresent(Bool.self, forKey: .darkAppearance) ?? false
7076
self.optionAsMeta = try container.decodeIfPresent(Bool.self, forKey: .optionAsMeta) ?? false
77+
self.clearToStartOnCommandK = try container.decodeIfPresent(
78+
Bool.self,
79+
forKey: .clearToStartOnCommandK
80+
) ?? true
7181
self.shell = try container.decodeIfPresent(TerminalShell.self, forKey: .shell) ?? .system
7282
self.font = try container.decodeIfPresent(TerminalFont.self, forKey: .font) ?? .init()
7383
self.cursorStyle = try container.decodeIfPresent(

CodeEdit/Features/Settings/Pages/TerminalSettings/TerminalSettingsView.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct TerminalSettingsView: View {
1616
Section {
1717
shellSelector
1818
optionAsMetaToggle
19+
clearToStartToggle
1920
}
2021
Section {
2122
useTextEditorFontToggle
@@ -69,6 +70,11 @@ private extension TerminalSettingsView {
6970
Toggle("Use \"Option\" key as \"Meta\"", isOn: $settings.optionAsMeta)
7071
}
7172

73+
private var clearToStartToggle: some View {
74+
Toggle("Clear to Start with ⌘K", isOn: $settings.clearToStartOnCommandK)
75+
.help("When enabled, ⌘K clears the terminal viewport and scrollback buffer while the terminal is focused.")
76+
}
77+
7278
private var useTextEditorFontToggle: some View {
7379
Toggle("Use text editor font", isOn: $settings.useTextEditorFont)
7480
}

CodeEdit/Features/TerminalEmulator/Views/CETerminalView.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,32 @@ class CETerminalView: TerminalView {
3737
pasteboard.setString(text, forType: .string)
3838
}
3939

40+
/// Clears the visible terminal contents and the scrollback buffer.
41+
///
42+
/// Equivalent to Terminal.app / VS Code "Clear to Start" (⌘K). Operates on the
43+
/// emulator buffer only — nothing is sent to the shell process.
44+
///
45+
/// Uses CSI sequences processed by SwiftTerm:
46+
/// - `CSI H` — move cursor home
47+
/// - `CSI 2 J` — erase the entire display
48+
/// - `CSI 3 J` — erase saved lines (scrollback)
49+
func clearToStart() {
50+
feed(text: "\u{001B}[H\u{001B}[2J\u{001B}[3J")
51+
}
52+
53+
/// Intercepts ⌘K when ``SettingsData/TerminalSettings/clearToStartOnCommandK`` is enabled.
54+
override func performKeyEquivalent(with event: NSEvent) -> Bool {
55+
let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask)
56+
guard flags == .command,
57+
event.charactersIgnoringModifiers?.lowercased() == "k",
58+
Settings.shared.preferences.terminal.clearToStartOnCommandK else {
59+
return super.performKeyEquivalent(with: event)
60+
}
61+
62+
clearToStart()
63+
return true
64+
}
65+
4066
override open func isAccessibilityElement() -> Bool {
4167
true
4268
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//
2+
// TerminalClearToStartTests.swift
3+
// CodeEditTests
4+
//
5+
// Created for issue #891 — Clear To Start (⌘K) on the integrated terminal.
6+
//
7+
8+
import AppKit
9+
import SwiftTerm
10+
import XCTest
11+
@testable import CodeEdit
12+
13+
final class TerminalClearToStartTests: XCTestCase {
14+
15+
// MARK: - Settings
16+
17+
func testClearToStartSettingDefaultsToTrueWhenMissing() throws {
18+
let json = Data("{}".utf8)
19+
let settings = try JSONDecoder().decode(SettingsData.TerminalSettings.self, from: json)
20+
21+
XCTAssertTrue(
22+
settings.clearToStartOnCommandK,
23+
"⌘K clear should be enabled by default to match Terminal.app / VS Code"
24+
)
25+
}
26+
27+
func testClearToStartSettingDecodesFalse() throws {
28+
let json = Data(#"{"clearToStartOnCommandK":false}"#.utf8)
29+
let settings = try JSONDecoder().decode(SettingsData.TerminalSettings.self, from: json)
30+
31+
XCTAssertFalse(settings.clearToStartOnCommandK)
32+
}
33+
34+
func testClearToStartSettingIsSearchable() {
35+
let settings = SettingsData.TerminalSettings()
36+
let keys = settings.searchKeys.map { $0.lowercased() }
37+
38+
XCTAssertTrue(
39+
keys.contains(where: { $0.contains("clear to start") }),
40+
"Expected a searchable label for Clear to Start / ⌘K, got: \(settings.searchKeys)"
41+
)
42+
}
43+
44+
// MARK: - Clear behavior
45+
46+
@MainActor
47+
func testClearToStartRemovesViewportAndScrollback() {
48+
let terminalView = CELocalShellTerminalView(frame: NSRect(x: 0, y: 0, width: 800, height: 600))
49+
let terminal = terminalView.getTerminal()
50+
51+
// Produce more lines than the viewport so scrollback is non-empty.
52+
for index in 0..<200 {
53+
terminalView.feed(text: "line-\(index)\n")
54+
}
55+
56+
let before = terminal.getText(
57+
start: Position(col: 0, row: 0),
58+
end: Position(col: max(0, terminal.cols - 1), row: max(0, terminal.buffer.yDisp + terminal.rows - 1))
59+
)
60+
XCTAssertTrue(before.contains("line-"), "Precondition: terminal should contain fed output")
61+
62+
terminalView.clearToStart()
63+
64+
XCTAssertEqual(terminal.buffer.x, 0, "Cursor should be at column 0")
65+
XCTAssertEqual(terminal.buffer.y, 0, "Cursor should be at row 0")
66+
XCTAssertEqual(terminal.buffer.yDisp, 0, "Viewport should be scrolled to the top")
67+
68+
let after = terminal.getText(
69+
start: Position(col: 0, row: 0),
70+
end: Position(col: max(0, terminal.cols - 1), row: max(0, terminal.rows - 1))
71+
)
72+
XCTAssertFalse(
73+
after.contains("line-"),
74+
"Cleared terminal should not still show previous output, got: \(after.prefix(200))"
75+
)
76+
77+
let visibleText = terminalView.accessibilityValue() as? String ?? ""
78+
XCTAssertFalse(
79+
visibleText.contains("line-"),
80+
"Accessibility value should also be cleared, got: \(visibleText.prefix(200))"
81+
)
82+
}
83+
}

Documentation.docc/AppPreferences/Sections/TerminalPreferencesView.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# ``CodeEdit/TerminalSettingsView``
22

3+
Configure the integrated terminal: shell, Option-as-Meta, **Clear to Start with ⌘K**, fonts, and cursor.
4+
5+
When **Clear to Start with ⌘K** is enabled (default), pressing ⌘K while the terminal is focused
6+
clears both the viewport and the scrollback buffer, matching Terminal.app and VS Code.
7+
38
## Topics
49

510
### Model

0 commit comments

Comments
 (0)