Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Desktop: Replace Ace with Code mirror #3284

Merged
merged 23 commits into from
Jun 6, 2020

Conversation

CalebJohn
Copy link
Collaborator

@CalebJohn CalebJohn commented May 27, 2020

for reference #3269 #3236 #3054 #3229 #3147 #1762

As mentioned on the forum I've been experimenting with a transition to codemirror rather than ace editor. I still haven't finished the transition yet but so far things are looking really good. There was a lot of code in the old module that was necessary to get around Ace's quirks that has been removed. I still have quite a bit of refactoring to do but I have a functional base here if anyone is keen to test.

Perhaps more importantly codemirror works with non-monospace fonts :) It can also handle varying line heights (adjustable with css), and it supports inline images (will need to be added in a different pull request).
image

Copy link
Owner

@laurent22 laurent22 left a comment

Choose a reason for hiding this comment

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

Thanks for looking info this @CalebJohn. There are a few things that a new text editor should support to really make it worthwhile. I've noted the following in particular:

  • Spell checking
  • Variable font size
  • Search within text (and highlight search results)

I don't know if CodeMirror supports all this but would be worth checking.

I've also made a few comments on the code, but the main thing is that the editor should be a separate component that implements NoteBodyEditorProps and there shouldn't be a need to modify AceEditor files.

It should be easy to get them working side by side and the added benefit is that we can even release the app with CodeMirror even if it's not finished (as beta, like for TinyMCE).

