@@ -13,7 +13,6 @@ use helix_core::{
13use helix_view::{
13use helix_view::{
14 document::{IndentStyle, Mode},
14 document::{IndentStyle, Mode},
15 editor::Action,
15 editor::Action,
17 input::KeyEvent,
16 input::KeyEvent,
18 keyboard::KeyCode,
17 keyboard::KeyCode,
19 view::{View, PADDING},
18 view::{View, PADDING},
@@ -36,7 +35,6 @@ use crate::{
37use crate::job::{self, Job, Jobs};
36use crate::job::{self, Job, Jobs};
38use futures_util::{FutureExt, TryFutureExt};
37use futures_util::{FutureExt, TryFutureExt};
39−use std::collections::HashMap;
40use std::num::NonZeroUsize;
38use std::num::NonZeroUsize;
41use std::{fmt, future::Future};
39use std::{fmt, future::Future};
@@ -45,7 +43,7 @@ use std::{
45 path::{Path, PathBuf},
43 path::{Path, PathBuf},
48−use once_cell::sync::{Lazy, OnceCell};
46+use once_cell::sync::Lazy;
49use serde::de::{self, Deserialize, Deserializer};
47use serde::de::{self, Deserialize, Deserializer};
51pub struct Context<'a> {
49pub struct Context<'a> {
@@ -74,18 +72,6 @@ impl<'a> Context<'a> {
74 self.on_next_key_callback = Some(Box::new(on_next_key_callback));
72 self.on_next_key_callback = Some(Box::new(on_next_key_callback));
78− pub fn on_next_key_mode(&mut self, map: HashMap<KeyEvent, fn(&mut Context)>) {
79− let count = self.count;
80− self.on_next_key(move |cx, event| {
82− cx.editor.autoinfo = None;
83− if let Some(func) = map.get(&event) {
90 pub fn callback<T, F>(
76 pub fn callback<T, F>(
91 &mut self,
77 &mut self,
@@ -139,13 +125,21 @@ fn align_view(doc: &Document, view: &mut View, align: Align) {
139/// A command is composed of a static name, and a function that takes the current state plus a count,
125/// A command is composed of a static name, and a function that takes the current state plus a count,
140/// and does a side-effect on the state (usually by creating and applying a transaction).
126/// and does a side-effect on the state (usually by creating and applying a transaction).
141#[derive(Copy, Clone)]
127#[derive(Copy, Clone)]
142−pub struct Command(&'static str, fn(cx: &mut Context));
128+pub struct Command {
130+ fun: fn(cx: &mut Context),
144macro_rules! commands {
134macro_rules! commands {
145− ( $($name:ident),* ) => {
135+ ( $($name:ident, $doc:literal),* ) => {
147 #[allow(non_upper_case_globals)]
137 #[allow(non_upper_case_globals)]
148− pub const $name: Self = Self(stringify!($name), $name);
138+ pub const $name: Self = Self {
139+ name: stringify!($name),
151 pub const COMMAND_LIST: &'static [Self] = &[
145 pub const COMMAND_LIST: &'static [Self] = &[
@@ -156,144 +150,159 @@ macro_rules! commands {
157impl Command {
151impl Command {
158 pub fn execute(&self, cx: &mut Context) {
152 pub fn execute(&self, cx: &mut Context) {
159− (self.1)(cx);
153+ (self.fun)(cx);
162 pub fn name(&self) -> &'static str {
156 pub fn name(&self) -> &'static str {
163− self.0
157+ self.name
160+ pub fn doc(&self) -> &'static str {
166 commands!(
165 commands!(
167− move_char_left,
166+ move_char_left, "Move left",
168− move_char_right,
167+ move_char_right, "Move right",
169− move_line_up,
168+ move_line_up, "Move up",
170− move_line_down,
169+ move_line_down, "Move down",
170+ extend_char_left, "Extend left",
171+ extend_char_right, "Extend right",
172+ extend_line_up, "Extend up",
173+ extend_line_down, "Extend down",
171− move_next_word_start,
174+ move_next_word_start, "Move to beginning of next word",
172− move_prev_word_start,
175+ move_prev_word_start, "Move to beginning of previous word",
173− move_next_word_end,
176+ move_next_word_end, "Move to end of next word",
174− move_next_long_word_start,
177+ move_next_long_word_start, "Move to beginning of next long word",
175− move_prev_long_word_start,
178+ move_prev_long_word_start, "Move to beginning of previous long word",
176− move_next_long_word_end,
179+ move_next_long_word_end, "Move to end of next long word",
177− extend_next_word_start,
180+ extend_next_word_start, "Extend to beginning of next word",
178− extend_prev_word_start,
181+ extend_prev_word_start, "Extend to beginning of previous word",
179− extend_next_word_end,
182+ extend_next_word_end, "Extend to end of next word",
180− find_till_char,
183+ find_till_char, "Move till next occurance of char",
181− find_next_char,
184+ find_next_char, "Move to next occurance of char",
182− extend_till_char,
185+ extend_till_char, "Extend till next occurance of char",
183− extend_next_char,
186+ extend_next_char, "Extend to next occurance of char",
184− till_prev_char,
187+ till_prev_char, "Move till previous occurance of char",
185− find_prev_char,
188+ find_prev_char, "Move to previous occurance of char",
186− extend_till_prev_char,
189+ extend_till_prev_char, "Extend till previous occurance of char",
187− extend_prev_char,
190+ extend_prev_char, "Extend to previous occurance of char",
188− replace,
191+ replace, "Replace with new char",
189− switch_case,
192+ switch_case, "Switch (toggle) case",
190− switch_to_uppercase,
193+ switch_to_uppercase, "Switch to uppercase",
191− switch_to_lowercase,
194+ switch_to_lowercase, "Switch to lowercase",
192− page_up,
195+ page_up, "Move page up",
193− page_down,
196+ page_down, "Move page down",
194− half_page_up,
197+ half_page_up, "Move half page up",
195− half_page_down,
198+ half_page_down, "Move half page down",
200− select_all,
199+ select_all, "Select whole document",
201− select_regex,
200+ select_regex, "Select all regex matches inside selections",
202− split_selection,
201+ split_selection, "Split selection into subselections on regex matches",
203− split_selection_on_newline,
202+ split_selection_on_newline, "Split selection on newlines",
204− search,
203+ search, "Search for regex pattern",
205− search_next,
204+ search_next, "Select next search match",
206− extend_search_next,
205+ extend_search_next, "Add next search match to selection",
207− search_selection,
206+ search_selection, "Use current selection as search pattern",
208− extend_line,
207+ extend_line, "Select current line, if already selected, extend to next line",
209− extend_to_line_bounds,
208+ extend_to_line_bounds, "Extend selection to line bounds (line-wise selection)",
210− delete_selection,
209+ delete_selection, "Delete selection",
211− change_selection,
210+ change_selection, "Change selection (delete and enter insert mode)",
212− collapse_selection,
211+ collapse_selection, "Collapse selection onto a single cursor",
213− flip_selections,
212+ flip_selections, "Flip selection cursor and anchor",
214− insert_mode,
213+ insert_mode, "Insert before selection",
215− append_mode,
214+ append_mode, "Insert after selection (append)",
216− command_mode,
215+ command_mode, "Enter command mode",
217− file_picker,
216+ file_picker, "Open file picker",
217+ code_action, "Perform code action",
218− buffer_picker,
218+ buffer_picker, "Open buffer picker",
219− symbol_picker,
219+ symbol_picker, "Open symbol picker",
220− last_picker,
220+ last_picker, "Open last picker",
221− prepend_to_line,
221+ prepend_to_line, "Insert at start of line",
222− append_to_line,
222+ append_to_line, "Insert at end of line",
223− open_below,
223+ open_below, "Open new line below selection",
224− open_above,
224+ open_above, "Open new line above selection",
225− normal_mode,
225+ normal_mode, "Enter normal mode",
227− select_mode,
226+ select_mode, "Enter selection extend mode",
228− exit_select_mode,
227+ exit_select_mode, "Exit selection mode",
229− goto_definition,
228+ goto_definition, "Goto definition",
230− goto_type_definition,
229+ goto_type_definition, "Goto type definition",
231− goto_implementation,
230+ goto_implementation, "Goto implementation",
232− goto_file_start,
231+ goto_file_start, "Goto file start",
233− goto_file_end,
232+ goto_file_end, "Goto file end",
234− goto_reference,
233+ goto_reference, "Goto references",
234+ goto_window_top, "Goto window top",
235+ goto_window_middle, "Goto window middle",
236+ goto_window_bottom, "Goto window bottom",
237+ goto_last_accessed_file, "Goto last accessed file",
235− goto_first_diag,
238+ goto_first_diag, "Goto first diagnostic",
236− goto_last_diag,
239+ goto_last_diag, "Goto last diagnostic",
237− goto_next_diag,
240+ goto_next_diag, "Goto next diagnostic",
238− goto_prev_diag,
241+ goto_prev_diag, "Goto previous diagnostic",
239− goto_line_start,
242+ goto_line_start, "Goto line start",
240− goto_line_end,
243+ goto_line_end, "Goto line end",
244+ // TODO: different description ?
241− goto_line_end_newline,
245+ goto_line_end_newline, "Goto line end",
242− goto_first_nonwhitespace,
246+ goto_first_nonwhitespace, "Goto first non-blank in line",
243− signature_help,
247+ signature_help, "Show signature help",
244− insert_tab,
248+ insert_tab, "Insert tab char",
245− insert_newline,
249+ insert_newline, "Insert newline char",
246− delete_char_backward,
250+ delete_char_backward, "Delete previous char",
247− delete_char_forward,
251+ delete_char_forward, "Delete next char",
248− delete_word_backward,
252+ delete_word_backward, "Delete previous word",
249− undo,
253+ undo, "Undo change",
250− redo,
254+ redo, "Redo change",
251− yank,
255+ yank, "Yank selection",
252− yank_joined_to_clipboard,
256+ yank_joined_to_clipboard, "Join and yank selections to clipboard",
253− yank_main_selection_to_clipboard,
257+ yank_main_selection_to_clipboard, "Yank main selection to clipboard",
254− replace_with_yanked,
258+ replace_with_yanked, "Replace with yanked text",
255− replace_selections_with_clipboard,
259+ replace_selections_with_clipboard, "Replace selections by clipboard content",
256− paste_after,
260+ paste_after, "Paste after selection",
257− paste_before,
261+ paste_before, "Paste before selection",
258− paste_clipboard_after,
262+ paste_clipboard_after, "Paste clipboard after selections",
259− paste_clipboard_before,
263+ paste_clipboard_before, "Paste clipboard before selections",
260− indent,
264+ indent, "Indent selection",
261− unindent,
265+ unindent, "Unindent selection",
262− format_selections,
266+ format_selections, "Format selection",
263− join_selections,
267+ join_selections, "Join lines inside selection",
264− keep_selections,
268+ keep_selections, "Keep selections matching regex",
265− keep_primary_selection,
269+ keep_primary_selection, "Keep primary selection",
266− completion,
270+ completion, "Invoke completion popup",
267− hover,
271+ hover, "Show docs for item under cursor",
268− toggle_comments,
272+ toggle_comments, "Comment/uncomment selections",
269− expand_selection,
273+ expand_selection, "Expand selection to parent syntax node",
271− jump_forward,
274+ jump_forward, "Jump forward on jumplist",
272− jump_backward,
275+ jump_backward, "Jump backward on jumplist",
274− rotate_view,
276+ rotate_view, "Goto next window",
275− hsplit,
277+ hsplit, "Horizontal bottom split",
276− vsplit,
278+ vsplit, "Vertical right split",
277− wclose,
279+ wclose, "Close window",
278− select_register,
280+ select_register, "Select register",
279− space_mode,
281+ align_view_middle, "Align view middle",
280− view_mode,
282+ align_view_top, "Align view top",
281− left_bracket_mode,
283+ align_view_center, "Align view center",
282− right_bracket_mode,
284+ align_view_bottom, "Align view bottom",
283− match_mode
285+ scroll_up, "Scroll view up",
286+ scroll_down, "Scroll view down",
287+ match_brackets, "Goto matching bracket",
288+ surround_add, "Surround add",
289+ surround_replace, "Surround replace",
290+ surround_delete, "Surround delete",
291+ select_textobject_around, "Select around object",
292+ select_textobject_inner, "Select inside object"
287impl fmt::Debug for Command {
296impl fmt::Debug for Command {
288 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
297 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
289− let Command(name, _) = self;
298+ let Command { name, .. } = self;
290 f.debug_tuple("Command").field(name).finish()
299 f.debug_tuple("Command").field(name).finish()
294impl fmt::Display for Command {
303impl fmt::Display for Command {
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296− let Command(name, _) = self;
305+ let Command { name, .. } = self;
297 f.write_str(name)
306 f.write_str(name)
@@ -305,7 +314,7 @@ impl std::str::FromStr for Command {
305 Command::COMMAND_LIST
314 Command::COMMAND_LIST
307 .copied()
316 .copied()
308− .find(|cmd| cmd.0 == s)
317+ .find(|cmd| cmd.name == s)
309 .ok_or_else(|| anyhow!("No command named '{}'", s))
318 .ok_or_else(|| anyhow!("No command named '{}'", s))
@@ -1171,7 +1180,7 @@ fn delete_selection(cx: &mut Context) {
1171 let reg_name = cx.selected_register.name();
1180 let reg_name = cx.selected_register.name();
1172 let (view, doc) = current!(cx.editor);
1181 let (view, doc) = current!(cx.editor);
1173 let registers = &mut cx.editor.registers;
1182 let registers = &mut cx.editor.registers;
1174− let reg = registers.get_or_insert(reg_name);
1183+ let reg = registers.get_mut(reg_name);
1175 delete_selection_impl(reg, doc, view.id);
1184 delete_selection_impl(reg, doc, view.id);
1177 doc.append_changes_to_history(view.id);
1186 doc.append_changes_to_history(view.id);
@@ -1184,7 +1193,7 @@ fn change_selection(cx: &mut Context) {
1184 let reg_name = cx.selected_register.name();
1193 let reg_name = cx.selected_register.name();
1185 let (view, doc) = current!(cx.editor);
1194 let (view, doc) = current!(cx.editor);
1186 let registers = &mut cx.editor.registers;
1195 let registers = &mut cx.editor.registers;
1187− let reg = registers.get_or_insert(reg_name);
1196+ let reg = registers.get_mut(reg_name);
1188 delete_selection_impl(reg, doc, view.id);
1197 delete_selection_impl(reg, doc, view.id);
1189 enter_insert_mode(doc);
1198 enter_insert_mode(doc);
@@ -1974,6 +1983,7 @@ mod cmd {
1974fn command_mode(cx: &mut Context) {
1983fn command_mode(cx: &mut Context) {
1975 let mut prompt = Prompt::new(
1984 let mut prompt = Prompt::new(
1976 ":".to_owned(),
1985 ":".to_owned(),
1977 |input: &str| {
1987 |input: &str| {
1978 // we use .this over split_whitespace() because we care about empty segments
1988 // we use .this over split_whitespace() because we care about empty segments
1979 let parts = input.split(' ').collect::<Vec<&str>>();
1989 let parts = input.split(' ').collect::<Vec<&str>>();
@@ -2147,6 +2157,112 @@ fn symbol_picker(cx: &mut Context) {
2160+pub fn code_action(cx: &mut Context) {
2161+ let (view, doc) = current!(cx.editor);
2163+ let language_server = match doc.language_server() {
2164+ Some(language_server) => language_server,
2168+ let range = range_to_lsp_range(
2170+ doc.selection(view.id).primary(),
2171+ language_server.offset_encoding(),
2174+ let future = language_server.code_actions(doc.identifier(), range);
2175+ let offset_encoding = language_server.offset_encoding();
2179+ move |_editor: &mut Editor,
2180+ compositor: &mut Compositor,
2181+ response: Option<lsp::CodeActionResponse>| {
2182+ if let Some(actions) = response {
2183+ let picker = Picker::new(
2185+ |action| match action {
2186+ lsp::CodeActionOrCommand::CodeAction(action) => {
2187+ action.title.as_str().into()
2189+ lsp::CodeActionOrCommand::Command(command) => command.title.as_str().into(),
2191+ move |editor, code_action, _action| match code_action {
2192+ lsp::CodeActionOrCommand::Command(command) => {
2193+ log::debug!("code action command: {:?}", command);
2194+ editor.set_error(String::from("Handling code action command is not implemented yet, see https://github.com/helix-editor/helix/issues/183"));
2196+ lsp::CodeActionOrCommand::CodeAction(code_action) => {
2197+ log::debug!("code action: {:?}", code_action);
2198+ if let Some(ref workspace_edit) = code_action.edit {
2199+ apply_workspace_edit(editor, offset_encoding, workspace_edit)
2204+ compositor.push(Box::new(picker))
2210+fn apply_workspace_edit(
2211+ editor: &mut Editor,
2212+ offset_encoding: OffsetEncoding,
2213+ workspace_edit: &lsp::WorkspaceEdit,
2215+ if let Some(ref changes) = workspace_edit.changes {
2216+ log::debug!("workspace changes: {:?}", changes);
2217+ editor.set_error(String::from("Handling workspace changesis not implemented yet, see https://github.com/helix-editor/helix/issues/183"));
2219+ // Not sure if it works properly, it'll be safer to just panic here to avoid breaking some parts of code on which code actions will be used
2220+ // TODO: find some example that uses workspace changes, and test it
2221+ // for (url, edits) in changes.iter() {
2222+ // let file_path = url.origin().ascii_serialization();
2223+ // let file_path = std::path::PathBuf::from(file_path);
2224+ // let file = std::fs::File::open(file_path).unwrap();
2225+ // let mut text = Rope::from_reader(file).unwrap();
2226+ // let transaction = edits_to_changes(&text, edits);
2227+ // transaction.apply(&mut text);
2231+ if let Some(ref document_changes) = workspace_edit.document_changes {
2232+ match document_changes {
2233+ lsp::DocumentChanges::Edits(document_edits) => {
2234+ for document_edit in document_edits {
2235+ let (view, doc) = current!(editor);
2236+ assert_eq!(doc.url().unwrap(), document_edit.text_document.uri);
2237+ let edits = document_edit
2240+ .map(|edit| match edit {
2241+ lsp::OneOf::Left(text_edit) => text_edit,
2242+ lsp::OneOf::Right(annotated_text_edit) => {
2243+ &annotated_text_edit.text_edit
2249+ let transaction = helix_lsp::util::generate_transaction_from_edits(
2254+ doc.apply(&transaction, view.id);
2255+ doc.append_changes_to_history(view.id);
2258+ lsp::DocumentChanges::Operations(operations) => {
2259+ log::debug!("document changes - operations: {:?}", operations);
2260+ editor.set_error(String::from("Handling document operations is not implemented yet, see https://github.com/helix-editor/helix/issues/183"));
2150fn last_picker(cx: &mut Context) {
2266fn last_picker(cx: &mut Context) {
2151 // TODO: last picker does not seemed to work well with buffer_picker
2267 // TODO: last picker does not seemed to work well with buffer_picker
2152 cx.callback = Some(Box::new(|compositor: &mut Compositor| {
2268 cx.callback = Some(Box::new(|compositor: &mut Compositor| {
@@ -2360,20 +2476,6 @@ fn exit_select_mode(cx: &mut Context) {
2360 doc_mut!(cx.editor).mode = Mode::Normal;
2476 doc_mut!(cx.editor).mode = Mode::Normal;
2363−fn goto_prehook(cx: &mut Context) -> bool {
2364− if let Some(count) = cx.count {
2365− push_jump(cx.editor);
2367− let (view, doc) = current!(cx.editor);
2368− let line_idx = std::cmp::min(count.get() - 1, doc.text().len_lines().saturating_sub(1));
2369− let pos = doc.text().line_to_char(line_idx);
2370− doc.set_selection(view.id, Selection::point(pos));
2377fn goto_impl(
2479fn goto_impl(
2378 editor: &mut Editor,
2480 editor: &mut Editor,
2379 compositor: &mut Compositor,
2481 compositor: &mut Compositor,
@@ -3823,199 +3925,3 @@ fn surround_delete(cx: &mut Context) {
3827−/// Do nothing, just for modeinfo.
3828−fn noop(_cx: &mut Context) -> bool {
3832−/// Generate modeinfo.
3834−/// If prehook returns true then it will stop the rest.
3835−macro_rules! mode_info {
3836− // TODO: reuse $mode for $stat
3837− (@join $first:expr $(,$rest:expr)*) => {
3838− concat!($first, $(", ", $rest),*)
3840− (@name #[doc = $name:literal] $(#[$rest:meta])*) => {
3844− #[doc = $name:literal] $(#[$doc:meta])* $mode:ident, $stat:ident,
3845− $(#[doc = $desc:literal] $($key:tt)|+ => $func:expr),+,
3850− $mode, $stat, noop,
3858− #[doc = $name:literal] $(#[$doc:meta])* $mode:ident, $stat:ident, $prehook:expr,
3859− $(#[doc = $desc:literal] $($key:tt)|+ => $func:expr),+,
3864− #[doc = "<table><tr><th>key</th><th>desc</th></tr><tbody>"]
3866− #[doc = "<tr><td>"]
3867− // TODO switch to this once we use rust 1.54
3868− // right now it will produce multiple rows
3869− // #[doc = mode_info!(@join $($key),+)]
3874− #[doc = "</td><td>"]
3876− #[doc = "</td></tr>"]
3878− #[doc = "</tbody></table>"]
3879− pub fn $mode(cx: &mut Context) {
3883− static $stat: OnceCell<Info> = OnceCell::new();
3884− cx.editor.autoinfo = Some($stat.get_or_init(|| Info::key(
3886− vec![$((&[$($key.parse().unwrap()),+], $desc)),+],
3888− use helix_core::hashmap;
3889− // TODO: try and convert this to match later
3890− let map = hashmap! {
3891− $($($key.parse::<KeyEvent>().unwrap() => $func as for<'r, 's> fn(&'r mut Context<'s>)),+),*
3893− cx.on_next_key_mode(map);
3900− space_mode, SPACE_MODE,
3901− /// resume last picker
3902− "'" => last_picker,
3904− "f" => file_picker,
3906− "b" => buffer_picker,
3908− "s" => symbol_picker,
3910− "w" => window_mode,
3911− /// yank joined to clipboard
3912− "y" => yank_joined_to_clipboard,
3913− /// yank main selection to clipboard
3914− "Y" => yank_main_selection_to_clipboard,
3915− /// paste system clipboard after selections
3916− "p" => paste_clipboard_after,
3917− /// paste system clipboard before selections
3918− "P" => paste_clipboard_before,
3919− /// replace selections with clipboard
3920− "R" => replace_selections_with_clipboard,
3921− /// keep primary selection
3922− "space" => keep_primary_selection,
3928− /// When specified with a count, it will go to that line without entering the mode.
3929− goto_mode, GOTO_MODE, goto_prehook,
3931− "g" => goto_file_start,
3933− "e" => goto_file_end,
3935− "h" => goto_line_start,
3937− "l" => goto_line_end,
3938− /// line first non blank
3939− "s" => goto_first_nonwhitespace,
3941− "d" => goto_definition,
3942− /// type references
3943− "y" => goto_type_definition,
3945− "r" => goto_reference,
3947− "i" => goto_implementation,
3949− "t" => goto_window_top,
3951− "m" => goto_window_middle,
3953− "b" => goto_window_bottom,
3954− /// last accessed file
3955− "a" => goto_last_accessed_file,
3960− window_mode, WINDOW_MODE,
3962− "w" | "C-w" => rotate_view,
3963− /// horizontal split
3973− match_mode, MATCH_MODE,
3974− /// matching character
3975− "m" => match_brackets,
3977− "s" => surround_add,
3978− /// surround replace
3979− "r" => surround_replace,
3980− /// surround delete
3981− "d" => surround_delete,
3983− "a" => select_textobject_around,
3985− "i" => select_textobject_inner,
3989− /// select to previous
3990− left_bracket_mode, LEFT_BRACKET_MODE,
3991− /// previous diagnostic
3992− "d" => goto_prev_diag,
3993− /// diagnostic (first)
3994− "D" => goto_first_diag,
3999− right_bracket_mode, RIGHT_BRACKET_MODE,
4001− "d" => goto_next_diag,
4002− /// diagnostic (last)
4003− "D" => goto_last_diag,
4008− view_mode, VIEW_MODE,
4010− "t" => align_view_top,
4011− /// align view center
4012− "z" | "c" => align_view_center,
4013− /// align view bottom
4014− "b" => align_view_bottom,
4015− /// align view middle
4016− "m" => align_view_middle,
4020− "j" => scroll_down,