Skip to content

Adding line numbers in editor#940

Draft
Mash707 wants to merge 1 commit into
cockpit-project:mainfrom
Mash707:adding-line-numbers-in-editor
Draft

Adding line numbers in editor#940
Mash707 wants to merge 1 commit into
cockpit-project:mainfrom
Mash707:adding-line-numbers-in-editor

Conversation

@Mash707

@Mash707 Mash707 commented Feb 10, 2025

Copy link
Copy Markdown
Contributor

#916
I have done a basic implementation for displaying line numbers in the editor. Currently I have tested it with resizing my browser window
image
image
image

@Mash707

Mash707 commented Feb 10, 2025

Copy link
Copy Markdown
Contributor Author

@jelly @garrett have a look at this. Thank You.

@jelly

jelly commented Feb 11, 2025

Copy link
Copy Markdown
Member

From a quick look at the PR:

  • The line numbers show a scrollbar, it should not as we have a scrollbar on the right already:
    image
  • Very long lines are shown incorrectly for example line 8 here:
    image

Resized, shown as 3 lines:

image

  • Create file does not yet have these line numbers, but that's because it uses its own TextArea, that can be fixed later by refactoring some things

@Mash707

Mash707 commented Feb 11, 2025

Copy link
Copy Markdown
Contributor Author

I have removed the scroll bar from line numbers. Working on long lines issue.

@allisonkarlitskaya

Copy link
Copy Markdown
Member

It might be worth looking at the history in #509 a bit to see what we've tried and failed with. @garrett tried adding line numbers there and we took them out because we couldn't get them working reliably.

@Mash707

Mash707 commented Feb 11, 2025

Copy link
Copy Markdown
Contributor Author

It might be worth looking at the history in #509 a bit to see what we've tried and failed with. @garrett tried adding line numbers there and we took them out because we couldn't get them working reliably.

I went through the discussion, implementing long lines thing is a bit complex. Currently I am trying to do something similar to this article.

@allisonkarlitskaya

Copy link
Copy Markdown
Member

I went through the discussion, implementing long lines thing is a bit complex. Currently I am trying to do something similar to this article.

The canvas-based approaches look pretty promising but potentially slow on larger text files. Please consider some form of memoization if you're going to do this.

@Mash707

Mash707 commented Feb 17, 2025

Copy link
Copy Markdown
Contributor Author

prism-react-editor uses prismjs , works pretty well but is dependency heavy.

@garrett

garrett commented Feb 18, 2025

Copy link
Copy Markdown
Member

Aside note: With my proof of concept, there were versions and updates. Make sure you're testing the latest version, not a previous one please. Some of the CSS changed to fix wrapping and line calculation, where it mostly worked in earlier versions, it's closer to (or exactly as, at least with what I tested) what is expected.

@Mash707

Mash707 commented Feb 18, 2025

Copy link
Copy Markdown
Contributor Author

For now, I do have a solution which implements horizontal scrolling instead of line-wrapping.

Recording.2025-02-18.234242.mp4

@garrett

garrett commented Feb 20, 2025

Copy link
Copy Markdown
Member

For now, I do have a solution which implements horizontal scrolling instead of line-wrapping.

We might even want to make it toggleable. In most cases, soft wrapping is the right approach.

I would like to look at this again when I have time to do so. Right now, I do not have the time to do so. I don't know when that would be, but it's not right this moment. Apologies!

Thanks for looking into this and trying to find a solution! Having a PR to work in is fantastic!

My demo seemed to be working outside of Cockpit, and there's the whole issue where something works in vitro but not in vivo. (That is, borrowing from science: something may work in an isolated area, like a test tube, but doesn't work quite the same when inside a body. Same for Cockpit, as there are a lot more things going on within Cockpit already and also with PatternFly than some simplified test area.)

@jelly jelly marked this pull request as draft February 20, 2025 12:58
@jelly

jelly commented Feb 20, 2025

Copy link
Copy Markdown
Member

I have taken a look at the PR and the line wrapping doesn't work as @garrett's demo code wasn't ported to this PR. Did you have issues integrating it?

I have taken a look and it doesn't seem super hard to create something with a canvas approach, we do need to work on optimization it. At least made some notes for myself.

The required code up front to create a canvas to use for context.measureText()

const textarea = document.querySelector('#editor-text-area')
const styles = window.getComputedStyle(textarea, null)

const parseValue = (v) => v.endsWith('px')  ? parseInt(v.slice(0, -2), 10)  : 0;

const textareaStyles = window.getComputedStyle(textarea);
const paddingLeft = parseValue(textareaStyles.paddingLeft);
const paddingRight = parseValue(textareaStyles.paddingRight);
const textareaWidth = textarea.getBoundingClientRect().width - paddingLeft - paddingRight;

