-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add text parser, use it for handling pasted text
The text parser recognizes multiple sections for text separated by line breaks and will parse lists when lines of text start with "*" (unordered) or "<number>." (ordered). fixes #263
- Loading branch information
Showing
4 changed files
with
258 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import assert from 'mobiledoc-kit/utils/assert'; | ||
import { | ||
MARKUP_SECTION_TYPE, | ||
LIST_SECTION_TYPE | ||
} from 'mobiledoc-kit/models/types'; | ||
import { | ||
DEFAULT_TAG_NAME as DEFAULT_MARKUP_SECTION_TAG_NAME | ||
} from 'mobiledoc-kit/models/markup-section'; | ||
|
||
const SECTION_BREAK = "\n"; | ||
const UL_LI_REGEX = /^\* (.*)$/; | ||
const OL_LI_REGEX = /^\d.? (.*)$/; | ||
|
||
export default class TextParser { | ||
constructor(builder, options) { | ||
this.builder = builder; | ||
this.options = options; | ||
|
||
this.post = this.builder.createPost(); | ||
this.prevSection = null; | ||
} | ||
|
||
/** | ||
* @param {String} text to parse | ||
* @return {Post} a post abstract | ||
*/ | ||
parse(text) { | ||
text.split(SECTION_BREAK).forEach(text => { | ||
let section = this._parseSection(text); | ||
this._appendSection(section); | ||
}); | ||
|
||
return this.post; | ||
} | ||
|
||
_parseSection(text) { | ||
let tagName = DEFAULT_MARKUP_SECTION_TAG_NAME, | ||
type = MARKUP_SECTION_TYPE, | ||
section; | ||
|
||
if (UL_LI_REGEX.test(text)) { | ||
tagName = 'ul'; | ||
type = LIST_SECTION_TYPE; | ||
text = text.match(UL_LI_REGEX)[1]; | ||
} else if (OL_LI_REGEX.test(text)) { | ||
tagName = 'ol'; | ||
type = LIST_SECTION_TYPE; | ||
text = text.match(OL_LI_REGEX)[1]; | ||
} | ||
|
||
let markers = [this.builder.createMarker(text)]; | ||
|
||
switch (type) { | ||
case LIST_SECTION_TYPE: | ||
let item = this.builder.createListItem(markers); | ||
let list = this.builder.createListSection(tagName, [item]); | ||
section = list; | ||
break; | ||
case MARKUP_SECTION_TYPE: | ||
section = this.builder.createMarkupSection(tagName, markers); | ||
break; | ||
default: | ||
assert(`Unknown type encountered ${type}`, false); | ||
} | ||
|
||
return section; | ||
} | ||
|
||
_appendSection(section) { | ||
let isSameListSection = | ||
section.isListSection && | ||
this.prevSection && this.prevSection.isListSection && | ||
this.prevSection.tagName === section.tagName; | ||
|
||
if (isSameListSection) { | ||
section.items.forEach(item => { | ||
this.prevSection.items.append(item.clone()); | ||
}); | ||
} else { | ||
this.post.sections.insertAfter(section, this.prevSection); | ||
this.prevSection = section; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import TextParser from 'mobiledoc-kit/parsers/text'; | ||
import PostNodeBuilder from 'mobiledoc-kit/models/post-node-builder'; | ||
import Helpers from '../../test-helpers'; | ||
|
||
const {module, test} = Helpers; | ||
|
||
let editorElement, builder, parser, editor; | ||
|
||
module('Unit: Parser: TextParser', { | ||
beforeEach() { | ||
editorElement = $('#editor')[0]; | ||
builder = new PostNodeBuilder(); | ||
parser = new TextParser(builder); | ||
}, | ||
afterEach() { | ||
builder = null; | ||
parser = null; | ||
if (editor) { | ||
editor.destroy(); | ||
editor = null; | ||
} | ||
} | ||
}); | ||
|
||
test('#parse returns a markup section when given single line of text', (assert) => { | ||
let text = 'some text'; | ||
let post = parser.parse(text); | ||
let expected = Helpers.postAbstract.build(({post, markupSection, marker}) => { | ||
return post([markupSection('p', [marker('some text')])]); | ||
}); | ||
|
||
assert.postIsSimilar(post, expected); | ||
}); | ||
|
||
test('#parse returns multiple markup sections when given multiple lines', (assert) => { | ||
let text = ['first section', 'second section'].join('\n'); | ||
let post = parser.parse(text); | ||
let expected = Helpers.postAbstract.build(({post, markupSection, marker}) => { | ||
return post([ | ||
markupSection('p', [marker('first section')]), | ||
markupSection('p', [marker('second section')]) | ||
]); | ||
}); | ||
|
||
assert.postIsSimilar(post, expected); | ||
}); | ||
|
||
test('#parse returns list section when text starts with "*"', (assert) => { | ||
let text = '* a list item'; | ||
|
||
let post = parser.parse(text); | ||
let expected = Helpers.postAbstract.build(({post, listSection, listItem, marker}) => { | ||
return post([ | ||
listSection('ul', [listItem([marker('a list item')])]) | ||
]); | ||
}); | ||
|
||
assert.postIsSimilar(post, expected); | ||
}); | ||
|
||
test('#parse returns list section with multiple items when text starts with "*"', (assert) => { | ||
let text = ['* first', '* second'].join('\n'); | ||
|
||
let post = parser.parse(text); | ||
let expected = Helpers.postAbstract.build(({post, listSection, listItem, marker}) => { | ||
return post([ | ||
listSection('ul', [ | ||
listItem([marker('first')]), | ||
listItem([marker('second')]) | ||
]) | ||
]); | ||
}); | ||
|
||
assert.postIsSimilar(post, expected); | ||
}); | ||
|
||
test('#parse returns list sections separated by markup sections', (assert) => { | ||
let text = ['* first list', 'middle section', '* second list'].join('\n'); | ||
|
||
let post = parser.parse(text); | ||
let expected = Helpers.postAbstract.build( | ||
({post, listSection, listItem, markupSection, marker}) => { | ||
return post([ | ||
listSection('ul', [ | ||
listItem([marker('first list')]) | ||
]), | ||
markupSection('p', [marker('middle section')]), | ||
listSection('ul', [ | ||
listItem([marker('second list')]) | ||
]) | ||
]); | ||
}); | ||
|
||
assert.postIsSimilar(post, expected); | ||
}); | ||
|
||
test('#parse returns ordered list items', (assert) => { | ||
let text = '1. first list'; | ||
|
||
let post = parser.parse(text); | ||
let expected = Helpers.postAbstract.build( | ||
({post, listSection, listItem, markupSection, marker}) => { | ||
return post([listSection('ol', [listItem([marker('first list')])])]); | ||
}); | ||
|
||
assert.postIsSimilar(post, expected); | ||
}); | ||
|
||
test('#parse can have ordered and unordered lists together', (assert) => { | ||
let text = ['1. ordered list', '* unordered list'].join('\n'); | ||
|
||
let post = parser.parse(text); | ||
let expected = Helpers.postAbstract.build( | ||
({post, listSection, listItem, markupSection, marker}) => { | ||
return post([ | ||
listSection('ol', [listItem([marker('ordered list')])]), | ||
listSection('ul', [listItem([marker('unordered list')])]) | ||
]); | ||
}); | ||
|
||
assert.postIsSimilar(post, expected); | ||
}); |