prettier 1.4.0 → 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +356 -380
- data/README.md +22 -11
- data/package.json +4 -4
- data/rubocop.yml +3 -0
- data/src/haml/parser.js +2 -2
- data/src/haml/parser.rb +0 -2
- data/src/parser/getNetcat.js +17 -10
- data/src/parser/netcat.js +0 -2
- data/src/parser/parseSync.js +25 -15
- data/src/parser/requestParse.js +3 -3
- data/src/parser/server.rb +7 -2
- data/src/plugin.js +7 -0
- data/src/rbs/parser.js +2 -2
- data/src/rbs/parser.rb +8 -4
- data/src/ruby/embed.js +61 -13
- data/src/ruby/nodes/args.js +86 -24
- data/src/ruby/nodes/arrays.js +36 -33
- data/src/ruby/nodes/class.js +17 -27
- data/src/ruby/nodes/commands.js +1 -1
- data/src/ruby/nodes/conditionals.js +1 -1
- data/src/ruby/nodes/hashes.js +28 -14
- data/src/ruby/nodes/heredocs.js +5 -3
- data/src/ruby/nodes/loops.js +4 -10
- data/src/ruby/nodes/methods.js +4 -11
- data/src/ruby/nodes/statements.js +3 -5
- data/src/ruby/parser.js +2 -2
- data/src/ruby/parser.rb +81 -5
- data/src/ruby/printer.js +8 -3
- data/src/utils.js +2 -1
- data/src/utils/inlineEnsureParens.js +8 -1
- data/src/utils/isEmptyBodyStmt.js +7 -0
- data/src/utils/isEmptyStmts.js +9 -5
- data/src/utils/literallineWithoutBreakParent.js +7 -0
- data/src/utils/printEmptyCollection.js +9 -2
- metadata +5 -4
- data/src/utils/literalLineNoBreak.js +0 -7
    
        data/README.md
    CHANGED
    
    | @@ -128,17 +128,19 @@ The `prettier` executable is now installed and ready for use: | |
| 128 128 |  | 
| 129 129 | 
             
            Below are the options (from [`src/plugin.js`](src/plugin.js)) that `@prettier/plugin-ruby` currently supports:
         | 
| 130 130 |  | 
| 131 | 
            -
            | API Option | 
| 132 | 
            -
            |  | 
| 133 | 
            -
            | `printWidth` | 
| 134 | 
            -
            | `requirePragma` | 
| 135 | 
            -
            | `rubyArrayLiteral` | 
| 136 | 
            -
            | `rubyHashLabel` | 
| 137 | 
            -
            | `rubyModifier` | 
| 138 | 
            -
            | ` | 
| 139 | 
            -
             | 
| 140 | 
            -
            | ` | 
| 141 | 
            -
            | ` | 
| 131 | 
            +
            | API Option          | CLI Option              | Default | Description                                                                                                                                                                              |
         | 
| 132 | 
            +
            | ------------------- | ----------------------- | :-----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
         | 
| 133 | 
            +
            | `printWidth`        | `--print-width`         |  `80`   | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)).                                                                                         |
         | 
| 134 | 
            +
            | `requirePragma`     | `--require-pragma`      | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)).                                                                                      |
         | 
| 135 | 
            +
            | `rubyArrayLiteral`  | `--ruby-array-literal`  | `true`  | When possible, favor the use of string and symbol array literals.                                                                                                                        |
         | 
| 136 | 
            +
            | `rubyHashLabel`     | `--ruby-hash-label`     | `true`  | When possible, uses the shortened hash key syntax, as opposed to hash rockets.                                                                                                           |
         | 
| 137 | 
            +
            | `rubyModifier`      | `--ruby-modifier`       | `true`  | When it fits on one line, allows while and until statements to use the modifier form.                                                                                                    |
         | 
| 138 | 
            +
            | `rubyNetcatCommand` | `--ruby-netcat-command` |         | The prefix of the command to execute to communicate between the node.js process and the Ruby process. (For example, `"nc -U"` or `"telnet -u"`) Normally you should not set this option. |
         | 
| 139 | 
            +
             | 
| 140 | 
            +
            | `rubySingleQuote` | `--ruby-single-quote` | `true` | When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals. |
         | 
| 141 | 
            +
            | `rubyToProc` | `--ruby-to-proc` | `false` | When possible, convert blocks to the more concise `Symbol#to_proc` syntax. |
         | 
