Skip to content

Add support for MarkupContent in diagnostic message#2845

Merged
jwortmann merged 12 commits intosublimelsp:mainfrom
jwortmann:diagnostics-markdown
Apr 8, 2026
Merged

Add support for MarkupContent in diagnostic message#2845
jwortmann merged 12 commits intosublimelsp:mainfrom
jwortmann:diagnostics-markdown

Conversation

@jwortmann
Copy link
Copy Markdown
Member

This is the code for the change I mentioned in #2788 (comment), with small tweaks to the styles.

From the 3.18 specs:

    /**
	 * The diagnostic's message.
	 *
	 * @since 3.18.0 - support for MarkupContent. This is guarded by the client
	 * capability `textDocument.diagnostic.markupMessageSupport`.
	 */
	message: string | MarkupContent;

Linting will fail until we update the LSP types to the 3.18 specs for this. But it seems that the upstream repo https://github.com/microsoft/lsprotocol is kind of stalled (last update 9 months ago). So I'll mark this as a draft for now, and we can re-run CI when the types eventually are updated.


Diagnostics in hover popup and annotations with Breakers / Mariana / Monokai color schemes (the foreground color for code gets hardcoded into the html by mdpopups, so it's not easily possible to change that for the annotations):

hover_breakers hover_mariana hover_monokai

Goto Diagnostic:

goto_diagnostic

Diagnostics output panel:

panel

@jwortmann
Copy link
Copy Markdown
Member Author

Linting will fail until we update the LSP types to the 3.18 specs for this. But it seems that the upstream repo https://github.com/microsoft/lsprotocol is kind of stalled (last update 9 months ago). So I'll mark this as a draft for now, and we can re-run CI when the types eventually are updated.

Alternatively we could use pyright: ignore[...] comments to override the lint errors for now: 3f0c062
Then we could already support this new feature, without needing to wait for the upstream update, if the upstream repo is currently unmaintained.

@jwortmann jwortmann marked this pull request as ready for review April 3, 2026 15:46
@predragnikolic
Copy link
Copy Markdown
Member

Hi Janos,

Linting will fail until we update the LSP types to the 3.18 specs for this. But it seems that the upstream repo https://github.com/microsoft/lsprotocol is kind of stalled (last update 9 months ago)

I have a feeling that you think that sublimelsp/lsp-python-types relies on microsoft/lsprotocol, but that is not correct.

sublimelsp/lsp-python-types relies on https://github.com/microsoft/vscode-languageserver-node

See here:
https://github.com/sublimelsp/lsp-python-types/blob/96f4b138b11ddfdf02c14a423c84804b303a7ac7/download_schemas.py#L8

This is the file that lsp-python-types fetches to generate the Python types
https://github.com/microsoft/vscode-languageserver-node/blob/main/protocol/metaModel.json

@jwortmann
Copy link
Copy Markdown
Member Author

I have a feeling that you think that sublimelsp/lsp-python-types relies on microsoft/lsprotocol, but that is not correct.

Oh indeed I didn't know that. So if the metaModel.json has the latest types from the website, then we could just update our types from https://github.com/sublimelsp/lsp-python-types and everything should work already?

@predragnikolic
Copy link
Copy Markdown
Member

predragnikolic commented Apr 3, 2026

So if the metaModel.json has the latest types from the website, then we could just update our types from https://github.com/sublimelsp/lsp-python-types and everything should work already?

Yes, that is correct.

If you want feel free to ask in https://github.com/microsoft/vscode-languageserver-node when they plan to release the metaModel.json file for 3.18

(Sorry, I accidentally closed the PR while typing this message on a phone)

@jwortmann
Copy link
Copy Markdown
Member Author

Okay, so it still depends on an update in an upstream repository, just a different one... :)

But it was added to the specs website already 2 years ago on 2024-03-19: microsoft/language-server-protocol@df7c77b
This also includes an update to the metaModel.json file in the specs repo with the new types.

But then it seems like it was later overwritten (accidentally?) in another commit microsoft/language-server-protocol@7e1b69d on 2024-08-05.

So maybe that's a bug that prevents the upstream types from ever being updated correctly.

@jwortmann
Copy link
Copy Markdown
Member Author

I've created microsoft/language-server-protocol#2246

