Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
da4d0f2
add vim mode to the search bar
Jun 19, 2026
6077ebf
Fix vim mode indicator and visual mode search/motion
Jun 19, 2026
672b3aa
docs: Update vim mode documentation with mode indicator, text objects…
Jun 19, 2026
1d355d6
Cleanup codebase and finalize cute vim mode indicator pill
Jun 19, 2026
6afdd52
Fix PR review feedback: handle MoveEndWord bounds, TextObjectWord whi…
Jun 19, 2026
902d9d0
chore: enable CI for vim-mode branch
Jun 19, 2026
3a6a1e0
fix(ui): change vim mode indicator from pill text to small colored ci…
Jun 19, 2026
831a198
fix(ui): use dynamic resources for vim mode settings text to support …
Jun 19, 2026
5902ece
docs: add XML docstrings to Vim mode public API
Jun 19, 2026
2dc28c1
refactor: remove unused label variable
Jun 19, 2026
1afa446
fix(ui): preserve WPF data bindings to fix auto-complete crash
Jun 19, 2026
18a3c81
Refactor VimMode logic, fix crashes, and optimize performance
Jun 19, 2026
4a2bcfb
Update packages.lock.json after build
Jun 19, 2026
bb3baab
Fix yank motion deletion bug
Jun 19, 2026
38e62de
Decrease VimMode indicator size
Jun 19, 2026
3c4ef29
Adjust VimModeIndicator vertical margin
Jun 19, 2026
dacfdab
Update lockfile
Jun 19, 2026
dddeb64
Fix V key behavior: V from Normal enters Visual Line mode, toggle log…
Jun 19, 2026
b35626f
Implement operation-level undo/redo stack, document u and Ctrl+R
Jun 19, 2026
df55bea
Fix repeat (.) for dw/cw/s/X/r and all motion-based operations
Jun 19, 2026
f1498ca
Address cubic review: fix state leaks, restore caret, refactor engine
Jun 19, 2026
08ede27
Merge remote-tracking branch 'upstream/dev' into dev
Jun 19, 2026
1246cd2
Fix Vim mode bugs: disable-lock, g-prefix, visual text objects, state…
Jun 20, 2026
84cd930
Fix operator+motion inclusivity and count handling in Vim mode
Jun 20, 2026
bd31efe
Clean up Vim mode PR for upstream: restore README, move docs, drop fo…
Jun 20, 2026
a1bbaf4
Document the color-coded dot mode indicator design choice
Jun 20, 2026
7171df4
Add P (paste before cursor), the counterpart to p
Jun 20, 2026
af6097a
Vim: fix Insert-mode undo, counted dot-repeat, and quote text-object …
Jun 20, 2026
2ad9468
Vim: flash a highlight over yanked text as confirmation
Jun 21, 2026
f860288
Vim: add gg/G, Ctrl-A/Ctrl-X, ib/aB aliases, and counted text objects
Jun 21, 2026
d320586
Vim: select the restored query on reopen when dismissed in Normal mode
Jun 23, 2026
d95b765
Merge remote-tracking branch 'upstream/dev' into vim-mode
namefailed Jun 30, 2026
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
14 changes: 14 additions & 0 deletions Flow.Launcher.Infrastructure/UserSettings/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@ public void Save()
_storage.Save();
}

private bool _enableVimMode = false;
public bool EnableVimMode
{
get => _enableVimMode;
set
{
if (_enableVimMode != value)
{
_enableVimMode = value;
OnPropertyChanged();
}
}
}
Comment on lines +63 to +74

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

vim_file="$(fd -i '^VimManager\.cs$' | head -n1)"
if [ -z "${vim_file}" ]; then
  echo "VimManager.cs not found"
  exit 1
fi

echo "Inspecting: ${vim_file}"
echo "---- EnableVimMode disabled branch ----"
rg -n -A20 -B8 'if \(!_settings\.EnableVimMode\)' "${vim_file}"

echo "---- Change subscriptions and UI reset hooks ----"
rg -n 'PropertyChanged|EnableVimMode|ApplyModeUI|SwitchToInsert|CaretBrush|InputMethod' "${vim_file}"

Repository: Flow-Launcher/Flow.Launcher

Length of output: 2839


🏁 Script executed:

#!/bin/bash
set -euo pipefail

vim_file="$(fd -i '^VimManager\.cs$' | head -n1)"

echo "---- Full ViewModel_PropertyChanged implementation ----"
rg -n -A30 'private void ViewModel_PropertyChanged' "${vim_file}"

echo ""
echo "---- Check for explicit EnableVimMode property handling ----"
rg -n 'EnableVimMode' "${vim_file}"

Repository: Flow-Launcher/Flow.Launcher

Length of output: 1380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

vim_file="$(fd -i '^VimManager\.cs$' | head -n1)"

echo "---- Full ApplyModeUI implementation ----"
rg -n -A15 'private void ApplyModeUI' "${vim_file}"

Repository: Flow-Launcher/Flow.Launcher

Length of output: 824


Add PropertyChanged handler to restore Insert-mode UI when EnableVimMode is disabled.

