From 6a9ac8ef22c0c00c87fe05c2c48fe385ac24f929 Mon Sep 17 00:00:00 2001 From: Ryan Leavengood Date: Sun, 8 Mar 2020 15:31:38 -0400 Subject: [PATCH] Implement the next/previous editor commands and keybindings This wraps around like VSCode. Fixes #778 and part of #1423. --- src/Model/Actions.re | 2 ++ src/Model/EditorGroup.re | 32 ++++++++++++++++++++++++++ src/Model/EditorGroup.rei | 2 ++ src/Model/EditorGroupReducer.re | 2 ++ src/Store/CommandStoreConnector.re | 15 ++++++++++++ src/Store/KeyBindingsStoreConnector.re | 20 ++++++++++++++++ src/Store/VimStoreConnector.re | 2 ++ 7 files changed, 75 insertions(+) diff --git a/src/Model/Actions.re b/src/Model/Actions.re index 6fe89a667a..4f16243154 100644 --- a/src/Model/Actions.re +++ b/src/Model/Actions.re @@ -128,6 +128,8 @@ type t = | StatusBarDisposeItem(int) | StatusBar(StatusBarModel.action) | ViewCloseEditor(int) + | ViewNextEditor + | ViewPreviousEditor | ViewSetActiveEditor(int) | EnableZenMode | DisableZenMode diff --git a/src/Model/EditorGroup.re b/src/Model/EditorGroup.re index 32005ad25a..b3c57beaa6 100644 --- a/src/Model/EditorGroup.re +++ b/src/Model/EditorGroup.re @@ -82,6 +82,38 @@ let _getAdjacentEditor = (editor: int, reverseTabOrder: list(int)) => { }; }; +let setActiveEditorByIndexDiff = (diff, model) => { + let tabs = model.reverseTabOrder; + let count = List.length(tabs); + + if (count <= 1) { + // Nothing to change + model + } else { + switch (model.activeEditorId) { + | Some(activeEditorId) => + switch (_getIndexOfElement(activeEditorId, tabs)) { + | (-1) => model + | idx => + let newIndex = + switch (idx + diff) { + // Wrapping negative, go to end + | -1 => count - 1 + // If this is past the end, go to zero, otherwise this index is fine + | i => i >= count ? 0 : i + }; + + {...model, activeEditorId: List.nth_opt(tabs, newIndex)}; + } + | None => model + }; + }; +} + +// The diff amounts are inverted because the list is in reverse order +let nextEditor = setActiveEditorByIndexDiff(-1); +let previousEditor = setActiveEditorByIndexDiff(1); + let isEmpty = model => IntMap.is_empty(model.editors); let removeEditorById = (state, editorId) => { diff --git a/src/Model/EditorGroup.rei b/src/Model/EditorGroup.rei index 614b92d629..84f33d3993 100644 --- a/src/Model/EditorGroup.rei +++ b/src/Model/EditorGroup.rei @@ -17,6 +17,8 @@ let getActiveEditor: t => option(Feature_Editor.Editor.t); let setActiveEditor: (t, int) => t; let getEditorById: (int, t) => option(Feature_Editor.Editor.t); let getOrCreateEditorForBuffer: (t, int) => (t, Feature_Editor.EditorId.t); +let nextEditor: t => t; +let previousEditor: t => t; let removeEditorById: (t, int) => t; let isEmpty: t => bool; diff --git a/src/Model/EditorGroupReducer.re b/src/Model/EditorGroupReducer.re index 9e15980f4a..792693ab29 100644 --- a/src/Model/EditorGroupReducer.re +++ b/src/Model/EditorGroupReducer.re @@ -25,6 +25,8 @@ let reduce = (v: EditorGroup.t, action: Actions.t) => { EditorGroup.getOrCreateEditorForBuffer(v, id); {...newState, activeEditorId: Some(activeEditorId)}; | ViewCloseEditor(id) => EditorGroup.removeEditorById(v, id) + | ViewNextEditor => EditorGroup.nextEditor(v) + | ViewPreviousEditor => EditorGroup.previousEditor(v) | ViewSetActiveEditor(id) => switch (IntMap.find_opt(id, v.editors)) { | None => v diff --git a/src/Store/CommandStoreConnector.re b/src/Store/CommandStoreConnector.re index 5f91471cad..eab19d5fc2 100644 --- a/src/Store/CommandStoreConnector.re +++ b/src/Store/CommandStoreConnector.re @@ -45,6 +45,18 @@ let createDefaultCommands = getState => { ~action=Command("view.closeEditor"), (), ), + Command.create( + ~category=Some("View"), + ~name="Open Next Editor", + ~action=Command("workbench.action.nextEditor"), + (), + ), + Command.create( + ~category=Some("View"), + ~name="Open Previous Editor", + ~action=Command("workbench.action.previousEditor"), + (), + ), Command.create( ~category=Some("View"), ~name="Toggle Problems (Errors, Warnings)", @@ -314,11 +326,14 @@ let start = (getState, contributedCommands) => { "workbench.action.closeQuickOpen", _ => singleActionEffect(QuickmenuClose), ), + ("list.focusDown", _ => singleActionEffect(ListFocusDown)), ("list.focusUp", _ => singleActionEffect(ListFocusUp)), ("list.select", _ => singleActionEffect(ListSelect)), ("list.selectBackground", _ => singleActionEffect(ListSelectBackground)), ("view.closeEditor", state => closeEditorEffect(state)), + ("workbench.action.nextEditor", _ => singleActionEffect(ViewNextEditor)), + ("workbench.action.previousEditor", _ => singleActionEffect(ViewPreviousEditor)), ("view.splitVertical", state => splitEditorEffect(state, Vertical)), ("view.splitHorizontal", state => splitEditorEffect(state, Horizontal)), ( diff --git a/src/Store/KeyBindingsStoreConnector.re b/src/Store/KeyBindingsStoreConnector.re index c73fd6037e..4fac030af2 100644 --- a/src/Store/KeyBindingsStoreConnector.re +++ b/src/Store/KeyBindingsStoreConnector.re @@ -204,6 +204,26 @@ let start = () => { command: "view.closeEditor", condition: WhenExpr.Value(True), }, + { + key: "", + command: "workbench.action.nextEditor", + condition: WhenExpr.Value(True), + }, + { + key: "", + command: "workbench.action.nextEditor", + condition: WhenExpr.Value(True), + }, + { + key: "", + command: "workbench.action.previousEditor", + condition: WhenExpr.Value(True), + }, + { + key: "", + command: "workbench.action.previousEditor", + condition: WhenExpr.Value(True), + }, ]; let reloadConfigOnWritePost = (~configPath, dispatch) => { diff --git a/src/Store/VimStoreConnector.re b/src/Store/VimStoreConnector.re index 344bb0fef4..c3b38b57bb 100644 --- a/src/Store/VimStoreConnector.re +++ b/src/Store/VimStoreConnector.re @@ -904,6 +904,8 @@ let start = ) | ViewSetActiveEditor(_) => (state, synchronizeEditorEffect(state)) | ViewCloseEditor(_) => (state, synchronizeEditorEffect(state)) + | ViewNextEditor => (state, synchronizeEditorEffect(state)) + | ViewPreviousEditor => (state, synchronizeEditorEffect(state)) | KeyboardInput(s) => (state, inputEffect(s)) | CopyActiveFilepathToClipboard => ( state,