Sublime Forum

Add syntax highlighting on top of JS syntax hightlighting

#1

Hi there!

After using Sublime for about five years, I am finally getting serious about customizing it. I was trying to set up syntax highlighting for ExtendScript, which is basically Adobe’s dialect of ES3 JavaScript and comes with tons of extra classes and their methods and properties.

Since it is based on JavaScript, the idea is to use Sublime’s default JavaScript hightlighting and then add the additional highlighting of the ExtendScript classes on top.

Now, I found a ExtendScript .tmLanguage file for Visual Studio code here that should do exactly that.

Using Sublime, I converted this to a .sublimeSyntax file and saved it into my user package.

While it gets detected and I can select it in the syntax settings it does not apply any new Syntax highlighting to my files, it just stays at the default JavaScript highlighting. Also, when I move my caret to a ExtendScript class name and check the scope, it does not show me anything related to ExtendScript.

Does anybody know what I am doing wrong / what is missing?

Here is the file that I have (shortening the class / properties lists for better readability).

%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: ExtendScript
file_extensions:
  - jsx
  - jsxinc
scope: source.jsx
contexts:
  main:
    - include: scope:source.js
    - match: \b(File|Folder|$)\b
      comment: This is js core
      scope: support.class.jsx
    - match: \b(Bounds|Button|Checkbox)\b
      comment: This is js script ui
      scope: support.class.jsx
    - match: \b(update|remove|createPackage)\b
      comment: This is ID methods
      scope: support.class.jsx
    - match: \b(activeDocument|pages|documentOffset)\b
      comment: This is ID properties
      scope: support.class.jsx
    - match: \b(Assignment|assignments|AssignedStory)\b
      comment: This is ID classes
      scope: support.class.jsx
    - match: \b(Application|AVLayer|BlendingMode)\b
      comment: this is AE
      scope: support.class.jsx

Thanks!

0 Likes

#2

Ok, correction on one point, the source.jsx scope shows up in my file once I assign the ExtendScript syntax. But it does so globally.

And I was expecting for the support.class.jsx scope to show up somewhere, but that does not happen. What am I doing wrong? I am still hoping extending the Javascript hightlighting with a few extra classes will not be so difficult.

0 Likes

#3

Unfortunately, it’s not quite that simple. The JavaScript highlighter doesn’t just look for individual words and symbols; in order to provide accurate highlighting, it has to parse the file in detail. As a result, you can’t just add extra stuff onto the main context.

What you’re going to have to do is copy the entire JavaScript syntax and add your code in the right spot. In this case, the right spot is the support context. You can see the various contexts below that provide the kind of highlighting you want to add. What you want to do is create a new context and include it in the support context, like so:

support:
  - include: support-variable-extendscript
  - include: support-variable-ecma
  - include: support-variable-console
  - include: support-variable-dom
  - include: support-variable-node

support-variable-extendscript:
  - match: (?:File|Folder){{identifier_break}}
    scope: support.class.js
    pop: true
  - match: (?:Bounds|Button|Checkbox){{identifier_break}}
    scope: support.class.js
    pop: true
  - match: (?:update|remove|createPackage){{identifier_break}}
    scope: support.class.js
    pop: true
  - match: (?:activeDocument|pages|documentOffset){{identifier_break}}
    scope: support.class.js
    pop: true
  - match: (?:Assignment|assignments|AssignedStory){{identifier_break}}
    scope: support.class.js
    pop: true
  - match: (?:Application|AVLayer|BlendingMode){{identifier_break}}
    scope: support.class.js
    pop: true

Notes:

  • I recommend installing PackageDev if you haven’t already. It provides special syntax highlighting for .sublime-syntax files, including highlighting regexps in match rules.
  • You can change the top-level scope from .js to .jsx in the header, as you did in the code you posted. You can also use the support.class.jsx scope, but most of the scopes will still end in .js. You can simply search-and-replace this if you like.
  • I’m not sure what the $ is for in - match: \b(File|Folder|$)\b. If it’s supposed to represent an identifier made of a dollar sign, then you have to escape it in the regexp (because $ is a special character in regexps).
  • Because of how the JavaScript syntax is implemented, you don’t have to use a \b at the beginning of an identifier match. At the end, use {{identifier_break}} instead of \b — some characters like the dollar sign are valid JavaScript identifier characters but not regexp word characters.

I wrote the JS Custom package to allow for extending and customizing the JavaScript syntax, but it doesn’t currently do what you want here. Adding user-defined support scopes is near the top of my to-do list.

2 Likes

#4

Thanks @ThomSmith or looking into this, much appreciated.

The longer I was digging around yesterday, the more I was suspecting that this is what I would have to do.

Thanks for all the pointers, I will give this a try later tonight and might return with some follow up questions.

Cool, looking forward to this!

0 Likes

#5

@ThomSmith Thanks again for your help, I finally found some time to look into this again and got the basics to work. However, I do have a follow-up question.

Following your recommendations and copying some stuff I found in the file I have structured my code like this now:

support:
    - include: support-variable-extendscript
    - include: support-variable-ecma
    - include: support-variable-console
    # ...

    support-variable-extendscript:
    - match: (?:File|Folder|\$){{identifier_break}}
      scope: support.class.jsx
      pop: true

    # enum classes
    - match: (?:AcrobatCompatibility|AddPageOptions|AdornmentOverprint
    # ... many more classes here
    ){{identifier_break}}
       scope: support.class.jsx
       set:
        - match: '{{dot_accessor}}'
          scope: punctuation.accessor.jsx
          set:
            - include: support-property-extendscript-enum
            - include: object-property
            - include: else-pop
        - include: else-pop
   
    support-property-extendscript-enum:
      - match: (?:ABBREVIATED|ABOVE_LINE|ABOVE_RIGHT
        # ... many more enum constants
      ){{identifier_break}}
      scope: support.constant.extendscript.jsx
      pop: true

This correctly assigns a support.constant.extendscript.jsx scope to the enum property and only if it follow a valid ExtendScript class. Nice!

Then further down I have a collection of property names and function names that get a scope as well, basically mimicking the support-property-ecma set that I found there:

support-property:
  - include: support-property-ecma
  - include: support-property-extendscript

support-property-extendscript:
    - match: (?:absoluteFlip|absoluteHorizontalScale| ...
    # ... many more properties
      ){{identifier_break}}
      scope: support.constant.extendscript.jsx
      pop: true

    - match: (?:abortPlaceGun|accept|activate| ...
    # ... many more methods
      ){{identifier_break}}
      scope: support.functions.extendscript.jsx
      pop: true

Again, this results in the appropriate types being associated with the correct scopes.
20200114-014628_Screenshot_SublimeText

Now for my question: In the package that I want to develop, I would like to write a completion file that offers completion suggestions for all available ExtendScript properties and methods, as soon as I type something like myVar.a. So as soon as, after a dot, I start typing the next letter, e.g. a, it should suggest all available classes starting with the letter a.

For this the dot would need its own scope somehow I think, right? However, as I see it now, it does not seem to have one assigned. Is there a way to assign a dedicated scope to a “property dot” right after it is inserted?

I suspect the object-property context needs to be extended somehow. But I am not sure how to go about this. Is there anybody who could point me in the right direction?

Thanks a lot!

0 Likes