-
Notifications
You must be signed in to change notification settings - Fork 7.6k
integration of code-folding extension into brackets #10792
Changes from 2 commits
d298c78
a69a289
1fb62d1
4d4029a
d9fcb1a
985fe8a
81cb981
f4c7091
b341c37
d386643
88261fc
a366170
16c4483
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/** | ||
* JSON values for default settings | ||
* @author Patrick Oladimeji | ||
* @date 8/23/14 15:45:35 PM | ||
*/ | ||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ | ||
/*global define*/ | ||
define(function (require, exports, module) { | ||
"use strict"; | ||
|
||
module.exports = { | ||
minFoldSize: 2, | ||
saveFoldStates: true, | ||
alwaysUseIndentFold: true, | ||
enableRegionFolding: true, | ||
fadeFoldButtons: false, | ||
maxFoldLevel: 2 // this value is only used when fold all is called | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: indentation |
||
}; | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/** | ||
* 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 | ||
*/ | ||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ | ||
/*global define, brackets*/ | ||
define(function (require, exports, module) { | ||
"use strict"; | ||
var PreferencesManager = brackets.getModule("preferences/PreferencesManager"), | ||
stateManager = PreferencesManager.stateManager.getPrefixedSystem("code-folding"), | ||
DefaultSettings = require("DefaultSettings"), | ||
store = {}, | ||
settings = {}, | ||
folds = "folds"; | ||
|
||
function simplify(folds) { | ||
if (!folds) { return folds; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tweak this to
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But how can you return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's equivalent to returning undefined. I've updated for clarity :). |
||
var res = {}, range; | ||
Object.keys(folds).map(function (line) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed that is a typo. Should be a forEach. |
||
range = folds[line]; | ||
res[line] = Array.isArray(range) ? range : [[range.from.line, range.from.ch], [range.to.line, range.to.ch]]; | ||
}); | ||
return res; | ||
} | ||
|
||
function inflate(folds) { | ||
if (!folds) { return folds; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Expand this out to multiple lines
|
||
//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; | ||
} | ||
|
||
module.exports = { | ||
get: function (id) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Common style is to make these named functions that you assign to the function get(id) {
}
module.exports.get = get; We probably also benefit from some jsdocs :) |
||
store = (stateManager.get(folds) || {}); | ||
return inflate(store[id]); | ||
}, | ||
set: function (id, value) { | ||
store[id] = simplify(value); | ||
stateManager.set(folds, store); | ||
stateManager.save(); | ||
}, | ||
getSetting: function (key) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indentation here wont be a problem once they are moved from here to named functions. |
||
settings = (stateManager.get("settings") || DefaultSettings); | ||
return settings[key]; | ||
}, | ||
setSetting: function (key, value) { | ||
settings[key] = value; | ||
stateManager.set("settings", settings); | ||
stateManager.save(); | ||
}, | ||
getAllSettings: function () { | ||
var res = {}, self = this; | ||
Object.keys(DefaultSettings).forEach(function (key) { | ||
res[key] = self.getSetting(key); | ||
}); | ||
return res; | ||
}, | ||
clearAllFolds: function () { | ||
stateManager.set(folds, {}); | ||
stateManager.save(); | ||
} | ||
}; | ||
|
||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/** | ||
* Configure and change settings for the code folding extension | ||
* @author Patrick Oladimeji | ||
* @date 8/23/14 12:32:46 PM | ||
*/ | ||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ | ||
/*global define, brackets, Mustache, $ */ | ||
define(function (require, exports, module) { | ||
"use strict"; | ||
var Dialogs = brackets.getModule("widgets/Dialogs"), | ||
DefaultSettings = require("DefaultSettings"), | ||
Strings = require("i18n!nls/strings"), | ||
settingsTemplate = require("text!htmlTemplates/settings-dialog.html"), | ||
preferences = require("Prefs"); | ||
|
||
function setFormValues(prefs) { | ||
$("#min-fold-size").val(prefs.minFoldSize || 2); | ||
$("#max-fold-level").val(prefs.maxFoldLevel || 2); | ||
$("#save-fold-states").prop("checked", prefs.saveFoldStates); | ||
$("#always-use-indent-fold").prop("checked", prefs.alwaysUseIndentFold); | ||
$("#enable-region-folding").prop("checked", prefs.enableRegionFolding); | ||
$("#fade-fold-buttons").prop("checked", prefs.fadeFoldButtons); | ||
} | ||
|
||
function restoreDefaults() { | ||
setFormValues(DefaultSettings); | ||
} | ||
|
||
function showDialog(cb) { | ||
var template = Mustache.render(settingsTemplate, Strings); | ||
var dialog = Dialogs.showModalDialogUsingTemplate(template); | ||
setFormValues(preferences.getAllSettings()); | ||
|
||
dialog.done(function (buttonId) { | ||
if (buttonId === "ok") { | ||
var $dialog = dialog.getElement(); | ||
var minFoldSize = $("#min-fold-size", $dialog).val(); | ||
var maxFoldLevel = $("#max-fold-level", $dialog).val(); | ||
preferences.setSetting("minFoldSize", isNaN(minFoldSize) || +minFoldSize === 0 ? | ||
+preferences.getSetting("minFoldSize") : +minFoldSize); | ||
preferences.setSetting("saveFoldStates", $("#save-fold-states", $dialog).prop("checked")); | ||
preferences.setSetting("maxFoldLevel", isNaN(maxFoldLevel) || +maxFoldLevel === 0 ? | ||
+preferences.getSetting("maxFoldLevel") : +maxFoldLevel); | ||
preferences.setSetting("alwaysUseIndentFold", $("#always-use-indent-fold", $dialog).prop("checked")); | ||
preferences.setSetting("enableRegionFolding", $("#enable-region-folding", $dialog).prop("checked")); | ||
preferences.setSetting("fadeFoldButtons", $("#fade-fold-buttons", $dialog).prop("checked")); | ||
if (cb && typeof cb === "function") { | ||
cb(); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
function bindListeners() { | ||
$("button[data-button-id='defaults']").on("click", function (e) { | ||
e.stopPropagation(); | ||
restoreDefaults(); | ||
}); | ||
} | ||
|
||
bindListeners(); | ||
|
||
module.exports = { | ||
show: showDialog | ||
}; | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
/** | ||
* Based on http://codemirror.net/addon/fold/foldcode.js | ||
* @author Patrick Oladimeji | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we still need to keep the original copyright from CodeMirror here |
||
* @date 10/28/13 8:41:46 AM | ||
* @last modified 20 April 2014 | ||
*/ | ||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ | ||
/*global define, brackets, document*/ | ||
define(function (require, exports, module) { | ||
"use strict"; | ||
var CodeMirror = brackets.getModule("thirdparty/CodeMirror2/lib/codemirror"), | ||
prefs = require("Prefs"); | ||
|
||
module.exports = function () { | ||
function doFold(cm, pos, options, force) { | ||
force = force || "fold"; | ||
if (typeof pos === "number") { | ||
pos = CodeMirror.Pos(pos, 0); | ||
} | ||
var finder = (options && options.rangeFinder) || CodeMirror.fold.auto; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instead of checking is options exists in each line that uses options = options || {}; |
||
var minSize = (options && options.minFoldSize) || prefs.getSetting("minFoldSize"); | ||
|
||
function getRange(allowFolded) { | ||
var range = options && options.range ? options.range : finder(cm, pos); | ||
if (!range || range.to.line - range.from.line < minSize) { | ||
return null; | ||
} | ||
var marks = cm.findMarksAt(range.from), | ||
i; | ||
for (i = 0; i < marks.length; ++i) { | ||
if (marks[i].__isFold && force !== "fold") { | ||
if (!allowFolded) { | ||
return null; | ||
} | ||
range.cleared = true; | ||
marks[i].clear(); | ||
} | ||
} | ||
//check for overlapping folds | ||
var lastMark, foldMarks; | ||
if (marks && marks.length) { | ||
foldMarks = marks.filter(function (d) { return d.__isFold; }); | ||
if (foldMarks && foldMarks.length) { | ||
lastMark = foldMarks[foldMarks.length - 1].find(); | ||
if (lastMark && range.from.line <= lastMark.to.line && lastMark.to.line < range.to.line) { | ||
return null; | ||
} | ||
} | ||
} | ||
return range; | ||
} | ||
|
||
function makeWidget() { | ||
var widget = document.createElement("span"); | ||
widget.className = "CodeMirror-foldmarker"; | ||
return widget; | ||
} | ||
|
||
var range = getRange(true); | ||
if (options && options.scanUp) { | ||
while (!range && pos.line > cm.firstLine()) { | ||
pos = CodeMirror.Pos(pos.line - 1, 0); | ||
range = getRange(false); | ||
} | ||
} | ||
if (!range || range.cleared || force === "unfold" || range.to.line - range.from.line < minSize) { | ||
if (range) { range.cleared = false; } | ||
return; | ||
} | ||
|
||
var myWidget = makeWidget(); | ||
var myRange = cm.markText(range.from, range.to, { | ||
replacedWith: myWidget, | ||
clearOnEnter: true, | ||
__isFold: true | ||
}); | ||
CodeMirror.on(myWidget, "mousedown", function () { | ||
myRange.clear(); | ||
}); | ||
myRange.on("clear", function (from, to) { | ||
delete cm._lineFolds[from.line]; | ||
CodeMirror.signal(cm, "unfold", cm, from, to); | ||
}); | ||
|
||
if (force === "fold") { | ||
delete range.cleared; | ||
cm._lineFolds[pos.line] = range; | ||
} else { | ||
delete cm._lineFolds[pos.line]; | ||
} | ||
CodeMirror.signal(cm, force, cm, range.from, range.to); | ||
return range; | ||
} | ||
|
||
CodeMirror.defineExtension("foldCode", function (pos, options, force) { | ||
return doFold(this, pos, options, force); | ||
}); | ||
|
||
//define an unfoldCode extension to quickly unfold folded code | ||
CodeMirror.defineExtension("unfoldCode", function (pos, options) { | ||
return doFold(this, pos, options, "unfold"); | ||
}); | ||
|
||
CodeMirror.registerHelper("fold", "combine", function () { | ||
var funcs = Array.prototype.slice.call(arguments, 0); | ||
return function (cm, start) { | ||
var i; | ||
for (i = 0; i < funcs.length; ++i) { | ||
var found = funcs[i] && funcs[i](cm, start); | ||
if (found) { | ||
return found; | ||
} | ||
} | ||
}; | ||
}); | ||
|
||
CodeMirror.defineExtension("isFolded", function (line) { | ||
return this._lineFolds[line]; | ||
}); | ||
/** | ||
Checks the validity of the ranges passed in the parameter and returns the foldranges | ||
that are still valid in the current document | ||
@param {object} folds the dictionary of lines in the current document that should be folded | ||
@returns {object} valid folds found in those passed in parameter | ||
*/ | ||
CodeMirror.defineExtension("getValidFolds", function (folds) { | ||
var keys, rf = CodeMirror.fold.auto, cm = this, result = {}; | ||
if (folds && (keys = Object.keys(folds)).length) { | ||
var range, cachedRange; | ||
keys.forEach(function (lineNumber) { | ||
lineNumber = +lineNumber; | ||
if (lineNumber >= cm.firstLine() && lineNumber <= cm.lastLine()) { | ||
range = rf(cm, CodeMirror.Pos(lineNumber)); | ||
cachedRange = folds[lineNumber]; | ||
if (range && cachedRange && range.from.line === cachedRange.from.line && | ||
range.to.line === cachedRange.to.line) { | ||
cm.foldCode(lineNumber, {range: folds[lineNumber]}, "fold"); | ||
result[lineNumber] = folds[lineNumber]; | ||
} | ||
} | ||
}); | ||
} | ||
return result; | ||
}); | ||
|
||
CodeMirror.commands.toggleFold = function (cm) { | ||
cm.foldCode(cm.getCursor()); | ||
}; | ||
CodeMirror.commands.fold = function (cm, options, force) { | ||
cm.foldCode(cm.getCursor(), options, "fold"); | ||
}; | ||
CodeMirror.commands.unfold = function (cm, options, force) { | ||
cm.foldCode(cm.getCursor(), options, "unfold"); | ||
}; | ||
CodeMirror.commands.foldAll = function (cm) { | ||
cm.operation(function () { | ||
var i, e; | ||
for (i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) { | ||
cm.foldCode(CodeMirror.Pos(i, 0), null, "fold"); | ||
} | ||
}); | ||
}; | ||
/** | ||
Folds the specified range. The descendants of any fold regions within the range are also folded up to | ||
a level set globally in the codeFolding preferences | ||
*/ | ||
CodeMirror.commands.foldToLevel = function (cm, start, end) { | ||
var rf = CodeMirror.fold.auto, level = prefs.getSetting("maxFoldLevel"); | ||
function foldLevel(n, from, to) { | ||
if (n > 0) { | ||
var i = from, range; | ||
while (i < to) { | ||
range = rf(cm, CodeMirror.Pos(i, 0)); | ||
if (range) { | ||
//call fold level for the range just folded | ||
foldLevel(n - 1, range.from.line + 1, range.to.line - 1); | ||
cm.foldCode(CodeMirror.Pos(i, 0), null, "fold"); | ||
i = range.to.line + 1; | ||
} else { | ||
i++; | ||
} | ||
} | ||
} | ||
} | ||
cm.operation(function () { | ||
start = start === undefined ? cm.firstLine() : start; | ||
end = end || cm.lastLine(); | ||
foldLevel(level, start, end); | ||
}); | ||
}; | ||
|
||
CodeMirror.commands.unfoldAll = function (cm, from, to) { | ||
from = from || cm.firstLine(); | ||
to = to || cm.lastLine(); | ||
cm.operation(function () { | ||
var i, e; | ||
for (i = from, e = to; i <= e; i++) { | ||
if (cm.isFolded(i)) { cm.unfoldCode(i, {range: cm._lineFolds[i]}); } | ||
} | ||
}); | ||
}; | ||
|
||
CodeMirror.registerHelper("fold", "auto", function (cm, start) { | ||
var helpers = cm.getHelpers(start, "fold"), i, cur; | ||
//ensure mode helper is loaded if there is one | ||
var mode = cm.getMode().name; | ||
var modeHelper = CodeMirror.fold[mode]; | ||
if (modeHelper && helpers.indexOf(modeHelper) < 0) { | ||
helpers.push(modeHelper); | ||
} | ||
for (i = 0; i < helpers.length; i++) { | ||
cur = helpers[i](cm, start); | ||
if (cur) { return cur; } | ||
} | ||
}); | ||
}; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Files are generally mixed tabs/spaces. For consistency, we should stick to 4 spaces.