Skip to content

Commit

Permalink
Completes post-inserter migration πŸ’†β€β™€οΈ
Browse files Browse the repository at this point in the history
  • Loading branch information
ZeeJab committed Aug 11, 2020
1 parent 371fbb4 commit 37e87e4
Show file tree
Hide file tree
Showing 11 changed files with 70 additions and 51 deletions.
45 changes: 26 additions & 19 deletions src/js/editor/post/post-inserter.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import assert, { expect } from '../../utils/assert'
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 from '../../models/_section'
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'
Expand Down Expand Up @@ -41,7 +44,7 @@ class Visitor {
this.postEditor.setRange(position)
}

visit(node: Section) {
visit(node: Post | Section) {
let method = node.type
assert(`Cannot visit node of type ${node.type}`, !!this[method])
this[method](node)
Expand Down Expand Up @@ -72,10 +75,11 @@ class Visitor {
}

[Type.POST](node: Post) {
if (this.cursorSection.isBlank && !this._isNested) {
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))
}
Expand Down Expand Up @@ -106,7 +110,7 @@ class Visitor {
this[NON_MARKERABLE](node)
}

[NON_MARKERABLE](section) {
[NON_MARKERABLE](section: Cloneable<Section>) {
if (this._isNested) {
this._breakNestedAtCursor()
} else if (!this.cursorSection.isBlank) {
Expand Down Expand Up @@ -142,9 +146,9 @@ class Visitor {
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
Expand Down Expand Up @@ -178,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 @@ -223,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 @@ -238,25 +242,28 @@ class Visitor {
this.cursorPosition = lastSection.tailPosition()
}

_insertSectionBefore(section, reference) {
console.log(this.cursorSection.parent)
let collection = (this as any).cursorSection.parent.sections
_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)

this.cursorPosition = section.tailPosition()
}

// 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: Markerable) {
_insertLeafSection(section: Cloneable<Section>) {
assert('Can only _insertLeafSection when cursor is at end of section', this.cursorPosition.isTail())

this._hasInsertedFirstLeafSection = true
Expand Down Expand Up @@ -286,7 +293,7 @@ export default class Inserter {
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
16 changes: 13 additions & 3 deletions src/js/models/_cloneable.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
export default interface Cloneable {
clone(): this
}
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>
}
4 changes: 2 additions & 2 deletions src/js/models/_markerable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import Section from './_section'
import Marker from './marker'
import { tagNameable } from './_tag-nameable'
import { Type } from './types'
import Cloneable from './_cloneable'
import { Cloneable } from './_cloneable'

type MarkerableType = Type.LIST_ITEM | Type.MARKUP_SECTION

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

Expand Down
11 changes: 8 additions & 3 deletions src/js/models/_section.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import LinkedItem from '../utils/linked-item'
import assert from '../utils/assert'
import { Option } from '../utils/types'
import Position from '../utils/cursor/position'
import Range from '../utils/cursor/range'
import Marker from './marker'
Expand All @@ -9,6 +10,10 @@ import { isListSection } from './is-list-section'
import PostNodeBuilder from './post-node-builder'
import { Type } from './types'

export interface WithParent<T> {
parent: Option<T>
}

export default class Section extends LinkedItem {
type: Type

Expand All @@ -20,10 +25,10 @@ export default class Section extends LinkedItem {
isLeafSection = true
isCardSection = false

post?: Post | null
renderNode: RenderNode | null = null
post?: Option<Post>
renderNode: Option<RenderNode> = null

parent: Section | null = null
parent: Option<Section> = null
builder!: PostNodeBuilder

constructor(type: Type) {
Expand Down
3 changes: 3 additions & 0 deletions src/js/models/list-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import { normalizeTagName } from '../utils/dom-utils'
import { contains } from '../utils/array-utils'
import Section from './_section'
import { expect } from '../utils/assert'
import { Option } from '../utils/types'
import Marker from './marker'
import ListSection from './list-section'

export const VALID_LIST_ITEM_TAGNAMES = ['li'].map(normalizeTagName)

export default class ListItem extends Markerable {
isListItem = true
isNested = true
section: Section | null = null
parent!: Option<ListSection>

constructor(tagName: string, markers: Marker[] = []) {
super(Type.LIST_ITEM, tagName, markers)
Expand Down
2 changes: 1 addition & 1 deletion src/js/models/list-section.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default class ListSection extends attributable(tagNameable(Section)) impl
return false
}

isValidTagName(normalizedTagName) {
isValidTagName(normalizedTagName: string) {
return contains(VALID_LIST_SECTION_TAGNAMES, normalizedTagName)
}

Expand Down
19 changes: 4 additions & 15 deletions src/js/models/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ListItem, { isListItem } from './list-item'
import MarkupSection from './markup-section'
import RenderNode from './render-node'
import HasChildSections from './_has-child-sections'
import { expectCloneable, Cloneable } from './_cloneable'

type SectionCallback = (section: Section, index: number) => void

Expand All @@ -25,10 +26,10 @@ type SectionCallback = (section: Section, index: number) => void
* When persisting a post, it must first be serialized (loss-lessly) into
* mobiledoc using {@link Editor#serialize}.
*/
export default class Post implements HasChildSections {
export default class Post implements HasChildSections<Cloneable<Section>> {
type = Type.POST
builder!: PostNodeBuilder
sections: LinkedList<Section>
sections: LinkedList<Cloneable<Section>>
renderNode!: RenderNode

constructor() {
Expand Down Expand Up @@ -212,7 +213,7 @@ export default class Post implements HasChildSections {
listParent: ListSection | null = null

this.walkLeafSections(range, section => {
let newSection: Section
let newSection: ListItem | MarkupSection | Cloneable<Section>
if (isMarkerable(section)) {
if (isListItem(section)) {
if (listParent) {
Expand Down Expand Up @@ -250,15 +251,3 @@ export default class Post implements HasChildSections {
return post
}
}

interface Cloneable<T> {
clone(): T
}

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

return section as T & Cloneable<T>
}
6 changes: 5 additions & 1 deletion src/js/utils/assert.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import MobiledocError from './mobiledoc-error'

export default function (message: string, conditional: unknown): asserts conditional {
export default function assert(message: string, conditional: unknown): asserts conditional {
if (!conditional) {
throw new MobiledocError(message)
}
Expand All @@ -12,6 +12,10 @@ export function assertNotNull<T>(message: string, value: T | null): asserts valu
}
}

export function assertType<T>(message: string, _value: any, conditional: boolean): asserts _value is T {
assert(message, conditional)
}

export function expect<T>(value: T | null | undefined, message: string): T {
if (value === null || value === undefined) {
throw new MobiledocError(message)
Expand Down
6 changes: 1 addition & 5 deletions src/js/utils/cursor/position.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import RenderTree from '../../models/render-tree'
import { isTextNode, containsNode, isElementNode } from '../dom-utils'
import { findOffsetInNode } from '../selection-utils'
import { DIRECTION } from '../key'
import assert from '../assert'
import assert, { assertType } from '../assert'
import Range from './range'
import Markerable from '../../models/_markerable'
import Section from '../../models/_section'
Expand Down Expand Up @@ -72,10 +72,6 @@ function assertIsCard(section: any): asserts section is Card {
assert('findOffsetInSection must be called with markerable or card section', section && section.isCardSection)
}

function assertType<T>(message: string, _value: any, conditional: boolean): asserts _value is T {
assert(message, conditional)
}

function isMarkerable(section: Section): section is Markerable {
return section.isMarkerable
}
Expand Down
7 changes: 6 additions & 1 deletion src/js/views/tooltip.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import View from './view'
import { positionElementCenteredBelow, getEventTargetMatchingTag, whenElementIsNotInDOM, Cancelable } from '../utils/element-utils'
import {
positionElementCenteredBelow,
getEventTargetMatchingTag,
whenElementIsNotInDOM,
Cancelable,
} from '../utils/element-utils'
import { editLink } from '../editor/ui'

const SHOW_DELAY = 200
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
"types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
Expand Down

0 comments on commit 37e87e4

Please sign in to comment.