Token = require './Token' TokenTypes = require './TokenTypes' NON_TEXT_OBJECT = new Token('none', -1, '') module.exports = \ ## # Cursor for navigating and manipulating text class Cursor ## # Create a new cursor over a text # # @param {String} text Text to traverse # @param {String} [pos=0] - foo constructor: (@text, @pos) -> @pos or= 0 @_calculate_positions() _calculate_offsets : -> @offsets = {} @offsets.char = @pos for tobjType of TokenTypes continue if tobjType is 'char' for idx, candidate of @positions[tobjType] if @pos >= candidate.index and @pos < candidate.index + candidate.length @offsets[tobjType] = parseInt(idx) _calculate_positions : -> @positions = {} for tobjType, re of TokenTypes @positions[tobjType] = [] while (group = re.exec(@text)) != null @positions[tobjType].push new Token(tobjType, group.index, group[0]) @_calculate_offsets() return @ ## # Delete a text object del: (tobj) -> @text = @text.substring(0, tobj.index) + @text.substring(tobj.index + tobj.length) @_calculate_positions() return @ ## # Insert a text object ins: (tobj) -> @text = @text.substring(0, tobj.index) + tobj.text + @text.substring(tobj.index) @_calculate_positions() return @ ## # Replace a text object sub: (oldObj) -> with : (newObj) => @del(oldObj) @ins(newObj, oldObj.index) return @ ## # Move the current position relative to the current position. # @param {Number} amount Number of units to move (can be negative) # @param {String} [tobjType='char'] Text object type # @see TokenTypes move : (amount, tobjType) -> @moveTo(@pos + amount, tobjType) ## # Move the current position to an absolute position # @param {Number} pos Position to move to # @param {String} [tobjType='char'] Unit to measure movements moveTo : (pos, tobjType) -> tobjType or= 'char' if tobjType isnt 'char' then throw new Error("'moveTo' only implemented for 'char' at the moment.") # console.log "Moving to #{tobjType}##{pos}" @pos = pos @_calculate_offsets() ## # Get the text object at the current cursor position # @param {String} [tobjType='char'] The text object type to get cur: (tobjType) -> tobjType or= 'char' @positions[tobjType][@offsets[tobjType]] or null _cur_plus_offset: (tobjType, offset) -> # if not currently in a e.g. word, number, sentence ... curOffset = @offsets[tobjType] if curOffset? curOffset += offset else for idx, candidate of @positions[tobjType] if (offset > 0 and @pos >= candidate.index) or (offset < 0 and @pos < candidate.index + candidate.length) curOffset = candidate.index curOffset += offset @positions[tobjType][curOffset] next: (amount, tobjType) -> if amount and amount of TokenTypes then [amount, tobjType] = [1, amount] @_cur_plus_offset(tobjType, Math.abs amount) # prev: (amount, tobjType) -> # if amount and amount of TokenTypes then [amount, tobjType] = [1, amount] # @_cur_plus_offset(tobjType, -1 * Math.abs amount) ## # Whether the cursor is at the end of the text. # @return {Boolean} whether the cursor is at the end of the text. atEOF : () -> return @pos == @text.length ## # whether the cursor is at the start of the text. # @return {Boolean} whether the cursor is at the start of the text. atSOF : () -> return @pos == 0