{Range} = require 'atom' AllWhitespace = /^\s$/ WholeWordRegex = /\S+/ {mergeRanges} = require './utils' class TextObject constructor: (@editor, @state) -> isComplete: -> true isRecordable: -> false execute: -> @select.apply(this, arguments) class SelectInsideWord extends TextObject select: -> for selection in @editor.getSelections() if selection.isEmpty() selection.selectRight() selection.expandOverWord() [true] class SelectInsideWholeWord extends TextObject select: -> for selection in @editor.getSelections() range = selection.cursor.getCurrentWordBufferRange({wordRegex: WholeWordRegex}) selection.setBufferRange(mergeRanges(selection.getBufferRange(), range)) true # SelectInsideQuotes and the next class defined (SelectInsideBrackets) are # almost-but-not-quite-repeated code. They are different because of the depth # checks in the bracket matcher. class SelectInsideQuotes extends TextObject constructor: (@editor, @char, @includeQuotes) -> findOpeningQuote: (pos) -> start = pos.copy() pos = pos.copy() while pos.row >= 0 line = @editor.lineTextForBufferRow(pos.row) pos.column = line.length - 1 if pos.column is -1 while pos.column >= 0 if line[pos.column] is @char if pos.column is 0 or line[pos.column - 1] isnt '\\' if @isStartQuote(pos) return pos else return @lookForwardOnLine(start) -- pos.column pos.column = -1 -- pos.row @lookForwardOnLine(start) isStartQuote: (end) -> line = @editor.lineTextForBufferRow(end.row) numQuotes = line.substring(0, end.column + 1).replace( "'#{@char}", '').split(@char).length - 1 numQuotes % 2 lookForwardOnLine: (pos) -> line = @editor.lineTextForBufferRow(pos.row) index = line.substring(pos.column).indexOf(@char) if index >= 0 pos.column += index return pos null findClosingQuote: (start) -> end = start.copy() escaping = false while end.row < @editor.getLineCount() endLine = @editor.lineTextForBufferRow(end.row) while end.column < endLine.length if endLine[end.column] is '\\' ++ end.column else if endLine[end.column] is @char -- start.column if @includeQuotes ++ end.column if @includeQuotes return end ++ end.column end.column = 0 ++ end.row return select: -> for selection in @editor.getSelections() start = @findOpeningQuote(selection.cursor.getBufferPosition()) if start? ++ start.column # skip the opening quote end = @findClosingQuote(start) if end? selection.setBufferRange(mergeRanges(selection.getBufferRange(), [start, end])) not selection.isEmpty() # SelectInsideBrackets and the previous class defined (SelectInsideQuotes) are # almost-but-not-quite-repeated code. They are different because of the depth # checks in the bracket matcher. class SelectInsideBrackets extends TextObject constructor: (@editor, @beginChar, @endChar, @includeBrackets) -> findOpeningBracket: (pos) -> pos = pos.copy() depth = 0 while pos.row >= 0 line = @editor.lineTextForBufferRow(pos.row) pos.column = line.length - 1 if pos.column is -1 while pos.column >= 0 switch line[pos.column] when @endChar then ++ depth when @beginChar return pos if -- depth < 0 -- pos.column pos.column = -1 -- pos.row findClosingBracket: (start) -> end = start.copy() depth = 0 while end.row < @editor.getLineCount() endLine = @editor.lineTextForBufferRow(end.row) while end.column < endLine.length switch endLine[end.column] when @beginChar then ++ depth when @endChar if -- depth < 0 -- start.column if @includeBrackets ++ end.column if @includeBrackets return end ++ end.column end.column = 0 ++ end.row return select: -> for selection in @editor.getSelections() start = @findOpeningBracket(selection.cursor.getBufferPosition()) if start? ++ start.column # skip the opening quote end = @findClosingBracket(start) if end? selection.setBufferRange(mergeRanges(selection.getBufferRange(), [start, end])) not selection.isEmpty() class SelectAWord extends TextObject select: -> for selection in @editor.getSelections() if selection.isEmpty() selection.selectRight() selection.expandOverWord() loop endPoint = selection.getBufferRange().end char = @editor.getTextInRange(Range.fromPointWithDelta(endPoint, 0, 1)) break unless AllWhitespace.test(char) selection.selectRight() true class SelectAWholeWord extends TextObject select: -> for selection in @editor.getSelections() range = selection.cursor.getCurrentWordBufferRange({wordRegex: WholeWordRegex}) selection.setBufferRange(mergeRanges(selection.getBufferRange(), range)) loop endPoint = selection.getBufferRange().end char = @editor.getTextInRange(Range.fromPointWithDelta(endPoint, 0, 1)) break unless AllWhitespace.test(char) selection.selectRight() true class Paragraph extends TextObject select: -> for selection in @editor.getSelections() @selectParagraph(selection) # Return a range delimted by the start or the end of a paragraph paragraphDelimitedRange: (startPoint) -> inParagraph = @isParagraphLine(@editor.lineTextForBufferRow(startPoint.row)) upperRow = @searchLines(startPoint.row, -1, inParagraph) lowerRow = @searchLines(startPoint.row, @editor.getLineCount(), inParagraph) new Range([upperRow + 1, 0], [lowerRow, 0]) searchLines: (startRow, rowLimit, startedInParagraph) -> for currentRow in [startRow..rowLimit] line = @editor.lineTextForBufferRow(currentRow) if startedInParagraph isnt @isParagraphLine(line) return currentRow rowLimit isParagraphLine: (line) -> (/\S/.test(line)) class SelectInsideParagraph extends Paragraph selectParagraph: (selection) -> oldRange = selection.getBufferRange() startPoint = selection.cursor.getBufferPosition() newRange = @paragraphDelimitedRange(startPoint) selection.setBufferRange(mergeRanges(oldRange, newRange)) true class SelectAParagraph extends Paragraph selectParagraph: (selection) -> oldRange = selection.getBufferRange() startPoint = selection.cursor.getBufferPosition() newRange = @paragraphDelimitedRange(startPoint) nextRange = @paragraphDelimitedRange(newRange.end) selection.setBufferRange(mergeRanges(oldRange, [newRange.start, nextRange.end])) true module.exports = {TextObject, SelectInsideWord, SelectInsideWholeWord, SelectInsideQuotes, SelectInsideBrackets, SelectAWord, SelectAWholeWord, SelectInsideParagraph, SelectAParagraph}