Skip to content
This repository was archived by the owner on Jun 30, 2023. It is now read-only.

Commit f14b77e

Browse files
committed
make the gui use grid_line representation
1 parent 843c4c2 commit f14b77e

File tree

3 files changed

+77
-89
lines changed

3 files changed

+77
-89
lines changed

neovim_gui/gtk_ui.py

Lines changed: 67 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,12 @@ def __init__(self, font):
9090
self._resize_timer_id = None
9191
self._pressed = None
9292
self._invalid = None
93-
self._pending = [0, 0, 0]
9493
self._reset_cache()
94+
self._attr_defs = {}
9595

9696
def start(self, bridge):
9797
"""Start the UI event loop."""
98-
bridge.attach(80, 24, rgb=True)
98+
bridge.attach(80, 24, rgb=True, ext_multigrid=True)
9999
drawing_area = Gtk.DrawingArea()
100100
drawing_area.connect('draw', self._gtk_draw)
101101
window = Gtk.Window()
@@ -135,15 +135,15 @@ def schedule_screen_update(self, apply_updates):
135135
"""Schedule screen updates to run in the UI event loop."""
136136
def wrapper():
137137
apply_updates()
138-
self._flush()
139138
self._start_blinking()
140139
self._screen_invalid()
141140
GObject.idle_add(wrapper)
142141

143142
def _screen_invalid(self):
144143
self._drawing_area.queue_draw()
145144

146-
def _nvim_resize(self, columns, rows):
145+
def _nvim_grid_resize(self, grid, columns, rows):
146+
assert grid == 1
147147
da = self._drawing_area
148148
# create FontDescription object for the selected font/size
149149
font_str = '{0} {1}'.format(self._font_name, self._font_size)
@@ -170,17 +170,12 @@ def _nvim_resize(self, columns, rows):
170170
self._screen = Screen(columns, rows)
171171
self._window.resize(pixel_width, pixel_height)
172172

173-
def _nvim_clear(self):
173+
def _nvim_grid_clear(self, grid):
174174
self._clear_region(self._screen.top, self._screen.bot + 1,
175175
self._screen.left, self._screen.right + 1)
176176
self._screen.clear()
177177

178-
def _nvim_eol_clear(self):
179-
row, col = self._screen.row, self._screen.col
180-
self._clear_region(row, row + 1, col, self._screen.right + 1)
181-
self._screen.eol_clear()
182-
183-
def _nvim_cursor_goto(self, row, col):
178+
def _nvim_grid_cursor_goto(self, grid, row, col):
184179
self._screen.cursor_goto(row, col)
185180

186181
def _nvim_busy_start(self):
@@ -198,18 +193,12 @@ def _nvim_mouse_off(self):
198193
def _nvim_mode_change(self, mode):
199194
self._insert_cursor = mode == 'insert'
200195

201-
def _nvim_set_scroll_region(self, top, bot, left, right):
202-
self._screen.set_scroll_region(top, bot, left, right)
203-
204-
def _nvim_scroll(self, count):
205-
self._flush()
206-
top, bot = self._screen.top, self._screen.bot + 1
207-
left, right = self._screen.left, self._screen.right + 1
196+
def _nvim_grid_scroll(self, grid, top, bot, left, right, rows, cols):
208197
# The diagrams below illustrate what will happen, depending on the
209198
# scroll direction. "=" is used to represent the SR(scroll region)
210199
# boundaries and "-" the moved rectangles. note that dst and src share
211200
# a common region
212-
if count > 0:
201+
if rows > 0:
213202
# move an rectangle in the SR up, this can happen while scrolling
214203
# down
215204
# +-------------------------+
@@ -221,8 +210,8 @@ def _nvim_scroll(self, count):
221210
# |-------------------------| dst_bot |
222211
# | src (cleared) | |
223212
# +=========================+ src_bot
224-
src_top, src_bot = top + count, bot
225-
dst_top, dst_bot = top, bot - count
213+
src_top, src_bot = top + rows, bot
214+
dst_top, dst_bot = top, bot - rows
226215
clr_top, clr_bot = dst_bot, src_bot
227216
else:
228217
# move a rectangle in the SR down, this can happen while scrolling
@@ -236,8 +225,8 @@ def _nvim_scroll(self, count):
236225
# |=========================| dst_bot |
237226
# | (clipped below SR) | v
238227
# +-------------------------+
239-
src_top, src_bot = top, bot + count
240-
dst_top, dst_bot = top - count, bot
228+
src_top, src_bot = top, bot + rows
229+
dst_top, dst_bot = top - rows, bot
241230
clr_top, clr_bot = src_top, dst_top
242231
self._cairo_surface.flush()
243232
self._cairo_context.save()
@@ -252,33 +241,59 @@ def _nvim_scroll(self, count):
252241
self._cairo_context.restore()
253242
# Clear the emptied region
254243
self._clear_region(clr_top, clr_bot, left, right)
255-
self._screen.scroll(count)
244+
self._screen.scroll(rows)
256245

