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

Add paste from markdown experimental #15393

Merged
merged 39 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4dce8d9
Feature: Paste from markdown PoC.
martnpaneq Oct 20, 2023
229d8ca
Added tests for paste from markdown.
martnpaneq Oct 23, 2023
80464f7
Cleaned up tests for a previous version.
martnpaneq Oct 30, 2023
aeb964e
Improved paste from markdown to detect markdown in HTML clipboard.
martnpaneq Oct 30, 2023
d97203d
Addded tests for paste from markdown.
martnpaneq Oct 31, 2023
c80ce50
Merge branch 'master' into ck/2321-paste-from-markdown
pomek Nov 7, 2023
0d94ac1
Extracted paste from markdown to a separate plugin as PasteFromMarkdo…
martnpaneq Nov 9, 2023
6b8b1ab
Added manual test for paste from markdown.
martnpaneq Nov 9, 2023
8ed8509
Added missing type augmentation for paste from markdown.
martnpaneq Nov 9, 2023
d178734
Added docs snippets for paste from markdown.
martnpaneq Nov 9, 2023
be999a3
Review fixes.
pomek Nov 10, 2023
2f41f3a
Merge branch 'master' into ck/2321-paste-from-markdown
pomek Nov 10, 2023
27312f0
Updated paste from markdown component to handle lists copied as `text…
martnpaneq Nov 10, 2023
e588a2a
Docs: guide placeholder. [short flow]
godai78 Nov 10, 2023
e925a0d
Docs: link fix. [short flow]
godai78 Nov 13, 2023
01ec018
Docs: adjustments. [short flow]
godai78 Nov 13, 2023
e819d1c
Clean up.
pomek Nov 13, 2023
4e4d357
Docs: link fixes; demo unification; various updates. [short flow]
godai78 Nov 14, 2023
0ac5e90
Docs: a typo.
godai78 Nov 14, 2023
96cca19
Docs: markdown example file.
godai78 Nov 15, 2023
43c9a3f
Merge branch 'master' into ck/2321-paste-from-markdown
godai78 Nov 16, 2023
80e4a7b
Docs: rename; crosslinking. [short flow]
godai78 Nov 17, 2023
40fa4bf
Merge branch 'master' into ck/2321-paste-from-markdown
pomek Nov 22, 2023
a0e11ad
Fixed pasting block quotes and fixed test coverage for paste from mar…
martnpaneq Nov 22, 2023
fe9db28
Removed todos from docs and manual tests.
martnpaneq Nov 22, 2023
2890cba
Review fixes.
pomek Nov 23, 2023
393ed71
Removed not needed `@private` tags in JSDoc.
martnpaneq Nov 23, 2023
82770e9
Addressed docs issues.
pomek Nov 24, 2023
6d8f1bf
Merge branch 'master' into ck/2321-paste-from-markdown
godai78 Nov 24, 2023
0fb3cdb
Docs: updating demos; meta data. [short flow]
godai78 Nov 24, 2023
003c5ee
Paste markdown logic improvements after review.
martnpaneq Nov 24, 2023
288837c
Fix test.
martnpaneq Nov 24, 2023
fec4a16
Changed logic to not parse markdown for first level formatting tags.
martnpaneq Nov 29, 2023
8730c57
Merge branch 'master' into ck/2321-paste-from-markdown
godai78 Dec 1, 2023
f68a762
Changed list of exclusions to list of accepted first level markdown t…
martnpaneq Dec 1, 2023
2f320d2
Added `CODE` to `ALLOWED_MARKDOWN_FIRST_LEVEL_TAGS` for Windows compa…
martnpaneq Dec 1, 2023
6c380b3
Changed logic in `_containsOnlyAllowedTags()` to use regex and not `D…
martnpaneq Dec 1, 2023
56606fe
Revert "Changed logic in `_containsOnlyAllowedTags()` to use regex an…
martnpaneq Dec 1, 2023
663babb
Final refactoring.
martnpaneq Dec 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
* @module markdown-gfm/pastefrommarkdownexperimental
*/

import { type Editor, Plugin } from 'ckeditor5/src/core';
import { type ClipboardInputTransformationEvent, ClipboardPipeline } from 'ckeditor5/src/clipboard';
import { Plugin, type Editor } from 'ckeditor5/src/core';
import { ClipboardPipeline, type ClipboardInputTransformationEvent } from 'ckeditor5/src/clipboard';
import GFMDataProcessor from './gfmdataprocessor';
import type { ViewDocumentKeyDownEvent } from 'ckeditor5/src/engine';

