Merge branch 'master' into config

This commit is contained in:
Nathan Sobo
2012-12-23 12:52:02 -07:00
11 changed files with 215 additions and 52 deletions

View File

@@ -2,6 +2,8 @@
$ = require 'jquery'
_ = require 'underscore'
Range = require 'range'
Point = require 'point'
module.exports =
class Gutter extends View
@@ -18,9 +20,17 @@ class Gutter extends View
@attached = true
editor = @editor()
highlightCursorLine = => @highlightCursorLine()
editor.on 'cursor-move', highlightCursorLine
editor.on 'selection-change', highlightCursorLine
highlightLines = => @highlightLines()
editor.on 'cursor-move', highlightLines
editor.on 'selection-change', highlightLines
@on 'click', '.line-number', (e) =>
row = parseInt($(e.target).text()) - 1
position = new Point(row, 0)
if e.shiftKey
@editor().selectToScreenPosition(position)
else
@editor().setCursorScreenPosition(position)
@calculateWidth()
editor: ->
@@ -63,8 +73,8 @@ class Gutter extends View
@calculateWidth()
@firstScreenRow = startScreenRow
@lastScreenRow = endScreenRow
@highlightedRow = null
@highlightCursorLine()
@highlightedRows = null
@highlightLines()
calculateWidth: ->
highestNumberWidth = @editor().getLineCount().toString().length * @editor().charWidth
@@ -73,17 +83,38 @@ class Gutter extends View
@lineNumbers.width(highestNumberWidth + @calculateLineNumberPadding())
@widthChanged?(@outerWidth())
highlightCursorLine: ->
if @editor().getSelection().isEmpty()
rowToHighlight = @editor().getCursorScreenPosition().row
return if rowToHighlight == @highlightedRow
return if rowToHighlight < @firstScreenRow or rowToHighlight > @lastScreenRow
removeLineHighlights: ->
return unless @highlightedLineNumbers
for line in @highlightedLineNumbers
line.classList.remove('cursor-line')
line.classList.remove('cursor-line-no-selection')
@highlightedLineNumbers = null
@highlightedLineNumber?.classList.remove('cursor-line')
if @highlightedLineNumber = @lineNumbers[0].children[rowToHighlight - @firstScreenRow]
@highlightedLineNumber.classList.add('cursor-line')
@highlightedRow = rowToHighlight
addLineHighlight: (row, emptySelection) ->
return if row < @firstScreenRow or row > @lastScreenRow
@highlightedLineNumbers ?= []
if highlightedLineNumber = @lineNumbers[0].children[row - @firstScreenRow]
highlightedLineNumber.classList.add('cursor-line')
highlightedLineNumber.classList.add('cursor-line-no-selection') if emptySelection
@highlightedLineNumbers.push(highlightedLineNumber)
highlightLines: ->
if @editor().getSelection().isEmpty()
row = @editor().getCursorScreenPosition().row
rowRange = new Range([row, 0], [row, 0])
return if @selectionEmpty and @highlightedRows?.isEqual(rowRange)
@removeLineHighlights()
@addLineHighlight(row, true)
@highlightedRows = rowRange
@selectionEmpty = true
else
@highlightedLineNumber?.classList.remove('cursor-line')
@highlightedLineNumber = null
@highlightedRow = null
selectedRows = @editor().getSelection().getScreenRange()
selectedRows = new Range([selectedRows.start.row, 0], [selectedRows.end.row, 0])
return if not @selectionEmpty and @highlightedRows?.isEqual(selectedRows)
@removeLineHighlights()
for row in [selectedRows.start.row..selectedRows.end.row]
@addLineHighlight(row, false)
@highlightedRows = selectedRows
@selectionEmpty = false

View File

@@ -186,5 +186,5 @@ class LanguageMode
if desiredIndentLevel < currentIndentLevel
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
tokenizeLine: (line, stack) ->
{tokens, stack} = @grammar.tokenizeLine(line, stack)
tokenizeLine: (line, stack, firstLine) ->
{tokens, stack} = @grammar.tokenizeLine(line, stack, firstLine)

View File

