Skip to content

Commit

Permalink
Incorporate HAML plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
kddnewton committed Jan 5, 2021
1 parent cd57940 commit a0e22b1
Show file tree
Hide file tree
Showing 40 changed files with 1,312 additions and 149 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -5,6 +5,7 @@
/pkg/
/test.rb
/test.rbs
/test.haml
*.gem

# This is to better support the GitHub actions checking - since bundler changes
Expand Down
1 change: 1 addition & 0 deletions .npmignore
Expand Up @@ -15,3 +15,4 @@
/yarn-error.log
/test.rb
/test.rbs
/test.haml
2 changes: 2 additions & 0 deletions .prettierignore
Expand Up @@ -7,10 +7,12 @@

/.eslintcache
/.*ignore
/bin/port
/docs/logo.png
/*.lock
/LICENSE
/test.rb
/test.rbs
/test.haml

*.txt
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
### Added

- [@kddeisz] - Handling of the RBS language.
- [@kddeisz] - Incorporate the HAML plugin.

## [1.2.5] - 2021-01-04

Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Expand Up @@ -5,5 +5,7 @@ source 'https://rubygems.org'
gemspec

gem 'bundler', '~> 2.1'
gem 'haml', '~> 5.2'
gem 'minitest', '~> 5.14'
gem 'rake', '~> 13.0'
gem 'rbs', '~> 1.0'
12 changes: 9 additions & 3 deletions README.md
Expand Up @@ -72,6 +72,12 @@ To run `prettier` with the Ruby plugin, you're going to need [`ruby`](https://ww

Note that currently the editor integrations work best with the `npm` package, as most of the major editor plugins expect a `node_modules` directory. You can get them to work with the Ruby gem, but it requires manually configuring the paths.

This plugin currently supports formatting the following kinds of files:

- All varieties of Ruby source files (e.g., `*.rb`, `*.gemspec`, `Gemfile`, etc.)
- [RBS type language](https://github.com/ruby/rbs) files - requires having the `rbs` gem in your gem path
- [HAML template language](https://haml.info/) files - requires having the `haml` gem in your gem path

### Ruby gem

Add this line to your application's Gemfile:
Expand All @@ -95,7 +101,7 @@ gem install prettier
The `rbprettier` executable is now installed and ready for use:

```bash
bundle exec rbprettier --write '**/*.{rb,rbs}'
bundle exec rbprettier --write '**/*'
```

### `npm` package
Expand All @@ -115,7 +121,7 @@ yarn add --dev prettier @prettier/plugin-ruby
The `prettier` executable is now installed and ready for use:

```bash
./node_modules/.bin/prettier --write '**/*.{rb,rbs}'
./node_modules/.bin/prettier --write '**/*'
```

## Configuration
Expand Down Expand Up @@ -146,7 +152,7 @@ file](https://prettier.io/docs/en/configuration.html). For example:
Or, they can be passed to `prettier` as arguments:

```bash
prettier --ruby-single-quote false --write '**/*.{rb,rbs}'
prettier --ruby-single-quote false --write '**/*'
```

### Usage with RuboCop
Expand Down
14 changes: 14 additions & 0 deletions bin/port
@@ -0,0 +1,14 @@
#!/usr/bin/env bash

LOW_BOUND=49152
RANGE=16384

while true; do
CANDIDATE=$[$LOW_BOUND + ($RANDOM % $RANGE)]
(echo "" >/dev/tcp/127.0.0.1/${CANDIDATE}) >/dev/null 2>&1

if [ $? -ne 0 ]; then
echo $CANDIDATE
break
fi
done
23 changes: 15 additions & 8 deletions bin/print
Expand Up @@ -4,15 +4,22 @@ const fs = require("fs");
const prettier = require("prettier");

let parser = "ruby";
let content = 2;
let contentIdx = 2;

if (process.argv[content] === "rbs") {
parser = "rbs";
content = 3;
if (["rbs", "haml"].includes(process.argv[contentIdx])) {
parser = process.argv[contentIdx];
contentIdx += 1;
}

const code = fs.existsSync(process.argv[content])
? fs.readFileSync(process.argv[content], "utf-8")
: process.argv.slice(content).join(" ").replace(/\\n/g, "\n");
let content;

console.log(prettier.format(code, { parser, plugins: ["."] }));
if (fs.existsSync(process.argv[contentIdx])) {
content = fs.readFileSync(process.argv[contentIdx], "utf-8");
} else if (process.argv.length === contentIdx) {
const extension = parser === "ruby" ? "rb" : parser;
content = fs.readFileSync(`test.${extension}`, "utf-8");
} else {
content = process.argv.slice(contentIdx).join(" ").replace(/\\n/g, "\n");
}

console.log(prettier.format(content, { parser, plugins: ["."] }));
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"check-format": "prettier --check '**/*'",
"lint": "eslint --cache .",
"test": "jest"
"test": "PORT=$(bin/port) jest"
},
"repository": {
"type": "git",
Expand Down
87 changes: 87 additions & 0 deletions src/haml/embed.js
@@ -0,0 +1,87 @@
const {
concat,
hardline,
indent,
literalline,
markAsRoot,
mapDoc,
stripTrailingHardline
} = require("../prettier");

// Get the name of the parser that is represented by the given element node,
// return null if a matching parser cannot be found
function getParser(name, opts) {
let parser = name;

// We don't want to deal with some weird recursive parser situation, so we
// need to explicitly call out the HAML parser here and just return null
if (parser === "haml") {
return null;
}

// In HAML the name of the JS filter is :javascript, whereas in prettier the
// name of the JS parser is babel. Here we explicitly handle that conversion.
if (parser === "javascript") {
parser = "babel";
}

// If there is a plugin that has a parser that matches the name of this
// element, then we're going to assume that's correct for embedding and go
// ahead and switch to that parser
if (
opts.plugins.some(
(plugin) =>
plugin.parsers &&
Object.prototype.hasOwnProperty.call(plugin.parsers, parser)
)
) {
return parser;
}

return null;
}

// This function is in here because it handles embedded parser values. I don't
// have a test that exercises it because I'm not sure for which parser it is
// necessary, but since it's in prettier core I'm keeping it here.
/* istanbul ignore next */
function replaceNewlines(doc) {
return mapDoc(doc, (currentDoc) =>
typeof currentDoc === "string" && currentDoc.includes("\n")
? concat(
currentDoc
.split(/(\n)/g)
.map((v, i) => (i % 2 === 0 ? v : literalline))
)
: currentDoc
);
}

function embed(path, _print, textToDoc, opts) {
const node = path.getValue();
if (node.type !== "filter") {
return null;
}

const parser = getParser(node.value.name, opts);
if (!parser) {
return null;
}

return markAsRoot(
concat([
":",
node.value.name,
indent(
concat([
hardline,
replaceNewlines(
stripTrailingHardline(textToDoc(node.value.text, { parser }))
)
])
)
])
);
}

module.exports = embed;
27 changes: 27 additions & 0 deletions src/haml/nodes/comment.js
@@ -0,0 +1,27 @@
const { concat, group, hardline, indent, join } = require("../../prettier");

// https://haml.info/docs/yardoc/file.REFERENCE.html#html-comments-
function comment(path, _opts, print) {
const { children, value } = path.getValue();
const parts = ["/"];

if (value.revealed) {
parts.push("!");
}

if (value.conditional) {
parts.push(value.conditional);
} else if (value.text) {
parts.push(" ", value.text);
}

if (children.length > 0) {
parts.push(
indent(concat([hardline, join(hardline, path.map(print, "children"))]))
);
}

return group(concat(parts));
}

module.exports = comment;
34 changes: 34 additions & 0 deletions src/haml/nodes/doctype.js
@@ -0,0 +1,34 @@
const { join } = require("../../prettier");

const types = {
basic: "Basic",
frameset: "Frameset",
mobile: "Mobile",
rdfa: "RDFa",
strict: "Strict",
xml: "XML"
};

const versions = ["1.1", "5"];

// https://haml.info/docs/yardoc/file.REFERENCE.html#doctype-
function doctype(path, _opts, _print) {
const { value } = path.getValue();
const parts = ["!!!"];

if (value.type in types) {
parts.push(types[value.type]);
} else if (versions.includes(value.version)) {
parts.push(value.version);
} else {
parts.push(value.type);
}

if (value.encoding) {
parts.push(value.encoding);
}

return join(" ", parts);
}

module.exports = doctype;
16 changes: 16 additions & 0 deletions src/haml/nodes/filter.js
@@ -0,0 +1,16 @@
const { concat, group, hardline, indent, join } = require("../../prettier");

// https://haml.info/docs/yardoc/file.REFERENCE.html#filters
function filter(path, _opts, _print) {
const { value } = path.getValue();

return group(
concat([
":",
value.name,
indent(concat([hardline, join(hardline, value.text.trim().split("\n"))]))
])
);
}

module.exports = filter;
21 changes: 21 additions & 0 deletions src/haml/nodes/hamlComment.js
@@ -0,0 +1,21 @@
const { concat, hardline, indent, join } = require("../../prettier");

// https://haml.info/docs/yardoc/file.REFERENCE.html#haml-comments--
function hamlComment(path, opts, _print) {
const node = path.getValue();
const parts = ["-#"];

if (node.value.text) {
if (opts.originalText.split("\n")[node.line - 1].trim() === "-#") {
const lines = node.value.text.trim().split("\n");

parts.push(indent(concat([hardline, join(hardline, lines)])));
} else {
parts.push(" ", node.value.text.trim());
}
}

return concat(parts);
}

module.exports = hamlComment;
6 changes: 6 additions & 0 deletions src/haml/nodes/plain.js
@@ -0,0 +1,6 @@
// https://haml.info/docs/yardoc/file.REFERENCE.html#plain-text
function plain(path, _opts, _print) {
return path.getValue().value.text;
}

module.exports = plain;
8 changes: 8 additions & 0 deletions src/haml/nodes/root.js
@@ -0,0 +1,8 @@
const { concat, hardline, join } = require("../../prettier");

// The root node in the AST
function root(path, _opts, print) {
return concat([join(hardline, path.map(print, "children")), hardline]);
}

module.exports = root;
33 changes: 33 additions & 0 deletions src/haml/nodes/script.js
@@ -0,0 +1,33 @@
const { concat, group, hardline, indent, join } = require("../../prettier");

// https://haml.info/docs/yardoc/file.REFERENCE.html#inserting_ruby
function script(path, opts, print) {
const { children, value } = path.getValue();
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());
}

if (children.length > 0) {
parts.push(
indent(concat([hardline, join(hardline, path.map(print, "children"))]))
);
}

return group(concat(parts));
}

module.exports = script;

0 comments on commit a0e22b1

Please sign in to comment.