Let's see if we get an answer there.

@predragnikolic
Copy link
Copy Markdown
Member

Until they answer,
I've created a PR at lsp-python-types that will unblock you for the time being.
sublimelsp/lsp-python-types#34

@jwortmann jwortmann mentioned this pull request Apr 5, 2026
@jwortmann
Copy link
Copy Markdown
Member Author

It seems that Pyright doesn't properly support TypeIs and that is why lint fails.
I believe that the code should be correct.

I can reproduce with a simpler example:

from typing import TypeIs, TypedDict


class MarkupContent(TypedDict):
    kind: str
    value: str


def is_markup_content(message: str | MarkupContent) -> TypeIs[MarkupContent]:
    return isinstance(message, dict)


def foo(message: str | MarkupContent) -> None:
    msg = message['value'] if is_markup_content(message) else message
error

@predragnikolic
Copy link
Copy Markdown
Member

I remember using TypeGuard to narrow down types (similar to TypeIs), pyright did understand TypeGuard

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 5, 2026

TypeGuard doesn't narrow the else branch though.

If pyright doesn't support it correctly then we should probably not use it.

I guess the alternatives could be to either:

  • have two separate functions that use TypeGuard
  • inline the isinstance

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 5, 2026

Quickly checked the LSP code base with pyrefly and noticed those:


Quite a contrasting color of formatted text (though you have mentioned that):

Screenshot 2026-04-05 at 22 44 29

Out of view text in annotation (probably because the text wraps).

Screenshot 2026-04-05 at 22 44 22

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 5, 2026

The newlines also seem to be missing...

Diagnostic from the screenshot:

    {
      "code": "missing-import",
      "codeDescription": {
        "href": "https://pyrefly.org/en/docs/error-kinds/#missing-import"
      },
      "data": "committing-transaction",
      "message": {
        "kind": "markdown",
        "value": "Cannot find module `sublime`\n  Looked in these locations (from config in `/usr/local/workspace/sublime-packages/LSP/pyproject.toml`):\n  Import root (inferred from project layout): \"/usr/local/workspace/sublime-packages/LSP\"\n  Site package path queried from interpreter: \\[\"/usr/local/workspace/sublime-packages/LSP/.venv/lib/python3.8/site-packages\"\\]"
      },
      "range": {
        "end": {
          "character": 14,
          "line": 27
        },
        "start": {
          "character": 7,
          "line": 27
        }
      },
      "severity": 1,
      "source": "Pyrefly"
    },

Pure markdown value is:

Cannot find module `sublime`
  Looked in these locations (from config in `/usr/local/workspace/sublime-packages/LSP/pyproject.toml`):
  Import root (inferred from project layout): "/usr/local/workspace/sublime-packages/LSP"
  Site package path queried from interpreter: \["/usr/local/workspace/sublime-packages/LSP/.venv/lib/python3.8/site-packages"\]

which github parser seems to render with newlines but without leading spaces:


Cannot find module sublime
Looked in these locations (from config in /usr/local/workspace/sublime-packages/LSP/pyproject.toml):
Import root (inferred from project layout): "/usr/local/workspace/sublime-packages/LSP"
Site package path queried from interpreter: ["/usr/local/workspace/sublime-packages/LSP/.venv/lib/python3.8/site-packages"]


So it doesn't seem to be perfectly authored markdown code.

@jwortmann
Copy link
Copy Markdown
Member Author

Quite a contrasting color of formatted text (though you have mentioned that):

It doesn't look like that for me (see screenshots in opening comment). Have you restarted ST? The CSS changes are only picked up after a restart.

@jwortmann
Copy link
Copy Markdown
Member Author

The newlines also seem to be missing...

That sounds like an issue with the new Markdown parser. The \n with 2 spaces after should be converted into a linebreak in Markdown.

It works fine here (on 3.8 plugin host):

pyrefly

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 5, 2026

I'm on a new internal build from few days ago that runs on 3.14 and mdpopups uses new parser of course.

Here is how the popup content looks like here:

Click to expand
<style>
  html {
    --mdpopups-fg: var(--foreground);
    --mdpopups-link: var(--bluish);
    --mdpopups-del: var(--redish);
    --mdpopups-kbd-fg: var(--background);
    --mdpopups-kbd-bg: var(--foreground);
    --mdpopups-font-mono: "sf mono", Consolas, "Liberation Mono", Menlo, Courier, monospace;

    --mdpopups-admon-fg: var(--foreground);
    --mdpopups-admon-note-fg: var(--foreground);
    --mdpopups-admon-tip-fg: var(--foreground);
    --mdpopups-admon-warning-fg: var(--foreground);
    --mdpopups-admon-caution-fg: var(--foreground);
    --mdpopups-admon-important-fg: var(--foreground);
    --mdpopups-admon-bg: var(--background);
    --mdpopups-admon-note-bg: var(--background);
    --mdpopups-admon-tip-bg: var(--background);
    --mdpopups-admon-warning-bg: var(--background);
    --mdpopups-admon-caution-bg: var(--background);
    --mdpopups-admon-important-bg: var(--background);
    --mdpopups-admon-title-fg: var(--foreground);
    --mdpopups-admon-note-title-fg: var(--foreground);
    --mdpopups-admon-tip-title-fg: var(--foreground);
    --mdpopups-admon-warning-title-fg: var(--foreground);
    --mdpopups-admon-caution-title-fg: var(--foreground);
    --mdpopups-admon-important-title-fg: var(--foreground);
    --mdpopups-admon-accent: color(var(--background) blend(var(--foreground) 50%));
    --mdpopups-admon-note-accent: color(var(--background) blend(var(--bluish) 50%));
    --mdpopups-admon-tip-accent: color(var(--background) blend(var(--greenish) 50%));
    --mdpopups-admon-warning-accent: color(var(--background) blend(var(--orangish) 50%));
    --mdpopups-admon-caution-accent: color(var(--background) blend(var(--redish) 50%));
    --mdpopups-admon-important-accent: color(var(--background) blend(var(--purplish) 50%));

    --mdpopups-admon-info-fg: var(--foreground);
    --mdpopups-admon-error-fg: var(--foreground);
    --mdpopups-admon-success-fg: var(--foreground);
    --mdpopups-admon-info-bg: var(--background);
    --mdpopups-admon-error-bg: var(--background);
    --mdpopups-admon-success-bg: var(--background);
    --mdpopups-admon-info-title-fg: var(--foreground);
    --mdpopups-admon-error-title-fg: var(--foreground);
    --mdpopups-admon-success-title-fg: var(--foreground);
    --mdpopups-admon-info-accent: color(var(--background) blend(var(--bluish) 50%));
    --mdpopups-admon-error-accent: color(var(--background) blend(var(--redish) 50%));
    --mdpopups-admon-success-accent: color(var(--background) blend(var(--greenish) 50%));
  }

  html.light {
    --mdpopups-bg: color(var(--background) blend(black 95%));
    --mdpopups-hr: color(var(--background) blend(black 85%));
    --mdpopups-kbd-border: color(var(--foreground) blend(white 80%));
    --mdpopups-hl-border: color(var(--background) blend(black 90%));
    --mdpopups-hl-bg: color(var(--background) blend(black 98%));
    background-color: var(--mdpopups-bg);
  }

  html.dark {
    --mdpopups-bg: color(var(--background) blend(white 95%));
    --mdpopups-hr: color(var(--background) blend(white 85%));
    --mdpopups-kbd-border: color(var(--foreground) blend(black 80%));
    --mdpopups-hl-border: color(var(--background) blend(white 90%));
    --mdpopups-hl-bg: color(var(--background) blend(white 98%));
    background-color: var(--mdpopups-bg);
  }

  html,
  body {
    padding: 0;
    margin: 0;
  }

  div.mdpopups {
    display: block;
    margin: 0;
    padding: 0;
    font-size: 1rem;
    line-height: 1.1rem;
    color: var(--mdpopups-fg);
    background-color: var(--mdpopups-bg);
  }

  .mdpopups a {
    color: var(--mdpopups-link);
  }

  .mdpopups ins {
    text-decoration: underline;
  }

  .mdpopups del {
    color: var(--mdpopups-del);
  }

  .mdpopups .highlight,
  .mdpopups code,
  .mdpopups var,
  .mdpopups tt {
    font-family: var(--mdpopups-font-mono);
  }

  .mdpopups div {
    display: block;
  }

  .mdpopups h1 {
    font-size: 1.5rem;
  }

  .mdpopups h2 {
    font-size: 1.4rem;
  }

  .mdpopups h3 {
    font-size: 1.3rem;
  }

  .mdpopups h4 {
    font-size: 1.2rem;
  }

  .mdpopups h5 {
    font-size: 1.1rem;
  }

  .mdpopups h6 {
    font-size: 1rem;
  }

  .mdpopups h1,
  .mdpopups h2,
  .mdpopups h3,
  .mdpopups h4,
  .mdpopups h5,
  .mdpopups h6 {
    margin-top: 0.2rem;
    margin-bottom: 0.2rem;
  }

  .mdpopups blockquote {
    display: block;
    margin-top: 0;
    margin-bottom: 0.5rem;
    margin-left: 0;
    margin-right: 0;
    padding: 0.5rem;
    border-left: 0.25rem solid var(--mdpopups-fg);
  }

  .mdpopups hr {
    display: block;
    border-color: var(--mdpopups-hr);
    border-style: solid;
    border-width: 0 0 1px 0;
    margin-top: 1rem;
    margin-bottom: 1rem;
  }

  .mdpopups dl {
    display: block;
  }

  .mdpopups dt {
    display: block;
    font-style: italic;
    font-weight: bold;
    margin-bottom: 0.5rem;
  }

  .mdpopups dd {
    display: block;
    margin-left: 1.5rem;
    margin-bottom: 0.5rem;
  }

  .mdpopups pre {
    display: block;
  }

  .mdpopups ol,
  .mdpopups ul,
  .mdpopups dl,
  .mdpopups p {
    padding: 0;
    margin-top: 0;
    margin-bottom: 0.5rem;
    margin-left: 0;
    margin-right: 0;
  }

  .mdpopups ul,
  .mdpopups ol {
    padding-left: 2rem;
  }

  .mdpopups kbd {
    display: inline;
    font-size: 0.9rem;
    padding: 0.05rem 0.25rem;
    border-radius: 0.25rem;
    background-color: var(--mdpopups-kbd-bg);
    color: var(--mdpopups-kbd-fg);
    border: 1px solid var(--mdpopups-kbd-border);
  }

  .mdpopups .admonition,
  .mdpopups blockquote.alert {
    display: block;
    padding: 0 0.5rem 0.5rem 0.5rem;
    border-radius: 0.25rem;
    margin-bottom: 0.5rem;
    background-color: var(--mdpopups-admon-bg);
    color: var(--mdpopups-admon-fg);
    border: 1px solid var(--mdpopups-admon-accent);
  }

  .mdpopups .admonition-title,
  .mdpopups .alert-title {
    font-weight: 600;
    font-size: 1.1rem;
    margin: 0 -0.5rem 0.5rem -0.5rem;
    padding: 0.1rem 0.5rem 0.1rem 0.5rem;
    border-top-left-radius: 0.25rem;
    border-top-right-radius: 0.25rem;
    color: var(--mdpopups-admon-title-fg);
    background-color: var(--mdpopups-admon-accent);
  }

  .mdpopups .admonition.panel-info .admonition-title {
    color: var(--mdpopups-admon-info-title-fg);
    background-color: var(--mdpopups-admon-info-accent);
  }

  .mdpopups .admonition.panel-info {
    color: var(--mdpopups-admon-info-fg);
    background-color: var(--mdpopups-admon-info-bg);
    border-color: var(--mdpopups-admon-info-accent);
  }

  .mdpopups .admonition.panel-success .admonition-title {
    color: var(--mdpopups-admon-success-title-fg);
    background-color: var(--mdpopups-admon-success-accent);
  }

  .mdpopups .admonition.panel-success {
    color: var(--mdpopups-admon-success-fg);
    background-color: var(--mdpopups-admon-success-bg);
    border-color: var(--mdpopups-admon-success-accent);
  }

  .mdpopups .admonition.panel-error .admonition-title {
    color: var(--mdpopups-admon-error-title-fg);
    background-color: var(--mdpopups-admon-error-accent);
  }

  .mdpopups .admonition.panel-error {
    color: var(--mdpopups-admon-error-fg);
    background-color: var(--mdpopups-admon-error-bg);
    border-color: var(--mdpopups-admon-error-accent);
  }

  .mdpopups .admonition.tip .admonition-title,
  .mdpopups blockquote.alert-tip .alert-title {
    color: var(--mdpopups-admon-tip-title-fg);
    background-color: var(--mdpopups-admon-tip-accent);
  }

  .mdpopups .admonition.tip,
  .mdpopups blockquote.alert-tip {
    color: var(--mdpopups-admon-tip-fg);
    background-color: var(--mdpopups-admon-tip-bg);
    border-color: var(--mdpopups-admon-tip-accent);
  }

  .mdpopups .admonition.warning .admonition-title,
  .mdpopups blockquote.alert-warning .alert-title,
  .mdpopups .admonition.panel-warning .admonition-title {
    color: var(--mdpopups-admon-warning-title-fg);
    background-color: var(--mdpopups-admon-warning-accent);
  }

  .mdpopups .admonition.warning,
  .mdpopups blockquote.alert-warning,
  .mdpopups .admonition.panel-warning {
    color: var(--mdpopups-admon-warning-fg);
    background-color: var(--mdpopups-admon-warning-bg);
    border-color: var(--mdpopups-admon-warning-accent);
  }

  .mdpopups .admonition.caution .admonition-title,
  .mdpopups blockquote.alert-caution .alert-title {
    color: var(--mdpopups-admon-caution-title-fg);
    background-color: var(--mdpopups-admon-caution-accent);
  }

  .mdpopups .admonition.caution,
  .mdpopups blockquote.alert-caution {
    color: var(--mdpopups-admon-caution-fg);
    background-color: var(--mdpopups-admon-caution-bg);
    border-color: var(--mdpopups-admon-caution-accent);
  }

  .mdpopups .admonition.note .admonition-title,
  .mdpopups blockquote.alert-note .alert-title {
    color: var(--mdpopups-admon-note-title-fg);
    background-color: var(--mdpopups-admon-note-accent);
  }

  .mdpopups .admonition.note,
  .mdpopups blockquote.alert-note {
    color: var(--mdpopups-admon-note-fg);
    background-color: var(--mdpopups-admon-note-bg);
    border-color: var(--mdpopups-admon-note-accent);
  }

  .mdpopups .admonition.important .admonition-title,
  .mdpopups blockquote.alert-important .alert-title {
    color: var(--mdpopups-admon-important-title-fg);
    background-color: var(--mdpopups-admon-important-accent);
  }

  .mdpopups .admonition.important,
  .mdpopups blockquote.alert-important {
    color: var(--mdpopups-admon-important-fg);
    background-color: var(--mdpopups-admon-important-bg);
    border-color: var(--mdpopups-admon-important-accent);
  }

  .mdpopups .highlight,
  .mdpopups code {
    border: 1px solid var(--mdpopups-hl-border);
    border-radius: 0.25rem;
    font-size: 0.9rem;
    background-color: var(--mdpopups-hl-bg);
  }

  .mdpopups div.highlight,
  .mdpopups pre.highlight {
    padding: 0.5rem;
    margin-bottom: 0.5rem;
    font-size: 1rem;
  }

  .mdpopups code.highlight,
  .mdpopups code {
    padding: 0.05rem 0.25rem;
  }

  .mdpopups pre code {
    border: none;
    border-radius: 0;
    padding: 0;
  }

  .lsp_popup {
    --font-size: 1rem;
    --font-size-sm: 0.9rem;
    font-family: system;
    font-size: var(--font-size);
  }

  .mdpopups h1,
  .mdpopups h2,
  .mdpopups h3,
  .mdpopups h4,
  .mdpopups h5,
  .mdpopups h6,
  .lsp_popup div.highlight,
  .lsp_popup pre.highlight {
    font-size: var(--font-size);
  }

  .lsp_popup code.highlight {
    font-size: var(--font-size-sm);
  }

  .mdpopups hr {
    margin-top: 0.5rem;
    margin-bottom: 0.5rem;
    border-color: color(var(--foreground) alpha(0.10));
  }

  .mdpopups kbd {
    background-color: color(var(--mdpopups-bg) lightness(+ 5%));
    border-color: color(var(--mdpopups-fg) alpha(0.25));
    color: var(--mdpopups-fg);
    font-size: 0.8rem;
    line-height: 0.8rem;
  }

  .mdpopups blockquote.alert {
    border-radius: 0;
    border-width: 0;
    border-left-width: 4px;
  }

  .mdpopups blockquote.alert p {
    margin-bottom: 0;
  }

  .mdpopups blockquote.alert p.alert-title {
    font-size: var(--font-size);
    margin-bottom: 0.5rem;
    margin-left: 0;
    padding-left: 0;
  }

  .mdpopups blockquote.alert-tip .alert-title {
    color: var(--mdpopups-admon-tip-accent);
    background-color: transparent;
  }

  .mdpopups blockquote.alert-warning .alert-title {
    color: var(--mdpopups-admon-warning-accent);
    background-color: transparent;
  }

  .mdpopups blockquote.alert-caution .alert-title {
    color: var(--mdpopups-admon-caution-accent);
    background-color: transparent;
  }

  .mdpopups blockquote.alert-note .alert-title {
    color: var(--mdpopups-admon-note-accent);
    background-color: transparent;
  }

  .mdpopups blockquote.alert-important .alert-title {
    color: var(--mdpopups-admon-important-accent);
    background-color: transparent;
  }

  .lsp_popup .wrapper {
    padding: 0.5rem;
    padding-bottom: 0;
  }

  .lsp_popup .wrapper--spacer {
    margin-top: .5rem;
  }

  .lsp_popup .m-0 {
    margin: 0;
  }

  .diagnostics code.highlight {
    background-color: color(white alpha(0.1));
    border-color: transparent;
  }

  html.light .diagnostics code.highlight {
    background-color: color(black alpha(0.1));
  }

  .color-muted {
    color: color(var(--foreground) alpha(0.6));
  }

  .error,
  .warning,
  .information,
  .hint {
    color: var(--foreground);
  }

  .error {
    background-color: color(var(--redish) alpha(0.2));
  }

  .warning {
    background-color: color(var(--yellowish) alpha(0.2));
  }

  .information {
    background-color: color(var(--bluish) alpha(0.2));
  }

  .hint {
    background-color: color(var(--bluish) alpha(0.2));
  }

  .signature-help-intro {
    font-size: var(--font-size-sm);
  }

  .actions {
    background-color: color(var(--foreground) alpha(0.1));
  }

  .actions a {
    text-decoration: none;
  }

  .lightbulb {
    position: relative;
    bottom: -2px;
    padding-right: 0.5rem;
  }

  .lightbulb img {
    width: 1rem;
    height: 1rem;
  }

  a.copy-icon {
    padding: 0.5rem;
    text-decoration: none;
    color: color(var(--foreground) alpha(0.6));
  }