const font_size = textareaStyles.getPropertyValue('font-size');
const font_family = textareaStyles.getPropertyValue('font-family');
const font = `${font_size} ${font_family}`;
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
context.font = font;

Also for reference take a look at this github example.

@jelly

jelly commented Feb 20, 2025

Copy link
Copy Markdown
Member

Some things for testing as a canvas can have a performance impact:

  • Large documents we limit the size to 1MB so we should handle that
  • Different charactersets like Japanese, Arabic, etc.

@Mash707

Mash707 commented Feb 20, 2025

Copy link
Copy Markdown
Contributor Author

I have taken a look at the PR and the line wrapping doesn't work as @garrett's demo code wasn't ported to this PR. Did you have issues integrating it?

I am testing code changes locally but haven't pushed them here.

@Mash707 Mash707 force-pushed the adding-line-numbers-in-editor branch from c6f7f51 to e8ae79f Compare February 23, 2025 19:07
@Mash707

Mash707 commented Feb 23, 2025

Copy link
Copy Markdown
Contributor Author

Also for reference take a look at this github example.

I have tried implementing the canvas approach. It still breaks though.

20250223-1924-55.4817904.mp4

@Mash707

Mash707 commented Feb 23, 2025

Copy link
Copy Markdown
Contributor Author

Here is a demo of pasting text.

Recording.2025-02-24.010501.mp4
Recording.2025-02-24.010649.mp4

English sample text
Japanese sample text

@Mash707

Mash707 commented Feb 23, 2025

Copy link
Copy Markdown
Contributor Author

I have some how messed up package.json.
node_modules are also shown in the changes.

@jelly jelly left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Progress looks good! There is an issue with the calculation of the textarea size which causes the "weird bugs" you see when resizing the textarea.

Comment thread src/dialogs/editor.tsx
const displayLineNumbers = () => {
const lineNumbers = calculateLineNumbers();
lineNumbersEle.innerHTML = lineNumbers
.map((num) => `<div>${num === 0 ? "&nbsp;" : num}</div>`)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like @garrett's example we should use a CSS property for the number of lines and use that with calc() instead of adding more html elements.

Comment thread src/dialogs/editor.tsx
});

const resizeObserver = new ResizeObserver(() => {
lineNumbersEle.style.height = `${textarea.getBoundingClientRect().height}px`;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done in layout, I don't understand why this is needed?

Comment thread src/dialogs/editor.tsx
(lineNumbersEle.style as any)[property] = textareaStyles[property];
}
});
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done once, and saved in state and changed on resize. paddingTop/paddingBottom should not be interesting for us.

Comment thread src/dialogs/editor.tsx

const paddingLeft = parseValue(textareaStyles.paddingLeft);
const paddingRight = parseValue(textareaStyles.paddingRight);
const textareaWidth = textarea.getBoundingClientRect().width - paddingLeft - paddingRight;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is called every input now and is rather expensive, needs to be put into state and only get called onResize and initially.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did some further digging and I found out why you have bugs, this width is wrong as it doesn't account for the scrollbar and this is why there are line number issues.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of textarea.getBoundingClientRect().width I tried using textarea.clientWidth , clientWidth excludes scrollbar width. The problem still persists.

Comment thread src/dialogs/editor.tsx

words.forEach((word) => {
const wordWidth = context.measureText(word + " ").width;
const lineWidth = context.measureText(currentLine).width;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it feels like we can optimize this away and just add a new variable let lineWidth = 0 and keep appending to that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use characterWidth instead of wordWidth? I think it will provide more accurate line wrapping.

@GraysonPeddie

GraysonPeddie commented Jul 10, 2026

Copy link
Copy Markdown

As a Senior Accessibility Analyst who just experienced the impact of missing line numbers while configuring DNS on Arch Linux, I wanted to add some user perspective to this technical discussion.

Real-world impact encountered today:

  • DNS named.conf error messages reference specific line numbers (e.g., "missing semi-colon in line 64")
  • Without line numbers, I had to manually count lines to fix the error
  • This significantly increased cognitive load and troubleshooting time
  • The error message was helpful, but the lack of line numbers made it frustrating to use

Accessibility considerations:

  • Reduces cognitive load for users with memory/attention difficulties
  • Helps users with visual processing challenges track their position
  • Improves efficiency for all users during error resolution

Appreciation for the technical work:
I understand this is technically challenging with line wrapping, performance, and international character support. Your work on addressing these challenges is valuable and will make Cockpit more accessible and usable for administrators who rely on GUI tools for system configuration.

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.

5 participants