快轉到主要內容

Rider-重新格式化代碼及代碼清理

·914 字·5 分鐘
Art
作者
Art
這是我的技術筆記。

簡單紀錄重新格式化代碼、代碼清理的使用及設定方法

為了要讓團隊成員在撰寫代碼的時候有統一的格式,讓其他成員能夠迅速的瞭解程式碼,不因為個人風格的喜好而影響,通常我們會定義程式碼撰寫風格來讓團隊成員遵守,這個東西大概就是團隊成員一同遵守的Coding Style,像是EditorConfig或是大名鼎鼎的ESLint等等工具,其實都是在定義一系列的規則,輔助團隊透過各種支援的 IDE 達成程式碼風格的一致性

Rider 所提供的 Reformat Code 功能則是可以依據自行定義好的規則進行自動格式化,當然這些規則需要自己去設定裡面先定義好各種語言的 Code Style

設定的細節非常的多,尤其是 csharp, 就不一一截圖了,一般使用者應該也是跟我一樣直接使用預設值,然後針對結果再去做微調的動作

而呼叫使用的方式,則可以從 keymap 裡面去搜尋然後自行設定熱鍵

可以看見有三個執行的方式

reformat code
#

這個方式就只是最簡單的幫你把代碼重新格式化排版而已

reformat and Cleanup
#

採用這個方式的話他會再跳出視窗詢問你要套用哪一套規則來做 CleanUp 的動作

當然在檔案總管的部分也可以透過右鍵找到 reformat and cleanup的選項,一樣可以跳出視窗,如果規則都設定完成的話,可以一次將專案所有程式碼都先作一次 cleanup 然後 Commit,之後的開發 commit 內容應該也會比較乾淨些,更容易看到 Diff 的差異

silent reformat and cleanup
#

跟上面一個一樣,只是會需要你先指定一份設定檔,就不會再跳出視窗詢問了 設定的方式在 Editor->Code Cleanup

上面的三個是預設的,沒有辦法修改,如果需要自訂,則必須要透過新增,或複製現有的設定出來再自行調整,指定 silent 的預設,可以從上方的 ICON 來指定預設使用哪一份 cleanup 的設定,調整細節就透過右邊的 checkbox

引入團隊
#

如果想要將統一的風格推給團隊,我目前的首選還是透過EditorConfig來做這件事情,先把規則設定完畢,然後將.editorConfig commit 進去,然後再請團隊成員依據他們慣用的編輯器來將 EditorConfig 的環境準備好

支援的編輯器可以在EditorConfig 官網下方看到,以VSCode為例,需要安裝EditorConfig for VS Code這套件,但其實還需要本機安裝EditorConfig JavaScript Core才行

Rider 可以將設定資料匯出,應該有點用

我的使用習慣
#

實務上我用到 cleanup 的機會其實不多,使用預設的 Reformat Code 來做格式化,因為預設的就蠻符合我要的,不變動太多程式碼,然後將縮排弄好,然後在自己新開發的程式碼檔案,在 Commit 之前才會作一次 cleanup,但如果是在前端 vue.js SFC 的部分,就會蠻常使用的。

因為我覺得他 Vue.js cleanup 的效果還不錯,尤其是亂亂的 HTML 屬性夾雜 vue 語法,有個統一的格式在習慣之後,維護起來也很方便,調整 Import 的順序也很不錯。

這些都有微調的空間,如果還想深究的話,官方文件及動手嘗試是好辦法。

補充 FileLayout.xml
#

Editor->Code Style->C#->File Layout

在組織程式直線碼的部分可以透過設定 FileLayout.xml 來處理,直接參考人家寫好的範例再自行微調會比較簡單,雖然在 Resharper 裡面有 GUI 可以設定,然後再將 XML 搬來 Rider 使用也行,但直接看 XML 應該也可以理解,尤其是範例寫得還蠻清楚,照著跑一次比對一下結果應該也能理解。所以沒有很推 GUI 的設定介面。

修改完畢 XML 之後執行 Silent Reformat and Cleanup 就可以看到結果了。

