2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 const EXPAND_TAB = "devtools.editor.expandtab";
9 const TAB_SIZE = "devtools.editor.tabsize";
10 const DETECT_INDENT = "devtools.editor.detectindentation";
11 const DETECT_INDENT_MAX_LINES = 500;
14 * Get the number of indentation units to use to indent a "block"
15 * and a boolean indicating whether indentation must be done using tabs.
17 * @return {Object} an object of the form {indentUnit, indentWithTabs}.
18 * |indentUnit| is the number of indentation units to use
19 * to indent a "block".
20 * |indentWithTabs| is a boolean which is true if indentation
21 * should be done using tabs.
23 function getTabPrefs() {
24 const indentWithTabs = !Services.prefs.getBoolPref(EXPAND_TAB);
25 const indentUnit = Services.prefs.getIntPref(TAB_SIZE, 2);
26 return { indentUnit, indentWithTabs };
30 * Get the indentation to use in an editor, or return false if the user has
31 * asked for the indentation to be guessed from some text.
33 * @return {false | Object}
34 * Returns false if the "detect indentation" pref is set.
35 * If the pref is not set, returns an object of the same
36 * form as returned by getTabPrefs.
38 function getIndentationFromPrefs() {
39 const shouldDetect = Services.prefs.getBoolPref(DETECT_INDENT);
48 * Given a function that can iterate over some text, compute the indentation to
49 * use. This consults various prefs to arrive at a decision.
51 * @param {Function} iterFunc A function of three arguments:
52 * (start, end, callback); where |start| and |end| describe
53 * the range of text lines to examine, and |callback| is a function
54 * to be called with the text of each line.
56 * @return {Object} an object of the form {indentUnit, indentWithTabs}.
57 * |indentUnit| is the number of indentation units to use
58 * to indent a "block".
59 * |indentWithTabs| is a boolean which is true if indentation
60 * should be done using tabs.
62 function getIndentationFromIteration(iterFunc) {
63 let indentWithTabs = !Services.prefs.getBoolPref(EXPAND_TAB);
64 let indentUnit = Services.prefs.getIntPref(TAB_SIZE);
65 const shouldDetect = Services.prefs.getBoolPref(DETECT_INDENT);
68 const indent = detectIndentation(iterFunc);
70 indentWithTabs = indent.tabs;
71 indentUnit = indent.spaces ? indent.spaces : indentUnit;
75 return { indentUnit, indentWithTabs };
79 * A wrapper for @see getIndentationFromIteration which computes the
80 * indentation of a given string.
82 * @param {String} string the input text
83 * @return {Object} an object of the same form as returned by
84 * getIndentationFromIteration
86 function getIndentationFromString(string) {
87 const iteratorFn = function (start, end, callback) {
88 const split = string.split(/\r\n|\r|\n|\f/);
89 split.slice(start, end).forEach(callback);
91 return getIndentationFromIteration(iteratorFn);
95 * Detect the indentation used in an editor. Returns an object
96 * with 'tabs' - whether this is tab-indented and 'spaces' - the
97 * width of one indent in spaces. Or `null` if it's inconclusive.
99 function detectIndentation(textIteratorFn) {
100 // # spaces indent -> # lines with that indent
102 // indentation width of the last line we saw
104 // # of lines that start with a tab
106 // # of indented lines (non-zero indent)
109 textIteratorFn(0, DETECT_INDENT_MAX_LINES, text => {
110 if (text.startsWith("\t")) {
116 while (text[width] === " ") {
119 // don't count lines that are all spaces
120 if (width == text.length) {
128 // see how much this line is offset from the line above it
129 const indent = Math.abs(width - last);
130 if (indent > 1 && indent <= 8) {
131 spaces[indent] = (spaces[indent] || 0) + 1;
136 // this file is not indented at all
141 // mark as tabs if they start more than half the lines
142 if (tabs >= total / 2) {
143 return { tabs: true };
146 // find most frequent non-zero width difference between adjacent lines
147 let freqIndent = null,
149 for (let width in spaces) {
150 width = parseInt(width, 10);
151 const tally = spaces[width];
161 return { tabs: false, spaces: freqIndent };
164 exports.EXPAND_TAB = EXPAND_TAB;
165 exports.TAB_SIZE = TAB_SIZE;
166 exports.DETECT_INDENT = DETECT_INDENT;
167 exports.getTabPrefs = getTabPrefs;
168 exports.getIndentationFromPrefs = getIndentationFromPrefs;
169 exports.getIndentationFromIteration = getIndentationFromIteration;
170 exports.getIndentationFromString = getIndentationFromString;