1. 程式人生 > >sublime - PackageDev自定義語法高亮規則

sublime - PackageDev自定義語法高亮規則

保存文件 char query variable point 修改 syn rac 測試

這幾天為sublime-syntax的語法簡直傷透了腦筋,網上能找到的教程都非常淺顯,而官方英文文檔的一時半會看不懂,中文文檔翻譯又實在糟心,糾結到最後還是下決心將英文文檔整個看了一遍,並翻譯成了我能看懂的文字。就以此作為我的第一篇正式博客吧!

使用步驟

1.安裝PackageDev插件

2.新建語法文件

Tools | Packages | Package Development | New Syntax Definition

3.定義語法高亮規則

見後文。

4.保存文件

保存完點擊 Tools | Build System | Convert to 編譯。
(感覺好像可以不用這一步,直接保存在user文件夾下就行,因為新版本已支持.sublime-syntax後綴文件。當然我也沒試過,只是猜想。)

5.修改語法文件

修改並保存可直接產生效果,不用等狀態欄100%。但最終要等100%才能退出sublime,以後會自動加載。

語法規則

name                ->  語法名稱。可選,如果不寫,將從文件名派生
file_extensions     ->  此語法應使用的文件擴展名
first_line_match    ->  沒有可識別的擴展名時,將對文件第一行進行匹配識別
scope               ->  分配給文件中所有文本的默認scope
hidden              ->  隱藏的語法定義,不會顯示在菜單中,但仍可以通過插件分配

Contexts

Contexts中通常有多種匹配文本

ctrl+alt+shift+p -> 查看當前文本所使用的scope

當內容能匹配多個context時,使用最左邊的一個;當多種context在同一位置生效時,使用先定義的一個。

push        ->  使用push入棧,將文本匹配切換到另一種context中,
            =>  剩下的將不再使用當前context,直到使用pop從棧中彈出
meta_scope  ->  為當前棧context的所有文本分配scope
pop         ->  從棧中彈出,繼續使用之前的scope
main        ->  每個語法必須定義一個main context, 它應用於文本最開始的位置.

Meta

meta_scope              ->  為當前棧context的所有文本分配scope, 包括觸發push和pop的文本
meta_content_scope      ->  同上,但是不應用於觸發push和pop的文本
meta_include_prototype  ->  false, 阻止當前文本自動應用prototype匹配.
clear_scopes            ->  移除當前棧中應用的scope,可以是整數或true. 
                        =>  應用於meta_scope和meta_content_scope之前. 
                        =>  通常僅在將一種語法嵌入另一種語法時使用。

meta 必須列在任何match或include之前。

Match

match               ->  用正則表達式來匹配文本。一次只針對一行文本匹配
                    =>  可以不使用‘‘,但當正則中有# : - { [ 或 > 時必須用引號
scope               ->  分配給匹配文本的著色類型
captures            ->  group數字對scope的映射,將匹配的內容按gruop分別著色
push                ->  推入堆棧的context,可以是context名及其列表,或者內聯的匿名context
pop                 ->  彈出堆棧中的當前context。此鍵唯一可接受的值是true
set                 ->  類似push,但先退出當前context,然後將給定的context推送到棧中
embed               ->  此鍵接受context名,類似push,但當escape時彈出所有嵌套的context。
                    =>  是將一種語法嵌入另一種語法的理想工具
    escape              ->  與embed配套使用,用於退出嵌入的context
                        =>  此內的所有反向引用與match中的group關聯
    embed_scope         ->  為escape前match的所有文本分配scope
    escape_capture      ->  escape中group數字對scope的映射,0表示所有匹配文本

push, pop, set 和 embed互斥。

Include

用於將一個context的內容包含在另一個context中。

例如註釋,可以使用include將其包含在內,而不用將它的內容復制到每個context中。

include的內容會被插入到當前位置,仍然可以通過調整插入位置來指定先後順序。

