Signed-off-by: Rob Hoes <[email protected]>
ocaml/doc/OMakefile | 4 + ocaml/doc/apidoc.html | 3 + ocaml/doc/apidoc.js | 137 ++++++++++++++++++++++++++++++++++++++++++++++--- ocaml/doc/jsapi.ml | 63 +++++++++++++++++++++- ocaml/doc/style.css | 13 +++- 5 files changed, 203 insertions(+), 17 deletions(-)
# HG changeset patch # User Rob Hoes <[email protected]> # Date 1274793987 -3600 # Node ID fbf3b7184a3fd1adabb500c2261659dc8a1bb9ee # Parent 277057c7e27ade920540db458a0c1b2c07eab17d Add lifecycle info and release notes to HTML API docs Signed-off-by: Rob Hoes <[email protected]> diff -r 277057c7e27a -r fbf3b7184a3f ocaml/doc/OMakefile --- a/ocaml/doc/OMakefile +++ b/ocaml/doc/OMakefile @@ -4,6 +4,10 @@ OCAML_LIBS += ../idl/datamodel OCAMLINCLUDES += ../idl + +CAMLP4_FILES = jsapi +UseCamlp4(rpc-light.syntax, $(CAMLP4_FILES)) + OCamlProgram(jsapi, jsapi) .PHONY: doc diff -r 277057c7e27a -r fbf3b7184a3f ocaml/doc/apidoc.html --- a/ocaml/doc/apidoc.html +++ b/ocaml/doc/apidoc.html @@ -6,10 +6,13 @@ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <script type="text/javascript" src="main.js"></script> <script type="text/javascript" src="api/index.json"></script> + <script type="text/javascript" src="api/releases.json"></script> <script type="text/javascript" src="apidoc.js"></script> <script> if (cls != "") document.write('<script src="api/', cls, '.json" type="text/JavaScript"><\/script>'); + if (rel != "") + document.write('<script src="api/', rel, '.json" type="text/JavaScript"><\/script>'); </script> <link rel="stylesheet" href="style.css" /> </head> diff -r 277057c7e27a -r fbf3b7184a3f ocaml/doc/apidoc.js --- a/ocaml/doc/apidoc.js +++ b/ocaml/doc/apidoc.js @@ -15,6 +15,7 @@ // global variables var cls = getQuerystring('c'); +var rel = getQuerystring('r'); function qualifier(q) { @@ -74,6 +75,29 @@ return 'Unknown[' + t + ']'; } +function current_lifecycle_stage(s) +{ + if (s.length == 0) + return 'Prototype'; + else { + last_transition = s[s.length-1][0]; + switch (last_transition) { + case 'Deprecated': + return 'Deprecated'; + break; + case 'Removed': + return 'Removed'; + break; + case 'Published': + case 'Changed': + case 'Extended': + default: + return ''; + break; + } + } +} + function make_field(fld, n) { name = fld.field_name; @@ -81,20 +105,22 @@ html = ""; html = '<div class="field' + toggle(n) + '">'; html += '<input type="button" class="small-button" value="details" onclick="showhide(document.getElementById(\'' + name + '_details\'))" />'; + html += '<div class="lifecycle">' + current_lifecycle_stage(fld.lifecycle) + '</div>'; html += '<div><span class="inline-type">' + transform_type(fld.ty) + '</span> <span class="field-name">' + name + '</span> <span class="inline-qualifier">[' + qualifier(fld.qualifier) + ']</span></div>'; html += '<div id="' + name + '_details" style="display: none">'; html += '<div class="field-description">' + fld.field_description + '</div>'; html += '<table class="field-table">'; - if (fld.release != undefined) { - html += '<tr><td width="130px"><span class="field-head">Introduced in:</span></td><td>' + fld.release.internal[1] + '</td></tr>'; - if (fld.release.internal_deprecated_since != undefined) - html += '<tr><td width="130px"><span class="field-head">Deprecated since:</span></td><td>' + fld.release.internal_deprecated_since + '</td></tr>'; - } if (fld.default_value != undefined) html += '<tr><td width="130px"><span class="field-head">Default value:</span></td><td>' + transform_default(fld.default_value) + '</td></tr>'; + html += '</table>'; + html += '<table class="field-table">'; + for (i in fld.lifecycle) { + l = fld.lifecycle[i]; + html += '<tr><td width="130px"><span class="field-head">' + l[0] + ' in:</span></td><td width="130px">' + l[1] + '</td><td>' + l[2] + '</td></tr>'; + } html += '</table>'; html += '</div></div>'; @@ -109,6 +135,7 @@ html += '<div class="field' + toggle(n) + '">'; html += '<input type="button" class="small-button" value="details" onclick="showhide(document.getElementById(\'' + name + '_details\'))" />'; + html += '<div class="lifecycle">' + current_lifecycle_stage(msg.msg_lifecycle) + '</div>'; html += '<div><span class="inline-type">' + (msg.msg_result != undefined ? transform_type(msg.msg_result[0]) : 'void') + '</span> <span class="field-name">' + name + '</span> <span class="inline-params">(' + @@ -142,10 +169,12 @@ } html += '</table></td></tr>'; } - if (msg.msg_release != undefined) { - html += '<tr><td><span class="field-head">Introduced in:</span></td><td>' + msg.msg_release.internal[1] + '</td></tr>'; - if (msg.msg_release.internal_deprecated_since != undefined) - html += '<tr><td><span class="field-head">Deprecated since:</span></td><td>' + msg.msg_release.internal_deprecated_since + '</td></tr>'; + html += '</table>'; + + html += '<table class="field-table">'; + for (i in msg.msg_lifecycle) { + l = msg.msg_lifecycle[i]; + html += '<tr><td width="130px"><span class="field-head">' + l[0] + ' in:</span></td><td width="130px">' + l[1] + '</td><td>' + l[2] + '</td></tr>'; } html += '</table>'; @@ -164,10 +193,21 @@ messages.sort(function(a, b){return a.msg_name.toLowerCase().charCodeAt(0) - b.msg_name.toLowerCase().charCodeAt(0)}); html = ""; + html += '<input type="button" class="small-button" value="details" onclick="showhide(document.getElementById(\'class_details\'))" />'; + html += '<div class="lifecycle">' + current_lifecycle_stage(clsdoc.obj_lifecycle) + '</div>'; html += '<h1 class="title">Class: ' + cls + '</h1>\n'; html += '<div class="description">' + clsdoc.description + '</div>'; + html += '<div id="class_details" style="display: none">'; + html += '<table class="field-table">'; + for (i in clsdoc.obj_lifecycle) { + l = clsdoc.obj_lifecycle[i]; + html += '<tr><td width="130px"><span class="field-head">' + l[0] + ' in:</span></td><td width="130px">' + l[1] + '</td><td>' + l[2] + '</td></tr>'; + } + html += '</table>'; + html += '</div>'; + html += '<h2>Fields</h2>'; if (fields.length > 0) { for (i in fields) @@ -187,9 +227,60 @@ set_content(html); } -function build() +function compare_release_notes(a, b) { + function change_to_num(x) { + if (x.indexOf('Published') > -1) return '0'; + else if (x.indexOf('Extended') > -1) return '1'; + else if (x.indexOf('Changed') > -1) return '2'; + else if (x.indexOf('Deprecated') > -1) return '3'; + else if (x.indexOf('Removed') > -1) return '4'; + else return '5'; + } + function element_to_num(x) { + if (x.indexOf('object') > -1) return '0'; + else if (x.indexOf('field') > -1) return '1'; + else if (x.indexOf('message') > -1) return '2'; + else return '3'; + } + a = change_to_num(a[0]) + element_to_num(a[0]) + (a[1]+a[2]).toLowerCase(); + b = change_to_num(b[0]) + element_to_num(b[0]) + (b[1]+b[2]).toLowerCase(); + return a > b; +} + +function release_doc() +{ + changes = []; + + for (i in release_info) { + c = release_info[i]; + for (j in c.obj_changes) + changes.push([c.obj_changes[j][0] + ' object', c.cls, '', c.obj_changes[j][2]]); + for (j in c.field_changes) + changes.push([c.field_changes[j][0] + ' field', c.cls, c.field_changes[j][1], c.field_changes[j][2]]); + for (j in c.msg_changes) + changes.push([c.msg_changes[j][0] + ' message', c.cls, c.msg_changes[j][1], c.msg_changes[j][2]]); + } + + changes.sort(compare_release_notes); + html = ""; + html += '<h1 class="title">Release notes: ' + rel + '</h1>\n'; + + html += '<table><tr><th style="width: 12em">Change</th><th>Element</th><th>Description</th></tr>'; + + for (i in changes) { + html += '<tr><td>' + changes[i][0] + '</td><td><a href="?c=' + changes[i][1] + (changes[i][2] != '' ? '#' + changes[i][2] : '') + '">' + + changes[i][1] + (changes[i][2] != '' ? '.' + changes[i][2] : '') + '</a></td><td>' + changes[i][3] + '</td></tr>'; + } + + html += '</table>'; + + set_content(html); +} + +function class_list() +{ html = '<h2 class="title">Classes</h2>'; classes.sort(function(a, b){return a.toLowerCase().charCodeAt(0) - b.toLowerCase().charCodeAt(0)}); @@ -199,9 +290,35 @@ } append_sidebar(html); +} + +function release_list() +{ + html = '<h2>Release notes</h2>'; + for (i in releases) { + r = releases[i]; + html += '<a href="?r=' + r + '">' + r + '</a><br>'; + } + + append_sidebar(html); +} + +function build() +{ if (cls != "") { + class_list(); + release_list(); class_doc(); } + else if (rel != "") { + class_list(); + release_list(); + release_doc(); + } + else { + class_list(); + release_list(); + } } diff -r 277057c7e27a -r fbf3b7184a3f ocaml/doc/jsapi.ml --- a/ocaml/doc/jsapi.ml +++ b/ocaml/doc/jsapi.ml @@ -11,17 +11,72 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) + +open Datamodel_types +open Stringext +type change_t = lifecycle_change * string * string +and changes_t = change_t list +with rpc + +let obj_change_in_release rel o = + let rec find_rel rel = function + | [] -> None + | (transition, release, doc) :: tl when release = rel -> Some (transition, o.name, doc) + | _ :: tl -> find_rel rel tl + in + find_rel rel o.obj_lifecycle + +let msg_change_in_release rel m = + let rec find_rel rel = function + | [] -> None + | (transition, release, doc) :: tl when release = rel -> Some (transition, m.msg_name, doc) + | _ :: tl -> find_rel rel tl + in + find_rel rel m.msg_lifecycle + +let field_change_in_release rel f = + let rec find_rel rel = function + | [] -> None + | (transition, release, doc) :: tl when release = rel -> Some (transition, f.field_name, doc) + | _ :: tl -> find_rel rel tl + in + find_rel rel f.lifecycle + let _ = - let api = (* Datamodel_utils.add_implicit_messages *) (Datamodel.all_api) in + let api = (Datamodel.all_api) in let objs = Dm_api.objects_of_api api in let create_json obj = - let name = obj.Datamodel_types.name in - let s = Jsonrpc.to_string (Datamodel_types.rpc_of_obj obj) in + let name = obj.name in + let s = Jsonrpc.to_string (rpc_of_obj obj) in Unixext.write_string_to_file ("api/" ^ name ^ ".json") ("clsdoc = " ^ s); name in let names = List.map create_json objs in let class_list = String.concat ", " (List.map (fun s -> "'" ^ s ^ "'") names) in - Unixext.write_string_to_file "api/index.json" ("classes = [" ^ class_list ^ "]") + Unixext.write_string_to_file "api/index.json" ("classes = [" ^ class_list ^ "]"); + let new_in_release rel = + let search_obj obj = + let obj_changes : changes_t = + match obj_change_in_release rel obj with + | None -> [] + | Some x -> [x] + in + + let msgs = List.filter (fun m -> not m.msg_hide_from_docs) obj.messages in + let msg_changes : changes_t = List.fold_left + (fun l m -> match msg_change_in_release rel m with None -> l | Some x -> x :: l) [] msgs in + + let flds = List.filter (function Field f -> true | _ -> false) obj.contents in + let field_changes : changes_t = List.fold_left + (fun l (Field f) -> match field_change_in_release rel f with None -> l | Some x -> x :: l) [] flds in + + "{'cls': '" ^ obj.name ^ "', 'obj_changes': " ^ Jsonrpc.to_string (rpc_of_changes_t obj_changes) ^ ", 'field_changes': " ^ Jsonrpc.to_string (rpc_of_changes_t field_changes) ^ ", 'msg_changes': " ^ Jsonrpc.to_string (rpc_of_changes_t msg_changes) ^ "}" + in + let release_info = String.concat ", " (List.map search_obj objs) in + Unixext.write_string_to_file ("api/" ^ rel ^ ".json") ("release_info = [" ^ release_info ^ "]") + in + List.iter new_in_release release_order; + let release_list = String.concat ", " (List.map (fun s -> "'" ^ s ^ "'") release_order) in + Unixext.write_string_to_file "api/releases.json" ("releases = [" ^ release_list ^ "]"); diff -r 277057c7e27a -r fbf3b7184a3f ocaml/doc/style.css --- a/ocaml/doc/style.css +++ b/ocaml/doc/style.css @@ -188,7 +188,7 @@ td { vertical-align: top; - padding: 3px; + padding: 3px 1em 3px 0; } th { @@ -274,11 +274,18 @@ margin-left: .5em; } +.lifecycle { + font-size: 90%; + float: right; + font-variant:small-caps; + color: #900; + margin: auto 0.5em; +} + .small-button { font-size: 70%; -// text-align: right; float: right; - margin: .5em 0; + margin: 0.2em 0 0.2em 0.5em; } .stat {
_______________________________________________ xen-api mailing list [email protected] http://lists.xensource.com/mailman/listinfo/xen-api
