Set my own syntax in Sublime Text

2019-02-15 13:41发布

问题:

Sometimes I use Sublime Text for writing reminders. I always use the same layout for this task which looks like this :

>Title
>>Subtitle
>>>Comment

> Title
>> ...

Where a > represent a tab character

So I'm wondering if it's possible to create my own syntax highlighting for this kinds of files, with one color for the title, another one for the subtitle and the regular color for the comment.

回答1:

Syntax highlighting is performed using .tmLanguage syntax definitions. They are formatted in Apple's XML-based PLIST format, although thanks to the excellent Sublime plugin PackageDev they can be written in JSON or in YAML, which I chose for its compactness, and the fact that I get sweet syntax highlighting with it using my theme*.

So, your syntax is very straightforward. You'll have three rules:

  1. Matching lines beginning with a single tab as "Title" lines
  2. Matching lines beginning with two tabs as "Subtitle" lines
  3. Matching lines beginning with three tabs as "Comment" lines.

Everything else will be displayed by Sublime as plain text.


Here it is, in YAML:

# [PackageDev] target_format: plist, ext: tmLanguage
---
name: Reminders
comment: Written for http://stackoverflow.com/q/25689365/1426065 by @MattDMo
scopeName: text.reminders
fileTypes: [todo]
uuid: 6B548E74-5E01-497A-B030-9D31131B7A70

patterns:
- name: text.title.reminders
  match: ^\t(?!\t+)(.*)

- name: text.subtitle.reminders
  match: ^\t\t(?!\t+)(.*)

- name: text.comment.reminders
  match: ^\t\t\t(.*)

Everything is pretty straightforward. The name is what shows up in the bottom right corner of Sublime, it was written by me, its base scope name is text.reminders, opening a file with the .todo extension will automatically apply this syntax, and the UUID is just a unique identifier. As I mentioned above, there are three patterns. One thing to keep in mind: this will only match if the line begins with a literal tab character, not space characters being inserted as a tab! That means you'll need to select View -> Indentation and make sure Indent Using Spaces is NOT checked. Just for good measure, select View -> Indentation -> Convert Indentation to Tabs as well. These settings can be applied just to "Reminders" views, I'll cover that later.


So we have our YAML, which is useless if you don't have PackageDev. However, translated to a PLIST it works much better:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>comment</key>
    <string>Written for http://stackoverflow.com/q/25689365/1426065 by @MattDMo</string>
    <key>fileTypes</key>
    <array>
        <string>todo</string>
    </array>
    <key>name</key>
    <string>Reminders</string>
    <key>patterns</key>
    <array>
        <dict>
            <key>match</key>
            <string>^\t(?!\t+)(.*)</string>
            <key>name</key>
            <string>text.title.reminders</string>
        </dict>
        <dict>
            <key>match</key>
            <string>^\t\t(?!\t+)(.*)</string>
            <key>name</key>
            <string>text.subtitle.reminders</string>
        </dict>
        <dict>
            <key>match</key>
            <string>^\t\t\t(.*)</string>
            <key>name</key>
            <string>text.comment.reminders</string>
        </dict>
    </array>
    <key>scopeName</key>
    <string>text.reminders</string>
    <key>uuid</key>
    <string>6B548E74-5E01-497A-B030-9D31131B7A70</string>
</dict>
</plist>

In Sublime, create a new file with XML syntax and copy the above XML into it. Find your Packages directory by selecting Preferences -> Browse Packages... then save this new file as Packages/User/Reminders.tmLanguage (make sure the L in tmLanguage is capitalized). There should now be a "Users -> Reminders" option in the language list in the bottom right of Sublime, or through the View -> Syntax menu option.


However, there's one more thing to do - get coloring. To do this you'll need to modify your .tmTheme color scheme file. Since you're using Sublime Text 2 (I assume), it's pretty easy. Open Preferences -> Settings-User and check the value of "color_scheme". Open the color scheme file by selecting File -> Open..., navigating to the Packages directory you found earlier with Preferences -> Browse Packages..., and opening the file in whatever subdirectory it's in. For example, if your settings file says "color_scheme": "Packages/Color Scheme - Default/Monokai.tmTheme" you'll navigate to the Packages/Color Scheme - Default directory in the open file dialog and open Monokai.tmTheme. Super easy.

Now that you have your color scheme file open, you can set the syntax to XML if you want, then scroll all the way down to the bottom. You'll want to insert your new colors (I'll get to that in a minute) above the lines that say:

    </array>
    <key>uuid</key>
    <string>06CD1FB2-A00A-4F8C-97B2-60E131912345</string>
</dict>
</plist>

The UUID may not even be there, it might just say:

    </array>
</dict>
</plist>

Regardless, whichever is there should always be the last lines in the file, or things will break. At any rate, above these lines, insert the following dicts:

    <dict>
        <key>name</key>
        <string>Reminders - Title</string>
        <key>scope</key>
        <string>text.reminders text.title.reminders</string>
        <key>settings</key>
        <dict>
            <key>fontStyle</key>
            <string>bold italic</string>
            <key>foreground</key>
            <string>#00FF00</string>
            <key>background</key>
            <string></string>
        </dict>
    </dict>
    <dict>
        <key>name</key>
        <string>Reminders - Subtitle</string>
        <key>scope</key>
        <string>text.reminders text.subtitle.reminders</string>
        <key>settings</key>
        <dict>
            <key>fontStyle</key>
            <string></string>
            <key>foreground</key>
            <string>#FF0080</string>
            <key>background</key>
            <string></string>
        </dict>
    </dict>
    <dict>
        <key>name</key>
        <string>Reminders - Comment</string>
        <key>scope</key>
        <string>text.reminders text.comment.reminders</string>
        <key>settings</key>
        <dict>
            <key>fontStyle</key>
            <string></string>
            <key>foreground</key>
            <string></string>
            <key>background</key>
            <string></string>
        </dict>
    </dict>

Feel free to customize the foreground and background colors and the fontStyle attributes ("bold" and "italic" are the only working values) however you like.


One more thing, if you'll remember - setting Sublime to use tabs just in Reminders views. Create a new file using JSON syntax, or if you installed PackageDev, with "Sublime Settings" syntax. Add to it the following:

{
    "translate_tabs_to_spaces": false,
    "extensions": 
    [
        "todo"
    ]
}

(Yes, I know "todo" is already in the language definition, I just like to be safe). You can add any other option here that's used in the Preferences -> Settings-Default and ... -> Settings-User files, just make sure the file is valid JSON. Save the file as Packages/User/Reminders.sublime-settings.


And that's it! You may need to restart Sublime for the changes to kick in, but then you're all set. To recap, the steps to making a new syntax definition are as follows:

  1. Get PackageDev, your life will be a lot easier.
  2. Write highlighting regexes in JSON or YAML format using the Oniguruma regex language, testing online with tools like Rubular (using Ruby 1.9.2 option).
  3. Compile to PLIST/XML.
  4. Modify color scheme for new scopes.
  5. Create a .sublime-settings file for syntax-specific settings like tabs.
  6. ???
  7. Profit!

* linked in my user profile if you're interested...