Add command and syntax grammar to existing language


#1

I have created a package named: ‘tnote’ providing a new command (tnode:insert-timestamp) only for “Plain Text” (language-text) file . The new command basically adds timestamp (ie: === 2016/09/06 (Tue) 10:07:21 PM ===) to the beginning of the file when it is called. I have also managed to bind a keymap to this command. All work as I wanted.

I went on to add syntax hight to that timestamp. It seemed to work. However, I noticed that the “Select Grammar” would list “Plain Text” twice.

If i selected the first “Plain Text”, I could call the the tnote:insert-timestamp , but the syntax wont highlight, if I selected the 2nd “Plain Text”, the syntax highlight would work, but the tnote:insert-timestamp is not available.

Anyone here could shed some light what I need to somehow merge the two Plain Text into one?

here are key content of the tnote package:

tnote/package.cson

{
  ...
  "activationHooks": [ "language-text:grammar-used" ],
  ...
}

tnote/lib/tnote.coffee

{CompositeDisposable} = require 'atom'

module.exports =
  activate: (state) ->
    atom.notifications.addInfo 'TNote is ready!'
    @subs = new CompositeDisposable
    @subs.add atom.commands.add 'atom-text-editor',
                                'tnote:insert-timestamp': (ev) =>
                                  @insertTimestamp ev.currentTarget.getModel()

  deactivate: ->
    @subs.dispose()

  insertTimestamp: (ed) ->
    weekdays = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]

    now      = new Date()
    year     = now.getFullYear()
    month    = @zerop(now.getMonth()+1)
    day      = @zerop(now.getDate())
    wday     = weekdays[now.getDay()]

    suffix   = (if (now.getHours() >= 12) then 'PM' else 'AM')
    hours    = @zerop(now.getHours() % 12 || 12)
    minutes  = @zerop(now.getMinutes())
    seconds  = @zerop(now.getSeconds())

    ed.setCursorBufferPosition(0)
    ed.insertText("=== #{year}/#{month}/#{day} (#{wday}) #{hours}:#{minutes}:#{seconds} #{suffix} ===\n\n\n")
    ed.moveUp(2)

  zerop: (value) ->
    return ('0' + value).slice(-2)

tnote/grammars/tnote.cson

'fileTypes': [
  'txt'
]

'name'     : 'Plain Text'
'scopeName': 'text.plain'

'patterns': [
  {
    'match'   : '^(={2,3}) ((\\d{4}\\/\\d{2}\\/\\d{2}) (\\(\\w{3}\\)) (\\d{2}:\\d{2}:\\d{2} (AM|PM))) (={2,3})(.*)'
    'name'    : 'tnote.marker'
    'captures':
      '1':
        'name': 'tnote.sep'
      '2':
        'name': 'tnote.timestamp'
      '3':
        'name': 'tnote.timestamp.date'
      '4':
        'name': 'tnote.timestamp.weekday'
      '5':
        'name': 'tnote.timestamp.time'
      '6':
        'name': 'tnote.timestamp.time.suffix'
      '7':
        'name': 'tnote.sep'
      '8':
        'name': 'tnote.notes'
  }
]

#2

You created a second “Plain Text” grammar in your tnote package. Atom isn’t designed to have multiple grammars with the same scope, for example text.plain. This is why things are going wonky on you :grinning:

The best approach to do what you’re describing is to create an “injection grammar”. You can look at the language-todo package for an example of doing something very similar to what you’re describing.


#3

leedohm, thank you very much. It works after I added the injection grammar.

'name'             : 'tNode'
'scopeName'        : 'text.tnote'
'injectionSelector': 'text.plain'

However, Select Grammar" would still list the tNote , is there a way to NOT list it.

Many thanks for the advise.!


#4

No, Atom lists all loaded grammars in the Grammar Selector. You’ll notice that TODO is listed in the Grammar Selector also.