Skip to content

Commit 44bfd9b

Browse files
committed
Change range formatting approach
NOTE: This doesn't pass its test yet. Note that since we're reading the indentation from the first line, it is expected not to change. However, a semicolon is added, and the lines outside the range are not changed. The new approach is roughly: * Require that the range exactly covers an integer number of lines of the input * Detect the indentation of the line the range starts on * Format the range's substring using `printAstToDoc` * Add enough `indent`s to the doc to restore the detected indentation * Format the doc to a string with `printDocToString` * Prepend/append the original input before/after the range See #1609 (comment) --- Given `tests/range/range.js`, run the following: prettier tests/range/range.js --range-start 165 --range-end 246 See the range's text with: dd if=tests/range/range.js ibs=1 skip=165 count=81 2>/dev/null
1 parent 8c99117 commit 44bfd9b

File tree

8 files changed

+72
-74
lines changed

8 files changed

+72
-74
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,8 @@ Prettier ships with a handful of customizable format options, usable in both the
254254
| **JSX Brackets on Same Line** - Put the `>` of a multi-line JSX element at the end of the last line instead of being alone on the next line | `false` | `--jsx-bracket-same-line` | `jsxBracketSameLine: <bool>` |
255255
| **Parser** - Specify which parser to use. | `babylon` | <code>--parser <flow&#124;babylon></code> | <code>parser: "<flow&#124;babylon>"</code> |
256256
| **Semicolons** - Print semicolons at the ends of statements.<br /><br />Valid options: <br /> - `true` - add a semicolon at the end of every statement <br /> - `false` - only add semicolons at the beginning of lines that may introduce ASI failures | `true` | `--no-semi` | `semi: <bool>` |
257-
| **Range Start** - Format code starting at a given character offset. | `0` | `--range-start <int>` | `rangeStart: <int>` |
258-
| **Range End** - Format code ending at a given character offset (exclusive). | `Infinity` | `--range-end <int>` | `rangeEnd: <int>` |
257+
| **Range Start** - Format code starting at a given character offset. The range must begin immediately before the start of a line. | `0` | `--range-start <int>` | `rangeStart: <int>` |
258+
| **Range End** - Format code ending at a given character offset (exclusive). The range must end immediately after the end of a line. | `Infinity` | `--range-end <int>` | `rangeEnd: <int>` |
259259

260260
### Excluding code from formatting
261261

