passing through options stale and path

passing through options stale and path

--- lib/request.coffee
+++ lib/request.coffee
@@ -45,7 +45,8 @@
           if body?

             cache = JSON.parse body
-            cache.stale = "1m"
+            cache.stale = self.stale
+            cache.path = req.url

             insert = new self.ds.Schema cache, self.ds


Fixed subtitle code

Fixed subtitle code

- make it more efficient and easier to maintain
- thanks for liuderchi's advice!

--- lib/welcome.coffee
+++ lib/welcome.coffee
@@ -18,9 +18,8 @@
     return if welcome is null
     unless welcome.getAttribute('data-localized') is 'true'
       subtitle = welcome.querySelector('.welcome-header h1.welcome-title')
-      subtitle.innerHTML = subtitle.innerHTML.replace("A hackable text editor for the 21", @defW.Welcome.subtitle.text1)
-      subtitle.querySelector('sup').textContent = @defW.Welcome.subtitle.superscript
-      subtitle.innerHTML = subtitle.innerHTML.replace(" Century", @defW.Welcome.subtitle.text2)
+      {text1, superscript, text2} = @defW.Welcome.subtitle
+      subtitle.innerHTML = "#{text1}<sup>#{superscript}</sup>#{text2}"

       helpGroup = welcome.querySelector('.welcome-panel')
       if helpGroup


fixed bugs and added dependencies and exports

--- lib/characters.coffee
+++ lib/characters.coffee
@@ -1,3 +1,6 @@
+Y = require "../../yjs"
+Y.Test = require "../../y-test"
+
 class Characters
   constructor: (content) ->
     @_chars = []
@@ -5,14 +8,14 @@

   _name: "Characters"
   _setModel: (model) ->
-    del @_chars
+    delete @_chars
     @_model = model

   _getModel: (Y, Operation) ->
     if (@_model == null)
       model = new Operation.ListManager(@).execute()
       model.insert 0, @_chars
-      _setModel model
+      @_setModel model
     return @_model

   # Function that creates a character object
@@ -144,3 +147,7 @@

         state.push deltaSelection
         return position + delta.retain
+
+if module?
+  module.exports.Characters = Characters
+  module.exports.Y = Y


rollbar: log on disconnects

rollbar: log on disconnects

--- client/Main/main.coffee
+++ client/Main/main.coffee
@@ -46,6 +46,8 @@
     modalSize  = options.modalSize  or= "big"
     notifyUser = options.notifyUser or= "yes"
     state      = "disconnected"
+
+    KD.logToExternalWithTime "User disconnected"

     log "disconnected",\
     "reason: #{reason}, modalSize: #{modalSize}, notifyUser: #{notifyUser}"


hahah i am running something on port 3000 right now so it got a 200 OK instead :)


controller, on syntax error open file to line

--- lib/controller.coffee
+++ lib/controller.coffee
@@ -126,12 +126,26 @@

     return unless expression

+    onError = (error) =>
+      if error.type is 'SyntaxError'
+        if path
+          # offset syntax error by position of selected text in file
+          row = range.getRows()[0] + error.error.syntaxErrors.line - 1
+          col = error.error.syntaxErrors.charPos
+          @openFile(path, null, row, col)
+
+      # dbug = JSON.stringify(error, undefined, 2)
+      # console.log dbug
+      # @bus.push "
#{dbug}
"
+
     if @activeRepl
       @activeRepl.eval(expression, false, path)
+        .then(null, onError)
     else
       @openPostWindow(@defaultURI)
         .then () =>
           @activeRepl.eval(expression, false, path)
+            .then(null, onError)

   openHelpFile: ->
     unless @editorIsSC()


Yet another bug fix related to clearing aliases cache.

--- acs.coffee
+++ acs.coffee
@@ -75,7 +75,6 @@
       updates["#{i[0]}._name"] = i[1]

   if actions.parameterNames?
-    newAliases = {} # possible new aliased parameters
     for p in actions.parameterNames
       if OBJECT_REGEX.test(p[0])
         path = p[0]
@@ -89,7 +88,9 @@
       updates["#{path}_timestamp"] = now

       # Add parameter (sans any dot suffix) if has an alias
-      newAliases[pp] = a if (a = parameters.getAlias(pp = path.slice(0, -1)))?
+      if (a = parameters.getAlias(pp = path.slice(0, -1)))?
+        newAliases = {} if not newAliases?
+        newAliases[pp] = a

   if actions.customCommands?
     for p in actions.customCommands
