From 5deb06d5891ce9278b74c253fd85741757b25c82 Mon Sep 17 00:00:00 2001 From: Luiza Pagliari Date: Tue, 13 Oct 2015 18:39:23 -0300 Subject: [PATCH] Create setting to control if a new line will be indented or not Currently pressing ENTER on a line that ends with ':', '[', '(' or '{' automaticaly indents the new line with 4 spaces. The variable added by this commit to settings.json allow an Etherpad instance to not have this behavior. --- settings.json.template | 5 ++ src/node/handler/PadMessageHandler.js | 7 +- src/node/utils/Settings.js | 5 ++ src/static/js/ace2_inner.js | 40 +++++----- tests/frontend/specs/indentation.js | 102 +++++++++++++++++++++++--- 5 files changed, 128 insertions(+), 31 deletions(-) diff --git a/settings.json.template b/settings.json.template index bfd0c7e66..06d388d8e 100644 --- a/settings.json.template +++ b/settings.json.template @@ -131,6 +131,11 @@ // Allow Load Testing tools to hit the Etherpad Instance. Warning this will disable security on the instance. "loadTest": false, + // Disable indentation on new line when previous line ends with some special chars (':', '[', '(', '{') + /* + "indentationOnNewLine": false, + */ + /* The toolbar buttons configuration. "toolbar": { "left": [ diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index e72625d06..9481889f3 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -630,8 +630,8 @@ function handleUserChanges(data, cb) messageLogger.warn("Dropped message, USER_CHANGES Message has no changeset!"); return cb(); } - //TODO: this might happen with other messages too => find one place to copy the session - //and always use the copy. atm a message will be ignored if the session is gone even + //TODO: this might happen with other messages too => find one place to copy the session + //and always use the copy. atm a message will be ignored if the session is gone even //if the session was valid when the message arrived in the first place if(!sessioninfos[client.id]) { @@ -960,7 +960,7 @@ function handleSwitchToPad(client, message) roomClients[i].leave(padId); } } - + // start up the new pad createSessionInfo(client, message); handleClientReady(client, message); @@ -1231,6 +1231,7 @@ function handleClientReady(client, message) "plugins": plugins.plugins, "parts": plugins.parts, }, + "indentationOnNewLine": settings.indentationOnNewLine, "initialChangesets": [] // FIXME: REMOVE THIS SHIT } diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 2c2f90bf8..1a60860f2 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -177,6 +177,11 @@ exports.disableIPlogging = false; */ exports.loadTest = false; +/** + * Enable indentation on new lines + */ +exports.indentationOnNewLine = true; + /* * log4js appender configuration */ diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index d1657a7c4..bb85adcfa 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -1894,7 +1894,11 @@ function Ace2Inner(){ var prevLine = rep.lines.prev(thisLine); var prevLineText = prevLine.text; var theIndent = /^ *(?:)/.exec(prevLineText)[0]; - if (/[\[\(\:\{]\s*$/.exec(prevLineText)) theIndent += THE_TAB; + var shouldIndent = parent.parent.clientVars.indentationOnNewLine; + if (shouldIndent && /[\[\(\:\{]\s*$/.exec(prevLineText)) + { + theIndent += THE_TAB; + } var cs = Changeset.builder(rep.lines.totalWidth()).keep( rep.lines.offsetOfIndex(lineNum), lineNum).insert( theIndent, [ @@ -2336,7 +2340,7 @@ function Ace2Inner(){ function getAttributeOnSelection(attributeName){ if (!(rep.selStart && rep.selEnd)) return - + var withIt = Changeset.makeAttribsString('+', [ [attributeName, 'true'] ], rep.apool); @@ -2347,14 +2351,14 @@ function Ace2Inner(){ } return rangeHasAttrib(rep.selStart, rep.selEnd) - + function rangeHasAttrib(selStart, selEnd) { // if range is collapsed -> no attribs in range if(selStart[1] == selEnd[1] && selStart[0] == selEnd[0]) return false - + if(selStart[0] != selEnd[0]) { // -> More than one line selected var hasAttrib = true - + // from selStart to the end of the first line hasAttrib = hasAttrib && rangeHasAttrib(selStart, [selStart[0], rep.lines.atIndex(selStart[0]).text.length]) @@ -2365,22 +2369,22 @@ function Ace2Inner(){ // for the last, potentially partial, line hasAttrib = hasAttrib && rangeHasAttrib([selEnd[0], 0], [selEnd[0], selEnd[1]]) - + return hasAttrib } - + // Logic tells us we now have a range on a single line - + var lineNum = selStart[0] , start = selStart[1] , end = selEnd[1] , hasAttrib = true - + // Iterate over attribs on this line - + var opIter = Changeset.opIterator(rep.alines[lineNum]) , indexIntoLine = 0 - + while (opIter.hasNext()) { var op = opIter.next(); var opStartInLine = indexIntoLine; @@ -2394,11 +2398,11 @@ function Ace2Inner(){ } indexIntoLine = opEndInLine; } - + return hasAttrib } } - + editorInfo.ace_getAttributeOnSelection = getAttributeOnSelection; function toggleAttributeOnSelection(attributeName) @@ -3641,7 +3645,7 @@ function Ace2Inner(){ // Is caret potentially hidden by the chat button? var myselection = document.getSelection(); // get the current caret selection var caretOffsetTop = myselection.focusNode.parentNode.offsetTop | myselection.focusNode.offsetTop; // get the carets selection offset in px IE 214 - + if(myselection.focusNode.wholeText){ // Is there any content? If not lineHeight will report wrong.. var lineHeight = myselection.focusNode.parentNode.offsetHeight; // line height of populated links }else{ @@ -3713,13 +3717,13 @@ function Ace2Inner(){ // As ubuntu cannot use Alt F10.... // Focus on the editbar. -- TODO: Move Focus back to previous state (we know it so we can use it) var firstEditbarElement = parent.parent.$('#editbar').children("ul").first().children().first().children().first().children().first(); - $(this).blur(); + $(this).blur(); firstEditbarElement.focus(); evt.preventDefault(); } if ((!specialHandled) && altKey && keyCode == 67 && type === "keydown"){ // Alt c focuses on the Chat window - $(this).blur(); + $(this).blur(); parent.parent.chat.show(); parent.parent.$("#chatinput").focus(); evt.preventDefault(); @@ -4961,7 +4965,7 @@ function Ace2Inner(){ // Disabled: https://github.com/ether/etherpad-lite/issues/2546 // Will break OL re-numbering: https://github.com/ether/etherpad-lite/pull/2533 - // $(document).on("cut", handleCut); + // $(document).on("cut", handleCut); $(root).on("blur", handleBlur); if (browser.msie) @@ -4972,7 +4976,7 @@ function Ace2Inner(){ // Don't paste on middle click of links $(root).on("paste", function(e){ - // TODO: this breaks pasting strings into URLS when using + // TODO: this breaks pasting strings into URLS when using // Control C and Control V -- the Event is never available // here.. :( if(e.target.a || e.target.localName === "a"){ diff --git a/tests/frontend/specs/indentation.js b/tests/frontend/specs/indentation.js index 8e851d873..71aafc7ac 100644 --- a/tests/frontend/specs/indentation.js +++ b/tests/frontend/specs/indentation.js @@ -15,7 +15,7 @@ describe("indentation button", function(){ //select this text element $firstTextElement.sendkeys('{selectall}'); - if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE var evtType = "keypress"; }else{ var evtType = "keydown"; @@ -31,7 +31,7 @@ describe("indentation button", function(){ }); it("indent text with button", function(done){ - var inner$ = helper.padInner$; + var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; var $indentButton = chrome$(".buttonicon-indent"); @@ -43,7 +43,7 @@ describe("indentation button", function(){ }); it("keeps the indent on enter for the new line", function(done){ - var inner$ = helper.padInner$; + var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; var $indentButton = chrome$(".buttonicon-indent"); @@ -51,9 +51,9 @@ describe("indentation button", function(){ //type a bit, make a line break and type again var $firstTextElement = inner$("div span").first(); - $firstTextElement.sendkeys('line 1'); - $firstTextElement.sendkeys('{enter}'); - $firstTextElement.sendkeys('line 2'); + $firstTextElement.sendkeys('line 1'); + $firstTextElement.sendkeys('{enter}'); + $firstTextElement.sendkeys('line 2'); $firstTextElement.sendkeys('{enter}'); helper.waitFor(function(){ @@ -68,13 +68,83 @@ describe("indentation button", function(){ }); }); + it("indents text with spaces on enter if previous line ends with ':', '[', '(', or '{'", function(done){ + var inner$ = helper.padInner$; + var chrome$ = helper.padChrome$; + + //type a bit, make a line break and type again + var $firstTextElement = inner$("div").first(); + $firstTextElement.sendkeys("line with ':'{enter}"); + $firstTextElement.sendkeys("line with '['{enter}"); + $firstTextElement.sendkeys("line with '('{enter}"); + $firstTextElement.sendkeys("line with '{{}'{enter}"); + + helper.waitFor(function(){ + return inner$("div span").first().text().indexOf("line with '{'") === -1; + }).done(function(){ + // we validate bottom to top for easier implementation + + // curly braces + var $lineWithCurlyBraces = inner$("div").first().next().next().next(); + $lineWithCurlyBraces.sendkeys('{{}'); + pressEnter(); // cannot use sendkeys('{enter}') here, browser does not read the command properly + var $lineAfterCurlyBraces = inner$("div").first().next().next().next().next(); + expect($lineAfterCurlyBraces.text()).to.match(/\s{4}/); // tab === 4 spaces + + // parenthesis + var $lineWithParenthesis = inner$("div").first().next().next(); + $lineWithParenthesis.sendkeys('('); + pressEnter(); + var $lineAfterParenthesis = inner$("div").first().next().next().next(); + expect($lineAfterParenthesis.text()).to.match(/\s{4}/); + + // bracket + var $lineWithBracket = inner$("div").first().next(); + $lineWithBracket.sendkeys('['); + pressEnter(); + var $lineAfterBracket = inner$("div").first().next().next(); + expect($lineAfterBracket.text()).to.match(/\s{4}/); + + // colon + var $lineWithColon = inner$("div").first(); + $lineWithColon.sendkeys(':'); + pressEnter(); + var $lineAfterColon = inner$("div").first().next(); + expect($lineAfterColon.text()).to.match(/\s{4}/); + + done(); + }); + }); + + it("appends indentation to the indent of previous line if previous line ends with ':', '[', '(', or '{'", function(done){ + var inner$ = helper.padInner$; + var chrome$ = helper.padChrome$; + + //type a bit, make a line break and type again + var $firstTextElement = inner$("div").first(); + $firstTextElement.sendkeys(" line with some indentation and ':'{enter}"); + $firstTextElement.sendkeys("line 2{enter}"); + + helper.waitFor(function(){ + return inner$("div span").first().text().indexOf("line 2") === -1; + }).done(function(){ + var $lineWithColon = inner$("div").first(); + $lineWithColon.sendkeys(':'); + pressEnter(); + var $lineAfterColon = inner$("div").first().next(); + expect($lineAfterColon.text()).to.match(/\s{6}/); // previous line indentation + regular tab (4 spaces) + + done(); + }); + }); + /* it("makes text indented and outdented", function() { //get the inner iframe var $inner = testHelper.$getPadInner(); - + //get the first text element out of the inner iframe var firstTextElement = $inner.find("div").first(); @@ -87,7 +157,7 @@ describe("indentation button", function(){ //ace creates a new dom element when you press a button, so just get the first text element again var newFirstTextElement = $inner.find("div").first(); - + // is there a list-indent class element now? var firstChild = newFirstTextElement.children(":first"); var isUL = firstChild.is('ul'); @@ -160,12 +230,12 @@ describe("indentation button", function(){ /* this test creates the below content, both should have double indentation line1 line2 - + firstTextElement.sendkeys('{rightarrow}'); // simulate a keypress of enter firstTextElement.sendkeys('{enter}'); // simulate a keypress of enter firstTextElement.sendkeys('line 1'); // simulate writing the first line - firstTextElement.sendkeys('{enter}'); // simulate a keypress of enter + firstTextElement.sendkeys('{enter}'); // simulate a keypress of enter firstTextElement.sendkeys('line 2'); // simulate writing the second line //get the second text element out of the inner iframe @@ -203,3 +273,15 @@ describe("indentation button", function(){ });*/ }); + +function pressEnter(){ + var inner$ = helper.padInner$; + if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE + var evtType = "keypress"; + }else{ + var evtType = "keydown"; + } + var e = inner$.Event(evtType); + e.keyCode = 13; // enter :| + inner$("#innerdocbody").trigger(e); +}