Merge remote-tracking branch 'origin/dev' into better-anchors

Conflicts:
	src/app/cursor.coffee
	src/app/editor.coffee
This commit is contained in:
Nathan Sobo
2013-01-30 12:59:04 -07:00
18 changed files with 470 additions and 91 deletions

View File

@@ -121,9 +121,9 @@ class Buffer
new Range([0, 0], [@getLastRow(), @getLastLine().length])
getTextInRange: (range) ->
range = Range.fromObject(range)
range = @clipRange(range)
if range.start.row == range.end.row
return @lines[range.start.row][range.start.column...range.end.column]
return @lineForRow(range.start.row)[range.start.column...range.end.column]
multipleLines = []
multipleLines.push @lineForRow(range.start.row)[range.start.column..] # first line
@@ -200,7 +200,7 @@ class Buffer
startPoint = [start, 0]
endPoint = [end + 1, 0]
@change(new Range(startPoint, endPoint), '')
@delete(new Range(startPoint, endPoint))
append: (text) ->
@insert(@getEofPosition(), text)
@@ -226,6 +226,10 @@ class Buffer
new Point(row, column)
clipRange: (range) ->
range = Range.fromObject(range)
new Range(@clipPosition(range.start), @clipPosition(range.end))
prefixAndSuffixForRange: (range) ->
prefix: @lines[range.start.row][0...range.start.column]
suffix: @lines[range.end.row][range.end.column..]

View File

@@ -9,7 +9,6 @@ class Cursor
screenPosition: null
bufferPosition: null
goalColumn: null
wordRegex: /(\w+)|([^\w\n]+)/g
visible: true
needsAutoscroll: null
@@ -58,9 +57,18 @@ class Cursor
isVisible: -> @visible
wordRegExp: ->
nonWordCharacters = config.get("editor.nonWordCharacters")
new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(nonWordCharacters)}]+|[#{_.escapeRegExp(nonWordCharacters)}]+", "g")
isLastCursor: ->
this == @editSession.getCursor()
isSurroundedByWhitespace: ->
{row, column} = @getBufferPosition()
range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]]
/^\s+$/.test @editSession.getTextInBufferRange(range)
clearAutoscroll: ->
@needsAutoscroll = null
@@ -149,14 +157,16 @@ class Cursor
allowPrevious = options.allowPrevious ? true
currentBufferPosition = @getBufferPosition()
previousNonBlankRow = @editSession.buffer.previousNonBlankRow(currentBufferPosition.row)
previousLinesRange = [[previousNonBlankRow, 0], currentBufferPosition]
range = [[previousNonBlankRow, 0], currentBufferPosition]
beginningOfWordPosition = currentBufferPosition
@editSession.backwardsScanInRange (options.wordRegex || @wordRegex), previousLinesRange, (match, matchRange, { stop }) =>
beginningOfWordPosition = null
@editSession.backwardsScanInRange (options.wordRegex ? @wordRegExp()), range, (match, matchRange, { stop }) =>
if matchRange.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious
beginningOfWordPosition = matchRange.start
stop()
beginningOfWordPosition
if not beginningOfWordPosition?.isEqual(currentBufferPosition)
stop()
beginningOfWordPosition or currentBufferPosition
getEndOfCurrentWordBufferPosition: (options = {}) ->
allowNext = options.allowNext ? true
@@ -164,11 +174,12 @@ class Cursor
range = [currentBufferPosition, @editSession.getEofBufferPosition()]
endOfWordPosition = null
@editSession.scanInRange (options.wordRegex || @wordRegex), range, (match, matchRange, { stop }) =>
endOfWordPosition = matchRange.end
if not allowNext and matchRange.start.isGreaterThan(currentBufferPosition)
endOfWordPosition = currentBufferPosition
stop()
@editSession.scanInRange (options.wordRegex ? @wordRegExp()), range, (match, matchRange, { stop }) =>
if matchRange.start.isLessThanOrEqual(currentBufferPosition) or allowNext
endOfWordPosition = matchRange.end
if not endOfWordPosition?.isEqual(currentBufferPosition)
stop()
endOfWordPosition or currentBufferPosition
getCurrentWordBufferRange: (options={}) ->

View File

