gerrit/Documentation/replace_macros.py
David Pursehouse 05af76fa4b Merge branch 'stable-2.15'
* stable-2.15:
  ChangeIT: Temporarily disable tests related to group matching
  Align SSH create-project with REST API
  SubmoduleOp: Fix formatting of submodule update commit message
  Fix Postgresql JDBC driver leaking memory
  Document that the build works with Python 2 or 3
  merge_jars.py: Fix for python 3 compatibility
  project.py: decode byte output from check_result
  license and doc: Add support for python3
  Bazel: Make build tool chain python 3 compatible
  ExternalIds NoteDb migration: Avoid intermediate migration state
  Make PrivateStateChanged and WorkInProgressStateChanged singletons
  BatchProgramModule: Bind GitReferenceUpdated.DISABLED
  BatchProgramModule: Don't bind event classes to null
  Document that Python 2 is required by the build
  Make event firing classes singleton
  Fix ConcurrentModificationException when posting reviews
  Revert partially "Trim multi-line arguments for task name and ssh_log"
  BaseCommand: Fix formatting of task description with arguments
  Add analytics to the list of plugins
  config-plugins: Consistently use "/+doc/" for documentation links
  config-plugins: Fix link to ref-protection plugin documentation
  config-plugins: Add readonly plugin to plugin list
  Add documentation for SshCreateCommandInterceptor
  Allow plugins to intercept ssh command creation
  GWT/Poly: Default to "Create initial commit" during project creation
  Revert "Reduce chance of deadlock in account cache"
  commit-msg: Adapt to awk behavior change on Cygwin/MSYS
  Expand docs on how to include external dependencies in core plugins
  Document how to bundle custom plugins in release.war
  Group members can't be added as reviewers if a matched username exists
  Set version to 2.13.11
  StreamEventsApiListener: Prevent NPE when account is null
  Set version to 2.11.11
  Replace links to code.google.com/p/gerrit
  Update issue tracker URL in documentation
  Update issue tracker URL in POM files
  Bump jsch to 0.54
  Update jsch to 0.1.53
  Bump Jsch to 1.52

Change-Id: Icec3dcfe53146da7d66d4d690afdd9e77ee10380
2018-03-12 09:03:47 +09:00

283 lines
8.3 KiB
Python
Executable File

