Skip to content

Commit

Permalink
Typescriptify Phase IV πŸ’ƒπŸ•Ί (#742)
Browse files Browse the repository at this point in the history
* WIP on View/Tooltip TSification

* Fix broken ImageSection inserting πŸ’ƒπŸ•Ί

* Completes post-inserter migration πŸ’†β€β™€οΈ

* PARSER! 0.2.0 🎩

* PARSER! 0.3.0 πŸͺ“πŸͺ“ AND all its side effects

* PARSER! 0.3.1…0.3.2 🀿 + healthy refactor of renderers++

* Migrate index 🎧

* Long road πŸ›£ to migrating dom parser

* Section + HTML parsers πŸ•΅οΈβ€β™€οΈ

* Text Parser migration πŸŽ–

* Temporarily disable image section test 🎭

* Migrate parse-utils 🚯

Co-authored-by: Tom Dale <[email protected]>
  • Loading branch information
ZeeJab and tomdale authored Sep 2, 2020
1 parent ed3dcc2 commit b7e109b
Show file tree
Hide file tree
Showing 50 changed files with 914 additions and 637 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ module.exports = {
"no-unneeded-ternary": "off",
"no-unused-expressions": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error"],
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"no-useless-backreference": "error",
"no-useless-call": "off",
"no-useless-computed-key": "error",
Expand Down
2 changes: 1 addition & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function commonPlugins() {
},
],
}),
typescript(),
typescript({ noEmitOnError: false }),
]
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
import assert from 'mobiledoc-kit/utils/assert'
import {
MARKUP_SECTION_TYPE,
LIST_SECTION_TYPE,
POST_TYPE,
CARD_TYPE,
IMAGE_SECTION_TYPE,
LIST_ITEM_TYPE,
} from 'mobiledoc-kit/models/types'

const MARKERABLE = 'markerable',
NESTED_MARKERABLE = 'nested_markerable',
NON_MARKERABLE = 'non_markerable'
import assert from '../../utils/assert'
import { Option } from '../../utils/types'
import { Type } from '../../models/types'
import Post from '../../models/post'
import Editor from '../editor'
import PostNodeBuilder from '../../models/post-node-builder'
import { Position } from '../../utils/cursor'
import Section, { WithParent } from '../../models/_section'
import MarkupSection from '../../models/markup-section'
import ListSection from '../../models/list-section'
import ListItem from '../../models/list-item'
import Card from '../../models/card'
import Image from '../../models/image'
import Markerable from '../../models/_markerable'
import { Cloneable } from '../../models/_cloneable'
import HasChildSections, { hasChildSections } from '../../models/_has-child-sections'

const MARKERABLE = 'markerable'
const NESTED_MARKERABLE = 'nested_markerable'
const NON_MARKERABLE = 'non_markerable'

