| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696 |
- /* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Ajax.org B.V. nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
- define(function(require, exports, module) {
- "use strict";
- require("./lib/fixoldbrowsers");
- var oop = require("./lib/oop");
- var dom = require("./lib/dom");
- var lang = require("./lib/lang");
- var useragent = require("./lib/useragent");
- var TextInput = require("./keyboard/textinput").TextInput;
- var MouseHandler = require("./mouse/mouse_handler").MouseHandler;
- var FoldHandler = require("./mouse/fold_handler").FoldHandler;
- var KeyBinding = require("./keyboard/keybinding").KeyBinding;
- var EditSession = require("./edit_session").EditSession;
- var Search = require("./search").Search;
- var Range = require("./range").Range;
- var EventEmitter = require("./lib/event_emitter").EventEmitter;
- var CommandManager = require("./commands/command_manager").CommandManager;
- var defaultCommands = require("./commands/default_commands").commands;
- var config = require("./config");
- var TokenIterator = require("./token_iterator").TokenIterator;
- /**
- * The main entry point into the Ace functionality.
- *
- * The `Editor` manages the [[EditSession]] (which manages [[Document]]s), as well as the [[VirtualRenderer]], which draws everything to the screen.
- *
- * Event sessions dealing with the mouse and keyboard are bubbled up from `Document` to the `Editor`, which decides what to do with them.
- * @class Editor
- **/
- /**
- * Creates a new `Editor` object.
- *
- * @param {VirtualRenderer} renderer Associated `VirtualRenderer` that draws everything
- * @param {EditSession} session The `EditSession` to refer to
- *
- *
- * @constructor
- **/
- var Editor = function(renderer, session) {
- var container = renderer.getContainerElement();
- this.container = container;
- this.renderer = renderer;
- this.commands = new CommandManager(useragent.isMac ? "mac" : "win", defaultCommands);
- this.textInput = new TextInput(renderer.getTextAreaContainer(), this);
- this.renderer.textarea = this.textInput.getElement();
- this.keyBinding = new KeyBinding(this);
- // TODO detect touch event support
- this.$mouseHandler = new MouseHandler(this);
- new FoldHandler(this);
- this.$blockScrolling = 0;
- this.$search = new Search().set({
- wrap: true
- });
- this.$historyTracker = this.$historyTracker.bind(this);
- this.commands.on("exec", this.$historyTracker);
- this.$initOperationListeners();
-
- this._$emitInputEvent = lang.delayedCall(function() {
- this._signal("input", {});
- if (this.session && this.session.bgTokenizer)
- this.session.bgTokenizer.scheduleStart();
- }.bind(this));
-
- this.on("change", function(_, _self) {
- _self._$emitInputEvent.schedule(31);
- });
- this.setSession(session || new EditSession(""));
- config.resetOptions(this);
- config._signal("editor", this);
- };
- (function(){
- oop.implement(this, EventEmitter);
- this.$initOperationListeners = function() {
- function last(a) {return a[a.length - 1]}
- this.selections = [];
- this.commands.on("exec", this.startOperation.bind(this), true);
- this.commands.on("afterExec", this.endOperation.bind(this), true);
- this.$opResetTimer = lang.delayedCall(this.endOperation.bind(this));
- this.on("change", function() {
- this.curOp || this.startOperation();
- this.curOp.docChanged = true;
- }.bind(this), true);
- this.on("changeSelection", function() {
- this.curOp || this.startOperation();
- this.curOp.selectionChanged = true;
- }.bind(this), true);
- };
- this.curOp = null;
- this.prevOp = {};
- this.startOperation = function(commadEvent) {
- if (this.curOp) {
- if (!commadEvent || this.curOp.command)
- return;
- this.prevOp = this.curOp;
- }
- if (!commadEvent) {
- this.previousCommand = null;
- commadEvent = {};
- }
- this.$opResetTimer.schedule();
- this.curOp = {
- command: commadEvent.command || {},
- args: commadEvent.args,
- scrollTop: this.renderer.scrollTop
- };
- if (this.curOp.command.name && this.curOp.command.scrollIntoView !== undefined)
- this.$blockScrolling++;
- };
- this.endOperation = function(e) {
- if (this.curOp) {
- if (e && e.returnValue === false)
- return this.curOp = null;
- this._signal("beforeEndOperation");
- var command = this.curOp.command;
- if (command.name && this.$blockScrolling > 0)
- this.$blockScrolling--;
- var scrollIntoView = command && command.scrollIntoView;
- if (scrollIntoView) {
- switch (scrollIntoView) {
- case "center-animate":
- scrollIntoView = "animate";
- /* fall through */
- case "center":
- this.renderer.scrollCursorIntoView(null, 0.5);
- break;
- case "animate":
- case "cursor":
- this.renderer.scrollCursorIntoView();
- break;
- case "selectionPart":
- var range = this.selection.getRange();
- var config = this.renderer.layerConfig;
- if (range.start.row >= config.lastRow || range.end.row <= config.firstRow) {
- this.renderer.scrollSelectionIntoView(this.selection.anchor, this.selection.lead);
- }
- break;
- default:
- break;
- }
- if (scrollIntoView == "animate")
- this.renderer.animateScrolling(this.curOp.scrollTop);
- }
-
- this.prevOp = this.curOp;
- this.curOp = null;
- }
- };
- // TODO use property on commands instead of this
- this.$mergeableCommands = ["backspace", "del", "insertstring"];
- this.$historyTracker = function(e) {
- if (!this.$mergeUndoDeltas)
- return;
- var prev = this.prevOp;
- var mergeableCommands = this.$mergeableCommands;
- // previous command was the same
- var shouldMerge = prev.command && (e.command.name == prev.command.name);
- if (e.command.name == "insertstring") {
- var text = e.args;
- if (this.mergeNextCommand === undefined)
- this.mergeNextCommand = true;
- shouldMerge = shouldMerge
- && this.mergeNextCommand // previous command allows to coalesce with
- && (!/\s/.test(text) || /\s/.test(prev.args)); // previous insertion was of same type
- this.mergeNextCommand = true;
- } else {
- shouldMerge = shouldMerge
- && mergeableCommands.indexOf(e.command.name) !== -1; // the command is mergeable
- }
- if (
- this.$mergeUndoDeltas != "always"
- && Date.now() - this.sequenceStartTime > 2000
- ) {
- shouldMerge = false; // the sequence is too long
- }
- if (shouldMerge)
- this.session.mergeUndoDeltas = true;
- else if (mergeableCommands.indexOf(e.command.name) !== -1)
- this.sequenceStartTime = Date.now();
- };
- /**
- * Sets a new key handler, such as "vim" or "windows".
- * @param {String} keyboardHandler The new key handler
- *
- **/
- this.setKeyboardHandler = function(keyboardHandler, cb) {
- if (keyboardHandler && typeof keyboardHandler === "string") {
- this.$keybindingId = keyboardHandler;
- var _self = this;
- config.loadModule(["keybinding", keyboardHandler], function(module) {
- if (_self.$keybindingId == keyboardHandler)
- _self.keyBinding.setKeyboardHandler(module && module.handler);
- cb && cb();
- });
- } else {
- this.$keybindingId = null;
- this.keyBinding.setKeyboardHandler(keyboardHandler);
- cb && cb();
- }
- };
- /**
- * Returns the keyboard handler, such as "vim" or "windows".
- *
- * @returns {String}
- *
- **/
- this.getKeyboardHandler = function() {
- return this.keyBinding.getKeyboardHandler();
- };
- /**
- * Emitted whenever the [[EditSession]] changes.
- * @event changeSession
- * @param {Object} e An object with two properties, `oldSession` and `session`, that represent the old and new [[EditSession]]s.
- *
- **/
- /**
- * Sets a new editsession to use. This method also emits the `'changeSession'` event.
- * @param {EditSession} session The new session to use
- *
- **/
- this.setSession = function(session) {
- if (this.session == session)
- return;
-
- // make sure operationEnd events are not emitted to wrong session
- if (this.curOp) this.endOperation();
- this.curOp = {};
- var oldSession = this.session;
- if (oldSession) {
- this.session.off("change", this.$onDocumentChange);
- this.session.off("changeMode", this.$onChangeMode);
- this.session.off("tokenizerUpdate", this.$onTokenizerUpdate);
- this.session.off("changeTabSize", this.$onChangeTabSize);
- this.session.off("changeWrapLimit", this.$onChangeWrapLimit);
- this.session.off("changeWrapMode", this.$onChangeWrapMode);
- this.session.off("changeFold", this.$onChangeFold);
- this.session.off("changeFrontMarker", this.$onChangeFrontMarker);
- this.session.off("changeBackMarker", this.$onChangeBackMarker);
- this.session.off("changeBreakpoint", this.$onChangeBreakpoint);
- this.session.off("changeAnnotation", this.$onChangeAnnotation);
- this.session.off("changeOverwrite", this.$onCursorChange);
- this.session.off("changeScrollTop", this.$onScrollTopChange);
- this.session.off("changeScrollLeft", this.$onScrollLeftChange);
- var selection = this.session.getSelection();
- selection.off("changeCursor", this.$onCursorChange);
- selection.off("changeSelection", this.$onSelectionChange);
- }
- this.session = session;
- if (session) {
- this.$onDocumentChange = this.onDocumentChange.bind(this);
- session.on("change", this.$onDocumentChange);
- this.renderer.setSession(session);
-
- this.$onChangeMode = this.onChangeMode.bind(this);
- session.on("changeMode", this.$onChangeMode);
-
- this.$onTokenizerUpdate = this.onTokenizerUpdate.bind(this);
- session.on("tokenizerUpdate", this.$onTokenizerUpdate);
-
- this.$onChangeTabSize = this.renderer.onChangeTabSize.bind(this.renderer);
- session.on("changeTabSize", this.$onChangeTabSize);
-
- this.$onChangeWrapLimit = this.onChangeWrapLimit.bind(this);
- session.on("changeWrapLimit", this.$onChangeWrapLimit);
-
- this.$onChangeWrapMode = this.onChangeWrapMode.bind(this);
- session.on("changeWrapMode", this.$onChangeWrapMode);
-
- this.$onChangeFold = this.onChangeFold.bind(this);
- session.on("changeFold", this.$onChangeFold);
-
- this.$onChangeFrontMarker = this.onChangeFrontMarker.bind(this);
- this.session.on("changeFrontMarker", this.$onChangeFrontMarker);
-
- this.$onChangeBackMarker = this.onChangeBackMarker.bind(this);
- this.session.on("changeBackMarker", this.$onChangeBackMarker);
-
- this.$onChangeBreakpoint = this.onChangeBreakpoint.bind(this);
- this.session.on("changeBreakpoint", this.$onChangeBreakpoint);
-
- this.$onChangeAnnotation = this.onChangeAnnotation.bind(this);
- this.session.on("changeAnnotation", this.$onChangeAnnotation);
-
- this.$onCursorChange = this.onCursorChange.bind(this);
- this.session.on("changeOverwrite", this.$onCursorChange);
-
- this.$onScrollTopChange = this.onScrollTopChange.bind(this);
- this.session.on("changeScrollTop", this.$onScrollTopChange);
-
- this.$onScrollLeftChange = this.onScrollLeftChange.bind(this);
- this.session.on("changeScrollLeft", this.$onScrollLeftChange);
-
- this.selection = session.getSelection();
- this.selection.on("changeCursor", this.$onCursorChange);
-
- this.$onSelectionChange = this.onSelectionChange.bind(this);
- this.selection.on("changeSelection", this.$onSelectionChange);
-
- this.onChangeMode();
-
- this.$blockScrolling += 1;
- this.onCursorChange();
- this.$blockScrolling -= 1;
-
- this.onScrollTopChange();
- this.onScrollLeftChange();
- this.onSelectionChange();
- this.onChangeFrontMarker();
- this.onChangeBackMarker();
- this.onChangeBreakpoint();
- this.onChangeAnnotation();
- this.session.getUseWrapMode() && this.renderer.adjustWrapLimit();
- this.renderer.updateFull();
- } else {
- this.selection = null;
- this.renderer.setSession(session);
- }
- this._signal("changeSession", {
- session: session,
- oldSession: oldSession
- });
-
- this.curOp = null;
-
- oldSession && oldSession._signal("changeEditor", {oldEditor: this});
- session && session._signal("changeEditor", {editor: this});
- };
- /**
- * Returns the current session being used.
- * @returns {EditSession}
- **/
- this.getSession = function() {
- return this.session;
- };
- /**
- * Sets the current document to `val`.
- * @param {String} val The new value to set for the document
- * @param {Number} cursorPos Where to set the new value. `undefined` or 0 is selectAll, -1 is at the document start, and 1 is at the end
- *
- * @returns {String} The current document value
- * @related Document.setValue
- **/
- this.setValue = function(val, cursorPos) {
- this.session.doc.setValue(val);
- if (!cursorPos)
- this.selectAll();
- else if (cursorPos == 1)
- this.navigateFileEnd();
- else if (cursorPos == -1)
- this.navigateFileStart();
- return val;
- };
- /**
- * Returns the current session's content.
- *
- * @returns {String}
- * @related EditSession.getValue
- **/
- this.getValue = function() {
- return this.session.getValue();
- };
- /**
- *
- * Returns the currently highlighted selection.
- * @returns {Selection} The selection object
- **/
- this.getSelection = function() {
- return this.selection;
- };
- /**
- * {:VirtualRenderer.onResize}
- * @param {Boolean} force If `true`, recomputes the size, even if the height and width haven't changed
- *
- *
- * @related VirtualRenderer.onResize
- **/
- this.resize = function(force) {
- this.renderer.onResize(force);
- };
- /**
- * {:VirtualRenderer.setTheme}
- * @param {String} theme The path to a theme
- * @param {Function} cb optional callback called when theme is loaded
- **/
- this.setTheme = function(theme, cb) {
- this.renderer.setTheme(theme, cb);
- };
- /**
- * {:VirtualRenderer.getTheme}
- *
- * @returns {String} The set theme
- * @related VirtualRenderer.getTheme
- **/
- this.getTheme = function() {
- return this.renderer.getTheme();
- };
- /**
- * {:VirtualRenderer.setStyle}
- * @param {String} style A class name
- *
- *
- * @related VirtualRenderer.setStyle
- **/
- this.setStyle = function(style) {
- this.renderer.setStyle(style);
- };
- /**
- * {:VirtualRenderer.unsetStyle}
- * @related VirtualRenderer.unsetStyle
- **/
- this.unsetStyle = function(style) {
- this.renderer.unsetStyle(style);
- };
- /**
- * Gets the current font size of the editor text.
- */
- this.getFontSize = function () {
- return this.getOption("fontSize") ||
- dom.computedStyle(this.container, "fontSize");
- };
- /**
- * Set a new font size (in pixels) for the editor text.
- * @param {String} size A font size ( _e.g._ "12px")
- *
- *
- **/
- this.setFontSize = function(size) {
- this.setOption("fontSize", size);
- };
- this.$highlightBrackets = function() {
- if (this.session.$bracketHighlight) {
- this.session.removeMarker(this.session.$bracketHighlight);
- this.session.$bracketHighlight = null;
- }
- if (this.$highlightPending) {
- return;
- }
- // perform highlight async to not block the browser during navigation
- var self = this;
- this.$highlightPending = true;
- setTimeout(function() {
- self.$highlightPending = false;
- var session = self.session;
- if (!session || !session.bgTokenizer) return;
- var pos = session.findMatchingBracket(self.getCursorPosition());
- if (pos) {
- var range = new Range(pos.row, pos.column, pos.row, pos.column + 1);
- } else if (session.$mode.getMatching) {
- var range = session.$mode.getMatching(self.session);
- }
- if (range)
- session.$bracketHighlight = session.addMarker(range, "ace_bracket", "text");
- }, 50);
- };
- // todo: move to mode.getMatching
- this.$highlightTags = function() {
- if (this.$highlightTagPending)
- return;
- // perform highlight async to not block the browser during navigation
- var self = this;
- this.$highlightTagPending = true;
- setTimeout(function() {
- self.$highlightTagPending = false;
-
- var session = self.session;
- if (!session || !session.bgTokenizer) return;
-
- var pos = self.getCursorPosition();
- var iterator = new TokenIterator(self.session, pos.row, pos.column);
- var token = iterator.getCurrentToken();
-
- if (!token || !/\b(?:tag-open|tag-name)/.test(token.type)) {
- session.removeMarker(session.$tagHighlight);
- session.$tagHighlight = null;
- return;
- }
-
- if (token.type.indexOf("tag-open") != -1) {
- token = iterator.stepForward();
- if (!token)
- return;
- }
-
- var tag = token.value;
- var depth = 0;
- var prevToken = iterator.stepBackward();
-
- if (prevToken.value == '<'){
- //find closing tag
- do {
- prevToken = token;
- token = iterator.stepForward();
-
- if (token && token.value === tag && token.type.indexOf('tag-name') !== -1) {
- if (prevToken.value === '<'){
- depth++;
- } else if (prevToken.value === '</'){
- depth--;
- }
- }
-
- } while (token && depth >= 0);
- } else {
- //find opening tag
- do {
- token = prevToken;
- prevToken = iterator.stepBackward();
-
- if (token && token.value === tag && token.type.indexOf('tag-name') !== -1) {
- if (prevToken.value === '<') {
- depth++;
- } else if (prevToken.value === '</') {
- depth--;
- }
- }
- } while (prevToken && depth <= 0);
-
- //select tag again
- iterator.stepForward();
- }
-
- if (!token) {
- session.removeMarker(session.$tagHighlight);
- session.$tagHighlight = null;
- return;
- }
-
- var row = iterator.getCurrentTokenRow();
- var column = iterator.getCurrentTokenColumn();
- var range = new Range(row, column, row, column+token.value.length);
-
- //remove range if different
- if (session.$tagHighlight && range.compareRange(session.$backMarkers[session.$tagHighlight].range)!==0) {
- session.removeMarker(session.$tagHighlight);
- session.$tagHighlight = null;
- }
-
- if (range && !session.$tagHighlight)
- session.$tagHighlight = session.addMarker(range, "ace_bracket", "text");
- }, 50);
- };
- /**
- *
- * Brings the current `textInput` into focus.
- **/
- this.focus = function() {
- // Safari needs the timeout
- // iOS and Firefox need it called immediately
- // to be on the save side we do both
- var _self = this;
- setTimeout(function() {
- _self.textInput.focus();
- });
- this.textInput.focus();
- };
- /**
- * Returns `true` if the current `textInput` is in focus.
- * @return {Boolean}
- **/
- this.isFocused = function() {
- return this.textInput.isFocused();
- };
- /**
- *
- * Blurs the current `textInput`.
- **/
- this.blur = function() {
- this.textInput.blur();
- };
- /**
- * Emitted once the editor comes into focus.
- * @event focus
- *
- *
- **/
- this.onFocus = function(e) {
- if (this.$isFocused)
- return;
- this.$isFocused = true;
- this.renderer.showCursor();
- this.renderer.visualizeFocus();
- this._emit("focus", e);
- };
- /**
- * Emitted once the editor has been blurred.
- * @event blur
- *
- *
- **/
- this.onBlur = function(e) {
- if (!this.$isFocused)
- return;
- this.$isFocused = false;
- this.renderer.hideCursor();
- this.renderer.visualizeBlur();
- this._emit("blur", e);
- };
- this.$cursorChange = function() {
- this.renderer.updateCursor();
- };
- /**
- * Emitted whenever the document is changed.
- * @event change
- * @param {Object} e Contains a single property, `data`, which has the delta of changes
- *
- *
- *
- **/
- this.onDocumentChange = function(delta) {
- // Rerender and emit "change" event.
- var wrap = this.session.$useWrapMode;
- var lastRow = (delta.start.row == delta.end.row ? delta.end.row : Infinity);
- this.renderer.updateLines(delta.start.row, lastRow, wrap);
- this._signal("change", delta);
-
- // Update cursor because tab characters can influence the cursor position.
- this.$cursorChange();
- this.$updateHighlightActiveLine();
- };
- this.onTokenizerUpdate = function(e) {
- var rows = e.data;
- this.renderer.updateLines(rows.first, rows.last);
- };
- this.onScrollTopChange = function() {
- this.renderer.scrollToY(this.session.getScrollTop());
- };
- this.onScrollLeftChange = function() {
- this.renderer.scrollToX(this.session.getScrollLeft());
- };
- /**
- * Emitted when the selection changes.
- *
- **/
- this.onCursorChange = function() {
- this.$cursorChange();
- if (!this.$blockScrolling) {
- config.warn("Automatically scrolling cursor into view after selection change",
- "this will be disabled in the next version",
- "set editor.$blockScrolling = Infinity to disable this message"
- );
- this.renderer.scrollCursorIntoView();
- }
- this.$highlightBrackets();
- this.$highlightTags();
- this.$updateHighlightActiveLine();
- this._signal("changeSelection");
- };
- this.$updateHighlightActiveLine = function() {
- var session = this.getSession();
- var highlight;
- if (this.$highlightActiveLine) {
- if ((this.$selectionStyle != "line" || !this.selection.isMultiLine()))
- highlight = this.getCursorPosition();
- if (this.renderer.$maxLines && this.session.getLength() === 1 && !(this.renderer.$minLines > 1))
- highlight = false;
- }
- if (session.$highlightLineMarker && !highlight) {
- session.removeMarker(session.$highlightLineMarker.id);
- session.$highlightLineMarker = null;
- } else if (!session.$highlightLineMarker && highlight) {
- var range = new Range(highlight.row, highlight.column, highlight.row, Infinity);
- range.id = session.addMarker(range, "ace_active-line", "screenLine");
- session.$highlightLineMarker = range;
- } else if (highlight) {
- session.$highlightLineMarker.start.row = highlight.row;
- session.$highlightLineMarker.end.row = highlight.row;
- session.$highlightLineMarker.start.column = highlight.column;
- session._signal("changeBackMarker");
- }
- };
- this.onSelectionChange = function(e) {
- var session = this.session;
- if (session.$selectionMarker) {
- session.removeMarker(session.$selectionMarker);
- }
- session.$selectionMarker = null;
- if (!this.selection.isEmpty()) {
- var range = this.selection.getRange();
- var style = this.getSelectionStyle();
- session.$selectionMarker = session.addMarker(range, "ace_selection", style);
- } else {
- this.$updateHighlightActiveLine();
- }
- var re = this.$highlightSelectedWord && this.$getSelectionHighLightRegexp();
- this.session.highlight(re);
- this._signal("changeSelection");
- };
- this.$getSelectionHighLightRegexp = function() {
- var session = this.session;
- var selection = this.getSelectionRange();
- if (selection.isEmpty() || selection.isMultiLine())
- return;
- var startOuter = selection.start.column - 1;
- var endOuter = selection.end.column + 1;
- var line = session.getLine(selection.start.row);
- var lineCols = line.length;
- var needle = line.substring(Math.max(startOuter, 0),
- Math.min(endOuter, lineCols));
- // Make sure the outer characters are not part of the word.
- if ((startOuter >= 0 && /^[\w\d]/.test(needle)) ||
- (endOuter <= lineCols && /[\w\d]$/.test(needle)))
- return;
- needle = line.substring(selection.start.column, selection.end.column);
- if (!/^[\w\d]+$/.test(needle))
- return;
- var re = this.$search.$assembleRegExp({
- wholeWord: true,
- caseSensitive: true,
- needle: needle
- });
- return re;
- };
- this.onChangeFrontMarker = function() {
- this.renderer.updateFrontMarkers();
- };
- this.onChangeBackMarker = function() {
- this.renderer.updateBackMarkers();
- };
- this.onChangeBreakpoint = function() {
- this.renderer.updateBreakpoints();
- };
- this.onChangeAnnotation = function() {
- this.renderer.setAnnotations(this.session.getAnnotations());
- };
- this.onChangeMode = function(e) {
- this.renderer.updateText();
- this._emit("changeMode", e);
- };
- this.onChangeWrapLimit = function() {
- this.renderer.updateFull();
- };
- this.onChangeWrapMode = function() {
- this.renderer.onResize(true);
- };
- this.onChangeFold = function() {
- // Update the active line marker as due to folding changes the current
- // line range on the screen might have changed.
- this.$updateHighlightActiveLine();
- // TODO: This might be too much updating. Okay for now.
- this.renderer.updateFull();
- };
-
- /**
- * Returns the string of text currently highlighted.
- * @returns {String}
- **/
- this.getSelectedText = function() {
- return this.session.getTextRange(this.getSelectionRange());
- };
-
- /**
- * Emitted when text is copied.
- * @event copy
- * @param {String} text The copied text
- *
- **/
- /**
- * Returns the string of text currently highlighted.
- * @returns {String}
- * @deprecated Use getSelectedText instead.
- **/
- this.getCopyText = function() {
- var text = this.getSelectedText();
- this._signal("copy", text);
- return text;
- };
- /**
- * Called whenever a text "copy" happens.
- **/
- this.onCopy = function() {
- this.commands.exec("copy", this);
- };
- /**
- * Called whenever a text "cut" happens.
- **/
- this.onCut = function() {
- this.commands.exec("cut", this);
- };
- /**
- * Emitted when text is pasted.
- * @event paste
- * @param {Object} an object which contains one property, `text`, that represents the text to be pasted. Editing this property will alter the text that is pasted.
- *
- *
- **/
- /**
- * Called whenever a text "paste" happens.
- * @param {String} text The pasted text
- *
- *
- **/
- this.onPaste = function(text, event) {
- var e = {text: text, event: event};
- this.commands.exec("paste", this, e);
- };
-
- this.$handlePaste = function(e) {
- if (typeof e == "string")
- e = {text: e};
- this._signal("paste", e);
- var text = e.text;
- if (!this.inMultiSelectMode || this.inVirtualSelectionMode) {
- this.insert(text);
- } else {
- var lines = text.split(/\r\n|\r|\n/);
- var ranges = this.selection.rangeList.ranges;
-
- if (lines.length > ranges.length || lines.length < 2 || !lines[1])
- return this.commands.exec("insertstring", this, text);
-
- for (var i = ranges.length; i--;) {
- var range = ranges[i];
- if (!range.isEmpty())
- this.session.remove(range);
-
- this.session.insert(range.start, lines[i]);
- }
- }
- };
- this.execCommand = function(command, args) {
- return this.commands.exec(command, this, args);
- };
- /**
- * Inserts `text` into wherever the cursor is pointing.
- * @param {String} text The new text to add
- *
- **/
- this.insert = function(text, pasted) {
- var session = this.session;
- var mode = session.getMode();
- var cursor = this.getCursorPosition();
- if (this.getBehavioursEnabled() && !pasted) {
- // Get a transform if the current mode wants one.
- var transform = mode.transformAction(session.getState(cursor.row), 'insertion', this, session, text);
- if (transform) {
- if (text !== transform.text) {
- this.session.mergeUndoDeltas = false;
- this.$mergeNextCommand = false;
- }
- text = transform.text;
- }
- }
-
- if (text == "\t")
- text = this.session.getTabString();
- // remove selected text
- if (!this.selection.isEmpty()) {
- var range = this.getSelectionRange();
- cursor = this.session.remove(range);
- this.clearSelection();
- }
- else if (this.session.getOverwrite()) {
- var range = new Range.fromPoints(cursor, cursor);
- range.end.column += text.length;
- this.session.remove(range);
- }
- if (text == "\n" || text == "\r\n") {
- var line = session.getLine(cursor.row);
- if (cursor.column > line.search(/\S|$/)) {
- var d = line.substr(cursor.column).search(/\S|$/);
- session.doc.removeInLine(cursor.row, cursor.column, cursor.column + d);
- }
- }
- this.clearSelection();
- var start = cursor.column;
- var lineState = session.getState(cursor.row);
- var line = session.getLine(cursor.row);
- var shouldOutdent = mode.checkOutdent(lineState, line, text);
- var end = session.insert(cursor, text);
- if (transform && transform.selection) {
- if (transform.selection.length == 2) { // Transform relative to the current column
- this.selection.setSelectionRange(
- new Range(cursor.row, start + transform.selection[0],
- cursor.row, start + transform.selection[1]));
- } else { // Transform relative to the current row.
- this.selection.setSelectionRange(
- new Range(cursor.row + transform.selection[0],
- transform.selection[1],
- cursor.row + transform.selection[2],
- transform.selection[3]));
- }
- }
- if (session.getDocument().isNewLine(text)) {
- var lineIndent = mode.getNextLineIndent(lineState, line.slice(0, cursor.column), session.getTabString());
- session.insert({row: cursor.row+1, column: 0}, lineIndent);
- }
- if (shouldOutdent)
- mode.autoOutdent(lineState, session, cursor.row);
- };
- this.onTextInput = function(text) {
- this.keyBinding.onTextInput(text);
- };
- this.onCommandKey = function(e, hashId, keyCode) {
- this.keyBinding.onCommandKey(e, hashId, keyCode);
- };
- /**
- * Pass in `true` to enable overwrites in your session, or `false` to disable. If overwrites is enabled, any text you enter will type over any text after it. If the value of `overwrite` changes, this function also emites the `changeOverwrite` event.
- * @param {Boolean} overwrite Defines wheter or not to set overwrites
- *
- *
- * @related EditSession.setOverwrite
- **/
- this.setOverwrite = function(overwrite) {
- this.session.setOverwrite(overwrite);
- };
- /**
- * Returns `true` if overwrites are enabled; `false` otherwise.
- * @returns {Boolean}
- * @related EditSession.getOverwrite
- **/
- this.getOverwrite = function() {
- return this.session.getOverwrite();
- };
- /**
- * Sets the value of overwrite to the opposite of whatever it currently is.
- * @related EditSession.toggleOverwrite
- **/
- this.toggleOverwrite = function() {
- this.session.toggleOverwrite();
- };
- /**
- * Sets how fast the mouse scrolling should do.
- * @param {Number} speed A value indicating the new speed (in milliseconds)
- **/
- this.setScrollSpeed = function(speed) {
- this.setOption("scrollSpeed", speed);
- };
- /**
- * Returns the value indicating how fast the mouse scroll speed is (in milliseconds).
- * @returns {Number}
- **/
- this.getScrollSpeed = function() {
- return this.getOption("scrollSpeed");
- };
- /**
- * Sets the delay (in milliseconds) of the mouse drag.
- * @param {Number} dragDelay A value indicating the new delay
- **/
- this.setDragDelay = function(dragDelay) {
- this.setOption("dragDelay", dragDelay);
- };
- /**
- * Returns the current mouse drag delay.
- * @returns {Number}
- **/
- this.getDragDelay = function() {
- return this.getOption("dragDelay");
- };
- /**
- * Emitted when the selection style changes, via [[Editor.setSelectionStyle]].
- * @event changeSelectionStyle
- * @param {Object} data Contains one property, `data`, which indicates the new selection style
- **/
- /**
- * Draw selection markers spanning whole line, or only over selected text. Default value is "line"
- * @param {String} style The new selection style "line"|"text"
- *
- **/
- this.setSelectionStyle = function(val) {
- this.setOption("selectionStyle", val);
- };
- /**
- * Returns the current selection style.
- * @returns {String}
- **/
- this.getSelectionStyle = function() {
- return this.getOption("selectionStyle");
- };
- /**
- * Determines whether or not the current line should be highlighted.
- * @param {Boolean} shouldHighlight Set to `true` to highlight the current line
- **/
- this.setHighlightActiveLine = function(shouldHighlight) {
- this.setOption("highlightActiveLine", shouldHighlight);
- };
- /**
- * Returns `true` if current lines are always highlighted.
- * @return {Boolean}
- **/
- this.getHighlightActiveLine = function() {
- return this.getOption("highlightActiveLine");
- };
- this.setHighlightGutterLine = function(shouldHighlight) {
- this.setOption("highlightGutterLine", shouldHighlight);
- };
- this.getHighlightGutterLine = function() {
- return this.getOption("highlightGutterLine");
- };
- /**
- * Determines if the currently selected word should be highlighted.
- * @param {Boolean} shouldHighlight Set to `true` to highlight the currently selected word
- *
- **/
- this.setHighlightSelectedWord = function(shouldHighlight) {
- this.setOption("highlightSelectedWord", shouldHighlight);
- };
- /**
- * Returns `true` if currently highlighted words are to be highlighted.
- * @returns {Boolean}
- **/
- this.getHighlightSelectedWord = function() {
- return this.$highlightSelectedWord;
- };
- this.setAnimatedScroll = function(shouldAnimate){
- this.renderer.setAnimatedScroll(shouldAnimate);
- };
- this.getAnimatedScroll = function(){
- return this.renderer.getAnimatedScroll();
- };
- /**
- * If `showInvisibles` is set to `true`, invisible characters—like spaces or new lines—are show in the editor.
- * @param {Boolean} showInvisibles Specifies whether or not to show invisible characters
- *
- **/
- this.setShowInvisibles = function(showInvisibles) {
- this.renderer.setShowInvisibles(showInvisibles);
- };
- /**
- * Returns `true` if invisible characters are being shown.
- * @returns {Boolean}
- **/
- this.getShowInvisibles = function() {
- return this.renderer.getShowInvisibles();
- };
- this.setDisplayIndentGuides = function(display) {
- this.renderer.setDisplayIndentGuides(display);
- };
- this.getDisplayIndentGuides = function() {
- return this.renderer.getDisplayIndentGuides();
- };
- /**
- * If `showPrintMargin` is set to `true`, the print margin is shown in the editor.
- * @param {Boolean} showPrintMargin Specifies whether or not to show the print margin
- *
- **/
- this.setShowPrintMargin = function(showPrintMargin) {
- this.renderer.setShowPrintMargin(showPrintMargin);
- };
- /**
- * Returns `true` if the print margin is being shown.
- * @returns {Boolean}
- **/
- this.getShowPrintMargin = function() {
- return this.renderer.getShowPrintMargin();
- };
- /**
- * Sets the column defining where the print margin should be.
- * @param {Number} showPrintMargin Specifies the new print margin
- *
- **/
- this.setPrintMarginColumn = function(showPrintMargin) {
- this.renderer.setPrintMarginColumn(showPrintMargin);
- };
- /**
- * Returns the column number of where the print margin is.
- * @returns {Number}
- **/
- this.getPrintMarginColumn = function() {
- return this.renderer.getPrintMarginColumn();
- };
- /**
- * If `readOnly` is true, then the editor is set to read-only mode, and none of the content can change.
- * @param {Boolean} readOnly Specifies whether the editor can be modified or not
- *
- **/
- this.setReadOnly = function(readOnly) {
- this.setOption("readOnly", readOnly);
- };
- /**
- * Returns `true` if the editor is set to read-only mode.
- * @returns {Boolean}
- **/
- this.getReadOnly = function() {
- return this.getOption("readOnly");
- };
- /**
- * Specifies whether to use behaviors or not. ["Behaviors" in this case is the auto-pairing of special characters, like quotation marks, parenthesis, or brackets.]{: #BehaviorsDef}
- * @param {Boolean} enabled Enables or disables behaviors
- *
- **/
- this.setBehavioursEnabled = function (enabled) {
- this.setOption("behavioursEnabled", enabled);
- };
- /**
- * Returns `true` if the behaviors are currently enabled. {:BehaviorsDef}
- *
- * @returns {Boolean}
- **/
- this.getBehavioursEnabled = function () {
- return this.getOption("behavioursEnabled");
- };
- /**
- * Specifies whether to use wrapping behaviors or not, i.e. automatically wrapping the selection with characters such as brackets
- * when such a character is typed in.
- * @param {Boolean} enabled Enables or disables wrapping behaviors
- *
- **/
- this.setWrapBehavioursEnabled = function (enabled) {
- this.setOption("wrapBehavioursEnabled", enabled);
- };
- /**
- * Returns `true` if the wrapping behaviors are currently enabled.
- **/
- this.getWrapBehavioursEnabled = function () {
- return this.getOption("wrapBehavioursEnabled");
- };
- /**
- * Indicates whether the fold widgets should be shown or not.
- * @param {Boolean} show Specifies whether the fold widgets are shown
- **/
- this.setShowFoldWidgets = function(show) {
- this.setOption("showFoldWidgets", show);
- };
- /**
- * Returns `true` if the fold widgets are shown.
- * @return {Boolean}
- **/
- this.getShowFoldWidgets = function() {
- return this.getOption("showFoldWidgets");
- };
- this.setFadeFoldWidgets = function(fade) {
- this.setOption("fadeFoldWidgets", fade);
- };
- this.getFadeFoldWidgets = function() {
- return this.getOption("fadeFoldWidgets");
- };
- /**
- * Removes the current selection or one character.
- * @param {String} dir The direction of the deletion to occur, either "left" or "right"
- *
- **/
- this.remove = function(dir) {
- if (this.selection.isEmpty()){
- if (dir == "left")
- this.selection.selectLeft();
- else
- this.selection.selectRight();
- }
- var range = this.getSelectionRange();
- if (this.getBehavioursEnabled()) {
- var session = this.session;
- var state = session.getState(range.start.row);
- var new_range = session.getMode().transformAction(state, 'deletion', this, session, range);
- if (range.end.column === 0) {
- var text = session.getTextRange(range);
- if (text[text.length - 1] == "\n") {
- var line = session.getLine(range.end.row);
- if (/^\s+$/.test(line)) {
- range.end.column = line.length;
- }
- }
- }
- if (new_range)
- range = new_range;
- }
- this.session.remove(range);
- this.clearSelection();
- };
- /**
- * Removes the word directly to the right of the current selection.
- **/
- this.removeWordRight = function() {
- if (this.selection.isEmpty())
- this.selection.selectWordRight();
- this.session.remove(this.getSelectionRange());
- this.clearSelection();
- };
- /**
- * Removes the word directly to the left of the current selection.
- **/
- this.removeWordLeft = function() {
- if (this.selection.isEmpty())
- this.selection.selectWordLeft();
- this.session.remove(this.getSelectionRange());
- this.clearSelection();
- };
- /**
- * Removes all the words to the left of the current selection, until the start of the line.
- **/
- this.removeToLineStart = function() {
- if (this.selection.isEmpty())
- this.selection.selectLineStart();
- this.session.remove(this.getSelectionRange());
- this.clearSelection();
- };
- /**
- * Removes all the words to the right of the current selection, until the end of the line.
- **/
- this.removeToLineEnd = function() {
- if (this.selection.isEmpty())
- this.selection.selectLineEnd();
- var range = this.getSelectionRange();
- if (range.start.column == range.end.column && range.start.row == range.end.row) {
- range.end.column = 0;
- range.end.row++;
- }
- this.session.remove(range);
- this.clearSelection();
- };
- /**
- * Splits the line at the current selection (by inserting an `'\n'`).
- **/
- this.splitLine = function() {
- if (!this.selection.isEmpty()) {
- this.session.remove(this.getSelectionRange());
- this.clearSelection();
- }
- var cursor = this.getCursorPosition();
- this.insert("\n");
- this.moveCursorToPosition(cursor);
- };
- /**
- * Transposes current line.
- **/
- this.transposeLetters = function() {
- if (!this.selection.isEmpty()) {
- return;
- }
- var cursor = this.getCursorPosition();
- var column = cursor.column;
- if (column === 0)
- return;
- var line = this.session.getLine(cursor.row);
- var swap, range;
- if (column < line.length) {
- swap = line.charAt(column) + line.charAt(column-1);
- range = new Range(cursor.row, column-1, cursor.row, column+1);
- }
- else {
- swap = line.charAt(column-1) + line.charAt(column-2);
- range = new Range(cursor.row, column-2, cursor.row, column);
- }
- this.session.replace(range, swap);
- };
- /**
- * Converts the current selection entirely into lowercase.
- **/
- this.toLowerCase = function() {
- var originalRange = this.getSelectionRange();
- if (this.selection.isEmpty()) {
- this.selection.selectWord();
- }
- var range = this.getSelectionRange();
- var text = this.session.getTextRange(range);
- this.session.replace(range, text.toLowerCase());
- this.selection.setSelectionRange(originalRange);
- };
- /**
- * Converts the current selection entirely into uppercase.
- **/
- this.toUpperCase = function() {
- var originalRange = this.getSelectionRange();
- if (this.selection.isEmpty()) {
- this.selection.selectWord();
- }
- var range = this.getSelectionRange();
- var text = this.session.getTextRange(range);
- this.session.replace(range, text.toUpperCase());
- this.selection.setSelectionRange(originalRange);
- };
- /**
- * Inserts an indentation into the current cursor position or indents the selected lines.
- *
- * @related EditSession.indentRows
- **/
- this.indent = function() {
- var session = this.session;
- var range = this.getSelectionRange();
- if (range.start.row < range.end.row) {
- var rows = this.$getSelectedRows();
- session.indentRows(rows.first, rows.last, "\t");
- return;
- } else if (range.start.column < range.end.column) {
- var text = session.getTextRange(range);
- if (!/^\s+$/.test(text)) {
- var rows = this.$getSelectedRows();
- session.indentRows(rows.first, rows.last, "\t");
- return;
- }
- }
-
- var line = session.getLine(range.start.row);
- var position = range.start;
- var size = session.getTabSize();
- var column = session.documentToScreenColumn(position.row, position.column);
- if (this.session.getUseSoftTabs()) {
- var count = (size - column % size);
- var indentString = lang.stringRepeat(" ", count);
- } else {
- var count = column % size;
- while (line[range.start.column] == " " && count) {
- range.start.column--;
- count--;
- }
- this.selection.setSelectionRange(range);
- indentString = "\t";
- }
- return this.insert(indentString);
- };
- /**
- * Indents the current line.
- * @related EditSession.indentRows
- **/
- this.blockIndent = function() {
- var rows = this.$getSelectedRows();
- this.session.indentRows(rows.first, rows.last, "\t");
- };
- /**
- * Outdents the current line.
- * @related EditSession.outdentRows
- **/
- this.blockOutdent = function() {
- var selection = this.session.getSelection();
- this.session.outdentRows(selection.getRange());
- };
- // TODO: move out of core when we have good mechanism for managing extensions
- this.sortLines = function() {
- var rows = this.$getSelectedRows();
- var session = this.session;
- var lines = [];
- for (i = rows.first; i <= rows.last; i++)
- lines.push(session.getLine(i));
- lines.sort(function(a, b) {
- if (a.toLowerCase() < b.toLowerCase()) return -1;
- if (a.toLowerCase() > b.toLowerCase()) return 1;
- return 0;
- });
- var deleteRange = new Range(0, 0, 0, 0);
- for (var i = rows.first; i <= rows.last; i++) {
- var line = session.getLine(i);
- deleteRange.start.row = i;
- deleteRange.end.row = i;
- deleteRange.end.column = line.length;
- session.replace(deleteRange, lines[i-rows.first]);
- }
- };
- /**
- * Given the currently selected range, this function either comments all the lines, or uncomments all of them.
- **/
- this.toggleCommentLines = function() {
- var state = this.session.getState(this.getCursorPosition().row);
- var rows = this.$getSelectedRows();
- this.session.getMode().toggleCommentLines(state, this.session, rows.first, rows.last);
- };
- this.toggleBlockComment = function() {
- var cursor = this.getCursorPosition();
- var state = this.session.getState(cursor.row);
- var range = this.getSelectionRange();
- this.session.getMode().toggleBlockComment(state, this.session, range, cursor);
- };
- /**
- * Works like [[EditSession.getTokenAt]], except it returns a number.
- * @returns {Number}
- **/
- this.getNumberAt = function(row, column) {
- var _numberRx = /[\-]?[0-9]+(?:\.[0-9]+)?/g;
- _numberRx.lastIndex = 0;
- var s = this.session.getLine(row);
- while (_numberRx.lastIndex < column) {
- var m = _numberRx.exec(s);
- if(m.index <= column && m.index+m[0].length >= column){
- var number = {
- value: m[0],
- start: m.index,
- end: m.index+m[0].length
- };
- return number;
- }
- }
- return null;
- };
- /**
- * If the character before the cursor is a number, this functions changes its value by `amount`.
- * @param {Number} amount The value to change the numeral by (can be negative to decrease value)
- *
- **/
- this.modifyNumber = function(amount) {
- var row = this.selection.getCursor().row;
- var column = this.selection.getCursor().column;
- // get the char before the cursor
- var charRange = new Range(row, column-1, row, column);
- var c = this.session.getTextRange(charRange);
- // if the char is a digit
- if (!isNaN(parseFloat(c)) && isFinite(c)) {
- // get the whole number the digit is part of
- var nr = this.getNumberAt(row, column);
- // if number found
- if (nr) {
- var fp = nr.value.indexOf(".") >= 0 ? nr.start + nr.value.indexOf(".") + 1 : nr.end;
- var decimals = nr.start + nr.value.length - fp;
- var t = parseFloat(nr.value);
- t *= Math.pow(10, decimals);
- if(fp !== nr.end && column < fp){
- amount *= Math.pow(10, nr.end - column - 1);
- } else {
- amount *= Math.pow(10, nr.end - column);
- }
- t += amount;
- t /= Math.pow(10, decimals);
- var nnr = t.toFixed(decimals);
- //update number
- var replaceRange = new Range(row, nr.start, row, nr.end);
- this.session.replace(replaceRange, nnr);
- //reposition the cursor
- this.moveCursorTo(row, Math.max(nr.start +1, column + nnr.length - nr.value.length));
- }
- }
- };
- /**
- * Removes all the lines in the current selection
- * @related EditSession.remove
- **/
- this.removeLines = function() {
- var rows = this.$getSelectedRows();
- this.session.removeFullLines(rows.first, rows.last);
- this.clearSelection();
- };
- this.duplicateSelection = function() {
- var sel = this.selection;
- var doc = this.session;
- var range = sel.getRange();
- var reverse = sel.isBackwards();
- if (range.isEmpty()) {
- var row = range.start.row;
- doc.duplicateLines(row, row);
- } else {
- var point = reverse ? range.start : range.end;
- var endPoint = doc.insert(point, doc.getTextRange(range), false);
- range.start = point;
- range.end = endPoint;
- sel.setSelectionRange(range, reverse);
- }
- };
- /**
- * Shifts all the selected lines down one row.
- *
- * @returns {Number} On success, it returns -1.
- * @related EditSession.moveLinesUp
- **/
- this.moveLinesDown = function() {
- this.$moveLines(1, false);
- };
- /**
- * Shifts all the selected lines up one row.
- * @returns {Number} On success, it returns -1.
- * @related EditSession.moveLinesDown
- **/
- this.moveLinesUp = function() {
- this.$moveLines(-1, false);
- };
- /**
- * Moves a range of text from the given range to the given position. `toPosition` is an object that looks like this:
- * ```json
- * { row: newRowLocation, column: newColumnLocation }
- * ```
- * @param {Range} fromRange The range of text you want moved within the document
- * @param {Object} toPosition The location (row and column) where you want to move the text to
- *
- * @returns {Range} The new range where the text was moved to.
- * @related EditSession.moveText
- **/
- this.moveText = function(range, toPosition, copy) {
- return this.session.moveText(range, toPosition, copy);
- };
- /**
- * Copies all the selected lines up one row.
- * @returns {Number} On success, returns 0.
- *
- **/
- this.copyLinesUp = function() {
- this.$moveLines(-1, true);
- };
- /**
- * Copies all the selected lines down one row.
- * @returns {Number} On success, returns the number of new rows added; in other words, `lastRow - firstRow + 1`.
- * @related EditSession.duplicateLines
- *
- **/
- this.copyLinesDown = function() {
- this.$moveLines(1, true);
- };
- /**
- * for internal use
- * @ignore
- *
- **/
- this.$moveLines = function(dir, copy) {
- var rows, moved;
- var selection = this.selection;
- if (!selection.inMultiSelectMode || this.inVirtualSelectionMode) {
- var range = selection.toOrientedRange();
- rows = this.$getSelectedRows(range);
- moved = this.session.$moveLines(rows.first, rows.last, copy ? 0 : dir);
- if (copy && dir == -1) moved = 0;
- range.moveBy(moved, 0);
- selection.fromOrientedRange(range);
- } else {
- var ranges = selection.rangeList.ranges;
- selection.rangeList.detach(this.session);
- this.inVirtualSelectionMode = true;
-
- var diff = 0;
- var totalDiff = 0;
- var l = ranges.length;
- for (var i = 0; i < l; i++) {
- var rangeIndex = i;
- ranges[i].moveBy(diff, 0);
- rows = this.$getSelectedRows(ranges[i]);
- var first = rows.first;
- var last = rows.last;
- while (++i < l) {
- if (totalDiff) ranges[i].moveBy(totalDiff, 0);
- var subRows = this.$getSelectedRows(ranges[i]);
- if (copy && subRows.first != last)
- break;
- else if (!copy && subRows.first > last + 1)
- break;
- last = subRows.last;
- }
- i--;
- diff = this.session.$moveLines(first, last, copy ? 0 : dir);
- if (copy && dir == -1) rangeIndex = i + 1;
- while (rangeIndex <= i) {
- ranges[rangeIndex].moveBy(diff, 0);
- rangeIndex++;
- }
- if (!copy) diff = 0;
- totalDiff += diff;
- }
-
- selection.fromOrientedRange(selection.ranges[0]);
- selection.rangeList.attach(this.session);
- this.inVirtualSelectionMode = false;
- }
- };
- /**
- * Returns an object indicating the currently selected rows. The object looks like this:
- *
- * ```json
- * { first: range.start.row, last: range.end.row }
- * ```
- *
- * @returns {Object}
- **/
- this.$getSelectedRows = function(range) {
- range = (range || this.getSelectionRange()).collapseRows();
- return {
- first: this.session.getRowFoldStart(range.start.row),
- last: this.session.getRowFoldEnd(range.end.row)
- };
- };
- this.onCompositionStart = function(text) {
- this.renderer.showComposition(this.getCursorPosition());
- };
- this.onCompositionUpdate = function(text) {
- this.renderer.setCompositionText(text);
- };
- this.onCompositionEnd = function() {
- this.renderer.hideComposition();
- };
- /**
- * {:VirtualRenderer.getFirstVisibleRow}
- *
- * @returns {Number}
- * @related VirtualRenderer.getFirstVisibleRow
- **/
- this.getFirstVisibleRow = function() {
- return this.renderer.getFirstVisibleRow();
- };
- /**
- * {:VirtualRenderer.getLastVisibleRow}
- *
- * @returns {Number}
- * @related VirtualRenderer.getLastVisibleRow
- **/
- this.getLastVisibleRow = function() {
- return this.renderer.getLastVisibleRow();
- };
- /**
- * Indicates if the row is currently visible on the screen.
- * @param {Number} row The row to check
- *
- * @returns {Boolean}
- **/
- this.isRowVisible = function(row) {
- return (row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow());
- };
- /**
- * Indicates if the entire row is currently visible on the screen.
- * @param {Number} row The row to check
- *
- *
- * @returns {Boolean}
- **/
- this.isRowFullyVisible = function(row) {
- return (row >= this.renderer.getFirstFullyVisibleRow() && row <= this.renderer.getLastFullyVisibleRow());
- };
- /**
- * Returns the number of currently visibile rows.
- * @returns {Number}
- **/
- this.$getVisibleRowCount = function() {
- return this.renderer.getScrollBottomRow() - this.renderer.getScrollTopRow() + 1;
- };
- this.$moveByPage = function(dir, select) {
- var renderer = this.renderer;
- var config = this.renderer.layerConfig;
- var rows = dir * Math.floor(config.height / config.lineHeight);
- this.$blockScrolling++;
- if (select === true) {
- this.selection.$moveSelection(function(){
- this.moveCursorBy(rows, 0);
- });
- } else if (select === false) {
- this.selection.moveCursorBy(rows, 0);
- this.selection.clearSelection();
- }
- this.$blockScrolling--;
- var scrollTop = renderer.scrollTop;
- renderer.scrollBy(0, rows * config.lineHeight);
- if (select != null)
- renderer.scrollCursorIntoView(null, 0.5);
- renderer.animateScrolling(scrollTop);
- };
- /**
- * Selects the text from the current position of the document until where a "page down" finishes.
- **/
- this.selectPageDown = function() {
- this.$moveByPage(1, true);
- };
- /**
- * Selects the text from the current position of the document until where a "page up" finishes.
- **/
- this.selectPageUp = function() {
- this.$moveByPage(-1, true);
- };
- /**
- * Shifts the document to wherever "page down" is, as well as moving the cursor position.
- **/
- this.gotoPageDown = function() {
- this.$moveByPage(1, false);
- };
- /**
- * Shifts the document to wherever "page up" is, as well as moving the cursor position.
- **/
- this.gotoPageUp = function() {
- this.$moveByPage(-1, false);
- };
- /**
- * Scrolls the document to wherever "page down" is, without changing the cursor position.
- **/
- this.scrollPageDown = function() {
- this.$moveByPage(1);
- };
- /**
- * Scrolls the document to wherever "page up" is, without changing the cursor position.
- **/
- this.scrollPageUp = function() {
- this.$moveByPage(-1);
- };
- /**
- * Moves the editor to the specified row.
- * @related VirtualRenderer.scrollToRow
- **/
- this.scrollToRow = function(row) {
- this.renderer.scrollToRow(row);
- };
- /**
- * Scrolls to a line. If `center` is `true`, it puts the line in middle of screen (or attempts to).
- * @param {Number} line The line to scroll to
- * @param {Boolean} center If `true`
- * @param {Boolean} animate If `true` animates scrolling
- * @param {Function} callback Function to be called when the animation has finished
- *
- *
- * @related VirtualRenderer.scrollToLine
- **/
- this.scrollToLine = function(line, center, animate, callback) {
- this.renderer.scrollToLine(line, center, animate, callback);
- };
- /**
- * Attempts to center the current selection on the screen.
- **/
- this.centerSelection = function() {
- var range = this.getSelectionRange();
- var pos = {
- row: Math.floor(range.start.row + (range.end.row - range.start.row) / 2),
- column: Math.floor(range.start.column + (range.end.column - range.start.column) / 2)
- };
- this.renderer.alignCursor(pos, 0.5);
- };
- /**
- * Gets the current position of the cursor.
- * @returns {Object} An object that looks something like this:
- *
- * ```json
- * { row: currRow, column: currCol }
- * ```
- *
- * @related Selection.getCursor
- **/
- this.getCursorPosition = function() {
- return this.selection.getCursor();
- };
- /**
- * Returns the screen position of the cursor.
- * @returns {Number}
- * @related EditSession.documentToScreenPosition
- **/
- this.getCursorPositionScreen = function() {
- return this.session.documentToScreenPosition(this.getCursorPosition());
- };
- /**
- * {:Selection.getRange}
- * @returns {Range}
- * @related Selection.getRange
- **/
- this.getSelectionRange = function() {
- return this.selection.getRange();
- };
- /**
- * Selects all the text in editor.
- * @related Selection.selectAll
- **/
- this.selectAll = function() {
- this.$blockScrolling += 1;
- this.selection.selectAll();
- this.$blockScrolling -= 1;
- };
- /**
- * {:Selection.clearSelection}
- * @related Selection.clearSelection
- **/
- this.clearSelection = function() {
- this.selection.clearSelection();
- };
- /**
- * Moves the cursor to the specified row and column. Note that this does not de-select the current selection.
- * @param {Number} row The new row number
- * @param {Number} column The new column number
- *
- *
- * @related Selection.moveCursorTo
- **/
- this.moveCursorTo = function(row, column) {
- this.selection.moveCursorTo(row, column);
- };
- /**
- * Moves the cursor to the position indicated by `pos.row` and `pos.column`.
- * @param {Object} pos An object with two properties, row and column
- *
- *
- * @related Selection.moveCursorToPosition
- **/
- this.moveCursorToPosition = function(pos) {
- this.selection.moveCursorToPosition(pos);
- };
- /**
- * Moves the cursor's row and column to the next matching bracket or HTML tag.
- *
- **/
- this.jumpToMatching = function(select, expand) {
- var cursor = this.getCursorPosition();
- var iterator = new TokenIterator(this.session, cursor.row, cursor.column);
- var prevToken = iterator.getCurrentToken();
- var token = prevToken || iterator.stepForward();
- if (!token) return;
- //get next closing tag or bracket
- var matchType;
- var found = false;
- var depth = {};
- var i = cursor.column - token.start;
- var bracketType;
- var brackets = {
- ")": "(",
- "(": "(",
- "]": "[",
- "[": "[",
- "{": "{",
- "}": "{"
- };
-
- do {
- if (token.value.match(/[{}()\[\]]/g)) {
- for (; i < token.value.length && !found; i++) {
- if (!brackets[token.value[i]]) {
- continue;
- }
- bracketType = brackets[token.value[i]] + '.' + token.type.replace("rparen", "lparen");
- if (isNaN(depth[bracketType])) {
- depth[bracketType] = 0;
- }
- switch (token.value[i]) {
- case '(':
- case '[':
- case '{':
- depth[bracketType]++;
- break;
- case ')':
- case ']':
- case '}':
- depth[bracketType]--;
- if (depth[bracketType] === -1) {
- matchType = 'bracket';
- found = true;
- }
- break;
- }
- }
- }
- else if (token && token.type.indexOf('tag-name') !== -1) {
- if (isNaN(depth[token.value])) {
- depth[token.value] = 0;
- }
-
- if (prevToken.value === '<') {
- depth[token.value]++;
- }
- else if (prevToken.value === '</') {
- depth[token.value]--;
- }
-
- if (depth[token.value] === -1) {
- matchType = 'tag';
- found = true;
- }
- }
- if (!found) {
- prevToken = token;
- token = iterator.stepForward();
- i = 0;
- }
- } while (token && !found);
- //no match found
- if (!matchType)
- return;
- var range, pos;
- if (matchType === 'bracket') {
- range = this.session.getBracketRange(cursor);
- if (!range) {
- range = new Range(
- iterator.getCurrentTokenRow(),
- iterator.getCurrentTokenColumn() + i - 1,
- iterator.getCurrentTokenRow(),
- iterator.getCurrentTokenColumn() + i - 1
- );
- pos = range.start;
- if (expand || pos.row === cursor.row && Math.abs(pos.column - cursor.column) < 2)
- range = this.session.getBracketRange(pos);
- }
- }
- else if (matchType === 'tag') {
- if (token && token.type.indexOf('tag-name') !== -1)
- var tag = token.value;
- else
- return;
- range = new Range(
- iterator.getCurrentTokenRow(),
- iterator.getCurrentTokenColumn() - 2,
- iterator.getCurrentTokenRow(),
- iterator.getCurrentTokenColumn() - 2
- );
- //find matching tag
- if (range.compare(cursor.row, cursor.column) === 0) {
- found = false;
- do {
- token = prevToken;
- prevToken = iterator.stepBackward();
-
- if (prevToken) {
- if (prevToken.type.indexOf('tag-close') !== -1) {
- range.setEnd(iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn() + 1);
- }
- if (token.value === tag && token.type.indexOf('tag-name') !== -1) {
- if (prevToken.value === '<') {
- depth[tag]++;
- }
- else if (prevToken.value === '</') {
- depth[tag]--;
- }
-
- if (depth[tag] === 0)
- found = true;
- }
- }
- } while (prevToken && !found);
- }
- //we found it
- if (token && token.type.indexOf('tag-name')) {
- pos = range.start;
- if (pos.row == cursor.row && Math.abs(pos.column - cursor.column) < 2)
- pos = range.end;
- }
- }
- pos = range && range.cursor || pos;
- if (pos) {
- if (select) {
- if (range && expand) {
- this.selection.setRange(range);
- } else if (range && range.isEqual(this.getSelectionRange())) {
- this.clearSelection();
- } else {
- this.selection.selectTo(pos.row, pos.column);
- }
- } else {
- this.selection.moveTo(pos.row, pos.column);
- }
- }
- };
- /**
- * Moves the cursor to the specified line number, and also into the indiciated column.
- * @param {Number} lineNumber The line number to go to
- * @param {Number} column A column number to go to
- * @param {Boolean} animate If `true` animates scolling
- *
- **/
- this.gotoLine = function(lineNumber, column, animate) {
- this.selection.clearSelection();
- this.session.unfold({row: lineNumber - 1, column: column || 0});
- this.$blockScrolling += 1;
- // todo: find a way to automatically exit multiselect mode
- this.exitMultiSelectMode && this.exitMultiSelectMode();
- this.moveCursorTo(lineNumber - 1, column || 0);
- this.$blockScrolling -= 1;
- if (!this.isRowFullyVisible(lineNumber - 1))
- this.scrollToLine(lineNumber - 1, true, animate);
- };
- /**
- * Moves the cursor to the specified row and column. Note that this does de-select the current selection.
- * @param {Number} row The new row number
- * @param {Number} column The new column number
- *
- *
- * @related Editor.moveCursorTo
- **/
- this.navigateTo = function(row, column) {
- this.selection.moveTo(row, column);
- };
- /**
- * Moves the cursor up in the document the specified number of times. Note that this does de-select the current selection.
- * @param {Number} times The number of times to change navigation
- *
- *
- **/
- this.navigateUp = function(times) {
- if (this.selection.isMultiLine() && !this.selection.isBackwards()) {
- var selectionStart = this.selection.anchor.getPosition();
- return this.moveCursorToPosition(selectionStart);
- }
- this.selection.clearSelection();
- this.selection.moveCursorBy(-times || -1, 0);
- };
- /**
- * Moves the cursor down in the document the specified number of times. Note that this does de-select the current selection.
- * @param {Number} times The number of times to change navigation
- *
- *
- **/
- this.navigateDown = function(times) {
- if (this.selection.isMultiLine() && this.selection.isBackwards()) {
- var selectionEnd = this.selection.anchor.getPosition();
- return this.moveCursorToPosition(selectionEnd);
- }
- this.selection.clearSelection();
- this.selection.moveCursorBy(times || 1, 0);
- };
- /**
- * Moves the cursor left in the document the specified number of times. Note that this does de-select the current selection.
- * @param {Number} times The number of times to change navigation
- *
- *
- **/
- this.navigateLeft = function(times) {
- if (!this.selection.isEmpty()) {
- var selectionStart = this.getSelectionRange().start;
- this.moveCursorToPosition(selectionStart);
- }
- else {
- times = times || 1;
- while (times--) {
- this.selection.moveCursorLeft();
- }
- }
- this.clearSelection();
- };
- /**
- * Moves the cursor right in the document the specified number of times. Note that this does de-select the current selection.
- * @param {Number} times The number of times to change navigation
- *
- *
- **/
- this.navigateRight = function(times) {
- if (!this.selection.isEmpty()) {
- var selectionEnd = this.getSelectionRange().end;
- this.moveCursorToPosition(selectionEnd);
- }
- else {
- times = times || 1;
- while (times--) {
- this.selection.moveCursorRight();
- }
- }
- this.clearSelection();
- };
- /**
- *
- * Moves the cursor to the start of the current line. Note that this does de-select the current selection.
- **/
- this.navigateLineStart = function() {
- this.selection.moveCursorLineStart();
- this.clearSelection();
- };
- /**
- *
- * Moves the cursor to the end of the current line. Note that this does de-select the current selection.
- **/
- this.navigateLineEnd = function() {
- this.selection.moveCursorLineEnd();
- this.clearSelection();
- };
- /**
- *
- * Moves the cursor to the end of the current file. Note that this does de-select the current selection.
- **/
- this.navigateFileEnd = function() {
- this.selection.moveCursorFileEnd();
- this.clearSelection();
- };
- /**
- *
- * Moves the cursor to the start of the current file. Note that this does de-select the current selection.
- **/
- this.navigateFileStart = function() {
- this.selection.moveCursorFileStart();
- this.clearSelection();
- };
- /**
- *
- * Moves the cursor to the word immediately to the right of the current position. Note that this does de-select the current selection.
- **/
- this.navigateWordRight = function() {
- this.selection.moveCursorWordRight();
- this.clearSelection();
- };
- /**
- *
- * Moves the cursor to the word immediately to the left of the current position. Note that this does de-select the current selection.
- **/
- this.navigateWordLeft = function() {
- this.selection.moveCursorWordLeft();
- this.clearSelection();
- };
- /**
- * Replaces the first occurance of `options.needle` with the value in `replacement`.
- * @param {String} replacement The text to replace with
- * @param {Object} options The [[Search `Search`]] options to use
- *
- *
- **/
- this.replace = function(replacement, options) {
- if (options)
- this.$search.set(options);
- var range = this.$search.find(this.session);
- var replaced = 0;
- if (!range)
- return replaced;
- if (this.$tryReplace(range, replacement)) {
- replaced = 1;
- }
- if (range !== null) {
- this.selection.setSelectionRange(range);
- this.renderer.scrollSelectionIntoView(range.start, range.end);
- }
- return replaced;
- };
- /**
- * Replaces all occurances of `options.needle` with the value in `replacement`.
- * @param {String} replacement The text to replace with
- * @param {Object} options The [[Search `Search`]] options to use
- *
- *
- **/
- this.replaceAll = function(replacement, options) {
- if (options) {
- this.$search.set(options);
- }
- var ranges = this.$search.findAll(this.session);
- var replaced = 0;
- if (!ranges.length)
- return replaced;
- this.$blockScrolling += 1;
- var selection = this.getSelectionRange();
- this.selection.moveTo(0, 0);
- for (var i = ranges.length - 1; i >= 0; --i) {
- if(this.$tryReplace(ranges[i], replacement)) {
- replaced++;
- }
- }
- this.selection.setSelectionRange(selection);
- this.$blockScrolling -= 1;
- return replaced;
- };
- this.$tryReplace = function(range, replacement) {
- var input = this.session.getTextRange(range);
- replacement = this.$search.replace(input, replacement);
- if (replacement !== null) {
- range.end = this.session.replace(range, replacement);
- return range;
- } else {
- return null;
- }
- };
- /**
- * {:Search.getOptions} For more information on `options`, see [[Search `Search`]].
- * @related Search.getOptions
- * @returns {Object}
- **/
- this.getLastSearchOptions = function() {
- return this.$search.getOptions();
- };
- /**
- * Attempts to find `needle` within the document. For more information on `options`, see [[Search `Search`]].
- * @param {String} needle The text to search for (optional)
- * @param {Object} options An object defining various search properties
- * @param {Boolean} animate If `true` animate scrolling
- *
- *
- * @related Search.find
- **/
- this.find = function(needle, options, animate) {
- if (!options)
- options = {};
- if (typeof needle == "string" || needle instanceof RegExp)
- options.needle = needle;
- else if (typeof needle == "object")
- oop.mixin(options, needle);
- var range = this.selection.getRange();
- if (options.needle == null) {
- needle = this.session.getTextRange(range)
- || this.$search.$options.needle;
- if (!needle) {
- range = this.session.getWordRange(range.start.row, range.start.column);
- needle = this.session.getTextRange(range);
- }
- this.$search.set({needle: needle});
- }
- this.$search.set(options);
- if (!options.start)
- this.$search.set({start: range});
- var newRange = this.$search.find(this.session);
- if (options.preventScroll)
- return newRange;
- if (newRange) {
- this.revealRange(newRange, animate);
- return newRange;
- }
- // clear selection if nothing is found
- if (options.backwards)
- range.start = range.end;
- else
- range.end = range.start;
- this.selection.setRange(range);
- };
- /**
- * Performs another search for `needle` in the document. For more information on `options`, see [[Search `Search`]].
- * @param {Object} options search options
- * @param {Boolean} animate If `true` animate scrolling
- *
- *
- * @related Editor.find
- **/
- this.findNext = function(options, animate) {
- this.find({skipCurrent: true, backwards: false}, options, animate);
- };
- /**
- * Performs a search for `needle` backwards. For more information on `options`, see [[Search `Search`]].
- * @param {Object} options search options
- * @param {Boolean} animate If `true` animate scrolling
- *
- *
- * @related Editor.find
- **/
- this.findPrevious = function(options, animate) {
- this.find(options, {skipCurrent: true, backwards: true}, animate);
- };
- this.revealRange = function(range, animate) {
- this.$blockScrolling += 1;
- this.session.unfold(range);
- this.selection.setSelectionRange(range);
- this.$blockScrolling -= 1;
- var scrollTop = this.renderer.scrollTop;
- this.renderer.scrollSelectionIntoView(range.start, range.end, 0.5);
- if (animate !== false)
- this.renderer.animateScrolling(scrollTop);
- };
- /**
- * {:UndoManager.undo}
- * @related UndoManager.undo
- **/
- this.undo = function() {
- this.$blockScrolling++;
- this.session.getUndoManager().undo();
- this.$blockScrolling--;
- this.renderer.scrollCursorIntoView(null, 0.5);
- };
- /**
- * {:UndoManager.redo}
- * @related UndoManager.redo
- **/
- this.redo = function() {
- this.$blockScrolling++;
- this.session.getUndoManager().redo();
- this.$blockScrolling--;
- this.renderer.scrollCursorIntoView(null, 0.5);
- };
- /**
- *
- * Cleans up the entire editor.
- **/
- this.destroy = function() {
- this.renderer.destroy();
- this._signal("destroy", this);
- if (this.session) {
- this.session.destroy();
- }
- };
- /**
- * Enables automatic scrolling of the cursor into view when editor itself is inside scrollable element
- * @param {Boolean} enable default true
- **/
- this.setAutoScrollEditorIntoView = function(enable) {
- if (!enable)
- return;
- var rect;
- var self = this;
- var shouldScroll = false;
- if (!this.$scrollAnchor)
- this.$scrollAnchor = document.createElement("div");
- var scrollAnchor = this.$scrollAnchor;
- scrollAnchor.style.cssText = "position:absolute";
- this.container.insertBefore(scrollAnchor, this.container.firstChild);
- var onChangeSelection = this.on("changeSelection", function() {
- shouldScroll = true;
- });
- // needed to not trigger sync reflow
- var onBeforeRender = this.renderer.on("beforeRender", function() {
- if (shouldScroll)
- rect = self.renderer.container.getBoundingClientRect();
- });
- var onAfterRender = this.renderer.on("afterRender", function() {
- if (shouldScroll && rect && (self.isFocused()
- || self.searchBox && self.searchBox.isFocused())
- ) {
- var renderer = self.renderer;
- var pos = renderer.$cursorLayer.$pixelPos;
- var config = renderer.layerConfig;
- var top = pos.top - config.offset;
- if (pos.top >= 0 && top + rect.top < 0) {
- shouldScroll = true;
- } else if (pos.top < config.height &&
- pos.top + rect.top + config.lineHeight > window.innerHeight) {
- shouldScroll = false;
- } else {
- shouldScroll = null;
- }
- if (shouldScroll != null) {
- scrollAnchor.style.top = top + "px";
- scrollAnchor.style.left = pos.left + "px";
- scrollAnchor.style.height = config.lineHeight + "px";
- scrollAnchor.scrollIntoView(shouldScroll);
- }
- shouldScroll = rect = null;
- }
- });
- this.setAutoScrollEditorIntoView = function(enable) {
- if (enable)
- return;
- delete this.setAutoScrollEditorIntoView;
- this.off("changeSelection", onChangeSelection);
- this.renderer.off("afterRender", onAfterRender);
- this.renderer.off("beforeRender", onBeforeRender);
- };
- };
- this.$resetCursorStyle = function() {
- var style = this.$cursorStyle || "ace";
- var cursorLayer = this.renderer.$cursorLayer;
- if (!cursorLayer)
- return;
- cursorLayer.setSmoothBlinking(/smooth/.test(style));
- cursorLayer.isBlinking = !this.$readOnly && style != "wide";
- dom.setCssClass(cursorLayer.element, "ace_slim-cursors", /slim/.test(style));
- };
- }).call(Editor.prototype);
- config.defineOptions(Editor.prototype, "editor", {
- selectionStyle: {
- set: function(style) {
- this.onSelectionChange();
- this._signal("changeSelectionStyle", {data: style});
- },
- initialValue: "line"
- },
- highlightActiveLine: {
- set: function() {this.$updateHighlightActiveLine();},
- initialValue: true
- },
- highlightSelectedWord: {
- set: function(shouldHighlight) {this.$onSelectionChange();},
- initialValue: true
- },
- readOnly: {
- set: function(readOnly) {
- // disabled to not break vim mode!
- // this.textInput.setReadOnly(readOnly);
- this.$resetCursorStyle();
- },
- initialValue: false
- },
- cursorStyle: {
- set: function(val) { this.$resetCursorStyle(); },
- values: ["ace", "slim", "smooth", "wide"],
- initialValue: "ace"
- },
- mergeUndoDeltas: {
- values: [false, true, "always"],
- initialValue: true
- },
- behavioursEnabled: {initialValue: true},
- wrapBehavioursEnabled: {initialValue: true},
- autoScrollEditorIntoView: {
- set: function(val) {this.setAutoScrollEditorIntoView(val)}
- },
- keyboardHandler: {
- set: function(val) { this.setKeyboardHandler(val); },
- get: function() { return this.keybindingId; },
- handlesSet: true
- },
- hScrollBarAlwaysVisible: "renderer",
- vScrollBarAlwaysVisible: "renderer",
- highlightGutterLine: "renderer",
- animatedScroll: "renderer",
- showInvisibles: "renderer",
- showPrintMargin: "renderer",
- printMarginColumn: "renderer",
- printMargin: "renderer",
- fadeFoldWidgets: "renderer",
- showFoldWidgets: "renderer",
- showLineNumbers: "renderer",
- showGutter: "renderer",
- displayIndentGuides: "renderer",
- fontSize: "renderer",
- fontFamily: "renderer",
- maxLines: "renderer",
- minLines: "renderer",
- scrollPastEnd: "renderer",
- fixedWidthGutter: "renderer",
- theme: "renderer",
- scrollSpeed: "$mouseHandler",
- dragDelay: "$mouseHandler",
- dragEnabled: "$mouseHandler",
- focusTimout: "$mouseHandler",
- tooltipFollowsMouse: "$mouseHandler",
- firstLineNumber: "session",
- overwrite: "session",
- newLineMode: "session",
- useWorker: "session",
- useSoftTabs: "session",
- tabSize: "session",
- wrap: "session",
- indentedSoftWrap: "session",
- foldStyle: "session",
- mode: "session"
- });
- exports.Editor = Editor;
- });
|