me an idiot fixing stuff I broke two weeks ago

--- client/Framework/core/utils.coffee
+++ client/Framework/core/utils.coffee
@@ -598,9 +598,9 @@
     if KD.config.entryPoint?.type is 'group' and KD.config.entryPoint?.slug
       group = KD.config.entryPoint.slug
     else
-      group = ''
-
-    KD.remote.api.JStatusUpdate.create body : status, (err,reply)=>
+      group = 'koding'
+
+    KD.remote.api.JStatusUpdate.create {body, group}, (err,reply)=>
       unless err
         KD.getSingleton("appManager").tell 'Activity', 'ownActivityArrived', reply
       else


activity editing

activity editing

--- client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
@@ -94,7 +94,8 @@
     @getSingleton("windowController").addLayer tabView

   switchToEditView:(activity)->
-
+
+    @setClass "edit-mode"
     @largeInput.setValue Encoder.htmlDecode activity.body
     @switchToLargeView()


terminal: Switched TERM to xterm-256color and fixed width problem by ignoring 80 column mode.

--- client/landing/app/Applications/WebTerm.kdapplication/src/ControlCodeReader.coffee
+++ client/landing/app/Applications/WebTerm.kdapplication/src/ControlCodeReader.coffee
@@ -238,7 +238,7 @@
             20:   ignored "automatic newline"
           "?h": eachParameter
             1:    -> terminal.inputHandler.useApplicationKeypad true
-            3:    -> terminal.screenBuffer.clear(); terminal.setSize 132, terminal.sizeY; terminal.cursor.moveTo 0, 0
+            3:    ignored "132 column mode"
             4:    ignored "smooth scroll"
             5:    ignored "reverse video"
             6:    -> originMode = true
@@ -265,7 +265,7 @@
             20:   ignored "normal linefeed"
           "?l": eachParameter
             1:    -> terminal.inputHandler.useApplicationKeypad false
-            3:    -> terminal.screenBuffer.clear(); terminal.setSize 80, terminal.sizeY; terminal.cursor.moveTo 0, 0
+            3:    ignored "80 column mode"
             4:    ignored "jump scroll"
             5:    ignored "normal video"
             6:    -> originMode = false


client: main: root route made /Home for teams

--- client/app/lib/kodingrouter.coffee
+++ client/app/lib/kodingrouter.coffee
@@ -95,9 +95,7 @@
     if not currentGroup or currentGroup.slug is 'koding'
       return '/IDE'

-    if groupsController.currentGroupHasStack()
-    then return '/IDE'
-    else return '/Stacks'
+    return '/Home'

   setPageTitle: (title = 'Koding') -> kd.singletons.pageTitle.update title


client: remove isGroup util

client: remove isGroup util

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/util/isGroup.coffee
+++ client/app/lib/util/isGroup.coffee
@@ -1,5 +0,0 @@
-globals = require 'globals'
-
-module.exports = ->
-
-  return globals.config.entryPoint?.type is 'group'


feat(work-window): give work window a standard toolbar

Fixes T3457 "Activity" window needs a close affordance

--- src/browser/window-manager.coffee
+++ src/browser/window-manager.coffee
@@ -95,11 +95,10 @@
     @_workWindow = null

   ensureWorkWindow: ->
-    console.log('ensureWorkWindow')
     @_workWindow ?= @newWindow
       windowType: 'work'
       title: 'Activity'
-      toolbar: false
+      toolbar: true
       neverClose: true
       width: 800
       height: 400


Background form does not require btn-save, resolves #129

--- wzk/ui/form/BackgroundForm.coffee
+++ wzk/ui/form/BackgroundForm.coffee
@@ -29,8 +29,10 @@
   ###
   decorate: (form) ->
     return unless form?
+
     dispatcher = form.querySelector '.btn-save'
-    throw new Error 'Missing a dispatcher button' unless dispatcher?
+    return unless dispatcher
+
     @btn = new goog.ui.Button()
     @btn.decorate dispatcher


Also checking if @_stateB is empty so we have nothing to animate to

--- framer/Animation.coffee
+++ framer/Animation.coffee
@@ -131,7 +131,7 @@
 			console.warn "Animation: nothing to animate, no animatable properties"
 			return @_noop()

-		if _.isEqual(@_stateA, @_stateB)
+		if _.isEqual(@_stateA, @_stateB) or _.keys(@_stateB).length is 0
 			console.warn "Animation: nothing to animate, all properties are equal to what it is now"
 			return @_noop()


make template values animatable

make template values animatable


Text Edit #4

Text Edit #4

Spendentext angepasst

--- src/components/LandingPage.coffee
+++ src/components/LandingPage.coffee
@@ -115,35 +115,33 @@

               when V.DONATE
                 div null,
-                  h2 null, "Hier kannst Du doppelte Wirkung zeigen."
+                  h2 null, "Etwas Gutes für morgen tun."
                   p null,
                     """
-                    Unsere Crowdfunding Kampagne auf www.betterplace.org
-                    wird von Ashoka und dem Softwareunternehmen SAP
-                    unterstützt.
-                    Jede Spende bis zu 200 Euro wird in den nächsten
-                    Wochen live und direkt verdoppelt.
+                    Die Plattform von morgen finanziert sich über Fördermittel
+                    verschiedener Programme und Wettbewerbe, als auch über Spenden.
+                    Wir, das Team von morgen, arbeiten ehrenamtlich.
                     """

                   p null,
                     """
-                    Die Spenden sollen für die Weiterentwicklung der
-                    Plattform von morgen verwendet werden.
-                    Verschieden Feature sind in Planung,
+                    Die Beiträge möchten wir für die Weiterentwicklung
+                    der Plattform verwenden.
+                    Verschieden Features sind in Planung,
                     u.a. die Themenkarte zur Einbettung in eigene
-                    Webseiten und den Positivfaktoren der
-                    gemeinwohl-orientierten Bewertung der einzelnen Orte.
+                    Websites und den Positivfaktoren - dem
+                    gemeinwohl-orientierten Bewertungssytem der einzelnen Orte.
                     """
                   p null,
-                    "Hier geht es zur Kampagne: "
+                    "Hier geht es zu unserer Crowdfunding-Kampagne: "
                     a href: URLs.DONATE.link, URLs.DONATE.name
                   p null,
                     """
                     Wir freuen uns über jeden kleinen und großen Beitrag
                     und hoffen auch bald in deiner Stadt verfügbar zu
-                    sein.
+                    sein. Auf bald!
                     """
-                  p null, "Alles Liebe, das Team von morgen"
+                  p null, "Dankend, das Team von morgen"

               when V.JOIN
                 div null,


RegExp-based mapping support

RegExp-based mapping support

--- src/components/MapProperty.coffee
+++ src/components/MapProperty.coffee
@@ -3,15 +3,19 @@
 class MapProperty extends noflo.Component
     constructor: ->
         @map = {}
+        @regexps = {}

         @inPorts =
             map: new noflo.ArrayPort()
+            regexp: new noflo.ArrayPort()
             in: new noflo.Port()
         @outPorts =
             out: new noflo.Port()

         @inPorts.map.on "data", (data) =>
             @prepareMap data
+        @inPorts.regexp.on "data", (data) =>
+            @prepareRegExp data

         @inPorts.in.on "begingroup", (group) =>
             @outPorts.out.beginGroup group
@@ -30,11 +34,26 @@
         mapParts = map.split "="
         @map[mapParts[0]] = mapParts[1]

+    prepareRegExp: (map) ->
+        mapParts = map.split "="
+        @regexps[mapParts[0]] = mapParts[1]
+
     mapData: (data) ->
         newData = {}
         for property, value of data
             if @map[property]
                 property = @map[property]
+
+            for expression, replacement of @regexps
+                regexp = new RegExp expression
+                matched = regexp.exec property
+                continue unless matched
+
+                for match, index in matched
+                    continue if replacement.indexOf("$#{index}") is -1
+                    replacement = replacement.replace "\$#{index}", match
+                property = replacement
+
             newData[property] = value
         @outPorts.out.send newData


Update meta tags instead of reimplementing ujs functionality

--- core/app/assets/javascripts/jquery_ujs_override.coffee
+++ core/app/assets/javascripts/jquery_ujs_override.coffee
@@ -1,14 +1,17 @@
 #This patches jquery_ujs, such that it grabs csrf token from localStorage
 #instead of from the meta tag.

