Pure Elm Text Editor

Thought you all would be interested in the pure elm text editor I’ve been working on. I’ve got text selection and quite a few keyboard shortcuts implemented.

Demo: https://sidneynemzer.github.io/elm-text-editor/
Github: https://github.com/SidneyNemzer/elm-text-editor

Next big features I want to implement are scrolling/viewport support, undo/redo, and line-wrap. (Check out the Github project for more plans).

I hope this inspires somebody to try some stuff in Elm they’ve been needing but seemed too big / hard for them! You might, like me with this project, find out it’s in your reach - no doubt thanks to Elm
- Janiczek (source)

26 Likes

Very cool. There is a bit of artifact when dragging the mouse to select text which makes the text box jump around for me.

This is extremely exciting! :smiley: Great work!

2 Likes

This really has a lot of potential.

I am curious what is the relationship with Janiczek’s pure Elm text editor? Does it work in a very similar way?

Awesome!

What is the overall goal with this project? I mean is developing an editor a means to support something else in this case? Do you have an application where you integrate this?

I see the readme mentions syntax highlighting as a planned feature. Does this mean it is primarily meant for editing programs?

1 Like

Thanks everyone!

If you’ve got the debug info shown, the debug text affects the editor’s centering on the page as state changes. If you don’t have the debug info open, then it’s definitely a bug, maybe you could open an issue with more info?

They started basically the same (I think I still have the branch that is heavily based of Janiczek’s editor?). Over time, they diverged as I split up modules and added new functionality. Some core pieces are still similar, like the event listeners and rendered DOM structure.

I would love if this editor could get to the level of Ace or CodeMirror! The company I work for has a web-IDE (similar to Mozilla’s Thimble) for our students. We’re using Ace at the moment via elm-ace. It works but it’s far from ideal and it inspired me to create a pure Elm solution.

I’ve been working on this editor in my spare time, so I can’t guarantee any particular timelines, but I’ll continue to work on it when I can. I’m pretty happy with it so far :smiley:

2 Likes

Confirm it is only jumpy when debug info displayed.

Cool, thanks for clarifying. It definitely annoys me that the debug info messes with the position of the editor, but I didn’t care enough to fix it. That’s just the demo app, not part of the editor of course. (contributions are welcome though :wink: )

Very very very cool! :slight_smile: :tada:

1 Like

Great work! Have you considered using a hidden input field for capturing user input? CodeMirror does it this way. So the editor would also work on mobile devices (soft keyboards).

With Elm 0.19 race conditions between the virtualdom and users typing fast (ie. in rendering the input field) should have gone away!

1 Like

Thanks! Yeah, at some point I think it will need to integrate a textarea, that seems to be the best way to support mobile. Ace uses a hidden textarea too.

For now, I’m focusing on desktop but in the future I also want to support mobile.

1 Like

Really promising!

For those interested, here is a related article about contentEditable from a CKEditor developer that gives a preview of the headache to support all use cases:

ContentEditable — The Good, the Bad and the Ugly.

And the recent design document of the rewrite of CodeMirror:

CodeMirror 6 design doc

Keep up the good work!

2 Likes

This looks really awesome! I have a question I have been asking my self while building something similar ( a full wysiwyg editor but not focused on code), which is: is such a package even possible in pure elm ?

My conclusion was, no. Because:

  1. copy and paste works smoothly and is safe and accessible only if one uses the selection API
  2. undo/redo has to be implemented ad hoc, breaking the native browser implementation
  3. in general accessibility becomes a bit a nightmare.

I wonder if you or any folk here has come to a different conclusion ?

Well, depends on what features you want! Looks like you’ve identified the problem areas…

I was planning to use a ‘local’ clipboard by default, which can be done with pure Elm. I’ll also provide functions to hook up ports that use JavaScript to capture paste and trigger copy events for the system clipboard.

Chrome has introduced an async clipboard API which means in the future we won’t need to use hacks like an offscreen textarea for coping arbitrary text! (It’s not even on caniuse.com though, so I don’t have much confidence it it’s adoption yet). It’s up to the consumer of the text editor to decide which clipboard API they want to use.

Yes, the editor has to handle undo/redo (I’m working on that now!). Not sure how that breaks the browser implementation? I believe Ace and CodeMirror also handle undo/redo in JavaScript.

