Skip to content
This repository has been archived by the owner on Jan 20, 2022. It is now read-only.

Inline markdown symbols like *, _ don't apply when not surrounded by whitespace. #110

Closed
Aerlinger opened this issue Aug 3, 2019 · 7 comments · Fixed by #150
Closed

Comments

@Aerlinger
Copy link
Contributor

Example input: this is **bold**.
should render "this is bold." in editor but instead renders "this is **bold**".

This happens whenever the markdown inline shortcuts are not surrounded by whitespace or at the beginning or end of the block. The underlying issue is from the logic defined in MarkdownShortcuts at

(beginningOfBlock || endOfBlock || surroundedByWhitespaces)
.

Let me know if I can provide other examples or a PR.

@tommoor
Copy link
Member

tommoor commented Aug 3, 2019

I haven't been able to reproduce this one so far 🤔

image

@Aerlinger
Copy link
Contributor Author

Here's my example:

image

@tommoor
Copy link
Member

tommoor commented Aug 4, 2019

It totally depends on how you type it, as you've seen the markdown syntax is only converted into an actual bold mark after hitting the space character. It could also be changed to convert to a mark on the final "*" character.

The key here is that the editor doesn't just take anything that looks like markdown and render it as markdown, characters that look like markdown but are in the regular content are escaped for this reason.

TLDR: Depending on how those examples above were typed they could be considered a feature and not a bug.

@Aerlinger
Copy link
Contributor Author

I do understand that this is intended behavior in the current design and that in many circumstances it may be more intuitive (even if not in line with the markdown spec). However, I do think the mark conversion should also happen for the onEnter event in the same way it does for the 'onSpace' event. As a user, this would seem to me to be the expected behavior.

I realize the onEnter case is a bit more complicated as there's a separate handler for this (not to mention the EditList plugin has its own onEnter behavior), however, I do have an example that works well for me if you're interested.

It could also be changed to convert to a mark on the final "*" character.

This was my initial thought and in my opinion would be the way to go if not for the fact that the final character is ambiguous in the case of bold/italic (i.e. when typing __ or ** the mark would be converted to italic after the first character). I suppose it would be possible to write a parser to distinguish these cases by detecting the type of opening delimiter, but that still does come with some edge cases.

@tommoor
Copy link
Member

tommoor commented Aug 5, 2019

The only hill I'm willing to die on here is that the content of the document should never change on reload – ie. If I see **bold** written in the document, no matter how it was inserted it should look the same on reload and not be converted to a bold tag. 😄

There's a slate autoReplace plugin which I think could manage this behavior as you've described, we'd essentially define a plugin for each individual mark type and have it convert on the last character. I think this is probably the way to go rather than hacking at the existing implementation – could even convert one mark at a time.

@Aerlinger
Copy link
Contributor Author

I 100% agree that the document state should remain consistent between reloads.

I did come up with this solution that seems to solve the problem without introducing too much complexity by toggling the mark on a range trimmed to exclude that whitespace:

import {Range} from 'slate';
 
export default function KeyboardShortcuts() {
  /**
   * Map the given keyboard event to the relevant mark to be toggled.
   *
   * @param {Event} ev keyboard event
   * @param {Editor} editor
   * @param {function} next middleware.
   * @return {*|void|Range|*}
   */
  function onKeyDown(ev, editor, next) {
    if (!isModKey(ev)) return next();
 
    switch (ev.key) {
      case 'b':
        ev.preventDefault();
        return toggleMark(editor, 'bold', next);
      case 'i':
        ev.preventDefault();
        return toggleMark(editor, 'italic', next);
      case 'u':
        ev.preventDefault();
        return toggleMark(editor, 'underlined', next);
      case 'd':
        ev.preventDefault();
        return toggleMark(editor, 'deleted', next);
      case 'k':
        ev.preventDefault();
        return editor.wrapLink('');
      default:
        return next();
    }
  }
 
  /**
   * Get a range for the current selection excluding any leading or trailing
   * whitespace (including newlines).
   *
   * @param {Editor} editor Reference to SlateJS Editor
   * @return {Range} for current selection including whitespace.
   */
  function getSelectionRangeExcludingWhitespace(editor) {
    const selectedText = value.fragment.text;
    const selectionRange = Range.create(editor.value.selection);
 
    const leadingSpace = /^\s*/.exec(selectedText)[0];
    const trailingSpace = /\s*$/.exec(selectedText)[0];
 
    const numLeadingSpaces = leadingSpace.length;
    const numTrailingSpaces = trailingSpace.length;
 
    return selectionRange
        .moveStartForward(numLeadingSpaces)
        .moveEndBackward(numTrailingSpaces);
  }
 
  /**
   * If we're not in a heading, remove any code marks in the current selection
   * and wrap the selected text with mark of `type`.
   *
   * @param {Editor} editor.
   * @param {String} type of mark to be applied.
   * @param {function} next middleware.
   * @return {Range} for current selection including whitespace.
   */
  function toggleMark(editor, type, next) {
    const {value} = editor;
 
    // don't allow formatting of main document title
    if (value.startBlock.type === 'heading1') return next();
 
    const trimmedSelection = getSelectionRangeExcludingWhitespace(editor);
 
    editor.removeMark('code').toggleMarkAtRange(trimmedSelection, type);
  }
 
  return {onKeyDown};
}

Let me know what you think.

@tommoor
Copy link
Member

tommoor commented Oct 4, 2019

It looks neat enough, but isn't the original issue around the inline shortcuts? Not keyboard shortcuts?

This was referenced Jan 19, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants