chutney 3.7.0 → 3.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +1 -2
- data/.rubocop.yml +4 -3
- data/Gemfile +9 -0
- data/README.md +49 -3
- data/chutney.gemspec +8 -17
- data/examples/emoji.feature +1 -2
- data/exe/chutney +2 -2
- data/exe/chutney-lsp +23 -0
- data/img/happy_chutney.png +0 -0
- data/lib/chutney/configuration.rb +4 -2
- data/lib/chutney/formatter/pie_formatter.rb +2 -2
- data/lib/chutney/linter/inconsistent_quoting.rb +1 -1
- data/lib/chutney/linter/invalid_file_name.rb +1 -1
- data/lib/chutney/linter/same_tag_different_case.rb +1 -1
- data/lib/chutney/linter/same_tag_for_all_scenarios.rb +2 -2
- data/lib/chutney/linter/unique_scenario_names.rb +1 -1
- data/lib/chutney/linter/unused_variable.rb +1 -1
- data/lib/chutney/linter/use_outline.rb +1 -1
- data/lib/chutney/linter.rb +1 -1
- data/lib/chutney/lsp/result.rb +16 -0
- data/lib/chutney/lsp/server.rb +177 -0
- data/lib/chutney/lsp.rb +7 -0
- data/lib/chutney/version.rb +1 -1
- data/lib/chutney.rb +0 -1
- data/usechutney.com/.gitignore +5 -0
- data/usechutney.com/Gemfile +37 -0
- data/usechutney.com/_config.yml +298 -0
- data/usechutney.com/_data/navigation.yml +112 -0
- data/usechutney.com/_data/ui-text.yml +2132 -0
- data/usechutney.com/_posts/2024-09-23-welcome-to-jekyll.markdown +28 -0
- data/usechutney.com/assets/images/mr_pickle.png +0 -0
- data/usechutney.com/assets/images/pug.png +0 -0
- data/usechutney.com/docs/configuration/index.md +104 -0
- data/usechutney.com/docs/disabling-rules/index.md +11 -0
- data/usechutney.com/docs/installing/index.md +30 -0
- data/usechutney.com/docs/language-server/index.md +24 -0
- data/usechutney.com/docs/rules/avoid-full-stops/index.md +23 -0
- data/usechutney.com/docs/rules/avoid-outline-for-single-example/index.md +28 -0
- data/usechutney.com/docs/rules/avoid-scripting/index.md +33 -0
- data/usechutney.com/docs/rules/avoid-splat-steps-in-background/index.md +25 -0
- data/usechutney.com/docs/rules/avoid-splat-steps-in-scenarios/index.md +29 -0
- data/usechutney.com/docs/rules/avoid-typographers-quotes/index.md +24 -0
- data/usechutney.com/docs/rules/background-does-more-than-setup/index.md +28 -0
- data/usechutney.com/docs/rules/background-requires-multiple-scenarios/index.md +26 -0
- data/usechutney.com/docs/rules/bad-scenario-name/index.md +28 -0
- data/usechutney.com/docs/rules/empty-feature-file/index.md +7 -0
- data/usechutney.com/docs/rules/file-name-differs-feature-name/index.md +37 -0
- data/usechutney.com/docs/rules/givens-after-background/index.md +36 -0
- data/usechutney.com/docs/rules/inconsistent-quoting/index.md +29 -0
- data/usechutney.com/docs/rules/invalid-step-flow/index.md +31 -0
- data/usechutney.com/docs/rules/invalid_file_name/index.md +19 -0
- data/usechutney.com/docs/rules/missing-example-name/index.md +49 -0
- data/usechutney.com/docs/rules/missing-example-table/index.md +34 -0
- data/usechutney.com/docs/rules/missing-feature-description/index.md +25 -0
- data/usechutney.com/docs/rules/missing-feature-name/index.md +19 -0
- data/usechutney.com/docs/rules/missing-scenario-name/index.md +19 -0
- data/usechutney.com/docs/rules/missing-scenario-outline/index.md +39 -0
- data/usechutney.com/docs/rules/missing-test-action/index.md +28 -0
- data/usechutney.com/docs/rules/missing-test-verification/index.md +28 -0
- data/usechutney.com/docs/rules/required-tag-starts-with/index.md +31 -0
- data/usechutney.com/docs/rules/same-tag-different-case/index.md +42 -0
- data/usechutney.com/docs/rules/same-tag-for-all-scenarios/index.md +42 -0
- data/usechutney.com/docs/rules/scenario-names-match/index.md +30 -0
- data/usechutney.com/docs/rules/tag-used-multiple-times/index.md +32 -0
- data/usechutney.com/docs/rules/too-clumsy/index.md +78 -0
- data/usechutney.com/docs/rules/too-long-step/index.md +30 -0
- data/usechutney.com/docs/rules/too-many-different-tags/index.md +32 -0
- data/usechutney.com/docs/rules/too-many-steps/index.md +79 -0
- data/usechutney.com/docs/rules/too-many-tags/index.md +32 -0
- data/usechutney.com/docs/rules/unique-scenario-names/index.md +39 -0
- data/usechutney.com/docs/rules/unknown-variable/index.md +38 -0
- data/usechutney.com/docs/rules/unused-variable/index.md +37 -0
- data/usechutney.com/docs/rules/use-background/index.md +40 -0
- data/usechutney.com/docs/rules/use-outline/index.md +39 -0
- data/usechutney.com/docs/running/index.md +33 -0
- data/usechutney.com/pages/404.html +25 -0
- data/usechutney.com/pages/about/index.md +22 -0
- data/usechutney.com/pages/index.markdown +40 -0
- metadata +76 -114
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06e7bafb2c4fb8f14e168e16a2f83a5aaf958f6a4d3430678f9f0c9da954835e
|
4
|
+
data.tar.gz: 07c8004a2cbd18de1ed90ab634df369c9548b1b7e952f8d111890924ccb77b67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1904c405fb2f33997023ee7628ad5371c9f5b621850c193dd09c7d057b5d5e9f7a8dabdad98ef3ee24b40cb71348d5fa8f51ee2e7ef8154e069319237f2bd430
|
7
|
+
data.tar.gz: 9add005095ab65e1ec8d858379ac231d8457b0ff99f7b55f5518772e8255dccfa0145d02325a9f3b52e5641caf7052cf48c722b591dc14798cba1eed44231ba2
|
data/.circleci/config.yml
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
# Use the latest 2.1 version of CircleCI pipeline processing engine, see https://circleci.com/docs/2.0/configuration-reference/
|
2
2
|
version: 2.1
|
3
3
|
|
4
|
-
|
5
4
|
jobs:
|
6
5
|
build:
|
7
6
|
docker:
|
8
|
-
- image:
|
7
|
+
- image: cimg/ruby:3.2
|
9
8
|
steps:
|
10
9
|
- checkout
|
11
10
|
- run: bundle install
|
data/.rubocop.yml
CHANGED
@@ -27,14 +27,13 @@ Layout/LineLength:
|
|
27
27
|
Metrics/MethodLength:
|
28
28
|
Max: 20
|
29
29
|
|
30
|
-
|
31
30
|
# Offense count: 1
|
32
31
|
# Cop supports --auto-correct.
|
33
32
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
34
33
|
# SupportedStyles: predicate, comparison
|
35
34
|
Style/NumericPredicate:
|
36
35
|
Exclude:
|
37
|
-
-
|
36
|
+
- "lib/chutney/linter/file_name_differs_feature_name.rb"
|
38
37
|
|
39
38
|
Metrics/ModuleLength:
|
40
39
|
Exclude:
|
@@ -63,4 +62,6 @@ Style/StringConcatenation:
|
|
63
62
|
|
64
63
|
AllCops:
|
65
64
|
NewCops: enable
|
66
|
-
SuggestExtensions: false
|
65
|
+
SuggestExtensions: false
|
66
|
+
Exclude:
|
67
|
+
- "usechutney.com/**"
|
data/Gemfile
CHANGED
@@ -3,3 +3,12 @@
|
|
3
3
|
source 'https://rubygems.org'
|
4
4
|
|
5
5
|
gemspec
|
6
|
+
|
7
|
+
gem 'coveralls', '~> 0.8'
|
8
|
+
gem 'cucumber', '>= 9.0'
|
9
|
+
gem 'pry-byebug', '~> 3.0'
|
10
|
+
gem 'rake', '~> 13.0'
|
11
|
+
gem 'rerun', '~> 0.13'
|
12
|
+
gem 'rspec', '~> 3.13'
|
13
|
+
gem 'rspec-expectations', '~> 3.0'
|
14
|
+
gem 'rubocop', '~> 1.66'
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
<h1 align="center">
|
2
|
-
<img src="https://raw.githubusercontent.com/BillyRuffian/chutney/master/img/
|
2
|
+
<img src="https://raw.githubusercontent.com/BillyRuffian/chutney/master/img/happy_chutney.png?sanitize=true" alt="Chutney" height="200">
|
3
3
|
<br>
|
4
4
|
Chutney
|
5
5
|
<br>
|
@@ -19,10 +19,56 @@
|
|
19
19
|
|
20
20
|
</div>
|
21
21
|
|
22
|
-
Read the documentation [here](https://
|
22
|
+
Read the documentation [here](https://www.usechutney.com/).
|
23
|
+
|
24
|
+
Your documentation is precious and should be treated as such. Chutney is a tool to help you keep your gherkin files in good shape. It will help you to write better gherkin and keep your feature files consistent through an opinionated, but optional, set of rules.
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
### Ruby
|
29
|
+
|
30
|
+
Chutney is a ruby gem, so relies on you having ruby installed. It requires ruby 3.2 or later.
|
31
|
+
|
32
|
+
For macOS, Linux or other Unix-like systems, I recommend using a version manager like [rvm](https://rvm.io), [asdf](https://asdf-vm.com) or [rbenv](https://github.com/rbenv/rbenv).
|
33
|
+
|
34
|
+
For Windows, you can use [RubyInstaller](https://rubyinstaller.org/).
|
35
|
+
|
36
|
+
### Chutney
|
37
|
+
|
38
|
+
To install chutney system-wide, run:
|
39
|
+
|
40
|
+
```bash
|
41
|
+
gem install chutney
|
42
|
+
```
|
43
|
+
|
44
|
+
To install chutney for a specific project, add it to your Gemfile:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
gem 'chutney'
|
48
|
+
```
|
49
|
+
|
50
|
+
## Usage
|
51
|
+
|
52
|
+
To run chutney, simply run:
|
53
|
+
|
54
|
+
```bash
|
55
|
+
chutney
|
56
|
+
```
|
57
|
+
|
58
|
+
It will search for any `.feature` files beneath the current directory and give you an opinion. It comes with a default set of rules and will give you a little nudge if you haven't got your own chutney configuration file.
|
59
|
+
|
60
|
+
To create a configuration file, run:
|
61
|
+
|
62
|
+
```bash
|
63
|
+
chutney --init
|
64
|
+
```
|
65
|
+
|
66
|
+
(Configuration files can in either `.chutney.yml` or `.chutney.yml` and reside in the top-level of the project or in a `/config` directory.)
|
67
|
+
|
23
68
|
|
24
69
|
See [this page](https://billyruffian.github.io/chutney/usage/rules.html) for a full list of the rules chutney encourages.
|
25
70
|
|
71
|
+
|
26
72
|
## Notes
|
27
73
|
|
28
|
-
Chutney 3+ has replaced its direct dependency on Cucumber and instead uses the excellent [cuke_modeller](https://github.com/enkessler/cuke_modeler) to parse your feature files.
|
74
|
+
Chutney 3+ has replaced its direct dependency on Cucumber and instead uses the excellent [cuke_modeller](https://github.com/enkessler/cuke_modeler) to parse your feature files.
|
data/chutney.gemspec
CHANGED
@@ -43,26 +43,17 @@ Gem::Specification.new do |spec|
|
|
43
43
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
44
44
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|s|features)/}) }
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
spec.bindir = 'exe'
|
48
48
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
49
49
|
spec.require_paths = ['lib']
|
50
|
-
|
51
|
-
spec.add_runtime_dependency 'amatch', '~> 0.4.0'
|
52
|
-
spec.add_runtime_dependency 'cuke_modeler', '~> 3.3'
|
53
|
-
spec.add_runtime_dependency 'i18n', '>= 1.8.2', '< 1.15.0'
|
54
|
-
spec.add_runtime_dependency 'pastel', '~> 0.7'
|
55
|
-
spec.add_runtime_dependency 'tty-pie', '~> 0.3'
|
56
50
|
|
51
|
+
spec.add_dependency 'amatch', '~> 0.4.0'
|
52
|
+
spec.add_dependency 'cuke_modeler', '~> 3.21'
|
53
|
+
spec.add_dependency 'i18n', '>= 1.8.2', '< 1.15.0'
|
54
|
+
spec.add_dependency 'language_server-protocol', '~> 3.17'
|
55
|
+
spec.add_dependency 'pastel', '~> 0.7'
|
56
|
+
spec.add_dependency 'tty-pie', '~> 0.3'
|
57
57
|
|
58
|
-
spec.
|
59
|
-
spec.add_development_dependency 'cucumber', '>= 7.0'
|
60
|
-
spec.add_development_dependency 'pry-byebug', '~> 3.0'
|
61
|
-
spec.add_development_dependency 'rake', '~> 13.0'
|
62
|
-
spec.add_development_dependency 'rerun', '~> 0.13'
|
63
|
-
spec.add_development_dependency 'rspec-expectations', '~> 3.0'
|
64
|
-
spec.add_development_dependency 'rubocop', '~> 1.50.2'
|
65
|
-
spec.add_development_dependency 'rspec', '~> 3.8'
|
66
|
-
|
67
|
-
spec.required_ruby_version = '>= 2.6'
|
58
|
+
spec.required_ruby_version = '>= 3.2'
|
68
59
|
end
|
data/examples/emoji.feature
CHANGED
data/exe/chutney
CHANGED
@@ -24,7 +24,7 @@ OptionParser.new do |opts|
|
|
24
24
|
|
25
25
|
opts.on('-q',
|
26
26
|
'--quiet',
|
27
|
-
'Disable chutney usage warnings. Does not affect the output of the formatters.') do
|
27
|
+
'Disable chutney usage warnings. Does not affect the output of the formatters.') do
|
28
28
|
quiet = true
|
29
29
|
end
|
30
30
|
|
@@ -90,7 +90,7 @@ basic_formatter = Chutney::Formatter.new
|
|
90
90
|
basic_formatter.results = report
|
91
91
|
|
92
92
|
if basic_formatter.files_with_issues.empty?
|
93
|
-
exit
|
93
|
+
exit
|
94
94
|
else
|
95
95
|
exit(false)
|
96
96
|
end
|
data/exe/chutney-lsp
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'language_server-protocol'
|
5
|
+
require 'logger'
|
6
|
+
require 'optparse'
|
7
|
+
require 'uri'
|
8
|
+
|
9
|
+
require 'chutney'
|
10
|
+
require 'chutney/lsp'
|
11
|
+
require 'chutney/lsp/result'
|
12
|
+
require 'chutney/lsp/server'
|
13
|
+
|
14
|
+
OptionParser.new do |opts|
|
15
|
+
opts.banner = 'Usage: chutney-lsp'
|
16
|
+
|
17
|
+
opts.on('-v', '--version', 'Display the version.') do
|
18
|
+
puts Chutney::VERSION
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
end.parse!
|
22
|
+
|
23
|
+
Chutney::LSP::Server.new.start
|
Binary file
|
@@ -20,8 +20,10 @@ module Chutney
|
|
20
20
|
|
21
21
|
def load_user_configuration
|
22
22
|
config_files = ['chutney.yml', '.chutney.yml'].map do |fname|
|
23
|
-
|
24
|
-
|
23
|
+
['.', 'config'].map do |dir|
|
24
|
+
Dir["#{dir}#{File::SEPARATOR}#{fname}"]
|
25
|
+
end
|
26
|
+
end.flatten.compact
|
25
27
|
|
26
28
|
self.user_configuration_path = config_files.first
|
27
29
|
return unless !user_configuration_path.nil? && File.exist?(user_configuration_path)
|
@@ -21,13 +21,13 @@ module Chutney
|
|
21
21
|
def print_report(data)
|
22
22
|
return if data.empty?
|
23
23
|
|
24
|
-
print TTY::Pie.new(data
|
24
|
+
print TTY::Pie.new(data:, radius: 8, legend: { format: '%<label>s %<name>s %<value>i' })
|
25
25
|
puts
|
26
26
|
end
|
27
27
|
|
28
28
|
def top_offences
|
29
29
|
offence = Hash.new(0)
|
30
|
-
files_with_issues.
|
30
|
+
files_with_issues.each_value do |linter|
|
31
31
|
linter.each do |lint|
|
32
32
|
offence[lint[:linter]] += lint[:issues].count
|
33
33
|
end
|
@@ -7,7 +7,7 @@ module Chutney
|
|
7
7
|
# matching group 1: opening quote; 2: quoted text; 3: closing quote
|
8
8
|
# opening and closing quote must match (via backrefs)
|
9
9
|
# apostrophes, both singular and plural posessives, are accounted for
|
10
|
-
QUOTED_STRING = /(?!\b\b)(['"])(.*(?:\b'\b[^\1]*)*(?!\b[\1]\b))(\1)
|
10
|
+
QUOTED_STRING = /(?!\b\b)(['"])(.*(?:\b'\b[^\1]*)*(?!\b[\1]\b))(\1)/
|
11
11
|
Parameter = Struct.new('Parameter', :quotation_mark, :name)
|
12
12
|
|
13
13
|
def lint
|
@@ -19,7 +19,7 @@ module Chutney
|
|
19
19
|
collision_with = case_collision(tag)
|
20
20
|
if collision_with
|
21
21
|
add_issue(I18n.t('linters.same_tag_different_case',
|
22
|
-
existing_tag: collision_with, tag:
|
22
|
+
existing_tag: collision_with, tag:),
|
23
23
|
feature, scenario)
|
24
24
|
else
|
25
25
|
@@all_known_tags << tag
|
@@ -20,7 +20,7 @@ module Chutney
|
|
20
20
|
|
21
21
|
add_issue(
|
22
22
|
I18n.t('linters.same_tag_for_all_scenarios.feature_level',
|
23
|
-
tag:
|
23
|
+
tag:),
|
24
24
|
feature
|
25
25
|
)
|
26
26
|
end
|
@@ -37,7 +37,7 @@ module Chutney
|
|
37
37
|
next if tag == 'skip'
|
38
38
|
|
39
39
|
add_issue(I18n.t('linters.same_tag_for_all_scenarios.example_level',
|
40
|
-
tag:
|
40
|
+
tag:), feature, scenario)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -11,7 +11,7 @@ module Chutney
|
|
11
11
|
example.rows.first.cells.map(&:value).each do |variable|
|
12
12
|
next if used?(variable, scenario)
|
13
13
|
|
14
|
-
add_issue(I18n.t('linters.unused_variable', variable:
|
14
|
+
add_issue(I18n.t('linters.unused_variable', variable:), feature, scenario, example)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
data/lib/chutney/linter.rb
CHANGED
@@ -82,7 +82,7 @@ module Chutney
|
|
82
82
|
|
83
83
|
def add_issue(message, feature = nil, scenario = nil, item = nil)
|
84
84
|
issues << Lint.new(
|
85
|
-
message
|
85
|
+
message:,
|
86
86
|
gherkin_type: type(feature, scenario, item),
|
87
87
|
location: location(feature, scenario, item),
|
88
88
|
feature: feature&.name,
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Chutney
|
4
|
+
module LSP
|
5
|
+
# A wrapper that holds a response to the query message
|
6
|
+
# from the LSP client
|
7
|
+
class Result
|
8
|
+
attr_reader :id, :response
|
9
|
+
|
10
|
+
def initialize(response:, id: nil)
|
11
|
+
@id = id
|
12
|
+
@response = response
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Chutney
|
4
|
+
module LSP
|
5
|
+
# A minimalistic language server which will lint gherkin
|
6
|
+
# files on open and save
|
7
|
+
class Server
|
8
|
+
LSP_CONST = LanguageServer::Protocol::Constant
|
9
|
+
LSP_IO = LanguageServer::Protocol::Transport::Stdio
|
10
|
+
LSP_IF = LanguageServer::Protocol::Interface
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@writer = LSP_IO::Writer.new
|
14
|
+
@reader = LSP_IO::Reader.new
|
15
|
+
@mutex = Mutex.new
|
16
|
+
@incoming_queue = Thread::Queue.new
|
17
|
+
@outgoing_queue = Thread::Queue.new
|
18
|
+
|
19
|
+
@dispatcher = Thread.new do
|
20
|
+
while (message = @outgoing_queue.pop)
|
21
|
+
if message.is_a? Result
|
22
|
+
@mutex.synchronize { @writer.write(id: message.id, result: message.response) }
|
23
|
+
else
|
24
|
+
@mutex.synchronize { @writer.write(message.to_hash) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
@worker = Thread.new do
|
30
|
+
while (message = @incoming_queue.pop)
|
31
|
+
process_message(message)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Thread.main.priority = 1
|
36
|
+
end
|
37
|
+
|
38
|
+
def process_message(message)
|
39
|
+
case message[:method]
|
40
|
+
when 'initialize'
|
41
|
+
run_initialize(message)
|
42
|
+
when 'initialized'
|
43
|
+
run_initialized
|
44
|
+
when 'textDocument/didOpen', 'textDocument/didSave'
|
45
|
+
run_did_change(message)
|
46
|
+
when 'textDocument/didClose'
|
47
|
+
# no-op
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_message(message)
|
52
|
+
return if outgoing_queue.closed?
|
53
|
+
|
54
|
+
outgoing_queue << message
|
55
|
+
end
|
56
|
+
|
57
|
+
def send_log(message, method: 'window/logMessage', error: false)
|
58
|
+
type = error ? LSP_CONST::MessageType::ERROR : LSP_CONST::MessageType::INFO
|
59
|
+
notification = LSP_IF::NotificationMessage.new(
|
60
|
+
method:,
|
61
|
+
jsonrpc: '2.0',
|
62
|
+
params: LSP_IF::ShowMessageParams.new(
|
63
|
+
type:,
|
64
|
+
message: "Chutney LSP [#{VERSION}]: #{message}"
|
65
|
+
)
|
66
|
+
)
|
67
|
+
send_message(notification)
|
68
|
+
end
|
69
|
+
|
70
|
+
def send_notification(message, error: false)
|
71
|
+
send_log(message, method: 'window/showMessage', error:)
|
72
|
+
end
|
73
|
+
|
74
|
+
def run_initialize(message)
|
75
|
+
initialize_result = LSP_IF::InitializeResult.new(
|
76
|
+
capabilities: LSP_IF::ServerCapabilities.new(
|
77
|
+
document_formatting_provider: true,
|
78
|
+
text_document_sync: LSP_IF::TextDocumentSyncOptions.new(
|
79
|
+
change: LSP_CONST::TextDocumentSyncKind::FULL,
|
80
|
+
open_close: true,
|
81
|
+
save: true
|
82
|
+
)
|
83
|
+
),
|
84
|
+
server_info: {
|
85
|
+
name: 'chutney-lsp',
|
86
|
+
version: VERSION
|
87
|
+
}
|
88
|
+
)
|
89
|
+
send_message(Result.new(id: message[:id], response: initialize_result))
|
90
|
+
send_log('Initializing')
|
91
|
+
end
|
92
|
+
|
93
|
+
def run_initialized
|
94
|
+
RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
|
95
|
+
send_notification('Chutney LSP Server up and running')
|
96
|
+
send_log('Initialized')
|
97
|
+
end
|
98
|
+
|
99
|
+
def run_did_change(message)
|
100
|
+
document = message.dig(:params, :textDocument)
|
101
|
+
filename = document[:uri].delete_prefix('file://')
|
102
|
+
send_log("Evaluating #{filename}")
|
103
|
+
linter = Chutney::ChutneyLint.new(*filename)
|
104
|
+
linter.configuration.quiet!
|
105
|
+
begin
|
106
|
+
offenses = linter.analyse.values.first.filter { |r| r[:issues].any? }
|
107
|
+
rescue StandardError => e
|
108
|
+
send_log("Could not parse #{filename} as Gherkin. Received: #{e.full_message}", error: true)
|
109
|
+
send_notification("Could not parse #{filename} as Gherkin.", error: true)
|
110
|
+
return
|
111
|
+
end
|
112
|
+
send_log("Found #{offenses.count} offenses")
|
113
|
+
diagnostics = offenses
|
114
|
+
.flat_map { |group| group[:issues].each { |issue| issue[:linter] = group[:linter] } }
|
115
|
+
.map { |offense| to_diagnostic(offense) }
|
116
|
+
send_message(diagnostic_message(document[:uri], diagnostics))
|
117
|
+
end
|
118
|
+
|
119
|
+
def diagnostic_message(file_uri, diagnostics)
|
120
|
+
{
|
121
|
+
method: 'textDocument/publishDiagnostics',
|
122
|
+
params: {
|
123
|
+
uri: file_uri,
|
124
|
+
diagnostics:
|
125
|
+
}
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
def to_diagnostic(offense)
|
130
|
+
code = offense[:linter]
|
131
|
+
message = offense[:message]
|
132
|
+
source = 'chutney'
|
133
|
+
{ code:, message:, source:, severity: 1, range: to_range(offense[:location]) }
|
134
|
+
end
|
135
|
+
|
136
|
+
def to_range(location)
|
137
|
+
{
|
138
|
+
start: { character: location.fetch(:column, 1) - 1, line: location.fetch(:line, 1) - 1 },
|
139
|
+
end: { character: 0, line: location.fetch(:line, 1) }
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
def shutdown
|
144
|
+
incoming_queue.clear
|
145
|
+
outgoing_queue.clear
|
146
|
+
incoming_queue.close
|
147
|
+
outgoing_queue.close
|
148
|
+
worker.join
|
149
|
+
dispatcher.join
|
150
|
+
send_log('Shutdown complete')
|
151
|
+
end
|
152
|
+
|
153
|
+
def start
|
154
|
+
reader.read do |message|
|
155
|
+
method = message[:method]
|
156
|
+
send_log("Received #{method}")
|
157
|
+
|
158
|
+
case method
|
159
|
+
when 'initialize', 'initialized', 'textDocument/didOpen', 'textDocument/didClose', 'textDocument/didSave'
|
160
|
+
incoming_queue.push(message)
|
161
|
+
when 'shutdown'
|
162
|
+
shutdown
|
163
|
+
when 'exit'
|
164
|
+
mutex.synchronize do
|
165
|
+
status = incoming_queue.closed? ? 0 : 1
|
166
|
+
exit(status)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
attr_reader :reader, :writer, :mutex, :incoming_queue, :outgoing_queue, :worker, :dispatcher
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
data/lib/chutney/lsp.rb
ADDED
data/lib/chutney/version.rb
CHANGED
data/lib/chutney.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
# Hello! This is where you manage which Jekyll version is used to run.
|
3
|
+
# When you want to use a different version, change it below, save the
|
4
|
+
# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
|
5
|
+
#
|
6
|
+
# bundle exec jekyll serve
|
7
|
+
#
|
8
|
+
# This will help ensure the proper Jekyll version is running.
|
9
|
+
# Happy Jekylling!
|
10
|
+
gem "jekyll", "~> 4.3.4"
|
11
|
+
# This is the default theme for new Jekyll sites. You may change this to anything you like.
|
12
|
+
gem "minima", "~> 2.5"
|
13
|
+
# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
|
14
|
+
# uncomment the line below. To upgrade, run `bundle update github-pages`.
|
15
|
+
# gem "github-pages", group: :jekyll_plugins
|
16
|
+
# If you have any plugins, put them here!
|
17
|
+
group :jekyll_plugins do
|
18
|
+
gem "jekyll-feed", "~> 0.12"
|
19
|
+
gem "jekyll-data"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
|
23
|
+
# and associated library.
|
24
|
+
platforms :mingw, :x64_mingw, :mswin, :jruby do
|
25
|
+
gem "tzinfo", ">= 1", "< 3"
|
26
|
+
gem "tzinfo-data"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Performance-booster for watching directories on Windows
|
30
|
+
gem "wdm", "~> 0.1", :platforms => [:mingw, :x64_mingw, :mswin]
|
31
|
+
|
32
|
+
# Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem
|
33
|
+
# do not have a Java counterpart.
|
34
|
+
gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]
|
35
|
+
gem "minimal-mistakes-jekyll"
|
36
|
+
gem 'base64'
|
37
|
+
gem 'csv'
|