Yep, I don’t expect good support for mobile devices at first, particularly since they have a software keyboard. In the future, a hidden textarea can potentially fix that. (Ace and CodeMirror also do this).

The conclusion I came to is that it will take a lot of work to implement features that a basic textarea supports. But I hope that we can support additional features without much effort like syntax highlighting, code annotations, and even multiple ‘editor’ views for a single buffer/file. I think a pure Elm text editor that mostly works is better than no pure Elm text editor!

1 Like

As linked previously, CodeMirror developers think that contentEditable is now the best way:

This was a good approach, in 2011, because contentEditable was terribly buggy, and browsers awfully incompatible. But it’s 2018, Internet Explorer is almost extinct (we do want to support version 11, possibly with some features degraded), and other browsers have come a long way. Our experience with ProseMirror shows that you can build something solid on contentEditable now.

1 Like

I’m curious to see how CodeMirror 6 progresses! For now I’m staying away from contentEditable in Elm because it means a lot of ports, but maybe I’ll end up using it in the future.

1 Like

Pure Elm Copy/Paste solution/suggestion:
I have an idea for how this maybe can be done in pure elm, hacky but pure Elm :slight_smile:

Copy:
Have a hidden textfield (offscreen) that always holds whatever is selected in your editor.
And also, whenever the selection is changed, put focus to this hidden textField, this should make this text in that field highlighted automatically (At least this is what happens when you tab into a textfield that already contains text on any webpage I ever seen)
So now the text you want is already hightlighted by the system/browser and any native Copy or Cut action will copy your text to clipboard. Ctrl+C…

Paste:
By implementing the logic above, the textfield should already be highlighted, either with text ready to be replaced, or empty, ready for new input.
When the user types or pasted, you pick up whatever is beeing typed or pasted in that textField, be it one character or a multiline thing… You already know the position where this input should start from and than you pass the new input to a function that includes this input to your model of the text.

Not tested this, but might be doable :slight_smile:
It also means that any copy/cut/paste happens in one browser native way, no need for a separate internal copy paste, should also not require any user ports… ?

1 Like

Awesome! I will keep an eye on your project! The route I have chosen it’s the opposite that is using content-editable and one port for the selection. I hope to open source soon!

2 Likes

Interesting ideas!

That’s not the behavior I’m seeing in Chrome 68, it just places the cursor at 0,0. Also the editor needs focus when you’re typing. Or more specifically the div that wraps all the other elements needs focus, because it needs to receive key events (which the browser only sends when an element has focus).

This is similar to how Ace and CodeMirror behave though. They stick a hidden textarea at the “cursor” which, among other things, tells mobile devices to show the keyboard.

Hi again,

Think of this hidden field as the input for all text into your editor. (After all its the Html.input function) :slight_smile: haha…
If you type a character, this comes into your editor content via this input field. If you paste something bigger, thats a single event with text to be placed inside your content as well.
Also, if you have something selected in your editor, this is already highlighted/selected inside this Html.input element, ready to be overwritten.
The more I think of it, the less hacky it feels :slight_smile:
So this input field does never hold your content, just what is to be put into your content.

The way I see it, the editor itself could even be rendered with Elm-UI or Svg+++ as long as you have a way of knowing the selected positions.

I gave it a try and updated your editor to Elm 0.19 (quick and dirty) to get the new Browser.Dom in Elm 0.19. (For setting focus to an element)
But it just gives focus to the inputBox, it does not seem highlight/select the content like a normal tab would do.

Paste from system clipboard actually works :slight_smile: I can paste whatever is on my system clipboard to any position selected by mouse or keyboard in your editor.

You can have a look and test it here:
(That index.html is already compiled, but I don’t know how to host it on github)

The change is simply that everytime you change your selection, the selected text would be put in the “copyPasteArea”
(Currently its just Debug.toString on your Selection, but would offcourse be the actual text in a real app)
Whenever there is something selected, I also set focus to that “copyPasteArea”

If Browser.Dom.focus had an option to also hightlight the content ( select() in JS )
Then any overwrite/copy/cut action would just work on any system and any Browser without relying on any API.
An alternative is to add something like Browser.Dom.focusAndSelect.
If that does not make it into the Browser package, a port for setting Focus().Select() on that input should also work.
Do you think the idea makes any more sense now ?

1 Like