Support setting syntax highlighters for quasiquotes?
I often use quasiquoters for things like JSON, SQL queries, JavaScript code, etc. It would be great if there was a way to configure quasiquoters with specific names to syntax highlight for the syntax they implement.
Is such a thing possible now? Is it possible at all with Atom's syntax highlighting support?
Sorry for the belated reply, I somehow managed to miss this.
So, answer to your question is both yes and no. While it's entirely possible to embed other grammars into language-haskell, it comes with its own set of challenges. And it's definitely not configurable on the user-end, at least not without some dark and forbidden magic (which is fragile by definition)
So at best, I could eventually add support for TH quasiquotes, since those have rigid syntax definitions. For user-defined quasiquoters, that's just not worth it, IMO -- this would create more problems in the long run than solve, e.g. a flaky third-party grammar can easily break highlighting in Haskell files almost completely.
P.S. also, support for user-defined quasiquoters opens a whole another can of worms if it's not configurable on the user-end, which I don't want to even think about.
I would also like to have this. I understand that the mappings are hard-coded into the grammar and can't be modified by the user, but I still think it would be useful. Something like this would be awesome:
I've never made a Textmate/Sublime/Atom grammar before, so I mocked that one up with this simple grammar:
name: 'Haskell'
scopeName: 'source.haskell'
fileTypes: [ 'hs' ]
patterns: [
{
begin: '\\[json\\|'
end: '\\|\\]'
patterns: [ include: 'source.json' ]
}
]
Obviously that's a bit simplistic. For the Real World :tm:, two naive solutions come to mind:
-
Add hard-coded support for all the built-in
language-*packages. That would cover most of the things people would be putting in quasi quotes (CSS, HTML, JSON, SQL, and so on). The downside is ignoring popular but not built in stuff like Docker or Nix. -
Translate the quasi quote identifier into the source language. I don't know if Atom's grammar actually supports this, but if it does: Turning
[Some.language| ... |]into asource.languagesection would neatly solve this problem without hard-coding anything. For example[Q.json| ... |]could be tagged assource.json. If this is possible, it would allow you to choose the inner syntax highlighting by choosing the appropriate identifier. (This is similar tolanguage-ruby's heredoc syntax of<<-SQL.)
Add hard-coded support for all the built-in language-* packages
I really don't like the idea of hard-coding this stuff, besides this grammar is used to highlight Haskell on GitHub as well, so I wonder if talking about language-* packages in Atom is even meaningful.
Adding all the "usual suspects" (json, yaml, sql, html, javascript, etc) would be possible in theory, but then it opens a can of worms of "why isn't X included". And with hardcoded prefixes there's another can of worms of "library Y uses prefix X for language Z". I'd personally much rather keep those proverbial cans closed.
Translate the quasi quote identifier into the source language.
Not possible. You'd need to include an external grammar based on what's parsed, and I'm pretty sure Atom's grammar model doesn't support this. I suspect partly because allowing something like that could break stuff really easily, and partly because it would be horribly inefficient (because you can't build a finite automaton if there are potentially infinite states)
Bummer.
For comparison, language-ruby supports a few nested heredoc syntaxes. It's been around for a while and they haven't had a bunch of requests for more grammars. In fact, I could only find two: https://github.com/atom/language-ruby/pull/187 and https://github.com/atom/language-ruby/pull/199.
I feel like a partial solution here is better than nothing at all. Properly syntax highlighting SQL inside a quasi quote is definitely "nice to have"; treating it as a string is obviously okay. In my mind, supporting a couple of languages is a nice bonus rather than a "this needs to allow embedding everything under the sun".
I came looking for a way to use SQL highlighting with the [here||] quasiquoter. I'd love to find or gain one!
With language-haskell v1.20 (just released), you can make a grammar package for that yourself quite easily (you could do this with earlier versions, but v1.20 makes it way easier):
- run the
package-generator:generate-language-packagecommand to create a new language package. - Name it however you like
- Delete
snippets,settingsandspecdirectories (we don't need them and defaults are useless) - Use the following template to write
grammars/*.cson:
injectionSelector: 'quoted.quasiquotes.qq-<your-quasiquote-function-name-here>.haskell'
scopeName: '<grammar-scope>' # the same as below
patterns: [ { include: '<grammar-scope>' } ]
# <grammar-scope> is the root scope name of the grammar you
# want to inject. To learn the root scope of any grammar,
# use `editor:log-cursor-scope` command
So, for instance to inject SQL grammar inside [here|...|] quasiquotes, one would do:
injectionSelector: 'quoted.quasiquotes.qq-here.haskell'
scopeName: 'source.sql'
patterns: [ { include: 'source.sql' } ]
Please be aware, that depending on the grammar being injected, it can easily escape the quasiquotes scope and basically "take over" the Haskell grammar with rather unappealing results:
This is not an issue with language-haskell.
@lierdakil Thanks! I didn't quite get it working, but I'm very excited. Can you spot anything I'm doing wrong?
My language package:

I used apm link to install it:

Result, still no injected syntax highlighting; I sanity-checked the scopes:

I also tried restarting atom, but no change. 😅
I can't see anything incorrect in the screenshots. Atom won't usually pick up the grammar injection package without a restart (or two), but usually running window:reload command a couple of times works too.
I can't make it go (I even rebooted), but thanks for looking it over for me.
@lierdakil blindly modeling after this gist, I switched the scopeName to match the injectionSelector, meaning I switched
injectionSelector: 'quoted.quasiquotes.qq-here.haskell'
scopeName: 'source.sql'
patterns: [ { include: 'source.sql' } ]
to
injectionSelector: 'quoted.quasiquotes.qq-here.haskell'
scopeName: 'quoted.quasiquotes.qq-here.haskell'
patterns: [ { include: 'source.sql' } ]
and that worked for me after a Window: Reload:
🎉 Thanks for your help.
Huh. Curious. Thanks for letting me know.