class Visitor {
constructor(inserter, cursorPosition) {
let { postEditor, post } = inserter
postEditor: Editor
builder: PostNodeBuilder
_post: Post
_hasInsertedFirstLeafSection: boolean
_cursorPosition!: Position

constructor({ postEditor, post }: Inserter, cursorPosition: Position) {
this.postEditor = postEditor
this._post = post
this.cursorPosition = cursorPosition
Expand All @@ -32,7 +44,7 @@ class Visitor {
this.postEditor.setRange(position)
}

visit(node) {
visit(node: Post | Section) {
let method = node.type
assert(`Cannot visit node of type ${node.type}`, !!this[method])
this[method](node)
Expand All @@ -51,7 +63,7 @@ class Visitor {
}

get cursorSection() {
return this.cursorPosition.section
return this.cursorPosition.section!
}

get cursorOffset() {
Expand All @@ -62,21 +74,22 @@ class Visitor {
return this.cursorSection.isNested
}

[POST_TYPE](node) {
if (this.cursorSection.isBlank && !this._isNested) {
[Type.POST](node: Post) {
let { cursorSection } = this
if (cursorSection.isBlank && !cursorSection.isNested) {
// replace blank section with entire post
let newSections = node.sections.map(s => s.clone())
this._replaceSection(this.cursorSection, newSections)
this._replaceSection(cursorSection as Section & WithParent<HasChildSections>, newSections)
} else {
node.sections.forEach(section => this.visit(section))
}
}

[MARKUP_SECTION_TYPE](node) {
[Type.MARKUP_SECTION](node: MarkupSection) {
this[MARKERABLE](node)
}

[LIST_SECTION_TYPE](node) {
[Type.LIST_SECTION](node: ListSection) {
let hasNext = !!node.next
node.items.forEach(item => this.visit(item))

Expand All @@ -85,19 +98,19 @@ class Visitor {
}
}

[LIST_ITEM_TYPE](node) {
[Type.LIST_ITEM](node: ListItem) {
this[NESTED_MARKERABLE](node)
}

[CARD_TYPE](node) {
[Type.CARD](node: Card) {
this[NON_MARKERABLE](node)
}

[IMAGE_SECTION_TYPE](node) {
[Type.IMAGE_SECTION](node: Image) {
this[NON_MARKERABLE](node)
}

[NON_MARKERABLE](section) {
[NON_MARKERABLE](section: Cloneable<Section>) {
if (this._isNested) {
this._breakNestedAtCursor()
} else if (!this.cursorSection.isBlank) {
Expand All @@ -107,7 +120,7 @@ class Visitor {
this._insertLeafSection(section)
}

[MARKERABLE](section) {
[MARKERABLE](section: Markerable) {
if (this._canMergeSection(section)) {
this._mergeSection(section)
} else if (this._isNested && this._isMarkerable) {
Expand All @@ -116,7 +129,7 @@ class Visitor {
this._breakAtCursor()

// Advance the cursor to the head of the blank list item
let nextPosition = this.cursorSection.next.headPosition()
let nextPosition = this.cursorSection.next!.headPosition()
this.cursorPosition = nextPosition

// Merge this section onto the list item
Expand All @@ -127,22 +140,22 @@ class Visitor {
}
}

[NESTED_MARKERABLE](section) {
[NESTED_MARKERABLE](section: Markerable) {
if (this._canMergeSection(section)) {
this._mergeSection(section)
return
}

section = this._isNested ? section : this._wrapNestedSection(section)
let insertedSection = this._isNested ? section : this._wrapNestedSection(section as ListItem)
this._breakAtCursor()
this._insertLeafSection(section)
this._insertLeafSection(insertedSection)
}

// break out of a nested cursor position
_breakNestedAtCursor() {
assert('Cannot call _breakNestedAtCursor if not nested', this._isNested)

let parent = this.cursorSection.parent
let parent = this.cursorSection.parent!
let cursorAtEndOfList = this.cursorPosition.isEqual(parent.tailPosition())

if (cursorAtEndOfList) {
Expand All @@ -160,6 +173,7 @@ class Visitor {
let list = this.cursorSection.parent,
position = this.cursorPosition,
blank = this.builder.createMarkupSection()

let [pre, post] = this.postEditor._splitListAtPosition(list, position)

let collection = this._post.sections,
Expand All @@ -168,14 +182,14 @@ class Visitor {
return [pre, blank, post]
}

_wrapNestedSection(section) {
let tagName = section.parent.tagName
_wrapNestedSection(section: ListItem) {
let tagName = section.parent!.tagName
let parent = this.builder.createListSection(tagName)
parent.items.append(section.clone())
return parent
}

_mergeSection(section) {
_mergeSection(section: Markerable) {
assert('Can only merge markerable sections', this._isMarkerable && section.isMarkerable)
this._hasInsertedFirstLeafSection = true

Expand Down Expand Up @@ -213,8 +227,8 @@ class Visitor {
this.cursorPosition = pre.tailPosition()
}

_replaceSection(section, newSections) {
assert('Cannot replace section that does not have parent.sections', section.parent && section.parent.sections)
_replaceSection(section: Section, newSections: Section[]) {
assert('Cannot replace section that does not have parent.sections', hasChildSections(section.parent!))
assert('Must pass enumerable to _replaceSection', !!newSections.forEach)

let collection = section.parent.sections
Expand All @@ -228,7 +242,11 @@ class Visitor {
this.cursorPosition = lastSection.tailPosition()
}

_insertSectionBefore(section, reference) {
_insertSectionBefore(section: Section, reference?: Option<Section>) {
assert(
'Cannot insert into section that does not have parent.sections',
hasChildSections(this.cursorSection.parent!)
)
let collection = this.cursorSection.parent.sections
this.postEditor.insertSectionBefore(collection, section, reference)

Expand All @@ -237,15 +255,15 @@ class Visitor {

// Insert a section after the parent section.
// E.g., add a markup section after a list section
_insertSectionAfter(section, parent) {
_insertSectionAfter(section: Section, parent: Section) {
assert('Cannot _insertSectionAfter nested section', !parent.isNested)
let reference = parent.next
let collection = this._post.sections
this.postEditor.insertSectionBefore(collection, section, reference)
this.cursorPosition = section.tailPosition()
}

_insertLeafSection(section) {
_insertLeafSection(section: Cloneable<Section>) {
assert('Can only _insertLeafSection when cursor is at end of section', this.cursorPosition.isTail())

this._hasInsertedFirstLeafSection = true
Expand All @@ -267,12 +285,15 @@ class Visitor {
}

export default class Inserter {
constructor(postEditor, post) {
postEditor: Editor
post: Post

constructor(postEditor: Editor, post: Post) {
this.postEditor = postEditor
this.post = post
}

insert(cursorPosition, newPost) {
insert(cursorPosition: Position, newPost: Post) {
let visitor = new Visitor(this, cursorPosition)
if (!newPost.isBlank) {
visitor.visit(newPost)
Expand Down
13 changes: 13 additions & 0 deletions src/js/models/_cloneable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Section from './_section'

export type Cloneable<T> = T & {
clone(): Cloneable<T>
}

export function expectCloneable<T extends Section>(section: T): Cloneable<T> {
if (!('clone' in section)) {
throw new Error('Expected section to be cloneable')
}

return section as Cloneable<T>
}
22 changes: 12 additions & 10 deletions src/js/models/_markerable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import Section from './_section'
import Marker from './marker'
import { tagNameable } from './_tag-nameable'
import { Type } from './types'
import { Cloneable } from './_cloneable'
import Markuperable from '../utils/markuperable'

type MarkerableType = Type.LIST_ITEM | Type.MARKUP_SECTION

export default abstract class Markerable extends tagNameable(Section) {
export default abstract class Markerable extends tagNameable(Section) implements Cloneable<Markerable> {
type: MarkerableType
markers: LinkedList<Marker>
markers: LinkedList<Markuperable>

constructor(type: MarkerableType, tagName: string, markers: Marker[] = []) {
constructor(type: MarkerableType, tagName: string, markers: Markuperable[] = []) {
super(type)
this.type = type
this.isMarkerable = true
Expand Down Expand Up @@ -70,7 +72,7 @@ export default abstract class Markerable extends tagNameable(Section) {
*
* @return {Number} The offset relative to the start of this section
*/
offsetOfMarker(marker: Marker, markerOffset = 0) {
offsetOfMarker(marker: Markuperable, markerOffset = 0) {
assert(`Cannot get offsetOfMarker for marker that is not child of this`, marker.section === this)

// FIXME it is possible, when we get a cursor position before having finished reparsing,
Expand Down Expand Up @@ -112,7 +114,7 @@ export default abstract class Markerable extends tagNameable(Section) {
return [beforeSection, afterSection]
}

abstract splitAtMarker(marker: Marker, offset: number): [Section, Section]
abstract splitAtMarker(marker: Markuperable, offset: number): [Section, Section]

/**
* Split this section's marker (if any) at the given offset, so that
Expand All @@ -128,7 +130,7 @@ export default abstract class Markerable extends tagNameable(Section) {
let markerOffset
let len = 0
let currentMarker = this.markers.head
let edit: { added: Marker[]; removed: Marker[] } = { added: [], removed: [] }
let edit: { added: Markuperable[]; removed: Markuperable[] } = { added: [], removed: [] }

if (!currentMarker) {
let blankMarker = this.builder.createMarker()
Expand Down Expand Up @@ -181,7 +183,7 @@ export default abstract class Markerable extends tagNameable(Section) {

markerPositionAtOffset(offset: number) {
let currentOffset = 0
let currentMarker: Marker | null = null
let currentMarker: Markuperable | null = null
let remaining = offset
this.markers.detect(marker => {
currentOffset = Math.min(remaining, marker.length)
Expand Down Expand Up @@ -211,7 +213,7 @@ export default abstract class Markerable extends tagNameable(Section) {
markersFor(headOffset: number, tailOffset: number) {
const range = Range.create(this, headOffset, this, tailOffset)

let markers: Marker[] = []
let markers: Markuperable[] = []
this._markersInRange(range, (marker, { markerHead, markerTail, isContained }) => {
const cloned = marker.clone()
if (!isContained) {
Expand All @@ -237,7 +239,7 @@ export default abstract class Markerable extends tagNameable(Section) {
// for each marker that is wholly or partially contained in the range.
_markersInRange(
range: Range,
callback: (marker: Marker, info: { markerHead: number; markerTail: number; isContained: boolean }) => void
callback: (marker: Markuperable, info: { markerHead: number; markerTail: number; isContained: boolean }) => void
) {
const { head, tail } = range
assert(
Expand Down Expand Up @@ -274,7 +276,7 @@ export default abstract class Markerable extends tagNameable(Section) {
// mutates this by appending the other section's (cloned) markers to it
join(otherSection: Markerable) {
let beforeMarker = this.markers.tail
let afterMarker: Marker | null = null
let afterMarker: Markuperable | null = null

otherSection.markers.forEach(m => {
if (!m.isBlank) {
Expand Down
Loading

0 comments on commit b7e109b

Please sign in to comment.