#!/usr/bin/env python2
# coding=utf-8
# Copyright (C) 2013 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from optparse import OptionParser
import re
import sys
PAT_GERRIT = re.compile(r'^GERRIT')
PAT_INCLUDE = re.compile(r'^(include::.*)(\[\])$')
PAT_GET = re.compile(r'^get::([^ \t\n]*)')
PAT_TITLE = re.compile(r'^\.(.*)')
PAT_STARS = re.compile(r'^\*\*\*\*')
PAT_SEARCHBOX = re.compile(r'^SEARCHBOX')
GERRIT_UPLINK = """
++++
<hr style=\"
height: 2px;
color: silver;
margin-top: 1.2em;
margin-bottom: 0.5em;
\">
++++
"""
GET_TITLE = '<div class="title">%s</div>'
GET_MACRO = """
++++
<div class="listingblock">
%s
<div class="content">
<a id=\"{0}\" onmousedown="javascript:
var i = document.URL.lastIndexOf(\'/Documentation/\');
var url = document.URL.substring(0, i) + \'{0}\';
document.getElementById(\'{0}\').href = url;">
GET {0} HTTP/1.0
</a>
</div>
</div>
++++
"""
SEARCH_BOX = """
++++
<div style="
position:fixed;
top:0px;
right:0px;
text-align:
right;
padding-top:2px;
padding-right:0.5em;
padding-bottom:2px;">
<input size="40"
style="line-height: 0.75em;font-size: 0.75em;"
id="docSearch"
type="text">
<button style="
background:none!important;
border:none;
padding:0!important;
vertical-align:bottom;
font-family:'Open Sans','DejaVu Sans',sans-serif;
font-size:0.8em;
color:#1d4b8f;
text-decoration:none;"
type="button"
id="searchBox">
Search
</button>
<script type="text/javascript">
var f = function() {
window.location = '../#/Documentation/' +
encodeURIComponent(document.getElementById("docSearch").value);
}
document.getElementById("searchBox").onclick = f;
document.getElementById("docSearch").onkeypress = function(e) {
if (13 == (e.keyCode ? e.keyCode : e.which)) {
f();
}
}
</script>
</div>
++++
"""
LINK_SCRIPT = """
++++
<script type="text/javascript">
decorate(document.getElementsByTagName('h1'));
decorate(document.getElementsByTagName('h2'));
decorate(document.getElementsByTagName('h3'));
decorate(document.getElementsByTagName('h4'));
var divs = document.getElementsByTagName('div');
var arr = new Array();
var excluded = getExcludedIds();
for(var i = 0; i < divs.length; i++) {
var d = divs[i];
var id = d.getAttribute('id');
if (id != null && !(id in excluded)) {
arr[arr.length] = d;
}
}
decorate(arr);
var anchors = document.getElementsByTagName('a');
arr = new Array();
for(var i = 0; i < anchors.length; i++) {
var a = anchors[i];
// if the anchor has no id there is no target to
// which we can link
if (a.getAttribute('id') != null) {
// if the anchor is empty there is no content which
// can receive the mouseover event, an empty anchor
// applies to the element that follows, move the
// element that follows into the anchor so that there
// is content which can receive the mouseover event
if (a.firstChild == null) {
var next = a.nextSibling;
if (next != null) {
next.parentNode.removeChild(next);
a.appendChild(next);
}
}
arr[arr.length] = a;
}
}
decorate(arr);
function decorate(e) {
for(var i = 0; i < e.length; i++) {
e[i].onmouseover = function (evt) {
var element = this;
// do nothing if the link icon is currently showing
var a = element.firstChild;
if (a != null && a instanceof Element
&& a.getAttribute('id') == 'LINK') {
return;
}
// if there is no id there is no target to link to
var id = element.getAttribute('id');
if (id == null) {
return;
}
// create and show a link icon that links to this element
a = document.createElement('a');
a.setAttribute('id', 'LINK');
a.setAttribute('href', '#' + id);
a.setAttribute('style', 'position: absolute;'
+ ' left: ' + (element.offsetLeft - 16 - 2 * 4) + 'px;'
+ ' padding-left: 4px; padding-right: 4px;');
var span = document.createElement('span');
span.setAttribute('style', 'height: ' + element.offsetHeight + 'px;'
+ ' display: inline-block; vertical-align: baseline;'
+ ' font-size: 16px; text-decoration: none; color: grey;');
a.appendChild(span);
var link = document.createTextNode('🔗');
span.appendChild(link);
element.insertBefore(a, element.firstChild);
// remove the link icon when the mouse is moved away,
// but keep it shown if the mouse is over the element, the link or the icon
hide = function(evt) {
if (document.elementFromPoint(evt.clientX, evt.clientY) != element
&& document.elementFromPoint(evt.clientX, evt.clientY) != a
&& document.elementFromPoint(evt.clientX, evt.clientY) != span
&& document.elementFromPoint(evt.clientX, evt.clientY) != link
&& element.contains(a)) {
element.removeChild(a);
}
}
element.onmouseout = hide;
a.onmouseout = hide;
span.onmouseout = hide;
link.onmouseout = hide;
}
}
}
function getExcludedIds() {
var excluded = {};
excluded['header'] = true;
excluded['toc'] = true;
excluded['toctitle'] = true;
excluded['content'] = true;
excluded['preamble'] = true;
excluded['footer'] = true;
excluded['footer-text'] = true;
return excluded;
}
</script>
++++
"""
opts = OptionParser()
opts.add_option('-o', '--out', help='output file')
opts.add_option('-s', '--src', help='source file')
opts.add_option('-x', '--suffix', help='suffix for included filenames')
opts.add_option('-b', '--searchbox', action="store_true", default=True,
help="generate the search boxes")
opts.add_option('--no-searchbox', action="store_false", dest='searchbox',
help="don't generate the search boxes")
options, _ = opts.parse_args()
try:
try:
out_file = open(options.out, 'w', errors='ignore')
src_file = open(options.src, 'r', errors='ignore')
except TypeError:
out_file = open(options.out, 'w')
src_file = open(options.src, 'r')
last_line = ''
ignore_next_line = False
last_title = ''
for line in src_file:
if PAT_GERRIT.match(last_line):
# Case of "GERRIT\n------" at the footer
out_file.write(GERRIT_UPLINK)
last_line = ''
elif PAT_SEARCHBOX.match(last_line):
# Case of 'SEARCHBOX\n---------'
if options.searchbox:
out_file.write(SEARCH_BOX)
last_line = ''
elif PAT_INCLUDE.match(line):
# Case of 'include::<filename>'
match = PAT_INCLUDE.match(line)
out_file.write(last_line)
last_line = match.group(1) + options.suffix + match.group(2) + '\n'
elif PAT_STARS.match(line):
if PAT_TITLE.match(last_line):
# Case of the title in '.<title>\n****\nget::<url>\n****'
match = PAT_TITLE.match(last_line)
last_title = GET_TITLE % match.group(1)
else:
out_file.write(last_line)
last_title = ''
elif PAT_GET.match(line):
# Case of '****\nget::<url>\n****' in rest api
url = PAT_GET.match(line).group(1)
out_file.write(GET_MACRO.format(url) % last_title)
ignore_next_line = True
elif ignore_next_line:
# Handle the trailing '****' of the 'get::' case
last_line = ''
ignore_next_line = False
else:
out_file.write(last_line)
last_line = line
out_file.write(last_line)
out_file.write(LINK_SCRIPT)
out_file.close()
except IOError as err:
sys.stderr.write(
"error while expanding %s to %s: %s" % (options.src, options.out, err))
exit(1)