bin/prettier.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ if (argv["help"] || (!filepatterns.length && !stdin)) {
219219
" --trailing-comma <none|es5|all>\n" +
220220
" Print trailing commas wherever possible. Defaults to none.\n" +
221221
" --parser <flow|babylon> Specify which parse to use. Defaults to babylon.\n" +
222-
" --range-start <int> Format code starting at a given character offset. Defaults to 0.\n" +
223-
" --range-end <int> Format code ending at a given character offset (exclusive). Defaults to Infinity.\n" +
222+
" --range-start <int> Format code starting at a given character offset. Must be at the start of a line. Defaults to 0.\n" +
223+
" --range-end <int> Format code ending at a given character offset (exclusive). Must be at the end of a line. Defaults to Infinity.\n" +
224224
" --no-color Do not colorize error messages.\n" +
225225
" --version or -v Print Prettier version.\n" +
226226
"\n"

index.js

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"use strict";
22

33
const comments = require("./src/comments");
4-
const util = require('./src/util');
54
const version = require("./package.json").version;
65
const printAstToDoc = require("./src/printer").printAstToDoc;
76
const printDocToString = require("./src/doc-printer").printDocToString;
@@ -28,7 +27,7 @@ function attachComments(text, ast, opts) {
2827
return astComments;
2928
}
3029

31-
function ensureAllCommentsPrinted(astComments, opts) {
30+
function ensureAllCommentsPrinted(astComments) {
3231
for (let i = 0; i < astComments.length; ++i) {
3332
if (astComments[i].value.trim() === "prettier-ignore") {
3433
// If there's a prettier-ignore, we're not printing that sub-tree so we
@@ -38,10 +37,7 @@ function ensureAllCommentsPrinted(astComments, opts) {
3837
}
3938

4039
astComments.forEach(comment => {
41-
if (
42-
!comment.printed &&
43-
!util.isOutsideRange(comment, opts.rangeStart, opts.rangeEnd)
44-
) {
40+
if (!comment.printed) {
4541
throw new Error(
4642
'Comment "' +
4743
comment.value.trim() +
@@ -52,16 +48,43 @@ function ensureAllCommentsPrinted(astComments, opts) {
5248
});
5349
}
5450

55-
function format(text, opts) {
51+
function format(text, opts, addIndents = 0) {
52+
const rangeStart = opts.rangeStart;
53+
const rangeEnd = Math.min(opts.rangeEnd, text.length)
54+
55+
if (0 < rangeStart || rangeEnd < text.length) {
56+
const rangeString = text.substring(rangeStart, rangeEnd)
57+
const numIndents = countIndents(rangeString, opts);
58+
59+
const rangeFormatted = format(rangeString, Object.assign({}, opts, {
60+
rangeStart: 0,
61+
rangeEnd: Infinity,
62+
printWidth: opts.printWidth - numIndents * opts.tabWidth
63+
}), numIndents);
64+
65+
return text.slice(0, rangeStart) + rangeFormatted + text.slice(rangeEnd)
66+
}
67+
5668
const ast = parser.parse(text, opts);
5769
const astComments = attachComments(text, ast, opts);
58-
const doc = printAstToDoc(ast, opts);
70+
const doc = printAstToDoc(ast, opts, addIndents)
5971
opts.newLine = guessLineEnding(text);
6072
const str = printDocToString(doc, opts);
61-
ensureAllCommentsPrinted(astComments, opts);
73+
ensureAllCommentsPrinted(astComments);
74+
// Remove added indentation after last newline
75+
if (addIndents > 0) {
76+
return str.trimRight() + opts.newLine;
77+
}
6278
return str;
6379
}
6480

81+
function countIndents(text, opts) {
82+
const tabSpaces = ' '.repeat(opts.tabWidth);
83+
const indentation = text.match(/^([ \t]*)/)[1].replace(/\t/g, tabSpaces);
84+
const numIndents = Math.floor(indentation.length / opts.tabWidth);
85+
return numIndents;
86+
}
87+
6588
function formatWithShebang(text, opts) {
6689
if (!text.startsWith("#!")) {
6790
return format(text, opts);

src/printer.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,7 @@ function genericPrint(path, options, printPath, args) {
6464
var node = path.getValue();
6565

6666
// Escape hatch
67-
if (
68-
hasPrettierIgnoreComment(node) ||
69-
util.isOutsideRange(node, options.rangeStart, options.rangeEnd)
70-
) {
67+
if (hasPrettierIgnoreComment(node)) {
7168
return options.originalText.slice(util.locStart(node), util.locEnd(node));
7269
}
7370

@@ -4251,7 +4248,7 @@ function removeLines(doc) {
42514248
});
42524249
}
42534250

4254-
function printAstToDoc(ast, options) {
4251+
function printAstToDoc(ast, options, addIndents = 0) {
42554252
function printGenerically(path, args) {
42564253
return comments.printComments(
42574254
path,
@@ -4263,7 +4260,14 @@ function printAstToDoc(ast, options) {
42634260

42644261
const doc = printGenerically(FastPath.from(ast));
42654262
docUtils.propagateBreaks(doc);
4266-
return doc;
4263+
return addIndentsToDoc(doc, addIndents);
4264+
}
4265+
4266+
function addIndentsToDoc(doc, numIndents) {
4267+
if (numIndents <= 0) {
4268+
return doc;
4269+
}
4270+
return addIndentsToDoc(indent(doc), numIndents - 1);
42674271
}
42684272

42694273
module.exports = { printAstToDoc };

src/util.js

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -320,16 +320,6 @@ function isBlockComment(comment) {
320320
return comment.type === "Block" || comment.type === "CommentBlock";
321321
}
322322

323-
function isOutsideRange(node, rangeStart, rangeEnd) {
324-
if (!node) {
325-
return false;
326-
}
327-
const nodeStart = locStart(node);
328-
const nodeEnd = locEnd(node);
329-
const isOutside = nodeEnd < rangeStart || nodeStart >= rangeEnd;
330-
return isOutside;
331-
}
332-
333323
module.exports = {
334324
getPrecedence,
335325
isExportDeclaration,
@@ -351,6 +341,5 @@ module.exports = {
351341
setLocEnd,
352342
startsWithNoLookaheadToken,
353343
hasBlockComments,
354-
isBlockComment,
355-
isOutsideRange
344+
isBlockComment
356345
};
Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,24 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

33
exports[`range.js 1`] = `
4-
// will NOT be formatted
5-
function ugly ( has , unPrettier , param,spacing) {
6-
7-
}
8-
9-
// will be formatted
10-
function ugly ( has , unPrettier , param,spacing) {
11-
\`multiline template string
12-
with too much indentation\`
13-
}
14-
15-
// will NOT be formatted
16-
function ugly ( has , unPrettier , param,spacing) {
17-
4+
function ugly ( {a=1, b = 2 } ) {
5+
function ugly ( {a=1, b = 2 } ) {
6+
function ugly ( {a=1, b = 2 } ) {
7+
\`multiline template string
8+
with too much indentation\`
9+
// The [165, 246) range selects the above two lines, including the second newline
10+
}
11+
}
1812
}
1913
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20-
// will NOT be formatted
21-
function ugly ( has , unPrettier , param,spacing) {
22-
23-
}
24-
25-
// will be formatted
26-
function ugly(has, unPrettier, param, spacing) {
27-
\`multiline template string
28-
with too much indentation\`;
29-
}
30-
31-
// will NOT be formatted
32-
function ugly ( has , unPrettier , param,spacing) {
33-
14+
function ugly ( {a=1, b = 2 } ) {
15+
function ugly ( {a=1, b = 2 } ) {
16+
function ugly ( {a=1, b = 2 } ) {
17+
\`multiline template string
18+
with too much indentation\`;
19+
// The [165, 246) range selects the above two lines, including the second newline
20+
}
21+
}
3422
}
3523
3624
`;

tests/range/jsfmt.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
run_spec(__dirname, { rangeStart: 103, rangeEnd: 159 });
1+
run_spec(__dirname, { rangeStart: 165, rangeEnd: 246 });

tests/range/range.js

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1-
// will NOT be formatted
2-
function ugly ( has , unPrettier , param,spacing) {
3-
4-
}
5-
6-
// will be formatted
7-
function ugly ( has , unPrettier , param,spacing) {
8-
`multiline template string
9-
with too much indentation`
10-
}
11-
12-
// will NOT be formatted
13-
function ugly ( has , unPrettier , param,spacing) {
14-
1+
function ugly ( {a=1, b = 2 } ) {
2+
function ugly ( {a=1, b = 2 } ) {
3+
function ugly ( {a=1, b = 2 } ) {
4+
`multiline template string
5+
with too much indentation`
6+
// The [165, 246) range selects the above two lines, including the second newline
7+
}
8+
}
159
}

0 commit comments

Comments
 (0)