Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(vim/#2968): Cursor management in command-line mode #3020

Merged
merged 10 commits into from
Jan 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES_CURRENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- #3011 - Vim: Visual Block - Handle 'I' and 'A' in visual block mode (fixes #1633)
- #3016 - Extensions: Fix memory leak in extension host language features (fixes #3009)
- #3019 - Extensions: Fix activation error for Ionide.Ionide-fsharp extension (fixes #2974)
- #3020 - Vim: Fix incsearch cursor movement (fixes #2968)

### Performance

Expand Down
2 changes: 1 addition & 1 deletion integration_test/RegressionCommandLineNoCompletionTest.re
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ runTest(
dispatch(KeyboardInput({isText: true, input: ":"}));

wait(~name="Mode switches to command line", (state: State.t) =>
Selectors.mode(state) == Vim.Mode.CommandLine
Vim.Mode.isCommandLine(Selectors.mode(state))
);

dispatch(KeyboardInput({isText: true, input: "e"}));
Expand Down
65 changes: 65 additions & 0 deletions integration_test/VimIncsearchScrollTest.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
open Oni_Core;
open Oni_Model;
open Oni_IntegrationTestLib;

runTest(~name="VimIncsearchScrollTest", ({dispatch, wait, input, key, _}) => {
wait(~name="Initial mode is normal", (state: State.t) =>
Selectors.mode(state) |> Vim.Mode.isNormal
);

let largeCFile = getAssetPath("large-c-file.c");
dispatch(Actions.OpenFileByPath(largeCFile, None, None));

// Wait for file to load
wait(
~timeout=30.0,
~name="Validate buffer is loaded",
(state: State.t) => {
let fileType =
Selectors.getActiveBuffer(state)
|> Option.map(Buffer.getFileType)
|> Option.map(Buffer.FileType.toString);

fileType == Some("c");
},
);

// Start searching in the file:
input("/");

// Validate we're now in command line
wait(~name="Mode switches to command line", (state: State.t) => {
Vim.Mode.isCommandLine(Selectors.mode(state))
});

// Search for something far down, that should trigger a scroll
// "xor" is on line 983...
input("\"xor");

// Validate editor has scrolled
wait(~name="Validate editor has scrolled some distance", (state: State.t) => {
let scrollY =
state.layout
|> Feature_Layout.activeEditor
|> Feature_Editor.Editor.scrollY;

scrollY > 100.;
});

// Cancel search, we should scroll back up...
key(EditorInput.Key.Escape);

// Validate editor has scrolled back to top
wait(
~name="Validate editor is back to start",
~timeout=30.,
(state: State.t) => {
let scrollY =
state.layout
|> Feature_Layout.activeEditor
|> Feature_Editor.Editor.scrollY;

scrollY < 1.;
},
);
});
2 changes: 1 addition & 1 deletion integration_test/VimlRemapCmdlineTest.re
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ runTest(~name="Viml Remap ö -> :", ({dispatch, wait, runEffects, input, _}) =>
input("ö");

wait(~name="Mode switches to command line", (state: State.t) => {
Selectors.mode(state) == Vim.Mode.CommandLine
Vim.Mode.isCommandLine(Selectors.mode(state))
});

input("e");
Expand Down
16 changes: 9 additions & 7 deletions integration_test/dune
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
SCMGitTest SyntaxHighlightPhpTest SyntaxHighlightTextMateTest
SyntaxHighlightTreesitterTest AddRemoveSplitTest TerminalSetPidTitleTest
TerminalConfigurationTest TypingBatchedTest TypingUnbatchedTest
VimSimpleRemapTest VimlRemapCmdlineTest ClipboardChangeTest
VimScriptLocalFunctionTest ZenModeSingleFileModeTest ZenModeSplitTest)
VimIncsearchScrollTest VimSimpleRemapTest VimlRemapCmdlineTest
ClipboardChangeTest VimScriptLocalFunctionTest ZenModeSingleFileModeTest
ZenModeSplitTest)
(libraries Oni_CLI Oni_IntegrationTestLib reason-native-crash-utils.asan))

