Skip to content

Commit 30445d4

Browse files
ehussJayflux
authored andcommitted
Add "popup" option to "rust_phantom_style". (#230)
* Add "popup" option to "rust_phantom_style". Fixes #229. * Don't hard-code the package name.
1 parent 56ffe48 commit 30445d4

File tree

4 files changed

+141
-64
lines changed

4 files changed

+141
-64
lines changed

RustEnhanced.sublime-settings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
// For errors/warnings, how to show the inline message.
3232
// "normal" - Shows the message inline.
33+
// "popup" - Show popup on mouse hover.
3334
// "none" - Do not show the message inline.
3435
"rust_phantom_style": "normal",
3536

cargo_build.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,3 +464,24 @@ class CargoBenchAtCursorCommand(sublime_plugin.TextCommand):
464464
def run(self, edit):
465465
pt = self.view.sel()[0].begin()
466466
_cargo_test_pt('bench', pt, self.view)
467+
468+
469+
class CargoMessageHover(sublime_plugin.ViewEventListener):
470+
471+
"""Displays a popup if `rust_phantom_style` is "popup" when the mouse
472+
hovers over a message region.
473+
474+
Limitation: If you edit the file and shift the region, the hover feature
475+
will not recognize the new region. This means that the popup will only
476+
show in the old location.
477+
"""
478+
479+
@classmethod
480+
def is_applicable(cls, settings):
481+
s = settings.get('syntax')
482+
package_name = __package__.split('.')[0]
483+
return s == 'Packages/%s/RustEnhanced.sublime-syntax' % (package_name,)
484+
485+
def on_hover(self, point, hover_zone):
486+
if util.get_setting('rust_phantom_style', 'normal') == 'popup':
487+
messages.message_popup(self.view, point, hover_zone)

docs/build.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ General settings (see [Settings](../README.md#settings)) for how messages are di
5353
| `rust_syntax_warning_color` | `"var(--yellowish)"` | Color of warning messages. |
5454
| `rust_syntax_note_color` | `"var(--greenish)"` | Color of note messages. |
5555
| `rust_syntax_help_color` | `"var(--bluish)"` | Color of help messages. |
56-
| `rust_phantom_style` | `"normal"` | How to display inline messages. Either `normal` or `none`. |
56+
| `rust_phantom_style` | `"normal"` | How to display inline messages. Either `normal`, `popup`, or `none`. |
5757
| `rust_region_style` | `"outline"` | How to highlight messages. Either `outline` or `none`. |
5858
| `rust_gutter_style` | `"shape"` | Type of icon to show in the gutter. Either `shape`, `circle`, or `none`. |
5959

rust/messages.py

Lines changed: 118 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sublime
44

55
import collections
6+
import functools
67
import html
78
import itertools
89
import os
@@ -27,7 +28,7 @@
2728
# attached detailed diagnostic information, child notes, etc.
2829
# - `path`: Absolute path to the file.
2930
# - `text`: The raw text of the message without any minihtml markup.
30-
# - `phantom_text`: The string used for showing phantoms that includes the
31+
# - `minihtml_text`: The string used for showing phantoms that includes the
3132
# minihtml markup.
3233
# - `output_panel_region`: Optional Sublime Region object that indicates the
3334
# region in the build output panel that corresponds with this message.
@@ -37,46 +38,53 @@
3738
LINK_PATTERN = r'(https?://[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-zA-Z]{2,6}\b[-a-zA-Z0-9@:%_+.~#?&/=]*)'
3839

3940

40-
PHANTOM_TEMPLATE = """
41+
CSS_TEMPLATE = """
42+
<style>
43+
span {{
44+
font-family: monospace;
45+
}}
46+
.rust-error {{
47+
color: {error_color};
48+
}}
49+
.rust-warning {{
50+
color: {warning_color};
51+
}}
52+
.rust-note {{
53+
color: {note_color};
54+
}}
55+
.rust-help {{
56+
color: {help_color};
57+
}}
58+
.rust-link {{
59+
background-color: var(--background);
60+
color: var(--bluish);
61+
text-decoration: none;
62+
border-radius: 0.5rem;
63+
padding: 0.1rem 0.3rem;
64+
border: 1px solid var(--bluish);
65+
}}
66+
.rust-links {{
67+
margin: 0.4rem 0rem;
68+
}}
69+
a {{
70+
text-decoration: inherit;
71+
padding: 0.35rem 0.5rem 0.45rem 0.5rem;
72+
position: relative;
73+
font-weight: bold;
74+
}}
75+
{extra_css}
76+
</style>
4177
<body id="rust-message">
42-
<style>
43-
span {{
44-
font-family: monospace;
45-
}}
46-
.rust-error {{
47-
color: {error_color};
48-
}}
49-
.rust-warning {{
50-
color: {warning_color};
51-
}}
52-
.rust-note {{
53-
color: {note_color};
54-
}}
55-
.rust-help {{
56-
color: {help_color};
57-
}}
58-
.rust-link {{
59-
background-color: var(--background);
60-
color: var(--bluish);
61-
text-decoration: none;
62-
border-radius: 0.5rem;
63-
padding: 0.1rem 0.3rem;
64-
border: 1px solid var(--bluish);
65-
}}
66-
.rust-links {{
67-
margin: 0.4rem 0rem;
68-
}}
69-
a {{
70-
text-decoration: inherit;
71-
padding: 0.35rem 0.5rem 0.45rem 0.5rem;
72-
position: relative;
73-
font-weight: bold;
74-
}}
75-
</style>
7678
{content}
7779
</body>
7880
"""
7981

82+
POPUP_CSS = """
83+
body {
84+
margin: 0.25em;
85+
}
86+
"""
87+
8088

8189
def clear_messages(window):
8290
WINDOW_MESSAGES.pop(window.id(), None)
@@ -88,7 +96,7 @@ def clear_messages(window):
8896
view.erase_regions('rust-help')
8997

9098

91-
def add_message(window, path, span, level, is_main, text, markup_text, msg_cb):
99+
def add_message(window, path, span, level, is_main, text, minihtml_text, msg_cb):
92100
"""Add a message to be displayed.
93101
94102
:param window: The Sublime window.
@@ -100,7 +108,7 @@ def add_message(window, path, span, level, is_main, text, markup_text, msg_cb):
100108
:param is_main: If True, this is a top-level message. False is used for
101109
attached detailed diagnostic information, child notes, etc.
102110
:param text: The raw text of the message without any minihtml markup.
103-
:param markup_text: The message to display with minihtml markup.
111+
:param minihtml_text: The message to display with minihtml markup.
104112
:param msg_cb: Callback that will be given the message. May be None.
105113
"""
106114
if 'macros>' in path:
@@ -119,30 +127,21 @@ def add_message(window, path, span, level, is_main, text, markup_text, msg_cb):
119127
}
120128
messages = messages_by_path.setdefault(path, [])
121129

122-
if markup_text:
123-
phantom_text = PHANTOM_TEMPLATE.format(content=markup_text,
124-
error_color=util.get_setting('rust_syntax_error_color', 'var(--redish)'),
125-
warning_color=util.get_setting('rust_syntax_warning_color', 'var(--yellowish)'),
126-
note_color=util.get_setting('rust_syntax_note_color', 'var(--greenish)'),
127-
help_color=util.get_setting('rust_syntax_help_color', 'var(--bluish)'),
128-
)
129-
else:
130-
phantom_text = None
131130
to_add = {
132131
'path': path,
133132
'level': level,
134133
'span': span,
135134
'is_main': is_main,
136135
'text': text,
137-
'phantom_text': phantom_text,
136+
'minihtml_text': minihtml_text,
138137
}
139138
if _is_duplicate(to_add, messages):
140139
# Don't add duplicates.
141140
return
142141
messages.append(to_add)
143142
view = window.find_open_file(path)
144143
if view:
145-
_show_phantom(view, level, span, phantom_text)
144+
_show_phantom(view, level, span, minihtml_text)
146145
if msg_cb:
147146
msg_cb(to_add)
148147

@@ -238,11 +237,75 @@ def check_in(region):
238237
sublime.DRAW_NO_FILL | sublime.DRAW_EMPTY)
239238

240239

241-
def _show_phantom(view, level, span, message):
242-
if util.get_setting('rust_phantom_style', 'normal') == 'none':
240+
def _wrap_css(content, extra_css=''):
241+
"""Takes the given minihtml content and places it inside a <body> with the
242+
appropriate CSS."""
243+
return CSS_TEMPLATE.format(content=content,
244+
error_color=util.get_setting('rust_syntax_error_color', 'var(--redish)'),
245+
warning_color=util.get_setting('rust_syntax_warning_color', 'var(--yellowish)'),
246+
note_color=util.get_setting('rust_syntax_note_color', 'var(--greenish)'),
247+
help_color=util.get_setting('rust_syntax_help_color', 'var(--bluish)'),
248+
extra_css=extra_css,
249+
)
250+
251+
252+
def message_popup(view, point, hover_zone):
253+
"""Displays a popup if there is a message at the given point."""
254+
paths = WINDOW_MESSAGES.get(view.window().id(), {}).get('paths', {})
255+
msgs = paths.get(view.file_name(), [])
256+
257+
if hover_zone == sublime.HOVER_GUTTER:
258+
# Collect all messages on this line.
259+
row = view.rowcol(point)[0]
260+
261+
def filter_row(msg):
262+
span = msg['span']
263+
if span:
264+
return row >= span[0][0] and row <= span[1][0]
265+
else:
266+
last_row = view.rowcol(view.size())[0]
267+
return row == last_row
268+
269+
msgs = filter(filter_row, msgs)
270+
else:
271+
# Collect all messages covering this point.
272+
def filter_point(msg):
273+
span = msg['span']
274+
if span:
275+
start_pt = view.text_point(*span[0])
276+
end_pt = view.text_point(*span[1])
277+
return point >= start_pt and point <= end_pt
278+
else:
279+
return point == view.size()
280+
281+
msgs = filter(filter_point, msgs)
282+
283+
if msgs:
284+
to_show = '\n'.join(msg['minihtml_text'] for msg in msgs)
285+
minihtml = _wrap_css(to_show, extra_css=POPUP_CSS)
286+
on_nav = functools.partial(_click_handler, view, hide_popup=True)
287+
max_width = view.em_width() * 79
288+
view.show_popup(minihtml, sublime.COOPERATE_WITH_AUTO_COMPLETE,
289+
point, max_width=max_width, on_navigate=on_nav)
290+
291+
292+
def _click_handler(view, url, hide_popup=False):
293+
if url == 'hide':
294+
clear_messages(view.window())
295+
if hide_popup:
296+
view.hide_popup()
297+
elif url.startswith('file:///'):
298+
view.window().open_file(url[8:], sublime.ENCODED_POSITION)
299+
else:
300+
webbrowser.open_new(url)
301+
302+
303+
def _show_phantom(view, level, span, minihtml_text):
304+
if util.get_setting('rust_phantom_style', 'normal') != 'normal':
243305
return
244-
if not message:
306+
if not minihtml_text:
245307
return
308+
246309
region = _span_to_region(view, span)
247310
# For some reason if you have a multi-line region, the phantom is only
248311
# displayed under the first line. I think it makes more sense for the
@@ -256,20 +319,12 @@ def _show_phantom(view, level, span, message):
256319
region.end()
257320
)
258321

259-
def click_handler(url):
260-
if url == 'hide':
261-
clear_messages(view.window())
262-
elif url.startswith('file:///'):
263-
view.window().open_file(url[8:], sublime.ENCODED_POSITION)
264-
else:
265-
webbrowser.open_new(url)
266-
267322
_sublime_add_phantom(
268323
view,
269324
'rust-syntax-phantom', region,
270-
message,
325+
_wrap_css(minihtml_text),
271326
sublime.LAYOUT_BLOCK,
272-
click_handler
327+
functools.partial(_click_handler, view)
273328
)
274329

275330

@@ -437,7 +492,7 @@ def _show_messages_for_view(view, messages):
437492
_show_phantom(view,
438493
message['level'],
439494
message['span'],
440-
message['phantom_text'])
495+
message['minihtml_text'])
441496
_draw_region_highlights(view, messages)
442497

443498

0 commit comments

Comments
 (0)