-
Notifications
You must be signed in to change notification settings - Fork 1
/
uberselection.py
93 lines (80 loc) · 3.55 KB
/
uberselection.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import sublime, sublime_plugin
import functools
import re
import selection
import location
import actions, vimactions
import newgrammar
import pyparsing
class UberSelectionCommand(sublime_plugin.TextCommand):
"""Executes vim ex-mode like commands.
"""
grammar = newgrammar.generate_grammar()
def run(self, edit, command=None):
if not command:
self.show_input_panel(edit)
else:
self.on_done(self.view, command)
def show_input_panel(self, edit):
self.view.window().show_input_panel(
"UberSelection:",
getattr(self, 'last_cmd_line', ''),
functools.partial(self.on_done, edit),
None, None)
def on_done(self, edit, s):
self.last_cmd_line = s
try:
tokens = self.grammar.parseString(s)
except pyparsing.ParseException:
sublime.status_message(
"[ERROR] UberSelection: Invalid command string.")
return
if tokens.vim_cmd:
vimactions.dispatch(self.view,
tokens.vim_cmd[0], *tokens.vim_cmd[1])
# Avoids showing input panel again.
return
elif tokens.complex_cmd:
selection.select_spanning_lines(self.parseRange(tokens.complex_cmd.range),
self.view)
for cmd in tokens.complex_cmd:
if cmd[0] == "V":
actions.include(self.view, cmd[1], cmd[2])
if cmd[0] == "-V":
actions.exclude(self.view, cmd[1], cmd[2])
if cmd[0] == "s":
actions.replace(self.view, cmd[2][0], cmd[3][0])
elif tokens.range:
selection.select_spanning_lines(self.parseRange(tokens.range), self.view)
elif tokens.cmd:
selection.select_spanning_lines(
self.parseRange(self.grammar.parseString(".").range),
self.view)
for cmd in tokens.cmd:
if cmd[0] == "s":
actions.replace(self.view, cmd[2][0], cmd[3][0])
else:
sublime.status_message("[ERROR] UberSelection: Unknown command.")
self.show_input_panel(self.view)
def parseRange(self, r):
if r.all == "%":
x, offset_x = "1", "0"
y, offset_y = "$", "0"
else:
x, offset_x = r.a.value, r.a.offset
y, offset_y = (x, offset_x) if not hasattr(r.b, "value") else (
r.b.value,
r.b.offset
)
return self.parseRangePart(x) + int(offset_x), self.parseRangePart(y) + int(offset_y)
def parseRangePart(self, p):
if p.isdigit():
return int(p)
# Order matters! This case can contain the next one with other semantics.
if p.startswith('/') or p.startswith('?'):
if p.startswith('?'):
return location.reverse_search(self.view, p[1:-1],
end=self.view.sel()[0].begin())
return location.search(self.view, p[1:-1])
if p in ('$', '.'):
return location.calculate_relative_ref(self.view, p)