1
|
|
|
''' |
2
|
|
|
Implements a UI for neovim using tkinter. |
3
|
|
|
|
4
|
|
|
* The widget has lines updated/deleted so that any |
5
|
|
|
given time it only contains what is being displayed. |
6
|
|
|
|
7
|
|
|
* The widget is filled with spaces |
8
|
|
|
''' |
9
|
|
|
import sys |
10
|
|
|
import math |
11
|
|
|
import time |
12
|
|
|
from distutils.spawn import find_executable |
13
|
|
|
from neovim import attach |
14
|
|
|
|
15
|
|
|
from pytknvim.ui_bridge import UIBridge |
16
|
|
|
from pytknvim.screen import Screen |
17
|
|
|
from pytknvim.util import _stringify_key, _stringify_color |
18
|
|
|
from pytknvim.util import _split_color, _invert_color |
19
|
|
|
from pytknvim.util import debug_echo, delay_call |
20
|
|
|
from pytknvim import tk_util |
21
|
|
|
|
22
|
|
|
try: |
23
|
|
|
import Tkinter as tk |
24
|
|
|
import tkFont as tkfont |
25
|
|
|
import ttk |
26
|
|
|
except ImportError: |
27
|
|
|
import tkinter as tk |
28
|
|
|
import tkinter.font as tkfont |
29
|
|
|
|
30
|
|
|
|
31
|
|
|
def parse_tk_state(state): |
32
|
|
|
if state & 0x4: |
33
|
|
|
return 'Ctrl' |
34
|
|
|
elif state & 0x8: |
35
|
|
|
return 'Alt' |
36
|
|
|
elif state & 0x1: |
37
|
|
|
return 'Shift' |
38
|
|
|
|
39
|
|
|
|
40
|
|
|
tk_modifiers = ('Alt_L', 'Alt_R', |
41
|
|
|
'Control_L', 'Control_R', |
42
|
|
|
'Shift_L', 'Shift_R', |
43
|
|
|
'Win_L', 'Win_R') |
44
|
|
|
|
45
|
|
|
|
46
|
|
|
KEY_TABLE = { |
47
|
|
|
'slash': '/', |
48
|
|
|
'backslash': '\\', |
49
|
|
|
'asciicircumf': '^', |
50
|
|
|
'at': '@', |
51
|
|
|
'numbersign': '#', |
52
|
|
|
'dollar': '$', |
53
|
|
|
'percent': '%', |
54
|
|
|
'ampersand': '&', |
55
|
|
|
'asterisk': '*', |
56
|
|
|
'parenleft': '(', |
57
|
|
|
'parenright': ')', |
58
|
|
|
'underscore': '_', |
59
|
|
|
'plus': '+', |
60
|
|
|
'minus': '-', |
61
|
|
|
'bracketleft': '[', |
62
|
|
|
'bracketright': ']', |
63
|
|
|
'braceleft': '{', |
64
|
|
|
'braceright': '}', |
65
|
|
|
'quotedbl': '"', |
66
|
|
|
'apostrophe': "'", |
67
|
|
|
'less': "<", |
68
|
|
|
'greater': ">", |
69
|
|
|
'comma': ",", |
70
|
|
|
'period': ".", |
71
|
|
|
'BackSpace': 'BS', |
72
|
|
|
'Return': 'CR', |
73
|
|
|
'Escape': 'Esc', |
74
|
|
|
'Delete': 'Del', |
75
|
|
|
'Next': 'PageUp', |
76
|
|
|
'Prior': 'PageDown', |
77
|
|
|
'Enter': 'CR', |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
|
81
|
|
|
class MixTk(): |
82
|
|
|
''' |
83
|
|
|
Tkinter actions we bind and use to communicate to neovim |
84
|
|
|
''' |
85
|
|
|
def _tk_key(self,event, **k): |
86
|
|
|
keysym = event.keysym |
87
|
|
|
state = parse_tk_state(event.state) |
88
|
|
|
if event.char not in ('', ' ') and not event.state: |
89
|
|
|
if event.keysym_num == ord(event.char): |
90
|
|
|
# Send through normal keys |
91
|
|
|
self._bridge.input(event.char) |
92
|
|
|
return |
93
|
|
|
if keysym in tk_modifiers: |
94
|
|
|
# We don't need to track the state of modifier bits |
95
|
|
|
return |
96
|
|
|
if keysym.startswith('KP_'): |
97
|
|
|
keysym = keysym[3:] |
98
|
|
|
|
99
|
|
|
# Translated so vim understands |
100
|
|
|
input_str = _stringify_key( |
101
|
|
|
KEY_TABLE.get(keysym, keysym), state) |
102
|
|
|
self._bridge.input(input_str) |
103
|
|
|
|
104
|
|
|
|
105
|
|
|
def _tk_quit(self, *args): |
106
|
|
|
self._bridge.exit() |
107
|
|
|
|
108
|
|
|
|
109
|
|
|
# @debug_echo |
110
|
|
|
@delay_call(0.1) |
111
|
|
|
def _tk_resize(self, event): |
112
|
|
|
'''Let Neovim know we are changing size''' |
113
|
|
|
# if not self._screen: |
114
|
|
|
# return |
115
|
|
|
cols = int(math.floor(event.width / self._colsize)) |
116
|
|
|
rows = int(math.floor(event.height / self._rowsize)) |
117
|
|
|
if self._screen.columns == cols: |
118
|
|
|
if self._screen.rows == rows: |
119
|
|
|
return |
120
|
|
|
self.current_cols = cols |
121
|
|
|
self.current_rows = rows |
122
|
|
|
self._bridge.resize(cols, rows) |
123
|
|
|
if self.debug_echo: |
124
|
|
|
print('resizing c, r, w, h', |
125
|
|
|
cols,rows, event.width, event.height) |
126
|
|
|
|
127
|
|
|
|
128
|
|
|
def bind_resize(self): |
129
|
|
|
''' |
130
|
|
|
after calling, |
131
|
|
|
widget changes will now be passed along to neovim |
132
|
|
|
''' |
133
|
|
|
self._configure_id = self.text.bind('<Configure>', |
134
|
|
|
self._tk_resize) |
135
|
|
|
|
136
|
|
|
|
137
|
|
|
def unbind_resize(self): |
138
|
|
|
''' |
139
|
|
|
after calling, |
140
|
|
|
widget size changes will not be passed along to nvim |
141
|
|
|
''' |
142
|
|
|
self.text.unbind('<Configure>', self._configure_id) |
143
|
|
|
|
144
|
|
|
|
145
|
|
|
def _get_row(self, screen_row): |
146
|
|
|
'''change a screen row to a tkinter row, |
147
|
|
|
defaults to screen.row''' |
148
|
|
|
if screen_row is None: |
149
|
|
|
screen_row = self._screen.row |
150
|
|
|
return screen_row + 1 |
151
|
|
|
|
152
|
|
|
|
153
|
|
|
def _get_col(self, screen_col): |
154
|
|
|
'''change a screen col to a tkinter row, |
155
|
|
|
defaults to screen.col''' |
156
|
|
|
if screen_col is None: |
157
|
|
|
screen_col = self._screen.col |
158
|
|
|
return screen_col |
159
|
|
|
|
160
|
|
|
|
161
|
|
|
def tk_delete_line(self, screen_col=None, screen_row=None, |
162
|
|
|
del_eol=False, count=1): |
163
|
|
|
''' |
164
|
|
|
To specifiy where to start the delete from |
165
|
|
|
screen_col (defualts to screen.row) |
166
|
|
|
screen_row (defaults to screen.col) |
167
|
|
|
|
168
|
|
|
To delete the eol char aswell |
169
|
|
|
del_eol (defaults to False) |
170
|
|
|
|
171
|
|
|
count is the number of lines to delete |
172
|
|
|
''' |
173
|
|
|
line = self._get_row(screen_row) |
174
|
|
|
col = self._get_col(screen_col) |
175
|
|
|
start = "%d.%d" % (line, col) |
176
|
|
|
if del_eol: |
177
|
|
|
end = "%d.0" % (line + count) |
178
|
|
|
else: |
179
|
|
|
end = "%d.end" % (line + count - 1) |
180
|
|
|
self.text.delete(start, end) |
181
|
|
|
gotten = self.text.get(start, end) |
182
|
|
|
if self.debug_echo == True: |
183
|
|
|
print('deleted from ' + start + ' to end ' +end) |
184
|
|
|
print('deleted '+repr(gotten)) |
185
|
|
|
|
186
|
|
|
|
187
|
|
|
def tk_pad_line(self, screen_col=None, add_eol=False, |
188
|
|
|
screen_row=None, count=1): |
189
|
|
|
''' |
190
|
|
|
add required blank spaces at the end of the line |
191
|
|
|
can apply action to multiple rows py passing a count |
192
|
|
|
in |
193
|
|
|
''' |
194
|
|
|
line = self._get_row(screen_row) |
195
|
|
|
col = self._get_col(screen_col) |
196
|
|
|
for n in range(0, count): |
197
|
|
|
start = "%d.%d" % (line + n, col) |
198
|
|
|
spaces = " " * (self.current_cols - col) |
199
|
|
|
if add_eol: |
200
|
|
|
spaces += '\n' |
201
|
|
|
if self.debug_echo: |
202
|
|
|
pass |
203
|
|
|
# print('padding from ', start, ' with %d: ' |
204
|
|
|
# % len(spaces)) |
205
|
|
|
# print(repr(spaces)) |
206
|
|
|
self.text.insert(start, spaces) |
207
|
|
|
|
208
|
|
|
|
209
|
|
|
def _start_blinking(self): |
210
|
|
|
# cursor is drawn seperatley in the window |
211
|
|
|
row, col = self._screen.row, self._screen.col |
212
|
|
|
text, attrs = self._screen.get_cursor() |
213
|
|
|
pos = "%d.%d" % (row +1, col) |
214
|
|
|
|
215
|
|
|
if not attrs: |
216
|
|
|
attrs = self._get_tk_attrs(None) |
217
|
|
|
fg = attrs[1].get('foreground') |
218
|
|
|
bg = attrs[1].get('background') |
219
|
|
|
try: |
220
|
|
|
self.text.stop_blink() |
221
|
|
|
except: |
222
|
|
|
pass |
223
|
|
|
self.text.blink_cursor(pos, fg, bg) |
224
|
|
|
|
225
|
|
|
|
226
|
|
|
|
227
|
|
|
class MixNvim(): |
228
|
|
|
|
229
|
|
|
|
230
|
|
|
'''These methods get called by neovim''' |
231
|
|
|
|
232
|
|
|
# @debug_echo |
233
|
|
|
def _nvim_resize(self, cols, rows): |
234
|
|
|
'''Let neovim update tkinter when neovim changes size''' |
235
|
|
|
# TODO |
236
|
|
|
# Make sure it works when user changes font, |
237
|
|
|
# only can support mono font i think.. |
238
|
|
|
self._screen = Screen(cols, rows) |
239
|
|
|
|
240
|
|
|
toplevel = self.text.master |
241
|
|
|
border = toplevel.winfo_rootx() - toplevel.winfo_x() |
242
|
|
|
titlebar = toplevel.winfo_rooty() - toplevel.winfo_y() |
243
|
|
|
width = (cols * self._colsize) + (2 * border) |
244
|
|
|
height = (rows * self._rowsize) + (titlebar + border) |
245
|
|
|
# fudge factor... |
246
|
|
|
height += self._rowsize/3 |
247
|
|
|
width += self._colsize/1.2 |
248
|
|
|
def resize(): |
249
|
|
|
self.unbind_resize() |
250
|
|
|
toplevel.geometry('%dx%d' % (width, height)) |
251
|
|
|
self.bind_resize() |
252
|
|
|
self.root.after_idle(resize) |
253
|
|
|
|
254
|
|
|
|
255
|
|
|
@debug_echo |
256
|
|
|
def _nvim_clear(self): |
257
|
|
|
''' |
258
|
|
|
wipe everyything, even the ~ and status bar |
259
|
|
|
''' |
260
|
|
|
self._screen.clear() |
261
|
|
|
|
262
|
|
|
self.tk_delete_line(del_eol=True, |
263
|
|
|
screen_row=0, |
264
|
|
|
screen_col=0, |
265
|
|
|
count=self.current_rows) |
266
|
|
|
# Add spaces everywhere |
267
|
|
|
self.tk_pad_line(screen_row=0, |
268
|
|
|
screen_col=0, |
269
|
|
|
count=self.current_rows, |
270
|
|
|
add_eol=True,) |
271
|
|
|
|
272
|
|
|
|
273
|
|
|
# @debug_echo |
274
|
|
|
def _nvim_eol_clear(self): |
275
|
|
|
''' |
276
|
|
|
delete from index to end of line, |
277
|
|
|
fill with whitespace |
278
|
|
|
leave eol intact |
279
|
|
|
''' |
280
|
|
|
self._screen.eol_clear() |
281
|
|
|
self.tk_delete_line(del_eol=False) |
282
|
|
|
self.tk_pad_line(screen_col=self._screen.col, |
283
|
|
|
add_eol=False) |
284
|
|
|
|
285
|
|
|
|
286
|
|
|
# @debug_echo |
287
|
|
|
def _nvim_cursor_goto(self, row, col): |
288
|
|
|
'''Move gui cursor to position''' |
289
|
|
|
self._screen.cursor_goto(row, col) |
290
|
|
|
self.text.see("1.0") |
291
|
|
|
|
292
|
|
|
|
293
|
|
|
def _nvim_busy_start(self): |
294
|
|
|
self._busy = True |
295
|
|
|
|
296
|
|
|
|
297
|
|
|
def _nvim_busy_stop(self): |
298
|
|
|
self._busy = False |
299
|
|
|
|
300
|
|
|
|
301
|
|
|
def _nvim_mouse_on(self): |
302
|
|
|
self.mouse_enabled = True |
303
|
|
|
|
304
|
|
|
|
305
|
|
|
def _nvim_mouse_off(self): |
306
|
|
|
self.mouse_enabled = False |
307
|
|
|
|
308
|
|
|
|
309
|
|
|
def _nvim_mode_change(self, mode): |
310
|
|
|
self._insert_cursor = mode == 'insert' |
311
|
|
|
|
312
|
|
|
|
313
|
|
|
# @debug_echo |
314
|
|
|
def _nvim_set_scroll_region(self, top, bot, left, right): |
315
|
|
|
self._screen.set_scroll_region(top, bot, left, right) |
316
|
|
|
|
317
|
|
|
|
318
|
|
|
def _nvim_scroll(self, count): |
319
|
|
|
self._flush() |
320
|
|
|
self._screen.scroll(count) |
321
|
|
|
abs_count = abs(count) |
322
|
|
|
# The minus 1 is because we want our tk_* functions |
323
|
|
|
# to operate on the row passed in |
324
|
|
|
delta = abs_count - 1 |
325
|
|
|
# Down |
326
|
|
|
if count > 0: |
327
|
|
|
delete_row = self._screen.top |
328
|
|
|
pad_row = self._screen.bot - delta |
329
|
|
|
# Up |
330
|
|
|
else: |
331
|
|
|
delete_row = self._screen.bot - delta |
332
|
|
|
pad_row = self._screen.top |
333
|
|
|
|
334
|
|
|
self.tk_delete_line(screen_row=delete_row, |
335
|
|
|
screen_col=0, |
336
|
|
|
del_eol=True, |
337
|
|
|
count=abs_count) |
338
|
|
|
self.tk_pad_line(screen_row=pad_row, |
339
|
|
|
screen_col=0, |
340
|
|
|
add_eol=True, |
341
|
|
|
count=abs_count) |
342
|
|
|
# self.text.yview_scroll(count, 'units') |
343
|
|
|
|
344
|
|
|
|
345
|
|
|
@debug_echo |
346
|
|
|
def _nvim_highlight_set(self, attrs): |
347
|
|
|
self._attrs = self._get_tk_attrs(attrs) |
348
|
|
|
|
349
|
|
|
|
350
|
|
|
def _reset_attrs_cache(self): |
351
|
|
|
self._tk_text_cache = {} |
352
|
|
|
self._tk_attrs_cache = {} |
353
|
|
|
|
354
|
|
|
|
355
|
|
|
def _get_tk_attrs(self, attrs): |
356
|
|
|
key = tuple(sorted((k, v,) for k, v in (attrs or {}).items())) |
357
|
|
|
rv = self._tk_attrs_cache.get(key, None) |
358
|
|
|
if rv is None: |
359
|
|
|
fg = self._foreground if self._foreground != -1\ |
360
|
|
|
else 0 |
361
|
|
|
bg = self._background if self._background != -1\ |
362
|
|
|
else 0xffffff |
363
|
|
|
n = {'foreground': _split_color(fg), |
364
|
|
|
'background': _split_color(bg),} |
365
|
|
|
if attrs: |
366
|
|
|
# make sure that fg and bg are assigned first |
367
|
|
|
for k in ['foreground', 'background']: |
368
|
|
|
if k in attrs: |
369
|
|
|
n[k] = _split_color(attrs[k]) |
370
|
|
|
for k, v in attrs.items(): |
371
|
|
|
if k == 'reverse': |
372
|
|
|
n['foreground'], n['background'] = \ |
373
|
|
|
n['background'], n['foreground'] |
374
|
|
|
elif k == 'italic': |
375
|
|
|
n['slant'] = 'italic' |
376
|
|
|
elif k == 'bold': |
377
|
|
|
n['weight'] = 'bold' |
378
|
|
|
# TODO |
379
|
|
|
# if self._bold_spacing: |
380
|
|
|
# n['letter_spacing'] \ |
381
|
|
|
# = str(self._bold_spacing) |
382
|
|
|
elif k == 'underline': |
383
|
|
|
n['underline'] = '1' |
384
|
|
|
c = dict(n) |
385
|
|
|
c['foreground'] = _invert_color(*_split_color(fg)) |
386
|
|
|
c['background'] = _invert_color(*_split_color(bg)) |
387
|
|
|
c['foreground'] = _stringify_color(*c['foreground']) |
388
|
|
|
c['background'] = _stringify_color(*c['background']) |
389
|
|
|
n['foreground'] = _stringify_color(*n['foreground']) |
390
|
|
|
n['background'] = _stringify_color(*n['background']) |
391
|
|
|
# n = normal, c = cursor |
392
|
|
|
rv = (n, c) |
393
|
|
|
self._tk_attrs_cache[key] = (n, c) |
394
|
|
|
return rv |
395
|
|
|
|
396
|
|
|
|
397
|
|
|
# @debug_echo |
398
|
|
|
def _nvim_put(self, text): |
399
|
|
|
''' |
400
|
|
|
put a charachter into position, we only write the lines |
401
|
|
|
when a new row is being edited |
402
|
|
|
''' |
403
|
|
|
if self._screen.row != self._pending[0]: |
404
|
|
|
# write to screen if vim puts stuff on a new line |
405
|
|
|
self._flush() |
406
|
|
|
|
407
|
|
|
self._screen.put(text, self._attrs) |
408
|
|
|
self._pending[1] = min(self._screen.col - 1, |
409
|
|
|
self._pending[1]) |
410
|
|
|
self._pending[2] = max(self._screen.col, |
411
|
|
|
self._pending[2]) |
412
|
|
|
|
413
|
|
|
|
414
|
|
|
def _nvim_bell(self): |
415
|
|
|
pass |
416
|
|
|
|
417
|
|
|
|
418
|
|
|
def _nvim_visual_bell(self): |
419
|
|
|
pass |
420
|
|
|
|
421
|
|
|
|
422
|
|
|
@debug_echo |
423
|
|
|
def _nvim_update_fg(self, fg): |
424
|
|
|
self._foreground = fg |
425
|
|
|
self._reset_attrs_cache() |
426
|
|
|
|
427
|
|
|
|
428
|
|
|
@debug_echo |
429
|
|
|
def _nvim_update_bg(self, bg): |
430
|
|
|
self._background = bg |
431
|
|
|
self._reset_attrs_cache() |
432
|
|
|
background = self._get_tk_attrs(None)[0]['background'] |
433
|
|
|
self.text.config(background=background) |
434
|
|
|
|
435
|
|
|
|
436
|
|
|
def _nvim_update_suspend(self, arg): |
437
|
|
|
self.root.iconify() |
438
|
|
|
|
439
|
|
|
|
440
|
|
|
def _nvim_set_title(self, title): |
441
|
|
|
self.root.title(title) |
442
|
|
|
|
443
|
|
|
|
444
|
|
|
def _nvim_set_icon(self, icon): |
445
|
|
|
self._icon = tk.PhotoImage(file=icon) |
446
|
|
|
self.root.tk.call('wm', 'iconphoto', |
447
|
|
|
self.root._w, self._icon) |
448
|
|
|
|
449
|
|
|
|
450
|
|
|
def _flush(self): |
451
|
|
|
row, startcol, endcol = self._pending |
452
|
|
|
self._pending[0] = self._screen.row |
453
|
|
|
self._pending[1] = self._screen.col |
454
|
|
|
self._pending[2] = self._screen.col |
455
|
|
|
if startcol == endcol: |
456
|
|
|
#print('startcol is endcol return, row %s col %s'% (self._screen.row, self._screen.col)) |
457
|
|
|
return |
458
|
|
|
ccol = startcol |
459
|
|
|
buf = [] |
460
|
|
|
bold = False |
461
|
|
|
for _, col, text, attrs in self._screen.iter(row, |
462
|
|
|
row, startcol, endcol - 1): |
463
|
|
|
newbold = attrs and 'bold' in attrs[0] |
464
|
|
|
if newbold != bold or not text: |
465
|
|
|
if buf: |
466
|
|
|
self._draw(row, ccol, buf) |
467
|
|
|
bold = newbold |
468
|
|
|
buf = [(text, attrs,)] |
469
|
|
|
ccol = col |
470
|
|
|
else: |
471
|
|
|
buf.append((text, attrs,)) |
472
|
|
|
if buf: |
473
|
|
|
self._draw(row, ccol, buf) |
474
|
|
|
else: |
475
|
|
|
pass |
476
|
|
|
# print('flush with no draw') |
477
|
|
|
|
478
|
|
|
|
479
|
|
|
def _draw(self, row, col, data): |
480
|
|
|
''' |
481
|
|
|
updates a line :) |
482
|
|
|
''' |
483
|
|
|
for text, attrs in data: |
484
|
|
|
try: |
485
|
|
|
start = end |
486
|
|
|
except UnboundLocalError: |
487
|
|
|
start = "{}.{}".format(row + 1, col) |
488
|
|
|
end = start+'+{0}c'.format(len(text)) |
489
|
|
|
|
490
|
|
|
if not attrs: |
491
|
|
|
attrs = self._get_tk_attrs(None) |
492
|
|
|
attrs = attrs[0] |
493
|
|
|
|
494
|
|
|
if self.debug_echo: |
495
|
|
|
print('replacing ', repr(self.text.get(start, end))) |
496
|
|
|
print('with ', repr(text), ' at ', start, ' ',end) |
497
|
|
|
self.text.replace(start, end, text) |
498
|
|
|
|
499
|
|
|
if attrs: |
500
|
|
|
self.text.apply_attribute(attrs, start, end) |
501
|
|
|
start |
502
|
|
|
|
503
|
|
|
|
504
|
|
|
def _nvim_exit(self, arg): |
505
|
|
|
self.root.destroy() |
506
|
|
|
|
507
|
|
|
|
508
|
|
|
class NvimTk(MixNvim, MixTk): |
509
|
|
|
''' |
510
|
|
|
Business Logic for making a tkinter neovim text widget |
511
|
|
|
|
512
|
|
|
we get keys, mouse movements inside tkinter, using binds, |
513
|
|
|
These binds are handed off to neovim using _input |
514
|
|
|
|
515
|
|
|
Neovim interpruts the actions and calls certain |
516
|
|
|
functions which are defined and implemented in tk |
517
|
|
|
|
518
|
|
|
The api from neovim does stuff line by line, |
519
|
|
|
so each callback from neovim produces a series |
520
|
|
|
of miniscule actions which in the end updates a line |
521
|
|
|
''' |
522
|
|
|
|
523
|
|
|
def __init__(self): |
524
|
|
|
# we destroy this when the layout changes |
525
|
|
|
self.start_time = time.time() |
526
|
|
|
self._insert_cursor = False |
527
|
|
|
self._screen = None |
528
|
|
|
self._foreground = -1 |
529
|
|
|
self._background = -1 |
530
|
|
|
self._pending = [0,0,0] |
531
|
|
|
self._attrs = {} |
532
|
|
|
self._reset_attrs_cache() |
533
|
|
|
|
534
|
|
|
def start(self, bridge): |
535
|
|
|
# Maximum cols and rows avaliable |
536
|
|
|
# When we resize then this changes |
537
|
|
|
cols, rows = 80, 24 |
538
|
|
|
self.current_cols = cols |
539
|
|
|
self.current_rows = rows |
540
|
|
|
self._screen = Screen(cols, rows) |
541
|
|
|
bridge.attach(cols, rows, rgb=True) |
542
|
|
|
self._bridge = bridge |
543
|
|
|
|
544
|
|
|
self.debug_echo = False |
545
|
|
|
|
546
|
|
|
self.root = tk.Tk() |
547
|
|
|
self.root.protocol('WM_DELETE_WINDOW', self._tk_quit) |
548
|
|
|
text = tk_util.Text(self.root) |
549
|
|
|
self.text = text |
550
|
|
|
# Hide tkinter cursor |
551
|
|
|
self.text.config(insertontime=0) |
552
|
|
|
|
553
|
|
|
# Remove Default Bindings and what happens on insert etc |
554
|
|
|
bindtags = list(text.bindtags()) |
555
|
|
|
bindtags.remove("Text") |
556
|
|
|
text.bindtags(tuple(bindtags)) |
557
|
|
|
|
558
|
|
|
text.pack(expand=1, fill=tk.BOTH) |
559
|
|
|
text.focus_set() |
560
|
|
|
|
561
|
|
|
text.bind('<Key>', self._tk_key) |
562
|
|
|
self.bind_resize() |
563
|
|
|
|
564
|
|
|
# The negative number makes it pixels instead of point sizes |
565
|
|
|
self._fnormal = tkfont.Font(family='Monospace', size=12) |
566
|
|
|
self._fbold = tkfont.Font(family='Monospace', weight='bold', size=12) |
567
|
|
|
self._fitalic = tkfont.Font(family='Monospace', slant='italic', size=12) |
568
|
|
|
self._fbolditalic = tkfont.Font(family='Monospace', weight='bold', |
569
|
|
|
slant='italic', size=12) |
570
|
|
|
self.text.config(font=self._fnormal, wrap=tk.NONE) |
571
|
|
|
self._colsize = self._fnormal.measure('M') |
572
|
|
|
self._rowsize = self._fnormal.metrics('linespace') |
573
|
|
|
|
574
|
|
|
self.text.config(background='black') |
575
|
|
|
self.root.mainloop() |
576
|
|
|
|
577
|
|
|
|
578
|
|
|
def schedule_screen_update(self, apply_updates): |
579
|
|
|
'''This function is called from the bridge, |
580
|
|
|
apply_updates calls the required nvim actions''' |
581
|
|
|
# if time.time() - self.start_time > 1: |
582
|
|
|
# print() |
583
|
|
|
# self.start_time = time.time() |
584
|
|
|
def do(): |
585
|
|
|
apply_updates() |
586
|
|
|
self._flush() |
587
|
|
|
self._start_blinking() |
588
|
|
|
self.root.after_idle(do) |
589
|
|
|
|
590
|
|
|
|
591
|
|
|
def quit(self): |
592
|
|
|
self.root.after_idle(self.root.quit) |
593
|
|
|
|
594
|
|
|
|
595
|
|
|
class NvimFriendly(NvimTk): |
596
|
|
|
'''Meant to be subclassed so the user can tweak easily, |
597
|
|
|
atm im just using it to keep the config code seperate''' |
598
|
|
|
|
599
|
|
|
def __init__(self): |
600
|
|
|
super().__init__() |
601
|
|
|
|
602
|
|
|
|
603
|
|
|
def main(address=None): |
604
|
|
|
if address: |
605
|
|
|
nvim = attach('socket', path=address) |
606
|
|
|
else: |
607
|
|
|
try: |
608
|
|
|
address = sys.argv[1] |
609
|
|
|
nvim = attach('socket', path=address) |
610
|
|
|
except: |
611
|
|
|
nvim_binary = find_executable('nvim') |
612
|
|
|
args = [nvim_binary, '--embed'] |
613
|
|
|
# args.extend(['-u', 'NONE']) |
614
|
|
|
nvim = attach('child', argv=args) |
615
|
|
|
ui = NvimFriendly() |
616
|
|
|
bridge = UIBridge() |
617
|
|
|
bridge.connect(nvim, ui) |
618
|
|
|
if len(sys.argv) > 1: |
619
|
|
|
nvim.command('edit ' + sys.argv[1]) |
620
|
|
|
|
621
|
|
|
if __name__ == '__main__': |
622
|
|
|
main() |
623
|
|
|
|