suplemon-editor 0.3.0__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.
- suplemon/__init__.py +0 -0
- suplemon/__main__.py +3 -0
- suplemon/cli.py +65 -0
- suplemon/config/defaults.json +153 -0
- suplemon/config/keymap.json +63 -0
- suplemon/config.py +324 -0
- suplemon/cursor.py +135 -0
- suplemon/editor.py +568 -0
- suplemon/file.py +193 -0
- suplemon/help.py +327 -0
- suplemon/helpers.py +104 -0
- suplemon/hex2xterm.py +23 -0
- suplemon/key_mappings.py +239 -0
- suplemon/lexer.py +88 -0
- suplemon/line.py +51 -0
- suplemon/linelight/__init__.py +0 -0
- suplemon/linelight/color_map.py +9 -0
- suplemon/linelight/css.py +21 -0
- suplemon/linelight/diff.py +17 -0
- suplemon/linelight/html.py +19 -0
- suplemon/linelight/js.py +21 -0
- suplemon/linelight/json.py +15 -0
- suplemon/linelight/lua.py +10 -0
- suplemon/linelight/md.py +19 -0
- suplemon/linelight/php.py +27 -0
- suplemon/linelight/py.py +27 -0
- suplemon/logger.py +90 -0
- suplemon/main.py +771 -0
- suplemon/module_loader.py +98 -0
- suplemon/modules/application_state.py +84 -0
- suplemon/modules/autocomplete.py +114 -0
- suplemon/modules/autodocstring.py +142 -0
- suplemon/modules/battery.py +118 -0
- suplemon/modules/bulk_delete.py +75 -0
- suplemon/modules/clock.py +22 -0
- suplemon/modules/comment.py +59 -0
- suplemon/modules/config.py +20 -0
- suplemon/modules/date.py +22 -0
- suplemon/modules/diff.py +42 -0
- suplemon/modules/eval.py +43 -0
- suplemon/modules/hostname.py +34 -0
- suplemon/modules/input_test.py +23 -0
- suplemon/modules/keymap.py +20 -0
- suplemon/modules/linter.py +274 -0
- suplemon/modules/lower.py +21 -0
- suplemon/modules/lstrip.py +20 -0
- suplemon/modules/paste.py +49 -0
- suplemon/modules/reload.py +16 -0
- suplemon/modules/replace_all.py +23 -0
- suplemon/modules/reverse.py +22 -0
- suplemon/modules/rstrip.py +19 -0
- suplemon/modules/save.py +16 -0
- suplemon/modules/save_all.py +19 -0
- suplemon/modules/sort_lines.py +41 -0
- suplemon/modules/strip.py +19 -0
- suplemon/modules/system_clipboard.py +121 -0
- suplemon/modules/tabstospaces.py +18 -0
- suplemon/modules/toggle_whitespace.py +18 -0
- suplemon/modules/upper.py +21 -0
- suplemon/prompt.py +319 -0
- suplemon/suplemon_module.py +210 -0
- suplemon/themes/8colors.tmTheme +179 -0
- suplemon/themes/monokai.tmTheme +284 -0
- suplemon/themes.py +201 -0
- suplemon/ui.py +656 -0
- suplemon/viewer.py +1004 -0
- suplemon_editor-0.3.0.dist-info/METADATA +515 -0
- suplemon_editor-0.3.0.dist-info/RECORD +72 -0
- suplemon_editor-0.3.0.dist-info/WHEEL +5 -0
- suplemon_editor-0.3.0.dist-info/entry_points.txt +3 -0
- suplemon_editor-0.3.0.dist-info/licenses/LICENSE +21 -0
- suplemon_editor-0.3.0.dist-info/top_level.txt +1 -0
suplemon/file.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# -*- encoding: utf-8
|
|
2
|
+
"""
|
|
3
|
+
File object for storing an opened file and editor.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import time
|
|
8
|
+
import logging
|
|
9
|
+
|
|
10
|
+
from .helpers import parse_path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class File:
|
|
14
|
+
def __init__(self, app=None):
|
|
15
|
+
self.app = app
|
|
16
|
+
self.logger = logging.getLogger(__name__)
|
|
17
|
+
self.name = ""
|
|
18
|
+
self.fpath = ""
|
|
19
|
+
self.data = None
|
|
20
|
+
self.read_only = False # Currently unused
|
|
21
|
+
self.last_save = None # Time of last save
|
|
22
|
+
self.opened = time.time() # Time of last open
|
|
23
|
+
self.editor = None
|
|
24
|
+
self.writable = True
|
|
25
|
+
self.is_help = False
|
|
26
|
+
|
|
27
|
+
def _path(self):
|
|
28
|
+
"""Get the full path of the file."""
|
|
29
|
+
return os.path.join(self.fpath, self.name)
|
|
30
|
+
|
|
31
|
+
def get_encoding(self):
|
|
32
|
+
"""Get the encoding to read and write this file with.
|
|
33
|
+
|
|
34
|
+
Comes from the editor's default_encoding setting. Without this the
|
|
35
|
+
files were opened with whatever the locale happened to be, so the
|
|
36
|
+
setting did nothing and a non UTF-8 locale mangled the contents.
|
|
37
|
+
|
|
38
|
+
:return: Name of the encoding to use.
|
|
39
|
+
:rtype: str
|
|
40
|
+
"""
|
|
41
|
+
default = "utf-8"
|
|
42
|
+
if self.app is None:
|
|
43
|
+
return default
|
|
44
|
+
try:
|
|
45
|
+
return self.app.config["editor"]["default_encoding"] or default
|
|
46
|
+
except (KeyError, TypeError):
|
|
47
|
+
return default
|
|
48
|
+
|
|
49
|
+
def path(self):
|
|
50
|
+
"""Get the full path of the file."""
|
|
51
|
+
# TODO: deprecate in favour of get_path()
|
|
52
|
+
return self._path()
|
|
53
|
+
|
|
54
|
+
def get_name(self):
|
|
55
|
+
"""Get the file name."""
|
|
56
|
+
return self.name
|
|
57
|
+
|
|
58
|
+
def get_path(self):
|
|
59
|
+
"""Get the full path of the file."""
|
|
60
|
+
return self._path()
|
|
61
|
+
|
|
62
|
+
def get_extension(self):
|
|
63
|
+
parts = self.name.split(".")
|
|
64
|
+
if len(parts) < 2:
|
|
65
|
+
return ""
|
|
66
|
+
return parts[-1]
|
|
67
|
+
|
|
68
|
+
def get_editor(self):
|
|
69
|
+
"""Get the associated editor."""
|
|
70
|
+
return self.editor
|
|
71
|
+
|
|
72
|
+
def set_name(self, name):
|
|
73
|
+
"""Set the file name."""
|
|
74
|
+
# TODO: sanitize
|
|
75
|
+
self.name = name
|
|
76
|
+
self.update_editor_extension()
|
|
77
|
+
|
|
78
|
+
def set_path(self, path):
|
|
79
|
+
"""Set the file path. Relative paths are sanitized."""
|
|
80
|
+
self.fpath, self.name = parse_path(path)
|
|
81
|
+
self.update_editor_extension()
|
|
82
|
+
|
|
83
|
+
def set_data(self, data):
|
|
84
|
+
"""Set the file data and apply to editor if it exists."""
|
|
85
|
+
self.data = data
|
|
86
|
+
if self.editor:
|
|
87
|
+
self.editor.set_data(data)
|
|
88
|
+
|
|
89
|
+
def set_editor(self, editor):
|
|
90
|
+
"""The editor instance set its file extension."""
|
|
91
|
+
self.editor = editor
|
|
92
|
+
self.update_editor_extension()
|
|
93
|
+
|
|
94
|
+
def on_load(self):
|
|
95
|
+
"""Does checks after file is loaded."""
|
|
96
|
+
self.writable = os.access(self._path(), os.W_OK)
|
|
97
|
+
if not self.writable:
|
|
98
|
+
self.logger.info("File not writable.")
|
|
99
|
+
|
|
100
|
+
def update_editor_extension(self):
|
|
101
|
+
"""Set the editor file extension from the current file name."""
|
|
102
|
+
if not self.editor:
|
|
103
|
+
return False
|
|
104
|
+
ext = self.get_extension()
|
|
105
|
+
if len(ext) >= 1:
|
|
106
|
+
self.editor.set_file_extension(ext)
|
|
107
|
+
|
|
108
|
+
def save(self):
|
|
109
|
+
"""Write the editor data to file."""
|
|
110
|
+
data = self.editor.get_data()
|
|
111
|
+
try:
|
|
112
|
+
with open(self._path(), "w", encoding=self.get_encoding()) as f:
|
|
113
|
+
f.write(data)
|
|
114
|
+
except (OSError, LookupError, UnicodeError):
|
|
115
|
+
self.logger.exception('Failed writing file "{file}"'.format(file=self._path()))
|
|
116
|
+
return False
|
|
117
|
+
self.data = data
|
|
118
|
+
self.last_save = time.time()
|
|
119
|
+
self.writable = os.access(self._path(), os.W_OK)
|
|
120
|
+
return True
|
|
121
|
+
|
|
122
|
+
def load(self, read=True):
|
|
123
|
+
"""Try to read the actual file and load the data into the editor instance."""
|
|
124
|
+
if not read:
|
|
125
|
+
return True
|
|
126
|
+
path = self._path()
|
|
127
|
+
if not os.path.isfile(path):
|
|
128
|
+
self.logger.debug("Given path isn't a file.")
|
|
129
|
+
return False
|
|
130
|
+
data = self._read(path)
|
|
131
|
+
if data is False:
|
|
132
|
+
return False
|
|
133
|
+
self.data = data
|
|
134
|
+
self.editor.set_data(data)
|
|
135
|
+
self.on_load()
|
|
136
|
+
return True
|
|
137
|
+
|
|
138
|
+
def _read(self, path):
|
|
139
|
+
data = self._read_text(path)
|
|
140
|
+
if data is False:
|
|
141
|
+
self.logger.warning("Normal file read failed.")
|
|
142
|
+
data = self._read_binary(path)
|
|
143
|
+
if data is False:
|
|
144
|
+
self.logger.warning("Fallback file read failed.")
|
|
145
|
+
return False
|
|
146
|
+
return data
|
|
147
|
+
|
|
148
|
+
def _read_text(self, file):
|
|
149
|
+
# Read text file
|
|
150
|
+
try:
|
|
151
|
+
with open(self._path(), encoding=self.get_encoding()) as f:
|
|
152
|
+
return f.read()
|
|
153
|
+
except (OSError, LookupError, UnicodeDecodeError):
|
|
154
|
+
self.logger.exception('Failed reading file "{file}"'.format(file=file))
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
def _read_binary(self, file):
|
|
158
|
+
# Read binary file and try to autodetect encoding
|
|
159
|
+
try:
|
|
160
|
+
f = open(self._path(), "rb")
|
|
161
|
+
data = f.read()
|
|
162
|
+
f.close()
|
|
163
|
+
import chardet
|
|
164
|
+
detection = chardet.detect(data)
|
|
165
|
+
charenc = detection["encoding"]
|
|
166
|
+
if charenc is None:
|
|
167
|
+
self.logger.warning("Failed to detect file encoding.")
|
|
168
|
+
return False
|
|
169
|
+
self.logger.info("Trying to decode with encoding '{0}'".format(charenc))
|
|
170
|
+
return data.decode(charenc)
|
|
171
|
+
except Exception:
|
|
172
|
+
self.logger.warning("Failed reading binary file!", exc_info=True)
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
def reload(self):
|
|
176
|
+
"""Reload file data."""
|
|
177
|
+
return self.load()
|
|
178
|
+
|
|
179
|
+
def is_changed(self):
|
|
180
|
+
"""Check if the editor data is different from the file."""
|
|
181
|
+
return self.editor.get_data() != self.data
|
|
182
|
+
|
|
183
|
+
def is_changed_on_disk(self):
|
|
184
|
+
path = self._path()
|
|
185
|
+
if os.path.isfile(path):
|
|
186
|
+
data = self._read(path)
|
|
187
|
+
if data != self.data:
|
|
188
|
+
return True
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
def is_writable(self):
|
|
192
|
+
"""Check if the file is writable."""
|
|
193
|
+
return self.writable
|
suplemon/help.py
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Help text for Suplemon
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
help_text = """
|
|
6
|
+
# Suplemon Help
|
|
7
|
+
|
|
8
|
+
*Contents*
|
|
9
|
+
1. General description
|
|
10
|
+
2. User interface
|
|
11
|
+
3. Selecting text
|
|
12
|
+
4. Default keyboard shortcuts
|
|
13
|
+
5. Commands
|
|
14
|
+
|
|
15
|
+
## 1. General description
|
|
16
|
+
Suplemon is designed to be an easy, intuitive and powerful text editor.
|
|
17
|
+
It emulates some features from Sublime Text and the user interface of Nano.
|
|
18
|
+
Multi cursor editing is a core feature. Suplemon also supports extensions
|
|
19
|
+
so you can customize it to work how you want.
|
|
20
|
+
|
|
21
|
+
## 2. User interface
|
|
22
|
+
The user interface is designed to be as intuitive and informative as possible.
|
|
23
|
+
There are two status bars, one at the top and one at the bottom. The top bar
|
|
24
|
+
shows the program version, a clock, and a list of opened files. The bottom bar
|
|
25
|
+
shows status messages and handles input for commands. Above the bottom status
|
|
26
|
+
bar there is a list of most common keyboard shortcuts.
|
|
27
|
+
|
|
28
|
+
## 3. Selecting text
|
|
29
|
+
Suplemon has no text selection. There is no way to select a range of
|
|
30
|
+
characters, with or without a mouse, and nothing highlights as you move.
|
|
31
|
+
What it has instead is cursors, and the unit of work is the whole line.
|
|
32
|
+
|
|
33
|
+
Copy (Ctrl + C) and cut (Ctrl + X) act on every line that has a cursor on
|
|
34
|
+
it. Where the cursor sits within the line makes no difference: one cursor
|
|
35
|
+
anywhere on a line copies that entire line. So "selecting" more text means
|
|
36
|
+
putting more cursors down.
|
|
37
|
+
|
|
38
|
+
* Alt + Down / Up
|
|
39
|
+
> Add a cursor on the next or previous line. This is the usual way to
|
|
40
|
+
> take several lines: press Alt + Down twice, then Ctrl + C, to copy
|
|
41
|
+
> three lines.
|
|
42
|
+
|
|
43
|
+
* Ctrl + D
|
|
44
|
+
> Add a cursor at the next occurance of the word the cursor is on.
|
|
45
|
+
> Press it repeatedly to gather up matches one at a time.
|
|
46
|
+
|
|
47
|
+
* Ctrl + A
|
|
48
|
+
> Add a cursor at every occurance of the last search term at once.
|
|
49
|
+
> Search with Ctrl + F or Ctrl + D first to set the term.
|
|
50
|
+
|
|
51
|
+
* ESC
|
|
52
|
+
> Collapse back to a single cursor when you're done.
|
|
53
|
+
|
|
54
|
+
Pasting (Ctrl + V) behaves in one of two ways. With a single cursor and
|
|
55
|
+
several copied lines, the lines are inserted as new lines. With several
|
|
56
|
+
cursors, or a single copied line, the text is inserted at each cursor
|
|
57
|
+
position instead, and a single copied line is repeated into every cursor.
|
|
58
|
+
That last case is what makes multi cursor editing useful: copy one line,
|
|
59
|
+
put ten cursors down, and paste it into all ten at once.
|
|
60
|
+
|
|
61
|
+
The practical consequence is that you cannot copy part of a line. If you
|
|
62
|
+
need a fragment, copy the whole line and trim what you don't want after
|
|
63
|
+
pasting it. This is the habit that takes the longest to unlearn if you're
|
|
64
|
+
coming from an editor with ordinary selection.
|
|
65
|
+
|
|
66
|
+
Copy and paste share text with other applications when a clipboard tool
|
|
67
|
+
such as xsel, xclip, wl-clipboard or pbcopy is installed. Without one they
|
|
68
|
+
still work, but only within Suplemon.
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## 4. Default keyboard shortcuts
|
|
72
|
+
The default keyboard shortcuts imitate those of common graphical editors.
|
|
73
|
+
Most shortcuts are also shown at the bottom in the legend area. Here's
|
|
74
|
+
the complete reference.
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
* F1 / Ctrl + H
|
|
78
|
+
> Show this help. On terminals where the backspace key sends ^H, Ctrl + H
|
|
79
|
+
> arrives as backspace and can't reach the help, so F1 always works.
|
|
80
|
+
|
|
81
|
+
* Ctrl + Q
|
|
82
|
+
> Exit
|
|
83
|
+
|
|
84
|
+
* Ctrl + W
|
|
85
|
+
> Close file or tab
|
|
86
|
+
|
|
87
|
+
* Ctrl + C
|
|
88
|
+
> Copy line(s) to buffer
|
|
89
|
+
|
|
90
|
+
* Ctrl + X
|
|
91
|
+
> Cut line(s) to buffer
|
|
92
|
+
|
|
93
|
+
* Ctrl + V
|
|
94
|
+
> Insert buffer
|
|
95
|
+
|
|
96
|
+
* Ctrl + K
|
|
97
|
+
> Duplicate line
|
|
98
|
+
|
|
99
|
+
* Ctrl + G
|
|
100
|
+
> Go to line number or file (type the beginning of a filename to switch to it).
|
|
101
|
+
> You can also use 'filena:42' to go to line 42 in filename.py etc.
|
|
102
|
+
|
|
103
|
+
* Ctrl + F
|
|
104
|
+
> Search for a string or regular expression (configurable)
|
|
105
|
+
|
|
106
|
+
* Ctrl + D
|
|
107
|
+
> Search for next occurance or find the word the cursor is on. Adds a new cursor at each new occurance.
|
|
108
|
+
|
|
109
|
+
* Ctrl + A
|
|
110
|
+
> Add a cursor at every occurance of the last search term. Search with
|
|
111
|
+
> Ctrl + F or Ctrl + D first, otherwise there is no term to look for.
|
|
112
|
+
|
|
113
|
+
* Ctrl + T
|
|
114
|
+
> Trim whitespace
|
|
115
|
+
|
|
116
|
+
* Alt + Arrow Key
|
|
117
|
+
> Add new curor in arrow direction
|
|
118
|
+
|
|
119
|
+
* Ctrl + Left / Right
|
|
120
|
+
> Jump to previous or next word or line
|
|
121
|
+
|
|
122
|
+
* Ctrl + Up / Down
|
|
123
|
+
> Jump three lines up or down
|
|
124
|
+
|
|
125
|
+
* ESC
|
|
126
|
+
> Revert to a single cursor / Cancel input prompt
|
|
127
|
+
|
|
128
|
+
* Alt + Page Up
|
|
129
|
+
> Move line(s) up
|
|
130
|
+
|
|
131
|
+
* Alt + Page Down
|
|
132
|
+
> Move line(s) down
|
|
133
|
+
|
|
134
|
+
* Tab / Shift + Tab
|
|
135
|
+
> Indent or unindent line(s)
|
|
136
|
+
|
|
137
|
+
* Ctrl + S
|
|
138
|
+
> Save current file
|
|
139
|
+
|
|
140
|
+
* F3
|
|
141
|
+
> Save file with new name
|
|
142
|
+
|
|
143
|
+
* F2
|
|
144
|
+
> Reload current file
|
|
145
|
+
|
|
146
|
+
* Ctrl + N
|
|
147
|
+
> New empty file
|
|
148
|
+
|
|
149
|
+
* Ctrl + O
|
|
150
|
+
> Open file
|
|
151
|
+
|
|
152
|
+
* Ctrl + W
|
|
153
|
+
> Close file
|
|
154
|
+
|
|
155
|
+
* F4 / Ctrl + Page Up / Ctrl + Alt + Page Up
|
|
156
|
+
> Switch to next file. Wraps around, so F4 alone cycles through them all.
|
|
157
|
+
|
|
158
|
+
* Shift + F4 / Ctrl + Page Down / Ctrl + Alt + Page Down
|
|
159
|
+
> Switch to previous file. Terminals often keep Ctrl + Page Up and Down
|
|
160
|
+
> for their own tabs, hence the alternatives. Shift + F4 and the
|
|
161
|
+
> Ctrl + Alt pair need a terminfo entry that describes those keys, which
|
|
162
|
+
> older ones (FreeBSD's among them) do not have; F4 works everywhere.
|
|
163
|
+
|
|
164
|
+
* Ctrl + E
|
|
165
|
+
> Run a command.
|
|
166
|
+
|
|
167
|
+
* F5 / Ctrl + Z
|
|
168
|
+
> Undo
|
|
169
|
+
|
|
170
|
+
* F6 / Ctrl + Y
|
|
171
|
+
> Redo
|
|
172
|
+
|
|
173
|
+
* F7
|
|
174
|
+
> Toggle visible whitespace
|
|
175
|
+
|
|
176
|
+
* F8
|
|
177
|
+
> Toggle mouse mode
|
|
178
|
+
|
|
179
|
+
* F9
|
|
180
|
+
> Toggle line numbers
|
|
181
|
+
|
|
182
|
+
* F10
|
|
183
|
+
> Toggle visible line endings. They start hidden; the character drawn is
|
|
184
|
+
> the "line_end_char" setting in your config.
|
|
185
|
+
|
|
186
|
+
* F11
|
|
187
|
+
> Toggle syntax highlighting. Bound but not implemented yet, so pressing
|
|
188
|
+
> it currently does nothing.
|
|
189
|
+
|
|
190
|
+
* F12
|
|
191
|
+
> Toggle full screen
|
|
192
|
+
|
|
193
|
+
### Mouse shortcuts
|
|
194
|
+
|
|
195
|
+
* Left Click
|
|
196
|
+
> Set cursor at mouse position. Reverts to a single cursor.
|
|
197
|
+
|
|
198
|
+
* Right Click
|
|
199
|
+
> Add a cursor at mouse position.
|
|
200
|
+
|
|
201
|
+
* Scroll Wheel Up / Down
|
|
202
|
+
> Scroll up & down.
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
## 5. Commands
|
|
206
|
+
Commands are special operations that can be performed (e.g. remove whitespace
|
|
207
|
+
or convert line to uppercase). Each command can be run by pressing Ctrl + E
|
|
208
|
+
and then typing the command name. Commands are extensions and are stored in
|
|
209
|
+
the modules folder in the Suplemon installation.
|
|
210
|
+
|
|
211
|
+
* autocomplete
|
|
212
|
+
|
|
213
|
+
A simple autocompletion module.
|
|
214
|
+
|
|
215
|
+
This adds autocomplete support for the tab key. It uses a word
|
|
216
|
+
list scanned from all open files for completions. By default it suggests
|
|
217
|
+
the shortest possible match. If there are no matches, the tab action is
|
|
218
|
+
run normally.
|
|
219
|
+
|
|
220
|
+
* autodocstring
|
|
221
|
+
|
|
222
|
+
Simple module for adding docstring placeholders.
|
|
223
|
+
|
|
224
|
+
This module is intended to generate docstrings for Python functions.
|
|
225
|
+
It adds placeholders for descriptions, arguments and return data.
|
|
226
|
+
Function arguments are crudely parsed from the function definition
|
|
227
|
+
and return statements are scanned from the function body.
|
|
228
|
+
|
|
229
|
+
* bulk_delete
|
|
230
|
+
|
|
231
|
+
Bulk delete lines and characters.
|
|
232
|
+
Asks what direction to delete in by default.
|
|
233
|
+
|
|
234
|
+
Add 'up' to delete lines above highest cursor.
|
|
235
|
+
Add 'down' to delete lines below lowest cursor.
|
|
236
|
+
Add 'left' to delete characters to the left of all cursors.
|
|
237
|
+
Add 'right' to delete characters to the right of all cursors.
|
|
238
|
+
|
|
239
|
+
* comment
|
|
240
|
+
|
|
241
|
+
Toggle line commenting based on current file syntax.
|
|
242
|
+
|
|
243
|
+
* config
|
|
244
|
+
|
|
245
|
+
Shortcut for openning the config files.
|
|
246
|
+
|
|
247
|
+
* diff
|
|
248
|
+
|
|
249
|
+
View a diff of the current file compared to it's on disk version.
|
|
250
|
+
|
|
251
|
+
* eval
|
|
252
|
+
|
|
253
|
+
Evaluate a python expression and show the result in the status bar.
|
|
254
|
+
|
|
255
|
+
If no expression is provided the current line(s) are evaluated and
|
|
256
|
+
replaced with the evaluation result.
|
|
257
|
+
|
|
258
|
+
* keymap
|
|
259
|
+
|
|
260
|
+
Shortcut to openning the keymap config file.
|
|
261
|
+
|
|
262
|
+
* linter
|
|
263
|
+
|
|
264
|
+
Linter for suplemon.
|
|
265
|
+
|
|
266
|
+
* lower
|
|
267
|
+
|
|
268
|
+
Transform current lines to lower case.
|
|
269
|
+
|
|
270
|
+
* lstrip
|
|
271
|
+
|
|
272
|
+
Trim whitespace from beginning of current lines.
|
|
273
|
+
|
|
274
|
+
* paste
|
|
275
|
+
|
|
276
|
+
Toggle paste mode (helpful when pasting over SSH if auto indent is enabled)
|
|
277
|
+
|
|
278
|
+
* reload
|
|
279
|
+
|
|
280
|
+
Reload all add-on modules.
|
|
281
|
+
|
|
282
|
+
* replace_all
|
|
283
|
+
|
|
284
|
+
Replace all occurrences in all files of given text with given replacement.
|
|
285
|
+
|
|
286
|
+
* reverse
|
|
287
|
+
|
|
288
|
+
Reverse text on current line(s).
|
|
289
|
+
|
|
290
|
+
* rstrip
|
|
291
|
+
|
|
292
|
+
Trim whitespace from the end of lines.
|
|
293
|
+
|
|
294
|
+
* save
|
|
295
|
+
|
|
296
|
+
Save the current file.
|
|
297
|
+
|
|
298
|
+
* save_all
|
|
299
|
+
|
|
300
|
+
Save all currently open files. Asks for confirmation.
|
|
301
|
+
|
|
302
|
+
* sort_lines
|
|
303
|
+
|
|
304
|
+
Sort current lines.
|
|
305
|
+
|
|
306
|
+
Sorts alphabetically by default.
|
|
307
|
+
Add 'length' to sort by length.
|
|
308
|
+
Add 'reverse' to reverse the sorting.
|
|
309
|
+
|
|
310
|
+
* strip
|
|
311
|
+
|
|
312
|
+
Trim whitespace from start and end of lines.
|
|
313
|
+
|
|
314
|
+
* tabstospaces
|
|
315
|
+
|
|
316
|
+
Convert tab characters to spaces in the entire file.
|
|
317
|
+
|
|
318
|
+
* toggle_whitespace
|
|
319
|
+
|
|
320
|
+
Toggle visually showing whitespace.
|
|
321
|
+
|
|
322
|
+
* upper
|
|
323
|
+
|
|
324
|
+
Transform current lines to upper case.
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
"""
|
suplemon/helpers.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# -*- encoding: utf-8
|
|
2
|
+
"""
|
|
3
|
+
Various helper constants and functions.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import sys
|
|
9
|
+
import time
|
|
10
|
+
import traceback
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def curr_time():
|
|
14
|
+
"""Current time in %H:%M"""
|
|
15
|
+
return time.strftime("%H:%M")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def curr_time_sec():
|
|
19
|
+
"""Current time in %H:%M:%S"""
|
|
20
|
+
return time.strftime("%H:%M:%S")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def multisplit(data, delimiters):
|
|
24
|
+
pattern = "|".join(map(re.escape, delimiters))
|
|
25
|
+
return re.split(pattern, data)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_error_info():
|
|
29
|
+
"""Return info about last error."""
|
|
30
|
+
msg = "{0}\n{1}".format(str(traceback.format_exc()), str(sys.exc_info()))
|
|
31
|
+
return msg
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_string_between(start, stop, s):
|
|
35
|
+
"""Search string for a substring between two delimeters. False if not found."""
|
|
36
|
+
i1 = s.find(start)
|
|
37
|
+
if i1 == -1:
|
|
38
|
+
return False
|
|
39
|
+
s = s[i1 + len(start):]
|
|
40
|
+
i2 = s.find(stop)
|
|
41
|
+
if i2 == -1:
|
|
42
|
+
return False
|
|
43
|
+
s = s[:i2]
|
|
44
|
+
return s
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def whitespace(line):
|
|
48
|
+
"""Return index of first non whitespace character on a line.
|
|
49
|
+
|
|
50
|
+
Tabs count as whitespace. Only spaces used to, which meant tab indented
|
|
51
|
+
files behaved as though they had no indentation at all.
|
|
52
|
+
"""
|
|
53
|
+
i = 0
|
|
54
|
+
for char in str(line):
|
|
55
|
+
if char not in " \t":
|
|
56
|
+
break
|
|
57
|
+
i += 1
|
|
58
|
+
return i
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def leading_whitespace(line):
|
|
62
|
+
"""Return the indentation of a line, as written.
|
|
63
|
+
|
|
64
|
+
Use this instead of whitespace() when the indentation is going to be
|
|
65
|
+
reproduced, so that tabs stay tabs and spaces stay spaces.
|
|
66
|
+
"""
|
|
67
|
+
line = str(line)
|
|
68
|
+
return line[:whitespace(line)]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def parse_path(path):
|
|
72
|
+
"""Parse a relative path and return full directory and filename as a tuple.
|
|
73
|
+
|
|
74
|
+
expanduser handles a bare "~" as well as "~/...", which the previous
|
|
75
|
+
manual prefix check did not: "~" on its own was treated as a filename
|
|
76
|
+
in the current directory.
|
|
77
|
+
"""
|
|
78
|
+
path = os.path.expanduser(path)
|
|
79
|
+
ab = os.path.abspath(path)
|
|
80
|
+
parts = os.path.split(ab)
|
|
81
|
+
return parts
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_filename_cursor_pos(name):
|
|
85
|
+
default = {
|
|
86
|
+
"name": name,
|
|
87
|
+
"row": 0,
|
|
88
|
+
"col": 0,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
m = re.match(r"(.*?):(\d+):?(\d+)?", name)
|
|
92
|
+
|
|
93
|
+
if not m:
|
|
94
|
+
return default
|
|
95
|
+
|
|
96
|
+
groups = m.groups()
|
|
97
|
+
if not groups[0]:
|
|
98
|
+
return default
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
"name": groups[0],
|
|
102
|
+
"row": abs(int(groups[1])-1) if groups[1] else 0,
|
|
103
|
+
"col": abs(int(groups[2])-1) if groups[2] else 0,
|
|
104
|
+
}
|
suplemon/hex2xterm.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
# Default color levels for the color cube
|
|
3
|
+
cubelevels = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
|
|
4
|
+
# Generate a list of midpoints of the above list
|
|
5
|
+
snaps = [(x+y)/2 for x, y in list(zip(cubelevels, [0]+cubelevels))[1:]]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def hex_to_rgb(value):
|
|
9
|
+
value = value.lstrip("#")
|
|
10
|
+
lv = len(value)
|
|
11
|
+
return tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def hex_to_xterm(hex):
|
|
15
|
+
"""
|
|
16
|
+
Converts hex color values to the nearest equivalent xterm-256 color.
|
|
17
|
+
"""
|
|
18
|
+
rgb = hex_to_rgb(hex)
|
|
19
|
+
r, g, b = rgb
|
|
20
|
+
# Using list of snap points, convert RGB value to cube indexes
|
|
21
|
+
r, g, b = map(lambda x: len(tuple(s for s in snaps if s < x)), (r, g, b))
|
|
22
|
+
# Simple colorcube transform
|
|
23
|
+
return r*36 + g*6 + b + 16
|