When EnableVimMode is toggled off, the UI state is not fully restored. HandlePreviewKeyDown only hides the vim block caret, leaving InputMethod suspended and the transparent CaretBrush in place. Add a check in ViewModel_PropertyChanged for the EnableVimMode property that calls ApplyModeUI(VimModes.Insert) when disabled, which properly restores both the InputMethod state and native caret brush.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Flow.Launcher.Infrastructure/UserSettings/Settings.cs` around lines 63 - 74,
When EnableVimMode is disabled, the UI state is not fully restored because
HandlePreviewKeyDown only hides the vim block caret, leaving InputMethod
suspended and CaretBrush transparent. Add a property changed handler in
ViewModel_PropertyChanged that detects when the EnableVimMode property changes
to false and calls ApplyModeUI(VimModes.Insert) to properly restore both the
InputMethod state and the native caret brush appearance.


public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";

private string _openResultModifiers = KeyConstant.Alt;
Expand Down
172 changes: 172 additions & 0 deletions Flow.Launcher.Test/VimEngineTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
using NUnit.Framework;
using System.Collections.Generic;
using Flow.Launcher.VimMode;

namespace Flow.Launcher.Test
{
[TestFixture]
public class VimEngineTest
{
[Test]
public void DefaultModeIsInsert()
{
var engine = new VimEngine();
Assert.That(engine.CurrentMode, Is.EqualTo(VimModeType.Insert));
}

[Test]
public void SwitchToInsertFromNormalFiresEvent()
{
var engine = new VimEngine();
engine.SwitchToNormal();
var modes = new List<VimModeType>();
engine.ModeChanged += m => modes.Add(m);

engine.SwitchToInsert();

Assert.That(engine.CurrentMode, Is.EqualTo(VimModeType.Insert));
Assert.That(modes, Is.EqualTo(new[] { VimModeType.Insert }));
}

[Test]
public void SwitchToInsertWhenAlreadyInsertDoesNotFire()
{
var engine = new VimEngine();
var fired = false;
engine.ModeChanged += _ => fired = true;

engine.SwitchToInsert();

Assert.That(fired, Is.False);
}

[Test]
public void SwitchToNormalFiresEvent()
{
var engine = new VimEngine();
var modes = new List<VimModeType>();
engine.ModeChanged += m => modes.Add(m);

engine.SwitchToNormal();

Assert.That(engine.CurrentMode, Is.EqualTo(VimModeType.Normal));
Assert.That(modes, Is.EqualTo(new[] { VimModeType.Normal }));
}

[Test]
public void SwitchToNormalWhenAlreadyNormalDoesNotFire()
{
var engine = new VimEngine();
engine.SwitchToNormal();
var fired = false;
engine.ModeChanged += _ => fired = true;

engine.SwitchToNormal();

Assert.That(fired, Is.False);
}

[Test]
public void SwitchToVisualFiresEvent()
{
var engine = new VimEngine();
engine.SwitchToNormal();
var modes = new List<VimModeType>();
engine.ModeChanged += m => modes.Add(m);

engine.SwitchToVisual();

Assert.That(engine.CurrentMode, Is.EqualTo(VimModeType.Visual));
Assert.That(modes, Is.EqualTo(new[] { VimModeType.Visual }));
}

[Test]
public void SwitchToVisualWhenAlreadyVisualDoesNotFire()
{
var engine = new VimEngine();
engine.SwitchToVisual();
var fired = false;
engine.ModeChanged += _ => fired = true;

engine.SwitchToVisual();

Assert.That(fired, Is.False);
}

[Test]
public void SwitchToVisualLineFiresEvent()
{
var engine = new VimEngine();
engine.SwitchToVisual();
var modes = new List<VimModeType>();
engine.ModeChanged += m => modes.Add(m);

engine.SwitchToVisualLine();

Assert.That(engine.CurrentMode, Is.EqualTo(VimModeType.VisualLine));
Assert.That(modes, Is.EqualTo(new[] { VimModeType.VisualLine }));
}

[Test]
public void SwitchToVisualLineWhenAlreadyDoesNotFire()
{
var engine = new VimEngine();
engine.SwitchToVisualLine();
var fired = false;
engine.ModeChanged += _ => fired = true;

engine.SwitchToVisualLine();

Assert.That(fired, Is.False);
}

[Test]
public void FullModeCycleTest()
{
var engine = new VimEngine();
var modes = new List<VimModeType>();
engine.ModeChanged += m => modes.Add(m);

engine.SwitchToNormal();
engine.SwitchToVisual();
engine.SwitchToNormal();
engine.SwitchToInsert();

Assert.That(engine.CurrentMode, Is.EqualTo(VimModeType.Insert));
Assert.That(modes, Is.EqualTo(new[] {
VimModeType.Normal, VimModeType.Visual, VimModeType.Normal, VimModeType.Insert
}));
}

[Test]
public void VisualLineCycleTest()
{
var engine = new VimEngine();
var modes = new List<VimModeType>();
engine.ModeChanged += m => modes.Add(m);

engine.SwitchToNormal();
engine.SwitchToVisualLine();
engine.SwitchToVisual();
engine.SwitchToVisualLine();
engine.SwitchToNormal();
engine.SwitchToInsert();

Assert.That(engine.CurrentMode, Is.EqualTo(VimModeType.Insert));
Assert.That(modes, Is.EqualTo(new[] {
VimModeType.Normal, VimModeType.VisualLine, VimModeType.Visual,
VimModeType.VisualLine, VimModeType.Normal, VimModeType.Insert
}));
}

[Test]
public void NoSubscriberDoesNotThrow()
{
var engine = new VimEngine();
Assert.DoesNotThrow(() => engine.SwitchToNormal());
Assert.DoesNotThrow(() => engine.SwitchToVisual());
Assert.DoesNotThrow(() => engine.SwitchToVisualLine());
Assert.DoesNotThrow(() => engine.SwitchToInsert());
}
}
}
Loading