include的內容中定義的meta語句會被忽略。

prototype

對於諸如註釋之類的元素,經常會用include來使它們在每個context中自動插入,但每次都寫include太過繁瑣。

可以使用prototype來完成此操作,它自動將內容插入到每個context的頂部,除非在context頂部用meta_include_prototype: false用標明。

Including Other Files

Sublime Syntax 支持一個語法嵌套另一個語法。
例如HTML嵌套Javascript。

main:
  - match: <script>
    push: Packages/JavaScript/JavaScript.sublime-syntax
    with_prototype:
      - match: (?=</script>)
        pop: true

with_prototype      ->   類似prototype, 將匹配應用於上面引入的所有context中。 
                    =>   但是它會忽略meta_include_prototype設置。

此處(?=</script>),當匹配到上面的內容後跟</script>時將JS從棧中彈出,並按原HTML規則匹配

雖然.sublime-syntax 和.tmLanguage語法都支持,但是在此不能混用。

例2:HTML模板語言的實現

scope: text.jinja
contexts:
  main:
    - match: ""
      push: "Packages/HTML/HTML.sublime-syntax"
      with_prototype:
        - match: "{{"
          push: expr

  expr:
    - match: "}}"
      pop: true
    - match: \b(if|else)\b
      scope: keyword.control

與HTML中嵌入JavaScript不同的是,模板語言傾向於從內到外操作:默認使用HTML,只有某些特殊語句轉義為模板語言

match: "" 默認匹配所有的文本,通過with_prototype語句,當遇到 {{ ... }} 時使用新的匹配.

Variables

幾個正則表達式中有部分完全相同的情況並不少見,為避免重復,可以使用變量代替。

variables:
  ident: ‘[A-Za-z_][A-Za-z_0-9]*‘
contexts:
  main:
    - match: ‘\b{{ident}}\b‘
      scope: keyword.control

變量必須在.sublime-syntax文件頂層定義,並通過{{varname}}在正則表達式中引用。

Variables may themselves include other variables. Note that any text that doesn‘t match {{[A-Za-z0-9_]+}} won‘t be considered as a variable, so regexes can still include literal {{ characers, for example.

實例

嵌套循環匹配

例:括號自動匹配, 高亮顯示單獨的閉括號)

contexts:
  main:
    - match: \(
      push: brackets
    - match: \)
      scope: invalid.illegal.stray-bracket-end

  brackets:
    - match: \)
      pop: true
    - include: main

高級棧的使用

例:匹配typedef的兩種定義

typedef int coordinate_t;

typedef struct
{
    int x;
    int y;
} point_t;

匹配代碼:

main:
  - match: \btypedef\b
    scope: keyword.control.c
    set: [typedef_after_typename, typename]

typename:
  - match: \bstruct\b
    set:
      - match: "{"
        set:
          - match: "}"
            pop: true
  - match: \b[A-Za-z_][A-Za-z_0-9]*\b
    pop: true

typedef_after_typename:
  - match: \b[A-Za-z_][A-Za-z_0-9]*\b
    scope: entity.name.type
    pop: true

main中使用匹配將兩個context推入堆棧,最右邊的處於棧的最頂層,先匹配,當最右邊彈出後,才開始匹配左邊的。

為了簡潔起見,typename中使用了context的匿名寫法。

對正則表達式中group的引用

例:PHP與 Heredocs語法

contexts:
  main:
    - match: <<<([A-Za-z][A-Za-z0-9_]*)
      push: heredoc

  heredoc:
    - meta_scope: string.unquoted.heredoc
    - match: ^\1;
        pop: true

此處使用了 \1 符號代指之前匹配的group

語法測試

可以定義一個語法測試文本來自動檢測,而不需要手動通過show_scope_name命令檢測
具體見官方文檔。

關於scope的語法容日後再行研究

引用:

非官方文檔(推薦)
官方文檔
中文版百度快照

sublime - PackageDev自定義語法高亮規則