</style>
<div class="mdpopups">
  <div class="lsp_popup">

    <body>
      <div class="diagnostics">
        <div class="wrapper error">
          <p>Cannot find module <code>mdpopups.marko</code>
            Looked in these locations (from config in <code>/usr/local/workspace/sublime-packages/LSP/pyproject.toml</code>):
            Search path (from config file): ["/usr/local/workspace/sublime-packages/LSP/stubs"]
            Import root (inferred from project layout): "/usr/local/workspace/sublime-packages/LSP"
            Site package path queried from interpreter: ["/usr/local/workspace/sublime-packages/LSP/.venv/lib/python3.8/site-packages"]</p>
          <span class="color-muted">Pyrefly(<a href='https://pyrefly.org/en/docs/error-kinds/#missing-import' title='https://pyrefly.org/en/docs/error-kinds/#missing-import'>missing-import</a>)</span><a class='copy-icon' title='Copy to clipboard' href='subl:lsp_copy_text {"text":"Cannot find module `mdpopups.marko`\n  Looked in these locations (from config in `/usr/local/workspace/sublime-packages/LSP/pyproject.toml`):\n  Search path (from config file): \\[\"/usr/local/workspace/sublime-packages/LSP/stubs\"\\]\n  Import root (inferred from project layout): \"/usr/local/workspace/sublime-packages/LSP\"\n  Site package path queried from interpreter: \\[\"/usr/local/workspace/sublime-packages/LSP/.venv/lib/python3.8/site-packages\"\\] (Pyrefly)"}'></a>
          <div class="wrapper--spacer"></div>
        </div>
      </div>
    </body>
  </div>
