/
githubmirror
/
brackets
Обзор
Документация
Войти
/
githubmirror
/
brackets
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/editor/EditorStatusBar.js
572 строки
22 KB
Nitesh Kumar
Sending Analytics Data for file Operation and LSP features (#14683)
09 апр 2019, 21:35
09 апр 2019, 21:35
5658e3d
Код
Авторство
О чём код?
/* * Copyright (c) 2013 - present Adobe Systems Incorporated. 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. * */ /** * Manages parts of the status bar related to the current editor's state. */ define(function (require, exports, module) { "use strict"; // Load dependent modules var _ = require("thirdparty/lodash"), AnimationUtils = require("utils/AnimationUtils"), AppInit = require("utils/AppInit"), DropdownButton = require("widgets/DropdownButton").DropdownButton, EditorManager = require("editor/EditorManager"), MainViewManager = require("view/MainViewManager"), Editor = require("editor/Editor").Editor, KeyEvent = require("utils/KeyEvent"), LanguageManager = require("language/LanguageManager"), PreferencesManager = require("preferences/PreferencesManager"), StatusBar = require("widgets/StatusBar"), Strings = require("strings"), FileUtils = require("file/FileUtils"), InMemoryFile = require("document/InMemoryFile"), Dialogs = require("widgets/Dialogs"), DefaultDialogs = require("widgets/DefaultDialogs"), ProjectManager = require("project/ProjectManager"), Async = require("utils/Async"), FileSystem = require("filesystem/FileSystem"), CommandManager = require("command/CommandManager"), Commands = require("command/Commands"), DocumentManager = require("document/DocumentManager"), StringUtils = require("utils/StringUtils"), HealthLogger = require("utils/HealthLogger"); var SupportedEncodingsText = require("text!supported-encodings.json"), SupportedEncodings = JSON.parse(SupportedEncodingsText); /* StatusBar indicators */ var languageSelect, // this is a DropdownButton instance encodingSelect, // this is a DropdownButton instance $cursorInfo, $fileInfo, $indentType, $indentWidthLabel, $indentWidthInput, $statusOverwrite; /** Special list item for the 'set as default' gesture in language switcher dropdown */ var LANGUAGE_SET_AS_DEFAULT = {}; /** * Determine string based on count * @param {number} number Count * @param {string} singularStr Singular string * @param {string} pluralStr Plural string * @return {string} Proper string to use for count */ function _formatCountable(number, singularStr, pluralStr) { return StringUtils.format(number > 1 ? pluralStr : singularStr, number); } /** * Update file mode * @param {Editor} editor Current editor */ function _updateLanguageInfo(editor) { var doc = editor.document, lang = doc.getLanguage(); // Show the current language as button title languageSelect.$button.text(lang.getName()); } /** * Update encoding * @param {Editor} editor Current editor */ function _updateEncodingInfo(editor) { var doc = editor.document; // Show the current encoding as button title if (!doc.file._encoding) { doc.file._encoding = "UTF-8"; } encodingSelect.$button.text(doc.file._encoding); } /** * Update file information * @param {Editor} editor Current editor */ function _updateFileInfo(editor) { var lines = editor.lineCount(); $fileInfo.text(_formatCountable(lines, Strings.STATUSBAR_LINE_COUNT_SINGULAR, Strings.STATUSBAR_LINE_COUNT_PLURAL)); } /** * Update indent type and size * @param {string} fullPath Path to file in current editor */ function _updateIndentType(fullPath) { var indentWithTabs = Editor.getUseTabChar(fullPath); $indentType.text(indentWithTabs ? Strings.STATUSBAR_TAB_SIZE : Strings.STATUSBAR_SPACES); $indentType.attr("title", indentWithTabs ? Strings.STATUSBAR_INDENT_TOOLTIP_SPACES : Strings.STATUSBAR_INDENT_TOOLTIP_TABS); $indentWidthLabel.attr("title", indentWithTabs ? Strings.STATUSBAR_INDENT_SIZE_TOOLTIP_TABS : Strings.STATUSBAR_INDENT_SIZE_TOOLTIP_SPACES); } /** * Get indent size based on type * @param {string} fullPath Path to file in current editor * @return {number} Indent size */ function _getIndentSize(fullPath) { return Editor.getUseTabChar(fullPath) ? Editor.getTabSize(fullPath) : Editor.getSpaceUnits(fullPath); } /** * Update indent size * @param {string} fullPath Path to file in current editor */ function _updateIndentSize(fullPath) { var size = _getIndentSize(fullPath); $indentWidthLabel.text(size); $indentWidthInput.val(size); } /** * Toggle indent type */ function _toggleIndentType() { var current = EditorManager.getActiveEditor(), fullPath = current && current.document.file.fullPath; Editor.setUseTabChar(!Editor.getUseTabChar(fullPath), fullPath); _updateIndentType(fullPath); _updateIndentSize(fullPath); } /** * Update cursor(s)/selection(s) information * @param {Event} event (unused) * @param {Editor} editor Current editor */ function _updateCursorInfo(event, editor) { editor = editor || EditorManager.getActiveEditor(); // compute columns, account for tab size var cursor = editor.getCursorPos(true); var cursorStr = StringUtils.format(Strings.STATUSBAR_CURSOR_POSITION, cursor.line + 1, cursor.ch + 1); var sels = editor.getSelections(), selStr = ""; if (sels.length > 1) { //Send analytics data for multicursor use HealthLogger.sendAnalyticsData( "multiCursor", "usage", "multiCursor", "use" ); selStr = StringUtils.format(Strings.STATUSBAR_SELECTION_MULTIPLE, sels.length); } else if (editor.hasSelection()) { var sel = sels[0]; if (sel.start.line !== sel.end.line) { var lines = sel.end.line - sel.start.line + 1; if (sel.end.ch === 0) { lines--; // end line is exclusive if ch is 0, inclusive otherwise } selStr = _formatCountable(lines, Strings.STATUSBAR_SELECTION_LINE_SINGULAR, Strings.STATUSBAR_SELECTION_LINE_PLURAL); } else { var cols = editor.getColOffset(sel.end) - editor.getColOffset(sel.start); // end ch is exclusive always selStr = _formatCountable(cols, Strings.STATUSBAR_SELECTION_CH_SINGULAR, Strings.STATUSBAR_SELECTION_CH_PLURAL); } } $cursorInfo.text(cursorStr + selStr); } /** * Change indent size * @param {string} fullPath Path to file in current editor * @param {string} value Size entered into status bar */ function _changeIndentWidth(fullPath, value) { $indentWidthLabel.removeClass("hidden"); $indentWidthInput.addClass("hidden"); // remove all event handlers from the input field $indentWidthInput.off("blur keyup"); // restore focus to the editor MainViewManager.focusActivePane(); var valInt = parseInt(value, 10); if (Editor.getUseTabChar(fullPath)) { if (!Editor.setTabSize(valInt, fullPath)) { return; // validation failed } } else { if (!Editor.setSpaceUnits(valInt, fullPath)) { return; // validation failed } } // update indicator _updateIndentSize(fullPath); // column position may change when tab size changes _updateCursorInfo(); } /** * Update insert/overwrite label * @param {Event} event (unused) * @param {Editor} editor Current editor * @param {string} newstate New overwrite state * @param {boolean=} doNotAnimate True if state should not be animated */ function _updateOverwriteLabel(event, editor, newstate, doNotAnimate) { if ($statusOverwrite.text() === (newstate ? Strings.STATUSBAR_OVERWRITE : Strings.STATUSBAR_INSERT)) { // label already up-to-date return; } $statusOverwrite.text(newstate ? Strings.STATUSBAR_OVERWRITE : Strings.STATUSBAR_INSERT); if (!doNotAnimate) { AnimationUtils.animateUsingClass($statusOverwrite[0], "flash", 1500); } } /** * Update insert/overwrite indicator * @param {Event} event (unused) */ function _updateEditorOverwriteMode(event) { var editor = EditorManager.getActiveEditor(), newstate = !editor._codeMirror.state.overwrite; // update label with no transition _updateOverwriteLabel(event, editor, newstate, true); editor.toggleOverwrite(newstate); } /** * Initialize insert/overwrite indicator * @param {Editor} currentEditor Current editor */ function _initOverwriteMode(currentEditor) { currentEditor.toggleOverwrite($statusOverwrite.text() === Strings.STATUSBAR_OVERWRITE); $statusOverwrite.attr("title", Strings.STATUSBAR_INSOVR_TOOLTIP); } /** * Handle active editor change event * @param {Event} event (unused) * @param {Editor} current Current editor * @param {Editor} previous Previous editor */ function _onActiveEditorChange(event, current, previous) { if (previous) { previous.off(".statusbar"); previous.document.off(".statusbar"); previous.document.releaseRef(); } if (!current) { StatusBar.hideAllPanes(); } else { var fullPath = current.document.file.fullPath; StatusBar.showAllPanes(); current.on("cursorActivity.statusbar", _updateCursorInfo); current.on("optionChange.statusbar", function () { _updateIndentType(fullPath); _updateIndentSize(fullPath); }); current.on("change.statusbar", function () { // async update to keep typing speed smooth window.setTimeout(function () { _updateFileInfo(current); }, 0); }); current.on("overwriteToggle.statusbar", _updateOverwriteLabel); current.document.addRef(); current.document.on("languageChanged.statusbar", function () { _updateLanguageInfo(current); }); _updateCursorInfo(null, current); _updateLanguageInfo(current); _updateEncodingInfo(current); _updateFileInfo(current); _initOverwriteMode(current); _updateIndentType(fullPath); _updateIndentSize(fullPath); } } /** * Populate the languageSelect DropdownButton's menu with all registered Languages */ function _populateLanguageDropdown() { // Get all non-binary languages var languages = _.values(LanguageManager.getLanguages()).filter(function (language) { return !language.isBinary(); }); // sort dropdown alphabetically languages.sort(function (a, b) { return a.getName().toLowerCase().localeCompare(b.getName().toLowerCase()); }); languageSelect.items = languages; // Add option to top of menu for persisting the override languageSelect.items.unshift("---"); languageSelect.items.unshift(LANGUAGE_SET_AS_DEFAULT); } /** * Change the encoding and reload the current document. * If passed then save the preferred encoding in state. */ function _changeEncodingAndReloadDoc(document) { var promise = document.reload(); promise.done(function (text, readTimestamp) { encodingSelect.$button.text(document.file._encoding); // Store the preferred encoding in the state var projectRoot = ProjectManager.getProjectRoot(), context = { location : { scope: "user", layer: "project", layerID: projectRoot.fullPath } }; var encoding = PreferencesManager.getViewState("encoding", context); encoding[document.file.fullPath] = document.file._encoding; PreferencesManager.setViewState("encoding", encoding, context); }); promise.fail(function (error) { console.log("Error reloading contents of " + document.file.fullPath, error); }); } /** * Populate the encodingSelect DropdownButton's menu with all registered encodings */ function _populateEncodingDropdown() { encodingSelect.items = SupportedEncodings; } /** * Initialize */ function _init() { $cursorInfo = $("#status-cursor"); $fileInfo = $("#status-file"); $indentType = $("#indent-type"); $indentWidthLabel = $("#indent-width-label"); $indentWidthInput = $("#indent-width-input"); $statusOverwrite = $("#status-overwrite"); languageSelect = new DropdownButton("", [], function (item, index) { var document = EditorManager.getActiveEditor().document, defaultLang = LanguageManager.getLanguageForPath(document.file.fullPath, true); if (item === LANGUAGE_SET_AS_DEFAULT) { var label = _.escape(StringUtils.format(Strings.STATUSBAR_SET_DEFAULT_LANG, LanguageManager.getCompoundFileExtension(document.file.fullPath))); return { html: label, enabled: document.getLanguage() !== defaultLang }; } var html = _.escape(item.getName()); // Show indicators for currently selected & default languages for the current file if (item === defaultLang) { html += " <span class='default-language'>" + Strings.STATUSBAR_DEFAULT_LANG + "</span>"; } if (item === document.getLanguage()) { html = "<span class='checked-language'></span>" + html; } return html; }); languageSelect.dropdownExtraClasses = "dropdown-status-bar"; languageSelect.$button.addClass("btn-status-bar"); $("#status-language").append(languageSelect.$button); languageSelect.$button.attr("title", Strings.STATUSBAR_LANG_TOOLTIP); encodingSelect = new DropdownButton("", [], function (item, index) { var document = EditorManager.getActiveEditor().document; var html = _.escape(item); // Show indicators for currently selected & default languages for the current file if (item === "UTF-8") { html += " <span class='default-language'>" + Strings.STATUSBAR_DEFAULT_LANG + "</span>"; } if (item === document.file._encoding) { html = "<span class='checked-language'></span>" + html; } return html; }); encodingSelect.dropdownExtraClasses = "dropdown-status-bar"; encodingSelect.$button.addClass("btn-status-bar"); $("#status-encoding").append(encodingSelect.$button); encodingSelect.$button.attr("title", Strings.STATUSBAR_ENCODING_TOOLTIP); // indentation event handlers $indentType.on("click", _toggleIndentType); $indentWidthLabel .on("click", function () { // update the input value before displaying var fullPath = EditorManager.getActiveEditor().document.file.fullPath; $indentWidthInput.val(_getIndentSize(fullPath)); $indentWidthLabel.addClass("hidden"); $indentWidthInput.removeClass("hidden"); $indentWidthInput.focus(); $indentWidthInput .on("blur", function () { _changeIndentWidth(fullPath, $indentWidthInput.val()); }) .on("keyup", function (event) { if (event.keyCode === KeyEvent.DOM_VK_RETURN) { $indentWidthInput.blur(); } else if (event.keyCode === KeyEvent.DOM_VK_ESCAPE) { _changeIndentWidth(fullPath, false); } }); }); $indentWidthInput.focus(function () { $indentWidthInput.select(); }); // Language select change handler languageSelect.on("select", function (e, lang) { var document = EditorManager.getActiveEditor().document, fullPath = document.file.fullPath; var fileType = (document.file instanceof InMemoryFile) ? "newFile" : "existingFile", filelanguageName = lang ? lang._name : ""; HealthLogger.sendAnalyticsData( HealthLogger.commonStrings.USAGE + HealthLogger.commonStrings.LANGUAGE_CHANGE + filelanguageName + fileType, HealthLogger.commonStrings.USAGE, HealthLogger.commonStrings.LANGUAGE_CHANGE, filelanguageName.toLowerCase(), fileType ); if (lang === LANGUAGE_SET_AS_DEFAULT) { // Set file's current language in preferences as a file extension override (only enabled if not default already) var fileExtensionMap = PreferencesManager.get("language.fileExtensions"); fileExtensionMap[LanguageManager.getCompoundFileExtension(fullPath)] = document.getLanguage().getId(); PreferencesManager.set("language.fileExtensions", fileExtensionMap); } else { // Set selected language as a path override for just this one file (not persisted) var defaultLang = LanguageManager.getLanguageForPath(fullPath, true); // if default language selected, pass null to clear the override LanguageManager.setLanguageOverrideForPath(fullPath, lang === defaultLang ? null : lang); } }); // Encoding select change handler encodingSelect.on("select", function (e, encoding) { var document = EditorManager.getActiveEditor().document, originalPath = document.file.fullPath, originalEncoding = document.file._encoding; document.file._encoding = encoding; if (!(document.file instanceof InMemoryFile) && document.isDirty) { CommandManager.execute(Commands.FILE_SAVE_AS, {doc: document}).done(function () { var doc = DocumentManager.getCurrentDocument(); if (originalPath === doc.file.fullPath) { _changeEncodingAndReloadDoc(doc); } else { document.file._encoding = originalEncoding; } }).fail(function () { document.file._encoding = originalEncoding; }); } else if (document.file instanceof InMemoryFile) { encodingSelect.$button.text(encoding); } else if (!document.isDirty) { _changeEncodingAndReloadDoc(document); } }); $statusOverwrite.on("click", _updateEditorOverwriteMode); } // Initialize: status bar focused listener EditorManager.on("activeEditorChange", _onActiveEditorChange); function _checkFileExistance(filePath, index, encoding) { var deferred = new $.Deferred(), fileEntry = FileSystem.getFileForPath(filePath); fileEntry.exists(function (err, exists) { if (!err && exists) { deferred.resolve(); } else { delete encoding[filePath]; deferred.reject(); } }); return deferred.promise(); } ProjectManager.on("projectOpen", function () { var projectRoot = ProjectManager.getProjectRoot(), context = { location : { scope: "user", layer: "project", layerID: projectRoot.fullPath } }; var encoding = PreferencesManager.getViewState("encoding", context); if (!encoding) { encoding = {}; PreferencesManager.setViewState("encoding", encoding, context); } Async.doSequentially(Object.keys(encoding), function (filePath, index) { return _checkFileExistance(filePath, index, encoding); }, false) .always(function () { PreferencesManager.setViewState("encoding", encoding, context); }); }); AppInit.htmlReady(_init); AppInit.appReady(function () { // Populate language switcher with all languages after startup; update it later if this set changes _populateLanguageDropdown(); _populateEncodingDropdown(); LanguageManager.on("languageAdded languageModified", _populateLanguageDropdown); _onActiveEditorChange(null, EditorManager.getActiveEditor(), null); StatusBar.show(); }); });