T17 Editor: CodeEditor with gutter, folds, minimap, rulers, guides, brackets, whitespace, cursors, multi-cursor, sticky scroll, inlay hints, lenses, decorations, ghost text, inline chat and diff (T17a); completions, signatures, hover, code actions, rename, references, outline, go to symbol, call and type hierarchy (T17b); find and replace, search panel and filters (T17c); problems, output, debug console, extensions, settings with JSON, keybindings, theme previews, welcome, recent projects, switcher, task runner (T17d); status items (T17e)
Shared: generic undo History, OverflowMenu icon and tooltip, StatusBarItem leading, six Lucide icons. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c229a096ca
commit
d05754ac19
59 files changed
+10820
-71
No files matched your search
@@ -0,0 +1,287 @@
|
||||
use gpui::{App, Context, InteractiveElement, KeyBinding, Window, actions};
|
||||
|
||||
use super::{buffer::Buffer, cursor::Motion, state::CodeEditor};
|
||||
|
||||
actions!(
|
||||
ely_editor,
|
||||
[
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
SelectLeft,
|
||||
SelectRight,
|
||||
SelectUp,
|
||||
SelectDown,
|
||||
WordLeft,
|
||||
WordRight,
|
||||
SelectWordLeft,
|
||||
SelectWordRight,
|
||||
LineStart,
|
||||
LineEnd,
|
||||
SelectLineStart,
|
||||
SelectLineEnd,
|
||||
DocumentStart,
|
||||
DocumentEnd,
|
||||
SelectDocumentStart,
|
||||
SelectDocumentEnd,
|
||||
PageUp,
|
||||
PageDown,
|
||||
Backspace,
|
||||
Delete,
|
||||
DeleteWordLeft,
|
||||
DeleteLineLeft,
|
||||
Newline,
|
||||
Indent,
|
||||
Outdent,
|
||||
Undo,
|
||||
Redo,
|
||||
SelectAll,
|
||||
Copy,
|
||||
Cut,
|
||||
Paste,
|
||||
AddCursorAbove,
|
||||
AddCursorBelow,
|
||||
SelectNext,
|
||||
SingleCursor,
|
||||
ToggleComment,
|
||||
Fold,
|
||||
Unfold,
|
||||
]
|
||||
);
|
||||
|
||||
pub(crate) const CONTEXT: &str = "ElyEditor";
|
||||
|
||||
/// Binds the editor's keys. `init` calls it.
|
||||
pub(crate) fn bind_keys(cx: &mut App) {
|
||||
let word = if cfg!(target_os = "macos") {
|
||||
"alt"
|
||||
} else {
|
||||
"ctrl"
|
||||
};
|
||||
let context = Some(CONTEXT);
|
||||
let mut bindings = vec![
|
||||
KeyBinding::new("left", Left, context),
|
||||
KeyBinding::new("right", Right, context),
|
||||
KeyBinding::new("up", Up, context),
|
||||
KeyBinding::new("down", Down, context),
|
||||
KeyBinding::new("shift-left", SelectLeft, context),
|
||||
KeyBinding::new("shift-right", SelectRight, context),
|
||||
KeyBinding::new("shift-up", SelectUp, context),
|
||||
KeyBinding::new("shift-down", SelectDown, context),
|
||||
KeyBinding::new(&format!("{word}-left"), WordLeft, context),
|
||||
KeyBinding::new(&format!("{word}-right"), WordRight, context),
|
||||
KeyBinding::new(&format!("{word}-shift-left"), SelectWordLeft, context),
|
||||
KeyBinding::new(&format!("{word}-shift-right"), SelectWordRight, context),
|
||||
KeyBinding::new("home", LineStart, context),
|
||||
KeyBinding::new("end", LineEnd, context),
|
||||
KeyBinding::new("shift-home", SelectLineStart, context),
|
||||
KeyBinding::new("shift-end", SelectLineEnd, context),
|
||||
KeyBinding::new("pageup", PageUp, context),
|
||||
KeyBinding::new("pagedown", PageDown, context),
|
||||
KeyBinding::new("backspace", Backspace, context),
|
||||
KeyBinding::new("shift-backspace", Backspace, context),
|
||||
KeyBinding::new("delete", Delete, context),
|
||||
KeyBinding::new(&format!("{word}-backspace"), DeleteWordLeft, context),
|
||||
KeyBinding::new("enter", Newline, context),
|
||||
KeyBinding::new("tab", Indent, context),
|
||||
KeyBinding::new("shift-tab", Outdent, context),
|
||||
KeyBinding::new("secondary-z", Undo, context),
|
||||
KeyBinding::new("secondary-shift-z", Redo, context),
|
||||
KeyBinding::new("secondary-a", SelectAll, context),
|
||||
KeyBinding::new("secondary-c", Copy, context),
|
||||
KeyBinding::new("secondary-x", Cut, context),
|
||||
KeyBinding::new("secondary-v", Paste, context),
|
||||
KeyBinding::new("secondary-d", SelectNext, context),
|
||||
KeyBinding::new("escape", SingleCursor, context),
|
||||
KeyBinding::new("secondary-/", ToggleComment, context),
|
||||
KeyBinding::new("secondary-alt-[", Fold, context),
|
||||
KeyBinding::new("secondary-alt-]", Unfold, context),
|
||||
];
|
||||
if cfg!(target_os = "macos") {
|
||||
bindings.extend([
|
||||
KeyBinding::new("cmd-left", LineStart, context),
|
||||
KeyBinding::new("cmd-right", LineEnd, context),
|
||||
KeyBinding::new("cmd-shift-left", SelectLineStart, context),
|
||||
KeyBinding::new("cmd-shift-right", SelectLineEnd, context),
|
||||
KeyBinding::new("cmd-up", DocumentStart, context),
|
||||
KeyBinding::new("cmd-down", DocumentEnd, context),
|
||||
KeyBinding::new("cmd-shift-up", SelectDocumentStart, context),
|
||||
KeyBinding::new("cmd-shift-down", SelectDocumentEnd, context),
|
||||
KeyBinding::new("cmd-backspace", DeleteLineLeft, context),
|
||||
KeyBinding::new("cmd-alt-up", AddCursorAbove, context),
|
||||
KeyBinding::new("cmd-alt-down", AddCursorBelow, context),
|
||||
]);
|
||||
} else {
|
||||
bindings.extend([
|
||||
KeyBinding::new("ctrl-home", DocumentStart, context),
|
||||
KeyBinding::new("ctrl-end", DocumentEnd, context),
|
||||
KeyBinding::new("ctrl-shift-home", SelectDocumentStart, context),
|
||||
KeyBinding::new("ctrl-shift-end", SelectDocumentEnd, context),
|
||||
KeyBinding::new("ctrl-alt-up", AddCursorAbove, context),
|
||||
KeyBinding::new("ctrl-alt-down", AddCursorBelow, context),
|
||||
KeyBinding::new("ctrl-y", Redo, context),
|
||||
]);
|
||||
}
|
||||
cx.bind_keys(bindings);
|
||||
}
|
||||
|
||||
/// Rows a page key moves.
|
||||
const PAGE: isize = 20;
|
||||
|
||||
fn line_left(buffer: &Buffer, at: usize) -> std::ops::Range<usize> {
|
||||
buffer.line_range(buffer.line_of(at)).start..at
|
||||
}
|
||||
|
||||
/// Wires every action to the editor that renders `root`.
|
||||
pub(crate) fn listen<E: InteractiveElement>(root: E, cx: &mut Context<CodeEditor>) -> E {
|
||||
macro_rules! on {
|
||||
($root:expr, $action:ty, $body:expr) => {
|
||||
$root.on_action(cx.listener(
|
||||
move |editor: &mut CodeEditor,
|
||||
_: &$action,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<CodeEditor>| {
|
||||
let run: fn(&mut CodeEditor, &mut Window, &mut Context<CodeEditor>) = $body;
|
||||
run(editor, window, cx)
|
||||
},
|
||||
))
|
||||
};
|
||||
}
|
||||
let root = on!(root, Left, |e, _, cx| e.motion(Motion::Left, false, cx));
|
||||
let root = on!(root, Right, |e, _, cx| e.motion(Motion::Right, false, cx));
|
||||
let root = on!(root, Up, |e, _, cx| e.motion(Motion::Up, false, cx));
|
||||
let root = on!(root, Down, |e, _, cx| e.motion(Motion::Down, false, cx));
|
||||
let root = on!(root, SelectLeft, |e, _, cx| e.motion(
|
||||
Motion::Left,
|
||||
true,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, SelectRight, |e, _, cx| e.motion(
|
||||
Motion::Right,
|
||||
true,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, SelectUp, |e, _, cx| e.motion(Motion::Up, true, cx));
|
||||
let root = on!(root, SelectDown, |e, _, cx| e.motion(
|
||||
Motion::Down,
|
||||
true,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, WordLeft, |e, _, cx| e.motion(
|
||||
Motion::WordLeft,
|
||||
false,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, WordRight, |e, _, cx| e.motion(
|
||||
Motion::WordRight,
|
||||
false,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, SelectWordLeft, |e, _, cx| e.motion(
|
||||
Motion::WordLeft,
|
||||
true,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, SelectWordRight, |e, _, cx| e.motion(
|
||||
Motion::WordRight,
|
||||
true,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, LineStart, |e, _, cx| e.motion(
|
||||
Motion::LineStart,
|
||||
false,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, LineEnd, |e, _, cx| e.motion(
|
||||
Motion::LineEnd,
|
||||
false,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, SelectLineStart, |e, _, cx| e.motion(
|
||||
Motion::LineStart,
|
||||
true,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, SelectLineEnd, |e, _, cx| e.motion(
|
||||
Motion::LineEnd,
|
||||
true,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, DocumentStart, |e, _, cx| e.motion(
|
||||
Motion::Start,
|
||||
false,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, DocumentEnd, |e, _, cx| e.motion(
|
||||
Motion::End,
|
||||
false,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, SelectDocumentStart, |e, _, cx| e.motion(
|
||||
Motion::Start,
|
||||
true,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, SelectDocumentEnd, |e, _, cx| e.motion(
|
||||
Motion::End,
|
||||
true,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, PageUp, |e, _, cx| e.motion(
|
||||
Motion::Page(-PAGE),
|
||||
false,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, PageDown, |e, _, cx| e.motion(
|
||||
Motion::Page(PAGE),
|
||||
false,
|
||||
cx
|
||||
));
|
||||
let root = on!(root, Backspace, |e, _, cx| {
|
||||
e.delete_by(|buffer, at| buffer.previous(at)..at, cx)
|
||||
});
|
||||
let root = on!(root, Delete, |e, _, cx| e
|
||||
.delete_by(|buffer, at| at..buffer.next(at), cx));
|
||||
let root = on!(root, DeleteWordLeft, |e, _, cx| {
|
||||
e.delete_by(|buffer, at| buffer.word_start(at)..at, cx)
|
||||
});
|
||||
let root = on!(root, DeleteLineLeft, |e, _, cx| e.delete_by(line_left, cx));
|
||||
let root = on!(root, Newline, |e, _, cx| e.newline(cx));
|
||||
let root = on!(root, Indent, |e, _, cx| e.indent(cx));
|
||||
let root = on!(root, Outdent, |e, _, cx| e.outdent(cx));
|
||||
let root = on!(root, Undo, |e, _, cx| e.undo(cx));
|
||||
let root = on!(root, Redo, |e, _, cx| e.redo(cx));
|
||||
let root = on!(root, SelectAll, |e, _, cx| e.select_all(cx));
|
||||
let root = on!(root, Copy, |e, _, cx| e.copy(cx));
|
||||
let root = on!(root, Cut, |e, _, cx| e.cut(cx));
|
||||
let root = on!(root, Paste, |e, _, cx| e.paste(cx));
|
||||
let root = on!(root, AddCursorAbove, |e, _, cx| e.add_cursor(false, cx));
|
||||
let root = on!(root, AddCursorBelow, |e, _, cx| e.add_cursor(true, cx));
|
||||
let root = on!(root, SelectNext, |e, _, cx| e.select_next(cx));
|
||||
let root = on!(root, SingleCursor, |e, window, cx| {
|
||||
if e.marks.chat.is_some() {
|
||||
e.set_chat(None, window, cx)
|
||||
} else {
|
||||
e.single_cursor(cx)
|
||||
}
|
||||
});
|
||||
let root = on!(root, ToggleComment, |e, _, cx| e.toggle_comment(cx));
|
||||
let root = on!(root, Fold, |e, _, cx| {
|
||||
let line = e.buffer.line_of(e.primary().head);
|
||||
let header = super::syntax::folds(&e.buffer)
|
||||
.into_iter()
|
||||
.filter(|(header, end)| *header <= line && line <= *end)
|
||||
.map(|(header, _)| header)
|
||||
.next_back();
|
||||
if let Some(header) = header.filter(|header| !e.folded.contains(header)) {
|
||||
e.toggle_fold(header, cx);
|
||||
}
|
||||
});
|
||||
on!(root, Unfold, |e, _, cx| {
|
||||
let line = e.buffer.line_of(e.primary().head);
|
||||
if e.folded.contains(&line) {
|
||||
e.toggle_fold(line, cx);
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user