/* jslint node: true */ 'use strict'; var View = require('./view.js').View; var miscUtil = require('./misc_util.js'); var strUtil = require('./string_util.js'); var ansi = require('./ansi_term.js'); //var TextBuffer = require('./text_buffer.js').TextBuffer; var assert = require('assert'); var _ = require('lodash'); exports.MultiLineEditTextView2 = MultiLineEditTextView2; function MultiLineEditTextView2(options) { if(!_.isBoolean(options.acceptsFocus)) { options.acceptsFocus = true; } if(!_.isBoolean(this.acceptsInput)) { options.acceptsInput = true; } View.call(this, options); var self = this; // // ANSI seems to want tabs to default to 8 characters. See the following: // * http://www.ansi-bbs.org/ansi-bbs2/control_chars/ // * http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/bansi.txt // this.tabWidth = _.isNumber(options.tabWidth) ? options.tabWidth : 8; this.textLines = []; this.topVisibleIndex = 0; this.redrawVisibleArea = function() { assert(self.topVisibleIndex < self.textLines.length); self.client.term.write(self.getSGR()); var bottomIndex = Math.min(self.topVisibleIndex + self.dimens.height, self.textLines.length); var row = self.position.row; for(var i = self.topVisibleIndex; i < bottomIndex; i++) { self.client.term.write(ansi.goto(row, this.position.col)); self.client.term.write(self.getRenderText(self.textLines[i].text)); ++row; } }; this.getRenderText = function(text) { text = text.replace(/\t/g, ' ') var remain = self.dimens.width - text.length; if(remain > 0) { text += new Array(remain).join(' '); } return text; } this.expandTab = function(col, expandChar) { expandChar = expandChar || ' '; var count = self.tabWidth - (col % self.tabWidth); return new Array(count).join(expandChar); }; this.wordWrapSingleLine = function(s, width) { // // Notes // * Sublime Text 3 for example considers spaces after a word // part of said word. For example, "word " would be wraped // in it's entirity. // // * Tabs in Sublime Text 3 are also treated as a word, so, e.g. // "\t" may resolve to " " and must fit within the space. // // note: we cannot simply use \s below as it includes \t var re = new RegExp( '\t|[ \f\n\r\v​\u00a0\u1680​\u180e\u2000​\u2001\u2002​\u2003\u2004\u2005\u2006​\u2007\u2008​\u2009\u200a​\u2028\u2029​\u202f\u205f​\u3000]+', 'g'); var m; var wordStart; var wrapped = [ '' ]; var i = 0; var word; function addWord() { if(wrapped[i].length + word.length > self.dimens.width) { wrapped[++i] = word; } else { wrapped[i] += word; } } do { wordStart = re.lastIndex + (_.isObject(m) ? m[0].length - 1 : 0); m = re.exec(s); if(null !== m) { word = s.substring(wordStart, re.lastIndex - 1); switch(m[0].charAt(0)) { case ' ' : word += m[0]; break; case '\t' : // // Expand tab given position // word += self.expandTab(wrapped[i].length, '\t'); break; } addWord(); } } while(0 !== re.lastIndex); // // Remainder // word = s.substring(wordStart); addWord(); return wrapped; }; this.insertText = function(text, row, col) { // // Perform the following on |text|: // * Normalize various line feed formats -> \n // * Remove some control characters (e.g. \b) // * Word wrap lines such that they fit in the visible workspace. // Each actual line will then take 1:n elements in textLines[]. // * Each tab will be appropriately expanded and take 1:n \t // characters. This allows us to know when we're in tab space // when doing cursor movement/etc. // // // Try to handle any possible newline that can be fed to us. // See http://stackoverflow.com/questions/5034781/js-regex-to-split-by-line // // :TODO: support row/col insertion point if(_.isNumber(row)) { if(_.isNumber(col)) { // // Modify text to have information from row // before and and after column // // :TODO: Need to clean this string (e.g. collapse tabs) text = self.textLines // :TODO: Remove original line @ row } } else { row = self.textLines.length; } var tempLines = text .replace(/\b/g, '') .split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g); var wrapped; for(var i = 0; i < tempLines.length; ++i) { wrapped = self.wordWrapSingleLine(tempLines[i], self.dimens.width); for(var j = 0; j < wrapped.length - 1; ++j) { self.textLines.splice(row++, 0, { text : wrapped[j] } ); } self.textLines.splice(row++, 0, { text : wrapped[wrapped.length - 1], eol : true }); } }; this.cursorUp = function() { console.log('up') }; } require('util').inherits(MultiLineEditTextView2, View); MultiLineEditTextView2.prototype.redraw = function() { MultiLineEditTextView2.super_.prototype.redraw.call(this); this.redrawVisibleArea(); }; MultiLineEditTextView2.prototype.setText = function(text) { this.textLines = []; //text = "You. Now \ttomorrow \tthere'll \tbe \ttwo \tsessions, \tof\t course, morning and afternoon."; this.insertText(text);//, 0, 0); console.log(this.textLines) }; MultiLineEditTextView2.prototype.onSpecialKeyPress = function(keyName) { var self = this; [ 'up', 'down', 'left', 'right' ].forEach(function key(arrowKey) { if(self.isSpecialKeyMapped(arrowKey, keyName)) { self['cursor' + arrowKey.substring(0,1).toUpperCase() + arrowKey.substring(1)](); } }); MultiLineEditTextView2.super_.prototype.onSpecialKeyPress.call(this, keyName); }