├── .gitignore
├── .no-sublime-package
├── BeautifyRuby.sublime-settings
├── Default (Linux).sublime-keymap
├── Default (OSX).sublime-keymap
├── Default (Windows).sublime-keymap
├── Default.sublime-commands
├── Gemfile
├── Gemfile.lock
├── Main.sublime-menu
├── README.md
├── beautify_ruby.py
├── lib
├── erbbeautify.rb
├── rbeautify.rb
└── rbeautify
│ ├── block_end.rb
│ ├── block_matcher.rb
│ ├── block_start.rb
│ ├── config
│ └── ruby.rb
│ ├── language.rb
│ ├── line.rb
│ └── tab_size.rb
├── license.txt
└── spec
├── fixtures
└── ruby.yml
├── rbeautify
├── block_matcher_spec.rb
├── block_start_spec.rb
├── config
│ └── ruby_spec.rb
└── line_spec.rb
├── rbeautify_spec.rb
└── spec_helper.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | *.orig
2 | *.pyc
--------------------------------------------------------------------------------
/.no-sublime-package:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraigWilliams/BeautifyRuby/43ce3e5b4e7244009f3d396deae179e3f33c24f9/.no-sublime-package
--------------------------------------------------------------------------------
/BeautifyRuby.sublime-settings:
--------------------------------------------------------------------------------
1 | {
2 | // Would you prefer a tab or two spaces to represent a tab
3 | // The default is two spaces represented by 'space'
4 | // anything else will use one tab character
5 | "file_patterns": ["\\.html\\.erb", "\\.rb", "\\.rake", "Rakefile", "Gemfile", "Vagrantfile"],
6 | "html_erb_patterns": ["\\.html\\.erb"],
7 | "run_on_save": false,
8 | "save_on_beautify": true
9 | }
10 |
--------------------------------------------------------------------------------
/Default (Linux).sublime-keymap:
--------------------------------------------------------------------------------
1 | [
2 | { "keys": ["ctrl+alt+k"], "command": "beautify_ruby" }
3 | ]
--------------------------------------------------------------------------------
/Default (OSX).sublime-keymap:
--------------------------------------------------------------------------------
1 | [{
2 | "keys": ["ctrl+super+k"],
3 | "command": "beautify_ruby"
4 | }]
5 |
--------------------------------------------------------------------------------
/Default (Windows).sublime-keymap:
--------------------------------------------------------------------------------
1 | [
2 | { "keys": ["ctrl+alt+k"], "command": "beautify_ruby" }
3 | ]
--------------------------------------------------------------------------------
/Default.sublime-commands:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "caption": "BeautifyRuby",
4 | "command": "beautify_ruby"
5 | }
6 | ]
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'rspec'
4 | gem 'pry'
5 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | coderay (1.1.0)
5 | diff-lcs (1.2.5)
6 | method_source (0.8.2)
7 | pry (0.10.1)
8 | coderay (~> 1.1.0)
9 | method_source (~> 0.8.1)
10 | slop (~> 3.4)
11 | rspec (3.1.0)
12 | rspec-core (~> 3.1.0)
13 | rspec-expectations (~> 3.1.0)
14 | rspec-mocks (~> 3.1.0)
15 | rspec-core (3.1.2)
16 | rspec-support (~> 3.1.0)
17 | rspec-expectations (3.1.0)
18 | diff-lcs (>= 1.2.0, < 2.0)
19 | rspec-support (~> 3.1.0)
20 | rspec-mocks (3.1.0)
21 | rspec-support (~> 3.1.0)
22 | rspec-support (3.1.0)
23 | slop (3.6.0)
24 |
25 | PLATFORMS
26 | ruby
27 |
28 | DEPENDENCIES
29 | pry
30 | rspec
31 |
--------------------------------------------------------------------------------
/Main.sublime-menu:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "edit",
4 | "children":
5 | [
6 | {"id": "wrap"},
7 | { "command": "beautify_ruby" }
8 | ]
9 | },
10 | {
11 | "id": "preferences",
12 | "children":
13 | [
14 | {
15 | "caption": "Package Settings",
16 | "mnemonic": "P",
17 | "id": "package-settings",
18 | "children":
19 | [
20 | {
21 | "caption": "BeautifyRuby",
22 | "children":
23 | [
24 | {
25 | "command": "open_file",
26 | "args": {"file": "${packages}/BeautifyRuby/BeautifyRuby.sublime-settings"},
27 | "caption": "Settings – Default"
28 | },
29 | {
30 | "command": "open_file",
31 | "args": { "file": "${packages}/User/BeautifyRuby.sublime-settings" },
32 | "caption": "Settings - User"
33 | },
34 | {
35 | "command": "open_file",
36 | "args": {
37 | "file": "${packages}/BeautifyRuby/Default (Windows).sublime-keymap",
38 | "platform": "Windows"
39 | },
40 | "caption": "Key Bindings – Default"
41 | },
42 | {
43 | "command": "open_file",
44 | "args": {
45 | "file": "${packages}/BeautifyRuby/Default (OSX).sublime-keymap",
46 | "platform": "OSX"
47 | },
48 | "caption": "Key Bindings – Default"
49 | },
50 | {
51 | "command": "open_file",
52 | "args": {
53 | "file": "${packages}/BeautifyRuby/Default (Linux).sublime-keymap",
54 | "platform": "Linux"
55 | },
56 | "caption": "Key Bindings – Default"
57 | },
58 | {
59 | "command": "open_file",
60 | "args": {
61 | "file": "${packages}/User/Default (Windows).sublime-keymap",
62 | "platform": "Windows"
63 | },
64 | "caption": "Key Bindings – User"
65 | },
66 | {
67 | "command": "open_file",
68 | "args": {
69 | "file": "${packages}/User/Default (OSX).sublime-keymap",
70 | "platform": "OSX"
71 | },
72 | "caption": "Key Bindings – User"
73 | },
74 | {
75 | "command": "open_file",
76 | "args": {
77 | "file": "${packages}/User/Default (Linux).sublime-keymap",
78 | "platform": "Linux"
79 | },
80 | "caption": "Key Bindings – User"
81 | },
82 | { "caption": "-" }
83 | ]
84 | }
85 | ]
86 | }
87 | ]
88 | }
89 | ]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [ ](https://www.codeship.io/projects/6700)
2 |
3 | # BeautifyRuby
4 |
5 | Erb html templates uses [Paul Battley's htmlbeautifier gem](https://github.com/threedaymonk/htmlbeautifier). This (as well as rubygems) is assumed to be installed as seen by the ruby interpreter. Note that if you beautify an erb file but `htmlbeautifier` is not found, the error message is 'check your ruby interpreter settings', do not be misled.
6 |
7 | ### Interpreter settings
8 |
9 | If an error is encountered while processing the file, Python receives an empty string and the following message is displayed but may have nothing to do with your Ruby settings.
10 |
11 | ```
12 | check your ruby interpreter settings
13 | ```
14 |
15 | ### Hooks
16 |
17 | This package offers a pre-save hook, i.e., your ruby and erb files will be reformatted automatically before saving. To activate this feature, set:
18 |
19 | "run_on_save": true,
20 |
21 | The sublime command "beautify_ruby" performs a save after formatting. You can disable this default by setting:
22 |
23 | "save_on_beautify": false
24 |
25 | You can change the file patterns handled by this plugin in the settings:
26 |
27 | "file_patterns": [ "\\.html\\.erb", "\\.rb", "\\.rake", "Rakefile", "Gemfile" ],
28 | "html_erb_patterns": ["\\.html\\.erb"],
29 |
30 | This plugin uses ruby scripts to beautify your buffer, so it needs ruby installed. You can configure your ruby interpreter under Preferences -> Package Settings -> BeautifyRuby -> Settings Default/User [click here for a screenshot](https://user-images.githubusercontent.com/15097447/62330753-f6fc5100-b4fc-11e9-8c41-1015b62ac943.png). Although the default should work on linux and osx, not setting this right is a common problem.
31 |
32 | If you do not use the system ruby, type in your favourite shell:
33 |
34 | ```
35 | which ruby
36 | ```
37 |
38 | and place that in the ruby setting.
39 |
40 | On windows, set Preferences -> Package Settings -> BeautifyRuby -> Settings Default
41 |
42 | ```
43 | "ruby": "ruby"
44 | ```
45 |
46 | If you use project-specific rubies and gem sets managed with `rvm`, then simply set
47 |
48 | "ruby": "~/.rvm/bin/rvm-auto-ruby",
49 |
50 | and then the `htmlbeautifier` gem is found even if it is only installed for this project.
51 |
52 | If you are using ruby on the Windows Subsystem for Linux, use:
53 |
54 | ```
55 | "ruby": "wsl ruby"
56 | ```
57 |
58 | ### Tabs or Spaces
59 |
60 | By default, Sublime does not translate tabs to spaces. If you wish to use tabs you will not need to change your settings. If you wish to use spaces, add the following setting.
61 |
62 | ```
63 | "translate_tabs_to_spaces": true
64 | ```
65 |
66 | Or if you wish to force the use of tabs use:
67 |
68 | ```
69 | "translate_tabs_to_spaces": false
70 | ```
71 | ### Tab size
72 |
73 | Sublime's default `tab_size` is set to 4. Override this setting to change the number of spaces to use when using spaces instead of tabs.
74 |
75 | ```
76 | "tab_size": 2
77 | ```
78 |
79 | ### Key Binding
80 |
81 | ```
82 | ctrl + cmd + k on OS X, or ctrl + alt + k on Windows
83 | ```
84 |
85 | # Installation
86 |
87 | ### Package Control
88 | Using [Package Control](http://wbond.net/sublime_packages/package_control), a
89 | package manager for Sublime Text 2.
90 |
91 | In ST2, press "cmd + shift + p" and then type "install".
92 |
93 | Once you see "Package Control: Install Package", enter.
94 |
95 | When the packages load, another selection window will appear. Type
96 |
97 | BeautifyRuby and enter. All done!
98 |
99 | ### Manual Installation
100 |
101 | ```bash
102 | cd "~/Library/Application Support/Sublime Text 2/Packages/"
103 | git clone git://github.com/CraigWilliams/BeautifyRuby.git
104 | ```
105 |
--------------------------------------------------------------------------------
/beautify_ruby.py:
--------------------------------------------------------------------------------
1 | import os.path
2 | import sublime, sublime_plugin, sys, re
3 | import subprocess
4 |
5 | class BeautifyRubyOnSave(sublime_plugin.EventListener):
6 | def on_pre_save(self, view):
7 | self.settings = sublime.load_settings('BeautifyRuby.sublime-settings')
8 | if self.settings.get('run_on_save'):
9 | view.run_command("beautify_ruby", {"save": False, "error": False})
10 |
11 | class BeautifyRubyCommand(sublime_plugin.TextCommand):
12 | def run(self, edit, error=True, save=True):
13 | self.load_settings()
14 |
15 | for s in ['translate_tabs_to_spaces', 'tab_size', 'keep_blank_lines']:
16 | self.view.settings().set(s, self.settings.get(s))
17 |
18 | self.filename = self.view.file_name()
19 | self.fname = os.path.basename(self.filename)
20 | self.erb = self.is_erb_file()
21 |
22 | try:
23 | if self.erb or self.is_ruby_file():
24 | self.beautify_buffer(edit)
25 | if save and self.settings.get('save_on_beautify'):
26 | self.view.run_command('save')
27 | else:
28 | if error:
29 | raise Exception("This is not a Ruby file.")
30 | except:
31 | msg = "Error: {0}".format(sys.exc_info()[1])
32 | sublime.error_message(msg)
33 |
34 | def beautify_buffer(self, edit):
35 | buffer_region = sublime.Region(0, self.view.size())
36 | buffer_text = self.view.substr(buffer_region)
37 | if buffer_text == "":
38 | return
39 | self.save_viewport_state()
40 | beautified_buffer = self.pipe(self.cmd(), buffer_text)
41 | fix_lines = beautified_buffer.replace(os.linesep,'\n')
42 | self.check_valid_output(fix_lines)
43 | self.view.replace(edit, buffer_region, fix_lines)
44 | self.reset_viewport_state()
45 |
46 | def check_valid_output(self, text):
47 | if text == "":
48 | msg = "invalid output. Check your ruby interpreter settings"
49 | raise Exception(msg)
50 |
51 | def cmd(self, path = "-"):
52 | if self.erb:
53 | script_name = 'erbbeautify.rb'
54 | else:
55 | script_name = 'rbeautify.rb'
56 |
57 | ruby_interpreter = self.settings.get('ruby') or self.which('ruby.exe') or self.which('ruby')
58 | ruby_script = os.path.join(sublime.packages_path(), 'BeautifyRuby', 'lib', script_name)
59 |
60 | if ruby_interpreter is None:
61 | msg = "ruby interpreter not found, set PATH environment variable or set ruby in settings"
62 | raise Exception(msg)
63 |
64 | if not os.path.exists(ruby_script):
65 | msg = "script: '" + ruby_script + "' not found."
66 | raise Exception(msg)
67 |
68 | args = ["'" + str(path) + "'"] + self.config_params()
69 |
70 | # Use translated path when wsl ruby
71 | if self.settings.get('ruby') == 'wsl ruby':
72 | ruby_script = subprocess.check_output("wsl wslpath '" + ruby_script + "'", shell=True).decode("utf-8").rstrip()
73 |
74 | return ruby_interpreter + " '" + ruby_script + "' " + ' '.join(args)
75 |
76 | def finalize_output(self, text):
77 | lines = text.splitlines()
78 | finalized_output = "\n".join(lines)
79 | if self.view.settings().get("ensure_newline_at_eof_on_save") and not text.endswith("\n"):
80 | text += "\n"
81 | return finalized_output
82 |
83 | def config_params(self):
84 | def create_parameter(name):
85 | return ['--'+name.replace('_','-'), str(self.view.settings().get(name))]
86 |
87 | result = []
88 | targets = ["tab_size","translate_tabs_to_spaces",'default_line_ending','keep_blank_lines']
89 | for target in targets:
90 | result += create_parameter(target)
91 |
92 | return result
93 |
94 | def load_settings(self):
95 | self.settings = sublime.load_settings('BeautifyRuby.sublime-settings')
96 |
97 | def save_viewport_state(self):
98 | self.previous_selection = [(region.a, region.b) for region in self.view.sel()]
99 | self.previous_position = self.view.viewport_position()
100 |
101 | def reset_viewport_state(self):
102 | self.view.set_viewport_position((0, 0,), False)
103 | self.view.set_viewport_position(self.previous_position, False)
104 | self.view.sel().clear()
105 | for a, b in self.previous_selection:
106 | self.view.sel().add(sublime.Region(a, b))
107 |
108 | def is_ruby_file(self):
109 | file_patterns = self.settings.get('file_patterns') or ['\.rb', '\.rake']
110 | return self.match_pattern(file_patterns)
111 |
112 | def is_erb_file(self):
113 | file_patterns = self.settings.get('html_erb_patterns') or ['\.html\.erb']
114 | return self.match_pattern(file_patterns)
115 |
116 | def match_pattern(self, file_patterns):
117 | patterns = re.compile(r'\b(?:%s)\b' % '|'.join(file_patterns))
118 | return patterns.search(self.fname)
119 |
120 | def pipe(self, cmd, text):
121 | cwd = os.path.dirname(self.filename)
122 | beautifier = subprocess.Popen(cmd, shell=True, cwd=cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
123 | out = beautifier.communicate(text.encode("utf-8"))[0]
124 | return out.decode('utf8')
125 |
126 | # http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python/377028#377028
127 | def which(self,program):
128 | import os
129 | def is_exe(fpath):
130 | return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
131 |
132 | fpath, fname = os.path.split(program)
133 | if fpath:
134 | if is_exe(program):
135 | return program
136 | else:
137 | for path in os.environ["PATH"].split(os.pathsep):
138 | path = path.strip('"')
139 | exe_file = os.path.join(path, program)
140 | if is_exe(exe_file):
141 | return exe_file
142 |
143 | return None
144 |
--------------------------------------------------------------------------------
/lib/erbbeautify.rb:
--------------------------------------------------------------------------------
1 | require 'htmlbeautifier'
2 | module ERBeautify
3 |
4 | class << self
5 | def beautify(input, output, options = {})
6 | output.write(HtmlBeautifier.beautify(input, options))
7 | end
8 |
9 | def main
10 | if(!ARGV[0])
11 | STDERR.puts "usage: Ruby filenames or \"-\" for stdin."
12 | exit 0
13 | else
14 | path = ARGV.shift
15 | config = generate_config(ARGV)
16 | options = Hash.new
17 |
18 | if config['translate_tabs_to_spaces'] == 'False'
19 | options[:indent] = '\t'
20 | end
21 |
22 | if config['tab_size'].to_i != 0 and config['translate_tabs_to_spaces'] != 'False'
23 | options[:tab_stops] = config['tab_size'].to_i
24 | end
25 |
26 | if config['keep_blank_lines'].to_i > 0
27 | options[:keep_blank_lines] = config['keep_blank_lines'].to_i
28 | end
29 |
30 | beautify $stdin.read.force_encoding('utf-8'), $stdout, options
31 | end
32 | end
33 |
34 | def generate_config args
35 | args.each_slice(2).with_object({}) do |parameter, result|
36 | result[parameter.first.gsub('--','').gsub('-','_')] = parameter.last
37 | end
38 | end
39 | end
40 | end
41 |
42 | # if launched as a standalone program, not loaded as a module
43 | if __FILE__ == $0
44 | ERBeautify.main
45 | end
46 |
--------------------------------------------------------------------------------
/lib/rbeautify.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/ruby -w
2 |
3 | =begin
4 | /***************************************************************************
5 | * Copyright (C) 2008, Joel Chippindale, Paul Lutus *
6 | * *
7 | * This program is free software: you can redistribute it and/or modify *
8 | * it under the terms of the GNU General Public License as published by *
9 | * the Free Software Foundation, either version 3 of the License, or *
10 | * (at your option) any later version. *
11 | * *
12 | * This program is distributed in the hope that it will be useful, *
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 | * GNU General Public License for more details. *
16 | * *
17 | * You should have received a copy of the GNU General Public License *
18 | * along with this program. If not, see . *
19 | * *
20 | ***************************************************************************/
21 | =end
22 |
23 | require_relative './rbeautify/block_start.rb'
24 | require_relative './rbeautify/block_end.rb'
25 | require_relative './rbeautify/block_matcher.rb'
26 | require_relative './rbeautify/language.rb'
27 | require_relative './rbeautify/line.rb'
28 | require_relative './rbeautify/tab_size.rb'
29 | require_relative './rbeautify/config/ruby.rb'
30 |
31 | module RBeautify
32 |
33 | class << self
34 | def beautify_string(language, source, config)
35 | dest = ""
36 | block = nil
37 |
38 | unless language.is_a? RBeautify::Language
39 | language = RBeautify::Language.language(language)
40 | end
41 |
42 | config['tab_size'] = RBeautify::TabSize.new(config).tab_size
43 | language.indent_size = config['tab_size']
44 |
45 | source.force_encoding("UTF-8").split("\n").each_with_index do |line_content, line_number|
46 | line = RBeautify::Line.new(language, line_content, line_number, block, config)
47 | dest += line.format + "\n"
48 | block = line.block
49 | end
50 |
51 | return dest
52 | end
53 |
54 | def beautify_file(path, config)
55 | backup = config["backup"] == 'True'
56 |
57 | if(path == '-') # stdin source
58 | source = STDIN.read
59 | print beautify_string(:ruby, source, config)
60 | else # named file source
61 | source = File.read(path)
62 | dest = beautify_string(:ruby, source, config)
63 | if(source != dest)
64 | if backup
65 | # make a backup copy
66 | File.open(path + "~","w") { |f| f.write(source) }
67 | end
68 | # overwrite the original
69 | File.open(path,"w") { |f| f.write(dest) }
70 | end
71 | return source != dest
72 | end
73 | end
74 |
75 | def main
76 | if(!ARGV[0])
77 | STDERR.puts "usage: Ruby filenames or \"-\" for stdin."
78 | exit 0
79 | else
80 | path = ARGV.shift
81 | config = generate_config(ARGV)
82 | beautify_file(path,config)
83 | end
84 | end
85 |
86 | def generate_config args
87 | args.each_slice(2).with_object({}) do |parameter, result|
88 | result[parameter.first.gsub('--','').gsub('-','_')] = parameter.last
89 | end
90 | end
91 |
92 | end
93 | end
94 |
95 | # if launched as a standalone program, not loaded as a module
96 | if __FILE__ == $0
97 | RBeautify.main
98 | end
99 |
--------------------------------------------------------------------------------
/lib/rbeautify/block_end.rb:
--------------------------------------------------------------------------------
1 | module RBeautify
2 |
3 | class BlockEnd
4 |
5 | attr_accessor :block_start, :offset, :match, :after_match
6 |
7 | def initialize(block_start, offset, match, after_match)
8 | self.block_start = block_start
9 | self.offset = offset
10 | self.match = match
11 | self.after_match = after_match
12 | end
13 |
14 | def end_offset
15 | offset + match.length
16 | end
17 |
18 | def end_can_also_be_start?
19 | block_start.block_matcher.end_can_also_be_start?
20 | end
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/lib/rbeautify/block_matcher.rb:
--------------------------------------------------------------------------------
1 | module RBeautify
2 |
3 | class BlockMatcher
4 |
5 | attr_reader :language, :name, :starts, :ends, :options
6 |
7 | def initialize(language, name, starts, ends, options = {})
8 | @language = language
9 | @name = name
10 | @starts = starts
11 | @ends = ends.nil? ? starts : ends
12 | @options = options
13 | end
14 |
15 | class << self
16 | def parse(language, original_block, line_number, string, current_offset)
17 | block_end = original_block && original_block.parse_block_end(string, current_offset)
18 |
19 | if (block_start = first_block_start(language,
20 | original_block,
21 | line_number,
22 | string,
23 | current_offset,
24 | block_end.nil? ? nil : block_end.offset))
25 |
26 | block_end = nil
27 | end
28 |
29 | if block_end
30 | # Check whether the section of the line which is a block end is also a block start
31 | if block_end.end_can_also_be_start? &&
32 | (block_start_candidate = first_block_start(language, block_end.block_start.parent, line_number, string, current_offset)) &&
33 | block_start_candidate.offset == block_end.offset
34 | block_start = block_start_candidate
35 | end
36 |
37 | end
38 |
39 | if block_start
40 | if debug
41 | puts "MATCH: '#{string.slice(0, string.length - block_start.match.length - block_start.after_match.length)}#{block_start.match}#{block_start.after_match}'"
42 | end
43 | parse(language, block_start, line_number, block_start.after_match, block_start.end_offset)
44 | elsif block_end
45 | if debug
46 | puts "MATCH: '#{string.slice(0, string.length - block_end.match.length - block_end.after_match.length)}#{block_end.match}#{block_end.after_match}'"
47 | end
48 | parse(language, block_end.block_start.parent, line_number, block_end.after_match, block_end.end_offset)
49 | else
50 | original_block
51 | end
52 | end
53 |
54 | def debug=(value)
55 | @debug = value
56 | end
57 |
58 | def debug
59 | @debug
60 | end
61 |
62 | private
63 | def first_block_start(language, parent_block, line_number, string, offset, maximum_offset = nil)
64 | first_block_start = nil
65 | language.matchers.each do |matcher|
66 | if matcher.can_nest?(parent_block)
67 | if (block_start_candidate = matcher.parse_block_start(string, parent_block, offset, line_number)) &&
68 | (maximum_offset.nil? || maximum_offset > block_start_candidate.offset)
69 | first_block_start = block_start_candidate
70 | maximum_offset = first_block_start.offset
71 | end
72 | end
73 | end
74 | first_block_start
75 | end
76 | end
77 |
78 | def parse_block_start(string, parent_block, offset, line_number)
79 | if !string.empty? && (match = starts.match(string))
80 | bs = RBeautify::BlockStart.new(self, parent_block, line_number, offset + match.begin(0), match[0], match.post_match)
81 | end
82 | end
83 |
84 | def indent_end_line?(block)
85 | evaluate_option_for_block(:indent_end_line, block)
86 | end
87 |
88 | def indent_size(block)
89 | evaluate_option_for_block(:indent_size, block) || language.indent_size
90 | end
91 |
92 | # Look for blocks within the content of this one
93 | def parse_content?
94 | options[:parse_content] != false
95 | end
96 |
97 | # Indent the content of this block
98 | def format_content?
99 | options[:format_content] != false
100 | end
101 |
102 | def can_nest?(parent_block)
103 | return false unless parent_block.nil? || parent_block.parse_content?
104 |
105 | if options[:nest_only]
106 | parent_block && options[:nest_only].include?(parent_block.name)
107 | else
108 | parent_block.nil? ||
109 | options[:nest_except].nil? || !options[:nest_except].include?(parent_block.name)
110 | end
111 | end
112 |
113 | def ends?
114 | ends != false
115 | end
116 |
117 | def end_is_implicit?
118 | options[:end] == :implicit
119 | end
120 |
121 | def end_can_also_be_start?
122 | if ends == starts
123 | options[:end_can_also_be_start] == true
124 | else
125 | options[:end_can_also_be_start] != false
126 | end
127 | end
128 |
129 | def negate_ends_match?
130 | options[:negate_ends_match]
131 | end
132 |
133 | # True if blocks can contain the escape character \ which needs to be
134 | # checked for on end match
135 | def escape_character?
136 | options[:escape_character] == true
137 | end
138 |
139 | def inspect
140 | name
141 | end
142 |
143 | private
144 | def evaluate_option_for_block(key, block)
145 | if options[key] && options[key].respond_to?(:call)
146 | options[key].call(block)
147 | else
148 | options[key]
149 | end
150 | end
151 |
152 | end
153 | end
154 |
--------------------------------------------------------------------------------
/lib/rbeautify/block_start.rb:
--------------------------------------------------------------------------------
1 | module RBeautify
2 |
3 | class BlockStart
4 |
5 | attr_reader :block_matcher, :parent, :offset, :match, :after_match, :line_number
6 |
7 | class << self
8 | def first_common_ancestor(first, second)
9 | if first.nil? || second.nil?
10 | nil
11 | else
12 | (first.ancestors & second.ancestors).last
13 | end
14 | end
15 | end
16 |
17 | def initialize(block_matcher, parent, line_number, offset, match, after_match)
18 | @block_matcher = block_matcher
19 | @parent = parent
20 | @offset = offset
21 | @match = match
22 | @after_match = after_match
23 | @line_number = line_number
24 | end
25 |
26 | def end_offset
27 | offset + match.length
28 | end
29 |
30 | def parse_block_end(string, offset)
31 | block_end = parse_explicit_block_end(string, offset)
32 |
33 | # Handle case where end is implicit
34 | if block_end.nil? && end_is_implicit? && parent
35 | block_end = parent.parse_block_end(string, offset)
36 | end
37 |
38 | block_end
39 | end
40 |
41 | def format_content?
42 | block_matcher.format_content?
43 | end
44 |
45 | def parse_content?
46 | block_matcher.parse_content?
47 | end
48 |
49 | def indent_end_line?
50 | block_matcher.indent_end_line?(self)
51 | end
52 |
53 | def total_indent_size
54 | parent.nil? ? indent_size : parent.total_indent_size + indent_size
55 | end
56 |
57 | def indent_size
58 | block_matcher.indent_size(self)
59 | end
60 |
61 | def end_is_implicit?
62 | block_matcher.end_is_implicit?
63 | end
64 |
65 | def name
66 | block_matcher.name
67 | end
68 |
69 | # Returns true if strict ancestor of
70 | def strict_ancestor_of?(block_start)
71 | block_start && block_start.parent && (self == block_start.parent || strict_ancestor_of?(block_start.parent))
72 | end
73 |
74 | def ancestors
75 | if parent
76 | parent.ancestors + [self]
77 | else
78 | [self]
79 | end
80 | end
81 |
82 | private
83 | def ends?
84 | block_matcher.ends?
85 | end
86 |
87 | def negate_ends_match?
88 | block_matcher.negate_ends_match?
89 | end
90 |
91 | def escape_character?
92 | block_matcher.escape_character?
93 | end
94 |
95 | def parse_explicit_block_end(string, offset)
96 | block_end = nil
97 |
98 | if ends?
99 |
100 | if match = block_matcher.ends.match(string)
101 | unless negate_ends_match?
102 | if escape_character? &&
103 | ((escape_chars = match.pre_match.match(/\\*$/)) && (escape_chars[0].size % 2 == 1))
104 | # If there are an odd number of escape characters just before
105 | # the match then this match should be skipped
106 | return parse_explicit_block_end(match.post_match, offset + escape_chars[0].size + match[0].length)
107 | else
108 | return RBeautify::BlockEnd.new(self, offset + match.begin(0), match[0], match.post_match)
109 | end
110 | end
111 | elsif negate_ends_match?
112 | return RBeautify::BlockEnd.new(self, offset, '', string)
113 | end
114 |
115 | end
116 | end
117 |
118 | end
119 | end
120 |
--------------------------------------------------------------------------------
/lib/rbeautify/config/ruby.rb:
--------------------------------------------------------------------------------
1 | # define ruby language
2 |
3 | unless RBeautify::Language.language(:ruby)
4 |
5 | ruby = RBeautify::Language.add_language(:ruby)
6 |
7 | pre_keyword_boundary = '(^|[^a-z0-9A-Z:._])' # like \b but with : , . _ all added to list of exceptions
8 | start_statement_boundary = '(^|(;|=)\s*)'
9 | continue_statement_boundary = '(^|;\s*)'
10 | ruby.indent_size ||= 2
11 |
12 | ruby.add_matcher(:program_end, /^__END__$/, false, :format_content => false, :parse_content => false)
13 |
14 | ruby.add_matcher(:multiline_comment, /^=begin/, /^=end/, :format_content => false, :parse_content => false)
15 |
16 | ruby.add_matcher(:double_quote,
17 | /"/,
18 | /"/,
19 | :parse_content => true,
20 | :format_content => false,
21 | :escape_character => true,
22 | :nest_except => [:double_quote, :single_quote, :regex, :back_tick, :percent_square, :percent_curly, :percent_round])
23 |
24 | # NEED TO MODIFY DOUBLE QUOTE TO BE FORMATTED to get this to work
25 | ruby.add_matcher(:interpolation,
26 | /#\{/,
27 | /\}/,
28 | :nest_only => [:double_quote, :regex, :backtick])
29 |
30 | ruby.add_matcher(:single_quote,
31 | /'/,
32 | /'/,
33 | :parse_content => false,
34 | :format_content => false,
35 | :escape_character => true,
36 | :nest_except => [:double_quote, :single_quote, :regex, :back_tick, :percent_square, :percent_curly, :percent_round])
37 |
38 | ruby.add_matcher(:regex,
39 | /(^|((,|=|~)\s*))\//, # Try to distinguish it from division sign
40 | /\//,
41 | :format_content => false,
42 | :escape_character => true,
43 | :end_can_also_be_start => false,
44 | :nest_except => [:double_quote, :single_quote, :regex, :back_tick, :percent_square, :percent_curly, :percent_round])
45 |
46 | ruby.add_matcher(:back_tick,
47 | /`/,
48 | /`/,
49 | :format_content => false,
50 | :escape_character => true,
51 | :nest_except => [:double_quote, :single_quote, :regex, :back_tick, :percent_square, :percent_curly, :percent_round])
52 |
53 | ruby.add_matcher(:percent_square,
54 | /%\w?\[/,
55 | /\]/,
56 | :format_content => false,
57 | :escape_character => true,
58 | :nest_except => [:double_quote, :single_quote, :regex, :back_tick, :percent_square, :percent_curly, :percent_round])
59 |
60 | ruby.add_matcher(:percent_curly,
61 | /%\w?\{/,
62 | /\}/,
63 | :format_content => false,
64 | :escape_character => true,
65 | :nest_except => [:double_quote, :single_quote, :regex, :back_tick, :percent_square, :percent_curly, :percent_round])
66 |
67 | ruby.add_matcher(:percent_round,
68 | /%\w?\(/,
69 | /\)/,
70 | :format_content => false,
71 | :escape_character => true,
72 | :nest_except => [:double_quote, :single_quote, :regex, :back_tick, :percent_square, :percent_curly, :percent_round])
73 |
74 | ruby.add_matcher(:standard,
75 | /((#{start_statement_boundary}(module|class(?!:)|def))|#{pre_keyword_boundary}do)\b/,
76 | /(((^|;|\s)end)|#{continue_statement_boundary}(rescue|ensure))\b/,
77 | :nest_except => [:double_quote, :regex, :backtick])
78 |
79 | ruby.add_matcher(:more,
80 | /#{start_statement_boundary}(until|for|while)\b/,
81 | /(((^|;|\s)end)|#{continue_statement_boundary}(rescue|ensure))\b/,
82 | :nest_except => [:double_quote, :regex, :backtick])
83 |
84 | ruby.add_matcher(:begin,
85 | /((#{start_statement_boundary}begin)|(#{continue_statement_boundary}(ensure|rescue)))\b/,
86 | /(((^|;|\s)end)|#{continue_statement_boundary}(rescue|ensure|else))\b/,
87 | :nest_except => [:double_quote, :regex, :backtick])
88 |
89 | ruby.add_matcher(:if,
90 | /((#{start_statement_boundary}(if|unless)(?!:))|#{continue_statement_boundary}(then|elsif|else))\b/,
91 | /(((^|;|\s)end)|(#{continue_statement_boundary}(then|elsif|else)))\b/,
92 | :nest_except => [:case, :double_quote, :regex, :backtick])
93 |
94 | ruby.add_matcher(:case,
95 | /#{pre_keyword_boundary}case\b/,
96 | /(^|;|\s)end\b/,
97 | :nest_except => [:double_quote, :regex, :backtick],
98 | :indent_size => 0)
99 |
100 | ruby.add_matcher(:inner_case,
101 | /#{continue_statement_boundary}(when|else|then)\b/,
102 | /#{continue_statement_boundary}(when|else|then)\b/,
103 | :nest_only => [:case],
104 | :end => :implicit,
105 | :end_can_also_be_start => true,
106 | :nest_except => [:double_quote, :regex, :backtick])
107 |
108 | # TODO: Improve the check that this is not a block with arguments. Will
109 | # currently match any bracket followed by spaces and |.
110 | bracket_indent_end_line_proc = Proc.new { |block| !block.after_match.empty? && !block.after_match.match(/^\|/) }
111 | bracket_indent_size_proc = Proc.new do |block|
112 | if bracket_indent_end_line_proc.call(block)
113 | strict_ancestors_on_same_line = block.ancestors.select { |a| a != block && a.line_number == block.line_number }
114 | block.end_offset - strict_ancestors_on_same_line.inject(0) { |sum, a| sum + a.indent_size }
115 | end
116 | end
117 |
118 | ruby.add_matcher(:curly_bracket,
119 | /\{\s*/,
120 | /\}/,
121 | :indent_end_line => bracket_indent_end_line_proc,
122 | :indent_size => bracket_indent_size_proc,
123 | :nest_except => [:double_quote, :regex, :backtick])
124 |
125 | ruby.add_matcher(:round_bracket,
126 | /\(\s*/,
127 | /\)/,
128 | :indent_end_line => bracket_indent_end_line_proc,
129 | :indent_size => bracket_indent_size_proc,
130 | :nest_except => [:double_quote, :regex, :backtick])
131 |
132 | ruby.add_matcher(:square_bracket,
133 | /\[\s*/,
134 | /\]/,
135 | :indent_end_line => bracket_indent_end_line_proc,
136 | :indent_size => bracket_indent_size_proc,
137 | :nest_except => [:double_quote, :regex, :backtick])
138 |
139 | ruby.add_matcher(:comment, /(\s*)?#/,
140 | /$/,
141 | :parse_content => false,
142 | :format_content => false,
143 | :nest_except => [:double_quote, :single_quote, :regex, :back_tick, :percent_square, :percent_curly, :percent_round])
144 |
145 | ruby.add_matcher(:continuing_line,
146 | /(,|\.|\+|-|=\>|=|&&|\|\||\\|==|\s\?|:|<<)(\s*)?(#.*)?$/,
147 | /(^|(,|\.|\+|-|=\>|=|&&|\|\||\\|==|\s\?|:|<<)(\s*)?)(#.*)?$/,
148 | :indent_end_line => true,
149 | :negate_ends_match => true,
150 | :nest_except => [:continuing_line, :curly_bracket, :round_bracket, :square_bracket, :double_quote, :single_quote, :regex, :back_tick, :percent_square, :percent_curly, :percent_round])
151 |
152 | end
153 |
--------------------------------------------------------------------------------
/lib/rbeautify/language.rb:
--------------------------------------------------------------------------------
1 | module RBeautify
2 | class Language
3 |
4 | @@languages = {}
5 |
6 | attr_reader :matchers
7 | attr_accessor :indent_size
8 |
9 | class << self
10 |
11 | def language(name)
12 | languages[name]
13 | end
14 |
15 | def languages
16 | @@languages
17 | end
18 |
19 | def add_language(name)
20 | languages[name] = new()
21 | end
22 | end
23 |
24 | def initialize
25 | @matchers = []
26 | end
27 |
28 | def add_matcher(name, starts, ends, options = {})
29 | self.matchers << BlockMatcher.new(self, name, starts, ends, options)
30 | end
31 |
32 | def matcher(name)
33 | matchers.detect { |matcher| matcher.name == name}
34 | end
35 |
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/lib/rbeautify/line.rb:
--------------------------------------------------------------------------------
1 | module RBeautify
2 | class Line
3 |
4 | attr_reader :language, :content, :line_number, :original_block, :block, :indent_character
5 |
6 | def initialize(language, content, line_number, original_block = nil, config)
7 | @tab_size = config["tab_size"]
8 | @use_tabs = config["translate_tabs_to_spaces"] == 'False'
9 | @language = language
10 | @content = content
11 | @original_block = original_block
12 | @indent_character = @use_tabs ? "\t" : " " * @tab_size
13 | @block = BlockMatcher.parse(language, original_block, line_number, stripped, 0)
14 | end
15 |
16 | def format
17 | if @formatted.nil?
18 | if format?
19 | if stripped.length == 0
20 | @formatted = ""
21 | elsif !content.match(/\s*?^=/).nil?
22 | @formatted = stripped
23 | else
24 | @formatted = tab_string + stripped
25 | end
26 | else
27 | @formatted = content
28 | end
29 | end
30 |
31 | @formatted
32 | end
33 |
34 | private
35 | def format?
36 | original_block.nil? || original_block.format_content?
37 | end
38 |
39 | def indent_size
40 | if (block.nil? || block.strict_ancestor_of?(original_block)) && (original_block && original_block.indent_end_line?)
41 | original_block.total_indent_size
42 | else
43 | common_ancestor = BlockStart.first_common_ancestor(original_block, block)
44 | common_ancestor.nil? ? 0 : common_ancestor.total_indent_size
45 | end
46 | end
47 |
48 | def tab_string
49 | indent_character * (indent_size / @tab_size ) + (indent_size.odd? ? ' ' : '')
50 | end
51 |
52 | def stripped
53 | @stripped = content.strip
54 | end
55 |
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/lib/rbeautify/tab_size.rb:
--------------------------------------------------------------------------------
1 | module RBeautify
2 | class TabSize
3 | DEFAULT_TAB_SIZE = 2
4 |
5 | def initialize(config)
6 | @tab_size = config['tab_size'].to_i
7 | end
8 |
9 | def tab_size
10 | @tab_size == 0 ? DEFAULT_TAB_SIZE : @tab_size
11 | end
12 |
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | All of BeautifyRuby is licensed under the MIT license.
2 |
3 | Copyright (c) 2011 Craig Williams
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/spec/fixtures/ruby.yml:
--------------------------------------------------------------------------------
1 | - name: indent if else end statement
2 | input: |
3 | if foo
4 | bar = 1
5 | elsif foo2
6 | bar = 2
7 | else
8 | bar = 3
9 | end
10 | output: |
11 | if foo
12 | bar = 1
13 | elsif foo2
14 | bar = 2
15 | else
16 | bar = 3
17 | end
18 |
19 | - name: handle case where if begins and ends on same line
20 | input: |
21 | foo do
22 | if a then b = 1 else b = 2 end
23 | end
24 | output: |
25 | foo do
26 | if a then b = 1 else b = 2 end
27 | end
28 |
29 | - name: indent not double indent case/when statement
30 | input: |
31 | case foo
32 | when 1
33 | bar = 'some string'
34 | when 2
35 | bar = 'some other string'
36 | when 3 then bar = '3'
37 | else
38 | bar = '4'
39 | end
40 | output: |
41 | case foo
42 | when 1
43 | bar = 'some string'
44 | when 2
45 | bar = 'some other string'
46 | when 3 then bar = '3'
47 | else
48 | bar = '4'
49 | end
50 |
51 | - name: indent while statement
52 | input: |
53 | def foo
54 | bar = 1
55 | while bar < 3
56 | puts bar
57 | bar = bar.next
58 | end
59 | end
60 | output: |
61 | def foo
62 | bar = 1
63 | while bar < 3
64 | puts bar
65 | bar = bar.next
66 | end
67 | end
68 |
69 | - name: ignore code after end of line comment
70 | input: |
71 | def method_containing_end_of_line_comment
72 | a = b # Comment containing do
73 | end
74 | output: |
75 | def method_containing_end_of_line_comment
76 | a = b # Comment containing do
77 | end
78 |
79 | - name :not indent multineline comment:
80 | input: |
81 | =begin
82 | Comment
83 | =end
84 | foo
85 |
86 | - name: indent lines after first of multiline code
87 | input: |
88 | def method_with_multiline_method_call
89 | multiline_method_call \
90 | first_arg,
91 | second_arg,
92 | third_arg
93 | end
94 | output: |
95 | def method_with_multiline_method_call
96 | multiline_method_call \
97 | first_arg,
98 | second_arg,
99 | third_arg
100 | end
101 |
102 | - name: indent method call with bracketed multiline arguments
103 | input: |
104 | def method_with_multiline_method_call
105 | multiline_method_call(foo,
106 | bar,
107 | foobar)
108 | end
109 | output: |
110 | def method_with_multiline_method_call
111 | multiline_method_call(foo,
112 | bar,
113 | foobar)
114 | end
115 |
116 | - name: indent method call with bracketed multiline arguments_even_if_not_first_block_on_line
117 | input: |
118 | if (foo = bar(first_arg,
119 | second_arg))
120 | do_something
121 | end
122 | output: |
123 | if (foo = bar(first_arg,
124 | second_arg))
125 | do_something
126 | end
127 |
128 | - name: indent method call with multiline arguments (implicit brackets)
129 | input: |
130 | def method_with_multiline_method_call
131 | multiline_method_call first_arg,
132 | second_arg,
133 | # Comment in the middle of all this
134 | third_arg
135 |
136 | another_method_call
137 | end
138 | output: |
139 | def method_with_multiline_method_call
140 | multiline_method_call first_arg,
141 | second_arg,
142 | # Comment in the middle of all this
143 | third_arg
144 |
145 | another_method_call
146 | end
147 |
148 | - name: should indent multiline method call chains
149 | input: |
150 | def method_with_multiline_method_call_chain
151 | multiline_method_call.
152 | foo.
153 | bar
154 |
155 | another_method_call
156 | end
157 | output: |
158 | def method_with_multiline_method_call_chain
159 | multiline_method_call.
160 | foo.
161 | bar
162 |
163 | another_method_call
164 | end
165 |
166 | - name: handle multiline code with escaped quotes in strings
167 | input: |
168 | def method_containing_multiline_code_with_strings
169 | a = "foo #{method}" +
170 | "bar"
171 | end
172 | output: |
173 | def method_containing_multiline_code_with_strings
174 | a = "foo #{method}" +
175 | "bar"
176 | end
177 |
178 | - name: not change the indentation of multiline strings
179 | input: |
180 | def method_containing_long_string
181 | a = "
182 | Some text across multiple lines
183 | And another line
184 | "
185 | b = 5
186 | end
187 | output: |
188 | def method_containing_long_string
189 | a = "
190 | Some text across multiple lines
191 | And another line
192 | "
193 | b = 5
194 | end
195 |
196 | - name: not treat divison as start of regex
197 | input: |
198 | def foo
199 | a = 1/2
200 | b = :foo
201 | end
202 | output: |
203 | def foo
204 | a = 1/2
205 | b = :foo
206 | end
207 |
208 | - name: not indent multiline string even if it uses non quote delimiter
209 | pending: implementation of block matcher for non quote delimited strings
210 | input: |
211 | foo = < 1, :bar => 2
268 | :c => 3, :d => 4 }
269 | end
270 | output: |
271 | class Foo
272 | @@bar = { :foo => 1, :bar => 2
273 | :c => 3, :d => 4 }
274 | end
275 |
276 | - name: indent multiline block delimited with curly brackets
277 | input: |
278 | foo = bar.collect { |paragraph|
279 | paragraph.strip
280 | }.join("\n")
281 | output: |
282 | foo = bar.collect { |paragraph|
283 | paragraph.strip
284 | }.join("\n")
285 |
286 | - name: indent method call with bracketed multiline arguments including continuing statements
287 | pending: Implementation of support for continuing statements in bracketed multline arguments
288 | input: |
289 | def foo
290 | bar(first_arg,
291 | second_part_a +
292 | second_part_b,
293 | third_arg)
294 | end
295 | output: |
296 | def foo
297 | bar(first_arg,
298 | second_part_a +
299 | second_part_b,
300 | third_arg)
301 | end
302 |
303 | - name: indent continuing lines ending in = and +
304 | input: |
305 | def foo
306 | @bar ||=
307 | 1 +
308 | 2
309 | end
310 | output: |
311 | def foo
312 | @bar ||=
313 | 1 +
314 | 2
315 | end
316 |
317 | - name: indent block within implicit brackets
318 | pending: Implementation of support for this feature
319 | input: |
320 | def foo
321 | bar first_arg,
322 | second_arg,
323 | [
324 | 1,
325 | 2,
326 | 3
327 | ],
328 | last_arg
329 | end
330 | output: |
331 | def foo
332 | bar first_arg,
333 | second_arg,
334 | [
335 | 1,
336 | 2,
337 | 3
338 | ],
339 | last_arg
340 | end
341 |
342 | - name: "should not treat ':', '_' or '.' as word boundaries before keywords"
343 | input: |
344 | case params[:do]
345 | when "update_if"
346 | update_if.if
347 | when "update_do"
348 | update_do.do
349 | end
350 | output: |
351 | case params[:do]
352 | when "update_if"
353 | update_if.if
354 | when "update_do"
355 | update_do.do
356 | end
357 |
358 | - name: should recognize interpolation and not match other content within double quotes
359 | input: |
360 | def foo
361 | return "Foo#{" (#{bar})"}"
362 | end
363 | output: |
364 | def foo
365 | return "Foo#{" (#{bar})"}"
366 | end
367 |
368 | - name: should not non interpolated content within regexes and strings and backticks
369 | input: |
370 | def foo
371 | @foo = [
372 | /" if bar/,
373 | `ls if`,
374 | "if fun =",
375 | 'else'
376 | ]
377 | end
378 | output: |
379 | def foo
380 | @foo = [
381 | /" if bar/,
382 | `ls if`,
383 | "if fun =",
384 | 'else'
385 | ]
386 | end
387 |
388 | - name: should handle one liners that include an end statement correctly
389 | input: |
390 | def foo; puts 'foo' end
391 | def bar; puts 'bar' end
392 | output: |
393 | def foo; puts 'foo' end
394 | def bar; puts 'bar' end
395 |
396 | - name: should handle if and unless statements that are terminated implicitly
397 | input: |
398 | def foo
399 | bar = (1 + 2) if foobar
400 | baz unless foobaz
401 | Boo.new(:boo) rescue raise('Houston we have a problem')
402 | end
403 | output: |
404 | def foo
405 | bar = (1 + 2) if foobar
406 | baz unless foobaz
407 | Boo.new(:boo) rescue raise('Houston we have a problem')
408 | end
409 |
410 | - name: should use user specified 4 spaces
411 | spaces: 4
412 | input: |
413 | def foo
414 | h = {one: 'one'}
415 | end
416 | output: |
417 | def foo
418 | h = {one: 'one'}
419 | end
420 |
--------------------------------------------------------------------------------
/spec/rbeautify/block_matcher_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative './../spec_helper.rb'
2 |
3 | describe RBeautify::BlockMatcher do
4 |
5 | describe 'class' do
6 | describe '#parse' do
7 | before(:each) do
8 | @ruby = RBeautify::Language.language(:ruby)
9 | end
10 |
11 | it 'should not match de' do
12 | expect(RBeautify::BlockMatcher.parse(@ruby, nil, 0, 'de foo', 0)).to eq(nil)
13 | end
14 |
15 | it 'should match def' do
16 | block = RBeautify::BlockMatcher.parse(@ruby, nil, 0, 'def foo', 0)
17 | expect(block).to_not eq(nil)
18 | expect(block.name).to eq(:standard)
19 | end
20 |
21 | it 'should be nested block' do
22 | block = RBeautify::BlockMatcher.parse(@ruby, nil, 0, 'if {', 0)
23 | expect(block).to_not eq(nil)
24 | expect(block.name).to eq(:curly_bracket)
25 | expect(block.parent).to_not eq(nil)
26 | expect(block.parent.name).to eq(:if)
27 | end
28 |
29 | it 'should be nested block (taking into account ends)' do
30 | block = RBeautify::BlockMatcher.parse(@ruby, nil, 0, 'if {}', 0)
31 | expect(block).to_not eq(nil)
32 | expect(block.name).to eq(:if)
33 | end
34 |
35 | it 'should be deeply nested block (taking into account ends)' do
36 | block = RBeautify::BlockMatcher.parse(@ruby, nil, 0, 'def foo(bar = {})', 0)
37 | expect(block).to_not eq(nil)
38 | expect(block.name).to eq(:standard)
39 | expect(block.parent).to eq(nil)
40 | end
41 |
42 | it 'should current block if no started or ended blocks' do
43 | block = RBeautify::BlockStart.new(@ruby.matcher(:standard), nil, 0, 0, 'def', ' foo')
44 | expect(RBeautify::BlockMatcher.parse(@ruby, block, 0, 'a = 3', 0)).to eq(block)
45 | end
46 |
47 | it 'should be newly started block if ends and starts' do
48 | current_block = RBeautify::BlockStart.new(@ruby.matcher(:if), nil, 0, 0, 'if', ' foo')
49 | block = RBeautify::BlockMatcher.parse(@ruby, current_block, 0, 'else', 0)
50 | expect(block).to_not eq(nil)
51 | expect(block.name).to eq(:if)
52 | expect(block.parent).to eq(nil)
53 | end
54 |
55 | it 'should be parent block if current block ends' do
56 | parent_block = RBeautify::BlockStart.new(@ruby.matcher(:standard), nil, 0, 0, 'class', ' Foo')
57 | child_block = RBeautify::BlockStart.new(@ruby.matcher(:standard), parent_block, 0, 0, 'def', ' foo')
58 | expect(RBeautify::BlockMatcher.parse(@ruby, child_block, 0, 'end', 0)).to eq(parent_block)
59 | end
60 |
61 | it 'should remove two blocks if top of stack ends implicitly' do
62 | parent_block = RBeautify::BlockStart.new(@ruby.matcher(:case), nil, 0, 0, 'case', ' foo')
63 | child_block = RBeautify::BlockStart.new(@ruby.matcher(:inner_case), parent_block, 0, 0, 'when', '2')
64 | expect(RBeautify::BlockMatcher.parse(@ruby, child_block, 0, 'end', 0)).to eq(nil)
65 | end
66 | end
67 | end
68 |
69 | describe '#can_nest?' do
70 | before(:each) do
71 | @language = double(RBeautify::Language)
72 | end
73 |
74 | it {
75 | expect(RBeautify::BlockMatcher.new(@language, :foo, /foo/, /bar/)).to be_can_nest(nil)
76 | }
77 |
78 | it {
79 | expect(RBeautify::BlockMatcher.new(@language, :foo, /foo/, /bar/)).to be_can_nest(double('block_start', :parse_content? => true))
80 | }
81 |
82 | it {
83 | expect(RBeautify::BlockMatcher.new(@language, :foo, /foo/, /bar/)).to_not be_can_nest(double('block_start', :parse_content? => false))
84 | }
85 |
86 | it {
87 | expect(RBeautify::BlockMatcher.new(@language, :foo, /foo/, /bar/, :nest_except => [:bar])).to be_can_nest(nil)
88 | }
89 |
90 | it {
91 | expect(RBeautify::BlockMatcher.new(@language, :foo, /foo/, /bar/, :nest_except => [:foo])).to be_can_nest(double('block_start', :name => :bar, :parse_content? => true))
92 | }
93 |
94 | it {
95 | expect(RBeautify::BlockMatcher.new(@language, :foo, /foo/, /bar/, :nest_except => [:foo])).to_not be_can_nest(double('block_start', :name => :bar, :parse_content? => false))
96 | }
97 |
98 | it {
99 | expect(RBeautify::BlockMatcher.new(@language, :foo, /foo/, /bar/, :nest_except => [:bar])).to_not be_can_nest(double('block_start', :name => :bar, :parse_content? => true))
100 | }
101 | end
102 |
103 | end
104 |
--------------------------------------------------------------------------------
/spec/rbeautify/block_start_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative './../spec_helper.rb'
2 |
3 | describe RBeautify::BlockStart do
4 | before(:each) do
5 | @ruby = RBeautify::Language.language(:ruby)
6 | end
7 |
8 | describe 'class' do
9 | describe '#first_common_ancestor' do
10 | before(:each) do
11 | @grand_parent = RBeautify::BlockStart.new(@ruby.matcher(:standard), nil, 0, 0, 'class', ' Foo')
12 | @parent = RBeautify::BlockStart.new(@ruby.matcher(:standard), @grand_parent, 0, 0, 'def', ' foo')
13 | @first = RBeautify::BlockStart.new(@ruby.matcher(:standard), @parent, 0, 0, 'def', ' foo')
14 | @second = RBeautify::BlockStart.new(@ruby.matcher(:standard), @parent, 0, 0, 'def', ' bar')
15 | @unrelated = RBeautify::BlockStart.new(@ruby.matcher(:standard), nil, 0, 0, 'class', ' Bar')
16 | end
17 |
18 | it { expect(RBeautify::BlockStart.first_common_ancestor(@grand_parent, nil)).to eq(nil) }
19 | it { expect(RBeautify::BlockStart.first_common_ancestor(@grand_parent, @unrelated)).to be_nil }
20 | it { expect(RBeautify::BlockStart.first_common_ancestor(@grand_parent, @parent)).to eq(@grand_parent ) }
21 | it { expect(RBeautify::BlockStart.first_common_ancestor(@grand_parent, @first)).to eq(@grand_parent ) }
22 | it { expect(RBeautify::BlockStart.first_common_ancestor(@first, @second)).to eq(@parent) }
23 | end
24 | end
25 |
26 | describe '#strict_ancestor_of?' do
27 | before(:each) do
28 | @block = RBeautify::BlockStart.new(@ruby.matcher(:standard), nil, 0, 'def', ' foo', 0)
29 | end
30 |
31 | it { expect(@block).to_not be_strict_ancestor_of(nil) }
32 | it { expect(@block).to_not be_strict_ancestor_of(@block) }
33 | it { expect(@block).to_not be_strict_ancestor_of(RBeautify::BlockStart.new(@ruby.matcher(:if), nil, 0, 0, 'if', ' foo') ) }
34 | it { expect(@block).to be_strict_ancestor_of(RBeautify::BlockStart.new(@ruby.matcher(:if), @block, 0, 0, 'if', ' foo') ) }
35 | end
36 |
37 | describe '#total_indent_size' do
38 | before(:each) do
39 | @block = RBeautify::BlockStart.new(@ruby.matcher(:standard), nil, 0, 'def', ' foo', 0)
40 | end
41 |
42 | it {
43 | expect(RBeautify::BlockStart.new(@ruby.matcher(:standard), nil, 0, 0, 'def', ' foo').total_indent_size).to eq(2)
44 | }
45 |
46 | it 'should sum with parents total indent size' do
47 | parent = double('parent_start_block', :total_indent_size => 4)
48 | expect(RBeautify::BlockStart.new(@ruby.matcher(:standard), parent, 0, 0, 'def', ' foo').total_indent_size).to eq(6)
49 | end
50 | end
51 |
52 | end
53 |
--------------------------------------------------------------------------------
/spec/rbeautify/config/ruby_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative './../../spec_helper.rb'
2 |
3 | run_fixtures_for_language(:ruby)
4 |
5 | describe 'Ruby' do
6 |
7 | describe 'matchers' do
8 | before(:each) do
9 | @ruby = RBeautify::Language.language(:ruby)
10 | end
11 |
12 | describe 'standard' do
13 | before(:each) do
14 | @matcher = @ruby.matcher(:standard)
15 | @current_block = RBeautify::BlockStart.new(@matcher, nil, 0, 0, '', '')
16 | end
17 |
18 | it { expect(@matcher.parse_block_start('class Foo; end', nil, 0, 0)).to be_block_start_like(:standard, 0, 'class', ' Foo; end') }
19 | it { expect(@matcher.parse_block_start('module Foo', nil, 0, 0)).to be_block_start_like(:standard, 0, 'module', ' Foo') }
20 | it { expect(@matcher.parse_block_start('def foo()', nil, 0, 0)).to be_block_start_like(:standard, 0, 'def', ' foo()') }
21 | it { expect(@matcher.parse_block_start('end Foo', nil, 0, 0)).to be_nil }
22 |
23 | it { expect(@current_block.parse_block_end('end', 0)).to be_block_end_like(@current_block, 0, 'end', '') }
24 | it { expect(@current_block.parse_block_end(';end', 0)).to be_block_end_like(@current_block, 0, ';end', '') }
25 | it { expect(@current_block.parse_block_end('; end', 0)).to be_block_end_like(@current_block, 1, ' end', '') }
26 | it { expect(@current_block.parse_block_end('rescue', 0)).to be_block_end_like(@current_block, 0, 'rescue', '') }
27 | it { expect(@current_block.parse_block_end('ensure', 0)).to be_block_end_like(@current_block, 0, 'ensure', '') }
28 | it { expect(@current_block.parse_block_end('}', 0)).to be_nil }
29 | end
30 |
31 | describe 'if' do
32 | before(:each) do
33 | @matcher = @ruby.matcher(:if)
34 | @current_block = RBeautify::BlockStart.new(@matcher, nil, 0, 0, 'if', ' foo')
35 | end
36 |
37 | it { expect(@matcher).to be_end_can_also_be_start}
38 |
39 | it { expect(@matcher.parse_block_start('if foo', nil, 0, 0)).to be_block_start_like(:if, 0, 'if', ' foo') }
40 | it { expect(@matcher.parse_block_start('then foo = bar', nil, 0, 0)).to be_block_start_like(:if, 0, 'then', ' foo = bar') }
41 | it { expect(@matcher.parse_block_start('if_foo', nil, 0, 0)).to be_nil }
42 | it { expect(@matcher.parse_block_start('if: foo', nil, 0, 0)).to be_nil }
43 |
44 | it { expect(@current_block.parse_block_end('end', 0)).to be_block_end_like(@current_block, 0, 'end', '') }
45 | it { expect(@current_block.parse_block_end('then', 0)).to be_block_end_like(@current_block, 0, 'then', '') }
46 | it { expect(@current_block.parse_block_end('else', 0)).to be_block_end_like(@current_block, 0, 'else', '') }
47 | it { expect(@current_block.parse_block_end('a = 3', 0)).to be_nil }
48 | end
49 |
50 | describe 'curly_bracket' do
51 | before(:each) do
52 | @matcher = @ruby.matcher(:curly_bracket)
53 | @current_block = RBeautify::BlockStart.new(@matcher, nil, 0, 0, '{', '')
54 | end
55 |
56 | it { expect(@matcher.parse_block_start('{', nil, 0, 0)).to be_block_start_like(:curly_bracket, 0, '{', '') }
57 | it { expect(@matcher.parse_block_start('end', nil, 0, 0)).to be_nil }
58 |
59 | it { expect(@current_block.parse_block_end('}', 0)).to be_block_end_like(@current_block, 0, '}', '') }
60 | it { expect(@current_block.parse_block_end('end', 0)).to be_nil }
61 |
62 | end
63 |
64 | describe 'double_quote' do
65 | before(:each) do
66 | @matcher = @ruby.matcher(:double_quote)
67 | @current_block = RBeautify::BlockStart.new(@matcher, nil, 0, 0, '"', 'foo"')
68 | end
69 |
70 | it { expect(@matcher.parse_block_start('a = "foo"', nil, 0, 0)).to be_block_start_like(:double_quote, 4, '"', 'foo"') }
71 | it { expect(@matcher.parse_block_start('a = 2', nil, 0, 0)).to be_nil }
72 |
73 | it { expect(@current_block.parse_block_end(' bar"', 0)).to be_block_end_like(@current_block, 4, '"', '') }
74 | it { expect(@current_block.parse_block_end(' " + bar + "', 0)).to be_block_end_like(@current_block, 1, '"', ' + bar + "') }
75 | it { expect(@current_block.parse_block_end('\\\\"', 0)).to be_block_end_like(@current_block, 2, '"', '') }
76 | it { expect(@current_block.parse_block_end('\\" more string"', 0)).to be_block_end_like(@current_block, 14, '"', '') }
77 | it { expect(@current_block.parse_block_end('a = 2', 0)).to be_nil }
78 | it { expect(@current_block.parse_block_end('\\"', 0)).to be_nil }
79 | it { expect(@current_block.parse_block_end('\\\\\\"', 0)).to be_nil }
80 | end
81 |
82 | describe 'single_quote' do
83 | before(:each) do
84 | @matcher = @ruby.matcher(:single_quote)
85 | @current_block = RBeautify::BlockStart.new(@matcher, nil, 0, 0, "'", "foo'")
86 | end
87 |
88 | it { expect(@matcher).not_to be_end_can_also_be_start}
89 | it { expect(@matcher.parse_block_start("describe '#foo?' do", nil, 0, 0)).to be_block_start_like(:single_quote, 9, "'", "#foo?' do") }
90 | it { expect(@matcher.parse_block_start('a = 2', nil, 0, 0)).to be_nil }
91 |
92 | it { expect(@current_block.parse_block_end("#foo?' do", 9)).to be_block_end_like(@current_block, 14, "'", ' do')}
93 | it { expect(@current_block.parse_block_end('a = 2', 0)).to be_nil }
94 | end
95 |
96 | describe 'continuing_line' do
97 | before(:each) do
98 | @matcher = @ruby.matcher(:continuing_line)
99 | @current_block = RBeautify::BlockStart.new(@matcher, nil, 0, 8, ',', '')
100 | end
101 |
102 | it { expect(@matcher.parse_block_start('foo :bar,', nil, 0, 0)).to be_block_start_like(:continuing_line, 8, ',', '') }
103 | it { expect(@matcher.parse_block_start('a = 3 &&', nil, 0, 0)).to be_block_start_like(:continuing_line, 6, '&&', '') }
104 | it { expect(@matcher.parse_block_start('a = 3 ||', nil, 0, 0)).to be_block_start_like(:continuing_line, 6, '||', '') }
105 | it { expect(@matcher.parse_block_start('a = 3 +', nil, 0, 0)).to be_block_start_like(:continuing_line, 6, '+', '') }
106 | it { expect(@matcher.parse_block_start('a = 3 -', nil, 0, 0)).to be_block_start_like(:continuing_line, 6, '-', '') }
107 | it { expect(@matcher.parse_block_start("a \\", nil, 0, 0)).to be_block_start_like(:continuing_line, 2, '\\', '') }
108 | it { expect(@matcher.parse_block_start('a ?', nil, 0, 0)).to be_block_start_like(:continuing_line, 1, ' ?', '') }
109 | it { expect(@matcher.parse_block_start('a ? # some comment', nil, 0, 0)).to be_block_start_like(:continuing_line, 1, ' ? # some comment', '') }
110 | it { expect(@matcher.parse_block_start('a = 3', nil, 0, 0)).to be_nil }
111 | it { expect(@matcher.parse_block_start('a = foo.bar?', nil, 0, 0)).to be_nil }
112 | it { expect(@matcher.parse_block_start('# just a comment', nil, 0, 0)).to be_nil }
113 |
114 | it { expect(@current_block.parse_block_end('a = 3', 0)).to be_block_end_like(@current_block, 0, '', 'a = 3') }
115 | it { expect(@current_block.parse_block_end('foo :bar,', 0)).to be_nil }
116 | it { expect(@current_block.parse_block_end('a = 3 &&', 0)).to be_nil }
117 | it { expect(@current_block.parse_block_end('a = 3 +', 0)).to be_nil }
118 | it { expect(@current_block.parse_block_end("a \\", 0)).to be_nil }
119 | it { expect(@current_block.parse_block_end('# just a comment', 0)).to be_nil }
120 | it { expect(@current_block.parse_block_end('#', 0)).to be_nil }
121 | end
122 |
123 | describe 'round_bracket' do
124 | before(:each) do
125 | @matcher = @ruby.matcher(:round_bracket)
126 | @current_block = RBeautify::BlockStart.new(@matcher, nil, 0, 0, '(', '')
127 | end
128 |
129 | it { expect(@matcher.parse_block_start('a = (foo,', nil, 0, 0)).to be_block_start_like(:round_bracket, 4, '(', 'foo,') }
130 | it { expect(@matcher.parse_block_start('anything else', nil, 0, 0)).to be_nil }
131 |
132 | it { expect(@current_block.parse_block_end('foo)', 0)).to be_block_end_like(@current_block, 3, ')', '') }
133 | it { expect(@current_block.parse_block_end('a = 3', 0)).to be_nil }
134 | end
135 |
136 | describe 'comment' do
137 | before(:each) do
138 | @matcher = @ruby.matcher(:comment)
139 | @current_block = RBeautify::BlockStart.new(@matcher, nil, 0, 8, '#', ' comment')
140 | end
141 |
142 | it { expect(@matcher.parse_block_start('anything # comment', nil, 0, 0)).to be_block_start_like(:comment, 8, ' #', ' comment') }
143 | it { expect(@matcher.parse_block_start('#', nil, 0, 0)).to be_block_start_like(:comment, 0, '#', '') }
144 | it { expect(@matcher.parse_block_start('anything else', nil, 0, 0)).to be_nil }
145 |
146 | it { expect(@current_block.parse_block_end('anything', 0)).to be_block_end_like(@current_block, 8, '', '') }
147 | it { expect(@current_block.parse_block_end('', 0)).to be_block_end_like(@current_block, 0, '', '') }
148 | end
149 |
150 | describe 'begin' do
151 | before(:each) do
152 | @matcher = @ruby.matcher(:begin)
153 | @current_block = RBeautify::BlockStart.new(@matcher, nil, 0, 0, 'begin', '')
154 | end
155 |
156 | it { expect(@matcher.parse_block_start('begin', nil, 0, 0)).to be_block_start_like(:begin, 0, 'begin', '') }
157 | it { expect(@matcher.parse_block_start('beginning', nil, 0, 0)).to be_nil }
158 |
159 | it { expect(@current_block.parse_block_end('rescue', 0)).to be_block_end_like(@current_block, 0, 'rescue', '') }
160 | it { expect(@current_block.parse_block_end('ensure', 0)).to be_block_end_like(@current_block, 0, 'ensure', '') }
161 | it { expect(@current_block.parse_block_end('else', 0)).to be_block_end_like(@current_block, 0, 'else', '') }
162 | it { expect(@current_block.parse_block_end('end', 0)).to be_block_end_like(@current_block, 0, 'end', '') }
163 | end
164 |
165 | describe 'regex' do
166 | before(:each) do
167 | @matcher = @ruby.matcher(:regex)
168 | @current_block = RBeautify::BlockStart.new(@matcher, nil, 0, 0, '/', 'foo/')
169 | end
170 |
171 | it { expect(@matcher.parse_block_start('/foo/', nil, 0, 0)).to be_block_start_like(:regex, 0, '/', 'foo/') }
172 | it { expect(@matcher.parse_block_start(', /foo/', nil, 0, 0)).to be_block_start_like(:regex, 0, ', /', 'foo/') }
173 | it { expect(@matcher.parse_block_start('foo = /bar/', nil, 0, 0)).to be_block_start_like(:regex, 4, '= /', 'bar/') }
174 | it { expect(@matcher.parse_block_start('foo =~ /bar/', nil, 0, 0)).to be_block_start_like(:regex, 5, '~ /', 'bar/') }
175 | it { expect(@matcher.parse_block_start('1/2', nil, 0, 0)).to be_nil }
176 | it { expect(@matcher.parse_block_start('anything else', nil, 0, 0)).to be_nil }
177 |
178 | it { expect(@current_block.parse_block_end('foo/', 0)).to be_block_end_like(@current_block, 3, '/', '') }
179 | it { expect(@current_block.parse_block_end('foo/', 0)).to be_block_end_like(@current_block, 3, '/', '') }
180 | it { expect(@current_block.parse_block_end('', 0)).to be_nil }
181 |
182 | end
183 | end
184 | end
185 |
--------------------------------------------------------------------------------
/spec/rbeautify/line_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative './../spec_helper.rb'
2 |
3 | describe RBeautify::Line do
4 |
5 | describe '#format' do
6 | let(:config) do
7 | {
8 | 'tab_size' => 2,
9 | 'translate_tabs_to_spaces' => 'true'
10 | }
11 | end
12 |
13 | before(:each) do
14 | @language = double(RBeautify::Language)
15 | end
16 |
17 | it 'should just strip with empty stack' do
18 | expect(RBeautify::BlockMatcher).to receive(:parse).and_return(nil)
19 | expect(RBeautify::Line.new(@language, ' a = 3 ', 0, config).format).to eq("a = 3")
20 | end
21 |
22 | it 'should indent with existing indent' do
23 | current_block = double('block_start',
24 | :total_indent_size => 2,
25 | :format_content? => true,
26 | :strict_ancestor_of? => false
27 | )
28 | expect(RBeautify::BlockStart).to receive(:first_common_ancestor).at_least(1).and_return(current_block)
29 | expect(RBeautify::BlockMatcher).to receive(:parse).and_return(current_block)
30 | expect(RBeautify::Line.new(@language, ' a = 3 ', 0, current_block, config).format).to eq(' a = 3')
31 | end
32 |
33 | it 'leave empty lines blank' do
34 | current_block = double('block_start', :format_content? => true)
35 | expect(RBeautify::BlockMatcher).to receive(:parse).and_return(current_block)
36 | expect(RBeautify::Line.new(@language, ' ', 0, current_block, config).format).to eq('')
37 | end
38 |
39 | it 'should remove indent with match to end of block' do
40 | current_block = double('block_start',
41 | :format_content? => true,
42 | :indent_end_line? => false
43 | )
44 | expect(RBeautify::BlockStart).to receive(:first_common_ancestor).at_least(1).and_return(nil)
45 | expect(RBeautify::BlockMatcher).to receive(:parse).and_return(nil)
46 | expect(RBeautify::Line.new(@language, ' end ', 0, current_block, config).format).to eq('end')
47 | end
48 |
49 | it 'should not remove indent with match to end of block if indent_end_line? is true' do
50 | current_block = double('block_start',
51 | :total_indent_size => 2,
52 | :format_content? => true,
53 | :indent_end_line? => true
54 | )
55 | expect(RBeautify::BlockMatcher).to receive(:parse).and_return(nil)
56 | expect(RBeautify::Line.new(@language, ' end ', 0, current_block, config).format).to eq(' end')
57 | end
58 |
59 | it 'should leave indent at old stack level with match of new block' do
60 | current_block = double('current_block_start',
61 | :total_indent_size => 2,
62 | :format_content? => true
63 | )
64 | new_block = double('new_block_start',
65 | :format_content? => true,
66 | :strict_ancestor_of? => false
67 | )
68 | expect(RBeautify::BlockStart).to receive(:first_common_ancestor).at_least(1).and_return(current_block)
69 | expect(RBeautify::BlockMatcher).to receive(:parse).and_return(new_block)
70 | expect(RBeautify::Line.new(@language, 'class Foo', 0, current_block, config).format).to eq(' class Foo')
71 | end
72 |
73 | it 'should remove indent if a block ends and starts' do
74 | current_block = double('current_block_start',
75 | :total_indent_size => 0,
76 | :format_content? => true
77 | )
78 | new_block = double('new_block_start',
79 | :format_content? => true,
80 | :strict_ancestor_of? => false
81 | )
82 | expect(RBeautify::BlockStart).to receive(:first_common_ancestor).at_least(1).and_return(current_block)
83 | expect(RBeautify::BlockMatcher).to receive(:parse).and_return(new_block)
84 | expect(RBeautify::Line.new(@language, ' else ', 0, current_block, config).format).to eq('else')
85 | end
86 |
87 | it 'should not change when format is false' do
88 | current_block = double('block_start', :format_content? => false)
89 | expect(RBeautify::BlockMatcher).to receive(:parse).and_return(current_block)
90 | expect(
91 | RBeautify::Line.new(
92 | @language,
93 | ' some content after program has finished. ', 0,
94 | current_block,
95 | config
96 | ).format
97 | ).to eq(" some content after program has finished. ")
98 | end
99 |
100 | it 'should leave indent with match to end of block (but no format)' do
101 | current_block = double('block_start',
102 | :format_content? => false
103 | )
104 | expect(RBeautify::BlockMatcher).to receive(:parse).and_return(nil)
105 | expect(RBeautify::Line.new(@language, ' "', 0, current_block, config).format).to eq(' "')
106 | end
107 |
108 | end
109 |
110 | end
111 |
--------------------------------------------------------------------------------
/spec/rbeautify_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative './spec_helper.rb'
2 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | unless ENV['CI'] == true
2 | require 'pry'
3 | end
4 |
5 | require 'yaml'
6 | require_relative './../lib/rbeautify.rb'
7 |
8 | module RBeautifyMatchers
9 | # Adds more descriptive failure messages to the dynamic be_valid matcher
10 | class BeBlockStartLike #:nodoc:
11 | def initialize(block_matcher_name, offset, match, after_match)
12 | # triggers the standard Spec::Matchers::Be magic, as if the original Spec::Matchers#method_missing had fired
13 | @block_matcher_name = block_matcher_name
14 | @offset = offset
15 | @match = match
16 | @after_match = after_match
17 | end
18 |
19 | def matches?(target_block)
20 | @target_block = target_block
21 | return !target_block.nil? &&
22 | (expected_string == got_string)
23 | end
24 |
25 | def failure_message
26 | "expected\n#{expected_string} but got\n#{got_string}"
27 | end
28 |
29 | def failure_message_when_negated
30 | "expected to be different from #{expected_string}"
31 | end
32 |
33 | def expected_string
34 | "name: #{@block_matcher_name}, offset: #{@offset}, match: '#{@match}', after_match: '#{@after_match}'"
35 | end
36 |
37 | def got_string
38 | "name: #{@target_block.block_matcher.name}, offset: #{@target_block.offset}, match: '#{@target_block.match}', after_match: '#{@target_block.after_match}'"
39 | end
40 |
41 | def description
42 | "block start with"
43 | end
44 | end
45 |
46 | class BeBlockEndLike #:nodoc:
47 | def initialize(block_start, offset, match, after_match)
48 | # triggers the standard Spec::Matchers::Be magic, as if the original Spec::Matchers#method_missing had fired
49 | @block_start = block_start
50 | @offset = offset
51 | @match = match
52 | @after_match = after_match
53 | end
54 |
55 | def matches?(target_block)
56 | @target_block = target_block
57 | expected_string == got_string
58 | end
59 |
60 | def failure_message
61 | "expected\n#{expected_string} but got\n#{got_string}"
62 | end
63 |
64 | def failure_message_when_negated
65 | "expected to be different from #{expected_string}"
66 | end
67 |
68 | def expected_string
69 | "block_end: #{@block_start.name}, offset: #{@offset}, match: '#{@match}', after_match: '#{@after_match}'"
70 | end
71 |
72 | def got_string
73 | if @target_block.nil?
74 | 'nil'
75 | else
76 | "block_end: #{@target_block.block_start.name}, offset: #{@target_block.offset}, match: '#{@target_block.match}', after_match: '#{@target_block.after_match}'"
77 | end
78 | end
79 |
80 | def description
81 | "block end with"
82 | end
83 |
84 | end
85 |
86 | def be_block_start_like(block_matcher, offset, match, after_match)
87 | BeBlockStartLike.new(block_matcher, offset, match, after_match)
88 | end
89 |
90 | def be_block_end_like(block_start, offset, match, after_match)
91 | BeBlockEndLike.new(block_start, offset, match, after_match)
92 | end
93 | end
94 |
95 | RSpec.configure do |config|
96 | config.include(RBeautifyMatchers)
97 | end
98 |
99 | def run_fixtures_for_language(language)
100 | fixtures = YAML.load_file(File.dirname(__FILE__) + "/fixtures/#{language}.yml")
101 |
102 | describe language do
103 | let(:config) do
104 | {
105 | 'tab_size' => 2,
106 | 'translate_tabs_to_spaces' => 'true'
107 | }
108 | end
109 |
110 | fixtures.each do |fixture|
111 | it "should #{fixture['name']}" do
112 | input = fixture['input']
113 | output = fixture['output'] || input
114 | debug = fixture['debug'] || false
115 |
116 | config['tab_size'] = fixture.fetch('spaces', 2)
117 |
118 | if fixture['pending']
119 | next
120 | pending fixture['pending'] do
121 | expect(RBeautify.beautify_string(language, input, config)).to eq(output)
122 | end
123 | else
124 | RBeautify::BlockMatcher.debug = debug
125 | expect(RBeautify.beautify_string(language, input, config)).to eq(output)
126 | end
127 | end
128 | end
129 | end
130 |
131 | end
132 |
--------------------------------------------------------------------------------