2017-11-27 05:45:24 -05:00
|
|
|
# frozen_string_literal: true
|
2012-11-26 23:28:14 -05:00
|
|
|
require 'json'
|
2015-03-23 01:15:15 -04:00
|
|
|
begin
|
|
|
|
require 'zlib'
|
|
|
|
rescue LoadError
|
|
|
|
end
|
2012-11-26 23:28:14 -05:00
|
|
|
|
|
|
|
##
|
|
|
|
# The JsonIndex generator is designed to complement an HTML generator and
|
|
|
|
# produces a JSON search index. This generator is derived from sdoc by
|
|
|
|
# Vladimir Kolesnikov and contains verbatim code written by him.
|
|
|
|
#
|
|
|
|
# This generator is designed to be used with a regular HTML generator:
|
|
|
|
#
|
|
|
|
# class RDoc::Generator::Darkfish
|
|
|
|
# def initialize options
|
|
|
|
# # ...
|
|
|
|
# @base_dir = Pathname.pwd.expand_path
|
|
|
|
#
|
|
|
|
# @json_index = RDoc::Generator::JsonIndex.new self, options
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# def generate
|
|
|
|
# # ...
|
|
|
|
# @json_index.generate
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# == Index Format
|
|
|
|
#
|
|
|
|
# The index is output as a JSON file assigned to the global variable
|
|
|
|
# +search_data+. The structure is:
|
|
|
|
#
|
|
|
|
# var search_data = {
|
|
|
|
# "index": {
|
|
|
|
# "searchIndex":
|
|
|
|
# ["a", "b", ...],
|
|
|
|
# "longSearchIndex":
|
|
|
|
# ["a", "a::b", ...],
|
|
|
|
# "info": [
|
|
|
|
# ["A", "A", "A.html", "", ""],
|
|
|
|
# ["B", "A::B", "A::B.html", "", ""],
|
|
|
|
# ...
|
|
|
|
# ]
|
|
|
|
# }
|
|
|
|
# }
|
|
|
|
#
|
|
|
|
# The same item is described across the +searchIndex+, +longSearchIndex+ and
|
|
|
|
# +info+ fields. The +searchIndex+ field contains the item's short name, the
|
|
|
|
# +longSearchIndex+ field contains the full_name (when appropriate) and the
|
|
|
|
# +info+ field contains the item's name, full_name, path, parameters and a
|
|
|
|
# snippet of the item's comment.
|
|
|
|
#
|
|
|
|
# == LICENSE
|
|
|
|
#
|
|
|
|
# Copyright (c) 2009 Vladimir Kolesnikov
|
|
|
|
#
|
|
|
|
# Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
# a copy of this software and associated documentation files (the
|
|
|
|
# "Software"), to deal in the Software without restriction, including
|
|
|
|
# without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
# permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
# the following conditions:
|
|
|
|
#
|
|
|
|
# The above copyright notice and this permission notice shall be
|
|
|
|
# included in all copies or substantial portions of the Software.
|
|
|
|
#
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
class RDoc::Generator::JsonIndex
|
|
|
|
|
|
|
|
include RDoc::Text
|
|
|
|
|
|
|
|
##
|
|
|
|
# Where the search index lives in the generated output
|
|
|
|
|
|
|
|
SEARCH_INDEX_FILE = File.join 'js', 'search_index.js'
|
|
|
|
|
|
|
|
attr_reader :index # :nodoc:
|
|
|
|
|
|
|
|
##
|
|
|
|
# Creates a new generator. +parent_generator+ is used to determine the
|
|
|
|
# class_dir and file_dir of links in the output index.
|
|
|
|
#
|
|
|
|
# +options+ are the same options passed to the parent generator.
|
|
|
|
|
|
|
|
def initialize parent_generator, options
|
|
|
|
@parent_generator = parent_generator
|
|
|
|
@store = parent_generator.store
|
|
|
|
@options = options
|
|
|
|
|
|
|
|
@template_dir = File.expand_path '../template/json_index', __FILE__
|
|
|
|
@base_dir = @parent_generator.base_dir
|
|
|
|
|
|
|
|
@classes = nil
|
|
|
|
@files = nil
|
|
|
|
@index = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Builds the JSON index as a Hash.
|
|
|
|
|
|
|
|
def build_index
|
|
|
|
reset @store.all_files.sort, @store.all_classes_and_modules.sort
|
|
|
|
|
|
|
|
index_classes
|
|
|
|
index_methods
|
|
|
|
index_pages
|
|
|
|
|
|
|
|
{ :index => @index }
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Output progress information if debugging is enabled
|
|
|
|
|
|
|
|
def debug_msg *msg
|
|
|
|
return unless $DEBUG_RDOC
|
|
|
|
$stderr.puts(*msg)
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Writes the JSON index to disk
|
|
|
|
|
|
|
|
def generate
|
|
|
|
debug_msg "Generating JSON index"
|
|
|
|
|
|
|
|
debug_msg " writing search index to %s" % SEARCH_INDEX_FILE
|
|
|
|
data = build_index
|
|
|
|
|
|
|
|
return if @options.dry_run
|
|
|
|
|
|
|
|
out_dir = @base_dir + @options.op_dir
|
|
|
|
index_file = out_dir + SEARCH_INDEX_FILE
|
|
|
|
|
|
|
|
FileUtils.mkdir_p index_file.dirname, :verbose => $DEBUG_RDOC
|
|
|
|
|
|
|
|
index_file.open 'w', 0644 do |io|
|
2016-09-07 18:23:38 -04:00
|
|
|
io.set_encoding Encoding::UTF_8
|
2012-11-26 23:28:14 -05:00
|
|
|
io.write 'var search_data = '
|
|
|
|
|
|
|
|
JSON.dump data, io, 0
|
|
|
|
end
|
2018-03-26 01:56:26 -04:00
|
|
|
unless ENV['SOURCE_DATE_EPOCH'].nil?
|
|
|
|
index_file.utime index_file.atime, Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).gmtime
|
|
|
|
end
|
2012-11-26 23:28:14 -05:00
|
|
|
|
|
|
|
Dir.chdir @template_dir do
|
|
|
|
Dir['**/*.js'].each do |source|
|
|
|
|
dest = File.join out_dir, source
|
|
|
|
|
2018-03-26 01:56:26 -04:00
|
|
|
FileUtils.install source, dest, :mode => 0644, :preserve => true, :verbose => $DEBUG_RDOC
|
2012-11-26 23:28:14 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-12-06 20:22:37 -05:00
|
|
|
##
|
|
|
|
# Compress the search_index.js file using gzip
|
|
|
|
|
|
|
|
def generate_gzipped
|
2017-11-27 05:45:24 -05:00
|
|
|
return if @options.dry_run or not defined?(Zlib)
|
2015-03-23 01:15:15 -04:00
|
|
|
|
2014-12-06 20:22:37 -05:00
|
|
|
debug_msg "Compressing generated JSON index"
|
|
|
|
out_dir = @base_dir + @options.op_dir
|
|
|
|
|
|
|
|
search_index_file = out_dir + SEARCH_INDEX_FILE
|
|
|
|
outfile = out_dir + "#{search_index_file}.gz"
|
|
|
|
|
|
|
|
debug_msg "Reading the JSON index file from %s" % search_index_file
|
2017-02-24 02:39:37 -05:00
|
|
|
search_index = search_index_file.read(mode: 'r:utf-8')
|
2014-12-06 20:22:37 -05:00
|
|
|
|
|
|
|
debug_msg "Writing gzipped search index to %s" % outfile
|
|
|
|
|
|
|
|
Zlib::GzipWriter.open(outfile) do |gz|
|
|
|
|
gz.mtime = File.mtime(search_index_file)
|
2015-12-22 07:08:13 -05:00
|
|
|
gz.orig_name = search_index_file.basename.to_s
|
2014-12-06 20:22:37 -05:00
|
|
|
gz.write search_index
|
|
|
|
gz.close
|
|
|
|
end
|
|
|
|
|
|
|
|
# GZip the rest of the js files
|
|
|
|
Dir.chdir @template_dir do
|
|
|
|
Dir['**/*.js'].each do |source|
|
|
|
|
dest = out_dir + source
|
|
|
|
outfile = out_dir + "#{dest}.gz"
|
|
|
|
|
|
|
|
debug_msg "Reading the original js file from %s" % dest
|
|
|
|
data = dest.read
|
|
|
|
|
|
|
|
debug_msg "Writing gzipped file to %s" % outfile
|
|
|
|
|
|
|
|
Zlib::GzipWriter.open(outfile) do |gz|
|
|
|
|
gz.mtime = File.mtime(dest)
|
2015-12-22 07:08:13 -05:00
|
|
|
gz.orig_name = dest.basename.to_s
|
2014-12-06 20:22:37 -05:00
|
|
|
gz.write data
|
|
|
|
gz.close
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-11-26 23:28:14 -05:00
|
|
|
##
|
|
|
|
# Adds classes and modules to the index
|
|
|
|
|
|
|
|
def index_classes
|
|
|
|
debug_msg " generating class search index"
|
|
|
|
|
|
|
|
documented = @classes.uniq.select do |klass|
|
|
|
|
klass.document_self_or_methods
|
|
|
|
end
|
|
|
|
|
|
|
|
documented.each do |klass|
|
|
|
|
debug_msg " #{klass.full_name}"
|
|
|
|
record = klass.search_record
|
|
|
|
@index[:searchIndex] << search_string(record.shift)
|
|
|
|
@index[:longSearchIndex] << search_string(record.shift)
|
|
|
|
@index[:info] << record
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Adds methods to the index
|
|
|
|
|
|
|
|
def index_methods
|
|
|
|
debug_msg " generating method search index"
|
|
|
|
|
|
|
|
list = @classes.uniq.map do |klass|
|
|
|
|
klass.method_list
|
|
|
|
end.flatten.sort_by do |method|
|
|
|
|
[method.name, method.parent.full_name]
|
|
|
|
end
|
|
|
|
|
|
|
|
list.each do |method|
|
|
|
|
debug_msg " #{method.full_name}"
|
|
|
|
record = method.search_record
|
|
|
|
@index[:searchIndex] << "#{search_string record.shift}()"
|
|
|
|
@index[:longSearchIndex] << "#{search_string record.shift}()"
|
|
|
|
@index[:info] << record
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Adds pages to the index
|
|
|
|
|
|
|
|
def index_pages
|
|
|
|
debug_msg " generating pages search index"
|
|
|
|
|
|
|
|
pages = @files.select do |file|
|
|
|
|
file.text?
|
|
|
|
end
|
|
|
|
|
|
|
|
pages.each do |page|
|
|
|
|
debug_msg " #{page.page_name}"
|
|
|
|
record = page.search_record
|
|
|
|
@index[:searchIndex] << search_string(record.shift)
|
|
|
|
@index[:longSearchIndex] << ''
|
|
|
|
record.shift
|
|
|
|
@index[:info] << record
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# The directory classes are written to
|
|
|
|
|
|
|
|
def class_dir
|
|
|
|
@parent_generator.class_dir
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# The directory files are written to
|
|
|
|
|
|
|
|
def file_dir
|
|
|
|
@parent_generator.file_dir
|
|
|
|
end
|
|
|
|
|
|
|
|
def reset files, classes # :nodoc:
|
|
|
|
@files = files
|
|
|
|
@classes = classes
|
|
|
|
|
|
|
|
@index = {
|
|
|
|
:searchIndex => [],
|
|
|
|
:longSearchIndex => [],
|
|
|
|
:info => []
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Removes whitespace and downcases +string+
|
|
|
|
|
|
|
|
def search_string string
|
|
|
|
string.downcase.gsub(/\s/, '')
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|