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

Support async parser for embedded languages #13211

Merged
merged 34 commits into from Aug 6, 2022
Merged

Conversation

thorn0
Copy link
Member

@thorn0 thorn0 commented Aug 2, 2022

Description

Checklist

  • I’ve added tests to confirm my change works.
  • (If changing the API or CLI) I’ve documented the changes I’ve made (in the docs/ directory).
  • (If the change is user-facing) I’ve added my changes to changelog_unreleased/*/XXXX.md file following changelog_unreleased/TEMPLATE.md.
  • I’ve read the contributing guidelines.

Try the playground for this PR


function embed(path, print, textToDoc, options, language) {
Copy link
Member

@fisker fisker Aug 2, 2022

Choose a reason for hiding this comment

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

What if we ask embed to return print function, so we don't need add an extra detectEmbeddedLanguage?

function embed() {
  const language = ...;
  return () => formatMarkdown(...)
}

Copy link
Member

Choose a reason for hiding this comment

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

And the logic don't need separate, and check language twice.

Copy link
Member Author

Choose a reason for hiding this comment

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

Changing embed's return type from

Doc | undefined | Promise<Doc | undefined>

to

Doc | undefined | () => Promise<Doc | undefined>

is an interesting idea, but I've run out of time for now. :(

Copy link
Member

Choose a reason for hiding this comment

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

Take your time, no rush. There is no async parser in the world yet.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree that this would be a better API.

Copy link
Member

Choose a reason for hiding this comment

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

@fisker
Copy link
Member

fisker commented Aug 4, 2022

I have one more concern, if this get merged, the print can't be async. But some plugins call parse in print, and we already allow parse to be async, so the plugin will be broken. I guess the answer is move those parts to embed?

@prettier/plugin-pug format embed languages in this way.

//cc @Shinigami92

@thorn0
Copy link
Member Author

thorn0 commented Aug 4, 2022

I can't see where it calls parse in print

@thorn0
Copy link
Member Author

thorn0 commented Aug 4, 2022

I guess that should be moved to embed then. Isn't this what embed is for?

@fisker
Copy link
Member

fisker commented Aug 4, 2022

As mentioned reason here, thorn0#1 (comment), this plugin has to move it to embed and add a fallback in print.

@fisker
Copy link
Member

fisker commented Aug 4, 2022

@Shinigami92 ^

Any comment?

@Shinigami92
Copy link
Member

@Shinigami92 ^

Any comment?

Would make things much harder for me (I wrote the whole plugin in an iterative way instead of a AST walker because pug works much better and more performant this way)
But yeah, as this breaking change introduced by prettier I would assume a major version bump
and then I and other plugin maintainers need to deal with the breaking change 🤷

@fisker
Copy link
Member

fisker commented Aug 4, 2022

As I understand, you don't use print either, you traverse AST yourself?

In that case, you can also do print (async stuff) in parse... Some other plugins do this..

@thorn0
Copy link
Member Author

thorn0 commented Aug 4, 2022

@Shinigami92 Making printer.print async lead to a 20% slowdown. See #13158 (comment)

@thorn0
Copy link
Member Author

thorn0 commented Aug 4, 2022

@fisker print can be allowed to be async if it's not used recursively (await only the root call), but that would be a really clumsy API. We might want to keep it undocumented.

@fisker
Copy link
Member

fisker commented Aug 4, 2022

We also have printer.preprocess() maybe should allow it to be async?

docs/plugins.md Outdated Show resolved Hide resolved
@thorn0 thorn0 marked this pull request as ready for review August 5, 2022 20:38
Copy link
Member

@sosukesuzuki sosukesuzuki left a comment

Choose a reason for hiding this comment

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

Thanks @thorn0 and @fisker for the great work!

@fisker fisker merged commit 4d5b645 into prettier:next Aug 6, 2022
@fisker
Copy link
Member

fisker commented Aug 6, 2022

Will you try this?

@thorn0 thorn0 deleted the alt-async branch August 6, 2022 16:50
@thorn0
Copy link
Member Author

thorn0 commented Aug 6, 2022

No, I really doubt it'll do a better job than ignoredProperties already does.

@thorn0
Copy link
Member Author

thorn0 commented Aug 6, 2022

How about moving printer.massageAstNode.ignoredProperties to printer.ignoredProperties? It also should be used in other places where the AST in traversed (e.g., hasNode).

@fisker
Copy link
Member

fisker commented Aug 6, 2022

If we are going to do this, I prefer

getVistorKeys(node) {}

Before we implement the real vistor keys, we can just simply use

const ignored = new Set(['tokens'])
getVistorKeys(node) {
	return Object.keys(node).filter(key => !ignored.has(key))
}

@thorn0
Copy link
Member Author

thorn0 commented Aug 6, 2022

That doesn't look performance-friendly. It will need to be called for each node: a function call, creating an array of keys, and iterating it. A simple set.has check must be much cheaper.

@fisker
Copy link
Member

fisker commented Aug 6, 2022

a function call, creating an array of keys, and iterating it.

When traverse ast, normally you need "get keys/values", "check keys", " iterating it", aren't they the same?

for (const [key, value] of Object.entries(ast)) {
if (!ignoredProperties.has(key) && typeof value !== "function") {
newObj[key] = massageAST(value, options, ast);
}
}

for (const key in node) {
if (
Object.prototype.hasOwnProperty.call(node, key) &&
!ignoredProperties?.has(key)
) {
if (Array.isArray(node[key])) {
path.each(recurse, key);
} else {
path.call(recurse, key);
}
}
}

@thorn0
Copy link
Member Author

thorn0 commented Aug 6, 2022

Yep. Somehow I misread what you're proposing. It might be not that bad.

@thorn0
Copy link
Member Author

thorn0 commented Aug 6, 2022

But why do you think a simple ignored set won't be good enough? Do you have any specific cases in mind?

@fisker
Copy link
Member

fisker commented Aug 6, 2022

Not really, just every estree traver I used use vistorKeys, maybe we can check what the reason behind it.

@fisker
Copy link
Member

fisker commented Aug 6, 2022

acorn add walk for each ast type from beginning, acornjs/acorn@b660ff6, I guess to be more effective?

@thorn0
Copy link
Member Author

thorn0 commented Aug 6, 2022

Probably. Also maybe it's important to visit keys in a specific order.

@fisker
Copy link
Member

fisker commented Aug 6, 2022

Many parsers come with traverse function, eg https://github.com/graphql/graphql-js/blob/67aefd9c1daefceacf937363c2da9e1117e550d9/src/language/visitor.ts#L177, if we remove AstPath someday, we may want use this function or vistorKeys.

@thorn0
Copy link
Member Author

thorn0 commented Aug 6, 2022

How are we going to know where we are at the tree without the path?
(Side question: do the tests pass locally for the next branch for you? I get 18 failed, seems to be related to color codes.)

@fisker
Copy link
Member

fisker commented Aug 6, 2022

Link to that I ask meriyah to supply such ithing meriyah/meriyah#180

@fisker
Copy link
Member

fisker commented Aug 6, 2022

I get 18 failed, seems to be related to color code

I found it few hours ago , was working on it, but late here. Caused by #13208. I changed cli test to not print color by default, but not working.

@thorn0
Copy link
Member Author

thorn0 commented Aug 6, 2022

Ah, okay. I'm glad that it's not something local with my env.
And another question: https://github.com/prettier/prettier/pull/13211/files#diff-f825b86383b78b8c50baa02327cfd46f6c1a3fee472409d978e5454672a83417R13
Is it expected that plugins persist across format calls? Looks like a bug to me.

@fisker
Copy link
Member

fisker commented Aug 6, 2022

How are we going to know where we are at the tree without the path?

Eslint use .parent #10785

@fisker
Copy link
Member

fisker commented Aug 6, 2022

Is it expected that plugins persist across format calls?

Not sure.

Maybe what I occurred in #13201 is related, but I forgot what the possible bug is, this "Revert plugin change to avoid possible bug" commit fixed test.

@fisker fisker mentioned this pull request Aug 6, 2022
4 tasks
@thorn0
Copy link
Member Author

thorn0 commented Aug 6, 2022

Is it expected that plugins persist across format calls?

Tracked it down: #13235

medikoo pushed a commit to medikoo/prettier-elastic that referenced this pull request Jan 18, 2024
Co-authored-by: fisker Cheung <lionkay@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants