編寫插件解決Jetbrains系列軟件Ctrl + Alt + L 和搜狗輸入法Ctrl切換語言的沖突

編寫插件解決Jetbrains系列軟件Ctrl + Alt + L 和搜狗輸入法Ctrl切換語言的沖突

以下內(nèi)容只針對(duì)Windows平臺(tái)

問題

  • 搜狗輸入法提供了兩種語言切換快捷鍵,一個(gè)是Shift一個(gè)是Ctrl,作為一個(gè)碼農(nóng),Shift肯定是少不了會(huì)一直按的,拿它作為語言切換快捷鍵會(huì)存在一個(gè)問題,就是只要你稍微猶豫了一下,只按了Shift但是沒按其它鍵,那么搜狗就會(huì)切語言了,然而這肯定并非爾之所愿,所謂的猶豫就會(huì)敗北.所以我將搜狗默認(rèn)的Shift切換語言改成Ctrl了.不過感覺周圍好像就我一個(gè)人這個(gè)干,我就想知道選擇Shift鍵作為語言切換的同學(xué)的是怎么忍受Shift天天沒事亂切換語言的...
  • 然后涉及第二個(gè)習(xí)慣,在使用IDEA/Android Studio過程中,我老是喜歡按下Ctrl + Alt + L去對(duì)齊全文代碼,可能我已經(jīng)習(xí)慣了整齊舒適的感覺.不過這個(gè)習(xí)慣經(jīng)常遭來禍端,比如改了別人的代碼,然后他提交的時(shí)候就沖突了.當(dāng)然我會(huì)告訴他,你沒對(duì)齊還怪我嘍[手動(dòng)滑稽]

基于以上兩個(gè)習(xí)慣,沖突了.........

按下Ctrl + Alt + L會(huì)切換語言. WTF.

按下Ctrl + L或者其它鍵并不會(huì)切換語言,只要帶上Ctrl + Alt就會(huì)切.所以這特么就是一個(gè)搜狗N年都沒解決的Bug..........

可能還是因?yàn)槭褂肅trl鍵作為語言切換的人太少了,沒反饋過去.

能怎么辦呢?搜狗不解決那只有自己解了.

解決辦法

  • 在搜狗改成Shift切換語言 : 去死!
  • 修改IDEA的快捷鍵 : 有效,容易和其它快捷鍵重疊,試了好幾個(gè)都發(fā)現(xiàn)有重疊,然后我嘗試覆蓋了一個(gè)Ctrl + L,有用,但是我特么已經(jīng)習(xí)慣了Ctrl + Alt + L了..........每次都會(huì)習(xí)慣的用Ctrl + Alt + L,所以這個(gè)方案放棄了.
  • 對(duì)搜狗屏蔽Alt : 感覺像是系統(tǒng)層才能實(shí)現(xiàn)的方式,太難了,告辭.而且搜狗也有一些Ctrl + Alt的快捷鍵,比如截圖,所以屏蔽了也不好.
  • 用一個(gè)程序監(jiān)聽按鍵消息,當(dāng)發(fā)生Ctrl + ALt + * 的組合快捷鍵時(shí),當(dāng)所有按鍵彈起時(shí)再按一次Ctrl來將搜狗的語言恢復(fù)回去.

最終也就是最后一個(gè)方案是較為可行的.

實(shí)現(xiàn)方案

有了想法,如何實(shí)現(xiàn)呢?Java不能在后臺(tái)檢測(cè)按鍵,其必須要有焦點(diǎn),所以寫個(gè)Java程序在后臺(tái)跑是不現(xiàn)實(shí)的;然后C/C++是可以實(shí)現(xiàn)后臺(tái)監(jiān)聽按鍵的,但是作為一個(gè)菜雞表示對(duì)C/C++不是很熟,而且一直跑的必要性不是很高;那么就試試寫個(gè)IDEA的插件嘍,本身只是在IDEA使用Ctrl + Alt + L才有的沖突,在IDEA中處理完就行了.

實(shí)現(xiàn)過程

了解插件的編寫

IDEA插件的編寫資料很少,尤其是中文資料更少,或者說不詳細(xì).特么的都是抄襲,點(diǎn)開幾個(gè)網(wǎng)頁內(nèi)容都是一致的好煩,濫竽充數(shù)真沒意思......你說你要是記筆記記在有道云不好嗎?

找到一篇較為詳細(xì)的中文資料,有興趣可以了解下.