@@ -29,8 +29,7 @@ class TextMateGrammar
data = {patterns: [data], tempName: name} if data.begin? or data.match?
@repository[name] = new Rule(this, data)
tokenizeLine: (line, ruleStack=[@initialRule]) ->
ruleStack ?= [@initialRule]
tokenizeLine: (line, ruleStack=[@initialRule], firstLine=false) ->
ruleStack = new Array(ruleStack...) # clone ruleStack
tokens = []
position = 0
@@ -42,9 +41,9 @@ class TextMateGrammar
tokens = [new Token(value: "", scopes: scopes)]
return { tokens, ruleStack }
break if position == line.length
break if position == line.length + 1 # include trailing newline position
if match = _.last(ruleStack).getNextTokens(ruleStack, line, position)
if match = _.last(ruleStack).getNextTokens(ruleStack, line, position, firstLine)
{ nextTokens, tokensStartPosition, tokensEndPosition } = match
if position < tokensStartPosition # unmatched text before next tokens
tokens.push(new Token(
@@ -56,12 +55,14 @@ class TextMateGrammar
position = tokensEndPosition
else # push filler token for unmatched text at end of line
tokens.push(new Token(
value: line[position...line.length]
scopes: scopes
))
if position < line.length
tokens.push(new Token(
value: line[position...line.length]
scopes: scopes
))
break
ruleStack.forEach (rule) -> rule.clearAnchorPosition()
{ tokens, ruleStack }
ruleForInclude: (name) ->
@@ -79,6 +80,7 @@ class Rule
patterns: null
allPatterns: null
createEndPattern: null
anchorPosition: -1
constructor: (@grammar, {@scopeName, patterns, @endPattern}) ->
patterns ?= []
@@ -95,14 +97,30 @@ class Rule
@allPatterns.push(pattern.getIncludedPatterns(included)...)
@allPatterns
getScanner: ->
@scanner ?= new OnigScanner(_.pluck(@getIncludedPatterns(), 'regexSource'))
clearAnchorPosition: -> @anchorPosition = -1
getNextTokens: (stack, line, position) ->
getScanner: (position, firstLine) ->
return @scanner if @scanner
anchored = false
regexes = []
@getIncludedPatterns().forEach (pattern) =>
if pattern.anchored
anchored = true
regex = pattern.replaceAnchor(firstLine, position, @anchorPosition)
else
regex = pattern.regexSource
regexes.push regex if regex
regexScanner = new OnigScanner(regexes)
@scanner = regexScanner unless anchored
regexScanner
getNextTokens: (stack, line, position, firstLine) ->
patterns = @getIncludedPatterns()
# Add a `\n` to appease patterns that contain '\n' explicitly
return null unless result = @getScanner().findNextMatch(line + "\n", position)
return null unless result = @getScanner(position, firstLine).findNextMatch("#{line}\n", position)
{ index, captureIndices } = result
# Since the `\n' (added above) is not part of the line, truncate captures to the line's actual length
lineLength = line.length
@@ -130,6 +148,7 @@ class Pattern
scopeName: null
captures: null
backReferences: null
anchored: false
constructor: (@grammar, { name, contentName, @include, match, begin, end, captures, beginCaptures, endCaptures, patterns, @popRule, hasBackReferences}) ->
@scopeName = name ? contentName # TODO: We need special treatment of contentName
@@ -144,6 +163,42 @@ class Pattern
@captures = beginCaptures ? captures
endPattern = new Pattern(@grammar, { match: end, captures: endCaptures ? captures, popRule: true})
@pushRule = new Rule(@grammar, { @scopeName, patterns, endPattern })
@anchored = @hasAnchor()
hasAnchor: ->
return false unless @regexSource
escape = false
for character in @regexSource.split('')
return true if escape and 'AGz'.indexOf(character) isnt -1
escape = not escape and character is '\\'
false
replaceAnchor: (firstLine, offset, anchor) ->
escaped = []
placeholder = '\uFFFF'
escape = false
for character in @regexSource.split('')
if escape
switch character
when 'A'
if firstLine
escaped.push("\\#{character}")
else
escaped.push(placeholder)
when 'G'
if offset is anchor
escaped.push("\\#{character}")
else
escaped.push(placeholder)
when 'z' then escaped.push('$(?!\n)(?<!\n)')
else escaped.push("\\#{character}")
escape = false
else if character is '\\'
escape = true
else
escaped.push(character)
escaped.join('')
resolveBackReferences: (line, beginCaptureIndices) ->
beginCaptures = []
@@ -180,7 +235,9 @@ class Pattern
else
tokens = [new Token(value: line[start...end], scopes: scopes)]
if @pushRule
stack.push(@pushRule.getRuleToPush(line, captureIndices))
ruleToPush = @pushRule.getRuleToPush(line, captureIndices)
ruleToPush.anchorPosition = captureIndices[2]
stack.push(ruleToPush)
else if @popRule
stack.pop()
@@ -226,4 +283,3 @@ shiftCapture = (captureIndices) ->
scopesFromStack = (stack) ->
_.compact(_.pluck(stack, "scopeName"))

View File

@@ -134,7 +134,7 @@ class TokenizedBuffer
buildTokenizedScreenLineForRow: (row, ruleStack) ->
line = @buffer.lineForRow(row)
{ tokens, ruleStack } = @languageMode.tokenizeLine(line, ruleStack)
{ tokens, ruleStack } = @languageMode.tokenizeLine(line, ruleStack, row is 0)
new ScreenLine({tokens, ruleStack, @tabLength})
lineForScreenRow: (row) ->