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

Commit

Permalink
Reinitiate tooling service on Node process crash (#14724)
Browse files Browse the repository at this point in the history
* Fix for node process failing

* Modification #1

* Code cleanup

* Code cleanup #1

* Code cleanup #2: Removing timeout and adding comments

* Fixing Logging issue
  • Loading branch information
shubhsnov authored and swmitra committed Apr 25, 2019
1 parent 16ed866 commit 6db26a2
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 26 deletions.
4 changes: 4 additions & 0 deletions src/extensions/default/PhpTooling/CodeHintsProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ define(function (require, exports, module) {
this.defaultCodeHintProviders = new DefaultProviders.CodeHintsProvider(client);
}

CodeHintsProvider.prototype.setClient = function (client) {
this.defaultCodeHintProviders.setClient(client);
};

function setStyleAndCacheToken($hintObj, token) {
$hintObj.addClass('brackets-hints-with-type-details');
$hintObj.data('completionItem', token);
Expand Down
12 changes: 11 additions & 1 deletion src/extensions/default/PhpTooling/PHPSymbolProviders.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
*/

/*jslint regexp: true */

/*eslint no-invalid-this: 0, max-len: 0*/
define(function (require, exports, module) {
"use strict";

Expand All @@ -34,6 +34,12 @@ define(function (require, exports, module) {

var SymbolKind = QuickOpen.SymbolKind;

function setClient(client) {
if (client) {
this.client = client;
}
}

function convertRangePosToEditorPos(rangePos) {
return {
line: rangePos.line,
Expand Down Expand Up @@ -112,6 +118,8 @@ define(function (require, exports, module) {
this.client = client;
}

DocumentSymbolsProvider.prototype.setClient = setClient;

DocumentSymbolsProvider.prototype.match = function (query) {
return query.startsWith("@");
};
Expand Down Expand Up @@ -171,6 +179,8 @@ define(function (require, exports, module) {
this.client = client;
}

ProjectSymbolsProvider.prototype.setClient = setClient;

ProjectSymbolsProvider.prototype.match = function (query) {
return query.startsWith("#");
};
Expand Down
56 changes: 47 additions & 9 deletions src/extensions/default/PhpTooling/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ define(function (require, exports, module) {
"use strict";

var LanguageTools = brackets.getModule("languageTools/LanguageTools"),
ClientLoader = brackets.getModule("languageTools/ClientLoader"),
AppInit = brackets.getModule("utils/AppInit"),
ExtensionUtils = brackets.getModule("utils/ExtensionUtils"),
ProjectManager = brackets.getModule("project/ProjectManager"),
Expand Down Expand Up @@ -61,13 +62,14 @@ define(function (require, exports, module) {
phpServerRunning = false,
serverCapabilities,
currentRootPath,
chProvider,
phProvider,
lProvider,
jdProvider,
dSymProvider,
pSymProvider,
refProvider;
chProvider = null,
phProvider = null,
lProvider = null,
jdProvider = null,
dSymProvider = null,
pSymProvider = null,
refProvider = null,
providersRegistered = false;

PreferencesManager.definePreference("php", "object", phpConfig, {
description: Strings.DESCRIPTION_PHP_TOOLING_CONFIGURATION
Expand Down Expand Up @@ -103,6 +105,18 @@ define(function (require, exports, module) {
}
};

function resetClientInProviders() {
var logErr = "PhpTooling: Can't reset client for : ";
chProvider ? chProvider.setClient(_client) : console.log(logErr, "CodeHintsProvider");
phProvider ? phProvider.setClient(_client) : console.log(logErr, "ParameterHintsProvider");
jdProvider ? jdProvider.setClient(_client) : console.log(logErr, "JumpToDefProvider");
dSymProvider ? dSymProvider.setClient(_client) : console.log(logErr, "DocumentSymbolsProvider");
pSymProvider ? pSymProvider.setClient(_client) : console.log(logErr, "ProjectSymbolsProvider");
refProvider ? refProvider.setClient(_client) : console.log(logErr, "FindReferencesProvider");
lProvider ? lProvider.setClient(_client) : console.log(logErr, "LintingProvider");
_client.addOnCodeInspection(lProvider.setInspectionResults.bind(lProvider));
}

function registerToolingProviders() {
chProvider = new CodeHintsProvider(_client),
phProvider = new DefaultProviders.ParameterHintsProvider(_client),
Expand Down Expand Up @@ -147,6 +161,8 @@ define(function (require, exports, module) {
CommandManager.get(Commands.NAVIGATE_GOTO_DEFINITION_PROJECT).setEnabled(true);

_client.addOnCodeInspection(lProvider.setInspectionResults.bind(lProvider));

providersRegistered = true;
}

function addEventHandlers() {
Expand Down Expand Up @@ -214,7 +230,13 @@ define(function (require, exports, module) {
function handlePostPhpServerStart() {
if (!phpServerRunning) {
phpServerRunning = true;
registerToolingProviders();

if (providersRegistered) {
resetClientInProviders();
} else {
registerToolingProviders();
}

addEventHandlers();
EditorManager.off("activeEditorChange.php");
LanguageManager.off("languageModified.php");
Expand Down Expand Up @@ -262,13 +284,29 @@ define(function (require, exports, module) {
}
}

AppInit.appReady(function () {
function initiateService(evt, onAppReady) {
if (onAppReady) {
console.log("Php tooling: Starting the service");
} else {
console.log("Php tooling: Something went wrong. Restarting the service");
}

phpServerRunning = false;
LanguageTools.initiateToolingService(clientName, clientFilePath, ['php']).done(function (client) {
_client = client;
//Attach only once
EditorManager.off("activeEditorChange.php");
EditorManager.on("activeEditorChange.php", activeEditorChangeHandler);
//Attach only once
LanguageManager.off("languageModified.php");
LanguageManager.on("languageModified.php", languageModifiedHandler);
activeEditorChangeHandler(null, EditorManager.getActiveEditor());
});
}

AppInit.appReady(function () {
initiateService(null, true);
ClientLoader.on("languageClientModuleInitialized", initiateService);
});

//Only for Unit testing
Expand Down
77 changes: 64 additions & 13 deletions src/languageTools/ClientLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ define(function (require, exports, module) {
var ToolingInfo = JSON.parse(require("text!languageTools/ToolingInfo.json")),
NodeDomain = require("utils/NodeDomain"),
FileUtils = require("file/FileUtils"),
EventDispatcher = require("utils/EventDispatcher"),
BracketsToNodeInterface = require("languageTools/BracketsToNodeInterface").BracketsToNodeInterface;

EventDispatcher.makeEventDispatcher(exports);
//Register paths required for Language Client and also register default brackets capabilities.
var _bracketsPath = FileUtils.getNativeBracketsDirectoryPath();
// The native directory path ends with either "test" or "src".
Expand All @@ -39,22 +41,12 @@ define(function (require, exports, module) {
var _modulePath = FileUtils.getNativeModuleDirectoryPath(module),
_nodePath = "node/RegisterLanguageClientInfo",
_domainPath = [_bracketsPath, _modulePath, _nodePath].join("/"),
clientInfoDomain = new NodeDomain("LanguageClientInfo", _domainPath),
//Init node with Information required by Language Client
clientInfoLoadedPromise = clientInfoDomain.exec("initialize", _bracketsPath, ToolingInfo),
clientInfoDomain = null,
clientInfoLoadedPromise = null,
//Clients that have to be loaded once the LanguageClient info is successfully loaded on the
//node side.
pendingClientsToBeLoaded = [];

//Attach success and failure function for the clientInfoLoadedPromise
clientInfoLoadedPromise.then(function () {
pendingClientsToBeLoaded.forEach(function (pendingClient) {
pendingClient.load();
});
}, function () {
console.log("Failed to Initialize LanguageClient Module Information.");
});

function syncPrefsWithDomain(languageToolsPrefs) {
if (clientInfoDomain) {
clientInfoDomain.exec("syncPreferences", languageToolsPrefs);
Expand Down Expand Up @@ -111,7 +103,7 @@ define(function (require, exports, module) {
var result = $.Deferred();

//Only load clients after the LanguageClient Info has been initialized
if (clientInfoLoadedPromise.state() === "pending") {
if (!clientInfoLoadedPromise || clientInfoLoadedPromise.state() === "pending") {
var pendingClient = {
load: _clientLoader.bind(null, clientName, clientFilePath, result)
};
Expand All @@ -123,6 +115,65 @@ define(function (require, exports, module) {
return result;
}

/**
* This function passes Brackets's native directory path as well as the tooling commands
* required by the LanguageClient node module. This information is then maintained in memory
* in the node process server for succesfully loading and functioning of all language clients
* since it is a direct dependency.
*/
function sendLanguageClientInfo() {
//Init node with Information required by Language Client
clientInfoLoadedPromise = clientInfoDomain.exec("initialize", _bracketsPath, ToolingInfo);

function logInitializationError() {
console.error("Failed to Initialize LanguageClient Module Information.");
}

//Attach success and failure function for the clientInfoLoadedPromise
clientInfoLoadedPromise.then(function (success) {
if (!success) {
logInitializationError();
return;
}

if (Array.isArray(pendingClientsToBeLoaded)) {
pendingClientsToBeLoaded.forEach(function (pendingClient) {
pendingClient.load();
});
} else {
exports.trigger("languageClientModuleInitialized");
}
pendingClientsToBeLoaded = null;
}, function () {
logInitializationError();
});
}

/**
* This function starts a domain which initializes the LanguageClient node module
* required by the Language Server Protocol framework in Brackets. All the LSP clients
* can only be successfully initiated once this domain has been successfully loaded and
* the LanguageClient info initialized. Refer to sendLanguageClientInfo for more.
*/
function initDomainAndHandleNodeCrash() {
clientInfoDomain = new NodeDomain("LanguageClientInfo", _domainPath);
//Initialize LanguageClientInfo once the domain has successfully loaded.
clientInfoDomain.promise().done(function () {
sendLanguageClientInfo();
//This is to handle the node failure. If the node process dies, we get an on close
//event on the websocket connection object. Brackets then spawns another process and
//restablishes the connection. Once the connection is restablished we send reinitialize
//the LanguageClient info.
clientInfoDomain.connection.on("close", function (event, reconnectedPromise) {
reconnectedPromise.done(sendLanguageClientInfo);
});
}).fail(function (err) {
console.error("ClientInfo domain could not be loaded: ", err);
});
}
initDomainAndHandleNodeCrash();


exports.initiateLanguageClient = initiateLanguageClient;
exports.syncPrefsWithDomain = syncPrefsWithDomain;
});
18 changes: 17 additions & 1 deletion src/languageTools/DefaultProviders.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

/*global Map*/
/* eslint-disable indent */
/* eslint max-len: ["error", { "code": 200 }]*/
/* eslint max-len: ["error", { "code": 200 }], no-invalid-this: 0*/
define(function (require, exports, module) {
"use strict";

Expand All @@ -44,12 +44,20 @@ define(function (require, exports, module) {

ExtensionUtils.loadStyleSheet(module, "styles/default_provider_style.css");

function setClient(client) {
if (client) {
this.client = client;
}
}

function CodeHintsProvider(client) {
this.client = client;
this.query = "";
this.ignoreQuery = ["-", "->", ">", ":", "::", "(", "()", ")", "[", "[]", "]", "{", "{}", "}"];
}

CodeHintsProvider.prototype.setClient = setClient;

function formatTypeDataForToken($hintObj, token) {
$hintObj.addClass('brackets-hints-with-type-details');
if (token.detail) {
Expand Down Expand Up @@ -197,6 +205,8 @@ define(function (require, exports, module) {
this.client = client;
}

ParameterHintsProvider.prototype.setClient = setClient;

ParameterHintsProvider.prototype.hasParameterHints = function (editor, implicitChar) {
if (!this.client) {
return false;
Expand Down Expand Up @@ -273,6 +283,8 @@ define(function (require, exports, module) {
this.client = client;
}

JumpToDefProvider.prototype.setClient = setClient;

JumpToDefProvider.prototype.canJumpToDef = function (editor, implicitChar) {
if (!this.client) {
return false;
Expand Down Expand Up @@ -342,6 +354,8 @@ define(function (require, exports, module) {
this._validateOnType = false;
}

LintingProvider.prototype.setClient = setClient;

LintingProvider.prototype.clearExistingResults = function (filePath) {
var filePathProvided = !!filePath;

Expand Down Expand Up @@ -451,6 +465,8 @@ define(function (require, exports, module) {
this.client = client;
}

ReferencesProvider.prototype.setClient = setClient;

ReferencesProvider.prototype.hasReferences = function() {
if (!this.client) {
return false;
Expand Down
10 changes: 8 additions & 2 deletions src/languageTools/node/RegisterLanguageClientInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,11 @@ function syncPreferences(prefs) {
global.LanguageClientInfo.preferences = prefs || global.LanguageClientInfo.preferences || {};
}

function initialize(bracketsSourcePath, toolingInfo) {
function initialize(bracketsSourcePath, toolingInfo, resolve) {
if (!bracketsSourcePath || !toolingInfo) {
resolve(true, null); //resolve with err param
}

var normalizedBracketsSourcePath = bracketsSourcePath.split(BACKWARD_SLASH).join(FORWARD_SLASH),
bracketsSourcePathArray = normalizedBracketsSourcePath.split(FORWARD_SLASH),
languageClientAbsolutePath = bracketsSourcePathArray.concat(LANGUAGE_CLIENT_RELATIVE_PATH_ARRAY).join(FORWARD_SLASH);
Expand All @@ -239,6 +243,8 @@ function initialize(bracketsSourcePath, toolingInfo) {
global.LanguageClientInfo.defaultBracketsCapabilities = defaultBracketsCapabilities;
global.LanguageClientInfo.toolingInfo = toolingInfo;
global.LanguageClientInfo.preferences = {};

resolve(null, true); //resolve with boolean denoting success
}

function init(domainManager) {
Expand All @@ -253,7 +259,7 @@ function init(domainManager) {
domainName,
"initialize",
initialize,
false,
true,
"Initialize node environment for Language Client Module",
[
{
Expand Down

0 comments on commit 6db26a2

Please sign in to comment.