pioul / Minimalist-Online-Markdown-Editor

This is the simplest and slickest online Markdown editor.
http://markdown.pioul.fr
MIT License
265 stars 45 forks source link

Please Explain this to me. #11

Closed espoal closed 9 years ago

espoal commented 9 years ago

Sorry for the troll-y title, but I couldn't find anything better :stuck_out_tongue:

I'm building an open source markdown+latex editor, and your work has been really inspiring to me. I wanted to make you some questions:

1) Why you didn't pick an editor, like codemirror or ACE?

2) If I understood your code correctly, everytime there's a change you re-render everything, is this right?

If so, aren't you concerned with performance?

3) Your sync scrolling is the best part of your app. Simple, effective and really useful.

And it is well written, because even I could understand how it worked. That's the part I like the most.

I was looking for a similar feature: highlight the original source when clicking on the preview.

I was thinking to do it using multiple hidden HTML tags, which would contain data about what has generated the surrounding content. thus allowing the UI to react accordingly.

Do you have a better idea? Would you think about implementing this in your app?

philippe-git commented 9 years ago

Hey, thanks a lot for those kind words and interesting questions! (And sorry it's taken me a while to answer.)

1: Why I didn't use an existing editor component

The two main reasons I went for a simple textarea are:

On the other hand, even though the editor is minimalist, some of the features these editors come packed with are still desirable, and I've found myself having to implement them on my own: tab support, text search

While this makes for interesting implementation challenges, it's also reinventing the wheel, so it's really just a matter of weighing the pros and cons depending on what features you need. I might reconsider not using an existing editor in the future for that reason.

2: Everytime there's an actual change to the Markdown source, the preview (or the HTML markup container, depending on what panel is visible) is updated. No debouncing or throttling, since I'm aiming for a snappy experience (letter appearing on the left = letter appearing on the right), and haven't seen noticeable slowdowns on tests I've run on decent machines with a relatively huge amount of Markdown.

But yeah, updating a large chunk of DOM that often is probably going to be the first performance bottleneck I run into, so I'm keeping an eye on it and will optimize as needed. The only place I'm debouncing stuff in the app is the undo manager (in the Chrome app), and it's only a UX consideration (although it has memory implications as well): you don't want every keystroke to be saved in the undo history, otherwise you're going to have a bad time undoing what you wrote.

3: Thanks again! Sync scrolling has required some amount of thought, the main issue with implementation being the textarea.

The most naive way to go with sync-scrolling would be to scroll the preview panel proportionately with the source panel. That approach is really easy to implement, but falls short as soon as the previewed content takes a different amount of space than the source, which is often – think of any image, the source would be ~15px high, while the image could be 500px high.

The ideal approach would be to perfectly align one element in the source panel with the same element, rendered in the preview panel. That could be the element that has the focus (i.e. the one where the caret is), or the one near the top of the page when you're scrolling (hence don't care about the caret). This ideal approach implies having a very fine-grained knowledge of what's in the source panel, in other words tokenizing it. The source tokens can then be mapped with their rendered counterparts.

Hopefully Markdown parsers are already doing this work as part of their job, so one that's extensible enough will allow you to hook into them and know what the previewed elements map to in the source. This is what Markdown-it has allowed me to do: every block-level element in the preview panel has some metadata attached to it. The source panel being a textarea, that metadata is the range of characters from the source that resulted in this rendered element.

In MME, that mapping allows the editor to know what rendered element has been generated by any part of the source: wherever the caret is, we know what rendered element should have the focus as well.

That's neat, but that's not the end of the story. When you're scrolling, there's no such caret position to rely on. So you want to be able to know what's displayed e.g. at the top of the source panel so that you can mirror it in the preview panel. Unfortunately, without a caret (and the associated HTMLTextAreaElement.selectionStart), there's no way of knowing where we are. We know the scroll position, but can't guess what part of the source is actually visible. Even counting the number of line breaks and multiplying it with the height of those lines would be imprecise, since lines wrap in hardly predictable ways.

The only way to know precisely what's visible in the textarea is to not have a textarea at all. If you use HTML instead of a dull textarea, you know exactly what height is every element, and can precisely map those in the source with those in the preview. Having HTML at hand is a given with "editor components" mentioned in (1), but requires having what's colloquially know as a mirror div when using a simple textarea: a div that contains the source, unaltered, wrapped in HTML elements, and styled exactly as the textarea's text. This div should be a pixel-perfect reproduction of the textarea, only with HTML tags at our disposal to be able to locate where every piece of source text comes from.

Such source <-> preview mapping, made possible by either "editor components" or by adding a mirror div to a simple textarea, makes two things possible:

These two things are, in my opinion, necessary to reach "perfect" sync-scrolling. They also make what you're looking for, "highlighting the original source when clicking on the preview", possible.

To answer your last question, I have every intention of implementing that in MME :-)

I hope this helps; don't hesitate to answer with more questions, it'll my pleasure to continue that discussion.

Have fun with your editor project!