Repository: sublimelsp/LSP

Issue:
Forced-break in hover popup can break syntax highlighting
Using: OSX / typescript-language-server

Line-break, that LSP forces in the popup, can cause syntax highlighting to break. For example, if breaking a plain string in JS syntax.

<img width="512" alt="line-break" src="https://user-images.githubusercontent.com/153197/72525594-cfa7ff00-3864-11ea-9e8a-c183e07995a1.png">

Notice that in the screenshot the whole string should have a yellow color. Syntax highlighting breaks because line break within a string is not a valid syntax.


Discussion:
related https://github.com/sublimelsp/LSP/issues/486
We might be able to fix this by writing some frontmatter: https://facelessuser.github.io/sublime-markdown-popups/api/#enable-code-wrapping
With `allow_code_wrap: false` option? I just feel like it would do bad things to some monospace code blocks. But maybe not...
No, we wrap manually now. If we set `allow_code_wrap: true` in the frontmatter, it might be possible to remove the manual wrapping.

https://github.com/sublimelsp/LSP/blob/c50a1547c7510c3fd9a9ccf45f8acb6a06ffbbbe/plugin/hover.py#L194-L195

I'm not sure what mdpopups can do that we're not already doing, but it might go about doing things a bit smarter with code blocks.
Sorry, I meant `allow_code_wrap: true`.
I did a quick hacky test. In that specific case, it did well but then it decided not to break (which is fine as it's not wider than popup width):
![Screenshot 2020-03-23 at 20 49 45](https://user-images.githubusercontent.com/153197/77357236-1bd15d00-6d48-11ea-93dc-df6d69ca0f1d.png)

So it seems like it actually never breaks if there is no break opportunity (spaces for example).
Compared also some other case that has spaces but I guess that case is special in LSP where it doesn't even try to break:

Without `frontmatter`:
![Screenshot 2020-03-23 at 20 54 44](https://user-images.githubusercontent.com/153197/77357566-a9ad4800-6d48-11ea-81fa-0cb58a310bcd.png)

With `frontmatter`:
![Screenshot 2020-03-23 at 20 53 59](https://user-images.githubusercontent.com/153197/77357585-af0a9280-6d48-11ea-8f07-98dbf530385a.png)
And another case where LSP actually does break the line:

Without `frontmatter`:
![Screenshot 2020-03-23 at 20 56 48](https://user-images.githubusercontent.com/153197/77357789-027ce080-6d49-11ea-9bd6-ed083d11d0d9.png)

With `frontmatter`:
![Screenshot 2020-03-23 at 20 58 06](https://user-images.githubusercontent.com/153197/77357804-090b5800-6d49-11ea-8916-2c9e1d772c82.png)

I think I like first and the last case better with `frontmatter` at least (maybe even all of them).
Sounds like you have a pull request brewing :)

Resolving patch:
diff --git a/boot.py b/boot.py
--- a/boot.py
+++ b/boot.py
@@ -1,30 +1,43 @@
-from .plugin.core.main import startup, shutdown
-
-# TODO: narrow down imports
-from .plugin.core.panels import *
+# Please keep this list sorted (Edit -> Sort Lines)
+from .plugin.code_actions import LspCodeActionBulbListener
+from .plugin.code_actions import LspCodeActionsCommand
+from .plugin.color import LspColorListener
+from .plugin.completion import CompletionHandler
+from .plugin.completion import LspSelectCompletionItemCommand
+from .plugin.completion import LspTrimCompletionCommand
+from .plugin.configuration import LspDisableLanguageServerGloballyCommand
+from .plugin.configuration import LspDisableLanguageServerInProjectCommand
+from .plugin.configuration import LspEnableLanguageServerGloballyCommand
+from .plugin.configuration import LspEnableLanguageServerInProjectCommand
+from .plugin.core.documents import DocumentSyncListener
+from .plugin.core.main import shutdown as plugin_unloaded
+from .plugin.core.main import startup as plugin_loaded
+from .plugin.core.panels import LspClearPanelCommand
+from .plugin.core.panels import LspUpdatePanelCommand
+from .plugin.core.panels import LspUpdateServerPanelCommand
 from .plugin.core.registry import LspRestartClientCommand
-from .plugin.core.documents import *
-from .plugin.panels import *
-from .plugin.edit import *
-from .plugin.completion import *
-from .plugin.diagnostics import *
-from .plugin.configuration import *
-from .plugin.formatting import *
-from .plugin.highlights import *
-from .plugin.goto import *
-from .plugin.hover import *
-from .plugin.references import *
-from .plugin.signature_help import *
-from .plugin.code_actions import *
-from .plugin.color import *
-from .plugin.symbols import *
-from .plugin.rename import *
-from .plugin.execute_command import *
-from .plugin.workspace_symbol import *
-
-def plugin_loaded():
-    startup()
-
-
-def plugin_unloaded():
-    shutdown()
+from .plugin.diagnostics import DiagnosticsCursorListener
+from .plugin.diagnostics import LspClearDiagnosticsCommand
+from .plugin.diagnostics import LspHideDiagnosticCommand
+from .plugin.diagnostics import LspNextDiagnosticCommand
+from .plugin.diagnostics import LspPreviousDiagnosticCommand
+from .plugin.edit import LspApplyDocumentEditCommand
+from .plugin.edit import LspApplyWorkspaceEditCommand
+from .plugin.execute_command import LspExecuteCommand
+from .plugin.formatting import FormatOnSaveListener
+from .plugin.formatting import LspFormatDocumentCommand
+from .plugin.formatting import LspFormatDocumentRangeCommand
+from .plugin.goto import LspSymbolDeclarationCommand
+from .plugin.goto import LspSymbolDefinitionCommand
+from .plugin.goto import LspSymbolImplementationCommand
+from .plugin.goto import LspSymbolTypeDefinitionCommand
+from .plugin.highlights import DocumentHighlightListener
+from .plugin.hover import HoverHandler
+from .plugin.hover import LspHoverCommand
+from .plugin.panels import LspShowDiagnosticsPanelCommand
+from .plugin.panels import LspToggleServerPanelCommand
+from .plugin.references import LspSymbolReferencesCommand
+from .plugin.rename import LspSymbolRenameCommand
+from .plugin.signature_help import SignatureHelpListener
+from .plugin.symbols import LspDocumentSymbolsCommand
+from .plugin.workspace_symbol import LspWorkspaceSymbolsCommand
diff --git a/plugin/core/transports.py b/plugin/core/transports.py
--- a/plugin/core/transports.py
+++ b/plugin/core/transports.py
@@ -244,6 +244,8 @@ def read_stdout(self) -> None:
             # We use the stdout thread to block and wait on the exiting process, or zombie processes may be the result.
             returncode = self.process.wait()
             debug("process {} exited with code {}".format(pid, returncode))
+            if returncode != 0:
+                self.close()
         self.send_queue.put(None)
 
     def send(self, content: str) -> None:
diff --git a/plugin/hover.py b/plugin/hover.py
--- a/plugin/hover.py
+++ b/plugin/hover.py
@@ -3,7 +3,6 @@
 import sublime_plugin
 import webbrowser
 import os
-import textwrap
 from html import escape
 from .code_actions import actions_manager, run_code_action_or_command
 from .code_actions import CodeActionOrCommand
@@ -127,7 +126,7 @@ def symbol_actions_content(self) -> str:
             actions.append("<a href='{}'>{}</a>".format('references', 'References'))
         if self.has_client_with_capability('renameProvider'):
             actions.append("<a href='{}'>{}</a>".format('rename', 'Rename'))
-        return "<p>" + " | ".join(actions) + "</p>"
+        return "<p class='actions'>" + " | ".join(actions) + "</p>"
 
     def format_diagnostic_related_info(self, info: DiagnosticRelatedInformation) -> str:
         file_path = info.location.file_path
@@ -191,16 +190,14 @@ def hover_content(self) -> str:
                 value = item.get("value")
                 language = item.get("language")
 
-            if '\n' not in value:
-                value = "\n".join(textwrap.wrap(value, 80))
-
             if language:
                 formatted.append("```{}\n{}\n```\n".format(language, value))
             else:
                 formatted.append(value)
 
         if formatted:
-            return mdpopups.md2html(self.view, "\n".join(formatted))
+            frontmatter_config = mdpopups.format_frontmatter({'allow_code_wrap': True})
+            return mdpopups.md2html(self.view, frontmatter_config + "\n".join(formatted))
 
         return ""
 


Test patch: