Skip to content

Commit 439f001

Browse files
committed
feat(vim): add toggleable Vim keybindings mode (Normal, Insert, Visual) with status bar integration
1 parent 9045ff7 commit 439f001

13 files changed

Lines changed: 1914 additions & 9 deletions

‎CHANGELOG.md‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,37 @@
1010

1111
Alle wesentlichen Änderungen an CodeBox werden hier dokumentiert.
1212

13+
## [0.1.9] - 2026-09-11
14+
15+
### Zuschaltbarer Vim-Keybindings Modus
16+
17+
- `core/vim_mode.py`: Neues Kernmodul mit vollständiger Vim-Zustandsmaschine (`VimEngine`, `VimMode`):
18+
- 4 Betriebsmodi: `NORMAL`, `INSERT`, `VISUAL` (zeichenweise Markierung) und `VISUAL_LINE` (zeilenweise Markierung).
19+
- Terminal-authentischer Blockcursor im Normal- und Visual-Modus via adaptiver Fontmetrik (`QFontMetrics.horizontalAdvance`).
20+
- Bewegungstasten: `h`, `j`, `k`, `l`, Wortsprünge `w`, `b`, `e`, Zeilenbegrenzungen `0`, `^`, `$`, Dokumentensprünge `gg`, `G`, `<N>G` sowie Halbseiten-Scrollen `Ctrl+D`, `Ctrl+U`.
21+
- Ziffernpuffer & Zähler-Multiplikation (`3w`, `5j`, `2dd`).
22+
- Umfassende Lösch- und Änderungsbefehle: `x`, `X`, `dd`, `dw`, `de`, `d$`/`D`, `d0`, `cw`, `cc`/`S`, `C`, `s`, `r<char>`.
23+
- Kopieren & Einfügen (Yank/Put): `yy`, `Y`, `yw`, `y$`, `p`, `P` mit nativer Unterscheidung zwischen zeilen- und zeichenbasiertem Einfügen sowie Synchronisation mit dem System-Clipboard.
24+
- Weitere Aktionen: Fallwechsel (`~`), Zeilen verbinden (`J`), Ein-/Ausrücken (`>>`, `<<`), Undo (`u`), Redo (`Ctrl+R`).
25+
- Bereichsoperationen im Visual- und Visual-Line-Modus (`d`, `x`, `y`, `c`, `>`, `<`, `~`, `u`, `U`).
26+
- `core/editor.py`:
27+
- `vim_engine` Instanz in `CodeEditor`.
28+
- Methoden `set_vim_mode_enabled(bool)` und `is_vim_mode_enabled() -> bool`.
29+
- Key-Event Routing im Editor fängt Tastenanschläge bei aktivem Vim-Modus modal ab.
30+
- `ui/main_window.py`:
31+
- Menü *Bearbeiten* -> *Vim-Modus* (`Ctrl+Alt+V`) mit checkbarem Zustand.
32+
- Statusleisten-Widget (`vim_label`) mit Anzeige von `-- NORMAL --`, `-- INSERT --`, `-- VISUAL --`, `-- VISUAL_LINE --` und aktuellem Befehlspuffer (z. B. ` [3d]`).
33+
- Propagierung des Vim-Zustands auf alle geöffneten Tabs beider Split-Ansichten.
34+
- Automatische Registrierung in der Schnellauswahl / Befehlspalette (`Ctrl+Shift+P`).
35+
- `config/__init__.py`:
36+
- `"vim_mode": False` in `DEFAULT_SETTINGS` aufgenommen.
37+
- `ui/settings_dialog.py`:
38+
- Checkbox zur globalen Aktivierung/Deaktivierung des Vim-Modus.
39+
- `ui/shortcuts_dialog.py`:
40+
- Neue Dokumentations-Kategorie *Vim-Modus* mit vollständiger Tabelle aller Tastenbelegungen sowie Ergänzung unter *Bearbeiten*.
41+
- `tests/test_vim_mode.py`:
42+
- 27 automatisierte Unit Tests für Zustände, Navigation, Operatoren, Visual Mode, Zwischenablage, Editor- und UI-Integration (Gesamt: 239 passed, 1 skipped).
43+
1344
## [0.1.8] - 2026-09-11
1445

