prettier 1.2.3 → 1.5.0

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +349 -358
  3. data/README.md +21 -93
  4. data/node_modules/prettier/index.js +54 -54
  5. data/package.json +1 -2
  6. data/rubocop.yml +26 -0
  7. data/src/haml/embed.js +87 -0
  8. data/src/haml/nodes/comment.js +27 -0
  9. data/src/haml/nodes/doctype.js +34 -0
  10. data/src/haml/nodes/filter.js +16 -0
  11. data/src/haml/nodes/hamlComment.js +21 -0
  12. data/src/haml/nodes/plain.js +6 -0
  13. data/src/haml/nodes/root.js +8 -0
  14. data/src/haml/nodes/script.js +33 -0
  15. data/src/haml/nodes/silentScript.js +59 -0
  16. data/src/haml/nodes/tag.js +193 -0
  17. data/src/haml/parser.js +22 -0
  18. data/src/haml/parser.rb +138 -0
  19. data/src/haml/printer.js +28 -0
  20. data/src/parser/getLang.js +32 -0
  21. data/src/parser/getNetcat.js +50 -0
  22. data/src/parser/netcat.js +15 -0
  23. data/src/parser/parseSync.js +33 -0
  24. data/src/parser/requestParse.js +74 -0
  25. data/src/parser/server.rb +61 -0
  26. data/src/plugin.js +26 -4
  27. data/src/prettier.js +1 -0
  28. data/src/rbs/parser.js +39 -0
  29. data/src/rbs/parser.rb +94 -0
  30. data/src/rbs/printer.js +605 -0
  31. data/src/ruby/embed.js +58 -8
  32. data/src/ruby/nodes/args.js +20 -6
  33. data/src/ruby/nodes/blocks.js +64 -59
  34. data/src/ruby/nodes/calls.js +12 -43
  35. data/src/ruby/nodes/class.js +17 -27
  36. data/src/ruby/nodes/commands.js +7 -2
  37. data/src/ruby/nodes/conditionals.js +1 -1
  38. data/src/ruby/nodes/hashes.js +28 -14
  39. data/src/ruby/nodes/hooks.js +9 -19
  40. data/src/ruby/nodes/loops.js +4 -10
  41. data/src/ruby/nodes/methods.js +8 -17
  42. data/src/ruby/nodes/params.js +22 -14
  43. data/src/ruby/nodes/patterns.js +9 -5
  44. data/src/ruby/nodes/rescue.js +32 -25
  45. data/src/ruby/nodes/return.js +0 -4
  46. data/src/ruby/nodes/statements.js +11 -13
  47. data/src/ruby/nodes/strings.js +27 -35
  48. data/src/ruby/parser.js +2 -49
  49. data/src/ruby/parser.rb +256 -232
  50. data/src/ruby/printer.js +0 -2
  51. data/src/ruby/toProc.js +4 -8
  52. data/src/utils.js +1 -0
  53. data/src/utils/isEmptyBodyStmt.js +7 -0
  54. data/src/utils/isEmptyStmts.js +9 -5
  55. data/src/utils/makeCall.js +3 -0
  56. data/src/utils/noIndent.js +1 -0
  57. data/src/utils/printEmptyCollection.js +9 -2
  58. metadata +26 -2
