From 36faf79b44e5f9e2bba4e90b2949e1c111c1337b Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 10 Mar 2015 11:01:35 -0400 Subject: [PATCH 01/21] Plumbing in preparation for multiarea --- src/controller.coffee | 16 +++++----- src/view.coffee | 68 ++++++++++++++++++++++++------------------- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/controller.coffee b/src/controller.coffee index 2e606efa..b821d2ed 100644 --- a/src/controller.coffee +++ b/src/controller.coffee @@ -1066,16 +1066,15 @@ define ['droplet-helper', if head instanceof model.StartToken acceptLevel = @getAcceptLevel @draggingBlock, head.container unless acceptLevel is helper.FORBID - dropPoint = @view.getViewNodeFor(head.container).dropPoint - - if dropPoint? + for dropPoint, i in @view.getViewNodeFor(head.container).dropPoints @dropPointQuadTree.insert x: dropPoint.x y: dropPoint.y w: 0 h: 0 acceptLevel: acceptLevel - _ice_node: head.container + _droplet_node: head.container + _droplet_index: i head = head.next @@ -1123,7 +1122,7 @@ define ['droplet-helper', mainPoint = @trackerPointToMain(position) - best = null; min = Infinity + best = bestIndex = null; min = Infinity # Check to see if the tree is empty; # if it is, drop on the tree always @@ -1149,14 +1148,15 @@ define ['droplet-helper', distance = mainPoint.from(point) distance.y *= 2; distance = distance.magnitude() if distance < min and mainPoint.from(point).magnitude() < MAX_DROP_DISTANCE and - @view.getViewNodeFor(point._ice_node).highlightArea? - best = point._ice_node + @view.getViewNodeFor(point._droplet_node).highlightAreas[point._droplet_index]? + best = point._droplet_node + bestIndex = point._droplet_index min = distance if best isnt @lastHighlight @clearHighlightCanvas() - if best? then @view.getViewNodeFor(best).highlightArea.draw @highlightCtx + if best? then @view.getViewNodeFor(best).highlightAreas[bestIndex].draw @highlightCtx @lastHighlight = best diff --git a/src/view.coffee b/src/view.coffee index 1f7cc3c1..baf70f4b 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -182,7 +182,8 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model # *Seventh pass variables* # computeDropAreas # each one is a @view.draw.Path (or null) - @dropArea = @highlightArea = null + @dropPoints = [] + @highlightAreas = [] # Versions. The corresponding # Model will keep corresponding version @@ -629,7 +630,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model # # If we cannot drop something on this node # (e.g. a socket that already contains a block), - # set `@dropArea` to null. + # set `@dropPoints` to [] # # Simultaneously, compute `@highlightArea`, which # is the white polygon that lights up @@ -1565,7 +1566,9 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model # ## computeOwnDropArea # By default, we will not have a # drop area (not be droppable). - computeOwnDropArea: -> @dropArea = @highlightArea = null + computeOwnDropArea: -> + @dropPoints = [] + @highlightAreas = [] # ## shouldAddTab # By default, we will ask @@ -1652,49 +1655,51 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model parentViewNode = @view.getViewNodeFor @model.visParent() destinationBounds = parentViewNode.bounds[1] - @dropPoint = new @view.draw.Point destinationBounds.x, destinationBounds.y + @dropPoints[0] = new @view.draw.Point destinationBounds.x, destinationBounds.y lastBoundsLeft = destinationBounds.x lastBoundsRight = destinationBounds.right() else if @carriageArrow is CARRIAGE_ARROW_SIDEALONG parentViewNode = @view.getViewNodeFor @model.visParent() destinationBounds = parentViewNode.bounds[1] - @dropPoint = new @view.draw.Point destinationBounds.x, + @dropPoints[0] = new @view.draw.Point destinationBounds.x, @bounds[@lineLength - 1].bottom() + @view.opts.padding lastBoundsLeft = destinationBounds.x lastBoundsRight = @bounds[@lineLength - 1].right() else - @dropPoint = new @view.draw.Point @bounds[@lineLength - 1].x, @bounds[@lineLength - 1].bottom() + @dropPoints[0] = new @view.draw.Point @bounds[@lineLength - 1].x, @bounds[@lineLength - 1].bottom() lastBoundsLeft = @bounds[@lineLength - 1].x lastBoundsRight = @bounds[@lineLength - 1].right() # Our highlight area is the a rectangle in the same place, # with a height that can be given by a different option. - @highlightArea = new @view.draw.Path() + highlightArea = new @view.draw.Path() highlightAreaPoints = [] - highlightAreaPoints.push new @view.draw.Point lastBoundsLeft, @dropPoint.y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip - highlightAreaPoints.push new @view.draw.Point lastBoundsLeft + @view.opts.bevelClip, @dropPoint.y - @view.opts.highlightAreaHeight / 2 + highlightAreaPoints.push new @view.draw.Point lastBoundsLeft, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip + highlightAreaPoints.push new @view.draw.Point lastBoundsLeft + @view.opts.bevelClip, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 - @addTabReverse highlightAreaPoints, new @view.draw.Point lastBoundsLeft + @view.opts.tabOffset, @dropPoint.y - @view.opts.highlightAreaHeight / 2 + @addTabReverse highlightAreaPoints, new @view.draw.Point lastBoundsLeft + @view.opts.tabOffset, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBoundsRight - @view.opts.bevelClip, @dropPoint.y - @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBoundsRight, @dropPoint.y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip + highlightAreaPoints.push new @view.draw.Point lastBoundsRight - @view.opts.bevelClip, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 + highlightAreaPoints.push new @view.draw.Point lastBoundsRight, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip - highlightAreaPoints.push new @view.draw.Point lastBoundsRight, @dropPoint.y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip - highlightAreaPoints.push new @view.draw.Point lastBoundsRight - @view.opts.bevelClip, @dropPoint.y + @view.opts.highlightAreaHeight / 2 + highlightAreaPoints.push new @view.draw.Point lastBoundsRight, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip + highlightAreaPoints.push new @view.draw.Point lastBoundsRight - @view.opts.bevelClip, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 - @addTab highlightAreaPoints, new @view.draw.Point lastBoundsLeft + @view.opts.tabOffset, @dropPoint.y + @view.opts.highlightAreaHeight / 2 + @addTab highlightAreaPoints, new @view.draw.Point lastBoundsLeft + @view.opts.tabOffset, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBoundsLeft + @view.opts.bevelClip, @dropPoint.y + @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBoundsLeft, @dropPoint.y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip + highlightAreaPoints.push new @view.draw.Point lastBoundsLeft + @view.opts.bevelClip, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 + highlightAreaPoints.push new @view.draw.Point lastBoundsLeft, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip - @highlightArea.push point for point in highlightAreaPoints + highlightArea.push point for point in highlightAreaPoints - @highlightArea.style.lineWidth = 1 - @highlightArea.style.strokeColor = '#ff0' - @highlightArea.style.fillColor = '#ff0' + highlightArea.style.lineWidth = 1 + highlightArea.style.strokeColor = '#ff0' + highlightArea.style.fillColor = '#ff0' + + @highlightAreas[0] = highlightArea # # SocketViewNode class SocketViewNode extends ContainerViewNode @@ -1787,13 +1792,16 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model # things. computeOwnDropArea: -> if @model.start.next.type is 'blockStart' - @dropArea = @highlightArea = null + @dropPoints = [] + @highlightAreas = [] else @dropPoint = @bounds[0].upperLeftCorner() - @highlightArea = @path.clone() - @highlightArea.noclip = true - @highlightArea.style.strokeColor = '#FF0' - @highlightArea.style.lineWidth = @view.opts.padding + highlightArea = @path.clone() + highlightArea.noclip = true + highlightArea.style.strokeColor = '#FF0' + highlightArea.style.lineWidth = @view.opts.padding + + @highlightAreas.push highlightArea # # IndentViewNode class IndentViewNode extends ContainerViewNode @@ -1860,10 +1868,10 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model computeOwnDropArea: -> lastBounds = new @view.draw.NoRectangle() if @model.start.next.type is 'newline' - @dropPoint = @bounds[1].upperLeftCorner() + @dropPoints[0] = @bounds[1].upperLeftCorner() lastBounds.copy @bounds[1] else - @dropPoint = @bounds[0].upperLeftCorner() + @dropPoints[0] = @bounds[0].upperLeftCorner() lastBounds.copy @bounds[0] lastBounds.width = Math.max lastBounds.width, @view.opts.indentDropAreaMinWidth @@ -1912,9 +1920,9 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model # can be dropped at their beginning. computeOwnDropArea: -> if @model.isLassoSegment - return @dropArea = null + return @dropPoints = [] else - @dropPoint = @bounds[0].upperLeftCorner() + @dropPoints[0] = @bounds[0].upperLeftCorner() @highlightArea = new @view.draw.Path() highlightAreaPoints = [] From 132a6cdcb336a055f721fec821a1a2b9a16a3f26 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 10 Mar 2015 11:10:38 -0400 Subject: [PATCH 02/21] Bug fix for indent drop areas --- src/view.coffee | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/view.coffee b/src/view.coffee index baf70f4b..8e9c414f 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -1878,7 +1878,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model # Our highlight area is the a rectangle in the same place, # with a height that can be given by a different option. - @highlightArea = new @view.draw.Path() + highlightArea = new @view.draw.Path() highlightAreaPoints = [] highlightAreaPoints.push new @view.draw.Point lastBounds.x, lastBounds.y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip @@ -1897,11 +1897,13 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model highlightAreaPoints.push new @view.draw.Point lastBounds.x + @view.opts.bevelClip, lastBounds.y + @view.opts.highlightAreaHeight / 2 highlightAreaPoints.push new @view.draw.Point lastBounds.x, lastBounds.y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip - @highlightArea.push point for point in highlightAreaPoints + highlightArea.push point for point in highlightAreaPoints + + highlightArea.style.lineWidth = 1 + highlightArea.style.strokeColor = '#ff0' + highlightArea.style.fillColor = '#ff0' - @highlightArea.style.lineWidth = 1 - @highlightArea.style.strokeColor = '#ff0' - @highlightArea.style.fillColor = '#ff0' + @highlightAreas[0] = highlightArea # # SegmentViewNode # Represents a Segment. Draws little, but @@ -1924,7 +1926,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model else @dropPoints[0] = @bounds[0].upperLeftCorner() - @highlightArea = new @view.draw.Path() + highlightArea = new @view.draw.Path() highlightAreaPoints = [] lastBounds = new @view.draw.NoRectangle() @@ -1947,10 +1949,12 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model highlightAreaPoints.push new @view.draw.Point lastBounds.x + @view.opts.bevelClip, lastBounds.y + @view.opts.highlightAreaHeight / 2 highlightAreaPoints.push new @view.draw.Point lastBounds.x, lastBounds.y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip - @highlightArea.push point for point in highlightAreaPoints + highlightArea.push point for point in highlightAreaPoints + + highlightArea.style.fillColor = '#ff0' + highlightArea.style.strokeColor = '#ff0' - @highlightArea.style.fillColor = '#ff0' - @highlightArea.style.strokeColor = '#ff0' + @highlightAreas[0] = highlightArea return null From b32e63defe41bdeafe4e2592db2d9aa4021e8387 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 10 Mar 2015 11:29:06 -0400 Subject: [PATCH 03/21] Parts of bullets --- src/coffee.coffee | 10 +++++----- src/model.coffee | 4 ++-- src/parser.coffee | 2 +- src/view.coffee | 37 +++++++++++++++++++++++-------------- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 29ad3678..7112889b 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -571,11 +571,10 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( @csBlock node, depth, 100, 'violet', wrappingParen, VALUE_ONLY if node.objects.length > 0 - @csIndentAndMark indentDepth, node.objects, depth + 1 + @csIndent indentDepth, node.objects[0], node.objects[node.objects.length - 1], depth + 1 for object in node.objects - if object.nodeType() is 'Value' and object.base.nodeType() is 'Literal' and - object.properties?.length in [0, undefined] - @csBlock object, depth + 2, 100, 'return', null, VALUE_ONLY + @csBlock object, depth + 2, 100, 'list-element', null, VALUE_ONLY, true + @csSocketAndMark object, depth + 3, 0, indentDepth # ### Return ### # Color RETURN, optional socket @expression. @@ -752,13 +751,14 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( # ## csBlock ## # A general utility function for adding an ICE editor # block around a given node. - csBlock: (node, depth, precedence, color, wrappingParen, classes = []) -> + csBlock: (node, depth, precedence, color, wrappingParen, classes = [], sideBullet = false) -> @addBlock { bounds: @getBounds (wrappingParen ? node) depth: depth precedence: precedence color: color classes: getClassesFor(node).concat classes + sideBullet: sideBullet parenWrapped: wrappingParen? } diff --git a/src/model.coffee b/src/model.coffee index d5c9d763..ac5b66e2 100644 --- a/src/model.coffee +++ b/src/model.coffee @@ -716,7 +716,7 @@ define ['droplet-helper'], (helper) -> serialize: -> "" exports.Block = class Block extends Container - constructor: (@precedence = 0, @color = 'blank', @socketLevel = helper.ANY_DROP, @classes = []) -> + constructor: (@precedence = 0, @color = 'blank', @socketLevel = helper.ANY_DROP, @classes = [], @sideBullet = false) -> @start = new BlockStartToken this @end = new BlockEndToken this @@ -725,7 +725,7 @@ define ['droplet-helper'], (helper) -> super _cloneEmpty: -> - clone = new Block @precedence, @color, @socketLevel, @classes + clone = new Block @precedence, @color, @socketLevel, @classes, @sideBUllet clone.currentlyParenWrapped = @currentlyParenWrapped return clone diff --git a/src/parser.coffee b/src/parser.coffee index f125b9ce..2cfa0de3 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -92,7 +92,7 @@ define ['droplet-helper', 'droplet-model'], (helper, model) -> opts.color, opts.socketLevel, opts.classes, - false + opts.sideBullet @addMarkup block, opts.bounds, opts.depth diff --git a/src/view.coffee b/src/view.coffee index 8e9c414f..686d2511 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -21,6 +21,8 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model CARRIAGE_ARROW_NONE = 2 CARRIAGE_GROW_DOWN = 3 + LEFT_BULLET_WIDTH = 20 + DEFAULT_OPTIONS = padding: 5 indentWidth: 10 @@ -40,20 +42,21 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model shadowBlur: 5 ctx: document.createElement('canvas').getContext('2d') colors: - error: '#ff0000' - return: '#ecec79' - control: '#efcf8f' - value: '#8cec79' - command: '#8fbfef' - - red: '#f2a6a6' - orange: '#efcf8f' - yellow: '#ecec79' - green: '#8cec79' - cyan: '#79ecd9' - blue: '#8fbfef' - violet: '#bfa6f2' - magenta: '#f2a6e5' + 'error': '#ff0000' + 'return': '#ecec79' + 'control': '#efcf8f' + 'value': '#8cec79' + 'command': '#8fbfef' + + 'list-element': '#dfdfdf' + 'red': '#f2a6a6' + 'orange': '#efcf8f' + 'yellow': '#ecec79' + 'green': '#8cec79' + 'cyan': '#79ecd9' + 'blue': '#8fbfef' + 'violet': '#bfa6f2' + 'magenta': '#f2a6e5' YES = -> yes NO = -> no @@ -1628,8 +1631,14 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model size.width = Math.max size.width, @view.opts.tabWidth + @view.opts.tabOffset + if @model.sideBullet + @minDimensions[0].width += LEFT_BULLET_WIDTH + return null + computeBoundingBoxX: (left, top) -> + super left, top, (if @model.sideBullet then LEFT_BULLET_WIDTH else 0) + shouldAddTab: -> if @model.parent? parent = @model.visParent() From c05c4f48cd5f7465f88cf143c2168d13920b46f7 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 10 Mar 2015 11:57:56 -0400 Subject: [PATCH 04/21] Side nubbies for list elements --- src/coffee.coffee | 13 +++++++------ src/model.coffee | 2 +- src/parser.coffee | 1 - src/view.coffee | 18 +++++++++++++----- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 7112889b..e53e85c4 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -11,6 +11,8 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( BLOCK_ONLY = ['block-only'] MOSTLY_BLOCK = ['mostly-block'] MOSTLY_VALUE = ['mostly-value'] + LIST_ITEM = ['list-item'] + LIST_WRAPPER = ['list-wrapper'] VALUE_ONLY = ['value-only'] LVALUE = ['lvalue'] FORBID_ALL = ['forbid-all'] @@ -571,9 +573,9 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( @csBlock node, depth, 100, 'violet', wrappingParen, VALUE_ONLY if node.objects.length > 0 - @csIndent indentDepth, node.objects[0], node.objects[node.objects.length - 1], depth + 1 + @csIndent indentDepth, node.objects[0], node.objects[node.objects.length - 1], depth + 1, LIST_WRAPPER for object in node.objects - @csBlock object, depth + 2, 100, 'list-element', null, VALUE_ONLY, true + @csBlock object, depth + 2, 100, 'list-element', null, LIST_ITEM @csSocketAndMark object, depth + 3, 0, indentDepth # ### Return ### @@ -751,20 +753,19 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( # ## csBlock ## # A general utility function for adding an ICE editor # block around a given node. - csBlock: (node, depth, precedence, color, wrappingParen, classes = [], sideBullet = false) -> + csBlock: (node, depth, precedence, color, wrappingParen, classes = []) -> @addBlock { bounds: @getBounds (wrappingParen ? node) depth: depth precedence: precedence color: color classes: getClassesFor(node).concat classes - sideBullet: sideBullet parenWrapped: wrappingParen? } # Add an indent node and guess # at the indent depth - csIndent: (indentDepth, firstNode, lastNode, depth) -> + csIndent: (indentDepth, firstNode, lastNode, depth, classes = []) -> first = @getBounds(firstNode).start last = @getBounds(lastNode).end @@ -785,8 +786,8 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( end: last } depth: depth - prefix: prefix + classes: classes } return trueDepth diff --git a/src/model.coffee b/src/model.coffee index ac5b66e2..311fdb7f 100644 --- a/src/model.coffee +++ b/src/model.coffee @@ -716,7 +716,7 @@ define ['droplet-helper'], (helper) -> serialize: -> "" exports.Block = class Block extends Container - constructor: (@precedence = 0, @color = 'blank', @socketLevel = helper.ANY_DROP, @classes = [], @sideBullet = false) -> + constructor: (@precedence = 0, @color = 'blank', @socketLevel = helper.ANY_DROP, @classes = []) -> @start = new BlockStartToken this @end = new BlockEndToken this diff --git a/src/parser.coffee b/src/parser.coffee index 2cfa0de3..a85df324 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -92,7 +92,6 @@ define ['droplet-helper', 'droplet-model'], (helper, model) -> opts.color, opts.socketLevel, opts.classes, - opts.sideBullet @addMarkup block, opts.bounds, opts.depth diff --git a/src/view.coffee b/src/view.coffee index 686d2511..5d6024c5 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -15,6 +15,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model MOSTLY_BLOCK = helper.MOSTLY_BLOCK MOSTLY_VALUE = helper.MOSTLY_VALUE VALUE_ONLY = helper.VALUE_ONLY + BULLET_ARROW_WIDTH = 5 CARRIAGE_ARROW_SIDEALONG = 0 CARRIAGE_ARROW_INDENT = 1 @@ -1055,7 +1056,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model # because of glue spacing (the space between lines # that keeps weird-shaped blocks continuous), which # can shift y-coordinates around. - computeBoundingBoxX: (left, line) -> + computeBoundingBoxX: (left, line, offset = 0) -> # Use cached data if possible if @computedVersion is @model.version and left is @bounds[line]?.x and not @changedBoundingBox @@ -1087,7 +1088,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model # placing children down and # adding padding and sizes # to make them not overlap. - childLeft = left + childLeft = left + offset # Get rendering info on each of these children for lineChild, i in @lineChildren[line] @@ -1277,6 +1278,11 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model if @multilineChildrenData[line] is NO_MULTILINE # Draw the left edge of the bounding box. left.push new @view.draw.Point bounds.x, bounds.y + if 'list-item' in @model.classes + console.log 'pushing new point' + left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 - BULLET_ARROW_WIDTH + left.push new @view.draw.Point bounds.x + BULLET_ARROW_WIDTH, (bounds.y + bounds.bottom()) / 2 + left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 + BULLET_ARROW_WIDTH left.push new @view.draw.Point bounds.x, bounds.bottom() # Draw the right edge of the bounding box. @@ -1631,16 +1637,18 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model size.width = Math.max size.width, @view.opts.tabWidth + @view.opts.tabOffset - if @model.sideBullet + if 'list-item' in @model.classes @minDimensions[0].width += LEFT_BULLET_WIDTH return null computeBoundingBoxX: (left, top) -> - super left, top, (if @model.sideBullet then LEFT_BULLET_WIDTH else 0) + super left, top, (if ('list-item' in @model.classes) then LEFT_BULLET_WIDTH else 0) shouldAddTab: -> - if @model.parent? + if 'list-item' in @model.classes + false + else if @model.parent? parent = @model.visParent() parent?.type isnt 'socket' else not ('mostly-value' in @model.classes or From 4a4673eef510dec9c4389eea9f4e85c45d1ff093 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 10 Mar 2015 13:32:02 -0400 Subject: [PATCH 05/21] Bullets but get rid of special block --- src/coffee.coffee | 16 ++++++++-------- src/view.coffee | 29 +++++++++++++++++++---------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index e53e85c4..405bfb09 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -11,8 +11,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( BLOCK_ONLY = ['block-only'] MOSTLY_BLOCK = ['mostly-block'] MOSTLY_VALUE = ['mostly-value'] - LIST_ITEM = ['list-item'] - LIST_WRAPPER = ['list-wrapper'] + LIST_WRAPPER = ['list'] VALUE_ONLY = ['value-only'] LVALUE = ['lvalue'] FORBID_ALL = ['forbid-all'] @@ -573,10 +572,11 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( @csBlock node, depth, 100, 'violet', wrappingParen, VALUE_ONLY if node.objects.length > 0 - @csIndent indentDepth, node.objects[0], node.objects[node.objects.length - 1], depth + 1, LIST_WRAPPER + @csIndentAndMark indentDepth, node.objects, depth + 1, LIST_WRAPPER for object in node.objects - @csBlock object, depth + 2, 100, 'list-element', null, LIST_ITEM - @csSocketAndMark object, depth + 3, 0, indentDepth + if object.nodeType() is 'Value' and object.base.nodeType() is 'Literal' and + object.properties?.length in [0, undefined] + @csBlock object, depth + 2, 100, 'return', null, VALUE_ONLY # ### Return ### # Color RETURN, optional socket @expression. @@ -786,14 +786,14 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( end: last } depth: depth - prefix: prefix classes: classes + prefix: prefix } return trueDepth - csIndentAndMark: (indentDepth, nodes, depth) -> - trueDepth = @csIndent indentDepth, nodes[0], nodes[nodes.length - 1], depth + csIndentAndMark: (indentDepth, nodes, depth, classes = []) -> + trueDepth = @csIndent indentDepth, nodes[0], nodes[nodes.length - 1], depth, classes for node in nodes @mark node, depth + 1, 0, null, trueDepth diff --git a/src/view.coffee b/src/view.coffee index 5d6024c5..aaab1df0 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -22,7 +22,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model CARRIAGE_ARROW_NONE = 2 CARRIAGE_GROW_DOWN = 3 - LEFT_BULLET_WIDTH = 20 + LEFT_BULLET_WIDTH = 15 DEFAULT_OPTIONS = padding: 5 @@ -1278,8 +1278,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model if @multilineChildrenData[line] is NO_MULTILINE # Draw the left edge of the bounding box. left.push new @view.draw.Point bounds.x, bounds.y - if 'list-item' in @model.classes - console.log 'pushing new point' + if @model.parent? and 'list' in @model.parent.classes left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 - BULLET_ARROW_WIDTH left.push new @view.draw.Point bounds.x + BULLET_ARROW_WIDTH, (bounds.y + bounds.bottom()) / 2 left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 + BULLET_ARROW_WIDTH @@ -1311,7 +1310,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model else right.push new @view.draw.Point bounds.right(), bounds.y right.push new @view.draw.Point bounds.right(), multilineBounds.y - if multilineChild.child.type is 'indent' + if multilineChild.child.type is 'indent' and not ('list' in multilineChild.child.classes) @addTab right, new @view.draw.Point multilineBounds.x + @view.opts.tabOffset, multilineBounds.y right.push new @view.draw.Point multilineBounds.x, multilineBounds.y right.push new @view.draw.Point multilineBounds.x, multilineBounds.bottom() @@ -1330,6 +1329,10 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model unless @multilineChildrenData[line - 1] in [MULTILINE_START, MULTILINE_END_START] and multilineChild.child.type is 'indent' right.push new @view.draw.Point multilineBounds.x, bounds.y + if 'list' in multilineChild.child.classes + right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 - BULLET_ARROW_WIDTH + right.push new @view.draw.Point multilineBounds.x + BULLET_ARROW_WIDTH, (multilineBounds.y + multilineBounds.bottom()) / 2 + right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 + BULLET_ARROW_WIDTH right.push new @view.draw.Point multilineBounds.x, bounds.bottom() # Case 4. End of an indent. @@ -1345,9 +1348,13 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model unless @multilineChildrenData[line - 1] in [MULTILINE_START, MULTILINE_END_START] and multilineChild.child.type is 'indent' right.push new @view.draw.Point multilineBounds.x, multilineBounds.y + if 'list' in multilineChild.child.classes + right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 - BULLET_ARROW_WIDTH + right.push new @view.draw.Point multilineBounds.x + BULLET_ARROW_WIDTH, (multilineBounds.y + multilineBounds.bottom()) / 2 + right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 + BULLET_ARROW_WIDTH right.push new @view.draw.Point multilineBounds.x, multilineBounds.bottom() - if multilineChild.child.type is 'indent' + if multilineChild.child.type is 'indent' and not ('list' in multilineChild.child.classes) @addTabReverse right, new @view.draw.Point multilineBounds.x + @view.opts.tabOffset, multilineBounds.bottom() right.push new @view.draw.Point multilineBounds.right(), multilineBounds.bottom() @@ -1492,8 +1499,9 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model # Special case for indents that start with newlines; # don't do any of the same-line-start multiline stuff. if multilineChild.child.type is 'indent' and multilineChild.child.start.next.type is 'newline' - right.push new @view.draw.Point @bounds[line].right(), glueTop + right.push new @view.draw.Point @bounds[line].right(), glueTop + unless ('list' in multilineChild.child.classes) @addTab right, new @view.draw.Point(@bounds[line + 1].x + @view.opts.indentWidth + @view.opts.tabOffset, glueTop), true @@ -1637,16 +1645,17 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model size.width = Math.max size.width, @view.opts.tabWidth + @view.opts.tabOffset - if 'list-item' in @model.classes + if @model.parent? and 'list' in @model.parent.classes @minDimensions[0].width += LEFT_BULLET_WIDTH return null computeBoundingBoxX: (left, top) -> - super left, top, (if ('list-item' in @model.classes) then LEFT_BULLET_WIDTH else 0) + super left, top, + (if @model.parent? and 'list' in @model.parent.classes then LEFT_BULLET_WIDTH else 0) shouldAddTab: -> - if 'list-item' in @model.classes + if @model.parent? and 'list' in @model.parent.classes false else if @model.parent? parent = @model.visParent() @@ -1812,7 +1821,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model @dropPoints = [] @highlightAreas = [] else - @dropPoint = @bounds[0].upperLeftCorner() + @dropPoints[0] = @bounds[0].upperLeftCorner() highlightArea = @path.clone() highlightArea.noclip = true highlightArea.style.strokeColor = '#FF0' From 20a31a67162ced947f9924168b7b9919a8cf3fbc Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 10 Mar 2015 13:42:20 -0400 Subject: [PATCH 06/21] Size change --- src/coffee.coffee | 5 +++++ src/view.coffee | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 405bfb09..5d5be825 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -960,6 +960,11 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( block.type is 'segment' return helper.ENCOURAGE + else if ('mostly-value' in block.classes or + 'value-only' in block.classes) and + 'list' in context.classes + return helper.ENCOURAGE + else if 'mostly-value' in block.classes return helper.DISCOURAGE diff --git a/src/view.coffee b/src/view.coffee index aaab1df0..7b2d4090 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -15,14 +15,14 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model MOSTLY_BLOCK = helper.MOSTLY_BLOCK MOSTLY_VALUE = helper.MOSTLY_VALUE VALUE_ONLY = helper.VALUE_ONLY - BULLET_ARROW_WIDTH = 5 CARRIAGE_ARROW_SIDEALONG = 0 CARRIAGE_ARROW_INDENT = 1 CARRIAGE_ARROW_NONE = 2 CARRIAGE_GROW_DOWN = 3 - LEFT_BULLET_WIDTH = 15 + LEFT_BULLET_WIDTH = 13 + BULLET_ARROW_WIDTH = 6 DEFAULT_OPTIONS = padding: 5 From 4ff74f9df08213bfac692b66903c9a6213b11626 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 10 Mar 2015 13:46:09 -0400 Subject: [PATCH 07/21] Fix highlight area array bug --- src/view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view.coffee b/src/view.coffee index 7b2d4090..a0c862eb 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -1827,7 +1827,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model highlightArea.style.strokeColor = '#FF0' highlightArea.style.lineWidth = @view.opts.padding - @highlightAreas.push highlightArea + @highlightAreas[0] = highlightArea # # IndentViewNode class IndentViewNode extends ContainerViewNode From e6f01ff118ed7938e478ca304279b545afcd61a4 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 10 Mar 2015 16:47:01 -0400 Subject: [PATCH 08/21] Formalize blank blocks in lists --- src/coffee.coffee | 3 ++- src/model.coffee | 2 +- src/parser.coffee | 2 +- src/view.coffee | 22 +++++++++++++++------- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 5d5be825..308a67b8 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -576,7 +576,8 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( for object in node.objects if object.nodeType() is 'Value' and object.base.nodeType() is 'Literal' and object.properties?.length in [0, undefined] - @csBlock object, depth + 2, 100, 'return', null, VALUE_ONLY + @csBlock object, depth + 2, 100, 'blank', null, ANY_DROP + @csSocket object, depth + 3, 100, [] # ### Return ### # Color RETURN, optional socket @expression. diff --git a/src/model.coffee b/src/model.coffee index 311fdb7f..d5c9d763 100644 --- a/src/model.coffee +++ b/src/model.coffee @@ -725,7 +725,7 @@ define ['droplet-helper'], (helper) -> super _cloneEmpty: -> - clone = new Block @precedence, @color, @socketLevel, @classes, @sideBUllet + clone = new Block @precedence, @color, @socketLevel, @classes clone.currentlyParenWrapped = @currentlyParenWrapped return clone diff --git a/src/parser.coffee b/src/parser.coffee index a85df324..97f9f0d4 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -191,7 +191,7 @@ define ['droplet-helper', 'droplet-model'], (helper, model) -> # Construct a handwritten block with the given # text inside constructHandwrittenBlock: (text) -> - block = new model.Block 0, 'blank', helper.ANY_DROP, false + block = new model.Block 0, 'blank', helper.ANY_DROP, ['Value', 'any-drop'] socket = new model.Socket 0, true textToken = new model.TextToken text diff --git a/src/view.coffee b/src/view.coffee index a0c862eb..1218c730 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -43,6 +43,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model shadowBlur: 5 ctx: document.createElement('canvas').getContext('2d') colors: + 'blank': '#dfdfdf' 'error': '#ff0000' 'return': '#ecec79' 'control': '#efcf8f' @@ -1278,7 +1279,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model if @multilineChildrenData[line] is NO_MULTILINE # Draw the left edge of the bounding box. left.push new @view.draw.Point bounds.x, bounds.y - if @model.parent? and 'list' in @model.parent.classes + if @model.parent? and 'list' in @model.visParent().classes left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 - BULLET_ARROW_WIDTH left.push new @view.draw.Point bounds.x + BULLET_ARROW_WIDTH, (bounds.y + bounds.bottom()) / 2 left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 + BULLET_ARROW_WIDTH @@ -1292,6 +1293,10 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model if @multilineChildrenData[line] is MULTILINE_START # Draw the left edge of the bounding box. left.push new @view.draw.Point bounds.x, bounds.y + if @model.parent? and 'list' in @model.visParent().classes + left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 - BULLET_ARROW_WIDTH + left.push new @view.draw.Point bounds.x + BULLET_ARROW_WIDTH, (bounds.y + bounds.bottom()) / 2 + left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 + BULLET_ARROW_WIDTH left.push new @view.draw.Point bounds.x, bounds.bottom() # Find the multiline child that's starting on this line, @@ -1329,7 +1334,8 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model unless @multilineChildrenData[line - 1] in [MULTILINE_START, MULTILINE_END_START] and multilineChild.child.type is 'indent' right.push new @view.draw.Point multilineBounds.x, bounds.y - if 'list' in multilineChild.child.classes + console.log @view.getViewNodeFor(multilineChild.child).lineChildren[line - multilineChild.startLine][0].startLine, line - multilineChild.startLine + if 'list' in multilineChild.child.classes and @view.getViewNodeFor(multilineChild.child).lineChildren[line - multilineChild.startLine][0].startLine is line - multilineChild.startLine right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 - BULLET_ARROW_WIDTH right.push new @view.draw.Point multilineBounds.x + BULLET_ARROW_WIDTH, (multilineBounds.y + multilineBounds.bottom()) / 2 right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 + BULLET_ARROW_WIDTH @@ -1645,17 +1651,19 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model size.width = Math.max size.width, @view.opts.tabWidth + @view.opts.tabOffset - if @model.parent? and 'list' in @model.parent.classes + if @model.parent? and 'list' in @model.visParent().classes @minDimensions[0].width += LEFT_BULLET_WIDTH return null - computeBoundingBoxX: (left, top) -> - super left, top, - (if @model.parent? and 'list' in @model.parent.classes then LEFT_BULLET_WIDTH else 0) + computeBoundingBoxX: (left, line) -> + if line is 0 and @model.parent? and 'list' in @model.visParent().classes + super left, line, LEFT_BULLET_WIDTH + else + super left, line shouldAddTab: -> - if @model.parent? and 'list' in @model.parent.classes + if @model.parent? and 'list' in @model.visParent().classes false else if @model.parent? parent = @model.visParent() From 69a869c9a1fdbde7bab016f1d2237c643ac98c89 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 10 Mar 2015 16:49:46 -0400 Subject: [PATCH 09/21] Drop rule tweaks --- src/coffee.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 308a67b8..006394f2 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -955,10 +955,10 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( return helper.DISCOURAGE else if context.type in ['indent', 'segment'] - if 'block-only' in block.classes or + if ('block-only' in block.classes or 'mostly-block' in block.classes or 'any-drop' in block.classes or - block.type is 'segment' + block.type is 'segment') and not ('list' in context.classes) return helper.ENCOURAGE else if ('mostly-value' in block.classes or From bc92d2d9532dfe72e5114ac3e734555c99b8b944 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 10 Mar 2015 17:32:48 -0400 Subject: [PATCH 10/21] Exclude commas, fix tests --- src/coffee.coffee | 13 +++++++++++-- src/parser.coffee | 2 +- test/coffee/tests.coffee | 6 +++--- test/data/parserSuccess.json | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 006394f2..748696e1 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -577,7 +577,15 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( if object.nodeType() is 'Value' and object.base.nodeType() is 'Literal' and object.properties?.length in [0, undefined] @csBlock object, depth + 2, 100, 'blank', null, ANY_DROP - @csSocket object, depth + 3, 100, [] + + # See if there is a comma after this object + bounds = @getBounds object + bounds.end.column -= /,\s*$/.exec(@lines[bounds.end.line][...bounds.end.column])?[0]?.length ? 0 + @addSocket + bounds: bounds + depth: depth + 3 + precedence: 100 + classes: [] # ### Return ### # Color RETURN, optional socket @expression. @@ -962,7 +970,8 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( return helper.ENCOURAGE else if ('mostly-value' in block.classes or - 'value-only' in block.classes) and + 'value-only' in block.classes or + 'any-drop' in block.classes) and 'list' in context.classes return helper.ENCOURAGE diff --git a/src/parser.coffee b/src/parser.coffee index 97f9f0d4..694d3fac 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -359,7 +359,7 @@ define ['droplet-helper', 'droplet-model'], (helper, model) -> container = new model.Socket attributes.precedence, attributes.handritten, attributes.classes?.split?(' ') when 'indent' - container = new model.Indent attributes.prefix, attributes.classe?.split?(' ') + container = new model.Indent attributes.prefix, attributes.classes?.split?(' ') when 'segment' # Root segment is optional unless stack.length is 0 diff --git a/test/coffee/tests.coffee b/test/coffee/tests.coffee index f8909a53..d2a1fea2 100644 --- a/test/coffee/tests.coffee +++ b/test/coffee/tests.coffee @@ -794,8 +794,8 @@ require ['droplet-helper', 'droplet-model', 'droplet-parser', 'droplet-coffee', strictEqual blockView.carriageArrow, 1, 'Carriage arrow flag is set' - strictEqual blockView.dropPoint.x, view_.opts.indentWidth, 'Drop point is on the left' - strictEqual blockView.dropPoint.y, + strictEqual blockView.dropPoints[0].x, view_.opts.indentWidth, 'Drop point is on the left' + strictEqual blockView.dropPoints[0].y, 1 * view_.opts.textHeight + 4 * view_.opts.padding + 2 * view_.opts.textPadding, 'Drop point is further down' @@ -821,7 +821,7 @@ require ['droplet-helper', 'droplet-model', 'droplet-parser', 'droplet-coffee', strictEqual blockView.carriageArrow, 0, 'Carriage arrow flag is set' - strictEqual blockView.dropPoint.x, view_.opts.indentWidth, 'Drop point is on the left' + strictEqual blockView.dropPoints[0].x, view_.opts.indentWidth, 'Drop point is on the left' indent = block.end.next.container indentView = view_.getViewNodeFor indent diff --git a/test/data/parserSuccess.json b/test/data/parserSuccess.json index bfbca34c..f9c5522f 100644 --- a/test/data/parserSuccess.json +++ b/test/data/parserSuccess.json @@ -97,6 +97,6 @@ { "message": "Array", "str": "[0, 1]", - "expected": "[0, <\/block>1<\/block><\/indent>]<\/block><\/segment>" + "expected": "[0, <\/block>1<\/block><\/indent>]<\/block><\/segment>" } ] From b5cfdd0d8c44b99fc9b7909aea4cfcd8a1af507e Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Wed, 11 Mar 2015 10:12:05 -0400 Subject: [PATCH 11/21] Make reparseable property to deal with commas --- src/controller.coffee | 30 ++++++++++++++++++++---------- src/model.coffee | 15 +++++++++++---- src/parser.coffee | 6 ++++++ src/view.coffee | 1 - 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/controller.coffee b/src/controller.coffee index b821d2ed..bdd45502 100644 --- a/src/controller.coffee +++ b/src/controller.coffee @@ -1215,10 +1215,10 @@ define ['droplet-helper', # Reparse the parent if we are # in a socket # - # TODO "reparseable" property, bubble up # TODO performance on large programs if @lastHighlight.type is 'socket' - @reparseRawReplace @draggingBlock.parent.parent + @reparseRawReplace @draggingBlock.parent.parent.parentWithQuality (x) => + x.type is 'block' and @mode.canReparse x else # If what we've dropped has a socket in it, @@ -1836,7 +1836,9 @@ define ['droplet-helper', shouldRecoverCursor = false cursorPosition = cursorParent = null - if @cursor.hasParent @textFocus.parent + reparseParent = @reparseParent @textFocus + + if @cursor.hasParent reparseParent shouldRecoverCursor = true cursorPosition = @getRecoverableCursorPosition() @@ -1867,7 +1869,7 @@ define ['droplet-helper', shouldPop = true # TODO make 'reparsable' property, bubble up until then - unless @reparseRawReplace @textFocus.parent + unless @reparseRawReplace(reparseParent) # If we can't reparse the parent, abort all reparses @populateSocket @textFocus, originalText @@ -2499,8 +2501,10 @@ define ['droplet-helper', if head.type is 'socketStart' and (head.next.type is 'text' or head.next is head.container.end) # Avoid problems with reparses - if @textFocus? and head.container.hasParent @textFocus.parent - persistentParent = @textFocus.getCommonParent(head.container).parent + if @textFocus? + reparseParent = @reparseParent @textFocus + if @textFocus? and head.container.hasParent reparseParent + persistentParent = reparseParent.getCommonParent(head.container).parent chars = getCharactersTo persistentParent, head.container.start @setTextInputFocus null @@ -2597,7 +2601,13 @@ define ['droplet-helper', return head.container + Editor::reparseParent = (node) -> + node.parent.parentWithQuality((x) => + x.type is 'block' and @mode.canReparse x) + hook 'keydown', 0, (event, state) -> + if @textFocus? + reparseParent = @reparseParent @textFocus if event.which isnt TAB_KEY then return if event.shiftKey if @textFocus? then head = @textFocus.start @@ -2608,8 +2618,8 @@ define ['droplet-helper', head = head.prev if head? - if @textFocus? and head.container.hasParent @textFocus.parent - persistentParent = @textFocus.getCommonParent(head.container).parent + if @textFocus? and head.container.hasParent reparseParent + persistentParent = reparseParent.getCommonParent(head.container).parent chars = getCharactersTo persistentParent, head.container.start @setTextInputFocus null @@ -2629,8 +2639,8 @@ define ['droplet-helper', (head.container.start.next.type is 'text' or head.container.start.next is head.container.end) head = head.next if head? - if @textFocus? and head.container.hasParent @textFocus.parent - persistentParent = @textFocus.getCommonParent(head.container).parent + if @textFocus? and head.container.hasParent reparseParent + persistentParent = reparseParent.getCommonParent(head.container).parent chars = getCharactersTo persistentParent, head.container.start @setTextInputFocus null diff --git a/src/model.coffee b/src/model.coffee index d5c9d763..ebf7a457 100644 --- a/src/model.coffee +++ b/src/model.coffee @@ -497,11 +497,18 @@ define ['droplet-helper'], (helper) -> return @end.nextVisibleToken() in [@parent?.end, @parent?.parent?.end, null] or @end.nextVisibleToken()?.type in ['newline', 'indentStart', 'indentEnd'] + parentWithQuality: (fn) -> + parent = @ + until fn parent + console.log 'going up to', parent + parent = parent.parent + return parent + visParent: -> - head = @parent - while head?.type is 'segment' and head.isLassoSegment - head = head.parent - return head + if @parent? + @parent.parentWithQuality (x) -> not (head?.type is 'segment' and head.isLassoSegment) + else + return null # Line mark mutators addLineMark: (mark) -> diff --git a/src/parser.coffee b/src/parser.coffee index 694d3fac..b33c147f 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -480,4 +480,10 @@ define ['droplet-helper', 'droplet-model'], (helper, model) -> drop: (block, context, pred) -> CustomParser.drop block, context, pred + canReparse: (node) -> + if node.parent? and 'list' in node.parent.classes + return false + else + return true + return exports diff --git a/src/view.coffee b/src/view.coffee index 1218c730..f4ab8f6e 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -1334,7 +1334,6 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model unless @multilineChildrenData[line - 1] in [MULTILINE_START, MULTILINE_END_START] and multilineChild.child.type is 'indent' right.push new @view.draw.Point multilineBounds.x, bounds.y - console.log @view.getViewNodeFor(multilineChild.child).lineChildren[line - multilineChild.startLine][0].startLine, line - multilineChild.startLine if 'list' in multilineChild.child.classes and @view.getViewNodeFor(multilineChild.child).lineChildren[line - multilineChild.startLine][0].startLine is line - multilineChild.startLine right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 - BULLET_ARROW_WIDTH right.push new @view.draw.Point multilineBounds.x + BULLET_ARROW_WIDTH, (multilineBounds.y + multilineBounds.bottom()) / 2 From 4786c2cb0146cc3ea43c14629272985b999bbd2d Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Wed, 11 Mar 2015 10:35:52 -0400 Subject: [PATCH 12/21] Fix parser comma bugs --- src/coffee.coffee | 13 +++++++++++-- src/parser.coffee | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 748696e1..1f320896 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -574,8 +574,9 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( if node.objects.length > 0 @csIndentAndMark indentDepth, node.objects, depth + 1, LIST_WRAPPER for object in node.objects - if object.nodeType() is 'Value' and object.base.nodeType() is 'Literal' and - object.properties?.length in [0, undefined] + subject = @getParenBase(object) + if subject.nodeType() is 'Value' and subject.base.nodeType() is 'Literal' and + subject.properties?.length in [0, undefined] @csBlock object, depth + 2, 100, 'blank', null, ANY_DROP # See if there is a comma after this object @@ -660,6 +661,14 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( else if a.column < b.column then b else a + getParenBase: (node) -> + while node.nodeType() is 'Value' and + node.base?.nodeType() is 'Parens' and + node.properties?.length in [0, undefined] and + node.base.body.expressions[0]?.nodeType() is 'Value' + node = node.base.body.expressions[0] + return node + # ## getBounds ## # Get the boundary locations of a CoffeeScript node, # using CoffeeScript location data and diff --git a/src/parser.coffee b/src/parser.coffee index b33c147f..3828ab61 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -294,7 +294,7 @@ define ['droplet-helper', 'droplet-model'], (helper, model) -> # If the a block is embedded # directly in another block, throw. if stack[stack.length - 1]?.type is 'block' - throw new Error 'Improper parser: block cannot nest immediately inside another block.' + throw new Error "Improper parser: block cannot nest immediately inside another block: line #{i}." when 'socketStart' # A socket is only allowed to be directly inside a block. From 0e232bae0abaaec8d8691495bc49c5b3a89866b5 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Wed, 11 Mar 2015 12:13:22 -0400 Subject: [PATCH 13/21] Fix objects without braces, a little bit --- EMBEDDING.md | 1 - src/coffee.coffee | 37 ++++++++++++++++++++++--------------- src/parser.coffee | 2 +- src/view.coffee | 6 +++--- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/EMBEDDING.md b/EMBEDDING.md index bc0f49d6..4def7843 100644 --- a/EMBEDDING.md +++ b/EMBEDDING.md @@ -151,4 +151,3 @@ Each function is associated with a configuration object. It can specify: - command: true if it's a "command" block that can serve as a top-level statement. - (specify both value and command if it can be both). - color: colorname to set the color of the block. If omitted, a default color is chosen. - diff --git a/src/coffee.coffee b/src/coffee.coffee index 1f320896..cc820fd9 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -12,6 +12,8 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( MOSTLY_BLOCK = ['mostly-block'] MOSTLY_VALUE = ['mostly-value'] LIST_WRAPPER = ['list'] + OBJ_WRAPPER = ['object', 'list'] + BLANK_BLOCK = ['blank-block'] VALUE_ONLY = ['value-only'] LVALUE = ['lvalue'] FORBID_ALL = ['forbid-all'] @@ -500,7 +502,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( if node.value.nodeType() is 'Code' @addCode node.value, depth + 1, indentDepth else - @csSocketAndMark node.value, depth + 1, 0, indentDepth + @csSocketAndMark node.value, depth + 1, -1, indentDepth # ### For ### # Color CONTROL, options sockets @index, @source, @name, @from. @@ -577,7 +579,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( subject = @getParenBase(object) if subject.nodeType() is 'Value' and subject.base.nodeType() is 'Literal' and subject.properties?.length in [0, undefined] - @csBlock object, depth + 2, 100, 'blank', null, ANY_DROP + @csBlock object, depth + 2, 100, 'blank', null, BLANK_BLOCK # See if there is a comma after this object bounds = @getBounds object @@ -639,11 +641,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( # maybe our View architecture is wrong. when 'Obj' @csBlock node, depth, 0, 'violet', wrappingParen, VALUE_ONLY - - for property in node.properties - if property.nodeType() is 'Assign' - @csSocketAndMark property.variable, depth + 1, 0, indentDepth, FORBID_ALL - @csSocketAndMark property.value, depth + 1, 0, indentDepth + @csIndentAndMark indentDepth, node.properties, depth + 1, OBJ_WRAPPER locationsAreIdentical: (a, b) -> @@ -733,6 +731,9 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( bounds.end.line -= 1 bounds.end.column = @lines[bounds.end.line].length + 1 + if node.nodeType() is 'Obj' + bounds.start = @boundMin bounds.start, @getWrappingBounds(node.properties[0], node.properties[node.properties.length - 1]).start + # When we have a 'Value' object, # its base may have some exceptions in it, # in which case we want to pass on to @@ -781,9 +782,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( parenWrapped: wrappingParen? } - # Add an indent node and guess - # at the indent depth - csIndent: (indentDepth, firstNode, lastNode, depth, classes = []) -> + getWrappingBounds: (firstNode, lastNode) -> first = @getBounds(firstNode).start last = @getBounds(lastNode).end @@ -791,6 +790,15 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( first.line -= 1 first.column = @lines[first.line].length + return { + start: first + end: last + } + + # Add an indent node and guess + # at the indent depth + csIndent: (indentDepth, firstNode, lastNode, depth, classes = []) -> + {start: first, end: last} = @getWrappingBounds firstNode, lastNode if first.line isnt last.line trueDepth = @lines[last.line].length - @lines[last.line].trimLeft().length prefix = @lines[last.line][indentDepth...trueDepth] @@ -799,10 +807,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( prefix = ' ' @addIndent { - bounds: { - start: first - end: last - } + bounds: {start: first, end: last} depth: depth classes: classes prefix: prefix @@ -975,12 +980,14 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( if ('block-only' in block.classes or 'mostly-block' in block.classes or 'any-drop' in block.classes or + 'blank-block' in block.classes or block.type is 'segment') and not ('list' in context.classes) return helper.ENCOURAGE else if ('mostly-value' in block.classes or 'value-only' in block.classes or - 'any-drop' in block.classes) and + 'any-drop' in block.classes or + 'blank-block' in block.classes) and 'list' in context.classes return helper.ENCOURAGE diff --git a/src/parser.coffee b/src/parser.coffee index 3828ab61..193578db 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -191,7 +191,7 @@ define ['droplet-helper', 'droplet-model'], (helper, model) -> # Construct a handwritten block with the given # text inside constructHandwrittenBlock: (text) -> - block = new model.Block 0, 'blank', helper.ANY_DROP, ['Value', 'any-drop'] + block = new model.Block 0, 'blank', helper.ANY_DROP, ['Value', 'blank-block'] socket = new model.Socket 0, true textToken = new model.TextToken text diff --git a/src/view.coffee b/src/view.coffee index f4ab8f6e..82664112 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -1279,7 +1279,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model if @multilineChildrenData[line] is NO_MULTILINE # Draw the left edge of the bounding box. left.push new @view.draw.Point bounds.x, bounds.y - if @model.parent? and 'list' in @model.visParent().classes + if @model.parent? and 'list' in @model.visParent().classes and line is 0 left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 - BULLET_ARROW_WIDTH left.push new @view.draw.Point bounds.x + BULLET_ARROW_WIDTH, (bounds.y + bounds.bottom()) / 2 left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 + BULLET_ARROW_WIDTH @@ -1293,7 +1293,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model if @multilineChildrenData[line] is MULTILINE_START # Draw the left edge of the bounding box. left.push new @view.draw.Point bounds.x, bounds.y - if @model.parent? and 'list' in @model.visParent().classes + if @model.parent? and 'list' in @model.visParent().classes and line is 0 left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 - BULLET_ARROW_WIDTH left.push new @view.draw.Point bounds.x + BULLET_ARROW_WIDTH, (bounds.y + bounds.bottom()) / 2 left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 + BULLET_ARROW_WIDTH @@ -1353,7 +1353,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model unless @multilineChildrenData[line - 1] in [MULTILINE_START, MULTILINE_END_START] and multilineChild.child.type is 'indent' right.push new @view.draw.Point multilineBounds.x, multilineBounds.y - if 'list' in multilineChild.child.classes + if 'list' in multilineChild.child.classes and @view.getViewNodeFor(multilineChild.child).lineChildren[line - multilineChild.startLine][0].startLine is line - multilineChild.startLine right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 - BULLET_ARROW_WIDTH right.push new @view.draw.Point multilineBounds.x + BULLET_ARROW_WIDTH, (multilineBounds.y + multilineBounds.bottom()) / 2 right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 + BULLET_ARROW_WIDTH From ca84f26460eac8c3fe09e5183582d1e5c5c7c611 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Thu, 12 Mar 2015 18:20:55 -0400 Subject: [PATCH 14/21] Fix parser tests --- test/data/parserSuccess.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/data/parserSuccess.json b/test/data/parserSuccess.json index f9c5522f..237ae270 100644 --- a/test/data/parserSuccess.json +++ b/test/data/parserSuccess.json @@ -7,7 +7,7 @@ { "message": "Variable assignment", "str": "a = b", - "expected": "a<\/socket> = b<\/socket><\/block><\/segment>" + "expected": "a<\/socket> = b<\/socket><\/block><\/segment>" }, { "message": "If statement, normal form", @@ -77,12 +77,12 @@ { "message": "Object literal, normal form", "str": "foo {\n a: b,\n c: d\n}", - "expected": "foo<\/socket> {\n a<\/socket>: b<\/socket>,\n c<\/socket>: d<\/socket>\n}<\/block><\/socket><\/block><\/segment>" + "expected": "foo {\na: b,\nc: d\n}" }, { "message": "Object literal, no braces or commas", "str": "foo\n a: b\n c: d", - "expected": "foo<\/socket>\n a<\/socket>: b<\/socket>\n c<\/socket>: d<\/socket><\/block><\/socket><\/block><\/segment>" + "expected": "foo\na: b\nc: d" }, { "message": "String interpolation", @@ -97,6 +97,6 @@ { "message": "Array", "str": "[0, 1]", - "expected": "[0, <\/block>1<\/block><\/indent>]<\/block><\/segment>" + "expected": "[0, <\/block>1<\/block><\/indent>]<\/block><\/segment>" } ] From 0b8b4c05e3e1d186bfdd8cab4d8f388a9a820173 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Thu, 12 Mar 2015 18:55:41 -0400 Subject: [PATCH 15/21] Some parser fixes --- src/coffee.coffee | 11 +++++++---- src/parser.coffee | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 33b3b059..89954bc2 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -641,7 +641,8 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( # maybe our View architecture is wrong. when 'Obj' @csBlock node, depth, 0, 'purple', wrappingParen, VALUE_ONLY - @csIndentAndMark indentDepth, node.properties, depth + 1, OBJ_WRAPPER + if node.properties.length > 0 + @csIndentAndMark indentDepth, node.properties, depth + 1, OBJ_WRAPPER locationsAreIdentical: (a, b) -> return a.line is b.line and a.column is b.column @@ -730,7 +731,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( bounds.end.line -= 1 bounds.end.column = @lines[bounds.end.line].length + 1 - if node.nodeType() is 'Obj' + if node.nodeType() is 'Obj' and node.properties.length > 0 bounds.start = @boundMin bounds.start, @getWrappingBounds(node.properties[0], node.properties[node.properties.length - 1]).start # When we have a 'Value' object, @@ -782,12 +783,14 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( } getWrappingBounds: (firstNode, lastNode) -> + if not firstNode? or not lastNode? + console.log 'err on', firstNode, firstNode?.locationData, lastNode, lastNode?.locationData first = @getBounds(firstNode).start last = @getBounds(lastNode).end - if @lines[first.line][...first.column].trim().length is 0 + if first.line > 0 and @lines[first.line][...first.column].trim().length is 0 first.line -= 1 - first.column = @lines[first.line].length + first.column = @lines[first.line].length + 1 return { start: first diff --git a/src/parser.coffee b/src/parser.coffee index 2ee5513a..9dad5897 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -322,7 +322,7 @@ define ['droplet-helper', 'droplet-model'], (helper, model) -> stack.push mark.token.container else if mark.token instanceof model.EndToken unless mark.token.container is stack[stack.length - 1] - throw new Error "Improper parser: #{head.container.type} ended too early." + throw new Error "Improper parser: #{head.container.type} ended too early: line #{i}" stack.pop() # Append the token From 3988805947442ea5b60138f09b376bd9dfdf64e8 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 17 Mar 2015 12:58:10 -0400 Subject: [PATCH 16/21] Fix for tests, not sure if ideal behavior for objects --- src/coffee.coffee | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 89954bc2..43d711b5 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -259,6 +259,24 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( @csSocketAndMark param, depth, 0, indentDepth, FORBID_ALL @mark node.body, depth, 0, null, indentDepth + checkShouldBeOneLine: (node) -> + bounds = @getBounds node + + # See if we want to wrap in a socket + # rather than an indent. + shouldBeOneLine = false + + # Check to see if any parent node is occupying a line + # we are on. If so, we probably want to wrap in + # a socket rather than an indent. + for line in [bounds.start.line..bounds.end.line] + shouldBeOneLine or= @hasLineBeenMarked[line] + + if @lines[bounds.start.line][...bounds.start.column].trim().length isnt 0 + shouldBeOneLine = true + + return shouldBeOneLine + # ## mark ## # Mark a single node. The main recursive function. mark: (node, depth, precedence, wrappingParen, indentDepth) -> @@ -276,18 +294,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( # whether we want to do it on one line or multiple lines. bounds = @getBounds node - # See if we want to wrap in a socket - # rather than an indent. - shouldBeOneLine = false - - # Check to see if any parent node is occupying a line - # we are on. If so, we probably want to wrap in - # a socket rather than an indent. - for line in [bounds.start.line..bounds.end.line] - shouldBeOneLine or= @hasLineBeenMarked[line] - - if @lines[bounds.start.line][...bounds.start.column].trim().length isnt 0 - shouldBeOneLine = true + shouldBeOneLine = @checkShouldBeOneLine node if shouldBeOneLine @csSocket node, depth, 0 @@ -694,6 +701,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( # of the last one if node.expressions.length > 0 bounds.end = @getBounds(node.expressions[node.expressions.length - 1]).end + bounds.start = @boundMin bounds.start, @getBounds(node.expressions[0]).start #If we have no child expressions, make the bounds actually empty. else From 6bbb1df916805583182bfc3fc7667fcc99a10c43 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 17 Mar 2015 13:18:38 -0400 Subject: [PATCH 17/21] Parens handling on object reparse to avoid bad syntax --- src/coffee.coffee | 6 ++++++ src/controller.coffee | 29 ++++++++++++++--------------- src/model.coffee | 1 - src/view.coffee | 8 ++++---- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 43d711b5..29db0b2d 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -1007,6 +1007,12 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( CoffeeScriptParser.parens = (leading, trailing, node, context) -> trailing trailing().replace /\s*,\s*$/, '' + if 'Obj' in node.classes + unless leading().match /.*{.*/ + leading '{' + leading() + unless trailing().match /.*{.*/ + trailing trailing() + '}' + return if context is null or context.type isnt 'socket' or context.precedence < node.precedence while true diff --git a/src/controller.coffee b/src/controller.coffee index a4625166..6bcabed1 100644 --- a/src/controller.coffee +++ b/src/controller.coffee @@ -831,20 +831,7 @@ define ['droplet-helper', # Move the cursor to the end of it. return clone.end - Editor::spliceOut = (node) -> - leading = node.getLeadingText() - if node.start.next is node.end.prev - trailing = null - else - trailing = node.getTrailingText() - - [leading, trailing] = @mode.parens leading, trailing, node.getReader(), null - - node.setLeadingText leading; node.setTrailingText trailing - - node.spliceOut() - - Editor::spliceIn = (node, location) -> + Editor::applyParens = (node, location) -> leading = node.getLeadingText() if node.start.next is node.end.prev trailing = null @@ -858,6 +845,16 @@ define ['droplet-helper', node.setLeadingText leading; node.setTrailingText trailing + return node + + Editor::spliceOut = (node) -> + @applyParens node, null + + node.spliceOut() + + Editor::spliceIn = (node, location) -> + @applyParens node, location + node.spliceIn location # At population-time, we will @@ -1244,6 +1241,7 @@ define ['droplet-helper', if newParse.start.next.container.end is newParse.end.prev and newBlock?.type is 'block' @addMicroUndoOperation new ReparseOperation oldBlock, newBlock + @applyParens newBlock, oldBlock.parent if @cursor.hasParent oldBlock pos = @getRecoverableCursorPosition() @@ -2658,8 +2656,9 @@ define ['droplet-helper', return head.container Editor::reparseParent = (node) -> - node.parent.parentWithQuality((x) => + parent = node.parent.parentWithQuality((x) => x.type is 'block' and @mode.canReparse x) + return parent hook 'keydown', 0, (event, state) -> if @textFocus? diff --git a/src/model.coffee b/src/model.coffee index e70d2194..af7e9850 100644 --- a/src/model.coffee +++ b/src/model.coffee @@ -500,7 +500,6 @@ define ['droplet-helper'], (helper) -> parentWithQuality: (fn) -> parent = @ until fn parent - console.log 'going up to', parent parent = parent.parent return parent diff --git a/src/view.coffee b/src/view.coffee index 84b6f13c..fae04bb7 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -1372,10 +1372,10 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model unless @multilineChildrenData[line - 1] in [MULTILINE_START, MULTILINE_END_START] and multilineChild.child.type is 'indent' right.push new @view.draw.Point multilineBounds.x, multilineBounds.y - if 'list' in multilineChild.child.classes and @view.getViewNodeFor(multilineChild.child).lineChildren[line - multilineChild.startLine][0].startLine is line - multilineChild.startLine - right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 - BULLET_ARROW_WIDTH - right.push new @view.draw.Point multilineBounds.x + BULLET_ARROW_WIDTH, (multilineBounds.y + multilineBounds.bottom()) / 2 - right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 + BULLET_ARROW_WIDTH + if 'list' in multilineChild.child.classes and @view.getViewNodeFor(multilineChild.child).lineChildren[line - multilineChild.startLine][0].startLine is line - multilineChild.startLine + right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 - BULLET_ARROW_WIDTH + right.push new @view.draw.Point multilineBounds.x + BULLET_ARROW_WIDTH, (multilineBounds.y + multilineBounds.bottom()) / 2 + right.push new @view.draw.Point multilineBounds.x, (multilineBounds.y + multilineBounds.bottom()) / 2 + BULLET_ARROW_WIDTH right.push new @view.draw.Point multilineBounds.x, multilineBounds.bottom() if multilineChild.child.type is 'indent' and not ('list' in multilineChild.child.classes) From 315b9bf21f575e75954044f94760723b6b4185a0 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 17 Mar 2015 13:27:07 -0400 Subject: [PATCH 18/21] Reparsing bug fixes --- src/coffee.coffee | 4 ++-- src/controller.coffee | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 29db0b2d..56010b93 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -1,4 +1,4 @@ -# # ICE Editor CoffeeScript mode +## ICE Editor CoffeeScript mode # # Copyright (c) Anthony Bau # MIT License @@ -1010,7 +1010,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( if 'Obj' in node.classes unless leading().match /.*{.*/ leading '{' + leading() - unless trailing().match /.*{.*/ + unless trailing().match /.*}.*/ trailing trailing() + '}' return if context is null or context.type isnt 'socket' or diff --git a/src/controller.coffee b/src/controller.coffee index 6bcabed1..5b4f1085 100644 --- a/src/controller.coffee +++ b/src/controller.coffee @@ -838,10 +838,14 @@ define ['droplet-helper', else trailing = node.getTrailingText() - container = location.container ? location.visParent() + if location? + container = location.container ? location.visParent() + if container.type is 'block' + container = container.visParent() + else + container = null - [leading, trailing] = @mode.parens leading, trailing, node.getReader(), - (if container.type is 'block' then container.visParent() else container)?.getReader?() ? null + [leading, trailing] = @mode.parens leading, trailing, node.getReader(), container?.getReader?() ? null node.setLeadingText leading; node.setTrailingText trailing From 8f29d75b971cea180aebda86083e0b3384795bb5 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 17 Mar 2015 15:16:59 -0400 Subject: [PATCH 19/21] Fix typo with lasso + lists --- src/controller.coffee | 2 +- src/model.coffee | 2 +- src/view.coffee | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controller.coffee b/src/controller.coffee index 5b4f1085..3786a6ba 100644 --- a/src/controller.coffee +++ b/src/controller.coffee @@ -840,7 +840,7 @@ define ['droplet-helper', if location? container = location.container ? location.visParent() - if container.type is 'block' + if container? and container.type is 'block' container = container.visParent() else container = null diff --git a/src/model.coffee b/src/model.coffee index af7e9850..9b4d4caf 100644 --- a/src/model.coffee +++ b/src/model.coffee @@ -505,7 +505,7 @@ define ['droplet-helper'], (helper) -> visParent: -> if @parent? - @parent.parentWithQuality (x) -> not (head?.type is 'segment' and head.isLassoSegment) + @parent.parentWithQuality (x) -> not (x?.type is 'segment' and x.isLassoSegment) else return null diff --git a/src/view.coffee b/src/view.coffee index fae04bb7..5738def1 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -1682,7 +1682,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model shouldAddTab: -> if @model.parent? and 'list' in @model.visParent().classes - false + return false else if @model.parent? parent = @model.visParent() parent?.type isnt 'socket' From aff87d4156c5ea6a3c86261a4630b2c277f9e995 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 17 Mar 2015 16:26:51 -0400 Subject: [PATCH 20/21] Single-line invisible blocks --- src/coffee.coffee | 2 + src/controller.coffee | 19 ++++- src/model.coffee | 21 +++++ src/view.coffee | 186 +++++++++++++++++++++++++----------------- 4 files changed, 150 insertions(+), 78 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index 56010b93..cc0222a1 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -1007,6 +1007,8 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( CoffeeScriptParser.parens = (leading, trailing, node, context) -> trailing trailing().replace /\s*,\s*$/, '' + if context? and 'list' in context.classes + trailing trailing() + ',' if 'Obj' in node.classes unless leading().match /.*{.*/ leading '{' + leading() diff --git a/src/controller.coffee b/src/controller.coffee index 3786a6ba..30303b89 100644 --- a/src/controller.coffee +++ b/src/controller.coffee @@ -856,6 +856,14 @@ define ['droplet-helper', node.spliceOut() + Editor::spliceInRaw = (node, location) -> + @applyParens node, location + + last = location.next + + location.append node.start + node.end.append last + Editor::spliceIn = (node, location) -> @applyParens node, location @@ -1200,10 +1208,17 @@ define ['droplet-helper', switch @lastHighlight.type when 'indent', 'socket' @addMicroUndoOperation new DropOperation @draggingBlock, @lastHighlight.start - @spliceIn @draggingBlock, @lastHighlight.start #MUTATION + if @lastHighlight.type is 'indent' and 'list' in @lastHighlight.classes and + @lastHighlight.lineLength() is 0 + @spliceInRaw @draggingBlock, @lastHighlight.start #MUTATION + else + @spliceIn @draggingBlock, @lastHighlight.start #MUTATION when 'block' @addMicroUndoOperation new DropOperation @draggingBlock, @lastHighlight.end - @spliceIn @draggingBlock, @lastHighlight.end #MUTATION + if @lastHighlight.inList() and @lastHighlight.parent.lineLength() is 0 + @spliceInRaw @draggingBlock, @lastHighlight.end + else + @spliceIn @draggingBlock, @lastHighlight.end #MUTATION else if @lastHighlight is @tree @addMicroUndoOperation new DropOperation @draggingBlock, @tree.start diff --git a/src/model.coffee b/src/model.coffee index 9b4d4caf..32b3ecf5 100644 --- a/src/model.coffee +++ b/src/model.coffee @@ -114,6 +114,15 @@ define ['droplet-helper'], (helper) -> return head is parent + inList: -> + @parent? and 'list' in @visParent().classes + + inMultiLineList: -> + @inList() and @visParent().lineLength() > 0 + + inSingleLineList: -> + @inList() and @visParent().lineLength() is 0 + getCommonParent: (other) -> head = @ until other.hasParent head @@ -201,6 +210,18 @@ define ['droplet-helper'], (helper) -> else unless value.length is 0 @end.insertBefore new TextToken value + each: (f) -> + head = @start.next + until head is @end + f head + head = head.next + return true + + lineLength: -> + length = 0 + @each (el) -> length++ if el.type is 'newline' + return length + # ## clone ## # Clone this container, with all the token inside, # but with no linked-list pointers in common. diff --git a/src/view.coffee b/src/view.coffee index 5738def1..d795d0b6 100644 --- a/src/view.coffee +++ b/src/view.coffee @@ -1298,7 +1298,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model if @multilineChildrenData[line] is NO_MULTILINE # Draw the left edge of the bounding box. left.push new @view.draw.Point bounds.x, bounds.y - if @model.parent? and 'list' in @model.visParent().classes and line is 0 + if @model.inMultiLineList() left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 - BULLET_ARROW_WIDTH left.push new @view.draw.Point bounds.x + BULLET_ARROW_WIDTH, (bounds.y + bounds.bottom()) / 2 left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 + BULLET_ARROW_WIDTH @@ -1312,7 +1312,7 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model if @multilineChildrenData[line] is MULTILINE_START # Draw the left edge of the bounding box. left.push new @view.draw.Point bounds.x, bounds.y - if @model.parent? and 'list' in @model.visParent().classes and line is 0 + if @model.inMultiLineList() left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 - BULLET_ARROW_WIDTH left.push new @view.draw.Point bounds.x + BULLET_ARROW_WIDTH, (bounds.y + bounds.bottom()) / 2 left.push new @view.draw.Point bounds.x, (bounds.y + bounds.bottom()) / 2 + BULLET_ARROW_WIDTH @@ -1669,19 +1669,19 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model size.width = Math.max size.width, @view.opts.tabWidth + @view.opts.tabOffset - if @model.parent? and 'list' in @model.visParent().classes + if @model.inMultiLineList() @minDimensions[0].width += LEFT_BULLET_WIDTH return null computeBoundingBoxX: (left, line) -> - if line is 0 and @model.parent? and 'list' in @model.visParent().classes + if line is 0 and @model.inMultiLineList() super left, line, LEFT_BULLET_WIDTH else super left, line shouldAddTab: -> - if @model.parent? and 'list' in @model.visParent().classes + if @model.inList() return false else if @model.parent? parent = @model.visParent() @@ -1692,67 +1692,84 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model computeOwnPath: -> super - @path.style.fillColor = @view.getColor @model.color - @path.style.strokeColor = '#888' - - @path.bevel = true + if 'blank-block' in @model.classes and @model.inSingleLineList() + @path.style.fillColor = 'transparent' + @path.style.strokeColor = 'transparent' + @path.bevel = false + else + @path.style.fillColor = @view.getColor @model.color + @path.style.strokeColor = '#888' + @path.bevel = true return @path computeOwnDropArea: -> - # Our drop area is a rectangle of - # height dropAreaHeight and a width - # equal to our last line width, - # positioned at the bottom of our last line. - if @carriageArrow is CARRIAGE_ARROW_INDENT - parentViewNode = @view.getViewNodeFor @model.visParent() - destinationBounds = parentViewNode.bounds[1] - - @dropPoints[0] = new @view.draw.Point destinationBounds.x, destinationBounds.y - lastBoundsLeft = destinationBounds.x - lastBoundsRight = destinationBounds.right() - else if @carriageArrow is CARRIAGE_ARROW_SIDEALONG - parentViewNode = @view.getViewNodeFor @model.visParent() - destinationBounds = parentViewNode.bounds[1] - - @dropPoints[0] = new @view.draw.Point destinationBounds.x, - @bounds[@lineLength - 1].bottom() + @view.opts.padding - lastBoundsLeft = destinationBounds.x - lastBoundsRight = @bounds[@lineLength - 1].right() + if @model.inSingleLineList() + lastBounds = @bounds[@bounds.length - 1] + @dropPoints[0] = new @view.draw.Point lastBounds.right(), lastBounds.y + lastBounds.height / 2 + + @highlightAreas[0] = highlightArea = new @view.draw.Path() + + highlightArea.push new @view.draw.Point lastBounds.right() - 5, lastBounds.y + highlightArea.push new @view.draw.Point lastBounds.right() + 5, lastBounds.y + highlightArea.push new @view.draw.Point lastBounds.right() + 5, lastBounds.bottom() + highlightArea.push new @view.draw.Point lastBounds.right() - 5, lastBounds.bottom() + + highlightArea.style.lineWidth = 1 + highlightArea.style.strokeColor = '#ff0' + highlightArea.style.fillColor = '#ff0' else - @dropPoints[0] = new @view.draw.Point @bounds[@lineLength - 1].x, @bounds[@lineLength - 1].bottom() - lastBoundsLeft = @bounds[@lineLength - 1].x - lastBoundsRight = @bounds[@lineLength - 1].right() + # Our drop area is a rectangle of + # height dropAreaHeight and a width + # equal to our last line width, + # positioned at the bottom of our last line. + if @carriageArrow is CARRIAGE_ARROW_INDENT + parentViewNode = @view.getViewNodeFor @model.visParent() + destinationBounds = parentViewNode.bounds[1] + + @dropPoints[0] = new @view.draw.Point destinationBounds.x, destinationBounds.y + lastBoundsLeft = destinationBounds.x + lastBoundsRight = destinationBounds.right() + else if @carriageArrow is CARRIAGE_ARROW_SIDEALONG + parentViewNode = @view.getViewNodeFor @model.visParent() + destinationBounds = parentViewNode.bounds[1] - # Our highlight area is the a rectangle in the same place, - # with a height that can be given by a different option. + @dropPoints[0] = new @view.draw.Point destinationBounds.x, + @bounds[@lineLength - 1].bottom() + @view.opts.padding + lastBoundsLeft = destinationBounds.x + lastBoundsRight = @bounds[@lineLength - 1].right() + else + @dropPoints[0] = new @view.draw.Point @bounds[@lineLength - 1].x, @bounds[@lineLength - 1].bottom() + lastBoundsLeft = @bounds[@lineLength - 1].x + lastBoundsRight = @bounds[@lineLength - 1].right() - highlightArea = new @view.draw.Path() - highlightAreaPoints = [] + # Our highlight area is the a rectangle in the same place, + # with a height that can be given by a different option. - highlightAreaPoints.push new @view.draw.Point lastBoundsLeft, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip - highlightAreaPoints.push new @view.draw.Point lastBoundsLeft + @view.opts.bevelClip, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 + highlightArea = new @view.draw.Path() - @addTabReverse highlightAreaPoints, new @view.draw.Point lastBoundsLeft + @view.opts.tabOffset, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 + highlightArea.push new @view.draw.Point lastBoundsLeft, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip + highlightArea.push new @view.draw.Point lastBoundsLeft + @view.opts.bevelClip, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBoundsRight - @view.opts.bevelClip, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBoundsRight, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip + @addTabReverse highlightArea, new @view.draw.Point lastBoundsLeft + @view.opts.tabOffset, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBoundsRight, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip - highlightAreaPoints.push new @view.draw.Point lastBoundsRight - @view.opts.bevelClip, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 + highlightArea.push new @view.draw.Point lastBoundsRight - @view.opts.bevelClip, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 + highlightArea.push new @view.draw.Point lastBoundsRight, @dropPoints[0].y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip - @addTab highlightAreaPoints, new @view.draw.Point lastBoundsLeft + @view.opts.tabOffset, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 + highlightArea.push new @view.draw.Point lastBoundsRight, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip + highlightArea.push new @view.draw.Point lastBoundsRight - @view.opts.bevelClip, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBoundsLeft + @view.opts.bevelClip, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBoundsLeft, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip + @addTab highlightArea, new @view.draw.Point lastBoundsLeft + @view.opts.tabOffset, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 - highlightArea.push point for point in highlightAreaPoints + highlightArea.push new @view.draw.Point lastBoundsLeft + @view.opts.bevelClip, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 + highlightArea.push new @view.draw.Point lastBoundsLeft, @dropPoints[0].y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip - highlightArea.style.lineWidth = 1 - highlightArea.style.strokeColor = '#ff0' - highlightArea.style.fillColor = '#ff0' + highlightArea.style.lineWidth = 1 + highlightArea.style.strokeColor = '#ff0' + highlightArea.style.fillColor = '#ff0' + + @highlightAreas[0] = highlightArea - @highlightAreas[0] = highlightArea # # SocketViewNode class SocketViewNode extends ContainerViewNode @@ -1937,44 +1954,59 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model # equal to our first line width, # positioned at the top of our firs tline computeOwnDropArea: -> - lastBounds = new @view.draw.NoRectangle() - if @model.start.next.type is 'newline' - @dropPoints[0] = @bounds[1].upperLeftCorner() - lastBounds.copy @bounds[1] + if @model.classes and @lineLength is 1 + firstBounds = @bounds[0] + @dropPoints[0] = new @view.draw.Point firstBounds.x, firstBounds.y + firstBounds.height / 2 + + @highlightAreas[0] = highlightArea = new @view.draw.Path() + + highlightArea.push new @view.draw.Point firstBounds.x - 5, firstBounds.y + highlightArea.push new @view.draw.Point firstBounds.x + 5, firstBounds.y + highlightArea.push new @view.draw.Point firstBounds.x + 5, firstBounds.bottom() + highlightArea.push new @view.draw.Point firstBounds.x - 5, firstBounds.bottom() + + highlightArea.style.lineWidth = 1 + highlightArea.style.strokeColor = '#ff0' + highlightArea.style.fillColor = '#ff0' else - @dropPoints[0] = @bounds[0].upperLeftCorner() - lastBounds.copy @bounds[0] - lastBounds.width = Math.max lastBounds.width, @view.opts.indentDropAreaMinWidth + lastBounds = new @view.draw.NoRectangle() + if @model.start.next.type is 'newline' + @dropPoints[0] = @bounds[1].upperLeftCorner() + lastBounds.copy @bounds[1] + else + @dropPoints[0] = @bounds[0].upperLeftCorner() + lastBounds.copy @bounds[0] + lastBounds.width = Math.max lastBounds.width, @view.opts.indentDropAreaMinWidth - # Our highlight area is the a rectangle in the same place, - # with a height that can be given by a different option. + # Our highlight area is the a rectangle in the same place, + # with a height that can be given by a different option. - highlightArea = new @view.draw.Path() - highlightAreaPoints = [] + highlightArea = new @view.draw.Path() + highlightAreaPoints = [] - highlightAreaPoints.push new @view.draw.Point lastBounds.x, lastBounds.y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip - highlightAreaPoints.push new @view.draw.Point lastBounds.x + @view.opts.bevelClip, lastBounds.y - @view.opts.highlightAreaHeight / 2 + highlightAreaPoints.push new @view.draw.Point lastBounds.x, lastBounds.y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip + highlightAreaPoints.push new @view.draw.Point lastBounds.x + @view.opts.bevelClip, lastBounds.y - @view.opts.highlightAreaHeight / 2 - @addTabReverse highlightAreaPoints, new @view.draw.Point lastBounds.x + @view.opts.tabOffset, lastBounds.y - @view.opts.highlightAreaHeight / 2 + @addTabReverse highlightAreaPoints, new @view.draw.Point lastBounds.x + @view.opts.tabOffset, lastBounds.y - @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBounds.right() - @view.opts.bevelClip, lastBounds.y - @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBounds.right(), lastBounds.y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip + highlightAreaPoints.push new @view.draw.Point lastBounds.right() - @view.opts.bevelClip, lastBounds.y - @view.opts.highlightAreaHeight / 2 + highlightAreaPoints.push new @view.draw.Point lastBounds.right(), lastBounds.y - @view.opts.highlightAreaHeight / 2 + @view.opts.bevelClip - highlightAreaPoints.push new @view.draw.Point lastBounds.right(), lastBounds.y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip - highlightAreaPoints.push new @view.draw.Point lastBounds.right() - @view.opts.bevelClip, lastBounds.y + @view.opts.highlightAreaHeight / 2 + highlightAreaPoints.push new @view.draw.Point lastBounds.right(), lastBounds.y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip + highlightAreaPoints.push new @view.draw.Point lastBounds.right() - @view.opts.bevelClip, lastBounds.y + @view.opts.highlightAreaHeight / 2 - @addTab highlightAreaPoints, new @view.draw.Point lastBounds.x + @view.opts.tabOffset, lastBounds.y + @view.opts.highlightAreaHeight / 2 + @addTab highlightAreaPoints, new @view.draw.Point lastBounds.x + @view.opts.tabOffset, lastBounds.y + @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBounds.x + @view.opts.bevelClip, lastBounds.y + @view.opts.highlightAreaHeight / 2 - highlightAreaPoints.push new @view.draw.Point lastBounds.x, lastBounds.y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip + highlightAreaPoints.push new @view.draw.Point lastBounds.x + @view.opts.bevelClip, lastBounds.y + @view.opts.highlightAreaHeight / 2 + highlightAreaPoints.push new @view.draw.Point lastBounds.x, lastBounds.y + @view.opts.highlightAreaHeight / 2 - @view.opts.bevelClip - highlightArea.push point for point in highlightAreaPoints + highlightArea.push point for point in highlightAreaPoints - highlightArea.style.lineWidth = 1 - highlightArea.style.strokeColor = '#ff0' - highlightArea.style.fillColor = '#ff0' + highlightArea.style.lineWidth = 1 + highlightArea.style.strokeColor = '#ff0' + highlightArea.style.fillColor = '#ff0' - @highlightAreas[0] = highlightArea + @highlightAreas[0] = highlightArea # # SegmentViewNode # Represents a Segment. Draws little, but @@ -2159,6 +2191,8 @@ define ['droplet-helper', 'droplet-draw', 'droplet-model'], (helper, draw, model return '#' + (twoDigitHex(k) for k in rgb).join '' avgColor = (a, factor, b) -> + if a is 'transparent' or b is 'transparent' + return 'transparent' a = toRGB a b = toRGB b From a5ee900907858e2e024acd6cfa35c5ddfdcde544 Mon Sep 17 00:00:00 2001 From: Anthony Bau Date: Tue, 24 Mar 2015 00:15:49 -0400 Subject: [PATCH 21/21] Some rudimentary plumbing for commas in arrays, maybe not right architecture --- src/coffee.coffee | 29 +++++++++++-------- src/controller.coffee | 28 ++++++++++++------- src/javascript.coffee | 16 +++++------ src/model.coffee | 65 +++++++++++++++++++++++++++++++++++-------- src/parser.coffee | 36 ++++++------------------ 5 files changed, 105 insertions(+), 69 deletions(-) diff --git a/src/coffee.coffee b/src/coffee.coffee index cc0222a1..1b3ef43a 100644 --- a/src/coffee.coffee +++ b/src/coffee.coffee @@ -1005,27 +1005,32 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], ( return helper.DISCOURAGE - CoffeeScriptParser.parens = (leading, trailing, node, context) -> - trailing trailing().replace /\s*,\s*$/, '' + CoffeeScriptParser.parens = (prev, node, next, context) -> if context? and 'list' in context.classes - trailing trailing() + ',' + node.trailing node.trailing().replace /\s*,\s*$/, '' + if next? + node.trailing node.trailing() + ',' + if prev? + prev.trailing prev.trailing().replace /\s*,\s*$/, '' + prev.trailing prev.trailing() + ',' + if 'Obj' in node.classes - unless leading().match /.*{.*/ - leading '{' + leading() - unless trailing().match /.*}.*/ - trailing trailing() + '}' + unless node.leading().match /.*{.*/ + node.leading '{' + node.leading() + unless node.trailing().match /.*}.*/ + node.trailing node.trailing() + '}' return if context is null or context.type isnt 'socket' or context.precedence < node.precedence while true - if leading().match(/^\s*\(/)? and trailing().match(/\)\s*/)? - leading leading().replace(/^\s*\(\s*/, '') - trailing trailing().replace(/\s*\)\s*$/, '') + if node.leading().match(/^\s*\(/)? and node.trailing().match(/\)\s*/)? + node.leading node.leading().replace(/^\s*\(\s*/, '') + node.trailing node.trailing().replace(/\s*\)\s*$/, '') else break else - leading '(' + leading() - trailing trailing() + ')' + node.leading '(' + node.leading() + node.trailing node.trailing() + ')' return diff --git a/src/controller.coffee b/src/controller.coffee index 30303b89..72f18def 100644 --- a/src/controller.coffee +++ b/src/controller.coffee @@ -832,22 +832,30 @@ define ['droplet-helper', return clone.end Editor::applyParens = (node, location) -> - leading = node.getLeadingText() - if node.start.next is node.end.prev - trailing = null - else - trailing = node.getTrailingText() - if location? container = location.container ? location.visParent() if container? and container.type is 'block' container = container.visParent() - else - container = null - [leading, trailing] = @mode.parens leading, trailing, node.getReader(), container?.getReader?() ? null + head = location + until not head? or head is container.start or head.type is 'blockEnd' + head = head.prev + if not head? or head is container.start + prev = null + else + prev = head.container + + head = location + until not head? or head is container.end or head.type is 'blockStart' + head = head.next + if not head? or head is container.end + next = null + else + next = head.container + else + prev = next = container = null - node.setLeadingText leading; node.setTrailingText trailing + @mode.parens prev?.getParenModifier() ? null, node.getParenModifier(), next?.getParenModifier?() ? null, container?.getReader?() ? null return node diff --git a/src/javascript.coffee b/src/javascript.coffee index c6e8e131..0e616170 100644 --- a/src/javascript.coffee +++ b/src/javascript.coffee @@ -462,26 +462,26 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'acorn'], (helper, @mark indentDepth, node, depth + 1, bounds - JavaScriptParser.parens = (leading, trailing, node, context) -> + JavaScriptParser.parens = (prev, node, next, context) -> if context?.type is 'socket' or (not context? and 'mostly-value' in node.classes or 'value-only' in node.classes) or 'ends-with-brace' in node.classes or node.type is 'segment' - trailing trailing().replace(/;?\s*$/, '') + node.trailing node.trailing().replace(/;?\s*$/, '') else - trailing trailing().replace(/;?\s*$/, ';') + node.trailing node.trailing().replace(/;?\s*$/, ';') if context is null or context.type isnt 'socket' or context.precedence > node.precedence while true - if leading().match(/^\s*\(/)? and trailing().match(/\)\s*/)? - leading leading().replace(/^\s*\(\s*/, '') - trailing trailing().replace(/\s*\)\s*$/, '') + if node.leading().match(/^\s*\(/)? and node.trailing().match(/\)\s*/)? + node.leading node.leading().replace(/^\s*\(\s*/, '') + node.trailing node.trailing().replace(/\s*\)\s*$/, '') else break else - leading '(' + leading() - trailing trailing() + ')' + node.leading '(' + node.leading() + node.trailing node.trailing() + ')' JavaScriptParser.drop = (block, context, pred) -> if context.type is 'socket' diff --git a/src/model.coffee b/src/model.coffee index 32b3ecf5..6f383f1c 100644 --- a/src/model.coffee +++ b/src/model.coffee @@ -14,6 +14,35 @@ define ['droplet-helper'], (helper) -> _id = 0 + # FOR TESTING ONLY + applyParens = (mode, node, location) -> + if location? + container = location.container ? location.visParent() + if container? and container.type is 'block' + container = container.visParent() + + head = location + until head is container.start or head.type is 'blockEnd' + head = head.prev + if head is container.start + prev = null + else + prev = head.container + + head = location + until head is container.end or head.type is 'blockStart' + head = head.next + if head is container.end + next = null + else + next = head.container + else + prev = next = container = null + + mode.parens prev?.getParenModifier() ? null, node.getParenModifier(), next?.getParenModifier?() ? null, container?.getReader?() ? null + + return node + # Getter/setter utility function Function::trigger = (prop, get, set) -> Object.defineProperty @prototype, prop, @@ -107,6 +136,26 @@ define ['droplet-helper'], (helper) -> classes: @classes } + getParenModifier: -> + leadingFn = (value) => + if value? + @setLeadingText value + return @getLeadingText() + + trailingFn = (value) => + if value? + @setTrailingText value + return @getTrailingText() + + return { + leading: leadingFn + trailing: trailingFn + id: @id + type: @type + precedence: @precedence + classes: @classes + } + hasParent: (parent) -> head = @ until head in [parent, null] @@ -192,6 +241,7 @@ define ['droplet-helper'], (helper) -> setLeadingText: (value) -> if value? + @version++ if @start.next.type is 'text' if value.length is 0 @start.next.remove() @@ -202,6 +252,7 @@ define ['droplet-helper'], (helper) -> setTrailingText: (value) -> if value? + @version++ if @end.prev.type is 'text' if value.length is 0 @end.prev.remove() @@ -392,22 +443,12 @@ define ['droplet-helper'], (helper) -> # USED FOR TESTING ONLY moveTo: (token, mode) -> if @start.prev? or @end.next? - leading = @getLeadingText() - trailing = @getTrailingText() - - [leading, trailing] = mode.parens leading, trailing, @, null - - @setLeadingText leading; @setTrailingText trailing + applyParens mode, @, null @spliceOut() if token? - leading = @getLeadingText() - trailing = @getTrailingText() - - [leading, trailing] = mode.parens leading, trailing, @, (token.container ? token.parent) - - @setLeadingText leading; @setTrailingText trailing + applyParens mode, @, token @spliceIn token diff --git a/src/parser.coffee b/src/parser.coffee index 9dad5897..00ca43ab 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -437,18 +437,18 @@ define ['droplet-helper', 'droplet-model'], (helper, model) -> else head = head.next - Parser.parens = (leading, trailing, node, context) -> + Parser.parens = (prev, node, next, context) -> if context is null or context.type isnt 'socket' or context?.precedence < node.precedence while true - if leading().match(/^\s*\(/)? and trailing().match(/\)\s*/)? - leading leading().replace(/^\s*\(\s*/, '') - trailing trailing().replace(/^\s*\)\s*/, '') + if node.leading().match(/^\s*\(/)? and node.trailing().match(/\)\s*/)? + node.leading node.leading().replace(/^\s*\(\s*/, '') + node.trailing node.trailing().replace(/^\s*\)\s*/, '') else break else - leading '(' + leading() - trailing trailing() + ')' + node.leading '(' + node.leading() + node.trailing node.trailing() + ')' Parser.drop = (block, context, pred) -> if block.type is 'segment' and context.type is 'socket' @@ -469,27 +469,9 @@ define ['droplet-helper', 'droplet-model'], (helper, model) -> opts ?= wrapAtRoot: true return @createParser(text)._parse opts - parens: (leading, trailing, node, context) -> - # leadingFn is always a getter/setter for leading - leadingFn = (value) -> - if value? - leading = value - return leading - - # trailingFn may either get/set leading or trailing; - # will point to leading if leading is the only token, - # but will point to trailing otherwise. - if trailing? - trailingFn = (value) -> - if value? - trailing = value - return trailing - else - trailingFn = leadingFn - - CustomParser.parens leadingFn, trailingFn, node, context - - return [leading, trailing] + parens: (prev, node, next, context) -> + CustomParser.parens prev, node, next, context + return null drop: (block, context, pred) -> CustomParser.drop block, context, pred