@@ -104,11 +105,8 @@
     db.devicesCollection.update({'_id' : currentRequest.deviceId}, {'$set' : updates, '$unset' : deletes}, {}, (err, count) ->
       throw err if err
       # Clear aliases cache if there's a new aliased parameter that has not been included in aliases cache
-      if newAliases?.length
-        db.redisClient.get('aliases', (err, res) ->
-          throw err if err
-          return if not res?
-          aliases = JSON.parse(res)
+      if newAliases?
+        db.getAliases((aliases) ->
           for p, a of newAliases
             if not aliases[a]? or p not in aliases[a]
               db.redisClient.del('aliases', (err, res) ->


getPath -> getPaths

getPath -> getPaths

--- lib/tag-reader.coffee
+++ lib/tag-reader.coffee
@@ -32,6 +32,7 @@

   getAllTags: (callback) ->
     projectTags = []
-    task = Task.once handlerPath, atom.project.getPath(), -> callback(projectTags)
+    [projectPath] = atom.project.getPaths()
+    task = Task.once handlerPath, projectPath, -> callback(projectTags)
     task.on 'tags', (paths) -> projectTags.push(paths...)
     task


null check on final object in tree

null check on final object in tree

--- lib/craigslist.coffee
+++ lib/craigslist.coffee
@@ -19,7 +19,7 @@

     @request.get_cache_first _url, (data) ->
       #console.log data
-      cb data?.data?.query?.results?.RDF?.item?.map _format
+      cb data?.data?.query?.results?.RDF?.item?.map? _format

 _fmt_craigslist_url = (loc,type,q) ->


don't save dispatcher options beyond the constructor

--- lib/dispatcher.coffee
+++ lib/dispatcher.coffee
@@ -11,16 +11,19 @@
 class Dispatcher

   # Initializer
-  constructor: (@options) ->
+  constructor: (options) ->
     @deps = {}
-    @_db = db @options.db_index
-    @_req = db @options.db_index
-    @_res = db @options.db_index
-    @_test_mode = @options.test_mode
-    @_verbose = @options.verbose
-    @_idle_timeout = @options.idle_timeout ? (if @_test_mode then 500 else 10000)
-    @_audit_stream = @options.audit
-    @_control_channel = _('control').namespace @options.db_index
+    @_test_mode = options.test_mode
+    @_verbose = options.verbose
+    @_idle_timeout = options.idle_timeout ? (if @_test_mode then 500 else 10000)
+    @_audit_stream = options.audit
+    {db_index} = options
+    @_db = db db_index
+    @_req = db db_index
+    @_res = db db_index
+    @_control_channel = _('control').namespace db_index
+    @_responses_channel = _('responses').namespace db_index
+    @_requests_channel = _('requests').namespace db_index
     @_count = {}
     @_state = {}
     @_cycles = {}
@@ -30,8 +33,8 @@
   listen: ->
     @_req.on 'message', (ch, str) => @_requested str
     @_res.on 'message', (ch, str) => @_responded str
-    @_req.subscribe _('requests').namespace(@options.db_index)
-    @_res.subscribe _('responses').namespace(@options.db_index)
+    @_req.subscribe @_responses_channel
+    @_res.subscribe @_responses_channel

   # Send quit signals to the work queues.
   quit: ->


fix extension dir watching

fix extension dir watching

--- lib/extensions.coffee
+++ lib/extensions.coffee
@@ -66,7 +66,7 @@
       for base in bases
         dir = path.join(pkg.dir, base)
         entry = path.relative dir, filename
-        unless relpath.match /^\.\./
+        unless entry.match /^\.\./
           return @loadModule pkg, dir, entry

       console.log "nothing to do for #{filename}"


Pass 'type' option to StatusView in GitCommit line 64

--- lib/git-commit.coffee
+++ lib/git-commit.coffee
@@ -61,7 +61,7 @@
       # reset editor for commitFile
       currentEditor = null
     stderror: (data) =>
-      new StatusView(message: data.toString())
+      new StatusView(type: 'alert', message: data.toString())
       atom.beep()
   })


starting to cleanup

starting to cleanup

--- workers/social/lib/social/models/graph/graph.coffee
+++ workers/social/lib/social/models/graph/graph.coffee
@@ -43,33 +43,7 @@
       collectObjects({klass:klass, selector:selector, modelName:modelName})

   fetchFromNeo4j:(query, options, callback)->
-    console.log ">>>>> options >>>>", options
     return @runQuery(query, options, callback)
-
-
-    # TODO: replies ???
-    # gets ids from neo4j, fetches objects from mongo, returns in the same order
-    @db.query query, params, (err, results)=>
-      if err
-        console.log ">>> err", err
-        return callback err
-
-      console.log "===== results ===="
-      console.log results
-      console.log "// ==== results ==="
-
-      if results.length == 0
-        callback null, []
-
-      wantedOrder = []
-      collections = {}
-      for result in results
-        oid = result.items._data.data.id
-        otype = result.items._data.data.name
-        wantedOrder.push({id: oid, collection: otype, idx: oid+'_'+otype})
-        collections[otype] ||= []
-        collections[otype].push(oid)
-      @fetchObjectsFromMongo(collections, wantedOrder, callback)

   objectify = (incomingObjects, callback)->
     incomingObjects = [].concat(incomingObjects)
@@ -172,13 +146,19 @@
       else
         collectRelations = race (i, res, fin)=>
           id = res.id
-
+          tempRes[i].replies = []
           @fetchRelatedItems id, (err, relatedResult)=>
             if err
               callback err
               fin()
             else
               tempRes[i].relationData =  relatedResult
+              if relatedResult.reply?
+                for reply in relatedResult.reply
+                  reply.bongo_ =
+                    constructorName: obj.name
+                    instanceId: obj.id
+                  tempRes[i].replies.push reply
               fin()
         , =>
           console.log "fetchAll finished"


SocialWorker: JGroup: remove MemberRoleChanged event and its usage

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -96,7 +96,6 @@
       slug          : 'unique'
     sharedEvents    :
       static        : [
-        { name: 'MemberRolesChanged' }
         { name: 'GroupDestroyed' }
         { name: 'broadcast' }
         { name: 'updateInstance' }
@@ -387,9 +386,6 @@
         @broadcast 'MemberLeftGroup',
           member : ObjectRef(member).data

-    @on 'MemberRolesChanged', (member) ->
-      @constructor.emit 'MemberRolesChanged', { group: this, member }
-
   @render        :
     loggedIn     :
       kodingHome : require '../../render/loggedin/kodinghome'


Touch the file before continue

Touch the file before continue

--- app.coffee
+++ app.coffee
@@ -67,6 +67,7 @@
   if app_env == "production"
     # Start listening to a unix socket.

+    fs.futimesSync config.faye.socket
     fs.unlinkSync config.faye.socket

     oldmask = process.umask(0o0000)
@@ -82,6 +83,10 @@

   if app_env == "production"
     # Start listening to the secure port using SSL.
+
+    fs.futimesSync config.faye.socketSecure
+    fs.unlinkSync config.faye.socketSecure
+
     oldmask = process.umask(0o0000)
     faye.listen config.faye.socketSecure,
       key: config.sslKey


redis

redis

--- app.coffee
+++ app.coffee
@@ -3,7 +3,6 @@
 partials = require 'express-partials'
 path = require 'path'
 RedisStore = require('connect-redis')(session)
-client = require('redis').createClient(process.env.REDIS_URL or 'localhost');
 favicon = require 'serve-favicon'
 app = express()
 logger = require 'morgan'
@@ -13,7 +12,10 @@
 CONFIG = require('./config').CONFIG
 server = require('http').Server(app)
 app.use session {
-  store: new RedisStore client: client
+  store: new RedisStore host: CONFIG?.DB?.REDIS?.HOST
+  port: CONFIG?.DB?.REDIS?.PORT
+  pass: CONFIG?.DB?.REDIS?.PASSWORD
+  user: CONFIG?.DB?.REDIS?.USER
   prefix: CONFIG?.DB?.REDIS?.PREFIX + 'sess:'
   key: CONFIG.EXPRESS.SESSION.KEY
   secret: CONFIG.EXPRESS.SESSION.SECRET


Fix issues when using reverse proxy

Fix issues when using reverse proxy

--- app.coffee
+++ app.coffee
@@ -102,7 +102,7 @@
 			if file is '.gitignore'
 				fileCallback null
 				return
-
+
 			packageFolder = config.packageFolder + file
 			logger.log "debug", "Parsing #{packageFolder}"
 			fs.stat packageFolder, (err, packageFolderStat) ->
@@ -263,7 +263,7 @@
 		writer.writeComment "This list was presented by Tims Package Server #{serverVersion}"
 		do writer.endElement
 		do writer.endDocument
-	res.end (do writer.toString).replace /\{\{packageServerHost\}\}/g, "#{req.protocol}://#{req.header 'host'}"
+	res.end (do writer.toString).replace /\{\{packageServerHost\}\}/g, config.basePath ? "#{req.protocol}://#{req.header 'host'}"

 app.all /^\/([a-z0-9_-]+\.[a-z0-9_-]+(?:\.[a-z0-9_-]+)+)\/([0-9]+\.[0-9]+\.[0-9]+(?:_(?:a|alpha|b|beta|d|dev|rc|pl)_[0-9]+)?)/i, (req, res) ->
 	res.attachment "#{req.params[0]}_#{req.params[1]}.tar"
@@ -273,8 +273,8 @@
 	app.get '/update', (req, res) ->
 		logger.log "info", 'Manual update was requested'
 		readPackages ->
-			res.redirect 303, '/'
-
+			res.redirect 303, config.basePath ? '/'
+
 readPackages ->
 	app.listen config.port


adding in a comment to explain logic

adding in a comment to explain logic

--- src/flux/student-dashboard.coffee
+++ src/flux/student-dashboard.coffee
@@ -41,6 +41,7 @@
       events[moment(day).startOf('isoweek').format('YYYYww')] or []

     canWorkTask: (event) ->
+      #students cannot work or view a task if it has been deleted and they haven't started it
       (
         new Date(event.opens_at) < TimeStore.getNow() and
         not (


OK THAT WAS TOO MUCH

OK THAT WAS TOO MUCH

--- modules/danbooru.coffee
+++ modules/danbooru.coffee
@@ -102,7 +102,7 @@
     @registerCommand 'waifu', {
       aliases: ['w'],
       allowDM: true
-    }, (msg)=>
+    }, (msg, args, d)=>
       if d.danbooruDate
         return d.reply 'Rate limit excedeed. Wait a few seconds.' if (new Date() - d.danbooruDate) < 10000
       Waifu.filter({ user: msg.author.id }).run()


Date fields are now returned to the UI for display.

--- client/zma_helpers.coffee
+++ client/zma_helpers.coffee
@@ -37,10 +37,16 @@
   return '' unless object?
   return object._id._str if path =='_id'and typeof object._id == 'object'
   result = object
+
   for part in path.split(".")
     result = result[part]
     return '' unless result?  # quit if you can't find anything here
-  if typeof result isnt 'object' then result else ''
+
+  # Return date objects and other non-object types
+  if typeof result isnt 'object' or result instanceof Date
+    return result
+  else
+    return ''

 # This is temporary until meteor supports mounting of apps.
 # It's so that we can remove all other css files and add our own.


little cleanup

little cleanup

--- bot.coffee
+++ bot.coffee
@@ -64,9 +64,9 @@
   log: (message) ->
     if @debug
       if @stdout == 'stderr'
-        console.error "> #{data}"
+        console.error message
       else
-        console.log "> #{data}"
+        console.log message

   connect: (roomId) ->
@@ -117,36 +117,45 @@
     #console.log 'THIS IS WEIRD AND SHOULD NOT APPEAR.'

-  onMessage: (msg) ->
-    data = msg.data
-
+  isHeartbeat: (data) ->
     heartbeat_rgx = /~m~[0-9]+~m~(~h~[0-9]+)/
-    if data.match heartbeat_rgx
-      @_heartbeat data.match(heartbeat_rgx)[1]
-      @lastHeartbeat = Date.now()
-      @updatePresence()
-      return
-
-    @log "> #{data}"
-
-    if msg.data == '~m~10~m~no_session'
-      @userAuthenticate ->
-        if not @_isConnected
-          @getFanOf (data) ->
-            @fanOf = data.fanof
-            @updatePresence()
-            # TODO: I don't like setInterval !
-            setInterval(@updatePresence.bind(@), 10000)
-            @emit 'ready'
-        @callback()
-        @_isConnected = true
-      return
-
-    @lastActivity = Date.now()
-
+    data.match heartbeat_rgx
+
+
+  isNoSession: (data) ->
+    data == '~m~10~m~no_session'
+
+
+  treatHeartbeat: (packet) ->
+    heartbeat_rgx = /~m~[0-9]+~m~(~h~[0-9]+)/
+    @_heartbeat packet.match(heartbeat_rgx)[1]
+    @lastHeartbeat = Date.now()
+    @updatePresence()
+
+
+  treatNoSession: (packet) ->
+    @userAuthenticate ->
+      if not @_isConnected
+        @getFanOf (data) ->
+          @fanOf = data.fanof
+          @updatePresence()
+          # TODO: I don't like setInterval !
+          setInterval(@updatePresence.bind(@), 10000)
+          @emit 'ready'
+      @callback()
+      @_isConnected = true
+
+
+  extractPacketJson: (packet) ->
     len_rgx = /~m~([0-9]+)~m~/
-    len = data.match(len_rgx)[1]
-    json = JSON.parse(data.substr(data.indexOf('{'), len))
+    len = packet.match(len_rgx)[1]
+    try
+      return JSON.parse(packet.substr(packet.indexOf('{'), len))
+    catch err
+      return null
+
+
+  executeCallback: (json) ->
     index = 0
     while index < @_cmds.length
       [id, rq, clb] = @_cmds[index]
@@ -158,10 +167,8 @@
             if json.success == true
               currentDj   = json.room.metadata.current_dj
               currentSong = json.room.metadata.current_song
-              if currentDj
-                @currentDjId = currentDj
-              if currentSong
-                @currentSongId = currentSong._id
+              @currentDjId = currentDj if currentDj
+              @currentSongId = currentSong._id if currentSong
           when 'room.register'
             if json.success == true
               @roomId = rq.roomid
@@ -182,55 +189,33 @@
         if not is_search and clb
           clb.call @, json

-        @_cmds.splice(i, 1)
+        @_cmds.splice(index, 1)
         break
       else
         index++

-    switch json['command']
-      when 'registered'
-        @emit 'registered', json
-      when 'deregistered'
-        @emit 'deregistered', json
-      when 'speak'
-        @emit 'speak', json
-      when 'pmmed'
-        @emit 'pmmed', json
+
+  treatCommand: (json) ->
+    command = json['command']
+    switch command
       when 'nosong'
         @currentDjId   = null
         @currentSongId = null
         @emit 'endsong', @tmpSong
-        @emit 'nosong', json
       when 'newsong'
         if @currentSongId
           @emit 'endsong', @tmpSong
         @currentDjId   = json.room.metadata.current_dj
         @currentSongId = json.room.metadata.current_song._id
         @setTmpSong json
-        @emit 'newsong', json
       when 'update_votes'
         if @tmpSong
           @tmpSong.room.metadata.upvotes = json.room.metadata.upvotes
           @tmpSong.room.metadata.downvotes = json.room.metadata.downvotes
           @tmpSong.room.metadata.listeners = json.room.metadata.listeners
-        @emit 'update_votes', json
-      when 'booted_user'
-        @emit 'booted_user', json
-      when 'update_user'
-        @emit 'update_user', json
-      when 'add_dj'
-        @emit 'add_dj', json
       when 'rem_dj'
         if json.modid
-          json['command'] = 'escort'
           @emit 'escort', json
-        @emit 'rem_dj', json
-      when 'new_moderator'
-        @emit 'new_moderator', json
-      when 'rem_moderator'
-        @emit 'rem_moderator', json
-      when 'snagged'
-        @emit 'snagged', json
       when 'search_complete'
         query = json['query']
         for i in [0...@currentSearches.length]
@@ -238,13 +223,22 @@
             @currentSearches[i].callback json
             @currentSearches.splice i, 1
             break
-      else
-        if json['command']
-          #console.log('Command: ', json)
-        else if typeof(json['msgid']) == 'number'
-          if not json['success']
-            1 == 1
-            #console.log(json)
+    @emit command, json
+
+
+  treatPacket: (packet) ->
+    json = @extractPacketJson(packet)
+    @executeCallback(json)
+    @treatCommand(json)
+
+
+  onMessage: (msg) ->
+    data = msg.data
+    return @treatHeartbeat(data) if @isHeartbeat(data)
+    @log "> #{data}"
+    return @treatNoSession(data) if @isNoSession(data)
+    @lastActivity = Date.now()
+    @treatPacket(data)

   _heartbeat: (msg) ->


Quick fix for merging queue

Quick fix for merging queue

--- source/server/sockets/user.coffee
+++ source/server/sockets/user.coffee
@@ -155,7 +155,7 @@
   ###

   queue_sync: (queue, clientTime, fn) ->
-    @sync.queue(queue, clientTime)
+    @sync.queue.merge(queue, clientTime)
     .then (results) -> fn(null, results)
     .catch (err) -> if fn then fn(true)


Update cursor positioning text for simplified token markup

--- spec/editor-component-spec.coffee
+++ spec/editor-component-spec.coffee
@@ -262,7 +262,7 @@
       cursor = node.querySelector('.cursor')
       cursorRect = cursor.getBoundingClientRect()

-      cursorLocationTextNode = node.querySelector('.storage.type.function.js').firstChild.firstChild
+      cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.storage.type.function.js').firstChild
       range = document.createRange()
       range.setStart(cursorLocationTextNode, 0)
       range.setEnd(cursorLocationTextNode, 1)


code formatting

code formatting

--- lib/submachine.coffee
+++ lib/submachine.coffee
@@ -40,7 +40,9 @@
           break

   switchTo: ( state, args... ) ->
-    throw new Error "invalid state #{state}" if not contains @states, state
+    if not contains @states, state
+      throw new Error "invalid state #{state}"
+
     @callbacks           ?= {}
     @callbacks[ @state ] ?= {}
     @callbacks[ state ]  ?= {}


Update to new package naming scheme

Update to new package naming scheme

--- spec/grammar-selector-spec.coffee
+++ spec/grammar-selector-spec.coffee
@@ -7,8 +7,8 @@
   beforeEach ->
     window.rootView = new RootView
     atom.activatePackage('grammar-selector')
-    atom.activatePackage('text-tmbundle', sync: true)
-    atom.activatePackage('javascript-tmbundle', sync: true)
+    atom.activatePackage('language-text', sync: true)
+    atom.activatePackage('language-javascript', sync: true)
     rootView.openSync('sample.js')
     editor = rootView.getActiveView()
     textGrammar = _.find syntax.grammars, (grammar) -> grammar.name is 'Plain Text'


Spec update

Spec update

--- spec/language-haskell-spec.coffee
+++ spec/language-haskell-spec.coffee
@@ -129,14 +129,14 @@
         { value : 'putStrLn', scopes : [ 'source.haskell', 'support.function.prelude.haskell' ] }
         { value : ' ', scopes : [ 'source.haskell' ] }
         { value : '::', scopes : [ 'source.haskell', 'keyword.other.double-colon.haskell' ] }
-        { value : ' ', scopes : [ 'source.haskell' ] }
-        { value : 'String', scopes : [ 'source.haskell', 'support.class.prelude.haskell' ] }
-        { value : ' ', scopes : [ 'source.haskell' ] }
-        { value : '->', scopes : [ 'source.haskell', 'keyword.other.arrow.haskell' ] }
-        { value : ' ', scopes : [ 'source.haskell' ] }
-        { value : 'IO', scopes : [ 'source.haskell', 'support.class.prelude.haskell' ] }
-        { value : ' ', scopes : [ 'source.haskell' ] }
-        { value : '()', scopes : [ 'source.haskell', 'constant.language.unit.haskell' ] }
+        { value : ' ', scopes : [ 'source.haskell', 'meta.type-signature.haskell' ] }
+        { value : 'String', scopes : [ 'source.haskell', 'meta.type-signature.haskell', 'support.class.prelude.haskell' ] }
+        { value : ' ', scopes : [ 'source.haskell', 'meta.type-signature.haskell' ] }
+        { value : '->', scopes : [ 'source.haskell', 'meta.type-signature.haskell', 'keyword.other.arrow.haskell' ] }
+        { value : ' ', scopes : [ 'source.haskell', 'meta.type-signature.haskell' ] }
+        { value : 'IO', scopes : [ 'source.haskell', 'meta.type-signature.haskell', 'support.class.prelude.haskell' ] }
+        { value : ' ', scopes : [ 'source.haskell', 'meta.type-signature.haskell' ] }
+        { value : '()', scopes : [ 'source.haskell', 'meta.type-signature.haskell', 'constant.language.unit.haskell' ] }
         { value : ')', scopes : [ 'source.haskell' ] }
         { value : ' ', scopes : [ 'source.haskell' ] }
         { value : '(', scopes : [ 'source.haskell' ] }
@@ -145,10 +145,11 @@
         { value : '"', scopes : [ 'source.haskell', 'string.quoted.double.haskell', 'punctuation.definition.string.end.haskell' ] }
         { value : ' ', scopes : [ 'source.haskell' ] }
         { value : '::', scopes : [ 'source.haskell', 'keyword.other.double-colon.haskell' ] }
-        { value : ' ', scopes : [ 'source.haskell' ] }
-        { value : 'String', scopes : [ 'source.haskell', 'support.class.prelude.haskell' ] }
+        { value : ' ', scopes : [ 'source.haskell', 'meta.type-signature.haskell' ] }
+        { value : 'String', scopes : [ 'source.haskell', 'meta.type-signature.haskell', 'support.class.prelude.haskell' ] }
         { value : ')', scopes : [ 'source.haskell' ] }
       ]
+

     it 'parses in-line non-parenthesised declarations', ->
       data = 'main = putStrLn "Hello World" :: IO ()'
@@ -164,8 +165,8 @@
         { value : '"', scopes : [ 'source.haskell', 'string.quoted.double.haskell', 'punctuation.definition.string.end.haskell' ] }
         { value : ' ', scopes : [ 'source.haskell' ] }
         { value : '::', scopes : [ 'source.haskell', 'keyword.other.double-colon.haskell' ] }
-        { value : ' ', scopes : [ 'source.haskell' ] }
-        { value : 'IO', scopes : [ 'source.haskell', 'support.class.prelude.haskell' ] }
-        { value : ' ', scopes : [ 'source.haskell' ] }
-        { value : '()', scopes : [ 'source.haskell', 'constant.language.unit.haskell' ] }
+        { value : ' ', scopes : [ 'source.haskell', 'meta.type-signature.haskell' ] }
+        { value : 'IO', scopes : [ 'source.haskell', 'meta.type-signature.haskell', 'support.class.prelude.haskell' ] }
+        { value : ' ', scopes : [ 'source.haskell', 'meta.type-signature.haskell' ] }
+        { value : '()', scopes : [ 'source.haskell', 'meta.type-signature.haskell', 'constant.language.unit.haskell' ] }
       ]


fix panel view status reset

fix panel view status reset

--- lib/view/panel.coffee
+++ lib/view/panel.coffee
@@ -56,6 +56,8 @@
   setStatus: (string, type) ->
     if type
       @status.attr 'class', 'text-' + type
+    else
+      @status.attr 'class', ''
     @status.text string

   attach: ->


Fail safely in cursors observer + better name for variables

--- lib/y-richtext.coffee
+++ lib/y-richtext.coffee
@@ -145,11 +145,14 @@
     @_model.getContent("cursors").observe (events)=> @locker.try ()=>
       for event in events
         author = event.changedBy
-        refOfWord = event.object.val(author)
-        if refOfWord is null
+        ref_to_char = event.object.val(author)
+        if ref_to_char is null
           position = @editor.getLength()
-        else if refOfWord?
-          position = refOfWord.getPosition()
+        else if ref_to_char?
+          position = ref_to_char.getPosition()
+        else
+          console.warn "ref_to_char is undefined"
+          return

         params =
           id: author


HTTP requests: improve error message, info

--- node-http-client.coffee
+++ node-http-client.coffee
@@ -18,11 +18,11 @@

 _resParsing = (c) ->
   (e, res, body) ->
-    return c e if e
+    return c e, res, body if e
     try
       info = JSON.parse body
     catch e
-      return c new Error "Error decoding JSON"
+      return c (new Error "Error decoding JSON"), res, body
     c null, res, info

@@ -40,8 +40,11 @@
     bufs = []
     res.on 'data', (data) -> bufs.push data
     res.on 'end', () ->
-      return c new Error "code #{res.statusCode}" if res.statusCode != expected_code
       body = Buffer.concat bufs
+      res.data = body
+      if res.statusCode != expected_code
+        e = new Error "Unexpected status code: #{res.statusCode}"
+        return c e, res
       c null, res, body
   if req_body
     req.end req_body


:green_heart: Fix cvs opening test

:green_heart: Fix cvs opening test

--- lib/table-edit.coffee
+++ lib/table-edit.coffee
@@ -52,6 +52,7 @@
           tableEditor.addRows(data)
           table.clearUndoStack()
           table.modified = false
+          table.updateCachedContents()

           resolve(tableEditor)


made some fancy updates to allow the iphone editor to work once again

--- demo/scripts/iphone-ot.coffee
+++ demo/scripts/iphone-ot.coffee
@@ -4,7 +4,6 @@

   toolbar.on 'update', (attributes) ->
     $.post('/ios-message/format-change', {json: JSON.stringify(attributes)})
-

   editor.getText = -> ""
   editor.setText = ->
@@ -14,15 +13,15 @@
   editor.addMessage = ->
   editor.updateComposing = ->

-
   delegate = new Object()
   jetClient = new JetClient({delegate: delegate})
-  textState = new JetTextState(editor, jetClient, JetDelta.makeDelta(Stypi.configs.head), Stypi.configs.sessionId)
+  delta = JetDelta.makeDelta(Stypi.configs.head)
+  delta.startLength = editor.doc.length
+  textState = new JetTextState(editor, jetClient, delta, Stypi.configs.sessionId)
   chatState = new JetChatState(editor, jetClient, [], Stypi.configs.sessionId)
-  cursorState = new JetCursorState(editor, jetClient, {}, Stypi.configs.sessionId)
   jetClient.addState(textState)
-  jetClient.addState(cursorState)
   jetClient.addState(chatState)
+
   jetClient.connect(Stypi.configs.docId, Stypi.configs.version)

   textState.applyDeltaToText = (delta, authorId) ->   # Hacky overwrite


Using udefine function calls

Using udefine function calls

--- src/animationframe.coffee
+++ src/animationframe.coffee
@@ -1,6 +1,6 @@
 vendors = ['ms', 'moz', 'webkit', 'o']

-define 'requestanimationframe', ['root'], (root) ->
+udefine 'requestanimationframe', ['root'], (root) ->
   {requestAnimationFrame} = root

   unless requestAnimationFrame
@@ -20,7 +20,7 @@

   requestAnimationFrame

-define 'cancelanimationframe', ['root'], (root) ->
+udefine 'cancelanimationframe', ['root'], (root) ->
   {cancelAnimationFrame} = root

   unless cancelAnimationFrame


rename banana.animate() to throw() and pass in force and angle

--- views/gorillas.coffee
+++ views/gorillas.coffee
@@ -131,7 +131,7 @@

   draw_banana:(gorilla_x, gorilla_y) ->
     banana = new Banana(@context, gorilla_x+30, gorilla_y-30)
-    banana.animate()
+    banana.throw(30, 45)

 class Banana
   constructor:(context, initial_x, initial_y) ->
@@ -142,14 +142,11 @@
     @y = 0
     @g = 9.8

-    F = 13
-    angle = 45.0
-    radian = angle*Math.PI/180
+  throw:(force, angle) ->
+    @force = force
+    @angle = angle

-    @dx = F/Math.cos(radian)
-    @dy = F/Math.sin(radian)
-
-  animate: ->
+    @calculate_initial_position()
     setTimeout (=> @draw_frame()), 150

   draw: ->
@@ -160,6 +157,11 @@
     @calculate_projection()

     setTimeout (=> @draw_frame()), 150
+
+  calculate_initial_position: ->
+    radian = @angle*Math.PI/180
+    @dx = @force/Math.cos(radian)
+    @dy = @force/Math.sin(radian)

   calculate_projection:() ->
     @x += @dx


fix issue with readranges import

fix issue with readranges import

--- angular/core/models/discussion_model.coffee
+++ angular/core/models/discussion_model.coffee
@@ -140,7 +140,7 @@
       @updateReadRanges()

     update: (attributes) ->
-      if _.isArray(@readRanges) && !_.isEqual(attributes.readRanges, @readRanges)
+      if _.isArray(@readRanges) && _.isArray(attributes.readRanges) && !_.isEqual(attributes.readRanges, @readRanges)
         attributes.readRanges = RangeSet.reduce(@readRanges.concat(attributes.readRanges))
       @baseUpdate(attributes)


reorg from the bottom to top, up to conditional

Signed-off-by: ysgard <125fa00567d70184e4d0dc6a16b66d7bf20d7d44@gmail.com>

--- grammars/d.cson
+++ grammars/d.cson
@@ -467,6 +467,634 @@
     #
     # Condition
     #
+
+
+    #
+    # Preprocessor and other directives
+    #
+    'pragma': {
+      'patterns': [
+        {
+          'match': 'pragma\\s?\\(\\s*[_\\w][_\\d\\w]*\\s*\\)',
+          'name': 'gfm.markup.header.preprocessor.pragma.d'
+        },
+        {
+          'begin': 'pragma\\s?\\(\\s*[_\\w][_\\d\\w]*\\s*,',
+          'end': '\\)',
+          'name': 'gfm.markup.header.preprocessor.pragma.arguments.d'
+          'patterns': [
+            { 'include': '#expression' },
+          ]
+        },
+        {
+          'match': '^#!.+',
+          'name': 'gfm.markup.header.preprocessor.script-tag.d'
+        }
+      ]
+    },
+    #
+    # Expressions
+    #
+    # We cheat a bit here, since highlighting expressions is overkill, and in
+    # any case we want lexical entities like strings and numbers to be
+    # clearly visible.  So we group expressions into a meta class for the
+    # purposes of other entities.  Expression entities themselves just highlight
+    # lexical elements as well as the base expression operators, and a few
+    # others.
+    #
+    'expression': {
+      'patterns': [
+          { 'include': '#operator' },
+          {
+            'match': ',',
+            'name': 'keyword.operator.comma.d'
+          }
+          { 'include': '#lexical' },
+      ]
+    },
+    'conditional-expression': {
+      'patterns': [
+        {
+          # ternary
+          'match': '\\?|:',
+          'name': 'keyword.operator.ternary.d'
+        },
+        {
+          # logical
+          'match': '!<>=|!<=|!>=|<>=|!<>|<=|>=|<>|!>|!<|==|!=|&&|\\|\\||<|>',
+          'name': 'keyword.operator.logical.d'
+        },
+        { 'include': '#lexical' }
+      ]
+    }
+    'operator': {
+      'patterns': [
+        {
+          # assign
+          'match': '=|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^=|~=|<<=|>>=|>>>=|^^=',
+          'name': 'keyword.operator.assign.d'
+        },
+        {
+          # ternary
+          'match': '\\?|:',
+          'name': 'keyword.operator.ternary.d'
+        },
+        {
+          # logical
+          'match': '!<>=|!<=|!>=|<>=|!<>|<=|>=|<>|!>|!<|==|!=|&&|\\|\\||<|>',
+          'name': 'keyword.operator.logical.d'
+        },
+        {
+          # bitwise
+          'match': '\\||\\^|&|<<|>>|>>>',
+          'name': 'keyword.operator.bitwise.d'
+        },
+        {
+          # arithmetic
+          'match': '\\+|-|\\*|/|%',
+          'name': 'keyword.operator.arithmetic.d'
+        },
+        {
+          # unary
+          'match': '[=~]\s*(&|\\+\\+|\\--|\\*|-|\\+|!)',
+          'name': 'keyword.operator.unary.d'
+        },
+        {
+          # misc
+          'match': '~|(\\s+!in\\s+)|(\\s+in\\s+)',
+          'name': 'keyword.operator.misc.d'
+        },
+        {
+          # primary expressions
+          'match': 'this|super|null|true|false|$',
+          'name': 'constant.language.primary.d'
+        }
+
+      ]
+    },
+    'assign-operator': {
+      'patterns': [
+        {
+          'match': '=|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^=|~=|<<=|>>=|>>>=|^^=',
+          'name': 'keyword.operator.assign.d'
+        },
+        { 'include': '#expression' },
+      ]
+    },
+    'ternary-operator': {
+      'patterns:': [
+        {
+          'match': '(\\?|:)',
+          'name': 'keyword.operator.ternary.d'
+        },
+        {
+          'include': '#expression',
+        }
+      ]
+    },
+    #
+    # Template
+    #
+    'template-declaration': {
+      'patterns': [
+        {
+          'match': 'template',
+          'name': 'support.class.template.d',
+        },
+        { 'include': '#identifier' },
+        { 'include': '#template-parameters' },
+        { 'include': '#constraint' },
+        {
+          'begin': '\\{',
+          'beginCaptures': { '0': { 'name': 'support.class.template.decldefs.begin.d' } },
+          'end': '\\}',
+          'endCaptures': { '0': { 'name': 'support.class.template.decldefs.end.d' } },
+          'patterns': [
+              { 'include': '#decl-defs' }
+          ]
+        }
+      ]
+    },
+    'template-parameters': {
+      'patterns': [
+        'begin': '\\(',
+        'beginCaptures': { '0': { 'name': 'support.class.template.parameters.begin.d' } },
+        'end': '\\)',
+        'endCaptures': { '0': { 'name': 'support.class.template.parameters.end.d' } },
+        'patterns': [
+          { 'include': '#template-parameter-list' }
+        ]
+      ]
+    },
+    'template-parameter-list': {
+      'patterns': [
+        {
+          'match': ',',
+          'name': 'keyword.operator.comma.d'
+        },
+        { 'include': '#template-parameter' }
+      ]
+    }
+    'template-parameter': {
+      'patterns': [
+        { 'include': '#template-type-parameter' },
+        { 'include': '#template-value-parameter' },
+        { 'include': '#template-alias-parameter' },
+        { 'include': '#template-tuple-parameter' },
+        { 'include': '#template-this-parameter' },
+      ]
+    },
+    'constraint': {
+      'patterns': [
+        'begin': 'if\\s+\\(',
+        'beginCaptures': { '0': { 'name': 'support.class.template.constraint.begin.d' } },
+        'end': '\\)',
+        'endCaptures': { '0': { 'name': 'support.class.template.constraint.end.d' } },
+        'patterns': [
+          { 'include': '#expression' }
+        ]
+      ]
+    },
+    'template-instance': {
+      'patterns': [
+        { 'include': '#template-arguments' },
+        { 'include': '#identifier' }
+      ]
+    },
+    'template-arguments': {
+      'patterns': [
+        {
+          'begin': '!\\s+\\(',
+          'beginCaptures': { '0': { 'name': 'support.class.template.arguments.begin.d' } },
+          'end': '\\)',
+          'endCaptures': { '0': { 'name': 'support.class.template.arguments.end.d' } },
+          'patterns': [
+            { 'include': '#template-argument-list' }
+          ]
+        },
+        {
+          'match': '!',
+          'name': 'support.class.template.argument.d'
+        },
+        { 'include': '#template-single-argument' }
+      ]
+    }
+    'template-argument-list': {
+      'patterns': [
+        {
+          'match': ',',
+          'name': 'keyword.operator.comma.d'
+        },
+        { 'include': '#template-argument' }
+      ]
+    },
+    'template-argument': {
+      'patterns': [
+        { 'include': '#type' },
+        { 'include': '#expression' },
+        { 'include': '#symbol' }
+      ]
+    },
+    'symbol': {
+      'patterns': [
+        { 'include': '#symbol-tail' }
+      ]
+    },
+    'symbol-tail': {
+      'patterns': [
+        { 'include': '#template-instance' },
+        { 'include': '#identifier' },
+      ]
+    },
+    'template-single-argument': {
+      'patterns': [
+        {
+          'match': 'true|false|null|this',
+          'name': 'constant.language.template-single-argument.d'
+        },
+        { 'include': '#special-keyword' },
+        { 'include': '#basic-type-x' },
+        { 'include': '#string-literal' },
+        { 'include': '#character-literal' },
+        { 'include': '#integer-literal' },
+        { 'include': '#float-literal' },
+        { 'include': '#identifier' }
+      ]
+    },
+    'template-type-parameter': {
+      'patterns': [
+        { 'include': '#identifier' },
+        { 'include': '#template-type-parameter-specialization' },
+        { 'include': '#template-type-parameter-default' },
+      ]
+    },
+    'template-type-parameter-specialization': {
+      'patterns': [
+        {
+          'match': ':',
+          'name': 'support.class.tag.d'
+        },
+        { 'include': '#type' }
+      ]
+    },
+    'template-type-parameter-default': {
+      'patterns': [
+        {
+          'match': '=',
+          'name': 'support.class.template.operator.equal.d'
+        },
+        { 'include': '#type' }
+      ]
+    },
+    'template-this-parameter': {
+      'patterns': [
+        {
+          'match': 'this',
+          'name': 'support.class.keyword.this.d'
+        },
+        { 'include': '#template-type-parameter' }
+      ]
+    },
+    'template-value-parameter': {
+      'patterns': [
+        { 'include': '#basic-type' },
+        { 'include': '#declarator' },
+        { 'include': '#template-value-parameter-specialization' },
+        { 'include': '#template-value-parameter-default' },
+      ]
+    },
+    'template-value-parameter-specialization': {
+      'patterns': [
+        {
+          'match': ':',
+          'name': 'support.class.tag.d'
+        },
+        { 'include': '#conditional-expression' }
+      ]
+    },
+    'template-value-parameter-default': {
+      'patterns': [
+        {
+          'match': '=',
+          'name': 'support.class.template.operator.equal.d'
+        },
+        { 'include': '#expression' },
+        { 'include': '#special-keyword' }
+      ]
+    },
+    'template-alias-parameter': {
+      'patterns': [
+        {
+          'match': 'alias',
+          'name': 'support.class.template.alias.d'
+        },
+        { 'include': '#identifier' },
+        { 'include': '#template-alias-parameter-specialization' },
+        { 'include': '#template-alias-parameter-default' },
+        { 'include': '#basic-type' },
+        { 'include': '#declarator' },
+      ]
+    },
+    'template-alias-parameter-specialization': {
+      'patterns': [
+        {
+          'match': ':',
+          'name': 'support.class.tag.d'
+        },
+        { 'include': '#type' },
+        { 'include': '#conditional-expression' }
+      ]
+    },
+    'template-alias-parameter-default': {
+      'patterns': [
+        {
+          'match': '=',
+          'name': 'support.class.template.operator.equal.d'
+        },
+        { 'include': '#type' },
+        { 'include': '#conditional-expression' }
+      ]
+    },
+    'template-tuple-parameter': {
+      'patterns': [
+        { 'include': '#identifier' },
+        {
+          'match': '\\.\\.\\.',
+          'name': 'support.type.tuple.d'
+        }
+      ]
+    },
+    'template-mixin-declaration': {
+      'patterns': [
+        'begin': 'mixin\\s+template\\s+',
+        'beginCaptures': { '0': { 'name': 'support.class.template.mixin.template.begin.d' } },
+        'end': '\\{(.*)\\}',
+        'endCaptures': {
+          '0': {
+            'name': 'support.class.template.mixin.template.end.d',
+            'patterns': [
+              { 'include': '#decl-defs' }
+            ]
+          },
+        },
+        'patterns': [
+          { 'include': '#identifier' },
+          { 'include': '#template-parameters' },
+          { 'include': '#constraint' }
+        ]
+      ]
+    },
+    'template-mixin': {
+      'patterns': [
+        'begin': 'mixin\\s+',
+        'beginCaptures': { '0': { 'name': 'support.class.template.mixin.begin.d' } },
+        'end': ';',
+        'endCaptures': { '0': { 'name': 'support.class.template.mixin.end.d' } },
+        'patterns': [
+          { 'include': '#mixin-template-name' },
+          { 'include': '#template-arguments' },
+          { 'include': '#identifier' }
+        ]
+      ]
+    },
+    'mixin-template-name': {
+      'patterns': [
+        { 'include': '#qualified-identifier-list' },
+        { 'include': '#typeof' },
+      ]
+    },
+    'qualified-identifier-list': {
+      'patterns': [
+        { 'include': '#identifier' },
+        { 'include': '#template-instance' },
+      ]
+    },
+
+
+
+
+    #
+    # Declarations
+    #
+    'declaration': {
+      'patterns': [
+        { 'include': '#alias-declaration' },
+        { 'include': '#aggregate-declaration' },
+        { 'include': '#enum-declaration' },
+        { 'include': '#import-declaration' },
+        { 'include': '#decl' }
+      ]
+    },
+    'alias-declaration': {
+      'patterns': [
+        {
+          'match': 'alias',
+          'name': 'keyword.alias.d'
+        },
+        { 'include': '#storage-class' },
+        { 'include': '#basic-type' },
+        { 'include': '#declarator' },
+        { 'include': '#alias-declaration-x' }
+      ]
+    },
+    #
+    # Initializers
+    #
+    'initializer': {
+      'patterns': [
+        { 'include': '#void-initializer' },
+        { 'include': '#non-void-initializer' }
+      ]
+    },
+    'void-initializer': {
+      'patterns': [
+        'match': 'void',
+        'name': 'support.type.void.d'
+      ]
+    },
+    'non-void-initializer': {
+      'patterns': [
+        { 'include': '#exp-initializer' },
+        { 'include': '#array-initializer' },
+        { 'include': '#struct-initializer' }
+      ]
+    },
+    'exp-initializer': {
+      'patterns': [
+        { 'include': '#expression' }
+      ]
+    },
+    'array-initializer': {
+      'patterns': [
+        'begin': '\\[',
+        'beginCaptures': { '0': { 'name': 'support.type.array.initializer.begin.d' } },
+        'end': '\\]',
+        'endCaptures': {'0': { 'name': 'support.type.array.initializer.end.d' } },
+        'patterns': [
+          {
+            'match': ',',
+            'name': 'keyword.operator.comma.d'
+          },
+          { 'include': '#array-member-initialization' },
+        ]
+      ]
+    },
+    'array-member-initializations': {
+      'patterns': [
+        {
+          'match': ',',
+          'name': 'keyword.operator.comma.d'
+        },
+        { 'include': '#array-member-initialization' },
+      ]
+    },
+    'array-member-initialization': {
+      'patterns': [
+        { 'include': '#non-void-initializer' },
+        { 'include': '#expression' },
+        {
+          'match': ':',
+          'name': 'support.type.tag.d'
+        }
+      ]
+    },
+    'struct-initializer': {
+      'patterns': [
+        'begin': '\\{',
+        'beginCaptures': { '0': { 'name': 'support.type.struct.initializer.begin.d' } },
+        'end': '\\}',
+        'endCaptures': { '0': { 'name': 'support.type.struct.initializer.end.d' } },
+        'patterns': [
+          { 'include': '#struct-member-initializers' }
+        ]
+      ]
+    }
+    'struct-member-initializers': {
+      'patterns': [
+        {
+          'match': ',',
+          'name': 'keyword.operator.comma.d'
+        },
+        { 'include': '#struct-member-initializer' },
+      ]
+    },
+    'struct-member-initializer': {
+      'patterns': [
+        { 'include': '#non-void-initializer' },
+        { 'include': '#identifier' },
+        {
+          'match': ':',
+          'name': 'support.type.tag.d'
+        },
+      ]
+    },
+    'default-initializer-expression': {
+      'patterns': [
+        { 'include': '#expression' },
+        { 'include': '#special-keyword' }
+      ]
+    },
+    #
+    # Assembly (Iasm)
+    #
+    'asm-statement': {
+      'patterns': [
+        'begin': 'asm\\s*\\{',
+        'end': '\\}',
+        'beginCaptures': { '0': { 'name': 'keyword.control.asm.begin.d' } },
+        'endCaptures': { '0': { 'name': 'keyword.control.asm.end.d' } },
+        'patterns': [
+          { 'include': '#asm-instruction' },
+        ],
+      ]
+    },
+    'asm-instruction': {
+      'patterns': [
+        {
+          'match': 'align|even|naked|db|ds|di|dl|df|dd|de|:',
+          'name': 'keyword.asm-instruction.d',
+        },
+        { 'include': '#operands' },
+        { 'include': '#integer-expression' }
+      ]
+    },
+    'operands': {
+      'patterns': [
+        {
+          'match': ',',
+          'name': 'keyword.operator.comma.assembly.d'
+        }
+        { 'include': '#asm-exp' },
+      ]
+    },
+    'asm-exp': {
+      'patterns': [
+        {
+          'match': '\\?|:',
+          'name': 'keyword.operator.ternary.assembly.d',
+        },
+        {
+          'match': '\\]|\\[',
+          'name': 'keyword.operator.bracket.assembly.d',
+        },
+        {
+          'match': '>>>|\\|\\||&&|==|!=|<=|>=|<<|>>|\\||\\^|&|<|>|\\+|-|\\*|/|%|~|!',
+          'name': 'keyword.operator.assembly.d'
+        },
+        { 'include': '#asm-una-exp' },
+      ]
+    }
+    'integer-expression': {
+      'patterns': [
+        { 'include': '#integer-literal' },
+        { 'include': '#identifier' },
+      ]
+    },
+    'asm-una-exp': {
+      'patterns': [
+        { 'include': '#asm-type-prefix' },
+        {
+          'match': 'offsetof|seg',
+          'name': 'support.type.assembly.d'
+        },
+        { 'include': '#asm-primary-expression' },
+      ]
+    },
+    'asm-primary-expression': {
+      'patterns': [
+        { 'include': '#integer-literal' },
+        { 'include': '#float-literal' },
+        {
+          'match': '__LOCAL_SIZE',
+          'name': 'constant.language.assembly.d'
+        },
+        { 'include': '#register' },
+        { 'include': '#register-64' },
+        { 'include': '#identifier' }
+      ]
+    },
+    'asm-type-prefix': {
+      'patterns': [
+        'match': '(near\\s+ptr)|(far\\s+ptr)|(byte\\s+ptr)|(short\\s+ptr)|(int\\s+ptr)|(word\\s+ptr)|(dword\\s+ptr)|(qword\\s+ptr)|(float\\s+ptr)|(double\\s+ptr)|(real\\s+ptr)',
+        'name': 'support.type.asm-type-prefix.d',
+      ]
+    },
+    'register': {
+      'patterns': [
+        'match': 'XMM0|XMM1|XMM2|XMM3|XMM4|XMM5|XMM6|XMM7|MM0|MM1|MM2|MM3|MM4|MM5|MM6|MM7|ST\\(0\\)|ST\\(1\\)|ST\\(2\\)|ST\\(3\\)|ST\\(4\\)|ST\\(5\\)|ST\\(6\\)|ST\\(7\\)|ST|TR1|TR2|TR3|TR4|TR5|TR6|TR7|DR0|DR1|DR2|DR3|DR4|DR5|DR6|DR7|CR0|CR2|CR3|CR4|EAX|EBX|ECX|EDX|EBP|ESP|EDI|ESI|AL|AH|AX|BL|BH|BX|CL|CH|CX|DL|DH|DX|BP|SP|DI|SI|ES|CS|SS|DS|GS|FS',
+        'name': 'storage.type.assembly.register.d',
+      ]
+    },
+    'register-64': {
+      'patterns': [
+        'match': 'RAX|RBX|RCX|RDX|BPL|RBP|SPL|RSP|DIL|RDI|SIL|RSI|R8B|R8W|R8D|R8|R9B|R9W|R9D|R9|R10B|R10W|R10D|R10|R11B|R11W|R11D|R11|R12B|R12W|R12D|R12|R13B|R13W|R13D|R13|R14B|R14W|R14D|R14|R15B|R15W|R15D|R15|XMM8|XMM9|XMM10|XMM11|XMM12|XMM13|XMM14|XMM15|YMM0|YMM1|YMM2|YMM3|YMM4|YMM5|YMM6|YMM7|YMM8|YMM9|YMM10|YMM11|YMM12|YMM13|YMM14|YMM15',
+        'name': 'storage.type.assembly.register-64.d'
+      ]
+    },
+
+    #############
+    # Conditional
+    #############
     'conditional-declaration': {
       'patterns': [
         { 'include': '#condition' },
@@ -475,10 +1103,7 @@
           'match': 'else',
           'name': 'keyword.control.else.d'
         },
-        {
-          'match': ':',
-          'name': 'support.class.tag.d'
-        },
+        { 'include': '#colon' },
         { 'include': '#decl-defs' },
       ]
     },
@@ -577,27 +1202,9 @@
         ],
       ]
     },
