Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Code Blocks - Thin Border Above #443

malasee started this conversation in Ideas
Jun 4, 2025 · 3 comments · 18 replies
Discussion options

Hi guys. Thanks for such a great plugin!

I have a quick question, let's say I have the following config for code blocks:

language_icon = true,
language_name = true,
border = 'thin',
above = "─",
below = "─",

If I set either language_icon or language_name to true, the "above" characters doesn't get rendered.

Is it possible to use these together? Maybe with an extra line above the language name and icon for the "above" characters? Or on the same line as the language name and icon, following it with a little padding (or before it if language rendered to the right)?

You must be logged in to vote

Replies: 3 comments 18 replies

Comment options

This is not currently possible.

I tried implementing something like this before and it ended up being fairly complicated and not looking that good (at least in my opinion). There are some edge cases to handle as well, like the language isn't the only thing that can appear in the info string above code blocks, for example this is also valid:

```python {filename="demo.py"}
print("Hello World")
```

You're welcome to give implementing it a shot, here's a previous request for this feature with some more context: #288

You must be logged in to vote
2 replies
Comment options

Thanks for your response and the extra context. I've been experimenting with the settings and got a style that I like.

vim.api.nvim_set_hl(0, "CustomHighlightBorder", {
 fg = "yellow",
 italic = true,
})

Omitting the "bg" so I get a transparent header border (but the bottom border is still visible). Then the following config:

 code = {
 sign = false,
 disable_background = true,
 width = "block",
 border = "thin",
 below = "",
 highlight_border = "CustomHighlightBorder",
 },

Which looks like this:

image

I would ideally like to control the color of the bottom border, but as soon as I set a "bg" value the top thick border becomes visible.

This works for me though :)

Comment options

If you remove the border value you'll get the default value which is hide. This will let you remove the bottom line entirely. Is that closer to what you were looking for?

Comment options

I've actually given it a shot. I'm pretty new to nvim and lua, but have a prototype created. I've tested for the inclusion of a filename, left and right align, and language padding. Here are a couple of screenshots so far:

image image

I still need to tidy things up quite a bit, and test other cases, but good progress so far :)

You must be logged in to vote
4 replies
Comment options

That's awesome, LMK once it's ready!

Comment options

I'm currently writing some tests to cover the different cases for the top thin border.

Let's say we have the following raw markdown:

 local lines = {
 '```rust',
 '```',
 '',
 '```rust {filename="test.rs"}',
 '```',
 '',
 '```rust',
 'println!("long line beyond header width");',
 '```',
 '',
 '```rust {filename="test.rs"}',
 'println!("long line beyond header width");',
 '```',
 }

This is how I expect the render to be for each of the following cases (I've replaced the actual icon with 'x' so it's easier to view on here):

 it('block left pad 0', function()
 code.width = 'block'
 code.position = 'left'
 code.language_pad = 0
 util.setup.text(lines, { code = code })
 util.assert_screen({
 'x rust',
 '──────',
 '',
 'x rust {filename="test.rs"}',
 '───────────────────────────',
 '',
 'x rust ───────────────────────────────────',
 'println!("long line beyond header width");',
 '──────────────────────────────────────────',
 '',
 'x rust {filename="test.rs"} ──────────────',
 'println!("long line beyond header width");',
 '──────────────────────────────────────────',
 })
 end)
 it('block left pad 2', function()
 code.width = 'block'
 code.position = 'left'
 code.language_pad = 2
 util.setup.text(lines, { code = code })
 util.assert_screen({
 '─ x rust ─',
 '──────────',
 '',
 '─ x rust {filename="test.rs"} ─',
 '───────────────────────────────',
 '',
 '─ x rust ─────────────────────────────────',
 'println!("long line beyond header width");',
 '──────────────────────────────────────────',
 '',
 '─ x rust {filename="test.rs"} ────────────',
 'println!("long line beyond header width");',
 '──────────────────────────────────────────',
 })
 end)
 it('block right pad 0', function()
 code.width = 'block'
 code.position = 'right'
 code.language_pad = 0
 util.setup.text(lines, { code = code })
 util.assert_screen({
 'x rust',
 '──────',
 '',
 '{filename="test.rs"} x rust',
 '───────────────────────────',
 '',
 '─────────────────────────────────── x rust',
 'println!("long line beyond header width");',
 '──────────────────────────────────────────',
 '',
 '{filename="test.rs"} ────────────── x rust',
 'println!("long line beyond header width");',
 '──────────────────────────────────────────',
 })
 end)
 it('block right pad 2', function()
 code.width = 'block'
 code.position = 'right'
 code.language_pad = 2
 util.setup.text(lines, { code = code })
 util.assert_screen({
 '─ x rust ─',
 '──────────',
 '',
 '─ {filename="test.rs"} x rust ─',
 '───────────────────────────────',
 '',
 '───────────────────────────────── x rust ─',
 'println!("long line beyond header width");',
 '──────────────────────────────────────────',
 '',
 '─ {filename="test.rs"} ────────── x rust ─',
 'println!("long line beyond header width");',
 '──────────────────────────────────────────',
 })
 end)

Note: if we have a language_pad of 1, it would just have a space without an above character. I could use the above character, but it would be squashed up next to the text and would look inconsistent. I think having the space either side of the text is important to make the header look nice. Generally, advise against using language_pad of 1, when the border is thin.

You mentioned potentially using a new tab border style, but I'm not sure if it's necessary with this approach, as the tab effect can be achieved by using thin and a language_pad. It also makes thin border work in the way I expected, with a thin border at the top as well as the bottom. Let me know your preference and I'll go with that :)

Please let me know if there's anything you would like to change on the proposed renderings.

By the way, as I've had to test a lot of edge cases for this, I came across a couple of bugs with existing functionality in some of these. I'll share the details of these later.

Comment options

You mentioned potentially using a new tab border style, but I'm not sure if it's necessary with this approach, as the tab effect can be achieved by using thin and a language_pad. It also makes thin border work in the way I expected, with a thin border at the top as well as the bottom. Let me know your preference and I'll go with that :)

I get why you would expect things to work that way. The idea behind using a new style is not to change the functionality for people without opting in to something new, it can be surprising if an update causes the rendering to behave differently. For anyone that may prefer the current rendering is there a way to get the old functionality back? Depending on the results I can be okay with changing the behavior, but there should be a way to achieve the same rendering as you get currently.

Please let me know if there's anything you would like to change on the proposed renderings.

It's a little hard to tell how things look from the screen draws, those really are just for quickly being able to unit test. But other than the things I mentioned above I don't have any other concerns.

By the way, as I've had to test a lot of edge cases for this, I came across a couple of bugs with existing functionality in some of these. I'll share the details of these later.

Sweet, feel free to create simpler bug reports just configuration + things that break would be enough. All the other stuff in the template is for filtering out common issues.

Comment options

I get why you would expect things to work that way. The idea behind using a new style is not to change the functionality for people without opting in to something new, it can be surprising if an update causes the rendering to behave differently. For anyone that may prefer the current rendering is there a way to get the old functionality back? Depending on the results I can be okay with changing the behavior, but there should be a way to achieve the same rendering as you get currently.

Of course, that makes perfect sense. I would say to revert back to current rendering in terms of the config, the above value would just need to be set to ' ' (or ''), indicating no top thin border is desired. Looking at the default config for above and below, these would be set to the current half height blocks. So existing users that don't have an explicit above value in their config would see this default char in the top border, even when there is a code or icon on the line (not just when these are empty).

The couple of bugs I came across trace back to a single issue. Let's say I have the following code block:

```lua {filename="demo.lua"}
vim.print('Hello, World!')
```

And the following config:

{
 win_options = {
 conceallevel = {
 default = vim.o.conceallevel,
 rendered = 0,
 },
 },
 code = {
 width = "block",
 }
}

This would render like this (trucating the header):

image
Comment options

the above value would just need to be set to ' ' (or ''), indicating no top thin border is desired.

I should be more clear about what the current behavior is. When the border value is set to anything other than none (thick, thin, hide), if this plugin rendered anything in the info section (icon or language), then it'll render the border as thick. Which just applies a highlight to the line and does not care about the values for above or below.

We're effectively going from using thick as our filler style to thin with this change.

Maybe it makes more sense to add a new language_border option, with the same set of possible values as border. The behavior would then be:

  • for the top border if the info section is not empty use language_border otherwise use border: (icon or has_more) and self.config.language_border or self.config.border
  • for the bottom border always use border: self.config.border
  • from there the main change in behavior is you've added support for adding a thin border on the info section
  • to get the current behavior users would set language_border = 'thick'

The couple of bugs I came across trace back to a single issue. Let's say I have the following code block:

Yeah, honestly don't think I want to bother fixing these until someone creates an issue about it. In general there are probably more weird behaviors in the case where the info line is wider than anything in the code body, but in practice I don't think this happens often.

You must be logged in to vote
12 replies
Comment options

Actually I have a solution to completely avoid the thin_border_pad_char. For a top thin border with language, I can apply the initial thick border background as it currently does, then just apply the thin borders over the top using offsets to position them (using the thin_border_pad, or just always have this set as 1).

Comment options

Would you be able to create a PR (even if it's a rough one) with whatever the current state of the change is?

It'll help me better understand what option makes sense and maybe think of other ways to approach this.

No need to fix any failing unit tests (unless you already have) mostly interested in how you actually generate the thin border extmark(s).

Comment options

No problem, I've just created one it in its current state. In terms of functionality, I think it's pretty close now, and has a few unit tests testing the layout of the new rendered output.

Comment options

I'm just going to tidy a few things and will push another PR, the rough one with the current state wasn't working after a couple of recent changes.

Edit: The issues seems to be related to some commits I merged in from last week, I'll take a look today and create a new PR

Comment options

PR has now been updated and back to a working state post merge. It's still work in progress, but you can see the approach, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Ideas
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /