From 0742a687c380cc5b39aeb6eb8707342c14ca69e7 Mon Sep 17 00:00:00 2001 From: Jason Woofenden Date: Thu, 7 Apr 2016 23:33:29 -0400 Subject: [PATCH] improve backspacing accross tag boundaries --- editor.coffee | 151 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 118 insertions(+), 33 deletions(-) diff --git a/editor.coffee b/editor.coffee index 2ad0acc..29cf7e3 100644 --- a/editor.coffee +++ b/editor.coffee @@ -1030,6 +1030,13 @@ class PeachHTML5Editor throw 'bork bork' unless new_cursor? @move_cursor new_cursor # TODO move content past cursor into this new block + find_block_parent: (n) -> + loop + n = n.parent + return null unless n? + return n if is_display_block n.el + return n if n is @tree_parent + return null on_key_backspace: (e) -> return false unless @cursor? if @is_lone_space @cursor.n # false if it's not in a tag @@ -1070,40 +1077,77 @@ class PeachHTML5Editor else @kill_cursor return - else if @cursor.i is 0 # start of non-empty tag - # TODO factor out function for moving children around - # find containing block - containing_block = @cursor.n - loop - containing_block = containing_block.parent - return unless containing_block? - return if containing_block is @tree_parent - break if is_display_block containing_block - # FIXME only continue if first child, else inline merge here - # find contaning_block's previous sibling - prev = null - for n, contaning_block_i in contaning_block.parent.children - break if n is containing_block - prev = n - containing_block.el.parent.removeChild containing_block.el - containing_block.parent.children.splice contaning_block_i, 1 - if prev is null - console.log "inimplemented: backspace at start of nested blocks" # FIXME + else if @cursor.i is 0 # start of text chunk + # FIXME clean this up: use new code for text runs + # FIXME handle backspacing a
even if it's near a inline tag boundary + # determine if cursor is at start of text run (text formatted inline) + block = @find_block_parent @cursor.n + return unless block + at_block_start = true + prev_pos = find_prev_cursor_position @tree, @cursor + unless prev_pos? + # if the cursor can't go back, then there's probably nowhere we can merge into + # TODO consider case of nested blocks. should backspace remove one? + return + prev_pos_block = @find_block_parent prev_pos.n + if prev_pos_block is block + # context: there is text before the cursor within the same block. + # FIXME clean up this hack for looking for
+ cursor_text_pi = @cursor.n.parent.children.indexOf @cursor.n + if cursor_text_pi > 0 + prev_node = @cursor.n.parent.children[cursor_text_pi - 1] + if prev_node.type is 'tag' and prev_node.name is 'br' + @remove_node prev_node + @text_cleanup @cursor.n.parent + @changed() + new_cursor = new_cursor_position n: prev_pos.n, i: prev_pos.i + if new_cursor? + @move_cursor new_cursor + else + @kill_cursor + return + # note: find_prev_cursor_position just crossed a boundary, not a character + # prev_pos is within the same block, try deleting there + @move_cursor prev_pos + # FIXME cleanup: don't call @move_cursor twice if the next line succeeds + return @on_key_backspace() + # context: backspace pressed at start of a display:block + return if block is @tree_parent # top level text + parent = block.parent + parent_i = parent.children.indexOf block + if parent_i is -1 + throw "BUG #98270918347" return - if is_display_block prev - # insert contents of containing_block into prev - ws_cleanup = false - if prev.children.length > 0 - prev_last = prev.children[prev.children.length - 1] - if prev_last.type is 'text' and containing_block.children[0].type is 'text' - prev_last.text = prev_last.el.textContent = prev_last.text + containing_block.children[0].text - ws_cleanup = true - containing_block.children.shift() - for n in containing_block.children - # TODO insert into prev - # TODO adjust whitespace property of prev_last if ws_cleanup + if parent_i is 0 + # no previous sibling to merge into, so instead move contents into parent + dest = parent + before = block else - # TODO insert contents of containing_block into prev.parent after prev + # FIXME prev_sib should be the previous in-flow element + # ie it should skip comments, hidden things, floating things, etc. + prev_sib = parent.children[parent_i - 1] + if is_display_block prev_sib.el + dest = prev_sib + before = null # null means append + else + dest = parent + before = block + if dest is @tree_parent + # don't remove outer-most blocks + return + while block.children.length > 0 + n = block.children[block.children.length - 1] + @move_node n, dest, before + block.children.pop() + before = n + @remove_node block + @text_cleanup dest + @changed() + new_cursor = new_cursor_position n: prev_pos.n, i: prev_pos.i + if new_cursor? + @move_cursor new_cursor + else + @kill_cursor return else # TODO handle case of removing last char @@ -1341,16 +1385,56 @@ class PeachHTML5Editor char + n.text.substr(i) n.el.nodeValue = n.text - # after calling this, you MUST call changed() and adjust_whitespace_style() + # WARNING: after calling this, you MUST call changed() and adjust_whitespace_style() remove_character: (n, i) -> n.text = n.text.substr(0, i) + n.text.substr(i + 1) n.el.nodeValue = n.text + # call this after you insert or remove inline nodes. It will: + # merge consecutive text nodes + # remove empty text nodes + # adjust white-space property + text_cleanup: (n) -> + return + # FIXME implement this + # WARNING: after calling this one or more times, you MUST: + # if it's inline: call @text_cleanup + # call @changed() + remove_node: (n) -> + i = n.parent.children.indexOf n + if i is -1 + throw "BUG #9187112313" + n.el.parentNode.removeChild n.el + n.parent.children.splice i, 1 + return + # remove a node from the tree/dom, insert into new_parent before insert_before?end + # WARNING: after calling this one or more times, you MUST: + # if it's inline: call @text_cleanup + # call @changed() + move_node: (n, new_parent, insert_before = null) -> + i = n.parent.children.indexOf n + if i is -1 + throw "Error: tried to remove node, but it's not in it's parents list of children" + return + if insert_before? + before_i = new_parent.children.indexOf insert_before + if i is -1 + throw "Error: tried to move a node to be before a non-existent node" + @remove_node n + if insert_before? + new_parent.insertBefore n.el, insert_before + new_parent.children.splice before_i, 0, n + else + new_parent.el.appendChild n.el, insert_before + new_parent.children.push n + n.parent = new_parent + return kill_cursor: -> # remove it, forget where it was if @cursor_visible @cursor_el.parentNode.removeChild @cursor_el @cursor_visible = false @cursor = null @annotate null + return move_cursor: (cursor) -> @cursor_ideal_x = cursor.x @cursor = cursor @@ -1367,6 +1451,7 @@ class PeachHTML5Editor @cursor_el.style.height = "#{Math.round height * 0.82}px" @annotate cursor.n @scroll_into_view cursor.y, height + return scroll_into_view: (y, h = 0) -> y += overlay_padding # convert units from @idoc to @wrap2 # very top of document -- 1.7.10.4