From b3645222b507a9c6ccd2efd8787da47f0b9dbd5c Mon Sep 17 00:00:00 2001 From: Shubham Yadav Date: Wed, 10 Apr 2019 09:23:13 +0530 Subject: [PATCH 01/10] Symbols Provider First Cut --- .../default/PhpTooling/SymbolsProvider.js | 321 ++++++++++++++++++ src/extensions/default/PhpTooling/main.js | 5 +- 2 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 src/extensions/default/PhpTooling/SymbolsProvider.js diff --git a/src/extensions/default/PhpTooling/SymbolsProvider.js b/src/extensions/default/PhpTooling/SymbolsProvider.js new file mode 100644 index 00000000000..9d580f9dae1 --- /dev/null +++ b/src/extensions/default/PhpTooling/SymbolsProvider.js @@ -0,0 +1,321 @@ +/* + * 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"), + QuickOpenHelper = brackets.getModule("search/QuickOpenHelper"), + Commands = brackets.getModule("command/Commands"), + DocumentManager = brackets.getModule("document/DocumentManager"), + StringMatch = brackets.getModule("utils/StringMatch"), + StringUtils = brackets.getModule("utils/StringUtils"), + CommandManager = brackets.getModule("command/CommandManager"), + PathConverters = brackets.getModule("languageTools/PathConverters"); + + + function match(query) { + return (query[0] === "@") || (query[0] === "#"); + } + + /** + * Scroll to the selected item in the current document (unless no query string entered yet, + * in which case the topmost list item is irrelevant) + * @param {?SearchResult} selectedItem + * @param {string} query + * @param {boolean} explicit False if this is only highlighted due to being at top of list after search() + */ + function itemFocus(selectedItem, query, explicit) { + if (!selectedItem || (query.length < 2 && !explicit)) { + return; + } + if (selectedItem.fileLocation.docSymbol) { + var fileLocation = selectedItem.fileLocation; + + var from = {line: fileLocation.lineFrom, ch: fileLocation.chFrom}; + var to = {line: fileLocation.lineTo, ch: fileLocation.chTo}; + EditorManager.getCurrentFullEditor().setSelection(from, to, true); + } + } + + /** + * Scroll to the selected item in the current document (unless no query string entered yet, + * in which case the topmost list item is irrelevant) + * @param {?SearchResult} selectedItem + * @param {string} query + */ + function itemSelect(selectedItem, query) { + if (selectedItem.fileLocation.docSymbol) { + itemFocus(selectedItem, query, true); + } else { + var fullPath = selectedItem.fileLocation && selectedItem.fileLocation.fullPath; + var from = {line: selectedItem.fileLocation.lineFrom, ch: selectedItem.fileLocation.chFrom}; + if (fullPath) { + CommandManager.execute(Commands.CMD_ADD_TO_WORKINGSET_AND_OPEN, {fullPath: fullPath}) + .done(function () { + if (from) { + var editor = EditorManager.getCurrentFullEditor(); + editor.setCursorPos(from.line, from.ch, true); + } + }); + } + } + } + + 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" + }; + + function highlightMatch(item, matchClass, rangeFilter) { + var label = item.label || item; + matchClass = matchClass || "quicksearch-namematch"; + + var stringRanges = item.stringRanges; + if (!stringRanges) { + // If result didn't come from stringMatch(), highlight nothing + stringRanges = [{ + text: label, + matched: false, + includesLastSegment: true + }]; + } + + var displayName = ""; + if (item.scoreDebug) { + var sd = item.scoreDebug; + displayName += '(' + item.matchGoodness + ') '; + } + + // Put the path pieces together, highlighting the matched parts + stringRanges.forEach(function (range) { + if (range.matched) { + displayName += ""; + } + + var rangeText = rangeFilter ? rangeFilter(range.includesLastSegment, range.text) : range.text; + displayName += StringUtils.breakableUrl(rangeText); + + if (range.matched) { + displayName += ""; + } + }); + return displayName; + } + + function _resultsFormatter(item, query) { + if (item.fileLocation.docSymbol) { + query = query.slice(query.indexOf("@") + 1, query.length); + var displayName = highlightMatch(item); + if (item.fileLocation.containerName) { + return "
  • " + displayName + " (" + item.fileLocation.type + ")" + "
    " + item.fileLocation.containerName + "
  • "; + } + return "
  • " + displayName + " (" + item.fileLocation.type + ")" + "
  • "; + } + query = query.slice(query.indexOf("#") + 1, query.length); + var displayName = highlightMatch(item); + if (item.fileLocation.containerName) { + return "
  • " + displayName + " (" + item.fileLocation.type + ")" + "
    " + item.fileLocation.containerName + "

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

    " + item.fileLocation.fullPath + "
  • "; + } + + /** + * FileLocation class + * @constructor + * @param {string} fullPath + * @param {number} line + * @param {number} chFrom column start position + * @param {number} chTo column end position + * @param {string} id + */ + function FileLocation(fullPath, line, chFrom, chTo, id, lineFrom, lineTo, docSymbol, containerName, type) { + this.fullPath = fullPath; + this.line = line; + this.lineFrom = lineFrom; + this.lineTo = lineTo; + this.chFrom = chFrom; + this.docSymbol = docSymbol; + this.containerName = containerName; + this.type = type; + this.chTo = chTo; + this.id = id; + } + + /** + * Returns a list of information about ID's for a single document. This array is populated + * by createIDList() + * @type {?Array.} + */ + function createList(list, isDocumentSymbol) { + var doc = DocumentManager.getCurrentDocument(); + if (!doc) { //Add guard for current document and results + return; + } + + var newlist = []; + for (var i = 0; i < list.length; i++) { + var symbolInfo = list[i], + symbolName = symbolInfo.name, + symbolType = SymbolKind[symbolInfo.kind.toString()], + symbolFile = isDocumentSymbol ? null : PathConverters.uriToPath(symbolInfo.location.uri), + symbolLine, symbolTo, symbolFrom; + + var rangeInSameLine = (symbolInfo.location.range.start.line === symbolInfo.location.range.end.line); + symbolLine = symbolInfo.location.range.start.line; + if (rangeInSameLine) { + symbolFrom = symbolInfo.location.range.start.character; + symbolTo = symbolInfo.location.range.end.character; + } else { + symbolFrom = symbolTo = symbolInfo.location.range.start.character; + } + + var lineFrom = symbolInfo.location.range.start.line, + lineTo = symbolInfo.location.range.end.line; + + newlist.push(new FileLocation(symbolFile, symbolLine, symbolFrom, symbolTo, symbolName, lineFrom, lineTo, isDocumentSymbol, symbolInfo.containerName, symbolType)); + } + return newlist; + } + + + + function PHPSymbolsProvider(client) { + this.client = client; + this._registerSymbolsProvider(); + } + + PHPSymbolsProvider.prototype._registerSymbolsProvider = function () { + var self = this; + QuickOpen.addQuickOpenPlugin({ + name: "PHP Symbols", + languageIds: ["php"], + search: self.search.bind(self), + match: match, + itemFocus: itemFocus, + itemSelect: itemSelect, + resultsFormatter: _resultsFormatter + }); + }; + + PHPSymbolsProvider.prototype.search = function (query, matcher) { + var queryText = ""; + if (query.startsWith("@")) { + queryText = query.slice(query.indexOf("@") + 1, query.length); + return this.getDocumentSymbols(queryText, matcher); + } else if (query.startsWith("#")) { + queryText = query.slice(query.indexOf("#") + 1, query.length); + return this.getWorkspaceSymbols(queryText, matcher); + } + }; + + PHPSymbolsProvider.prototype.getDocumentSymbols = function (query, matcher) { + var editor = EditorManager.getActiveEditor(), + docPath = editor.document.file._path, + retval = $.Deferred(); + + this.client.requestSymbolsForDocument({ + filePath: docPath + }).done(function (results) { + console.log("Document Symbols:", results); + + var list = createList(results, true); + + // Filter and rank how good each match is + var filteredList = $.map(list, function (fileLocation) { + var searchResult = matcher.match(fileLocation.id, query); + if (searchResult) { + searchResult.fileLocation = fileLocation; + } + return searchResult; + }); + + // Sort based on ranking & basic alphabetical order + StringMatch.basicMatchSort(filteredList); + + retval.resolve(filteredList); + }); + + return retval; + }; + + PHPSymbolsProvider.prototype.getWorkspaceSymbols = function (query, matcher) { + var retval = $.Deferred(); + + this.client.requestSymbolsForWorkspace({ + query: query + }).done(function (results) { + console.log("Workspace Symbols:", results); + + var list = createList(results); + + // Filter and rank how good each match is + var filteredList = $.map(list, function (fileLocation) { + var searchResult = matcher.match(fileLocation.id, query); + if (searchResult) { + searchResult.fileLocation = fileLocation; + } + return searchResult; + }); + + // Sort based on ranking & basic alphabetical order + StringMatch.basicMatchSort(filteredList); + + retval.resolve(filteredList); + }); + + return retval; + }; + + exports.PHPSymbolsProvider = PHPSymbolsProvider; +}); diff --git a/src/extensions/default/PhpTooling/main.js b/src/extensions/default/PhpTooling/main.js index d2ecc966bb1..51d32d4ded1 100755 --- a/src/extensions/default/PhpTooling/main.js +++ b/src/extensions/default/PhpTooling/main.js @@ -35,6 +35,7 @@ define(function (require, exports, module) { CodeInspection = brackets.getModule("language/CodeInspection"), DefaultProviders = brackets.getModule("languageTools/DefaultProviders"), CodeHintsProvider = require("CodeHintsProvider").CodeHintsProvider, + PHPSymbolsProvider = require("SymbolsProvider").PHPSymbolsProvider, DefaultEventHandlers = brackets.getModule("languageTools/DefaultEventHandlers"), PreferencesManager = brackets.getModule("preferences/PreferencesManager"), Strings = brackets.getModule("strings"), @@ -61,7 +62,8 @@ define(function (require, exports, module) { chProvider, phProvider, lProvider, - jdProvider; + jdProvider, + symProvider; PreferencesManager.definePreference("php", "object", phpConfig, { description: Strings.DESCRIPTION_PHP_TOOLING_CONFIGURATION @@ -102,6 +104,7 @@ define(function (require, exports, module) { phProvider = new DefaultProviders.ParameterHintsProvider(_client), lProvider = new DefaultProviders.LintingProvider(_client), jdProvider = new DefaultProviders.JumpToDefProvider(_client); + symProvider = new PHPSymbolsProvider(_client); JumpToDefManager.registerJumpToDefProvider(jdProvider, ["php"], 0); CodeHintManager.registerHintProvider(chProvider, ["php"], 0); From 766f46942a23ede2a0f60376935c9fd84c795dc5 Mon Sep 17 00:00:00 2001 From: Shubham Yadav Date: Wed, 10 Apr 2019 13:16:59 +0530 Subject: [PATCH 02/10] Linting the Symbols Provider --- .../default/PhpTooling/SymbolsProvider.js | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/extensions/default/PhpTooling/SymbolsProvider.js b/src/extensions/default/PhpTooling/SymbolsProvider.js index 9d580f9dae1..40ea9f4ac33 100644 --- a/src/extensions/default/PhpTooling/SymbolsProvider.js +++ b/src/extensions/default/PhpTooling/SymbolsProvider.js @@ -28,8 +28,7 @@ define(function (require, exports, module) { var EditorManager = brackets.getModule("editor/EditorManager"), QuickOpen = brackets.getModule("search/QuickOpen"), - QuickOpenHelper = brackets.getModule("search/QuickOpenHelper"), - Commands = brackets.getModule("command/Commands"), + Commands = brackets.getModule("command/Commands"), DocumentManager = brackets.getModule("document/DocumentManager"), StringMatch = brackets.getModule("utils/StringMatch"), StringUtils = brackets.getModule("utils/StringUtils"), @@ -41,13 +40,6 @@ define(function (require, exports, module) { return (query[0] === "@") || (query[0] === "#"); } - /** - * Scroll to the selected item in the current document (unless no query string entered yet, - * in which case the topmost list item is irrelevant) - * @param {?SearchResult} selectedItem - * @param {string} query - * @param {boolean} explicit False if this is only highlighted due to being at top of list after search() - */ function itemFocus(selectedItem, query, explicit) { if (!selectedItem || (query.length < 2 && !explicit)) { return; @@ -55,26 +47,31 @@ define(function (require, exports, module) { if (selectedItem.fileLocation.docSymbol) { var fileLocation = selectedItem.fileLocation; - var from = {line: fileLocation.lineFrom, ch: fileLocation.chFrom}; - var to = {line: fileLocation.lineTo, ch: fileLocation.chTo}; + var from = { + line: fileLocation.lineFrom, + ch: fileLocation.chFrom + }; + var to = { + line: fileLocation.lineTo, + ch: fileLocation.chTo + }; EditorManager.getCurrentFullEditor().setSelection(from, to, true); } } - /** - * Scroll to the selected item in the current document (unless no query string entered yet, - * in which case the topmost list item is irrelevant) - * @param {?SearchResult} selectedItem - * @param {string} query - */ function itemSelect(selectedItem, query) { if (selectedItem.fileLocation.docSymbol) { itemFocus(selectedItem, query, true); } else { var fullPath = selectedItem.fileLocation && selectedItem.fileLocation.fullPath; - var from = {line: selectedItem.fileLocation.lineFrom, ch: selectedItem.fileLocation.chFrom}; + var from = { + line: selectedItem.fileLocation.lineFrom, + ch: selectedItem.fileLocation.chFrom + }; if (fullPath) { - CommandManager.execute(Commands.CMD_ADD_TO_WORKINGSET_AND_OPEN, {fullPath: fullPath}) + CommandManager.execute(Commands.CMD_ADD_TO_WORKINGSET_AND_OPEN, { + fullPath: fullPath + }) .done(function () { if (from) { var editor = EditorManager.getCurrentFullEditor(); From a36c0fb7d81ce12d2705c153acba3cbae3e454d6 Mon Sep 17 00:00:00 2001 From: Shubham Yadav Date: Thu, 11 Apr 2019 23:48:31 +0530 Subject: [PATCH 03/10] Symbols Provider Second Cut --- src/base-config/keyboard.json | 6 ++++++ src/command/Commands.js | 2 ++ .../default/PhpTooling/SymbolsProvider.js | 8 ++++---- src/languageTools/LanguageClientWrapper.js | 2 +- src/nls/root/strings.js | 3 +++ src/search/QuickOpen.js | 18 ++++++++++++++++++ 6 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/base-config/keyboard.json b/src/base-config/keyboard.json index aa9c67cd4d5..bcb89507649 100644 --- a/src/base-config/keyboard.json +++ b/src/base-config/keyboard.json @@ -281,6 +281,12 @@ "navigate.gotoDefinition": [ "Ctrl-T" ], + "navigate.findDocumentSymbols": [ + "Ctrl-Shift-T" + ], + "navigate.findProjectSymbols": [ + "Ctrl-Shift-P" + ], "navigate.jumptoDefinition": [ "Ctrl-J" ], diff --git a/src/command/Commands.js b/src/command/Commands.js index dc147eeac6f..f49e068bbec 100644 --- a/src/command/Commands.js +++ b/src/command/Commands.js @@ -125,6 +125,8 @@ 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_FIND_DOCUMENT_SYMBOLS = "navigate.findDocumentSymbols"; // QuickOpen.js doSymbolSearchInDocument() + exports.NAVIGATE_FIND_PROJECT_SYMBOLS = "navigate.findProjectSymbols"; // QuickOpen.js doSymbolSearchInProject() 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/extensions/default/PhpTooling/SymbolsProvider.js b/src/extensions/default/PhpTooling/SymbolsProvider.js index 40ea9f4ac33..5366daf35fb 100644 --- a/src/extensions/default/PhpTooling/SymbolsProvider.js +++ b/src/extensions/default/PhpTooling/SymbolsProvider.js @@ -37,7 +37,7 @@ define(function (require, exports, module) { function match(query) { - return (query[0] === "@") || (query[0] === "#"); + return (query[0] === "~") || (query[0] === "#"); } function itemFocus(selectedItem, query, explicit) { @@ -152,7 +152,7 @@ define(function (require, exports, module) { function _resultsFormatter(item, query) { if (item.fileLocation.docSymbol) { - query = query.slice(query.indexOf("@") + 1, query.length); + query = query.slice(query.indexOf("~") + 1, query.length); var displayName = highlightMatch(item); if (item.fileLocation.containerName) { return "
  • " + displayName + " (" + item.fileLocation.type + ")" + "
    " + item.fileLocation.containerName + "
  • "; @@ -247,8 +247,8 @@ define(function (require, exports, module) { PHPSymbolsProvider.prototype.search = function (query, matcher) { var queryText = ""; - if (query.startsWith("@")) { - queryText = query.slice(query.indexOf("@") + 1, query.length); + if (query.startsWith("~")) { + queryText = query.slice(query.indexOf("~") + 1, query.length); return this.getDocumentSymbols(queryText, matcher); } else if (query.startsWith("#")) { queryText = query.slice(query.indexOf("#") + 1, query.length); diff --git a/src/languageTools/LanguageClientWrapper.js b/src/languageTools/LanguageClientWrapper.js index abd0c9b41f5..e7657dee728 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 f9f8d92de3b..c8dd95c771a 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -419,6 +419,9 @@ define({ "CMD_QUICK_OPEN" : "Quick Open", "CMD_GOTO_LINE" : "Go to Line", "CMD_GOTO_DEFINITION" : "Quick Find Definition", + "CMD_FIND_DOCUMENT_SYMBOLS" : "Find Document Symbols", + "CMD_FIND_PROJECT_SYMBOLS" : "Find Project Symbols", + "CMD_GOTO_DEFINITION" : "Quick Find Definition", "CMD_GOTO_FIRST_PROBLEM" : "Go to First Problem", "CMD_TOGGLE_QUICK_EDIT" : "Quick Edit", "CMD_TOGGLE_QUICK_DOCS" : "Quick Docs", diff --git a/src/search/QuickOpen.js b/src/search/QuickOpen.js index 21255cc5f41..b37d4750315 100644 --- a/src/search/QuickOpen.js +++ b/src/search/QuickOpen.js @@ -613,6 +613,12 @@ define(function (require, exports, module) { case "@": dialogLabel = Strings.CMD_GOTO_DEFINITION + "\u2026"; break; + case "~": + dialogLabel = Strings.CMD_FIND_DOCUMENT_SYMBOLS + "\u2026"; + break; + case "#": + dialogLabel = Strings.CMD_FIND_PROJECT_SYMBOLS + "\u2026"; + break; default: dialogLabel = ""; break; @@ -731,6 +737,16 @@ define(function (require, exports, module) { beginSearch("@", getCurrentEditorSelectedText()); } } + + function doSymbolSearchInDocument() { + if (DocumentManager.getCurrentDocument()) { + beginSearch("~", getCurrentEditorSelectedText()); + } + } + + function doSymbolSearchInProject() { + beginSearch("#", getCurrentEditorSelectedText()); + } // Listen for a change of project to invalidate our file list ProjectManager.on("projectOpen", function () { @@ -739,6 +755,8 @@ define(function (require, exports, module) { 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_FIND_DOCUMENT_SYMBOLS, Commands.NAVIGATE_FIND_DOCUMENT_SYMBOLS, doSymbolSearchInDocument); + CommandManager.register(Strings.CMD_FIND_PROJECT_SYMBOLS, Commands.NAVIGATE_FIND_PROJECT_SYMBOLS, doSymbolSearchInProject); CommandManager.register(Strings.CMD_GOTO_LINE, Commands.NAVIGATE_GOTO_LINE, doGotoLine); exports.beginSearch = beginSearch; From eba72f27882dba282f82c61adc42409688ee365c Mon Sep 17 00:00:00 2001 From: Shubham Yadav Date: Fri, 12 Apr 2019 01:57:01 +0530 Subject: [PATCH 04/10] Document Symbol and Project Symbol support in Brackets --- src/command/DefaultMenus.js | 2 + .../default/PhpTooling/PHPSymbolsProvider.js | 213 ++++++++++++ .../default/PhpTooling/SymbolsProvider.js | 318 ------------------ src/extensions/default/PhpTooling/main.js | 14 +- src/search/QuickOpen.js | 105 ++++-- 5 files changed, 298 insertions(+), 354 deletions(-) create mode 100644 src/extensions/default/PhpTooling/PHPSymbolsProvider.js delete mode 100644 src/extensions/default/PhpTooling/SymbolsProvider.js diff --git a/src/command/DefaultMenus.js b/src/command/DefaultMenus.js index 2eb6f871925..5c857aaa177 100644 --- a/src/command/DefaultMenus.js +++ b/src/command/DefaultMenus.js @@ -172,6 +172,8 @@ 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_FIND_DOCUMENT_SYMBOLS); + menu.addMenuItem(Commands.NAVIGATE_FIND_PROJECT_SYMBOLS); menu.addMenuItem(Commands.NAVIGATE_JUMPTO_DEFINITION); menu.addMenuItem(Commands.NAVIGATE_GOTO_FIRST_PROBLEM); menu.addMenuDivider(); diff --git a/src/extensions/default/PhpTooling/PHPSymbolsProvider.js b/src/extensions/default/PhpTooling/PHPSymbolsProvider.js new file mode 100644 index 00000000000..8aa0daf5726 --- /dev/null +++ b/src/extensions/default/PhpTooling/PHPSymbolsProvider.js @@ -0,0 +1,213 @@ +/* + * 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"), + DocumentManager = brackets.getModule("document/DocumentManager"), + 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 doc = DocumentManager.getCurrentDocument(); + + //Should only function when a document is open + if (!doc) { + return []; + } + + 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; + } + + function PHPSymbolsProvider(client) { + this.client = client; + } + + PHPSymbolsProvider.prototype.search = function (query, matcher) { + var queryText = query.slice(1); + if (query.startsWith("~")) { + return this.getDocumentSymbols(queryText, matcher); + } else if (query.startsWith("#")) { + return this.getWorkspaceSymbols(queryText, matcher); + } + }; + + PHPSymbolsProvider.prototype.getDocumentSymbols = function (query, matcher) { + var editor = EditorManager.getActiveEditor(), + docPath = editor.document.file._path, + retval = $.Deferred(); + + this.client.requestSymbolsForDocument({ + filePath: docPath + }).done(function (results) { + console.log("Document Symbols:", results); + var resultList = transFormToSymbolList(query, matcher, results, true); + retval.resolve(resultList); + }); + + return retval; + }; + + PHPSymbolsProvider.prototype.getWorkspaceSymbols = function (query, matcher) { + var retval = $.Deferred(); + + this.client.requestSymbolsForWorkspace({ + query: query + }).done(function (results) { + console.log("Workspace Symbols:", results); + var resultList = transFormToSymbolList(query, matcher, results); + retval.resolve(resultList); + }); + + return retval; + }; + + PHPSymbolsProvider.prototype.match = function (query) { + return (query.startsWith("~") || query.startsWith("#")); + } + + PHPSymbolsProvider.prototype.itemFocus = function (selectedItem, query, explicit) { + if (!selectedItem || (query.length < 2 && !explicit)) { + return; + } + + if (selectedItem.symbolInfo.isDocumentSymbolRequest) { + var range = selectedItem.symbolInfo.selectionRange; + EditorManager.getCurrentFullEditor().setSelection(range.from, range.to, true); + } + } + + PHPSymbolsProvider.prototype.itemSelect = function (selectedItem, query) { + if (selectedItem.symbolInfo.isDocumentSymbolRequest) { + itemFocus(selectedItem, query, true); + } else { + 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); + } + }); + } + } + } + + PHPSymbolsProvider.prototype.resultsFormatter = function (item, query) { + var displayName = QuickOpen.highlightMatch(item); + query = query.slice(1); + + if (item.symbolInfo.isDocumentSymbolRequest) { + if (item.symbolInfo.scope) { + return "
  • " + displayName + " (" + item.symbolInfo.type + ")" + "
    " + item.symbolInfo.scope + "
  • "; + } + return "
  • " + displayName + " (" + item.symbolInfo.type + ")" + "
  • "; + } + + if (item.symbolInfo.scope) { + return "
  • " + displayName + " (" + item.symbolInfo.type + ")" + "
    " + item.symbolInfo.scope + "

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

    " + item.symbolInfo.fullPath + "
  • "; + } + + exports.SymbolsProvider = PHPSymbolsProvider; +}); diff --git a/src/extensions/default/PhpTooling/SymbolsProvider.js b/src/extensions/default/PhpTooling/SymbolsProvider.js deleted file mode 100644 index 5366daf35fb..00000000000 --- a/src/extensions/default/PhpTooling/SymbolsProvider.js +++ /dev/null @@ -1,318 +0,0 @@ -/* - * 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"), - DocumentManager = brackets.getModule("document/DocumentManager"), - StringMatch = brackets.getModule("utils/StringMatch"), - StringUtils = brackets.getModule("utils/StringUtils"), - CommandManager = brackets.getModule("command/CommandManager"), - PathConverters = brackets.getModule("languageTools/PathConverters"); - - - function match(query) { - return (query[0] === "~") || (query[0] === "#"); - } - - function itemFocus(selectedItem, query, explicit) { - if (!selectedItem || (query.length < 2 && !explicit)) { - return; - } - if (selectedItem.fileLocation.docSymbol) { - var fileLocation = selectedItem.fileLocation; - - var from = { - line: fileLocation.lineFrom, - ch: fileLocation.chFrom - }; - var to = { - line: fileLocation.lineTo, - ch: fileLocation.chTo - }; - EditorManager.getCurrentFullEditor().setSelection(from, to, true); - } - } - - function itemSelect(selectedItem, query) { - if (selectedItem.fileLocation.docSymbol) { - itemFocus(selectedItem, query, true); - } else { - var fullPath = selectedItem.fileLocation && selectedItem.fileLocation.fullPath; - var from = { - line: selectedItem.fileLocation.lineFrom, - ch: selectedItem.fileLocation.chFrom - }; - if (fullPath) { - CommandManager.execute(Commands.CMD_ADD_TO_WORKINGSET_AND_OPEN, { - fullPath: fullPath - }) - .done(function () { - if (from) { - var editor = EditorManager.getCurrentFullEditor(); - editor.setCursorPos(from.line, from.ch, true); - } - }); - } - } - } - - 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" - }; - - function highlightMatch(item, matchClass, rangeFilter) { - var label = item.label || item; - matchClass = matchClass || "quicksearch-namematch"; - - var stringRanges = item.stringRanges; - if (!stringRanges) { - // If result didn't come from stringMatch(), highlight nothing - stringRanges = [{ - text: label, - matched: false, - includesLastSegment: true - }]; - } - - var displayName = ""; - if (item.scoreDebug) { - var sd = item.scoreDebug; - displayName += '(' + item.matchGoodness + ') '; - } - - // Put the path pieces together, highlighting the matched parts - stringRanges.forEach(function (range) { - if (range.matched) { - displayName += ""; - } - - var rangeText = rangeFilter ? rangeFilter(range.includesLastSegment, range.text) : range.text; - displayName += StringUtils.breakableUrl(rangeText); - - if (range.matched) { - displayName += ""; - } - }); - return displayName; - } - - function _resultsFormatter(item, query) { - if (item.fileLocation.docSymbol) { - query = query.slice(query.indexOf("~") + 1, query.length); - var displayName = highlightMatch(item); - if (item.fileLocation.containerName) { - return "
  • " + displayName + " (" + item.fileLocation.type + ")" + "
    " + item.fileLocation.containerName + "
  • "; - } - return "
  • " + displayName + " (" + item.fileLocation.type + ")" + "
  • "; - } - query = query.slice(query.indexOf("#") + 1, query.length); - var displayName = highlightMatch(item); - if (item.fileLocation.containerName) { - return "
  • " + displayName + " (" + item.fileLocation.type + ")" + "
    " + item.fileLocation.containerName + "

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

    " + item.fileLocation.fullPath + "
  • "; - } - - /** - * FileLocation class - * @constructor - * @param {string} fullPath - * @param {number} line - * @param {number} chFrom column start position - * @param {number} chTo column end position - * @param {string} id - */ - function FileLocation(fullPath, line, chFrom, chTo, id, lineFrom, lineTo, docSymbol, containerName, type) { - this.fullPath = fullPath; - this.line = line; - this.lineFrom = lineFrom; - this.lineTo = lineTo; - this.chFrom = chFrom; - this.docSymbol = docSymbol; - this.containerName = containerName; - this.type = type; - this.chTo = chTo; - this.id = id; - } - - /** - * Returns a list of information about ID's for a single document. This array is populated - * by createIDList() - * @type {?Array.} - */ - function createList(list, isDocumentSymbol) { - var doc = DocumentManager.getCurrentDocument(); - if (!doc) { //Add guard for current document and results - return; - } - - var newlist = []; - for (var i = 0; i < list.length; i++) { - var symbolInfo = list[i], - symbolName = symbolInfo.name, - symbolType = SymbolKind[symbolInfo.kind.toString()], - symbolFile = isDocumentSymbol ? null : PathConverters.uriToPath(symbolInfo.location.uri), - symbolLine, symbolTo, symbolFrom; - - var rangeInSameLine = (symbolInfo.location.range.start.line === symbolInfo.location.range.end.line); - symbolLine = symbolInfo.location.range.start.line; - if (rangeInSameLine) { - symbolFrom = symbolInfo.location.range.start.character; - symbolTo = symbolInfo.location.range.end.character; - } else { - symbolFrom = symbolTo = symbolInfo.location.range.start.character; - } - - var lineFrom = symbolInfo.location.range.start.line, - lineTo = symbolInfo.location.range.end.line; - - newlist.push(new FileLocation(symbolFile, symbolLine, symbolFrom, symbolTo, symbolName, lineFrom, lineTo, isDocumentSymbol, symbolInfo.containerName, symbolType)); - } - return newlist; - } - - - - function PHPSymbolsProvider(client) { - this.client = client; - this._registerSymbolsProvider(); - } - - PHPSymbolsProvider.prototype._registerSymbolsProvider = function () { - var self = this; - QuickOpen.addQuickOpenPlugin({ - name: "PHP Symbols", - languageIds: ["php"], - search: self.search.bind(self), - match: match, - itemFocus: itemFocus, - itemSelect: itemSelect, - resultsFormatter: _resultsFormatter - }); - }; - - PHPSymbolsProvider.prototype.search = function (query, matcher) { - var queryText = ""; - if (query.startsWith("~")) { - queryText = query.slice(query.indexOf("~") + 1, query.length); - return this.getDocumentSymbols(queryText, matcher); - } else if (query.startsWith("#")) { - queryText = query.slice(query.indexOf("#") + 1, query.length); - return this.getWorkspaceSymbols(queryText, matcher); - } - }; - - PHPSymbolsProvider.prototype.getDocumentSymbols = function (query, matcher) { - var editor = EditorManager.getActiveEditor(), - docPath = editor.document.file._path, - retval = $.Deferred(); - - this.client.requestSymbolsForDocument({ - filePath: docPath - }).done(function (results) { - console.log("Document Symbols:", results); - - var list = createList(results, true); - - // Filter and rank how good each match is - var filteredList = $.map(list, function (fileLocation) { - var searchResult = matcher.match(fileLocation.id, query); - if (searchResult) { - searchResult.fileLocation = fileLocation; - } - return searchResult; - }); - - // Sort based on ranking & basic alphabetical order - StringMatch.basicMatchSort(filteredList); - - retval.resolve(filteredList); - }); - - return retval; - }; - - PHPSymbolsProvider.prototype.getWorkspaceSymbols = function (query, matcher) { - var retval = $.Deferred(); - - this.client.requestSymbolsForWorkspace({ - query: query - }).done(function (results) { - console.log("Workspace Symbols:", results); - - var list = createList(results); - - // Filter and rank how good each match is - var filteredList = $.map(list, function (fileLocation) { - var searchResult = matcher.match(fileLocation.id, query); - if (searchResult) { - searchResult.fileLocation = fileLocation; - } - return searchResult; - }); - - // Sort based on ranking & basic alphabetical order - StringMatch.basicMatchSort(filteredList); - - retval.resolve(filteredList); - }); - - return retval; - }; - - exports.PHPSymbolsProvider = PHPSymbolsProvider; -}); diff --git a/src/extensions/default/PhpTooling/main.js b/src/extensions/default/PhpTooling/main.js index 51d32d4ded1..a66674b68c8 100755 --- a/src/extensions/default/PhpTooling/main.js +++ b/src/extensions/default/PhpTooling/main.js @@ -30,12 +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, - PHPSymbolsProvider = require("SymbolsProvider").PHPSymbolsProvider, + SymbolsProvider = require("PHPSymbolsProvider").SymbolsProvider, DefaultEventHandlers = brackets.getModule("languageTools/DefaultEventHandlers"), PreferencesManager = brackets.getModule("preferences/PreferencesManager"), Strings = brackets.getModule("strings"), @@ -104,7 +105,7 @@ define(function (require, exports, module) { phProvider = new DefaultProviders.ParameterHintsProvider(_client), lProvider = new DefaultProviders.LintingProvider(_client), jdProvider = new DefaultProviders.JumpToDefProvider(_client); - symProvider = new PHPSymbolsProvider(_client); + symProvider = new SymbolsProvider(_client); JumpToDefManager.registerJumpToDefProvider(jdProvider, ["php"], 0); CodeHintManager.registerHintProvider(chProvider, ["php"], 0); @@ -113,6 +114,15 @@ define(function (require, exports, module) { name: "", scanFileAsync: lProvider.getInspectionResultsAsync.bind(lProvider) }); + QuickOpen.addQuickOpenPlugin({ + name: "PHP Symbols", + languageIds: ["php"], + search: symProvider.search.bind(symProvider), + match: symProvider.match, + itemFocus: symProvider.itemFocus, + itemSelect: symProvider.itemSelect, + resultsFormatter: symProvider.resultsFormatter + }); _client.addOnCodeInspection(lProvider.setInspectionResults.bind(lProvider)); } diff --git a/src/search/QuickOpen.js b/src/search/QuickOpen.js index b37d4750315..225b5e17246 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,17 @@ define(function (require, exports, module) { */ var _curDialog; + 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 +169,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 +391,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 +497,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. @@ -737,13 +773,13 @@ define(function (require, exports, module) { beginSearch("@", getCurrentEditorSelectedText()); } } - + function doSymbolSearchInDocument() { if (DocumentManager.getCurrentDocument()) { beginSearch("~", getCurrentEditorSelectedText()); } } - + function doSymbolSearchInProject() { beginSearch("#", getCurrentEditorSelectedText()); } @@ -762,6 +798,7 @@ define(function (require, exports, module) { 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; From 825239c56e2f020706649f3754315a7bc893b33a Mon Sep 17 00:00:00 2001 From: Shubham Yadav Date: Fri, 12 Apr 2019 02:09:26 +0530 Subject: [PATCH 05/10] Minor Fixes --- .../default/PhpTooling/PHPSymbolsProvider.js | 12 ++++++------ src/extensions/default/PhpTooling/main.js | 8 ++++---- src/search/QuickOpen.js | 9 ++++++++- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/extensions/default/PhpTooling/PHPSymbolsProvider.js b/src/extensions/default/PhpTooling/PHPSymbolsProvider.js index 8aa0daf5726..9394934edf5 100644 --- a/src/extensions/default/PhpTooling/PHPSymbolsProvider.js +++ b/src/extensions/default/PhpTooling/PHPSymbolsProvider.js @@ -155,10 +155,10 @@ define(function (require, exports, module) { return retval; }; - + PHPSymbolsProvider.prototype.match = function (query) { return (query.startsWith("~") || query.startsWith("#")); - } + }; PHPSymbolsProvider.prototype.itemFocus = function (selectedItem, query, explicit) { if (!selectedItem || (query.length < 2 && !explicit)) { @@ -169,11 +169,11 @@ define(function (require, exports, module) { var range = selectedItem.symbolInfo.selectionRange; EditorManager.getCurrentFullEditor().setSelection(range.from, range.to, true); } - } + }; PHPSymbolsProvider.prototype.itemSelect = function (selectedItem, query) { if (selectedItem.symbolInfo.isDocumentSymbolRequest) { - itemFocus(selectedItem, query, true); + this.itemFocus(selectedItem, query, true); } else { var fullPath = selectedItem.symbolInfo.fullPath, range = selectedItem.symbolInfo.selectionRange; @@ -190,7 +190,7 @@ define(function (require, exports, module) { }); } } - } + }; PHPSymbolsProvider.prototype.resultsFormatter = function (item, query) { var displayName = QuickOpen.highlightMatch(item); @@ -207,7 +207,7 @@ define(function (require, exports, module) { return "
  • " + displayName + " (" + item.symbolInfo.type + ")" + "
    " + item.symbolInfo.scope + "

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

    " + item.symbolInfo.fullPath + "
  • "; - } + }; exports.SymbolsProvider = PHPSymbolsProvider; }); diff --git a/src/extensions/default/PhpTooling/main.js b/src/extensions/default/PhpTooling/main.js index a66674b68c8..9953968062d 100755 --- a/src/extensions/default/PhpTooling/main.js +++ b/src/extensions/default/PhpTooling/main.js @@ -118,10 +118,10 @@ define(function (require, exports, module) { name: "PHP Symbols", languageIds: ["php"], search: symProvider.search.bind(symProvider), - match: symProvider.match, - itemFocus: symProvider.itemFocus, - itemSelect: symProvider.itemSelect, - resultsFormatter: symProvider.resultsFormatter + match: symProvider.match.bind(symProvider), + itemFocus: symProvider.itemFocus.bind(symProvider), + itemSelect: symProvider.itemSelect.bind(symProvider), + resultsFormatter: symProvider.resultsFormatter.bind(symProvider) }); _client.addOnCodeInspection(lProvider.setInspectionResults.bind(lProvider)); diff --git a/src/search/QuickOpen.js b/src/search/QuickOpen.js index 225b5e17246..9f2e46a4ce3 100644 --- a/src/search/QuickOpen.js +++ b/src/search/QuickOpen.js @@ -103,6 +103,11 @@ 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(); @@ -781,7 +786,9 @@ define(function (require, exports, module) { } function doSymbolSearchInProject() { - beginSearch("#", getCurrentEditorSelectedText()); + if (DocumentManager.getCurrentDocument()) { + beginSearch("#", getCurrentEditorSelectedText()); + } } // Listen for a change of project to invalidate our file list From 1553a25e96a24ee497137c723c353ed640bfc988 Mon Sep 17 00:00:00 2001 From: Shubham Yadav Date: Fri, 12 Apr 2019 02:47:07 +0530 Subject: [PATCH 06/10] Removing Typo, and adding null checks --- .../default/PhpTooling/PHPSymbolsProvider.js | 20 +++++++++++++++++-- src/nls/root/strings.js | 1 - 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/extensions/default/PhpTooling/PHPSymbolsProvider.js b/src/extensions/default/PhpTooling/PHPSymbolsProvider.js index 9394934edf5..77e09331cc8 100644 --- a/src/extensions/default/PhpTooling/PHPSymbolsProvider.js +++ b/src/extensions/default/PhpTooling/PHPSymbolsProvider.js @@ -127,6 +127,15 @@ define(function (require, exports, module) { }; PHPSymbolsProvider.prototype.getDocumentSymbols = 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(); @@ -134,7 +143,6 @@ define(function (require, exports, module) { this.client.requestSymbolsForDocument({ filePath: docPath }).done(function (results) { - console.log("Document Symbols:", results); var resultList = transFormToSymbolList(query, matcher, results, true); retval.resolve(resultList); }); @@ -143,12 +151,20 @@ define(function (require, exports, module) { }; PHPSymbolsProvider.prototype.getWorkspaceSymbols = function (query, matcher) { + if (!this.client) { + return $.Deferred().reject(); + } + + var serverCapabilities = this.client.getServerCapabilities(); + if (!serverCapabilities || !serverCapabilities.workspaceSymbolProvider) { + return $.Deferred().reject(); + } + var retval = $.Deferred(); this.client.requestSymbolsForWorkspace({ query: query }).done(function (results) { - console.log("Workspace Symbols:", results); var resultList = transFormToSymbolList(query, matcher, results); retval.resolve(resultList); }); diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index c8dd95c771a..5b4aa6d2078 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -421,7 +421,6 @@ define({ "CMD_GOTO_DEFINITION" : "Quick Find Definition", "CMD_FIND_DOCUMENT_SYMBOLS" : "Find Document Symbols", "CMD_FIND_PROJECT_SYMBOLS" : "Find Project Symbols", - "CMD_GOTO_DEFINITION" : "Quick Find Definition", "CMD_GOTO_FIRST_PROBLEM" : "Go to First Problem", "CMD_TOGGLE_QUICK_EDIT" : "Quick Edit", "CMD_TOGGLE_QUICK_DOCS" : "Quick Docs", From f7f025f1136ebf7902854368397d6fce6c125181 Mon Sep 17 00:00:00 2001 From: Shubham Yadav Date: Fri, 12 Apr 2019 17:11:54 +0530 Subject: [PATCH 07/10] Addressing review comments --- src/base-config/keyboard.json | 5 +- src/command/Commands.js | 3 +- src/command/DefaultMenus.js | 3 +- ...mbolsProvider.js => PHPSymbolProviders.js} | 111 ++++++++++-------- src/extensions/default/PhpTooling/main.js | 35 ++++-- src/nls/root/strings.js | 7 +- src/search/QuickOpen.js | 52 ++++++-- 7 files changed, 137 insertions(+), 79 deletions(-) rename src/extensions/default/PhpTooling/{PHPSymbolsProvider.js => PHPSymbolProviders.js} (72%) diff --git a/src/base-config/keyboard.json b/src/base-config/keyboard.json index bcb89507649..07611fea90c 100644 --- a/src/base-config/keyboard.json +++ b/src/base-config/keyboard.json @@ -281,12 +281,9 @@ "navigate.gotoDefinition": [ "Ctrl-T" ], - "navigate.findDocumentSymbols": [ + "navigate.gotoDefinitionInProject": [ "Ctrl-Shift-T" ], - "navigate.findProjectSymbols": [ - "Ctrl-Shift-P" - ], "navigate.jumptoDefinition": [ "Ctrl-J" ], diff --git a/src/command/Commands.js b/src/command/Commands.js index f49e068bbec..e5811dd229c 100644 --- a/src/command/Commands.js +++ b/src/command/Commands.js @@ -125,8 +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_FIND_DOCUMENT_SYMBOLS = "navigate.findDocumentSymbols"; // QuickOpen.js doSymbolSearchInDocument() - exports.NAVIGATE_FIND_PROJECT_SYMBOLS = "navigate.findProjectSymbols"; // QuickOpen.js doSymbolSearchInProject() + 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 5c857aaa177..912d9eaa306 100644 --- a/src/command/DefaultMenus.js +++ b/src/command/DefaultMenus.js @@ -172,8 +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_FIND_DOCUMENT_SYMBOLS); - menu.addMenuItem(Commands.NAVIGATE_FIND_PROJECT_SYMBOLS); + 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/PHPSymbolsProvider.js b/src/extensions/default/PhpTooling/PHPSymbolProviders.js similarity index 72% rename from src/extensions/default/PhpTooling/PHPSymbolsProvider.js rename to src/extensions/default/PhpTooling/PHPSymbolProviders.js index 77e09331cc8..7852f15f978 100644 --- a/src/extensions/default/PhpTooling/PHPSymbolsProvider.js +++ b/src/extensions/default/PhpTooling/PHPSymbolProviders.js @@ -113,20 +113,18 @@ define(function (require, exports, module) { return filteredList; } - function PHPSymbolsProvider(client) { + /** + * Provider for Document Symbols + */ + function DocumentSymbolsProvider(client) { this.client = client; } - PHPSymbolsProvider.prototype.search = function (query, matcher) { - var queryText = query.slice(1); - if (query.startsWith("~")) { - return this.getDocumentSymbols(queryText, matcher); - } else if (query.startsWith("#")) { - return this.getWorkspaceSymbols(queryText, matcher); - } + DocumentSymbolsProvider.prototype.match = function (query) { + return query.startsWith("@"); }; - PHPSymbolsProvider.prototype.getDocumentSymbols = function (query, matcher) { + DocumentSymbolsProvider.prototype.search = function (query, matcher) { if (!this.client) { return $.Deferred().reject(); } @@ -139,6 +137,7 @@ define(function (require, exports, module) { var editor = EditorManager.getActiveEditor(), docPath = editor.document.file._path, retval = $.Deferred(); + query = query.slice(1); this.client.requestSymbolsForDocument({ filePath: docPath @@ -150,7 +149,41 @@ define(function (require, exports, module) { return retval; }; - PHPSymbolsProvider.prototype.getWorkspaceSymbols = function (query, matcher) { + 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(); } @@ -161,6 +194,7 @@ define(function (require, exports, module) { } var retval = $.Deferred(); + query = query.slice(1); this.client.requestSymbolsForWorkspace({ query: query @@ -172,58 +206,41 @@ define(function (require, exports, module) { return retval; }; - PHPSymbolsProvider.prototype.match = function (query) { - return (query.startsWith("~") || query.startsWith("#")); - }; - - PHPSymbolsProvider.prototype.itemFocus = function (selectedItem, query, explicit) { + ProjectSymbolsProvider.prototype.itemFocus = function (selectedItem, query, explicit) { if (!selectedItem || (query.length < 2 && !explicit)) { return; } - - if (selectedItem.symbolInfo.isDocumentSymbolRequest) { - var range = selectedItem.symbolInfo.selectionRange; - EditorManager.getCurrentFullEditor().setSelection(range.from, range.to, true); - } }; - PHPSymbolsProvider.prototype.itemSelect = function (selectedItem, query) { - if (selectedItem.symbolInfo.isDocumentSymbolRequest) { - this.itemFocus(selectedItem, query, true); - } else { - 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.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); + } + }); } }; - PHPSymbolsProvider.prototype.resultsFormatter = function (item, query) { + ProjectSymbolsProvider.prototype.resultsFormatter = function (item, query) { var displayName = QuickOpen.highlightMatch(item); query = query.slice(1); - if (item.symbolInfo.isDocumentSymbolRequest) { - if (item.symbolInfo.scope) { - return "
  • " + displayName + " (" + item.symbolInfo.type + ")" + "
    " + item.symbolInfo.scope + "
  • "; - } - return "
  • " + displayName + " (" + item.symbolInfo.type + ")" + "
  • "; - } - if (item.symbolInfo.scope) { return "
  • " + displayName + " (" + item.symbolInfo.type + ")" + "
    " + item.symbolInfo.scope + "

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

    " + item.symbolInfo.fullPath + "
  • "; }; - exports.SymbolsProvider = PHPSymbolsProvider; + exports.SymbolProviders = { + DocumentSymbolsProvider: DocumentSymbolsProvider, + ProjectSymbolsProvider: ProjectSymbolsProvider + }; }); diff --git a/src/extensions/default/PhpTooling/main.js b/src/extensions/default/PhpTooling/main.js index 9953968062d..a2de7502d50 100755 --- a/src/extensions/default/PhpTooling/main.js +++ b/src/extensions/default/PhpTooling/main.js @@ -36,7 +36,7 @@ define(function (require, exports, module) { CodeInspection = brackets.getModule("language/CodeInspection"), DefaultProviders = brackets.getModule("languageTools/DefaultProviders"), CodeHintsProvider = require("CodeHintsProvider").CodeHintsProvider, - SymbolsProvider = require("PHPSymbolsProvider").SymbolsProvider, + SymbolProviders = require("PHPSymbolProviders").SymbolProviders, DefaultEventHandlers = brackets.getModule("languageTools/DefaultEventHandlers"), PreferencesManager = brackets.getModule("preferences/PreferencesManager"), Strings = brackets.getModule("strings"), @@ -64,7 +64,8 @@ define(function (require, exports, module) { phProvider, lProvider, jdProvider, - symProvider; + dSymProvider, + pSymProvider; PreferencesManager.definePreference("php", "object", phpConfig, { description: Strings.DESCRIPTION_PHP_TOOLING_CONFIGURATION @@ -105,7 +106,8 @@ define(function (require, exports, module) { phProvider = new DefaultProviders.ParameterHintsProvider(_client), lProvider = new DefaultProviders.LintingProvider(_client), jdProvider = new DefaultProviders.JumpToDefProvider(_client); - symProvider = new SymbolsProvider(_client); + dSymProvider = new SymbolProviders.DocumentSymbolsProvider(_client); + pSymProvider = new SymbolProviders.ProjectSymbolsProvider(_client); JumpToDefManager.registerJumpToDefProvider(jdProvider, ["php"], 0); CodeHintManager.registerHintProvider(chProvider, ["php"], 0); @@ -114,15 +116,30 @@ define(function (require, exports, module) { name: "", scanFileAsync: lProvider.getInspectionResultsAsync.bind(lProvider) }); + //Attach plugin for Document Symbols QuickOpen.addQuickOpenPlugin({ - name: "PHP Symbols", + name: "PHP Document Symbols", + label: Strings.CMD_FIND_DOCUMENT_SYMBOLS + "\u2026", languageIds: ["php"], - search: symProvider.search.bind(symProvider), - match: symProvider.match.bind(symProvider), - itemFocus: symProvider.itemFocus.bind(symProvider), - itemSelect: symProvider.itemSelect.bind(symProvider), - resultsFormatter: symProvider.resultsFormatter.bind(symProvider) + 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/nls/root/strings.js b/src/nls/root/strings.js index 5b4aa6d2078..e06a02e7b3e 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -419,8 +419,7 @@ define({ "CMD_QUICK_OPEN" : "Quick Open", "CMD_GOTO_LINE" : "Go to Line", "CMD_GOTO_DEFINITION" : "Quick Find Definition", - "CMD_FIND_DOCUMENT_SYMBOLS" : "Find Document Symbols", - "CMD_FIND_PROJECT_SYMBOLS" : "Find Project Symbols", + "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", @@ -890,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 9f2e46a4ce3..3d4fc92471a 100644 --- a/src/search/QuickOpen.js +++ b/src/search/QuickOpen.js @@ -654,11 +654,8 @@ define(function (require, exports, module) { case "@": dialogLabel = Strings.CMD_GOTO_DEFINITION + "\u2026"; break; - case "~": - dialogLabel = Strings.CMD_FIND_DOCUMENT_SYMBOLS + "\u2026"; - break; case "#": - dialogLabel = Strings.CMD_FIND_PROJECT_SYMBOLS + "\u2026"; + dialogLabel = Strings.CMD_GOTO_DEFINITION_PROJECT + "\u2026"; break; default: dialogLabel = ""; @@ -779,16 +776,24 @@ define(function (require, exports, module) { } } - function doSymbolSearchInDocument() { + function doDefinitionSearchInProject() { if (DocumentManager.getCurrentDocument()) { - beginSearch("~", getCurrentEditorSelectedText()); + beginSearch("#", getCurrentEditorSelectedText()); } } - function doSymbolSearchInProject() { - 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; } // Listen for a change of project to invalidate our file list @@ -796,10 +801,33 @@ define(function (require, exports, module) { 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(); + + var plugins = _providerRegistrationHandler.getProvidersForLanguageId(newLanguageId); + 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); + } + }); + 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_FIND_DOCUMENT_SYMBOLS, Commands.NAVIGATE_FIND_DOCUMENT_SYMBOLS, doSymbolSearchInDocument); - CommandManager.register(Strings.CMD_FIND_PROJECT_SYMBOLS, Commands.NAVIGATE_FIND_PROJECT_SYMBOLS, doSymbolSearchInProject); + 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; From 80d5a4950e47cdffed5d980b060fcea83af909df Mon Sep 17 00:00:00 2001 From: Shubham Yadav Date: Fri, 12 Apr 2019 19:20:38 +0530 Subject: [PATCH 08/10] Handle menu toggling in case of language change --- src/search/QuickOpen.js | 47 +++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/src/search/QuickOpen.js b/src/search/QuickOpen.js index 3d4fc92471a..9d4078ff0b3 100644 --- a/src/search/QuickOpen.js +++ b/src/search/QuickOpen.js @@ -782,7 +782,7 @@ define(function (require, exports, module) { } } - function canHandleTrigger(trigger, plugins) { + function _canHandleTrigger(trigger, plugins) { var retval = false; plugins.some(function (plugin, index) { @@ -796,6 +796,21 @@ define(function (require, exports, module) { 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; @@ -810,19 +825,29 @@ define(function (require, exports, module) { 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); + }); - var plugins = _providerRegistrationHandler.getProvidersForLanguageId(newLanguageId); - if (canHandleTrigger("@", plugins)) { - CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION).setEnabled(true); - } else { - CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION).setEnabled(false); + if (!oldFile) { + return; } - if (canHandleTrigger("#", plugins)) { - CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION_PROJECT).setEnabled(true); - } else { - CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION_PROJECT).setEnabled(false); - } + 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); From 7bb0376c210de7e6f75170d2f8ca2109b4023f98 Mon Sep 17 00:00:00 2001 From: Shubham Yadav Date: Fri, 12 Apr 2019 22:37:21 +0530 Subject: [PATCH 09/10] Remove active document check --- src/extensions/default/PhpTooling/PHPSymbolProviders.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/extensions/default/PhpTooling/PHPSymbolProviders.js b/src/extensions/default/PhpTooling/PHPSymbolProviders.js index 7852f15f978..8517e1db574 100644 --- a/src/extensions/default/PhpTooling/PHPSymbolProviders.js +++ b/src/extensions/default/PhpTooling/PHPSymbolProviders.js @@ -29,7 +29,6 @@ define(function (require, exports, module) { var EditorManager = brackets.getModule("editor/EditorManager"), QuickOpen = brackets.getModule("search/QuickOpen"), Commands = brackets.getModule("command/Commands"), - DocumentManager = brackets.getModule("document/DocumentManager"), CommandManager = brackets.getModule("command/CommandManager"), PathConverters = brackets.getModule("languageTools/PathConverters"); @@ -52,13 +51,6 @@ define(function (require, exports, module) { } function createList(list, isDocumentSymbolRequest) { - var doc = DocumentManager.getCurrentDocument(); - - //Should only function when a document is open - if (!doc) { - return []; - } - var newlist = []; for (var i = 0; i < list.length; i++) { var symbolInfo = list[i], From 137e8a0373aa78f6ed82dee65fbfb569a6954d12 Mon Sep 17 00:00:00 2001 From: Shubham Yadav Date: Fri, 12 Apr 2019 23:44:05 +0530 Subject: [PATCH 10/10] Add unittests for QuickOpen Symbols Provider --- .../PhpTooling/unittest-files/test/test4.php | 13 ++ .../default/PhpTooling/unittests.js | 130 +++++++++++++++++- 2 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 src/extensions/default/PhpTooling/unittest-files/test/test4.php 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); + }); + }); }); });