diff --git a/documentation/coffee/array_comprehensions.coffee b/documentation/coffee/array_comprehensions.coffee
index b9d9e166..8050de6e 100644
--- a/documentation/coffee/array_comprehensions.coffee
+++ b/documentation/coffee/array_comprehensions.coffee
@@ -1,7 +1,2 @@
# Eat lunch.
-lunch = eat food for food in ['toast', 'cheese', 'wine']
-
-# Naive collision detection.
-for roid, pos in asteroids
- for roid2 in asteroids when roid isnt roid2
- roid.explode() if roid.overlaps roid2
\ No newline at end of file
+eat food for food in ['toast', 'cheese', 'wine']
diff --git a/documentation/index.html.erb b/documentation/index.html.erb
index 6e985acf..7b45d6f2 100644
--- a/documentation/index.html.erb
+++ b/documentation/index.html.erb
@@ -517,6 +517,15 @@ coffee --bare --print --stdio
end of your comprehension.
+ Note how because we are assigning the value of the comprehensions to a
+ variable in the example above, CoffeeScript is collecting the result of
+ each iteration into an array. Sometimes functions end with loops that are
+ intended to run only for their side-effects. Be careful that you're not
+ accidentally returning the results of the comprehension in these cases,
+ by adding a meaningful return value, like true, or null,
+ to the bottom of your function.
+
Comprehensions can also be used to iterate over the keys and values in
an object. Use of to signal comprehension over the properties of
diff --git a/documentation/js/array_comprehensions.js b/documentation/js/array_comprehensions.js
index bbbe4c90..07f136c7 100644
--- a/documentation/js/array_comprehensions.js
+++ b/documentation/js/array_comprehensions.js
@@ -1,17 +1,6 @@
-var food, lunch, pos, roid, roid2, _i, _j, _len, _len2, _len3, _ref;
+var food, _i, _len, _ref;
_ref = ['toast', 'cheese', 'wine'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
food = _ref[_i];
- lunch = eat(food);
-}
-for (pos = 0, _len2 = asteroids.length; pos < _len2; pos++) {
- roid = asteroids[pos];
- for (_j = 0, _len3 = asteroids.length; _j < _len3; _j++) {
- roid2 = asteroids[_j];
- if (roid !== roid2) {
- if (roid.overlaps(roid2)) {
- roid.explode();
- }
- }
- }
+ eat(food);
}
\ No newline at end of file
diff --git a/documentation/js/splices.js b/documentation/js/splices.js
index ed47b9b2..a6f916f1 100644
--- a/documentation/js/splices.js
+++ b/documentation/js/splices.js
@@ -1,3 +1,3 @@
-var numbers, _ref;
+var numbers;
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
-([].splice.apply(numbers, [3, 6 - 3 + 1].concat(_ref = [-3, -4, -5, -6])), _ref);
\ No newline at end of file
+[].splice.apply(numbers, [3, 4].concat([-3, -4, -5, -6]));
\ No newline at end of file
diff --git a/index.html b/index.html
index 75c421e0..42126459 100644
--- a/index.html
+++ b/index.html
@@ -854,28 +854,12 @@ lyrics = function() {
would use a loop, each/forEach, map, or select/filter.
# Eat lunch.
-lunch = eat food for food in ['toast', 'cheese', 'wine']
-
-# Naive collision detection.
-for roid, pos in asteroids
- for roid2 in asteroids when roid isnt roid2
- roid.explode() if roid.overlaps roid2
-
var food, lunch, pos, roid, roid2, _i, _j, _len, _len2, _len3, _ref;
+eat food for food in ['toast', 'cheese', 'wine']
+
+ Note how because we are assigning the value of the comprehensions to a
+ variable in the example above, CoffeeScript is collecting the result of
+ each iteration into an array. Sometimes functions end with loops that are
+ intended to run only for their side-effects. Be careful that you're not
+ accidentally returning the results of the comprehension in these cases,
+ by adding a meaningful return value, like true, or null,
+ to the bottom of your function.
+
Comprehensions can also be used to iterate over the keys and values in
an object. Use of to signal comprehension over the properties of
@@ -975,12 +968,12 @@ middle = copy.slice(3, 6 + 1);;alert(middle);'>run: middle apply(numbers, [3, 4].concat([-3, -4, -5, -6]));
+
+[].splice.apply(numbers, [3, 4].concat([-3, -4, -5, -6]));;alert(numbers);'>run: numbers
Note that JavaScript strings are immutable, and can't be spliced.
@@ -1920,7 +1913,7 @@ task('build:parser ');
stdio.on('data', function(buffer) {
diff --git a/lib/scope.js b/lib/scope.js
index 49feb13c..5097955e 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -48,6 +48,9 @@
return false;
};
Scope.prototype.parameter = function(name) {
+ if (this.shared && this.check(name, true)) {
+ return;
+ }
return this.add(name, 'param');
};
Scope.prototype.check = function(name, immediate) {
diff --git a/src/command.coffee b/src/command.coffee
index 9a7711d7..6034db64 100644
--- a/src/command.coffee
+++ b/src/command.coffee
@@ -7,6 +7,7 @@
# External dependencies.
fs = require 'fs'
path = require 'path'
+util = require 'util'
helpers = require './helpers'
optparse = require './optparse'
CoffeeScript = require './coffee-script'
@@ -154,7 +155,7 @@ writeJs = (source, js, base) ->
js = ' ' if js.length <= 0
fs.writeFile jsPath, js, (err) ->
if err then printLine err.message
- else if opts.compile and opts.watch then printLine "Compiled #{source}"
+ else if opts.compile and opts.watch then util.log "compiled #{source}"
path.exists dir, (exists) ->
if exists then compile() else exec "mkdir -p #{dir}", compile
diff --git a/src/nodes.coffee b/src/nodes.coffee
index 26a07830..cfb96ac6 100644
--- a/src/nodes.coffee
+++ b/src/nodes.coffee
@@ -433,7 +433,11 @@ exports.Call = class Call extends Base
# Tag this invocation as creating a new instance.
newInstance: ->
- @isNew = true
+ base = @variable.base or @variable
+ if base instanceof Call
+ base.newInstance()
+ else
+ @isNew = true
this
# Grab the reference to the superclass's implementation of the current
@@ -978,9 +982,10 @@ exports.Code = class Code extends Base
# arrow, generates a wrapper that saves the current value of `this` through
# a closure.
compileNode: (o) ->
- sharedScope = del o, 'sharedScope'
- o.scope = scope = sharedScope or new Scope o.scope, @body, this
- o.indent += TAB
+ sharedScope = del o, 'sharedScope'
+ o.scope = sharedScope or new Scope o.scope, @body, this
+ o.scope.shared = yes if sharedScope
+ o.indent += TAB
delete o.bare
delete o.globals
vars = []
@@ -1004,7 +1009,7 @@ exports.Code = class Code extends Base
wasEmpty = @body.isEmpty()
exprs.unshift splats if splats
@body.expressions.unshift exprs... if exprs.length
- scope.parameter vars[i] = v.compile o for v, i in vars unless splats
+ o.scope.parameter vars[i] = v.compile o for v, i in vars unless splats
@body.makeReturn() unless wasEmpty or @noReturn
idt = o.indent
code = 'function'
@@ -1421,9 +1426,8 @@ exports.For = class For extends Base
scope = o.scope
name = @name and @name.compile o, LEVEL_LIST
index = @index and @index.compile o, LEVEL_LIST
- unless hasCode
- scope.find(name, immediate: yes) if name and not @pattern
- scope.find(index, immediate: yes) if index
+ scope.find(name, immediate: yes) if name and not @pattern
+ scope.find(index, immediate: yes) if index
rvar = scope.freeVariable 'results' if @returns and not hasPure
ivar = (if @range then name else index) or scope.freeVariable 'i'
varPart = ''
diff --git a/src/repl.coffee b/src/repl.coffee
index 78e49f73..a89f2cc6 100644
--- a/src/repl.coffee
+++ b/src/repl.coffee
@@ -12,6 +12,10 @@ readline = require 'readline'
# Start by opening up **stdio**.
stdio = process.openStdin()
+# Log an error.
+error = (err) ->
+ stdio.write (err.stack or err.toString()) + '\n\n'
+
# Quick alias for quitting the REPL.
helpers.extend global, quit: -> process.exit(0)
@@ -23,9 +27,12 @@ run = (buffer) ->
val = CoffeeScript.eval buffer.toString(), bare: on, globals: on, fileName: 'repl'
console.log val if val isnt undefined
catch err
- console.error err.stack or err.toString()
+ error err
repl.prompt()
+# Make sure that uncaught exceptions don't kill the REPL.
+process.on 'uncaughtException', error
+
# Create the REPL by listening to **stdin**.
repl = readline.createInterface stdio
repl.setPrompt 'coffee> '
diff --git a/src/scope.coffee b/src/scope.coffee
index 238d0a9e..cde01160 100644
--- a/src/scope.coffee
+++ b/src/scope.coffee
@@ -44,6 +44,7 @@ exports.Scope = class Scope
# Reserve a variable name as originating from a function parameter for this
# scope. No `var` required for internal references.
parameter: (name) ->
+ return if @shared and @check name, yes
@add name, 'param'
# Just check to see if a variable has already been declared, without reserving,
diff --git a/test/test_comprehensions.coffee b/test/test_comprehensions.coffee
index 86dff3ff..54f802e9 100644
--- a/test/test_comprehensions.coffee
+++ b/test/test_comprehensions.coffee
@@ -227,4 +227,17 @@ foo = ->
for j in [0..7]
-> i + j
-eq foo()[3][4](), 7
\ No newline at end of file
+eq foo()[3][4](), 7
+
+
+# Issue #897: Ensure that plucked function variables aren't leaked.
+facets = {}
+list = ['one', 'two']
+
+(->
+ for entity in list
+ facets[entity] = -> entity
+)()
+
+eq typeof entity, 'undefined'
+eq facets['two'](), 'two'
diff --git a/test/test_functions.coffee b/test/test_functions.coffee
index 1bb26f21..1e10e217 100644
--- a/test/test_functions.coffee
+++ b/test/test_functions.coffee
@@ -346,6 +346,7 @@ eq ok, new ->
### Should `return` implicitly ###
### even with trailing comments. ###
+
#855: execution context for `func arr...` should be `null`
(->
global = @
@@ -355,3 +356,14 @@ eq ok, new ->
contextTest.apply null, array
contextTest array...
)()
+
+
+# #894: Splatting against constructor-chained functions.
+x = null
+
+class Foo
+ bar: (y) -> x = y
+
+new Foo().bar([101]...)
+
+eq x, 101
\ No newline at end of file