const FORMATTING_TAGS = [ 'B', 'STRONG', 'I', 'EM', 'MARK', 'SMALL', 'DEL', 'INS', 'SUB', 'SUP' ];
martnpaneq marked this conversation as resolved.
Show resolved Hide resolved

/**
* The GitHub Flavored Markdown (GFM) paste plugin.
*
Expand Down Expand Up @@ -77,6 +79,10 @@ export default class PasteFromMarkdownExperimental extends Plugin {
return;
}

if ( this._isNotMarkdownBecauseContainsFormattingTags( dataAsTextHtml ) ) {
return;
}

const markdownFromHtml = this._parseMarkdownFromHtml( dataAsTextHtml );

if ( markdownFromHtml ) {
Expand All @@ -95,6 +101,7 @@ export default class PasteFromMarkdownExperimental extends Plugin {
*/
private _parseMarkdownFromHtml( htmlString: string ): string | null {
martnpaneq marked this conversation as resolved.
Show resolved Hide resolved
const withoutOsSpecificTags = this._removeOsSpecificTags( htmlString );

const withoutWrapperTag = this._removeFirstLevelWrapperTagsAndBrs( withoutOsSpecificTags );

if ( this._containsAnyRemainingHtmlTags( withoutWrapperTag ) ) {
martnpaneq marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -121,28 +128,44 @@ export default class PasteFromMarkdownExperimental extends Plugin {
return withoutBodyTag.replace( /^<!--StartFragment-->/, '' ).replace( /<!--EndFragment-->$/, '' ).trim();
}

/**
* If the input HTML string contains any first level formatting tags
* like <b>, <strong> or <i>, then we should not treat it as markdown.
*
* @param htmlString Clipboard content.
*/
private _isNotMarkdownBecauseContainsFormattingTags( htmlString: string ): boolean {
martnpaneq marked this conversation as resolved.
Show resolved Hide resolved
const parser = new DOMParser();
const { body: tempElement } = parser.parseFromString( htmlString, 'text/html' );

const tagNames = Array.from( tempElement.children ).map( el => el.tagName );

return tagNames.some( el => FORMATTING_TAGS.includes( el ) );
}

/**
* Removes multiple HTML wrapper tags from a list of sibling HTML tags.
*
* @param htmlString Clipboard content without any OS specific tags.
*/
private _removeFirstLevelWrapperTagsAndBrs( htmlString: string ): string {
const tempDiv = document.createElement( 'div' );
tempDiv.innerHTML = htmlString;
const parser = new DOMParser();
const { body: tempElement } = parser.parseFromString( htmlString, 'text/html' );

const brElements = tempElement.querySelectorAll( 'br' );

for ( const br of brElements ) {
br.replaceWith( '\n' );
}

const outerElements = tempDiv.querySelectorAll( ':not(:empty)' );
const brElements = tempDiv.querySelectorAll( 'br' );
const outerElements = tempElement.querySelectorAll( ':scope > *' );

for ( const element of outerElements ) {
const elementClone = element.cloneNode( true );
element.replaceWith( ...elementClone.childNodes );
}

for ( const br of brElements ) {
br.replaceWith( '\n' );
}

return tempDiv.innerHTML;
return tempElement.innerHTML;
}

/**
Expand All @@ -159,8 +182,8 @@ export default class PasteFromMarkdownExperimental extends Plugin {
*/
private _replaceHtmlReservedEntitiesWithCharacters( htmlString: string ) {
return htmlString
.replace( new RegExp( '&gt;', 'g' ), '>' )
.replace( new RegExp( '&lt;', 'g' ), '<' )
.replace( new RegExp( '&nbsp;', 'g' ), ' ' );
.replace( /&gt;/g, '>' )
.replace( /&lt;/g, '<' )
.replace( /&nbsp;/g, ' ' );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ describe( 'PasteFromMarkdownExperimental', () => {
);
} );

it( 'should not parse as markdown if first level formatting tags detected', () => {
setData( editor.model, '<paragraph>[]</paragraph>' );
pasteHtml( editor, '<b>foo **bar**</b><br><span>foo **bar**</span>' );

expect( getData( editor.model ) ).to.equal(
'<paragraph><$text bold="true">foo **bar**</$text>foo **bar**[]</paragraph>'
);
} );

// TODO add Chrome, Firefox, Safari, Edge clipboard examples.
describe( 'Mac', () => {
it( 'should parse correctly Mac type clipboard', () => {
Expand Down