From dfb7530e6bf62f9dcc31ee2eaa98d69b7a586bbe Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 5 Sep 2018 12:30:34 -0700 Subject: [PATCH] Message updates. - Add `rust_message_status_bar` option to display the message under the cursor in the window status bar. - Add `rust_message_popup` command to manually show a popup under the cursor. - Add underline styles to `rust_region_style`. - Fix various issues with multiple views into the same file. - Fix on-save checking being triggered multiple times with "Save All" and the build command when the buffer is unsaved. - Fix Next/Prev when a message is hidden (such as a completed suggestion). - Fix hitting Esc in the middle of the build to hide/dismiss future messages. --- RustEnhanced.sublime-commands | 4 + RustEnhanced.sublime-settings | 6 ++ SyntaxCheckPlugin.py | 37 ++++--- cargo_build.py | 100 ++++++++++++++----- docs/img/region_style_none.png | Bin 2569 -> 1980 bytes docs/img/region_style_outline.png | Bin 2617 -> 2143 bytes docs/img/region_style_solid_underline.png | Bin 0 -> 1995 bytes docs/img/region_style_squiggly_underline.png | Bin 0 -> 2007 bytes docs/img/region_style_stippled_underline.png | Bin 0 -> 1987 bytes docs/messages.md | 16 +++ rust/batch.py | 6 +- rust/messages.py | 94 +++++++++++++---- rust/util.py | 23 ++++- tests/rust_test_common.py | 6 ++ 14 files changed, 227 insertions(+), 65 deletions(-) create mode 100644 docs/img/region_style_solid_underline.png create mode 100644 docs/img/region_style_squiggly_underline.png create mode 100644 docs/img/region_style_stippled_underline.png diff --git a/RustEnhanced.sublime-commands b/RustEnhanced.sublime-commands index 68eac02a..2ba0b823 100644 --- a/RustEnhanced.sublime-commands +++ b/RustEnhanced.sublime-commands @@ -56,4 +56,8 @@ "caption": "Rust: Open Debug Log", "command": "rust_open_log" }, + { + "caption": "Rust: Popup Message At Cursor", + "command": "rust_message_popup" + } ] diff --git a/RustEnhanced.sublime-settings b/RustEnhanced.sublime-settings index 1bbc9dc8..32b13dcf 100644 --- a/RustEnhanced.sublime-settings +++ b/RustEnhanced.sublime-settings @@ -37,6 +37,9 @@ // For errors/warnings, how to highlight the region of the error. // "outline" - Outlines the region. + // "solid_underline" - A solid underline. + // "stippled_underline" - A stippled underline. + // "squiggly_underline" - A squiggly underline. // "none" - No outlining. "rust_region_style": "outline", @@ -51,6 +54,9 @@ // "solid" - Solid background color. "rust_message_theme": "clear", + // If `true`, displays diagnostic messages under the cursor in the status bar. + "rust_message_status_bar": false, + // If your cargo project has several build targets, it's possible to specify mapping of // source code filenames to the target names to enable syntax checking. // "projects": { diff --git a/SyntaxCheckPlugin.py b/SyntaxCheckPlugin.py index baeaeedd..3a0a32e7 100755 --- a/SyntaxCheckPlugin.py +++ b/SyntaxCheckPlugin.py @@ -1,9 +1,9 @@ import sublime import sublime_plugin import os +import time from .rust import (messages, rust_proc, rust_thread, util, target_detect, cargo_settings, semver, log) -from pprint import pprint """On-save syntax checking. @@ -13,26 +13,31 @@ """ +# TODO: Use ViewEventListener if +# https://github.com/SublimeTextIssues/Core/issues/2411 is fixed. class RustSyntaxCheckEvent(sublime_plugin.EventListener): - # Beware: This gets called multiple times if the same buffer is opened in - # multiple views (with the same view passed in each time). See: - # https://github.com/SublimeTextIssues/Core/issues/289 + last_save = 0 + def on_post_save(self, view): - # Are we in rust scope and is it switched on? - # We use phantoms which were added in 3118 - if int(sublime.version()) < 3118: + enabled = util.get_setting('rust_syntax_checking', True) + if not enabled or not util.active_view_is_rust(view=view): + return + prev_save = self.last_save + self.last_save = time.time() + if self.last_save - prev_save < 0.25: + # This is a guard for a few issues. + # * `on_post_save` gets called multiple times if the same buffer + # is opened in multiple views (with the same view passed in each + # time). See: + # https://github.com/SublimeTextIssues/Core/issues/289 + # * When using "Save All" we want to avoid launching a bunch of + # threads and then immediately killing them. return log.clear_log(view.window()) - - enabled = util.get_setting('rust_syntax_checking', True) - if enabled and util.active_view_is_rust(view=view): - t = RustSyntaxCheckThread(view) - t.start() - elif not enabled: - # If the user has switched OFF the plugin, remove any phantom - # lines. - messages.clear_messages(view.window()) + messages.erase_status(view) + t = RustSyntaxCheckThread(view) + t.start() class RustSyntaxCheckThread(rust_thread.RustThread, rust_proc.ProcListener): diff --git a/cargo_build.py b/cargo_build.py index 6bba0615..c2fc49c8 100644 --- a/cargo_build.py +++ b/cargo_build.py @@ -222,32 +222,22 @@ def run(self): ON_LOAD_MESSAGES_ENABLED = True -class CargoEventListener(sublime_plugin.EventListener): +class MessagesViewEventListener(sublime_plugin.ViewEventListener): """Every time a new file is loaded, check if is a Rust file with messages, and if so, display the messages. """ - def on_load(self, view): - if ON_LOAD_MESSAGES_ENABLED and util.active_view_is_rust(view=view): - # For some reason, view.window() returns None here. - # Use set_timeout to give it time to attach to a window. - sublime.set_timeout( - lambda: messages.show_messages_for_view(view), 1) + @classmethod + def is_applicable(cls, settings): + return ON_LOAD_MESSAGES_ENABLED and util.is_rust_view(settings) - def on_query_context(self, view, key, operator, operand, match_all): - # Used by the Escape-key keybinding to dismiss inline phantoms. - if key == 'rust_has_messages': - try: - winfo = messages.WINDOW_MESSAGES[view.window().id()] - has_messages = not winfo['hidden'] - except KeyError: - has_messages = False - if operator == sublime.OP_EQUAL: - return operand == has_messages - elif operator == sublime.OP_NOT_EQUAL: - return operand != has_messages - return None + @classmethod + def applies_to_primary_view_only(cls): + return False + + def on_load_async(self): + messages.show_messages_for_view(self.view) class NextPrevBase(sublime_plugin.WindowCommand): @@ -486,15 +476,79 @@ class CargoMessageHover(sublime_plugin.ViewEventListener): @classmethod def is_applicable(cls, settings): - s = settings.get('syntax') - package_name = __package__.split('.')[0] - return s == 'Packages/%s/RustEnhanced.sublime-syntax' % (package_name,) + return util.is_rust_view(settings) + + @classmethod + def applies_to_primary_view_only(cls): + return False def on_hover(self, point, hover_zone): if util.get_setting('rust_phantom_style', 'normal') == 'popup': messages.message_popup(self.view, point, hover_zone) +class RustMessagePopupCommand(sublime_plugin.TextCommand): + + """Manually display a popup for any message under the cursor.""" + + def run(self, edit): + for r in self.view.sel(): + messages.message_popup(self.view, r.begin(), sublime.HOVER_TEXT) + + +class RustMessageStatus(sublime_plugin.ViewEventListener): + + """Display message under cursor in status bar.""" + + @classmethod + def is_applicable(cls, settings): + return (util.is_rust_view(settings) + and util.get_setting('rust_message_status_bar', False)) + + @classmethod + def applies_to_primary_view_only(cls): + return False + + def on_selection_modified_async(self): + # https://github.com/SublimeTextIssues/Core/issues/289 + # Only works with the primary view, get the correct view. + # (Also called for each view, unfortunately.) + active_view = self.view.window().active_view() + if active_view and active_view.buffer_id() == self.view.buffer_id(): + view = active_view + else: + view = self.view + messages.update_status(view) + + +class RustEventListener(sublime_plugin.EventListener): + + def on_activated_async(self, view): + # This is a workaround for this bug: + # https://github.com/SublimeTextIssues/Core/issues/2411 + # It would be preferable to use ViewEventListener, but it doesn't work + # on duplicate views created with Goto Anything. + if not util.active_view_is_rust(view=view): + return + if util.get_setting('rust_message_status_bar', False): + messages.update_status(view) + messages.draw_regions_if_missing(view) + + def on_query_context(self, view, key, operator, operand, match_all): + # Used by the Escape-key keybinding to dismiss inline phantoms. + if key == 'rust_has_messages': + try: + winfo = messages.WINDOW_MESSAGES[view.window().id()] + has_messages = not winfo['hidden'] + except KeyError: + has_messages = False + if operator == sublime.OP_EQUAL: + return operand == has_messages + elif operator == sublime.OP_NOT_EQUAL: + return operand != has_messages + return None + + class RustAcceptSuggestedReplacement(sublime_plugin.TextCommand): """Used for suggested replacements issued by the compiler to apply the diff --git a/docs/img/region_style_none.png b/docs/img/region_style_none.png index 35063747de46b5b5ba000ba2907a5d78c5ad6a5b..26ba51912f3edace7b0c2628e7bba506635f1106 100644 GIT binary patch literal 1980 zcmaJ?`8yMi1D-`WIcN!Smbt{Y1d=^P?Xm$&$Pfk&TEO^5Zp{UMX{!7q>M!i`9C3ExF!t%1pQF4Ski z`M>qurvg0M=9Dz%g5d@|Btk!ZL3e@uups+RH#>UmG6?wS!lf{eM zpZ}l0wt7LGe;_3TvG<@eSz*I<3h9izot~FzXLGC?#xnb5^{`vpiKjNMY~3GJWhlzF zEf^u{;H7_1Hw}K%vK*>gh@%rb(t9L^Dv@?X*^lRK)3u~9Bq={c5Jh}y!%jBDc~3Hs z>aCp_{6joj5~$+K`e5ti56^X8C%EkA&<6KS{ne&iAhqlKdOcHO!ZdF`M#hHzp+;Je z_086*=MQLg+(1)(&qTTK2$If~T~b?}t5Pk|WOclg0~EV8lneXjiy;VL6S<<2WfoS_ zu=Ld_ln3HXh)N&)%mG+q$oXJUJ@B#BTBsHnp0tfz=&B7WY^{#h8>*W9`T*Px!tlzMG8^GB>@2BTTgr;lwdFFAE!9@Vze)c_eRqU^TJ#7n)`ADN3#Rm`3A9Ua zJ6i9w7e!a|e`CRZT+v@edpYp%{+@de zX@(edy&0^;-dicrX$Tz%)F$Rm&G*=OhUS3VFzW2|e^X|y4=-fKT zd8Pgtf9pbOJ)p7Aj`L&dju8}?(n(650^FLpTmbhG@s&|Cj*mDh)cqEfYRy`U8#{Ed zcHj<`v80jTTsWIPs0w+Z6?(UgjIca$FD^jvw%luPTnEG?r%?PyOocyEA{7MeUe|g} zOh=PlP}95iBM}tzZ~#VaQFlZ4psK*@1+Z3?r36Fb%Xe8kS92%2*>tvyolngT7=FYx ztNU5<%*H9zTCc(;%CJ~M%+<&FbWEfYdVV|cKkPi~s+N%0doI^qFT^=bX7XS|lIG1> z)t}{C_@N+r1iU#jb2eqB**-9Qv#vK{Cowigm-|NALBydpNpHcoam<+#qbC{LXNw|E zM3H;a@ggqjbDl~&YwY}8gbLP9Zlpk?C6Q%tJY7z5CG$bP%2Lwgjjn7bKWvS88f{tD z*Ff+eUIzzB(4z1G(92Eu;WoC0%W&)*5Lcza{ccN~$kl(=Bee_Bk~m59h&dgZ_E8l- zyOQ9{Eswx{!aA8skeYre5sl@Z2MJ+8+eXtu7wgFX|X8v$=or3|6;;9yF5(lH#GNwbh-}$WOX6*L;@8C^r{|r9IzM>K(kdEcx9a@S4S%i|m?!Hx+3_^H_*#I-k zGFpytiBXW~6;a8B%`-3l#Jmoj9W(ED_X*%$h76zVy04)jl#YG-P#z7{KL43=R^IjY z%>D`h6%tp=comCj zQZKpz#ieHt`wa2an%V*PApG;Ly3w0qw3>nOnQPJF%Dz5-;qp@Ju-zbAY#9uR`DrOi zwR~hR=QkkCDXK1a3$g-7omMJt_nOtO{p7_%x!H%CG&{o2%rZ@}{1F&fb=5A3d$C^?27%&~xL% zf)m!91<)U#9L8jC=(haK=am$0hCk(#cdTQ_xfPA2`4#W%i_-DBuaEPh!r zf`t@M5QIFk9#eUbjrn0dt88edm|&9$G3=4T$&V6JAO)-c6-fKQv`9+-?R2FR2d@mU MFtstMFha-u7uV?5wg3PC literal 2569 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 V=;l$XV)_68002ovPDHLkV1n8_DU1LB literal 2617 zcmV-93dZ$`P)cH9>+hz5DEgtsECMwGYra0RCtQQVnIcMPKpQJFfGe+X?vK$L;mKV}$)HyLomX?;HD@XvF9bMNoo`@O&4 zz4v+OA(Ojn5fQO4tnRl1A|h=e3RA=)5rrvYk%+<+u}DN=idZC~Fhwj9$^CZV|B1r7 zm+rR<_Y}F!7v0vDC`>a^Soc!5PTc*VJNqc6ax?HCh!S)Wv%9i_*@P)XZ6rsTWBA({ zRb#HIEt1*1V1N!jgXRx*N}R%*WF01 zVa?)Q%818{y+jLw#PaDa_(OfDry6 zLdt5~y71ecmFfH?-G?Pxr!&l^ZJXB^hu7wX%p#s$i-UQ{d|nwaS(pP| z`k_|e09^6vtEH^s;yrNZ?vZ^y7TK6+RJmP?QXL^EUT1S;1-lYaI;;TKzO_#cDYB|EVZi3 z;a@9~I2SXAxdR)_?qP?*Y}`8*jUPQ{3`a z7B$icgb#i;q(JNWFJNyJWrT$}$R_(DOxTK$tVBpThV+eLhSe&Be$bRcQ&W(6HVsyT zBphM464~Uz&HFYX31Qnjq$c_RK1^Jy7#bi#2i$VSuNST?5klw$}xe#B$4Zku(xcN{`$I�JUkc+A5jIZ4 z!SEyyWLhNozqYDf@LQxG!)+kdvVX=&!??=FNt@ZqPVHtxQ6MofgIm>ew{$2>+1?rW z`M*efelN$6M!X-2JURf6xK+){8ziAz+L%Dt zFVj(s3BV(62Kk=Lk+TFCP_8J@aBh%XWQc5>BZA7o4!)|awVnQf#%8yGYQS&7g_6sm$6D;gxe6t7J} z?v%u1lO97B{S0I)I6QL-4(RuNsb>*V)4->J1LKpM_LuR2fDOXrBzV(n=jL;oQ2ayTZg&J`j%qWt5jo_`QEWd?@9&-%L%Fju17Pk^7cW zoKuY!kknK)vrs)X2I>;IxjmbD4M=KELDFFkhK*r>vEhx~JhAW-uHOP#-f^gQqlZiD zn9>%`-|YtX2>R$FsmgO4cpjo|Z+Rzt7>LIJEyd^Cm{9^ju0@jfOS7j9gMENPG%5+I zaZABI5$Vz|P}gYCILX1q4zC(D8bFruSDh$kZ39j-@>PF>C{&xXx!rpV#JUiRDhcO3vIsn64&LcD!JmBTf|u=s+^8c9iBlT!+Rs8&*Q;SSS3^ihJ8CtGb-TA@}$ zKNmbYvg;%rZpj~Bm&=in`dvdQB2o8w6r|-)Qbp6SIzm_oo_T91yCR3@hC;s~98b}=QCWdi&K9HzpYywMZczBc zRV7tddNga}!js{^VQP*WJq&spu4d_NZ9wkLh(aaB`r_aF4+pn$$SZI1%g2wPg1uaB zceM0di8I+;Z`bDD(xEV~&ly5b#TqsO@h^Tz>m5p4Xg~v6>!#_g zb$X<^NofgO{fXdLR<|0g5|i36g-ZDDd(z@#c=EG!gz$&3UHv24M%kIroZ5jIW7L*%ZQ4{I)5tFw$n9~?x|8U1dr<2XC3Lj#9UP#dobz|K z>NKXMat8<8E?4NL!l5^)EAMd4&=LVomvJC;0)1?C2aM9Svq94|8SW=Cz$p&mhtt!v z0N5pD#}k22ZbpoT8;M*rI=P$XgTVHz%k&=});e{0`$DO}4Gq-WIwOqwC;aVcM(&zQ z;Yol>sH~^xsj)cN-6VzIl z!?fEq7Z(Pp&v65wUthRf(Ww>UwS5*U&kY<;ehF#{8MS6Q2VVQwQ2tQWe{hTRg!|2Un6dL{BWFJ!Z zadzT(URUg3iQ!~!1#)k2lW{8bO}KR(ihdyYooI^xTcH=&C0tj(P2rNokhGwwz6+aq zB7T!@Y=H(z0C=gf+ED~FzrFT4um;trZDf3|gvwmPH@;3r)VEkwH%8$7uxA=+Q;)$^ zHHx2Bke@gXqNz~ntsEyPn++wAPC-pxrj@Yl+PqynOo_~2K1(^3;x!|`5 z5(Y8i&(kQb>e#-!Y3i#F&n73aK5G)eE6+50(O^thbs>Jl(P=8FShOmDNk z_0ZS_!CSEeAO4cxEKrn9*dWYI;3GCO?fv#ec{i9@n2{`e06yZdzxFMQUS7qCkT^y< znj_&vBBUrezjGafix*L|Hoc9Db<3fyS12EjE@J5$o5*VCg;wK79JL8yU24N@sr*6b zLHP8oJNqaK(~O8;u$vE2m?9R5c=!>qNJL?ZSR|q_MJy6gm?9R5C`=KHL=>ioMIs7Q b#3K10Bg*H(%?@<%00000NkvXXu0mjfGobY~ diff --git a/docs/img/region_style_solid_underline.png b/docs/img/region_style_solid_underline.png new file mode 100644 index 0000000000000000000000000000000000000000..3cc4ae67c218225d53e351d1184f1e5c0499e75f GIT binary patch literal 1995 zcmV;+2Q>JJP)S|zZlmDWvUVG*IBYt(KZ zrbL+vA>NudP1zQ0jgvO2_JL7M-sWzJSmLD%0me&ei*zZFfTOO8wsh@$_u=|p`}&Ow zIKi9FPeQ`?+r?HiPp7FjFB;yu-e(5Ei@*d>Jk`!}Yo|pFLEgmt>O}v5f4ozQQXvb-=p@@J|5qCT5~

