diff --git a/src/base-config/keyboard.json b/src/base-config/keyboard.json index aa9c67cd4d5..07611fea90c 100644 --- a/src/base-config/keyboard.json +++ b/src/base-config/keyboard.json @@ -281,6 +281,9 @@ "navigate.gotoDefinition": [ "Ctrl-T" ], + "navigate.gotoDefinitionInProject": [ + "Ctrl-Shift-T" + ], "navigate.jumptoDefinition": [ "Ctrl-J" ], diff --git a/src/command/Commands.js b/src/command/Commands.js index dc147eeac6f..e5811dd229c 100644 --- a/src/command/Commands.js +++ b/src/command/Commands.js @@ -125,6 +125,7 @@ define(function (require, exports, module) { exports.NAVIGATE_QUICK_OPEN = "navigate.quickOpen"; // QuickOpen.js doFileSearch() exports.NAVIGATE_JUMPTO_DEFINITION = "navigate.jumptoDefinition"; // JumpToDefManager.js _doJumpToDef() exports.NAVIGATE_GOTO_DEFINITION = "navigate.gotoDefinition"; // QuickOpen.js doDefinitionSearch() + exports.NAVIGATE_GOTO_DEFINITION_PROJECT = "navigate.gotoDefinitionInProject"; // QuickOpen.js doDefinitionSearchInProject() exports.NAVIGATE_GOTO_LINE = "navigate.gotoLine"; // QuickOpen.js doGotoLine() exports.NAVIGATE_GOTO_FIRST_PROBLEM = "navigate.gotoFirstProblem"; // CodeInspection.js handleGotoFirstProblem() exports.TOGGLE_QUICK_EDIT = "navigate.toggleQuickEdit"; // EditorManager.js _toggleInlineWidget() diff --git a/src/command/DefaultMenus.js b/src/command/DefaultMenus.js index 2eb6f871925..912d9eaa306 100644 --- a/src/command/DefaultMenus.js +++ b/src/command/DefaultMenus.js @@ -172,6 +172,7 @@ define(function (require, exports, module) { menu.addMenuItem(Commands.NAVIGATE_QUICK_OPEN); menu.addMenuItem(Commands.NAVIGATE_GOTO_LINE); menu.addMenuItem(Commands.NAVIGATE_GOTO_DEFINITION); + menu.addMenuItem(Commands.NAVIGATE_GOTO_DEFINITION_PROJECT); menu.addMenuItem(Commands.NAVIGATE_JUMPTO_DEFINITION); menu.addMenuItem(Commands.NAVIGATE_GOTO_FIRST_PROBLEM); menu.addMenuDivider(); diff --git a/src/extensions/default/PhpTooling/PHPSymbolProviders.js b/src/extensions/default/PhpTooling/PHPSymbolProviders.js new file mode 100644 index 00000000000..8517e1db574 --- /dev/null +++ b/src/extensions/default/PhpTooling/PHPSymbolProviders.js @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2019 - present Adobe. 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. + * + */ + +/*jslint regexp: true */ + +define(function (require, exports, module) { + "use strict"; + + var EditorManager = brackets.getModule("editor/EditorManager"), + QuickOpen = brackets.getModule("search/QuickOpen"), + Commands = brackets.getModule("command/Commands"), + CommandManager = brackets.getModule("command/CommandManager"), + PathConverters = brackets.getModule("languageTools/PathConverters"); + + var SymbolKind = QuickOpen.SymbolKind; + + function convertRangePosToEditorPos(rangePos) { + return { + line: rangePos.line, + ch: rangePos.character + }; + } + + function SymbolInformation(label, fullPath, selectionRange, type, scope, isDocumentSymbolRequest) { + this.label = label; + this.fullPath = fullPath; + this.selectionRange = selectionRange; + this.type = type; + this.scope = scope; + this.isDocumentSymbolRequest = isDocumentSymbolRequest; + } + + function createList(list, isDocumentSymbolRequest) { + var newlist = []; + for (var i = 0; i < list.length; i++) { + var symbolInfo = list[i], + label = symbolInfo.name, + type = SymbolKind[symbolInfo.kind.toString()], + fullPath = null, + selectionRange = null, + scope = symbolInfo.containerName, + range = null; + + if (!isDocumentSymbolRequest) { + fullPath = PathConverters.uriToPath(symbolInfo.location.uri); + } else { + if (symbolInfo.selectionRange) { + range = symbolInfo.selectionRange; + selectionRange = { + from: convertRangePosToEditorPos(range.start), + to: convertRangePosToEditorPos(range.end) + }; + } + } + + if (!selectionRange) { + range = symbolInfo.location.range; + selectionRange = { + from: convertRangePosToEditorPos(range.start), + to: convertRangePosToEditorPos(range.end) + }; + } + + newlist.push(new SymbolInformation(label, fullPath, selectionRange, type, scope, isDocumentSymbolRequest)); + } + + return newlist; + } + + function transFormToSymbolList(query, matcher, results, isDocumentSymbolRequest) { + var list = createList(results, isDocumentSymbolRequest); + + // Filter and rank how good each match is + var filteredList = $.map(list, function (symbolInfo) { + var searchResult = matcher.match(symbolInfo.label, query); + if (searchResult) { + searchResult.symbolInfo = symbolInfo; + } + return searchResult; + }); + + // Sort based on ranking & basic alphabetical order + QuickOpen.basicMatchSort(filteredList); + + return filteredList; + } + + /** + * Provider for Document Symbols + */ + function DocumentSymbolsProvider(client) { + this.client = client; + } + + DocumentSymbolsProvider.prototype.match = function (query) { + return query.startsWith("@"); + }; + + DocumentSymbolsProvider.prototype.search = function (query, matcher) { + if (!this.client) { + return $.Deferred().reject(); + } + + var serverCapabilities = this.client.getServerCapabilities(); + if (!serverCapabilities || !serverCapabilities.documentSymbolProvider) { + return $.Deferred().reject(); + } + + var editor = EditorManager.getActiveEditor(), + docPath = editor.document.file._path, + retval = $.Deferred(); + query = query.slice(1); + + this.client.requestSymbolsForDocument({ + filePath: docPath + }).done(function (results) { + var resultList = transFormToSymbolList(query, matcher, results, true); + retval.resolve(resultList); + }); + + return retval; + }; + + DocumentSymbolsProvider.prototype.itemFocus = function (selectedItem, query, explicit) { + if (!selectedItem || (query.length < 2 && !explicit)) { + return; + } + + var range = selectedItem.symbolInfo.selectionRange; + EditorManager.getCurrentFullEditor().setSelection(range.from, range.to, true); + }; + + DocumentSymbolsProvider.prototype.itemSelect = function (selectedItem, query) { + this.itemFocus(selectedItem, query, true); + }; + + DocumentSymbolsProvider.prototype.resultsFormatter = function (item, query) { + var displayName = QuickOpen.highlightMatch(item); + query = query.slice(1); + + if (item.symbolInfo.scope) { + return "
  • " + displayName + " (" + item.symbolInfo.type + ")" + "
    " + item.symbolInfo.scope + "
  • "; + } + return "
  • " + displayName + " (" + item.symbolInfo.type + ")" + "
  • "; + }; + + /** + * Provider for Project Symbols + */ + function ProjectSymbolsProvider(client) { + this.client = client; + } + + ProjectSymbolsProvider.prototype.match = function (query) { + return query.startsWith("#"); + }; + + ProjectSymbolsProvider.prototype.search = function (query, matcher) { + if (!this.client) { + return $.Deferred().reject(); + } + + var serverCapabilities = this.client.getServerCapabilities(); + if (!serverCapabilities || !serverCapabilities.workspaceSymbolProvider) { + return $.Deferred().reject(); + } + + var retval = $.Deferred(); + query = query.slice(1); + + this.client.requestSymbolsForWorkspace({ + query: query + }).done(function (results) { + var resultList = transFormToSymbolList(query, matcher, results); + retval.resolve(resultList); + }); + + return retval; + }; + + ProjectSymbolsProvider.prototype.itemFocus = function (selectedItem, query, explicit) { + if (!selectedItem || (query.length < 2 && !explicit)) { + return; + } + }; + + ProjectSymbolsProvider.prototype.itemSelect = function (selectedItem, query) { + var fullPath = selectedItem.symbolInfo.fullPath, + range = selectedItem.symbolInfo.selectionRange; + + if (fullPath) { + CommandManager.execute(Commands.CMD_ADD_TO_WORKINGSET_AND_OPEN, { + fullPath: fullPath + }) + .done(function () { + if (range.from) { + var editor = EditorManager.getCurrentFullEditor(); + editor.setCursorPos(range.from.line, range.from.ch, true); + } + }); + } + }; + + ProjectSymbolsProvider.prototype.resultsFormatter = function (item, query) { + var displayName = QuickOpen.highlightMatch(item); + query = query.slice(1); + + if (item.symbolInfo.scope) { + return "
  • " + displayName + " (" + item.symbolInfo.type + ")" + "
    " + item.symbolInfo.scope + "

    " + item.symbolInfo.fullPath + "
  • "; + } + return "
  • " + displayName + " (" + item.symbolInfo.type + ")" + "

    " + item.symbolInfo.fullPath + "
  • "; + }; + + exports.SymbolProviders = { + DocumentSymbolsProvider: DocumentSymbolsProvider, + ProjectSymbolsProvider: ProjectSymbolsProvider + }; +}); diff --git a/src/extensions/default/PhpTooling/main.js b/src/extensions/default/PhpTooling/main.js index d2ecc966bb1..a2de7502d50 100755 --- a/src/extensions/default/PhpTooling/main.js +++ b/src/extensions/default/PhpTooling/main.js @@ -30,11 +30,13 @@ define(function (require, exports, module) { EditorManager = brackets.getModule("editor/EditorManager"), LanguageManager = brackets.getModule("language/LanguageManager"), CodeHintManager = brackets.getModule("editor/CodeHintManager"), + QuickOpen = brackets.getModule("search/QuickOpen"), ParameterHintManager = brackets.getModule("features/ParameterHintsManager"), JumpToDefManager = brackets.getModule("features/JumpToDefManager"), CodeInspection = brackets.getModule("language/CodeInspection"), DefaultProviders = brackets.getModule("languageTools/DefaultProviders"), CodeHintsProvider = require("CodeHintsProvider").CodeHintsProvider, + SymbolProviders = require("PHPSymbolProviders").SymbolProviders, DefaultEventHandlers = brackets.getModule("languageTools/DefaultEventHandlers"), PreferencesManager = brackets.getModule("preferences/PreferencesManager"), Strings = brackets.getModule("strings"), @@ -61,7 +63,9 @@ define(function (require, exports, module) { chProvider, phProvider, lProvider, - jdProvider; + jdProvider, + dSymProvider, + pSymProvider; PreferencesManager.definePreference("php", "object", phpConfig, { description: Strings.DESCRIPTION_PHP_TOOLING_CONFIGURATION @@ -102,6 +106,8 @@ define(function (require, exports, module) { phProvider = new DefaultProviders.ParameterHintsProvider(_client), lProvider = new DefaultProviders.LintingProvider(_client), jdProvider = new DefaultProviders.JumpToDefProvider(_client); + dSymProvider = new SymbolProviders.DocumentSymbolsProvider(_client); + pSymProvider = new SymbolProviders.ProjectSymbolsProvider(_client); JumpToDefManager.registerJumpToDefProvider(jdProvider, ["php"], 0); CodeHintManager.registerHintProvider(chProvider, ["php"], 0); @@ -110,6 +116,30 @@ define(function (require, exports, module) { name: "", scanFileAsync: lProvider.getInspectionResultsAsync.bind(lProvider) }); + //Attach plugin for Document Symbols + QuickOpen.addQuickOpenPlugin({ + name: "PHP Document Symbols", + label: Strings.CMD_FIND_DOCUMENT_SYMBOLS + "\u2026", + languageIds: ["php"], + search: dSymProvider.search.bind(dSymProvider), + match: dSymProvider.match.bind(dSymProvider), + itemFocus: dSymProvider.itemFocus.bind(dSymProvider), + itemSelect: dSymProvider.itemSelect.bind(dSymProvider), + resultsFormatter: dSymProvider.resultsFormatter.bind(dSymProvider) + }); + CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION).setEnabled(true); + //Attach plugin for Project Symbols + QuickOpen.addQuickOpenPlugin({ + name: "PHP Project Symbols", + label: Strings.CMD_FIND_PROJECT_SYMBOLS + "\u2026", + languageIds: ["php"], + search: pSymProvider.search.bind(pSymProvider), + match: pSymProvider.match.bind(pSymProvider), + itemFocus: pSymProvider.itemFocus.bind(pSymProvider), + itemSelect: pSymProvider.itemSelect.bind(pSymProvider), + resultsFormatter: pSymProvider.resultsFormatter.bind(pSymProvider) + }); + CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION_PROJECT).setEnabled(true); _client.addOnCodeInspection(lProvider.setInspectionResults.bind(lProvider)); } diff --git a/src/extensions/default/PhpTooling/unittest-files/test/test4.php b/src/extensions/default/PhpTooling/unittest-files/test/test4.php new file mode 100644 index 00000000000..7caa64749ed --- /dev/null +++ b/src/extensions/default/PhpTooling/unittest-files/test/test4.php @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/src/extensions/default/PhpTooling/unittests.js b/src/extensions/default/PhpTooling/unittests.js index 722acc5b993..3724743b5e7 100644 --- a/src/extensions/default/PhpTooling/unittests.js +++ b/src/extensions/default/PhpTooling/unittests.js @@ -27,7 +27,8 @@ define(function (require, exports, module) { var SpecRunnerUtils = brackets.getModule("spec/SpecRunnerUtils"), Strings = brackets.getModule("strings"), FileUtils = brackets.getModule("file/FileUtils"), - StringUtils = brackets.getModule("utils/StringUtils"); + StringUtils = brackets.getModule("utils/StringUtils"), + StringMatch = brackets.getModule("utils/StringMatch"); var extensionRequire, phpToolingExtension, @@ -37,11 +38,13 @@ define(function (require, exports, module) { CodeInspection, DefaultProviders, CodeHintsProvider, + SymbolProviders, EditorManager, testEditor, testFolder = FileUtils.getNativeModuleDirectoryPath(module) + "/unittest-files/", testFile1 = "test1.php", - testFile2 = "test2.php"; + testFile2 = "test2.php", + testFile4 = "test4.php"; describe("PhpTooling", function () { @@ -59,8 +62,11 @@ define(function (require, exports, module) { afterLast(function () { waitsForDone(phpToolingExtension.getClient().stop(), "stoping php server"); + testEditor = null; + testWindow = null; + brackets = null; + EditorManager = null; SpecRunnerUtils.closeTestWindow(); - testWindow = null; }); @@ -71,6 +77,7 @@ define(function (require, exports, module) { CodeInspection.toggleEnabled(true); DefaultProviders = testWindow.brackets.getModule("languageTools/DefaultProviders"); CodeHintsProvider = extensionRequire("CodeHintsProvider"); + SymbolProviders = extensionRequire("PHPSymbolProviders").SymbolProviders; }); /** @@ -316,6 +323,56 @@ define(function (require, exports, module) { }); } + /** + * Show the document/project symbols for a language type. + * + * @param {SymbolProvider} provider The symbol provider to use for the request. + * @param {string} query The query string for the request. + * @param {Array} expectedSymbols Expected results for the request. + */ + function expectSymbols(provider, query, expectedSymbols) { + var requestStatus = null; + var request, + matcher; + + runs(function () { + matcher = new StringMatch.StringMatcher(); + request = new provider(phpToolingExtension.getClient()).search(query, matcher); + request.done(function (status) { + requestStatus = status; + }); + + waitsForDone(request, "Expected Symbols did not resolve", 3000); + }); + + if (expectedSymbols === []) { + expect(requestStatus).toBe([]); + return; + } + + function matchSymbols(symbols) { + var n = symbols.length > 4 ? 4 : symbols.length, + i; + + for (i = 0; i < n; i++) { + var symbolInfo = symbols[i].symbolInfo; + expect(symbolInfo.label).toBe(expectedSymbols[i].label); + expect(symbolInfo.type).toBe(expectedSymbols[i].type); + expect(symbolInfo.scope).toBe(expectedSymbols[i].scope); + + if (expectedSymbols[i].fullPath === null) { + expect(symbolInfo.fullPath).toBe(null); + } else { + expect(symbolInfo.fullPath.includes(expectedSymbols[i].fullPath)).toBe(true); + } + } + + } + runs(function() { + matchSymbols(requestStatus); + }); + } + /** * Trigger a jump to definition, and verify that the editor jumped to * the expected location. The new location is the variable definition @@ -495,5 +552,72 @@ define(function (require, exports, module) { editorJumped({line: 4, ch: 0, file: "test3.php"}); }); }); + + it("should fetch document symbols for a given file", function () { + waitsForDone(SpecRunnerUtils.openProjectFiles([testFile4]), "open test file: " + testFile4); + runs(function () { + var provider = SymbolProviders.DocumentSymbolsProvider, + query = "@", + expectedSymbols = [ + { + "label": "constantValue", + "fullPath": null, + "type": "Constant", + "scope": "MyClass" + }, + { + "label": "MyClass", + "fullPath": null, + "type": "Class", + "scope": "" + }, + { + "label": "publicFunction", + "fullPath": null, + "type": "Method", + "scope": "MyClass" + }, + { + "label": "publicValue", + "fullPath": null, + "type": "Property", + "scope": "MyClass" + } + ]; + expectSymbols(provider, query, expectedSymbols); + }); + }); + + it("should fetch no document symbols for a given file", function () { + waitsForDone(SpecRunnerUtils.openProjectFiles([testFile1]), "open test file: " + testFile1); + runs(function () { + var provider = SymbolProviders.DocumentSymbolsProvider, + query = "@", + expectedSymbols = []; + expectSymbols(provider, query, expectedSymbols); + }); + }); + + it("should fetch project symbols for a given file", function () { + runs(function () { + var provider = SymbolProviders.ProjectSymbolsProvider, + query = "#as", + expectedSymbols = [ + { + "label": "MyClass", + "fullPath": "test4.php", + "type": "Class", + "scope": "" + }, + { + "label": "TestCase", + "fullPath": "test2.php", + "type": "Class", + "scope": "test" + } + ]; + expectSymbols(provider, query, expectedSymbols); + }); + }); }); }); diff --git a/src/languageTools/LanguageClientWrapper.js b/src/languageTools/LanguageClientWrapper.js index 9cd7a5a42b1..edd9a6b9602 100644 --- a/src/languageTools/LanguageClientWrapper.js +++ b/src/languageTools/LanguageClientWrapper.js @@ -110,7 +110,7 @@ define(function (require, exports, module) { } case ToolingInfo.FEATURES.PROJECT_SYMBOLS: { - if (params && params.query && typeof params.query === "string") { + if (hasValidProp(params, "query") && typeof params.query === "string") { validatedParams = params; } break; diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index a3afc09921b..f4874acbd6c 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -419,6 +419,7 @@ define({ "CMD_QUICK_OPEN" : "Quick Open", "CMD_GOTO_LINE" : "Go to Line", "CMD_GOTO_DEFINITION" : "Quick Find Definition", + "CMD_GOTO_DEFINITION_PROJECT" : "Quick Find Definition in Project", "CMD_GOTO_FIRST_PROBLEM" : "Go to First Problem", "CMD_TOGGLE_QUICK_EDIT" : "Quick Edit", "CMD_TOGGLE_QUICK_DOCS" : "Quick Docs", @@ -888,5 +889,7 @@ define({ "OPEN_PREFERENNCES" : "Open Preferences", //Strings for LanguageTools Preferences - "LANGUAGE_TOOLS_PREFERENCES" : "Preferences for Language Tools" + "LANGUAGE_TOOLS_PREFERENCES" : "Preferences for Language Tools", + "CMD_FIND_DOCUMENT_SYMBOLS" : "Find Document Symbols", + "CMD_FIND_PROJECT_SYMBOLS" : "Find Project Symbols" }); diff --git a/src/search/QuickOpen.js b/src/search/QuickOpen.js index 5c9f105292f..98336ac40a0 100644 --- a/src/search/QuickOpen.js +++ b/src/search/QuickOpen.js @@ -44,8 +44,40 @@ define(function (require, exports, module) { LanguageManager = require("language/LanguageManager"), ModalBar = require("widgets/ModalBar").ModalBar, QuickSearchField = require("search/QuickSearchField").QuickSearchField, - StringMatch = require("utils/StringMatch"); - + StringMatch = require("utils/StringMatch"), + ProviderRegistrationHandler = require("features/PriorityBasedRegistration").RegistrationHandler; + + var _providerRegistrationHandler = new ProviderRegistrationHandler(), + _registerQuickOpenProvider = _providerRegistrationHandler.registerProvider.bind(_providerRegistrationHandler); + + var SymbolKind = { + "1": "File", + "2": "Module", + "3": "Namespace", + "4": "Package", + "5": "Class", + "6": "Method", + "7": "Property", + "8": "Field", + "9": "Constructor", + "10": "Enum", + "11": "Interface", + "12": "Function", + "13": "Variable", + "14": "Constant", + "15": "String", + "16": "Number", + "17": "Boolean", + "18": "Array", + "19": "Object", + "20": "Key", + "21": "Null", + "22": "EnumMember", + "23": "Struct", + "24": "Event", + "25": "Operator", + "26": "TypeParameter" + }; /** * The regular expression to check the cursor position @@ -53,12 +85,6 @@ define(function (require, exports, module) { */ var CURSOR_POS_EXP = new RegExp(":([^,]+)?(,(.+)?)?"); - /** - * List of plugins - * @type {Array.} - */ - var plugins = []; - /** * Current plugin * @type {QuickOpenPlugin} @@ -77,6 +103,22 @@ define(function (require, exports, module) { */ var _curDialog; + /** + * Helper function to get the plugins based on the type of the current document. + * @private + * @returns {Array} Returns the plugings based on the languageId of the current document. + */ + function _getPluginsForCurrentContext() { + var curDoc = DocumentManager.getCurrentDocument(); + + if (curDoc) { + var languageId = curDoc.getLanguage().getId(); + return _providerRegistrationHandler.getProvidersForLanguageId(languageId); + } + + return _providerRegistrationHandler.getProvidersForLanguageId(); //plugins registered for all + } + /** * Defines API for new QuickOpen plug-ins */ @@ -132,18 +174,22 @@ define(function (require, exports, module) { * cancels Quick Open (via Esc), those changes are automatically reverted. */ function addQuickOpenPlugin(pluginDef) { - plugins.push(new QuickOpenPlugin( - pluginDef.name, - pluginDef.languageIds, - pluginDef.done, - pluginDef.search, - pluginDef.match, - pluginDef.itemFocus, - pluginDef.itemSelect, - pluginDef.resultsFormatter, - pluginDef.matcherOptions, - pluginDef.label - )); + var quickOpenProvider = new QuickOpenPlugin( + pluginDef.name, + pluginDef.languageIds, + pluginDef.done, + pluginDef.search, + pluginDef.match, + pluginDef.itemFocus, + pluginDef.itemSelect, + pluginDef.resultsFormatter, + pluginDef.matcherOptions, + pluginDef.label + ), + providerLanguageIds = pluginDef.languageIds.length ? pluginDef.languageIds : ["all"], + providerPriority = pluginDef.priority || 0; + + _registerQuickOpenProvider(quickOpenProvider, providerLanguageIds, providerPriority); } /** @@ -350,9 +396,10 @@ define(function (require, exports, module) { this.closePromise = modalBarClosePromise; this.isOpen = false; - var i; + var i, + plugins = _getPluginsForCurrentContext(); for (i = 0; i < plugins.length; i++) { - var plugin = plugins[i]; + var plugin = plugins[i].provider; if (plugin.done) { plugin.done(); } @@ -455,17 +502,11 @@ define(function (require, exports, module) { return { error: null }; } - // Try to invoke a search plugin - var curDoc = DocumentManager.getCurrentDocument(), languageId; - if (curDoc) { - languageId = curDoc.getLanguage().getId(); - } - - var i; + var i, + plugins = _getPluginsForCurrentContext(); for (i = 0; i < plugins.length; i++) { - var plugin = plugins[i]; - var languageIdMatch = plugin.languageIds.length === 0 || plugin.languageIds.indexOf(languageId) !== -1; - if (languageIdMatch && plugin.match(query)) { + var plugin = plugins[i].provider; + if(plugin.match(query)) { currentPlugin = plugin; // Look up the StringMatcher for this plugin. @@ -613,6 +654,9 @@ define(function (require, exports, module) { case "@": dialogLabel = Strings.CMD_GOTO_DEFINITION + "\u2026"; break; + case "#": + dialogLabel = Strings.CMD_GOTO_DEFINITION_PROJECT + "\u2026"; + break; default: dialogLabel = ""; break; @@ -732,18 +776,89 @@ define(function (require, exports, module) { } } + function doDefinitionSearchInProject() { + if (DocumentManager.getCurrentDocument()) { + beginSearch("#", getCurrentEditorSelectedText()); + } + } + + function _canHandleTrigger(trigger, plugins) { + var retval = false; + + plugins.some(function (plugin, index) { + var provider = plugin.provider; + if (provider.match(trigger)) { + retval = true; + return true; + } + }); + + return retval; + } + + function _setMenuItemStateForLanguage(languageId) { + var plugins = _providerRegistrationHandler.getProvidersForLanguageId(languageId); + if (_canHandleTrigger("@", plugins)) { + CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION).setEnabled(true); + } else { + CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION).setEnabled(false); + } + + if (_canHandleTrigger("#", plugins)) { + CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION_PROJECT).setEnabled(true); + } else { + CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION_PROJECT).setEnabled(false); + } + } + // Listen for a change of project to invalidate our file list ProjectManager.on("projectOpen", function () { fileList = null; }); + MainViewManager.on("currentFileChange", function (event, newFile, newPaneId, oldFile, oldPaneId) { + if (!newFile) { + CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION).setEnabled(false); + CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION_PROJECT).setEnabled(false); + return; + } + + var newFilePath = newFile.fullPath, + newLanguageId = LanguageManager.getLanguageForPath(newFilePath).getId(); + _setMenuItemStateForLanguage(newLanguageId); + + DocumentManager.getDocumentForPath(newFilePath) + .done(function (newDoc) { + newDoc.on("languageChanged.quickFindDefinition", function () { + var changedLanguageId = LanguageManager.getLanguageForPath(newDoc.file.fullPath).getId(); + _setMenuItemStateForLanguage(changedLanguageId); + }); + }).fail(function (err) { + console.error(err); + }); + + if (!oldFile) { + return; + } + + var oldFilePath = oldFile.fullPath; + DocumentManager.getDocumentForPath(oldFilePath) + .done(function (oldDoc) { + oldDoc.off("languageChanged.quickFindDefinition"); + }).fail(function (err) { + console.error(err); + }); + }); + CommandManager.register(Strings.CMD_QUICK_OPEN, Commands.NAVIGATE_QUICK_OPEN, doFileSearch); CommandManager.register(Strings.CMD_GOTO_DEFINITION, Commands.NAVIGATE_GOTO_DEFINITION, doDefinitionSearch); + CommandManager.register(Strings.CMD_GOTO_DEFINITION_PROJECT, Commands.NAVIGATE_GOTO_DEFINITION_PROJECT, doDefinitionSearchInProject); CommandManager.register(Strings.CMD_GOTO_LINE, Commands.NAVIGATE_GOTO_LINE, doGotoLine); exports.beginSearch = beginSearch; exports.addQuickOpenPlugin = addQuickOpenPlugin; exports.highlightMatch = highlightMatch; + exports.SymbolKind = SymbolKind; // Convenience exports for functions that most QuickOpen plugins would need. exports.stringMatch = StringMatch.stringMatch;