(install
Expand Down Expand Up @@ -58,8 +59,9 @@
SyntaxServerParentPidCorrectTest.exe SyntaxServerReadExceptionTest.exe
TerminalSetPidTitleTest.exe TerminalConfigurationTest.exe
AddRemoveSplitTest.exe TypingBatchedTest.exe TypingUnbatchedTest.exe
VimScriptLocalFunctionTest.exe VimSimpleRemapTest.exe
VimlRemapCmdlineTest.exe ZenModeSingleFileModeTest.exe
ZenModeSplitTest.exe ClipboardChangeTest.exe large-c-file.c lsan.supp
some-test-file.json some-test-file.txt test.crlf test.lf test-syntax.php
utf8.txt utf8-test-file.htm Inconsolata-Regular.ttf PlugScriptLocal.vim))
VimIncsearchScrollTest.exe VimScriptLocalFunctionTest.exe
VimSimpleRemapTest.exe VimlRemapCmdlineTest.exe
ZenModeSingleFileModeTest.exe ZenModeSplitTest.exe ClipboardChangeTest.exe
large-c-file.c lsan.supp some-test-file.json some-test-file.txt test.crlf
test.lf test-syntax.php utf8.txt utf8-test-file.htm
Inconsolata-Regular.ttf PlugScriptLocal.vim))
3 changes: 2 additions & 1 deletion src/Feature/Editor/Editor.re
Original file line number Diff line number Diff line change
Expand Up @@ -1328,7 +1328,8 @@ let mapCursor = (~f, editor) => {
// When scrolling in operator pending, cancel the pending operator
| Operator({cursor, _}) => Vim.Mode.Normal({cursor: f(cursor)})
// Don't do anything for command line mode
| CommandLine => CommandLine
| CommandLine({cursor, text, commandCursor, commandType}) =>
CommandLine({text, commandCursor, commandType, cursor: f(cursor)})
| Normal({cursor}) => Normal({cursor: f(cursor)})
| Visual(curr) =>
Visual(Vim.VisualRange.{...curr, cursor: f(curr.cursor)})
Expand Down
4 changes: 2 additions & 2 deletions src/Model/ModeManager.re
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Internal = {
| Vim.Mode.Visual(range) =>
Mode.TerminalVisual({range: Oni_Core.VisualRange.ofVim(range)})
| Vim.Mode.Operator({pending, _}) => Mode.Operator({pending: pending})
| Vim.Mode.CommandLine => Mode.CommandLine
| Vim.Mode.CommandLine(_) => Mode.CommandLine
| _ => Mode.TerminalNormal;
};

Expand Down Expand Up @@ -44,7 +44,7 @@ let current: State.t => Oni_Core.Mode.t =
| Vim.Mode.Replace({cursor}) => Mode.Replace({cursor: cursor})
| Vim.Mode.Operator({pending, _}) =>
Mode.Operator({pending: pending})
| Vim.Mode.CommandLine => Mode.CommandLine
| Vim.Mode.CommandLine(_) => Mode.CommandLine
},
);
};
10 changes: 8 additions & 2 deletions src/Service/Vim/Service_Vim.re
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ let quitAll = () =>
module Effects = {
let paste = (~toMsg, text) => {
Isolinear.Effect.createWithDispatch(~name="vim.clipboardPaste", dispatch => {
let isCmdLineMode = Vim.Mode.current() == Vim.Mode.CommandLine;
let isCmdLineMode = Vim.Mode.isCommandLine(Vim.Mode.current());
let isInsertMode = Vim.Mode.isInsert(Vim.Mode.current());

if (isInsertMode || isCmdLineMode) {
Expand Down Expand Up @@ -123,7 +123,13 @@ module Effects = {
})
| Replace({cursor}) =>
Replace({cursor: adjustBytePositionForEdit(cursor, edit)})
| CommandLine => CommandLine
| CommandLine({commandType, text, commandCursor, cursor}) =>
CommandLine({
commandType,
text,
commandCursor,
cursor: adjustBytePositionForEdit(cursor, edit),
})
| Operator({cursor, pending}) =>
Operator({cursor: adjustBytePositionForEdit(cursor, edit), pending})
| Visual(_) as vis => vis
Expand Down
63 changes: 26 additions & 37 deletions src/Store/VimStoreConnector.re
Original file line number Diff line number Diff line change
Expand Up @@ -486,56 +486,45 @@ let start =
let lastCompletionMeet = ref(None);
let isCompleting = ref(false);

let checkCommandLineCompletions = () => {
let checkCommandLineCompletions = (~text: string, ~position: int) => {
Log.debug("checkCommandLineCompletions");

let position = Vim.CommandLine.getPosition();
Vim.CommandLine.getText()
|> Option.iter(commandStr =>
if (position == String.length(commandStr)
&& !StringEx.isEmpty(commandStr)) {
let context = Oni_Model.VimContext.current(getState());
let completions = Vim.CommandLine.getCompletions(~context, ());

Log.debugf(m =>
m(" got %n completions.", Array.length(completions))
);

let items =
Array.map(
name =>
Actions.{
name,
category: None,
icon: None,
command: () => Noop,
highlight: [],
handle: None,
},
completions,
);
if (position == String.length(text) && !StringEx.isEmpty(text)) {
let context = Oni_Model.VimContext.current(getState());
let completions = Vim.CommandLine.getCompletions(~context, ());

Log.debugf(m => m(" got %n completions.", Array.length(completions)));

let items =
Array.map(
name =>
Actions.{
name,
category: None,
icon: None,
command: () => Noop,
highlight: [],
handle: None,
},
completions,
);

dispatch(Actions.QuickmenuUpdateFilterProgress(items, Complete));
}
);
dispatch(Actions.QuickmenuUpdateFilterProgress(items, Complete));
};
};

let _: unit => unit =
Vim.CommandLine.onUpdate(({text, position: cursorPosition, _}) => {
Vim.CommandLine.onUpdate(({text, position: cursorPosition, cmdType}) => {
dispatch(Actions.QuickmenuCommandlineUpdated(text, cursorPosition));

let cmdlineType = Vim.CommandLine.getType();
let cmdlineType = cmdType;
switch (cmdlineType) {
| Ex =>
let text =
switch (Vim.CommandLine.getText()) {
| Some(v) => v
| None => ""
};
let meet = Feature_Vim.CommandLine.getCompletionMeet(text);
lastCompletionMeet := meet;

isCompleting^ ? () : checkCommandLineCompletions();
isCompleting^
? () : checkCommandLineCompletions(~position=cursorPosition, ~text);

| SearchForward
| SearchReverse =>
Expand Down
4 changes: 0 additions & 4 deletions src/reason-libvim/CommandLine.re
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,8 @@ let getCompletions = (~context: Context.t=Context.current(), ()) => {
completions;
};

let getText = Native.vimCommandLineGetText;

let getPosition = Native.vimCommandLineGetPosition;

let getType = Native.vimCommandLineGetType;

let onEnter = f => Event.add(f, Listeners.commandLineEnter);
let onLeave = f => Event.add(f, Listeners.commandLineLeave);
let onUpdate = f => Event.add(f, Listeners.commandLineUpdate);
24 changes: 19 additions & 5 deletions src/reason-libvim/Mode.re
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ open EditorCoreTypes;
type t =
| Normal({cursor: BytePosition.t})
| Insert({cursors: list(BytePosition.t)})
| CommandLine
| CommandLine({
text: string,
commandCursor: ByteIndex.t,
commandType: Types.cmdlineType,
cursor: BytePosition.t,
})
| Replace({cursor: BytePosition.t})
| Visual(VisualRange.t)
| Operator({
Expand All @@ -16,7 +21,7 @@ let show = (mode: t) => {
switch (mode) {
| Normal(_) => "Normal"
| Visual(_) => "Visual"
| CommandLine => "CommandLine"
| CommandLine(_) => "CommandLine"
| Replace(_) => "Replace"
| Operator(_) => "Operator"
| Insert(_) => "Insert"
Expand All @@ -32,7 +37,7 @@ let cursors =
| Visual(range) => [range |> VisualRange.cursor]
| Operator({cursor, _}) => [cursor]
| Select(range) => [range |> VisualRange.cursor]
| CommandLine => [];
| CommandLine({cursor, _}) => [cursor];

let current = () => {
let nativeMode: Native.mode = Native.vimGetMode();
Expand All @@ -41,7 +46,11 @@ let current = () => {
switch (nativeMode) {
| Native.Normal => Normal({cursor: cursor})
| Native.Visual => Visual(VisualRange.current())
| Native.CommandLine => CommandLine
| Native.CommandLine =>
let commandCursor = Native.vimCommandLineGetPosition() |> ByteIndex.ofInt;
let commandType = Native.vimCommandLineGetType();
let text = Native.vimCommandLineGetText() |> Option.value(~default="");
CommandLine({cursor, commandCursor, commandType, text});
| Native.Replace => Replace({cursor: cursor})
| Native.Operator =>
Operator({
Expand All @@ -68,6 +77,11 @@ let isInsert =
| Insert(_) => true
| _ => false;

let isCommandLine =
fun
| CommandLine(_) => true
| _ => false;

let isNormal =
fun
| Normal(_) => true
Expand Down Expand Up @@ -139,7 +153,7 @@ let trySet = newMode => {
// These modes cannot be explicitly transitioned to currently
| Operator(_)
| Replace(_)
| CommandLine => ()
| CommandLine(_) => ()
};

current();
Expand Down
8 changes: 4 additions & 4 deletions src/reason-libvim/Vim.re
Original file line number Diff line number Diff line change
Expand Up @@ -206,16 +206,16 @@ let runWith = (~context: Context.t, f) => {

BufferInternal.checkCurrentBufferForUpdate();

if (mode != prevMode) {
if (mode == CommandLine) {
if (!Mode.isCommandLine(mode) || !Mode.isCommandLine(prevMode)) {
if (Mode.isCommandLine(mode)) {
Event.dispatch(
CommandLineInternal.getState(),
Listeners.commandLineEnter,
);
} else if (prevMode == CommandLine) {
} else if (Mode.isCommandLine(prevMode)) {
Event.dispatch((), Listeners.commandLineLeave);
};
} else if (mode == CommandLine) {
} else if (Mode.isCommandLine(mode)) {
Event.dispatch(
CommandLineInternal.getState(),
Listeners.commandLineUpdate,
Expand Down
11 changes: 8 additions & 3 deletions src/reason-libvim/Vim.rei
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,12 @@ module Mode: {
type t =
| Normal({cursor: BytePosition.t})
| Insert({cursors: list(BytePosition.t)})
| CommandLine
| CommandLine({
text: string,
commandCursor: ByteIndex.t,
commandType: Types.cmdlineType,
cursor: BytePosition.t,
})
| Replace({cursor: BytePosition.t})
| Visual(VisualRange.t)
| Operator({
Expand All @@ -113,6 +118,7 @@ module Mode: {

let current: unit => t;

let isCommandLine: t => bool;
let isInsert: t => bool;
let isNormal: t => bool;
let isVisual: t => bool;
Expand Down Expand Up @@ -191,9 +197,8 @@ module CommandLine: {
type t = Types.cmdline;

let getCompletions: (~context: Context.t=?, unit) => array(string);
let getText: unit => option(string);

let getPosition: unit => int;
let getType: unit => Types.cmdlineType;

let onEnter: (Event.handler(t), unit) => unit;
let onLeave: (Event.handler(unit), unit) => unit;
Expand Down
Loading