Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contexts #39

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 24 additions & 24 deletions example/example.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -263,28 +263,28 @@ require ['droplet'], (droplet) ->
{
block: '''
button 'Click', ->
``
__
'''
title: 'Make a button and do something when clicked'
},
{
block: '''
for [1..3]
``
__
'''
title: 'Do something multiple times'
},
{
block: '''
for x in [1..3]
``
__
'''
title: 'Do something multiple times...?'
},
{
block: '''
while ``
``
while __
__
'''
title: 'Repeat something while a condition is true'
},
Expand All @@ -297,17 +297,17 @@ require ['droplet'], (droplet) ->
},
{
block: '''
if ``
``
if __
__
'''
title: 'Do something only if a condition is true'
},
{
block: '''
if ``
``
if __
__
else
``
__
'''
title: 'Do something if a condition is true, otherwise do something else'
},
Expand All @@ -317,23 +317,23 @@ require ['droplet'], (droplet) ->
name: 'Calculate'
color: 'green'
blocks: [
{block:'x = ``', title:'Set a variable'},
{block:'`` + ``', title:'Add two numbers'},
{block:'`` - ``', title:'Subtract two numbers'},
{block:'`` * ``', title:'Multiply two numbers'},
{block:'`` / ``', title:'Divide two numbers'},
{block:'`` is ``', title:'Compare two values'},
{block:'`` < ``', title:'Compare two values'},
{block:'`` > ``', title:'Compare two values'},
{block:'x = __', title:'Set a variable'},
{block:'__ + __', title:'Add two numbers'},
{block:'__ - __', title:'Subtract two numbers'},
{block:'__ * __', title:'Multiply two numbers'},
{block:'__ / __', title:'Divide two numbers'},
{block:'__ is __', title:'Compare two values'},
{block:'__ < __', title:'Compare two values'},
{block:'__ > __', title:'Compare two values'},
{block:'random position', title:'Get a random number in a range'},
{block:'round ``', title:'Round to the nearest integer'},
{block:'abs ``', title:'Absolute value'},
{block:'max ``, ``', title:'Get the larger of two numbers'},
{block:'min ``, ``', title:'Get the smaller on two numbers'},
{block:'round __', title:'Round to the nearest integer'},
{block:'abs __', title:'Absolute value'},
{block:'max __, __', title:'Get the larger of two numbers'},
{block:'min __, __', title:'Get the smaller on two numbers'},
{
block:'''
f = (param) ->
``
__
'''
title: 'Define a new function'
},
Expand All @@ -353,7 +353,7 @@ require ['droplet'], (droplet) ->
{
block:'''
tick 1, ->
``
__
'''
title: 'Do something at equally-spaced times'
}
Expand Down
162 changes: 91 additions & 71 deletions src/coffee.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,71 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], (
@csSocketAndMark param, depth, 0, indentDepth, FORBID_ALL
@mark node.body, depth, 0, null, indentDepth

markExpressionBlock: (indentDepth, expressions, depth, bounds = null) ->
# Abort if empty
if expressions.length is 0 then return

bounds ?= {
start: @getBounds(expressions[0]).start
end: @getBounds(expressions[expressions.length - 1]).end
}

# 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

if shouldBeOneLine
@addSocket {
bounds: bounds
depth: depth
precedence: 0
classes: ['Block']
}

# Otherwise, wrap in an indent.
else
# Determine the new indent depth by literal text inspection
firstLine = expressions[0].locationData.first_line
textLine = @lines[firstLine]
trueIndentDepth = textLine.length - textLine.trimLeft().length

# As a block, we also want to consume as much whitespace above us as possible
# (to free it from actual ICE editor blocks).
while bounds.start.line > 0 and @lines[bounds.start.line - 1].trim().length is 0
bounds.start.line -= 1
bounds.start.column = @lines[bounds.start.line].length + 1

# Move the boundaries back by one line,
# as per the standard way to add an Indent.
bounds.start.line -= 1
bounds.start.column = @lines[bounds.start.line].length + 1

@addIndent {
depth: depth
bounds: bounds
prefix: @lines[firstLine][indentDepth...trueIndentDepth]
}

# Then update indent depth data to reflect.
indentDepth = trueIndentDepth

# Mark children. We do this at depth + 3 to
# make room for semicolon wrappers where necessary.
for expr in expressions
@mark expr, depth + 3, 0, null, indentDepth

# Wrap semicolons.
@wrapSemicolons expressions, depth

# ## mark ##
# Mark a single node. The main recursive function.
mark: (node, depth, precedence, wrappingParen, indentDepth) ->
Expand All @@ -266,62 +331,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], (
# A Block is a group of expressions,
# which is represented by either an indent or a socket.
when 'Block'
# Abort if empty
if node.expressions.length is 0 then return

# Otherwise, get the bounds to determine
# 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

if shouldBeOneLine
@csSocket node, depth, 0

# Otherwise, wrap in an indent.
else
# Determine the new indent depth by literal text inspection
textLine = @lines[node.locationData.first_line]
trueIndentDepth = textLine.length - textLine.trimLeft().length

# As a block, we also want to consume as much whitespace above us as possible
# (to free it from actual ICE editor blocks).
while bounds.start.line > 0 and @lines[bounds.start.line - 1].trim().length is 0
bounds.start.line -= 1
bounds.start.column = @lines[bounds.start.line].length + 1

# Move the boundaries back by one line,
# as per the standard way to add an Indent.
bounds.start.line -= 1
bounds.start.column = @lines[bounds.start.line].length + 1

@addIndent {
depth: depth
bounds: bounds
prefix: @lines[node.locationData.first_line][indentDepth...trueIndentDepth]
}

# Then update indent depth data to reflect.
indentDepth = trueIndentDepth

# Mark children. We do this at depth + 3 to
# make room for semicolon wrappers where necessary.
for expr in node.expressions
@mark expr, depth + 3, 0, null, indentDepth

# Wrap semicolons.
@wrapSemicolons node.expressions, depth
@markExpressionBlock indentDepth, node.expressions, depth, @getBounds(node)

# ### Parens ###
# Parens are special; they get no marks
Expand Down Expand Up @@ -372,7 +382,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], (
node.first?.base?.nodeType?() is 'Literal'
return

@csBlock node, depth, OPERATOR_PRECEDENCES[node.operator], 'value', wrappingParen, VALUE_ONLY
@csBlock node, depth, OPERATOR_PRECEDENCES[node.operator], 'value', wrappingParen, VALUE_ONLY, new parser.ParsingContext('a = ')

@csSocketAndMark node.first, depth + 1, OPERATOR_PRECEDENCES[node.operator], indentDepth

Expand Down Expand Up @@ -407,7 +417,7 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], (

# Fake-remove backticks hack
else if node.base.nodeType() is 'Literal' and
node.base.value is ''
node.base.value is '__'
fakeBlock =
@csBlock node.base, depth, 0, 'value', wrappingParen, ANY_DROP
fakeBlock.flagToRemove = true
Expand Down Expand Up @@ -601,12 +611,19 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], (
if node.subject? then @csSocketAndMark node.subject, depth + 1, 0, indentDepth

for switchCase in node.cases
if switchCase[0].constructor is Array
for condition in switchCase[0]
@csSocketAndMark condition, depth + 1, 0, indentDepth # (condition)
else
@csSocketAndMark switchCase[0], depth + 1, 0, indentDepth # (condition)
@mark switchCase[1], depth + 1, 0, null, indentDepth # (body)
switchCase.nodeType = -> '__Case'

@markExpressionBlock indentDepth, node.cases, depth + 1

when '__Case' # Case is an artificial flag added by Switch
@csBlock node, depth, 0, 'control', wrappingParen, MOSTLY_BLOCK, new parser.ParsingContext 'switch\n', '', ' '

if node[0] instanceof Array
for condition in node[0]
@csSocketAndMark condition, depth + 1, 0, indentDepth # (condition)
else
@csSocketAndMark node[0], depth + 1, 0, indentDepth # (condition)
@mark node[1], depth + 1, 0, null, indentDepth # (body)

if node.otherwise?
@mark node.otherwise, depth + 1, 0, null, indentDepth
Expand Down Expand Up @@ -704,8 +721,10 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], (

# Hack: Functions should end immediately
# when their bodies end.
if node.nodeType() is 'Code' and node.body?
if node.nodeType() is 'Code'and node.body?
bounds.end = @getBounds(node.body).end
if node.nodeType() is '__Case'
bounds.end = @getBounds(node[1]).end

# The fourth is general. Sometimes we get
# spaces at the start of the next line.
Expand Down Expand Up @@ -752,14 +771,15 @@ 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 = [], parsingContext) ->
@addBlock {
bounds: @getBounds (wrappingParen ? node)
depth: depth
precedence: precedence
color: color
classes: getClassesFor(node).concat classes
parenWrapped: wrappingParen?
parsingContext: parsingContext
}

