prettier-vscode icon indicating copy to clipboard operation
prettier-vscode copied to clipboard

Extension sometimes adds an extra closing tag when formatting

Open phoenixeliot opened this issue 2 years ago • 4 comments

Summary

Under some circumstances, Prettier will erroneously add an extra closing tag when formatting. From my testing, when it does this is inconsistent — I had to change my code a few times to get a simple example reproducing. I don't have a solid idea of when it does or does not happen, but for a given input it is deterministic.

This issue does not occur when running prettier via the command line.

I confirmed that it was this extension in particular (or the extension interacting with VS Code itself) by disabling Prettier, starting Extension bisect (to disable all other extensions), then enabling Prettier (so it was the only extension enabled now) and testing the behavior.

Github Repository to Reproduce Issue

https://github.com/phoenixeliot/prettier-vscode-closing-tag-bug

Steps To Reproduce:

  1. Clone and open the demo repo in VS Code
  2. Open demo.html
  3. With the command palette, run "Format Document With...", then select "Prettier"

Alternate Steps to Reproduce:

These steps match my actual use case, but are equivalent to the above using the demo repo.

  1. Start with this code:
<!DOCTYPE html>
<html lang="en">
  <body>
    <a class="footer-link" href="https://google.com">Words words words words words words words words words words</a>
  </body>
</html>
  1. Format it with prettier. This generates:
<!DOCTYPE html>
<html lang="en">
  <body>
    <a class="footer-link" href="https://google.com"
      >Words words words words words words words words words words</a
    >
  </body>
</html>
  1. Add spaces before and after the text to assist the formatter to not have those awkward line-broken open/close tags:
<!DOCTYPE html>
<html lang="en">
  <body>
    <a class="footer-link" href="https://google.com"
      > Words words words words words words words words words words </a
    >
  </body>
</html>
  1. Format the code using prettier again

Expected result

Formatted code should be this:

<!DOCTYPE html>
<html lang="en">
  <body>
    <a class="footer-link" href="https://google.com">
      Words words words words words words words words words words
    </a>
  </body>
</html>

Note that npx prettier ./demo.html (using prettier v2.3.1, same as the extension I think) produces this correct output, where the VS Code extension does not.

Actual result

Formatted code is actually this, with an extra inserted (and invalid) </a> on line 4.

<!DOCTYPE html>
<html lang="en">
  <body>
    <a class="footer-link" href="https://google.com"></a>
      Words words words words words words words words words words
    </a>
  </body>
</html>

Note: If you use cmd-Z, it appears that this happened in two 'undo steps' — the first undo removes the </a> and the second undo undoes the initial formatting.

Additional information

N/A

VS Code Version: Version: 1.59.0 (Universal)

Prettier Extension Version: v8.1.0

OS and version: macOS 11.5 (20G71)

Extra examples

These code samples do trigger the bug behavior:

<!DOCTYPE html>
<html lang="en">
  <body>
    <a class="footer-link" href="https://google.com"
      > Words words words words words words words words words words </a
    >
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <body>
    <div></div>
    <a class="footer-link" href="https://google.com"
      > Words words words words words words words words words words </a
    >
  </body>
</html>

These code samples below do not trigger this bug behavior, and behave correctly:

<!DOCTYPE html>
<html lang="en">
  <body>
    
    <a class="footer-link" href="https://google.com"
      > Words words words words words words words words words words </a
    >
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <body>
    <div>
      
    <a class="footer-link" href="https://google.com"
      > Words words words words words words words words words words </a
    >
  </div>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <body>
    <div>
      <div></div>

    <a class="footer-link" href="https://google.com"
    
      > Words words words words words words words words words words </a
    >
  </div>
  </body>
</html>

Using newlines instead of spaces to space it out:

<!DOCTYPE html>
<html lang="en">
  <body>
    <a class="footer-link" href="https://google.com"
      >
      Words words words words words words words words words words
      </a
    >
  </body>
</html>

Prettier Log Output

