giblish 0.8.2 → 2.0.0.pre.alpha1
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/.github/workflows/unit_tests.yml +30 -0
- data/.gitignore +7 -3
- data/.ruby-version +1 -1
- data/Changelog.adoc +59 -0
- data/README.adoc +261 -0
- data/docs/concepts/text_search.adoc +213 -0
- data/docs/concepts/text_search_im/cgi-search_request.puml +35 -0
- data/docs/concepts/text_search_im/cgi-search_request.svg +397 -0
- data/docs/concepts/text_search_im/search_request.puml +40 -0
- data/docs/concepts/text_search_im/search_request.svg +408 -0
- data/docs/howtos/trigger_generation.adoc +180 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/Render Documents.png +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/View Documents.png +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_hooks.graphml +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_hooks.svg +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_jenkins.graphml +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_jenkins.svg +0 -0
- data/docs/howtos/trigger_generation_im/docgen_github.puml +51 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/giblish_deployment.graphml +0 -0
- data/docs/howtos/trigger_generation_im/post-receive-example.sh +50 -0
- data/docs/reference/box_flow_spec.adoc +22 -0
- data/docs/reference/search_spec.adoc +185 -0
- data/giblish.gemspec +54 -32
- data/lib/giblish/adocsrc_providers.rb +23 -0
- data/lib/giblish/application.rb +214 -41
- data/lib/giblish/cmdline.rb +273 -259
- data/lib/giblish/config_utils.rb +41 -0
- data/lib/giblish/configurator.rb +163 -0
- data/lib/giblish/conversion_info.rb +120 -0
- data/lib/giblish/docattr_providers.rb +125 -0
- data/lib/giblish/docid/docid.rb +181 -0
- data/lib/giblish/github_trigger/webhook_manager.rb +64 -0
- data/lib/giblish/gitrepos/checkoutmanager.rb +124 -0
- data/lib/giblish/{gititf.rb → gitrepos/gititf.rb} +30 -4
- data/lib/giblish/gitrepos/gitsummary.erb +61 -0
- data/lib/giblish/gitrepos/gitsummaryprovider.rb +78 -0
- data/lib/giblish/gitrepos/history_pb.rb +41 -0
- data/lib/giblish/indexbuilders/d3treegraph.rb +88 -0
- data/lib/giblish/indexbuilders/depgraphbuilder.rb +109 -0
- data/lib/giblish/indexbuilders/dotdigraphadoc.rb +174 -0
- data/lib/giblish/indexbuilders/standard_index.erb +10 -0
- data/lib/giblish/indexbuilders/subtree_indices.rb +132 -0
- data/lib/giblish/indexbuilders/templates/circles.html.erb +111 -0
- data/lib/giblish/indexbuilders/templates/flame.html.erb +61 -0
- data/lib/giblish/indexbuilders/templates/tree.html.erb +366 -0
- data/lib/giblish/indexbuilders/templates/treemap.html.erb +127 -0
- data/lib/giblish/indexbuilders/verbatimtree.rb +94 -0
- data/lib/giblish/pathtree.rb +473 -74
- data/lib/giblish/resourcepaths.rb +150 -0
- data/lib/giblish/search/expand_adoc.rb +55 -0
- data/lib/giblish/search/headingindexer.rb +312 -0
- data/lib/giblish/search/request_manager.rb +110 -0
- data/lib/giblish/search/searchquery.rb +68 -0
- data/lib/giblish/search/textsearcher.rb +349 -0
- data/lib/giblish/subtreeinfobuilder.rb +77 -0
- data/lib/giblish/treeconverter.rb +272 -0
- data/lib/giblish/utils.rb +142 -294
- data/lib/giblish/version.rb +1 -1
- data/lib/giblish.rb +10 -7
- data/scripts/hooks/post-receive.example +66 -0
- data/{docgen/scripts/githook_examples → scripts/hooks}/post-update.example +0 -0
- data/{docgen → scripts}/resources/css/adoc-colony.css +0 -0
- data/scripts/resources/css/giblish-serif.css +419 -0
- data/scripts/resources/css/giblish.css +1979 -419
- data/{docgen → scripts}/resources/fonts/Ubuntu-B.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/Ubuntu-BI.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/Ubuntu-R.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/Ubuntu-RI.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/mplus1p-regular-fallback.ttf +0 -0
- data/{docgen → scripts}/resources/images/giblish_logo.png +0 -0
- data/{docgen → scripts}/resources/images/giblish_logo.svg +0 -0
- data/{docgen → scripts}/resources/themes/giblish.yml +0 -0
- data/scripts/wserv_development.rb +32 -0
- data/web_apps/cgi_search/gibsearch.rb +43 -0
- data/web_apps/gh_webhook_trigger/config.ru +2 -0
- data/web_apps/gh_webhook_trigger/gh_webhook_trigger.rb +73 -0
- data/web_apps/gh_webhook_trigger/public/dummy.txt +3 -0
- data/web_apps/sinatra_search/config.ru +2 -0
- data/web_apps/sinatra_search/public/dummy.txt +3 -0
- data/web_apps/sinatra_search/sinatra_search.rb +34 -0
- data/web_apps/sinatra_search/tmp/restart.txt +0 -0
- metadata +188 -85
- data/.rubocop.yml +0 -7
- data/.travis.yml +0 -3
- data/Changelog +0 -16
- data/Gemfile +0 -4
- data/README.adoc +0 -1
- data/Rakefile +0 -41
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/data/testdocs/malformed/no_header.adoc +0 -5
- data/data/testdocs/toplevel.adoc +0 -19
- data/data/testdocs/wellformed/adorned_purpose.adoc +0 -17
- data/data/testdocs/wellformed/docidtest/docid_1.adoc +0 -24
- data/data/testdocs/wellformed/docidtest/docid_2.adoc +0 -8
- data/data/testdocs/wellformed/simple.adoc +0 -14
- data/data/testdocs/wellformed/source_highlighting/highlight_source.adoc +0 -38
- data/docgen/resources/css/giblish.css +0 -1979
- data/docgen/scripts/Jenkinsfile +0 -18
- data/docgen/scripts/gen_adoc_org.sh +0 -58
- data/docs/README.adoc +0 -387
- data/docs/setup_server.adoc +0 -202
- data/lib/giblish/buildgraph.rb +0 -216
- data/lib/giblish/buildindex.rb +0 -459
- data/lib/giblish/core.rb +0 -451
- data/lib/giblish/docconverter.rb +0 -308
- data/lib/giblish/docid.rb +0 -180
- data/lib/giblish/docinfo.rb +0 -75
- data/lib/giblish/indexheadings.rb +0 -251
- data/lib/giblish-search.cgi +0 -459
- data/scripts/hooks/post-receive +0 -57
- data/scripts/publish_html.sh +0 -99
@@ -1,251 +0,0 @@
|
|
1
|
-
require "json"
|
2
|
-
require "pathname"
|
3
|
-
require "asciidoctor"
|
4
|
-
require "asciidoctor/extensions"
|
5
|
-
require_relative "./utils"
|
6
|
-
|
7
|
-
# put the indexing in the giblish namespace
|
8
|
-
module Giblish
|
9
|
-
# This hook is called by Asciidoctor once for each document _before_
|
10
|
-
# Asciidoctor processes the adoc content.
|
11
|
-
#
|
12
|
-
# It indexes all headings found in all documents in the tree.
|
13
|
-
# The resulting index can be serialized to a JSON file
|
14
|
-
# with the following format:
|
15
|
-
#
|
16
|
-
# {
|
17
|
-
# file_infos : [{
|
18
|
-
# filepath : filepath_1,
|
19
|
-
# title : Title,
|
20
|
-
# sections : [{
|
21
|
-
# id : section_id_1,
|
22
|
-
# title : section_title_1,
|
23
|
-
# line_no : line_no
|
24
|
-
# },
|
25
|
-
# {
|
26
|
-
# id : section_id_1,
|
27
|
-
# title : section_title_1,
|
28
|
-
# line_no : line_no
|
29
|
-
# },
|
30
|
-
# ...
|
31
|
-
# ]
|
32
|
-
# },
|
33
|
-
# {
|
34
|
-
# filepath : filepath_1,
|
35
|
-
# ...
|
36
|
-
# }]
|
37
|
-
# }
|
38
|
-
class IndexHeadings < Asciidoctor::Extensions::Preprocessor
|
39
|
-
# Use a class-global heading_index dict since asciidoctor creates a new instance
|
40
|
-
# of this class for each processed file
|
41
|
-
@heading_index = { "file_infos" => [] }
|
42
|
-
|
43
|
-
# prio order:
|
44
|
-
# 1. values in this hash
|
45
|
-
# 2. values taken from the document
|
46
|
-
# 3. default values
|
47
|
-
@id_elements = {
|
48
|
-
# prefix: "_",
|
49
|
-
# separator: "_"
|
50
|
-
}
|
51
|
-
|
52
|
-
class << self
|
53
|
-
attr_accessor :id_elements
|
54
|
-
|
55
|
-
attr_reader :heading_index
|
56
|
-
|
57
|
-
def clear_index
|
58
|
-
@heading_index = { "file_infos" => [] }
|
59
|
-
end
|
60
|
-
|
61
|
-
# write the index to a file in dst_dir and remove the base_dir
|
62
|
-
# part of the path for each filename
|
63
|
-
def serialize(dst_dir, base_dir = "")
|
64
|
-
dst_dir = Pathname.new(dst_dir) unless dst_dir.respond_to?(:join)
|
65
|
-
base_dir = Pathname.new(base_dir) unless base_dir.respond_to?(:join)
|
66
|
-
|
67
|
-
if base_dir.to_s.empty?
|
68
|
-
heading_index
|
69
|
-
else
|
70
|
-
# remove the base_dir part of the file path
|
71
|
-
heading_index["file_infos"].each do |file_info|
|
72
|
-
file_info["filepath"] = Pathname.new(file_info["filepath"])
|
73
|
-
.relative_path_from(base_dir)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
Giblog.logger.info { "writing json to #{dst_dir.join('heading_index.json')}" }
|
78
|
-
File.open(dst_dir.join("heading_index.json").to_s, "w") do |f|
|
79
|
-
f.write(heading_index.to_json)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def process(document, reader)
|
85
|
-
# Add doc as a source dependency for doc ids
|
86
|
-
src_path = document.attributes["docfile"]
|
87
|
-
|
88
|
-
# Note: the nil check is there to prevent us adding generated
|
89
|
-
# asciidoc docs that does not exist in the file system (e.g. the
|
90
|
-
# generated index pages). This is a bit hackish and should maybe be
|
91
|
-
# done differently
|
92
|
-
return if src_path.nil?
|
93
|
-
|
94
|
-
# get the title from thw raw text (asciidoctor has not yet
|
95
|
-
# processed the text)
|
96
|
-
title = find_title reader.lines
|
97
|
-
|
98
|
-
# make sure we use the correct id elements when indexing
|
99
|
-
# sections
|
100
|
-
opts = find_id_attributes(reader.lines)
|
101
|
-
|
102
|
-
# Index all headings in the doc
|
103
|
-
Giblog.logger.debug { "indexing headings in #{src_path}" }
|
104
|
-
sections = []
|
105
|
-
file_info_hash = {
|
106
|
-
"filepath" => src_path,
|
107
|
-
"title" => title,
|
108
|
-
"sections" => sections
|
109
|
-
}
|
110
|
-
|
111
|
-
index_sections(reader, file_info_hash, opts)
|
112
|
-
|
113
|
-
heading_index["file_infos"] << file_info_hash
|
114
|
-
reader
|
115
|
-
end
|
116
|
-
|
117
|
-
private
|
118
|
-
|
119
|
-
# build the 'sections' array
|
120
|
-
def index_sections(reader, file_info_hash, opts)
|
121
|
-
sections = file_info_hash["sections"]
|
122
|
-
|
123
|
-
heading_regex = Regexp.new(/^=+\s+(.*)$/)
|
124
|
-
anchor_regex = Regexp.new(/^\[\[(\w+)\]\]\s*$/)
|
125
|
-
|
126
|
-
line_no = 0
|
127
|
-
m = nil
|
128
|
-
match_str = ""
|
129
|
-
state = :text
|
130
|
-
reader.lines.each do |line|
|
131
|
-
line_no += 1
|
132
|
-
# implement a state machine that supports both custom
|
133
|
-
# anchors for a heading and the default heading ids generated
|
134
|
-
# by asciidoctor
|
135
|
-
case state
|
136
|
-
when :text
|
137
|
-
m = heading_regex.match(line)
|
138
|
-
if m
|
139
|
-
state = :heading
|
140
|
-
match_str = m[1]
|
141
|
-
else
|
142
|
-
m = anchor_regex.match(line)
|
143
|
-
if m
|
144
|
-
state = :expecting_heading
|
145
|
-
match_str = m[1]
|
146
|
-
end
|
147
|
-
end
|
148
|
-
when :expecting_heading
|
149
|
-
m = heading_regex.match(line)
|
150
|
-
if m
|
151
|
-
# we have an anchor and got a heading as expected, index it
|
152
|
-
section = { "id" => match_str }
|
153
|
-
section["title"] = m[1].strip
|
154
|
-
section["line_no"] = line_no
|
155
|
-
sections << section
|
156
|
-
else
|
157
|
-
Giblog.logger.debug do
|
158
|
-
"Did not index the anchor: #{match_str} at "\
|
159
|
-
"line #{line_no}, probably not associated "\
|
160
|
-
"with a heading."
|
161
|
-
end
|
162
|
-
end
|
163
|
-
state = :text
|
164
|
-
when :heading
|
165
|
-
# we got a heading without an accompanying anchor, index it
|
166
|
-
section = { "id" => get_unique_id(file_info_hash, match_str, opts) }
|
167
|
-
section["title"] = m[1].strip
|
168
|
-
section["line_no"] = line_no
|
169
|
-
sections << section
|
170
|
-
state = :text
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def find_title(lines)
|
176
|
-
title = "No title Found!"
|
177
|
-
Giblish.process_header_lines(lines) do |line|
|
178
|
-
m = /^=+(.*)$/.match(line)
|
179
|
-
if m
|
180
|
-
# We found a decent title
|
181
|
-
title = m[1].strip
|
182
|
-
end
|
183
|
-
end
|
184
|
-
title
|
185
|
-
end
|
186
|
-
|
187
|
-
# id elements prio:
|
188
|
-
# 1. values in class variable
|
189
|
-
# 2. values taken from doc
|
190
|
-
# 3. default values
|
191
|
-
def find_id_attributes(lines)
|
192
|
-
result = {}
|
193
|
-
# prio 1
|
194
|
-
result[:id_prefix] = IndexHeadings.id_elements.fetch(:id_prefix, nil)
|
195
|
-
result[:id_separator] = IndexHeadings.id_elements.fetch(:id_separator, nil)
|
196
|
-
return result if result[:id_prefix] && result[:id_separator]
|
197
|
-
|
198
|
-
# prio 2
|
199
|
-
# check if the doc specifies id attributes
|
200
|
-
Giblish.process_header_lines(lines) do |line|
|
201
|
-
m = /^:idprefix:(.*)$/.match(line)
|
202
|
-
n = /^:idseparator:(.*)$/.match(line)
|
203
|
-
if m && !result[:id_prefix]
|
204
|
-
# We found a id prefix
|
205
|
-
result[:id_prefix] = m[1].strip
|
206
|
-
end
|
207
|
-
if n && !result[:id_separator]
|
208
|
-
# We found a id separator
|
209
|
-
result[:id_separator] = n[1].strip
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
# prio 3
|
214
|
-
# default values
|
215
|
-
result[:id_prefix] = "_" unless result[:id_prefix]
|
216
|
-
result[:id_separator] = "_" unless result[:id_separator]
|
217
|
-
result
|
218
|
-
end
|
219
|
-
|
220
|
-
def get_unique_id(doc_heading_dict, heading_str, opts)
|
221
|
-
id_base = Giblish.to_valid_id(heading_str, opts[:id_prefix], opts[:id_separator])
|
222
|
-
return id_base unless doc_heading_dict.key? id_base
|
223
|
-
|
224
|
-
# handle the case with several sections with the same name
|
225
|
-
idx = 1
|
226
|
-
heading_id = ""
|
227
|
-
loop do
|
228
|
-
idx += 1
|
229
|
-
heading_id = "#{id_base}_#{idx}"
|
230
|
-
# some code here
|
231
|
-
break unless doc_heading_dict.key? heading_id
|
232
|
-
end
|
233
|
-
heading_id
|
234
|
-
end
|
235
|
-
|
236
|
-
# Helper method to shorten calls to the heading_index from instance methods
|
237
|
-
def heading_index
|
238
|
-
self.class.heading_index
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
# Helper method to register the docid preprocessor extension with
|
243
|
-
# the asciidoctor engine.
|
244
|
-
def register_index_heading_extension
|
245
|
-
Asciidoctor::Extensions.register do
|
246
|
-
preprocessor IndexHeadings
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
module_function :register_index_heading_extension
|
251
|
-
end
|
data/lib/giblish-search.cgi
DELETED
@@ -1,459 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "pathname"
|
4
|
-
require "json"
|
5
|
-
require "asciidoctor"
|
6
|
-
require "open3"
|
7
|
-
require "cgi"
|
8
|
-
require "uri"
|
9
|
-
|
10
|
-
class GrepDocTree
|
11
|
-
Line_info = Struct.new(:line, :line_no) {
|
12
|
-
def initialize(line, line_no)
|
13
|
-
self.line = line
|
14
|
-
self.line_no = Integer(line_no)
|
15
|
-
end
|
16
|
-
}
|
17
|
-
|
18
|
-
# grep_opts:
|
19
|
-
# :search_top
|
20
|
-
# :search_phrase
|
21
|
-
# :ignorecase
|
22
|
-
# :useregexp
|
23
|
-
def initialize(grep_opts)
|
24
|
-
@grep_opts = "-nHr --include '*.adoc' "
|
25
|
-
@grep_opts += "-i " if grep_opts[:ignorecase]
|
26
|
-
@grep_opts += "-F " unless grep_opts[:useregexp]
|
27
|
-
|
28
|
-
@search_root = grep_opts[:searchassetstop]
|
29
|
-
@input = grep_opts[:search_phrase]
|
30
|
-
|
31
|
-
@output = ""
|
32
|
-
@error = ""
|
33
|
-
@status = 0
|
34
|
-
@match_index = {}
|
35
|
-
end
|
36
|
-
|
37
|
-
def grep
|
38
|
-
# This console code sequence will only show the matching word in bold ms=01:mc=:sl=:cx=:fn=:ln=:bn=:se=
|
39
|
-
grep_env = "GREP_COLORS=\"ms=01:mc=:sl=:cx=:fn=:ln=:bn=:se=\""
|
40
|
-
@grep_opts += " --color=always"
|
41
|
-
|
42
|
-
|
43
|
-
@output, @error, @status = Open3.capture3("#{grep_env} grep #{@grep_opts} \"#{@input}\" #{@search_root}")
|
44
|
-
|
45
|
-
begin
|
46
|
-
@output.force_encoding(Encoding::UTF_8)
|
47
|
-
@output.gsub!(/\x1b\[01m\x1b\[K/, "##")
|
48
|
-
@output.gsub!(/\x1b\[m\x1b\[K/, "##")
|
49
|
-
rescue StandardError => e
|
50
|
-
print e.message
|
51
|
-
print e.backtrace.inspect
|
52
|
-
exit 0
|
53
|
-
end
|
54
|
-
|
55
|
-
grep2hash @search_root
|
56
|
-
end
|
57
|
-
|
58
|
-
# returns an indexed output where each match from the search is associated with the
|
59
|
-
# corresponding src file's closest heading.
|
60
|
-
# the format of the output:
|
61
|
-
# {html_filename#heading : [line_1, line_2, ...], ...}
|
62
|
-
#
|
63
|
-
# The heading_db has the following JSON format
|
64
|
-
# {
|
65
|
-
# file_infos : [{
|
66
|
-
# filepath : filepath_1,
|
67
|
-
# title : Title,
|
68
|
-
# sections : [{
|
69
|
-
# id : section_id_1,
|
70
|
-
# title : section_title_1,
|
71
|
-
# line_no : line_no
|
72
|
-
# },
|
73
|
-
# {
|
74
|
-
# id : section_id_1,
|
75
|
-
# title : section_title_1,
|
76
|
-
# line_no : line_no
|
77
|
-
# },
|
78
|
-
# ...
|
79
|
-
# ]
|
80
|
-
# },
|
81
|
-
# {
|
82
|
-
# filepath : filepath_1,
|
83
|
-
# ...
|
84
|
-
# }]
|
85
|
-
# }
|
86
|
-
def match_with_headings heading_db
|
87
|
-
matches = []
|
88
|
-
|
89
|
-
# for each file with at least one match
|
90
|
-
@match_index.each do |file_path, match_infos|
|
91
|
-
# assume that max one file with the specified path
|
92
|
-
# exists
|
93
|
-
files = heading_db["file_infos"].select do |fi|
|
94
|
-
fi["filepath"] == file_path.to_s
|
95
|
-
end
|
96
|
-
next if files.empty?
|
97
|
-
|
98
|
-
file_anchors = construct_user_info files.first, match_infos
|
99
|
-
matches << file_anchors
|
100
|
-
end
|
101
|
-
matches
|
102
|
-
end
|
103
|
-
|
104
|
-
# Produce a hash with all info needed for the user to navigate to the
|
105
|
-
# matching html section for all matches to the file in the supplied file
|
106
|
-
# info hash.
|
107
|
-
#
|
108
|
-
# format of the resulting hash:
|
109
|
-
# {
|
110
|
-
# filepath : Filepath,
|
111
|
-
# title : Title,
|
112
|
-
# matches : {
|
113
|
-
# section_id :
|
114
|
-
# {
|
115
|
-
# section_title : Section Title,
|
116
|
-
# location : Location,
|
117
|
-
# lines : [line_1, line_2, ...]
|
118
|
-
# }
|
119
|
-
# }
|
120
|
-
# ]
|
121
|
-
# }
|
122
|
-
#
|
123
|
-
def construct_user_info file_info, match_infos
|
124
|
-
matches = {}
|
125
|
-
file_anchors = {
|
126
|
-
"filepath" => file_info["filepath"],
|
127
|
-
"title" => file_info["title"],
|
128
|
-
"matches" => matches
|
129
|
-
}
|
130
|
-
|
131
|
-
match_infos.each do |match_info|
|
132
|
-
match_line_nr = match_info.line_no
|
133
|
-
|
134
|
-
# find section with closest lower line_no to line_info
|
135
|
-
best_so_far = 0
|
136
|
-
chosen_section_info = {}
|
137
|
-
file_info["sections"].each do |section_info|
|
138
|
-
l = Integer(section_info["line_no"])
|
139
|
-
if l <= match_line_nr && l > best_so_far
|
140
|
-
chosen_section_info = section_info
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
matches[chosen_section_info["id"]] =
|
145
|
-
{
|
146
|
-
"section_title" => chosen_section_info["title"],
|
147
|
-
"location" => "#{Pathname.new(file_info["filepath"]).sub_ext(".html").to_s}##{chosen_section_info["id"]}",
|
148
|
-
"lines" => []
|
149
|
-
} unless matches.key?(chosen_section_info["id"])
|
150
|
-
matches[chosen_section_info["id"]]["lines"] << match_info.line
|
151
|
-
end
|
152
|
-
file_anchors
|
153
|
-
end
|
154
|
-
|
155
|
-
def formatted_output
|
156
|
-
# assume we have an updated index
|
157
|
-
adoc_str = ""
|
158
|
-
@match_index.each do |k, v|
|
159
|
-
adoc_str += "#{k}::\n"
|
160
|
-
v.each { |line_info|
|
161
|
-
adoc_str += "#{line_info.line_no} : #{line_info.line}\n"
|
162
|
-
}
|
163
|
-
end
|
164
|
-
adoc_str
|
165
|
-
end
|
166
|
-
|
167
|
-
private
|
168
|
-
|
169
|
-
# converts the 'raw' matches from grep into a hash.
|
170
|
-
# i.e. from:
|
171
|
-
# <filename>:<line_no>:<line>
|
172
|
-
# <filename>:<line_no>:<line>
|
173
|
-
# ...
|
174
|
-
#
|
175
|
-
# to
|
176
|
-
# {file_path : [line_info1, line_info2, ...], ...}
|
177
|
-
def grep2hash(base_dir)
|
178
|
-
@match_index = {}
|
179
|
-
@output.split("\n").each do |line|
|
180
|
-
tokens = line.split(":", 3)
|
181
|
-
|
182
|
-
# remove all lines starting with :<attrib>:
|
183
|
-
tokens[2].gsub!(/^:[[:graph:]]+:.*$/, "")
|
184
|
-
next if tokens[2].empty?
|
185
|
-
|
186
|
-
# remove everything above the repo root from the filepath
|
187
|
-
file_path = Pathname.new(tokens[0]).relative_path_from Pathname.new(base_dir)
|
188
|
-
@match_index[file_path] = [] unless @match_index.key? file_path
|
189
|
-
@match_index[file_path] << Line_info.new(tokens[2], tokens[1])
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
class SearchDocTree
|
195
|
-
def initialize(input_data)
|
196
|
-
@input_data = input_data
|
197
|
-
end
|
198
|
-
|
199
|
-
def search
|
200
|
-
# read the heading_db from file
|
201
|
-
jsonpath = @input_data[:searchassetstop].join("heading_index.json")
|
202
|
-
json = File.read(jsonpath.to_s)
|
203
|
-
src_index = JSON.parse(json)
|
204
|
-
|
205
|
-
# search the doc tree for regex
|
206
|
-
gt = GrepDocTree.new @input_data
|
207
|
-
gt.grep
|
208
|
-
|
209
|
-
matches = gt.match_with_headings src_index
|
210
|
-
|
211
|
-
format_search_adoc matches, get_uri_top
|
212
|
-
end
|
213
|
-
|
214
|
-
private
|
215
|
-
|
216
|
-
def get_uri_top
|
217
|
-
return @input_data[:referer][0, @input_data[:referer].rindex('/')]
|
218
|
-
end
|
219
|
-
|
220
|
-
def wash_line line
|
221
|
-
# remove any '::'
|
222
|
-
result = line.gsub(/::*/, "")
|
223
|
-
# remove =,| at the start of a line
|
224
|
-
result.gsub!(/^[=|]+/, "")
|
225
|
-
result
|
226
|
-
end
|
227
|
-
|
228
|
-
# index is an array of file_info, see construct_user_info
|
229
|
-
# for format per file
|
230
|
-
# == Title (filename)
|
231
|
-
#
|
232
|
-
# <<location,section_title>>::
|
233
|
-
# line_1
|
234
|
-
# line_2
|
235
|
-
# ...
|
236
|
-
def format_search_adoc index, uri_top
|
237
|
-
str = ""
|
238
|
-
# debug print referer...
|
239
|
-
# str << "uri_top: #{uri_top}\n"
|
240
|
-
index.each do |file_info|
|
241
|
-
filename = Pathname.new(file_info["filepath"]).basename
|
242
|
-
str << "== #{file_info["title"]}\n\n"
|
243
|
-
file_info["matches"].each do |section_id, info|
|
244
|
-
str << "#{uri_top}/#{info["location"]}[#{info["section_title"]}]::\n\n"
|
245
|
-
# str << "<<#{info["location"]},#{info["section_title"]}>>::\n\n"
|
246
|
-
str << "[subs=\"quotes\"]\n"
|
247
|
-
str << "----\n"
|
248
|
-
info["lines"].each do |line|
|
249
|
-
str << "-- #{wash_line(line)}\n"
|
250
|
-
end.join("\n\n")
|
251
|
-
str << "----\n"
|
252
|
-
end
|
253
|
-
str << "\n"
|
254
|
-
end
|
255
|
-
|
256
|
-
<<~ADOC
|
257
|
-
= Search Result
|
258
|
-
|
259
|
-
#{str}
|
260
|
-
ADOC
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
def init_web_server web_root
|
265
|
-
require 'webrick'
|
266
|
-
|
267
|
-
root = File.expand_path web_root
|
268
|
-
puts "Trying to start a WEBrick instance at port 8000 serving files from #{web_root}..."
|
269
|
-
|
270
|
-
server = WEBrick::HTTPServer.new(
|
271
|
-
:Port => 8000,
|
272
|
-
:DocumentRoot => root,
|
273
|
-
:Logger => WEBrick::Log.new("webrick.log", WEBrick::Log::DEBUG)
|
274
|
-
)
|
275
|
-
|
276
|
-
puts "WEBrick instance now listening to localhost:8000"
|
277
|
-
|
278
|
-
trap 'INT' do
|
279
|
-
server.shutdown
|
280
|
-
end
|
281
|
-
|
282
|
-
server.start
|
283
|
-
end
|
284
|
-
|
285
|
-
def hello_world
|
286
|
-
require "pp"
|
287
|
-
|
288
|
-
# init a new cgi 'connection'
|
289
|
-
cgi = CGI.new
|
290
|
-
print cgi.header
|
291
|
-
print "<br>"
|
292
|
-
print "Useful cgi parameters and variables."
|
293
|
-
print "<br>"
|
294
|
-
print cgi.public_methods(false).sort
|
295
|
-
print "<br>"
|
296
|
-
print "<br>"
|
297
|
-
print "referer: #{cgi.referer}<br>"
|
298
|
-
print "path: #{URI(cgi.referer).path}<br>"
|
299
|
-
print "host: #{cgi.host}<br>"
|
300
|
-
print "client_sent_topdir: #{cgi["topdir"]}<br>"
|
301
|
-
print "<br>"
|
302
|
-
print "client_sent_reldir: #{cgi["reltop"]}<br>"
|
303
|
-
print "<br>"
|
304
|
-
print "ENV: "
|
305
|
-
pp ENV
|
306
|
-
print "<br>"
|
307
|
-
end
|
308
|
-
|
309
|
-
|
310
|
-
# assume that the file tree looks like this when rendering
|
311
|
-
# a git branch:
|
312
|
-
#
|
313
|
-
# root_dir
|
314
|
-
# |- index.html (the generated index of rendered git branches and tags)
|
315
|
-
# |- branch_1_top_dir
|
316
|
-
# | |- index.html (the generated index of this branch)
|
317
|
-
# | |- file_1.html
|
318
|
-
# | |- dir_1
|
319
|
-
# | | |- file2.html
|
320
|
-
# |- branch_2_top_dir
|
321
|
-
# |- branch_x_...
|
322
|
-
# |- web_assets (only if a custom stylesheet is used...)
|
323
|
-
# |- search_assets
|
324
|
-
# | |- branch_1_top_dir
|
325
|
-
# | |- heading_index.json
|
326
|
-
# | |- file1.adoc
|
327
|
-
# | |- dir_1
|
328
|
-
# | | |- file2.html
|
329
|
-
# | |- ...
|
330
|
-
# | |- branch_2_top_dir
|
331
|
-
# | | ...
|
332
|
-
|
333
|
-
# assume that the file tree looks like this when not
|
334
|
-
# rendering a git branch:
|
335
|
-
#
|
336
|
-
# root_dir
|
337
|
-
# |- index.html (the generated index of all rendered files)
|
338
|
-
# |- file_1.html
|
339
|
-
# |- dir_1
|
340
|
-
# | |- file2.html
|
341
|
-
# |...
|
342
|
-
# |- web_assets (only if a custom stylesheet is used...)
|
343
|
-
# |- search_assets
|
344
|
-
# | |- heading_index.json
|
345
|
-
# | |- file1.adoc
|
346
|
-
# | |- dir_1
|
347
|
-
# | | |- file2.html
|
348
|
-
# | |- ...
|
349
|
-
|
350
|
-
def cgi_main(cgi, debug_mode = false)
|
351
|
-
# retrieve the form data supplied by user
|
352
|
-
input_data = {
|
353
|
-
search_phrase: cgi["searchphrase"],
|
354
|
-
ignorecase: cgi.has_key?("ignorecase"),
|
355
|
-
useregexp: cgi.has_key?("useregexp"),
|
356
|
-
searchassetstop: Pathname.new(
|
357
|
-
cgi.has_key?("searchassetstop") ? cgi["searchassetstop"] : ""),
|
358
|
-
webassetstop: Pathname.new(
|
359
|
-
cgi.has_key?("webassetstop") ? cgi["webassetstop"] : nil),
|
360
|
-
client_css:
|
361
|
-
cgi.has_key?("css") ? cgi["css"] : nil,
|
362
|
-
referer: cgi.referer
|
363
|
-
}
|
364
|
-
|
365
|
-
if input_data[:searchassetstop].nil? || !Dir.exist?(input_data[:searchassetstop])
|
366
|
-
raise ScriptError, "Could not find search_assets dir (#{input_data[:searchassetstop]}) !"
|
367
|
-
end
|
368
|
-
|
369
|
-
adoc_attributes = {
|
370
|
-
"data-uri" => 1,
|
371
|
-
}
|
372
|
-
|
373
|
-
# Set attributes so that the generated result page uses the same
|
374
|
-
# css as the other docs
|
375
|
-
if !input_data[:client_css].nil? && !input_data[:webassetstop].nil?
|
376
|
-
adoc_attributes.merge!(
|
377
|
-
{
|
378
|
-
"linkcss" => 1,
|
379
|
-
"stylesdir" => "#{input_data[:webassetstop]}/css",
|
380
|
-
"stylesheet" => input_data[:client_css],
|
381
|
-
"copycss!" => 1
|
382
|
-
}
|
383
|
-
)
|
384
|
-
end
|
385
|
-
|
386
|
-
converter_options = {
|
387
|
-
backend: "html5",
|
388
|
-
# need this to let asciidoctor include the default css if user
|
389
|
-
# has not specified any css
|
390
|
-
safe: Asciidoctor::SafeMode::SAFE,
|
391
|
-
header_footer: true,
|
392
|
-
attributes: adoc_attributes
|
393
|
-
}
|
394
|
-
|
395
|
-
|
396
|
-
# search the docs and render html
|
397
|
-
sdt = SearchDocTree.new(input_data)
|
398
|
-
docstr = sdt.search
|
399
|
-
|
400
|
-
if debug_mode
|
401
|
-
# print some useful data for debugging
|
402
|
-
docstr = <<~EOF
|
403
|
-
|
404
|
-
== Input data
|
405
|
-
|
406
|
-
#{input_data.to_s}
|
407
|
-
|
408
|
-
== Adoc attributes
|
409
|
-
|
410
|
-
#{adoc_attributes.to_s}
|
411
|
-
|
412
|
-
#{docstr}
|
413
|
-
EOF
|
414
|
-
end
|
415
|
-
|
416
|
-
# send the result back to the client
|
417
|
-
print Asciidoctor.convert(docstr, converter_options)
|
418
|
-
end
|
419
|
-
|
420
|
-
|
421
|
-
# Usage:
|
422
|
-
# to start a local web server for development work
|
423
|
-
# ruby giblish-search.cgi <web_root>
|
424
|
-
#
|
425
|
-
# to run as a cgi script via a previously setup web server
|
426
|
-
# giblish-search.cgi
|
427
|
-
#
|
428
|
-
# (note that you might need to rename the script to eg
|
429
|
-
# giblish-search.cgi or similar depending on your web server
|
430
|
-
# setup)
|
431
|
-
#
|
432
|
-
if __FILE__ == $PROGRAM_NAME
|
433
|
-
|
434
|
-
STDOUT.sync = true
|
435
|
-
if ARGV.length == 0
|
436
|
-
# 'Normal' cgi usage, as called from a web server
|
437
|
-
|
438
|
-
# init a new cgi 'connection' and print headers
|
439
|
-
cgi = CGI.new
|
440
|
-
print cgi.header
|
441
|
-
begin
|
442
|
-
cgi_main(cgi, false)
|
443
|
-
rescue Exception => e
|
444
|
-
print e.message
|
445
|
-
print ""
|
446
|
-
print e.backtrace
|
447
|
-
exit 1
|
448
|
-
end
|
449
|
-
exit 0
|
450
|
-
end
|
451
|
-
|
452
|
-
if ARGV.length == 1
|
453
|
-
# Run a simple web server to test this locally..
|
454
|
-
# and then create the html docs using:
|
455
|
-
# giblish -c -m -w <web_root> -r <resource_dir> -s <style_name> -g <git_branch> <src_root> <web_root>
|
456
|
-
init_web_server ARGV[0]
|
457
|
-
exit 0
|
458
|
-
end
|
459
|
-
end
|