uniword 1.6.0 → 1.7.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 +4 -4
- data/exe/uniword +9 -0
- data/lib/uniword/accessibility/accessibility_report.rb +8 -0
- data/lib/uniword/builder/chart_builder.rb +129 -134
- data/lib/uniword/cli/completions.rb +99 -0
- data/lib/uniword/cli/generate_cli.rb +5 -0
- data/lib/uniword/cli/help_switch.rb +45 -0
- data/lib/uniword/cli/helpers.rb +1 -0
- data/lib/uniword/cli/main.rb +118 -21
- data/lib/uniword/cli/no_color.rb +21 -0
- data/lib/uniword/diff/package_differ.rb +16 -15
- data/lib/uniword/document_factory.rb +2 -2
- data/lib/uniword/document_writer.rb +32 -10
- data/lib/uniword/docx/package_integrity_checker.rb +10 -11
- data/lib/uniword/docx/reconciler/body.rb +1 -0
- data/lib/uniword/docx/reconciler/helpers.rb +27 -12
- data/lib/uniword/docx/reconciler/parts.rb +5 -0
- data/lib/uniword/find_replace/paragraph_walker.rb +49 -12
- data/lib/uniword/find_replace/scope.rb +7 -4
- data/lib/uniword/infrastructure/mime_packager.rb +3 -10
- data/lib/uniword/infrastructure/mime_parser.rb +23 -1
- data/lib/uniword/math/plurimath_adapter.rb +28 -24
- data/lib/uniword/math_equation.rb +1 -1
- data/lib/uniword/mhtml/html_part.rb +19 -13
- data/lib/uniword/mhtml/math_converter.rb +18 -12
- data/lib/uniword/mhtml/mime_part.rb +5 -16
- data/lib/uniword/mhtml/xml_part.rb +3 -3
- data/lib/uniword/ooxml/additional_characteristics.rb +4 -4
- data/lib/uniword/ooxml/element_order.rb +21 -0
- data/lib/uniword/ooxml/schema/element_serializer.rb +27 -35
- data/lib/uniword/ooxml/schema_library.rb +2 -2
- data/lib/uniword/ooxml/types/characteristics_val.rb +16 -0
- data/lib/uniword/ooxml/types/schema_library_val.rb +17 -0
- data/lib/uniword/ooxml/types/variant_types.rb +10 -2
- data/lib/uniword/ooxml/types/vt_attr.rb +19 -0
- data/lib/uniword/ooxml/types/wml_int.rb +17 -0
- data/lib/uniword/ooxml/types/wml_val.rb +19 -0
- data/lib/uniword/ooxml/types.rb +12 -0
- data/lib/uniword/properties/alignment.rb +4 -0
- data/lib/uniword/properties/boolean_element_factory.rb +1 -1
- data/lib/uniword/properties/font_size.rb +3 -0
- data/lib/uniword/review/review_manager.rb +34 -5
- data/lib/uniword/review/revision_resolver.rb +190 -0
- data/lib/uniword/review.rb +1 -0
- data/lib/uniword/revision.rb +3 -3
- data/lib/uniword/serialization/ooxml_serializer.rb +3 -3
- data/lib/uniword/streaming_parser.rb +22 -20
- data/lib/uniword/theme/theme_xml_parser.rb +4 -6
- data/lib/uniword/transformation/html_element_builder.rb +62 -48
- data/lib/uniword/transformation/html_formatting_mapper.rb +3 -3
- data/lib/uniword/transformation/html_to_ooxml_converter.rb +11 -11
- data/lib/uniword/transformation/mhtml_element_renderer.rb +1 -2
- data/lib/uniword/validation/opc_validator.rb +6 -7
- data/lib/uniword/validation/rules/document_context.rb +3 -3
- data/lib/uniword/validation/rules/images_rule.rb +1 -1
- data/lib/uniword/validation/rules/theme_rule.rb +2 -2
- data/lib/uniword/validation/schema_registry.rb +10 -17
- data/lib/uniword/validation/validators/xml_schema_validator.rb +75 -26
- data/lib/uniword/validation/verify_orchestrator.rb +1 -1
- data/lib/uniword/version.rb +1 -1
- data/lib/uniword/wordprocessingml/deletion.rb +41 -0
- data/lib/uniword/wordprocessingml/insertion.rb +41 -0
- data/lib/uniword/wordprocessingml/page_margins.rb +7 -7
- data/lib/uniword/wordprocessingml/paragraph.rb +30 -4
- data/lib/uniword/wordprocessingml/settings.rb +3 -4
- data/lib/uniword/wordprocessingml/style_cleanup.rb +5 -14
- data/lib/uniword/wordprocessingml/table_borders.rb +1 -1
- data/lib/uniword/wordprocessingml/table_cell_borders.rb +1 -1
- data/lib/uniword/wordprocessingml/text.rb +0 -1
- data/lib/uniword/wordprocessingml.rb +5 -0
- data/lib/uniword/wp_drawing/position_h.rb +1 -1
- data/lib/uniword/wp_drawing/position_v.rb +1 -1
- data/lib/uniword/wp_drawing/size_rel_h.rb +1 -1
- data/lib/uniword/wp_drawing/size_rel_v.rb +1 -1
- data/lib/uniword.rb +9 -7
- metadata +19 -15
- data/lib/uniword/infrastructure/xml_namespace_normalizer.rb +0 -107
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 34ddbf784b5b85ba8aedc6b4df9b255763e2b8014bcdea0124563e261555b32b
|
|
4
|
+
data.tar.gz: e4ef3fce0529a6db2642cbf547c28628512834339ce636f15d90c30dccba7eb2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ae06c93c3b13ffffc70b0bddc7137945e4a035f2a4f75e315406547166c4423cdfe8ff8835b47ae5df50be9c6a021f500b89bd17512ae646cb3dd4c890bf8d8b
|
|
7
|
+
data.tar.gz: 7a37bcf5e4cc1ece3a92ad962757a48613be7f960890bd52017dcf8343bb1989172c7b9bd931a384dfe4bcee58c24de27b80b76e9dd4d3eca09280b078f8e3f1
|
data/exe/uniword
CHANGED
|
@@ -10,5 +10,14 @@ if defined?(RubyVM::YJIT) && !RubyVM::YJIT.enabled? &&
|
|
|
10
10
|
RubyVM::YJIT.enable
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
# POSIX NO_COLOR convention: suppress colored CLI output.
|
|
14
|
+
if ENV["NO_COLOR"] && !ENV["NO_COLOR"].empty?
|
|
15
|
+
require "uniword/cli/no_color"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Never let a task's return value become the process exit status:
|
|
19
|
+
# success is 0, and failures raise SystemExit inside `start`
|
|
20
|
+
# (exit_on_failure? / handle_error).
|
|
13
21
|
require "uniword"
|
|
14
22
|
Uniword::CLI.start(ARGV)
|
|
23
|
+
exit 0
|
|
@@ -46,6 +46,14 @@ module Uniword
|
|
|
46
46
|
errors.empty?
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
# Report-contract predicate shared with Quality::QualityReport:
|
|
50
|
+
# true when the document has no error-level violations.
|
|
51
|
+
#
|
|
52
|
+
# @return [Boolean]
|
|
53
|
+
def valid?
|
|
54
|
+
compliant?
|
|
55
|
+
end
|
|
56
|
+
|
|
49
57
|
# Get all error-level violations
|
|
50
58
|
#
|
|
51
59
|
# @return [Array<AccessibilityViolation>] Error violations
|
|
@@ -28,13 +28,16 @@ module Uniword
|
|
|
28
28
|
# end
|
|
29
29
|
class ChartBuilder
|
|
30
30
|
CHART_NS = "http://schemas.openxmlformats.org/drawingml/2006/chart"
|
|
31
|
+
DRAWINGML_MAIN_NS =
|
|
32
|
+
"http://schemas.openxmlformats.org/drawingml/2006/main"
|
|
33
|
+
OFFICE_DOCUMENT_RELS_NS =
|
|
34
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
|
31
35
|
CHART_REL_TYPE = Ooxml::PartRegistry.find_by_key(:chart).rel_type
|
|
32
36
|
CHART_CONTENT_TYPE =
|
|
33
37
|
Ooxml::PartRegistry.find_by_key(:chart).content_type
|
|
34
38
|
|
|
35
|
-
attr_reader :chart_type, :title_text, :series_list
|
|
36
|
-
|
|
37
|
-
attr_reader :show_legend, :width, :height, :series_list
|
|
39
|
+
attr_reader :chart_type, :title_text, :series_list, :show_legend, :width,
|
|
40
|
+
:height
|
|
38
41
|
|
|
39
42
|
def initialize(chart_type: :bar)
|
|
40
43
|
@chart_type = chart_type
|
|
@@ -101,16 +104,15 @@ module Uniword
|
|
|
101
104
|
#
|
|
102
105
|
# @return [String] Chart XML
|
|
103
106
|
def build_xml
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
builder.to_xml
|
|
107
|
+
@document = Moxml.new.create_document
|
|
108
|
+
@document.add_child(@document.create_declaration("1.0", "UTF-8"))
|
|
109
|
+
root = @document.create_element("c:chartSpace")
|
|
110
|
+
root.add_namespace("c", CHART_NS)
|
|
111
|
+
root.add_namespace("a", DRAWINGML_MAIN_NS)
|
|
112
|
+
root.add_namespace("r", OFFICE_DOCUMENT_RELS_NS)
|
|
113
|
+
@document.root = root
|
|
114
|
+
build_chart(root)
|
|
115
|
+
@document.to_xml
|
|
114
116
|
end
|
|
115
117
|
|
|
116
118
|
# Register chart on a document and create the Drawing element
|
|
@@ -147,166 +149,159 @@ module Uniword
|
|
|
147
149
|
include DeterministicId
|
|
148
150
|
|
|
149
151
|
# Build the inner chart XML
|
|
150
|
-
def
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
if @show_legend
|
|
181
|
-
xml["c"].legend do
|
|
182
|
-
xml["c"].legendPos("val" => @legend_position)
|
|
183
|
-
xml["c"].overlay("val" => "0")
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
xml["c"].plotVisOnly("val" => "1")
|
|
152
|
+
def build_chart(parent)
|
|
153
|
+
chart = add_element(parent, "c:chart")
|
|
154
|
+
if @title_text
|
|
155
|
+
title = add_element(chart, "c:title")
|
|
156
|
+
tx = add_element(title, "c:tx")
|
|
157
|
+
rich = add_element(tx, "c:rich")
|
|
158
|
+
add_element(rich, "a:bodyPr")
|
|
159
|
+
add_element(rich, "a:lstStyle")
|
|
160
|
+
p_el = add_element(rich, "a:p")
|
|
161
|
+
r_el = add_element(p_el, "a:r")
|
|
162
|
+
add_text(add_element(r_el, "a:t"), @title_text)
|
|
163
|
+
add_element(title, "c:overlay", "val" => "0")
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
add_element(chart, "c:autoTitleDeleted", "val" => "0")
|
|
167
|
+
|
|
168
|
+
plot_area = add_element(chart, "c:plotArea")
|
|
169
|
+
build_chart_type(plot_area)
|
|
170
|
+
|
|
171
|
+
unless @chart_type == :pie
|
|
172
|
+
build_axis(plot_area, "catAx", "10", "b", "20")
|
|
173
|
+
build_axis(plot_area, "valAx", "20", "l", "10")
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
if @show_legend
|
|
177
|
+
legend = add_element(chart, "c:legend")
|
|
178
|
+
add_element(legend, "c:legendPos", "val" => @legend_position)
|
|
179
|
+
add_element(legend, "c:overlay", "val" => "0")
|
|
188
180
|
end
|
|
181
|
+
|
|
182
|
+
add_element(chart, "c:plotVisOnly", "val" => "1")
|
|
183
|
+
chart
|
|
189
184
|
end
|
|
190
185
|
|
|
191
186
|
# Build the specific chart type element
|
|
192
|
-
def build_chart_type(
|
|
187
|
+
def build_chart_type(parent)
|
|
193
188
|
case @chart_type
|
|
194
189
|
when :bar
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
end
|
|
190
|
+
chart = add_element(parent, "c:barChart")
|
|
191
|
+
add_element(chart, "c:barDir", "val" => "col")
|
|
192
|
+
add_element(chart, "c:grouping", "val" => "clustered")
|
|
193
|
+
add_element(chart, "c:varyColors", "val" => "0")
|
|
194
|
+
build_all_series(chart)
|
|
195
|
+
add_element(chart, "c:axId", "val" => "10")
|
|
196
|
+
add_element(chart, "c:axId", "val" => "20")
|
|
203
197
|
when :line
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
end
|
|
198
|
+
chart = add_element(parent, "c:lineChart")
|
|
199
|
+
add_element(chart, "c:grouping", "val" => "standard")
|
|
200
|
+
add_element(chart, "c:varyColors", "val" => "0")
|
|
201
|
+
build_all_series(chart)
|
|
202
|
+
add_element(chart, "c:axId", "val" => "10")
|
|
203
|
+
add_element(chart, "c:axId", "val" => "20")
|
|
211
204
|
when :pie
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
end
|
|
205
|
+
chart = add_element(parent, "c:pieChart")
|
|
206
|
+
add_element(chart, "c:varyColors", "val" => "1")
|
|
207
|
+
build_all_series(chart)
|
|
216
208
|
end
|
|
217
209
|
end
|
|
218
210
|
|
|
219
211
|
# Build all series XML blocks
|
|
220
|
-
def build_all_series(
|
|
212
|
+
def build_all_series(parent)
|
|
221
213
|
@series_list.each_with_index do |s, i|
|
|
222
|
-
build_series(
|
|
214
|
+
build_series(parent, s, i)
|
|
223
215
|
end
|
|
224
216
|
end
|
|
225
217
|
|
|
226
218
|
# Build a single series XML block
|
|
227
|
-
def build_series(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
build_values(xml, series[:data])
|
|
242
|
-
end
|
|
219
|
+
def build_series(parent, series, index)
|
|
220
|
+
ser = add_element(parent, "c:ser")
|
|
221
|
+
add_element(ser, "c:idx", "val" => index)
|
|
222
|
+
add_element(ser, "c:order", "val" => index)
|
|
223
|
+
tx = add_element(ser, "c:tx")
|
|
224
|
+
str_ref = add_element(tx, "c:strRef")
|
|
225
|
+
add_text(add_element(str_ref, "c:f"), "Sheet1!$B$#{index + 1}")
|
|
226
|
+
str_cache = add_element(str_ref, "c:strCache")
|
|
227
|
+
add_element(str_cache, "c:ptCount", "val" => 1)
|
|
228
|
+
pt = add_element(str_cache, "c:pt", "idx" => 0)
|
|
229
|
+
add_text(add_element(pt, "c:v"), series[:name])
|
|
230
|
+
build_categories(ser)
|
|
231
|
+
build_values(ser, series[:data])
|
|
232
|
+
ser
|
|
243
233
|
end
|
|
244
234
|
|
|
245
235
|
# Build category data XML
|
|
246
|
-
def build_categories(
|
|
236
|
+
def build_categories(parent)
|
|
247
237
|
return if @categories.empty?
|
|
248
238
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
end
|
|
258
|
-
end
|
|
239
|
+
cat = add_element(parent, "c:cat")
|
|
240
|
+
str_ref = add_element(cat, "c:strRef")
|
|
241
|
+
add_text(add_element(str_ref, "c:f"), "Sheet1!$A$2:$A$100")
|
|
242
|
+
str_cache = add_element(str_ref, "c:strCache")
|
|
243
|
+
add_element(str_cache, "c:ptCount", "val" => @categories.size)
|
|
244
|
+
@categories.each_with_index do |category, idx|
|
|
245
|
+
pt = add_element(str_cache, "c:pt", "idx" => idx)
|
|
246
|
+
add_text(add_element(pt, "c:v"), category)
|
|
259
247
|
end
|
|
260
248
|
end
|
|
261
249
|
|
|
262
250
|
# Build values data XML
|
|
263
|
-
def build_values(
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
end
|
|
274
|
-
end
|
|
251
|
+
def build_values(parent, data)
|
|
252
|
+
val = add_element(parent, "c:val")
|
|
253
|
+
num_ref = add_element(val, "c:numRef")
|
|
254
|
+
add_text(add_element(num_ref, "c:f"), "Sheet1!$C$2:$C$100")
|
|
255
|
+
num_cache = add_element(num_ref, "c:numCache")
|
|
256
|
+
add_text(add_element(num_cache, "c:formatCode"), "General")
|
|
257
|
+
add_element(num_cache, "c:ptCount", "val" => data.size)
|
|
258
|
+
data.each_with_index do |value, idx|
|
|
259
|
+
pt = add_element(num_cache, "c:pt", "idx" => idx)
|
|
260
|
+
add_text(add_element(pt, "c:v"), value.to_s)
|
|
275
261
|
end
|
|
276
262
|
end
|
|
277
263
|
|
|
278
264
|
# Build a chart axis
|
|
279
|
-
def build_axis(
|
|
265
|
+
def build_axis(parent, tag, id, position, cross_id)
|
|
280
266
|
case tag
|
|
281
267
|
when "catAx"
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
end
|
|
268
|
+
write_axis_content(add_element(parent, "c:catAx"), id, position,
|
|
269
|
+
cross_id)
|
|
285
270
|
when "valAx"
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
end
|
|
271
|
+
write_axis_content(add_element(parent, "c:valAx"), id, position,
|
|
272
|
+
cross_id)
|
|
289
273
|
when "serAx"
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
end
|
|
274
|
+
write_axis_content(add_element(parent, "c:serAx"), id, position,
|
|
275
|
+
cross_id)
|
|
293
276
|
else
|
|
294
277
|
raise ArgumentError, "Unknown axis type: #{tag}"
|
|
295
278
|
end
|
|
296
279
|
end
|
|
297
280
|
|
|
298
|
-
def write_axis_content(
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
281
|
+
def write_axis_content(axis, id, position, cross_id)
|
|
282
|
+
add_element(axis, "c:axId", "val" => id)
|
|
283
|
+
scaling = add_element(axis, "c:scaling")
|
|
284
|
+
add_element(scaling, "c:orientation", "val" => "minMax")
|
|
285
|
+
add_element(axis, "c:delete", "val" => "0")
|
|
286
|
+
add_element(axis, "c:axPos", "val" => position)
|
|
287
|
+
add_element(axis, "c:majorGridlines")
|
|
288
|
+
add_element(axis, "c:numFmt", "formatCode" => "General",
|
|
289
|
+
"sourceLinked" => "1")
|
|
290
|
+
add_element(axis, "c:tickLblPos", "val" => "nextTo")
|
|
291
|
+
add_element(axis, "c:crossAx", "val" => cross_id)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Append an element with attributes to a parent node.
|
|
295
|
+
def add_element(parent, name, attributes = {})
|
|
296
|
+
element = @document.create_element(name)
|
|
297
|
+
attributes.each { |key, value| element[key] = value }
|
|
298
|
+
parent.add_child(element)
|
|
299
|
+
element
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Append a text node to a parent node.
|
|
303
|
+
def add_text(parent, content)
|
|
304
|
+
parent.add_child(@document.create_text(content))
|
|
310
305
|
end
|
|
311
306
|
|
|
312
307
|
# Create a Drawing element with chart reference
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Uniword
|
|
4
|
+
module Cli
|
|
5
|
+
# Builds shell completion scripts from live CLI metadata so new
|
|
6
|
+
# commands appear in completions without editing the scripts.
|
|
7
|
+
module Completions
|
|
8
|
+
BUILTINS = %w[help completions].freeze
|
|
9
|
+
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
# @param shell [String] "bash", "zsh", or "fish"
|
|
13
|
+
# @param root [Class] the root Thor class (Uniword::CLI)
|
|
14
|
+
# @return [String] completion script
|
|
15
|
+
def script(shell, root:)
|
|
16
|
+
commands = root.all_commands.keys - BUILTINS
|
|
17
|
+
subcommands = subcommand_map(root)
|
|
18
|
+
|
|
19
|
+
case shell
|
|
20
|
+
when "bash" then bash(commands, subcommands)
|
|
21
|
+
when "zsh" then zsh(commands, subcommands)
|
|
22
|
+
when "fish" then fish(commands, subcommands)
|
|
23
|
+
else
|
|
24
|
+
raise ArgumentError,
|
|
25
|
+
"unsupported shell: #{shell.inspect} (bash|zsh|fish)"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Map command name → registered subcommand Thor class, derived
|
|
30
|
+
# from class names (ThemeCLI → "theme"), so `subcommand` call
|
|
31
|
+
# sites stay the single source of truth.
|
|
32
|
+
# Space-separated subcommand word list for one class.
|
|
33
|
+
def words_for(klass)
|
|
34
|
+
klass.all_commands.keys.reject { |c| c == "help" }.join(" ")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def subcommand_map(root)
|
|
38
|
+
root.subcommand_classes.filter_map do |name, klass|
|
|
39
|
+
[name, klass]
|
|
40
|
+
end.to_h
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def bash(commands, subcommands)
|
|
44
|
+
cmds = commands.join(" ")
|
|
45
|
+
script = <<~BASH
|
|
46
|
+
# uniword bash completion — generated by `uniword completions bash`
|
|
47
|
+
_uniword() {
|
|
48
|
+
local cur="${COMP_WORDS[COMP_CWORD]}"
|
|
49
|
+
local prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
50
|
+
local cmds="#{cmds}"
|
|
51
|
+
BASH
|
|
52
|
+
subcommands.each do |name, klass|
|
|
53
|
+
words = words_for(klass)
|
|
54
|
+
script << " if [ \"$prev\" = \"#{name}\" ]; then\n " \
|
|
55
|
+
"COMPREPLY=( $(compgen -W \"#{words}\" " \
|
|
56
|
+
"-- \"$cur\") )\n return\n fi\n"
|
|
57
|
+
end
|
|
58
|
+
script << " COMPREPLY=( $(compgen -W \"$cmds\" -- \"$cur\") )\n" \
|
|
59
|
+
"}\n" \
|
|
60
|
+
"complete -F _uniword uniword\n"
|
|
61
|
+
script
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def zsh(commands, subcommands)
|
|
65
|
+
script = <<~ZSH
|
|
66
|
+
#compdef uniword
|
|
67
|
+
# uniword zsh completion — generated by `uniword completions zsh`
|
|
68
|
+
_uniword() {
|
|
69
|
+
local -a cmds
|
|
70
|
+
cmds=(#{commands.map { |c| "'#{c}'" }.join(' ')})
|
|
71
|
+
_describe 'command' cmds
|
|
72
|
+
ZSH
|
|
73
|
+
subcommands.each do |name, klass|
|
|
74
|
+
words = words_for(klass).split.map { |c| "'#{c}'" }.join(" ")
|
|
75
|
+
script << " if (( CURRENT == 2 )) && [[ #{name} == * ]]; " \
|
|
76
|
+
"then\n local -a sub\n sub=(#{words})\n " \
|
|
77
|
+
"_describe '#{name} command' sub\n fi\n"
|
|
78
|
+
end
|
|
79
|
+
script << "}\n"
|
|
80
|
+
script
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def fish(commands, subcommands)
|
|
84
|
+
script = +<<~FISH
|
|
85
|
+
# uniword fish completion — generated by `uniword completions fish`
|
|
86
|
+
FISH
|
|
87
|
+
commands.each do |c|
|
|
88
|
+
script << "complete -c uniword -n '__fish_use_subcommand' -a '#{c}'\n"
|
|
89
|
+
end
|
|
90
|
+
subcommands.each do |name, klass|
|
|
91
|
+
words = words_for(klass)
|
|
92
|
+
script << "complete -c uniword -n '__fish_seen_subcommand_from " \
|
|
93
|
+
"#{name}' -a '#{words}'\n"
|
|
94
|
+
end
|
|
95
|
+
script
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -10,6 +10,11 @@ module Uniword
|
|
|
10
10
|
class GenerateCLI < Thor
|
|
11
11
|
include CLIHelpers
|
|
12
12
|
|
|
13
|
+
# `uniword generate INPUT OUTPUT ...` dispatches straight to the
|
|
14
|
+
# generate task; the explicit `generate generate ...` form stays
|
|
15
|
+
# valid for backwards compatibility.
|
|
16
|
+
default_task :generate
|
|
17
|
+
|
|
13
18
|
desc "generate INPUT OUTPUT",
|
|
14
19
|
"Generate DOCX from structured text"
|
|
15
20
|
long_desc <<~DESC
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Uniword
|
|
4
|
+
module Cli
|
|
5
|
+
# Rewrites help/version flag invocations before Thor dispatch so
|
|
6
|
+
# help works on tasks with required arguments:
|
|
7
|
+
#
|
|
8
|
+
# uniword convert --help → uniword help convert
|
|
9
|
+
# uniword images add --help → uniword help images add
|
|
10
|
+
# uniword --help / -h → root help
|
|
11
|
+
# uniword --version / -V → uniword version
|
|
12
|
+
#
|
|
13
|
+
# The rewrite only fires when the help/version flag is the first
|
|
14
|
+
# flag-like token and everything before it looks like a task path
|
|
15
|
+
# (no options), so real option values (e.g. `--from docx`) are
|
|
16
|
+
# never mistaken for the chain.
|
|
17
|
+
module HelpSwitch
|
|
18
|
+
HELP_FLAGS = %w[--help -h].freeze
|
|
19
|
+
VERSION_FLAGS = %w[--version -V].freeze
|
|
20
|
+
|
|
21
|
+
def start(given_args = ARGV, config = {})
|
|
22
|
+
args = given_args.map(&:to_s)
|
|
23
|
+
|
|
24
|
+
if (help_idx = args.index { |a| HELP_FLAGS.include?(a) })
|
|
25
|
+
chain = args[0...help_idx]
|
|
26
|
+
if option_free_task_path?(chain)
|
|
27
|
+
return super(chain.unshift("help"), config)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if args.size == 1 && VERSION_FLAGS.include?(args[0])
|
|
32
|
+
return super(["version"], config)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
super
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def option_free_task_path?(chain)
|
|
41
|
+
chain.all? { |a| !a.start_with?("-") }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|