#Raw block with line numbers in page margin

37 messages · Page 1 of 1 (latest)

carmine path
#

In Latex, I was using listings with line numbers displayed in the page margin, and I would like to have that in Typst, too.
Is there some way to achieve this?
I am currently using codly with

#codly(
  languages: codly-languages,
  inset: (x: .32em, y: 0.25em),
  lang-format: none,
  number-align: right + top,
  number-placement: "outside",
  zebra-fill: none,
  fill: luma(245),
  highlighted-default-color: luma(230),
  reference-sep: [, line~],
)

and I don't see any options for what I want in the Codly docs.
But I would probably be fine without Codly/another package, if that makes things easier.
Thanks for any ideas or hints!

peak relic
peak relic
#

Is that fits?

#
#let custom_raw(raw_text) = {
  columns(2, gutter: -100%)[
    #pad(left: -20pt)[
      #let number_of_lines = raw_text.text.split("\n").len()
      #for i in range(number_of_lines) {
        show raw: set text(fill: gray.darken(25%))
        
        if (i < 9) {
          raw(" " + str(i+1) + "\n")
        }
        else {
          raw(str(i+1) + "\n")
        }
      }
    ]
    #colbreak()
    #box(stroke: .5pt, outset: (x: 5pt, y: 5pt), width: 100%, raw_text)
  ]
}
#

It's pretty customizable I think. Many parameters you can adjust how you like

peak relic
#

But it's unbreakable to next page. Is it breakable in LaTeX?

arctic steppe
#

interesting idea to use columns. That's probably why it's not breakable? A grid could do it instead maybe?

arctic steppe
#

Here's a try with a grid

#

?r theme=light

#set page(margin: 1cm, width: auto)
#let code = raw(block: true, lang: "typst",
"#show raw.where(block: true): it => {
  set block(stroke: 0.5pt, outset: 0.4em)
  grid(
    columns: (0pt, auto),
    align: (right, left),
    row-gutter: par.leading,
    ..for (nr, line) in it.lines.enumerate(start: 1) {
      (
        pdf.artifact(place(right + bottom, dx: -0.8em, {
          set text(luma(20%), size: 0.9em)
          [#nr]
        })),
        line,
      )
    }
  )
}
#doc")

#show: doc => {
  eval(code.text, mode: "markup", scope: (doc: doc))
}

#code
peak relic
arctic steppe
#

this one is breakable

#

oh yeah, a box is never breakable. But a block is breakable by default.

peak relic
#

Yes, I've used wrong way to draw rectangle around code. It have it's own way to do it

peak relic
arctic steppe
#

looks like the line spacing for empty lines is a problem there right?

peak relic
#

I tried to change my first function with column to use grid and it looks like some error gets bigger over amount of lines

#

Linebreaking also

arctic steppe
#

I can't reproduce that line spacing problem with my code

#

the idea with a grid is to put line number and line in column 1 and column 2, so they can't go out of sync that way

peak relic
arctic steppe
#

it can be worked around with a conditional like if line.text == "" { "" } else { line } (maybe there are better ways)

peak relic
# arctic steppe it can be worked around with a conditional like `if line.text == "" { "" } else ...
#show raw: set block(stroke: .5pt, outset: (x: 0pt, y: 5pt), width: 100%)

#let style-number(number) = {
  show raw: set text(fill: luma(140))
  raw(str(number))
}

#show raw.where(block: true): it => context {
  let max = it.lines.len()
  
  let line_numbers_width = measure(style-number(max)).width
  
  grid(columns: (0pt, auto), column-gutter: 0%, row-gutter: 4pt, ..it.lines
  .enumerate()
  .map(((i, line)) => (
    pad(left: -line_numbers_width - 5pt)[#style-number(i + 1)], 
    line))
  .flatten())
}
arctic steppe
#

seems good, pad is a good alternative to place

peak relic
#

Only what I kinda don't like is this left line. It's actually many lines at one place

arctic steppe
#

what's wrong with it?

peak relic
# arctic steppe what's wrong with it?

Depends on PDF viewer and zoom. This tiny dots appear in Firefox (which uses PDF.js) at some values of zoom. But if print it on paper nothing bad will appear. And what's least important that it's probably not perfectionist's way to draw many lines on the same place when one is enough

#

It's like this corners in LaTeX (example from Overleaf docs). A small not important detail which probably should look differently

carmine path
#

thanks for your solutions!
I told a friend (@outer mesa) about my issue and, in parallel to you, he came up with a solution that I'm using right now. The issue there is that if a line breaks, its line numer is shown on the bottom-most instead of the top-most resulting line, but that's not an issue for me at the moment. When I find the time, I will see how it compares to your solutions.

here's his snippet:

let x-inset = 5pt
let padding = 5pt
let colour = luma(180)

show raw.where(block: true): b => {
  show raw.line: it => {
    let num = text(.85em, fill: grey-colour.darken(20%))[#it.number]
    context {
      let width = measure([#b.lines.len()]).width
      let effective-pad = padding + x-inset 
      [#h(-width -effective-pad) #box(width: width, align(right, num))#h(effective-pad)#box(width: 100%, it.body)]
    }
  }
  set block(stroke: grey-colour, inset: (x: x-inset, y: 5pt), radius: 3pt)
  b
}
#

looks like this right now

peak relic
#

I think this style is better for a fragmets of code that are way less than a page and not too huge. So nice. Maybe it's a good idea to make it public via GitHub Gist so someone can find it in Google search

peak relic