257-
def _nvim_highlight_set(self, attrs):
258-
self._attrs = self._get_pango_attrs(attrs)
246+
def _nvim_hl_attr_define(self, hlid, attr, info):
247+
self._attr_defs[hlid] = attr
248+
249+
def _nvim_grid_line(self, grid, row, col_start, cells):
250+
assert grid == 1
259251

260-
def _nvim_put(self, text):
261-
if self._screen.row != self._pending[0]:
262-
# flush pending text if jumped to a different row
263-
self._flush()
264-
# work around some redraw glitches that can happen
265-
self._redraw_glitch_fix()
266252
# Update internal screen
267-
self._screen.put(self._get_pango_text(text), self._attrs)
268-
self._pending[1] = min(self._screen.col - 1, self._pending[1])
269-
self._pending[2] = max(self._screen.col, self._pending[2])
253+
col = col_start
254+
attr = None # will be set in first cell
255+
for cell in cells:
256+
text = cell[0]
257+
if len(cell) > 1:
258+
hl_id = cell[1]
259+
attr = self._get_pango_attrs(hl_id)
260+
repeat = cell[2] if len(cell) > 2 else 1
261+
for i in range(repeat):
262+
self._screen.put(row, col, self._get_pango_text(text), attr)
263+
col += 1
264+
col_end = col
265+
266+
# work around some redraw glitches that can happen
267+
col_start, col_end = self._redraw_glitch_fix(row, col_start, col_end)
268+
269+
self._cairo_context.save()
270+
ccol = col_start
271+
buf = []
272+
bold = False
273+
for _, col, text, attrs in self._screen.iter(row, row, col_start,
274+
col_end - 1):
275+
newbold = attrs and 'bold' in attrs[0]
276+
if newbold != bold or not text:
277+
if buf:
278+
self._pango_draw(row, ccol, buf)
279+
bold = newbold
280+
buf = [(text, attrs,)]
281+
ccol = col
282+
else:
283+
buf.append((text, attrs,))
284+
if buf:
285+
self._pango_draw(row, ccol, buf)
286+
self._cairo_context.restore()
287+
270288

271289
def _nvim_bell(self):
272290
self._window.get_window().beep()
273291

274292
def _nvim_visual_bell(self):
275293
pass
276294

277-
def _nvim_update_fg(self, fg):
295+
def _nvim_default_colors_set(self, fg, bg, sp, cterm_fg, cterm_bg):
278296
self._foreground = fg
279-
self._reset_cache()
280-
281-
def _nvim_update_bg(self, bg):
282297
self._background = bg
283298
self._reset_cache()
284299

@@ -427,7 +442,6 @@ def blink(*args):
427442
blink()
428443

429444
def _clear_region(self, top, bot, left, right):
430-
self._flush()
431445
self._cairo_context.save()
432446
self._mask_region(top, bot, left, right)
433447
r, g, b = _split_color(self._background)
@@ -453,37 +467,11 @@ def _get_coords(self, row, col):
453467
y = row * self._cell_pixel_height
454468
return x, y
455469

456-
def _flush(self):
457-
row, startcol, endcol = self._pending
458-
self._pending[0] = self._screen.row
459-
self._pending[1] = self._screen.col
460-
self._pending[2] = self._screen.col
461-
if startcol == endcol:
462-
return
463-
self._cairo_context.save()
464-
ccol = startcol
465-
buf = []
466-
bold = False
467-
for _, col, text, attrs in self._screen.iter(row, row, startcol,
468-
endcol - 1):
469-
newbold = attrs and 'bold' in attrs[0]
470-
if newbold != bold or not text:
471-
if buf:
472-
self._pango_draw(row, ccol, buf)
473-
bold = newbold
474-
buf = [(text, attrs,)]
475-
ccol = col
476-
else:
477-
buf.append((text, attrs,))
478-
if buf:
479-
self._pango_draw(row, ccol, buf)
480-
self._cairo_context.restore()
481-
482470
def _pango_draw(self, row, col, data, cr=None, cursor=False):
483471
markup = []
484472
for text, attrs in data:
485473
if not attrs:
486-
attrs = self._get_pango_attrs(None)
474+
attrs = self._get_pango_attrs(0)
487475
attrs = attrs[1] if cursor else attrs[0]
488476
markup.append('<span {0}>{1}</span>'.format(attrs, text))
489477
markup = ''.join(markup)
@@ -508,10 +496,10 @@ def _get_pango_text(self, text):
508496
self._pango_text_cache[text] = rv
509497
return rv
510498

