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.
Files changed (72) hide show
  1. suplemon/__init__.py +0 -0
  2. suplemon/__main__.py +3 -0
  3. suplemon/cli.py +65 -0
  4. suplemon/config/defaults.json +153 -0
  5. suplemon/config/keymap.json +63 -0
  6. suplemon/config.py +324 -0
  7. suplemon/cursor.py +135 -0
  8. suplemon/editor.py +568 -0
  9. suplemon/file.py +193 -0
  10. suplemon/help.py +327 -0
  11. suplemon/helpers.py +104 -0
  12. suplemon/hex2xterm.py +23 -0
  13. suplemon/key_mappings.py +239 -0
  14. suplemon/lexer.py +88 -0
  15. suplemon/line.py +51 -0
  16. suplemon/linelight/__init__.py +0 -0
  17. suplemon/linelight/color_map.py +9 -0
  18. suplemon/linelight/css.py +21 -0
  19. suplemon/linelight/diff.py +17 -0
  20. suplemon/linelight/html.py +19 -0
  21. suplemon/linelight/js.py +21 -0
  22. suplemon/linelight/json.py +15 -0
  23. suplemon/linelight/lua.py +10 -0
  24. suplemon/linelight/md.py +19 -0
  25. suplemon/linelight/php.py +27 -0
  26. suplemon/linelight/py.py +27 -0
  27. suplemon/logger.py +90 -0
  28. suplemon/main.py +771 -0
  29. suplemon/module_loader.py +98 -0
  30. suplemon/modules/application_state.py +84 -0
  31. suplemon/modules/autocomplete.py +114 -0
  32. suplemon/modules/autodocstring.py +142 -0
  33. suplemon/modules/battery.py +118 -0
  34. suplemon/modules/bulk_delete.py +75 -0
  35. suplemon/modules/clock.py +22 -0
  36. suplemon/modules/comment.py +59 -0
  37. suplemon/modules/config.py +20 -0
  38. suplemon/modules/date.py +22 -0
  39. suplemon/modules/diff.py +42 -0
  40. suplemon/modules/eval.py +43 -0
  41. suplemon/modules/hostname.py +34 -0
  42. suplemon/modules/input_test.py +23 -0
  43. suplemon/modules/keymap.py +20 -0
  44. suplemon/modules/linter.py +274 -0
  45. suplemon/modules/lower.py +21 -0
  46. suplemon/modules/lstrip.py +20 -0
  47. suplemon/modules/paste.py +49 -0
  48. suplemon/modules/reload.py +16 -0
  49. suplemon/modules/replace_all.py +23 -0
  50. suplemon/modules/reverse.py +22 -0
  51. suplemon/modules/rstrip.py +19 -0
  52. suplemon/modules/save.py +16 -0
  53. suplemon/modules/save_all.py +19 -0
  54. suplemon/modules/sort_lines.py +41 -0
  55. suplemon/modules/strip.py +19 -0
  56. suplemon/modules/system_clipboard.py +121 -0
  57. suplemon/modules/tabstospaces.py +18 -0
  58. suplemon/modules/toggle_whitespace.py +18 -0
  59. suplemon/modules/upper.py +21 -0
  60. suplemon/prompt.py +319 -0
  61. suplemon/suplemon_module.py +210 -0
  62. suplemon/themes/8colors.tmTheme +179 -0
  63. suplemon/themes/monokai.tmTheme +284 -0
  64. suplemon/themes.py +201 -0
  65. suplemon/ui.py +656 -0
  66. suplemon/viewer.py +1004 -0
  67. suplemon_editor-0.3.0.dist-info/METADATA +515 -0
  68. suplemon_editor-0.3.0.dist-info/RECORD +72 -0
  69. suplemon_editor-0.3.0.dist-info/WHEEL +5 -0
  70. suplemon_editor-0.3.0.dist-info/entry_points.txt +3 -0
  71. suplemon_editor-0.3.0.dist-info/licenses/LICENSE +21 -0
  72. suplemon_editor-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,239 @@
