forked from mirrors/gecko-dev
		
	 925311bc77
			
		
	
	
		925311bc77
		
	
	
	
	
		
			
			Differential Revision: https://phabricator.services.mozilla.com/D42300 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			172 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
|  * This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| const Services = require("Services");
 | |
| 
 | |
| const EXPAND_TAB = "devtools.editor.expandtab";
 | |
| const TAB_SIZE = "devtools.editor.tabsize";
 | |
| const DETECT_INDENT = "devtools.editor.detectindentation";
 | |
| const DETECT_INDENT_MAX_LINES = 500;
 | |
| 
 | |
| /**
 | |
|  * Get the number of indentation units to use to indent a "block"
 | |
|  * and a boolean indicating whether indentation must be done using tabs.
 | |
|  *
 | |
|  * @return {Object} an object of the form {indentUnit, indentWithTabs}.
 | |
|  *        |indentUnit| is the number of indentation units to use
 | |
|  *        to indent a "block".
 | |
|  *        |indentWithTabs| is a boolean which is true if indentation
 | |
|  *        should be done using tabs.
 | |
|  */
 | |
| function getTabPrefs() {
 | |
|   const indentWithTabs = !Services.prefs.getBoolPref(EXPAND_TAB);
 | |
|   const indentUnit = Services.prefs.getIntPref(TAB_SIZE);
 | |
|   return { indentUnit, indentWithTabs };
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Get the indentation to use in an editor, or return false if the user has
 | |
|  * asked for the indentation to be guessed from some text.
 | |
|  *
 | |
|  * @return {false | Object}
 | |
|  *        Returns false if the "detect indentation" pref is set.
 | |
|  *        If the pref is not set, returns an object of the same
 | |
|  *        form as returned by getTabPrefs.
 | |
|  */
 | |
| function getIndentationFromPrefs() {
 | |
|   const shouldDetect = Services.prefs.getBoolPref(DETECT_INDENT);
 | |
|   if (shouldDetect) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return getTabPrefs();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Given a function that can iterate over some text, compute the indentation to
 | |
|  * use.  This consults various prefs to arrive at a decision.
 | |
|  *
 | |
|  * @param {Function} iterFunc A function of three arguments:
 | |
|  *        (start, end, callback); where |start| and |end| describe
 | |
|  *        the range of text lines to examine, and |callback| is a function
 | |
|  *        to be called with the text of each line.
 | |
|  *
 | |
|  * @return {Object} an object of the form {indentUnit, indentWithTabs}.
 | |
|  *        |indentUnit| is the number of indentation units to use
 | |
|  *        to indent a "block".
 | |
|  *        |indentWithTabs| is a boolean which is true if indentation
 | |
|  *        should be done using tabs.
 | |
|  */
 | |
| function getIndentationFromIteration(iterFunc) {
 | |
|   let indentWithTabs = !Services.prefs.getBoolPref(EXPAND_TAB);
 | |
|   let indentUnit = Services.prefs.getIntPref(TAB_SIZE);
 | |
|   const shouldDetect = Services.prefs.getBoolPref(DETECT_INDENT);
 | |
| 
 | |
|   if (shouldDetect) {
 | |
|     const indent = detectIndentation(iterFunc);
 | |
|     if (indent != null) {
 | |
|       indentWithTabs = indent.tabs;
 | |
|       indentUnit = indent.spaces ? indent.spaces : indentUnit;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return { indentUnit, indentWithTabs };
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * A wrapper for @see getIndentationFromIteration which computes the
 | |
|  * indentation of a given string.
 | |
|  *
 | |
|  * @param {String} string the input text
 | |
|  * @return {Object} an object of the same form as returned by
 | |
|  *                  getIndentationFromIteration
 | |
|  */
 | |
| function getIndentationFromString(string) {
 | |
|   const iteratorFn = function(start, end, callback) {
 | |
|     const split = string.split(/\r\n|\r|\n|\f/);
 | |
|     split.slice(start, end).forEach(callback);
 | |
|   };
 | |
|   return getIndentationFromIteration(iteratorFn);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Detect the indentation used in an editor. Returns an object
 | |
|  * with 'tabs' - whether this is tab-indented and 'spaces' - the
 | |
|  * width of one indent in spaces. Or `null` if it's inconclusive.
 | |
|  */
 | |
| function detectIndentation(textIteratorFn) {
 | |
|   // # spaces indent -> # lines with that indent
 | |
|   const spaces = {};
 | |
|   // indentation width of the last line we saw
 | |
|   let last = 0;
 | |
|   // # of lines that start with a tab
 | |
|   let tabs = 0;
 | |
|   // # of indented lines (non-zero indent)
 | |
|   let total = 0;
 | |
| 
 | |
|   textIteratorFn(0, DETECT_INDENT_MAX_LINES, text => {
 | |
|     if (text.startsWith("\t")) {
 | |
|       tabs++;
 | |
|       total++;
 | |
|       return;
 | |
|     }
 | |
|     let width = 0;
 | |
|     while (text[width] === " ") {
 | |
|       width++;
 | |
|     }
 | |
|     // don't count lines that are all spaces
 | |
|     if (width == text.length) {
 | |
|       last = 0;
 | |
|       return;
 | |
|     }
 | |
|     if (width > 1) {
 | |
|       total++;
 | |
|     }
 | |
| 
 | |
|     // see how much this line is offset from the line above it
 | |
|     const indent = Math.abs(width - last);
 | |
|     if (indent > 1 && indent <= 8) {
 | |
|       spaces[indent] = (spaces[indent] || 0) + 1;
 | |
|     }
 | |
|     last = width;
 | |
|   });
 | |
| 
 | |
|   // this file is not indented at all
 | |
|   if (total == 0) {
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   // mark as tabs if they start more than half the lines
 | |
|   if (tabs >= total / 2) {
 | |
|     return { tabs: true };
 | |
|   }
 | |
| 
 | |
|   // find most frequent non-zero width difference between adjacent lines
 | |
|   let freqIndent = null,
 | |
|     max = 1;
 | |
|   for (let width in spaces) {
 | |
|     width = parseInt(width, 10);
 | |
|     const tally = spaces[width];
 | |
|     if (tally > max) {
 | |
|       max = tally;
 | |
|       freqIndent = width;
 | |
|     }
 | |
|   }
 | |
|   if (!freqIndent) {
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   return { tabs: false, spaces: freqIndent };
 | |
| }
 | |
| 
 | |
| exports.EXPAND_TAB = EXPAND_TAB;
 | |
| exports.TAB_SIZE = TAB_SIZE;
 | |
| exports.DETECT_INDENT = DETECT_INDENT;
 | |
| exports.getTabPrefs = getTabPrefs;
 | |
| exports.getIndentationFromPrefs = getIndentationFromPrefs;
 | |
| exports.getIndentationFromIteration = getIndentationFromIteration;
 | |
| exports.getIndentationFromString = getIndentationFromString;
 |