From ed34a94545a54d2d301a6e9aa51cbdbb313969c1 Mon Sep 17 00:00:00 2001 From: Nipuna Ranasinghe Date: Sat, 8 Jun 2019 18:58:15 +0530 Subject: [PATCH 1/3] Improve server wrapper error and crash handling --- .../wrapper/LanguageServerWrapper.java | 265 ++++++++++-------- 1 file changed, 147 insertions(+), 118 deletions(-) diff --git a/src/main/java/org/wso2/lsp4intellij/client/languageserver/wrapper/LanguageServerWrapper.java b/src/main/java/org/wso2/lsp4intellij/client/languageserver/wrapper/LanguageServerWrapper.java index a2ef6646..8e360fac 100644 --- a/src/main/java/org/wso2/lsp4intellij/client/languageserver/wrapper/LanguageServerWrapper.java +++ b/src/main/java/org/wso2/lsp4intellij/client/languageserver/wrapper/LanguageServerWrapper.java @@ -15,39 +15,16 @@ */ package org.wso2.lsp4intellij.client.languageserver.wrapper; -import static org.wso2.lsp4intellij.client.languageserver.ServerStatus.INITIALIZED; -import static org.wso2.lsp4intellij.client.languageserver.ServerStatus.STARTED; -import static org.wso2.lsp4intellij.client.languageserver.ServerStatus.STARTING; -import static org.wso2.lsp4intellij.client.languageserver.ServerStatus.STOPPED; -import static org.wso2.lsp4intellij.requests.Timeout.getTimeout; -import static org.wso2.lsp4intellij.requests.Timeouts.INIT; -import static org.wso2.lsp4intellij.requests.Timeouts.SHUTDOWN; - import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.Messages; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import com.intellij.remoteServer.util.CloudNotifier; +import com.intellij.util.PlatformIcons; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair; @@ -102,16 +79,44 @@ import org.wso2.lsp4intellij.editor.listeners.EditorMouseMotionListenerImpl; import org.wso2.lsp4intellij.extensions.LSPExtensionManager; import org.wso2.lsp4intellij.requests.Timeouts; -import org.wso2.lsp4intellij.utils.ApplicationUtils; import org.wso2.lsp4intellij.utils.FileUtils; import org.wso2.lsp4intellij.utils.LSPException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.wso2.lsp4intellij.client.languageserver.ServerStatus.INITIALIZED; +import static org.wso2.lsp4intellij.client.languageserver.ServerStatus.STARTED; +import static org.wso2.lsp4intellij.client.languageserver.ServerStatus.STARTING; +import static org.wso2.lsp4intellij.client.languageserver.ServerStatus.STOPPED; +import static org.wso2.lsp4intellij.requests.Timeout.getTimeout; +import static org.wso2.lsp4intellij.requests.Timeouts.INIT; +import static org.wso2.lsp4intellij.requests.Timeouts.SHUTDOWN; +import static org.wso2.lsp4intellij.utils.ApplicationUtils.invokeLater; + /** * The implementation of a LanguageServerWrapper (specific to a serverDefinition and a project) */ public class LanguageServerWrapper { private Logger LOG = Logger.getInstance(LanguageServerWrapper.class); + private static CloudNotifier notifier = new CloudNotifier("Language Server Protocol client"); private static final Map, LanguageServerWrapper> uriToLanguageServerWrapper = new ConcurrentHashMap<>(); private final LSPExtensionManager extManager; @@ -135,15 +140,11 @@ public class LanguageServerWrapper { private long initializeStartTime = 0L; public LanguageServerWrapper(LanguageServerDefinition serverDefinition, Project project) { - this.serverDefinition = serverDefinition; - this.project = project; - this.rootPath = project.getBasePath(); - this.statusWidget = LSPServerStatusWidget.createWidgetFor(this); - this.extManager = null; + this(serverDefinition, project, null); } public LanguageServerWrapper(LanguageServerDefinition serverDefinition, Project project, - LSPExtensionManager extManager) { + LSPExtensionManager extManager) { this.serverDefinition = serverDefinition; this.project = project; this.rootPath = project.getBasePath(); @@ -177,8 +178,9 @@ public LanguageServerDefinition getServerDefinition() { * @return if the server supports willSaveWaitUntil */ public boolean isWillSaveWaitUntil() { + ServerCapabilities serverCapabilities = getServerCapabilities(); Either capabilities = - getServerCapabilities() != null ? getServerCapabilities().getTextDocumentSync() : null; + serverCapabilities != null ? serverCapabilities.getTextDocumentSync() : null; if (capabilities == null) { return false; } @@ -207,12 +209,12 @@ public ServerCapabilities getServerCapabilities() { } } catch (TimeoutException e) { notifyFailure(INIT); - String msg = "LanguageServer for definition\n " + serverDefinition + "\nnot initialized after " - + getTimeout(INIT) / 1000 + "s\nCheck settings"; + String msg = String.format("%s \n not initialized after %ds \n Check settings", + serverDefinition.toString(), getTimeout(INIT) / 1000); LOG.warn(msg, e); - ApplicationUtils.invokeLater(() -> { + invokeLater(() -> { if (!alreadyShownTimeout) { - Messages.showErrorDialog(msg, "LSP Error"); + notifier.showMessage(msg, MessageType.WARNING); alreadyShownTimeout = true; } }); @@ -286,64 +288,72 @@ public void connect(Editor editor) { start(); if (initializeFuture != null) { ServerCapabilities capabilities = getServerCapabilities(); - if (capabilities != null) { - initializeFuture.thenRun(() -> { - if (!connectedEditors.containsKey(uri)) { - try { - Either syncOptions = capabilities - .getTextDocumentSync(); - TextDocumentSyncKind syncKind = null; - if (syncOptions != null) { - if (syncOptions.isRight()) { - syncKind = syncOptions.getRight().getChange(); - } else if (syncOptions.isLeft()) { - syncKind = syncOptions.getLeft(); - } - //Todo - Implement - // SelectionListenerImpl selectionListener = new SelectionListenerImpl(); - DocumentListenerImpl documentListener = new DocumentListenerImpl(); - EditorMouseListenerImpl mouseListener = new EditorMouseListenerImpl(); - EditorMouseMotionListenerImpl mouseMotionListener = new EditorMouseMotionListenerImpl(); - - ServerOptions serverOptions = new ServerOptions(syncKind, - capabilities.getCompletionProvider(), capabilities.getSignatureHelpProvider(), - capabilities.getCodeLensProvider(), - capabilities.getDocumentOnTypeFormattingProvider(), - capabilities.getDocumentLinkProvider(), - capabilities.getExecuteCommandProvider(), - capabilities.getSemanticHighlighting()); - EditorEventManager manager; - if (extManager != null) { - manager = extManager - .getExtendedEditorEventManagerFor(editor, documentListener, mouseListener, - mouseMotionListener, requestManager, serverOptions, this); - } else { - manager = new EditorEventManager(editor, documentListener, mouseListener, + if (capabilities == null) { + LOG.warn("Capabilities are null for " + serverDefinition); + return; + } + + initializeFuture.thenRun(() -> { + if (connectedEditors.containsKey(uri)) { + return; + } + try { + Either syncOptions = capabilities + .getTextDocumentSync(); + TextDocumentSyncKind syncKind = null; + if (syncOptions != null) { + if (syncOptions.isRight()) { + syncKind = syncOptions.getRight().getChange(); + } else if (syncOptions.isLeft()) { + syncKind = syncOptions.getLeft(); + } + //Todo - Implement + // SelectionListenerImpl selectionListener = new SelectionListenerImpl(); + DocumentListenerImpl documentListener = new DocumentListenerImpl(); + EditorMouseListenerImpl mouseListener = new EditorMouseListenerImpl(); + EditorMouseMotionListenerImpl mouseMotionListener = new EditorMouseMotionListenerImpl(); + + ServerOptions serverOptions = new ServerOptions(syncKind, + capabilities.getCompletionProvider(), capabilities.getSignatureHelpProvider(), + capabilities.getCodeLensProvider(), + capabilities.getDocumentOnTypeFormattingProvider(), + capabilities.getDocumentLinkProvider(), + capabilities.getExecuteCommandProvider(), + capabilities.getSemanticHighlighting()); + + EditorEventManager manager; + if (extManager != null) { + manager = extManager + .getExtendedEditorEventManagerFor(editor, documentListener, mouseListener, mouseMotionListener, requestManager, serverOptions, this); - } - // selectionListener.setManager(manager); - documentListener.setManager(manager); - mouseListener.setManager(manager); - mouseMotionListener.setManager(manager); - manager.registerListeners(); - connectedEditors.put(uri, manager); - manager.documentOpened(); - LOG.info("Created a manager for " + uri); - synchronized (toConnect) { - toConnect.remove(editor); - } - for (Editor ed : toConnect) { - connect(ed); - } + if (manager == null) { + manager = new EditorEventManager(editor, documentListener, mouseListener, + mouseMotionListener, requestManager, serverOptions, this); } - } catch (Exception e) { - LOG.error(e); + } else { + manager = new EditorEventManager(editor, documentListener, mouseListener, + mouseMotionListener, requestManager, serverOptions, this); + } + // selectionListener.setManager(manager); + documentListener.setManager(manager); + mouseListener.setManager(manager); + mouseMotionListener.setManager(manager); + manager.registerListeners(); + connectedEditors.put(uri, manager); + manager.documentOpened(); + LOG.info("Created a manager for " + uri); + synchronized (toConnect) { + toConnect.remove(editor); + } + for (Editor ed : toConnect) { + connect(ed); } } - }); - } else { - LOG.warn("Capabilities are null for " + serverDefinition); - } + } catch (Exception e) { + LOG.error(e); + } + }); + } else { synchronized (toConnect) { toConnect.add(editor); @@ -356,7 +366,7 @@ public void connect(Editor editor) { * * @param uri The uri of the editor */ - public void disconnect(String uri) { + private void disconnect(String uri) { EditorEventManager manager = connectedEditors.remove(uri); if (manager != null) { manager.removeListeners(); @@ -434,7 +444,7 @@ public LanguageServer getServer() { /** * Starts the LanguageServer */ - public void start() { + private void start() { if (status == STOPPED && !alreadyShownCrash && !alreadyShownTimeout) { setStatus(STARTING); try { @@ -444,7 +454,7 @@ public void start() { InitializeParams initParams = getInitParams(); ExecutorService executorService = Executors.newCachedThreadPool(); MessageHandler messageHandler = new MessageHandler(serverDefinition.getServerListener()); - if (extManager != null) { + if (extManager != null && extManager.getExtendedServerInterface() != null) { Class remoteServerInterFace = extManager.getExtendedServerInterface(); client = extManager.getExtendedClientFor(new ServerWrapperBaseClientContext(this)); @@ -467,8 +477,10 @@ public void start() { initializeResult = res; LOG.info("Got initializeResult for " + serverDefinition + " ; " + rootPath); if (extManager != null) { - requestManager = extManager - .getExtendedRequestManagerFor(this, languageServer, client, res.getCapabilities()); + requestManager = extManager.getExtendedRequestManagerFor(this, languageServer, client, res.getCapabilities()); + if (requestManager == null) { + requestManager = new DefaultRequestManager(this, languageServer, client, res.getCapabilities()); + } } else { requestManager = new DefaultRequestManager(this, languageServer, client, res.getCapabilities()); } @@ -481,9 +493,9 @@ public void start() { initializeStartTime = System.currentTimeMillis(); } catch (LSPException | IOException e) { LOG.warn(e); - ApplicationUtils.invokeLater(() -> Messages - .showErrorDialog("Can't start server, please check " + "settings\n" + e.getMessage(), - "LSP Error")); + invokeLater(() -> + notifier.showMessage(String.format("Can't start server due to %s", e.getMessage()), + MessageType.WARNING)); removeServerWrapper(); } } @@ -549,25 +561,42 @@ private void setStatus(ServerStatus status) { public void crashed(Exception e) { crashCount += 1; - if (crashCount < 2) { - final Set editors = connectedEditors.keySet(); - stop(false); - for (String uri : editors) { - connect(uri); - } + if (crashCount <= 3) { + reconnect(); } else { - removeServerWrapper(); - if (!alreadyShownCrash) { - ApplicationUtils.invokeLater(() -> { - if (!alreadyShownCrash) { - Messages.showErrorDialog( - "LanguageServer for definition " + serverDefinition + ", project " + project - + " keeps crashing due to \n" + e.getMessage() + "\nCheck settings.", - "LSP Error"); - alreadyShownCrash = true; + invokeLater(() -> { + if (alreadyShownCrash) { + reconnect(); + } else { + int response = Messages.showYesNoDialog(String.format( + "LanguageServer for definition %s, project %s keeps crashing due to \n%s\n" + , serverDefinition.toString(), project.getName(), e.getMessage()), + "Language Server Client Warning", "Keep Connected", "Disconnect", null); + if (response == Messages.NO) { + int confirm = Messages.showYesNoDialog("All the language server based plugin features will be disabled until the next IDEA restart. " + + "Do you wish to continue?", "", PlatformIcons.WARNING_INTRODUCTION_ICON); + if (confirm == Messages.YES) { + // Disconnects from the language server. + removeServerWrapper(); + } else { + reconnect(); + } + } else { + reconnect(); } - }); - } + } + alreadyShownCrash = true; + crashCount = 0; + }); + } + } + + private void reconnect() { + // Need to copy by value since connected editors gets cleared during 'stop()' invocation. + final Set connected = new HashSet<>(connectedEditors.keySet()); + stop(false); + for (String uri : connected) { + connect(uri); } } @@ -597,7 +626,7 @@ public void disconnect(Editor editor) { } private void removeServerWrapper() { - stop(false); + stop(true); removeWidget(); IntellijLanguageClient.removeWrapper(this); } From efd4cfaf5234b63564d25f6ca2af0b29a438f80b Mon Sep 17 00:00:00 2001 From: Nipuna Ranasinghe Date: Sun, 9 Jun 2019 12:18:37 +0530 Subject: [PATCH 2/3] Fix wrapper removing and add minor improvements --- .../lsp4intellij/IntellijLanguageClient.java | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/wso2/lsp4intellij/IntellijLanguageClient.java b/src/main/java/org/wso2/lsp4intellij/IntellijLanguageClient.java index 3db5d3e7..7020595e 100644 --- a/src/main/java/org/wso2/lsp4intellij/IntellijLanguageClient.java +++ b/src/main/java/org/wso2/lsp4intellij/IntellijLanguageClient.java @@ -60,13 +60,13 @@ public class IntellijLanguageClient implements ApplicationComponent { + private static Logger LOG = Logger.getInstance(IntellijLanguageClient.class); + private static final Map, LanguageServerWrapper> extToLanguageWrapper = new ConcurrentHashMap<>(); private static Map> projectToLanguageWrappers = new ConcurrentHashMap<>(); private static Map extToServerDefinition = new ConcurrentHashMap<>(); private static Map extToExtManager = new ConcurrentHashMap<>(); - private static Logger LOG = Logger.getInstance(IntellijLanguageClient.class); - @Override public void initComponent() { // LSPState.getInstance.getState(); //Need that to trigger loadState @@ -139,14 +139,14 @@ public static boolean isExtensionSupported(String ext) { public static void editorOpened(Editor editor) { VirtualFile file = FileDocumentManager.getInstance().getFile(editor.getDocument()); if (!FileUtils.isFileSupported(file)) { - LOG.debug("Handling open on a editor which host a virtual file"); + LOG.debug("Handling open on a editor which host a LightVirtual/Null file"); return; } Project project = editor.getProject(); String rootPath = FileUtils.editorToProjectFolderPath(editor); String rootUri = FileUtils.pathToUri(rootPath); - if (file != null && rootUri != null) { + if (rootUri != null && project != null) { ApplicationUtils.pool(() -> { String ext = file.getExtension(); final String fileName = file.getName(); @@ -183,23 +183,25 @@ public static void editorOpened(Editor editor) { // Update project mapping for language servers final String projectUri = FileUtils.pathToUri(project.getBasePath()); - Set wrappers = projectToLanguageWrappers.get(projectUri); - if (wrappers == null) { - wrappers = new HashSet<>(); - projectToLanguageWrappers.put(projectUri, wrappers); - } - if (!wrappers.contains(wrapper)) { - wrappers.add(wrapper); - } + Set wrappers = projectToLanguageWrappers + .computeIfAbsent(projectUri, k -> new HashSet<>()); + wrappers.add(wrapper); } else { LOG.info("Wrapper already existing for " + ext + " , " + rootUri); } LOG.info("Adding file " + fileName); wrapper.connect(editor); + } else { + LOG.warn("Could not find a server definition for " + ext); } }); } else { - LOG.warn("File for editor " + editor.getDocument().getText() + " is null"); + if (rootUri == null) { + LOG.warn("File for editor " + editor.getDocument().getText() + " is null"); + } + if (project == null) { + LOG.warn("Project for editor " + editor.getDocument().getText() + " is null"); + } } } @@ -211,22 +213,17 @@ public static void editorOpened(Editor editor) { public static void editorClosed(Editor editor) { VirtualFile file = FileDocumentManager.getInstance().getFile(editor.getDocument()); if (!FileUtils.isFileSupported(file)) { - LOG.debug("Handling close on a editor which host a virtual file"); + LOG.debug("Handling close on a editor which host a LightVirtual/Null file"); return; } - if (file != null) { - ApplicationUtils.pool(() -> { - String ext = file.getExtension(); - LanguageServerWrapper serverWrapper = LanguageServerWrapper.forEditor(editor); - if (serverWrapper != null) { - LOG.info("Disconnecting " + FileUtils.editorToURIString(editor)); - serverWrapper.disconnect(editor); - } - }); - } else { - LOG.warn("File for editor " + editor.getDocument().getText() + " is null"); - } + ApplicationUtils.pool(() -> { + LanguageServerWrapper serverWrapper = LanguageServerWrapper.forEditor(editor); + if (serverWrapper != null) { + LOG.info("Disconnecting " + FileUtils.editorToURIString(editor)); + serverWrapper.disconnect(editor); + } + }); } /** @@ -339,11 +336,13 @@ private static void flattenExtensions() { public static void removeWrapper(LanguageServerWrapper wrapper) { if (wrapper.getProject() != null) { - extToLanguageWrapper.remove(new MutablePair<>(wrapper.getServerDefinition().ext, - FileUtils.pathToUri(wrapper.getProject().getBasePath()))); + String[] extensions = wrapper.getServerDefinition().ext.split(LanguageServerDefinition.SPLIT_CHAR); + for (String ext : extensions) { + extToLanguageWrapper.remove(new MutablePair<>(ext, + FileUtils.pathToUri(wrapper.getProject().getBasePath()))); + } } else { LOG.error("No attached projects found for wrapper"); - } } From 1a60106c9b304b00c10a1125efc642bea825a068 Mon Sep 17 00:00:00 2001 From: Nipuna Ranasinghe Date: Mon, 10 Jun 2019 15:44:50 +0530 Subject: [PATCH 3/3] Add minor changes --- .../wrapper/LanguageServerWrapper.java | 9 +++++---- .../lsp4intellij/editor/EditorEventManager.java | 15 ++++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/wso2/lsp4intellij/client/languageserver/wrapper/LanguageServerWrapper.java b/src/main/java/org/wso2/lsp4intellij/client/languageserver/wrapper/LanguageServerWrapper.java index 8e360fac..35041996 100644 --- a/src/main/java/org/wso2/lsp4intellij/client/languageserver/wrapper/LanguageServerWrapper.java +++ b/src/main/java/org/wso2/lsp4intellij/client/languageserver/wrapper/LanguageServerWrapper.java @@ -79,6 +79,7 @@ import org.wso2.lsp4intellij.editor.listeners.EditorMouseMotionListenerImpl; import org.wso2.lsp4intellij.extensions.LSPExtensionManager; import org.wso2.lsp4intellij.requests.Timeouts; +import org.wso2.lsp4intellij.utils.ApplicationUtils; import org.wso2.lsp4intellij.utils.FileUtils; import org.wso2.lsp4intellij.utils.LSPException; @@ -571,13 +572,13 @@ public void crashed(Exception e) { int response = Messages.showYesNoDialog(String.format( "LanguageServer for definition %s, project %s keeps crashing due to \n%s\n" , serverDefinition.toString(), project.getName(), e.getMessage()), - "Language Server Client Warning", "Keep Connected", "Disconnect", null); + "Language Server Client Warning", "Keep Connected", "Disconnect", PlatformIcons.CHECK_ICON); if (response == Messages.NO) { - int confirm = Messages.showYesNoDialog("All the language server based plugin features will be disabled until the next IDEA restart. " + + int confirm = Messages.showYesNoDialog("All the language server based plugin features will be disabled.\n" + "Do you wish to continue?", "", PlatformIcons.WARNING_INTRODUCTION_ICON); if (confirm == Messages.YES) { // Disconnects from the language server. - removeServerWrapper(); + stop(true); } else { reconnect(); } @@ -594,7 +595,7 @@ public void crashed(Exception e) { private void reconnect() { // Need to copy by value since connected editors gets cleared during 'stop()' invocation. final Set connected = new HashSet<>(connectedEditors.keySet()); - stop(false); + stop(true); for (String uri : connected) { connect(uri); } diff --git a/src/main/java/org/wso2/lsp4intellij/editor/EditorEventManager.java b/src/main/java/org/wso2/lsp4intellij/editor/EditorEventManager.java index e6504241..1c52a045 100644 --- a/src/main/java/org/wso2/lsp4intellij/editor/EditorEventManager.java +++ b/src/main/java/org/wso2/lsp4intellij/editor/EditorEventManager.java @@ -117,8 +117,9 @@ import org.wso2.lsp4intellij.utils.FileUtils; import org.wso2.lsp4intellij.utils.GUIUtils; -import javax.swing.*; -import java.awt.*; +import javax.swing.Icon; +import java.awt.Cursor; +import java.awt.Point; import java.io.File; import java.net.URI; import java.net.URISyntaxException; @@ -726,7 +727,7 @@ private void requestAndShowDoc(long curTime, LogicalPosition editorPos, Point po wrapper.notifySuccess(Timeouts.HOVER); if (hover != null) { String string = HoverHandler.getHoverString(hover); - if (string != null && !string.equals("")) { + if (!Strings.isNullOrEmpty(string)) { if (getIsCtrlDown()) { invokeLater(() -> { if (!editor.isDisposed()) { @@ -743,12 +744,12 @@ private void requestAndShowDoc(long curTime, LogicalPosition editorPos, Point po }); } } else { - LOG.warn("Hover string returned is null for file " + identifier.getUri() + " and pos (" + serverPos - .getLine() + ";" + serverPos.getCharacter() + ")"); + LOG.warn(String.format("Hover string returned is null for file %s and pos (%d;%d)", + identifier.getUri(), serverPos.getLine(), serverPos.getCharacter())); } } else { - LOG.warn("Hover is null for file " + identifier.getUri() + " and pos (" + serverPos.getLine() + ";" - + serverPos.getCharacter() + ")"); + LOG.warn(String.format("Hover is null for file %s and pos (%d;%d)", + identifier.getUri(), serverPos.getLine(), serverPos.getCharacter())); } } catch (TimeoutException e) { LOG.warn(e);