Added Docs engine

This commit is contained in:
kisenka 2015-02-12 14:11:07 +03:00
parent e247d36399
commit c3082ea173
14 changed files with 443 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
_site
_includes/page.html
app
HelpTOC.json

14
Rakefile Normal file
View File

@ -0,0 +1,14 @@
CONFIG = {
:source_dir => __dir__,
:tmp_dir => "#{__dir__}/_tmp",
:build_destination => "_site",
:preview_host => "0.0.0.0",
:preview_port => 4000,
:default_env => 'dev'
}
Dir['_rake/*.rake'].each { |r| load r }
task :default do
system('rake -T')
end

10
_SUMMARY.md Normal file
View File

@ -0,0 +1,10 @@
<!--
Similar to GitBook SUMMARY.md - https://github.com/GitbookIO/gitbook#summarymd,
but list items without links will not br included in the table of contents.
Also you can use HTML-comments.
-->
## Getting started
* [Getting started with SDK](index.html)
* Some planned topic
<!-- * [Another planned topic](topic.html) -->

22
_config.yml Normal file
View File

@ -0,0 +1,22 @@
product_name: IntelliJ SDK
product_version:
markdown: kramdown
markdown_ext: foo
kramdown:
input: GFM2
hard_wrap: false
auto_ids: false
highlighter: pygments
exclude:
- '*.scss'
- README*
- Rakefile
- lib
- webhelp-template
- code_samples
defaults:
- values:
layout: 'webhelp'

5
_includes/head.html Normal file
View File

@ -0,0 +1,5 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}</title>
<link rel="stylesheet" href="styles/styles.css">

32
_layouts/webhelp.html Normal file
View File

@ -0,0 +1,32 @@
{% capture _t %}
{% assign product_name = site.product_name %}
{% assign product_version = site.product_version %}
{% assign page_title = page.title %}
{% assign page_title_with_h1 = '' %}
{% assign page_id = page.name | replace:'.md','' %}
{% assign page_content = content %}
{% capture shortcut_switcher %}
<div class="shortcuts-switcher"><label for="switch-shortcuts">Keymap:</label><select id="switch-shortcuts" class="select _shortcuts" height="1">
<option data-group="primary" value="default" selected>Default</option>
<option data-group="primary" value="default_for_gnome">GNOME</option>
<option data-group="primary" value="default_for_kde">KDE</option>
<option data-group="primary" value="default_for_xwin">XWindow</option>
<option data-group="primary" value="emacs">Emacs</option>
<option data-group="primary" value="jbuilder">JBuilder</option>
<option data-group="primary" value="visual_studio">Visual Studio</option>
<option data-group="primary" value="netbeans_6.5">NetBeans 6.5</option>
<option data-group="primary" value="eclipse">Eclipse</option>
<option data-group="secondary" value="mac_os_x_10.5_">OS X 10.5+</option>
<option data-group="secondary" value="mac_os_x">OS X</option>
<option data-group="secondary" value="eclipse_mac_os_x">OS X Eclipse</option></select>
</div>
{% endcapture %}
{% assign is_show_shortcut_switcher = 'false' %}
{% assign current_year = 'now' | date:'%Y' %}
{% assign last_modified = '' %}
{% assign disqus = '' %}
{% assign baseurl = '.' %}
{% endcapture %}
{% capture rendered_content %}{% include page.html %}{% endcapture %}
{{ rendered_content | replace:'</head>','<link rel="stylesheet" href="styles/styles.css"></head>' }}

View File