</div>

@jwortmann
Copy link
Copy Markdown
Member Author

The \n with 2 spaces after should be converted into a linebreak in Markdown.

No, actually it should be spaces before the linebreak: \n. So then it would be a server issue to not format the Markdown content correctly (if real linebreaks are intended, what I assume here).

And now I see why the formatting looked good on my side; it turned out that these are soft wraps due to the max width of the popup matching perfectly to that content, lol.

With something like "popup_max_characters_width": 200, I also see no linebreaks here.

But then it's still a server issue and we can't really do anything about that I guess.

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 5, 2026

That sounds like an issue with the new Markdown parser. The \n with 2 spaces after should be converted into a linebreak in Markdown.

Maybe expected in new parser but asked in facelessuser/sublime-markdown-popups#158

@jwortmann
Copy link
Copy Markdown
Member Author

jwortmann commented Apr 5, 2026

Out of view text in annotation (probably because the text wraps).
Screenshot 2026-04-05 at 22 44 22

I haven't found a way to reproduce that here.

annotation

But also it seems like the sublime in your annotation is rendered differently (like regular text) ... 😕

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 5, 2026

It's likely that default style of p causes it here. Maybe old parser doesn't add those:

<body id="annotation" class="lsp_annotation">
    <style>
        .lsp_annotation {
            margin: 0;
            border-width: 0;
            font-family: system;
        }

        .error {
            color: color(var(--redish) alpha(0.85));
        }

        .warning {
            color: color(var(--yellowish) alpha(0.85));
        }

        .information {
            color: color(var(--bluish) alpha(0.85));
        }

        .hint {
            color: color(var(--bluish) alpha(0.85));
        }

        .color-muted {
            color: color(var(--foreground) alpha(0.6));
        }
    </style>
    <div class="error">
        <p>Cannot find module <code>sublime</code>
            Looked in these locations (from config in <code>/usr/local/workspace/sublime-packages/LSP/pyproject.toml</code>):
            Import root (inferred from project layout): &quot;/usr/local/workspace/sublime-packages/LSP&quot;
            Site package path queried from interpreter: [&quot;/usr/local/workspace/sublime-packages/LSP/.venv/lib/python3.8/site-packages&quot;]</p>
        <span class="color-muted">Pyrefly</span>
    </div>
