/
githubmirror
/
brackets
Обзор
Документация
Войти
/
githubmirror
/
brackets
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/extensions/default/PrefsCodeHints/main.js
428 строк
16 KB
ficristo
Move most inline jslint directives to config files
12 авг 2016, 10:22
12 авг 2016, 10:22
074382a
Код
Авторство
О чём код?
/* * Copyright (c) 2015 - 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. * */ define(function (require, exports, module) { "use strict"; // Load dependencies. var AppInit = brackets.getModule("utils/AppInit"), CodeHintManager = brackets.getModule("editor/CodeHintManager"), PreferencesManager = brackets.getModule("preferences/PreferencesManager"), StringMatch = brackets.getModule("utils/StringMatch"), ExtensionUtils = brackets.getModule("utils/ExtensionUtils"), EditorManager = brackets.getModule("editor/EditorManager"), LanguageManager = brackets.getModule("language/LanguageManager"), JSONUtils = brackets.getModule("language/JSONUtils"), Strings = brackets.getModule("strings"), ThemeManager = brackets.getModule("view/ThemeManager"), CodeInspection = brackets.getModule("language/CodeInspection"), _ = brackets.getModule("thirdparty/lodash"), languages = LanguageManager.getLanguages(), isPrefDocument = false, isPrefHintsEnabled = false; // Stores data of preferences used by Brackets and its core/thirdparty extensions. var data = { language: { type: "object", description: Strings.DESCRIPTION_LANGUAGE }, path: { type: "object", description: Strings.DESCRIPTION_PATH } }; var stringMatcherOptions = { preferPrefixMatches: true }; // List of parent keys for which no key hints will be provided. var parentKeyBlacklist = [ "language.fileExtensions", "language.fileNames", "path" ]; // Define a preference for code hinting. PreferencesManager.definePreference("codehint.PrefHints", "boolean", true, { description: Strings.DESCRIPTION_PREF_HINTS }); /** * @private * * Determines whether or not the current document is a preferences document and * user has enabled code hints * * @return {Boolean} */ function _isPrefHintsEnabled() { return (isPrefDocument && PreferencesManager.get("showCodeHints") !== false && PreferencesManager.get("codehint.PrefHints") !== false); } /** * @private * * Determines whether or not the name of a file matches the preferences files * * @param {!Document} document * @return {Boolean} */ function _isPrefDocument(document) { return (/^\.?brackets\.json$/).test(document.file._name); } // Set listeners on preference, editor and language changes. PreferencesManager.on("change", "showCodeHints", function () { isPrefHintsEnabled = _isPrefHintsEnabled(); }); PreferencesManager.on("change", "codehint.PrefHints", function () { isPrefHintsEnabled = _isPrefHintsEnabled(); }); EditorManager.on("activeEditorChange", function (e, editor) { if (editor) { isPrefDocument = _isPrefDocument(editor.document); } isPrefHintsEnabled = _isPrefHintsEnabled(); }); LanguageManager.on("languageAdded", function () { languages = LanguageManager.getLanguages(); }); /* * Returns a sorted and formatted list of hints with the query substring * highlighted. * * @param {Array.<Object>} hints - the list of hints to format * @param {string} query - querystring used for highlighting matched * portions of each hint * @return {Array.jQuery} sorted Array of jQuery DOM elements to insert */ function formatHints(hints, query) { var hasMetadata = hints.some(function (token) { return token.type || token.description; }); StringMatch.basicMatchSort(hints); return hints.map(function (token) { var $hintItem = $("<span>").addClass("brackets-pref-hints"), $hintObj = $("<span>").addClass("hint-obj"); // highlight the matched portion of each hint if (token.stringRanges) { token.stringRanges.forEach(function (item) { if (item.matched) { $hintObj.append($("<span>") .text(item.text) .addClass("matched-hint")); } else { $hintObj.append(item.text); } }); } else { $hintObj.text(token.value); } $hintItem.append($hintObj); if (hasMetadata) { $hintItem.data("type", token.type); if (token.description) { $hintItem.append($("<span>") .addClass("hint-description") .text(token.description)); } } return $hintItem; }); } /** * @constructor */ function PrefsCodeHints() { this.ctxInfo = null; // Add all the preferences defined except the excluded ones. var preferences = PreferencesManager.getAllPreferences(), preference; Object.keys(preferences).forEach(function (pref) { preference = preferences[pref]; if (preference.excludeFromHints) { return; } data[pref] = $.extend(data[pref], preference); // If child keys found, add them. if (preference.keys) { data[pref].keys = _.clone(preference.keys); } }); } /** * Determines whether or not hints are available in the current context * * @param {!Editor} editor * @param {String} implicitChar * @return {Boolean} */ PrefsCodeHints.prototype.hasHints = function (editor, implicitChar) { if (isPrefHintsEnabled && editor.getModeForSelection() === "application/json") { this.editor = editor; this.ctxInfo = JSONUtils.getContextInfo(this.editor, this.editor.getCursorPos(), true); if (this.ctxInfo && this.ctxInfo.tokenType) { // Disallow hints for blacklisted keys. if (this.ctxInfo.tokenType === JSONUtils.TOKEN_KEY && parentKeyBlacklist.indexOf(this.ctxInfo.parentKeyName) !== -1) { return false; } return true; } } return false; }; /** * Returns a list of hints available in the current context * * @param {String} implicitChar * @return {!{hints: Array.<jQueryObject>, match: string, selectInitial: boolean, handleWideResults: boolean}} */ PrefsCodeHints.prototype.getHints = function (implicitChar) { var hints = [], ctxInfo, query, keys, values, option = {type: null, description: null, values: null}; ctxInfo = this.ctxInfo = JSONUtils.getContextInfo(this.editor, this.editor.getCursorPos(), true); if (ctxInfo && ctxInfo.token) { query = JSONUtils.stripQuotes(ctxInfo.token.string.substr(0, ctxInfo.offset)).trim(); if (JSONUtils.regexAllowedChars.test(query)) { query = ""; } if (ctxInfo.tokenType === JSONUtils.TOKEN_KEY) { // Provide hints for keys // Get options for parent key else use general options. if (data[ctxInfo.parentKeyName] && data[ctxInfo.parentKeyName].keys) { keys = data[ctxInfo.parentKeyName].keys; } else if (ctxInfo.parentKeyName === "language") { keys = languages; option.type = "object"; } else { keys = data; } hints = $.map(Object.keys(keys), function (key) { if (ctxInfo.exclusionList.indexOf(key) === -1) { var match = StringMatch.stringMatch(key, query, stringMatcherOptions); if (match) { match.type = keys[key].type || option.type; match.description = keys[key].description || null; return match; } } }); } else if (ctxInfo.tokenType === JSONUtils.TOKEN_VALUE) { // Provide hints for values. // Get the key from data. if (data[ctxInfo.parentKeyName] && data[ctxInfo.parentKeyName].keys && data[ctxInfo.parentKeyName].keys[ctxInfo.keyName]) { option = data[ctxInfo.parentKeyName].keys[ctxInfo.keyName]; } else if (data[ctxInfo.keyName]) { option = data[ctxInfo.keyName]; } // Get the values depending on the selected key. if (option && option.type === "boolean") { values = ["false", "true"]; } else if (option && option.values && (["number", "string"].indexOf(option.type) !== -1 || (option.type === "array" && ctxInfo.isArray))) { values = option.values; } else if (ctxInfo.isArray && ctxInfo.keyName === "linting.prefer" && languages[ctxInfo.parentKeyName]) { values = CodeInspection.getProviderIDsForLanguage(ctxInfo.parentKeyName); } else if (ctxInfo.keyName === "themes.theme") { values = ThemeManager.getAllThemes().map(function (theme) { return theme.name; }); } else if (ctxInfo.parentKeyName === "language.fileExtensions" || ctxInfo.parentKeyName === "language.fileNames") { values = Object.keys(languages); } else { return null; } // Convert integers to strings, so StringMatch.stringMatch can match it. if (option.type === "number" || option.valueType === "number") { values = values.map(function (val) { return val.toString(); }); } // filter through the values. hints = $.map(values, function (value) { var match = StringMatch.stringMatch(value, query, stringMatcherOptions); if (match) { match.type = option.valueType || option.type; match.description = option.description || null; return match; } }); } return { hints: formatHints(hints, query), match: null, selectInitial: true, handleWideResults: false }; } return null; }; /** * Inserts a completion at current position * * @param {!String} completion * @return {Boolean} */ PrefsCodeHints.prototype.insertHint = function (completion) { var ctxInfo = JSONUtils.getContextInfo(this.editor, this.editor.getCursorPos(), false, true), pos = this.editor.getCursorPos(), start = {line: -1, ch: -1}, end = {line: -1, ch: -1}, startChar, quoteChar, type; if (completion.jquery) { type = completion.data("type"); completion = completion.find(".hint-obj").text(); } start.line = end.line = pos.line; if (ctxInfo.tokenType === JSONUtils.TOKEN_KEY) { startChar = ctxInfo.token.string.charAt(0); // Get the quote char. if (/^['"]$/.test(startChar)) { quoteChar = startChar; } // Put quotes around completion. completion = quoteChar + completion + quoteChar; // Append colon and braces, brackets and quotes. if (!ctxInfo.shouldReplace) { completion += ": "; switch (type) { case "object": completion += "{}"; break; case "array": completion += "[]"; break; case "string": completion += "\"\""; break; } } start.ch = pos.ch - ctxInfo.offset; end.ch = ctxInfo.token.end; this.editor.document.replaceRange(completion, start, end); // Place cursor inside the braces, brackets or quotes. if (["object", "array", "string"].indexOf(type) !== -1) { this.editor.setCursorPos(start.line, start.ch + completion.length - 1); // Start a new session in case it is an array or string. if (type !== "object" && !ctxInfo.shouldReplace) { return true; } return false; } return true; } else if (ctxInfo.tokenType === JSONUtils.TOKEN_VALUE) { // In case the current token is a white-space, start and end will be same. if (JSONUtils.regexAllowedChars.test(ctxInfo.token.string)) { start.ch = end.ch = pos.ch; } else if (ctxInfo.shouldReplace) { start.ch = ctxInfo.token.start; end.ch = ctxInfo.token.end; } else { start.ch = pos.ch - ctxInfo.offset; end.ch = ctxInfo.token.end; } if (!type || type === "string") { startChar = ctxInfo.token.string.charAt(0); if (/^['"]$/.test(startChar)) { quoteChar = startChar; } else { quoteChar = "\""; } completion = quoteChar + completion + quoteChar; } this.editor.document.replaceRange(completion, start, end); return false; } }; /** * @private * * `isPrefHintsEnabled` must be set to true to allow code hints * * It also loads a set of preferences that we need for running unit tests, this * will not break unit tests in case we add new preferences in the future. * * @param {!Document} testDocument * @param {!Object} testPreferences */ function _setupTestEnvironment(testDocument, testPreferences) { isPrefHintsEnabled = _isPrefDocument(testDocument); data = testPreferences; } AppInit.appReady(function () { var hintProvider = new PrefsCodeHints(); CodeHintManager.registerHintProvider(hintProvider, ["json"], 0); ExtensionUtils.loadStyleSheet(module, "styles/brackets-prefs-hints.css"); // For unit tests only. exports.hintProvider = hintProvider; exports._setupTestEnvironment = _setupTestEnvironment; }); });