Sublime Forum

Understanding syntax scope popping

#1

Hey, there! I’m a syntax definition noob trying to understand scopes and popping. My goal is to write a syntax definition that supports embedding HTML inside of JS, like JSX, but specifically for HTML template strings (see nanohtml). Here’s an example of what the language looks like:

script.js

// This is normal javascript
const myFunction = () => {
  return html`
    <!-- This is HTML -->
    <div>Look ma! Just plain ol' HTML. ${ 'And this should be the embedded_js scope' }</div>
    <div>Weirdly, when I ctrl+alt+shift+P here, I'm still in the embedded_js scope. It hasn't popped. Any idea why?</div>
  `
}

And here’s my attempt at a syntax definition:

%YAML 1.2
---
file_extensions:
  - js
scope: source.nanohtml
contexts:
  main:
    - match: ''
      push: 'scope:source.js'
      with_prototype:
        - match: html\`
          push: "Packages/HTML/HTML.sublime-syntax"
          with_prototype:
            - match: \`
              pop: true
            - match: \$\{
              push: embedded_js

  embedded_js:
    - meta_content_scope: source.nanohtml.embedded_js
    - match: \}
      pop: true
    - include: 'scope:source.nanohtml'

For some reason, my pop in embedded_js doesn’t seem to be working. When I ctrl+alt+shift+P in a line following ${ }, I’m still in the embedded_js context. Any idea why?

0 Likes

#2

I can’t recommend such an approach at all anymore. Using with_prototype to inject stuff in a complex syntax definition used to be the only way in past but is very likely to bloat it in ways which cause the maximum allowed complexity to be exceeded easily.

The reason is, with_prototype prepanding the patterns to any of the pushed contexts, which creates a whole new syntax tree literly doupling the number of overal contexts. In some situation it even causes so called inclusion loops.

Maybe “JSCustom” package offers some helpers to extend an existing JS syntax in the way you want.

The way to go with Sublime Text 4 was to extend an existing syntax and add your patterns to an appropriate already existing context.

If somehow possible you should use embed keyword to inject external syntax as it adds just a reference to it (instead of adding its whole context tree), which saves some RAM. Furthermore embedded syntax definitions are lazily loaded on demand only.

Note: The escape statement causes the HTML syntax to be terminated as soon as the pattern matches a token no matter where or when.

%YAML 1.2
---
scope: source.js.nanohtml
version: 2
extends: Packages/JavaScript/JavaScript.sublime-syntax

file_extensions:
  - js

contexts:

  expression-begin:
    - meta_prepend: true
    - match: (html)(\`)
      captures:
        1: constant.other.language-name.js
        2: punctuation.section.nanohtml.begin.js
      embed: scope:text.html.basic
      embed_scope: text.html.basic.embedded.js
      escape: \`
      escape_captures:
        0: punctuation.section.nanohtml.end.js
1 Like

#3

Wow, thank you! JSCustom turned out to have exactly what I was looking for: https://github.com/Thom1729/Sublime-JS-Custom/blob/master/docs/recipes.md#highlight-html-in-template-strings

I didn’t even know there was a sublime text 4, lol. Guess I should look at that at some point…

Thanks for your help!

0 Likes