# Add an indent node and guess
Expand All @@ -773,8 +793,8 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], (
first.column = @lines[first.line].length

if first.line isnt last.line
trueDepth = @lines[last.line].length - @lines[last.line].trimLeft().length
prefix = @lines[last.line][indentDepth...trueDepth]
trueDepth = @lines[first.line + 1].length - @lines[first.line + 1].trimLeft().length
prefix = @lines[first.line + 1][indentDepth...trueDepth]
else
trueDepth = indentDepth + 2
prefix = ' '
Expand Down Expand Up @@ -916,15 +936,15 @@ define ['droplet-helper', 'droplet-model', 'droplet-parser', 'coffee-script'], (
if n < 0 or n >= lines.length
return false
# Refuse to add another empty backtick line if there is one already
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/backtick/underscore/

if n + 1 < lines.length and /^\s*``$/.test lines[n + 1]
if n + 1 < lines.length and /^\s*__$/.test lines[n + 1]
return false
leading = /^\s*/.exec lines[n]
# If we are all spaces then fail.
if not leading or leading[0].length >= lines[n].length
return false
lines.splice n + 1, 0, leading[0] + ' ``'
lines.splice n + 1, 0, leading[0] + ' __'

CoffeeScriptParser.empty = "``"
CoffeeScriptParser.empty = "__"

CoffeeScriptParser.drop = (block, context, pred) ->
if context.type is 'socket'
Expand Down
14 changes: 9 additions & 5 deletions src/controller.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -1238,8 +1238,12 @@ define ['droplet-helper',
@endDrag()

Editor::reparseRawReplace = (oldBlock) ->
try
newParse = @mode.parse(oldBlock.stringify(@mode.empty), wrapAtRoot: true)
#try
console.log oldBlock.parsingContext
newParse = @mode.parse(oldBlock.stringify(@mode.empty), {
wrapAtRoot: true
context: oldBlock.parsingContext ? null
})
newBlock = newParse.start.next.container
if newParse.start.next.container.end is newParse.end.prev and
newBlock?.type is 'block'
Expand All @@ -1252,9 +1256,9 @@ define ['droplet-helper',
else
newBlock.rawReplace oldBlock

catch e
throw e
return false
#catch e
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why removed?

# throw e
# return false

Editor::findForReal = (token) ->
head = @tree.start; i = 0
Expand Down
Loading