1546
### Git-Staging & Commit-Dialog

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
[![Python 3.10+](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue.svg)](https://www.python.org/)
77
[![Platform: Windows | Linux | macOS](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)]()
88
[![CI](https://github.com/dev-bricks/CodeBox/actions/workflows/ci.yml/badge.svg)](https://github.com/dev-bricks/CodeBox/actions/workflows/ci.yml)
9-
[![Tests](https://img.shields.io/badge/tests-212%20passed%20%7C%20100%25-brightgreen.svg)]()
9+
[![Tests](https://img.shields.io/badge/tests-239%20passed%20%7C%20100%25-brightgreen.svg)]()
1010
[![Privacy: Zero-Egress](https://img.shields.io/badge/privacy-100%25%20local--first%20%7C%20zero--egress-success.svg)](SECURITY.md)
1111
[![Security Policy](https://img.shields.io/badge/security-bilingual%20policy-blue.svg)](SECURITY.md)
1212
[![Ecosystem: dev-bricks](https://img.shields.io/badge/ecosystem-dev--bricks-blue.svg)](https://github.com/dev-bricks)
1313
[![Part of: open-bricks](https://img.shields.io/badge/part%20of-open--bricks-blue.svg)](https://github.com/open-bricks)
1414
[![LSP Ready](https://img.shields.io/badge/LSP-ready-purple.svg)]()
15-
[![Version: 0.1.8](https://img.shields.io/badge/version-0.1.8-green.svg)](CHANGELOG.md)
15+
[![Version: 0.1.9](https://img.shields.io/badge/version-0.1.9-green.svg)](CHANGELOG.md)
1616
[![llms.txt](https://img.shields.io/badge/llms.txt-available-green.svg)](llms.txt)
1717

1818
[Deutsch](README_de.md) | English

‎README_de.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
[![Python 3.10+](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue.svg)](https://www.python.org/)
77
[![Plattform: Windows | Linux | macOS](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)]()
88
[![CI](https://github.com/dev-bricks/CodeBox/actions/workflows/ci.yml/badge.svg)](https://github.com/dev-bricks/CodeBox/actions/workflows/ci.yml)
9-
[![Tests](https://img.shields.io/badge/tests-212%20passed%20%7C%20100%25-brightgreen.svg)]()
9+
[![Tests](https://img.shields.io/badge/tests-239%20passed%20%7C%20100%25-brightgreen.svg)]()
1010
[![Datenschutz: Zero-Egress](https://img.shields.io/badge/datenschutz-100%25%20local--first%20%7C%20zero--egress-success.svg)](SECURITY.md)
1111
[![Sicherheitsrichtlinie](https://img.shields.io/badge/sicherheit-zweisprachige%20policy-blue.svg)](SECURITY.md)
1212
[![Ökosystem: dev-bricks](https://img.shields.io/badge/ecosystem-dev--bricks-blue.svg)](https://github.com/dev-bricks)
1313
[![Dachverband: open-bricks](https://img.shields.io/badge/part%20of-open--bricks-blue.svg)](https://github.com/open-bricks)
1414
[![LSP Ready](https://img.shields.io/badge/LSP-ready-purple.svg)]()
15-
[![Version: 0.1.8](https://img.shields.io/badge/version-0.1.8-green.svg)](CHANGELOG.md)
15+
[![Version: 0.1.9](https://img.shields.io/badge/version-0.1.9-green.svg)](CHANGELOG.md)
1616
[![llms.txt](https://img.shields.io/badge/llms.txt-verf%C3%BCgbar-green.svg)](llms.txt)
1717

1818
[English](README.md) | Deutsch

‎config/__init__.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"theme": "dark",
1111
"auto_save": False,
1212
"show_minimap": True,
13-
"recent_files": []
13+
"recent_files": [],
14+
"vim_mode": False
1415
}
1516

1617
_SETTINGS_FILE = Path(__file__).parent / "settings.json"

‎core/__init__.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
from .highlighter import UniversalHighlighter
44
from .tabs import TabWidget, EditorTab
55
from .output import OutputPanel
6+
from .vim_mode import VimEngine, VimMode
67

7-
__all__ = ["CodeEditor", "UniversalHighlighter", "TabWidget", "EditorTab", "OutputPanel"]
8+
__all__ = ["CodeEditor", "UniversalHighlighter", "TabWidget", "EditorTab", "OutputPanel", "VimEngine", "VimMode"]

‎core/editor.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from core.folding import FoldingManager
1616
from core.multi_cursor import MultiCursorManager
17+
from core.vim_mode import VimEngine
1718

1819

1920
class LineNumberArea(QWidget):
@@ -261,6 +262,7 @@ def __init__(self, parent=None):
261262
self.FOLD_AREA_WIDTH = 14
262263
self.folding_manager = FoldingManager(self)
263264
self.multi_cursor_manager = MultiCursorManager(self)
265+
self.vim_engine = VimEngine(self)
264266
self._column_drag_start = None
265267
self._fold_timer = QTimer(self)
266268
self._fold_timer.setSingleShot(True)
@@ -306,6 +308,15 @@ def is_minimap_visible(self) -> bool:
306308
"""Gibt zurück, ob die Minimap für diesen Editor sichtbar sein soll."""
307309
return self._minimap_visible
308310

311+
def set_vim_mode_enabled(self, enabled: bool):
312+
"""Aktiviert oder deaktiviert den Vim-Modus für diesen Editor."""
313+
if hasattr(self, 'vim_engine'):
314+
self.vim_engine.set_enabled(enabled)
315+
316+
def is_vim_mode_enabled(self) -> bool:
317+
"""Prüft, ob der Vim-Modus in diesem Editor aktiv ist."""
318+
return hasattr(self, 'vim_engine') and self.vim_engine.is_enabled()
319+
309320
def set_completer_words(self, words: List[str]):
310321
"""Setzt die Completion-Wörter"""
311322
self.completer = QCompleter(sorted(set(words)), self)
@@ -614,6 +625,11 @@ def keyPressEvent(self, event):
614625
event.ignore()
615626
return
616627

628+
# Vim-Modus Tasten-Handling
629+
if hasattr(self, 'vim_engine') and self.vim_engine.is_enabled():
630+
if self.vim_engine.handle_key_event(event):
631+
return
632+
617633
# Multi-Cursor Shortcuts
618634
modifiers = event.modifiers()
619635
if (modifiers & Qt.KeyboardModifier.ControlModifier) and (modifiers & Qt.KeyboardModifier.AltModifier):

0 commit comments

Comments
 (0)