-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Add cursorOffset
option for cursor translation
#1637
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
Conversation
Omg this is so exciting! I'll review this when I get a chance |
Shouldn't a |
Thanks @vjeux, I'm looking forward to the feedback! @CiGit, since the input text is a single string, rather than an array of lines, I decided to use a single zero-based integer as the |
index.js
Outdated
@@ -1,5 +1,8 @@ | |||
"use strict"; | |||
|
|||
const traverse = require('babel-traverse').default; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it work for flow AST?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, it didn't, but I added support in cbc1929
Sorry I don't have the time to review it right now, I'll take another pass later. |
See prettier#1637 (comment) For example: prettier tests/flow/any/any.js --cursor-offset 40 --parser flow # prints 44
No worries, I still have to refactor this to make it work with the range formatting changes (#1609) anyway. |
Should I wait for you to refactor it based on the findings in the other diff? I'd love to get this feature in! |
I haven't done any further work on this yet, so if you'd like to, feel free! The range-formatting feature does make it more complex, though, since this change introduces a separate Also note that if a range is specified the cursor could technically be inside or outside of it, rather than just on one of its endpoints, but that's not likely to happen in practice, so we can probably ignore those cases for now. EDIT: Now that I think about it, range-formatting probably doesn't currently take shebang handling into account, meaning that the formatted range will be offset by the shebang length... |
That way, we won't have to do any arithmetic on range and cursor locations when there is a shebang. See here for details: prettier#1637 (comment) This required changing comment printing such that comments that are actually shebangs are just ignored.
* Verify shebang tests against Babylon as well as Flow/Typescript * Pass entire text through formatWithShebang() to format() That way, we won't have to do any arithmetic on range and cursor locations when there is a shebang. See here for details: #1637 (comment) This required changing comment printing such that comments that are actually shebangs are just ignored. * Add curly brace after `if` See #1718 (comment)
cursorOffset
option for cursor translationcursorOffset
option for cursor translation
This addresses prettier#93 by adding a new option, `cursorOffset`, that tells prettier to determine the location of the cursor after the code has been formatted. This is accessible through the API via a new function, `formatWithCursor`, which returns a `{formatted: string, cursorOffset: ?number}`. Here's a usage example: ```js require("prettier").formatWithCursor(" 1", { cursorOffset: 2 }); // -> { formatted: '1;\n', cursorOffset: 1 } ```
It will print out the offset instead of the formatted output. This makes it easier to test. For example: echo ' 1' | prettier --stdin --cursor-offset 2 # prints 1
addAlignmentSize = addAlignmentSize || 0; | ||
|
||
const ast = parser.parse(text, opts); | ||
|
||
const formattedRangeOnly = formatRange(text, opts, ast); | ||
if (formattedRangeOnly) { | ||
return formattedRangeOnly; | ||
return { formatted: formattedRangeOnly }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cursor-translation functionality still needs to be unified with the range-formatting functionality (currently, either one or the other can be used). I think it might be easiest to do this by keeping track of the range start/end positions in the same way as the cursor position, then having an API that returns all 3 positions (if provided), and only formats the text necessary to satisfy the range.
@vjeux, is there any chance this can be merged and land in tomorrow's release? It definitely still needs some work (as mentioned in #1637 (comment)), but releasing it would allow editor integrations to start building upon it. |
src/printer.js
Outdated
path, | ||
p => genericPrint(p, options, printGenerically, args), | ||
options, | ||
args && args.needsSemi | ||
); | ||
|
||
if (path.getNode() === options.cursorNode) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably want to put this inside of printComments, there are a few places where we don't print the node but do print the comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that hadn't occurred to me. Fixed in d75906d
bin/prettier.js
Outdated
} | ||
}); | ||
} | ||
|
||
function writeOutput(result) { | ||
if (options.cursorOffset) { | ||
console.log(result.cursorOffset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we just output the cursor offset from CLI? We don't output the formatted text? Could we print the formatted text on stdout and the cursor on stderr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, good idea, I hadn't thought of that. Fixed in 8f72d51
Okay let's ship it tomorrow. I just asked a few questions. |
@@ -202,7 +204,7 @@ function format(input, opt) { | |||
return; | |||
} | |||
|
|||
return prettier.format(input, opt); | |||
return prettier.formatWithCursor(input, opt); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really upset that we have to introduce a new api and didn't think about this when we built .format :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:/ It's a somewhat unwieldy name, too
@@ -4,6 +4,7 @@ const validate = require("jest-validate").validate; | |||
const deprecatedConfig = require("./deprecated"); | |||
|
|||
const defaults = { | |||
cursorOffset: -1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't it be undefined
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, undefined
would make more sense. However, when I tried it, I got a validation error:
FAIL tests/cursor/jsfmt.spec.js
● translates cursor correctly in basic case
Error
● Validation Error:
Option "cursorOffset" must be of type:
undefined
but instead received:
number
Example:
{
"cursorOffset": undefined
}
✕ translates cursor correctly in basic case (3ms)
Here's the diff I tested:
diff --git a/index.js b/index.js
index 5a66c9cb..e9e67231 100644
--- a/index.js
+++ b/index.js
@@ -64,7 +64,7 @@ function formatWithCursor(text, opts, addAlignmentSize) {
}
let cursorOffset;
- if (opts.cursorOffset >= 0) {
+ if (opts.cursorOffset !== undefined) {
const cursorNodeAndParents = findNodeAtOffset(ast, opts.cursorOffset);
const cursorNode = cursorNodeAndParents.node;
if (cursorNode) {
diff --git a/src/options.js b/src/options.js
index 73d40426..956168af 100644
--- a/src/options.js
+++ b/src/options.js
@@ -4,7 +4,7 @@ const validate = require("jest-validate").validate;
const deprecatedConfig = require("./deprecated");
const defaults = {
- cursorOffset: -1,
+ cursorOffset: undefined,
rangeStart: 0,
rangeEnd: Infinity,
useTabs: false,
Is there a good way to get around this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohhh, okay. Can you not put it in defaults? Or somehow make it optional there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise don't worry, it's fine having -1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could remove it from defaults
, but then there's a validation warning:
console.warn node_modules/jest-validate/build/utils.js:43
● Validation Warning:
Unknown option "cursorOffset" with value 2 was found.
This is probably a typing mistake. Fixing it will remove this message.
Not sure if there's a way to make it optional, so I'll leave it as-is for now. We can always revisit it later.
} | ||
|
||
let cursorOffset; | ||
if (opts.cursorOffset >= 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!== undefined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(for future reference, the discussion about this is above)
const cursorNode = cursorNodeAndParents.node; | ||
if (cursorNode) { | ||
cursorOffset = opts.cursorOffset - util.locStart(cursorNode); | ||
opts.cursorNode = cursorNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't mutate please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, we're already mutating elsewhere with opts.newLine = guessLineEnding(text);
and opts.originalText = text.trimRight();
. I can take a look refactoring it tomorrow though, if you're set on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So sad. Would be nice to refactor but I can live with this for this PR.
src/doc-printer.js
Outdated
@@ -411,6 +416,16 @@ function printDocToString(doc, options) { | |||
} | |||
} | |||
} | |||
|
|||
const cursorPlaceholderIndex = out.indexOf(cursor.placeholder); | |||
if (cursorPlaceholderIndex >= 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!== -1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in c4e08d6
src/doc-printer.js
Outdated
const cursorPlaceholderIndex = out.indexOf(cursor.placeholder); | ||
if (cursorPlaceholderIndex >= 0) { | ||
const beforeCursor = out.slice(0, cursorPlaceholderIndex).join(""); | ||
options.cursorOffsetResult = beforeCursor.length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't mutate. Change the API such that it returns {formatted, cursor}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's much cleaner :) Fixed in 1fa6b4e
This lets us continue to print the formatted code, while also communicating the updated cursor position. See prettier#1637 (comment)
cursorOffset
option for cursor translationcursorOffset
option for cursor translation
Is it ready to review again? |
Yup, I'll follow up on any feedback tomorrow. |
Thanks for this! It's going to be awesome <3 |
Thanks for merging on such short notice before the release! I'll create a new issue for the task in #1637 (comment). |
Since the `cursorOffset` option (introduced in prettier#1637) works by tracking the cursor position relative to an AST node (rather than a CST token), it can produce incorrect results. See prettier#1981
Since the `cursorOffset` option (introduced in prettier#1637) works by tracking the cursor position relative to an AST node (rather than a CST token), it can produce incorrect results. See prettier#1981
* Add test demonstrating SourceElement-relative cursor translation See #1981 (comment) * Translate cursor relative to nearest node, not SourceElement This partially address #1981 See #1981 (comment) * Add test demonstrating incorrect cursor translation Since the `cursorOffset` option (introduced in #1637) works by tracking the cursor position relative to an AST node (rather than a CST token), it can produce incorrect results. See #1981
This addresses #93 by
adding a new option,
cursorOffset
, that tells prettier to determinethe location of the cursor after the code has been formatted. This is
accessible through the API via a new function,
formatWithCursor
, whichreturns a
{formatted: string, cursorOffset: ?number}
.Here's a usage example:
This implementation works roughly as described in #93 (comment).
It doesn't yet have tests, but I wanted to go ahead and get some
feedback on the API to see if this seems like the right way to go.
Thoughts?