simple_english 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c05cc78b762e2b44f4c841ef5a78537e91d9bcd408ed8739fcc00f3538f14eb
4
- data.tar.gz: 9abac46821a9260b3ec7312a1c258464e08a943262eaccab893383104479dcee
3
+ metadata.gz: 18c24cb1079203715bb6c9decdf37cbae2221ef7db6ce93cc91cea6cd40e80ec
4
+ data.tar.gz: 25c81010ee8974bc23393acb0e7b692634616122201ca693e12a979ef310bf53
5
5
  SHA512:
6
- metadata.gz: 6c4ac5c79f9a62c848ae37977dfb48ec2b815b27e5a38af7ab448a489206b14b8b26dec4f560b5115dd5be3c3688bfed254ebecbfd5e57c83c84d4a545367780
7
- data.tar.gz: 699067a5a056a54186fe40d62ee17dd358492409dd86a76fc17cd2e1be8d110ee1147daa3ecb5f4d8b995cfd98f4108e96adbf6ed96603a43566ee820fd48729
6
+ metadata.gz: 85550424bc3b8cbd339180ba2f1c91a3125690af0bebb8b11efdf4d367569b15952cff57122e3c768989d8233c4daa23eb4f8359e6a2ce20a1ba5b9471ef1f12
7
+ data.tar.gz: 7a51bf55350c8b34501c5a0537236890ab8783d1199cc8c57e84bd3f3d24107aafa2e8e2944e49e909601f5a33e5ed1f11cb9812e9f5bb500aea05e60bc6db3e
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ [![CI](https://github.com/TonyCTHsu/simple-english/actions/workflows/ci.yml/badge.svg)](https://github.com/TonyCTHsu/simple-english/actions/workflows/ci.yml) <!-- se: ignore=SE_PARAGRAPH_TOO_LONG -->
2
+ [![Gem Version](https://badge.fury.io/rb/simple_english.svg)](https://badge.fury.io/rb/simple_english)
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/TonyCTHsu/simple-english/blob/master/LICENSE)
4
+
1
5
  # simple_english
2
6
 
3
7
  > Write for human readers, not for reviewers or another AI.
@@ -9,9 +13,9 @@ instead.
9
13
  ```console
10
14
  $ printf 'You should leverage this tool in order to make sure that your docs are readable.' > note.md
11
15
  $ se note.md
12
- note.md:1: [SE_MODAL_RESTRICTED] Use can, will, or must. State the requirement exactly.
13
- note.md:1: [SE_SLOP_IN_ORDER_TO] Write "to".
14
- note.md:1: [SE_SLOP_LEVERAGE] Write "use".
16
+ note.md:1:5: [SE_MODAL_RESTRICTED] "should" - Use can, will, or must. State the requirement exactly.
17
+ note.md:1:12: [SE_SLOP_LEVERAGE] "leverage" - Write "use".
18
+ note.md:1:31: [SE_SLOP_IN_ORDER_TO] "in order to" - Write "to".
15
19
  ```
16
20
 
17
21
  Markdown prose plus code comments in Python, Ruby, JavaScript,
@@ -144,7 +144,9 @@ module SimpleEnglish
144
144
  )
145
145
  else
146
146
  results.each do |path, finding|
147
- puts "#{path}:#{finding.line}: [#{finding.rule}] #{finding.message}"
147
+ location = "#{path}:#{finding.line}"
148
+ location << ":#{finding.column}" if finding.column
149
+ puts "#{location}: [#{finding.rule}] #{finding.message}"
148
150
  end
149
151
  end
150
152
  end
@@ -29,6 +29,26 @@ module SimpleEnglish
29
29
  line
30
30
  end
31
31
 
32
+ # The 1-based column of a UTF-16 offset within its line. Astral
33
+ # characters count as two units, like the match offset. A match
34
+ # starting at the newline itself is column one of the next line.
35
+ def offset_to_column(text, offset)
36
+ column = 0
37
+ units = 0
38
+ text.each_char do |char|
39
+ if char == "\n"
40
+ return 1 if units >= offset
41
+ column = 0
42
+ units += 1
43
+ next
44
+ end
45
+ return column + 1 if units >= offset
46
+ units += (char.ord > 0xFFFF) ? 2 : 1
47
+ column += 1
48
+ end
49
+ column + 1
50
+ end
51
+
32
52
  DEFAULT_PORT = 8181
33
53
  REQUEST_TIMEOUT = 30
34
54
 
@@ -64,15 +84,27 @@ module SimpleEnglish
64
84
  matches.map do |match|
65
85
  line, column = payload.locate(match.fetch("offset"))
66
86
  Finding.new(line: line, column: column,
67
- rule: match.fetch("rule").fetch("id"), message: match.fetch("message"))
87
+ rule: match.fetch("rule").fetch("id"),
88
+ message: with_context(match.fetch("message"), match))
68
89
  end
69
90
  end
70
91
 
92
+ # Prefix the offending text, so a finding says what to change,
93
+ # not only how. LanguageTool returns it in the match context.
94
+ # Context offsets are Java UTF-16 code units, like the match
95
+ # offset.
96
+ def with_context(message, match)
97
+ context = match["context"] or return message
98
+ matched = context.fetch("text", "")[
99
+ context.fetch("offset", 0).to_i, context.fetch("length", 0).to_i]
100
+ matched.empty? ? message : "\"#{matched}\" - #{message}"
101
+ end
102
+
71
103
  def to_payload(payload)
72
104
  payload.is_a?(String) ? PlainText.new(payload) : payload
73
105
  end
74
106
 
75
- private_class_method :to_payload
107
+ private_class_method :to_payload, :with_context
76
108
 
77
109
  # Full lint via the se daemon. Raw Markdown in, or code
78
110
  # source with a language for the comment pipeline. nil when
@@ -109,15 +141,15 @@ module SimpleEnglish
109
141
  # caller owns the exit status. Diagnostics go to stderr here, where the cause
110
142
  # is known.
111
143
  def ensure_up(install: SimpleEnglish::Install.from_env)
112
- unless File.exist?(install.server_jar)
113
- warn install.setup_error
114
- return false
115
- end
116
144
  return true if up?
117
145
  if ENV["SE_SERVER_URL"]
118
146
  warn "error: SE_SERVER_URL is set but #{url} does not answer."
119
147
  return false
120
148
  end
149
+ unless File.exist?(install.server_jar)
150
+ warn install.setup_error
151
+ return false
152
+ end
121
153
  warn "se: daemon not running; starting it (first lint takes ~15s)..."
122
154
  bin = File.expand_path("../../bin/se", __dir__)
123
155
  Process.spawn(RbConfig.ruby, bin, "serve", out: File::NULL, err: File::NULL)
@@ -9,7 +9,10 @@ require_relative "paragraph"
9
9
 
10
10
  module SimpleEnglish
11
11
  module Markdown
12
- SENTENCE_END = /[.!?]/
12
+ # A terminator ends a sentence only before whitespace or at the
13
+ # end of the text, so periods inside URLs and file names do not
14
+ # split sentences.
15
+ SENTENCE_END = /(?<=[.!?])(?:\s+|$)/
13
16
  # Non-prose blocks: their lines never reach the counting rules or
14
17
  # LanguageTool.
15
18
  BLANKED_BLOCKS = %w[fenced_code_block indented_code_block thematic_break].freeze
@@ -27,7 +30,10 @@ module SimpleEnglish
27
30
  end
28
31
 
29
32
  def strip_line(line)
30
- line.gsub(/`[^`]*`/, "X").sub(/\A\#{1,6} /, "")
33
+ # Width-preserving replacements, so a finding column points at
34
+ # the source line, not at the stripped copy.
35
+ line.gsub(/`[^`]*`/) { |code| "X" * code.length }
36
+ .sub(/\A\#{1,6} /) { |marker| " " * marker.length }
31
37
  end
32
38
 
33
39
  # A vertical list is not one paragraph: each list item is its own.
@@ -10,7 +10,8 @@ module SimpleEnglish
10
10
  end
11
11
 
12
12
  def locate(utf16_offset)
13
- [Client.offset_to_line(text, utf16_offset), nil]
13
+ [Client.offset_to_line(text, utf16_offset),
14
+ Client.offset_to_column(text, utf16_offset)]
14
15
  end
15
16
  end
16
17
  end
@@ -59,10 +59,12 @@ module SimpleEnglish
59
59
  rules_dir = Dir.mktmpdir("se-rules")
60
60
  stage_rules(rules_dir)
61
61
  # The inner JVM's stderr goes to a file so failure messages can quote
62
- # its first line. The dev log has a path. $stderr does not, so fall
63
- # back to a temp file that Ruby unlinks when the process exits.
62
+ # its first line. Only an explicitly opened dev log (a File) is reused
63
+ # for that. $stderr reports path "<STDERR>", so it creates a file
64
+ # by that name in the CWD. Anything else falls back to a temp file
65
+ # that Ruby unlinks when the process exits.
64
66
  inner_log_path =
65
- if log.respond_to?(:path)
67
+ if log.is_a?(File)
66
68
  log.path
67
69
  else
68
70
  inner_log = Tempfile.new("se-inner")
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleEnglish
4
+ VERSION = "0.2.0"
5
+ end
@@ -4,6 +4,7 @@
4
4
  # Pattern rules run on LanguageTool. Counting rules run here.
5
5
  # This file is the composition root. The pieces live in lib/simple_english/.
6
6
 
7
+ require_relative "simple_english/version"
7
8
  require_relative "simple_english/finding"
8
9
  require_relative "simple_english/markdown"
9
10
  require_relative "simple_english/counts"
@@ -19,8 +20,6 @@ require_relative "simple_english/client"
19
20
  require_relative "simple_english/server"
20
21
 
21
22
  module SimpleEnglish
22
- VERSION = "0.1.0"
23
-
24
23
  module_function
25
24
 
26
25
  # Returns findings, or nil when the daemon is unreachable. The
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_english
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TonyCTHsu
@@ -72,14 +72,28 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '5.0'
75
+ version: '6.0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '5.0'
82
+ version: '6.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest-mock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '5.27'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '5.27'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: json
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -140,6 +154,7 @@ files:
140
154
  - lib/simple_english/server.rb
141
155
  - lib/simple_english/span.rb
142
156
  - lib/simple_english/suppressions.rb
157
+ - lib/simple_english/version.rb
143
158
  - rules/simple-english.xml
144
159
  homepage: https://github.com/TonyCTHsu/simple-english
145
160
  licenses: