mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
[CS2] Comments (#4572)
* Make `addLocationDataFn` more DRY * Style fixes * Provide access to full parser inside our custom function running in parser.js; rename the function to lay the groundwork for adding data aside from location data * Fix style. * Fix style. * Label test comments * Update grammar to remove comment tokens; update DSL to call new helper function that preserves comments through parsing * New implementation of compiling block comments: the lexer pulls them out of the token stream, attaching them as a property to a token; the rewriter moves the attachment around so it lives on a token that is destined to make it through to compilation (and in a good placement); and the nodes render the block comment. All tests but one pass (commented out). * If a comment follows a class declaration, move the comment inside the class body * Style * Improve indentation of multiline comments * Fix indentation for block comments, at least in the cases covered by the one failing test * Don’t reverse the order of unshifted comments * Simplify rewriter’s handling of comments, generalizing the special case * Expand the list of tokens we need to avoid for passing comments through the parser; get some literal tokens to have nodes created for them so that the comments pass through * Improve comments; fix multiline flag * Prepare HereComments for processing line comments * Line comments, first draft: the tests pass, but the line comments aren’t indented and sometimes trail previous lines when they shouldn’t; updated compiler output in following commit * Updated compiler, now with line comments * `process` doesn’t exist in the browser, so we should check for its existence first * Update parser output * Test that proves #4290 is fixed * Indent line comments, first pass * Compiled output with indented line comments * Comments that start a new line shouldn’t trail; don’t skip comments attached to generated tokens; stop looking for indentation once we hit a newline * Revised output * Cleanup * Split “multiline” line comment tokens, shifting them forward or back as appropriate * Fix comments in module specifiers * Abstract attaching comments to a node * Line comments in interpolated strings * Line comments can’t be multiline anymore * Improve handling of blank lines and indentation of following comments that start a new line (i.e. don’t trail) * Make comments compilation more object-oriented * Remove lots of dead code that we don’t need anymore because a comment is never a node, only a fragment * Improve eqJS helper * Fix #4290 definitively, with improved output for arrays with interspersed block comments * Add support for line comments output interspersed within arrays * Fix mistake, don’t lose the variable we’re working on * Remove redundant replacements * Check for indentation only from the start of the string * Indentations in generated JS are always multiples of two spaces (never tabs) so just look for 2+ spaces * Update package versions; run Babel twice, once for each preset, temporarily until a Babili bug is fixed that prevents it from running with the env preset * Don’t rely on `fragment.type`, which can break when the compiler is minified * Updated generated docs and browser compiler * Output block comments after function arguments * Comments appear above scope `var` declarations; better tracking of generated `JS` tokens created only to shepherd comments through to the output * Create new FuncGlyph node, to hold comments we want to output near the function parameters * Block comments between `)` and `->`/`=>` get output between `)` and `{`. * Fix indentation of comments that are the first line inside a bare mode block * Updated output * Full Flow example * Updated browser compiler * Abstract and organize comment fragment generation code; store more properties on the comment fragment objects; make `throw` behave like `return` * Abstract token insertion code * Add missing locationData to STRING_START token, giving it the locationData of the overall StringWithInterpolations token so that comments attached to STRING_START end up on the StringWithInterpolations node * Allow `SUPER` tokens to carry comments * Rescue comments from `Existence` nodes and `If` nodes’ conditions * Rescue comments after `\` line continuation tokens * Updated compiled output * Updated browser compiler * Output block comments in the same `compileFragments` method as line comments, except for inline block comments * Comments before splice * Updated browser compiler * Track compiledComments as a property of Base, to ensure that it’s not a global variable * Docs: split up the Usage section * Docs for type annotations via Flow; updated docs output * Update regular comments documentation * Updated browser compiler * Comments before soak * Comments before static methods, and probably before `@variable =` (this) assignments generally * Comments before ‘if exists?’, refactor comment before ‘if this.var’ to be more precise, improve helper methods * Comments before a method that contains ‘super()’ should output above the method property, not above the ‘super.method()’ call * Fix missing comments before `if not` (i.e. before a UNARY token) * Fix comments before ‘for’; add test for comment before assignment if (fixed in earlier commit) * Comments within heregexes * Updated browser compiler * Update description to reflect what’s now happening in compileCommentFragments * Preserve blank lines between line comments; output “whitespace-only” line comments as blank lines, rather than `//` following by whitespace * Better future-proof comments tests * Comments before object destructuring; abstract method for setting comments aside before compilation * Handle more cases of comments before or after `for` loop declaration lines * Fix indentation of comments preceding `for` loops * Fix comment before splat function parameter * Catch another RegexWithInterpolations comment edge case * Updated browser compiler * Change heregex example to one that’s more readable; update output * Remove a few last references to the defunct HERECOMMENT token * Abstract location hash creation into a function * Improved clarity per code review notes * Updated browser compiler
This commit is contained in:
parent
6c9cf37811
commit
6d21dc5495
46 changed files with 10394 additions and 1437 deletions
|
@ -1,19 +1,27 @@
|
|||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var buildLocationData, extend, flatten, ref, repeat, syntaxErrorToString;
|
||||
// This file contains the common helper functions that we'd like to share among
|
||||
// the **Lexer**, **Rewriter**, and the **Nodes**. Merge objects, flatten
|
||||
// arrays, count characters, that sort of thing.
|
||||
|
||||
// Peek at the beginning of a given string to see if it matches a sequence.
|
||||
var attachCommentsToNode, buildLocationData, buildLocationHash, extend, flatten, ref, repeat, syntaxErrorToString;
|
||||
|
||||
exports.starts = function(string, literal, start) {
|
||||
return literal === string.substr(start, literal.length);
|
||||
};
|
||||
|
||||
// Peek at the end of a given string to see if it matches a sequence.
|
||||
exports.ends = function(string, literal, back) {
|
||||
var len;
|
||||
len = literal.length;
|
||||
return literal === string.substr(string.length - len - (back || 0), len);
|
||||
};
|
||||
|
||||
// Repeat a string `n` times.
|
||||
exports.repeat = repeat = function(str, n) {
|
||||
var res;
|
||||
// Use clever algorithm to have O(log(n)) string concatenation operations.
|
||||
res = '';
|
||||
while (n > 0) {
|
||||
if (n & 1) {
|
||||
|
@ -25,6 +33,7 @@
|
|||
return res;
|
||||
};
|
||||
|
||||
// Trim out all falsy values from an array.
|
||||
exports.compact = function(array) {
|
||||
var i, item, len1, results;
|
||||
results = [];
|
||||
|
@ -37,6 +46,7 @@
|
|||
return results;
|
||||
};
|
||||
|
||||
// Count the number of occurrences of a string in a string.
|
||||
exports.count = function(string, substr) {
|
||||
var num, pos;
|
||||
num = pos = 0;
|
||||
|
@ -49,10 +59,14 @@
|
|||
return num;
|
||||
};
|
||||
|
||||
// Merge objects, returning a fresh copy with attributes from both sides.
|
||||
// Used every time `Base#compile` is called, to allow properties in the
|
||||
// options hash to propagate down the tree without polluting other branches.
|
||||
exports.merge = function(options, overrides) {
|
||||
return extend(extend({}, options), overrides);
|
||||
};
|
||||
|
||||
// Extend a source object with the properties of another object (shallow copy).
|
||||
extend = exports.extend = function(object, properties) {
|
||||
var key, val;
|
||||
for (key in properties) {
|
||||
|
@ -62,6 +76,8 @@
|
|||
return object;
|
||||
};
|
||||
|
||||
// Return a flattened version of an array.
|
||||
// Handy for getting a list of `children` from the nodes.
|
||||
exports.flatten = flatten = function(array) {
|
||||
var element, flattened, i, len1;
|
||||
flattened = [];
|
||||
|
@ -76,6 +92,8 @@
|
|||
return flattened;
|
||||
};
|
||||
|
||||
// Delete a key from an object, returning the value. Useful when a node is
|
||||
// looking for a particular method in an options hash.
|
||||
exports.del = function(obj, key) {
|
||||
var val;
|
||||
val = obj[key];
|
||||
|
@ -83,6 +101,7 @@
|
|||
return val;
|
||||
};
|
||||
|
||||
// Typical Array::some
|
||||
exports.some = (ref = Array.prototype.some) != null ? ref : function(fn) {
|
||||
var e, i, len1, ref1;
|
||||
ref1 = this;
|
||||
|
@ -95,12 +114,18 @@
|
|||
return false;
|
||||
};
|
||||
|
||||
// Helper function for extracting code from Literate CoffeeScript by stripping
|
||||
// out all non-code blocks, producing a string of CoffeeScript code that can
|
||||
// be compiled “normally.”
|
||||
exports.invertLiterate = function(code) {
|
||||
var blankLine, i, indented, insideComment, len1, line, listItemStart, out, ref1;
|
||||
out = [];
|
||||
blankLine = /^\s*$/;
|
||||
indented = /^[\t ]/;
|
||||
listItemStart = /^(?:\t?| {0,3})(?:[\*\-\+]|[0-9]{1,9}\.)[ \t]/;
|
||||
listItemStart = /^(?:\t?| {0,3})(?:[\*\-\+]|[0-9]{1,9}\.)[ \t]/; // Up to one tab, or up to three spaces, or neither;
|
||||
// followed by `*`, `-` or `+`;
|
||||
// or by an integer up to 9 digits long, followed by a period;
|
||||
// followed by a space or a tab.
|
||||
insideComment = false;
|
||||
ref1 = code.split('\n');
|
||||
for (i = 0, len1 = ref1.length; i < len1; i++) {
|
||||
|
@ -121,6 +146,8 @@
|
|||
return out.join('\n');
|
||||
};
|
||||
|
||||
// Merge two jison-style location data objects together.
|
||||
// If `last` is not provided, this will simply return `first`.
|
||||
buildLocationData = function(first, last) {
|
||||
if (!last) {
|
||||
return first;
|
||||
|
@ -134,15 +161,59 @@
|
|||
}
|
||||
};
|
||||
|
||||
exports.addLocationDataFn = function(first, last) {
|
||||
buildLocationHash = function(loc) {
|
||||
return `${loc.first_line}x${loc.first_column}-${loc.last_line}x${loc.last_column}`;
|
||||
};
|
||||
|
||||
// This returns a function which takes an object as a parameter, and if that
|
||||
// object is an AST node, updates that object's locationData.
|
||||
// The object is returned either way.
|
||||
exports.addDataToNode = function(parserState, first, last) {
|
||||
return function(obj) {
|
||||
if (((typeof obj) === 'object') && (!!obj['updateLocationDataIfMissing'])) {
|
||||
var i, len1, objHash, ref1, token, tokenHash;
|
||||
// Add location data
|
||||
if (((obj != null ? obj.updateLocationDataIfMissing : void 0) != null) && (first != null)) {
|
||||
obj.updateLocationDataIfMissing(buildLocationData(first, last));
|
||||
}
|
||||
// Add comments data
|
||||
if (!parserState.tokenComments) {
|
||||
parserState.tokenComments = {};
|
||||
ref1 = parserState.parser.tokens;
|
||||
for (i = 0, len1 = ref1.length; i < len1; i++) {
|
||||
token = ref1[i];
|
||||
if (!token.comments) {
|
||||
continue;
|
||||
}
|
||||
tokenHash = buildLocationHash(token[2]);
|
||||
if (parserState.tokenComments[tokenHash] == null) {
|
||||
parserState.tokenComments[tokenHash] = token.comments;
|
||||
} else {
|
||||
parserState.tokenComments[tokenHash].push(...token.comments);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (obj.locationData != null) {
|
||||
objHash = buildLocationHash(obj.locationData);
|
||||
if (parserState.tokenComments[objHash] != null) {
|
||||
attachCommentsToNode(parserState.tokenComments[objHash], obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
};
|
||||
|
||||
exports.attachCommentsToNode = attachCommentsToNode = function(comments, node) {
|
||||
if ((comments == null) || comments.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (node.comments == null) {
|
||||
node.comments = [];
|
||||
}
|
||||
return node.comments.push(...comments);
|
||||
};
|
||||
|
||||
// Convert jison location data to a string.
|
||||
// `obj` can be a token, or a locationData.
|
||||
exports.locationDataToString = function(obj) {
|
||||
var locationData;
|
||||
if (("2" in obj) && ("first_line" in obj[2])) {
|
||||
|
@ -157,6 +228,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
// A `.coffee.md` compatible version of `basename`, that returns the file sans-extension.
|
||||
exports.baseFileName = function(file, stripExt = false, useWinPathSep = false) {
|
||||
var parts, pathSep;
|
||||
pathSep = useWinPathSep ? /\\|\// : /\//;
|
||||
|
@ -173,24 +245,36 @@
|
|||
return parts.join('.');
|
||||
};
|
||||
|
||||
// Determine if a filename represents a CoffeeScript file.
|
||||
exports.isCoffee = function(file) {
|
||||
return /\.((lit)?coffee|coffee\.md)$/.test(file);
|
||||
};
|
||||
|
||||
// Determine if a filename represents a Literate CoffeeScript file.
|
||||
exports.isLiterate = function(file) {
|
||||
return /\.(litcoffee|coffee\.md)$/.test(file);
|
||||
};
|
||||
|
||||
// Throws a SyntaxError from a given location.
|
||||
// The error's `toString` will return an error message following the "standard"
|
||||
// format `<filename>:<line>:<col>: <message>` plus the line with the error and a
|
||||
// marker showing where the error is.
|
||||
exports.throwSyntaxError = function(message, location) {
|
||||
var error;
|
||||
error = new SyntaxError(message);
|
||||
error.location = location;
|
||||
error.toString = syntaxErrorToString;
|
||||
// Instead of showing the compiler's stacktrace, show our custom error message
|
||||
// (this is useful when the error bubbles up in Node.js applications that
|
||||
// compile CoffeeScript for example).
|
||||
error.stack = error.toString();
|
||||
throw error;
|
||||
};
|
||||
|
||||
// Update a compiler SyntaxError with source code information if it didn't have
|
||||
// it already.
|
||||
exports.updateSyntaxError = function(error, code, filename) {
|
||||
// Avoid screwing up the `stack` property of other errors (i.e. possible bugs).
|
||||
if (error.toString === syntaxErrorToString) {
|
||||
error.code || (error.code = code);
|
||||
error.filename || (error.filename = filename);
|
||||
|
@ -214,8 +298,10 @@
|
|||
filename = this.filename || '[stdin]';
|
||||
codeLine = this.code.split('\n')[first_line];
|
||||
start = first_column;
|
||||
// Show only the first line on multi-line errors.
|
||||
end = first_line === last_line ? last_column + 1 : codeLine.length;
|
||||
marker = codeLine.slice(0, start).replace(/[^\s]/g, ' ') + repeat('^', end - start);
|
||||
// Check to see if we're running on a color-enabled TTY.
|
||||
if (typeof process !== "undefined" && process !== null) {
|
||||
colorsEnabled = ((ref1 = process.stdout) != null ? ref1.isTTY : void 0) && !((ref2 = process.env) != null ? ref2.NODE_DISABLE_COLORS : void 0);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue