I'm trying to get syntax highlighting/coloring for a glsl fragment shader in a script tag in an html file.
Even though I installed all related Shader Syntax packages in Sublime Text I still don't get syntax highlighting. I believe those only fix it for external files or for non-html files. But I'm using an internal script tag.
Has anyone ran into this?
To enable Syntax Highlighting click on “View” in the top bar, then hover your mouse over “Syntax”, and select your programming language from the list. Alternatively, if you save a document with a supported file extension, Sublime Text 3 will automatically apply the Syntax Highlighting for that language.
Sublime Text can use both . sublime-syntax and . tmLanguage files for syntax highlighting.
All I have to do in order to install a new syntax scheme is open Sublime's Command Palette (⌘-⇧-P). and issue the following command: Package Control: Install Package . Now I can choose whatever package I want to install — easy! And just like that!
🖍 Text Marker (Highlighter) Simply use Alt + Space to mark selected text.
Indeed you are correct that in Sublime a syntax definition generally refers to a particular type of file based on external criteria: the extension that the file has, the first line of the file (e.g. a bash shebang) or the user specifically overriding the automatic detection.
It is possible for one syntax to allow another to temporarily "take control" of the syntax highlighting, but this needs to be a conscious decision implemented by the author of the base syntax definition. The system needs to know when it should enter a new syntax, when it should leave back to the previous one and (most importantly) what syntax to actually use in between.
For example, Sublime ships with both a JavaScript syntax that applies to .js
files on their own, but the HTML syntax it ships with also includes a rule that allows the contents of an appropriate <script>
tag to be highlighted as JavaScript by handing control to that syntax, and taking it back when the enclosing </script>
tag appears.
So in your case, what you want to do is technically possible, but it doesn't work "out of the box" so to speak; you need an HTML syntax that knows to handle that type of script tag specially. Such a syntax might already exist on Package Control. This is also a modification that you can make yourself if you're so inclined. The remainder of this answer tells you how to do that (and there is a link at the bottom to a modified file as well).
The basic idea is that we are going to create an override
of the HTML syntax file that ships with Sublime. As the name suggests, that means that the file we're creating will always be used in place of the file that ships with Sublime transparently. Note however that even if the underlying file gets updated, your changed file will still be used (and you get no direct warning). One of the features of the OverrideAudit package (disclaimer: I'm the author) is to tell you when that happens so you can be sure you're not missing out on new features or bug fixes in the underlying file.
These instructions also assume that you are using the latest stable build of Sublime, which is 3176 at the moment. New builds often bring new syntaxes with them, so if you're running an older version now (or a newer version if you're from the mysterious future) the fundamental instructions remain the same but the content of the file might be different. As we'll see, we're basing our changes on copying existing functionality.
If you don't already have it installed, install the PackageResourceViewer package, and then use the command PackageResourceViewer: Open Resource
from the command palette (be sure you don't accidentally pick the extract
variant) and pick first the HTML
package, and then the HTML.sublime-syntax
file. This opens the file for editing purposes; when you save the file an override will automatically be created for you.
Behind the scenes this is creating a folder named HTML
in the Packages
folder (use Preferences > Browse Packages...
to find that) and storing a copy of the resource file inside. Deleting that file will restore the original syntax.
You can manually create the override by using the View Package File
built in command to open the HTML/HTML.sublime-syntax
resource and save it in the appropriate location. If you do this, you need to close and reopen the file because it will open as read-only.
Near the top of the open syntax file is a set of variables
, which in the syntax are a way to provide named regular expressions to make following steps easier to read. In the variable list is a variable named javascript_mime_type
which is defined like this:
javascript_mime_type: |-
(?ix)(?:
# https://mimesniff.spec.whatwg.org/#javascript-mime-type
(?:application|text)/(?:x-)?(?:java|ecma)script
| text/javascript1\.[0-5]
| text/jscript
| text/livescript
)
Right below that, we will create our own variable that contains the regular expression to match the internals of the type
attribute of the script tag. An example of that is the following:
shader_mime_type: |-
(?ix)(?:
x-shader/x-fragment
)
This may or may not need to be extended (I'm unfamiliar with how this tag is used for WebGL). If there is more than one variant of possible contents, this can be extended as in the JavaScript example above to include extras.
Further down in the file (in build 3176 this is around line 315 or so after making the above change) is a context named script-javascript
that contains the syntax rules for knowing how to highlight the contents of a javascript based <script>
tag, and it looks like this:
script-javascript:
- meta_content_scope: meta.tag.script.begin.html
- include: script-common
- match: '>'
scope: punctuation.definition.tag.end.html
set:
- include: script-close-tag
- match: (?=\S)
embed: scope:source.js
embed_scope: source.js.embedded.html
escape: (?i)(?=(?:-->\s*)?</script)
The first few lines include the logic for syntax highlighting the rest of the attributes in the <script>
that follow the type
attribute. The contents of set
are the magic here; they say that if the body of the tag is not empty, the contents should be highlighting by embed
ing the Javascript syntax, and that a closing </script>
tag is what escapes the embed
and goes back to regular HTML.
Here we will create our own section for hangling a glsl
script:
script-glsl:
- meta_content_scope: meta.tag.script.begin.html
- include: script-common
- match: '>'
scope: punctuation.definition.tag.end.html
set:
- include: script-close-tag
- match: (?=\S)
embed: scope:source.glsl
embed_scope: source.glsl.embedded.html
escape: (?i)(?=(?:-->\s*)?</script)
As we can see, this is nearly identical to the above, but a different scope is used (more on that below).
script
tag about our new rulesWe've laid the groundwork, now to tie everything together. Lower down in the file (in 3176 this is roughly line 390 after the above changes) there is a set of rules entitled script-type-decider
. There are five match rules in this context, so for brevity we're only going to show the first one here.
script-type-decider:
- match: (?i)(?={{javascript_mime_type}}(?!{{unquoted_attribute_value}})|'{{javascript_mime_type}}'|"{{javascript_mime_type}}")
set:
- script-javascript
- tag-generic-attribute-meta
- tag-generic-attribute-value
This context lists the rules that gets applied while inside of a <script>
tag's type
attribute to see what should happen to the contents of this particular tag. The rule specifically quoted here uses the variable outlined above to determine that this is a JavaScript tag and uses the rule we just looked at to embed the JavaScript syntax.
Right below this match
(order of rules matters) we will add our own match
to mimic this one, using our own variable and rule set:
- match: (?i)(?={{shader_mime_type}}(?!{{unquoted_attribute_value}})|'{{shader_mime_type}}'|"{{shader_mime_type}}")
set:
- script-glsl
- tag-generic-attribute-meta
- tag-generic-attribute-value
With all of our changes made, all you have to do is save
the file to make them active. Once you do so, Sublime will immediately recompile the changed syntax file and put it into effect. The Sublime console (View > Show Console
) will say generating syntax summary
when it does this. If you don't see any error messages, you're ready to go:
A full version of the file with the changes talked about is available in this gist for comparison purposes and was used to generate the above image. The gist is laid out with the base file as revision 1 and the modifications as version 2, so it is easy to see exactly what changes were made to the file.
It's worth mentioning that the package in question actually provides three different syntaxes (Cg
, HLSL
and GLSL
). The syntax modifications above only handle the GLSL
syntax embedding, which I guessed was what you were going for.
Following the same instructions as above, you can swap the syntax used or add rules for the others as well (assuming you know what type
should go in the <script>
tag to differentiate them). The same rules will also apply if there is another syntax that provides a subjectively "better" highlighting experience.
The important piece of the puzzle is the scope
that is used in the embedded syntax. In the above example we used source.glsl
, which is something that is defined directly in the syntax definition for this file type in the package that you're using.
The other scopes in that package are source.cg
and source.hlsl
. The easiest way to determine the appropriate scope is to create a new file, use the appropriate Set Syntax:
item from the command palette to set the syntax for the empty buffer, and use Tools > Developer > Show Scope Name
to get a popup that will tell you.
not a direct answer but an alternative solution.
Using ES6 import
you can store your shaders in separate files
// some-vertex-shader.glsl
export default `
void main() {
gl_Position = vec4(0, 0, 0, 1);
}
`;
Then in your JavaScript you can include it like this
import someVertexShaderSource from 'path/to/some-shader-source.glsl';
...
With that you can probably setup Sublime to highlight code in .glsl files
You can then use something like rollup to merge all your files into one if you want to support older browsers.
This is what three.js does.
Also in VSCode (sorry, not sublimetext) I believe you can tag template literals with a language to get it to highlight
const shaderSource = /* glsl */`
void main() {
gl_Position = vec4(0, 0, 0, 1);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With