@ -0,0 +1,154 @@
require 'kramdown'
require 'pygments'
module Kramdown
module Converter
class Upsrc < Html
def convert_header(el, indent)
attr = el.attr.dup
el_id = generate_id(el.options[:raw_text])
if @options[:auto_ids] && !attr['id']
attr['id'] = el_id
end
@toc << [el.options[:level], attr['id'], el.children] if attr['id'] && in_toc?(el)
level = output_header_level(el.options[:level])
if level <= 3
anchor = Element.new(:a, nil, {'href' => '#' + el_id, 'class' => 'anchor-link'})
el.children.push(anchor)
end
anchor = format_as_block_html("a", {'name' => el_id, 'class' => 'elem-anchor'}, inner(Element.new(:a, nil), indent), indent)
header = format_as_block_html("h#{level}", attr, inner(el, indent), indent)
anchor + header
end
def convert_codeblock(el, indent)
attr = el.attr.dup
lang = self.extract_code_language(attr) || 'text'
highlight_lines = ''
if attr['class'] and attr['class'].scan(/\{[\d\-\,]+\}/).length > 0
lang_parts = attr['class'].split('{')
highlight_lines = "{#{lang_parts[1]}"
end
code = pygmentize(el.value, lang, highlight_lines)
code_attr = {}
code_attr['class'] = "code-block__wrapper"
code_attr['class'] += " code-block _highlighted lang_#{lang}" if lang
"<pre><code#{html_attributes(code_attr)}>#{code}</code></pre>\n"
end
# Extract the code block/span language from the attributes.
def extract_code_language(attr)
if attr['class']
class_attr = attr['class']
if class_attr.scan(/\{|\}/).length > 0
class_attr = class_attr.split('{')[0]
end
class_attr.scan(/\blanguage-(\w+)\b/).first.first
end
end
def convert_codespan(el, indent)
attr = el.attr.dup
lang = extract_code_language!(attr) || 'text'
code = pygmentize(el.value, lang)
attr['class'] = 'code'
attr['class'] += " highlight language-#{lang}" if lang
"<code#{html_attributes(attr)}>#{code}</code>"
end
def convert_blockquote(el, indent)
format_as_indented_block_html(el.type, el.attr, inner(el, indent), indent)
end
def convert_a(el, indent)
res = inner(el, indent)
attr = el.attr.dup
attr['href'] = '' if attr['href'].nil?
is_external = attr['href'].start_with?('http://', 'https://', 'ftp://', '//')
attr['data-bypass'] = 'yes' if is_external
if attr['href'].start_with?('mailto:')
mail_addr = attr['href'][7..-1]
attr['href'] = obfuscate('mailto') << ":" << obfuscate(mail_addr)
res = obfuscate(res) if res == mail_addr
end
format_as_span_html(el.type, attr, "<span>#{res}</span>")
end
def convert_img(el, indent)
"<img#{html_attributes(el.attr)} />"
end
def pygmentize(code, lang, highlight_lines = nil)
hl_lines = ''
if highlight_lines
hl_lines = highlight_lines.gsub(/[{}]/, '').split(',').map do |ln|
if matches = /(\d+)-(\d+)/.match(ln)
ln = Range.new(matches[1], matches[2]).to_a.join(' ')
end
ln
end.join(' ')
end
if lang
Pygments.highlight(code,
:lexer => lang,
:options => {
:encoding => 'utf-8',
:nowrap => true,
:hl_lines => hl_lines
}
)
else
escape_html(code)
end
end
end
end
module Parser
class GFM2 < GFM
def parse
super
end
FENCED_CODEBLOCK_MATCH = /^(([~`]){3,})\s*?(\w+[\{\}\,\d\-]*?)?\s*?\n(.*?)^\1\2*\s*?\n/m
end
end
end
module Jekyll
class KramdownPygments < Jekyll::Converter
def matches(ext)
ext =~ /^\.md$/i
end
def output_ext(ext)
".html"
end
def convert(content)
html = Kramdown::Document.new(content, {
:auto_ids => @config['kramdown']['auto_ids'],
:footnote_nr => @config['kramdown']['footnote_nr'],
:hard_wrap => @config['kramdown']['hard_wrap'],
:entity_output => @config['kramdown']['entity_output'],
:toc_levels => @config['kramdown']['toc_levels'],
:smart_quotes => @config['kramdown']['smart_quotes'],
:coderay_default_lang => @config['kramdown']['default_lang'],
:input => @config['kramdown']['input']
}).to_upsrc
return html
end
end
end

10
_rake/build.rake Normal file
View File

@ -0,0 +1,10 @@
desc 'Build docs'
task :build do
dest = ENV['dest'] || CONFIG[:build_destination]
Rake::Task['build_toc'].invoke
Rake::Task['prepare_assets'].invoke
command = "jekyll build --trace --destination=#{dest}"
sh command
end

13
_rake/build_toc.rake Normal file
View File

@ -0,0 +1,13 @@
desc 'Build docs table of contents index'
task :build_toc do
src_dir = CONFIG[:source_dir]
toc_file = ENV['dest'] || "#{src_dir}/HelpTOC.json"
load "#{src_dir}/lib/toc_generator.rb"
kramdown_config = YAML::load_file("#{src_dir}/_config.yml")['kramdown']
toc = Docs::TocGenerator.extract("#{src_dir}/_SUMMARY.md", kramdown_config)
res = File.write("#{toc_file}", toc.to_json)
puts "TOC successfully created in #{toc_file}" if res
end

22
_rake/prepare_assets.rake Normal file
View File

@ -0,0 +1,22 @@
desc 'Preparing assets'
task :prepare_assets do
dest = ENV["dest"] || CONFIG[:build_destination]
appsrc = 'webhelp-template/app'
appdest = 'app'
# webhelp template
RakeFileUtils.cp 'webhelp-template/app/templates/page.html', '_includes'
# assets dir
RakeFileUtils.mkdir_p %W(#{appdest}/css #{appdest}/fonts #{appdest}/img #{appdest}/js/vendor/requirejs)
# js
RakeFileUtils.cp_r "#{appsrc}/js/main.build.js", "#{appdest}/js"
RakeFileUtils.cp_r "#{appsrc}/js/vendor/requirejs/require.js", "#{appdest}/js/vendor/requirejs"
# css
RakeFileUtils.cp_r "#{appsrc}/css/styles.min.css", "#{appdest}/css"
# images & fonts
RakeFileUtils.cp_r %W(#{appsrc}/fonts #{appsrc}/img), appdest
end

11
_rake/preview.rake Normal file
View File

@ -0,0 +1,11 @@
desc "Runs site on a local preview webserver"
task :preview do
host = ENV["host"] || CONFIG[:preview_host]
port = ENV["port"] || CONFIG[:preview_port]
Rake::Task["build_toc"].invoke
Rake::Task['prepare_assets'].invoke
command = "jekyll serve --trace --host=#{host} --port=#{port} --watch --force_polling"
sh command
end

8
index.md Normal file
View File

@ -0,0 +1,8 @@
---
title: Getting started
---
# {{ page.title }}
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus doloremque enim explicabo fugiat modi, obcaecati
officia sunt unde. A assumenda dolorum hic, in ipsam nisi officia porro praesentium provident sequi.

113
lib/toc_generator.rb Normal file
View File

@ -0,0 +1,113 @@
require 'yaml'
require 'json'
require 'kramdown'
require 'rexml/document'
require 'rexml/xpath'
module Docs
class TocGenerator
ITEM_TYPE_HEADER = 'header'
# @param path {String} path to Markdown file
# @param kramdown_config {Hash} Kramdown config
# @return {Hash}
def self.extract(path, kramdown_config = {})
toc = []
content = File.read(path)
kramdown_config = kramdown_config.merge({:html_to_native => true})
kramdown_doc = Kramdown::Document.new(content, kramdown_config)
kramdown_doc_content = <<-XML
<?xml version="1.0" encoding="UTF-8" ?>
<root>
#{kramdown_doc.to_html}
</root>
XML
kramdown_doc_content = kramdown_doc_content.gsub(/<!--(.*?)-->/m, '')
xml = REXML::Document.new kramdown_doc_content
xml.elements.each("/root/*") do |node|
items = extract_from_node(node)
toc.concat(items) if items != nil
end
# Removing headers of empty sections
delete_list = []
(0).upto(toc.length-1) do |i|
item = toc[i]
prev = toc[i-1] != nil ? toc[i-1] : nil
item_is_header = (item[:type] != nil and item[:type] == self::ITEM_TYPE_HEADER)
prev_is_header = (prev != nil and prev[:type] != nil and prev[:type] == self::ITEM_TYPE_HEADER)
if item_is_header and prev_is_header
delete_list.push(i-1)
end
end
delete_list.each do |del_index|
toc.delete_at(del_index)
end
return toc
end
private
# @param node {REXML::Node}
# @return {Hash}
def self.extract_from_node(node)
items = []
case node.name
when 'ul'
REXML::XPath.each(node, './li') do |node|
item = extract_items(node)
items.push(item) if item != nil
end
when 'h2'
items.push(extract_header(node))
end
return items.length > 0 ? items : nil
end
# @param node {REXML::Node}
# @return {Hash}
def self.extract_header(header_node)
return {
:title => header_node.text(),
:type => self::ITEM_TYPE_HEADER
}
end
# @param node {REXML::Node}
# @return {Hash}
def self.extract_items(li_node)
item = nil
REXML::XPath.each(li_node, 'a') do |link|
href = link.attribute('href').to_s
id = File.basename(href, File.extname(href))
is_external = href.start_with?('http://', 'https://', 'ftp://', '//')
item = {
:id => id,
:title => link.text(),
:url => href
}
item[:is_external] = true if is_external
pages = []
REXML::XPath.each(li_node, 'ul/li') do |li|
pages.push(extract_items(li))
end
item['pages'] = pages unless pages.empty?
end
return item
end
end
end

25
lib/util/erb.rb Normal file
View File

@ -0,0 +1,25 @@
require 'ostruct'
require 'erb'
module Docs
module Util
class Erb
def self.render(path, data = {})
vars = ErbBinding.new(data)
erb_contents = File.read(path)
erb = ERB.new(erb_contents)
vars_binding = vars.send(:get_binding)
erb.result(vars_binding)
end
class ErbBinding < OpenStruct
def get_binding
binding
end
end
end
end
end