| 142 | 
            +
            | `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). |
         | 
| 143 | 
            +
            | `trailingComma` | `--trailing-comma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#trailing-comma)). `"es5"` is equivalent to `true`. |
         | 
| 142 144 |  | 
| 143 145 | 
             
            Any of these can be added to your existing [prettier configuration
         | 
| 144 146 | 
             
            file](https://prettier.io/docs/en/configuration.html). For example:
         | 
| @@ -163,11 +165,20 @@ with some of RuboCop's functionality. | |
| 163 165 | 
             
            Prettier provides a RuboCop configuration file to disable the rules which clash.
         | 
| 164 166 | 
             
            To enable, add the following config at the top of your project's `.rubocop.yml`:
         | 
| 165 167 |  | 
| 168 | 
            +
            #### Ruby gem
         | 
| 169 | 
            +
             | 
| 166 170 | 
             
            ```yaml
         | 
| 167 171 | 
             
            inherit_gem:
         | 
| 168 172 | 
             
              prettier: rubocop.yml
         | 
| 169 173 | 
             
            ```
         | 
| 170 174 |  | 
| 175 | 
            +
            #### `npm` package
         | 
| 176 | 
            +
             | 
| 177 | 
            +
            ```yaml
         | 
| 178 | 
            +
            inherit_from:
         | 
| 179 | 
            +
              - node_modules/@prettier/plugin-ruby/rubocop.yml
         | 
| 180 | 
            +
            ```
         | 
| 181 | 
            +
             | 
| 171 182 | 
             
            ## Contributing
         | 
| 172 183 |  | 
| 173 184 | 
             
            Check out our [contributing guide](CONTRIBUTING.md). Bug reports and pull requests are welcome on GitHub at https://github.com/prettier/plugin-ruby.
         | 
    
        data/package.json
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            {
         | 
| 2 2 | 
             
              "name": "@prettier/plugin-ruby",
         | 
| 3 | 
            -
              "version": "1.4 | 
| 3 | 
            +
              "version": "1.5.4",
         | 
| 4 4 | 
             
              "description": "prettier plugin for the Ruby programming language",
         | 
| 5 5 | 
             
              "main": "src/plugin.js",
         | 
| 6 6 | 
             
              "scripts": {
         | 
| @@ -22,9 +22,9 @@ | |
| 22 22 | 
             
                "prettier": ">=1.10"
         | 
| 23 23 | 
             
              },
         | 
| 24 24 | 
             
              "devDependencies": {
         | 
| 25 | 
            -
                "eslint": "^7. | 
| 26 | 
            -
                "eslint-config-prettier": "^ | 
| 27 | 
            -
                "husky": "^ | 
| 25 | 
            +
                "eslint": "^7.22.0",
         | 
| 26 | 
            +
                "eslint-config-prettier": "^8.0.0",
         | 
| 27 | 
            +
                "husky": "^5.0.9",
         | 
| 28 28 | 
             
                "jest": "^26.0.0",
         | 
| 29 29 | 
             
                "pretty-quick": "^3.1.0"
         | 
| 30 30 | 
             
              },
         | 
    
        data/rubocop.yml
    CHANGED
    
    
    
        data/src/haml/parser.js
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            const parseSync = require("../parser/parseSync");
         | 
| 2 2 |  | 
| 3 | 
            -
            const parse = (text, _parsers,  | 
| 4 | 
            -
              return parseSync("haml", text);
         | 
| 3 | 
            +
            const parse = (text, _parsers, opts) => {
         | 
| 4 | 
            +
              return parseSync("haml", text, opts);
         | 
| 5 5 | 
             
            };
         | 
| 6 6 |  | 
| 7 7 | 
             
            const pragmaPattern = /^\s*-#\s*@(prettier|format)/;
         | 
    
        data/src/haml/parser.rb
    CHANGED
    
    
    
        data/src/parser/getNetcat.js
    CHANGED
    
    | @@ -16,35 +16,42 @@ function hasCommand(name) { | |
| 16 16 |  | 
| 17 17 | 
             
            // Finds an netcat-like adapter to use for sending data to a socket. We order
         | 
| 18 18 | 
             
            // these by likelihood of being found so we can avoid some shell-outs.
         | 
| 19 | 
            -
            function  | 
| 19 | 
            +
            function getCommandAndArgs() {
         | 
| 20 20 | 
             
              if (hasCommand("nc")) {
         | 
| 21 | 
            -
                return ["nc", "-U"];
         | 
| 21 | 
            +
                return ["nc", ["-U"]];
         | 
| 22 22 | 
             
              }
         | 
| 23 23 |  | 
| 24 24 | 
             
              if (hasCommand("telnet")) {
         | 
| 25 | 
            -
                return ["telnet", "-u"];
         | 
| 25 | 
            +
                return ["telnet", ["-u"]];
         | 
| 26 26 | 
             
              }
         | 
| 27 27 |  | 
| 28 28 | 
             
              if (hasCommand("ncat")) {
         | 
| 29 | 
            -
                return ["ncat", "-U"];
         | 
| 29 | 
            +
                return ["ncat", ["-U"]];
         | 
| 30 30 | 
             
              }
         | 
| 31 31 |  | 
| 32 32 | 
             
              if (hasCommand("socat")) {
         | 
| 33 | 
            -
                return ["socat", "-"];
         | 
| 33 | 
            +
                return ["socat", ["-"]];
         | 
| 34 34 | 
             
              }
         | 
| 35 35 |  | 
| 36 | 
            -
              return ["node", require.resolve("./netcat.js")];
         | 
| 36 | 
            +
              return ["node", [require.resolve("./netcat.js")]];
         | 
| 37 37 | 
             
            }
         | 
| 38 38 |  | 
| 39 39 | 
             
            let command;
         | 
| 40 | 
            -
            let  | 
| 40 | 
            +
            let args;
         | 
| 41 41 |  | 
| 42 | 
            -
            function getNetcat() {
         | 
| 42 | 
            +
            function getNetcat(opts) {
         | 
| 43 43 | 
             
              if (!command) {
         | 
| 44 | 
            -
                 | 
| 44 | 
            +
                if (opts.rubyNetcatCommand) {
         | 
| 45 | 
            +
                  const splits = opts.rubyNetcatCommand.split(" ");
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  command = splits[0];
         | 
| 48 | 
            +
                  args = splits.slice(1);
         | 
| 49 | 
            +
                } else {
         | 
| 50 | 
            +
                  [command, args] = getCommandAndArgs();
         | 
| 51 | 
            +
                }
         | 
| 45 52 | 
             
              }
         | 
| 46 53 |  | 
| 47 | 
            -
              return { command,  | 
| 54 | 
            +
              return { command, args };
         | 
| 48 55 | 
             
            }
         | 
| 49 56 |  | 
| 50 57 | 
             
            module.exports = getNetcat;
         | 
    
        data/src/parser/netcat.js
    CHANGED
    
    | @@ -5,11 +5,9 @@ | |
| 5 5 | 
             
            const { createConnection } = require("net");
         | 
| 6 6 |  | 
| 7 7 | 
             
            const sock = process.argv[process.argv.length - 1];
         | 
| 8 | 
            -
             | 
| 9 8 | 
             
            const client = createConnection(sock, () => process.stdin.pipe(client));
         | 
| 10 9 |  | 
| 11 10 | 
             
            client.on("data", (data) => process.stdout.write(data));
         | 
| 12 | 
            -
             | 
| 13 11 | 
             
            client.on("error", (error) => {
         | 
| 14 12 | 
             
              console.error(error);
         | 
| 15 13 | 
             
            });
         | 
    
        data/src/parser/parseSync.js
    CHANGED
    
    | @@ -4,27 +4,37 @@ const requestParse = require("./requestParse"); | |
| 4 4 | 
             
            // like it) here since Prettier requires the results of `parse` to be
         | 
| 5 5 | 
             
            // synchronous and Node.js does not offer a mechanism for synchronous socket
         | 
| 6 6 | 
             
            // requests.
         | 
| 7 | 
            -
            function parseSync(parser, source) {
         | 
| 8 | 
            -
              const  | 
| 7 | 
            +
            function parseSync(parser, source, opts) {
         | 
| 8 | 
            +
              const { stdout, stderr, status } = requestParse(parser, source, opts);
         | 
| 9 9 |  | 
| 10 | 
            -
               | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 10 | 
            +
              // We need special handling in case the user's version of nc doesn't support
         | 
| 11 | 
            +
              // using unix sockets.
         | 
| 12 | 
            +
              if (stderr.includes("invalid option -- U")) {
         | 
| 13 | 
            +
                throw new Error(`
         | 
| 14 | 
            +
                  @prettier/plugin-ruby uses netcat to communicate over unix sockets between
         | 
| 15 | 
            +
                  the node.js process running prettier and an underlying Ruby process used
         | 
| 16 | 
            +
                  for parsing. Unfortunately the version of netcat that you have installed
         | 
| 17 | 
            +
                  (GNU netcat) does not support unix sockets. To solve this either uninstall
         | 
| 18 | 
            +
                  GNU netcat and use a different implementation, or change the value of the
         | 
| 19 | 
            +
                  rubyNetcatCommand option in your prettier configuration.
         | 
| 20 | 
            +
                `);
         | 
| 21 | 
            +
              }
         | 
| 16 22 |  | 
| 17 | 
            -
             | 
| 23 | 
            +
              // If we didn't receive anything over stdout or we have a bad exit status,
         | 
| 24 | 
            +
              // then throw whatever we can.
         | 
| 25 | 
            +
              if (stdout.length === 0 || (status !== null && status !== 0)) {
         | 
| 26 | 
            +
                throw new Error(stderr || "An unknown error occurred");
         | 
| 18 27 | 
             
              }
         | 
| 19 28 |  | 
| 20 | 
            -
              const parsed = JSON.parse( | 
| 29 | 
            +
              const parsed = JSON.parse(stdout);
         | 
| 21 30 |  | 
| 22 31 | 
             
              if (parsed.error) {
         | 
| 23 | 
            -
                 | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 32 | 
            +
                const error = new Error(parsed.error);
         | 
| 33 | 
            +
                if (parsed.loc) {
         | 
| 34 | 
            +
                  error.loc = parsed.loc;
         | 
| 35 | 
            +
                }
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                throw error;
         | 
| 28 38 | 
             
              }
         | 
| 29 39 |  | 
| 30 40 | 
             
              return parsed;
         | 
    
        data/src/parser/requestParse.js
    CHANGED
    
    | @@ -54,11 +54,11 @@ function ensureParseServer() { | |
| 54 54 | 
             
            }
         | 
| 55 55 |  | 
| 56 56 | 
             
            // Sends a request to the parse server to parse the given content.
         | 
| 57 | 
            -
            function requestParse(parser, source) {
         | 
| 57 | 
            +
            function requestParse(parser, source, opts) {
         | 
| 58 58 | 
             
              ensureParseServer();
         | 
| 59 59 |  | 
| 60 | 
            -
              const { command,  | 
| 61 | 
            -
              const { stdout, stderr, status } = spawnSync(command,  | 
| 60 | 
            +
              const { command, args } = getNetcat(opts);
         | 
| 61 | 
            +
              const { stdout, stderr, status } = spawnSync(command, args.concat(sockfile), {
         | 
| 62 62 | 
             
                input: `${parser}|${source}`,
         | 
| 63 63 | 
             
                maxBuffer: 15 * 1024 * 1024
         | 
| 64 64 | 
             
              });
         | 
    
        data/src/parser/server.rb
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require 'bundler/setup' if ENV[' | 
| 3 | 
            +
            require 'bundler/setup' if ENV['PLUGIN_RUBY_CI']
         | 
| 4 4 | 
             
            require 'socket'
         | 
| 5 5 | 
             
            require 'json'
         | 
| 6 6 |  | 
| @@ -14,7 +14,7 @@ $PROGRAM_NAME = 'prettier-ruby-parser' | |
| 14 14 | 
             
            # Make sure we trap these signals to be sure we get the quit command coming from
         | 
| 15 15 | 
             
            # the parent node process
         | 
| 16 16 | 
             
            quit = false
         | 
| 17 | 
            -
            trap(:QUIT) { quit = true }
         | 
| 17 | 
            +
            trap(:QUIT) { quit = true } if RUBY_PLATFORM != 'java'
         | 
| 18 18 | 
             
            trap(:INT) { quit = true }
         | 
| 19 19 | 
             
            trap(:TERM) { quit = true }
         | 
| 20 20 |  | 
| @@ -48,6 +48,11 @@ loop do | |
| 48 48 | 
             
                else
         | 
| 49 49 | 
             
                  socket.write('{ "error": true }')
         | 
| 50 50 | 
             
                end
         | 
| 51 | 
            +
              rescue Prettier::Parser::ParserError => error
         | 
| 52 | 
            +
                loc = { start: { line: error.lineno, column: error.column } }
         | 
| 53 | 
            +
                socket.write(JSON.fast_generate(error: error.message, loc: loc))
         | 
| 54 | 
            +
              rescue StandardError => error
         | 
| 55 | 
            +
                socket.write(JSON.fast_generate(error: error.message))
         | 
| 51 56 | 
             
              ensure
         | 
| 52 57 | 
             
                socket.close
         | 
| 53 58 | 
             
              end
         | 
    
        data/src/plugin.js
    CHANGED
    
    | @@ -36,6 +36,7 @@ module.exports = { | |
| 36 36 | 
             
                    ".rabl",
         | 
| 37 37 | 
             
                    ".rake",
         | 
| 38 38 | 
             
                    ".rb",
         | 
| 39 | 
            +
                    ".rbi",
         | 
| 39 40 | 
             
                    ".rbuild",
         | 
| 40 41 | 
             
                    ".rbw",
         | 
| 41 42 | 
             
                    ".rbx",
         | 
| @@ -118,6 +119,12 @@ module.exports = { | |
| 118 119 | 
             
                  description:
         | 
| 119 120 | 
             
                    "When it fits on one line, allows if, unless, while, and until statements to use the modifier form."
         | 
| 120 121 | 
             
                },
         | 
| 122 | 
            +
                rubyNetcatCommand: {
         | 
| 123 | 
            +
                  type: "string",
         | 
| 124 | 
            +
                  category: "Ruby",
         | 
| 125 | 
            +
                  description:
         | 
| 126 | 
            +
                    'The prefix of the command to execute to communicate between the node.js process and the Ruby process. (For example, "nc -U" or "telnet -u") Normally you should not set this option.'
         | 
| 127 | 
            +
                },
         | 
| 121 128 | 
             
                rubySingleQuote: {
         | 
| 122 129 | 
             
                  type: "boolean",
         | 
| 123 130 | 
             
                  category: "Ruby",
         | 
    
        data/src/rbs/parser.js
    CHANGED
    
    | @@ -4,8 +4,8 @@ const parseSync = require("../parser/parseSync"); | |
| 4 4 | 
             
            // to prettier a JavaScript object that is the equivalent AST that represents
         | 
| 5 5 | 
             
            // the code stored in that string. We accomplish this by spawning a new Ruby
         | 
| 6 6 | 
             
            // process of parser.rb and reading JSON off STDOUT.
         | 
| 7 | 
            -
            function parse(text, _parsers,  | 
| 8 | 
            -
              return parseSync("rbs", text);
         | 
| 7 | 
            +
            function parse(text, _parsers, opts) {
         | 
| 8 | 
            +
              return parseSync("rbs", text, opts);
         | 
| 9 9 | 
             
            }
         | 
| 10 10 |  | 
| 11 11 | 
             
            const pragmaPattern = /#\s*@(prettier|format)/;
         | 
    
        data/src/rbs/parser.rb
    CHANGED
    
    | @@ -23,8 +23,14 @@ end | |
| 23 23 | 
             
            class RBS::Location
         | 
| 24 24 | 
             
              def to_json(*args)
         | 
| 25 25 | 
             
                {
         | 
| 26 | 
            -
                  start: { | 
| 27 | 
            -
             | 
| 26 | 
            +
                  start: {
         | 
| 27 | 
            +
                    line: start_line,
         | 
| 28 | 
            +
                    column: start_column
         | 
| 29 | 
            +
                  },
         | 
| 30 | 
            +
                  end: {
         | 
| 31 | 
            +
                    line: end_line,
         | 
| 32 | 
            +
                    column: end_column
         | 
| 33 | 
            +
                  },
         | 
| 28 34 | 
             
                  start_pos: start_pos,
         | 
| 29 35 | 
             
                  end_pos: end_pos
         | 
| 30 36 | 
             
                }.to_json(*args)
         | 
| @@ -81,8 +87,6 @@ module Prettier | |
| 81 87 | 
             
              class RBSParser
         | 
| 82 88 | 
             
                def self.parse(text)
         | 
| 83 89 | 
             
                  { declarations: RBS::Parser.parse_signature(text) }
         | 
| 84 | 
            -
                rescue StandardError
         | 
| 85 | 
            -
                  false
         | 
| 86 90 | 
             
                end
         | 
| 87 91 | 
             
              end
         | 
| 88 92 | 
             
            end
         | 
    
        data/src/ruby/embed.js
    CHANGED
    
    | @@ -8,7 +8,7 @@ const { | |
| 8 8 | 
             
              stripTrailingHardline
         | 
| 9 9 | 
             
            } = require("../prettier");
         | 
| 10 10 |  | 
| 11 | 
            -
            const {  | 
| 11 | 
            +
            const { literallineWithoutBreakParent } = require("../utils");
         | 
| 12 12 |  | 
| 13 13 | 
             
            const parsers = {
         | 
| 14 14 | 
             
              css: "css",
         | 
| @@ -24,18 +24,54 @@ const parsers = { | |
| 24 24 | 
             
            // have a test that exercises it because I'm not sure for which parser it is
         | 
| 25 25 | 
             
            // necessary, but since it's in prettier core I'm keeping it here.
         | 
| 26 26 | 
             
            /* istanbul ignore next */
         | 
| 27 | 
            -
             | 
| 28 | 
            -
              mapDoc(doc, (currentDoc) =>
         | 
| 27 | 
            +
            function replaceNewlines(doc) {
         | 
| 28 | 
            +
              return mapDoc(doc, (currentDoc) =>
         | 
| 29 29 | 
             
                typeof currentDoc === "string" && currentDoc.includes("\n")
         | 
| 30 30 | 
             
                  ? concat(
         | 
| 31 31 | 
             
                      currentDoc
         | 
| 32 32 | 
             
                        .split(/(\n)/g)
         | 
| 33 | 
            -
                        .map((v, i) => (i % 2 === 0 ? v :  | 
| 33 | 
            +
                        .map((v, i) => (i % 2 === 0 ? v : literallineWithoutBreakParent))
         | 
| 34 34 | 
             
                    )
         | 
| 35 35 | 
             
                  : currentDoc
         | 
| 36 36 | 
             
              );
         | 
| 37 | 
            +
            }
         | 
| 37 38 |  | 
| 38 | 
            -
             | 
| 39 | 
            +
            // Returns a number that represents the minimum amount of leading whitespace
         | 
| 40 | 
            +
            // that is present on every line in the given string. So for example if you have
         | 
| 41 | 
            +
            // the following heredoc:
         | 
| 42 | 
            +
            //
         | 
| 43 | 
            +
            //     <<~HERE
         | 
| 44 | 
            +
            //       my
         | 
| 45 | 
            +
            //       content
         | 
| 46 | 
            +
            //       here
         | 
| 47 | 
            +
            //     HERE
         | 
| 48 | 
            +
            //
         | 
| 49 | 
            +
            // then the return value of this function would be 2. If you indented every line
         | 
| 50 | 
            +
            // of the inner content 2 more spaces then this function would return 4.
         | 
| 51 | 
            +
            function getCommonLeadingWhitespace(content) {
         | 
| 52 | 
            +
              const pattern = /^\s+/;
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              return content
         | 
| 55 | 
            +
                .split("\n")
         | 
| 56 | 
            +
                .slice(0, -1)
         | 
| 57 | 
            +
                .reduce((minimum, line) => {
         | 
| 58 | 
            +
                  const matched = pattern.exec(line);
         | 
| 59 | 
            +
                  const length = matched ? matched[0].length : 0;
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  return minimum === null ? length : Math.min(minimum, length);
         | 
| 62 | 
            +
                }, null);
         | 
| 63 | 
            +
            }
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            // Returns a new string with the common whitespace stripped out. Effectively it
         | 
| 66 | 
            +
            // emulates what a squiggly heredoc does in Ruby.
         | 
| 67 | 
            +
            function stripCommonLeadingWhitespace(content) {
         | 
| 68 | 
            +
              const lines = content.split("\n");
         | 
| 69 | 
            +
              const minimum = getCommonLeadingWhitespace(content);
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              return lines.map((line) => line.slice(minimum)).join("\n");
         | 
| 72 | 
            +
            }
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            function embed(path, print, textToDoc, _opts) {
         | 
| 39 75 | 
             
              const node = path.getValue();
         | 
| 40 76 |  | 
| 41 77 | 
             
              // Currently we only support embedded formatting on heredoc nodes
         | 
| @@ -45,6 +81,8 @@ const embed = (path, print, textToDoc, _opts) => { | |
| 45 81 |  | 
| 46 82 | 
             
              // First, ensure that we don't have any interpolation
         | 
| 47 83 | 
             
              const { beging, body, ending } = node;
         | 
| 84 | 
            +
              const isSquiggly = beging.body[2] === "~";
         | 
| 85 | 
            +
             | 
| 48 86 | 
             
              if (body.some((part) => part.type !== "@tstring_content")) {
         | 
| 49 87 | 
             
                return null;
         | 
| 50 88 | 
             
              }
         | 
| @@ -56,24 +94,32 @@ const embed = (path, print, textToDoc, _opts) => { | |
| 56 94 | 
             
                return null;
         | 
| 57 95 | 
             
              }
         | 
| 58 96 |  | 
| 59 | 
            -
              // Get the content as if it were a source string | 
| 60 | 
            -
               | 
| 61 | 
            -
             | 
| 97 | 
            +
              // Get the content as if it were a source string.
         | 
| 98 | 
            +
              let content = body.map((part) => part.body).join("");
         | 
| 99 | 
            +
             | 
| 100 | 
            +
              // If we're using a squiggly heredoc, then we're going to manually strip off
         | 
| 101 | 
            +
              // the leading whitespace of each line up to the minimum leading whitespace so
         | 
| 102 | 
            +
              // that the embedded parser can handle that for us.
         | 
| 103 | 
            +
              if (isSquiggly) {
         | 
| 104 | 
            +
                content = stripCommonLeadingWhitespace(content);
         | 
| 105 | 
            +
              }
         | 
| 106 | 
            +
             | 
| 107 | 
            +
              // Pass that content into the embedded parser. Get back the doc node.
         | 
| 62 108 | 
             
              const formatted = concat([
         | 
| 63 | 
            -
                 | 
| 109 | 
            +
                literallineWithoutBreakParent,
         | 
| 64 110 | 
             
                replaceNewlines(stripTrailingHardline(textToDoc(content, { parser })))
         | 
| 65 111 | 
             
              ]);
         | 
| 66 112 |  | 
| 67 113 | 
             
              // If we're using a squiggly heredoc, then we can properly handle indentation
         | 
| 68 114 | 
             
              // ourselves.
         | 
| 69 | 
            -
              if ( | 
| 115 | 
            +
              if (isSquiggly) {
         | 
| 70 116 | 
             
                return concat([
         | 
| 71 117 | 
             
                  path.call(print, "beging"),
         | 
| 72 118 | 
             
                  lineSuffix(
         | 
| 73 119 | 
             
                    group(
         | 
| 74 120 | 
             
                      concat([
         | 
| 75 121 | 
             
                        indent(markAsRoot(formatted)),
         | 
| 76 | 
            -
                         | 
| 122 | 
            +
                        literallineWithoutBreakParent,
         | 
| 77 123 | 
             
                        ending.trim()
         | 
| 78 124 | 
             
                      ])
         | 
| 79 125 | 
             
                    )
         | 
| @@ -86,9 +132,11 @@ const embed = (path, print, textToDoc, _opts) => { | |
| 86 132 | 
             
              return markAsRoot(
         | 
| 87 133 | 
             
                concat([
         | 
| 88 134 | 
             
                  path.call(print, "beging"),
         | 
| 89 | 
            -
                  lineSuffix( | 
| 135 | 
            +
                  lineSuffix(
         | 
| 136 | 
            +
                    group(concat([formatted, literallineWithoutBreakParent, ending.trim()]))
         | 
| 137 | 
            +
                  )
         | 
| 90 138 | 
             
                ])
         | 
| 91 139 | 
             
              );
         | 
| 92 | 
            -
            } | 
| 140 | 
            +
            }
         | 
| 93 141 |  | 
| 94 142 | 
             
            module.exports = embed;
         |