From ac88e1bf21d8fa04a7fb3d908ef0151134e0f428 Mon Sep 17 00:00:00 2001 From: ficristo Date: Sat, 1 Apr 2017 18:26:42 +0200 Subject: [PATCH] Add indentBlockComment preference. Support indent block comments on line comment command --- src/editor/Editor.js | 66 ++++++--- src/editor/EditorCommandHandlers.js | 56 +++++++- src/nls/root/strings.js | 1 + test/spec/EditorCommandHandlers-test.js | 178 +++++++++++++++++++++++- 4 files changed, 273 insertions(+), 28 deletions(-) diff --git a/src/editor/Editor.js b/src/editor/Editor.js index 0596833d3dc..28d6bc1f56b 100644 --- a/src/editor/Editor.js +++ b/src/editor/Editor.js @@ -80,23 +80,24 @@ define(function (require, exports, module) { _ = require("thirdparty/lodash"); /** Editor preferences */ - var CLOSE_BRACKETS = "closeBrackets", - CLOSE_TAGS = "closeTags", - DRAG_DROP = "dragDropText", - HIGHLIGHT_MATCHES = "highlightMatches", - LINEWISE_COPY_CUT = "lineWiseCopyCut", - SCROLL_PAST_END = "scrollPastEnd", - SHOW_CURSOR_SELECT = "showCursorWhenSelecting", - SHOW_LINE_NUMBERS = "showLineNumbers", - SMART_INDENT = "smartIndent", - SOFT_TABS = "softTabs", - SPACE_UNITS = "spaceUnits", - STYLE_ACTIVE_LINE = "styleActiveLine", - TAB_SIZE = "tabSize", - UPPERCASE_COLORS = "uppercaseColors", - USE_TAB_CHAR = "useTabChar", - WORD_WRAP = "wordWrap", - INDENT_LINE_COMMENT = "indentLineComment"; + var CLOSE_BRACKETS = "closeBrackets", + CLOSE_TAGS = "closeTags", + DRAG_DROP = "dragDropText", + HIGHLIGHT_MATCHES = "highlightMatches", + LINEWISE_COPY_CUT = "lineWiseCopyCut", + SCROLL_PAST_END = "scrollPastEnd", + SHOW_CURSOR_SELECT = "showCursorWhenSelecting", + SHOW_LINE_NUMBERS = "showLineNumbers", + SMART_INDENT = "smartIndent", + SOFT_TABS = "softTabs", + SPACE_UNITS = "spaceUnits", + STYLE_ACTIVE_LINE = "styleActiveLine", + TAB_SIZE = "tabSize", + UPPERCASE_COLORS = "uppercaseColors", + USE_TAB_CHAR = "useTabChar", + WORD_WRAP = "wordWrap", + INDENT_LINE_COMMENT = "indentLineComment", + INDENT_BLOCK_COMMENT = "indentBlockComment"; /** * A list of gutter name and priorities currently registered for editors. @@ -228,6 +229,10 @@ define(function (require, exports, module) { description: Strings.DESCRIPTION_INDENT_LINE_COMMENT }); + PreferencesManager.definePreference(INDENT_BLOCK_COMMENT, "boolean", false, { + description: Strings.DESCRIPTION_INDENT_BLOCK_COMMENT + }); + var editorOptions = Object.keys(cmOptions); /** Editor preferences */ @@ -2682,8 +2687,8 @@ define(function (require, exports, module) { }; /** - * Sets lineCommentIndent option. - * + * Sets indentLineComment option. + * Affects any editors that share the same preference location. * @param {boolean} value * @param {string=} fullPath Path to file to get preference for * @return {boolean} true if value was valid @@ -2694,7 +2699,7 @@ define(function (require, exports, module) { }; /** - * Returns true if word wrap is enabled for the specified or current file + * Returns true if indentLineComment is enabled for the specified or current file * @param {string=} fullPath Path to file to get preference for * @return {boolean} */ @@ -2702,6 +2707,27 @@ define(function (require, exports, module) { return PreferencesManager.get(INDENT_LINE_COMMENT, _buildPreferencesContext(fullPath)); }; + /** + * Returns true if blockCommentIndent is enabled for the specified or current file + * Affects any editors that share the same preference location. + * @param {boolean} value + * @param {string=} fullPath Path to file to get preference for + * @return {boolean} true if value was valid + */ + Editor.setIndentBlockComment = function (value, fullPath) { + var options = fullPath && {context: fullPath}; + return PreferencesManager.set(INDENT_BLOCK_COMMENT, value, options); + }; + + /** + * Returns true if indentBlockComment is enabled for the specified or current file + * @param {string=} fullPath Path to file to get preference for + * @return {boolean} + */ + Editor.getIndentBlockComment = function (fullPath) { + return PreferencesManager.get(INDENT_BLOCK_COMMENT, _buildPreferencesContext(fullPath)); + }; + /** * Runs callback for every Editor instance that currently exists * @param {!function(!Editor)} callback diff --git a/src/editor/EditorCommandHandlers.js b/src/editor/EditorCommandHandlers.js index 3a0dcf7c457..4eda5c9fdaa 100644 --- a/src/editor/EditorCommandHandlers.js +++ b/src/editor/EditorCommandHandlers.js @@ -297,6 +297,22 @@ define(function (require, exports, module) { return false; } + /** + * Return the column of the first non whitespace char in the given line. + * + * @private + * @param {!Document} doc + * @param {number} lineNum + * @returns {number} the column index or null + */ + function _firstNotWs(doc, lineNum) { + var text = doc.getLine(lineNum); + if (text === null || text === undefined) { + return null; + } + + return text.search(/\S|$/); + } /** * Generates an edit that adds or removes block-comment tokens to the selection, preserving selection @@ -345,6 +361,8 @@ define(function (require, exports, module) { var searchCtx, atSuffix, suffixEnd, initialPos, endLine; + var indentBlockComment = Editor.getIndentBlockComment(); + if (!selectionsToTrack) { // Track the original selection. selectionsToTrack = [_.cloneDeep(sel)]; @@ -467,12 +485,29 @@ define(function (require, exports, module) { // Comment out - add the suffix to the start and the prefix to the end. if (canComment) { var completeLineSel = sel.start.ch === 0 && sel.end.ch === 0 && sel.start.line < sel.end.line; + var startCh = _firstNotWs(doc, sel.start.line); if (completeLineSel) { - editGroup.push({text: suffix + "\n", start: sel.end}); - editGroup.push({text: prefix + "\n", start: sel.start}); + if (indentBlockComment) { + var endCh = _firstNotWs(doc, sel.end.line - 1); + editGroup.push({ + text: _.repeat(" ", endCh) + suffix + "\n", + start: {line: sel.end.line, ch: 0} + }); + editGroup.push({ + text: prefix + "\n" + _.repeat(" ", startCh), + start: {line: sel.start.line, ch: startCh} + }); + } else { + editGroup.push({text: suffix + "\n", start: sel.end}); + editGroup.push({text: prefix + "\n", start: sel.start}); + } } else { editGroup.push({text: suffix, start: sel.end}); - editGroup.push({text: prefix, start: sel.start}); + if (indentBlockComment) { + editGroup.push({text: prefix, start: { line: sel.start.line, ch: startCh }}); + } else { + editGroup.push({text: prefix, start: sel.start}); + } } // Correct the tracked selections. We can't just use the default selection fixup, @@ -497,7 +532,7 @@ define(function (require, exports, module) { if (completeLineSel) { // Just move the line down. pos.line++; - } else if (pos.line === sel.start.line) { + } else if (!indentBlockComment && pos.line === sel.start.line) { pos.ch += prefix.length; } } @@ -513,16 +548,21 @@ define(function (require, exports, module) { // If both are found we assume that a complete line selection comment added new lines, so we remove them. var line = doc.getLine(prefixPos.line).trim(), prefixAtStart = prefixPos.ch === 0 && prefix.length === line.length, - suffixAtStart = false; + prefixIndented = indentBlockComment && prefix.length === line.length, + suffixAtStart = false, + suffixIndented = false; if (suffixPos) { line = doc.getLine(suffixPos.line).trim(); suffixAtStart = suffixPos.ch === 0 && suffix.length === line.length; + suffixIndented = indentBlockComment && suffix.length === line.length; } // Remove the suffix if there is one if (suffixPos) { - if (prefixAtStart && suffixAtStart) { + if (prefixIndented) { + editGroup.push({text: "", start: {line: suffixPos.line, ch: 0}, end: {line: suffixPos.line + 1, ch: 0}}); + } else if (prefixAtStart && suffixAtStart) { editGroup.push({text: "", start: suffixPos, end: {line: suffixPos.line + 1, ch: 0}}); } else { editGroup.push({text: "", start: suffixPos, end: {line: suffixPos.line, ch: suffixPos.ch + suffix.length}}); @@ -530,7 +570,9 @@ define(function (require, exports, module) { } // Remove the prefix - if (prefixAtStart && suffixAtStart) { + if (suffixIndented) { + editGroup.push({text: "", start: {line: prefixPos.line, ch: 0}, end: {line: prefixPos.line + 1, ch: 0}}); + } else if (prefixAtStart && suffixAtStart) { editGroup.push({text: "", start: prefixPos, end: {line: prefixPos.line + 1, ch: 0}}); } else { editGroup.push({text: "", start: prefixPos, end: {line: prefixPos.line, ch: prefixPos.ch + prefix.length}}); diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 036b400b9bc..ee06aaf474f 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -795,6 +795,7 @@ define({ "DEFAULT_PREFERENCES_JSON_DEFAULT" : "Default", "DESCRIPTION_PURE_CODING_SURFACE" : "true to enable code only mode and hide all other UI elements in {APP_NAME}", "DESCRIPTION_INDENT_LINE_COMMENT" : "true to enable indenting of line comments", + "DESCRIPTION_INDENT_BLOCK_COMMENT" : "true to enable indenting of block comments", "DESCRIPTION_RECENT_FILES_NAV" : "Enable/disable navigation in recent files", "DESCRIPTION_LIVEDEV_WEBSOCKET_PORT" : "Port on which WebSocket Server runs for Live Preview" }); diff --git a/test/spec/EditorCommandHandlers-test.js b/test/spec/EditorCommandHandlers-test.js index 52737ced36b..204f45e2054 100644 --- a/test/spec/EditorCommandHandlers-test.js +++ b/test/spec/EditorCommandHandlers-test.js @@ -37,7 +37,8 @@ define(function (require, exports, module) { require("editor/EditorCommandHandlers"); - var shouldIndentLineComment = Editor.getIndentLineComment(); + var shouldIndentLineComment = Editor.getIndentLineComment(), + shouldIndentBlockComment = Editor.getIndentBlockComment(); describe("EditorCommandHandlers", function () { @@ -935,6 +936,181 @@ define(function (require, exports, module) { }); }); + describe("Line comment/uncomment in languages with only block comments and with indentBlockComment enabled", function () { + var htmlContent = "\n" + + " \n" + + "

Hello

\n" + + " \n" + + ""; + + beforeEach(function () { + setupFullEditor(htmlContent, "html"); + PreferencesManager.set("indentBlockComment", true); + }); + + afterEach(function () { + PreferencesManager.set("indentBlockComment", shouldIndentBlockComment); + }); + + it("should comment/uncomment a single line, cursor at start", function () { + myEditor.setCursorPos(2, 0); + + var lines = htmlContent.split("\n"); + lines[2] = " "; + var expectedText = lines.join("\n"); + + testToggleLine(expectedText, {line: 2, ch: 0}); + }); + + it("should comment/uncomment a block", function () { + myEditor.setSelection({line: 1, ch: 7}, {line: 3, ch: 7}); + + CommandManager.execute(Commands.EDIT_LINE_COMMENT, myEditor); + + var expectedText = "\n" + + " \n" + + ""; + + expect(myDocument.getText()).toEqual(expectedText); + expectSelection({start: {line: 2, ch: 7}, end: {line: 4, ch: 7}}); + }); + + it("should comment/uncomment a block with not closing tag ", function () { + myEditor.setSelection({line: 1, ch: 7}, {line: 2, ch: 7}); + + CommandManager.execute(Commands.EDIT_LINE_COMMENT, myEditor); + + var expectedText = "\n" + + " \n" + + " \n" + + ""; + + expect(myDocument.getText()).toEqual(expectedText); + expectSelection({start: {line: 2, ch: 7}, end: {line: 3, ch: 7}}); + }); + + it("should comment/uncomment a block with not closing tag at end of file", function () { + myEditor.setSelection({line: 3, ch: 9}, {line: 4, ch: 5}); + + CommandManager.execute(Commands.EDIT_LINE_COMMENT, myEditor); + + var expectedText = "\n" + + " \n" + + "

Hello

\n" + + " \n"; + + expect(myDocument.getText()).toEqual(expectedText); + expectSelection({start: {line: 4, ch: 9}, end: {line: 5, ch: 5}}); + }); + }); + + describe("Comment/uncomment with mixed syntax modes with indentLineComment and indentBlockComment enabled", function () { + var htmlContent = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "

Hello

\n" + + "

World

\n" + + " \n" + + ""; + + beforeEach(function () { + setupFullEditor(htmlContent, "html"); + PreferencesManager.set("indentLineComment", true); + PreferencesManager.set("indentBlockComment", true); + }); + + afterEach(function () { + PreferencesManager.set("indentLineComment", shouldIndentLineComment); + PreferencesManager.set("indentBlockComment", shouldIndentBlockComment); + }); + + it("should line comment/uncomment generic JS, CSS and HTML code with multiple cursors at start of line", function () { + myEditor.setSelections([ + {start: {line: 4, ch: 0}, end: {line: 4, ch: 0}}, + {start: {line: 10, ch: 0}, end: {line: 10, ch: 0}}, + {start: {line: 16, ch: 0}, end: {line: 16, ch: 0}} + ]); + + CommandManager.execute(Commands.EDIT_LINE_COMMENT, myEditor); + + var lines = htmlContent.split("\n"); + lines[4] = " /*font-size: 15px;*/"; + lines[10] = " //a();"; + lines[16] = " "; + var expectedText = lines.join("\n"); + + expect(myDocument.getText()).toEqual(expectedText); + expectSelections([ + {start: {line: 4, ch: 0}, end: {line: 4, ch: 0}, reversed: false, primary: false}, + {start: {line: 10, ch: 0}, end: {line: 10, ch: 0}, reversed: false, primary: false}, + {start: {line: 16, ch: 0}, end: {line: 16, ch: 0}, reversed: false, primary: true} + ]); + + // Uncomment + CommandManager.execute(Commands.EDIT_LINE_COMMENT, myEditor); + expect(myDocument.getText()).toEqual(htmlContent); + expectSelections([ + {start: {line: 4, ch: 0}, end: {line: 4, ch: 0}, reversed: false, primary: false}, + {start: {line: 10, ch: 0}, end: {line: 10, ch: 0}, reversed: false, primary: false}, + {start: {line: 16, ch: 0}, end: {line: 16, ch: 0}, reversed: false, primary: true} + ]); + }); + + it("should line comment/uncomment generic JS, CSS and HTML code with multiple cursors at end of line", function () { + myEditor.setSelections([ + {start: {line: 4, ch: 32}, end: {line: 4, ch: 32}}, + {start: {line: 10, ch: 24}, end: {line: 10, ch: 24}}, + {start: {line: 16, ch: 20}, end: {line: 16, ch: 20}} + ]); + + CommandManager.execute(Commands.EDIT_LINE_COMMENT, myEditor); + + var lines = htmlContent.split("\n"); + lines[4] = " /*font-size: 15px;*/"; + lines[10] = " //a();"; + lines[16] = " "; + var expectedText = lines.join("\n"); + + expect(myDocument.getText()).toEqual(expectedText); + expectSelections([ + {start: {line: 4, ch: 32}, end: {line: 4, ch: 32}, reversed: false, primary: false}, + {start: {line: 10, ch: 26}, end: {line: 10, ch: 26}, reversed: false, primary: false}, + {start: {line: 16, ch: 20}, end: {line: 16, ch: 20}, reversed: false, primary: true} + ]); + + // Uncomment + CommandManager.execute(Commands.EDIT_LINE_COMMENT, myEditor); + expect(myDocument.getText()).toEqual(htmlContent); + expectSelections([ + {start: {line: 4, ch: 30}, end: {line: 4, ch: 30}, reversed: false, primary: false}, + {start: {line: 10, ch: 24}, end: {line: 10, ch: 24}, reversed: false, primary: false}, + {start: {line: 16, ch: 16}, end: {line: 16, ch: 16}, reversed: false, primary: true} + ]); + }); + }); + describe("Line comment in languages with mutiple line comment prefixes", function () { // Define a special version of JavaScript for testing purposes LanguageManager.defineLanguage("javascript2", {