Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Lsp symbols #15317

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
3 changes: 3 additions & 0 deletions src/base-config/keyboard.json
Original file line number Diff line number Diff line change
@@ -281,6 +281,9 @@
"navigate.gotoDefinition": [
"Ctrl-T"
],
"navigate.gotoDefinitionInProject": [
"Ctrl-Shift-T"
],
"navigate.jumptoDefinition": [
"Ctrl-J"
],
1 change: 1 addition & 0 deletions src/command/Commands.js
Original file line number Diff line number Diff line change
@@ -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()
1 change: 1 addition & 0 deletions src/command/DefaultMenus.js
Original file line number Diff line number Diff line change
@@ -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();
238 changes: 238 additions & 0 deletions src/extensions/default/PhpTooling/PHPSymbolProviders.js
Original file line number Diff line number Diff line change
@@ -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 "<li>" + displayName + " (" + item.symbolInfo.type + ")" + "<br /><span class='quick-open-path'>" + item.symbolInfo.scope + "</span></li>";
}
return "<li>" + displayName + " (" + item.symbolInfo.type + ")" + "</li>";
};

/**
* 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 "<li>" + displayName + " (" + item.symbolInfo.type + ")" + "<br /><span class='quick-open-path'>" + item.symbolInfo.scope + "</span><br /><br /><span class='quick-open-path'>" + item.symbolInfo.fullPath + "</span></li>";
}
return "<li>" + displayName + " (" + item.symbolInfo.type + ")" + "<br /><br /><span class='quick-open-path'>" + item.symbolInfo.fullPath + "</span></li>";
};

exports.SymbolProviders = {
DocumentSymbolsProvider: DocumentSymbolsProvider,
ProjectSymbolsProvider: ProjectSymbolsProvider
};
});
32 changes: 31 additions & 1 deletion src/extensions/default/PhpTooling/main.js
Original file line number Diff line number Diff line change
@@ -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));
}
13 changes: 13 additions & 0 deletions src/extensions/default/PhpTooling/unittest-files/test/test4.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

class MyClass{
public static $staticValue = 'static';
const constantValue = 'const';
public $publicValue = 'public';
public static function staticMethod(){}
public function publicFunction(){}
}

$myclass = new MyClass();

?>
130 changes: 127 additions & 3 deletions src/extensions/default/PhpTooling/unittests.js
Original file line number Diff line number Diff line change
@@ -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);
});
});
});
});
2 changes: 1 addition & 1 deletion src/languageTools/LanguageClientWrapper.js
Original file line number Diff line number Diff line change
@@ -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;
5 changes: 4 additions & 1 deletion src/nls/root/strings.js
Original file line number Diff line number Diff line change
@@ -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"
});
179 changes: 147 additions & 32 deletions src/search/QuickOpen.js
Original file line number Diff line number Diff line change
@@ -44,21 +44,47 @@ 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
* @const {RegExp}
*/
var CURSOR_POS_EXP = new RegExp(":([^,]+)?(,(.+)?)?");

/**
* List of plugins
* @type {Array.<QuickOpenPlugin>}
*/
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;