@@ -0,0 +1,27 @@
1
+ const { concat, group, hardline, indent, join } = require("../../prettier");
2
+
3
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#html-comments-
4
+ function comment(path, _opts, print) {
5
+ const { children, value } = path.getValue();
6
+ const parts = ["/"];
7
+
8
+ if (value.revealed) {
9
+ parts.push("!");
10
+ }
11
+
12
+ if (value.conditional) {
13
+ parts.push(value.conditional);
14
+ } else if (value.text) {
15
+ parts.push(" ", value.text);
16
+ }
17
+
18
+ if (children.length > 0) {
19
+ parts.push(
20
+ indent(concat([hardline, join(hardline, path.map(print, "children"))]))
21
+ );
22
+ }
23
+
24
+ return group(concat(parts));
25
+ }
26
+
27
+ module.exports = comment;
@@ -0,0 +1,34 @@
1
+ const { join } = require("../../prettier");
2
+
3
+ const types = {
4
+ basic: "Basic",
5
+ frameset: "Frameset",
6
+ mobile: "Mobile",
7
+ rdfa: "RDFa",
8
+ strict: "Strict",
9
+ xml: "XML"
10
+ };
11
+
12
+ const versions = ["1.1", "5"];
13
+
14
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#doctype-
15
+ function doctype(path, _opts, _print) {
16
+ const { value } = path.getValue();
17
+ const parts = ["!!!"];
18
+
19
+ if (value.type in types) {
20
+ parts.push(types[value.type]);
21
+ } else if (versions.includes(value.version)) {
22
+ parts.push(value.version);
23
+ } else {
24
+ parts.push(value.type);
25
+ }
26
+
27
+ if (value.encoding) {
28
+ parts.push(value.encoding);
29
+ }
30
+
31
+ return join(" ", parts);
32
+ }
33
+
34
+ module.exports = doctype;
@@ -0,0 +1,16 @@
1
+ const { concat, group, hardline, indent, join } = require("../../prettier");
2
+
3
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#filters
4
+ function filter(path, _opts, _print) {
5
+ const { value } = path.getValue();
6
+
7
+ return group(
8
+ concat([
9
+ ":",
10
+ value.name,
11
+ indent(concat([hardline, join(hardline, value.text.trim().split("\n"))]))
12
+ ])
13
+ );
14
+ }
15
+
16
+ module.exports = filter;
@@ -0,0 +1,21 @@
1
+ const { concat, hardline, indent, join } = require("../../prettier");
2
+
3
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#haml-comments--
4
+ function hamlComment(path, opts, _print) {
5
+ const node = path.getValue();
6
+ const parts = ["-#"];
7
+
8
+ if (node.value.text) {
9
+ if (opts.originalText.split("\n")[node.line - 1].trim() === "-#") {
10
+ const lines = node.value.text.trim().split("\n");
11
+
12
+ parts.push(indent(concat([hardline, join(hardline, lines)])));
13
+ } else {
14
+ parts.push(" ", node.value.text.trim());
15
+ }
16
+ }
17
+
18
+ return concat(parts);
19
+ }
20
+
21
+ module.exports = hamlComment;
@@ -0,0 +1,6 @@
1
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#plain-text
2
+ function plain(path, _opts, _print) {
3
+ return path.getValue().value.text;
4
+ }
5
+
6
+ module.exports = plain;
@@ -0,0 +1,8 @@
1
+ const { concat, hardline, join } = require("../../prettier");
2
+
3
+ // The root node in the AST
4
+ function root(path, _opts, print) {
5
+ return concat([join(hardline, path.map(print, "children")), hardline]);
6
+ }
7
+
8
+ module.exports = root;
@@ -0,0 +1,33 @@
1
+ const { concat, group, hardline, indent, join } = require("../../prettier");
2
+
3
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#inserting_ruby
4
+ function script(path, opts, print) {
5
+ const { children, value } = path.getValue();
6
+ const parts = [];
7
+
8
+ if (value.escape_html) {
9
+ parts.unshift("&");
10
+ }
11
+
12
+ if (value.preserve) {
13
+ parts.push("~");
14
+ } else if (!value.interpolate) {
15
+ parts.push("=");
16
+ }
17
+
18
+ if (value.escape_html && !value.preserve && value.interpolate) {
19
+ parts.push(" ", value.text.trim().slice(1, -1));
20
+ } else {
21
+ parts.push(" ", value.text.trim());
22
+ }
23
+
24
+ if (children.length > 0) {
25
+ parts.push(
26
+ indent(concat([hardline, join(hardline, path.map(print, "children"))]))
27
+ );
28
+ }
29
+
30
+ return group(concat(parts));
31
+ }
32
+
33
+ module.exports = script;
@@ -0,0 +1,59 @@
1
+ const { concat, group, hardline, indent, join } = require("../../prettier");
2
+
3
+ function findKeywordIndices(children, keywords) {
4
+ const indices = [];
5
+
6
+ children.forEach((child, index) => {
7
+ if (child.type !== "silent_script") {
8
+ return;
9
+ }
10
+
11
+ if (keywords.includes(child.value.keyword)) {
12
+ indices.push(index);
13
+ }
14
+ });
15
+
16
+ return indices;
17
+ }
18
+
19
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#running-ruby--
20
+ function silentScript(path, _opts, print) {
21
+ const { children, value } = path.getValue();
22
+ const parts = [`- ${value.text.trim()}`];
23
+
24
+ if (children.length > 0) {
25
+ const scripts = path.map(print, "children");
26
+
27
+ if (value.keyword === "case") {
28
+ const keywordIndices = findKeywordIndices(children, ["when", "else"]);
29
+
30
+ parts.push(
31
+ concat(
32
+ scripts.map((script, index) => {
33
+ const concated = concat([hardline, script]);
34
+
35
+ return keywordIndices.includes(index) ? concated : indent(concated);
36
+ })
37
+ )
38
+ );
39
+ } else if (["if", "unless"].includes(value.keyword)) {
40
+ const keywordIndices = findKeywordIndices(children, ["elsif", "else"]);
41
+
42
+ parts.push(
43
+ concat(
44
+ scripts.map((script, index) => {
45
+ const concated = concat([hardline, script]);
46
+
47
+ return keywordIndices.includes(index) ? concated : indent(concated);
48
+ })
49
+ )
50
+ );
51
+ } else {
52
+ parts.push(indent(concat([hardline, join(hardline, scripts)])));
53
+ }
54
+ }
55
+
56
+ return group(concat(parts));
57
+ }
58
+
59
+ module.exports = silentScript;
@@ -0,0 +1,193 @@
1
+ const {
2
+ align,
3
+ concat,
4
+ fill,
5
+ group,
6
+ hardline,
7
+ ifBreak,
8
+ indent,
9
+ join,
10
+ line,
11
+ softline
12
+ } = require("../../prettier");
13
+
14
+ function getDynamicAttributes(header, attributes) {
15
+ const pairs = attributes
16
+ .slice(1, -2)
17
+ .split(",")
18
+ .map((pair) => pair.slice(1).split('" => '));
19
+
20
+ const parts = [concat([pairs[0][0], "=", pairs[0][1]])];
21
+ pairs.slice(1).forEach((pair) => {
22
+ parts.push(line, concat([pair[0], "=", pair[1]]));
23
+ });
24
+
25
+ return group(concat(["(", align(header + 1, fill(parts)), ")"]));
26
+ }
27
+
28
+ function getHashValue(value, opts) {
29
+ if (typeof value !== "string") {
30
+ return value.toString();
31
+ }
32
+
33
+ // This is a very special syntax created by the parser to let us know that
34
+ // this should be printed literally instead of as a string.
35
+ if (value.startsWith("&")) {
36
+ return value.slice(1);
37
+ }
38
+
39
+ const quote = opts.rubySingleQuote ? "'" : '"';
40
+ return `${quote}${value}${quote}`;
41
+ }
42
+
43
+ function getHashKey(key, opts) {
44
+ let quoted = key;
45
+ const joiner = opts.rubyHashLabel ? ":" : " =>";
46
+
47
+ if (key.includes(":") || key.includes("-")) {
48
+ const quote = opts.rubySingleQuote ? "'" : '"';
49
+ quoted = `${quote}${key}${quote}`;
50
+ }
51
+
52
+ return `${opts.rubyHashLabel ? "" : ":"}${quoted}${joiner}`;
53
+ }
54
+
55
+ function getKeyValuePair(key, value, opts) {
56
+ return `${getHashKey(key, opts)} ${getHashValue(value, opts)}`;
57
+ }
58
+
59
+ function getStaticAttributes(header, attributes, opts) {
60
+ const keys = Object.keys(attributes).filter(
61
+ (name) => !["class", "id"].includes(name)
62
+ );
63
+
64
+ const parts = [getKeyValuePair(keys[0], attributes[keys[0]], opts)];
65
+
66
+ keys.slice(1).forEach((key) => {
67
+ parts.push(",", line, getKeyValuePair(key, attributes[key], opts));
68
+ });
69
+
70
+ return group(concat(["{", align(header + 1, fill(parts)), "}"]));
71
+ }
72
+
73
+ function getAttributesObject(object, opts, level = 0) {
74
+ if (typeof object !== "object") {
75
+ return getHashValue(object, opts);
76
+ }
77
+
78
+ const boundary = level === 0 ? softline : line;
79
+ const parts = Object.keys(object).map((key) =>
80
+ concat([
81
+ getHashKey(key, opts),
82
+ " ",
83
+ getAttributesObject(object[key], opts, level + 1)
84
+ ])
85
+ );
86
+
87
+ return group(
88
+ concat([
89
+ "{",
90
+ indent(group(concat([boundary, join(concat([",", line]), parts)]))),
91
+ boundary,
92
+ "}"
93
+ ])
94
+ );
95
+ }
96
+
97
+ function getHeader(value, opts) {
98
+ const { attributes } = value;
99
+ const parts = [];
100
+
101
+ if (value.name !== "div") {
102
+ parts.push(`%${value.name}`);
103
+ }
104
+
105
+ if (attributes.class) {
106
+ parts.push(`.${attributes.class.replace(/ /g, ".")}`);
107
+ }
108
+
109
+ if (attributes.id) {
110
+ parts.push(`#${attributes.id}`);
111
+ }
112
+
113
+ if (value.dynamic_attributes.new) {
114
+ parts.push(
115
+ getDynamicAttributes(parts.join("").length, value.dynamic_attributes.new)
116
+ );
117
+ }
118
+
119
+ if (
120
+ Object.keys(attributes).some((name) => name !== "class" && name !== "id")
121
+ ) {
122
+ parts.push(getStaticAttributes(parts.join("").length, attributes, opts));
123
+ }
124
+
125
+ if (value.dynamic_attributes.old) {
126
+ if (parts.length === 0) {
127
+ parts.push("%div");
128
+ }
129
+
130
+ if (typeof value.dynamic_attributes.old === "string") {
131
+ parts.push(value.dynamic_attributes.old);
132
+ } else {
133
+ parts.push(getAttributesObject(value.dynamic_attributes.old, opts));
134
+ }
135
+ }
136
+
137
+ if (value.object_ref) {
138
+ if (parts.length === 0) {
139
+ parts.push("%div");
140
+ }
141
+ parts.push(value.object_ref);
142
+ }
143
+
144
+ if (value.nuke_outer_whitespace) {
145
+ parts.push(">");
146
+ }
147
+
148
+ if (value.nuke_inner_whitespace) {
149
+ parts.push("<");
150
+ }
151
+
152
+ if (value.self_closing) {
153
+ parts.push("/");
154
+ }
155
+
156
+ if (value.value) {
157
+ const prefix = value.parse ? "= " : ifBreak("", " ");
158
+
159
+ return group(
160
+ concat([
161
+ group(concat(parts)),
162
+ indent(concat([softline, prefix, value.value]))
163
+ ])
164
+ );
165
+ }
166
+
167
+ // In case none of the other if statements have matched and we're printing a
168
+ // div, we need to explicitly add it back into the array.
169
+ if (parts.length === 0 && value.name === "div") {
170
+ parts.push("%div");
171
+ }
172
+
173
+ return group(concat(parts));
174
+ }
175
+
176
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#element-name-
177
+ function tag(path, opts, print) {
178
+ const { children, value } = path.getValue();
179
+ const header = getHeader(value, opts);
180
+
181
+ if (children.length === 0) {
182
+ return header;
183
+ }
184
+
185
+ return group(
186
+ concat([
187
+ header,
188
+ indent(concat([hardline, join(hardline, path.map(print, "children"))]))
189
+ ])
190
+ );
191
+ }
192
+
193
+ module.exports = tag;
@@ -0,0 +1,22 @@
1
+ const parseSync = require("../parser/parseSync");
2
+
3
+ const parse = (text, _parsers, _opts) => {
4
+ return parseSync("haml", text);
5
+ };
6
+
7
+ const pragmaPattern = /^\s*-#\s*@(prettier|format)/;
8
+ const hasPragma = (text) => pragmaPattern.test(text);
9
+
10
+ // These functions are just placeholders until we can actually perform this
11
+ // properly. The functions are necessary otherwise the format with cursor
12
+ // functions break.
13
+ const locStart = (_node) => 0;
14
+ const locEnd = (_node) => 0;
15
+
16
+ module.exports = {
17
+ parse,
18
+ astFormat: "haml",
19
+ hasPragma,
20
+ locStart,
21
+ locEnd
22
+ };
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ripper'
4
+
5
+ begin
6
+ require 'haml'
7
+ rescue LoadError
8
+ # If we can't load the haml gem, then we're going to provide a shim parser
9
+ # that will warn and bail out.
10
+ class Prettier::HAMLParser
11
+ def self.parse(text)
12
+ warn(
13
+ 'The `haml` gem could not be loaded. Please ensure you have it ' \
14
+ 'installed and that it is available in the gem path.'
15
+ )
16
+
17
+ false
18
+ end
19
+ end
20
+
21
+ return
22
+ end
23
+
24
+ class Haml::Parser::ParseNode
25
+ class DeepAttributeParser
26
+ def parse(string)
27
+ Haml::AttributeParser.available? ? parse_value(string) : string
28
+ end
29
+
30
+ private
31
+
32
+ def literal(string, level)
33
+ level == 0 ? string : "&#{string}"
34
+ end
35
+
36
+ def parse_value(string, level = 0)
37
+ response = Ripper.sexp(string)
38
+ return literal(string, level) unless response
39
+
40
+ case response[1][0][0]
41
+ when :hash
42
+ hash = Haml::AttributeParser.parse(string)
43
+
44
+ if hash
45
+ # Explicitly not using Enumerable#to_h here to support Ruby 2.5
46
+ hash.each_with_object({}) do |(key, value), response|
47
+ response[key] = parse_value(value, level + 1)
48
+ end
49
+ else
50
+ literal(string, level)
51
+ end
52
+ when :string_literal
53
+ string[1...-1]
54
+ else
55
+ literal(string, level)
56
+ end
57
+ end
58
+ end
59
+
60
+ ESCAPE = /Haml::Helpers.html_escape\(\((.+)\)\)/.freeze
61
+
62
+ # If a node comes in as the plain type but starts with one of the special
63
+ # characters that haml parses, then we need to escape it with a \ when
64
+ # printing. So here we make a regexp pattern to check if the node needs to be
65
+ # escaped.
66
+ special_chars =
67
+ Haml::Parser::SPECIAL_CHARACTERS.map { |char| Regexp.escape(char) }
68
+
69
+ SPECIAL_START = /\A(?:#{special_chars.join('|')})/
70
+
71
+ def as_json
72
+ case type
73
+ when :comment, :doctype, :silent_script
74
+ to_h.tap do |json|
75
+ json.delete(:parent)
76
+ json[:children] = children.map(&:as_json)
77
+ end
78
+ when :filter, :haml_comment
79
+ to_h.tap { |json| json.delete(:parent) }
80
+ when :plain
81
+ to_h.tap do |json|
82
+ json.delete(:parent)
83
+ json[:children] = children.map(&:as_json)
84
+
85
+ text = json[:value][:text]
86
+ json[:value][:text] = "\\#{text}" if text.match?(SPECIAL_START)
87
+ end
88
+ when :root
89
+ to_h.tap { |json| json[:children] = children.map(&:as_json) }
90
+ when :script
91
+ to_h.tap do |json|
92
+ json.delete(:parent)
93
+ json[:children] = children.map(&:as_json)
94
+
95
+ if json[:value][:text].match?(ESCAPE)
96
+ json[:value][:text].gsub!(ESCAPE) { $1 }
97
+ json[:value].merge!(escape_html: 'escape_html', interpolate: true)
98
+ end
99
+ end
100
+ when :tag
101
+ to_h.tap do |json|
102
+ json.delete(:parent)
103
+
104
+ # For some reason this is actually using a symbol to represent a null
105
+ # object ref instead of nil itself, so just replacing it here for
106
+ # simplicity in the printer
107
+ json[:value][:object_ref] = nil if json[:value][:object_ref] == :nil
108
+
109
+ # Get a reference to the dynamic attributes hash
110
+ dynamic_attributes = value[:dynamic_attributes].to_h
111
+
112
+ # If we have any in the old style, then we're going to pass it through
113
+ # the deep attribute parser filter.
114
+ if dynamic_attributes[:old]
115
+ dynamic_attributes[:old] =
116
+ DeepAttributeParser.new.parse(dynamic_attributes[:old])
117
+ end
118
+
119
+ json.merge!(
120
+ children: children.map(&:as_json),
121
+ value: value.merge(dynamic_attributes: dynamic_attributes)
122
+ )
123
+ end
124
+ else
125
+ raise ArgumentError, "Unsupported type: #{type}"
126
+ end
127
+ end
128
+ end
129
+
130
+ module Prettier
131
+ class HAMLParser
132
+ def self.parse(source)
133
+ Haml::Parser.new({}).call(source).as_json
134
+ rescue StandardError
135
+ false
136
+ end
137
+ end
138
+ end