From 3876ffc671c2ad6d1850747d11fc04f307567077 Mon Sep 17 00:00:00 2001 From: tophf Date: Wed, 29 Mar 2017 08:30:14 +0300 Subject: [PATCH] Preserve selection/cursor after beautify-css --- beautify/beautify-css-mod.js | 106 ++++++++++++++++++++++++++++------- edit.js | 9 +++ 2 files changed, 96 insertions(+), 19 deletions(-) diff --git a/beautify/beautify-css-mod.js b/beautify/beautify-css-mod.js index f30ae8ff..d23ea125 100644 --- a/beautify/beautify-css-mod.js +++ b/beautify/beautify-css-mod.js @@ -114,6 +114,12 @@ var newline_before_open_brace = defaultOption(options.newline_before_open_brace, false); var newline_after_open_brace = defaultOption(options.newline_after_open_brace, true); var newline_before_close_brace = defaultOption(options.newline_before_close_brace, true); + var translatePos = (options.translate_positions || [])[0]; + var translatePosIndex = 0; + var translatePosLine = translatePos && translatePos.line; + var translatePosCol = translatePos && translatePos.ch; + var inputPosLine = 0, inputPosCol = 0; + var outputPosLine = 0, outputPosCol = 0; /* STYLUS: hack end */ if (options.indent_with_tabs) { @@ -140,20 +146,46 @@ ch; var parenLevel = 0; - function next() { + function next(resetLine, resetCol) { + if (resetLine !== undefined) { + inputPosLine = resetLine; + inputPosCol = resetCol; + if (inputPosCol < 0) { + inputPosLine--; + inputPosCol = pos - source_text.lastIndexOf('\n', pos); + } + } ch = source_text.charAt(++pos); + if (translatePos) { + inputPosCol++; + if (ch == '\n') { + inputPosLine++; + inputPosCol = 0; + } + if (inputPosLine == translatePosLine && inputPosCol >= translatePosCol + || inputPosLine > translatePosLine) { + translatePos.line = outputPosLine - (inputPosLine - translatePosLine); + translatePos.ch = outputPosCol - (inputPosCol - translatePosCol); + translatePos.ch += translatePos.ch ? 1 : 0; + translatePos = options.translate_positions[++translatePosIndex]; + translatePosLine = translatePos && translatePos.line; + translatePosCol = translatePos && translatePos.ch; + } + } return ch || ''; } function peek(skipWhitespace) { var result = ''; var prev_pos = pos; + var prevInputPosLine = inputPosLine; + var prevInputPosCol = inputPosCol; if (skipWhitespace) { eatWhitespace(); } result = source_text.charAt(pos + 1) || ''; pos = prev_pos - 1; - next(); + next(prevInputPosLine, prevInputPosCol - 1); return result; } @@ -173,9 +205,11 @@ function peekString(endChar) { var prev_pos = pos; + var prevInputPosLine = inputPosLine; + var prevInputPosCol = inputPosCol; var str = eatString(endChar); pos = prev_pos - 1; - next(); + next(prevInputPosLine, prevInputPosCol - 1); return str; } @@ -267,23 +301,19 @@ var print = {}; print["{"] = function(ch) { - /* STYLUS: hack start */ newline_before_open_brace ? print.newLine() : print.singleSpace(); - /* STYLUS: hack end */ output.push(ch); + outputPosCol++; if (!eatWhitespace(true)) { - /* STYLUS: hack start */ newline_after_open_brace ? print.newLine() : print.singleSpace(); - /* STYLUS: hack end */ } }; print["}"] = function(newline) { if (newline) { - /* STYLUS: hack start */ newline_before_close_brace ? print.newLine() : (print.trim(), print.singleSpace()); - /* STYLUS: hack end */ } output.push('}'); + outputPosCol++; if (!eatWhitespace(true)) { print.newLine(); } @@ -299,17 +329,22 @@ print.trim(); } else if (output[output.length - 1] === basebaseIndentString) { output.pop(); + outputPosCol -= basebaseIndentString.length; } output.push('\n'); + outputPosLine++; + outputPosCol = 0; if (basebaseIndentString) { output.push(basebaseIndentString); + outputPosCol += basebaseIndentString.length; } } }; print.singleSpace = function() { if (output.length && !print._lastCharWhitespace()) { output.push(' '); + outputPosCol++; } }; @@ -321,7 +356,25 @@ print.trim = function() { while (print._lastCharWhitespace()) { - output.pop(); + const text = output.pop(); + if (text.indexOf('\n') >= 0) { + outputPosLine -= text.match(/\n/g).length; + } + } + outputPosCol = 0; + let i = output.length, token; + while (--i >= 0 && (token = output[i]) != '\n') { + outputPosCol += token.length; + } + }; + + print.text = function(text) { + output.push(text); + if (text.indexOf('\n') < 0) { + outputPosCol += text.length; + } else { + outputPosLine += text.match(/\n/g).length; + outputPosCol = text.length - text.lastIndexOf('\n') - 1; } }; @@ -351,7 +404,7 @@ print.newLine(); } - output.push(eatComment()); + print.text(eatComment()); print.newLine(); if (header) { print.newLine(true); @@ -361,16 +414,17 @@ print.trim(); } print.singleSpace(); - output.push(eatComment()); + print.text(eatComment()); print.newLine(); } else if (ch === '@') { print.preserveSingleSpace(); // deal with less propery mixins @{...} if (peek() === '{') { - output.push(eatString('}')); + print.text(eatString('}')); } else { output.push(ch); + outputPosCol++; // strip trailing space, if present, for hash property checks var variableOrRule = peekString(": ,;{}()[]/='\""); @@ -379,7 +433,7 @@ // we have a variable or pseudo-class, add it and insert one space before continuing next(); variableOrRule = eatString(": ").replace(/\s$/, ''); - output.push(variableOrRule); + print.text(variableOrRule); print.singleSpace(); } @@ -395,13 +449,14 @@ } } else if (ch === '#' && peek() === '{') { print.preserveSingleSpace(); - output.push(eatString('}')); + print.text(eatString('}')); } else if (ch === '{') { if (peek(true) === '}') { eatWhitespace(); next(); print.singleSpace(); output.push("{"); + outputPosCol++; print['}'](false); if (newlinesFromLastWSEat < 2 && newline_between_rules && indentLevel === 0) { print.newLine(true); @@ -437,6 +492,7 @@ // 'property: value' delimiter // which could be in a conditional group query output.push(':'); + outputPosCol++; if (!insidePropertyValue) { insidePropertyValue = true; print.singleSpace(); @@ -448,34 +504,37 @@ // preserve space before pseudoclasses/pseudoelements, as it means "in any child" if (lookBack(" ") && output[output.length - 1] !== " ") { output.push(" "); + outputPosCol++; } if (peek() === ":") { // pseudo-element next(); output.push("::"); + outputPosCol += 2; } else { // pseudo-class output.push(':'); + outputPosCol++; } } } else if (ch === '"' || ch === '\'') { print.preserveSingleSpace(); - output.push(eatString(ch)); + print.text(eatString(ch)); } else if (ch === ';') { insidePropertyValue = false; output.push(ch); + outputPosCol++; if (!eatWhitespace(true)) { - /* STYLUS: hack start */ newline_between_properties ? print.newLine() : print.singleSpace(); - /* STYLUS: hack end */ } } else if (ch === '(') { // may be a url if (lookBack("url")) { output.push(ch); + outputPosCol++; eatWhitespace(); if (next()) { if (ch !== ')' && ch !== '"' && ch !== '\'') { - output.push(eatString(')')); + print.text(eatString(')')); } else { pos--; } @@ -484,13 +543,16 @@ parenLevel++; print.preserveSingleSpace(); output.push(ch); + outputPosCol++; eatWhitespace(); } } else if (ch === ')') { output.push(ch); + outputPosCol++; parenLevel--; } else if (ch === ',') { output.push(ch); + outputPosCol++; if (!eatWhitespace(true) && selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) { print.newLine(); } else { @@ -502,9 +564,11 @@ if (space_around_combinator) { print.singleSpace(); output.push(ch); + outputPosCol++; print.singleSpace(); } else { output.push(ch); + outputPosCol++; eatWhitespace(); // squash extra whitespace if (ch && whiteRe.test(ch)) { @@ -513,18 +577,22 @@ } } else if (ch === ']') { output.push(ch); + outputPosCol++; } else if (ch === '[') { print.preserveSingleSpace(); output.push(ch); + outputPosCol++; } else if (ch === '=') { // no whitespace before or after eatWhitespace(); output.push('='); + outputPosCol++; if (whiteRe.test(ch)) { ch = ''; } } else { print.preserveSingleSpace(); output.push(ch); + outputPosCol++; } } diff --git a/edit.js b/edit.js index 7d9fbc28..ffcffe7a 100644 --- a/edit.js +++ b/edit.js @@ -1049,6 +1049,7 @@ function beautify(event) { if (cm.beautifyChange && cm.beautifyChange[cm.changeGeneration()]) { delete cm.beautifyChange[cm.changeGeneration()]; cm.undo(); + cm.scrollIntoView(cm.getCursor()); undoable |= cm.beautifyChange[cm.changeGeneration()]; } }); @@ -1057,6 +1058,9 @@ function beautify(event) { scope.forEach(function(cm) { setTimeout(function() { + const pos = options.translate_positions = + [].concat.apply([], cm.doc.sel.ranges.map(r => + [Object.assign({}, r.anchor), Object.assign({}, r.head)])); var text = cm.getValue(); var newText = exports.css_beautify(text, options); if (newText != text) { @@ -1065,6 +1069,11 @@ function beautify(event) { cm.beautifyChange = {}; } cm.setValue(newText); + const selections = []; + for (let i = 0; i < pos.length; i += 2) { + selections.push({anchor: pos[i], head: pos[i + 1]}); + } + cm.setSelections(selections); cm.beautifyChange[cm.changeGeneration()] = true; undoButton.disabled = false; }