From e3a868bf6de213e8212a5f9e68d1db57c660a54a Mon Sep 17 00:00:00 2001 From: Amit Solanki Date: Mon, 23 Jul 2018 11:24:17 +0530 Subject: [PATCH 1/3] WIP: comments support --- src/index.js | 72 ++++++++++++++++++- .../comments/__snapshots__/jsfmt.spec.js.snap | 20 ++++++ tests/comments/comments.rb | 8 +++ tests/comments/jsfmt.spec.js | 1 + vendor/ruby/astexport.rb | 19 +++-- 5 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 tests/comments/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/comments/comments.rb create mode 100644 tests/comments/jsfmt.spec.js diff --git a/src/index.js b/src/index.js index 5583169..8c19433 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ "use strict"; +const utilShared = require("prettier").util; const util = require("./_util-from-prettier"); const parse = require("./parser"); const print = require("./printer"); @@ -20,10 +21,79 @@ const languages = [ const parsers = { ruby: { parse, - astFormat: "ruby" + astFormat: "ruby", + locStart: locStart, + locEnd: locEnd } }; +function locStart(node) { + // This function is copied from the code that used to live in the main prettier repo. + + // Handle nodes with decorators. They should start at the first decorator + if ( + node.declaration && + node.declaration.decorators && + node.declaration.decorators.length > 0 + ) { + return locStart(node.declaration.decorators[0]); + } + if (node.decorators && node.decorators.length > 0) { + return locStart(node.decorators[0]); + } + + if (node.__location) { + return node.__location.startOffset; + } + if (node.range) { + return node.range[0]; + } + if (typeof node.start === "number") { + return node.start; + } + if (node.source) { + return ( + utilShared.lineColumnToIndex(node.source.start, node.source.input.css) - 1 + ); + } + if (node.loc) { + return node.loc.start; + } + + return 0; +} + +function locEnd(node) { + // This function is copied from the code that used to live in the main prettier repo. + + const endNode = node.nodes && utilShared.getLast(node.nodes); + if (endNode && node.source && !node.source.end) { + node = endNode; + } + + let loc; + if (node.range) { + loc = node.range[1]; + } else if (typeof node.end === "number") { + loc = node.end; + } else if (node.source) { + loc = utilShared.lineColumnToIndex(node.source.end, node.source.input.css); + } + + if (node.__location) { + return node.__location.endOffset; + } + if (node.typeAnnotation) { + return Math.max(loc, locEnd(node.typeAnnotation)); + } + + if (node.loc && !loc) { + return node.loc.end; + } + + return loc || 0; +} + function canAttachComment(node) { return node.ast_type && node.ast_type !== "comment"; } diff --git a/tests/comments/__snapshots__/jsfmt.spec.js.snap b/tests/comments/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 0000000..816962d --- /dev/null +++ b/tests/comments/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`comments.rb 1`] = ` +alias $foo $bar #comment + +alias store []= #comment 2 + +alias :foo :bar #comment test test test test + +alias foo bar +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +alias $foo $bar # comment + +alias store []= # comment 2 + +alias :foo :bar # comment test test test test + +alias foo bar + +`; diff --git a/tests/comments/comments.rb b/tests/comments/comments.rb new file mode 100644 index 0000000..a52fc8a --- /dev/null +++ b/tests/comments/comments.rb @@ -0,0 +1,8 @@ +alias $foo $bar #comment + +alias store []= #comment 2 + +alias :foo :bar #comment test test test test + # asdasd +alias foo bar #### + diff --git a/tests/comments/jsfmt.spec.js b/tests/comments/jsfmt.spec.js new file mode 100644 index 0000000..fb0b5ea --- /dev/null +++ b/tests/comments/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["ruby"]); diff --git a/vendor/ruby/astexport.rb b/vendor/ruby/astexport.rb index e39d1a4..8b4c324 100644 --- a/vendor/ruby/astexport.rb +++ b/vendor/ruby/astexport.rb @@ -11,13 +11,14 @@ def initialize(code) @tokens = Ripper.lex(code) @alltokens = Ripper.lex(code) @sexp = Ripper.sexp(code) + @comments = [] @json = visit(@sexp) - @json[:comments] = [] (token_line, _), token = last_token last_token_is_END = token == :on___end__ if last_token_is_END @json[:body] << { ast_type: "__END__", content: code.lines[token_line..-1] } end + @json[:comments] = @comments end def last_token @@ -52,7 +53,7 @@ def take_token(token) if(current_token_type === token) next_token else - # throw "Got token #{token}, expected #{current_token_type}, tokens: #{@tokens}" + throw "Got token #{token}, expected #{current_token_type}, tokens: #{@tokens}" end end @@ -75,7 +76,13 @@ def hardline? end def remove_space_or_newline - while(current_token_type === :on_sp || current_token_type === :on_nl || current_token_type === :on_ignored_nl) + while(current_token_type === :on_sp || current_token_type === :on_nl || current_token_type === :on_ignored_nl || current_token_type === :on_comment) + if(current_token_type === :on_comment) + (line, column), type, value = current_token + # throw "#{line}, #{column}, #{type}, #{value}" + v = { ast_type: "comment", start: line, end: column, value: value } + @comments.push v + end next_token end end @@ -90,7 +97,7 @@ def remove_token(operator) if(operator === current_token_value) next_token else - # throw "expected operator #{current_token_value}, but got #{operator}" + throw "expected operator #{current_token_value}, but got #{operator}" end end @@ -526,7 +533,9 @@ def visit_alias(node) from = visit(from) remove_space to = visit(to) - { ast_type: type, from: from, to: to } + hardline = hardline? + remove_space_or_newline + { ast_type: type, from: from, to: to, hardline: hardline } end def visit_BEGIN(node) From 7a802ef5d6e2d936fe4342be98e9763364f365be Mon Sep 17 00:00:00 2001 From: Amit Solanki Date: Tue, 24 Jul 2018 19:25:53 +0530 Subject: [PATCH 2/3] Initial Support for Comments, (only for alias) --- src/index.js | 16 ++++++++-- src/printer/index.js | 9 +++--- tests/comments/comments.rb | 3 +- vendor/ruby/astexport.rb | 60 +++++++++++++++++++++++++++----------- 4 files changed, 63 insertions(+), 25 deletions(-) diff --git a/src/index.js b/src/index.js index 8c19433..671c24d 100644 --- a/src/index.js +++ b/src/index.js @@ -102,8 +102,20 @@ function printComment(commentPath) { const comment = commentPath.getValue(); switch (comment.ast_type) { - case "comment": - return comment.value; + case "comment": { + const hasNewLine = comment.value.lastIndexOf("\n"); + let value = comment.value; + const hasSpaceAfterHash = value.charAt(1) === " "; + if (hasNewLine) { + value = value.slice(0, hasNewLine); + } + + if (!hasSpaceAfterHash) { + value = "# " + value.slice(1, value.length); + } + + return value; + } default: throw new Error("Not a comment: " + JSON.stringify(comment)); } diff --git a/src/printer/index.js b/src/printer/index.js index 36b2879..725ff20 100644 --- a/src/printer/index.js +++ b/src/printer/index.js @@ -208,7 +208,7 @@ function genericPrint(path, options, print) { } case "@ident": { - return concat([n.value, n.hardline ? hardline : ""]); + return path.call(print, "value"); } case "var_field": { @@ -525,7 +525,7 @@ function genericPrint(path, options, print) { : path.call(print, "args"); let finalBody = group(concat(["(", body, ")"])); const name = n.name && path.call(print, "name"); - const value = name && name.parts[0]; + const value = name; if (keywords.hasOwnProperty(value)) { finalBody = group(concat([" ", body])); } @@ -602,7 +602,7 @@ function genericPrint(path, options, print) { } case "symbol": { - return concat([":", path.call(print, "symbol")]); + return group(concat([":", path.call(print, "symbol")])); } case "dyna_symbol": { @@ -651,7 +651,8 @@ function genericPrint(path, options, print) { " ", path.call(print, "from"), " ", - path.call(print, "to") + path.call(print, "to"), + n.hardline ? hardline : "" ]); } diff --git a/tests/comments/comments.rb b/tests/comments/comments.rb index a52fc8a..54c5edf 100644 --- a/tests/comments/comments.rb +++ b/tests/comments/comments.rb @@ -3,6 +3,5 @@ alias store []= #comment 2 alias :foo :bar #comment test test test test - # asdasd -alias foo bar #### +alias foo bar diff --git a/vendor/ruby/astexport.rb b/vendor/ruby/astexport.rb index 8b4c324..a847925 100644 --- a/vendor/ruby/astexport.rb +++ b/vendor/ruby/astexport.rb @@ -9,7 +9,24 @@ def initialize(code) @json = [] @code = code @tokens = Ripper.lex(code) - @alltokens = Ripper.lex(code) + total = 0 + currentLine = 0 + lastCol = 0 + t = @tokens.map do |n| + (line, col), _, value = n + if currentLine < line + total = lastCol + currentLine = line + end + start = total + col + n.push start + last = start + value.length + lastCol = last + n.push last + n + end + @alltokens = t.map { |t1| t1 } + @tokens = t @sexp = Ripper.sexp(code) @comments = [] @json = visit(@sexp) @@ -53,7 +70,7 @@ def take_token(token) if(current_token_type === token) next_token else - throw "Got token #{token}, expected #{current_token_type}, tokens: #{@tokens}" + # throw "Got token #{token}, expected #{current_token_type}, tokens: #{@tokens}" end end @@ -68,21 +85,27 @@ def next_is_newline? end def hardline? - hardline = line? && next_is_newline? + while( current_token_type === :on_nl) + next_token + end + hardline = current_token_type === :on_ignored_nl while(line?) next_token end hardline end + def take_comment + while(current_token_type === :on_comment) + (line, column), type, value, start, sEnd = current_token + v = { ast_type: "comment", start: start, end: sEnd, loc: {start: {line: line, column: column}, end: {line: line, column: value.length}}, value: value } + @comments.push v + next_token + end + end + def remove_space_or_newline - while(current_token_type === :on_sp || current_token_type === :on_nl || current_token_type === :on_ignored_nl || current_token_type === :on_comment) - if(current_token_type === :on_comment) - (line, column), type, value = current_token - # throw "#{line}, #{column}, #{type}, #{value}" - v = { ast_type: "comment", start: line, end: column, value: value } - @comments.push v - end + while(current_token_type === :on_sp || current_token_type === :on_nl || current_token_type === :on_ignored_nl) next_token end end @@ -97,7 +120,7 @@ def remove_token(operator) if(operator === current_token_value) next_token else - throw "expected operator #{current_token_value}, but got #{operator}" + # throw "expected operator #{current_token_value}, but got #{operator}" end end @@ -199,13 +222,13 @@ def visit(node) type, value = node take_token(:on_gvar) remove_space - { ast_type: type, value: value, newline: line?, hardline: hardline? } + { ast_type: type, value: value } when :@op # [:@op, "[]=", [1, 1]] type, value = node take_token(:on_op) remove_space - { ast_type: type, value: value, newline: line?, hardline: hardline? } + { ast_type: type, value: value } when :@int # [:@int, "123", [1, 0]] type, int = node @@ -229,12 +252,12 @@ def visit(node) { ast_type: type, value: char } when :@ident take_token(:on_ident) - { ast_type: '@ident', value: node[1], newline: line?, hardline: hardline? } + { ast_type: '@ident', value: node[1] } when :@kw take_token(:on_kw) # [:@kw, "nil", [1, 0]] type, value = node - { ast_type: type, value: value, newline: line?, hardline: hardline? } + { ast_type: type, value: value } when :@const # [:@const, "Constant", [1, 0]] type, value = node @@ -528,14 +551,17 @@ def visit_super(node) def visit_alias(node) # [:alias, from, to] type, from, to = node + (startLine, startColumn), _, _, start = current_token take_token(:on_kw) remove_space from = visit(from) remove_space to = visit(to) + remove_space + (endLine, endColumn), _, _, _, sEnd = current_token + take_comment hardline = hardline? - remove_space_or_newline - { ast_type: type, from: from, to: to, hardline: hardline } + { ast_type: type, from: from, to: to, hardline: hardline, start: start, end: sEnd, loc: {start: {line: startLine, column: startColumn}, end: { line: endLine, column: endColumn}} } end def visit_BEGIN(node) From 4b7b1918e2ce66d24a4592882c1e837ef6afa00a Mon Sep 17 00:00:00 2001 From: Amit Solanki Date: Wed, 25 Jul 2018 22:37:36 +0530 Subject: [PATCH 3/3] Comments for arrays, grouping , formatiing --- src/printer/index.js | 23 ++++-- tests/array/__snapshots__/jsfmt.spec.js.snap | 6 +- tests/array/empty_array.rb | 1 + tests/array/number_array.rb | 3 +- vendor/ruby/astexport.rb | 80 ++++++++++++++++---- 5 files changed, 87 insertions(+), 26 deletions(-) diff --git a/src/printer/index.js b/src/printer/index.js index 725ff20..789a560 100644 --- a/src/printer/index.js +++ b/src/printer/index.js @@ -714,13 +714,13 @@ function genericPrint(path, options, print) { const targetParts = []; targetParts.push(path.call(print, "target"), line, "="); let value = group(path.call(print, "value")); - if (n.value.ast_type !== "hash") { - value = indent(group(concat([line, value]))); - } else { + if (n.value.ast_type === "hash" || n.value.ast_type === "array") { targetParts.push(line); + } else { + value = indent(group(concat([line, value]))); } const target = group(concat(targetParts)); - return concat([target, value]); + return group(concat([target, value, n.hardline ? hardline : ""])); } case "massign": { @@ -1015,8 +1015,8 @@ function genericPrint(path, options, print) { } case "array": { - if (n.body === null) { - return "[]"; + if (n.body === null || (n.body && n.body.length === 0)) { + return group(concat(["[", "]"])); } let parts = []; const type = n.array_type; @@ -1025,8 +1025,15 @@ function genericPrint(path, options, print) { parts = group( concat([ "[", - join(concat([", ", softline]), path.map(print, "body")), - "]" + indent( + group( + concat([ + softline, + join(concat([",", line]), path.map(print, "body")) + ]) + ) + ), + concat([softline, dedent("]")]) ]) ); break; diff --git a/tests/array/__snapshots__/jsfmt.spec.js.snap b/tests/array/__snapshots__/jsfmt.spec.js.snap index 955c65a..e2b14fd 100644 --- a/tests/array/__snapshots__/jsfmt.spec.js.snap +++ b/tests/array/__snapshots__/jsfmt.spec.js.snap @@ -2,18 +2,22 @@ exports[`empty_array.rb 1`] = ` empty_array = [] + empty_array ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ empty_array = [] + empty_array `; exports[`number_array.rb 1`] = ` -numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] +numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] + numbers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] + numbers `; diff --git a/tests/array/empty_array.rb b/tests/array/empty_array.rb index 312bf6a..a12a3b6 100644 --- a/tests/array/empty_array.rb +++ b/tests/array/empty_array.rb @@ -1,2 +1,3 @@ empty_array = [] + empty_array diff --git a/tests/array/number_array.rb b/tests/array/number_array.rb index ced05e1..a2bcbfe 100644 --- a/tests/array/number_array.rb +++ b/tests/array/number_array.rb @@ -1,2 +1,3 @@ -numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] +numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] + numbers diff --git a/vendor/ruby/astexport.rb b/vendor/ruby/astexport.rb index a847925..367da2a 100644 --- a/vendor/ruby/astexport.rb +++ b/vendor/ruby/astexport.rb @@ -126,11 +126,17 @@ def remove_token(operator) def visit_assign(node) type, target, value = node + remove_space + (sLine, sColumn), _, _, start = current_token target = visit(target) remove_space remove_token("=") + remove_space value = visit_assign_value(value) - { ast_type: type, target: target, value: value } + remove_space + take_comment + (eLine, eColumn), _, _, _,sEnd = current_token + { ast_type: type, target: target, value: value, start: start, end: sEnd, hardline: hardline? } end def visit_opassign(node) @@ -151,48 +157,77 @@ def visit_assign_value(node) def visit_array(node) type, body = node array_type = "normal" - remove_space_or_newline - if current_token_type === :on_qwords_beg + remove_space + (eLine, eColumn), _, _, start = current_token + if current_token_type == :on_qwords_beg take_token(:on_qwords_beg) array_type = "words" body = visit_q_or_i_elements(body) - elsif current_token_type === :on_qsymbols_beg + (eLine, eColumn), _, _, _, sEnd = current_token + remove_space + take_comment + elsif current_token_type == :on_qsymbols_beg take_token(:on_qsymbols_beg) array_type = "symbols" body = visit_q_or_i_elements(body) + (eLine, eColumn), _, _, _, sEnd = current_token + remove_space + take_comment else body = visit_literal_elements(body) + (eLine, eColumn), _, _, _, sEnd = current_token + remove_space + take_comment end - { ast_type: type, body: body, newline: line?, hardline: hardline?, array_type: array_type } + { ast_type: type, body: body, array_type: array_type, start: start, end: sEnd } end def visit_literal_elements(nodes) remove_token("[") - remove_space_or_newline + remove_space + take_comment + hardline? exprs = [] unless nodes.nil? nodes.each do |exp| type, _ = exp + remove_space + take_comment + hardline? exprs << visit(exp) unless type == :void_stmt - remove_space_or_newline + remove_space + take_comment + hardline? remove_token(",") if current_token_type === :on_comma - remove_space_or_newline + remove_space + take_comment + hardline? end end + remove_space + take_comment remove_token("]") exprs end def visit_q_or_i_elements(nodes) - remove_space_or_newline + remove_space + take_comment + hardline? exprs = [] nodes.each do |exp| type, _ = exp exprs << visit(exp) unless type == :void_stmt - remove_space_or_newline + remove_space + take_comment + hardline? take_token(:on_tstring_end) if current_token_type === :on_tstring_end - remove_space_or_newline + remove_space + take_comment + hardline? end + remove_space + take_comment exprs end @@ -232,8 +267,10 @@ def visit(node) when :@int # [:@int, "123", [1, 0]] type, int = node - remove_token(int) - { ast_type: type, value: int } + remove_space + (line, column), _, _, start, sEnd = current_token + take_token(:on_int) + { ast_type: type, value: int, start: start, end: sEnd } when :@float # [:@float, "123.45", [1, 0]] type, float = node @@ -332,7 +369,7 @@ def visit(node) args, local_args = visit_block_args(node) { ast_type: 'block_var', args: args, local_args: local_args } when :var_ref - { ast_type: 'var_ref', ref: visit(node[1]) } + { ast_type: 'var_ref', ref: visit(node[1]), hardline: hardline? } when :arg_paren type, args = node @@ -722,14 +759,23 @@ def visit_defs(node) def visit_def(node) type, name, params, body = node take_token(:on_kw) + remove_space + name = visit(name) + remove_space if params[0] == :paren params = visit(params[1]) else remove_space params = visit(params) end - remove_space_or_newline - { ast_type: type, name: visit(name), params: params, bodystmt: visit(body) } + remove_space + take_comment + hardline? + body = visit(body) + remove_space + take_comment + hardline? + { ast_type: type, name: name, params: params, bodystmt: body } end def visit_return(node) @@ -849,6 +895,8 @@ def visit_params(node) label_params = visit_label_params(label_params) if label_params double_star_param = visit(double_star_param) if double_star_param blockarg = visit(blockarg) if blockarg + take_comment + remove_space_or_newline { ast_type: type, pre_rest_params: pre_rest_params,