import os
from enum import Enum, auto, unique
from pexpect import replwrap
from fortrace.utility.console_applications.console_application import KeyCodes
from fortrace.utility.console_applications.text_editor.text_editor import (
GenericConsoleTextEditor,
)
[docs]
@unique
class VIMMode(Enum):
NORMAL = auto()
INSERT = auto()
VISUAL = auto()
VISUAL_LINE = auto()
VISUAL_BLOCK = auto()
[docs]
class VIM(GenericConsoleTextEditor):
"""VIM text editor."""
def __init__(self, pty: replwrap.REPLWrapper):
super().__init__(pty, "VIM")
self._mode = VIMMode.NORMAL
[docs]
def replace(self, old: str, new: str, count: int = -1):
raise NotImplementedError
[docs]
def go_to_line(self, line_num: int):
"""Go to a specific line.
VIM's line indices start at 1. Place the cursor at the first column.
Args:
line_num: line to set cursor to. If -1 set cursor at the end of the file. If line_num is larger than the
real number of lines of a file, VIM will set the cursor to the last line as well.
"""
prev_mode = self._mode
self.change_mode(VIMMode.NORMAL)
if line_num == -1:
self._virsh.send("G")
else:
self._virsh.send(line_num)
self._virsh.send("G")
self.change_mode(prev_mode)
[docs]
def delete_current_line(self):
self.send_command("dd")
self._virsh.readline()
[docs]
def close(self):
self.send_command(":q!")
self._virsh.expect_exact(self._pty.prompt)
# TODO: readlines until prompt is reached?
[docs]
def save_file(self):
self.send_command(":w")
self._document_saved = True
[docs]
def send_command(self, command: str):
"""Enter a specified command in VIM's normal mode.
Enters VIM's normal mode and enters the specified command. If possible use change_mode, since some commands do
not work with a RET appended, and this will be done automatically.
Notes:
VIM will stay in normal mode after this method.
Args:
command: command to be entered
"""
self.change_mode(VIMMode.NORMAL)
self._virsh.send(command)
self.send_key_combination(KeyCodes.RET)
[docs]
def change_mode(self, mode: VIMMode):
"""Change VIM to the specified mode.
Args:
mode: target mode
"""
# TODO: refactor this, since it looks awfully
if mode is VIMMode.NORMAL:
self.send_key_combination(KeyCodes.ESC)
elif mode is VIMMode.INSERT:
self.send_key_combination(KeyCodes.ESC)
self._virsh.sendline("i")
elif mode is VIMMode.VISUAL:
self.send_key_combination(KeyCodes.ESC)
self._virsh.sendline("v")
elif mode is VIMMode.VISUAL_LINE:
self.send_key_combination(KeyCodes.ESC)
self._virsh.sendline("V")
elif mode is VIMMode.VISUAL_BLOCK:
self.send_key_combination(KeyCodes.ESC)
self._virsh.sendcontrol("v")
else:
raise ValueError(f"Unknown mode {mode} provided")
self._mode = mode
[docs]
def send_file(self, path: os.PathLike):
self.change_mode(VIMMode.INSERT)
super().send_file(path)
self.change_mode(VIMMode.NORMAL)
@property
def mode(self):
return self._mode