LFl? zb=wQE*E+D(wL<7|x;uY6YXJnxJK*(oRCtzPuXSLrb#SBYROTjO$%oK5pQN*)W03!J zY)abp*(*EQOD#JOe?N0J#h_c5Yb<5}B%_PM0*Py5M{kQk^Tpm)iM_UpU~kTC=|8a6 z&E}Fx&itw)vC(D$##wZ3F#v|J7xo_q9^=yu|CrrCt5;whIl$!M`{r$PJd)7~j$F$6 zd$=A>{tl01bV9DgS1;^L+HMGU)3oC_mrK6Mp|zi7+{Q&S-4JMchg*%sKp0Bi698qt zX4Q*m`=4Un_9shbGA|~?{Zi8&D;y;Rj^%Dl2qXSVx4gi0C9wSj{>s=7G*oUWnCvjfvs=305uP5hnQP)ZO|=DRofh+mSN71~)tb zp>7uKl6RdU?1i2I#+H?9bF)yly6bJ{J_5}A)AlcYHk|nYOe?u})xYI2S5<@QBDs+< z_1RE})to$yNg4WV78DT#Uw##MkAkiHqU{A{^;hlYisq_{C?sPuRGO&ptW?)BL1b~c zqhgnNVmQvGIPu0~$;Ud<%V>&CrlG)^vIv?hGlp`PXNdNaNZ4_I$gRlrhx!M2#`}YW z0w5k`VVFpBlO-Qa#q@T%;u`uipJI2tA?(Ip>t+&tIheh-g}}%lpR9I5@2N-*cPzT_ zI&DaWEcEM%UZ~Uop)PKotAmZXU~*~42zAAT*a$WYg#N^qnN+T? zUIC%=(R&lTqioj$XT01XN43d1vM;6RW=bXQ1TzY=yS|7$Yu${x^YA+wXxQ{N(z93C zR`m+UbcAtTj88)7eBvJn7|qNGG8g*s*VIsM%pu^AczC~?pz_DkWy3IIQwE;0t_R{a zq{a)$&j%Wg!m=xe8w)ob_cjBeQH zYJk70cBvjTt>{wcph=k+%bXS>=&s7|nfjRiISgP3d!c_?&F|t13;^4aKZD7|7cVC} z8_gWHBZq)8rZ=nu{1Gps-v%|ODs?4>uDB4Y6ewlBW{nU(F5Q;a#-yQFVSExy79K!b zEJp8@1*M=CS!s@i^mj2;t!LJJRa1%M)JK1{qns~uw7^;3d}+SL$N z;c4M)&2F{b5O!m$1AnI*Dx=ROtBO;ht#skU>%)<;q8>`#B(HM0zFNK>jtWMv4MW}! zxa?d>?V4L0i>=HH%yD5_E*wVH)WeJI#X^FejnLH?eJ&<%mPzfcY)1|etZApa^O8EZ zv!#F6%z=^bs+F{LWFKRmC!vOzd*n6nS1R+APX$K9Iof`F--xNm6Hg8Rf>kwCo3C-v z+YKZ+rI-hXO#L&Oe5~|^?&G8XWn7*i-YociX49Dn@^MX`dW4m&NJbYer*`qeg{HK1 z#$0tFzWJF0?|thRbeW&#wYINkv|q{SgpJkutKkLYt~a)^i7s}u{^7RtpJ8D>#0 literal 0 HcmV?d00001 diff --git a/docs/img/region_style_squiggly_underline.png b/docs/img/region_style_squiggly_underline.png new file mode 100644 index 0000000000000000000000000000000000000000..432b83a00b2919671e2f89e25f8d43ea93334a45 GIT binary patch literal 2007 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 +### Popup Command +You can bind the `rust_message_popup` command to a keyboard shortcut to force +a popup to open if there is a message under the cursor. Example: + +```json +{"keys": ["f8"], "command": "rust_message_popup", "context": + [ + {"key": "selector", "operator":"equal", "operand": "source.rust"} + ] +} +``` + ## Phantom Themes The style of the phantom messages is controlled with the `rust_message_theme` @@ -81,6 +93,9 @@ outline. | Value | Example | Description | | :---- | :------ | :---------- | | `outline` | | Regions are highlighted with an outline. | +| `solid_underline` | | Solid underline. | +| `stippled_underline` | | Stippled underline. | +| `squiggly_underline` | | Squiggly underline. | | `none` | | Regions are not highlighted. | ## Gutter Images @@ -105,3 +120,4 @@ A few other settings are available for controlling messages: | :------ | :------ | :---------- | | `show_panel_on_build` | `true` | If true, an output panel is displayed at the bottom of the window showing the compiler output. | | `rust_syntax_hide_warnings` | `false` | If true, will not display warning messages. | +| `rust_message_status_bar` | `false` | If true, will display the message under the cursor in the window status bar. | diff --git a/rust/batch.py b/rust/batch.py index 8d5620c4..a7e7f9d7 100644 --- a/rust/batch.py +++ b/rust/batch.py @@ -1,5 +1,7 @@ """Classes used for aggregating messages that are on the same line.""" +from . import util + class MessageBatch: @@ -48,8 +50,8 @@ def _dismiss(self, window): # (user has to close and reopen the file). I don't know of any good # workarounds. for msg in self: - view = window.find_open_file(msg.path) - if view: + views = util.open_views_for_file(window, msg.path) + for view in views: view.erase_regions(msg.region_key) view.erase_phantoms(msg.region_key) diff --git a/rust/messages.py b/rust/messages.py index 151e6b8a..540e84b1 100644 --- a/rust/messages.py +++ b/rust/messages.py @@ -221,8 +221,8 @@ def clear_messages(window, soft=False): winfo = WINDOW_MESSAGES.pop(window.id(), {}) for path, batches in winfo.get('paths', {}).items(): - view = window.find_open_file(path) - if view: + views = util.open_views_for_file(window, path) + for view in views: for batch in batches: for msg in batch: view.erase_regions(msg.region_key) @@ -256,7 +256,18 @@ def messages_finished(window): def _draw_region_highlights(view, batch): - if util.get_setting('rust_region_style') == 'none': + region_style = util.get_setting('rust_region_style') + flags = sublime.DRAW_NO_FILL | sublime.DRAW_EMPTY + if region_style == 'none': + return + elif region_style == 'solid_underline': + flags |= sublime.DRAW_NO_OUTLINE | sublime.DRAW_SOLID_UNDERLINE + elif region_style == 'stippled_underline': + flags |= sublime.DRAW_NO_OUTLINE | sublime.DRAW_STIPPLED_UNDERLINE + elif region_style == 'squiggly_underline': + flags |= sublime.DRAW_NO_OUTLINE | sublime.DRAW_SQUIGGLY_UNDERLINE + + if batch.hidden: return # Collect message regions by level. @@ -295,13 +306,11 @@ def _draw_region_highlights(view, batch): scope = 'info' icon = util.icon_path(level) for key, region in regions[level]: - _sublime_add_regions( - view, key, [region], scope, icon, - sublime.DRAW_NO_FILL | sublime.DRAW_EMPTY) + _sublime_add_regions(view, key, [region], scope, icon, flags) -def message_popup(view, point, hover_zone): - """Displays a popup if there is a message at the given point.""" +def batches_at_point(view, point, hover_zone): + """Return a list of message batches at the given point.""" try: winfo = WINDOW_MESSAGES[view.window().id()] except KeyError: @@ -334,7 +343,12 @@ def filter_point(batch): return False batches = filter(filter_point, batches) + return list(batches) + +def message_popup(view, point, hover_zone): + """Displays a popup if there is a message at the given point.""" + batches = batches_at_point(view, point, hover_zone) if batches: theme = themes.THEMES[util.get_setting('rust_message_theme')] minihtml = '\n'.join(theme.render(view, batch, for_popup=True) for batch in batches) @@ -346,6 +360,25 @@ def filter_point(batch): point, max_width=max_width, on_navigate=on_nav) +STATUS_KEY = 'rust-msg-status' + + +def update_status(view): + """Display diagnostic messages in status bar under the cursor.""" + for r in view.sel(): + batches = batches_at_point(view, r.begin(), sublime.HOVER_TEXT) + if batches: + msg = batches[0].first() + view.set_status(STATUS_KEY, msg.text) + return + view.erase_status(STATUS_KEY) + + +def erase_status(view): + """Clear the status in the message bar.""" + view.erase_status(STATUS_KEY) + + def _click_handler(view, url, hide_popup=False): if url == 'hide': clear_messages(view.window(), soft=True) @@ -578,9 +611,13 @@ def redraw_all_open_views(window): return winfo['hidden'] = False for path, batches in winfo['paths'].items(): - view = window.find_open_file(path) - if view: - show_messages_for_view(view) + views = util.open_views_for_file(window, path) + if views: + for batch in batches: + # Phantoms seem to be attached to the buffer. + _show_phantom(views[0], batch) + for view in views: + _draw_region_highlights(view, batch) def show_messages_for_view(view): @@ -597,6 +634,20 @@ def show_messages_for_view(view): _draw_region_highlights(view, batch) +def draw_regions_if_missing(view): + try: + winfo = WINDOW_MESSAGES[view.window().id()] + except KeyError: + return + if winfo['hidden']: + return + batches = winfo['paths'].get(view.file_name(), []) + msgs = itertools.chain.from_iterable(batches) + if not any((view.get_regions(msg.region_key) for msg in msgs)): + for batch in batches: + _draw_region_highlights(view, batch) + + def _ith_iter_item(d, i): return next(itertools.islice(d, i, None)) @@ -620,7 +671,7 @@ def _advance_next_message(window, levels, wrap_around=False): batches = _ith_iter_item(paths.values(), path_idx) while batch_idx < len(batches): batch = batches[batch_idx] - if _is_matching_level(levels, batch.first()): + if not batch.hidden and _is_matching_level(levels, batch.first()): current_idx = (path_idx, batch_idx) win_info['batch_index'] = current_idx return current_idx @@ -660,7 +711,7 @@ def _advance_prev_message(window, levels, wrap_around=False): batches = _ith_iter_item(paths.values(), path_idx) while batch_idx >= 0: batch = batches[batch_idx] - if _is_matching_level(levels, batch.first()): + if not batch.hidden and _is_matching_level(levels, batch.first()): current_idx = (path_idx, batch_idx) win_info['batch_index'] = current_idx return current_idx @@ -1102,10 +1153,13 @@ def _save_batches(window, batches, msg_cb): path_batches.append(batch) for i, msg in enumerate(batch): msg.region_key = 'rust-%i' % (num + i,) - view = window.find_open_file(batch.path()) - if view: - _show_phantom(view, batch) - _draw_region_highlights(view, batch) - if msg_cb: - for msg in batch: - msg_cb(msg) + if not WINDOW_MESSAGES[wid]['hidden']: + views = util.open_views_for_file(window, batch.path()) + if views: + # Phantoms seem to be attached to the buffer. + _show_phantom(views[0], batch) + for view in views: + _draw_region_highlights(view, batch) + if msg_cb: + for msg in batch: + msg_cb(msg) diff --git a/rust/util.py b/rust/util.py index 08055f9a..93c55158 100644 --- a/rust/util.py +++ b/rust/util.py @@ -2,11 +2,12 @@ import sublime import textwrap -import threading -import time import os +PACKAGE_NAME = __package__.split('.')[0] + + def index_with(l, cb): """Find the index of a value in a sequence using a callback. @@ -98,6 +99,12 @@ def active_view_is_rust(window=None, view=None): return 'source.rust' in view.scope_name(0) +def is_rust_view(settings): + """Helper for use with ViewEventListener.""" + s = settings.get('syntax') + return (s == 'Packages/%s/RustEnhanced.sublime-syntax' % (PACKAGE_NAME,)) + + def get_cargo_metadata(window, cwd, toolchain=None): """Load Cargo metadata. @@ -138,7 +145,6 @@ def icon_path(level, res=None): if level not in ('error', 'warning', 'note', 'help', 'none'): return '' gutter_style = get_setting('rust_gutter_style', 'shape') - package_name = __package__.split('.')[0] if gutter_style == 'none': return '' else: @@ -147,4 +153,13 @@ def icon_path(level, res=None): else: res_suffix = '' return 'Packages/%s/images/gutter/%s-%s%s.png' % ( - package_name, gutter_style, level, res_suffix) + PACKAGE_NAME, gutter_style, level, res_suffix) + + +def open_views_for_file(window, file_name): + """Return all views for the given file name.""" + view = window.find_open_file(file_name) + if view is None: + return [] + + return [v for v in window.views() if v.buffer_id() == view.buffer_id()] diff --git a/tests/rust_test_common.py b/tests/rust_test_common.py index 0c721d4b..3c0af6c9 100644 --- a/tests/rust_test_common.py +++ b/tests/rust_test_common.py @@ -71,6 +71,12 @@ def setUp(self): # Override settings. self._original_settings = {} self.settings = sublime.load_settings('RustEnhanced.sublime-settings') + # Ensure all settings are at defaults. + defaults = sublime.load_resource('Packages/%s/RustEnhanced.sublime-settings' % ( + util.PACKAGE_NAME,)) + defaults = sublime.decode_value(defaults) + for key, value in defaults.items(): + self._override_setting(key, value) self._override_setting('show_panel_on_build', False) self._override_setting('cargo_build', {}) # Disable incremental compilation (first enabled in 1.24). It slows