orthotypo 1.0.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd24cbdb2c5adaa4f6ecf91acf144025549b7599aeb0f944e3f1987331448450
4
- data.tar.gz: 6d9a7bb478c4cc0b76722128a1065fc99666f7d1f284cc82cb6f26acfe89bf70
3
+ metadata.gz: f2f6a258873f18c7dc354138d1c32a75eb53c185ff8df8352222d5ea67eff0cf
4
+ data.tar.gz: be6d6756ec6f2925304776c150448c0e34469a32b95e92cbf3e54b64a9f581ac
5
5
  SHA512:
6
- metadata.gz: e51b243378c8d97df6fd3bfa7f59a7aeb7f516d3f54d82cce4eb0ae8d3c7b6a5893211c857c72f8c249a73e0333f3d187ddf4d6145a45ab6de5039633aeaf457
7
- data.tar.gz: dc77c1043a6210122c48a8d5849d63df04247d3d957707469d9e3029ed0cf1a9bec745cd12115bb4563254abc6e227c74e13f02285fc0cada9a0d75ab0be2ad6
6
+ metadata.gz: 1138ac9e055c6b87db9117b6640bb32457dd20a35306e87da7665a5496485aa89a82e18f9bde2329c77533a64815cdfbfbd44bb6fa2d17c2f5b0f576f80adee0
7
+ data.tar.gz: fd5f3571d645b7facf19afa56d7c6700724dbe6d8f5372ce3bc8a68bfa14d346408cf7a64fe8c8b34fc3db5e5037f4173ed268651c8fdcde133e3a89a39e3542
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- orthotypo (1.0.0)
4
+ orthotypo (1.0.2)
5
5
  htmlentities
6
6
  nokogiri
7
7
 
@@ -9,54 +9,52 @@ GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
11
  ast (2.4.2)
12
- base64 (0.1.1)
13
12
  byebug (11.1.3)
14
- diff-lcs (1.5.0)
13
+ diff-lcs (1.5.1)
15
14
  htmlentities (4.3.4)
16
- json (2.6.3)
15
+ json (2.7.1)
17
16
  language_server-protocol (3.17.0.3)
18
- nokogiri (1.15.4-arm64-darwin)
17
+ nokogiri (1.16.2-arm64-darwin)
19
18
  racc (~> 1.4)
20
- nokogiri (1.15.4-x86_64-darwin)
19
+ nokogiri (1.16.2-x86_64-darwin)
21
20
  racc (~> 1.4)
22
- parallel (1.23.0)
23
- parser (3.2.2.4)
21
+ parallel (1.24.0)
22
+ parser (3.3.0.5)
24
23
  ast (~> 2.4.1)
25
24
  racc
26
- racc (1.7.1)
25
+ racc (1.7.3)
27
26
  rainbow (3.1.1)
28
- rake (13.0.6)
29
- regexp_parser (2.8.2)
27
+ rake (13.1.0)
28
+ regexp_parser (2.9.0)
30
29
  rexml (3.2.6)
31
- rspec (3.12.0)
32
- rspec-core (~> 3.12.0)
33
- rspec-expectations (~> 3.12.0)
34
- rspec-mocks (~> 3.12.0)
35
- rspec-core (3.12.2)
36
- rspec-support (~> 3.12.0)
37
- rspec-expectations (3.12.3)
30
+ rspec (3.13.0)
31
+ rspec-core (~> 3.13.0)
32
+ rspec-expectations (~> 3.13.0)
33
+ rspec-mocks (~> 3.13.0)
34
+ rspec-core (3.13.0)
35
+ rspec-support (~> 3.13.0)
36
+ rspec-expectations (3.13.0)
38
37
  diff-lcs (>= 1.2.0, < 2.0)
39
- rspec-support (~> 3.12.0)
40
- rspec-mocks (3.12.6)
38
+ rspec-support (~> 3.13.0)
39
+ rspec-mocks (3.13.0)
41
40
  diff-lcs (>= 1.2.0, < 2.0)
42
- rspec-support (~> 3.12.0)
41
+ rspec-support (~> 3.13.0)
43
42
  rspec-nc (0.3.0)
44
43
  rspec (>= 3)
45
44
  terminal-notifier (>= 1.4)
46
- rspec-support (3.12.1)
47
- rubocop (1.57.0)
48
- base64 (~> 0.1.1)
45
+ rspec-support (3.13.0)
46
+ rubocop (1.60.2)
49
47
  json (~> 2.3)
50
48
  language_server-protocol (>= 3.17.0)
51
49
  parallel (~> 1.10)
52
- parser (>= 3.2.2.4)
50
+ parser (>= 3.3.0.2)
53
51
  rainbow (>= 2.2.2, < 4.0)
54
52
  regexp_parser (>= 1.8, < 3.0)
55
53
  rexml (>= 3.2.5, < 4.0)
56
- rubocop-ast (>= 1.28.1, < 2.0)
54
+ rubocop-ast (>= 1.30.0, < 2.0)
57
55
  ruby-progressbar (~> 1.7)
58
56
  unicode-display_width (>= 2.4.0, < 3.0)
59
- rubocop-ast (1.29.0)
57
+ rubocop-ast (1.30.0)
60
58
  parser (>= 3.2.1.0)
61
59
  ruby-progressbar (1.13.0)
62
60
  terminal-notifier (2.0.0)
@@ -64,6 +62,7 @@ GEM
64
62
 
65
63
  PLATFORMS
66
64
  arm64-darwin-22
65
+ arm64-darwin-23
67
66
  x86_64-darwin-22
68
67
 
69
68
  DEPENDENCIES
data/README.md CHANGED
@@ -34,6 +34,21 @@ Ne pas endommager l'HTML et les HTML entities (&nbsp;)
34
34
  S'adapter aux locales (détecter I18n)
35
35
  Permettre les configs
36
36
 
37
+ ## Tests
38
+
39
+ ```
40
+ rake
41
+ ```
42
+
43
+ ```
44
+ bundle exec rspec
45
+ ```
46
+
47
+ Pour jouer un seul test :
48
+ ```
49
+ bundle exec rspec ./spec/composer/fr_spec.rb:56
50
+ ```
51
+
37
52
  ## Sources
38
53
 
39
54
  - https://fr.wikipedia.org/wiki/Code_typographique
@@ -12,12 +12,11 @@ module Orthotypo
12
12
  def chars_with_space_after
13
13
  [
14
14
  ',',
15
- '.',
16
15
  '...',
17
16
  '…'
18
17
  ]
19
18
  end
20
-
19
+
21
20
  def chars_with_space_around
22
21
  [
23
22
  ';',
@@ -27,6 +26,12 @@ module Orthotypo
27
26
  ]
28
27
  end
29
28
 
29
+ def chars_with_no_space_before
30
+ [
31
+ '.'
32
+ ]
33
+ end
34
+
30
35
  def chars_with_no_space_around
31
36
  [
32
37
  "'",
@@ -40,11 +45,10 @@ module Orthotypo
40
45
  '«»'
41
46
  ]
42
47
  end
43
-
48
+
44
49
  def pairs_with_no_space_around
45
50
  [
46
51
  '“”',
47
- '‘’',
48
52
  '‹›',
49
53
  '""',
50
54
  "''",
@@ -31,11 +31,15 @@ module Orthotypo
31
31
  '.'
32
32
  ]
33
33
  end
34
-
34
+
35
35
  def chars_with_space_around
36
36
  []
37
37
  end
38
38
 
39
+ def chars_with_no_space_before
40
+ []
41
+ end
42
+
39
43
  def chars_with_no_space_around
40
44
  []
41
45
  end
@@ -43,7 +47,7 @@ module Orthotypo
43
47
  def pairs_with_space_around
44
48
  []
45
49
  end
46
-
50
+
47
51
  def pairs_with_no_space_around
48
52
  []
49
53
  end
