How can I selectively load a language based on file content?


There is a language package I like called “language-TIS-100”. The TIS-100 files all end in “.txt” so they load as “Plain Text”, but they always start with the same two characters: “@0”. I would like to modify the package such that any file with the “.txt” extension and starting with “@0” uses “TIS-100” instead of “Plain Text”.

I asked on IRC for some help, and I learned a little about atom.workspace.observeTextEditors() and atom.grammars.grammarForScopeName(). Here is the code that I have been trying:

'use babel';

export default {
    activate(state) {
        atom.workspace.observeTextEditors(editor => {
            if (editor.getTextInBufferRange([[0, 0], [0, 1]]) == '@0') {

This is in main.js in the root of my dev package, and I have modified the package.json to use main.js as the main – the current package on GitHub has no main.

I don’t really know what I’m doing. Can someone help me out?


Language packages generally aren’t activated on startup. They’re activated when a file that uses that grammar is opened. So putting something like this in a language package’s activate function isn’t going to achieve what you’re looking for.

What you’re really looking for is the firstLineMatch property in the grammar definition. For example, from the language-shellscript package:

This is what activates the language-shellscript package whenever any file is opened that starts with a shebang line like #!/bin/bash.


That is exactly what I was looking for. Thank you so much for your help!


Alas, this doesn’t seem to work. :confounded:

I added the following line to grammars/TIS-100-assembly.cson: 'firstLineMatch': '@0'. I then opened a file named 00150.0.txt with the following contents:









The mode did not change from Plain Text. Any ideas on why?


Probably either because the CSON isn’t correctly formed and Atom doesn’t know what it’s reading, or you didn’t reload Atom after adding the line.


It’s possible the CSON isn’t correctly formed. I looked online for a validator and didn’t see anything obvious, which is a shame. That being said, it’s a single line which I have quoted above so I don’t think that’s the problem.

I was able to confirm that Atom was reading my package, because I changed the README and the change was visible.


You could still share it just in case that’s the issue.


I have made two changes to this file: removed the fileTypes list because they were wrong, and added the firstLineMatch line.

I can make a gist of the file I posted earlier in the thread if you think that would help.


I was able to fix this by adding a beginning-of-line character to the regular expression. firstLineMatch: '^@0' works for me.

A couple of other things: you have two scopeName attributes. I believe the second one will take priority, but you should just have one. You also don’t need quotes around single-word keys; you certainly can, but I find CSON easier to read with the keys and values being colored differently.


The grammar file came with both scopeName lines, and I wasn’t sure which to delete. I’ll remove the first one.

I changed the regular expression to match yours – 'firstLineMatch': '^@0' – and still got no success.

Here is my input file:

It comes up Plain Text every time, even when I verify that I’m running the correct code by looking at the packages menu, selecting the development package, and then clicking the view code button. :frowning:


You’re right. I screwed up my testing and wasn’t properly clearing the previous grammar, so Atom’s serialization was getting me.

After some more messing around, I’m not sure that there’s a good way to do this since your code files are going to be .txt. Atom greatly prefers to match by file type. So I went back to your original idea and made that work. This is the code I used to test the algorithm. If you plug it directly into your, you can see it in action.

# Set grammar to TIS if first line is @0
#atom.commands.add 'atom-text-editor', 'user:set-tis', ->
atom.workspace.observeTextEditors (editor) ->
  editor.onDidSave () ->
    if editor.lineTextForBufferRow(0) == '@0'
      atom.notifications.addSuccess('A TIS file has been identified')
      atom.notifications.addError('No TIS file')

The commented out line of code is an alternative first line that defines a command that you can use to activate it on a keypress instead of via onDidSave. I then went and added another listener to respond to when a file is opened, which is very slightly different:

# Set grammar to TIS if first line is @0
atom.workspace.observeTextEditors (editor) ->
  editor.onDidSave () ->
    if editor.lineTextForBufferRow(0) == '@0'

atom.workspace.onDidOpen (editor) ->
  if editor.item.lineTextForBufferRow(0) == '@0'

This code will quietly override the grammar for any file that starts with @0. There’s not even a flicker (though there might be if you opened a very large file).


This works perfectly! Thanks for being patient with me.