@@ -311,6 +311,10 @@ class EditSession
fold.destroy()
@setCursorBufferPosition([fold.startRow, 0])
isFoldedAtBufferRow: (bufferRow) ->
screenRow = @screenPositionForBufferPosition([bufferRow]).row
@isFoldedAtScreenRow(screenRow)
isFoldedAtScreenRow: (screenRow) ->
@lineForScreenRow(screenRow)?.fold?
@@ -338,6 +342,81 @@ class EditSession
toggleLineCommentsForBufferRows: (start, end) ->
@languageMode.toggleLineCommentsForBufferRows(start, end)
moveLineUp: ->
selection = @getSelectedBufferRange()
return if selection.start.row is 0
lastRow = @buffer.getLastRow()
return if selection.isEmpty() and selection.start.row is lastRow and @buffer.getLastLine() is ''
@transact =>
foldedRows = []
rows = [selection.start.row..selection.end.row]
if selection.start.row isnt selection.end.row and selection.end.column is 0
rows.pop() unless @isFoldedAtScreenRow(@screenPositionForBufferPosition(selection.end).row)
for row in rows
screenRow = @screenPositionForBufferPosition([row]).row
if @isFoldedAtScreenRow(screenRow)
bufferRange = @bufferRangeForScreenRange([[screenRow], [screenRow + 1]])
startRow = bufferRange.start.row
endRow = bufferRange.end.row - 1
foldedRows.push(endRow - 1)
else
startRow = row
endRow = row
endPosition = Point.min([endRow + 1], @buffer.getEofPosition())
lines = @buffer.getTextInRange([[startRow], endPosition])
if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row)
lines = "#{lines}\n"
@buffer.deleteRows(startRow, endRow)
@buffer.insert([startRow - 1], lines)
@foldBufferRow(foldedRow) for foldedRow in foldedRows
newStartPosition = [selection.start.row - 1, selection.start.column]
newEndPosition = [selection.end.row - 1, selection.end.column]
@setSelectedBufferRange([newStartPosition, newEndPosition], preserveFolds: true)
moveLineDown: ->
selection = @getSelectedBufferRange()
lastRow = @buffer.getLastRow()
return if selection.end.row is lastRow
return if selection.end.row is lastRow - 1 and @buffer.getLastLine() is ''
@transact =>
foldedRows = []
rows = [selection.end.row..selection.start.row]
if selection.start.row isnt selection.end.row and selection.end.column is 0
rows.shift() unless @isFoldedAtScreenRow(@screenPositionForBufferPosition(selection.end).row)
for row in rows
screenRow = @screenPositionForBufferPosition([row]).row
if @isFoldedAtScreenRow(screenRow)
bufferRange = @bufferRangeForScreenRange([[screenRow], [screenRow + 1]])
startRow = bufferRange.start.row
endRow = bufferRange.end.row - 1
foldedRows.push(endRow + 1)
else
startRow = row
endRow = row
if endRow + 1 is lastRow
endPosition = [endRow, @buffer.lineLengthForRow(endRow)]
else
endPosition = [endRow + 1]
lines = @buffer.getTextInRange([[startRow], endPosition])
@buffer.deleteRows(startRow, endRow)
insertPosition = Point.min([startRow + 1], @buffer.getEofPosition())
if insertPosition.row is @buffer.getLastRow() and insertPosition.column > 0
lines = "\n#{lines}"
@buffer.insert(insertPosition, lines)
@foldBufferRow(foldedRow) for foldedRow in foldedRows
newStartPosition = [selection.start.row + 1, selection.start.column]
newEndPosition = [selection.end.row + 1, selection.end.column]
@setSelectedBufferRange([newStartPosition, newEndPosition], preserveFolds: true)
mutateSelectedText: (fn) ->
@transact => fn(selection) for selection in @getSelections()

View File

@@ -19,6 +19,7 @@ class Editor extends View
autosave: false
autoIndent: true
autoIndentOnPaste: false
nonWordCharacters: "./\\()\"-_:,.;<>~!@#$%^&*|+=[]{}`~?"
@content: (params) ->
@div class: @classes(params), tabindex: -1, =>
@@ -183,6 +184,8 @@ class Editor extends View
'editor:close-all-edit-sessions': @destroyAllEditSessions
'editor:select-grammar': @selectGrammar
'editor:copy-path': @copyPathToPasteboard
'editor:move-line-up': @moveLineUp
'editor:move-line-down': @moveLineDown
documentation = {}
for name, method of editorBindings
@@ -204,6 +207,8 @@ class Editor extends View
moveCursorToBeginningOfLine: -> @activeEditSession.moveCursorToBeginningOfLine()
moveCursorToFirstCharacterOfLine: -> @activeEditSession.moveCursorToFirstCharacterOfLine()
moveCursorToEndOfLine: -> @activeEditSession.moveCursorToEndOfLine()
moveLineUp: -> @activeEditSession.moveLineUp()
moveLineDown: -> @activeEditSession.moveLineDown()
setCursorScreenPosition: (position, options) -> @activeEditSession.setCursorScreenPosition(position, options)
getCursorScreenPosition: -> @activeEditSession.getCursorScreenPosition()
getCursorScreenRow: -> @activeEditSession.getCursorScreenRow()
@@ -271,6 +276,7 @@ class Editor extends View
destroyFold: (foldId) -> @activeEditSession.destroyFold(foldId)
destroyFoldsContainingBufferRow: (bufferRow) -> @activeEditSession.destroyFoldsContainingBufferRow(bufferRow)
isFoldedAtScreenRow: (screenRow) -> @activeEditSession.isFoldedAtScreenRow(screenRow)
isFoldedAtBufferRow: (bufferRow) -> @activeEditSession.isFoldedAtBufferRow(bufferRow)
lineForScreenRow: (screenRow) -> @activeEditSession.lineForScreenRow(screenRow)
linesForScreenRows: (start, end) -> @activeEditSession.linesForScreenRows(start, end)
@@ -312,7 +318,7 @@ class Editor extends View
setInvisibles: (@invisibles={}) ->
_.defaults @invisibles,
eol: '\u00ac'
space: '\u2022'
space: '\u00b7'
tab: '\u00bb'
cr: '\u00a4'
@resetDisplay()
@@ -1056,8 +1062,6 @@ class Editor extends View
if fold = screenLine.fold
lineAttributes = { class: 'fold line', 'fold-id': fold.id }
if @activeEditSession.selectionIntersectsBufferRange(fold.getBufferRange())
lineAttributes.class += ' selected'
else
lineAttributes = { class: 'line' }
@@ -1090,6 +1094,8 @@ class Editor extends View
if invisibles.eol
line.push("<span class='invisible'>#{invisibles.eol}</span>")
line.push("<span class='fold-marker'/>") if fold
line.push('</pre>')
line.join('')

View File

@@ -36,8 +36,8 @@ class Fold
@displayBuffer.unregisterFold(@startRow, this)
return
@updateStartRow(event)
@updateEndRow(event)
@startRow += @getRowDelta(event, @startRow)
@endRow += @getRowDelta(event, @endRow)
if @startRow != oldStartRow
@displayBuffer.unregisterFold(oldStartRow, this)
@@ -49,26 +49,12 @@ class Fold
isContainedByFold: (fold) ->
@isContainedByRange(fold.getBufferRange())
updateStartRow: (event) ->
getRowDelta: (event, row) ->
{ newRange, oldRange } = event
if oldRange.end.row < @startRow
delta = newRange.end.row - oldRange.end.row
else if newRange.end.row < @startRow
delta = newRange.end.row - @startRow
if oldRange.end.row <= row
newRange.end.row - oldRange.end.row
else if newRange.end.row < row
newRange.end.row - row
else
delta = 0
@startRow += delta
updateEndRow: (event) ->
{ newRange, oldRange } = event
if oldRange.end.row <= @endRow
delta = newRange.end.row - oldRange.end.row
else if newRange.end.row <= @endRow
delta = newRange.end.row - @endRow
else
delta = 0
@endRow += delta
0

View File

@@ -58,16 +58,19 @@ class Gutter extends View
@renderLineNumbers(renderFrom, renderTo) if performUpdate
renderLineNumbers: (startScreenRow, endScreenRow) ->
rows = @editor().bufferRowsForScreenRows(startScreenRow, endScreenRow)
editor = @editor()
rows = editor.bufferRowsForScreenRows(startScreenRow, endScreenRow)
cursorScreenRow = @editor().getCursorScreenPosition().row
cursorScreenRow = editor.getCursorScreenPosition().row
@lineNumbers[0].innerHTML = $$$ ->
for row in rows
if row == lastScreenRow
rowValue = ''
else
rowValue = row + 1
@div {class: 'line-number'}, rowValue
classes = ['line-number']
classes.push('fold') if editor.isFoldedAtBufferRow(row)
@div rowValue, class: classes.join(' ')
lastScreenRow = row
@calculateWidth()

View File

@@ -38,3 +38,5 @@
'meta-P': 'editor:close-all-edit-sessions'
'meta-L': 'editor:select-grammar'
'ctrl-C': 'editor:copy-path'
'ctrl-meta-up': 'editor:move-line-up'
'ctrl-meta-down': 'editor:move-line-down'

View File

@@ -133,6 +133,7 @@ class LineMap
new Range(start, end)
bufferRangeForScreenRange: (screenRange) ->
screenRange = Range.fromObject(screenRange)
start = @bufferPositionForScreenPosition(screenRange.start)
end = @bufferPositionForScreenPosition(screenRange.end)
new Range(start, end)
@@ -141,4 +142,3 @@ class LineMap
for row in [start..end]
line = @lineForScreenRow(row).text
console.log row, line, line.length

View File

@@ -11,6 +11,14 @@ class Point
new Point(row, column)
@min: (point1, point2) ->
point1 = @fromObject(point1)
point2 = @fromObject(point2)
if point1.isLessThanOrEqual(point2)
point1
else
point2
constructor: (@row=0, @column=0) ->
copy: ->

View File

@@ -97,7 +97,10 @@ class Selection
@screenRangeChanged()
selectWord: ->
@setBufferRange(@cursor.getCurrentWordBufferRange())
options = {}
options.wordRegex = /[\t ]*/ if @cursor.isSurroundedByWhitespace()
@setBufferRange(@cursor.getCurrentWordBufferRange(options))
@wordwise = true
@initialScreenRange = @getScreenRange()