<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
    <TypePattern DisplayName="Non-reorderable types">
        <TypePattern.Match>
            <Or>
                <And>
                    <Kind Is="Interface" />
                    <Or>
                        <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" />
                        <HasAttribute Name="System.Runtime.InteropServices.ComImport" />
                    </Or>
                </And>
                <Kind Is="Struct" />
                <HasAttribute Name="JetBrains.Annotations.NoReorderAttribute" />
                <HasAttribute Name="JetBrains.Annotations.NoReorder" />
            </Or>
        </TypePattern.Match>
    </TypePattern>

    <TypePattern DisplayName="xUnit.net Test Classes" RemoveRegions="All">
        <TypePattern.Match>
            <And>
                <Kind Is="Class" />
                <HasMember>
                    <And>
                        <Kind Is="Method" />
                        <HasAttribute Name="Xunit.FactAttribute" Inherited="True" />
                        <HasAttribute Name="Xunit.TheoryAttribute" Inherited="True" />
                    </And>
                </HasMember>
            </And>
        </TypePattern.Match>

        <Region Name="Setup/Teardown">
            <Entry DisplayName="Setup/Teardown Methods">
                <Entry.Match>
                    <Or>
                        <Kind Is="Constructor" />
                        <And>
                            <Kind Is="Method" />
                            <ImplementsInterface Name="System.IDisposable" />
                        </And>
                    </Or>
                </Entry.Match>

                <Entry.SortBy>
                    <Kind>
                        <Kind.Order>
                            <DeclarationKind>Constructor</DeclarationKind>
                        </Kind.Order>
                    </Kind>
                </Entry.SortBy>
            </Entry>
        </Region>

        <Entry DisplayName="All other members" />

        <Entry DisplayName="Test Methods" Priority="100">
            <Entry.Match>
                <And>
                    <Kind Is="Method" />
                    <Or>
                        <HasAttribute Name="Xunit.FactAttribute" Inherited="false" />
                        <HasAttribute Name="Xunit.TheoryAttribute" Inherited="false" />
                    </Or>
                </And>
            </Entry.Match>

            <Entry.SortBy>
                <Name />
            </Entry.SortBy>
        </Entry>
    </TypePattern>

    <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All">
        <TypePattern.Match>
            <And>
                <Kind Is="Class" />
                <Or>
                    <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="true" />
                    <HasAttribute Name="NUnit.Framework.TestFixtureSourceAttribute" Inherited="true" />
                    <HasMember>
                        <And>
                            <Kind Is="Method" />
                            <HasAttribute Name="NUnit.Framework.TestAttribute" Inherited="false" />
                            <HasAttribute Name="NUnit.Framework.TestCaseAttribute" Inherited="false" />
                            <HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" Inherited="false" />
                        </And>
                    </HasMember>
                </Or>
            </And>
        </TypePattern.Match>

        <Region Name="Setup/Teardown">
            <Entry DisplayName="Setup/Teardown Methods">
                <Entry.Match>
                    <And>
                        <Kind Is="Method" />
                        <Or>
                            <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="true" />
                            <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="true" />
                            <HasAttribute Name="NUnit.Framework.TestFixtureSetUpAttribute" Inherited="true" />
                            <HasAttribute Name="NUnit.Framework.TestFixtureTearDownAttribute" Inherited="true" />
                            <HasAttribute Name="NUnit.Framework.OneTimeSetUpAttribute" Inherited="true" />
                            <HasAttribute Name="NUnit.Framework.OneTimeTearDownAttribute" Inherited="true" />
                        </Or>
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Entry DisplayName="All other members" />

        <Entry DisplayName="Test Methods" Priority="100">
            <Entry.Match>
                <And>
                    <Kind Is="Method" />
                    <Or>
                        <HasAttribute Name="NUnit.Framework.TestAttribute" Inherited="false" />
                        <HasAttribute Name="NUnit.Framework.TestCaseAttribute" Inherited="false" />
                        <HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" Inherited="false" />
                    </Or>
                </And>
            </Entry.Match>

            <Entry.SortBy>
                <Name />
            </Entry.SortBy>
        </Entry>
    </TypePattern>

    <TypePattern DisplayName="MSTest Test Classes" RemoveRegions="All">
        <TypePattern.Match>
            <And>
                <Kind Is="Class" />
                <Or>
                    <HasAttribute Name="Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute" Inherited="true" />
                    <HasMember>
                        <And>
                            <Kind Is="Method" />
                            <HasAttribute Name="Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute" Inherited="false" />
                            <HasAttribute Name="Microsoft.VisualStudio.TestTools.UnitTesting.TestInitializeAttribute" Inherited="false" />
                        </And>
                    </HasMember>
                </Or>
            </And>
        </TypePattern.Match>

        <Region Name="Setup/Teardown">
            <Entry DisplayName="Setup/Teardown Methods">
                <Entry.Match>
                    <And>
                        <Kind Is="Method" />
                        <Or>
                            <HasAttribute Name="Microsoft.VisualStudio.TestTools.UnitTesting.TestInitializeAttribute" Inherited="false" />
                        </Or>
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Entry DisplayName="All other members" />

        <Entry DisplayName="Test Methods" Priority="100">
            <Entry.Match>
                <And>
                    <Kind Is="Method" />
                    <Or>
                        <HasAttribute Name="Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute" Inherited="false" />
                    </Or>
                </And>
            </Entry.Match>

            <Entry.SortBy>
                <Name />
            </Entry.SortBy>
        </Entry>
    </TypePattern>

    <TypePattern DisplayName="Default Pattern">
        <Region Name="PUBLIC CONSTANTS">
            <Entry DisplayName="Public Constants">
                <Entry.Match>
                    <And>
                        <Access Is="Public" />
                        <Kind Is="Constant" />
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="PUBLIC READONLY FIELDS">
            <Entry DisplayName="Public Readonly Fields">
                <Entry.Match>
                    <And>
                        <Access Is="Public" />
                        <Readonly />
                        <Kind Is="Field" />
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="PUBLIC STATIC FIELDS">
            <Entry DisplayName="Public Static Fields">
                <Entry.Match>
                    <And>
                        <Access Is="Public" />
                        <Static />
                        <Kind Is="Field" />
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="PUBLIC FIELDS">
            <Entry DisplayName="Public Fields">
                <Entry.Match>
                    <And>
                        <Access Is="Public" />
                        <Kind Is="Field" />
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="CONSTANTS">
            <Entry DisplayName="Constants">
                <Entry.Match>
                    <Kind Is="Constant" />
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="READONLY FIELDS">
            <Entry DisplayName="Readonly Fields">
                <Entry.Match>
                    <And>
                        <Readonly />
                        <Kind Is="Field" />
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="STATIC FIELDS">
            <Entry DisplayName="Static Fields">
                <Entry.Match>
                    <And>
                        <Static />
                        <Kind Is="Field" />
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="FIELDS">
            <Entry DisplayName="Fields">
                <Entry.Match>
                    <And>
                        <Kind Is="Field" />
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="PUBLIC ENUMS">
            <Entry DisplayName="Public Enums" Priority="100">
                <Entry.Match>
                    <And>
                        <Access Is="Public" />
                        <Kind Is="Enum" />
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="ENUMS">
            <Entry DisplayName="Enums" Priority="100">
                <Entry.Match>
                    <Kind Is="Enum" />
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="PUBLIC DELEGATES">
            <Entry DisplayName="Public Delegates" Priority="100">
                <Entry.Match>
                    <And>
                        <Access Is="Public" />
                        <Or>
                            <Kind Is="Delegate" />
                            <Kind Is="Event" />
                        </Or>
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="DELEGATES">
            <Entry DisplayName="Delegates" Priority="100">
                <Entry.Match>
                    <Or>
                        <Kind Is="Delegate" />
                        <Kind Is="Event" />
                    </Or>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="PUBLIC PROPERTIES">
            <Entry DisplayName="Public Properties">
                <Entry.Match>
                    <And>
                        <Access Is="Public" />
                        <Kind Is="Property" />
                    </And>
                </Entry.Match>
                <Entry.SortBy>
                    <Kind>
                        <Kind.Order>
                            <DeclarationKind>Autoproperty</DeclarationKind>
                        </Kind.Order>
                    </Kind>
                </Entry.SortBy>
            </Entry>
        </Region>

        <Region Name="PROPERTIES">
            <Entry DisplayName="Properties">
                <Entry.Match>
                    <Kind Is="Property" />
                </Entry.Match>
                <Entry.SortBy>
                    <Kind>
                        <Kind.Order>
                            <DeclarationKind>Autoproperty</DeclarationKind>
                        </Kind.Order>
                    </Kind>
                </Entry.SortBy>
            </Entry>
        </Region>

        <Region Name="INDEXERS">
            <Entry DisplayName="Indexers">
                <Entry.Match>
                    <Kind Is="Indexer" />
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="OPERATORS">
            <Entry DisplayName="Operators">
                <Entry.Match>
                    <Kind Is="Operator" />
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="CONSTRUCTORS">
            <Entry DisplayName="Constructors">
                <Entry.Match>
                    <Kind Is="Constructor" />
                </Entry.Match>
                <Entry.SortBy>
                    <Static/>
                </Entry.SortBy>
            </Entry>
        </Region>

        <Region Name="DESTRUCTORS">
            <Entry DisplayName="Destructors">
                <Entry.Match>
                    <Kind Is="Destructor" />
                </Entry.Match>
                <Entry.SortBy>
                    <Static/>
                </Entry.SortBy>
            </Entry>
        </Region>

        <Region Name="INTERFACE IMPLEMENTATIONS" Priority="100">
            <Region Name="${0}" Priority="100">
                <Region.GroupBy>
                    <ImplementsInterface Immediate="True" />
                </Region.GroupBy>
                <Entry DisplayName="Interface Members" Priority="100">
                    <Entry.Match>
                        <And>
                            <Kind Is="Member" />
                            <ImplementsInterface />
                        </And>
                    </Entry.Match>
                    <Entry.SortBy>
                        <ImplementsInterface Immediate="true" />
                    </Entry.SortBy>
                </Entry>
            </Region>
        </Region>

        <Region Name="PUBLIC METHODS">
            <Entry DisplayName="Public Methods">
                <Entry.Match>
                    <And>
                        <Access Is="Public" />
                        <Kind Is="Method" />
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="METHODS">
            <Entry DisplayName="Methods">
                <Entry.Match>
                    <Kind Is="Method" />
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="OTHER MEMBERS">
            <Entry DisplayName="All Other Members" />
        </Region>

        <Region Name="PUBLIC NESTED STRUCTS">
            <Entry DisplayName="Public Nested Structs">
                <Entry.Match>
                    <And>
                        <Access Is="Public" />
                        <Kind Is="Struct" />
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="NESTED STRUCTS">
            <Entry DisplayName="Nested STRUCTS">
                <Entry.Match>
                    <Kind Is="Struct" />
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="PUBLIC NESTED CLASSES">
            <Entry DisplayName="Public Nested Classes">
                <Entry.Match>
                    <And>
                        <Access Is="Public" />
                        <Kind Is="Class" />
                    </And>
                </Entry.Match>
            </Entry>
        </Region>

        <Region Name="NESTED CLASSES">
            <Entry DisplayName="Nested Classes">
                <Entry.Match>
                    <Kind Is="Class" />
                </Entry.Match>
            </Entry>
        </Region>
    </TypePattern>
</Patterns>

參考資源
#

  1. JetBrains Rider Document:Code cleanup
  2. Using the .editorconfig in VS2019 and VSCode
  3. 使用 EditorConfig 建立可攜式自訂編輯器設定
  4. EditorConfig for VS Code not working
  5. Jakob-PB/rider-csharp-filelayout.xml