-# Make sure that every Ajax request sends the CSRF token
-$.rails.CSRFProtection = (xhr) ->
-  token = safeLocalStorage.factlink_csrf_token
-  if token
-    xhr.setRequestHeader('X-CSRF-Token', token)
+updateRailsCsrfMetaTags = ->
+  if !$('meta[name=csrf-token]').length
+    $('<meta name="csrf-token">').appendTo(document.head)
+    $('<meta name="csrf-param">').appendTo(document.head)
+  $('meta[name=csrf-token]').attr('content', safeLocalStorage.factlink_csrf_token)
+  $('meta[name=csrf-param]').attr('content', safeLocalStorage.factlink_csrf_param)

-# making sure that all forms have actual up-to-date token(cached forms contain old one)
-$.rails.refreshCSRFTokens = ->
-  csrfToken = safeLocalStorage.factlink_csrf_token
-  csrfParam = safeLocalStorage.factlink_csrf_param
-  $('form input[name="' + csrfParam + '"]').val(csrfToken);
+updateCsrfTagsBeforeExecution = (func) -> ->
+  updateRailsCsrfMetaTags()
+  func.apply(@, arguments)
+
+['refreshCSRFTokens', 'CSRFProtection', 'handleMethod'].forEach (name) ->
+  $.rails[name] = updateCsrfTagsBeforeExecution $.rails[name]
+


modify validation method

modify validation method

--- packages/rocketchat-lib/client/MessageAction.coffee
+++ packages/rocketchat-lib/client/MessageAction.coffee
@@ -98,8 +98,7 @@
 			$(input).keyup()
 			RocketChat.MessageAction.hideDropDown()
 		validation: (message) ->
-			room = RocketChat.models.Rooms.findOne({ _id: message.rid })
-			if Array.isArray(room.usernames) && room.usernames.indexOf(Meteor.user().username) is -1
+			if not RocketChat.models.Subscriptions.findOne({ rid: message.rid })?
 				return false
 			return true
 		order: 1


Removed status code and header from callbacks.

--- src/akismet.coffee
+++ src/akismet.coffee
@@ -33,51 +33,51 @@

   # Verifies the key.
   #
-  # callback: err, isValid (true or false), statusCode, headers
+  # callback: err, isValid (true or false)
   verifyKey: (callback) ->
     callback or= () ->
     @keyClient or= http.createClient @port, @host
     @postRequest @keyClient, '/1.1/verify-key', { key: @apiKey, blog: @blog }, (err, status, headers, body) ->
-      callback err, (if status >= 200 and status < 300 and body is 'valid' then true else false), status, headers
+      callback err, (if status >= 200 and status < 300 and body is 'valid' then true else false)

   # Checks if a comment is spam
   #
   # options: key/value pair of options to send to the Akismet API
-  # callback: err, isSpam (true or false), statusCode, headers
+  # callback: err, isSpam (true or false)
   checkSpam: (options, callback) ->
     callback or= () ->
     options.blog = @blog
     options.user_agent = @userAgent
     @client or= http.createClient @port, @endPoint
     @postRequest @client, '/1.1/comment-check', options, (err, status, headers, body) ->
-      callback err, (if status >= 200 and status < 300 and body is 'true' then true else false), status, headers
+      callback err, (if status >= 200 and status < 300 and body is 'true' then true else false)

   # Marks a comment as spam
   #
   # options: key/value pair of options to send to the Akismet API
-  # callback: err, statusCode, headers
+  # callback: err
   submitSpam: (options, callback) ->
     callback or= () ->
     options.blog = @blog
     options.user_agent = @userAgent
     @client or= http.createClient @port, @endPoint
     @postRequest @client, '/1.1/submit-spam', options, (err, status, headers, body) ->
-      callback err, status, headers
+      callback err

   # Marks a comment as NOT spam
   #
   # options: key/value pair of options to send to the Akismet API
-  # callback: err, statusCode, headers
+  # callback: err
   submitHam: (options, callback) ->
     callback or= () ->
     options.blog = @blog
     options.user_agent = @userAgent
     @client or= http.createClient @port, @endPoint
     @postRequest @client, '/1.1/submit-ham', options, (err, status, headers, body) ->
-      callback err, status, headers
+      callback err

   # Posts a request to the Akismet API server.


Add an "impossible" rule to Perl's __END__ pattern

While Atom permits the omission of a range pattern's "end" rule, not all
engines using TextMate's rendering engine do, including TextMate itself.
For example, this rule is ignored by the Lightshow app when previewing a
piece of Perl code containing freeform text following an __END__ line.

--- grammars/perl.cson
+++ grammars/perl.cson
@@ -745,6 +745,7 @@
       '1':
         'name': 'constant.language.perl'
     'contentName': 'text.embedded.perl'