@@ -5,14 +5,15 @@ import { useState, useEffect, useRef, forwardRef, useCallback, useImperativeHand
import { EditorCommand, NoteBodyEditorProps } from '../../utils/types';
Copy link
Owner

Choose a reason for hiding this comment

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

Any reason for the changes on Ace Editor related files? The app can handle multiple text editors, which makes it much easier to migrate to other editors. In NoteEditor.tsx, you could simply import your CodeMirror component, then if props.bodyEditor === "CodeMirror", load it.

So I guess instead of changing the AceEditor files, you'd just copy what you've done so far to NoteBody/CodeMirror and revert the changes on the AceEditor files. If you need some more info on how to get it working let me know.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Basically just laziness, I didn't know the new aspects of the code very well and I didn't want to waste time figuring out how to support a third editor before I even had one working.
I was initially planning on doing exactly what you suggested before making this public but decided it was better to get some visibility first. I'll definitely make those changes before taking this out of draft.

Comment on lines 28 to 41
export interface EditorProps {
value: string,
mode: string,
style: any,
theme: any,
readOnly: boolean,
autoMatchBraces: boolean,
keyMap: string,
cancelledKeys: CancelledKeys,
onChange: any,
onScroll: any,
onEditorContextMenu: any,
onEditorPaste: any,
}
Copy link
Owner

Choose a reason for hiding this comment

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

The editor should support the NoteBodyEditorProps (doesn't have to support all of them, but that should be the input).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've restructured the code a bit, so what you're looking at here is basically a react wrapper around codemirror. There is another component that handles the integration with Joplin. Confusingly I've renamed that other one to CodeMirror.tsx and called the wrapper Editor.tsx.

@CalebJohn
Copy link
Collaborator Author

In reference to your 3 items.

  1. Codemirror technically does support spellcheck, but when I did a quick test I wasn't able to get it working. Fortunately it's not that hard to get working with codemirror since it's fairly extendable.
  2. For now I have variable sized headers set in style.css, but we can discuss how we want this done, the editor definitely supports it though!
  3. Codemirror includes a builtin search function which I previously had turned on. It also has good support for marking arbitrary text (and reflecting the mark in the sidebar). I think the best option is to keep the joplin search and then write a custom plugin to integrate with it. I probably won't do that in this pull request, but it will be relatively straightforward.

Overall we also benefit by having an editor that's easier to interact with and won't require as much hacking to get basic functionality.

Drag and drop with selected text now works
Drag and drop will actually drop at the cursor position
When using toolbar buttons (or hotkeys), selections are now intuitive
@laurent22
Copy link
Owner

Codemirror technically does support spellcheck, but when I did a quick test I wasn't able to get it working. Fortunately it's not that hard to get working with codemirror since it's fairly extendable.

Eventually, I hope we can use the Atom spell checking package throughout the app to load dictionaries and get spelling suggestions, then each component (CodeMirror, TinyMCE, and title text input) would handle displaying the results. As long as CodeMirror has a well supported way to highlight terms then it's most likely good enough. Last time I've checked this with Ace Editor it was quite complicated.

For now I have variable sized headers set in style.css, but we can discuss how we want this done, the editor definitely supports it though!

Ok that's good to know. It might be that we leave it as monospace by default, but it's good if users can change it, and that will also allow us to improve the default editor styling, with larger heading, bold fonts, etc.

Codemirror includes a builtin search function which I previously had turned on. It also has good support for marking arbitrary text (and reflecting the mark in the sidebar). I think the best option is to keep the joplin search and then write a custom plugin to integrate with it. I probably won't do that in this pull request, but it will be relatively straightforward.

Ok that should work then, likewise if there's a well supported way to highlight terms it should work.

@CalebJohn CalebJohn marked this pull request as ready for review May 31, 2020 07:23
@CalebJohn
Copy link
Collaborator Author

This is ready to be reviewed I think. I have all the functionality (that I know of) that was in the original AceEditor and seems to be bug free (from my testing). But I plan to use this branch myself for the next few days to be sure.
I also added in some smart functionality to do with lists (basically adding and extending the functionality provided by @sinkuu in #2790 and #2772) to show off how we can easily make extensions to codemirror in order to add more complex behavior. Similarly I added a line sorting function because it was in the aceeditor and it does get used.

What I haven't done yet, but would like to do, is add a new file to the documentation that outlines how to extend the codemirror editor component, so standby for that before merging.

@laurent22
Copy link
Owner

Thanks for the update @CalebJohn. Obviously it's a huge PR and not so easy to review. There's a lot of duplicate code between the AceEditor and CodeMirror files so long term we should only have one "Code View" editor to keep things maintainable. If CodeMirror is superior we'll use it and drop Ace Editor.

One thing I didn't find is the vim/emacs support? You've imported some files related to it, but I'm not sure they are used yet?

Also is there any reason why the editor code is split between CodeMirror.tsx and Editor.ts? I can sort of see why you've done that, but the way I see it CodeMirror.tsx should have direct access to the editor instance, without a wrapper in between. The wrapper means we'll need to have some kind of bridge so that CodeMirror.tsx can access the properties and methods of the editor. I feel that's not necessary and might make the code more difficult to maintain.

Also the role of each file is not entirely clear, I guess Editor.tsx is lower level, but I think there's some overlap.

For AceEditor.tsx and TinyMCE.tsx, I've put the editor instance directly accessible from the component. Then I've used a bunch of React hooks to lower down the line count and keep things more manageable. What do you think? Would it make sense to put the editor instance in CodeMirror.tsx?

By the way we haven't really discussed the various code editors. Obviously there are other ones, like Monaco for instance, and CodeMirror 6 might be released some day. Any reason why you went for CodeMirror in particular?

@CalebJohn
Copy link
Collaborator Author

Obviously it's a huge PR and not so easy to review. There's a lot of duplicate code between the AceEditor and CodeMirror files so long term we should only have one "Code View" editor to keep things maintainable. If CodeMirror is superior we'll use it and drop Ace Editor.

👍

One thing I didn't find is the vim/emacs support? You've imported some files related to it, but I'm not sure they are used yet?

This is because of the way codemirror plugins work, during import the plugin registers itself with the codemirror instance, so we just need to pass it a keymap name (default, vim, emacs) and codemiror will handle the rest.

Also is there any reason why the editor code is split between CodeMirror.tsx and Editor.ts? I can sort of see why you've done that, but the way I see it CodeMirror.tsx should have direct access to the editor instance, without a wrapper in between. The wrapper means we'll need to have some kind of bridge so that CodeMirror.tsx can access the properties and methods of the editor. I feel that's not necessary and might make the code more difficult to maintain.
Also the role of each file is not entirely clear, I guess Editor.tsx is lower level, but I think there's some overlap.
For AceEditor.tsx and TinyMCE.tsx, I've put the editor instance directly accessible from the component. Then I've used a bunch of React hooks to lower down the line count and keep things more manageable. What do you think? Would it make sense to put the editor instance in CodeMirror.tsx?

What I was going for was as you've guessed. Separating the Joplin behavior from the codemirror behavior. Which I did for a few reasons.

  1. Make updating editor behavior more manageable. In the future I want to improve the codemirror editor by adding more plugins so I think it's good to make a distinction between editor behavior that interacts with Joplin, and just plain editor behavior.
  2. Anticipating the upgrade to Codemirror 6, I feel that splitting things up like this makes the Codemirror.tsx file (the one that handle Joplin) more robust against changes in the underlying editor because it only makes use of basic editor functions, or ones that I have defined in the utils folder.
  3. There is a lot of boilerplate code necessary for the interactions with the codemirror instance itself, so it's nice to keep this separate from the rest of the codebase. You'll notice that the entirety of Editor.tsx is devoted to these boilerplate interactions. The more complex codemirror code is formatted like plugins in the utils folder (this puts different behaviors in different files).

I actually did end up giving CodeMirror.tsx direct access to the codemirror editor, but most if not all the interactions with it are handled by higher level functions defined in one of the utils modules. Since this is such a large pull request (and I think you'll find it's not quite as large as it seems), I would be open to discussing it in a bit more detail over the discord, that way I can give you a much quicker introduction to why I structured it like this and how everything interacts.

By the way we haven't really discussed the various code editors. Obviously there are other ones, like Monaco for instance, and CodeMirror 6 might be released some day. Any reason why you went for CodeMirror in particular?

Not a terribly strong reason, but the Jupyter environment uses codemirror to power it's markdown editor (with variable font size) so I have some trust in it. I compared markdown support in Monaco as well and it seemed to be lacking the advanced features that were either already in codemirror (variable font size) or could be added fairly easily (inline images, etc.). I like how easy it is to write plugins for codemirror so even if we have to add support for certain features ourselves it won't be as much of a pain as it was with aceeditor.

On the topic of codemirror 6, I just don't trust that it will be ready for use in Joplin for at least a few years, so as long as we keep the codebase manageable we should be able to upgrade then (or stick with the more stable codemirror 5).

@laurent22
Copy link
Owner

Thanks for the info @CalebJohn, I'll go through it again and get back to you. I just thought about over things that ideally should work in a new editor:

All these I expect should be fixed if the editor properly handle unicode and non-monospace fonts, but worth checking anyway.

@CalebJohn
Copy link
Collaborator Author

Thanks @laurent22 I had meant to check these cases but I forgot.
I checked emoji support following issue #2244 and I was able to reproduce the error with Ace but not with Codemirror
I checked #113 and #1351 using the Japanese mozc input on my computer and I could at least reproduce the weird characters while editing, but the didn't persist after a selection was made (Ace). I did not have any issues while using codemirror.

I couldn't reproduce #3134 but I reached out to the poster and I hope to check it soon. That one may be difficult to reproduce because I think it might be a font issue, but I'm fairly confident it will be fixed.

@laurent22
Copy link
Owner

Thanks for checking this @CalebJohn. I've just gave a try to the branch and didn't find any issue, and since your changes are quite well isolated I'm keen to merge this so that we can start using it everyday, and find any remaining edge case if any. If you find any issue, feel free to open a new pull request for it, and thanks again for this great improvement!

@laurent22 laurent22 merged commit a8c8539 into laurent22:master Jun 6, 2020
@CalebJohn
Copy link
Collaborator Author

Thanks Laurent! Fingers crossed there aren't too many issues!

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.

2 participants