511-
def _get_pango_attrs(self, attrs):
512-
key = tuple(sorted((k, v,) for k, v in (attrs or {}).items()))
513-
rv = self._pango_attrs_cache.get(key, None)
499+
def _get_pango_attrs(self, hl_id):
500+
rv = self._pango_attrs_cache.get(hl_id, None)
514501
if rv is None:
502+
attrs = self._attr_defs.get(hl_id, {})
515503
fg = self._foreground if self._foreground != -1 else 0
516504
bg = self._background if self._background != -1 else 0xffffff
517505
n = {
@@ -545,36 +533,31 @@ def _get_pango_attrs(self, attrs):
545533
n = ' '.join(['{0}="{1}"'.format(k, v) for k, v in n.items()])
546534
c = ' '.join(['{0}="{1}"'.format(k, v) for k, v in c.items()])
547535
rv = (n, c,)
548-
self._pango_attrs_cache[key] = rv
536+
self._pango_attrs_cache[hl_id] = rv
549537
return rv
550538

551539
def _reset_cache(self):
552540
self._pango_text_cache = {}
553541
self._pango_attrs_cache = {}
554542

555-
def _redraw_glitch_fix(self):
556-
row, col = self._screen.row, self._screen.col
557-
text, attrs = self._screen.get_cursor()
543+
def _redraw_glitch_fix(self, row, col_start, col_end):
558544
# when updating cells in italic or bold words, the result can become
559545
# messy(characters can be clipped or leave remains when removed). To
560546
# prevent that, always update non empty sequences of cells and the
561547
# surrounding space.
562548
# find the start of the sequence
563-
lcol = col - 1
564-
while lcol >= 0:
565-
text, _ = self._screen.get_cell(row, lcol)
566-
lcol -= 1
549+
while col_start-1 >= 0:
550+
text, _ = self._screen.get_cell(row, col_start-1)
567551
if text == ' ':
568552
break
569-
self._pending[1] = min(lcol + 1, self._pending[1])
553+
col_start -= 1
570554
# find the end of the sequence
571-
rcol = col + 1
572-
while rcol < self._screen.columns:
573-
text, _ = self._screen.get_cell(row, rcol)
574-
rcol += 1
555+
while col_end < self._screen.columns:
556+
text, _ = self._screen.get_cell(row, col_end)
575557
if text == ' ':
576558
break
577-
self._pending[2] = max(rcol, self._pending[2])
559+
col_end += 1
560+
return col_start, col_end
578561

579562

580563
def _split_color(n):

neovim_gui/screen.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,10 @@ def scroll(self, count):
8888
for row in range(stop, stop + count, step):
8989
self._clear_region(row, row, left, right)
9090

91-
def put(self, text, attrs):
91+
def put(self, row, col, text, attrs):
9292
"""Put character on virtual cursor position."""
93-
cell = self._cells[self.row][self.col]
93+
cell = self._cells[row][col]
9494
cell.set(text, attrs)
95-
self.cursor_goto(self.row, self.col + 1)
9695

9796
def get_cell(self, row, col):
9897
"""Get text, attrs at row, col."""

neovim_gui/ui_bridge.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ def connect(self, nvim, ui, profile=None, notify=False):
2222
self._ui = ui
2323
self._profile = profile
2424
self._sem = Semaphore(0)
25-
self.debug_events = len(os.environ.get("NVIM_PYTHON_UI_DEBUG", "")) > 0
25+
debug_env = os.environ.get("NVIM_PYTHON_UI_DEBUG", "")
26+
if debug_env == "2":
27+
self.debug_events = 2
28+
else:
29+
self.debug_events = len(debug_env) > 0
2630
t = Thread(target=self._nvim_event_loop)
2731
t.daemon = True
2832
t.start()
@@ -99,10 +103,12 @@ def apply_updates():
99103
if self.debug_events:
100104
print(repr(update), file=sys.stderr)
101105
else:
102-
if self.debug_events and len(update[1]) > nparam:
106+
if self.debug_events == 2 or (self.debug_events and len(update[1]) > nparam):
103107
print(repr(update), file=sys.stderr)
104108
for args in update[1:]:
105109
handler(*args[:nparam])
110+
if self.debug_events == 2:
111+
print("<flush>")
106112
except Exception:
107113
self._error = format_exc()
108114
self._call(self._nvim.quit)

0 commit comments

Comments
 (0)