https://cloud.tencent.com/developer/article/1348741

創(chuàng)建工程

IDEA插件的創(chuàng)建方式有兩種,一種是IntelliJ Platform Plugin一種是Gradle.

作為一個(gè)Android開發(fā)者對(duì)Gradle有著莫名的好感.所以,一開始是使用的Gradle,結(jié)果它第一次使用要下載依賴.......下了半天沒啥動(dòng)靜....當(dāng)我把插件寫完了,它終于下載完了....

然后很無奈只好選擇另一種方式創(chuàng)建了.

創(chuàng)建完了里面大概就一個(gè)plugin.xml,配置一些插件信息和注冊(cè)一些組件,類似Android的AndroidManifest.xml.

此篇水文并不是教怎么寫插件的,只是記錄一個(gè)解決問題的過程,所以在此不對(duì)plugin.xml做說明,如果想了解插件編寫可以先看下上面鏈接的文章.

創(chuàng)建組件

IDEA常用的組件應(yīng)該是Action居多吧,它可以實(shí)現(xiàn)IDEA的菜單和快捷鍵調(diào)用,很方便,不過這里是一個(gè)監(jiān)聽操作,所以用不到Action.

這里我們需要做的是注冊(cè)一個(gè)ApplicationComponent,這個(gè)組件可以實(shí)現(xiàn)在IDEA打開的時(shí)候就初始化.然后我們需要的正是這個(gè)效果 : 在IDEA打開時(shí)注冊(cè)一個(gè)按鍵事件的監(jiān)聽回調(diào).

簡述其創(chuàng)建過程,如下.創(chuàng)建一個(gè)接口,繼承于ApplicationComponent,舉例:

package com.yxf.plugin;

import org.jetbrains.annotations.NotNull;

import com.intellij.openapi.components.ApplicationComponent;

public interface Component extends ApplicationComponent {

}

Java 8有了接口默認(rèn)實(shí)現(xiàn),所以不需要實(shí)現(xiàn)其方法,真好.

然后編寫一個(gè)類實(shí)現(xiàn)這個(gè)接口,舉例:

package com.yxf.plugin;
public class FixSouGouConflictComponent implements Component {

    @NotNull
    @Override
    public String getComponentName() {
        return "FixSgC.FixSouGouConflictComponent";
    }

    @Override
    public void initComponent() {

    }

    @Override
    public void disposeComponent() {

    }

獲取名稱,初始化,銷毀,實(shí)現(xiàn)此三個(gè)接口即可.

然后別忘了將組件注冊(cè)到plugin.xml中.

    <actions>
        <!-- Add your actions here -->
    </actions>

    <application-components>
        <component>
            <interface-class>com.yxf.plugin.Component</interface-class>
            <implementation-class>com.yxf.plugin.FixSouGouConflictComponent</implementation-class>
        </component>
    </application-components>

<application-components>這個(gè)標(biāo)簽?zāi)J(rèn)沒有,需要自己添加.

監(jiān)聽事件

然后如何實(shí)現(xiàn)按鍵事件的監(jiān)聽呢?中文網(wǎng)站沒搜索到這部分內(nèi)容,然后Google找到了一個(gè)線索,有個(gè)網(wǎng)友提到IdeEventQueue.addPostprocessor可以實(shí)現(xiàn).然后我針對(duì)addPostprocessor搜索了下沒發(fā)現(xiàn)啥.然后我想起了萬能的GayHub,呸,GitHub.一搜索發(fā)現(xiàn)了一個(gè)Kotlin的例子,可以取出其中的KeyEvent.然后根據(jù)其方法修改實(shí)現(xiàn):

    private IdeEventQueue.EventDispatcher mDispatcher = awtEvent -> {
        if (awtEvent instanceof KeyEvent) {
            return onKeyEvent((KeyEvent) awtEvent);
        }
        return false;
    };
    
    @Override
    public void initComponent() {
        IdeEventQueue queue = IdeEventQueue.getInstance();
        queue.addPostprocessor(mDispatcher, null);
    }
    
    private boolean onKeyEvent(KeyEvent event) {
        //................
        return false;
    }
    

