We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Feature request: Toggle diagnostic panel
Hi Tom, I have a suggestion.

Right now I have a keyboard shortcut to open the diagnostic panel `ctrl+shift+m`.
As you can see. When I open the panel with that keybinding, and press the keybinding again the panel is still visible. 

![notoggle](https://user-images.githubusercontent.com/22029477/36727088-45b28d4a-1bbc-11e8-9f72-3a6d7f611f09.gif)

Wouldn't it be better if the panel could be toggled like this? :)

![output3](https://user-images.githubusercontent.com/22029477/36727446-8e5a78d6-1bbd-11e8-8968-a914264a20da.gif)


If the answer is yes? I have already done that and I could create a pull request if you want :)

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `plugin/diagnostics.py`
Content:
```
1 import html
2 import os
3 import sublime
4 import sublime_plugin
5 
6 try:
7     from typing import Any, List, Dict, Tuple, Callable, Optional
8     assert Any and List and Dict and Tuple and Callable and Optional
9 except ImportError:
10     pass
11 
12 from .core.settings import settings, PLUGIN_NAME
13 from .core.protocol import Diagnostic, DiagnosticSeverity
14 from .core.events import Events
15 from .core.configurations import is_supported_syntax
16 from .core.diagnostics import DiagnosticsUpdate, get_window_diagnostics, get_line_diagnostics
17 from .core.workspace import get_project_path
18 from .core.panels import create_output_panel
19 
20 diagnostic_severity_names = {
21     DiagnosticSeverity.Error: "error",
22     DiagnosticSeverity.Warning: "warning",
23     DiagnosticSeverity.Information: "info",
24     DiagnosticSeverity.Hint: "hint"
25 }
26 
27 diagnostic_severity_scopes = {
28     DiagnosticSeverity.Error: 'markup.deleted.lsp sublimelinter.mark.error markup.error.lsp',
29     DiagnosticSeverity.Warning: 'markup.changed.lsp sublimelinter.mark.warning markup.warning.lsp',
30     DiagnosticSeverity.Information: 'markup.inserted.lsp sublimelinter.gutter-mark markup.info.lsp',
31     DiagnosticSeverity.Hint: 'markup.inserted.lsp sublimelinter.gutter-mark markup.info.suggestion.lsp'
32 }
33 
34 stylesheet = '''
35             <style>
36                 div.error-arrow {
37                     border-top: 0.4rem solid transparent;
38                     border-left: 0.5rem solid color(var(--redish) blend(var(--background) 30%));
39                     width: 0;
40                     height: 0;
41                 }
42                 div.error {
43                     padding: 0.4rem 0 0.4rem 0.7rem;
44                     margin: 0 0 0.2rem;
45                     border-radius: 0 0.2rem 0.2rem 0.2rem;
46                 }
47 
48                 div.error span.message {
49                     padding-right: 0.7rem;
50                 }
51 
52                 div.error a {
53                     text-decoration: inherit;
54                     padding: 0.35rem 0.7rem 0.45rem 0.8rem;
55                     position: relative;
56                     bottom: 0.05rem;
57                     border-radius: 0 0.2rem 0.2rem 0;
58                     font-weight: bold;
59                 }
60                 html.dark div.error a {
61                     background-color: #00000018;
62                 }
63                 html.light div.error a {
64                     background-color: #ffffff18;
65                 }
66             </style>
67         '''
68 
69 UNDERLINE_FLAGS = (sublime.DRAW_SQUIGGLY_UNDERLINE
70                    | sublime.DRAW_NO_OUTLINE
71                    | sublime.DRAW_NO_FILL
72                    | sublime.DRAW_EMPTY_AS_OVERWRITE)
73 
74 BOX_FLAGS = sublime.DRAW_NO_FILL | sublime.DRAW_EMPTY_AS_OVERWRITE
75 
76 
77 def create_phantom_html(text: str) -> str:
78     global stylesheet
79     return """<body id=inline-error>{}
80                 <div class="error-arrow"></div>
81                 <div class="error">
82                     <span class="message">{}</span>
83                     <a href="code-actions">Code Actions</a>
84                 </div>
85                 </body>""".format(stylesheet, html.escape(text, quote=False))
86 
87 
88 def on_phantom_navigate(view: sublime.View, href: str, point: int):
89     # TODO: don't mess with the user's cursor.
90     sel = view.sel()
91     sel.clear()
92     sel.add(sublime.Region(point))
93     view.run_command("lsp_code_actions")
94 
95 
96 def create_phantom(view: sublime.View, diagnostic: Diagnostic) -> sublime.Phantom:
97     region = diagnostic.range.to_region(view)
98     # TODO: hook up hide phantom (if keeping them)
99     content = create_phantom_html(diagnostic.message)
100     return sublime.Phantom(
101         region,
102         '<p>' + content + '</p>',
103         sublime.LAYOUT_BELOW,
104         lambda href: on_phantom_navigate(view, href, region.begin())
105     )
106 
107 
108 def format_severity(severity: int) -> str:
109     return diagnostic_severity_names.get(severity, "???")
110 
111 
112 def format_diagnostic(diagnostic: Diagnostic) -> str:
113     location = "{:>8}:{:<4}".format(
114         diagnostic.range.start.row + 1, diagnostic.range.start.col + 1)
115     message = diagnostic.message.replace("\n", " ").replace("\r", "")
116     return " {}\t{:<12}\t{:<10}\t{}".format(
117         location, diagnostic.source, format_severity(diagnostic.severity), message)
118 
119 
120 phantom_sets_by_buffer = {}  # type: Dict[int, sublime.PhantomSet]
121 
122 
123 def update_diagnostics_phantoms(view: sublime.View, diagnostics: 'List[Diagnostic]'):
124     global phantom_sets_by_buffer
125 
126     buffer_id = view.buffer_id()
127     if not settings.show_diagnostics_phantoms or view.is_dirty():
128         phantoms = None
129     else:
130         phantoms = list(
131             create_phantom(view, diagnostic) for diagnostic in diagnostics)
132     if phantoms:
133         phantom_set = phantom_sets_by_buffer.get(buffer_id)
134         if not phantom_set:
135             phantom_set = sublime.PhantomSet(view, "lsp_diagnostics")
136             phantom_sets_by_buffer[buffer_id] = phantom_set
137         phantom_set.update(phantoms)
138     else:
139         phantom_sets_by_buffer.pop(buffer_id, None)
140 
141 
142 def update_diagnostics_regions(view: sublime.View, diagnostics: 'List[Diagnostic]', severity: int):
143     region_name = "lsp_" + format_severity(severity)
144     if settings.show_diagnostics_phantoms and not view.is_dirty():
145         regions = None
146     else:
147         regions = list(diagnostic.range.to_region(view) for diagnostic in diagnostics
148                        if diagnostic.severity == severity)
149     if regions:
150         scope_name = diagnostic_severity_scopes[severity]
151         view.add_regions(
152             region_name, regions, scope_name, settings.diagnostics_gutter_marker,
153             UNDERLINE_FLAGS if settings.diagnostics_highlight_style == "underline" else BOX_FLAGS)
154     else:
155         view.erase_regions(region_name)
156 
157 
158 def update_diagnostics_in_view(view: sublime.View, diagnostics: 'List[Diagnostic]'):
159     if view and view.is_valid():
160         update_diagnostics_phantoms(view, diagnostics)
161         for severity in range(DiagnosticSeverity.Error, DiagnosticSeverity.Information):
162             update_diagnostics_regions(view, diagnostics, severity)
163 
164 
165 Events.subscribe("document.diagnostics",
166                  lambda update: handle_diagnostics(update))
167 
168 
169 def handle_diagnostics(update: DiagnosticsUpdate):
170     window = update.window
171     view = window.find_open_file(update.file_path)
172     if view:
173         update_diagnostics_in_view(view, update.diagnostics)
174     update_diagnostics_panel(window)
175 
176 
177 class DiagnosticsCursorListener(sublime_plugin.ViewEventListener):
178     def __init__(self, view):
179         self.view = view
180         self.has_status = False
181 
182     @classmethod
183     def is_applicable(cls, view_settings):
184         syntax = view_settings.get('syntax')
185         return settings.show_diagnostics_in_view_status and syntax and is_supported_syntax(syntax)
186 
187     def on_selection_modified_async(self):
188         selections = self.view.sel()
189         if len(selections) > 0:
190             pos = selections[0].begin()
191             line_diagnostics = get_line_diagnostics(self.view, pos)
192             if len(line_diagnostics) > 0:
193                 self.show_diagnostics_status(line_diagnostics)
194             elif self.has_status:
195                 self.clear_diagnostics_status()
196 
197     def show_diagnostics_status(self, line_diagnostics):
198         self.has_status = True
199         self.view.set_status('lsp_diagnostics', line_diagnostics[0].message)
200 
201     def clear_diagnostics_status(self):
202         self.view.erase_status('lsp_diagnostics')
203         self.has_status = False
204 
205 
206 class LspShowDiagnosticsPanelCommand(sublime_plugin.WindowCommand):
207     def run(self):
208         ensure_diagnostics_panel(self.window)
209         self.window.run_command("show_panel", {"panel": "output.diagnostics"})
210 
211 
212 def create_diagnostics_panel(window):
213     panel = create_output_panel(window, "diagnostics")
214     panel.settings().set("result_file_regex", r"^\s*\S\s+(\S.*):$")
215     panel.settings().set("result_line_regex", r"^\s+([0-9]+):?([0-9]+).*$")
216     panel.assign_syntax("Packages/" + PLUGIN_NAME +
217                         "/Syntaxes/Diagnostics.sublime-syntax")
218     # Call create_output_panel a second time after assigning the above
219     # settings, so that it'll be picked up as a result buffer
220     # see: Packages/Default/exec.py#L228-L230
221     panel = window.create_output_panel("diagnostics")
222     return panel
223 
224 
225 def ensure_diagnostics_panel(window: sublime.Window):
226     return window.find_output_panel("diagnostics") or create_diagnostics_panel(window)
227 
228 
229 def update_diagnostics_panel(window: sublime.Window):
230     assert window, "missing window!"
231     base_dir = get_project_path(window)
232 
233     panel = ensure_diagnostics_panel(window)
234     assert panel, "must have a panel now!"
235 
236     diagnostics_by_file = get_window_diagnostics(window)
237     if diagnostics_by_file is not None:
238         active_panel = window.active_panel()
239         is_active_panel = (active_panel == "output.diagnostics")
240         panel.settings().set("result_base_dir", base_dir)
241         panel.set_read_only(False)
242         if diagnostics_by_file:
243             to_render = []
244             for file_path, source_diagnostics in diagnostics_by_file.items():
245                 relative_file_path = os.path.relpath(file_path, base_dir) if base_dir else file_path
246                 if source_diagnostics:
247                     to_render.append(format_diagnostics(relative_file_path, source_diagnostics))
248             panel.run_command("lsp_update_panel", {"characters": "\n".join(to_render)})
249             if settings.auto_show_diagnostics_panel and not active_panel:
250                 window.run_command("show_panel",
251                                    {"panel": "output.diagnostics"})
252         else:
253             panel.run_command("lsp_clear_panel")
254             if settings.auto_show_diagnostics_panel and is_active_panel:
255                 window.run_command("hide_panel",
256                                    {"panel": "output.diagnostics"})
257         panel.set_read_only(True)
258 
259 
260 def format_diagnostics(file_path, origin_diagnostics):
261     content = " ◌ {}:\n".format(file_path)
262     for origin, diagnostics in origin_diagnostics.items():
263         for diagnostic in diagnostics:
264             item = format_diagnostic(diagnostic)
265             content += item + "\n"
266     return content
267
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/plugin/diagnostics.py b/plugin/diagnostics.py
--- a/plugin/diagnostics.py
+++ b/plugin/diagnostics.py
@@ -206,7 +206,13 @@
 class LspShowDiagnosticsPanelCommand(sublime_plugin.WindowCommand):
     def run(self):
         ensure_diagnostics_panel(self.window)
-        self.window.run_command("show_panel", {"panel": "output.diagnostics"})
+        active_panel = self.window.active_panel()
+        is_active_panel = (active_panel == "output.diagnostics")
+
+        if is_active_panel:
+            self.window.run_command("hide_panel", {"panel": "output.diagnostics"})
+        else:
+            self.window.run_command("show_panel", {"panel": "output.diagnostics"})
 
 
 def create_diagnostics_panel(window):

{"golden_diff": "diff --git a/plugin/diagnostics.py b/plugin/diagnostics.py\n--- a/plugin/diagnostics.py\n+++ b/plugin/diagnostics.py\n@@ -206,7 +206,13 @@\n class LspShowDiagnosticsPanelCommand(sublime_plugin.WindowCommand):\n     def run(self):\n         ensure_diagnostics_panel(self.window)\n-        self.window.run_command(\"show_panel\", {\"panel\": \"output.diagnostics\"})\n+        active_panel = self.window.active_panel()\n+        is_active_panel = (active_panel == \"output.diagnostics\")\n+\n+        if is_active_panel:\n+            self.window.run_command(\"hide_panel\", {\"panel\": \"output.diagnostics\"})\n+        else:\n+            self.window.run_command(\"show_panel\", {\"panel\": \"output.diagnostics\"})\n \n \n def create_diagnostics_panel(window):\n", "issue": "Feature request: Toggle diagnostic panel\nHi Tom, I have a suggestion.\r\n\r\nRight now I have a keyboard shortcut to open the diagnostic panel `ctrl+shift+m`.\r\nAs you can see. When I open the panel with that keybinding, and press the keybinding again the panel is still visible. \r\n\r\n![notoggle](https://user-images.githubusercontent.com/22029477/36727088-45b28d4a-1bbc-11e8-9f72-3a6d7f611f09.gif)\r\n\r\nWouldn't it be better if the panel could be toggled like this? :)\r\n\r\n![output3](https://user-images.githubusercontent.com/22029477/36727446-8e5a78d6-1bbd-11e8-8968-a914264a20da.gif)\r\n\r\n\r\nIf the answer is yes? I have already done that and I could create a pull request if you want :)\n", "before_files": [{"content": "import html\nimport os\nimport sublime\nimport sublime_plugin\n\ntry:\n    from typing import Any, List, Dict, Tuple, Callable, Optional\n    assert Any and List and Dict and Tuple and Callable and Optional\nexcept ImportError:\n    pass\n\nfrom .core.settings import settings, PLUGIN_NAME\nfrom .core.protocol import Diagnostic, DiagnosticSeverity\nfrom .core.events import Events\nfrom .core.configurations import is_supported_syntax\nfrom .core.diagnostics import DiagnosticsUpdate, get_window_diagnostics, get_line_diagnostics\nfrom .core.workspace import get_project_path\nfrom .core.panels import create_output_panel\n\ndiagnostic_severity_names = {\n    DiagnosticSeverity.Error: \"error\",\n    DiagnosticSeverity.Warning: \"warning\",\n    DiagnosticSeverity.Information: \"info\",\n    DiagnosticSeverity.Hint: \"hint\"\n}\n\ndiagnostic_severity_scopes = {\n    DiagnosticSeverity.Error: 'markup.deleted.lsp sublimelinter.mark.error markup.error.lsp',\n    DiagnosticSeverity.Warning: 'markup.changed.lsp sublimelinter.mark.warning markup.warning.lsp',\n    DiagnosticSeverity.Information: 'markup.inserted.lsp sublimelinter.gutter-mark markup.info.lsp',\n    DiagnosticSeverity.Hint: 'markup.inserted.lsp sublimelinter.gutter-mark markup.info.suggestion.lsp'\n}\n\nstylesheet = '''\n            <style>\n                div.error-arrow {\n                    border-top: 0.4rem solid transparent;\n                    border-left: 0.5rem solid color(var(--redish) blend(var(--background) 30%));\n                    width: 0;\n                    height: 0;\n                }\n                div.error {\n                    padding: 0.4rem 0 0.4rem 0.7rem;\n                    margin: 0 0 0.2rem;\n                    border-radius: 0 0.2rem 0.2rem 0.2rem;\n                }\n\n                div.error span.message {\n                    padding-right: 0.7rem;\n                }\n\n                div.error a {\n                    text-decoration: inherit;\n                    padding: 0.35rem 0.7rem 0.45rem 0.8rem;\n                    position: relative;\n                    bottom: 0.05rem;\n                    border-radius: 0 0.2rem 0.2rem 0;\n                    font-weight: bold;\n                }\n                html.dark div.error a {\n                    background-color: #00000018;\n                }\n                html.light div.error a {\n                    background-color: #ffffff18;\n                }\n            </style>\n        '''\n\nUNDERLINE_FLAGS = (sublime.DRAW_SQUIGGLY_UNDERLINE\n                   | sublime.DRAW_NO_OUTLINE\n                   | sublime.DRAW_NO_FILL\n                   | sublime.DRAW_EMPTY_AS_OVERWRITE)\n\nBOX_FLAGS = sublime.DRAW_NO_FILL | sublime.DRAW_EMPTY_AS_OVERWRITE\n\n\ndef create_phantom_html(text: str) -> str:\n    global stylesheet\n    return \"\"\"<body id=inline-error>{}\n                <div class=\"error-arrow\"></div>\n                <div class=\"error\">\n                    <span class=\"message\">{}</span>\n                    <a href=\"code-actions\">Code Actions</a>\n                </div>\n                </body>\"\"\".format(stylesheet, html.escape(text, quote=False))\n\n\ndef on_phantom_navigate(view: sublime.View, href: str, point: int):\n    # TODO: don't mess with the user's cursor.\n    sel = view.sel()\n    sel.clear()\n    sel.add(sublime.Region(point))\n    view.run_command(\"lsp_code_actions\")\n\n\ndef create_phantom(view: sublime.View, diagnostic: Diagnostic) -> sublime.Phantom:\n    region = diagnostic.range.to_region(view)\n    # TODO: hook up hide phantom (if keeping them)\n    content = create_phantom_html(diagnostic.message)\n    return sublime.Phantom(\n        region,\n        '<p>' + content + '</p>',\n        sublime.LAYOUT_BELOW,\n        lambda href: on_phantom_navigate(view, href, region.begin())\n    )\n\n\ndef format_severity(severity: int) -> str:\n    return diagnostic_severity_names.get(severity, \"???\")\n\n\ndef format_diagnostic(diagnostic: Diagnostic) -> str:\n    location = \"{:>8}:{:<4}\".format(\n        diagnostic.range.start.row + 1, diagnostic.range.start.col + 1)\n    message = diagnostic.message.replace(\"\\n\", \" \").replace(\"\\r\", \"\")\n    return \" {}\\t{:<12}\\t{:<10}\\t{}\".format(\n        location, diagnostic.source, format_severity(diagnostic.severity), message)\n\n\nphantom_sets_by_buffer = {}  # type: Dict[int, sublime.PhantomSet]\n\n\ndef update_diagnostics_phantoms(view: sublime.View, diagnostics: 'List[Diagnostic]'):\n    global phantom_sets_by_buffer\n\n    buffer_id = view.buffer_id()\n    if not settings.show_diagnostics_phantoms or view.is_dirty():\n        phantoms = None\n    else:\n        phantoms = list(\n            create_phantom(view, diagnostic) for diagnostic in diagnostics)\n    if phantoms:\n        phantom_set = phantom_sets_by_buffer.get(buffer_id)\n        if not phantom_set:\n            phantom_set = sublime.PhantomSet(view, \"lsp_diagnostics\")\n            phantom_sets_by_buffer[buffer_id] = phantom_set\n        phantom_set.update(phantoms)\n    else:\n        phantom_sets_by_buffer.pop(buffer_id, None)\n\n\ndef update_diagnostics_regions(view: sublime.View, diagnostics: 'List[Diagnostic]', severity: int):\n    region_name = \"lsp_\" + format_severity(severity)\n    if settings.show_diagnostics_phantoms and not view.is_dirty():\n        regions = None\n    else:\n        regions = list(diagnostic.range.to_region(view) for diagnostic in diagnostics\n                       if diagnostic.severity == severity)\n    if regions:\n        scope_name = diagnostic_severity_scopes[severity]\n        view.add_regions(\n            region_name, regions, scope_name, settings.diagnostics_gutter_marker,\n            UNDERLINE_FLAGS if settings.diagnostics_highlight_style == \"underline\" else BOX_FLAGS)\n    else:\n        view.erase_regions(region_name)\n\n\ndef update_diagnostics_in_view(view: sublime.View, diagnostics: 'List[Diagnostic]'):\n    if view and view.is_valid():\n        update_diagnostics_phantoms(view, diagnostics)\n        for severity in range(DiagnosticSeverity.Error, DiagnosticSeverity.Information):\n            update_diagnostics_regions(view, diagnostics, severity)\n\n\nEvents.subscribe(\"document.diagnostics\",\n                 lambda update: handle_diagnostics(update))\n\n\ndef handle_diagnostics(update: DiagnosticsUpdate):\n    window = update.window\n    view = window.find_open_file(update.file_path)\n    if view:\n        update_diagnostics_in_view(view, update.diagnostics)\n    update_diagnostics_panel(window)\n\n\nclass DiagnosticsCursorListener(sublime_plugin.ViewEventListener):\n    def __init__(self, view):\n        self.view = view\n        self.has_status = False\n\n    @classmethod\n    def is_applicable(cls, view_settings):\n        syntax = view_settings.get('syntax')\n        return settings.show_diagnostics_in_view_status and syntax and is_supported_syntax(syntax)\n\n    def on_selection_modified_async(self):\n        selections = self.view.sel()\n        if len(selections) > 0:\n            pos = selections[0].begin()\n            line_diagnostics = get_line_diagnostics(self.view, pos)\n            if len(line_diagnostics) > 0:\n                self.show_diagnostics_status(line_diagnostics)\n            elif self.has_status:\n                self.clear_diagnostics_status()\n\n    def show_diagnostics_status(self, line_diagnostics):\n        self.has_status = True\n        self.view.set_status('lsp_diagnostics', line_diagnostics[0].message)\n\n    def clear_diagnostics_status(self):\n        self.view.erase_status('lsp_diagnostics')\n        self.has_status = False\n\n\nclass LspShowDiagnosticsPanelCommand(sublime_plugin.WindowCommand):\n    def run(self):\n        ensure_diagnostics_panel(self.window)\n        self.window.run_command(\"show_panel\", {\"panel\": \"output.diagnostics\"})\n\n\ndef create_diagnostics_panel(window):\n    panel = create_output_panel(window, \"diagnostics\")\n    panel.settings().set(\"result_file_regex\", r\"^\\s*\\S\\s+(\\S.*):$\")\n    panel.settings().set(\"result_line_regex\", r\"^\\s+([0-9]+):?([0-9]+).*$\")\n    panel.assign_syntax(\"Packages/\" + PLUGIN_NAME +\n                        \"/Syntaxes/Diagnostics.sublime-syntax\")\n    # Call create_output_panel a second time after assigning the above\n    # settings, so that it'll be picked up as a result buffer\n    # see: Packages/Default/exec.py#L228-L230\n    panel = window.create_output_panel(\"diagnostics\")\n    return panel\n\n\ndef ensure_diagnostics_panel(window: sublime.Window):\n    return window.find_output_panel(\"diagnostics\") or create_diagnostics_panel(window)\n\n\ndef update_diagnostics_panel(window: sublime.Window):\n    assert window, \"missing window!\"\n    base_dir = get_project_path(window)\n\n    panel = ensure_diagnostics_panel(window)\n    assert panel, \"must have a panel now!\"\n\n    diagnostics_by_file = get_window_diagnostics(window)\n    if diagnostics_by_file is not None:\n        active_panel = window.active_panel()\n        is_active_panel = (active_panel == \"output.diagnostics\")\n        panel.settings().set(\"result_base_dir\", base_dir)\n        panel.set_read_only(False)\n        if diagnostics_by_file:\n            to_render = []\n            for file_path, source_diagnostics in diagnostics_by_file.items():\n                relative_file_path = os.path.relpath(file_path, base_dir) if base_dir else file_path\n                if source_diagnostics:\n                    to_render.append(format_diagnostics(relative_file_path, source_diagnostics))\n            panel.run_command(\"lsp_update_panel\", {\"characters\": \"\\n\".join(to_render)})\n            if settings.auto_show_diagnostics_panel and not active_panel:\n                window.run_command(\"show_panel\",\n                                   {\"panel\": \"output.diagnostics\"})\n        else:\n            panel.run_command(\"lsp_clear_panel\")\n            if settings.auto_show_diagnostics_panel and is_active_panel:\n                window.run_command(\"hide_panel\",\n                                   {\"panel\": \"output.diagnostics\"})\n        panel.set_read_only(True)\n\n\ndef format_diagnostics(file_path, origin_diagnostics):\n    content = \" \u25cc {}:\\n\".format(file_path)\n    for origin, diagnostics in origin_diagnostics.items():\n        for diagnostic in diagnostics:\n            item = format_diagnostic(diagnostic)\n            content += item + \"\\n\"\n    return content\n", "path": "plugin/diagnostics.py"}], "after_files": [{"content": "import html\nimport os\nimport sublime\nimport sublime_plugin\n\ntry:\n    from typing import Any, List, Dict, Tuple, Callable, Optional\n    assert Any and List and Dict and Tuple and Callable and Optional\nexcept ImportError:\n    pass\n\nfrom .core.settings import settings, PLUGIN_NAME\nfrom .core.protocol import Diagnostic, DiagnosticSeverity\nfrom .core.events import Events\nfrom .core.configurations import is_supported_syntax\nfrom .core.diagnostics import DiagnosticsUpdate, get_window_diagnostics, get_line_diagnostics\nfrom .core.workspace import get_project_path\nfrom .core.panels import create_output_panel\n\ndiagnostic_severity_names = {\n    DiagnosticSeverity.Error: \"error\",\n    DiagnosticSeverity.Warning: \"warning\",\n    DiagnosticSeverity.Information: \"info\",\n    DiagnosticSeverity.Hint: \"hint\"\n}\n\ndiagnostic_severity_scopes = {\n    DiagnosticSeverity.Error: 'markup.deleted.lsp sublimelinter.mark.error markup.error.lsp',\n    DiagnosticSeverity.Warning: 'markup.changed.lsp sublimelinter.mark.warning markup.warning.lsp',\n    DiagnosticSeverity.Information: 'markup.inserted.lsp sublimelinter.gutter-mark markup.info.lsp',\n    DiagnosticSeverity.Hint: 'markup.inserted.lsp sublimelinter.gutter-mark markup.info.suggestion.lsp'\n}\n\nstylesheet = '''\n            <style>\n                div.error-arrow {\n                    border-top: 0.4rem solid transparent;\n                    border-left: 0.5rem solid color(var(--redish) blend(var(--background) 30%));\n                    width: 0;\n                    height: 0;\n                }\n                div.error {\n                    padding: 0.4rem 0 0.4rem 0.7rem;\n                    margin: 0 0 0.2rem;\n                    border-radius: 0 0.2rem 0.2rem 0.2rem;\n                }\n\n                div.error span.message {\n                    padding-right: 0.7rem;\n                }\n\n                div.error a {\n                    text-decoration: inherit;\n                    padding: 0.35rem 0.7rem 0.45rem 0.8rem;\n                    position: relative;\n                    bottom: 0.05rem;\n                    border-radius: 0 0.2rem 0.2rem 0;\n                    font-weight: bold;\n                }\n                html.dark div.error a {\n                    background-color: #00000018;\n                }\n                html.light div.error a {\n                    background-color: #ffffff18;\n                }\n            </style>\n        '''\n\nUNDERLINE_FLAGS = (sublime.DRAW_SQUIGGLY_UNDERLINE\n                   | sublime.DRAW_NO_OUTLINE\n                   | sublime.DRAW_NO_FILL\n                   | sublime.DRAW_EMPTY_AS_OVERWRITE)\n\nBOX_FLAGS = sublime.DRAW_NO_FILL | sublime.DRAW_EMPTY_AS_OVERWRITE\n\n\ndef create_phantom_html(text: str) -> str:\n    global stylesheet\n    return \"\"\"<body id=inline-error>{}\n                <div class=\"error-arrow\"></div>\n                <div class=\"error\">\n                    <span class=\"message\">{}</span>\n                    <a href=\"code-actions\">Code Actions</a>\n                </div>\n                </body>\"\"\".format(stylesheet, html.escape(text, quote=False))\n\n\ndef on_phantom_navigate(view: sublime.View, href: str, point: int):\n    # TODO: don't mess with the user's cursor.\n    sel = view.sel()\n    sel.clear()\n    sel.add(sublime.Region(point))\n    view.run_command(\"lsp_code_actions\")\n\n\ndef create_phantom(view: sublime.View, diagnostic: Diagnostic) -> sublime.Phantom:\n    region = diagnostic.range.to_region(view)\n    # TODO: hook up hide phantom (if keeping them)\n    content = create_phantom_html(diagnostic.message)\n    return sublime.Phantom(\n        region,\n        '<p>' + content + '</p>',\n        sublime.LAYOUT_BELOW,\n        lambda href: on_phantom_navigate(view, href, region.begin())\n    )\n\n\ndef format_severity(severity: int) -> str:\n    return diagnostic_severity_names.get(severity, \"???\")\n\n\ndef format_diagnostic(diagnostic: Diagnostic) -> str:\n    location = \"{:>8}:{:<4}\".format(\n        diagnostic.range.start.row + 1, diagnostic.range.start.col + 1)\n    message = diagnostic.message.replace(\"\\n\", \" \").replace(\"\\r\", \"\")\n    return \" {}\\t{:<12}\\t{:<10}\\t{}\".format(\n        location, diagnostic.source, format_severity(diagnostic.severity), message)\n\n\nphantom_sets_by_buffer = {}  # type: Dict[int, sublime.PhantomSet]\n\n\ndef update_diagnostics_phantoms(view: sublime.View, diagnostics: 'List[Diagnostic]'):\n    global phantom_sets_by_buffer\n\n    buffer_id = view.buffer_id()\n    if not settings.show_diagnostics_phantoms or view.is_dirty():\n        phantoms = None\n    else:\n        phantoms = list(\n            create_phantom(view, diagnostic) for diagnostic in diagnostics)\n    if phantoms:\n        phantom_set = phantom_sets_by_buffer.get(buffer_id)\n        if not phantom_set:\n            phantom_set = sublime.PhantomSet(view, \"lsp_diagnostics\")\n            phantom_sets_by_buffer[buffer_id] = phantom_set\n        phantom_set.update(phantoms)\n    else:\n        phantom_sets_by_buffer.pop(buffer_id, None)\n\n\ndef update_diagnostics_regions(view: sublime.View, diagnostics: 'List[Diagnostic]', severity: int):\n    region_name = \"lsp_\" + format_severity(severity)\n    if settings.show_diagnostics_phantoms and not view.is_dirty():\n        regions = None\n    else:\n        regions = list(diagnostic.range.to_region(view) for diagnostic in diagnostics\n                       if diagnostic.severity == severity)\n    if regions:\n        scope_name = diagnostic_severity_scopes[severity]\n        view.add_regions(\n            region_name, regions, scope_name, settings.diagnostics_gutter_marker,\n            UNDERLINE_FLAGS if settings.diagnostics_highlight_style == \"underline\" else BOX_FLAGS)\n    else:\n        view.erase_regions(region_name)\n\n\ndef update_diagnostics_in_view(view: sublime.View, diagnostics: 'List[Diagnostic]'):\n    if view and view.is_valid():\n        update_diagnostics_phantoms(view, diagnostics)\n        for severity in range(DiagnosticSeverity.Error, DiagnosticSeverity.Information):\n            update_diagnostics_regions(view, diagnostics, severity)\n\n\nEvents.subscribe(\"document.diagnostics\",\n                 lambda update: handle_diagnostics(update))\n\n\ndef handle_diagnostics(update: DiagnosticsUpdate):\n    window = update.window\n    view = window.find_open_file(update.file_path)\n    if view:\n        update_diagnostics_in_view(view, update.diagnostics)\n    update_diagnostics_panel(window)\n\n\nclass DiagnosticsCursorListener(sublime_plugin.ViewEventListener):\n    def __init__(self, view):\n        self.view = view\n        self.has_status = False\n\n    @classmethod\n    def is_applicable(cls, view_settings):\n        syntax = view_settings.get('syntax')\n        return settings.show_diagnostics_in_view_status and syntax and is_supported_syntax(syntax)\n\n    def on_selection_modified_async(self):\n        selections = self.view.sel()\n        if len(selections) > 0:\n            pos = selections[0].begin()\n            line_diagnostics = get_line_diagnostics(self.view, pos)\n            if len(line_diagnostics) > 0:\n                self.show_diagnostics_status(line_diagnostics)\n            elif self.has_status:\n                self.clear_diagnostics_status()\n\n    def show_diagnostics_status(self, line_diagnostics):\n        self.has_status = True\n        self.view.set_status('lsp_diagnostics', line_diagnostics[0].message)\n\n    def clear_diagnostics_status(self):\n        self.view.erase_status('lsp_diagnostics')\n        self.has_status = False\n\n\nclass LspShowDiagnosticsPanelCommand(sublime_plugin.WindowCommand):\n    def run(self):\n        ensure_diagnostics_panel(self.window)\n        active_panel = self.window.active_panel()\n        is_active_panel = (active_panel == \"output.diagnostics\")\n\n        if is_active_panel:\n            self.window.run_command(\"hide_panel\", {\"panel\": \"output.diagnostics\"})\n        else:\n            self.window.run_command(\"show_panel\", {\"panel\": \"output.diagnostics\"})\n\n\ndef create_diagnostics_panel(window):\n    panel = create_output_panel(window, \"diagnostics\")\n    panel.settings().set(\"result_file_regex\", r\"^\\s*\\S\\s+(\\S.*):$\")\n    panel.settings().set(\"result_line_regex\", r\"^\\s+([0-9]+):?([0-9]+).*$\")\n    panel.assign_syntax(\"Packages/\" + PLUGIN_NAME +\n                        \"/Syntaxes/Diagnostics.sublime-syntax\")\n    # Call create_output_panel a second time after assigning the above\n    # settings, so that it'll be picked up as a result buffer\n    # see: Packages/Default/exec.py#L228-L230\n    panel = window.create_output_panel(\"diagnostics\")\n    return panel\n\n\ndef ensure_diagnostics_panel(window: sublime.Window):\n    return window.find_output_panel(\"diagnostics\") or create_diagnostics_panel(window)\n\n\ndef update_diagnostics_panel(window: sublime.Window):\n    assert window, \"missing window!\"\n    base_dir = get_project_path(window)\n\n    panel = ensure_diagnostics_panel(window)\n    assert panel, \"must have a panel now!\"\n\n    diagnostics_by_file = get_window_diagnostics(window)\n    if diagnostics_by_file is not None:\n        active_panel = window.active_panel()\n        is_active_panel = (active_panel == \"output.diagnostics\")\n        panel.settings().set(\"result_base_dir\", base_dir)\n        panel.set_read_only(False)\n        if diagnostics_by_file:\n            to_render = []\n            for file_path, source_diagnostics in diagnostics_by_file.items():\n                relative_file_path = os.path.relpath(file_path, base_dir) if base_dir else file_path\n                if source_diagnostics:\n                    to_render.append(format_diagnostics(relative_file_path, source_diagnostics))\n            panel.run_command(\"lsp_update_panel\", {\"characters\": \"\\n\".join(to_render)})\n            if settings.auto_show_diagnostics_panel and not active_panel:\n                window.run_command(\"show_panel\",\n                                   {\"panel\": \"output.diagnostics\"})\n        else:\n            panel.run_command(\"lsp_clear_panel\")\n            if settings.auto_show_diagnostics_panel and is_active_panel:\n                window.run_command(\"hide_panel\",\n                                   {\"panel\": \"output.diagnostics\"})\n        panel.set_read_only(True)\n\n\ndef format_diagnostics(file_path, origin_diagnostics):\n    content = \" \u25cc {}:\\n\".format(file_path)\n    for origin, diagnostics in origin_diagnostics.items():\n        for diagnostic in diagnostics:\n            item = format_diagnostic(diagnostic)\n            content += item + \"\\n\"\n    return content\n", "path": "plugin/diagnostics.py"}]}