Debug logs
["INFO" - 3:27:35 PM] Extension Name: esbenp.prettier-vscode.
["INFO" - 3:27:35 PM] Extension Version: 8.1.0.
["DEBUG" - 3:27:35 PM] Enabling Prettier globally
{
  "languageSelector": [
    {
      "language": "javascript",
      "scheme": "file"
    },
    {
      "language": "mongo",
      "scheme": "file"
    },
    {
      "language": "javascriptreact",
      "scheme": "file"
    },
    {
      "language": "typescript",
      "scheme": "file"
    },
    {
      "language": "typescriptreact",
      "scheme": "file"
    },
    {
      "language": "json",
      "scheme": "file"
    },
    {
      "language": "jsonc",
      "scheme": "file"
    },
    {
      "language": "json5",
      "scheme": "file"
    },
    {
      "language": "css",
      "scheme": "file"
    },
    {
      "language": "postcss",
      "scheme": "file"
    },
    {
      "language": "less",
      "scheme": "file"
    },
    {
      "language": "scss",
      "scheme": "file"
    },
    {
      "language": "handlebars",
      "scheme": "file"
    },
    {
      "language": "graphql",
      "scheme": "file"
    },
    {
      "language": "markdown",
      "scheme": "file"
    },
    {
      "language": "mdx",
      "scheme": "file"
    },
    {
      "language": "html",
      "scheme": "file"
    },
    {
      "language": "vue",
      "scheme": "file"
    },
    {
      "language": "yaml",
      "scheme": "file"
    },
    {
      "language": "ansible",
      "scheme": "file"
    },
    {
      "language": "home-assistant",
      "scheme": "file"
    },
    {
      "language": "javascript",
      "scheme": "untitled"
    },
    {
      "language": "mongo",
      "scheme": "untitled"
    },
    {
      "language": "javascriptreact",
      "scheme": "untitled"
    },
    {
      "language": "typescript",
      "scheme": "untitled"
    },
    {
      "language": "typescriptreact",
      "scheme": "untitled"
    },
    {
      "language": "json",
      "scheme": "untitled"
    },
    {
      "language": "jsonc",
      "scheme": "untitled"
    },
    {
      "language": "json5",
      "scheme": "untitled"
    },
    {
      "language": "css",
      "scheme": "untitled"
    },
    {
      "language": "postcss",
      "scheme": "untitled"
    },
    {
      "language": "less",
      "scheme": "untitled"
    },
    {
      "language": "scss",
      "scheme": "untitled"
    },
    {
      "language": "handlebars",
      "scheme": "untitled"
    },
    {
      "language": "graphql",
      "scheme": "untitled"
    },
    {
      "language": "markdown",
      "scheme": "untitled"
    },
    {
      "language": "mdx",
      "scheme": "untitled"
    },
    {
      "language": "html",
      "scheme": "untitled"
    },
    {
      "language": "vue",
      "scheme": "untitled"
    },
    {
      "language": "yaml",
      "scheme": "untitled"
    },
    {
      "language": "ansible",
      "scheme": "untitled"
    },
    {
      "language": "home-assistant",
      "scheme": "untitled"
    },
    {
      "language": "jsonc",
      "scheme": "vscode-userdata"
    }
  ],
  "rangeLanguageSelector": [
    {
      "language": "javascript",
      "scheme": "file"
    },
    {
      "language": "javascriptreact",
      "scheme": "file"
    },
    {
      "language": "typescript",
      "scheme": "file"
    },
    {
      "language": "typescriptreact",
      "scheme": "file"
    },
    {
      "language": "json",
      "scheme": "file"
    },
    {
      "language": "graphql",
      "scheme": "file"
    },
    {
      "language": "javascript",
      "scheme": "untitled"
    },
    {
      "language": "javascriptreact",
      "scheme": "untitled"
    },
    {
      "language": "typescript",
      "scheme": "untitled"
    },
    {
      "language": "typescriptreact",
      "scheme": "untitled"
    },
    {
      "language": "json",
      "scheme": "untitled"
    },
    {
      "language": "graphql",
      "scheme": "untitled"
    }
  ]
}
["DEBUG" - 3:27:35 PM] Enabling Prettier for Workspace /Users/phoenix/Code/bugreports/prettier-closing-tag-bug
{
  "languageSelector": [
    {
      "pattern": "/Users/phoenix/Code/bugreports/prettier-closing-tag-bug/**/*.{js,_js,bones,cjs,es,es6,frag,gs,jake,jsb,jscad,jsfl,jsm,jss,mjs,njs,pac,sjs,ssjs,xsjs,xsjslib,wxs,js.flow,jsx,ts,tsx,json,avsc,geojson,gltf,har,ice,JSON-tmLanguage,mcmeta,tfstate,tfstate.backup,topojson,webapp,webmanifest,yy,yyp,jsonc,sublime-build,sublime-commands,sublime-completions,sublime-keymap,sublime-macro,sublime-menu,sublime-mousemap,sublime-project,sublime-settings,sublime-theme,sublime-workspace,sublime_metrics,sublime_session,json5,css,wxss,pcss,postcss,less,scss,handlebars,hbs,graphql,gql,graphqls,md,markdown,mdown,mdwn,mkd,mkdn,mkdown,ronn,scd,workbook,mdx,component.html,html,htm,html.hl,inc,xht,xhtml,mjml,vue,yml,mir,reek,rviz,sublime-syntax,syntax,yaml,yaml-tmlanguage,yaml.sed,yml.mysql}",
      "scheme": "file"
    },
    {
      "language": "javascript",
      "scheme": "file"
    },
    {
      "language": "mongo",
      "scheme": "file"
    },
    {
      "language": "javascriptreact",
      "scheme": "file"
    },
    {
      "language": "typescript",
      "scheme": "file"
    },
    {
      "language": "typescriptreact",
      "scheme": "file"
    },
    {
      "language": "json",
      "scheme": "file"
    },
    {
      "language": "jsonc",
      "scheme": "file"
    },
    {
      "language": "json5",
      "scheme": "file"
    },
    {
      "language": "css",
      "scheme": "file"
    },
    {
      "language": "postcss",
      "scheme": "file"
    },
    {
      "language": "less",
      "scheme": "file"
    },
    {
      "language": "scss",
      "scheme": "file"
    },
    {
      "language": "handlebars",
      "scheme": "file"
    },
    {
      "language": "graphql",
      "scheme": "file"
    },
    {
      "language": "markdown",
      "scheme": "file"
    },
    {
      "language": "mdx",
      "scheme": "file"
    },
    {
      "language": "html",
      "scheme": "file"
    },
    {
      "language": "vue",
      "scheme": "file"
    },
    {
      "language": "yaml",
      "scheme": "file"
    },
    {
      "language": "ansible",
      "scheme": "file"
    },
    {
      "language": "home-assistant",
      "scheme": "file"
    },
    {
      "language": "javascript",
      "scheme": "untitled"
    },
    {
      "language": "mongo",
      "scheme": "untitled"
    },
    {
      "language": "javascriptreact",
      "scheme": "untitled"
    },
    {
      "language": "typescript",
      "scheme": "untitled"
    },
    {
      "language": "typescriptreact",
      "scheme": "untitled"
    },
    {
      "language": "json",
      "scheme": "untitled"
    },
    {
      "language": "jsonc",
      "scheme": "untitled"
    },
    {
      "language": "json5",
      "scheme": "untitled"
    },
    {
      "language": "css",
      "scheme": "untitled"
    },
    {
      "language": "postcss",
      "scheme": "untitled"
    },
    {
      "language": "less",
      "scheme": "untitled"
    },
    {
      "language": "scss",
      "scheme": "untitled"
    },
    {
      "language": "handlebars",
      "scheme": "untitled"
    },
    {
      "language": "graphql",
      "scheme": "untitled"
    },
    {
      "language": "markdown",
      "scheme": "untitled"
    },
    {
      "language": "mdx",
      "scheme": "untitled"
    },
    {
      "language": "html",
      "scheme": "untitled"
    },
    {
      "language": "vue",
      "scheme": "untitled"
    },
    {
      "language": "yaml",
      "scheme": "untitled"
    },
    {
      "language": "ansible",
      "scheme": "untitled"
    },
    {
      "language": "home-assistant",
      "scheme": "untitled"
    },
    {
      "language": "jsonc",
      "scheme": "vscode-userdata"
    }
  ],
  "rangeLanguageSelector": [
    {
      "language": "javascript",
      "scheme": "file"
    },
    {
      "language": "javascriptreact",
      "scheme": "file"
    },
    {
      "language": "typescript",
      "scheme": "file"
    },
    {
      "language": "typescriptreact",
      "scheme": "file"
    },
    {
      "language": "json",
      "scheme": "file"
    },
    {
      "language": "graphql",
      "scheme": "file"
    },
    {
      "language": "javascript",
      "scheme": "untitled"
    },
    {
      "language": "javascriptreact",
      "scheme": "untitled"
    },
    {
      "language": "typescript",
      "scheme": "untitled"
    },
    {
      "language": "typescriptreact",
      "scheme": "untitled"
    },
    {
      "language": "json",
      "scheme": "untitled"
    },
    {
      "language": "graphql",
      "scheme": "untitled"
    }
  ]
}
["INFO" - 3:27:42 PM] Formatting /Users/phoenix/Code/bugreports/prettier-closing-tag-bug/demo.html
["INFO" - 3:27:42 PM] Using ignore file (if present) at /Users/phoenix/Code/bugreports/prettier-closing-tag-bug/.prettierignore
["INFO" - 3:27:42 PM] File Info:
{
  "ignored": false,
  "inferredParser": "html"
}
["INFO" - 3:27:42 PM] No local configuration (i.e. .prettierrc or .editorconfig) detected, falling back to VS Code configuration
["INFO" - 3:27:42 PM] Prettier Options:
{
  "arrowParens": "always",
  "bracketSpacing": true,
  "endOfLine": "lf",
  "htmlWhitespaceSensitivity": "css",
  "insertPragma": false,
  "jsxBracketSameLine": false,
  "jsxSingleQuote": false,
  "printWidth": 80,
  "proseWrap": "preserve",
  "quoteProps": "as-needed",
  "requirePragma": false,
  "semi": true,
  "singleQuote": false,
  "tabWidth": 2,
  "trailingComma": "es5",
  "useTabs": false,
  "vueIndentScriptAndStyle": false,
  "filepath": "/Users/phoenix/Code/bugreports/prettier-closing-tag-bug/demo.html",
  "parser": "html"
}
["INFO" - 3:27:42 PM] Formatting completed in 48.765625ms.

