@@ -42,8 +42,6 @@ class Message:
42
42
be None if the content is raw markup (such as a minihtml link) or if
43
43
it is an outline-only region (which happens with things such as
44
44
dual-region messages added in 1.21).
45
- :ivar minihtml_text: The string used for showing phantoms that includes
46
- the minihtml markup. May be None.
47
45
:ivar level: Message level as a string such as "error", or "info".
48
46
:ivar span: Location of the message (0-based):
49
47
`((line_start, col_start), (line_end, col_end))`
@@ -57,17 +55,21 @@ class Message:
57
55
:ivar children: List of additional Message objects. This is *not*
58
56
recursive (children cannot have children).
59
57
:ivar parent: The primary Message object if this a child.
58
+ :iver hidden: If true, don't show this message.
59
+ :ivar suggested_replacement: An optional string of text as a suggestion to
60
+ replace at the given span. If this is set, `text` will NOT be set.
60
61
"""
61
62
region_key = None
62
63
text = None
63
- minihtml_text = None
64
64
level = None
65
65
span = None
66
66
path = None
67
67
code = None
68
68
output_panel_region = None
69
69
primary = True
70
70
parent = None
71
+ hidden = False
72
+ suggested_replacement = None
71
73
72
74
def __init__ (self ):
73
75
self .id = uuid .uuid4 ()
@@ -100,8 +102,8 @@ def escaped_text(self, view, indent):
100
102
multiple lines. Typically a series of to get correct
101
103
alignment.
102
104
"""
103
- if self .minihtml_text :
104
- return self .minihtml_text
105
+ if self .suggested_replacement is not None :
106
+ return self ._render_suggested_replacement ()
105
107
if not self .text :
106
108
return ''
107
109
@@ -127,10 +129,43 @@ def escape_and_link(i_txt):
127
129
parts = re .split (LINK_PATTERN , text )
128
130
return ' ' .join (map (escape_and_link , enumerate (parts )))
129
131
132
+ def _render_suggested_replacement (self ):
133
+ replacement_template = util .multiline_fix ("""
134
+ <div class="rust-replacement"><a href="replace:%s" class="rust-button">Accept Replacement:</a> %s</div>
135
+ """ )
136
+ html_suggestion = html .escape (self .suggested_replacement , quote = False )
137
+ if '\n ' in html_suggestion :
138
+ # Start on a new line so the text doesn't look too weird.
139
+ html_suggestion = '\n ' + html_suggestion
140
+ html_suggestion = html_suggestion \
141
+ .replace (' ' , ' ' )\
142
+ .replace ('\n ' , '<br>\n ' )
143
+ return replacement_template % (
144
+ urllib .parse .urlencode ({
145
+ 'id' : self .id ,
146
+ 'replacement' : self .suggested_replacement ,
147
+ }),
148
+ html_suggestion ,
149
+ )
150
+
151
+ def suggestion_count (self ):
152
+ """Number of suggestions in this message.
153
+
154
+ This is used to know once all suggestions have been accepted that a
155
+ message can be dismissed.
156
+ """
157
+ if self .parent :
158
+ return self .parent .suggestion_count ()
159
+ count = 0
160
+ for m in self :
161
+ if m .suggested_replacement is not None and not m .hidden :
162
+ count += 1
163
+ return count
164
+
130
165
def is_similar (self , other ):
131
166
"""Returns True if this message is essentially the same as the given
132
167
message. Used for deduplication."""
133
- keys = ('path' , 'span' , 'level' , 'text' , 'minihtml_text ' )
168
+ keys = ('path' , 'span' , 'level' , 'text' , 'suggested_replacement ' )
134
169
for key in keys :
135
170
if getattr (other , key ) != getattr (self , key ):
136
171
return False
@@ -140,10 +175,18 @@ def is_similar(self, other):
140
175
def sublime_region (self , view ):
141
176
"""Returns a sublime.Region object for this message."""
142
177
if self .span :
143
- return sublime .Region (
144
- view .text_point (self .span [0 ][0 ], self .span [0 ][1 ]),
145
- view .text_point (self .span [1 ][0 ], self .span [1 ][1 ])
146
- )
178
+ regions = view .get_regions (self .region_key )
179
+ if regions :
180
+ self .span = (
181
+ view .rowcol (regions [0 ].a ),
182
+ view .rowcol (regions [0 ].b )
183
+ )
184
+ return regions [0 ]
185
+ else :
186
+ return sublime .Region (
187
+ view .text_point (self .span [0 ][0 ], self .span [0 ][1 ]),
188
+ view .text_point (self .span [1 ][0 ], self .span [1 ][1 ])
189
+ )
147
190
else :
148
191
# Place at bottom of file for lack of anywhere better.
149
192
return sublime .Region (view .size ())
@@ -265,24 +308,21 @@ def message_popup(view, point, hover_zone):
265
308
row = view .rowcol (point )[0 ]
266
309
267
310
def filter_row (batch ):
268
- span = batch .first (). span
269
- if span :
270
- return row >= span [ 0 ][ 0 ] and row <= span [ 1 ][ 0 ]
271
- else :
272
- last_row = view .rowcol (view . size ())[0 ]
273
- return row == last_row
311
+ if batch .hidden :
312
+ return False
313
+ region = batch . first (). sublime_region ( view )
314
+ batch_row_a = view . rowcol ( region . begin ())[ 0 ]
315
+ batch_row_b = view .rowcol (region . end ())[0 ]
316
+ return row >= batch_row_a and row <= batch_row_b
274
317
275
318
batches = filter (filter_row , batches )
276
319
else :
277
320
# Collect all messages covering this point.
278
321
def filter_point (batch ):
322
+ if batch .hidden :
323
+ return False
279
324
for msg in batch :
280
- if msg .span :
281
- start_pt = view .text_point (* msg .span [0 ])
282
- end_pt = view .text_point (* msg .span [1 ])
283
- if point >= start_pt and point <= end_pt :
284
- return True
285
- elif point == view .size ():
325
+ if not msg .hidden and msg .sublime_region (view ).contains (point ):
286
326
return True
287
327
return False
288
328
@@ -307,7 +347,7 @@ def _click_handler(view, url, hide_popup=False):
307
347
elif url .startswith ('file:///' ):
308
348
view .window ().open_file (url [8 :], sublime .ENCODED_POSITION )
309
349
elif url .startswith ('replace:' ):
310
- info = urllib .parse .parse_qs (url [8 :])
350
+ info = urllib .parse .parse_qs (url [8 :], keep_blank_values = True )
311
351
_accept_replace (view , info ['id' ][0 ], info ['replacement' ][0 ])
312
352
if hide_popup :
313
353
view .hide_popup ()
@@ -333,11 +373,21 @@ def batch_and_msg():
333
373
print ('Rust Enhanced internal error: Could not find region for suggestion.' )
334
374
return
335
375
region = (regions [0 ].a , regions [0 ].b )
336
- batch .dismiss (view .window ())
337
376
view .run_command ('rust_accept_suggested_replacement' , {
338
377
'region' : region ,
339
378
'replacement' : replacement
340
379
})
380
+ msg .hidden = True
381
+ if msg .suggestion_count ():
382
+ # Additional suggestions still exist, re-render the phantom.
383
+ view .erase_phantoms (batch .first ().region_key )
384
+ for m in batch :
385
+ # Force `span` to be updated to the most recent value.
386
+ m .sublime_region (view )
387
+ _show_phantom (view , batch )
388
+ else :
389
+ # No more suggestions, just hide the diagnostic completely.
390
+ batch .primary ().dismiss (view .window ())
341
391
342
392
343
393
def _show_phantom (view , batch ):
@@ -820,24 +870,28 @@ def set_primary_message(span, text):
820
870
message .text = text
821
871
message .level = info ['level' ]
822
872
823
- def add_additional (span , text , level ):
873
+ def add_additional (span , text , level , suggested_replacement = None ):
824
874
child = Message ()
825
875
child .text = text
876
+ child .suggested_replacement = suggested_replacement
826
877
child .level = level
827
878
child .primary = False
828
879
if 'macros>' in span ['file_name' ]:
829
880
# Nowhere to display this, just send it to the console via msg_cb.
830
881
msg_cb (child )
831
882
else :
832
883
child .path = make_span_path (span )
884
+ if not os .path .exists (child .path ):
885
+ # Sometimes rust gives messages that link to libstd in the
886
+ # directory where it was built (such as on Travis).
887
+ return
833
888
child .span = make_span_region (span )
834
889
if any (map (lambda m : m .is_similar (child ), message .children )):
835
890
# Duplicate message, skip. This happens with some of the
836
891
# macro help messages.
837
- return child
892
+ return
838
893
child .parent = message
839
894
message .children .append (child )
840
- return child
841
895
842
896
if len (info ['spans' ]) == 0 :
843
897
if parent_info :
@@ -925,11 +979,7 @@ def find_span_r(span, expansion=None):
925
979
# Primary child message.
926
980
add_additional (span , info ['message' ], info ['level' ])
927
981
else :
928
- # Check if the main message is already set since there might
929
- # be multiple spans that are primary (in which case, we
930
- # arbitrarily show the main message on the first one).
931
- if not message .path :
932
- set_primary_message (span , info ['message' ])
982
+ set_primary_message (span , info ['message' ])
933
983
934
984
label = span ['label' ]
935
985
# Some spans don't have a label. These seem to just imply
@@ -943,27 +993,11 @@ def find_span_r(span, expansion=None):
943
993
if label is not None :
944
994
# Display the label for this Span.
945
995
add_additional (span , label , info ['level' ])
946
- if span ['suggested_replacement' ]:
947
- # The "suggested_replacement" contains the code that
948
- # should replace the span.
949
- child = add_additional (span , None , 'help' )
950
- replacement_template = util .multiline_fix ("""
951
- <div class="rust-replacement"><a href="replace:%s" class="rust-button">Accept Replacement:</a> %s</div>
952
- """ )
953
- html_suggestion = html .escape (span ['suggested_replacement' ], quote = False )
954
- if '\n ' in html_suggestion :
955
- # Start on a new line so the text doesn't look too weird.
956
- html_suggestion = '\n ' + html_suggestion
957
- html_suggestion = html_suggestion \
958
- .replace (' ' , ' ' )\
959
- .replace ('\n ' , '<br>\n ' )
960
- child .minihtml_text = replacement_template % (
961
- urllib .parse .urlencode ({
962
- 'id' : child .id ,
963
- 'replacement' : span ['suggested_replacement' ],
964
- }),
965
- html_suggestion ,
966
- )
996
+ if span ['suggested_replacement' ] is not None :
997
+ # The "suggested_replacement" contains the code that should
998
+ # replace the span.
999
+ add_additional (span , None , 'help' ,
1000
+ suggested_replacement = span ['suggested_replacement' ])
967
1001
968
1002
# Recurse into children (which typically hold notes).
969
1003
for child in info ['children' ]:
0 commit comments