-    #
-    # Static assert
-    #
-    'static-assert': {
-      'patterns': [
-        'begin': 'static\\s+assert\\s*\\(',
-        'end': '\\)',
-        'beginCaptures': { '0': { 'name': 'keyword.static-assert.begin.d' } },
-        'endCaptures': { '0': { 'name': 'keyword.static-assert.end.d' } },
-        'patterns': [
-          {
-            'match': ',',
-            'name': 'keyword.operator.comma.d'
-          },
-          { 'include': '#expression' }
-        ]
-      ]
-    },
-    #
-    # Import and Module
-    #
+    ########
+    # Module
+    ########
     'module': {
       'packages': [
         { 'import': '#module-declaration' },
@@ -633,650 +1240,43 @@
         { 'include': '#mixin-declaration' },
         { 'include': '#shared-static-constructor' },
         { 'include': '#shared-static-destructor' },
-        {
-          'match': ';',
-          'name': 'meta.semicolon.d'
-        },
-      ]
-    },
-
-
+        { 'include': '#semi-colon' },
+      ]
+    },
     'module-declaration': {
       'patterns': [
         'begin': 'module\\s+',
         'beginCaptures': { '0': { 'name': 'keyword.package.module.d' } },
         'end': ';',
         'patterns': [
-          { 'include': '#identifier' }
-        ]
-      ]
-    },
-
-    #
-    # Preprocessor and other directives
-    #
-    'pragma': {
-      'patterns': [
-        {
-          'match': 'pragma\\s?\\(\\s*[_\\w][_\\d\\w]*\\s*\\)',
-          'name': 'gfm.markup.header.preprocessor.pragma.d'
-        },
-        {
-          'begin': 'pragma\\s?\\(\\s*[_\\w][_\\d\\w]*\\s*,',
-          'end': '\\)',
-          'name': 'gfm.markup.header.preprocessor.pragma.arguments.d'
-          'patterns': [
-            { 'include': '#expression' },
-          ]
-        },
-        {
-          'match': '^#!.+',
-          'name': 'gfm.markup.header.preprocessor.script-tag.d'
-        }
-      ]
-    },
-    #
-    # Expressions
-    #
-    # We cheat a bit here, since highlighting expressions is overkill, and in
-    # any case we want lexical entities like strings and numbers to be
-    # clearly visible.  So we group expressions into a meta class for the
-    # purposes of other entities.  Expression entities themselves just highlight
-    # lexical elements as well as the base expression operators, and a few
-    # others.
-    #
-    'expression': {
-      'patterns': [
-          { 'include': '#operator' },
-          {
-            'match': ',',
-            'name': 'keyword.operator.comma.d'
-          }
-          { 'include': '#lexical' },
-      ]
-    },
-    'conditional-expression': {
-      'patterns': [
-        {
-          # ternary
-          'match': '\\?|:',
-          'name': 'keyword.operator.ternary.d'
-        },
-        {
-          # logical
-          'match': '!<>=|!<=|!>=|<>=|!<>|<=|>=|<>|!>|!<|==|!=|&&|\\|\\||<|>',
-          'name': 'keyword.operator.logical.d'
-        },
-        { 'include': '#lexical' }
-      ]
-    }
-    'operator': {
-      'patterns': [
-        {
-          # assign
-          'match': '=|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^=|~=|<<=|>>=|>>>=|^^=',
-          'name': 'keyword.operator.assign.d'
-        },
-        {
-          # ternary
-          'match': '\\?|:',
-          'name': 'keyword.operator.ternary.d'
-        },
-        {
-          # logical
-          'match': '!<>=|!<=|!>=|<>=|!<>|<=|>=|<>|!>|!<|==|!=|&&|\\|\\||<|>',
-          'name': 'keyword.operator.logical.d'
-        },
-        {
-          # bitwise
-          'match': '\\||\\^|&|<<|>>|>>>',
-          'name': 'keyword.operator.bitwise.d'
-        },
-        {
-          # arithmetic
-          'match': '\\+|-|\\*|/|%',
-          'name': 'keyword.operator.arithmetic.d'
-        },
-        {
-          # unary
-          'match': '[=~]\s*(&|\\+\\+|\\--|\\*|-|\\+|!)',
-          'name': 'keyword.operator.unary.d'
-        },
-        {
-          # misc
-          'match': '~|(\\s+!in\\s+)|(\\s+in\\s+)',
-          'name': 'keyword.operator.misc.d'
-        },
-        {
-          # primary expressions
-          'match': 'this|super|null|true|false|$',
-          'name': 'constant.language.primary.d'
-        }
-
-      ]
-    },
-    'assign-operator': {
-      'patterns': [
-        {
-          'match': '=|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^=|~=|<<=|>>=|>>>=|^^=',
-          'name': 'keyword.operator.assign.d'
-        },
-        { 'include': '#expression' },
-      ]
-    },
-    'ternary-operator': {
-      'patterns:': [
-        {
-          'match': '(\\?|:)',
-          'name': 'keyword.operator.ternary.d'
-        },
-        {
-          'include': '#expression',
-        }
-      ]
-    },
-    #
-    # Template
-    #
-    'template-declaration': {
-      'patterns': [
-        {
-          'match': 'template',
-          'name': 'support.class.template.d',
-        },
-        { 'include': '#identifier' },
-        { 'include': '#template-parameters' },
-        { 'include': '#constraint' },
-        {
-          'begin': '\\{',
-          'beginCaptures': { '0': { 'name': 'support.class.template.decldefs.begin.d' } },
-          'end': '\\}',
-          'endCaptures': { '0': { 'name': 'support.class.template.decldefs.end.d' } },
-          'patterns': [
-              { 'include': '#decl-defs' }
-          ]
-        }
-      ]
-    },
-    'template-parameters': {
-      'patterns': [
-        'begin': '\\(',
-        'beginCaptures': { '0': { 'name': 'support.class.template.parameters.begin.d' } },
-        'end': '\\)',
-        'endCaptures': { '0': { 'name': 'support.class.template.parameters.end.d' } },
-        'patterns': [
-          { 'include': '#template-parameter-list' }
-        ]
-      ]
-    },
-    'template-parameter-list': {
-      'patterns': [
-        {
-          'match': ',',
-          'name': 'keyword.operator.comma.d'
-        },
-        { 'include': '#template-parameter' }
-      ]
-    }
-    'template-parameter': {
-      'patterns': [
-        { 'include': '#template-type-parameter' },
-        { 'include': '#template-value-parameter' },
-        { 'include': '#template-alias-parameter' },
-        { 'include': '#template-tuple-parameter' },
-        { 'include': '#template-this-parameter' },
-      ]
-    },
-    'constraint': {
-      'patterns': [
-        'begin': 'if\\s+\\(',
-        'beginCaptures': { '0': { 'name': 'support.class.template.constraint.begin.d' } },
-        'end': '\\)',
-        'endCaptures': { '0': { 'name': 'support.class.template.constraint.end.d' } },
-        'patterns': [
-          { 'include': '#expression' }
-        ]
-      ]
-    },
-    'template-instance': {
-      'patterns': [
-        { 'include': '#template-arguments' },
+          { 'include': '#module-fully-qualified-name' }
+        ]
+      ]
+    },
+    'module-fully-qualified-name': {
+      'patterns': [
+        { 'include': '#module-name' },
+      ]
+    },
+    'module-name': {
+      'patterns': [
         { 'include': '#identifier' }
       ]
     },
-    'template-arguments': {
-      'patterns': [
-        {
-          'begin': '!\\s+\\(',
-          'beginCaptures': { '0': { 'name': 'support.class.template.arguments.begin.d' } },
-          'end': '\\)',
-          'endCaptures': { '0': { 'name': 'support.class.template.arguments.end.d' } },
-          'patterns': [
-            { 'include': '#template-argument-list' }
-          ]
-        },
-        {
-          'match': '!',
-          'name': 'support.class.template.argument.d'
-        },
-        { 'include': '#template-single-argument' }
-      ]
-    }
-    'template-argument-list': {
-      'patterns': [
-        {
-          'match': ',',
-          'name': 'keyword.operator.comma.d'
-        },
-        { 'include': '#template-argument' }
-      ]
-    },
-    'template-argument': {
-      'patterns': [
-        { 'include': '#type' },
-        { 'include': '#expression' },
-        { 'include': '#symbol' }
-      ]
-    },
-    'symbol': {
-      'patterns': [
-        { 'include': '#symbol-tail' }
-      ]
-    },
-    'symbol-tail': {
-      'patterns': [
-        { 'include': '#template-instance' },
-        { 'include': '#identifier' },
-      ]
-    },
-    'template-single-argument': {
-      'patterns': [
-        {
-          'match': 'true|false|null|this',
-          'name': 'constant.language.template-single-argument.d'
-        },
-        { 'include': '#special-keyword' },
-        { 'include': '#basic-type-x' },
-        { 'include': '#string-literal' },
-        { 'include': '#character-literal' },
-        { 'include': '#integer-literal' },
-        { 'include': '#float-literal' },
+    'packages': {
+      'patterns': [
+        { 'include': '#package-name' }
+      ]
+    },
+    'package-name': {
+      'patterns': [
         { 'include': '#identifier' }
       ]
     },
-    'template-type-parameter': {
-      'patterns': [
-        { 'include': '#identifier' },
-        { 'include': '#template-type-parameter-specialization' },
-        { 'include': '#template-type-parameter-default' },
-      ]
-    },
-    'template-type-parameter-specialization': {
-      'patterns': [
-        {
-          'match': ':',
-          'name': 'support.class.tag.d'
-        },
-        { 'include': '#type' }
-      ]
-    },
-    'template-type-parameter-default': {
-      'patterns': [
-        {
-          'match': '=',
-          'name': 'support.class.template.operator.equal.d'
-        },
-        { 'include': '#type' }
-      ]
-    },
-    'template-this-parameter': {
-      'patterns': [
-        {
-          'match': 'this',
-          'name': 'support.class.keyword.this.d'
-        },
-        { 'include': '#template-type-parameter' }
-      ]
-    },
-    'template-value-parameter': {
-      'patterns': [
-        { 'include': '#basic-type' },
-        { 'include': '#declarator' },
-        { 'include': '#template-value-parameter-specialization' },
-        { 'include': '#template-value-parameter-default' },
-      ]
-    },
-    'template-value-parameter-specialization': {
-      'patterns': [
-        {
-          'match': ':',
-          'name': 'support.class.tag.d'
-        },
-        { 'include': '#conditional-expression' }
-      ]
-    },
-    'template-value-parameter-default': {
-      'patterns': [
-        {
-          'match': '=',
-          'name': 'support.class.template.operator.equal.d'
-        },
-        { 'include': '#expression' },
-        { 'include': '#special-keyword' }
-      ]
-    },
-    'template-alias-parameter': {
-      'patterns': [
-        {
-          'match': 'alias',
-          'name': 'support.class.template.alias.d'
-        },
-        { 'include': '#identifier' },
-        { 'include': '#template-alias-parameter-specialization' },
-        { 'include': '#template-alias-parameter-default' },
-        { 'include': '#basic-type' },
-        { 'include': '#declarator' },
-      ]
-    },
-    'template-alias-parameter-specialization': {
-      'patterns': [
-        {
-          'match': ':',
-          'name': 'support.class.tag.d'
-        },
-        { 'include': '#type' },
-        { 'include': '#conditional-expression' }
-      ]
-    },
-    'template-alias-parameter-default': {
-      'patterns': [
-        {
-          'match': '=',
-          'name': 'support.class.template.operator.equal.d'
-        },
-        { 'include': '#type' },
-        { 'include': '#conditional-expression' }
-      ]
-    },
-    'template-tuple-parameter': {
-      'patterns': [
-        { 'include': '#identifier' },
-        {
-          'match': '\\.\\.\\.',
-          'name': 'support.type.tuple.d'
-        }
-      ]
-    },
-    'template-mixin-declaration': {
-      'patterns': [
-        'begin': 'mixin\\s+template\\s+',
-        'beginCaptures': { '0': { 'name': 'support.class.template.mixin.template.begin.d' } },
-        'end': '\\{(.*)\\}',
-        'endCaptures': {
-          '0': {
-            'name': 'support.class.template.mixin.template.end.d',
-            'patterns': [
-              { 'include': '#decl-defs' }
-            ]
-          },
-        },
-        'patterns': [
-          { 'include': '#identifier' },
-          { 'include': '#template-parameters' },
-          { 'include': '#constraint' }
-        ]
-      ]
-    },
-    'template-mixin': {
-      'patterns': [
-        'begin': 'mixin\\s+',
-        'beginCaptures': { '0': { 'name': 'support.class.template.mixin.begin.d' } },
-        'end': ';',
-        'endCaptures': { '0': { 'name': 'support.class.template.mixin.end.d' } },
-        'patterns': [
-          { 'include': '#mixin-template-name' },
-          { 'include': '#template-arguments' },
-          { 'include': '#identifier' }
-        ]
-      ]
-    },
-    'mixin-template-name': {
-      'patterns': [
-        { 'include': '#qualified-identifier-list' },
-        { 'include': '#typeof' },
-      ]
-    },
-    'qualified-identifier-list': {
-      'patterns': [
-        { 'include': '#identifier' },
-        { 'include': '#template-instance' },
-      ]
-    },
-
-
-
-
-    #
-    # Declarations
-    #
-    'declaration': {
-      'patterns': [
-        { 'include': '#alias-declaration' },
-        { 'include': '#aggregate-declaration' },
-        { 'include': '#enum-declaration' },
-        { 'include': '#import-declaration' },
-        { 'include': '#decl' }
-      ]
-    },
-    'alias-declaration': {
-      'patterns': [
-        {
-          'match': 'alias',
-          'name': 'keyword.alias.d'
-        },
-        { 'include': '#storage-class' },
-        { 'include': '#basic-type' },
-        { 'include': '#declarator' },
-        { 'include': '#alias-declaration-x' }
-      ]
-    },
-    #
-    # Initializers
-    #
-    'initializer': {
-      'patterns': [
-        { 'include': '#void-initializer' },
-        { 'include': '#non-void-initializer' }
-      ]
-    },
-    'void-initializer': {
-      'patterns': [
-        'match': 'void',
-        'name': 'support.type.void.d'
-      ]
-    },
-    'non-void-initializer': {
-      'patterns': [
-        { 'include': '#exp-initializer' },
-        { 'include': '#array-initializer' },
-        { 'include': '#struct-initializer' }
-      ]
-    },
-    'exp-initializer': {
-      'patterns': [
-        { 'include': '#expression' }
-      ]
-    },
-    'array-initializer': {
-      'patterns': [
-        'begin': '\\[',
-        'beginCaptures': { '0': { 'name': 'support.type.array.initializer.begin.d' } },
-        'end': '\\]',
-        'endCaptures': {'0': { 'name': 'support.type.array.initializer.end.d' } },
-        'patterns': [
-          {
-            'match': ',',
-            'name': 'keyword.operator.comma.d'
-          },
-          { 'include': '#array-member-initialization' },
-        ]
-      ]
-    },
-    'array-member-initializations': {
-      'patterns': [
-        {
-          'match': ',',
-          'name': 'keyword.operator.comma.d'
-        },
-        { 'include': '#array-member-initialization' },
-      ]
-    },
-    'array-member-initialization': {
-      'patterns': [
-        { 'include': '#non-void-initializer' },
-        { 'include': '#expression' },
-        {
-          'match': ':',
-          'name': 'support.type.tag.d'
-        }
-      ]
-    },
-    'struct-initializer': {
-      'patterns': [
-        'begin': '\\{',
-        'beginCaptures': { '0': { 'name': 'support.type.struct.initializer.begin.d' } },
-        'end': '\\}',
-        'endCaptures': { '0': { 'name': 'support.type.struct.initializer.end.d' } },
-        'patterns': [
-          { 'include': '#struct-member-initializers' }
-        ]
-      ]
-    }
-    'struct-member-initializers': {
-      'patterns': [
-        {
-          'match': ',',
-          'name': 'keyword.operator.comma.d'
-        },
-        { 'include': '#struct-member-initializer' },
-      ]
-    },
-    'struct-member-initializer': {
-      'patterns': [
-        { 'include': '#non-void-initializer' },
-        { 'include': '#identifier' },
-        {
-          'match': ':',
-          'name': 'support.type.tag.d'
-        },
-      ]
-    },
-    'default-initializer-expression': {
-      'patterns': [
-        { 'include': '#expression' },
-        { 'include': '#special-keyword' }
-      ]
-    },
-    #
-    # Assembly (Iasm)
-    #
-    'asm-statement': {
-      'patterns': [
-        'begin': 'asm\\s*\\{',
-        'end': '\\}',
-        'beginCaptures': { '0': { 'name': 'keyword.control.asm.begin.d' } },
-        'endCaptures': { '0': { 'name': 'keyword.control.asm.end.d' } },
-        'patterns': [
-          { 'include': '#asm-instruction' },
-        ],
-      ]
-    },
-    'asm-instruction': {
-      'patterns': [
-        {
-          'match': 'align|even|naked|db|ds|di|dl|df|dd|de|:',
-          'name': 'keyword.asm-instruction.d',
-        },
-        { 'include': '#operands' },
-        { 'include': '#integer-expression' }
-      ]
-    },
-    'operands': {
-      'patterns': [
-        {
-          'match': ',',
-          'name': 'keyword.operator.comma.assembly.d'
-        }
-        { 'include': '#asm-exp' },
-      ]
-    },
-    'asm-exp': {
-      'patterns': [
-        {
-          'match': '\\?|:',
-          'name': 'keyword.operator.ternary.assembly.d',
-        },
-        {
-          'match': '\\]|\\[',
-          'name': 'keyword.operator.bracket.assembly.d',
-        },
-        {
-          'match': '>>>|\\|\\||&&|==|!=|<=|>=|<<|>>|\\||\\^|&|<|>|\\+|-|\\*|/|%|~|!',
-          'name': 'keyword.operator.assembly.d'
-        },
-        { 'include': '#asm-una-exp' },
-      ]
-    }
-    'integer-expression': {
-      'patterns': [
-        { 'include': '#integer-literal' },
-        { 'include': '#identifier' },
-      ]
-    },
-    'asm-una-exp': {
-      'patterns': [
-        { 'include': '#asm-type-prefix' },
-        {
-          'match': 'offsetof|seg',
-          'name': 'support.type.assembly.d'
-        },
-        { 'include': '#asm-primary-expression' },
-      ]
-    },
-    'asm-primary-expression': {
-      'patterns': [
-        { 'include': '#integer-literal' },
-        { 'include': '#float-literal' },
-        {
-          'match': '__LOCAL_SIZE',
-          'name': 'constant.language.assembly.d'
-        },
-        { 'include': '#register' },
-        { 'include': '#register-64' },
-        { 'include': '#identifier' }
-      ]
-    },
-    'asm-type-prefix': {
-      'patterns': [
-        'match': '(near\\s+ptr)|(far\\s+ptr)|(byte\\s+ptr)|(short\\s+ptr)|(int\\s+ptr)|(word\\s+ptr)|(dword\\s+ptr)|(qword\\s+ptr)|(float\\s+ptr)|(double\\s+ptr)|(real\\s+ptr)',
-        'name': 'support.type.asm-type-prefix.d',
-      ]
-    },
-    'register': {
-      'patterns': [
-        'match': 'XMM0|XMM1|XMM2|XMM3|XMM4|XMM5|XMM6|XMM7|MM0|MM1|MM2|MM3|MM4|MM5|MM6|MM7|ST\\(0\\)|ST\\(1\\)|ST\\(2\\)|ST\\(3\\)|ST\\(4\\)|ST\\(5\\)|ST\\(6\\)|ST\\(7\\)|ST|TR1|TR2|TR3|TR4|TR5|TR6|TR7|DR0|DR1|DR2|DR3|DR4|DR5|DR6|DR7|CR0|CR2|CR3|CR4|EAX|EBX|ECX|EDX|EBP|ESP|EDI|ESI|AL|AH|AX|BL|BH|BX|CL|CH|CX|DL|DH|DX|BP|SP|DI|SI|ES|CS|SS|DS|GS|FS',
-        'name': 'storage.type.assembly.register.d',
-      ]
-    },
-    'register-64': {
-      'patterns': [
-        'match': 'RAX|RBX|RCX|RDX|BPL|RBP|SPL|RSP|DIL|RDI|SIL|RSI|R8B|R8W|R8D|R8|R9B|R9W|R9D|R9|R10B|R10W|R10D|R10|R11B|R11W|R11D|R11|R12B|R12W|R12D|R12|R13B|R13W|R13D|R13|R14B|R14W|R14D|R14|R15B|R15W|R15D|R15|XMM8|XMM9|XMM10|XMM11|XMM12|XMM13|XMM14|XMM15|YMM0|YMM1|YMM2|YMM3|YMM4|YMM5|YMM6|YMM7|YMM8|YMM9|YMM10|YMM11|YMM12|YMM13|YMM14|YMM15',
-        'name': 'storage.type.assembly.register-64.d'
-      ]
-    },
-    #
+
+    #####################
     # Import Declarations
-    #
+    #####################
     'import-declaration': {
       'patterns': [
         {
@@ -1318,10 +1318,7 @@
     'import-bind': {
       'patterns': [
         { 'include': '#identifier' },
-        {
-          'match': '=',
-          'name': 'keyword.operator.equal.d'
-        }
+        { 'include': '#equal' }
       ]
     },
     'module-alias-identifier': {
@@ -1329,9 +1326,9 @@
         { 'include': '#identifier' }
       ]
     }
-    #
+    ############
     # Mixin Decl
-    #
+    ############
     'mixin-declaration': {
       'patterns': [
         'begin': 'mixin\\s*\\(',
@@ -1343,9 +1340,9 @@
         ]
       ]
     },
-    #
+    ######
     # Misc
-    #
+    ######
     'comma': {
       'patterns': [
         'match': ',',
@@ -1362,6 +1359,12 @@
       'patterns': [
         'match': '=',
         'name': 'keyword.operator.equal.d'
+      ]
+    },
+    'semi-colon' {
+      'patterns': [
+        'match': ';',
+        'name': 'meta.statement.semi-colon.d'
       ]
     },


use a star for the exit

use a star for the exit

--- graphics.coffee
+++ graphics.coffee
@@ -214,6 +214,16 @@
           when 'program'
             @ctx.fillRect(x*@block-.5, y*@block-.5, @block, @block)
             @ctx.strokeRect(x*@block-.5, y*@block-.5, @block, @block)
+    @ctx.strokeStyle = 'white'
+    @ctx.lineWidth = 1.5
+    for x in [0...@game_state.Wi]
+      for y in [0...@game_state.Hi]
+        switch @game_state.level[x][y].type
+          when 'exit'
+            @ctx.save()
+            @ctx.translate((x+.5)*@block-.5, (y+.5)*@block-.5)
+            @renderShape('star5', @block*.375)
+            @ctx.restore()
     @ctx.restore()

   renderBot: (sprite) =>
@@ -227,6 +237,7 @@
         @ctx.scale(stretch, 1/stretch)
     @ctx.lineWidth = 1.5
     @renderShape('circle', @block*.4, true)
+    #@renderShape('star8', @block*.4, true)

   makeBotSprite: ->
     gfx = this
@@ -552,8 +563,17 @@
     switch shape
       when 'circle'
         @ctx.arc(0,0,radius*.8,0,Math.PI*2)
-      when 'star'
-        @ctx.arc(0,0,radius*.8,0,Math.PI)
+      when 'star8', 'star5'
+        sides = if shape == 'star8' then 8 else 5
+        inner = radius*.5
+        @ctx.moveTo(0, -radius)
+        @ctx.save()
+        for i in [0...sides]
+          @ctx.rotate(Math.PI/sides)
+          @ctx.lineTo(0, -inner)
+          @ctx.rotate(Math.PI/sides)
+          @ctx.lineTo(0, -radius)
+        @ctx.restore()
       when 'square'
         r = radius*.75
         @ctx.moveTo(-r,-r)


activity - more gracefully handle cases when notification db hasn't been initialized

--- salvus/page/activity.coffee
+++ salvus/page/activity.coffee
@@ -232,6 +232,8 @@
     #console.log("render_notifications")
     notification_list_body.empty()
     important_count = 0
+    if not notifications_syncdb?
+        return
     v = notifications_syncdb.select(where:{table:'activity'})
     v.sort(misc.timestamp_cmp)
     ensure_titles_and_user_names v, () =>
@@ -251,6 +253,8 @@
         update_important_count(false)

 delete_oldest_notification = () ->
+    if not notifications_syncdb?
+        return
     v = notifications_syncdb.select(where:{table:'activity'})
     v.sort(misc.timestamp_cmp)
     if v.length > 0
@@ -276,7 +280,7 @@
 is_important = (x) -> not x.seen and x.actions?['comment']

 update_important_count = (recalculate) ->
-    if recalculate
+    if recalculate and notifications_syncdb?
         important_count = (x for x in notifications_syncdb.select(where:{table:'activity'}) when is_important(x)).length

     if important_count == 0
@@ -294,6 +298,8 @@

 update_notifications = (changes) ->
     #console.log("update_notifications: #{misc.to_json(changes)}")
+    if not notifications_syncdb?
+        return
     for c in changes
         if c.insert?
             render_notification(c.insert)
@@ -404,6 +410,8 @@

 mark_visible_notifications = (mark) ->
     set = {}; set[mark] = true
+    if not notifications_syncdb?
+        return
     for x in notifications_syncdb.select(where:{table:'activity'})
         if not x[mark]
             elt = notification_elements["#{x.project_id}/#{x.path}"]


Remove old authenticator stuff from base.js

--- app/assets/javascripts/lib/core/base.coffee
+++ app/assets/javascripts/lib/core/base.coffee
@@ -3,34 +3,11 @@
   class Base

     constructor: (args={})->
-      @authenticateUser()
-
       @showUserBasket()
       @initAds() unless args.secure
       @showCookieComplianceMsg()
       @initialiseSelectGroupManager()
       @addNavTracking()
-
-    authenticateUser: ->
-      @auth = new Authenticator()
-
-      $.ajax
-        url: @auth.getNewStatusUrl()
-        dataType: "json"
-        error: =>
-          @auth.update()
-        success: (user) =>
-          # The data returned is defined in community at: app/controllers/users_controller.rb@status
-          window.lp.user = user
-
-          # Legacy, keep until the old stuff is discarded and Authenticator has been refactored.
-          window.lpLoggedInUsername = user.username || "";
-          window.facebookUserId = user.facebook_uid;
-          window.surveyEnabled = "false";
-          window.timestamp = user.timestamp;
-          window.referer = "null";
-
-          @auth.update()

     initAds: ->
       if (window.lp && window.lp.ads)


fast commit

fast commit

--- app/assets/javascripts/todo_items.js.coffee
+++ app/assets/javascripts/todo_items.js.coffee
@@ -1,10 +1,9 @@
 # To-do item form events

 $(document).on 'cancel_todo_item_form', '.todo_list', (event) ->
-  $addTodoItemFromContainer = $(event.target).closest('.todo_list').find behavior_selector('add_item_trigger_container')
+  $addTodoItemFromContainer = $(event.target).find behavior_selector('add_item_trigger_container')
   $addTodoItemFromContainer.show()

 $(document).on 'add_todo_item_form:before', '.todo_list', (event) ->
   $addTodoItemFromContainer = $(event.target).find behavior_selector('add_item_trigger_container')
-  console.debug $addTodoItemFromContainer.size()
   $addTodoItemFromContainer.hide()


correction/refactor

correction/refactor

--- gulp-nav.coffee
+++ gulp-nav.coffee
@@ -4,7 +4,7 @@
  gulp-nav is a gulp plugin to help build navigation elements. gulp-nav adds
  "nav" objects to vinyl file objects. nav objects contain titles, (relative)
  href links, active flags, parents, children, and siblings. The last two
- properties are lists, which may optionally be ordered. For other than default
+ properties are arrays, which may optionally be ordered. For other than default
  behavior, call the exported function with an object that defines one or more
  of the following options (the first five can be a single property name, or an
  array of property names):
@@ -59,8 +59,7 @@
       # skip this file?
       for skip in skips
         if skip of source and source[skip]
-          @push file
-          return transformCallback()
+          return transformCallback null, file
       # normalize the path and break it into its constituent elements
       path = resolve '/', file.relative
         .replace /index\.[^/]+$/, ''          # index identified with directory


Fix mocha reporter option typo

Fix mocha reporter option typo

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -40,7 +40,7 @@

 gulp.task 'test', ->
   gulp.src('./test')
-    .pipe(mocha({ report: 'nyan', compilers: 'coffee:coffee-script/register' }))
+    .pipe(mocha({ reporter: 'nyan', compilers: 'coffee:coffee-script/register' }))
     .pipe(gulp.dest('.'))

 gulp.task 'build', (cb) ->


incremental commits

incremental commits

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -73,7 +73,7 @@

 gulp.task "css:ref", ->
   return gulp.src "./templates/includes/head.jade"
-          .pipe rev_replace( { manifest: gulp.src("assets/rev_manifest/rev-manifest.json"), replaceInExtensions: ['.jade']} )
+          .pipe rev_replace( { manifest: gulp.src("./assets/rev_manifest/rev-manifest.json"), replaceInExtensions: ['.jade']} )
           .pipe gulp.dest "./"

 gulp.task "css:clean", ->


Set build task as dependency for serve task

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -57,7 +57,7 @@
   'replace-bower'
 ]

-gulp.task 'serve', ->
+gulp.task 'serve', ['build'], ->
   connect.server(root: ['./dist'])

 gulp.task 'deploy', ['build'], ->


updates to gulpfile

updates to gulpfile

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -64,6 +64,8 @@
   js_sideNav()

 js = ()->
+  js_bkgVideo()
+  js_sideNav()
   jscoffee()

 gulp.task "newPost", ->


Tweak

Tweak

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -72,7 +72,6 @@
 gulp.task 'watch', ['build'], -> LIBRARIES.map (library) -> gulp.watch library.paths, -> buildLibrary(library)
 gulp.task 'release', ['build'], -> LIBRARIES.map minifyLibrary

-# gulp.task 'test', ->
 gulp.task 'test', ['release'], ->
   buildLibrary {paths: ["test/_examples/**/*.coffee"], modules: {type: 'local-shim', file_name: "_localization_examples.js", umd: {symbol: "knockback-locale-manager", dependencies: ['knockback']}}, destination: './test/_examples/build'}, ->


- gulpfile updated

- gulpfile updated

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -31,7 +31,7 @@
   fnInfo = (string) ->
     text = $.trim string
     if text.length
-      $.i text.replace(/\r/g, '\n').replace /\n{2,}/g, ''
+      $.log text.replace(/\r/g, '\n').replace /\n{2,}/g, ''

   #execute
   child = exec cmd


with NO_ZIP env variable it does not create compressed files

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -306,6 +306,8 @@
         if err?
             console.log ('Error: ' + err) if err?
         else if appPaths?.length > 0
+            if process.env.NO_ZIP
+                return deferred.resolve()
             json = JSON.parse(fs.readFileSync('./package.json'))
             zippath = "#{appPaths[0]}/"
             if platform == 'darwin'


[atom] Add ctrl-shift-w to close all tabs

--- modules/atom/config/keymap.cson
+++ modules/atom/config/keymap.cson
@@ -15,3 +15,7 @@
 'atom-text-editor:not([mini])':
   'f5': 'unset!' # sort-lines:sort default
   'f9': 'sort-lines:sort'
+
+'body':
+  'ctrl-shift-w': 'unset!' # window:close default
+  'ctrl-shift-w': 'tabs:close-all-tabs'


cleanup

cleanup

--- models/artwork.coffee
+++ models/artwork.coffee
@@ -44,22 +44,6 @@
     if linkType == "external" then "_blank" else "_self"

   artistLink: -> "/artist/#{@get('artist').id}"
-
-  displayDimensions: ->
-    # Return the first edition sets dimensions or dimensions, editions if there is one edition set
-    edition_sets = @get('edition_sets')
-    if edition_sets.length is 1
-      edition_set = _.first(edition_sets)
-      _.compact([ @superScriptFractions(edition_set.dimensions?.in), edition_set.dimensions?.cm, edition_set.editions ]).join('
')
-    else if edition_sets.length > 1
-      # If there is more than one edition set we display each one in a list -- and return undefined here as we display nothing
-      undefined
-    else
-      dimensions = @dimensions()
-      _.compact([ @superScriptFractions(dimensions?.in), dimensions?.cm, (if @get('unique') then "Unique" else undefined) ]).join('
')
-
-  displayDimensionsForEdition: (edition_set) ->
-    _.compact([ @superScriptFractions(edition_set.dimensions?.in), edition_set.dimensions?.cm ]).join('
')

   getTitle: -> if @get('title') then @get('title') else '(Untitled)'


update joyent description

update joyent description

--- models/sponsor.coffee
+++ models/sponsor.coffee
@@ -114,9 +114,9 @@
   url:   'http://www.joyent.com/'
   description:
     '''
-    Cloud Software for Service Providers. Increase growth and widen margins by
-    operating public, private, and hybrid clouds and managing services on your
-    existing networks.
+    Joyent is a global cloud computing software and service provider that
+    offers an integrated cloud technology suite designed for developers,
+    service providers and enterprises.
     '''
 ,
   name:  'Linode'


Compress CSS

Compress CSS

--- webpack/index.coffee
+++ webpack/index.coffee
@@ -28,7 +28,7 @@
       loader: "coffee-loader?literate"
     styl:
       test: /[.]styl$/
-      loader: 'raw!stylus'
+      loader: 'raw!stylus?compress'

 brk = (s)->
   s.split ' '


Add order model

Add order model

--- node/fabricate.coffee
+++ node/fabricate.coffee
@@ -604,4 +604,48 @@
       suggested_next_bid_cents: 325000
       updated_at: "2013-11-14T00:03:27Z"

+    when 'order'
+      id: _.uniqueId()
+      code: '484A30'
+      state: 'pending'
+      notes: 'some order notes'
+      item_total_cents: 180000
+      tax_total_cents: 0
+      total_cents: 180000
+      item_total: '$1,800.00'
+      tax_total: '$0.00'
+      total: '$1,800.00'
+      shipping_address: null
+      email: null
+      telephone: null
+      line_items: [{
+        id: 'line-item-id'
+        quantity: 1
+        shipping_note: "HI I'm a shipping note"
+        sale_conditions_url: 'http://shipping.conditions.com'
+        price_cents: 180000
+        tax_cents: 0
+        subtotal_cents: 180000
+        price: '$1,800.00'
+        subtotal: '$1,800.00'
+        artwork: fabricate('artwork')
+        edition_set: fabricate('edition_set', acquireable: true, sold: false, forsale: true, price: '$1,800' )
+        partner: fabricate('partner')
+        partner_location: {
+          name: 'Eli Ridgway'
+          address: '172 Minna Street'
+          address_2: ''
+          city: 'San Francisco'
+          country: 'United States'
+          state: 'CA'
+          postal_code: '94105'
+          phone: '415-777-1366'
+          coordinates: null
+          position: 1
+          email: 'info@eliridgway.com'
+          fax: ''
+          publicly_viewable: true
+        }
+      }]
+
   , extObj


Missing parenthesis

Missing parenthesis

--- admin/static/coffee/modals.coffee
+++ admin/static/coffee/modals.coffee
@@ -362,12 +362,12 @@

             if @$('.verification').val().toLowerCase() is 'remove'
                 query = r.db('rethinkdb').table('table_config')
-                    .get(@model.get('id').delete()
+                    .get(@model.get('id')).delete()
                 driver.run_once query, (err, result) =>
-                    if err
-                        @on_success_with_error()
-                    else
-                        @on_success()
+                        if err?
+                            @on_success_with_error()
+                        else
+                            @on_success()
             else
                 @$('.error_verification').slideDown 'fast'
                 @reset_buttons()


Updated clojure command

Updated clojure command

--- scripts/clojure.coffee
+++ scripts/clojure.coffee
@@ -8,7 +8,7 @@
 #   None
 #
 # Commands:
-#   hubot clojure|clj <script> - Evaluate one line of Clojure script
+#   hubot (<script>) - Evaluate one line of Clojure script
 #
 # Author:
 #   jingweno
@@ -16,7 +16,7 @@
 ringSessionID = ''

 module.exports = (robot) ->
-  robot.hear /^(\(.*\))/i, (msg)->
+  robot.respond /(\(.*\))/i, (msg)->
     script = msg.match[1]

     msg.http("http://www.tryclj.com/eval.json")


PaymentController: send UserPlanUpdated event after succesfull subscribe result

--- client/Main/payment/paymentcontroller.coffee
+++ client/Main/payment/paymentcontroller.coffee
@@ -8,7 +8,10 @@
     params.email    = options.email     if options.email
     params.provider = options.provider  or DEFAULT_PROVIDER

-    @api().subscribe params, callback
+    @api().subscribe params, (err, result)=>
+      @emit "UserPlanUpdated"  unless err?
+      callback err, result
+

   subscriptions: (callback)-> @api().subscriptions {}, callback

@@ -192,7 +195,6 @@
             @confirmReactivation existingSubscription, (err, subscription) =>
               return KD.showError err  if err
               @emit "SubscriptionReactivated", subscription
-              @emit "UserPlanUpdated"
         else if createAccount
           { cardFirstName: firstName, cardLastName: lastName } = billing
           { JUser } = KD.remote.api
@@ -201,7 +203,6 @@
             JUser.logout ->
         else
           @emit "SubscriptionCompleted"
-          @emit "UserPlanUpdated"

         log 'KD.singletons.dock.getView().show()'


Fix: trim trailing slash from path

Fix: trim trailing slash from path

--- src/marbles/http/client.js.coffee
+++ src/marbles/http/client.js.coffee
@@ -18,7 +18,7 @@
     if hosts
       host = hosts.shift()
       throw @InvalidSchemeError(host) unless @urlHasValidScheme(host)
-      host.toString().replace(/\/$/, '') + @buildPath(path)
+      host.toString().replace(/\/$/, '') + @buildPath(path).replace(/\/$/, '')
     else
       @buildPath(path)


Added string alnum function

Added string alnum function

--- src/builtin/string.coffee
+++ src/builtin/string.coffee
@@ -61,6 +61,16 @@
     else
       return index

+  isalnum: ->
+    alnums = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
+    if @value.__len__() == 0
+      return false
+    else
+      for char in @value
+        if not char in alnums
+          return false
+      return true
+
   isalpha: ->
     alphas = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
     if @value.__len__() == 0


Made builtin.string functions return pythonic types. Fixed builtin.string.center's return when delta is odd by using fillChar's value

--- src/builtin/string.coffee
+++ src/builtin/string.coffee
@@ -38,7 +38,7 @@
     if delta % 2 is 0
       new (type(@)) pad + @value + pad
     else
-      new (type(@)) pad + @value + pad + fillchar
+      new (type(@)) pad + @value + pad + fillchar.value

   count: (sub, start = 0, end = @__len__().value) ->
     count = 0
@@ -63,7 +63,7 @@
     index = slicedStr.lastIndexOf(suffix)
     if index < 0
       return false
-    return (index + suffix.length) is end
+    return new Bool (index + suffix.length) is end

   expandtabs: -> return

@@ -88,22 +88,22 @@
   isalnum: ->
     alnums = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
     if @__len__().value is 0
-      return false
+      return new Bool false
     else
       for c in @value
         if not (c in alnums)
-          return false
-      return true
+          return new Bool false
+      return new Bool true

   isalpha: ->
     alphas = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
     if @__len__().value is 0
-      return false
+      return new Bool false
     else
       for c in @value
         if not (c in alphas)
-          return false
-      return true
+          return new Bool false
+      return new Bool true

   isdigit: ->
     digits = '0123456789'
@@ -112,8 +112,8 @@
     else
       for c in @value
         if not (c in digits)
-          return false
-      return true
+          return new Bool false
+      return new Bool true

   # Returns true if all cased characters are lowercase and there exists a cased character.
   # Otherwise return false
@@ -121,15 +121,15 @@
     lowers = 'abcdefghijklmnopqrstuvwxyz'
     uppers = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
     if @__len__().value is 0
-      return false
+      return new Bool false
     else
       containsLower = false
       for c in @value
         if c in lowers
           containsLower = true
         if c in uppers
-          return false
-      return containsLower
+          return new Bool false
+      return new Bool containsLower

   isspace: ->
     spaces = '\t\n\x0b\x0c\r '
@@ -138,8 +138,8 @@
     else
       for c in @value
         if not (c in spaces)
-          return false
-      return true
+          return new Bool false
+      return new Bool true

   ###
   **Fix-me**: Account for punctuation as described in:
@@ -154,8 +154,8 @@
       if word.length is 1
         continue
       if not firstChar.islower()
-        return false
-    return true
+        return new Bool false
+    return new Bool true

   # Returns true if all cased characters are lowercase and there exists a cased character.
   # Otherwise return false
@@ -170,8 +170,8 @@
         if c in uppers
           containsUpper = true
         if c in lowers
-          return false
-      return containsUpper
+          return new Bool false
+      return new Bool containsUpper

   join: (iterable) -> return

@@ -204,7 +204,7 @@

   rfind: (sub, start = 0, end = @__len__().value) ->
     lastIndex = @__slice__(start, end).lastIndexOf(sub)
-    return lastIndex
+    return new Int lastIndex

   rindex: (sub, start = 0, end = @__len__().value) ->
     lastIndex = substr.lastIndexOf(sub)
@@ -234,7 +234,7 @@
   split: -> return
   splitlines: -> return
   startswith: (prefix, start = 0, end = @__len__().value) ->
-    return @value.slice(start, end).indexOf(prefix) == 0
+    return new Bool @value.slice(start, end).indexOf(prefix) == 0

   strip: -> return


Default ip addr to underscore (for VPC hosts)

--- scripts/pudding.coffee
+++ scripts/pudding.coffee
@@ -180,6 +180,7 @@
     response = '```\n'

     instances.map (inst) ->
+      inst.ip ?= '_'
       response += format_instance(inst)

     msg.send response + '```'


Fix hiding the more pages button.

Fix hiding the more pages button.

--- lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee
+++ lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee
@@ -18,6 +18,7 @@
       @collection.on "reset", (discussion) =>
         board = $(".current-board").html()
         @displayedCollection.current_page = discussion.current_page
+        @displayedCollection.pages = discussion.pages
         @displayedCollection.reset discussion.models
         # TODO: filter correctly
         # target = _.filter($("a.topic:contains('#{board}')"), (el) -> el.innerText == "General" || el.innerHTML == "General")
@@ -121,9 +122,6 @@
       @$(".more-pages").html('

')
       @$(".more-pages").addClass("loading")
       @collection.retrieveAnotherPage(@current_search, @discussionIds, @sortBy)
-      if not @collection.hasMorePages()
-        $(".more-pages").hide()
-

     renderThread: (thread) =>
       content = $(_.template($("#thread-list-item-template").html())(thread.toJSON()))


remove sanbox js

remove sanbox js

--- scripts/sandbox.coffee
+++ scripts/sandbox.coffee
@@ -1,21 +0,0 @@
-# Description:
-#   sandbox - run javascript in a sandbox!
-#
-# Dependencies:
-#   "sandbox": "0.8.6"
-#
-# Configuration:
-#   None
-#
-# Commands:
-#   hubot (run|sandbox) <javascript> - Execute the javascript code
-
-
-Sandbox = require('sandbox')
-
-module.exports = (robot) ->
-  robot.respond /(run|sandbox|js) ((.|\s)+)/i, (msg) ->
-    sandbox = new Sandbox
-    sandbox.run(msg.match[2], (output) ->
-      msg.send output.result
-    )


Don't overwrite the content id

Don't overwrite the content id

--- src/scripts/models/content.coffee
+++ src/scripts/models/content.coffee
@@ -40,6 +40,9 @@
       type = MEDIA_TYPES[response.mediaType]
       toc = @get('toc')
       @set('type', type)
+
+      # Keep the id with the desired version number included
+      delete response.id

       if type isnt 'book' then return response

@@ -99,7 +102,6 @@

       @set('page', num)

-      window.x = @
       page = @get('toc').at(num-1)
       @get('currentPage')?.set('active', false)
       @set('currentPage', page)


Show collect even if there's an error when assigning is skipped

--- src/smc-webapp/course/common.cjsx
+++ src/smc-webapp/course/common.cjsx
@@ -363,7 +363,7 @@
                     <Col md={width} key='last_collect'>
                         {@render_last('Collect', @props.info.last_collect, 'collected', @props.info.last_assignment? or skip_assignment,
                            "Copy the assignment from your student's project back to your project so you can grade their work.",
-                           "Open the copy of your student's work in your own project, so that you can grade their work.") if not @props.info.last_assignment?.error}
+                           "Open the copy of your student's work in your own project, so that you can grade their work.") if skip_assignment or not @props.info.last_assignment?.error}
                     </Col>
                     {@render_peer_assign()  if peer_grade and @props.info.peer_assignment and not @props.info.last_collect?.error}
                     {@render_peer_collect() if peer_grade and @props.info.peer_collect and not @props.info.peer_assignment?.error}


webapp/project-files: change links in files listing control to buttons to make it look better

--- src/smc-webapp/project_files.cjsx
+++ src/smc-webapp/project_files.cjsx
@@ -88,16 +88,16 @@
     handle_click: ->
         @props.actions.open_directory(@props.path)

-    render_link: ->
-        {@props.display}
-
-    render: ->
+    render_content: ->
         if @props.full_name and @props.full_name isnt @props.display
             <Tip title='Full name' tip={@props.full_name}>
-                {@render_link()}
+                {@props.display}
             </Tip>
         else
-            return @render_link()
+            return @props.display
+
+    render: ->
+        <Button bsSize='small' onClick={@handle_click}>{@render_content()}</Button>

 FileCheckbox = rclass
     displayName : 'ProjectFiles-FileCheckbox'
@@ -691,7 +691,7 @@

     make_path: ->
         v = []
-        v.push <PathSegmentLink path='' display={<Icon name='home' />} key='home' actions={@props.actions} />
+        v.push <PathSegmentLink path='' display={<Icon name='home' />} key=0 actions={@props.actions} />
         if @props.current_path == ""
             return v
         path = @props.current_path
@@ -700,22 +700,20 @@
             path = path[1..]
         path_segments = path.split('/')
         for segment, i in path_segments
-            if i == 0 and root
-                v.push  /<Space/>
-            else
-                v.push <Space/>/<Space/>
             v.push <PathSegmentLink
                     path      = {path_segments[..i].join('/')}
                     display   = {misc.trunc_middle(segment, 15)}
                     full_name = {segment}
-                    key       = {2 * i + 2}
+                    key       = {i+1}
                     actions   = {@props.actions} />
         return v

     render: ->
-

-            {@make_path()}
-

+        <ButtonToolbar>
+            <ButtonGroup bsSize='small'>
+                {@make_path()}
+            </ButtonGroup>
+        </ButtonToolbar>

 ProjectFilesButtons = rclass
     displayName : 'ProjectFiles-ProjectFilesButtons'
@@ -733,29 +731,37 @@
         e.preventDefault()
         @props.actions.setState(show_hidden : not @props.show_hidden)

+    handle_backup: (e) ->
+        e.preventDefault()
+        @props.actions.open_directory('.snapshots')
+
     render_refresh: ->
-        <Icon name='refresh' />
+        <Button bsSize='small' onClick={@handle_refresh}>
+            <Icon name='refresh' />
+        </Button>

     render_hidden_toggle: ->
-        if @props.show_hidden
-            <Icon name='eye' />
-        else
-            <Icon name='eye-slash' />
+        icon = if @props.show_hidden then 'eye' else 'eye-slash'
+        <Button bsSize='small' onClick={@handle_hidden_toggle}>
+            <Icon name={icon} />
+        </Button>

     render_backup: ->
         if @props.public_view or not require('./customize').commercial
             return
         # NOTE -- snapshots aren't available except in commercial version -- they are complicated nontrivial thing that isn't usually setup...
-        e.preventDefault(); @props.actions.open_directory('.snapshots')}>
-            <Icon name='life-saver' /> Backups
-
+        <Button bsSize='small' onClick={@handle_backup}>
+            <Icon name='life-saver' /> Backups
+        </Button>

     render: ->
-

-            {@render_refresh()}
-            {@render_hidden_toggle()}
-            {@render_backup()}
-

+        <ButtonToolbar>
+            <ButtonGroup bsSize='small' className='pull-right'>
+                {@render_refresh()}
+                {@render_hidden_toggle()}
+                {@render_backup()}
+            </ButtonGroup>
+        </ButtonToolbar>

 ProjectFilesActions = rclass
     displayName : 'ProjectFiles-ProjectFilesActions'
@@ -905,7 +911,7 @@
                 'download'
             ]
         <ButtonGroup bsSize='small'>
-                {(@render_action_button(v) for v in action_buttons)}
+            {(@render_action_button(v) for v in action_buttons)}
         </ButtonGroup>

     render: ->