有了KeyEvent就容易很多了,然后很容易就可以實(shí)現(xiàn)針對(duì)Ctrl + Alt + [*]快捷鍵的監(jiān)控,發(fā)生后再按下Ctrl鍵恢復(fù)語言.具體實(shí)現(xiàn)如下.

    private Robot mRobot;
    private Set<Integer> mKeyDownSet = new HashSet<Integer>();
    private boolean mTriggered = false;

    private boolean onKeyEvent(KeyEvent event) {
        int keyCode = event.getKeyCode();
        switch (event.getID()) {
            case KeyEvent.KEY_PRESSED:
                if (event.isControlDown() && event.isAltDown()) {
                    if (keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_ALT) {
                        return false;
                    }
                    mKeyDownSet.add(keyCode);
                    mTriggered = true;
                }
                break;
            case KeyEvent.KEY_RELEASED:
                if (keyCode != KeyEvent.VK_CONTROL && keyCode != KeyEvent.VK_ALT) {
                    mKeyDownSet.remove(keyCode);
                }
                if (mTriggered && mKeyDownSet.size() == 0 && !event.isControlDown() && !event.isAltDown()) {
                    mTriggered = false;
                    if (mRobot != null) {
                        mRobot.keyPress(KeyEvent.VK_CONTROL);
                        mRobot.delay(50);
                        mRobot.keyRelease(KeyEvent.VK_CONTROL);
                    }
                }
                break;
        }

        return false;
    }

其中Robot類是用于模擬鼠標(biāo)和鍵盤的.

至此邏輯就完了,寫一個(gè)簡單的插件實(shí)際上就和創(chuàng)建一個(gè)Android的Activity差不多.不過要實(shí)現(xiàn)復(fù)雜的插件,就像你想創(chuàng)建一個(gè)Dialog但是沒有文檔一樣難受.IDEA的插件編寫文檔也是特別少,其官方文檔寫的貌似也不怎么樣,講道理最好的學(xué)習(xí)方式真的就是去GitHub找源代碼看........

添加依賴

完了嗎?Naive,哪有這么簡單,這樣做出了的插件直接運(yùn)行沒問題,放在Android Studio中沒問題,但是非Java系列的軟件上就有問題了,比如Pycharm/Rider,這樣直接做出了的插件放到Pycharm根本沒用,但是特么也不報(bào)錯(cuò),也沒log..........

Google了一把沒多少有效信息,看到了一個(gè)關(guān)于說引用了python模塊無法在IDEA中使用的......然后懷疑是不是缺失了什么依賴.然后回去plugin.xml中尋找線索,發(fā)現(xiàn)這樣一段注釋

    <!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
         on how to target different products -->
    <!-- uncomment to enable plugin in all products
    <depends>com.intellij.modules.lang</depends>
    -->

這是引導(dǎo)我如何對(duì)他們不同的產(chǎn)品做處理,哇,這正是所需要的.貼下網(wǎng)址

http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html

其內(nèi)容大致是說每個(gè)產(chǎn)品依賴了不同的模塊,有些公共模塊,有些是IDEA/Android Studio才有的有些Pycharm才有的,諸如此類.......然后如果插件需要通用的話需要申明需要的模塊.這部分它官方文檔也沒給個(gè)例子,差評(píng).

而且國內(nèi)的IDEA插件開發(fā)資料基本上沒有這個(gè)信息.......

為了找個(gè)例子,繼續(xù)GitHub搜吧,搜索關(guān)鍵字com.intellij.modules找出一堆.........發(fā)現(xiàn)很多Pycharm的插件,確實(shí)都有加一些依賴.然后我將其所有編輯器共有的依賴項(xiàng)都加上去了,如下

    <!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
    <idea-version since-build="173.0"/>

    <!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
         on how to target different products -->
    <!-- uncomment to enable plugin in all products
    <depends>com.intellij.modules.lang</depends>
    -->
    <depends>com.intellij.modules.platform</depends>
    <depends>com.intellij.modules.lang</depends>
    <depends>com.intellij.modules.vcs</depends>
    <depends>com.intellij.modules.xml</depends>
    <depends>com.intellij.modules.xdebugger</depends>

    <extensions defaultExtensionNs="com.intellij">
        <!-- Add your extensions here -->
    </extensions>

重新編譯后放到Pycharm和Rider中運(yùn)行正常,只要使用Ctrl + Alt + [*]的快捷鍵,切換了語言又會(huì)馬上切換回來.

曲線救國成功\(^o^)/YES!

此插件已經(jīng)上傳至Jetbrains插件倉庫,所以可以在倉庫中直接搜索FixSgC安裝

源碼

FixSgC

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容