@@ -57,7 +61,7 @@ module Orthotypo
57
61
 
58
62
  def chars_in_numbers
59
63
  [
60
- '.',
64
+ '.',
61
65
  ',',
62
66
  '/',
63
67
  ':'
@@ -73,18 +77,23 @@ module Orthotypo
73
77
  @contains_html_entities ||= html_entities.decode(string) != string
74
78
  end
75
79
 
80
+ def prepare_linebreaks
81
+ @string.gsub! "\r\n", "<br>"
82
+ @string.gsub! "\r", "<br>"
83
+ @string.gsub! "\n", "<br>"
84
+ end
85
+
76
86
  def prepare_ortho
77
87
  @ortho = string.dup
78
- # @ortho = html_entities.decode(@ortho) if contains_html_entities?
79
88
  @nokogiri = Nokogiri::HTML.fragment @ortho
80
89
  end
81
90
 
82
91
  def clean_ortho
83
92
  @ortho = @nokogiri.to_s
84
- # @ortho = html_entities.encode(@ortho) if contains_html_entities?
85
93
  end
86
94
 
87
95
  def parse
96
+ prepare_linebreaks
88
97
  prepare_ortho
89
98
  preserve_precious_things
90
99
  # Chars
@@ -92,13 +101,14 @@ module Orthotypo
92
101
  parse_chars_with_space_before_after_digit
93
102
  parse_chars_with_space_after
94
103
  parse_chars_with_space_around
104
+ parse_chars_with_no_space_before
95
105
  parse_chars_with_no_space_around
96
106
  # Pairs
97
107
  parse_pairs_with_space_around
98
108
  parse_pairs_with_no_space_around
99
109
  # Numbers
100
110
  parse_chars_in_numbers
101
- #
111
+ #
102
112
  clean_ortho
103
113
  restore_precious_things
104
114
  end
@@ -106,20 +116,40 @@ module Orthotypo
106
116
  def preserve_precious_things
107
117
  @precious_things = []
108
118
  @nokogiri.traverse do |node|
109
- next unless node.text?
110
- new_content = node.content.split(SPACE).map { |fragment|
111
- if Analyzer::precious?(fragment)
112
- token = "#{PRECIOUS_TOKEN}#{@precious_things.length}"
113
- @precious_things << fragment
114
- token
115
- else
116
- fragment
119
+ if node.text?
120
+ has_leading_space = node.content.start_with? SPACE
121
+ has_trailing_space = node.content.end_with? SPACE
122
+ node.content = node.content.split(SPACE).map { |fragment|
123
+ store_if_precious(fragment)
124
+ }.join(SPACE)
125
+ node.content = SPACE + node.content if has_leading_space
126
+ node.content = node.content + SPACE if has_trailing_space
127
+ elsif node.element?
128
+ if node.name == 'a'
129
+ node.attributes.each do |key, attribute|
130
+ if attribute.name == 'href'
131
+ attribute.value = store_precious_thing(attribute.value)
132
+ end
133
+ end
117
134
  end
118
- }.join(SPACE)
119
- node.content = new_content
135
+ end
120
136
  end
121
137
  end
122
138
 
139
+ def store_if_precious(string)
140
+ Analyzer::precious?(string) ? store_precious_thing(string)
141
+ : string
142
+ end
143
+
144
+ def store_precious_thing(string)
145
+ # Create token identifier
146
+ token = "#{PRECIOUS_TOKEN}#{@precious_things.length}"
147
+ # Store value
148
+ @precious_things << string
149
+ # Return identifier
150
+ token
151
+ end
152
+
123
153
  def restore_precious_things
124
154
  @precious_things.each_with_index do |value, index|
125
155
  @ortho.gsub! "#{PRECIOUS_TOKEN}#{index}", value
@@ -131,22 +161,23 @@ module Orthotypo
131
161
  # Espace normal avant -> espace fine insécable avant
132
162
  fix(SPACE + char, NNBSP + char)
133
163
  # Pas d'espace avant -> espace fine insécable avant
134
- fix(/([[:alpha:]])[#{char}]/, "\\1" + NNBSP + char)
164
+ fix(/([[:alpha:]])#{Regexp.quote(char)}/, "\\1" + NNBSP + char)
135
165
  end
136
166
  end
137
167
 
138
168
  def parse_chars_with_space_before_after_digit
139
169
  chars_with_space_before_after_digit.each do |char|
140
- fix(/([[:digit:]])[#{char}]/, "\\1" + NNBSP + char)
170
+ fix(/([[:digit:]])#{Regexp.quote(char)}/, "\\1" + NNBSP + char)
141
171
  end
142
172
  end
143
173
 
144
174
  def parse_chars_with_space_after
145
175
  chars_with_space_after.each do |char|
146
- # Espace avant -> pas d'espace avant
176
+ # Espace avant -> pas d'espace avant
147
177
  fix(SPACE + char, char)
148
178
  # Pas d'espace après -> espace après
149
- fix(/[#{char}]([[:alpha:]])/, char + SPACE + "\\1")
179
+ # FIXME
180
+ fix(/#{Regexp.quote(char)}([[:alpha:]])/, char + SPACE + "\\1")
150
181
  end
151
182
  end
152
183
 
@@ -155,15 +186,22 @@ module Orthotypo
155
186
  # Espace normal avant -> espace fine insécable avant
156
187
  fix(SPACE + char, NNBSP + char)
157
188
  # Pas d'espace avant -> espace fine insécable avant
158
- fix(/([[:alpha:]])[#{char}]/, "\\1" + NNBSP + char)
189
+ fix(/([[:alpha:]])#{Regexp.quote(char)}/, "\\1" + NNBSP + char)
190
+ end
191
+ end
192
+
193
+ def parse_chars_with_no_space_before
194
+ chars_with_no_space_before.each do |char|
195
+ # Espace avant -> pas d'espace avant
196
+ fix(SPACE + char, char)
159
197
  end
160
198
  end
161
199
 
162
200
  def parse_chars_with_no_space_around
163
201
  chars_with_no_space_around.each do |char|
164
- # Espace avant -> pas d'espace avant
202
+ # Espace avant -> pas d'espace avant
165
203
  fix(SPACE + char, char)
166
- # Espace après -> pas d'espace après
204
+ # Espace après -> pas d'espace après
167
205
  fix(char + SPACE, char)
168
206
  end
169
207
  end
@@ -176,8 +214,8 @@ module Orthotypo
176
214
  fix(opening + SPACE, opening + NNBSP)
177
215
  fix(SPACE + closing, NNBSP + closing)
178
216
  # Pas d'espace -> espace fine insécable
179
- fix(/[#{opening}]([^[:space:]])/, opening + NNBSP + "\\1")
180
- fix(/([^[:space:]])[#{closing}]/, "\\1" + NNBSP + closing)
217
+ fix(/#{Regexp.quote(opening)}([^[:space:]])/, opening + NNBSP + "\\1")
218
+ fix(/([^[:space:]])#{Regexp.quote(closing)}/, "\\1" + NNBSP + closing)
181
219
  end
182
220
  end
183
221
 
@@ -186,16 +224,15 @@ module Orthotypo
186
224
  opening = marks.chars.first
187
225
  closing = marks.chars.last
188
226
  # Espace -> pas d'espace
189
- fix(opening + SPACE, opening)
190
- fix(SPACE + closing, closing)
227
+ fix(/#{Regexp.quote(opening)}[[:space:]](.+)[[:space:]]#{Regexp.quote(closing)}/, opening + "\\1" + closing)
191
228
  end
192
229
  end
193
230
 
194
231
  def parse_chars_in_numbers
195
232
  chars_in_numbers.each do |char|
196
- fix(/([[:digit:]])[[:space:]][#{char}]([[:digit:]])/, "\\1" + char + "\\2")
197
- fix(/([[:digit:]])[[:space:]][#{char}][[:space:]]([[:digit:]])/, "\\1" + char + "\\2")
198
- fix(/([[:digit:]])[#{char}][[:space:]]([[:digit:]])/, "\\1" + char + "\\2")
233
+ fix(/([[:digit:]])[[:space:]]#{Regexp.quote(char)}([[:digit:]])/, "\\1" + char + "\\2")
234
+ fix(/([[:digit:]])[[:space:]]#{Regexp.quote(char)}[[:space:]]([[:digit:]])/, "\\1" + char + "\\2")
235
+ fix(/([[:digit:]])#{Regexp.quote(char)}[[:space:]]([[:digit:]])/, "\\1" + char + "\\2")
199
236
  end
200
237
  end
201
238
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Orthotypo
4
- VERSION = "1.0.0"
4
+ VERSION = "1.0.2"
5
5
  end
@@ -17,27 +17,32 @@ describe Orthotypo::Composer::Fr do
17
17
  expect("é : suite".ortho).to(eq("é : suite"))
18
18
  expect("1 : suite".ortho).to(eq("1 : suite"))
19
19
  end
20
-
20
+
21
21
  it 'fixes space before simple punctuation' do
22
22
  expect("mot , suite".ortho).to(eq("mot, suite"))
23
23
  expect("mot . suite".ortho).to(eq("mot. suite"))
24
24
  expect("l 'approche".ortho).to(eq("l'approche"))
25
25
  expect("l' approche".ortho).to(eq("l'approche"))
26
26
  end
27
-
27
+
28
28
  it 'fixes space after simple punctuation' do
29
29
  expect("mot,suite".ortho).to(eq("mot, suite"))
30
+ expect("etudiant.gouv.fr".ortho).to(eq("etudiant.gouv.fr"))
30
31
  expect("4,5".ortho).to(eq("4,5"))
32
+ expect("4.5".ortho).to(eq("4.5"))
33
+ # Le test suivant, on ne fait rien, on ne peut pas résoudre, car s'apparente au cas précédent.
34
+ expect("Il est né en 1984.5 maisons en paille".ortho).to(eq("Il est né en 1984.5 maisons en paille"))
31
35
  end
32
36
 
33
37
  it 'fixes quotation marks' do
34
38
  expect("«mot»".ortho).to(eq("« mot »"))
35
39
  expect("« mot »".ortho).to(eq("« mot »"))
36
40
  expect("“ mot ”".ortho).to(eq("“mot”"))
37
- expect("‘ mot ’".ortho).to(eq("‘mot’"))
38
41
  expect("‹ mot ›".ortho).to(eq("‹mot›"))
39
42
  expect("\" mot \"".ortho).to(eq("\"mot\""))
40
43
  expect("' mot '".ortho).to(eq("'mot'"))
44
+ expect("( une phrase entre parenthèses )".ortho).to(eq("(une phrase entre parenthèses)"))
45
+ expect('photos "On the Job", stock'.ortho).to(eq('photos "On the Job", stock'))
41
46
  end
42
47
 
43
48
  it 'fixes percent' do
@@ -53,18 +58,36 @@ describe Orthotypo::Composer::Fr do
53
58
  expect("10 octobre 2023 16:00".ortho).to(eq("10 octobre 2023 16:00"))
54
59
  end
55
60
 
61
+ it 'does well with HTML' do
62
+ expect("<p><a href=\"https://www.linkedin.com/in/marie-dewet-1397a094/\">Marie Dewet</a>, Co-fondatrice de <a href=\"https://www.linkedin.com/company/maisoncleo/\">MaisonCléo</a> nous apporte ses lumières.</p>".ortho).to(eq("<p><a href=\"https://www.linkedin.com/in/marie-dewet-1397a094/\">Marie Dewet</a>, Co-fondatrice de <a href=\"https://www.linkedin.com/company/maisoncleo/\">MaisonCléo</a> nous apporte ses lumières.</p>"))
63
+ expect("<p></p><p>Nous aimons la qualité</p><p></p>".ortho).to(eq("<p></p><p>Nous aimons la qualité</p><p></p>"))
64
+ expect('<p>Série de photos "On the Job", Death to the stock</p>'.ortho).to(eq('<p>Série de photos "On the Job", Death to the stock</p>'))
65
+ end
66
+
67
+ it 'manages linebreaks' do
68
+ expect("A parallel between wildlife and urban ensembles.\r\rShot during summer 2014 on our trip from north to south of Portugal.".ortho).to(eq("A parallel between wildlife and urban ensembles.<br><br>Shot during summer 2014 on our trip from north to south of Portugal."))
69
+ end
70
+
56
71
  # https://www.scribbr.fr/elements-linguistiques/les-espaces/
57
72
  it 'tests de Justine Debret' do
58
- expect("Elle a vu son cousin,sa tante et son oncle.Ils allaient tous très bien.".ortho).to(eq("Elle a vu son cousin, sa tante et son oncle. Ils allaient tous très bien."))
73
+ # FIXME
74
+ # expect("Elle a vu son cousin,sa tante et son oncle.Ils allaient tous très bien.".ortho).to(eq("Elle a vu son cousin, sa tante et son oncle. Ils allaient tous très bien."))
75
+ expect("Elle ne disait plus rien…jusqu’au moment du repas.".ortho).to(eq("Elle ne disait plus rien… jusqu’au moment du repas."))
76
+
59
77
  expect("Elle dit: qui voudrait bien venir voir ce film? Il répond: moi; à moins qu’il ne fasse très beau!".ortho).to(eq("Elle dit : qui voudrait bien venir voir ce film ? Il répond : moi ; à moins qu’il ne fasse très beau !"))
78
+
60
79
  expect("L’ hiver ne va pas durer toute l’année.".ortho).to(eq("L’hiver ne va pas durer toute l’année."))
61
- expect("Elle ne disait plus rien…jusqu’au moment du repas.".ortho).to(eq("Elle ne disait plus rien… jusqu’au moment du repas."))
80
+
62
81
  expect("Il a dit : «J’arrive ce matin ( ou plus tard ) à Paris [ rue de la République ] pour son anniversaire.»".ortho).to(eq("Il a dit : « J’arrive ce matin (ou plus tard) à Paris [rue de la République] pour son anniversaire. »"))
82
+
63
83
  # Le test suivant n'est pas automatisable, parce qu'on ne peut distinguer un Paris-Brest (le gâteau) d'un Paris - Brest (le trajet)
64
84
  # expect("Nous l’avons rencontré à Saint - Martin.".ortho).to(eq("Nous l’avons rencontré à Saint-Martin."))
85
+
65
86
  expect("Il roule pendant 31, 5 km.".ortho).to(eq("Il roule pendant 31,5 km."))
87
+
66
88
  # Le test suivant est-il automatisable ?
67
89
  # expect("Il roule pendant 31, 5km.".ortho).to(eq("Il roule pendant 31,5 km."))
90
+
68
91
  # Pas automatisable, rien ne permet de distinguer s'il s'agit d'un rang ou d'un nombre
69
92
  # expect("Il en compte 1,000, 10,000, 36,742, 500,000, puis 1,000,000, 25,000,000, etc.".ortho).to(eq("Il en compte 1 000, 10 000, 36 742, 500 000, puis 1 000 000, 25 000 000, etc."))
70
93
  # expect("Le numéro gagnant est le 3 541 672.".ortho).to(eq("Le numéro gagnant est le 3541672."))
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: orthotypo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arnaud Levy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-23 00:00:00.000000000 Z
11
+ date: 2024-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: htmlentities
@@ -140,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
140
  - !ruby/object:Gem::Version
141
141
  version: '0'
142
142
  requirements: []
143
- rubygems_version: 3.4.6
143
+ rubygems_version: 3.4.10
144
144
  signing_key:
145
145
  specification_version: 4
146
146
  summary: Pour un texte correctement typographié