phoenixeliot avatar Aug 05 '21 21:08 phoenixeliot

I confirmed this happens with another tag (e.g. p, span, div, etc).

tatsuyah avatar Nov 26 '21 03:11 tatsuyah

Hi,

~This is not a bug.~ This is not a bug of prettier-vscode. I've inspected and confirmed that the return value of provideDocumentFormattingEdits is correct as shown below.

<!DOCTYPE html>
<html lang="en">
  <body>
    <a class="footer-link" href="https://google.com">
      Words words words words words words words words words words
    </a>
  </body>
</html>

https://github.com/prettier/prettier-vscode/blob/4a9ad9d27c23d200e8103a09bbb0f78cc5b0570b/src/PrettierEditProvider.ts#L39-L49

And then I've found that it's because a conflict of prettier's html parser and vscode's built-in "HTML: Auto Closing Tags" settings.

スクリーンショット 2021-11-26 21 07 17

To avoid this weird behavior, you just need to disable this vscode settings.

tatsuyah avatar Nov 26 '21 12:11 tatsuyah

I think it might be better vscode handle this issue because prettier-vscode returns right value from provideDocumentFormattingEdits method and there aren't many things to do to fix it. So, I created an issue about it. https://github.com/microsoft/vscode/issues/137938

tatsuyah avatar Nov 26 '21 13:11 tatsuyah

Awesome, thank you for doing that investigation :)

phoenixeliot avatar Nov 27 '21 21:11 phoenixeliot

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

github-actions[bot] avatar Nov 19 '22 00:11 github-actions[bot]