</body>

@jwortmann
Copy link
Copy Markdown
Member Author

It's likely that default style of p causes it here. Maybe old parser doesn't add those:

I had put this code in _format_diagnostic_message for that reason:

        html = minihtml(view, message, FORMAT_MARKUP_CONTENT)
        if html.startswith('<p>') and html.endswith('</p>'):
            html = html[3:-4]

I think it should apply here. Can you check what the raw output of the html = minihtml(...) line is?

@jwortmann
Copy link
Copy Markdown
Member Author

jwortmann commented Apr 5, 2026

Interestingly mdpopups on Py3.14 uses just <code>sublime</code> instead of what I see here with <code class="highlight"><span style="color: #f6e5cb;">sublime</span></code>.

The simple code tag without any hardcoded foreground color is exactly what we need to apply manual styling. Then we can add some CSS rule for that tag to tweak the foreground color.

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 6, 2026

think it should apply here. Can you check what the raw output of the html = minihtml(...) line is?

I think it's just a trailing whitespace that trips it but to be honest I don't think that messing with html is a good approach.
What if we have two sibling paragraphs? Then we'll remove the outer p tags but leave two inner ones.
If the intention is to to not have a block tag messing things then it's potentially better to just override its style to display: inline. Or override margings. Or whatever is needed.

<p>Cannot find module <code>sublime</code>
Looked in these locations (from config in <code>/usr/local/workspace/sublime-packages/LSP/pyproject.toml</code>):
Import root (inferred from project layout): &quot;/usr/local/workspace/sublime-packages/LSP&quot;
Site package path queried from interpreter: [&quot;/usr/local/workspace/sublime-packages/LSP/.venv/lib/python3.8/site-packages&quot;]</p>

@jwortmann
Copy link
Copy Markdown
Member Author

I changed that to use a CSS style instead.

Now I also know why the styles for the popup didn't work for you. The reason was because mdpopups on 3.14 doesn't use the .highlight class for the code tag. I changed the CSS selector to account for that.

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 6, 2026

The style of code in annotations differs quite a bit from how it looks in hover popup:

Screenshot 2026-04-06 at 17 55 46 Screenshot 2026-04-06 at 18 00 06

We could import more of mdpopups and our defaults to make those match better but it's fine by me for now given how rare those are.

@jwortmann
Copy link
Copy Markdown
Member Author

I'm open for suggestions for the code style in annotations. Currently the only property that I've set in the CSS is font-family: monospace;. We could adjust the foreground color too, but not sure what exactly to use there. I wouldn't say that the hardcoded color on Py3.8, which uses the regular text color, really looks better (see my previous screenshots).

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 6, 2026

