spyder-okvim 0.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spyder_okvim/__init__.py +10 -0
- spyder_okvim/conftest.py +153 -0
- spyder_okvim/executor/__init__.py +8 -0
- spyder_okvim/executor/executor_base.py +171 -0
- spyder_okvim/executor/executor_colon.py +86 -0
- spyder_okvim/executor/executor_easymotion.py +362 -0
- spyder_okvim/executor/executor_leader.py +112 -0
- spyder_okvim/executor/executor_normal.py +756 -0
- spyder_okvim/executor/executor_sub.py +875 -0
- spyder_okvim/executor/executor_surround.py +112 -0
- spyder_okvim/executor/executor_visual.py +475 -0
- spyder_okvim/executor/executor_vline.py +460 -0
- spyder_okvim/spyder/__init__.py +0 -0
- spyder_okvim/spyder/api.py +57 -0
- spyder_okvim/spyder/config.py +42 -0
- spyder_okvim/spyder/confpage.py +113 -0
- spyder_okvim/spyder/plugin.py +180 -0
- spyder_okvim/spyder/widgets.py +515 -0
- spyder_okvim/utils/__init__.py +0 -0
- spyder_okvim/utils/easymotion.py +104 -0
- spyder_okvim/utils/helper_action.py +684 -0
- spyder_okvim/utils/helper_motion.py +1377 -0
- spyder_okvim/utils/misc.py +2 -0
- spyder_okvim/utils/path_finder.py +243 -0
- spyder_okvim/utils/vim_status.py +998 -0
- spyder_okvim-0.9.dist-info/METADATA +29 -0
- spyder_okvim-0.9.dist-info/RECORD +31 -0
- spyder_okvim-0.9.dist-info/WHEEL +5 -0
- spyder_okvim-0.9.dist-info/entry_points.txt +2 -0
- spyder_okvim-0.9.dist-info/licenses/LICENSE +21 -0
- spyder_okvim-0.9.dist-info/top_level.txt +1 -0
spyder_okvim/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# -----------------------------------------------------------------------------
|
|
3
|
+
# Copyright (c) Spyder Project Contributors
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the terms of the MIT License
|
|
6
|
+
# (see LICENSE.txt for details)
|
|
7
|
+
# -----------------------------------------------------------------------------
|
|
8
|
+
"""Spyder OkVim Plugin."""
|
|
9
|
+
|
|
10
|
+
__version__ = "0.0.1-dev0"
|
spyder_okvim/conftest.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Conftest."""
|
|
2
|
+
# Standard library imports
|
|
3
|
+
import os
|
|
4
|
+
import os.path as osp
|
|
5
|
+
from unittest.mock import Mock
|
|
6
|
+
|
|
7
|
+
# Third party imports
|
|
8
|
+
import pytest
|
|
9
|
+
import requests
|
|
10
|
+
from pytestqt.plugin import QtBot
|
|
11
|
+
from qtpy.QtGui import QFont
|
|
12
|
+
from qtpy.QtWidgets import QVBoxLayout, QWidget
|
|
13
|
+
from spyder.config.manager import CONF
|
|
14
|
+
from spyder.plugins.editor.widgets.editorstack import EditorStack
|
|
15
|
+
|
|
16
|
+
# Local imports
|
|
17
|
+
from spyder_okvim.spyder.config import CONF_DEFAULTS, CONF_SECTION
|
|
18
|
+
from spyder_okvim.spyder.plugin import OkVim
|
|
19
|
+
|
|
20
|
+
LOCATION = osp.realpath(osp.join(os.getcwd(), osp.dirname(__file__)))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class VimTesting(OkVim):
|
|
24
|
+
CONF_FILE = False
|
|
25
|
+
CONF_SECTION = CONF_SECTION
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class EditorMock(QWidget):
|
|
29
|
+
"""Editor plugin mock."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, editor_stack):
|
|
32
|
+
"""Editor Mock constructor."""
|
|
33
|
+
QWidget.__init__(self, None)
|
|
34
|
+
self.editor_stack = editor_stack
|
|
35
|
+
self.editorsplitter = self.editor_stack
|
|
36
|
+
self.open_action = Mock()
|
|
37
|
+
self.new_action = Mock()
|
|
38
|
+
self.save_action = Mock()
|
|
39
|
+
self.close_action = Mock()
|
|
40
|
+
|
|
41
|
+
layout = QVBoxLayout()
|
|
42
|
+
layout.addWidget(self.editor_stack)
|
|
43
|
+
self.setLayout(layout)
|
|
44
|
+
|
|
45
|
+
def get_widget(self):
|
|
46
|
+
"""Return self, mimicking Spyder 6 API."""
|
|
47
|
+
return self
|
|
48
|
+
|
|
49
|
+
def get_current_editorstack(self):
|
|
50
|
+
"""Return EditorStack instance."""
|
|
51
|
+
return self.editor_stack
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class StatusBarMock(QWidget):
|
|
55
|
+
"""Stausbar mock."""
|
|
56
|
+
|
|
57
|
+
def __init__(self):
|
|
58
|
+
QWidget.__init__(self, None)
|
|
59
|
+
self.layout = QVBoxLayout()
|
|
60
|
+
self.setLayout(self.layout)
|
|
61
|
+
|
|
62
|
+
def add_status_widget(self, widget):
|
|
63
|
+
self.layout.addWidget(widget)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class MainMock(QWidget):
|
|
67
|
+
"""Spyder MainWindow mock."""
|
|
68
|
+
|
|
69
|
+
def __init__(self, editor_stack, qtbot_module):
|
|
70
|
+
"""Main Window Mock constructor."""
|
|
71
|
+
QWidget.__init__(self, None)
|
|
72
|
+
self.main = QWidget()
|
|
73
|
+
self.plugin_focus_changed = Mock()
|
|
74
|
+
self.editor = EditorMock(editor_stack)
|
|
75
|
+
self.status_bar = StatusBarMock()
|
|
76
|
+
self.projects = Mock()
|
|
77
|
+
self.open_file = Mock()
|
|
78
|
+
self.projects.get_active_project_path = lambda: None
|
|
79
|
+
layout = QVBoxLayout()
|
|
80
|
+
layout.addWidget(self.editor)
|
|
81
|
+
layout.addWidget(self.status_bar)
|
|
82
|
+
self.setLayout(layout)
|
|
83
|
+
|
|
84
|
+
for name, dict_info in CONF_DEFAULTS:
|
|
85
|
+
if name != CONF_SECTION:
|
|
86
|
+
continue
|
|
87
|
+
for key, val in dict_info.items():
|
|
88
|
+
CONF.set(name, key, val)
|
|
89
|
+
|
|
90
|
+
self.add_dockwidget = Mock()
|
|
91
|
+
|
|
92
|
+
qtbot_module.add_widget(self.main)
|
|
93
|
+
qtbot_module.add_widget(self.editor)
|
|
94
|
+
qtbot_module.add_widget(self.status_bar)
|
|
95
|
+
|
|
96
|
+
def get_plugin(self, dummy, error=True):
|
|
97
|
+
return self.status_bar
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@pytest.fixture(scope="session")
|
|
101
|
+
def qtbot_module(qapp, request):
|
|
102
|
+
"""Module fixture for qtbot."""
|
|
103
|
+
result = QtBot(request)
|
|
104
|
+
return result
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@pytest.fixture(scope="session", autouse=True)
|
|
108
|
+
def vim_bot(qtbot_module):
|
|
109
|
+
"""Editorstack pytest fixture."""
|
|
110
|
+
text = (
|
|
111
|
+
" 123\n" "line 1\n" "line 2\n" "line 3\n" "line 4"
|
|
112
|
+
) # a newline is added at end
|
|
113
|
+
editor_stack = EditorStack(None, [])
|
|
114
|
+
|
|
115
|
+
# Fix the area of the selection
|
|
116
|
+
font = QFont("Courier New")
|
|
117
|
+
font.setPointSize(10)
|
|
118
|
+
editor_stack.set_default_font(font)
|
|
119
|
+
editor_stack.setMinimumWidth(400)
|
|
120
|
+
editor_stack.setMinimumHeight(400)
|
|
121
|
+
|
|
122
|
+
editor_stack.set_find_widget(Mock())
|
|
123
|
+
editor_stack.set_io_actions(Mock(), Mock(), Mock(), Mock())
|
|
124
|
+
finfo0 = editor_stack.new(osp.join(LOCATION, "foo.py"), "utf-8", text)
|
|
125
|
+
finfo1 = editor_stack.new(osp.join(LOCATION, "foo1.py"), "utf-8", text)
|
|
126
|
+
finfo2 = editor_stack.new(osp.join(LOCATION, "foo2.py"), "utf-8", text)
|
|
127
|
+
finfo3 = editor_stack.new(osp.join(LOCATION, "foo3.py"), "utf-8", text)
|
|
128
|
+
main = MainMock(editor_stack, qtbot_module)
|
|
129
|
+
|
|
130
|
+
vim = VimTesting(main, None)
|
|
131
|
+
vim.on_initialize()
|
|
132
|
+
|
|
133
|
+
qtbot_module.addWidget(editor_stack)
|
|
134
|
+
qtbot_module.addWidget(finfo0.editor)
|
|
135
|
+
qtbot_module.addWidget(finfo1.editor)
|
|
136
|
+
qtbot_module.addWidget(finfo2.editor)
|
|
137
|
+
qtbot_module.addWidget(finfo3.editor)
|
|
138
|
+
qtbot_module.addWidget(main)
|
|
139
|
+
|
|
140
|
+
# Show GUI
|
|
141
|
+
main.show()
|
|
142
|
+
yield main, editor_stack, finfo0.editor, vim, qtbot_module
|
|
143
|
+
|
|
144
|
+
finfo0.editor.close()
|
|
145
|
+
finfo1.editor.close()
|
|
146
|
+
finfo2.editor.close()
|
|
147
|
+
finfo3.editor.close()
|
|
148
|
+
finfo0.deleteLater()
|
|
149
|
+
finfo1.deleteLater()
|
|
150
|
+
finfo2.deleteLater()
|
|
151
|
+
finfo3.deleteLater()
|
|
152
|
+
editor_stack.deleteLater()
|
|
153
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Excecutor __init__."""
|
|
3
|
+
# %% Import
|
|
4
|
+
# Local imports
|
|
5
|
+
from .executor_normal import ExecutorNormalCmd
|
|
6
|
+
from .executor_visual import ExecutorVisualCmd
|
|
7
|
+
from .executor_vline import ExecutorVlineCmd
|
|
8
|
+
from .executor_leader import ExecutorLeaderKey
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""."""
|
|
3
|
+
# %% Import
|
|
4
|
+
# Standard library imports
|
|
5
|
+
from typing import NamedTuple, Any, List
|
|
6
|
+
|
|
7
|
+
# Local imports
|
|
8
|
+
from spyder_okvim.utils.helper_motion import HelperMotion
|
|
9
|
+
from spyder_okvim.utils.helper_action import HelperAction
|
|
10
|
+
|
|
11
|
+
FUNC_INFO = NamedTuple("FUNC_INFO", [("func", Any), ("has_arg", bool)])
|
|
12
|
+
RETURN_EXECUTOR_METHOD_INFO = NamedTuple(
|
|
13
|
+
"RETURN_EXECUTOR_METHOD_INFO", [("sub_mode", Any), ("clear_command_line", bool)]
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ExecutorBase:
|
|
18
|
+
"""Baseclass Executor."""
|
|
19
|
+
|
|
20
|
+
SYMBOLS_REPLACEMENT = {
|
|
21
|
+
"!": "exclamation",
|
|
22
|
+
"?": "question",
|
|
23
|
+
"<": "less",
|
|
24
|
+
">": "greater",
|
|
25
|
+
"|": "pipe",
|
|
26
|
+
" ": "space",
|
|
27
|
+
"\b": "backspace",
|
|
28
|
+
"\r": "enter",
|
|
29
|
+
"@": "at",
|
|
30
|
+
"$": "dollar",
|
|
31
|
+
"0": "zero",
|
|
32
|
+
"^": "caret",
|
|
33
|
+
'"': "quote",
|
|
34
|
+
"%": "percent",
|
|
35
|
+
"~": "tilde",
|
|
36
|
+
":": "colon",
|
|
37
|
+
"/": "slash",
|
|
38
|
+
";": "semicolon",
|
|
39
|
+
",": "comma",
|
|
40
|
+
".": "dot",
|
|
41
|
+
"[": "opensquarebracket",
|
|
42
|
+
"]": "closesquarebracket",
|
|
43
|
+
"*": "asterisk",
|
|
44
|
+
"#": "sharp",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
def __init__(self, vim_status):
|
|
48
|
+
"""__init__.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
vim_status : spyder_okvim.utils.vim_status.VimStatus
|
|
53
|
+
vim status
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
self.vim_status = vim_status
|
|
57
|
+
self.get_cursor = vim_status.get_cursor
|
|
58
|
+
self.set_cursor = vim_status.set_cursor
|
|
59
|
+
self.get_editor = vim_status.get_editor
|
|
60
|
+
self.get_editorstack = vim_status.get_editorstack
|
|
61
|
+
self.get_block_no_start_in_selection = (
|
|
62
|
+
vim_status.get_block_no_start_in_selection
|
|
63
|
+
)
|
|
64
|
+
self.get_block_no_end_in_selection = vim_status.get_block_no_end_in_selection
|
|
65
|
+
self.get_pos_start_in_selection = vim_status.get_pos_start_in_selection
|
|
66
|
+
self.get_pos_end_in_selection = vim_status.get_pos_end_in_selection
|
|
67
|
+
self.set_cursor_pos = vim_status.cursor.set_cursor_pos
|
|
68
|
+
self.helper_motion = HelperMotion(vim_status)
|
|
69
|
+
self.helper_action = HelperAction(vim_status)
|
|
70
|
+
|
|
71
|
+
self.has_zero_cmd = True
|
|
72
|
+
self.pattern_cmd = None
|
|
73
|
+
self.allow_leaderkey = True
|
|
74
|
+
|
|
75
|
+
def update_input_cmd_info(self, num_str, cmd, input_txt):
|
|
76
|
+
"""Update input cmd to vim_status."""
|
|
77
|
+
self.vim_status.input_cmd_prev.set(self.vim_status.input_cmd)
|
|
78
|
+
self.vim_status.input_cmd.num_str = num_str
|
|
79
|
+
self.vim_status.input_cmd.cmd = cmd
|
|
80
|
+
|
|
81
|
+
def set_parent_info_to_submode(self, submode, num, num_str):
|
|
82
|
+
"""Set info to submode."""
|
|
83
|
+
submode.parent_num = [num]
|
|
84
|
+
submode.parent_num_str = [num_str]
|
|
85
|
+
|
|
86
|
+
def __call__(self, txt):
|
|
87
|
+
"""Parse txt and executor command.
|
|
88
|
+
|
|
89
|
+
Returns
|
|
90
|
+
-------
|
|
91
|
+
bool
|
|
92
|
+
if return is True, Clear command line
|
|
93
|
+
|
|
94
|
+
"""
|
|
95
|
+
if self.has_zero_cmd and txt == "0":
|
|
96
|
+
# Special case to simplify regexp
|
|
97
|
+
key = "0"
|
|
98
|
+
num_str = "1"
|
|
99
|
+
else:
|
|
100
|
+
match = self.pattern_cmd.match(txt)
|
|
101
|
+
if not match:
|
|
102
|
+
if txt.isdigit():
|
|
103
|
+
return False
|
|
104
|
+
else:
|
|
105
|
+
self.vim_status.sub_mode = None
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
num_str, key = match.groups()
|
|
109
|
+
|
|
110
|
+
num = int(num_str) if num_str else 1
|
|
111
|
+
self.update_input_cmd_info(num_str, key, txt)
|
|
112
|
+
|
|
113
|
+
key = self.SYMBOLS_REPLACEMENT.get(key, key)
|
|
114
|
+
|
|
115
|
+
ret = None
|
|
116
|
+
try:
|
|
117
|
+
method = self.__getattribute__(key)
|
|
118
|
+
except AttributeError:
|
|
119
|
+
print("unknown key", key)
|
|
120
|
+
else:
|
|
121
|
+
ret = method(num=num, num_str=num_str)
|
|
122
|
+
|
|
123
|
+
return self.process_return(ret)
|
|
124
|
+
|
|
125
|
+
def process_return(self, ret: Any):
|
|
126
|
+
"""Process return of method."""
|
|
127
|
+
if ret:
|
|
128
|
+
self.vim_status.sub_mode = ret.sub_mode
|
|
129
|
+
return ret.clear_command_line
|
|
130
|
+
else:
|
|
131
|
+
self.vim_status.sub_mode = None
|
|
132
|
+
return True
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class ExecutorSubBase(ExecutorBase):
|
|
136
|
+
"""Baseclass for submode of executor."""
|
|
137
|
+
|
|
138
|
+
def __init__(self, vim_status):
|
|
139
|
+
super().__init__(vim_status)
|
|
140
|
+
|
|
141
|
+
self.has_zero_cmd = False
|
|
142
|
+
self.parent_num = []
|
|
143
|
+
self.parent_num_str = []
|
|
144
|
+
self.func_list_deferred: List[FUNC_INFO] = []
|
|
145
|
+
self.return_deferred: Any = None
|
|
146
|
+
|
|
147
|
+
def set_parent_info_to_submode(self, submode, num, num_str):
|
|
148
|
+
"""Set parent and self into to submode."""
|
|
149
|
+
submode.parent_num = self.parent_num.copy()
|
|
150
|
+
submode.parent_num_str = self.parent_num_str.copy()
|
|
151
|
+
|
|
152
|
+
submode.parent_num.append(num)
|
|
153
|
+
submode.parent_num_str.append(num_str)
|
|
154
|
+
|
|
155
|
+
def set_func_list_deferred(self, f_list: List[FUNC_INFO], ret: Any = None):
|
|
156
|
+
"""Set func list."""
|
|
157
|
+
self.func_list_deferred = f_list
|
|
158
|
+
self.return_deferred = ret
|
|
159
|
+
|
|
160
|
+
def execute_func_deferred(self, arg=None):
|
|
161
|
+
"""Execute method passed in from previous executor."""
|
|
162
|
+
for func_info in self.func_list_deferred:
|
|
163
|
+
if func_info.has_arg:
|
|
164
|
+
func_info.func(arg)
|
|
165
|
+
else:
|
|
166
|
+
func_info.func()
|
|
167
|
+
return self.return_deferred
|
|
168
|
+
|
|
169
|
+
def update_input_cmd_info(self, num_str, cmd, input_txt):
|
|
170
|
+
"""Add input cmd to vim_status."""
|
|
171
|
+
self.vim_status.input_cmd.cmd += input_txt
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""."""
|
|
3
|
+
# Local imports
|
|
4
|
+
from spyder_okvim.executor.executor_base import ExecutorSubBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ExecutorColon(ExecutorSubBase):
|
|
8
|
+
"""Execute the method by enter."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, vim_status):
|
|
11
|
+
super().__init__(vim_status)
|
|
12
|
+
self.allow_leaderkey = False
|
|
13
|
+
|
|
14
|
+
self.editor_widget = vim_status.editor_widget
|
|
15
|
+
|
|
16
|
+
def __call__(self, txt):
|
|
17
|
+
"""Parse txt and executor command.
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
bool
|
|
22
|
+
if return is True, Clear command line
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
if txt[-1] == "\b":
|
|
26
|
+
cmd_line = self.vim_status.cmd_line
|
|
27
|
+
if len(txt) <= 2:
|
|
28
|
+
self.vim_status.sub_mode = None
|
|
29
|
+
return True
|
|
30
|
+
else:
|
|
31
|
+
cmd_line.setText(txt[:-2])
|
|
32
|
+
if txt[-1] != "\r":
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
if txt[0] != ":" or len(txt) <= 2:
|
|
36
|
+
self.vim_status.sub_mode = None
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
# ':' is saved to input cmd_info at ExecutorBase.
|
|
40
|
+
# So we need only txt[1:].
|
|
41
|
+
self.update_input_cmd_info(None, None, txt[1:])
|
|
42
|
+
|
|
43
|
+
txt = txt[1:-1] # remove :, \r
|
|
44
|
+
cmd = txt.split(None, 1)
|
|
45
|
+
args = cmd[1] if len(cmd) > 1 else ""
|
|
46
|
+
cmd = cmd[0]
|
|
47
|
+
|
|
48
|
+
for symbol, text in self.SYMBOLS_REPLACEMENT.items():
|
|
49
|
+
cmd = cmd.replace(symbol, text)
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
method = self.__getattribute__(cmd)
|
|
53
|
+
except AttributeError:
|
|
54
|
+
print("unknown command", cmd)
|
|
55
|
+
else:
|
|
56
|
+
method(args)
|
|
57
|
+
|
|
58
|
+
self.vim_status.sub_mode = None
|
|
59
|
+
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
def w(self, arg=""):
|
|
63
|
+
"""Save current file."""
|
|
64
|
+
self.editor_widget.get_widget().save_action.trigger()
|
|
65
|
+
self.vim_status.cursor.draw_vim_cursor()
|
|
66
|
+
|
|
67
|
+
def q(self, arg=""):
|
|
68
|
+
"""Close current file."""
|
|
69
|
+
self.editor_widget.get_widget().close_action.trigger()
|
|
70
|
+
self.vim_status.set_focus_to_vim()
|
|
71
|
+
|
|
72
|
+
def qexclamation(self, arg=""):
|
|
73
|
+
"""Close current file without saving."""
|
|
74
|
+
# TODO :
|
|
75
|
+
self.editor_widget.get_widget().close_action.trigger()
|
|
76
|
+
self.vim_status.set_focus_to_vim()
|
|
77
|
+
|
|
78
|
+
def wq(self, arg=""):
|
|
79
|
+
"""Save and close current file."""
|
|
80
|
+
self.w(arg)
|
|
81
|
+
self.q()
|
|
82
|
+
|
|
83
|
+
def n(self, args=""):
|
|
84
|
+
"""Create new file."""
|
|
85
|
+
self.editor_widget.get_widget().new_action.trigger()
|
|
86
|
+
self.vim_status.set_focus_to_vim()
|