Socket
Socket
Sign inDemoInstall

@prettier/plugin-ruby

Package Overview
Dependencies
1
Maintainers
12
Versions
78
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.5.5 to 1.6.0

18

CHANGELOG.md

@@ -9,2 +9,17 @@ # Changelog

## [1.6.0] - 2021-06-23
### Added
- [#859](https://github.com/prettier/plugin-ruby/issues/859) - azz, kddeisz - Support the `--insert-pragma` option for the incremental adoption workflow.
- [#904](https://github.com/prettier/plugin-ruby/issues/904) - Hansenq, kddeisz - Support the `%s` symbol literal syntax.
- [#833](https://github.com/prettier/plugin-ruby/issues/883) - kddeisz - Support for prettier >= v2.3.0.
### Changed
- [#854](https://github.com/prettier/plugin-ruby/issues/854) - jflinter, kddeisz - Parentheses should not be stripped from optional RBS union types.
- [#888](https://github.com/prettier/plugin-ruby/issues/888) - MaxNotarangelo, kddeisz - Ensure parentheses wrap conditionals that get transformed into the modifier form when they're used within a binary node.
- [#874](https://github.com/prettier/plugin-ruby/issues/874) - yratanov, kddeisz - Ensure that method calls chained onto the ends of blocks still print their arguments.
- [#897](https://github.com/prettier/plugin-ruby/pull/897) - Hansenq - Reenable the `Layout/LineLength` rubocop rule in our shipped config so that line lengths for other cops are calculated correctly.
## [1.5.5] - 2021-03-25

@@ -1108,3 +1123,4 @@

[unreleased]: https://github.com/prettier/plugin-ruby/compare/v1.5.5...HEAD
[unreleased]: https://github.com/prettier/plugin-ruby/compare/v1.6.0...HEAD
[1.6.0]: https://github.com/prettier/plugin-ruby/compare/v1.5.5...v1.6.0
[1.5.5]: https://github.com/prettier/plugin-ruby/compare/v1.5.4...v1.5.5

@@ -1111,0 +1127,0 @@ [1.5.4]: https://github.com/prettier/plugin-ruby/compare/v1.5.3...v1.5.4

4

CONTRIBUTING.md

@@ -78,3 +78,3 @@ # Contributing

As the nodes are printing themselves and their children, they're additionally building up a second AST. That AST is built using the `builder` commands from prettier core, described [here](https://github.com/prettier/prettier/blob/master/commands.md). As an example, below is how a `binary` node (like the one representing the `1 + 1` above) would handle printing itself:
As the nodes are printing themselves and their children, they're additionally building up a second AST. That AST is built using the `builder` commands from prettier core, described [here](https://github.com/prettier/prettier/blob/main/commands.md). As an example, below is how a `binary` node (like the one representing the `1 + 1` above) would handle printing itself:

@@ -163,3 +163,3 @@ ```javascript

- [Prettier plugin documentation](https://prettier.io/docs/en/plugins.html) - documentation around `prettier`'s plugin system
- [Builder commands](https://github.com/prettier/prettier/blob/master/commands.md) - the functions used to build the `prettier` IR
- [Builder commands](https://github.com/prettier/prettier/blob/main/commands.md) - the functions used to build the `prettier` IR
- [Writing a Prettier plugin](https://medium.com/@fvictorio/how-to-write-a-plugin-for-prettier-a0d98c845e70) - a nice tutorial on how to build a `prettier` plugin

@@ -166,0 +166,0 @@

{
"name": "@prettier/plugin-ruby",
"version": "1.5.5",
"version": "1.6.0",
"description": "prettier plugin for the Ruby programming language",

@@ -27,4 +27,4 @@ "main": "src/plugin.js",

"eslint-config-prettier": "^8.0.0",
"husky": "^5.0.9",
"jest": "^26.0.0",
"husky": "^6.0.0",
"jest": "^27.0.1",
"pretty-quick": "^3.1.0"

@@ -31,0 +31,0 @@ },

@@ -131,14 +131,14 @@ <div align="center">

| API Option | CLI Option | Default | Description |
| ------------------- | ----------------------- | :-----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). |
| `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). |
| `rubyArrayLiteral` | `--ruby-array-literal` | `true` | When possible, favor the use of string and symbol array literals. |
| `rubyHashLabel` | `--ruby-hash-label` | `true` | When possible, uses the shortened hash key syntax, as opposed to hash rockets. |
| `rubyModifier` | `--ruby-modifier` | `true` | When it fits on one line, allows while and until statements to use the modifier form. |
| `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. |
| `rubySingleQuote` | `--ruby-single-quote` | `true` | When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals. |
| `rubyToProc` | `--ruby-to-proc` | `false` | When possible, convert blocks to the more concise `Symbol#to_proc` syntax. |
| `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). |
| `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`. |
| API Option | CLI Option | Default | Description |
| ------------------- | ----------------------- | :------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). |
| `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). |
| `rubyArrayLiteral` | `--ruby-array-literal` | `true` | When possible, favor the use of string and symbol array literals. |
| `rubyHashLabel` | `--ruby-hash-label` | `true` | When possible, uses the shortened hash key syntax, as opposed to hash rockets. |
| `rubyModifier` | `--ruby-modifier` | `true` | When it fits on one line, allows while and until statements to use the modifier form. |
| `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. |
| `rubySingleQuote` | `--ruby-single-quote` | `true` | When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals. |
| `rubyToProc` | `--ruby-to-proc` | `false` | When possible, convert blocks to the more concise `Symbol#to_proc` syntax. |
| `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). |
| `trailingComma` | `--trailing-comma` | `"none"` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#trailing-comma)). `"es5"` is equivalent to `true`. |

@@ -182,2 +182,21 @@ Any of these can be added to your existing [prettier configuration

### Usage with an editor
For [supported editor integrations](https://github.com/prettier/prettier/blob/main/website/data/editors.yml), you should follow the instructions for installing the integration, then install the npm version of this plugin as a development dependency of your project. For most integrations, that should be sufficient. For convenience, the instructions for integrating with VSCode are used as an example below:
- Install the [Prettier - Code Formatter](https://github.com/prettier/prettier-vscode) extension.
- Add the npm `@prettier/plugin-ruby` package to your project as described above.
- Configure in your `settings.json` (`formatOnSave` is optional):
```json
{
"[ruby]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
}
```
Refer to [this issue](https://github.com/prettier/plugin-ruby/issues/113#issuecomment-783426539) if you're having difficulties.
## Contributing

@@ -184,0 +203,0 @@

const parseSync = require("../parser/parseSync");
const parse = (text, _parsers, opts) => {
function parse(text, _parsers, opts) {
return parseSync("haml", text, opts);
};
}
const pragmaPattern = /^\s*-#\s*@(prettier|format)/;
const hasPragma = (text) => pragmaPattern.test(text);
function hasPragma(text) {
return /^\s*-#\s*@(prettier|format)/.test(text);
}

@@ -10,0 +11,0 @@ // These functions are just placeholders until we can actually perform this

@@ -0,28 +1,438 @@

const {
align,
concat,
fill,
group,
hardline,
ifBreak,
indent,
join,
line,
softline
} = require("../prettier");
const embed = require("./embed");
const nodes = {
comment: require("./nodes/comment"),
doctype: require("./nodes/doctype"),
filter: require("./nodes/filter"),
haml_comment: require("./nodes/hamlComment"),
plain: require("./nodes/plain"),
root: require("./nodes/root"),
script: require("./nodes/script"),
silent_script: require("./nodes/silentScript"),
tag: require("./nodes/tag")
const docTypes = {
basic: "Basic",
frameset: "Frameset",
mobile: "Mobile",
rdfa: "RDFa",
strict: "Strict",
xml: "XML"
};
const genericPrint = (path, opts, print) => {
const { type } = path.getValue();
const docVersions = ["1.1", "5"];
/* istanbul ignore next */
if (!(type in nodes)) {
throw new Error(`Unsupported node encountered: ${type}`);
// Prints out a hash key according to the configured prettier options.
function printHashKey(key, opts) {
let quoted = key;
const joiner = opts.rubyHashLabel ? ":" : " =>";
if (key.includes(":") || key.includes("-")) {
const quote = opts.rubySingleQuote ? "'" : '"';
quoted = `${quote}${key}${quote}`;
}
return nodes[type](path, opts, print);
};
return `${opts.rubyHashLabel ? "" : ":"}${quoted}${joiner}`;
}
// Prints out the value inside of a hash key-value pair according to the
// configured prettier options.
function printHashValue(value, opts) {
if (typeof value !== "string") {
return value.toString();
}
// This is a very special syntax created by the parser to let us know that
// this should be printed literally instead of as a string.
if (value.startsWith("&")) {
return value.slice(1);
}
const quote = opts.rubySingleQuote && !value.includes("#{") ? "'" : '"';
return `${quote}${value}${quote}`;
}
// This will print an attributes object to a Doc node. It handles nesting on
// multiple levels and will print out according to whether or not the version of
// HAML being used supports multi-line attributes.
function printAttributes(object, opts, level = 0) {
if (typeof object !== "object") {
return printHashValue(object, opts);
}
const boundary = level === 0 ? softline : line;
const parts = Object.keys(object).map((key) =>
concat([
printHashKey(key, opts),
" ",
printAttributes(object[key], opts, level + 1)
])
);
// If we have support for multi-line attributes laid out like a regular hash,
// then we print them that way here.
if (opts.supportsMultiline) {
return group(
concat([
"{",
indent(group(concat([boundary, join(concat([",", line]), parts)]))),
boundary,
"}"
])
);
}
// Otherwise, if we only have one attribute, then just print it inline
// regardless of how long it is.
if (parts.length === 0) {
return group(concat(["{", parts[0], "}"]));
}
// Otherwise, depending on how long the line is it will split the content into
// multi-line attributes that old Haml understands.
return group(
concat([
"{",
parts[0],
",",
align(
opts.headerLength + 1,
concat([line, join(concat([",", line]), parts.slice(1))])
),
"}"
])
);
}
// A utility function used in a silent script that is meant to determine if a
// child node is a continuation of a parent node (as in a when clause within a
// case statement or an else clause within an if).
function isContinuation(parentNode, childNode) {
if (childNode.type !== "silent_script") {
return false;
}
const parent = parentNode.value.keyword;
const child = childNode.value.keyword;
return (
(parent === "case" && ["when", "else"].includes(child)) ||
(["if", "unless"].includes(parent) && ["elsif", "else"].includes(child))
);
}
// This is our printer's main print function that will switch on the type of
// node and print it out by returning a Doc tree.
function printNode(path, opts, print) {
const node = path.getValue();
const { value } = node;
switch (node.type) {
case "comment":
return printComment();
case "doctype":
return printDoctype();
case "filter":
return printFilter();
case "haml_comment":
return printHamlComment();
case "plain":
return printPlain();
case "root":
return printRoot();
case "script":
return printScript();
case "silent_script":
return printSilentScript();
case "tag":
return printTag();
default:
throw new Error(`Unsupported node encountered: ${node.type}`);
}
// It's common to a couple of nodes to attach nested child nodes on the
// children property. This utility prints them out grouped together with their
// parent node docs.
function printWithChildren(docs) {
if (node.children.length === 0) {
return docs;
}
return group(
concat([
docs,
indent(concat([hardline, join(hardline, path.map(print, "children"))]))
])
);
}
// https://haml.info/docs/yardoc/file.REFERENCE.html#html-comments-
function printComment() {
const parts = ["/"];
if (value.revealed) {
parts.push("!");
}
if (value.conditional) {
parts.push(value.conditional);
} else if (value.text) {
parts.push(" ", value.text);
}
return printWithChildren(group(concat(parts)));
}
// https://haml.info/docs/yardoc/file.REFERENCE.html#doctype-
function printDoctype() {
const parts = ["!!!"];
if (value.type in docTypes) {
parts.push(docTypes[value.type]);
} else if (docVersions.includes(value.version)) {
parts.push(value.version);
} else {
parts.push(value.type);
}
if (value.encoding) {
parts.push(value.encoding);
}
return group(join(" ", parts));
}
// https://haml.info/docs/yardoc/file.REFERENCE.html#filters
function printFilter() {
return group(
concat([
":",
value.name,
indent(
concat([hardline, join(hardline, value.text.trim().split("\n"))])
)
])
);
}
// https://haml.info/docs/yardoc/file.REFERENCE.html#haml-comments--
function printHamlComment() {
const parts = ["-#"];
if (value.text) {
if (opts.originalText.split("\n")[node.line - 1].trim() === "-#") {
const lines = value.text.trim().split("\n");
parts.push(indent(concat([hardline, join(hardline, lines)])));
} else {
parts.push(" ", value.text.trim());
}
}
return concat(parts);
}
// https://haml.info/docs/yardoc/file.REFERENCE.html#plain-text
function printPlain() {
return value.text;
}
// The root node in the AST that we build in the parser.
function printRoot() {
return concat([join(hardline, path.map(print, "children")), hardline]);
}
// https://haml.info/docs/yardoc/file.REFERENCE.html#inserting_ruby
function printScript() {
const parts = [];
if (value.escape_html) {
parts.unshift("&");
}
if (value.preserve) {
parts.push("~");
} else if (!value.interpolate) {
parts.push("=");
}
if (value.escape_html && !value.preserve && value.interpolate) {
parts.push(" ", value.text.trim().slice(1, -1));
} else {
parts.push(" ", value.text.trim());
}
return printWithChildren(group(concat(parts)));
}
// https://haml.info/docs/yardoc/file.REFERENCE.html#running-ruby--
function printSilentScript() {
const parts = [`- ${value.text.trim()}`];
if (node.children.length > 0) {
parts.push(
concat(
path.map((childPath) => {
const child = childPath.getValue();
const concated = concat([hardline, print(childPath)]);
return isContinuation(node, child) ? concated : indent(concated);
}, "children")
)
);
}
return group(concat(parts));
}
// https://haml.info/docs/yardoc/file.REFERENCE.html#element-name-
function printTag() {
const { attributes, dynamic_attributes } = value;
const parts = [];
// If we have a tag that isn't a div, then we need to print out that name of
// that tag first. If it is a div, first we'll check if there are any other
// things that would force us to print out the div explicitly, and otherwise
// we'll leave it off.
if (value.name !== "div") {
parts.push(`%${value.name}`);
}
// If we have a class attribute, then we're going to print that here using
// the special class syntax.
if (attributes.class) {
parts.push(`.${attributes.class.replace(/ /g, ".")}`);
}
// If we have an id attribute, then we're going to print that here using the
// special id syntax.
if (attributes.id) {
parts.push(`#${attributes.id}`);
}
// If we're using dynamic attributes on this tag, then they come in as a
// string that looks like the output of Hash#inspect from Ruby. So here
// we're going to split it all up and print it out nicely.
if (dynamic_attributes.new) {
const pairs = dynamic_attributes.new
.slice(1, -2)
.split(",")
.map((pair) => join("=", pair.slice(1).split('" => ')));
parts.push(
group(
concat([
"(",
align(parts.join("").length + 1, fill(join(line, pairs).parts)),
")"
])
)
);
}
// If there are any static attributes that are not class or id (because we
// already took care of those), then we're going to print them out here.
const staticAttributes = Object.keys(attributes).filter(
(name) => !["class", "id"].includes(name)
);
if (staticAttributes.length > 0) {
const docs = staticAttributes.reduce((accum, key) => {
const doc = `${printHashKey(key, opts)} ${printHashValue(
attributes[key],
opts
)}`;
return accum.length === 0 ? [doc] : accum.concat(",", line, doc);
}, []);
parts.push(
group(concat(["{", align(parts.join("").length + 1, fill(docs)), "}"]))
);
}
// If there are dynamic attributes that don't use the newer syntax, then
// we're going to print them out here.
if (dynamic_attributes.old) {
if (parts.length === 0) {
parts.push("%div");
}
if (typeof dynamic_attributes.old === "string") {
parts.push(dynamic_attributes.old);
} else {
const attrOptions = {
// This is kind of a total hack in that I don't think you're really
// supposed to directly use `path.stack`, but it's the easiest way to
// get the root node without having to know how many levels deep we
// are.
supportsMultiline: path.stack[0].supports_multiline,
headerLength: parts.join("").length
};
parts.push(
printAttributes(
dynamic_attributes.old,
Object.assign({}, opts, attrOptions)
)
);
}
}
// https://haml.info/docs/yardoc/file.REFERENCE.html#object-reference-
if (value.object_ref) {
if (parts.length === 0) {
parts.push("%div");
}
parts.push(value.object_ref);
}
// https://haml.info/docs/yardoc/file.REFERENCE.html#whitespace-removal--and-
if (value.nuke_outer_whitespace) {
parts.push(">");
}
if (value.nuke_inner_whitespace) {
parts.push("<");
}
// https://haml.info/docs/yardoc/file.REFERENCE.html#empty-void-tags-
if (value.self_closing) {
parts.push("/");
}
if (value.value) {
const prefix = value.parse ? "= " : ifBreak("", " ");
return printWithChildren(
group(
concat([
group(concat(parts)),
indent(concat([softline, prefix, value.value]))
])
)
);
}
// In case none of the other if statements have matched and we're printing
// a div, we need to explicitly add it back into the array.
if (parts.length === 0 && value.name === "div") {
parts.push("%div");
}
return printWithChildren(group(concat(parts)));
}
}
// This function handles adding the format pragma to a source string. This is an
// optional workflow for incremental adoption.
function insertPragma(text) {
return `-# @format${text.startsWith("-#") ? "\n" : "\n\n"}${text}`;
}
module.exports = {
embed,
print: genericPrint
print: printNode,
insertPragma
};

@@ -138,11 +138,13 @@ const { spawn, spawnSync, execSync } = require("child_process");

stderr.includes("invalid option -- U") ||
stderr.includes("invalid option -- 'u'") ||
stderr.includes("Protocol not supported")
) {
throw new Error(`
@prettier/plugin-ruby uses netcat to communicate over unix sockets between
the node.js process running prettier and an underlying Ruby process used
for parsing. Unfortunately the version of netcat that you have installed
does not support unix sockets. To solve this either uninstall the version
of netcat that you're using and use a different implementation, or change
the value of the rubyNetcatCommand option in your prettier configuration.
@prettier/plugin-ruby uses unix sockets to communicate between the node.js
process running prettier and an underlying Ruby process used for parsing.
Unfortunately the command that it tried to use to do that
(${netcat.command}) does not support unix sockets. To solve this either
uninstall the version of ${netcat.command} that you're using and use a
different implementation, or change the value of the rubyNetcatCommand
option in your prettier configuration.
`);

@@ -149,0 +151,0 @@ }

@@ -13,3 +13,3 @@ const rubyPrinter = require("./ruby/printer");

* https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
* https://github.com/rubocop-hq/rubocop/blob/master/spec/rubocop/target_finder_spec.rb
* https://github.com/rubocop/rubocop/blob/master/spec/rubocop/target_finder_spec.rb
*/

@@ -16,0 +16,0 @@

@@ -11,8 +11,6 @@ const parseSync = require("../parser/parseSync");

const pragmaPattern = /#\s*@(prettier|format)/;
// This function handles checking whether or not the source string has the
// pragma for prettier. This is an optional workflow for incremental adoption.
function hasPragma(text) {
return pragmaPattern.test(text);
return /^\s*#[^\S\n]*@(format|prettier)\s*(\n|$)/.test(text);
}

@@ -19,0 +17,0 @@

@@ -150,3 +150,3 @@ const {

// look like all kinds of things, listed in the case statement below.
function printType(path, { forceUnionParens = false } = {}) {
function printType(path, { forceParens = false } = {}) {
const node = path.getValue();

@@ -161,3 +161,9 @@

case "optional":
return concat([path.call(printType, "type"), "?"]);
return concat([
path.call(
(typePath) => printType(typePath, { forceParens: true }),
"type"
),
"?"
]);
case "tuple":

@@ -178,3 +184,3 @@ // If we don't have any sub types, we explicitly need the space in between

if (forceUnionParens || path.getParentNode().class === "intersection") {
if (forceParens) {
return concat(["(", doc, ")"]);

@@ -185,4 +191,19 @@ }

}
case "intersection":
return group(join(concat([line, "& "]), path.map(printType, "types")));
case "intersection": {
const doc = group(
join(
concat([line, "& "]),
path.map(
(typePath) => printType(typePath, { forceParens: true }),
"types"
)
)
);
if (forceParens) {
return concat(["(", doc, ")"]);
}
return doc;
}
case "class_singleton":

@@ -528,3 +549,3 @@ return concat(["singleton(", node.name, ")"]);

path.call(
(typePath) => printType(typePath, { forceUnionParens: true }),
(typePath) => printType(typePath, { forceParens: true }),
"type",

@@ -617,5 +638,12 @@ "return_type"

// This function handles adding the format pragma to a source string. This is an
// optional workflow for incremental adoption.
function insertPragma(text) {
return `# @format\n${text}`;
}
module.exports = {
print: printNode,
hasPrettierIgnore
hasPrettierIgnore,
insertPragma
};

@@ -111,8 +111,14 @@ const {

const node = path.getValue();
const blockNode = node.body[1];
const parts = path.call(print, "body", 0);
if (node.body[1]) {
if (blockNode) {
let blockDoc = path.call(print, "body", 1);
if (node.body[1].comments) {
if (!(blockNode.comments || []).some(({ leading }) => leading)) {
// If we don't have any leading comments, we can just prepend the
// operator.
blockDoc = concat(["&", blockDoc]);
} else if (Array.isArray(blockDoc[0])) {
// If we have a method call like:

@@ -127,6 +133,16 @@ //

// before the comment.
blockDoc.parts[2] = concat(["&", blockDoc.parts[2]]);
//
// In prettier >= 2.3.0, the comments are printed as an array before the
// content. I don't love this kind of reflection, but it's the simplest
// way at the moment to get this right.
blockDoc = blockDoc[0].concat(
concat(["&", blockDoc[1]]),
blockDoc.slice(2)
);
} else {
// If we don't have any comments, we can just prepend the operator
blockDoc = concat(["&", blockDoc]);
// In prettier < 2.3.0, the comments are printed as part of a concat, so
// we can reflect on how many leading comments there are to determine
// which doc node we should modify.
const index = blockNode.comments.filter(({ leading }) => leading).length;
blockDoc.parts[index] = concat(["&", blockDoc.parts[index]]);
}

@@ -141,6 +157,30 @@

function printArgsAddStar(path, opts, print) {
const node = path.getValue();
const docs = path.map(print, "body");
let docs = [];
if (node.body[1].comments) {
path.each((argPath, argIndex) => {
const doc = print(argPath);
// If it's the first child, then it's an array of args, so we're going to
// concat that onto the existing docs if there are any.
if (argIndex === 0) {
if (doc.length > 0) {
docs = docs.concat(doc);
}
return;
}
// If it's after the splat, then it's an individual arg, so we're just going
// to push it onto the array.
if (argIndex !== 1) {
docs.push(doc);
return;
}
// If we don't have any leading comments, we can just prepend the operator.
const argsNode = argPath.getValue();
if (!(argsNode.comments || []).some(({ leading }) => leading)) {
docs.push(concat(["*", doc]));
return;
}
// If we have an array like:

@@ -153,18 +193,22 @@ //

//
// or if we have an array like:
// then we need to make sure we don't accidentally prepend the operator
// before the comment(s).
//
// [
// *values # comment
// ]
//
// then we need to make sure we don't accidentally prepend the operator
// before the comment.
const index = node.body[1].comments.filter(({ leading }) => leading).length;
docs[1].parts[index] = concat(["*", docs[1].parts[index]]);
} else {
// If we don't have any comments, we can just prepend the operator
docs[1] = concat(["*", docs[1]]);
}
// In prettier >= 2.3.0, the comments are printed as an array before the
// content. I don't love this kind of reflection, but it's the simplest way
// at the moment to get this right.
if (Array.isArray(doc[0])) {
docs.push(doc[0].concat(concat(["*", doc[1]]), doc.slice(2)));
return;
}
return docs[0].concat(docs[1]).concat(docs.slice(2));
// In prettier < 2.3.0, the comments are printed as part of a concat, so
// we can reflect on how many leading comments there are to determine which
// doc node we should modify.
const index = argsNode.comments.filter(({ leading }) => leading).length;
doc.parts[index] = concat(["*", doc.parts[index]]);
docs = docs.concat(doc);
}, "body");
return docs;
}

@@ -171,0 +215,0 @@

@@ -7,2 +7,3 @@ const {

indent,
join,
softline

@@ -137,3 +138,9 @@ } = require("../../prettier");

return concat([methodDoc, argsDoc]);
// If there are already parentheses, then we can just use the doc that's
// already printed.
if (argNode.type == "arg_paren") {
return concat([methodDoc, argsDoc]);
}
return concat([methodDoc, " ", join(", ", argsDoc), " "]);
}

@@ -140,0 +147,0 @@

@@ -193,54 +193,56 @@ const {

// A normalized print function for both `if` and `unless` nodes.
const printConditional = (keyword) => (path, { rubyModifier }, print) => {
if (canTernary(path)) {
let ternaryParts = [path.call(print, "body", 0), " ? "].concat(
printTernaryClauses(
keyword,
path.call(print, "body", 1),
path.call(print, "body", 2, "body", 0)
)
);
const printConditional =
(keyword) =>
(path, { rubyModifier }, print) => {
if (canTernary(path)) {
let ternaryParts = [path.call(print, "body", 0), " ? "].concat(
printTernaryClauses(
keyword,
path.call(print, "body", 1),
path.call(print, "body", 2, "body", 0)
)
);
if (["binary", "call"].includes(path.getParentNode().type)) {
ternaryParts = ["("].concat(ternaryParts).concat(")");
if (["binary", "call"].includes(path.getParentNode().type)) {
ternaryParts = ["("].concat(ternaryParts).concat(")");
}
return group(
ifBreak(printWithAddition(keyword, path, print), concat(ternaryParts))
);
}
return group(
ifBreak(printWithAddition(keyword, path, print), concat(ternaryParts))
);
}
const [predicate, statements, addition] = path.getValue().body;
const [predicate, statements, addition] = path.getValue().body;
// If there's an additional clause that wasn't matched earlier, we know we
// can't go for the inline option.
if (addition) {
return group(printWithAddition(keyword, path, print, { breaking: true }));
}
// If there's an additional clause that wasn't matched earlier, we know we
// can't go for the inline option.
if (addition) {
return group(printWithAddition(keyword, path, print, { breaking: true }));
}
// If the body of the conditional is empty, then we explicitly have to use the
// block form.
if (isEmptyStmts(statements)) {
return concat([
`${keyword} `,
align(keyword.length + 1, path.call(print, "body", 0)),
concat([hardline, "end"])
]);
}
// If the body of the conditional is empty, then we explicitly have to use the
// block form.
if (isEmptyStmts(statements)) {
return concat([
`${keyword} `,
align(keyword.length + 1, path.call(print, "body", 0)),
concat([hardline, "end"])
]);
}
// If the predicate of the conditional contains an assignment, then we can't
// know for sure that it doesn't impact the body of the conditional, so we
// have to default to the block form.
if (containsAssignment(predicate)) {
return concat([
`${keyword} `,
align(keyword.length + 1, path.call(print, "body", 0)),
indent(concat([hardline, path.call(print, "body", 1)])),
concat([hardline, "end"])
]);
}
// If the predicate of the conditional contains an assignment, then we can't
// know for sure that it doesn't impact the body of the conditional, so we
// have to default to the block form.
if (containsAssignment(predicate)) {
return concat([
`${keyword} `,
align(keyword.length + 1, path.call(print, "body", 0)),
indent(concat([hardline, path.call(print, "body", 1)])),
concat([hardline, "end"])
]);
}
return printSingle(keyword)(path, { rubyModifier }, print);
};
return printSingle(keyword)(path, { rubyModifier }, print);
};
module.exports = {

@@ -247,0 +249,0 @@ else: (path, opts, print) => {

@@ -59,14 +59,3 @@ const {

case "dyna_symbol": {
const { parts } = print(path);
// We're going to slice off the starting colon character so that we can
// move it to the end. If there are comments, then we're going to go
// further into the printed doc nodes.
if (parts[0] === ":") {
parts.splice(0, 1);
} else {
parts[1].parts.splice(0, 1);
}
return concat(parts.concat(":"));
return concat([print(path), ":"]);
}

@@ -78,6 +67,8 @@ }

const node = path.getValue();
const doc = print(path);
let doc = print(path);
if (node.type === "@label") {
return `:${doc.slice(0, doc.length - 1)} =>`;
doc = concat([":", doc.slice(0, doc.length - 1)]);
} else if (node.type === "dyna_symbol") {
doc = concat([":", doc]);
}

@@ -84,0 +75,0 @@

@@ -20,11 +20,4 @@ const {

function printParams(path, opts, print) {
const [
reqs,
optls,
rest,
post,
kwargs,
kwargRest,
block
] = path.getValue().body;
const [reqs, optls, rest, post, kwargs, kwargRest, block] =
path.getValue().body;
let parts = [];

@@ -31,0 +24,0 @@

@@ -85,9 +85,102 @@ const {

function printPercentSDynaSymbol(path, opts, print) {
const node = path.getValue();
const parts = [];
// Push on the quote, which includes the opening character.
parts.push(node.quote);
path.each((childPath) => {
const childNode = childPath.getValue();
if (childNode.type !== "@tstring_content") {
// Here we are printing an embedded variable or expression.
parts.push(print(childPath));
} else {
// Here we are printing plain string content.
parts.push(join(literalline, childNode.body.split("\n")));
}
}, "body");
// Push on the closing character, which is the opposite of the third
// character from the opening.
parts.push(quotePairs[node.quote[2]]);
return concat(parts);
}
// We don't actually want to print %s symbols, as they're much more rarely seen
// in the wild. But we're going to be forced into it if it's a multi-line symbol
// or if the quoting would get super complicated.
function shouldPrintPercentSDynaSymbol(node) {
// We shouldn't print a %s dyna symbol if it was not already that way in the
// original source.
if (node.quote[0] !== "%") {
return false;
}
// Here we're going to check if there is a closing character, a new line, or a
// quote in the content of the dyna symbol. If there is, then quoting could
// get weird, so just bail out and stick to the original bounds in the source.
const closing = quotePairs[node.quote[2]];
return node.body.some(
(child) =>
child.type === "@tstring_content" &&
(child.body.includes("\n") ||
child.body.includes(closing) ||
child.body.includes("'") ||
child.body.includes('"'))
);
}
// Prints a dynamic symbol. Assumes there's a quote property attached to the
// node that will tell us which quote to use when printing. We're just going to
// use whatever quote was provided.
//
// In the case of a plain dyna symbol, node.quote will be either :" or :'
// For %s dyna symbols, node.quote will be %s[, %s(, %s{, or %s<
function printDynaSymbol(path, opts, print) {
const { quote } = path.getValue();
const node = path.getValue();
return concat([":", quote].concat(path.map(print, "body")).concat(quote));
if (shouldPrintPercentSDynaSymbol(node)) {
return printPercentSDynaSymbol(path, opts, print);
}
const parts = [];
let quote;
if (isQuoteLocked(node)) {
if (node.quote.startsWith("%")) {
quote = opts.rubySingleQuote ? "'" : '"';
} else {
quote = node.quote.slice(1);
}
} else {
quote = opts.rubySingleQuote && isSingleQuotable(node) ? "'" : '"';
}
parts.push(quote);
path.each((childPath) => {
const child = childPath.getValue();
if (child.type !== "@tstring_content") {
parts.push(print(childPath));
} else {
parts.push(
join(literalline, normalizeQuotes(child.body, quote).split("\n"))
);
}
}, "body");
parts.push(quote);
// If we're inside of an assoc_new node as the key, then it will handle
// printing the : on its own since it could change sides.
const parentNode = path.getParentNode();
if (parentNode.type !== "assoc_new" || parentNode.body[0] !== node) {
parts.unshift(":");
}
return concat(parts);
}

@@ -94,0 +187,0 @@

@@ -11,8 +11,6 @@ const parseSync = require("../parser/parseSync");

const pragmaPattern = /#\s*@(prettier|format)/;
// This function handles checking whether or not the source string has the
// pragma for prettier. This is an optional workflow for incremental adoption.
function hasPragma(text) {
return pragmaPattern.test(text);
return /^\s*#[^\S\n]*@(format|prettier)\s*(\n|$)/.test(text);
}

@@ -19,0 +17,0 @@

@@ -130,2 +130,10 @@ const { concat, trim } = require("../prettier");

// This function handles adding the format pragma to a source string. This is an
// optional workflow for incremental adoption.
function insertPragma(text) {
const boundary = text.startsWith("#") ? "\n" : "\n\n";
return `# @format${boundary}${text}`;
}
module.exports = {

@@ -138,3 +146,4 @@ embed,

printComment,
isBlockComment
isBlockComment,
insertPragma
};

@@ -5,2 +5,3 @@ const needsParens = [

"assoc_new",
"binary",
"call",

@@ -7,0 +8,0 @@ "massign",

@@ -1,2 +0,9 @@

const skippable = ["array", "hash", "heredoc", "lambda", "regexp_literal"];
const skippable = [
"array",
"dyna_symbol",
"hash",
"heredoc",
"lambda",
"regexp_literal"
];

@@ -3,0 +10,0 @@ function skipAssignIndent(node) {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc