diff --git a/src/extensions/default/CodeFolding/Prefs.js b/src/extensions/default/CodeFolding/Prefs.js deleted file mode 100644 index 9423a540006..00000000000 --- a/src/extensions/default/CodeFolding/Prefs.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Wrapper around brackets pref system to ensure preferences are stored in in one single object instead of using multiple keys. - * This is to make it easy for the user who edits their preferences file to easily manage the potentially numerous lines of preferences generated by the persisting code-folding state. - * @author Patrick Oladimeji - * @date 3/22/14 20:39:53 PM - */ - -define(function (require, exports, module) { - "use strict"; - - var ProjectManager = brackets.getModule("project/ProjectManager"), - PreferencesManager = brackets.getModule("preferences/PreferencesManager"), - Strings = brackets.getModule("strings"), - prefs = PreferencesManager.getExtensionPrefs("code-folding"), - FOLDS_PREF_KEY = "code-folding.folds", - // preference key strings are here for now since they are not used in any UI - ENABLE_CODE_FOLDING = "Enable code folding", - MIN_FOLD_SIZE = "Minimum fold size", - SAVE_FOLD_STATES = "Save fold states", - ALWAYS_USE_INDENT_FOLD = "Always use indent fold", - HIDE_FOLD_BUTTONS = "Hide fold triangles", - MAX_FOLD_LEVEL = "Max fold level", - MAKE_SELECTIONS_FOLDABLE = "Makes selections foldable"; - - //default preference values - prefs.definePreference("enabled", "boolean", true, - {name: ENABLE_CODE_FOLDING, description: Strings.DESCRIPTION_CODE_FOLDING_ENABLED}); - prefs.definePreference("minFoldSize", "number", 2, - {name: MIN_FOLD_SIZE, description: Strings.DESCRIPTION_CODE_FOLDING_MIN_FOLD_SIZE}); - prefs.definePreference("saveFoldStates", "boolean", true, - {name: SAVE_FOLD_STATES, description: Strings.DESCRIPTION_CODE_FOLDING_SAVE_FOLD_STATES}); - prefs.definePreference("alwaysUseIndentFold", "boolean", false, - {name: ALWAYS_USE_INDENT_FOLD, description: Strings.DESCRIPTION_CODE_FOLDING_ALWAY_USE_INDENT_FOLD}); - prefs.definePreference("hideUntilMouseover", "boolean", false, - {name: HIDE_FOLD_BUTTONS, description: Strings.DESCRIPTION_CODE_FOLDING_HIDE_UNTIL_MOUSEOVER}); - prefs.definePreference("maxFoldLevel", "number", 2, - {name: MAX_FOLD_LEVEL, description: Strings.DESCRIPTION_CODE_FOLDING_MAX_FOLD_LEVEL}); - prefs.definePreference("makeSelectionsFoldable", "boolean", true, - {name: MAKE_SELECTIONS_FOLDABLE, description: Strings.DESCRIPTION_CODE_FOLDING_MAKE_SELECTIONS_FOLDABLE}); - - PreferencesManager.stateManager.definePreference(FOLDS_PREF_KEY, "object", {}); - - /** - * Simplifies the fold ranges into an array of pairs of numbers. - * @param {!Object} folds the raw fold ranges indexed by line numbers - * @return {Object} an object whose keys are line numbers and the values are array - * of two 2-element arrays. First array contains [from.line, from.ch] and the second contains [to.line, to.ch] - */ - function simplify(folds) { - if (!folds) { - return; - } - var res = {}, range; - Object.keys(folds).forEach(function (line) { - range = folds[line]; - res[line] = Array.isArray(range) ? range : [[range.from.line, range.from.ch], [range.to.line, range.to.ch]]; - }); - return res; - } - - /** - * Inflates the fold ranges stored as simplified numeric arrays. The inflation converts the data into - * objects whose keys are line numbers and whose values are objects in the format {from: {line, ch}, to: {line, ch}}. - * @param {Object} folds the simplified fold ranges - * @return {Object} the converted fold ranges - */ - function inflate(folds) { - if (!folds) { - return; - } - //transform the folds into objects with from and to properties - var ranges = {}, obj; - Object.keys(folds).forEach(function (line) { - obj = folds[line]; - ranges[line] = {from: {line: obj[0][0], ch: obj[0][1]}, to: {line: obj[1][0], ch: obj[1][1]}}; - }); - - return ranges; - } - - /** - * Returns a 'context' object for getting/setting project-specific view state preferences. - * Similar to code in MultiRangeInlineEditor._getPrefsContext()... - */ - function getViewStateContext() { - var projectRoot = ProjectManager.getProjectRoot(); // note: null during unit tests! - return { location : { scope: "user", - layer: "project", - layerID: projectRoot && projectRoot.fullPath } }; - } - - /** - * Gets the line folds saved for the specified path. - * @param {string} path the document path - * @return {Object} the line folds for the document at the specified path - */ - function getFolds(path) { - var context = getViewStateContext(); - var folds = PreferencesManager.getViewState(FOLDS_PREF_KEY, context); - return inflate(folds[path]); - } - - /** - * Saves the line folds for the specified path - * @param {!string} path the path to the document - * @param {Object} folds the fold ranges to save for the current document - */ - function setFolds(path, folds) { - var context = getViewStateContext(); - var allFolds = PreferencesManager.getViewState(FOLDS_PREF_KEY, context); - allFolds[path] = simplify(folds); - PreferencesManager.setViewState(FOLDS_PREF_KEY, allFolds, context); - } - - /** - * Get the code folding setting with the specified key from the store - * @param {!string} key The key for the setting to retrieve - * @return {string} the setting with the specified key - */ - function getSetting(key) { - return prefs.get(key); - } - - /** - * Clears all the saved line folds for all documents. - */ - function clearAllFolds() { - PreferencesManager.setViewState(FOLDS_PREF_KEY, {}); - } - - module.exports.getFolds = getFolds; - module.exports.setFolds = setFolds; - module.exports.getSetting = getSetting; - module.exports.clearAllFolds = clearAllFolds; - module.exports.prefsObject = prefs; -}); diff --git a/src/extensions/default/CodeFolding/Prefs.ts b/src/extensions/default/CodeFolding/Prefs.ts new file mode 100644 index 00000000000..a87a938914b --- /dev/null +++ b/src/extensions/default/CodeFolding/Prefs.ts @@ -0,0 +1,128 @@ +/** + * Wrapper around brackets pref system to ensure preferences are stored in in one single object instead of using multiple keys. + * This is to make it easy for the user who edits their preferences file to easily manage the potentially numerous lines of preferences generated by the persisting code-folding state. + * @author Patrick Oladimeji + * @date 3/22/14 20:39:53 PM + */ + +const ProjectManager = brackets.getModule("project/ProjectManager"); +const PreferencesManager = brackets.getModule("preferences/PreferencesManager"); +const Strings = brackets.getModule("strings"); +const prefs = PreferencesManager.getExtensionPrefs("code-folding"); +const FOLDS_PREF_KEY = "code-folding.folds"; +// preference key strings are here for now since they are not used in any UI +const ENABLE_CODE_FOLDING = "Enable code folding"; +const MIN_FOLD_SIZE = "Minimum fold size"; +const SAVE_FOLD_STATES = "Save fold states"; +const ALWAYS_USE_INDENT_FOLD = "Always use indent fold"; +const HIDE_FOLD_BUTTONS = "Hide fold triangles"; +const MAX_FOLD_LEVEL = "Max fold level"; +const MAKE_SELECTIONS_FOLDABLE = "Makes selections foldable"; + +// default preference values +prefs.definePreference("enabled", "boolean", true, + {name: ENABLE_CODE_FOLDING, description: Strings.DESCRIPTION_CODE_FOLDING_ENABLED}); +prefs.definePreference("minFoldSize", "number", 2, + {name: MIN_FOLD_SIZE, description: Strings.DESCRIPTION_CODE_FOLDING_MIN_FOLD_SIZE}); +prefs.definePreference("saveFoldStates", "boolean", true, + {name: SAVE_FOLD_STATES, description: Strings.DESCRIPTION_CODE_FOLDING_SAVE_FOLD_STATES}); +prefs.definePreference("alwaysUseIndentFold", "boolean", false, + {name: ALWAYS_USE_INDENT_FOLD, description: Strings.DESCRIPTION_CODE_FOLDING_ALWAY_USE_INDENT_FOLD}); +prefs.definePreference("hideUntilMouseover", "boolean", false, + {name: HIDE_FOLD_BUTTONS, description: Strings.DESCRIPTION_CODE_FOLDING_HIDE_UNTIL_MOUSEOVER}); +prefs.definePreference("maxFoldLevel", "number", 2, + {name: MAX_FOLD_LEVEL, description: Strings.DESCRIPTION_CODE_FOLDING_MAX_FOLD_LEVEL}); +prefs.definePreference("makeSelectionsFoldable", "boolean", true, + {name: MAKE_SELECTIONS_FOLDABLE, description: Strings.DESCRIPTION_CODE_FOLDING_MAKE_SELECTIONS_FOLDABLE}); + +PreferencesManager.stateManager.definePreference(FOLDS_PREF_KEY, "object", {}); + +/** + * Simplifies the fold ranges into an array of pairs of numbers. + * @param {!Object} folds the raw fold ranges indexed by line numbers + * @return {Object} an object whose keys are line numbers and the values are array + * of two 2-element arrays. First array contains [from.line, from.ch] and the second contains [to.line, to.ch] + */ +function simplify(folds) { + if (!folds) { + return; + } + const res = {}; + Object.keys(folds).forEach(function (line) { + const range = folds[line]; + res[line] = Array.isArray(range) ? range : [[range.from.line, range.from.ch], [range.to.line, range.to.ch]]; + }); + return res; +} + +/** + * Inflates the fold ranges stored as simplified numeric arrays. The inflation converts the data into + * objects whose keys are line numbers and whose values are objects in the format {from: {line, ch}, to: {line, ch}}. + * @param {Object} folds the simplified fold ranges + * @return {Object} the converted fold ranges + */ +function inflate(folds) { + if (!folds) { + return; + } + // transform the folds into objects with from and to properties + const ranges = {}; + Object.keys(folds).forEach(function (line) { + const obj = folds[line]; + ranges[line] = {from: {line: obj[0][0], ch: obj[0][1]}, to: {line: obj[1][0], ch: obj[1][1]}}; + }); + + return ranges; +} + +/** + * Returns a 'context' object for getting/setting project-specific view state preferences. + * Similar to code in MultiRangeInlineEditor._getPrefsContext()... + */ +function getViewStateContext() { + const projectRoot = ProjectManager.getProjectRoot(); // note: null during unit tests! + return { location : { scope: "user", + layer: "project", + layerID: projectRoot && projectRoot.fullPath } }; +} + +/** + * Gets the line folds saved for the specified path. + * @param {string} path the document path + * @return {Object} the line folds for the document at the specified path + */ +export function getFolds(path) { + const context = getViewStateContext(); + const folds = PreferencesManager.getViewState(FOLDS_PREF_KEY, context); + return inflate(folds[path]); +} + +/** + * Saves the line folds for the specified path + * @param {!string} path the path to the document + * @param {Object} folds the fold ranges to save for the current document + */ +export function setFolds(path, folds) { + const context = getViewStateContext(); + const allFolds = PreferencesManager.getViewState(FOLDS_PREF_KEY, context); + allFolds[path] = simplify(folds); + PreferencesManager.setViewState(FOLDS_PREF_KEY, allFolds, context); +} + +/** + * Get the code folding setting with the specified key from the store + * @param {!string} key The key for the setting to retrieve + * @return {string} the setting with the specified key + */ +export function getSetting(key) { + return prefs.get(key); +} + +/** + * Clears all the saved line folds for all documents. + */ +export function clearAllFolds() { + PreferencesManager.setViewState(FOLDS_PREF_KEY, {}); +} + +export const prefsObject = prefs; diff --git a/src/extensions/default/CodeFolding/main.js b/src/extensions/default/CodeFolding/main.js deleted file mode 100644 index 627a3d2433d..00000000000 --- a/src/extensions/default/CodeFolding/main.js +++ /dev/null @@ -1,466 +0,0 @@ -/* -* Copyright (c) 2013 Patrick Oladimeji. All rights reserved. -* -* Permission is hereby granted, free of charge, to any person obtaining a -* copy of this software and associated documentation files (the "Software"), -* to deal in the Software without restriction, including without limitation -* the rights to use, copy, modify, merge, publish, distribute, sublicense, -* and/or sell copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -* DEALINGS IN THE SOFTWARE. -* -*/ -/** - * Code folding extension for brackets - * @author Patrick Oladimeji - * @date 10/24/13 9:35:26 AM - */ - -define(function (require, exports, module) { - "use strict"; - - var CodeMirror = brackets.getModule("thirdparty/CodeMirror/lib/codemirror"), - Strings = brackets.getModule("strings"), - AppInit = brackets.getModule("utils/AppInit"), - CommandManager = brackets.getModule("command/CommandManager"), - DocumentManager = brackets.getModule("document/DocumentManager"), - Editor = brackets.getModule("editor/Editor").Editor, - EditorManager = brackets.getModule("editor/EditorManager"), - ProjectManager = brackets.getModule("project/ProjectManager"), - ViewStateManager = brackets.getModule("view/ViewStateManager"), - KeyBindingManager = brackets.getModule("command/KeyBindingManager"), - ExtensionUtils = brackets.getModule("utils/ExtensionUtils"), - Menus = brackets.getModule("command/Menus"), - prefs = require("Prefs"), - COLLAPSE_ALL = "codefolding.collapse.all", - COLLAPSE = "codefolding.collapse", - EXPAND = "codefolding.expand", - EXPAND_ALL = "codefolding.expand.all", - GUTTER_NAME = "CodeMirror-foldgutter", - CODE_FOLDING_GUTTER_PRIORITY = Editor.CODE_FOLDING_GUTTER_PRIORITY, - codeFoldingMenuDivider = "codefolding.divider", - collapseKey = "Ctrl-Alt-[", - expandKey = "Ctrl-Alt-]", - collapseAllKey = "Alt-1", - expandAllKey = "Shift-Alt-1", - collapseAllKeyMac = "Cmd-1", - expandAllKeyMac = "Cmd-Shift-1"; - - ExtensionUtils.loadStyleSheet(module, "main.less"); - - // Load CodeMirror addons - brackets.getModule(["thirdparty/CodeMirror/addon/fold/brace-fold"]); - brackets.getModule(["thirdparty/CodeMirror/addon/fold/comment-fold"]); - brackets.getModule(["thirdparty/CodeMirror/addon/fold/markdown-fold"]); - - // Still using slightly modified versions of the foldcode.js and foldgutter.js since we - // need to modify the gutter click handler to take care of some collapse and expand features - // e.g. collapsing all children when 'alt' key is pressed - var foldGutter = require("foldhelpers/foldgutter"), - foldCode = require("foldhelpers/foldcode"), - indentFold = require("foldhelpers/indentFold"), - handlebarsFold = require("foldhelpers/handlebarsFold"), - selectionFold = require("foldhelpers/foldSelected"); - - - /** Set to true when init() has run; set back to false after deinit() has run */ - var _isInitialized = false; - - /** Used to keep track of files for which line folds have been restored.*/ - - /** - * Restores the linefolds in the editor using values fetched from the preference store - * Checks the document to ensure that changes have not been made (e.g., in a different editor) - * to invalidate the saved line folds. - * Selection Folds are found by comparing the line folds in the preference store with the - * selection ranges in the viewState of the current document. Any selection range in the view state - * that is folded in the prefs will be folded. Unlike other fold range finder, the only validation - * on selection folds is to check that they satisfy the minimum fold range. - * @param {Editor} editor the editor whose saved line folds should be restored - */ - function restoreLineFolds(editor) { - /** - * Checks if the range from and to Pos is the same as the selection start and end Pos - * @param {Object} range {from, to} where from and to are CodeMirror.Pos objects - * @param {Object} selection {start, end} where start and end are CodeMirror.Pos objects - * @returns {Boolean} true if the range and selection span the same region and false otherwise - */ - function rangeEqualsSelection(range, selection) { - return range.from.line === selection.start.line && range.from.ch === selection.start.ch && - range.to.line === selection.end.line && range.to.ch === selection.end.ch; - } - - /** - * Checks if the range is equal to one of the selections in the viewState - * @param {Object} range {from, to} where from and to are CodeMirror.Pos objects. - * @param {Object} viewState The current editor's ViewState object - * @returns {Boolean} true if the range is found in the list of selections or false if not. - */ - function isInViewStateSelection(range, viewState) { - if (!viewState || !viewState.selections) { - return false; - } - - return viewState.selections.some(function (selection) { - return rangeEqualsSelection(range, selection); - }); - } - - var saveFolds = prefs.getSetting("saveFoldStates"); - - if (!editor || !saveFolds) { - if (editor) { - editor._codeMirror._lineFolds = editor._codeMirror._lineFolds || {}; - } - return; - } - - var cm = editor._codeMirror; - var viewState = ViewStateManager.getViewState(editor.document.file); - var path = editor.document.file.fullPath; - var folds = cm._lineFolds || prefs.getFolds(path) || {}; - - //separate out selection folds from non-selection folds - var nonSelectionFolds = {}, selectionFolds = {}, range; - Object.keys(folds).forEach(function (line) { - range = folds[line]; - if (isInViewStateSelection(range, viewState)) { - selectionFolds[line] = range; - } else { - nonSelectionFolds[line] = range; - } - }); - nonSelectionFolds = cm.getValidFolds(nonSelectionFolds); - //add the selection folds - Object.keys(selectionFolds).forEach(function (line) { - nonSelectionFolds[line] = selectionFolds[line]; - }); - cm._lineFolds = nonSelectionFolds; - prefs.setFolds(path, cm._lineFolds); - Object.keys(cm._lineFolds).forEach(function (line) { - cm.foldCode(Number(line), {range: cm._lineFolds[line]}); - }); - } - - /** - * Saves the line folds in the editor using the preference storage - * @param {Editor} editor the editor whose line folds should be saved - */ - function saveLineFolds(editor) { - var saveFolds = prefs.getSetting("saveFoldStates"); - if (!editor || !saveFolds) { - return; - } - var folds = editor._codeMirror._lineFolds || {}; - var path = editor.document.file.fullPath; - if (Object.keys(folds).length) { - prefs.setFolds(path, folds); - } else { - prefs.setFolds(path, undefined); - } - } - - /** - * Event handler for gutter click. Manages folding and unfolding code regions. If the Alt key - * is pressed while clicking the fold gutter, child code fragments are also folded/unfolded - * up to a level defined in the `maxFoldLevel' preference. - * @param {!CodeMirror} cm the CodeMirror object - * @param {number} line the line number for the clicked gutter - * @param {string} gutter the name of the gutter element clicked - * @param {!KeyboardEvent} event the underlying dom event triggered for the gutter click - */ - function onGutterClick(cm, line, gutter, event) { - var opts = cm.state.foldGutter.options, pos = CodeMirror.Pos(line); - if (gutter !== opts.gutter) { return; } - var range; - var _lineFolds = cm._lineFolds; - if (cm.isFolded(line)) { - if (event.altKey) { // unfold code including children - range = _lineFolds[line]; - CodeMirror.commands.unfoldAll(cm, range.from.line, range.to.line); - } else { - cm.unfoldCode(line, {range: _lineFolds[line]}); - } - } else { - if (event.altKey) { - range = CodeMirror.fold.auto(cm, pos); - if (range) { - CodeMirror.commands.foldToLevel(cm, range.from.line, range.to.line); - } - } else { - cm.foldCode(line); - } - } - } - - /** - * Collapses the code region nearest the current cursor position. - * Nearest is found by searching from the current line and moving up the document until an - * opening code-folding region is found. - */ - function collapseCurrent() { - var editor = EditorManager.getFocusedEditor(); - if (!editor) { - return; - } - var cm = editor._codeMirror; - var cursor = editor.getCursorPos(), i; - // Move cursor up until a collapsible line is found - for (i = cursor.line; i >= 0; i--) { - if (cm.foldCode(i)) { - editor.setCursorPos(i); - return; - } - } - } - - /** - * Expands the code region at the current cursor position. - */ - function expandCurrent() { - var editor = EditorManager.getFocusedEditor(); - if (editor) { - var cursor = editor.getCursorPos(), cm = editor._codeMirror; - cm.unfoldCode(cursor.line); - } - } - - /** - * Collapses all foldable regions in the current document. Folding is done up to a level 'n' - * which is defined in the `maxFoldLevel` preference. Levels refer to fold heirarchies e.g., for the following - * code fragment, the function is level 1, the if statement is level 2 and the forEach is level 3 - * - * function sample() { - * if (debug) { - * logMessages.forEach(function (m) { - * console.debug(m); - * }); - * } - * } - */ - function collapseAll() { - var editor = EditorManager.getFocusedEditor(); - if (editor) { - var cm = editor._codeMirror; - CodeMirror.commands.foldToLevel(cm); - } - } - - /** - * Expands all folded regions in the current document - */ - function expandAll() { - var editor = EditorManager.getFocusedEditor(); - if (editor) { - var cm = editor._codeMirror; - CodeMirror.commands.unfoldAll(cm); - } - } - - function clearGutter(editor) { - var cm = editor._codeMirror; - var BLANK_GUTTER_CLASS = "CodeMirror-foldgutter-blank"; - editor.clearGutter(GUTTER_NAME); - var blank = window.document.createElement("div"); - blank.className = BLANK_GUTTER_CLASS; - var vp = cm.getViewport(); - cm.operation(function () { - cm.eachLine(vp.from, vp.to, function (line) { - editor.setGutterMarker(line.lineNo(), GUTTER_NAME, blank); - }); - }); - } - - /** - * Renders and sets up event listeners the code-folding gutter. - * @param {Editor} editor the editor on which to initialise the fold gutter - */ - function setupGutterEventListeners(editor) { - var cm = editor._codeMirror; - $(editor.getRootElement()).addClass("folding-enabled"); - cm.setOption("foldGutter", {onGutterClick: onGutterClick}); - - $(cm.getGutterElement()).on({ - mouseenter: function () { - if (prefs.getSetting("hideUntilMouseover")) { - foldGutter.updateInViewport(cm); - } else { - $(editor.getRootElement()).addClass("over-gutter"); - } - }, - mouseleave: function () { - if (prefs.getSetting("hideUntilMouseover")) { - clearGutter(editor); - } else { - $(editor.getRootElement()).removeClass("over-gutter"); - } - } - }); - } - - /** - * Remove the fold gutter for a given CodeMirror instance. - * @param {Editor} editor the editor instance whose gutter should be removed - */ - function removeGutters(editor) { - Editor.unregisterGutter(GUTTER_NAME); - $(editor.getRootElement()).removeClass("folding-enabled"); - CodeMirror.defineOption("foldGutter", false, null); - } - - /** - * Add gutter and restore saved expand/collapse state. - * @param {Editor} editor the editor instance where gutter should be added. - */ - function enableFoldingInEditor(editor) { - restoreLineFolds(editor); - setupGutterEventListeners(editor); - editor._codeMirror.refresh(); - } - - /** - * When a brand new editor is seen, initialise fold-gutter and restore line folds in it. - * Save line folds in departing editor in case it's getting closed. - * @param {object} event the event object - * @param {Editor} current the current editor - * @param {Editor} previous the previous editor - */ - function onActiveEditorChanged(event, current, previous) { - if (current && !current._codeMirror._lineFolds) { - enableFoldingInEditor(current); - } - if (previous) { - saveLineFolds(previous); - } - } - - /** - * Saves the line folds in the current full editor before it is closed. - */ - function saveBeforeClose() { - // We've already saved all other open editors when they go active->inactive - saveLineFolds(EditorManager.getActiveEditor()); - } - - /** - * Remove code-folding functionality - */ - function deinit() { - _isInitialized = false; - - KeyBindingManager.removeBinding(collapseKey); - KeyBindingManager.removeBinding(expandKey); - KeyBindingManager.removeBinding(collapseAllKey); - KeyBindingManager.removeBinding(expandAllKey); - KeyBindingManager.removeBinding(collapseAllKeyMac); - KeyBindingManager.removeBinding(expandAllKeyMac); - - //remove menus - Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).removeMenuDivider(codeFoldingMenuDivider.id); - Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).removeMenuItem(COLLAPSE); - Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).removeMenuItem(EXPAND); - Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).removeMenuItem(COLLAPSE_ALL); - Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).removeMenuItem(EXPAND_ALL); - - EditorManager.off(".CodeFolding"); - DocumentManager.off(".CodeFolding"); - ProjectManager.off(".CodeFolding"); - - // Remove gutter & revert collapsed sections in all currently open editors - Editor.forEveryEditor(function (editor) { - CodeMirror.commands.unfoldAll(editor._codeMirror); - }); - removeGutters(); - } - - /** - * Enable code-folding functionality - */ - function init() { - _isInitialized = true; - - foldCode.init(); - foldGutter.init(); - - // Many CodeMirror modes specify which fold helper should be used for that language. For a few that - // don't, we register helpers explicitly here. We also register a global helper for generic indent-based - // folding, which cuts across all languages if enabled via preference. - CodeMirror.registerGlobalHelper("fold", "selectionFold", function (mode, cm) { - return prefs.getSetting("makeSelectionsFoldable"); - }, selectionFold); - CodeMirror.registerGlobalHelper("fold", "indent", function (mode, cm) { - return prefs.getSetting("alwaysUseIndentFold"); - }, indentFold); - - CodeMirror.registerHelper("fold", "handlebars", handlebarsFold); - CodeMirror.registerHelper("fold", "htmlhandlebars", handlebarsFold); - CodeMirror.registerHelper("fold", "htmlmixed", handlebarsFold); - - EditorManager.on("activeEditorChange.CodeFolding", onActiveEditorChanged); - DocumentManager.on("documentRefreshed.CodeFolding", function (event, doc) { - restoreLineFolds(doc._masterEditor); - }); - - ProjectManager.on("beforeProjectClose.CodeFolding beforeAppClose.CodeFolding", saveBeforeClose); - - //create menus - codeFoldingMenuDivider = Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).addMenuDivider(); - Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).addMenuItem(COLLAPSE_ALL); - Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).addMenuItem(EXPAND_ALL); - Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).addMenuItem(COLLAPSE); - Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).addMenuItem(EXPAND); - - //register keybindings - KeyBindingManager.addBinding(COLLAPSE_ALL, [ {key: collapseAllKey}, {key: collapseAllKeyMac, platform: "mac"} ]); - KeyBindingManager.addBinding(EXPAND_ALL, [ {key: expandAllKey}, {key: expandAllKeyMac, platform: "mac"} ]); - KeyBindingManager.addBinding(COLLAPSE, collapseKey); - KeyBindingManager.addBinding(EXPAND, expandKey); - - - // Add gutters & restore saved expand/collapse state in all currently open editors - Editor.registerGutter(GUTTER_NAME, CODE_FOLDING_GUTTER_PRIORITY); - Editor.forEveryEditor(function (editor) { - enableFoldingInEditor(editor); - }); - } - - /** - * Register change listener for the preferences file. - */ - function watchPrefsForChanges() { - prefs.prefsObject.on("change", function (e, data) { - if (data.ids.indexOf("enabled") > -1) { - // Check if enabled state mismatches whether code-folding is actually initialized (can't assume - // since preference change events can occur when the value hasn't really changed) - var isEnabled = prefs.getSetting("enabled"); - if (isEnabled && !_isInitialized) { - init(); - } else if (!isEnabled && _isInitialized) { - deinit(); - } - } - }); - } - - AppInit.htmlReady(function () { - CommandManager.register(Strings.COLLAPSE_ALL, COLLAPSE_ALL, collapseAll); - CommandManager.register(Strings.EXPAND_ALL, EXPAND_ALL, expandAll); - CommandManager.register(Strings.COLLAPSE_CURRENT, COLLAPSE, collapseCurrent); - CommandManager.register(Strings.EXPAND_CURRENT, EXPAND, expandCurrent); - - if (prefs.getSetting("enabled")) { - init(); - } - watchPrefsForChanges(); - }); -}); diff --git a/src/extensions/default/CodeFolding/main.ts b/src/extensions/default/CodeFolding/main.ts new file mode 100644 index 00000000000..f184f0d6a3d --- /dev/null +++ b/src/extensions/default/CodeFolding/main.ts @@ -0,0 +1,505 @@ +/* +* Copyright (c) 2013 Patrick Oladimeji. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +*/ +/** + * Code folding extension for brackets + * @author Patrick Oladimeji + * @date 10/24/13 9:35:26 AM + */ + +/// + +import type { MenuItem } from "command/Menus"; + +const CodeMirror = brackets.getModule("thirdparty/CodeMirror/lib/codemirror"); +const Strings = brackets.getModule("strings"); +const AppInit = brackets.getModule("utils/AppInit"); +const CommandManager = brackets.getModule("command/CommandManager"); +const DocumentManager = brackets.getModule("document/DocumentManager"); +const Editor = brackets.getModule("editor/Editor").Editor; +const EditorManager = brackets.getModule("editor/EditorManager"); +const ProjectManager = brackets.getModule("project/ProjectManager"); +const ViewStateManager = brackets.getModule("view/ViewStateManager"); +const KeyBindingManager = brackets.getModule("command/KeyBindingManager"); +const ExtensionUtils = brackets.getModule("utils/ExtensionUtils"); +const Menus = brackets.getModule("command/Menus"); +import * as prefs from "Prefs"; +const COLLAPSE_ALL = "codefolding.collapse.all"; +const COLLAPSE = "codefolding.collapse"; +const EXPAND = "codefolding.expand"; +const EXPAND_ALL = "codefolding.expand.all"; +const GUTTER_NAME = "CodeMirror-foldgutter"; +const CODE_FOLDING_GUTTER_PRIORITY = Editor.CODE_FOLDING_GUTTER_PRIORITY; +let codeFoldingMenuDivider: MenuItem; +const collapseKey = "Ctrl-Alt-["; +const expandKey = "Ctrl-Alt-]"; +const collapseAllKey = "Alt-1"; +const expandAllKey = "Shift-Alt-1"; +const collapseAllKeyMac = "Cmd-1"; +const expandAllKeyMac = "Cmd-Shift-1"; + +ExtensionUtils.loadStyleSheet(module, "main.less"); + +// Load CodeMirror addons +brackets.getModule(["thirdparty/CodeMirror/addon/fold/brace-fold"]); +brackets.getModule(["thirdparty/CodeMirror/addon/fold/comment-fold"]); +brackets.getModule(["thirdparty/CodeMirror/addon/fold/markdown-fold"]); + +interface SimpleRange { + from: CodeMirror.Position; + to: CodeMirror.Position; +} + +// Some methods are defined in foldhelpers folder. +// TODO: we should try to use upstream code where possible. +declare module "codemirror" { + + interface Editor { + _lineFolds: Record; + } + + interface Fold { + auto: (cm: Editor, pos: Position) => SimpleRange; + } + const fold: Fold; + + // TODO: Not present in upstream types. + const registerGlobalHelper: ( + type: string, + name: string, + predicate: (mode: any, cm: Editor) => void, + value: (cm: Editor, start: Position) => void + ) => void; + + interface CommandActions { + unfoldAll: (cm: Editor, fromLine?: number, toline?: number) => void; + foldToLevel: (cm: Editor, fromLine?: number, toline?: number) => void; + } +} + +// Still using slightly modified versions of the foldcode.js and foldgutter.js since we +// need to modify the gutter click handler to take care of some collapse and expand features +// e.g. collapsing all children when 'alt' key is pressed +import * as foldGutter from "foldhelpers/foldgutter"; +import * as foldCode from "foldhelpers/foldcode"; +import * as indentFold from "foldhelpers/indentFold"; +import * as handlebarsFold from "foldhelpers/handlebarsFold"; +import * as selectionFold from "foldhelpers/foldSelected"; + + +/** Set to true when init() has run; set back to false after deinit() has run */ +let _isInitialized = false; + +/** Used to keep track of files for which line folds have been restored. */ + +/** + * Restores the linefolds in the editor using values fetched from the preference store + * Checks the document to ensure that changes have not been made (e.g., in a different editor) + * to invalidate the saved line folds. + * Selection Folds are found by comparing the line folds in the preference store with the + * selection ranges in the viewState of the current document. Any selection range in the view state + * that is folded in the prefs will be folded. Unlike other fold range finder, the only validation + * on selection folds is to check that they satisfy the minimum fold range. + * @param {Editor} editor the editor whose saved line folds should be restored + */ +function restoreLineFolds(editor) { + /** + * Checks if the range from and to Pos is the same as the selection start and end Pos + * @param {Object} range {from, to} where from and to are CodeMirror.Pos objects + * @param {Object} selection {start, end} where start and end are CodeMirror.Pos objects + * @returns {Boolean} true if the range and selection span the same region and false otherwise + */ + function rangeEqualsSelection(range, selection) { + return range.from.line === selection.start.line && range.from.ch === selection.start.ch && + range.to.line === selection.end.line && range.to.ch === selection.end.ch; + } + + /** + * Checks if the range is equal to one of the selections in the viewState + * @param {Object} range {from, to} where from and to are CodeMirror.Pos objects. + * @param {Object} viewState The current editor's ViewState object + * @returns {Boolean} true if the range is found in the list of selections or false if not. + */ + function isInViewStateSelection(range, viewState) { + if (!viewState || !viewState.selections) { + return false; + } + + return viewState.selections.some(function (selection) { + return rangeEqualsSelection(range, selection); + }); + } + + const saveFolds = prefs.getSetting("saveFoldStates"); + + if (!editor || !saveFolds) { + if (editor) { + editor._codeMirror._lineFolds = editor._codeMirror._lineFolds || {}; + } + return; + } + + const cm = editor._codeMirror; + const viewState = ViewStateManager.getViewState(editor.document.file); + const path = editor.document.file.fullPath; + const folds = cm._lineFolds || prefs.getFolds(path) || {}; + + // separate out selection folds from non-selection folds + let nonSelectionFolds = {}; + const selectionFolds = {}; + let range; + Object.keys(folds).forEach(function (line) { + range = folds[line]; + if (isInViewStateSelection(range, viewState)) { + selectionFolds[line] = range; + } else { + nonSelectionFolds[line] = range; + } + }); + nonSelectionFolds = cm.getValidFolds(nonSelectionFolds); + // add the selection folds + Object.keys(selectionFolds).forEach(function (line) { + nonSelectionFolds[line] = selectionFolds[line]; + }); + cm._lineFolds = nonSelectionFolds; + prefs.setFolds(path, cm._lineFolds); + Object.keys(cm._lineFolds).forEach(function (line) { + cm.foldCode(Number(line), {range: cm._lineFolds[line]}); + }); +} + +/** + * Saves the line folds in the editor using the preference storage + * @param {Editor} editor the editor whose line folds should be saved + */ +function saveLineFolds(editor) { + const saveFolds = prefs.getSetting("saveFoldStates"); + if (!editor || !saveFolds) { + return; + } + const folds = editor._codeMirror._lineFolds || {}; + const path = editor.document.file.fullPath; + if (Object.keys(folds).length) { + prefs.setFolds(path, folds); + } else { + prefs.setFolds(path, undefined); + } +} + +/** + * Event handler for gutter click. Manages folding and unfolding code regions. If the Alt key + * is pressed while clicking the fold gutter, child code fragments are also folded/unfolded + * up to a level defined in the `maxFoldLevel' preference. + * @param {!CodeMirror} cm the CodeMirror object + * @param {number} line the line number for the clicked gutter + * @param {string} gutter the name of the gutter element clicked + * @param {!KeyboardEvent} event the underlying dom event triggered for the gutter click + */ +function onGutterClick(cm, line, gutter, event) { + const opts = cm.state.foldGutter.options; + const pos = CodeMirror.Pos(line); + if (gutter !== opts.gutter) { return; } + + const _lineFolds = cm._lineFolds; + if (cm.isFolded(line)) { + if (event.altKey) { // unfold code including children + const range = _lineFolds[line]; + CodeMirror.commands.unfoldAll(cm, range.from.line, range.to.line); + } else { + cm.unfoldCode(line, {range: _lineFolds[line]}); + } + } else { + if (event.altKey) { + const range = CodeMirror.fold.auto(cm, pos); + if (range) { + CodeMirror.commands.foldToLevel(cm, range.from.line, range.to.line); + } + } else { + cm.foldCode(line); + } + } +} + +/** + * Collapses the code region nearest the current cursor position. + * Nearest is found by searching from the current line and moving up the document until an + * opening code-folding region is found. + */ +function collapseCurrent() { + const editor = EditorManager.getFocusedEditor(); + if (!editor) { + return; + } + const cm = editor._codeMirror; + const cursor = editor.getCursorPos(); + // Move cursor up until a collapsible line is found + for (let i = cursor.line; i >= 0; i--) { + if (cm.foldCode(i)) { + editor.setCursorPos(i); + return; + } + } +} + +/** + * Expands the code region at the current cursor position. + */ +function expandCurrent() { + const editor = EditorManager.getFocusedEditor(); + if (editor) { + const cursor = editor.getCursorPos(); + const cm = editor._codeMirror; + cm.unfoldCode(cursor.line); + } +} + +/** + * Collapses all foldable regions in the current document. Folding is done up to a level 'n' + * which is defined in the `maxFoldLevel` preference. Levels refer to fold heirarchies e.g., for the following + * code fragment, the function is level 1, the if statement is level 2 and the forEach is level 3 + * + * function sample() { + * if (debug) { + * logMessages.forEach(function (m) { + * console.debug(m); + * }); + * } + * } + */ +function collapseAll() { + const editor = EditorManager.getFocusedEditor(); + if (editor) { + const cm = editor._codeMirror; + CodeMirror.commands.foldToLevel(cm); + } +} + +/** + * Expands all folded regions in the current document + */ +function expandAll() { + const editor = EditorManager.getFocusedEditor(); + if (editor) { + const cm = editor._codeMirror; + CodeMirror.commands.unfoldAll(cm); + } +} + +function clearGutter(editor) { + const cm = editor._codeMirror; + const BLANK_GUTTER_CLASS = "CodeMirror-foldgutter-blank"; + editor.clearGutter(GUTTER_NAME); + const blank = window.document.createElement("div"); + blank.className = BLANK_GUTTER_CLASS; + const vp = cm.getViewport(); + cm.operation(function () { + cm.eachLine(vp.from, vp.to, function (line) { + editor.setGutterMarker(line.lineNo(), GUTTER_NAME, blank); + }); + }); +} + +/** + * Renders and sets up event listeners the code-folding gutter. + * @param {Editor} editor the editor on which to initialise the fold gutter + */ +function setupGutterEventListeners(editor) { + const cm = editor._codeMirror; + $(editor.getRootElement()).addClass("folding-enabled"); + cm.setOption("foldGutter", {onGutterClick: onGutterClick}); + + $(cm.getGutterElement()).on({ + mouseenter: function () { + if (prefs.getSetting("hideUntilMouseover")) { + foldGutter.updateInViewport(cm); + } else { + $(editor.getRootElement()).addClass("over-gutter"); + } + }, + mouseleave: function () { + if (prefs.getSetting("hideUntilMouseover")) { + clearGutter(editor); + } else { + $(editor.getRootElement()).removeClass("over-gutter"); + } + } + }); +} + +/** + * Remove gutter & revert collapsed sections in all currently open editors + */ +function removeGutters() { + Editor.forEveryEditor(function (editor) { + CodeMirror.commands.unfoldAll(editor._codeMirror); + }); + + Editor.unregisterGutter(GUTTER_NAME); + + Editor.forEveryEditor(function (editor) { + $(editor.getRootElement()).removeClass("folding-enabled"); + }); + + CodeMirror.defineOption("foldGutter", false, null as any); +} + +/** + * Add gutter and restore saved expand/collapse state. + * @param {Editor} editor the editor instance where gutter should be added. + */ +function enableFoldingInEditor(editor) { + restoreLineFolds(editor); + setupGutterEventListeners(editor); + editor._codeMirror.refresh(); +} + +/** + * When a brand new editor is seen, initialise fold-gutter and restore line folds in it. + * Save line folds in departing editor in case it's getting closed. + * @param {object} event the event object + * @param {Editor} current the current editor + * @param {Editor} previous the previous editor + */ +function onActiveEditorChanged(event, current, previous) { + if (current && !current._codeMirror._lineFolds) { + enableFoldingInEditor(current); + } + if (previous) { + saveLineFolds(previous); + } +} + +/** + * Saves the line folds in the current full editor before it is closed. + */ +function saveBeforeClose() { + // We've already saved all other open editors when they go active->inactive + saveLineFolds(EditorManager.getActiveEditor()); +} + +/** + * Remove code-folding functionality + */ +function deinit() { + _isInitialized = false; + + KeyBindingManager.removeBinding(collapseKey); + KeyBindingManager.removeBinding(expandKey); + KeyBindingManager.removeBinding(collapseAllKey); + KeyBindingManager.removeBinding(expandAllKey); + KeyBindingManager.removeBinding(collapseAllKeyMac); + KeyBindingManager.removeBinding(expandAllKeyMac); + + // remove menus + Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).removeMenuDivider(codeFoldingMenuDivider.id); + Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).removeMenuItem(COLLAPSE); + Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).removeMenuItem(EXPAND); + Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).removeMenuItem(COLLAPSE_ALL); + Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).removeMenuItem(EXPAND_ALL); + + EditorManager.off(".CodeFolding"); + DocumentManager.off(".CodeFolding"); + ProjectManager.off(".CodeFolding"); + + removeGutters(); +} + +/** + * Enable code-folding functionality + */ +function init() { + _isInitialized = true; + + foldCode.init(); + foldGutter.init(); + + // Many CodeMirror modes specify which fold helper should be used for that language. For a few that + // don't, we register helpers explicitly here. We also register a global helper for generic indent-based + // folding, which cuts across all languages if enabled via preference. + CodeMirror.registerGlobalHelper("fold", "selectionFold", function (mode, cm) { + return prefs.getSetting("makeSelectionsFoldable"); + }, selectionFold); + CodeMirror.registerGlobalHelper("fold", "indent", function (mode, cm) { + return prefs.getSetting("alwaysUseIndentFold"); + }, indentFold); + + CodeMirror.registerHelper("fold", "handlebars", handlebarsFold); + CodeMirror.registerHelper("fold", "htmlhandlebars", handlebarsFold); + CodeMirror.registerHelper("fold", "htmlmixed", handlebarsFold); + + EditorManager.on("activeEditorChange.CodeFolding", onActiveEditorChanged); + DocumentManager.on("documentRefreshed.CodeFolding", function (event, doc) { + restoreLineFolds(doc._masterEditor); + }); + + ProjectManager.on("beforeProjectClose.CodeFolding beforeAppClose.CodeFolding", saveBeforeClose); + + // create menus + codeFoldingMenuDivider = Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).addMenuDivider()!; + Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).addMenuItem(COLLAPSE_ALL); + Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).addMenuItem(EXPAND_ALL); + Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).addMenuItem(COLLAPSE); + Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).addMenuItem(EXPAND); + + // register keybindings + KeyBindingManager.addBinding(COLLAPSE_ALL, [ {key: collapseAllKey}, {key: collapseAllKeyMac, platform: "mac"} ]); + KeyBindingManager.addBinding(EXPAND_ALL, [ {key: expandAllKey}, {key: expandAllKeyMac, platform: "mac"} ]); + KeyBindingManager.addBinding(COLLAPSE, collapseKey); + KeyBindingManager.addBinding(EXPAND, expandKey); + + + // Add gutters & restore saved expand/collapse state in all currently open editors + Editor.registerGutter(GUTTER_NAME, CODE_FOLDING_GUTTER_PRIORITY); + Editor.forEveryEditor(function (editor) { + enableFoldingInEditor(editor); + }); +} + +/** + * Register change listener for the preferences file. + */ +function watchPrefsForChanges() { + prefs.prefsObject.on("change", function (e, data) { + if (data.ids.indexOf("enabled") > -1) { + // Check if enabled state mismatches whether code-folding is actually initialized (can't assume + // since preference change events can occur when the value hasn't really changed) + const isEnabled = prefs.getSetting("enabled"); + if (isEnabled && !_isInitialized) { + init(); + } else if (!isEnabled && _isInitialized) { + deinit(); + } + } + }); +} + +AppInit.htmlReady(function () { + CommandManager.register(Strings.COLLAPSE_ALL, COLLAPSE_ALL, collapseAll); + CommandManager.register(Strings.EXPAND_ALL, EXPAND_ALL, expandAll); + CommandManager.register(Strings.COLLAPSE_CURRENT, COLLAPSE, collapseCurrent); + CommandManager.register(Strings.EXPAND_CURRENT, EXPAND, expandCurrent); + + if (prefs.getSetting("enabled")) { + init(); + } + watchPrefsForChanges(); +}); diff --git a/src/extensions/default/CodeFolding/unittests.js b/src/extensions/default/CodeFolding/unittests.js index 19c6f3ce93b..b1fade26b99 100644 --- a/src/extensions/default/CodeFolding/unittests.js +++ b/src/extensions/default/CodeFolding/unittests.js @@ -111,6 +111,14 @@ define(function (require, exports, module) { * Closes the test window */ function tearDown() { + testWindow = null; + EditorManager = null; + DocumentManager = null; + PreferencesManager = null; + CommandManager = null; + + prefs = null; + SpecRunnerUtils.closeTestWindow(); } @@ -254,21 +262,29 @@ define(function (require, exports, module) { }, "Fold markers now visible in gutter", 500); } - beforeEach(function () { - setup(); - }); - - afterEach(function () { - testWindow.closeAllFiles(); - tearDown(); - }); - Object.keys(testFilesSpec).forEach(function (file) { var testFilePath = testFilesSpec[file].filePath; var foldableLines = testFilesSpec[file].foldableLines; var testFileSpec = testFilesSpec[file]; describe(file + " - Editor/Gutter", function () { + beforeFirst(function () { + setup(); + }); + + afterLast(function () { + tearDown(); + }); + beforeEach(function () { + // default preference values + prefs.set("enabled", true); + prefs.set("minFoldSize", 2); + prefs.set("saveFoldStates", true); + prefs.set("alwaysUseIndentFold", false); + prefs.set("hideUntilMouseover", false); + prefs.set("maxFoldLevel", 2); + prefs.set("makeSelectionsFoldable", true); + runs(function () { openTestFile(testFilePath); }); diff --git a/src/extensions/default/quadre-whitespaces/nobreakspace.ts b/src/extensions/default/quadre-whitespaces/nobreakspace.ts index d4de019d499..6c708cb309c 100644 --- a/src/extensions/default/quadre-whitespaces/nobreakspace.ts +++ b/src/extensions/default/quadre-whitespaces/nobreakspace.ts @@ -2,7 +2,7 @@ const CodeMirror = brackets.getModule("thirdparty/CodeMirror/lib/codemirror"); CodeMirror.defineOption("showNoBreakSpace", false, function (cm, val, prev) { // eslint-disable-next-line eqeqeq - if (prev == CodeMirror.Init) { // tslint:disable-line:triple-equals + if (prev == (CodeMirror as any).Init) { // tslint:disable-line:triple-equals prev = false; } if (prev && !val) { diff --git a/src/types/index.ts b/src/types/index.ts index 24850d97439..38c6ac6daec 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -6,6 +6,7 @@ import * as KeyBindingManager from "command/KeyBindingManager"; import * as Menus from "command/Menus"; import * as DocumentManager from "document/DocumentManager"; import * as CodeHintManager from "editor/CodeHintManager"; +import * as Editor from "editor/Editor"; import * as EditorManager from "editor/EditorManager"; import * as InlineWidget from "editor/InlineWidget"; import * as MultiRangeInlineEditor from "editor/MultiRangeInlineEditor"; @@ -48,6 +49,7 @@ import * as StringMatch from "utils/StringMatch"; import * as StringUtils from "utils/StringUtils"; import * as TokenUtils from "utils/TokenUtils"; import * as ViewUtils from "utils/ViewUtils"; +import * as ViewStateManager from "view/ViewStateManager"; import * as DefaultDialogs from "widgets/DefaultDialogs"; import * as Dialogs from "widgets/Dialogs"; import * as InlineMenu from "widgets/InlineMenu"; @@ -56,7 +58,7 @@ import * as Strings from "strings"; import * as Acorn from "thirdparty/acorn/acorn"; import * as AcornLoose from "thirdparty/acorn/acorn_loose"; import * as ASTWalker from "thirdparty/acorn/walk"; -import * as CodeMirror from "thirdparty/CodeMirror/lib/codemirror"; +import * as CodeMirror from "codemirror"; import * as _ from "lodash"; import * as Mustache from "thirdparty/mustache/mustache"; import * as PathUtils from "thirdparty/path-utils/path-utils"; @@ -85,8 +87,9 @@ declare global { : T extends "command/Commands" ? typeof Commands : T extends "command/KeyBindingManager" ? typeof KeyBindingManager : T extends "command/Menus" ? typeof Menus - : T extends "document/DocumentManager" ? typeof DocumentManager + : T extends "document/DocumentManager" ? typeof DocumentManager & EventDispatcher.DispatcherEvents : T extends "editor/CodeHintManager" ? typeof CodeHintManager + : T extends "editor/Editor" ? typeof Editor : T extends "editor/EditorManager" ? typeof EditorManager & EventDispatcher.DispatcherEvents : T extends "editor/InlineWidget" ? typeof InlineWidget : T extends "editor/MultiRangeInlineEditor" ? typeof MultiRangeInlineEditor @@ -131,6 +134,7 @@ declare global { : T extends "utils/StringUtils" ? typeof StringUtils : T extends "utils/TokenUtils" ? typeof TokenUtils : T extends "utils/ViewUtils" ? typeof ViewUtils + : T extends "view/ViewStateManager" ? typeof ViewStateManager : T extends "widgets/DefaultDialogs" ? typeof DefaultDialogs : T extends "widgets/Dialogs" ? typeof Dialogs : T extends "widgets/InlineMenu" ? typeof InlineMenu