1
+ # -*- encoding: utf-8
2
+ """
3
+ This file maps ugly curses keycodes to human readable versions.
4
+ """
5
+
6
+ import curses
7
+
8
+
9
+ key_map = {
10
+ # Single keys
11
+ curses.KEY_F1: "f1", # 265
12
+ curses.KEY_F2: "f2", # 266
13
+ curses.KEY_F3: "f3", # 267
14
+ curses.KEY_F4: "f4", # 268
15
+ curses.KEY_F5: "f5", # 269
16
+ curses.KEY_F6: "f6", # 270
17
+ curses.KEY_F7: "f7", # 271
18
+ curses.KEY_F8: "f8", # 272
19
+ curses.KEY_F9: "f9", # 273
20
+ curses.KEY_F10: "f10", # 274
21
+ curses.KEY_F11: "f11", # 275
22
+ curses.KEY_F12: "f12", # 276
23
+ "KEY_F(1)": "f1",
24
+ "KEY_F(2)": "f2",
25
+ "KEY_F(3)": "f3",
26
+ "KEY_F(4)": "f4",
27
+ "KEY_F(5)": "f5",
28
+ "KEY_F(6)": "f6",
29
+ "KEY_F(7)": "f7",
30
+ "KEY_F(8)": "f8",
31
+ "KEY_F(9)": "f9",
32
+ "KEY_F(10)": "f10",
33
+ "KEY_F(11)": "f11",
34
+ "KEY_F(12)": "f12",
35
+
36
+
37
+ curses.KEY_UP: "up",
38
+ curses.KEY_DOWN: "down",
39
+ curses.KEY_LEFT: "left",
40
+ curses.KEY_RIGHT: "right",
41
+ "KEY_UP": "up",
42
+ "KEY_DOWN": "down",
43
+ "KEY_LEFT": "left",
44
+ "KEY_RIGHT": "right",
45
+
46
+ curses.KEY_ENTER: "enter",
47
+ "KEY_ENTER": "enter",
48
+ "\n": "enter",
49
+ "^J": "enter",
50
+ 343: "shift+enter",
51
+
52
+ curses.KEY_BACKSPACE: "backspace",
53
+ "KEY_BACKSPACE": "backspace",
54
+ "^?": "backspace",
55
+
56
+ curses.KEY_DC: "delete",
57
+ curses.KEY_HOME: "home",
58
+ curses.KEY_END: "end",
59
+ curses.KEY_PPAGE: "pageup",
60
+ curses.KEY_NPAGE: "pagedown",
61
+ "KEY_DC": "delete",
62
+ "KEY_HOME": "home",
63
+ "KEY_END": "end",
64
+ "KEY_PPAGE": "pageup",
65
+ "KEY_NPAGE": "pagedown",
66
+
67
+ curses.KEY_IC: "insert",
68
+ "KEY_IC": "insert",
69
+ 331: "insert",
70
+ "\t": "tab",
71
+ "^I": "tab",
72
+ "^[": "escape",
73
+
74
+
75
+ # Control
76
+ "^A": "ctrl+a",
77
+ "^B": "ctrl+b",
78
+ "^C": "ctrl+c",
79
+ "^D": "ctrl+d",
80
+ "^E": "ctrl+e",
81
+ "^F": "ctrl+f",
82
+ "^G": "ctrl+g",
83
+ "^H": "ctrl+h",
84
+ # "^I": "ctrl+i", # Conflicts with 'tab'
85
+ # "^J": "ctrl+j", # Conflicts with 'enter'
86
+ "^K": "ctrl+k",
87
+ "^L": "ctrl+l",
88
+ # "^M": "ctrl+m", # Conflicts with 'enter'
89
+ "^N": "ctrl+n",
90
+ "^O": "ctrl+o",
91
+ "^P": "ctrl+p",
92
+ "^Q": "ctrl+q",
93
+ "^R": "ctrl+r",
94
+ "^S": "ctrl+s",
95
+ "^T": "ctrl+t",
96
+ "^U": "ctrl+u",
97
+ "^V": "ctrl+v",
98
+ "^W": "ctrl+w",
99
+ "^X": "ctrl+x",
100
+ "^Y": "ctrl+y",
101
+ "^Z": "ctrl+z", # Conflicts with suspend
102
+
103
+ "kLFT5": "ctrl+left",
104
+ "kRIT5": "ctrl+right",
105
+ "kUP5": "ctrl+up",
106
+ "kDN5": "ctrl+down",
107
+
108
+ "kIC5": "ctrl+insert",
109
+ "kDC5": "ctrl+delete",
110
+ "kHOM5": "ctrl+home",
111
+ "kEND5": "ctrl+end",
112
+
113
+ "kPRV5": "ctrl+pageup",
114
+ "kNXT5": "ctrl+pagedown",
115
+
116
+ "O5P": "ctrl+f1",
117
+ "O5Q": "ctrl+f2",
118
+ "O5R": "ctrl+f3",
119
+ "O5S": "ctrl+f4",
120
+
121
+ "KEY_F(29)": "ctrl+f5",
122
+ "KEY_F(30)": "ctrl+f6",
123
+ "KEY_F(31)": "ctrl+f7",
124
+ "KEY_F(32)": "ctrl+f8",
125
+ "KEY_F(33)": "ctrl+f9",
126
+ "KEY_F(34)": "ctrl+f10",
127
+ "KEY_F(35)": "ctrl+f11",
128
+ "KEY_F(36)": "ctrl+f12",
129
+
130
+ # Alt
131
+ "kUP3": "alt+up",
132
+ "kDN3": "alt+down",
133
+ "kLFT3": "alt+left",
134
+ "kRIT3": "alt+right",
135
+
136
+ "kIC3": "alt+insert",
137
+ "kDC3": "alt+delete",
138
+ "kHOM3": "alt+home",
139
+ "kEND3": "alt+end",
140
+
141
+ "kPRV3": "alt+pageup",
142
+ "kNXT3": "alt+pagedown",
143
+
144
+ "KEY_F(53)": "alt+f5",
145
+ "KEY_F(54)": "alt+f6",
146
+ "KEY_F(55)": "alt+f7",
147
+ "KEY_F(56)": "alt+f8",
148
+ "KEY_F(57)": "alt+f9",
149
+ "KEY_F(58)": "alt+f10",
150
+ "KEY_F(59)": "alt+f11",
151
+ "KEY_F(60)": "alt+f12",
152
+
153
+ # Shift
154
+ curses.KEY_SLEFT: "shift+left",
155
+ curses.KEY_SRIGHT: "shift+right",
156
+ "KEY_SLEFT": "shift+left",
157
+ "KEY_SRIGHT": "shift+right",
158
+ "KEY_SR": "shift+up",
159
+ "KEY_SF": "shift+down",
160
+
161
+ 353: "shift+tab",
162
+ "KEY_BTAB": "shift+tab",
163
+
164
+ "KEY_SDC": "shift+delete",
165
+ "KEY_SHOME": "shift+home",
166
+ "KEY_SEND": "shift+end",
167
+
168
+ "KEY_SPREVIOUS": "shift+pageup",
169
+ "KEY_SNEXT": "shift+pagedown",
170
+
171
+ "O2P": "shift+f1",
172
+ "O2Q": "shift+f2",
173
+ "O2R": "shift+f3",
174
+ "O2S": "shift+f4",
175
+
176
+ # Terminals that describe shifted function keys as kf13-kf24 report these
177
+ # instead. shift+f5 upwards were already covered; f1-f4 were not.
178
+ "KEY_F(13)": "shift+f1",
179
+ "KEY_F(14)": "shift+f2",
180
+ "KEY_F(15)": "shift+f3",
181
+ "KEY_F(16)": "shift+f4",
182
+
183
+ "KEY_F(17)": "shift+f5",
184
+ "KEY_F(18)": "shift+f6",
185
+ "KEY_F(19)": "shift+f7",
186
+ "KEY_F(20)": "shift+f8",
187
+ "KEY_F(21)": "shift+f9",
188
+ "KEY_F(22)": "shift+f10",
189
+ "KEY_F(23)": "shift+f11",
190
+ "KEY_F(24)": "shift+f12",
191
+
192
+
193
+ # Alt Gr
194
+ "O1P": "altgr+f1",
195
+ "O1Q": "altgr+f2",
196
+ "O1R": "altgr+f3",
197
+ "O1S": "altgr+f4",
198
+
199
+
200
+ # Shift + Alt
201
+ "kUP4": "shift+alt+up",
202
+ "kDN4": "shift+alt+down",
203
+ "kLFT4": "shift+alt+left",
204
+ "kRIT4": "shift+alt+right",
205
+
206
+ "kIC4": "shift+alt+inset",
207
+ "kDC4": "shift+alt+delete",
208
+ "kHOM4": "shift+alt+home",
209
+ "kEND4": "shift+alt+end",
210
+
211
+
212
+ # Control + Shift
213
+ "kUP6": "ctrl+shift+up",
214
+ "kDN6": "ctrl+shift+down",
215
+ "kLFT6": "ctrl+shift+left",
216
+ "kRIT6": "ctrl+shift+right",
217
+
218
+ "kDC6": "ctrl+shift+delete",
219
+ "kHOM6": "ctrl+shift+home",
220
+ "kEND6": "ctrl+shift+end",
221
+
222
+
223
+ # Control + Alt
224
+ "kUP7": "ctrl+alt+up",
225
+ "kDN7": "ctrl+alt+down",
226
+ "kLFT7": "ctrl+alt+left",
227
+ "kRIT7": "ctrl+alt+right",
228
+
229
+ "kPRV7": "ctrl+alt+pageup",
230
+ "kNXT7": "ctrl+alt+pagedown",
231
+
232
+ "kIC7": "ctrl+alt+insert",
233
+ "kDC7": "ctrl+alt+delete",
234
+ "kHOM7": "ctrl+alt+home",
235
+ "kEND7": "ctrl+alt+end",
236
+
237
+ # Special events
238
+ "KEY_RESIZE": "resize",
239
+ }
suplemon/lexer.py ADDED
@@ -0,0 +1,88 @@
1
+ # -*- encoding: utf-8
2
+
3
+ import logging
4
+ import pygments
5
+ import pygments.token
6
+ import pygments.lexers
7
+
8
+
9
+ class Lexer:
10
+ def __init__(self, app):
11
+ self.app = app
12
+ self.logger = logging.getLogger(__name__)
13
+ self.token_map = {
14
+ pygments.token.Text: "generic",
15
+ pygments.token.Generic.Strong: "string",
16
+ pygments.token.Generic.Subheading: "string",
17
+ pygments.token.Generic.Deleted: "invalid",
18
+ pygments.token.Punctuation: "punctuation",
19
+ pygments.token.Operator: "keyword",
20
+ pygments.token.Operator.Word: "keyword.control",
21
+
22
+ pygments.token.Comment: "comment",
23
+ pygments.token.Comment.Single: "comment",
24
+ pygments.token.Comment.Multiline: "comment",
25
+
26
+ pygments.token.Name: "entity.name",
27
+ pygments.token.Name.Other: "entity.name",
28
+ pygments.token.Name.Tag: "entity.name.tag",
29
+ pygments.token.Name.Class: "entity.name.class",
30
+ pygments.token.Name.Function: "entity.name.function",
31
+ pygments.token.Name.Function.Magic: "entity.name.function",
32
+ pygments.token.Name.Attribute: "entity.other.attribute-name",
33
+ pygments.token.Name.Variable: "variable",
34
+ pygments.token.Name.Variable.Magic: "variable",
35
+ pygments.token.Name.Builtin: "constant.language",
36
+ pygments.token.Name.Builtin.Pseudo: "constant.language",
37
+ pygments.token.Name.Namespace: "constant.language",
38
+
39
+ pygments.token.Literal.String: "string",
40
+ pygments.token.Literal.String.Doc: "string",
41
+ pygments.token.Literal.String.Double: "string",
42
+ pygments.token.Literal.String.Regex: "string",
43
+ pygments.token.Literal.String.Backtick: "string",
44
+ pygments.token.Literal.String.Interpol: "string.interpolated",
45
+ pygments.token.Literal.Number: "constant.numeric",
46
+ pygments.token.Literal.Number.Hex: "constant.numeric",
47
+ pygments.token.Literal.Number.Float: "constant.numeric",
48
+ pygments.token.Literal.Number.Integer: "constant.numeric",
49
+
50
+ pygments.token.Keyword: "keyword",
51
+ pygments.token.Keyword.Reserved: "constant.language",
52
+ pygments.token.Keyword.Constant: "constant.language",
53
+ pygments.token.Keyword.Namespace: "keyword",
54
+ pygments.token.Keyword.Declaration: "keyword",
55
+
56
+ pygments.token.Error: "invalid",
57
+ }
58
+
59
+ def lex(self, code, lex):
60
+ """Return tokenified code.
61
+
62
+ Return a list of tuples (scope, word) where word is the word to be
63
+ printed and scope the scope name representing the context.
64
+
65
+ :param str code: Code to tokenify.
66
+ :param lex: Lexer to use.
67
+ :return:
68
+ """
69
+ if lex is None:
70
+ if not type(code) is str:
71
+ # if not suitable lexer is found, return decoded code
72
+ code = code.decode("utf-8")
73
+ return (("global", code),)
74
+
75
+ words = pygments.lex(code, lex)
76
+
77
+ scopes = []
78
+ for word in words:
79
+ token = word[0]
80
+ scope = "global"
81
+
82
+ if token in self.token_map.keys():
83
+ scope = self.token_map[token]
84
+ else:
85
+ self.logger.warning("Token '{0}' for word '{1}' not found in token_map.".format(token, word[1]))
86
+
87
+ scopes.append((scope, word[1]))
88
+ return scopes
suplemon/line.py ADDED
@@ -0,0 +1,51 @@
1
+ # -*- encoding: utf-8
2
+ """
3
+ Line object to represent a single line in the text editor.
4
+ """
5
+
6
+
7
+ class Line:
8
+ def __init__(self, data=""):
9
+ if isinstance(data, Line):
10
+ data = data.data
11
+ self.data = data
12
+ self.x_scroll = 0
13
+ self.number_color = 8
14
+
15
+ def __getitem__(self, i):
16
+ return self.data[i]
17
+
18
+ def __setitem__(self, i, v):
19
+ self.data[i] = v
20
+
21
+ def __str__(self):
22
+ return self.data
23
+
24
+ def __add__(self, other):
25
+ return Line(self.data + other)
26
+
27
+ def __radd__(self, other):
28
+ return Line(other + self.data)
29
+
30
+ def __len__(self):
31
+ return len(self.data)
32
+
33
+ def get_data(self):
34
+ return self.data
35
+
36
+ def set_data(self, data):
37
+ if isinstance(data, Line):
38
+ data = data.get_data()
39
+ self.data = data
40
+
41
+ def set_number_color(self, color):
42
+ self.number_color = color
43
+
44
+ def find(self, what, start=0):
45
+ return self.data.find(what, start)
46
+
47
+ def strip(self, *args):
48
+ return self.data.strip(*args)
49
+
50
+ def reset_number_color(self):
51
+ self.number_color = 8
File without changes
@@ -0,0 +1,9 @@
1
+ color_map = {
2
+ "red": 1,
3
+ "green": 2,
4
+ "yellow": 3,
5
+ "blue": 4,
6
+ "magenta": 5,
7
+ "cyan": 6,
8
+ "white": 7
9
+ }
@@ -0,0 +1,21 @@
1
+ from suplemon.linelight.color_map import color_map
2
+
3
+
4
+ class Syntax:
5
+ def get_comment(self):
6
+ return ("/*", "*/")
7
+
8
+ def get_color(self, raw_line):
9
+ color = color_map["white"]
10
+ line = raw_line.strip()
11
+ if line.startswith("@import"):
12
+ color = color_map["blue"]
13
+ elif line.startswith("$"):
14
+ color = color_map["green"]
15
+ elif line.startswith("/*") or line.endswith("*/"):
16
+ color = color_map["magenta"]
17
+ elif line.startswith("{") or line.endswith(("}", "{")):
18
+ color = color_map["cyan"]
19
+ elif line.endswith(";"):
20
+ color = color_map["yellow"]
21
+ return color
@@ -0,0 +1,17 @@
1
+ from suplemon.linelight.color_map import color_map
2
+
3
+
4
+ class Syntax:
5
+ def get_comment(self):
6
+ return ("/*", "*/")
7
+
8
+ def get_color(self, raw_line):
9
+ color = color_map["white"]
10
+ line = str(raw_line)
11
+ if line.startswith("+"):
12
+ color = color_map["green"]
13
+ elif line.startswith("-"):
14
+ color = color_map["red"]
15
+ elif line.startswith("@@"):
16
+ color = color_map["blue"]
17
+ return color
@@ -0,0 +1,19 @@
1
+ from suplemon.linelight.color_map import color_map
2
+
3
+
4
+ class Syntax:
5
+ def get_comment(self):
6
+ return ("<!--", "-->")
7
+
8
+ def get_color(self, raw_line):
9
+ color = color_map["white"]
10
+ line = raw_line.strip()
11
+ if line.startswith(("#", "//", "/*", "*/", "<!--")):
12
+ color = color_map["magenta"]
13
+ elif line.endswith(("*/", "-->")):
14
+ color = color_map["magenta"]
15
+ elif line.startswith("<"):
16
+ color = color_map["cyan"]
17
+ elif line.endswith(">"):
18
+ color = color_map["cyan"]
19
+ return color
@@ -0,0 +1,21 @@
1
+ from suplemon.linelight.color_map import color_map
2
+
3
+
4
+ class Syntax:
5
+ def get_comment(self):
6
+ return ("//", "")
7
+
8
+ def get_color(self, raw_line):
9
+ color = color_map["white"]
10
+ line = raw_line.strip()
11
+ if line.startswith("function"):
12
+ color = color_map["cyan"]
13
+ elif line.startswith("return"):
14
+ color = color_map["red"]
15
+ elif line.startswith("this."):
16
+ color = color_map["cyan"]
17
+ elif line.startswith(("//", "/*", "*/", "*")):
18
+ color = color_map["magenta"]
19
+ elif line.startswith(("if", "else", "for ", "while ", "continue", "break")):
20
+ color = color_map["yellow"]
21
+ return color
@@ -0,0 +1,15 @@
1
+ from suplemon.linelight.color_map import color_map
2
+
3
+
4
+ class Syntax:
5
+ def get_comment(self, line):
6
+ return ("", "")
7
+
8
+ def get_color(self, raw_line):
9
+ color = color_map["white"]
10
+ line = raw_line.strip()
11
+ if line.startswith(("{", "}")):
12
+ color = color_map["yellow"]
13
+ elif line.startswith('"'):
14
+ color = color_map["green"]
15
+ return color
@@ -0,0 +1,10 @@
1
+ from suplemon.linelight.color_map import color_map
2
+
3
+
4
+ class Syntax:
5
+ def get_comment(self):
6
+ return ("-- ", "")
7
+
8
+ def get_color(self, raw_line):
9
+ color = color_map["white"]
10
+ return color
@@ -0,0 +1,19 @@
1
+ from suplemon.linelight.color_map import color_map
2
+
3
+
4
+ class Syntax:
5
+ def get_comment(self, line):
6
+ return ("", "")
7
+
8
+ def get_color(self, raw_line):
9
+ color = color_map["white"]
10
+ line = raw_line.strip()
11
+ if line.startswith(("*", "-")): # List
12
+ color = color_map["cyan"]
13
+ elif line.startswith("#"): # Header
14
+ color = color_map["green"]
15
+ elif line.startswith(">"): # Item description
16
+ color = color_map["yellow"]
17
+ elif raw_line.startswith(" "): # Code
18
+ color = color_map["magenta"]
19
+ return color
@@ -0,0 +1,27 @@
1
+ from suplemon.linelight.color_map import color_map
2
+
3
+
4
+ class Syntax:
5
+ def get_comment(self):
6
+ return ("//", "")
7
+
8
+ def get_color(self, raw_line):
9
+ color = color_map["white"]
10
+ line = raw_line.strip()
11
+ keywords = ("if", "else", "finally", "try", "catch", "foreach",
12
+ "while", "continue", "pass", "break")
13
+ if line.startswith(("include", "require")):
14
+ color = color_map["blue"]
15
+ elif line.startswith(("class", "public", "private", "function")):
16
+ color = color_map["green"]
17
+ elif line.startswith("def"):
18
+ color = color_map["cyan"]
19
+ elif line.startswith("return"):
20
+ color = color_map["red"]
21
+ elif line.startswith("$"):
22
+ color = color_map["cyan"]
23
+ elif line.startswith(("#", "//", "/*", "*/")):
24
+ color = color_map["magenta"]
25
+ elif line.startswith(keywords):
26
+ color = color_map["yellow"]
27
+ return color
@@ -0,0 +1,27 @@
1
+ from suplemon.linelight.color_map import color_map
2
+
3
+
4
+ class Syntax:
5
+ def get_comment(self):
6
+ return ("# ", "")
7
+
8
+ def get_color(self, raw_line):
9
+ color = color_map["white"]
10
+ line = raw_line.strip()
11
+ keywords = ("if", "elif", "else", "finally", "try", "except",
12
+ "for ", "while ", "continue", "pass", "break")
13
+ if line.startswith(("import", "from")):
14
+ color = color_map["blue"]
15
+ elif line.startswith("class"):
16
+ color = color_map["green"]
17
+ elif line.startswith("def"):
18
+ color = color_map["cyan"]
19
+ elif line.startswith(("return", "yield")):
20
+ color = color_map["red"]
21
+ elif line.startswith("self."):
22
+ color = color_map["cyan"]
23
+ elif line.startswith(("#", "//", '"', "'", ":")):
24
+ color = color_map["magenta"]
25
+ elif line.startswith(keywords):
26
+ color = color_map["yellow"]
27
+ return color
suplemon/logger.py ADDED
@@ -0,0 +1,90 @@
1
+ """
2
+ Basic logging to delay printing until curses is unloaded.
3
+ """
4
+ import os
5
+ import logging
6
+ from logging.handlers import BufferingHandler, RotatingFileHandler
7
+ import sys
8
+
9
+
10
+ # Define an mix of BufferingHandler and MemoryHandler which store records internally and flush on `close`
11
+ # https://docs.python.org/3.3/library/logging.handlers.html#logging.handlers.BufferingHandler
12
+ # https://docs.python.org/3.3/library/logging.handlers.html#logging.handlers.MemoryHandler
13
+ class BufferingTargetHandler(BufferingHandler):
14
+ # Set up capacity and target for MemoryHandler
15
+ def __init__(self, capacity, fd_target):
16
+ """
17
+ :param int capacity: Amount of records to store in memory
18
+ https://github.com/python/cpython/blob/3.3/Lib/logging/handlers.py#L1161-L1176
19
+ :param object fd_target: File descriptor to write output to (e.g. `sys.stdout`)
20
+ """
21
+ # Call our BufferingHandler init
22
+ if issubclass(BufferingTargetHandler, object):
23
+ super(BufferingTargetHandler, self).__init__(capacity)
24
+ else:
25
+ BufferingHandler.__init__(self, capacity)
26
+
27
+ # Save target for later
28
+ self._fd_target = fd_target
29
+
30
+ # Records printed to the target, so callers can tell whether the user
31
+ # was actually shown anything
32
+ self.flushed_records = 0
33
+
34
+ def close(self):
35
+ """Upon `close`, flush our internal info to the target"""
36
+ # Flush our buffers to the target
37
+ # https://github.com/python/cpython/blob/3.3/Lib/logging/handlers.py#L1185
38
+ # https://github.com/python/cpython/blob/3.3/Lib/logging/handlers.py#L1241-L1256
39
+ self.acquire()
40
+ try:
41
+ for record in self.buffer:
42
+ if record.levelno < self.level:
43
+ continue
44
+ msg = self.format(record)
45
+ print(msg, file=self._fd_target)
46
+ self.flushed_records += 1
47
+ finally:
48
+ self.release()
49
+
50
+ # Then, run our normal close actions
51
+ if issubclass(BufferingTargetHandler, object):
52
+ super(BufferingTargetHandler, self).close()
53
+ else:
54
+ BufferingHandler.close(self)
55
+
56
+
57
+ # Initialize logging
58
+ logging.basicConfig(level=logging.NOTSET)
59
+ logger = logging.getLogger()
60
+ logger.handlers = []
61
+
62
+ # Generate and configure handlers
63
+ log_filepath = os.path.join(os.path.expanduser("~"), ".config", "suplemon", "output.log")
64
+ logger_handlers = [
65
+ # Buffer 64k records in memory at a time
66
+ BufferingTargetHandler(64 * 1024, fd_target=sys.stderr),
67
+ ]
68
+
69
+ # Error recovery when log_filepath isn't writable
70
+ try:
71
+ if not os.path.exists(os.path.dirname(log_filepath)):
72
+ os.makedirs(os.path.dirname(log_filepath))
73
+ # Output up to 4MB of records to `~/.config/suplemon/output.log` for live debugging
74
+ # https://docs.python.org/3.3/library/logging.handlers.html#logging.handlers.RotatingFileHandler
75
+ # DEV: We use append mode to prevent erasing out logs
76
+ # DEV: We use 1 backup count since it won't truncate otherwise =/
77
+ rfh = RotatingFileHandler(log_filepath, mode="a+", maxBytes=(4 * 1024 * 1024), backupCount=1)
78
+ logger_handlers.append(rfh)
79
+ except OSError:
80
+ # Can't recover and can't log this error
81
+ pass
82
+
83
+ fmt = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
84
+ logger_formatter = logging.Formatter(fmt)
85
+ for logger_handler in logger_handlers:
86
+ logger_handler.setFormatter(logger_formatter)
87
+
88
+ # Save handlers for our logger
89
+ for logger_handler in logger_handlers:
90
+ logger.addHandler(logger_handler)