Merge branch 'ide/editing' into feat/editor-ide

# Conflicts:
#	src/ui/code_editor.rs
#	src/ui/keymap.rs
This commit is contained in:
l0ng-ai
2026-09-28 17:20:38 +08:00
6 changed files with 123 additions and 0 deletions
+20
View File
@@ -2187,6 +2187,22 @@ impl Tty7App {
)
.menu(t(L10nKey::AppMenuSelectAll), Box::new(edit::SelectAll))
.separator()
// The line commands act on every line a cursor or selection
// touches. Their chords live on gpui-component's `CodeEditor` key
// context and are re-added in `keymap::fixed_bindings`, so they
// beat the app's own keys while the editor has focus.
.menu(
t(L10nKey::EditorToggleComment),
Box::new(edit::ToggleLineComment),
)
.menu(t(L10nKey::EditorMoveLineUp), Box::new(edit::MoveLineUp))
.menu(t(L10nKey::EditorMoveLineDown), Box::new(edit::MoveLineDown))
.menu(
t(L10nKey::EditorDuplicateLine),
Box::new(edit::CopyLineDown),
)
.menu(t(L10nKey::EditorDeleteLine), Box::new(edit::DeleteLine))
.separator()
.menu(t(L10nKey::AppMenuFind), Box::new(edit::Search))
.menu(
t(L10nKey::EditorGoToLineAction),
@@ -2196,6 +2212,10 @@ impl Tty7App {
t(L10nKey::EditorGitRevertChange),
Box::new(crate::core::actions::EditorRevertChange),
!this.editor_gutter_can_revert(id, cx),
)
.menu(
t(L10nKey::EditorGoToMatchingBracket),
Box::new(edit::MoveToMatchingBracket),
);
let menu = this.lsp_menu_items(menu, id, cx);
this.editor_file_menu_items(menu, id, app, cx)
+6
View File
@@ -1208,6 +1208,12 @@ pub fn translate_en(key: L10nKey) -> &'static str {
L10nKey::EditorAlreadyOpen => "\"{path}\" is already open in the editor",
L10nKey::EditorGoToLine => "Go to line",
L10nKey::EditorGoToLineAction => "Go to Line…",
L10nKey::EditorGoToMatchingBracket => "Go to Matching Bracket",
L10nKey::EditorToggleComment => "Toggle Comment",
L10nKey::EditorMoveLineUp => "Move Line Up",
L10nKey::EditorMoveLineDown => "Move Line Down",
L10nKey::EditorDuplicateLine => "Duplicate Line",
L10nKey::EditorDeleteLine => "Delete Line",
L10nKey::EditorCopyRelativePath => "Copy Relative Path",
L10nKey::EditorGitNextChange => "Go to Next Change",
L10nKey::EditorGitPrevChange => "Go to Previous Change",
+6
View File
@@ -1256,6 +1256,12 @@ pub fn translate_ja(key: L10nKey) -> Option<&'static str> {
L10nKey::EditorAlreadyOpen => "「{path}」はすでにエディタで開かれています",
L10nKey::EditorGoToLine => "行へ移動",
L10nKey::EditorGoToLineAction => "行へ移動…",
L10nKey::EditorGoToMatchingBracket => "対応する括弧へ移動",
L10nKey::EditorToggleComment => "コメントの切り替え",
L10nKey::EditorMoveLineUp => "行を上へ移動",
L10nKey::EditorMoveLineDown => "行を下へ移動",
L10nKey::EditorDuplicateLine => "行を複製",
L10nKey::EditorDeleteLine => "行を削除",
L10nKey::EditorCopyRelativePath => "相対パスをコピー",
L10nKey::EditorGitNextChange => "次の変更へ移動",
L10nKey::EditorGitPrevChange => "前の変更へ移動",
+6
View File
@@ -940,6 +940,12 @@ l10n_keys! {
EditorAlreadyOpen,
EditorGoToLine,
EditorGoToLineAction,
EditorGoToMatchingBracket,
EditorToggleComment,
EditorMoveLineUp,
EditorMoveLineDown,
EditorDuplicateLine,
EditorDeleteLine,
EditorCopyRelativePath,
EditorGitNextChange,
EditorGitPrevChange,
+6
View File
@@ -1128,6 +1128,12 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> {
L10nKey::EditorAlreadyOpen => "“{path}”已在编辑器中打开",
L10nKey::EditorGoToLine => "跳转到行",
L10nKey::EditorGoToLineAction => "跳转到行…",
L10nKey::EditorGoToMatchingBracket => "跳转到匹配的括号",
L10nKey::EditorToggleComment => "切换注释",
L10nKey::EditorMoveLineUp => "上移行",
L10nKey::EditorMoveLineDown => "下移行",
L10nKey::EditorDuplicateLine => "复制行",
L10nKey::EditorDeleteLine => "删除行",
L10nKey::EditorCopyRelativePath => "复制相对路径",
L10nKey::EditorGitNextChange => "转到下一处更改",
L10nKey::EditorGitPrevChange => "转到上一处更改",
+79
View File
@@ -110,6 +110,32 @@ fn fixed_bindings() -> Vec<KeyBinding> {
bindings.push(KeyBinding::new("alt-f5", EditorNextChange, input));
bindings.push(KeyBinding::new("shift-alt-f5", EditorPrevChange, input));
}
// The code editor's line commands, for the same tie: ⌘/ is the shortcut
// sheet, ⌘↵ fullscreen, ⌘⇧↵ maximize and ⌥↑/⌥↓ pane focus off macOS, all
// context-free. Bound on `CodeEditor`, which only a multi-line code
// editor declares, so every other text field and the terminal keep the
// app's keys.
{
use gpui_component::input::{
CODE_EDITOR_CONTEXT, CopyLineDown, CopyLineUp, DeleteLine, InsertLineAbove,
InsertLineBelow, MoveLineDown, MoveLineUp, MoveToMatchingBracket, SelectLine,
ToggleBlockComment, ToggleLineComment,
};
let editor = Some(CODE_EDITOR_CONTEXT);
bindings.extend([
KeyBinding::new("secondary-/", ToggleLineComment, editor),
KeyBinding::new("alt-shift-a", ToggleBlockComment, editor),
KeyBinding::new("alt-up", MoveLineUp, editor),
KeyBinding::new("alt-down", MoveLineDown, editor),
KeyBinding::new("alt-shift-up", CopyLineUp, editor),
KeyBinding::new("alt-shift-down", CopyLineDown, editor),
KeyBinding::new("secondary-shift-k", DeleteLine, editor),
KeyBinding::new("secondary-enter", InsertLineBelow, editor),
KeyBinding::new("secondary-shift-enter", InsertLineAbove, editor),
KeyBinding::new("secondary-l", SelectLine, editor),
KeyBinding::new("secondary-shift-\\", MoveToMatchingBracket, editor),
]);
}
bindings
}
@@ -2730,6 +2756,59 @@ mod gpui_tests {
.collect()
}
/// The code editor's line commands sit on chords the app also spends —
/// ⌘/ is the shortcut sheet, ⌘↵ fullscreen, ⌘⇧↵ maximize, ⌥↑/⌥↓ pane
/// focus off macOS. `fixed_bindings` re-adds them on the `CodeEditor`
/// context the editor declares beside `Input`, after the app's table, so
/// inside the editor they win and everywhere else nothing changes.
#[gpui::test]
fn the_code_editor_chords_win_inside_the_editor(cx: &mut TestAppContext) {
cx.update(|cx| {
running_on_json(cx, "{}");
let first = |keys: &str, stack: &[&str]| {
let input: Vec<Keystroke> = keys
.split(' ')
.map(|k| Keystroke::parse(k).expect("the typed keystroke parses"))
.collect();
let stack: Vec<gpui::KeyContext> = stack
.iter()
.map(|c| gpui::KeyContext::parse(c).expect("the context parses"))
.collect();
cx.key_bindings()
.borrow()
.bindings_for_input(&input, &stack)
.0
.first()
.map(|b| b.action().name())
};
let editor = ["Workspace", "Input CodeEditor"];
for (keys, action) in [
("secondary-/", "input::ToggleLineComment"),
("alt-up", "input::MoveLineUp"),
("alt-down", "input::MoveLineDown"),
("alt-shift-up", "input::CopyLineUp"),
("alt-shift-down", "input::CopyLineDown"),
("secondary-shift-k", "input::DeleteLine"),
("secondary-enter", "input::InsertLineBelow"),
("secondary-shift-enter", "input::InsertLineAbove"),
("secondary-l", "input::SelectLine"),
("secondary-shift-\\", "input::MoveToMatchingBracket"),
] {
assert_eq!(first(keys, &editor), Some(action), "{keys} in the editor");
assert_ne!(
first(keys, &["Workspace", "Input"]),
Some(action),
"{keys} in a plain text field"
);
assert_ne!(
first(keys, &["Workspace", "Terminal"]),
Some(action),
"{keys} in a terminal"
);
}
});
}
#[gpui::test]
fn a_chord_added_in_config_keeps_the_default_one(cx: &mut TestAppContext) {
use gpui::Action as _;