For one the text in code is bigger than in the rest of the text in my case (because the font is different).

I will tweak things a bit and you can decide if we keep those.

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 6, 2026

Here is a fun bug in ST when using background-color for code element:

Screenshot 2026-04-06 at 18 35 03

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 6, 2026

With updated styles it's marginally better IMO

Screenshot 2026-04-06 at 18 39 32

@jwortmann
Copy link
Copy Markdown
Member Author

Now code font looks really tiny on Windows, if SF Mono isn't available (which most likely isn't)...

annotation

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 6, 2026

But I think it now should match the look in the hover popup (at least size wise).

@jwortmann
Copy link
Copy Markdown
Member Author

I didn't realize that code in popups also uses such a small font size (from mdpopups defaults), because I have an override for --mdpopups-font-mono in my user mdpopups.css. But that override isn't applied in annotations.

I pushed a commit c9b3470 that should make it look good on all OS.

Also removed the CSS variables, since they were only used in one place and annotations are not rendered via mdpopups (so user overrides have no effect for annotations, and using the same variable names there would just be misleading). And I assume "sf mono" is the default monospace font on Mac, so using just monospace should be equivalent there (and makes matching font size more reliable for other platforms).

@rchl
Copy link
Copy Markdown
Member

rchl commented Apr 7, 2026

so user overrides have no effect for annotations, and using the same variable names there would just be misleading

I think it would still make sense to keep same variable names as a way to indicate that those should be kept in sync.

Though mdpopups overrides not applying in annotations is indeed not ideal.

And I assume "sf mono" is the default monospace font on Mac, so using just monospace should be equivalent there (and makes matching font size more reliable for other platforms).

"sf mono" is not included on Mac by default. "Menlo" is the default.

Technically using "monospace" should be no different from having all those fonts specified for a stock Mac setup but:
a) it means that the style won't necessarily match between hover popup and annotations if the setup is not stock
b) don't think that it makes it any more reliable in the cross-platform sense since the default monospace fonts are still different across platforms. I would argue that the previous setup was more reliable since if someone manually installed Consolas, for example (or "sf mono" on both platforms) then he would get the same look on both.

@jwortmann
Copy link
Copy Markdown
Member Author

jwortmann commented Apr 7, 2026

I think it would still make sense to keep same variable names as a way to indicate that those should be kept in sync.

But these should not be kept in sync because html popups and the annotations use different styling. For example the font color for diagnostics in popups is white/black and in annotations it uses red/yellow/blue font color based on the severity. The annotations only support / use basic font styling. Using same variable names for things that are different is misleading and could be confusing to users if they think they could override the CSS variables.

I would argue that the previous setup was more reliable since if someone manually installed Consolas, for example (or "sf mono" on both platforms) then he would get the same look on both.

Consolas is even a paid font, so I assume it's rare for Mac users to install that font. The 1rem monospace perfectly fits to 1rem system font size on Windows. But if I manually install SF Mono, the font sizes doesn't match anymore because that font appears taller. I'd say we should probably even fix the code font size from mdpopups defaults in the hover popups and/or override the --mdpopups-font-mono variable to use just monospace. The idea of trying to force "sf mono" on Windows users with the side effect of bugged font sizes for default fonts can only come from a Mac user.

@jwortmann jwortmann merged commit 93a0815 into sublimelsp:main Apr 8, 2026
8 checks passed
@jwortmann jwortmann deleted the diagnostics-markdown branch April 8, 2026 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants