Skip to content

Commit f74636a

Browse files
authored
Merge pull request #126 from ConstantinGahr/remove-duplicate-phantoms
Remove duplicate phantoms
2 parents 2896eee + dbb3b94 commit f74636a

3 files changed

Lines changed: 65 additions & 37 deletions

File tree

helium.py

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
Copyright (c) 2016-2018, NEGORO Tetsuya (https://github.com/ngr-t)
66
"""
77

8-
import bisect
98
import json
109
import os
1110
import re
@@ -18,7 +17,7 @@
1817
from sublime_plugin import EventListener, TextCommand, ViewEventListener
1918

2019
from .lib.kernel import KernelConnection
21-
from .lib.utils import add_path, chain_callbacks
20+
from .lib.utils import add_path, chain_callbacks, get_cell
2221

2322
with add_path(os.path.join(os.path.dirname(__file__), "lib/client")):
2423
# Import jupyter_client related functions and classes.
@@ -33,6 +32,7 @@
3332
HANDLER = StreamHandler()
3433
HANDLER.setLevel(INFO)
3534

35+
3636
if len(HELIUM_LOGGER.handlers) == 0:
3737
HELIUM_LOGGER.setLevel(INFO)
3838
HELIUM_LOGGER.addHandler(HANDLER)
@@ -716,30 +716,6 @@ def get_block(view: sublime.View, s: sublime.Region) -> (str, sublime.Region):
716716
return (view.substr(block_region), block_region)
717717

718718

719-
def get_cell(
720-
view: sublime.View, region: sublime.Region, *, logger=HELIUM_LOGGER
721-
) -> (str, sublime.Region):
722-
"""Get the code cell under the cursor.
723-
724-
Cells are separated by markers.
725-
Those are defined in `cell_delimiter_pattern` in the config file.
726-
727-
If `s` is a selected region, the code cell is it.
728-
"""
729-
if not region.empty():
730-
return (view.substr(region), region)
731-
cell_delimiter_pattern = sublime.load_settings("Helium.sublime-settings").get(
732-
"cell_delimiter_pattern"
733-
)
734-
separators = view.find_all(cell_delimiter_pattern)
735-
separators.append(sublime.Region(view.size() + 1, view.size() + 1))
736-
r = sublime.Region(region.begin() + 1, region.begin() + 1)
737-
start_point = separators[bisect.bisect(separators, r) - 1].end() + 1
738-
end_point = separators[bisect.bisect(separators, r)].begin() - 1
739-
cell_region = sublime.Region(start_point, end_point)
740-
return (view.substr(cell_region), cell_region)
741-
742-
743719
@chain_callbacks
744720
def _execute_block(view, *, logger=HELIUM_LOGGER):
745721
try:

lib/kernel.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,12 @@
77
import re
88
from collections import defaultdict
99
from datetime import datetime
10-
from threading import Event, Thread, RLock
1110
from queue import Empty, Queue
11+
from threading import Event, RLock, Thread
1212

1313
import sublime
1414

15-
from .utils import show_password_input
16-
from .utils import get_png_dimensions
17-
15+
from .utils import get_cell, get_png_dimensions, show_password_input
1816

1917
JUPYTER_PROTOCOL_VERSION = "5.0"
2018

@@ -149,6 +147,7 @@ class IOPubMessageReceiver(MessageReceiver):
149147
def run(self):
150148
"""Run main routine."""
151149
# TODO: log, handle other message types.
150+
152151
while not self.exit.is_set():
153152
try:
154153
msg = self._kernel.client.get_iopub_msg(timeout=1)
@@ -163,6 +162,9 @@ def run(self):
163162
if msg_type == MSG_TYPE_STATUS:
164163
self._kernel._execution_state = content["execution_state"]
165164
elif msg_type == MSG_TYPE_EXECUTE_INPUT:
165+
# if code is executed deleted all phantoms in this region
166+
self._kernel._clear_phantoms_in_region(region, view)
167+
166168
self._kernel._write_text_to_view("\n\n")
167169
if sublime.load_settings("Helium.sublime-settings").get(
168170
"output_code"
@@ -191,6 +193,7 @@ def run(self):
191193
self._kernel._handle_stream(
192194
content["name"], content["text"], region, view,
193195
)
196+
194197
except Empty:
195198
pass
196199
except Exception as ex:
@@ -260,6 +263,7 @@ def __init__(
260263
self._connection_name = connection_name
261264
self._execution_state = "unknown"
262265
self._init_receivers()
266+
self.phantoms = {}
263267

264268
def __del__(self): # noqa
265269
self._shell_msg_receiver.shutdown()
@@ -387,7 +391,7 @@ def _handle_stream(
387391
pass
388392

389393
def _write_out_execution_count(self, execution_count) -> None:
390-
self._write_text_to_view("\nOut[{}]: ".format(execution_count))
394+
self._write_text_to_view("\nOut[{}]: \n".format(execution_count))
391395

392396
def _write_text_to_view(self, text: str) -> None:
393397
if self._show_inline_output:
@@ -416,14 +420,18 @@ def _write_inline_html_phantom(
416420
):
417421
if self._show_inline_output:
418422
id = HELIUM_FIGURE_PHANTOMS + datetime.now().isoformat()
423+
419424
html = TEXT_PHANTOM.format(content=content)
420-
view.add_phantom(
425+
int_id = view.add_phantom(
421426
id,
422427
region,
423428
html,
424429
sublime.LAYOUT_BLOCK,
425-
on_navigate=lambda href, id=id: view.erase_phantoms(id),
430+
on_navigate=lambda href, id=id, view=view: self._erase_phantom(
431+
id, view=view
432+
),
426433
)
434+
self.phantoms[id] = int_id
427435
self._logger.info("Created inline phantom {}".format(html))
428436

429437
def _write_inline_image_phantom(
@@ -450,20 +458,40 @@ def _write_inline_image_phantom(
450458

451459
html = IMAGE_PHANTOM.format(data=data, width=width, height=height)
452460

453-
view.add_phantom(
461+
int_id = view.add_phantom(
454462
id,
455463
region,
456464
html,
457465
sublime.LAYOUT_BLOCK,
458-
on_navigate=lambda href, id=id: view.erase_phantoms(id),
466+
on_navigate=lambda href, id=id, view=view: self._erase_phantom(
467+
id, view=view
468+
),
459469
)
470+
self.phantoms[id] = int_id
460471
self._logger.info("Created inline phantom image")
461472

473+
def _clear_phantoms_in_region(self, region: sublime.Region, view: sublime.View):
474+
_, cell = get_cell(view, region, logger="")
475+
remove = [
476+
pid
477+
for pid, int_id in self.phantoms.items()
478+
if cell.contains(view.query_phantom(int_id)[0])
479+
]
480+
481+
for pid in remove:
482+
self._erase_phantom(pid, view=view)
483+
484+
def _erase_phantom(self, pid: str, *, view: sublime.View):
485+
if pid in self.phantoms:
486+
_ = self.phantoms.pop(pid)
487+
view.erase_phantoms(pid)
488+
462489
def _write_mime_data_to_view(
463490
self, mime_data: dict, region: sublime.Region, view: sublime.View
464491
) -> None:
465492
# Now we use basically text/plain for text type.
466493
# Jupyter kernels often emits html whom minihtml cannot render.
494+
467495
if "text/plain" in mime_data:
468496
content = mime_data["text/plain"]
469497
lines = "\n(display data): {content}".format(content=content)

lib/utils.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
import bisect
12
import re
23
import sys
3-
from functools import wraps
4-
54
from base64 import b64decode
5+
from functools import wraps
66

77
import sublime
88
from sublime_plugin import TextCommand
@@ -108,3 +108,27 @@ def get_png_dimensions(base64):
108108
iwidth = int.from_bytes(wh[1:5], byteorder="big")
109109
iheight = int.from_bytes(wh[5:], byteorder="big")
110110
return (iwidth, iheight)
111+
112+
113+
def get_cell(
114+
view: sublime.View, region: sublime.Region, *, logger: str
115+
) -> (str, sublime.Region):
116+
"""Get the code cell under the cursor.
117+
118+
Cells are separated by markers.
119+
Those are defined in `cell_delimiter_pattern` in the config file.
120+
121+
If `s` is a selected region, the code cell is it.
122+
"""
123+
if not region.empty():
124+
return (view.substr(region), region)
125+
cell_delimiter_pattern = sublime.load_settings("Helium.sublime-settings").get(
126+
"cell_delimiter_pattern"
127+
)
128+
separators = view.find_all(cell_delimiter_pattern)
129+
separators.append(sublime.Region(view.size() + 2, view.size() + 2))
130+
r = sublime.Region(region.begin() + 1, region.begin() + 1)
131+
start_point = separators[bisect.bisect(separators, r) - 1].end() + 1
132+
end_point = separators[bisect.bisect(separators, r)].begin() - 1
133+
cell_region = sublime.Region(start_point, end_point)
134+
return (view.substr(cell_region), cell_region)

0 commit comments

Comments
 (0)