+    'end': '(?=N)A'
   }
   {
     'match': '(?<!->)\\b(continue|die|do|else|elsif|exit|for|foreach|goto|if|last|next|redo|return|select|unless|until|wait|while|switch|case|require|use|eval)\\b'


Add underlines to mdoc URL/mailto macros

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -1325,6 +1325,16 @@
 			]
 		},{

+			# .Lk/.Mt: Hyperlink or mailto
+			name: "meta.function.$1.callable.macro.mdoc.roff"
+			begin: "(?<=Lk|Mt)\\G|(?<=\\s)(Lk|Mt)(?=\\s)"
+			end:   "$|(?=\\\\\")|(\\S+?)(?=$|\\s|\\\\\")"
+			beginCaptures:
+				1: name: "entity.function.name.roff"
+			endCaptures:
+				0: name: "markup.underline.link.hyperlink.mdoc.roff"
+				1: patterns: [include: "#escapes"]
+		},{
 			# Embedded macro name inside argument list
 			name: "meta.function.$1.callable.macro.mdoc.roff"
 			match: """(?x) (?<=[ \t])


Remove boundary-checking for generic macro names

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -1158,7 +1158,7 @@

 			# Generic macro highlighting
 			name: "meta.function.macro.roff"
-			begin: "^([.'])[ \t]*(\\S+)\\b"
+			begin: "^([.'])[ \t]*(\\S+)"
 			end:   "(?<!\\\\)(?=$)|(?=\\\\\")"
 			beginCaptures:
 				1: {name: "punctuation.definition.macro.roff"}


Globalise number-matching pattern

Globalise number-matching pattern

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -68,8 +68,16 @@
 		patterns: [
 			{include: "#escapes"}
 			{include: "#string"}
-			{match: "[-+]?\\d+", name: "constant.numeric.roff"}
+			{include: "#number"}
 		]
+
+
+	# Numeric literal
+	number:
+		name: "constant.numeric.roff"
+		match: "(?:(?<!\\w)[-+])?\\d+(?:\\.\\d+)?([A-Za-z]+)?"
+		captures:
+			1: {name: "keyword.other.unit.roff"}

 	# "Double-quoted string"
@@ -423,12 +431,8 @@
 					1: {name: "punctuation.arithmetic.begin.roff"}
 					2: {patterns: [{include: "#arithmetic"}]}
 					3: {name: "punctuation.arithmetic.end.roff"}
-			},{
-				name: "constant.numeric.arithmetic.roff"
-				match: "\\d+(?:\\.\\d+)?([A-Za-z]+)?"
-				captures:
-					1: {name: "punctuation.unit.arithmetic.roff"}
 			}
+			{include: "#number"}
 			{match: "[-/+*%]",     name: "keyword.operator.arithmetic.roff"}
 			{match: ":|&|[<=>]=?", name: "keyword.operator.logical.roff"}
 			{match: "\\|",         name: "keyword.operator.absolute.roff"}


Reorganise pattern repository

Reorganise pattern repository

The grammar's rules are now grouped by lexical definition, as opposed to
their perceived relevance to each other.

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -35,207 +35,24 @@
 repository:

 	# Common patterns
-	main: {
+	main:
 		patterns: [
-			{ include: "#escapes"              }
-			{ include: "#comments"             }
-			{ include: "#requests"             }
-			{ include: "#bold"                 }
-			{ include: "#italic"               }
-			{ include: "#bold-italic"          }
-			{ include: "#headings"             }
-			{ include: "#macro-bold"           }
-			{ include: "#macro-italic"         }
-			{ include: "#macro-bold-italic"    }
-			{ include: "#macro-bold-roman"     }
-			{ include: "#macro-italic-bold"    }
-			{ include: "#macro-italic-roman"   }
-			{ include: "#macro-roman-bold"     }
-			{ include: "#macro-roman-italic"   }
+			{include: "#escapes"}
+			{include: "#requests"}
+			{include: "#macros"}
 		]
-	}
-
-
-
-
-	# Escape sequences
-	escapes:
-		patterns: [{
-
-				# Comments
-				name: "comment.line.roff"
-				begin: "(?:^[.'])?\\s*\\\\\""
-				end: "$"
-				beginCaptures:
-					0: {name: "punctuation.definition.begin.comment.roff"}
-			}, {
-
-				# Empty control lines
-				name: "comment.empty.roff"
-				match: "^[.'][ \t]*$"
-			},
-
-
-			# Basic sequences
-			{match: "\\x5C{2}",  name: "constant.character.escape.backslash.roff"}
-			{match: "\\\\e",     name: "constant.character.escape.current-escape-char.roff"}
-			{match: "\\\\´",     name: "constant.character.escape.acute-accent.roff"}
-			{match: "\\\\`",     name: "constant.character.escape.grave-accent.roff"}
-			{match: "\\\\-",     name: "constant.character.escape.minus.roff"}
-			{match: "\\\\\\.",   name: "constant.character.escape.dot.roff"}
-			{match: "\\\\ ",     name: "constant.character.escape.space.roff"}
-			{match: "\\\\0",     name: "constant.character.escape.space.digit-width.roff"}
-			{match: "\\\\\\|",   name: "constant.character.escape.space.one-sixth-em.roff"}
-			{match: "\\\\\\^",   name: "constant.character.escape.space.one-twelfth-em.roff"}
-			{match: "\\\\&",     name: "constant.character.escape.zero-width-marker.roff"}
-			{match: "\\\\!",     name: "constant.character.escape.transparent-line.roff"}  # May need its own rule
-			{match: "\\\\%",     name: "constant.character.escape.hyphenation-char.roff"}
-			{match: "\\\\a",     name: "constant.character.escape.leader-char.roff"}
-			{match: "\\\\c",     name: "constant.character.escape.connect.roff"}
-			{match: "\\\\d",     name: "constant.character.escape.downwards.roff"}
-			{match: "\\\\p",     name: "constant.character.escape.spread-line.roff"}
-			{match: "\\\\r",     name: "constant.character.escape.reverse.roff"}
-			{match: "\\\\t",     name: "constant.character.escape.tab.roff"}
-			{match: "\\\\u",     name: "constant.character.escape.upwards.roff"}
-			{match: "\\\\\\{",   name: "constant.character.escape.conditional.begin.roff"}
-			{match: "\\\\\\}",   name: "constant.character.escape.conditional.end.roff"}
-			{match: "\\\\\n",    name: "constant.character.escape.newline.roff"}, {
-
-				# \(aa - Character named "aa"
-				name: "constant.character.escape.named-char.roff"
-				match: "(?x)
-					(\\\\ \\()
-					(
-						em|hy|bu|sq|ru|14|12|34|fi|fl|ff|Fi|Fl|de|dg|fm|ct|rg|co|pl|mi|eq|sc|aa|ga|ul|sl|ts|sr|rn|ap|
-						ua|da|mu|di|cu|ca|sb|sp|ib|ip|if|pd|gr|no|is|pt|es|mo|br|dd|rh|lh|bs|or|ci|lt|lb|rt|rb|lk|rk|
-						bv|lf|rf|lc|rc|[>=<|!]=|->|<-|[+]-|\\*[A-IK-UW-Z*a-ik-uw-z]
-					)"
-				captures:
-					1: {name: "punctuation.definition.begin.roff"}
-					2: {name: "entity.name.roff"}
-
-			}, {
-
-				# \*x, \*(xx - Interpolate string "x" or "xx"
-				name: "constant.character.escape.interpolate-string.roff"
-				match: "(\\\\\\*)(\\(\\S{2}|\\S)"
-				captures:
-					1: {name: "punctuation.definition.begin.roff"}
-					2: {name: "variable.parameter.roff"}
-			}, {
-
-				# \$N - Interpolate argument number N (valid range: 1-9)
-				name: "constant.character.escape.interpolate-argument.roff"
-				match: "(\\\\\\$)(\\d)"
-				captures:
-					1: {name: "punctuation.definition.begin.roff"}
-					2: {name: "variable.parameter.roff"}
-			}, {
-
-				# Parametric/function-like escape sequences
-				name: "constant.character.escape.function.roff"
-				match: "(\\\\[bCDhHSlLovwxXN])((.)(.*?)(\\3))"
-				captures:
-					1: {name: "entity.name.function.roff"}
-					2: {name: "string.other.roff"}
-					3: {name: "punctuation.definition.begin.roff"}
-					4: {patterns: [{include: "#escapes"}]}
-					5: {name: "punctuation.definition.end.roff"}
-			}, {
-
-				# Font: Italics (typically rendered by nroff as underlined text)
-				name: "markup.italic.roff"
-				begin: "(\\\\)(f[I2])"
-				end: "(?=\\\\f[A-Za-z0-9])((\\\\)(f[RP1]))?"
-				beginCaptures:
-					1: {name: "punctuation.definition.backslash.roff"}
-					2: {name: "entity.name.roff"}
-				endCaptures:
-					1: {name: "meta.terminator.roff"}
-					2: {name: "punctuation.definition.backslash.roff"}
-					3: {name: "entity.name.roff"}
-			}, {
-
-				# Font: Bold
-				name: "markup.bold.roff"
-				begin: "(\\\\)(f[B3])"
-				end: "(?=\\\\f[A-Za-z0-9])((\\\\)(f[RP1]))?"
-				beginCaptures:
-					1: {name: "punctuation.definition.backslash.roff"}
-					2: {name: "entity.name.roff"}
-				endCaptures:
-					1: {name: "meta.terminator.roff"}
-					2: {name: "punctuation.definition.backslash.roff"}
-					3: {name: "entity.name.roff"}
-			}, {
-
-				# Font: Bold and italic
-				name: "markup.bold.italic.roff"
-				begin: "(\\\\)(f4)"
-				end: "(?=\\\\f[A-Za-z0-9])((\\\\)(f[RP1]))?"
-				beginCaptures:
-					1: {name: "punctuation.definition.backslash.roff"}
-					2: {name: "entity.name.roff"}
-				endCaptures:
-					1: {name: "meta.terminator.roff"}
-					2: {name: "punctuation.definition.backslash.roff"}
-					3: {name: "entity.name.roff"}
-			}, {
-
-				# \fX, \f(XX, \fN - Change to font named "X" or "XX", or position N
-				name: "constant.character.escape.font.roff"
-				match: "(\\\\f)(\\(\\S{2}|\\S)"
-				captures:
-					1: {name: "punctuation.definition.begin.roff"}
-					2: {name: "variable.parameter.roff"}
-			}, {
-
-				# \gx, \g(xx - Format of number register "x" or "xx"
-				name: "constant.character.escape.format-register.roff"
-				match: "(\\\\g)(\\(\\S{2}|\\S)"
-				captures:
-					1: {name: "punctuation.definition.begin.roff"}
-					2: {name: "variable.parameter.roff"}
-			}, {
-
-				# \kX - Mark horizontal input place in register "X"
-				name: "constant.character.escape.mark-input.roff"
-				match: "(\\\\k)(\\S)"
-				captures:
-					1: {name: "punctuation.definition.begin.roff"}
-					2: {name: "variable.parameter.roff"}
-			}, {
-
-				# \nX, \n(XX - Contents of number register "X" or "XX"
-				name: "constant.character.escape.expand-register.roff"
-				match: "(\\\\n)(\\(\\S{2}|\\S)"
-				captures:
-					1: {name: "punctuation.definition.begin.roff"}
-					2: {name: "variable.parameter.roff"}
-			}, {
-
-				# \sN, \s±N - Point-size change function; also \s(NN, \s±(NN
-				name: "constant.character.escape.point-size.roff"
-				match: "(\\\\s)([-+]?\\(?\\d+)"
-				captures:
-					1: {name: "punctuation.definition.begin.roff"}
-					2: {name: "variable.parameter.roff"}
-			}, {
-
-				# \zC - Print "C" with zero width (without spacing)
-				name: "constant.character.escape.zero-width-print.roff"
-				match: "(\\\\z)(\\S)"
-				captures:
-					1: {name: "punctuation.definition.begin.roff"}
-					2: {name: "variable.parameter.roff"}
-			}, {
-
-				# \Z - Any character not listed above
-				match: "\\\\(\\S)",
-				name: "constant.character.escape.misc.roff"
+
+
+	# Control line parameters
+	params:
+		patterns: [
+			{include: "#escapes"}
+			{include: "#string"}, {
+				name: "string.unquoted.roff"
+				begin: "(?=[A-Za-z_])"
+				end: "(?=[^\\w])"
 			}
 		]
-

 	# "Double-quoted string"
@@ -243,7 +60,7 @@
 		patterns: [{
 			name: "string.quoted.double.empty.roff",
 			match: '(")(")'
-		}, {
+		},{
 			name: "string.quoted.double.roff",
 			begin: '"(?!")'
 			end:   '(?<!")"(?!")|(?<!\\\\)$'
@@ -256,60 +73,6 @@
 		}]

-
-
-	# Request parameters
-	params:
-		patterns: [
-			{include: "#escapes"}
-			{include: "#string"}, {
-				name: "string.unquoted.roff"
-				begin: "(?=[A-Za-z_])"
-				end: "(?=[^\\w])"
-			}
-		]
-
-
-	# Titles, sections and subsections
-	headings:
-		patterns: [{
-
-			# Titles (GNU manual macro)
-			name: "markup.heading.title.gnu.man.macro.roff"
-			begin: "^([.'][ \t]*TH)(?=[ \t]|$)"
-			end: "$"
-			beginCaptures:
-				1: {name: "entity.name.function.roff"}
-			patterns: [
-				{ include: "#main"    }
-				{ include: "#params"  }
-			]
-		}, {
-
-			# Sections
-			name: "markup.heading.section.man.macro.roff"
-			begin: "^([.'][ \t]*SH)(?=[ \t]|$)"
-			end: "$"
-			beginCaptures:
-				1: {name: "entity.name.function.roff"}
-			patterns: [
-				{ include: "#main"    }
-				{ include: "#params"  }
-			]
-		}, {
-
-			# Subsections
-			name: "markup.heading.subsection.man.macro.roff"
-			begin: "^([.'][ \t]*Ss)(?=[ \t]|$)"
-			end: "$"
-			beginCaptures:
-				1: {name: "entity.name.function.roff"}
-			patterns: [
-				{ include: "#main"    }
-				{ include: "#params"  }
-			]
-
-		}]

 	# Requests
@@ -331,7 +94,7 @@
 				2: {name: "entity.name.roff"}
 			patterns: [{include: "#params"}]

-		}, {
+		},{

 			# Flow control/conditional requests
 			name: "meta.function.request.$1.roff"
@@ -342,7 +105,7 @@
 				2: {name: "keyword.control.roff"}
 			patterns: [{include: "#params"}]

-		}, {
+		},{

 			# Underlined passage
 			name: "meta.request.ul.roff"
@@ -360,204 +123,413 @@

-
-
-
-
-	# Macro: .B
-	"macro-bold":
-		name: "markup.bold.roff"
-		begin: "^[.'][ \t]*B(?:$|[ \t]+|(?=\\\\))"
-		end:   "([^\n ]+ ?\n)"
-		patterns: [{ include: "$self" }]
-
-
-	# Macro: .I
-	"macro-italic":
-		name: "markup.italic.roff"
-		begin: "^[.'][ \t]*I(?:$|[ \t]+|(?=\\\\))"
-		end:   "([^\n ]+ ?\n)"
-		patterns: [{ include: "$self" }]
-
-
-	# Macro: .BI
-	"macro-bold-italic":
-		name: "markup.bold-italic.roff"
-		begin: "^([.'][ \t]*BI)(?=[ \t\\\\]|$)"
-		end:   "(?<!\\\\)(?=\n)"
-		beginCaptures:
-			1: {name: "entity.function.name.roff"}
+	# Escape sequences
+	escapes:
 		patterns: [{
-			begin: '(?x)(?<=
-				^[.\']        BI|
-				^[.\'][ \t]{1}BI|
-				^[.\'][ \t]{2}BI|
-				^[.\'][ \t]{3}BI|
-				^[.\'][ \t]{4}BI)
-				[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
-			end: '(?<=")|(?<!\\\\)$'
-			patterns: [{include: "#italic-strings"}]
-			beginCaptures:
-				1:
-					name: "markup.bold.roff"
-					patterns: [{include: "#bold-strings"}]
-		}, {
-			begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
-			end: '$|(?:^|[ \t]+|(?<="))(("[^"]*")|\\S+)'
-			patterns: [{include: "#bold-strings"}]
+
+			# Comments
+			name: "comment.line.roff"
+			begin: "(?:^[.'])?\\s*\\\\\""
+			end: "$"
+			beginCaptures:
+				0: {name: "punctuation.definition.begin.comment.roff"}
+		},{
+
+			# Empty control lines
+			name: "comment.empty.roff"
+			match: "^[.'][ \t]*$"
+		},
+
+
+		# Basic sequences
+		{match: "\\x5C{2}",  name: "constant.character.escape.backslash.roff"}
+		{match: "\\\\e",     name: "constant.character.escape.current-escape-char.roff"}
+		{match: "\\\\´",     name: "constant.character.escape.acute-accent.roff"}
+		{match: "\\\\`",     name: "constant.character.escape.grave-accent.roff"}
+		{match: "\\\\-",     name: "constant.character.escape.minus.roff"}
+		{match: "\\\\\\.",   name: "constant.character.escape.dot.roff"}
+		{match: "\\\\ ",     name: "constant.character.escape.space.roff"}
+		{match: "\\\\0",     name: "constant.character.escape.space.digit-width.roff"}
+		{match: "\\\\\\|",   name: "constant.character.escape.space.one-sixth-em.roff"}
+		{match: "\\\\\\^",   name: "constant.character.escape.space.one-twelfth-em.roff"}
+		{match: "\\\\&",     name: "constant.character.escape.zero-width-marker.roff"}
+		{match: "\\\\!",     name: "constant.character.escape.transparent-line.roff"}  # May need its own rule
+		{match: "\\\\%",     name: "constant.character.escape.hyphenation-char.roff"}
+		{match: "\\\\a",     name: "constant.character.escape.leader-char.roff"}
+		{match: "\\\\c",     name: "constant.character.escape.connect.roff"}
+		{match: "\\\\d",     name: "constant.character.escape.downwards.roff"}
+		{match: "\\\\p",     name: "constant.character.escape.spread-line.roff"}
+		{match: "\\\\r",     name: "constant.character.escape.reverse.roff"}
+		{match: "\\\\t",     name: "constant.character.escape.tab.roff"}
+		{match: "\\\\u",     name: "constant.character.escape.upwards.roff"}
+		{match: "\\\\\\{",   name: "constant.character.escape.conditional.begin.roff"}
+		{match: "\\\\\\}",   name: "constant.character.escape.conditional.end.roff"}
+		{match: "\\\\\n",    name: "constant.character.escape.newline.roff"}, {
+
+			# \(aa - Character named "aa"
+			name: "constant.character.escape.named-char.roff"
+			match: "(?x)
+				(\\\\ \\()
+				(
+					em|hy|bu|sq|ru|14|12|34|fi|fl|ff|Fi|Fl|de|dg|fm|ct|rg|co|pl|mi|eq|sc|aa|ga|ul|sl|ts|sr|rn|ap|
+					ua|da|mu|di|cu|ca|sb|sp|ib|ip|if|pd|gr|no|is|pt|es|mo|br|dd|rh|lh|bs|or|ci|lt|lb|rt|rb|lk|rk|
+					bv|lf|rf|lc|rc|[>=<|!]=|->|<-|[+]-|\\*[A-IK-UW-Z*a-ik-uw-z]
+				)"
+			captures:
+				1: {name: "punctuation.definition.begin.roff"}
+				2: {name: "entity.name.roff"}
+
+		},{
+
+			# \*x, \*(xx - Interpolate string "x" or "xx"
+			name: "constant.character.escape.interpolate-string.roff"
+			match: "(\\\\\\*)(\\(\\S{2}|\\S)"
+			captures:
+				1: {name: "punctuation.definition.begin.roff"}
+				2: {name: "variable.parameter.roff"}
+		},{
+
+			# \$N - Interpolate argument number N (valid range: 1-9)
+			name: "constant.character.escape.interpolate-argument.roff"
+			match: "(\\\\\\$)(\\d)"
+			captures:
+				1: {name: "punctuation.definition.begin.roff"}
+				2: {name: "variable.parameter.roff"}
+		},{
+
+			# Parametric/function-like escape sequences
+			name: "constant.character.escape.function.roff"
+			match: "(\\\\[bCDhHSlLovwxXN])((.)(.*?)(\\3))"
+			captures:
+				1: {name: "entity.name.function.roff"}
+				2: {name: "string.other.roff"}
+				3: {name: "punctuation.definition.begin.roff"}
+				4: {patterns: [{include: "#escapes"}]}
+				5: {name: "punctuation.definition.end.roff"}
+		},{
+
+			# Font: Italics (typically rendered by nroff as underlined text)
+			name: "markup.italic.roff"
+			begin: "(\\\\)(f[I2])"
+			end: "(?=\\\\f[A-Za-z0-9])((\\\\)(f[RP1]))?"
+			beginCaptures:
+				1: {name: "punctuation.definition.backslash.roff"}
+				2: {name: "entity.name.roff"}
 			endCaptures:
-				1: {name: "markup.italic.roff"}
-				2: {patterns: [{include: "#string"}]}
-		}]
-
-
-	# Macro: .BR
-	"macro-bold-roman":
-		name: "markup.bold-roman.roff"
-		begin: "^([.'][ \t]*BR)(?=[ \t\\\\]|$)"
-		end:   "(?<!\\\\)(?=\n)"
-		beginCaptures:
-			1: {name: "entity.function.name.roff"}
+				1: {name: "meta.terminator.roff"}
+				2: {name: "punctuation.definition.backslash.roff"}
+				3: {name: "entity.name.roff"}
+		},{
+
+			# Font: Bold
+			name: "markup.bold.roff"
+			begin: "(\\\\)(f[B3])"
+			end: "(?=\\\\f[A-Za-z0-9])((\\\\)(f[RP1]))?"
+			beginCaptures:
+				1: {name: "punctuation.definition.backslash.roff"}
+				2: {name: "entity.name.roff"}
+			endCaptures:
+				1: {name: "meta.terminator.roff"}
+				2: {name: "punctuation.definition.backslash.roff"}
+				3: {name: "entity.name.roff"}
+		},{
+
+			# Font: Bold and italic
+			name: "markup.bold.italic.roff"
+			begin: "(\\\\)(f4)"
+			end: "(?=\\\\f[A-Za-z0-9])((\\\\)(f[RP1]))?"
+			beginCaptures:
+				1: {name: "punctuation.definition.backslash.roff"}
+				2: {name: "entity.name.roff"}
+			endCaptures:
+				1: {name: "meta.terminator.roff"}
+				2: {name: "punctuation.definition.backslash.roff"}
+				3: {name: "entity.name.roff"}
+		},{
+
+			# \fX, \f(XX, \fN - Change to font named "X" or "XX", or position N
+			name: "constant.character.escape.font.roff"
+			match: "(\\\\f)(\\(\\S{2}|\\S)"
+			captures:
+				1: {name: "punctuation.definition.begin.roff"}
+				2: {name: "variable.parameter.roff"}
+		},{
+
+			# \gx, \g(xx - Format of number register "x" or "xx"
+			name: "constant.character.escape.format-register.roff"
+			match: "(\\\\g)(\\(\\S{2}|\\S)"
+			captures:
+				1: {name: "punctuation.definition.begin.roff"}
+				2: {name: "variable.parameter.roff"}
+		},{
+
+			# \kX - Mark horizontal input place in register "X"
+			name: "constant.character.escape.mark-input.roff"
+			match: "(\\\\k)(\\S)"
+			captures:
+				1: {name: "punctuation.definition.begin.roff"}
+				2: {name: "variable.parameter.roff"}
+		},{
+
+			# \nX, \n(XX - Contents of number register "X" or "XX"
+			name: "constant.character.escape.expand-register.roff"
+			match: "(\\\\n)(\\(\\S{2}|\\S)"
+			captures:
+				1: {name: "punctuation.definition.begin.roff"}
+				2: {name: "variable.parameter.roff"}
+		},{
+
+			# \sN, \s±N - Point-size change function; also \s(NN, \s±(NN
+			name: "constant.character.escape.point-size.roff"
+			match: "(\\\\s)([-+]?\\(?\\d+)"
+			captures:
+				1: {name: "punctuation.definition.begin.roff"}
+				2: {name: "variable.parameter.roff"}
+		},{
+
+			# \zC - Print "C" with zero width (without spacing)
+			name: "constant.character.escape.zero-width-print.roff"
+			match: "(\\\\z)(\\S)"
+			captures:
+				1: {name: "punctuation.definition.begin.roff"}
+				2: {name: "variable.parameter.roff"}
+		},{
+
+			# \Z - Any character not listed above
+			match: "\\\\(\\S)",
+			name: "constant.character.escape.misc.roff"
+		}]
+
+
+
+
+	# Macros
+	macros:
 		patterns: [{
-			begin: '(?x)(?<=
-				^[.\']        BR|
-				^[.\'][ \t]{1}BR|
-				^[.\'][ \t]{2}BR|
-				^[.\'][ \t]{3}BR|
-				^[.\'][ \t]{4}BR)
-				[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
-			end: '(?<=")|(?<!\\\\)$'
+
+			# .TH: Title (GNU manual macro)
+			name: "markup.heading.title.gnu.man.macro.roff"
 			patterns: [{include: "#params"}]
-			beginCaptures:
-				1:
-					name: "markup.bold.roff"
-					patterns: [{include: "#bold-strings"}]
-		}, {
-			begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
-			end: '$|(?:^|[ \t]+|(?<="))(?:("[^"]*")|\\S+)'
-			patterns: [{include: "#bold-strings"}]
-			endCaptures:
-				1: {patterns: [{include: "#string"}]}
-		}]
-
-
-	# Macro: .IB
-	"macro-italic-bold":
-		name: "markup.italic-bold.roff"
-		begin: "^([.'][ \t]*IB)(?=[ \t\\\\]|$)"
-		end:   "(?<!\\\\)(?=\n)"
-		beginCaptures:
-			1: {name: "entity.function.name.roff"}
-		patterns: [{
-			begin: '(?x)(?<=
-				^[.\']        IB|
-				^[.\'][ \t]{1}IB|
-				^[.\'][ \t]{2}IB|
-				^[.\'][ \t]{3}IB|
-				^[.\'][ \t]{4}IB)
-				[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
-			end: '(?<=")|(?<!\\\\)$'
-			patterns: [{include: "#bold-strings"}]
-			beginCaptures:
-				1:
-					name: "markup.italic.roff"
-					patterns: [{include: "#italic-strings"}]
-		}, {
-			begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
-			end: '$|(?:^|[ \t]+|(?<="))(("[^"]*")|\\S+)'
-			patterns: [{include: "#italic-strings"}]
-			endCaptures:
-				1: {name: "markup.bold.roff"}
-				2: {patterns: [{include: "#string"}]}
-		}]
+			begin: "^([.'][ \t]*TH)(?=[ \t]|$)"
+			end: "$"
+			beginCaptures:
+				1: {name: "entity.name.function.roff"}
+		},{
+
+			# .SH: Section heading
+			name: "markup.heading.section.man.macro.roff"
+			patterns: [{include: "#params"}]
+			begin: "^([.'][ \t]*SH)(?=[ \t]|$)"
+			end: "$"
+			beginCaptures:
+				1: {name: "entity.name.function.roff"}
+		},{
+
+			# .Ss: Subsection
+			name: "markup.heading.subsection.man.macro.roff"
+			patterns: [{include: "#params"}]
+			begin: "^([.'][ \t]*Ss)(?=[ \t]|$)"
+			end: "$"
+			beginCaptures:
+				1: {name: "entity.name.function.roff"}
+		},{
+
+			# .B: Bold
+			name: "markup.bold.roff"
+			begin: "^[.'][ \t]*B(?:$|[ \t]+|(?=\\\\))"
+			end:   "([^\n ]+ ?\n)"
+			patterns: [{ include: "$self" }]
+
+		},{
+
+			# .I: Italic
+			name: "markup.italic.roff"
+			begin: "^[.'][ \t]*I(?:$|[ \t]+|(?=\\\\))"
+			end:   "([^\n ]+ ?\n)"
+			patterns: [{ include: "$self" }]
+
+		},{
+
+			# .BI: Bold and italic
+			name: "markup.bold-italic.roff"
+			begin: "^([.'][ \t]*BI)(?=[ \t\\\\]|$)"
+			end:   "(?<!\\\\)(?=\n)"
+			beginCaptures:
+				1: {name: "entity.function.name.roff"}
+			patterns: [{
+				begin: '(?x)(?<=
+					^[.\']        BI|
+					^[.\'][ \t]{1}BI|
+					^[.\'][ \t]{2}BI|
+					^[.\'][ \t]{3}BI|
+					^[.\'][ \t]{4}BI)
+					[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
+				end: '(?<=")|(?<!\\\\)$'
+				patterns: [{include: "#italic-strings"}]
+				beginCaptures:
+					1:
+						name: "markup.bold.roff"
+						patterns: [{include: "#bold-strings"}]
+			},{
+				begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
+				end: '$|(?:^|[ \t]+|(?<="))(("[^"]*")|\\S+)'
+				patterns: [{include: "#bold-strings"}]
+				endCaptures:
+					1: {name: "markup.italic.roff"}
+					2: {patterns: [{include: "#string"}]}
+			}]
+
+		},{
+
+			# .BR: Bold and Roman
+			name: "markup.bold-roman.roff"
+			begin: "^([.'][ \t]*BR)(?=[ \t\\\\]|$)"
+			end:   "(?<!\\\\)(?=\n)"
+			beginCaptures:
+				1: {name: "entity.function.name.roff"}
+			patterns: [{
+				begin: '(?x)(?<=
+					^[.\']        BR|
+					^[.\'][ \t]{1}BR|
+					^[.\'][ \t]{2}BR|
+					^[.\'][ \t]{3}BR|
+					^[.\'][ \t]{4}BR)
+					[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
+				end: '(?<=")|(?<!\\\\)$'
+				patterns: [{include: "#params"}]
+				beginCaptures:
+					1:
+						name: "markup.bold.roff"
+						patterns: [{include: "#bold-strings"}]
+			},{
+				begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
+				end: '$|(?:^|[ \t]+|(?<="))(?:("[^"]*")|\\S+)'
+				patterns: [{include: "#bold-strings"}]
+				endCaptures:
+					1: {patterns: [{include: "#string"}]}
+			}]

-
-	# Macro: .IR
-	"macro-italic-roman":
-		name: "markup.italic-roman.roff"
-		begin: "^([.'][ \t]*IR)(?=[ \t\\\\]|$)"
-		end:   "(?<!\\\\)(?=\n)"
-		beginCaptures:
-			1: {name: "entity.function.name.roff"}
-		patterns: [{
-			begin: '(?x)(?<=
-				^[.\']        IR|
-				^[.\'][ \t]{1}IR|
-				^[.\'][ \t]{2}IR|
-				^[.\'][ \t]{3}IR|
-				^[.\'][ \t]{4}IR)
-				[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
-			end: '(?<=")|(?<!\\\\)$'
-			patterns: [{include: "#params"}]
-			beginCaptures:
-				1:
-					name: "markup.italic.roff"
-					patterns: [{include: "#italic-strings"}]
-		}, {
-			begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
-			end: '$|(?:^|[ \t]+|(?<="))(?:("[^"]*")|\\S+)'
-			patterns: [{include: "#italic-strings"}]
-			endCaptures:
-				1: {patterns: [{include: "#string"}]}
-		}]
-
-
-	# Macro: .RB
-	"macro-roman-bold":
-		name: "markup.roman-bold.roff"
-		begin: "^([.'][ \t]*RB)(?=[ \t\\\\]|$)"
-		end:   "(?<!\\\\)(?=\n)"
-		beginCaptures:
-			1: {name: "entity.function.name.roff"}
-		patterns: [{
-			begin: '(?x)(?<=
-				^[.\']        RB|
-				^[.\'][ \t]{1}RB|
-				^[.\'][ \t]{2}RB|
-				^[.\'][ \t]{3}RB|
-				^[.\'][ \t]{4}RB)
-				[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
-			end: '(?<=")|(?<!\\\\)$'
-			patterns: [{include: "#bold-strings"}]
-			beginCaptures:
-				1: patterns: [{include: "#string"}]
-		}, {
-			begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
-			end: '$|(?:^|[ \t]+|(?<="))(("[^"]*")|\\S+)'
-			patterns: [{include: "#string"}]
-			endCaptures:
-				1: {name: "markup.bold.roff"}
-				2: {patterns: [{include: "#string"}]}
-		}]
-
-
-	# Macro: .RI
-	"macro-roman-italic":
-		name: "markup.roman-italic.roff"
-		begin: "^([.'][ \t]*RI)(?=[ \t\\\\]|$)"
-		end:   "(?<!\\\\)(?=\n)"
-		beginCaptures:
-			1: {name: "entity.function.name.roff"}
-		patterns: [{
-			begin: '(?x)(?<=
-				^[.\']        RI|
-				^[.\'][ \t]{1}RI|
-				^[.\'][ \t]{2}RI|
-				^[.\'][ \t]{3}RI|
-				^[.\'][ \t]{4}RI)
-				[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
-			end: '(?<=")|(?<!\\\\)$'
-			patterns: [{include: "#italic-strings"}]
-			beginCaptures:
-				1: patterns: [{include: "#string"}]
-		}, {
-			begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
-			end: '$|(?:^|[ \t]+|(?<="))(("[^"]*")|\\S+)'
-			patterns: [{include: "#string"}]
-			endCaptures:
-				1: {name: "markup.italic.roff"}
-				2: {patterns: [{include: "#string"}]}
-		}]
+		},{
+
+			# .IB: Italic and bold
+			name: "markup.italic-bold.roff"
+			begin: "^([.'][ \t]*IB)(?=[ \t\\\\]|$)"
+			end:   "(?<!\\\\)(?=\n)"
+			beginCaptures:
+				1: {name: "entity.function.name.roff"}
+			patterns: [{
+				begin: '(?x)(?<=
+					^[.\']        IB|
+					^[.\'][ \t]{1}IB|
+					^[.\'][ \t]{2}IB|
+					^[.\'][ \t]{3}IB|
+					^[.\'][ \t]{4}IB)
+					[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
+				end: '(?<=")|(?<!\\\\)$'
+				patterns: [{include: "#bold-strings"}]
+				beginCaptures:
+					1:
+						name: "markup.italic.roff"
+						patterns: [{include: "#italic-strings"}]
+			},{
+				begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
+				end: '$|(?:^|[ \t]+|(?<="))(("[^"]*")|\\S+)'
+				patterns: [{include: "#italic-strings"}]
+				endCaptures:
+					1: {name: "markup.bold.roff"}
+					2: {patterns: [{include: "#string"}]}
+			}]
+
+		},{
+
+			# .IR: Italic and Roman
+			name: "markup.italic-roman.roff"
+			begin: "^([.'][ \t]*IR)(?=[ \t\\\\]|$)"
+			end:   "(?<!\\\\)(?=\n)"
+			beginCaptures:
+				1: {name: "entity.function.name.roff"}
+			patterns: [{
+				begin: '(?x)(?<=
+					^[.\']        IR|
+					^[.\'][ \t]{1}IR|
+					^[.\'][ \t]{2}IR|
+					^[.\'][ \t]{3}IR|
+					^[.\'][ \t]{4}IR)
+					[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
+				end: '(?<=")|(?<!\\\\)$'
+				patterns: [{include: "#params"}]
+				beginCaptures:
+					1:
+						name: "markup.italic.roff"
+						patterns: [{include: "#italic-strings"}]
+			},{
+				begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
+				end: '$|(?:^|[ \t]+|(?<="))(?:("[^"]*")|\\S+)'
+				patterns: [{include: "#italic-strings"}]
+				endCaptures:
+					1: {patterns: [{include: "#string"}]}
+			}]
+
+		},{
+
+			# .RB: Roman and bold
+			name: "markup.roman-bold.roff"
+			begin: "^([.'][ \t]*RB)(?=[ \t\\\\]|$)"
+			end:   "(?<!\\\\)(?=\n)"
+			beginCaptures:
+				1: {name: "entity.function.name.roff"}
+			patterns: [{
+				begin: '(?x)(?<=
+					^[.\']        RB|
+					^[.\'][ \t]{1}RB|
+					^[.\'][ \t]{2}RB|
+					^[.\'][ \t]{3}RB|
+					^[.\'][ \t]{4}RB)
+					[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
+				end: '(?<=")|(?<!\\\\)$'
+				patterns: [{include: "#bold-strings"}]
+				beginCaptures:
+					1: patterns: [{include: "#string"}]
+			},{
+				begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
+				end: '$|(?:^|[ \t]+|(?<="))(("[^"]*")|\\S+)'
+				patterns: [{include: "#string"}]
+				endCaptures:
+					1: {name: "markup.bold.roff"}
+					2: {patterns: [{include: "#string"}]}
+			}]
+
+		},{
+
+			# .RI: Roman and italic
+			name: "markup.roman-italic.roff"
+			begin: "^([.'][ \t]*RI)(?=[ \t\\\\]|$)"
+			end:   "(?<!\\\\)(?=\n)"
+			beginCaptures:
+				1: {name: "entity.function.name.roff"}
+			patterns: [{
+				begin: '(?x)(?<=
+					^[.\']        RI|
+					^[.\'][ \t]{1}RI|
+					^[.\'][ \t]{2}RI|
+					^[.\'][ \t]{3}RI|
+					^[.\'][ \t]{4}RI)
+					[ \\t]+(".*?(?<!")"(?!")|[^"\\s]\\S*)[ \t]*(?=")'
+				end: '(?<=")|(?<!\\\\)$'
+				patterns: [{include: "#italic-strings"}]
+				beginCaptures:
+					1: patterns: [{include: "#string"}]
+			},{
+				begin: '(?=("[^"]*(?<!\\\\)")|\\S+)'
+				end: '$|(?:^|[ \t]+|(?<="))(("[^"]*")|\\S+)'
+				patterns: [{include: "#string"}]
+				endCaptures:
+					1: {name: "markup.italic.roff"}
+					2: {patterns: [{include: "#string"}]}
+			}]
+		}]
+
+

 	# Bold words ("Quoted"/Unquoted)


Duplicate formatted substring rules for italicised text

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -308,3 +308,38 @@
 				patterns: [{include: "#bold-bareword"}]
 			}]
 		}]
+
+
+
+	# Italic: "Quoted"
+	"italic-string":
+		patterns: [{
+			name: "markup.italic.roff"
+			begin: '(?=")'
+			end:   '$|(?<=")'
+			patterns: [{
+
+				# Concealed newline
+				begin: "(?<=\\S)\\\\(?=\n)"
+				end: "(?<!\\\\)(?=\\s|$)"
+				beginCaptures: 0: {name: "constant.character.escape.newline.roff"}
+				patterns: [{include: "#italic-string"}]
+
+			}, {include: "#double-quoted-string"}]
+		}]
+
+
+	# Italic: Unquoted
+	"italic-bareword":
+		patterns: [{
+			name: "markup.italic.roff"
+			begin: "\\G(?!\\\\\n)"
+			end: "(?=\\s|$)"
+			patterns: [{
+				# Concealed newline
+				begin: "(?<=\\S)\\\\(?=\n)"
+				end: "(?<!\\\\)(?=\\s|$)"
+				beginCaptures: 0: {name: "constant.character.escape.newline.roff"}
+				patterns: [{include: "#italic-bareword"}]
+			}]
+		}]


Add generic parameter highlighting

Add generic parameter highlighting

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -69,6 +69,10 @@
 			{include: "#escapes"}
 			{include: "#string"}
 			{include: "#number"}
+
+			# Highlight anything else as a parameter
+			match: "\\S+"
+			name: "variable.parameter.roff"
 		]


Restores the emission of tracking events

--- src/api/api.coffee
+++ src/api/api.coffee
@@ -70,6 +70,15 @@

       onSuccess = (res={})->
         setCurrentUser(res.headers) if res.provider == 'hull'
+        headers = res?.headers
+        if headers? and res.provider == 'hull'
+          hullTrack = headers['Hull-Track']
+          if hullTrack
+            try
+              [eventName, trackParams] = JSON.parse(atob(hullTrack))
+              emitter.emit(eventName, trackParams)
+            catch error
+              false
         callback(res.response)
         deferred.resolve(res.response, res.headers)
       onError = (err={})->


Update Atom config

Update Atom config

--- dotfiles/.atom/config.cson
+++ dotfiles/.atom/config.cson
@@ -11,8 +11,9 @@
     ignored: [
       "linter"
     ]
+  "autosave-onchange":
+    disableOnCommit: false
   core:
-    closeDeletedFileTabs: true
     customFileTypes:
       customFileTypes:
         "source.ruby": [
@@ -52,6 +53,20 @@
       "tester-jest"
       "tree-view"
       "teletype"
+      "linter"
+    ]
+    excludeVcsIgnoredPaths: false
+    ignoredNames: [
+      ".git"
+      ".hg"
+      ".svn"
+      ".DS_Store"
+      "._*"
+      "Thumbs.db"
+      "desktop.ini"
+      "node_modules"
+      "dist"
+      "build"
     ]
     openEmptyEditorOnStart: false
     projectHome: "/Users/MoOx/Sync/Development"
@@ -120,106 +135,8 @@
       enableAutomaticSquareBracketInsertion: false
     "nuclide-python": {}
     use:
-      "atom-ide-busy-signal": "default"
-      "atom-ide-code-actions": "default"
-      "atom-ide-code-format": "default"
-      "atom-ide-code-highlight": "default"
-      "atom-ide-console": "default"
-      "atom-ide-datatip": "default"
-      "atom-ide-definitions": "default"
-      "atom-ide-diagnostics": "default"
-      "atom-ide-diagnostics-ui": "default"
-      "atom-ide-find-references": "default"
-      "atom-ide-global": "default"
-      "atom-ide-outline-view": "default"
-      "atom-ide-signature-help": "default"
-      hyperclick: "default"
-      "nuclide-adb-logcat": "default"
-      "nuclide-adb-sdb": "default"
-      "nuclide-atom-notifications": "default"
-      "nuclide-autocomplete": "default"
-      "nuclide-blame": "default"
-      "nuclide-blame-provider-hg": "default"
-      "nuclide-bookshelf": "default"
-      "nuclide-buck": "default"
-      "nuclide-buck-ios": "default"
-      "nuclide-clang": "default"
-      "nuclide-clipboard-path": "default"
-      "nuclide-code-search": "default"
-      "nuclide-context-view": "default"
-      "nuclide-cquery-lsp": "default"
-      "nuclide-ctags": "default"
-      "nuclide-current-working-directory": "default"
-      "nuclide-debugger": "default"
-      "nuclide-debugger-native": "default"
-      "nuclide-debugger-new": "default"
-      "nuclide-debugger-php": "default"
-      "nuclide-debugger-vsp": "default"
-      "nuclide-deep-link": "default"
-      "nuclide-definition-preview": "default"
-      "nuclide-definition-preview-provider": "default"
-      "nuclide-deprecation-cop": "default"
-      "nuclide-device-panel": "default"
-      "nuclide-device-panel-ios": "default"
-      "nuclide-diagnostics-extras": "default"
-      "nuclide-distraction-free-mode": "default"
-      "nuclide-file-family": "default"
-      "nuclide-file-family-projectionist": "default"
-      "nuclide-file-tree": "default"
-      "nuclide-file-watcher": "default"
-      "nuclide-flow": "default"
-      "nuclide-fuzzy-filename-provider": "default"
-      "nuclide-graphql": "default"
-      "nuclide-hack": "default"
-      "nuclide-health": "default"
-      "nuclide-hg-repository": "default"
-      "nuclide-hhvm": "default"
-      "nuclide-home": "default"
-      "nuclide-http-request-sender": "default"
-      "nuclide-infer-al": "default"
-      "nuclide-ios-simulator-logs": "default"
-      "nuclide-js-imports-client": "default"
-      "nuclide-json": "default"
-      "nuclide-key-binding-hint-status": "default"
-      "nuclide-language-hack": "default"
-      "nuclide-language-infer-al": "default"
-      "nuclide-language-reason": "default"
-      "nuclide-metro": "default"
-      "nuclide-move-item-to-available-pane": "default"
-      "nuclide-navigation-stack": "default"
-      "nuclide-navigation-stack-status-bar": "default"
-      "nuclide-notifications": "default"
-      "nuclide-nux": "default"
-      "nuclide-objc": "default"
-      "nuclide-ocaml": "default"
-      "nuclide-open-filenames-provider": "default"
-      "nuclide-outline-view-extras": "default"
-      "nuclide-project": "default"
-      "nuclide-python": "default"
-      "nuclide-quick-open": "default"
-      "nuclide-react-inspector": "default"
-      "nuclide-recent-files-provider": "default"
-      "nuclide-recent-files-service": "default"
-      "nuclide-refactorizer": "default"
-      "nuclide-related-files": "default"
-      "nuclide-remote-atom": "default"
-      "nuclide-remote-projects": "default"
-      "nuclide-settings": "default"
       "nuclide-side-bar": false
       "nuclide-source-control-side-bar": false
-      "nuclide-ssh-tunnel": "default"
-      "nuclide-swift": "default"
-      "nuclide-syntactic-selection": "default"
-      "nuclide-task-runner": "default"
-      "nuclide-terminal": "default"
-      "nuclide-test-runner": "default"
-      "nuclide-type-coverage": "default"
-      "nuclide-type-hint": "default"
-      "nuclide-ui": "default"
-      "nuclide-unicode-datatip": "default"
-      "nuclide-url-hyperclick": "default"
-      "nuclide-vcs-log": "default"
-      "nuclide-working-sets": "default"
   "ocaml-merlin": {}
   "one-vibrancy":
     effect: "dark"
@@ -227,11 +144,10 @@
   "prettier-atom":
     formatOnSaveOptions:
       enabled: true
-      excludedGlobs: [
-        "*.json"
-      ]
       respectEslintignore: false
       showInStatusBar: true
+    prettierEslintOptions:
+      prettierLast: true
     prettierOptions: {}
   "reason-refmt": {}
   "remote-ftp":


Protect against removing resources that don't exist

--- server/models/crud.coffee
+++ server/models/crud.coffee
@@ -71,10 +71,12 @@
     Resource.findOne { _id : id}, (err, resource) ->
       if err
         callback err
-      else
+      else if resource
         resource.remove (err) ->
           callback err
-
+      else
+        callback null
+
   find: find
   findById: findById
   findOneBy: findOneBy


Unset importing after upload confirmation

--- server/publication.coffee
+++ server/publication.coffee
@@ -102,8 +102,9 @@
     ,
       $set:
         _.extend _.pick(metadata or {}, 'authorsRaw', 'title', 'abstract', 'doi'),
-          confirmed: true
           updated: moment.utc().toDate()
+      $unset:
+        importing: ''

 Meteor.publish 'publications-by-author-slug', (authorSlug) ->
   if not authorSlug


fix a bug where the double click functionality was looking at the wrong node

--- modules/tree/main/index.coffee
+++ modules/tree/main/index.coffee
@@ -62,7 +62,7 @@
     renderLazyChildren(treeNode, true)
     treeNode.selectAll('.hx-tree-node-children').style('display', 'block')

-    if treeNode.style('display') is 'block'
+    if treeNode.shallowSelect('.hx-tree-node-children').style('display') is 'block'
       formatChildren(tree, treeNode.selectAll('.hx-tree-node'), animate)
     else
       toggle(iconElement)


Change the save button when new content is created

(added to the allContent)

--- scripts/views/workspace/menu/auth.coffee
+++ scripts/views/workspace/menu/auth.coffee
@@ -32,8 +32,12 @@

     initialize: () ->
       @listenTo(session, 'login logout', @render)
+
       # When a model has changed (triggered `dirty`) update the Save button
       @listenTo content, 'dirty', (model, options) => @changed()
+      # Update the Save button when new Folder/Book/Module is created (added to `content`)
+      @listenTo content, 'add remove', (model, collection, options) =>
+        @changed() if not (options.loading or options.parse)

       # Bind a function to the window if the user tries to navigate away from this page
       $(window).on 'beforeunload', () ->


#579 Lowercase actions

#579 Lowercase actions

--- app/assets/javascripts/admin/views/dataset/dataset_list_item_view.coffee
+++ app/assets/javascripts/admin/views/dataset/dataset_list_item_view.coffee
@@ -71,13 +71,13 @@
         <% if(isActive){ %>

             <a href="/datasets/<%= name %>/view" title="View dataset">
-               View
+               view

-               Start Skeleton Tracing
+               start Skeleton Tracing

-               Start Volume Tracing
+               start Volume Tracing

         <% } %>


SCAL-1264: using correct variable

SCAL-1264: using correct variable

--- app/assets/javascripts/infrastructures/infrastructures_booster.js.coffee
+++ app/assets/javascripts/infrastructures/infrastructures_booster.js.coffee
@@ -1,6 +1,6 @@
 class window.InfrastructuresBooster
   constructor: (@infrastructureName, @disabledInfrastructures, @dialogId) ->
-    @dialog = $("##{dialogId}")
+    @dialog = $("##{@dialogId}")
     @schedulerForm = $('#scheduler-form form')
     @bindToSubmissionForms()


Display a SaveAs dialog if build is run with an unsaved text editor

--- lib/builder.coffee
+++ lib/builder.coffee
@@ -66,15 +66,17 @@
     @ltConsole.show()
     @ltConsole.clear()

-    te = atom.workspace.getActiveTextEditor()
-    if te == ''
-      @ltConsole.addContent("Focus the text editor before invoking a build")
-      return
-
     # save on build
+    # if unsaved, run saveAs
+    unless te.getPath()?
+      atom.workspace.getActivePane().saveItem(te)

     if te.isModified()
       te.save()
+
+    unless te.getPath()?
+      alert 'Please save your file before attempting to build'
+      return

     fname = get_tex_root(te)

@@ -105,6 +107,10 @@
       user_program = directives.program
     else
       user_program = atom.config.get("latextools.builderSettings.program")
+
+    # prepare the build console
+    @ltConsole.show()
+    @ltConsole.clear()

     # Now prepare path
     # TODO: also env if needed


revise modalAlert

revise modalAlert

--- static/js/script.coffee
+++ static/js/script.coffee
@@ -149,7 +149,6 @@
 		jqxhr = $.post '/joinsdi/upload/form', form, (body)->
 			submitID = body
 			submitAvatar()
-
 		jqxhr.fail ->
 			modalAlert('网络错误', '信息表上传失败。请确认网络链接正常，或向我们反馈')

@@ -182,7 +181,7 @@
 		xhr.onload = ->
 			if this.status == 200
 				uploaded = true
-				modalAlert('上传成功', '我们已经向您所填写的邮箱发送了确认邮件，请注意查收', true)
+				modalAlert('上传成功', '我们已经向您所填写的邮箱发送了确认邮件，请注意查收')
 			else
 				modalAlert('网络错误', '报名表上传失败。请确认网络链接正常，或向我们反馈')
 		xhr.onerror = ->
@@ -193,18 +192,14 @@
 	initModalProgress()
 	submitForm()

-modalAlert = (header, content, final)->
+modalAlert = (header, content)->
 	element = $('#error')
 	element.children('.header').text(header)
 	element.children('.content').text(content)
-	if final
-		element.modal
-			allowMultiple: true
-			onHide: ->
-				$('#progress').modal('hide')
-	else
-		element.modal
-			allowMultiple: true
+	element.modal
+		allowMultiple: true
+		onHide: ->
+			$('#progress').modal('hide')

 	element.modal('setting', 'transition', 'vertical flip').modal('show')


binding: don't process scripts

binding: don't process scripts

--- src/binding.coffee
+++ src/binding.coffee
@@ -421,6 +421,8 @@

             attrName = element.nodeName.toLowerCase()
             testDirective attrName, args
+            if attrName is 'script'  # don't process scripts
+                skipChildren = true

             args.attr_type = 'A'
             for attr in element.attributes