From 3c468751db8f19f34cc543a755aebbb7438d065a Mon Sep 17 00:00:00 2001 From: Zhiming Ma Date: Fri, 16 Aug 2024 15:02:04 +0800 Subject: [PATCH] feat(eclipse): implement statusbar item sync. (#2896) --- clients/eclipse/feature/feature.xml | 4 +- clients/eclipse/plugin/META-INF/MANIFEST.MF | 2 +- clients/eclipse/plugin/images/check-dark.png | Bin 375 -> 0 bytes .../eclipse/plugin/images/check-dark@2x.png | Bin 713 -> 0 bytes clients/eclipse/plugin/images/check-light.png | Bin 373 -> 0 bytes .../eclipse/plugin/images/check-light@2x.png | Bin 715 -> 0 bytes clients/eclipse/plugin/images/check_tsk.png | Bin 0 -> 283 bytes .../eclipse/plugin/images/check_tsk@2x.png | Bin 0 -> 421 bytes clients/eclipse/plugin/images/hprio_tsk.png | Bin 0 -> 234 bytes .../eclipse/plugin/images/hprio_tsk@2x.png | Bin 0 -> 441 bytes .../eclipse/plugin/images/progress_task.png | Bin 0 -> 182 bytes .../plugin/images/progress_task@2x.png | Bin 0 -> 335 bytes clients/eclipse/plugin/images/warn_tsk.png | Bin 0 -> 465 bytes clients/eclipse/plugin/images/warn_tsk@2x.png | Bin 0 -> 1098 bytes .../src/com/tabbyml/tabby4eclipse/Images.java | 25 ++-- .../editor/InlineCompletionService.java | 12 +- .../tabby4eclipse/lsp/ConnectionProvider.java | 6 + .../tabby4eclipse/lsp/LanguageClientImpl.java | 12 +- .../lsp/protocol/ClientCapabilities.java | 22 ++- .../lsp/protocol/ILanguageServer.java | 5 +- .../lsp/protocol/IStatusService.java | 17 +++ ...eExt.java => ITextDocumentServiceExt.java} | 2 +- .../StatusIgnoredIssuesEditParams.java | 35 +++++ .../lsp/protocol/StatusInfo.java | 61 ++++++++ .../lsp/protocol/StatusRequestParams.java | 16 +++ .../statusbar/StatusInfoHolder.java | 53 +++++++ .../statusbar/StatusbarContribution.java | 131 ++++++++++++++++-- 27 files changed, 365 insertions(+), 38 deletions(-) delete mode 100644 clients/eclipse/plugin/images/check-dark.png delete mode 100644 clients/eclipse/plugin/images/check-dark@2x.png delete mode 100644 clients/eclipse/plugin/images/check-light.png delete mode 100644 clients/eclipse/plugin/images/check-light@2x.png create mode 100644 clients/eclipse/plugin/images/check_tsk.png create mode 100644 clients/eclipse/plugin/images/check_tsk@2x.png create mode 100644 clients/eclipse/plugin/images/hprio_tsk.png create mode 100644 clients/eclipse/plugin/images/hprio_tsk@2x.png create mode 100644 clients/eclipse/plugin/images/progress_task.png create mode 100644 clients/eclipse/plugin/images/progress_task@2x.png create mode 100644 clients/eclipse/plugin/images/warn_tsk.png create mode 100644 clients/eclipse/plugin/images/warn_tsk@2x.png create mode 100644 clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/IStatusService.java rename clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/{TextDocumentServiceExt.java => ITextDocumentServiceExt.java} (88%) create mode 100644 clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusIgnoredIssuesEditParams.java create mode 100644 clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusInfo.java create mode 100644 clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusRequestParams.java create mode 100644 clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusInfoHolder.java diff --git a/clients/eclipse/feature/feature.xml b/clients/eclipse/feature/feature.xml index 70ad69781b2d..3657b12090c0 100644 --- a/clients/eclipse/feature/feature.xml +++ b/clients/eclipse/feature/feature.xml @@ -2,7 +2,7 @@ @@ -19,6 +19,6 @@ + version="0.0.1.11"/> diff --git a/clients/eclipse/plugin/META-INF/MANIFEST.MF b/clients/eclipse/plugin/META-INF/MANIFEST.MF index 5825fe077637..e8ec68c66c56 100644 --- a/clients/eclipse/plugin/META-INF/MANIFEST.MF +++ b/clients/eclipse/plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Tabby Plugin for Eclipse Bundle-SymbolicName: com.tabbyml.tabby4eclipse;singleton:=true -Bundle-Version: 0.0.1.10 +Bundle-Version: 0.0.1.11 Bundle-Activator: com.tabbyml.tabby4eclipse.Activator Bundle-Vendor: com.tabbyml Require-Bundle: org.eclipse.ui, diff --git a/clients/eclipse/plugin/images/check-dark.png b/clients/eclipse/plugin/images/check-dark.png deleted file mode 100644 index e3dfdce7cb6aed97f096981b6a4555d530d0a4d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 375 zcmV--0f_#IP)Px$F-b&0R5(wilCes}KoEv!HxN53Ed?LJDww%RN(+&D0Pz8AtVLTNKsyUTM9|8@ z-pVAKjUjCi%?o%~q?Clqjwsx5-kqMg+xnkx{+<69u;3F5&cj@QAP81{-+%vF-Wrf) z*-BManwfWq=%p2)D2kz_vwAP)bX|8SC?l_L~HXi%%3sDpu0f0+H z-GNAJy>5&NrIcH)>poBG%n-oLLMatXDK8N5GD(u`AvCY7^_7nNR VcM*w0yR85K002ovPDHLkV1oEnpb!86 diff --git a/clients/eclipse/plugin/images/check-dark@2x.png b/clients/eclipse/plugin/images/check-dark@2x.png deleted file mode 100644 index 9ba3ed1ed0ba0cb49283420005023dfc5b694491..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 713 zcmV;)0yh1LP)Px%gGod|R9HvtmOp4zK@i5j+2wL#P!JR>EJUzSut+#A?{;6*AX->xVQHflb_#-j z@DBkIj38Rr*=T2FXGAUJ@OblH#KVw4sw=dxNI(d9NJw;V%*q8~lIPun^HP|tXJ>wI zX6O4JVVw`HbA4<8{68I-n3(9T*XtLS9m zABdXe+bp7yvW-ul8T> z&t+@vgI2|VujcZga=E-Im&*+k(N1gaxporx7-lYsNSTQ80G?*K`v*X!QrV%Dnj)ed zYl+5MKuMD9cFuhyqHO@)8DowRk!u%z)d-mR7=Sk-@;#T!9T*rG_>`7xY;0_+bFK|qg@xkK(9qA7Nkv4*as1Y6yNJwct@}%*(&sG2 zQvsT9!H;5(h>V0`cw=Q^X7>8-b$*q>WPwQCf+R`$opbNKjsrMst*tC2j-u!i5j{*Z zn2rE6U-vBlcf6vB$o|2>!C7xGMD!Yf0&v$_doxSHOkUuBB$$~O3kCpQDy6QpV=!5O zS_+Y0vqqya1z@v?)QM<|h*XtQ2h$9uqX5lOobdwFO#CIE&-WDyg|Ar(rNy@Fz_Qi6 vwx|y!t@Us#gUJHcHb9o7SzKP)Px$FG)l}R5(wiQn5N+a0fi1uhD?HUC%39K;r}uS74ZSY2XJu~U3~!E90Umu@0OQ75BPN!)a5z(QDT-J8L%vVJ8EF$|~+5OvrZQBP#wBUK(1`(CD1ekdf zz;2Qxi*Xz$)#iE*TCLWUVHmermaRom^i~Bha|ghAQ4}5D_h0&J|Na6q?*Ul)wAjNb zrRD*Id7iHZLGUPx%g-Jv~R9HvtmOW?_VI0PvCka#x3PK7F4k9=xI9Q5pg(mmD!C)O+Y9|-#;HDtr z2YwVpp@`t%=F)C1t&@t1+`X3&387-4(8)muhYo>)5)!!N{(6!?KN6d?X*$?j@819O zd;ee05j*^FhxM@w@c(2Wl}bfD&%0)fnOxa--O4~nDfODN&}) zBI5bJpGc?Exvl|dt;YdO1%e`S(HQfxYb8h~lV?Qa9e^-^CywLXS-W?(tpxFS{8%Is znFA07@WyeR^Fb220$^EIjO0AYLjV>^rP5$7mn${ex*gBJ$jC@fxm?bY>=%(G5jp9) z?ozXr-*Et?)GWzS07a6gZQEY>m)~?0Kq>W*fYyk9#$aFhrR{;zU5BEkQkwucdjh03u zAj`52ll(&RfQY1Hu~>3uW~SOI{JIioty2I#1Wi<_R8C|vnQzU44GawIk4B@P0UQPJ zwOB0n=kxiWt%|QJLH(do>IKOw0Ooz)Kjpe^Wo^*0v9WNWP_RiRL}Xb+`druj-i~-v zfS?ybq0l0LBO)?sjJdluuu^J@VagvtfIGGh0x=!+Mm$%w0 zn_5I#>-&EI9t9iC^StBPY<9V3Fz*3`06caa=RrF+Zr2NfABj&S2SwycX0?9UDXpGsXlH(vhcQUa(o3U4Y#T{08z^3NT&IlT3tk`k7Z^-anCqZ?#xk*mQ^BL5dp~jxIEXEuA<=hpz52NiR|2Qr zFG~whJk#8D(A@Wja-E^_EMfm;7rI$&OyPx$Ur9tkR9HvtR#9%kFbuWnMZ!(!5fI%P{@^6QNnl^3TMdt$o1g~;D0foPkLn*=3wh?Fa~s8?h#-`NL#ab-`zkA0zjW$ zH%PeUqC}XR7jqd5fd0@kMEdM1&Syy(Xcc#;PtJfoYlYwR^>=fCgDI+Ym+2*AU|E+; z`|S6<9E&RVIc^dFR`KRoRMUHQ|8M}7;v32^H*?br27rwQiS|ClC)Ge`+{7AS3OU$z ztI*1BDc&<~A^_J!tX}0>rZ0j0O-S*g7PM?L!)ZuF(s2_pa0`+uIcD6V0C4cKF4@Jr zt+=;fLYfh+%ik&fI zTUC9=En0-0R`79f!90`!do+PiL591KHzWZNlQbqasDYSdePW*gMrYs$DbUd&0=U8q P00000NkvXXu0mjfZSb_q literal 0 HcmV?d00001 diff --git a/clients/eclipse/plugin/images/hprio_tsk.png b/clients/eclipse/plugin/images/hprio_tsk.png new file mode 100644 index 0000000000000000000000000000000000000000..4e2910d5c9203f07a9739c7ffe2ecba5f0facbd1 GIT binary patch literal 234 zcmVG_|Es{bGibj9bPXn>{7JCOhXWak16uzNv9Alem!0upHZ`(HMWN#vyi2L}!yW+5*f zSlK!Kva-AO%i8YTFB=OV07*qoM6N<$f@>~ig#Z8m literal 0 HcmV?d00001 diff --git a/clients/eclipse/plugin/images/hprio_tsk@2x.png b/clients/eclipse/plugin/images/hprio_tsk@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fafd371e259ce235048b39806da9f5949d9e0028 GIT binary patch literal 441 zcmV;q0Y?6bP)288_o zQ(XH0Pg%wP-x1LuNBjeZ$ZT5so&{*hm*0*q|Nc}|{YS>Xz5GCq_({`H_-1Y{1NOad zz<&$}ghhg*;=6%`0?ix%GJMnlqYfC^4)||uECP0bk3UWa{5Lg~pm9lPap#x0_1{0m zB?t$AN<({>zrRf_9)k)inmXX8u~`7nr+lOdQy9knew*m`Bj1Li%H5 z<^!w~?!qe~WAkSqhta)C{;%!8^WVTgU<5WsOic}bnwV|>Wo+>Zm?%F1@qu54W+rsA z92oy!K=uD`PakkK{L{=5lnDQTT1oV1itPqj{wFgVQ3m7|z#MUe_6{&MfAQPH2YZ7A z7#(zRz%Mt?0dxQ)3jY5|PD3~#D+gpbouUAd2><``@cs`{qX||wgt?uX3hTZzK-*Fmm{0a-%rBK&0h}io>-fCH2c7B`Td3l9{hJ2Zd~g7 h|K!2NFQ*SMFyu4KEjEf5I0SSEgQu&X%Q~loCIEy3N>cy; literal 0 HcmV?d00001 diff --git a/clients/eclipse/plugin/images/progress_task@2x.png b/clients/eclipse/plugin/images/progress_task@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1c8333bbdb50e58b879faf1d1361af0b977cd307 GIT binary patch literal 335 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=hEVFp7D)IEGZ*dV9^+o2gLXcwu~( zzQ&Pmmjw4#gM)lc?KK+N;o(uD3%@*a-BtS5RANfgbiLRnIRQtNsPN#Fl%|~)j-TU` z-hTLh@VU+X$1F@$ARztPWYdG?|D`AFuniE}b?;}H{U4E^rVGAa2>F!G__m7g!rkm} zPF5bbK90MIjMK~HChRVm8gSrog_cu!L_@A9!`J)OUK$TFjf59k>l`rfXS}*=n-EvU zzZu;ZWW^?Ei!r{v`$lVFgZXpsmUkN(O8)UoxZW^vw*XMFLS+r#iR=R@`$RyTX@-oI zyai&~g1ZhgeY>pcaPiH_)5~@w`c9sz^+zReXM^+Uwkd_GKsz0ttl+6jVthD7^2_9Z f_Fuki+h_Xc!18?)i;k`X1{8y*tDnm{r-UW|6~mA3 literal 0 HcmV?d00001 diff --git a/clients/eclipse/plugin/images/warn_tsk.png b/clients/eclipse/plugin/images/warn_tsk.png new file mode 100644 index 0000000000000000000000000000000000000000..b900f3b909fcb9e3ef075c15369b37a0eab11ddc GIT binary patch literal 465 zcmV;?0WSWDP)st25rbJ=y-@;mXKeBx$}g&oBP*hSbkL z-)#H;=k50Y4^~8ezCAy{fjFnnci_0a!2kKzYm@%Zsj&FJw%hyv$Fpt!Z!ZWqPm}>S z=eQLJ7yPaVA@73!67pK_#JJ)Zv8m9p#I`w3Zx9LAW)ot?WbdSmB z(>-RNPxqRG>64wtU(fcM9X#G{pg=GxL5k4vsV;4a!I|j*^eFt&!ty({00000NkvXX Hu0mjf-M9*K literal 0 HcmV?d00001 diff --git a/clients/eclipse/plugin/images/warn_tsk@2x.png b/clients/eclipse/plugin/images/warn_tsk@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..97339df8e0b838f193d16ba178c81ae9136a5679 GIT binary patch literal 1098 zcmV-Q1hxB#P)DZ@Wg|?-&z5c$6vBWJkU6#qf&D5l1vLgSOkk9sQC5Cd z+S$5TXV6UMx}U@t8#mWl`bSxR_-9}T4w5kgu`sZY?>XNnCf=-F(HlJ)b2?h z{xTg55C6D{1)mw-j(k2J(xzuDYjHj8aOixRoq#EOBj7UuvnEV?RAAiD`BcxaCg^sAF637GM;z~^npx3#;` zUY&&deHzRTevW}JcKazl#k65J(5p%+?>(0U@0BCKL_0c8r=WDtvm6&HB%B+&b{tI8 zr%IHCefK?WuRrKfz5$=R0l2F{gv8fpaCBiIZ}E?)FKz74dMLmCU=(CcON_2AnOsnw+h>X!qGbFB#}5Cwz*Fm9YT> z+R`Y%Q^t7~AG^FCY>l&Zr~ZkGJ6grtx|_udPQ`2fIa@K{`Ur4-0NgHtP8t_5pp(WD zSIl`9UvQMbrI3KuqFXMOglwQ&zFqH-@4&sQ`+)8fKvw}^e}{04I({!I-dcl`StQre@U2bT(&b8!F1EGP?K zfa2YC7`vJwXo&x(?%wyQU^#5E=-_+7dV1S90e|YVDA@raIXeN{VSd!gqL%_apeT5e zj$Qa^c$}Y94rYJe3zma08MFaQ{VQOYzm@%{@DZViqllXAeJD?CSTayZ_kgp6^E}RU zOi$|;^8{_cR2MbfSsMl4jcj3OjzXc+*_+@g#l+15DBs%zMbSnKT|Xqeg2eMU(~Nem zknRCvmFR0jl?V*x`mL0Z8cG%oYa>GpAr{s%vBHzjqj}s+zxm=iLsdja=>LG|0x?!b zRNSf(O)e#TOjL@-4V9a!_2nB^hvlR6YmI0v9q03p1Evk>_FEY%Hq)$07*qoM6N<$f;^%SGynhq literal 0 HcmV?d00001 diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/Images.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/Images.java index fb919ed34b02..4f168ede67f8 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/Images.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/Images.java @@ -6,6 +6,7 @@ import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.RGB; import org.eclipse.ui.PlatformUI; @@ -14,22 +15,29 @@ public class Images { private static Bundle bundle = Platform.getBundle(Activator.PLUGIN_ID); private static Logger logger = new Logger("Images"); - + private static boolean isDark = isDarkTheme(); - private static Image iconCheck = null; + private static ImageRegistry icons = Activator.getDefault().getImageRegistry(); + + public static final String ICON_CHECK = "check_tsk.png"; + public static final String ICON_ERROR = "hprio_tsk.png"; + public static final String ICON_WARN = "warn_tsk.png"; + public static final String ICON_LOADING = "progress_task.png"; - public static Image getIconCheck() { - if (iconCheck == null) { - iconCheck = createImage(isDark ? "images/check-dark.png" : "images/check-light.png"); + public static Image getIcon(String filename) { + Image icon = icons.get(filename); + if (icon == null) { + icon = createImageFromFile(filename); + icons.put(filename, icon); } - return iconCheck; + return icon; } private static boolean isDarkTheme() { RGB bgColor = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry() .getRGB("org.eclipse.ui.workbench.ACTIVE_TAB_BG_START"); if (bgColor != null) { - boolean isBgDark = (bgColor.red + bgColor.green + bgColor.blue) / 3 < 128; + boolean isBgDark = (bgColor.red + bgColor.green + bgColor.blue) / 3 < 128; logger.info("Detected theme: " + (isBgDark ? "dark" : "light")); return isBgDark; } @@ -37,7 +45,8 @@ private static boolean isDarkTheme() { return false; } - private static Image createImage(String path) { + private static Image createImageFromFile(String filename) { + String path = "images/" + filename; URL url = FileLocator.find(bundle, new Path(path)); ImageDescriptor imageDesc = ImageDescriptor.createFromURL(url); return imageDesc.createImage(); diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/InlineCompletionService.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/InlineCompletionService.java index 9fafa27f79a6..6df323efcdd8 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/InlineCompletionService.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/InlineCompletionService.java @@ -33,7 +33,7 @@ import com.tabbyml.tabby4eclipse.lsp.LanguageServerService; import com.tabbyml.tabby4eclipse.lsp.protocol.ILanguageServer; import com.tabbyml.tabby4eclipse.lsp.protocol.InlineCompletionParams; -import com.tabbyml.tabby4eclipse.lsp.protocol.TextDocumentServiceExt; +import com.tabbyml.tabby4eclipse.lsp.protocol.ITextDocumentServiceExt; public class InlineCompletionService { public static InlineCompletionService getInstance() { @@ -114,9 +114,9 @@ public void provideInlineCompletion(ITextEditor textEditor, int offset, int offs } current = null; } - + ITextViewer textViewer = (ITextViewer) textEditor.getAdapter(ITextViewer.class); - + InlineCompletionContext.Request request = new InlineCompletionContext.Request(textEditor, offset); logger.debug("Request request: " + request.offset + "," + offsetInWidget); InlineCompletionParams params = request.toInlineCompletionParams(); @@ -126,7 +126,7 @@ public void provideInlineCompletion(ITextEditor textEditor, int offset, int offs } Function> jobFn = ( server) -> { - TextDocumentServiceExt textDocumentService = ((ILanguageServer) server).getTextDocumentServiceExt(); + ITextDocumentServiceExt textDocumentService = ((ILanguageServer) server).getTextDocumentServiceExt(); return textDocumentService.inlineCompletion(params); }; CompletableFuture job = LanguageServerService @@ -283,7 +283,7 @@ private ITextEditor getActiveEditor() { private boolean isActiveEditor(ITextEditor textEditor) { return textEditor == getActiveEditor(); } - + private class TriggerEvent { private ITextEditor textEditor; private long modificationStamp; @@ -376,7 +376,7 @@ private static int getDocumentOffset(ITextEditor textEditor, DocumentEvent event return event.getOffset() + event.getText().length(); } } - + private static long getDocumentModificationStamp(ITextEditor textEditor) { IDocument document = LSPEclipseUtils.getDocument(textEditor.getEditorInput()); if (document instanceof IDocumentExtension4 documentExt) { diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/ConnectionProvider.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/ConnectionProvider.java index 86e23d38e39c..76fffcc83351 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/ConnectionProvider.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/ConnectionProvider.java @@ -20,6 +20,7 @@ import com.tabbyml.tabby4eclipse.lsp.protocol.ClientCapabilities.TextDocumentClientCapabilities; import com.tabbyml.tabby4eclipse.lsp.protocol.ClientInfo; import com.tabbyml.tabby4eclipse.lsp.protocol.ClientInfo.TabbyPluginInfo; +import com.tabbyml.tabby4eclipse.statusbar.StatusInfoHolder; import com.tabbyml.tabby4eclipse.lsp.protocol.ClientProvidedConfig; import com.tabbyml.tabby4eclipse.lsp.protocol.InitializationOptions; @@ -44,6 +45,7 @@ public ConnectionProvider() { } } if (nodeExecutableFile == null) { + StatusInfoHolder.getInstance().setConnectionFailed(true); logger.error("Cannot find node executable."); return; } @@ -51,6 +53,7 @@ public ConnectionProvider() { Bundle bundle = Platform.getBundle(Activator.PLUGIN_ID); URL agentScriptUrl = FileLocator.find(bundle, new Path("tabby-agent/dist/node/index.js")); if (agentScriptUrl == null) { + StatusInfoHolder.getInstance().setConnectionFailed(true); logger.error("Cannot find tabby-agent script."); return; } @@ -61,6 +64,7 @@ public ConnectionProvider() { logger.info("Will use command " + commands.toString() + " to start Tabby language server."); this.setCommands(commands); } catch (IOException e) { + StatusInfoHolder.getInstance().setConnectionFailed(true); logger.error("Failed to setup command to start Tabby language server.", e); } } @@ -108,6 +112,8 @@ private ClientCapabilities getClientCapabilities() { textDocumentClientCapabilities.setInlineCompletion(true); TabbyClientCapabilities tabbyClientCapabilities = new TabbyClientCapabilities(); + tabbyClientCapabilities.setConfigDidChangeListener(false); + tabbyClientCapabilities.setStatusDidChangeListener(true); ClientCapabilities clientCapabilities = new ClientCapabilities(); clientCapabilities.setTextDocument(textDocumentClientCapabilities); diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/LanguageClientImpl.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/LanguageClientImpl.java index c997d4c0eb3c..8fefaf705f2e 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/LanguageClientImpl.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/LanguageClientImpl.java @@ -1,5 +1,15 @@ package com.tabbyml.tabby4eclipse.lsp; -public class LanguageClientImpl extends org.eclipse.lsp4e.LanguageClientImpl { +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import com.tabbyml.tabby4eclipse.lsp.protocol.StatusInfo; +import com.tabbyml.tabby4eclipse.statusbar.StatusInfoHolder; + +public class LanguageClientImpl extends org.eclipse.lsp4e.LanguageClientImpl { + @JsonNotification("tabby/status/didChange") + void statusDidChange(StatusInfo params) { + StatusInfoHolder.getInstance().setStatusInfo(params); + } } diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ClientCapabilities.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ClientCapabilities.java index 2d1213b1b237..8c6563a1dc0d 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ClientCapabilities.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ClientCapabilities.java @@ -50,7 +50,8 @@ public void setInlineCompletion(boolean inlineCompletion) { } public static class TabbyClientCapabilities { - private boolean agent; + private boolean configDidChangeListener; + private boolean statusDidChangeListener; private boolean workspaceFileSystem; private boolean dataStore; private boolean languageSupport; @@ -58,7 +59,8 @@ public static class TabbyClientCapabilities { private boolean editorOptions; public TabbyClientCapabilities() { - this.agent = false; + this.configDidChangeListener = false; + this.statusDidChangeListener = false; this.workspaceFileSystem = false; this.dataStore = false; this.languageSupport = false; @@ -66,12 +68,20 @@ public TabbyClientCapabilities() { this.editorOptions = false; } - public boolean getAgent() { - return agent; + public boolean getConfigDidChangeListener() { + return configDidChangeListener; } - public void setAgent(boolean agent) { - this.agent = agent; + public void setConfigDidChangeListener(boolean configDidChangeListener) { + this.configDidChangeListener = configDidChangeListener; + } + + public boolean getStatusDidChangeListener() { + return statusDidChangeListener; + } + + public void setStatusDidChangeListener(boolean statusDidChangeListener) { + this.statusDidChangeListener = statusDidChangeListener; } public boolean getWorkspaceFileSystem() { diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ILanguageServer.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ILanguageServer.java index 7d066b84507c..bf8184c93fe3 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ILanguageServer.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ILanguageServer.java @@ -5,5 +5,8 @@ public interface ILanguageServer extends LanguageServer { @JsonDelegate - TextDocumentServiceExt getTextDocumentServiceExt(); + ITextDocumentServiceExt getTextDocumentServiceExt(); + + @JsonDelegate + IStatusService getStatusService(); } diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/IStatusService.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/IStatusService.java new file mode 100644 index 000000000000..72343619cd62 --- /dev/null +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/IStatusService.java @@ -0,0 +1,17 @@ +package com.tabbyml.tabby4eclipse.lsp.protocol; + +import java.util.concurrent.CompletableFuture; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; + +@JsonSegment("tabby") +public interface IStatusService { + @JsonRequest("status") + CompletableFuture getStatus(StatusRequestParams params); + + @JsonRequest("status/showHelpMessage") + CompletableFuture showHelpMessage(Object params); + + @JsonRequest("status/ignoredIssues/edit") + CompletableFuture editIngoredIssues(StatusIgnoredIssuesEditParams params); +} diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/TextDocumentServiceExt.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ITextDocumentServiceExt.java similarity index 88% rename from clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/TextDocumentServiceExt.java rename to clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ITextDocumentServiceExt.java index 12b73c3e69f0..ecd67941fe92 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/TextDocumentServiceExt.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ITextDocumentServiceExt.java @@ -5,7 +5,7 @@ import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; @JsonSegment("textDocument") -public interface TextDocumentServiceExt { +public interface ITextDocumentServiceExt { @JsonRequest CompletableFuture inlineCompletion(InlineCompletionParams params); } diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusIgnoredIssuesEditParams.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusIgnoredIssuesEditParams.java new file mode 100644 index 000000000000..53572627b3c3 --- /dev/null +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusIgnoredIssuesEditParams.java @@ -0,0 +1,35 @@ +package com.tabbyml.tabby4eclipse.lsp.protocol; + +public class StatusIgnoredIssuesEditParams { + private String operation; + private String[] issues; + + public StatusIgnoredIssuesEditParams() { + } + + public String getOperation() { + return operation; + } + + public void setOperation(String operation) { + this.operation = operation; + } + + public String[] getIssues() { + return issues; + } + + public void setIssues(String[] issues) { + this.issues = issues; + } + + public static class Operation { + public static final String ADD = "add"; + public static final String REMOVE = "remove"; + public static final String REMOVE_ALL = "removeAll"; + } + + public static class StatusIssuesName { + public static final String COMPLETION_RESPONSE_SLOW = "completionResponseSlow"; + } +} diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusInfo.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusInfo.java new file mode 100644 index 000000000000..0f496c8149a4 --- /dev/null +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusInfo.java @@ -0,0 +1,61 @@ +package com.tabbyml.tabby4eclipse.lsp.protocol; + +import java.util.Map; + +import org.eclipse.lsp4j.Command; + +public class StatusInfo { + private String status; + private String tooltip; + private Map serverHealth; + private Command command; + + public StatusInfo() { + this.status = Status.NOT_INITIALIZED; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getTooltip() { + return tooltip; + } + + public void setTooltip(String tooltip) { + this.tooltip = tooltip; + } + + public Map getServerHealth() { + return serverHealth; + } + + public void setServerHealth(Map serverHealth) { + this.serverHealth = serverHealth; + } + + public Command getCommand() { + return command; + } + + public void setCommand(Command command) { + this.command = command; + } + + public static class Status { + public static final String NOT_INITIALIZED = "notInitialized"; + public static final String FINALIZED = "finalized"; + public static final String CONNECTING = "connecting"; + public static final String UNAUTHORIZED = "unauthorized"; + public static final String DISCONNECTED = "disconnected"; + public static final String READY = "ready"; + public static final String READY_FOR_AUTO_TRIGGER = "readyForAutoTrigger"; + public static final String READY_FOR_MANUAL_TRIGGER = "readyForManualTrigger"; + public static final String FETCHING = "fetching"; + public static final String COMPLETION_RESPONSE_SLOW = "completionResponseSlow"; + } +} diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusRequestParams.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusRequestParams.java new file mode 100644 index 000000000000..d0f08866f567 --- /dev/null +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusRequestParams.java @@ -0,0 +1,16 @@ +package com.tabbyml.tabby4eclipse.lsp.protocol; + +public class StatusRequestParams { + private boolean recheckConnection; + + public StatusRequestParams() { + } + + public boolean getRecheckConnection() { + return recheckConnection; + } + + public void setRecheckConnection(boolean recheckConnection) { + this.recheckConnection = recheckConnection; + } +} diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusInfoHolder.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusInfoHolder.java new file mode 100644 index 000000000000..3e05961e371c --- /dev/null +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusInfoHolder.java @@ -0,0 +1,53 @@ +package com.tabbyml.tabby4eclipse.statusbar; + +import java.util.ArrayList; +import java.util.List; + +import com.tabbyml.tabby4eclipse.lsp.protocol.StatusInfo; + +public class StatusInfoHolder { + public static StatusInfoHolder getInstance() { + return LazyHolder.INSTANCE; + } + + private static class LazyHolder { + private static final StatusInfoHolder INSTANCE = new StatusInfoHolder(); + } + + private boolean isConnectionFailed = false; + private StatusInfo statusInfo = new StatusInfo(); + private List listeners = new ArrayList<>(); + + public boolean isConnectionFailed() { + return isConnectionFailed; + } + + public void setConnectionFailed(boolean isConnectionFailed) { + if (this.isConnectionFailed == isConnectionFailed) { + return; + } + this.isConnectionFailed = isConnectionFailed; + for (StatusDidChangeListener listener : listeners) { + listener.statusDidChange(); + } + } + + public StatusInfo getStatusInfo() { + return statusInfo; + } + + public void setStatusInfo(StatusInfo statusInfo) { + this.statusInfo = statusInfo; + for (StatusDidChangeListener listener : listeners) { + listener.statusDidChange(); + } + } + + public void addStatusDidChangeListener(StatusDidChangeListener listener) { + listeners.add(listener); + } + + public interface StatusDidChangeListener { + void statusDidChange(); + } +} diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusbarContribution.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusbarContribution.java index f2b20c4f48ca..325398f11f1f 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusbarContribution.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusbarContribution.java @@ -1,36 +1,143 @@ package com.tabbyml.tabby4eclipse.statusbar; -import org.eclipse.swt.graphics.Image; +import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.ExecuteCommandParams; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; -import org.eclipse.swt.custom.CLabel; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.ui.ISharedImages; -import org.eclipse.ui.PlatformUI; import org.eclipse.ui.menus.WorkbenchWindowControlContribution; import com.tabbyml.tabby4eclipse.Images; +import com.tabbyml.tabby4eclipse.lsp.LanguageServerService; +import com.tabbyml.tabby4eclipse.lsp.protocol.StatusInfo; public class StatusbarContribution extends WorkbenchWindowControlContribution { - private CLabel label; + private static final String TOOLTIP_INITIALIZATION_FAILED = "Tabby: Initialization Failed"; + + private StatusInfoHolder statusInfoHolder = StatusInfoHolder.getInstance(); @Override protected Control createControl(Composite parent) { - label = new CLabel(parent, 0); + CLabel label = new CLabel(parent, 0); label.setText("Tabby"); - label.setToolTipText("Tabby"); label.addMouseListener(new MouseAdapter() { @Override public void mouseUp(MouseEvent e) { - Menu menu = new Menu(label); - MenuItem item = new MenuItem(menu, 0); - item.setText("Tabby plugin is active."); + Menu menu = createMenu(label); menu.setVisible(true); } }); + + updateLabel(label); + + StatusInfoHolder.getInstance().addStatusDidChangeListener(() -> { + label.getDisplay().asyncExec(() -> { + updateLabel(label); + }); + }); + return label; } + + private void updateLabel(CLabel label) { + if (statusInfoHolder.isConnectionFailed()) { + label.setImage(Images.getIcon(Images.ICON_ERROR)); + label.setToolTipText(TOOLTIP_INITIALIZATION_FAILED); + } + StatusInfo statusInfo = statusInfoHolder.getStatusInfo(); + if (statusInfo.getTooltip() != null) { + label.setToolTipText(statusInfo.getTooltip()); + } else { + label.setToolTipText("Tabby: " + statusInfo.getStatus()); + } + switch (statusInfo.getStatus()) { + case StatusInfo.Status.NOT_INITIALIZED: + label.setImage(Images.getIcon(Images.ICON_LOADING)); + break; + case StatusInfo.Status.CONNECTING: + label.setImage(Images.getIcon(Images.ICON_LOADING)); + break; + case StatusInfo.Status.UNAUTHORIZED: + label.setImage(Images.getIcon(Images.ICON_WARN)); + break; + case StatusInfo.Status.DISCONNECTED: + label.setImage(Images.getIcon(Images.ICON_ERROR)); + break; + case StatusInfo.Status.READY: + label.setImage(Images.getIcon(Images.ICON_CHECK)); + break; + case StatusInfo.Status.FETCHING: + label.setImage(Images.getIcon(Images.ICON_LOADING)); + break; + case StatusInfo.Status.COMPLETION_RESPONSE_SLOW: + label.setImage(Images.getIcon(Images.ICON_WARN)); + break; + default: + break; + } + } + + private Menu createMenu(CLabel label) { + Menu menu = new Menu(label); + MenuItem statusItem = new MenuItem(menu, 0); + + if (statusInfoHolder.isConnectionFailed()) { + statusItem.setImage(Images.getIcon(Images.ICON_ERROR)); + statusItem.setText(TOOLTIP_INITIALIZATION_FAILED); + } + + StatusInfo statusInfo = statusInfoHolder.getStatusInfo(); + if (statusInfo.getTooltip() != null) { + statusItem.setText(statusInfo.getTooltip()); + } else { + statusItem.setText("Tabby: " + statusInfo.getStatus()); + } + Command commmand = statusInfo.getCommand(); + if (commmand != null) { + statusItem.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + LanguageServerService.getInstance().getServer().execute((server) -> { + ExecuteCommandParams params = new ExecuteCommandParams(); + params.setCommand(commmand.getCommand()); + params.setArguments(commmand.getArguments()); + server.getWorkspaceService().executeCommand(params); + return null; + }); + } + }); + } + switch (statusInfo.getStatus()) { + case StatusInfo.Status.NOT_INITIALIZED: + statusItem.setImage(Images.getIcon(Images.ICON_LOADING)); + break; + case StatusInfo.Status.CONNECTING: + statusItem.setImage(Images.getIcon(Images.ICON_LOADING)); + break; + case StatusInfo.Status.UNAUTHORIZED: + statusItem.setImage(Images.getIcon(Images.ICON_WARN)); + break; + case StatusInfo.Status.DISCONNECTED: + statusItem.setImage(Images.getIcon(Images.ICON_ERROR)); + break; + case StatusInfo.Status.READY: + statusItem.setImage(Images.getIcon(Images.ICON_CHECK)); + break; + case StatusInfo.Status.FETCHING: + statusItem.setImage(Images.getIcon(Images.ICON_LOADING)); + break; + case StatusInfo.Status.COMPLETION_RESPONSE_SLOW: + statusItem.setImage(Images.getIcon(Images.ICON_WARN)); + break; + default: + break; + } + return menu; + } }