Skip to content

Conversation

@Mash707
Copy link
Contributor

@Mash707 Mash707 commented Feb 10, 2025

#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
Copy link
Contributor Author

Mash707 commented Feb 10, 2025

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

@jelly
Copy link
Member

jelly commented Feb 11, 2025

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
Copy link
Contributor Author

Mash707 commented Feb 11, 2025

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

@allisonkarlitskaya
Copy link
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
Copy link
Contributor Author

Mash707 commented Feb 11, 2025

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
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
Copy link
Contributor Author

Mash707 commented Feb 17, 2025

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

@garrett
Copy link
Member

garrett commented Feb 18, 2025

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
Copy link
Contributor Author

Mash707 commented Feb 18, 2025

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

Recording.2025-02-18.234242.mp4

@garrett
Copy link
Member

garrett commented Feb 20, 2025

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
Copy link
Member

jelly commented Feb 20, 2025

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
Copy link
Member

jelly commented Feb 20, 2025

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
Copy link
Contributor Author

Mash707 commented Feb 20, 2025

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
Copy link
Contributor Author

Mash707 commented Feb 23, 2025

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
Copy link
Contributor Author

Mash707 commented Feb 23, 2025

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
Copy link
Contributor Author

Mash707 commented Feb 23, 2025

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

Copy link
Member

@jelly jelly left a comment

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.

const displayLineNumbers = () => {
const lineNumbers = calculateLineNumbers();
lineNumbersEle.innerHTML = lineNumbers
.map((num) => `<div>${num === 0 ? "&nbsp;" : num}</div>`)
Copy link
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.

});

const resizeObserver = new ResizeObserver(() => {
lineNumbersEle.style.height = `${textarea.getBoundingClientRect().height}px`;
Copy link
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?

(lineNumbersEle.style as any)[property] = textareaStyles[property];
}
});
};
Copy link
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.


const paddingLeft = parseValue(textareaStyles.paddingLeft);
const paddingRight = parseValue(textareaStyles.paddingRight);
const textareaWidth = textarea.getBoundingClientRect().width - paddingLeft - paddingRight;
Copy link
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
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
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.


words.forEach((word) => {
const wordWidth = context.measureText(word + " ").width;
const lineWidth = context.measureText(currentLine).width;
Copy link
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
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.

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.

4 participants