From: Luca Bruno Date: Sat, 10 Apr 2010 21:29:19 +0000 (+0200) Subject: doclet/gtkdoc: Added first version X-Git-Tag: 0.37.1~3^2~480 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f5ba3d9d8cb8af6ea3fd36cf37e78d66cf07b58d;p=thirdparty%2Fvala.git doclet/gtkdoc: Added first version --- diff --git a/configure.in b/configure.in index 43ff79056..581ec8ecb 100644 --- a/configure.in +++ b/configure.in @@ -67,6 +67,7 @@ AC_CONFIG_FILES([Makefile src/doclets/valadoc.org/Makefile src/doclets/devhelp/Makefile src/doclets/xml/Makefile + src/doclets/gtkdoc/Makefile src/valadoc/Makefile]) AC_OUTPUT diff --git a/src/doclets/Makefile.am b/src/doclets/Makefile.am index 15f033b41..4ca1524ed 100644 --- a/src/doclets/Makefile.am +++ b/src/doclets/Makefile.am @@ -7,6 +7,7 @@ SUBDIRS = htm \ xml \ devhelp \ valadoc.org \ + gtkdoc \ $(NULL) diff --git a/src/doclets/gtkdoc/Makefile.am b/src/doclets/gtkdoc/Makefile.am new file mode 100644 index 000000000..be0e1fc77 --- /dev/null +++ b/src/doclets/gtkdoc/Makefile.am @@ -0,0 +1,62 @@ +NULL = + + +AM_CFLAGS = -g \ + -DPACKAGE_ICONDIR=\"$(datadir)/valadoc/icons/\" \ + -I ../../libvaladoc/ \ + $(GLIB_CFLAGS) \ + $(LIBGEE_CFLAGS) \ + $(LIBVALA_CFLAGS) \ + $(NULL) + + + +BUILT_SOURCES = libdoclet.vala.stamp + + +docletdir = $(libdir)/valadoc/plugins/gtkdoc + + +libdoclet_la_LDFLAGS = -module -avoid-version -no-undefined + + +doclet_LTLIBRARIES = \ + libdoclet.la \ + $(NULL) + + +libdoclet_la_VALASOURCES = \ + commentconverter.vala \ + doclet.vala \ + gcomment.vala \ + generator.vala \ + utils.vala \ + $(NULL) + + +libdoclet_la_SOURCES = \ + libdoclet.vala.stamp \ + $(libdoclet_la_VALASOURCES:.vala=.c) \ + $(NULL) + + +libdoclet.vala.stamp: $(libdoclet_la_VALASOURCES) + $(VALAC) -C --vapidir $(top_srcdir)/src/vapi --vapidir $(top_srcdir)/src/libvaladoc --pkg vala-1.0 --pkg gee-1.0 --pkg valadoc-1.0 --basedir . $^ + touch $@ + + +libdoclet_la_LIBADD = \ + ../../libvaladoc/libvaladoc.la \ + $(GLIB_LIBS) \ + $(LIBGEE_LIBS) \ + $(LIBVALA_LIBS) \ + $(NULL) + + +EXTRA_DIST = $(libdoclet_la_VALASOURCES) libdoclet.vala.stamp + + +MAINTAINERCLEANFILES = \ + $(libdoclet_la_VALASOURCES:.vala=.c) \ + $(NULL) + diff --git a/src/doclets/gtkdoc/commentconverter.vala b/src/doclets/gtkdoc/commentconverter.vala new file mode 100644 index 000000000..387a2d76e --- /dev/null +++ b/src/doclets/gtkdoc/commentconverter.vala @@ -0,0 +1,242 @@ +/* commentconverter.vala + * + * Copyright (C) 2010 Luca Bruno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Luca Bruno + */ + +using Valadoc; +using Valadoc.Api; +using Valadoc.Content; + + + +public class Gtkdoc.CommentConverter : ContentVisitor { + public string brief_comment; + public string long_comment; + public Gee.List
headers = new Gee.LinkedList
(); + public string returns; + public Gee.List
versioning = new Gee.LinkedList
(); + + private StringBuilder current_builder = new StringBuilder (); + private bool in_brief_comment = true; + + public void convert (Comment comment) { + comment.accept (this); + brief_comment = brief_comment.strip (); + long_comment = current_builder.str.strip (); + if (long_comment == "") { + long_comment = null; + } + } + + public override void visit_comment (Comment c) { + c.accept_children (this); + } + + public override void visit_embedded (Embedded em) { + current_builder.append ("
"); + if (em.caption != null) { + current_builder.append_printf ("%s", em.caption); + } + + current_builder.append_printf ("", em.url); + + if (em.caption != null) { + current_builder.append_printf ("%s", em.caption); + } + + em.accept_children (this); + current_builder.append (""); + current_builder.append ("
"); + } + + public override void visit_headline (Headline hl) { + // what to do here? + warning ("GtkDoc: Headline elements not supported"); + current_builder.append ("\n"); + hl.accept_children (this); + current_builder.append ("\n"); + } + + public override void visit_link (Link link) { + current_builder.append_printf ("", link.url); + link.accept_children (this); + current_builder.append (""); + } + + public override void visit_symbol_link (SymbolLink sl) { + current_builder.append (get_reference (sl.symbol) ?? sl.label); + } + + public override void visit_list (Content.List list) { + string tag = "orderedlist"; + switch (list.bullet) { + case Content.List.Bullet.NONE: + current_builder.append (""); + tag = "itemizedlist"; + break; + + case Content.List.Bullet.UNORDERED: + current_builder.append (""); + tag = "itemizedlist"; + break; + + case Content.List.Bullet.ORDERED: + current_builder.append (""); + break; + + case Content.List.Bullet.ORDERED_NUMBER: + current_builder.append (""); + break; + + case Content.List.Bullet.ORDERED_LOWER_CASE_ALPHA: + current_builder.append (""); + break; + + case Content.List.Bullet.ORDERED_UPPER_CASE_ALPHA: + current_builder.append (""); + break; + + case Content.List.Bullet.ORDERED_LOWER_CASE_ROMAN: + current_builder.append (""); + break; + + case Content.List.Bullet.ORDERED_UPPER_CASE_ROMAN: + current_builder.append (""); + break; + + default: + warning ("GtkDoc: unsupported list type: %s", list.bullet.to_string ()); + break; + } + + list.accept_children (this); + current_builder.append_printf ("", tag); + } + + public override void visit_list_item (ListItem item) { + current_builder.append (""); + item.accept_children (this); + current_builder.append (""); + } + + public override void visit_paragraph (Paragraph para) { + current_builder.append ("\n"); + para.accept_children (this); + current_builder.append ("\n"); + if (in_brief_comment) { + brief_comment = current_builder.str; + current_builder = new StringBuilder (); + in_brief_comment = false; + } + } + + public override void visit_page (Page page) { + page.accept_children (this); + } + + public override void visit_run (Run run) { + string? tag = null; + switch (run.style) { + case Run.Style.BOLD: + current_builder.append (""); + tag = "emphasis"; + break; + + case Run.Style.ITALIC: + current_builder.append (""); + tag = "emphasis"; + break; + + case Run.Style.UNDERLINED: + current_builder.append (""); + tag = "emphasis"; + break; + + case Run.Style.MONOSPACED: + current_builder.append ("
"); + tag = "blockquote"; + break; + } + run.accept_children (this); + + if (tag != null) { + current_builder.append_printf ("", tag); + } + } + + public override void visit_source_code (SourceCode code) { + current_builder.append (""); + code.accept_children (this); + current_builder.append (""); + } + + public override void visit_table (Table t) { + current_builder.append (""); + t.accept_children (this); + current_builder.append ("
"); + } + + public override void visit_table_row (TableRow row) { + current_builder.append (""); + row.accept_children (this); + current_builder.append (""); + } + + public override void visit_table_cell (TableCell cell) { + current_builder.append (""); + cell.accept_children (this); + current_builder.append (""); + } + + public override void visit_taglet (Taglet t) { + var old_builder = (owned)current_builder; + current_builder = new StringBuilder (); + + t.accept_children (this); + if (t is Taglets.Param) { + var header = new Header ("@"+((Taglets.Param)t).parameter_name, current_builder.str); + headers.add (header); + } else if (t is Taglets.InheritDoc) { + ((Taglets.InheritDoc)t).produce_content().accept (this); + } else if (t is Taglets.Return) { + returns = current_builder.str; + } else if (t is Taglets.Since) { + var header = new Header ("Since", ((Taglets.Since)t).version); + versioning.add (header); + } else if (t is Taglets.Deprecated) { + var header = new Header ("Deprecated", current_builder.str); + versioning.add (header); + } else if (t is Taglets.See) { + var see = (Taglets.See)t; + old_builder.append_printf ("\nSee Also: %s\n", get_reference (see.symbol) ?? see.symbol_name); + } else if (t is Taglets.Link) { + ((Taglets.Link)t).produce_content().accept (this); + } else { + warning ("GtkDoc: Taglet not supported"); // TODO + } + current_builder = (owned)old_builder; + } + + public override void visit_text (Text t) { + current_builder.append (t.content); + t.accept_children (this); + } +} + diff --git a/src/doclets/gtkdoc/doclet.vala b/src/doclets/gtkdoc/doclet.vala new file mode 100644 index 000000000..e145abac1 --- /dev/null +++ b/src/doclets/gtkdoc/doclet.vala @@ -0,0 +1,327 @@ +/* doclet.vala + * + * Copyright (C) 2010 Luca Bruno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Luca Bruno + */ + +using Valadoc; +using Valadoc.Api; +using Valadoc.Content; + +namespace Gtkdoc.Config { + public static bool nohtml; + public static string library_filename; + public static string ignore_headers; + public static string deprecated_guards; + public static string ignore_decorators; + + private static const GLib.OptionEntry[] options = { + { "library", 'l', 0, OptionArg.FILENAME, ref library_filename, "Shared library path", "FILENAME" }, + { "ignore-headers", 'x', 0, OptionArg.STRING, ref ignore_headers, "A space-separated list of header files not to scan", "FILES" }, + { "deprecated-guards", 'd', 0, OptionArg.STRING, ref deprecated_guards, "A |-separated list of symbols used as deprecation guards", "GUARDS" }, + { "ignore-decorators", 0, 0, OptionArg.STRING, ref ignore_decorators, "A |-separated list of addition decorators in declarations that should be ignored", "DECS" }, + { "nohtml", 0, 0, OptionArg.NONE, ref nohtml, "Disable HTML generation", null }, + { null } + }; + + public static bool parse (string[] rargs) { + string[] args = { "gtkdoc" }; + foreach (var arg in rargs) { + args += arg; + } + + try { + var opt_context = new OptionContext ("- Vala GTK-Doc"); + opt_context.set_help_enabled (true); + opt_context.add_main_entries (options, null); + opt_context.parse (ref args); + } catch (OptionError e) { + warning ("GtkDoc: Error: %s", e.message); + warning ("GtkDoc: Run '-X --help' to see a full list of available command line options.\n"); + return false; + } + + return true; + } +} + + + +public class Gtkdoc.Director : Valadoc.Doclet, Object { + private Settings settings; + private Api.Tree tree; + private string[] vala_headers; + private string[] c_headers; + + /* + 1) Scan normal code, this generates -decl.txt for both C and Vala. + 2) Scan C code into a temp cscan directory. This generates C sections. + Move C -sections.txt file to the real output -sections.txt. + 3) Generate and append Vala sections to -sections.txt. + Done. Now we have -decl.txt of the whole code and -sections.txt containing C sections + and Vala sections. + */ + public void process (Settings settings, Api.Tree tree) { + this.settings = settings; + if (!Config.parse (settings.pluginargs)) { + return; + } + this.tree = tree; + + DirUtils.create_with_parents (settings.path, 0777); + + find_headers (); + if (vala_headers.length <= 0) { + warning ("GtkDoc: No vala header found"); + return; + } + + if (!scan (settings.path)) { + return; + } + + var cscan_dir = Path.build_filename (settings.path, "cscan"); + DirUtils.create_with_parents (cscan_dir, 0777); + + if (!scan (cscan_dir, vala_headers)) { + return; + } + + FileUtils.rename (Path.build_filename (cscan_dir, "%s-sections.txt".printf (settings.pkg_name)), + Path.build_filename (settings.path, "%s-sections.txt".printf (settings.pkg_name))); + + var generator = new Gtkdoc.Generator (); + if (!generator.execute (settings, tree)) + return; + + if (!scangobj ()) { + return; + } + + if (!mkdb ()) { + return; + } + + if (!mkhtml ()) { + return; + } + } + + private void find_headers () { + vala_headers = new string[]{}; + c_headers = new string[]{}; + Dir dir; + try { + dir = Dir.open (settings.basedir ?? "."); + } catch (Error e) { + warning ("GtkDoc: Can't open %s: %s", settings.basedir, e.message); + return; + } + + string filename; + + while ((filename = dir.read_name()) != null) { + if (filename.has_suffix (".h")) { + var stream = FileStream.open (filename, "r"); + if (stream != null) { + var line = stream.read_line (); + if (line != null) { + if (line.str ("generated by valac") != null) { + vala_headers += filename; + } else { + c_headers += filename; + } + } + } + } else if (filename.has_suffix (".c")) { + try { + string contents; + FileUtils.get_contents (filename, out contents); + FileUtils.set_contents (Path.build_filename (settings.path, "ccomments", Path.get_basename (filename)), contents); + } catch (Error e) { + warning ("GtkDoc: Can't copy %s", filename); + return; + } + } + } + } + + private bool scan (string output_dir, string[]? ignore_headers = null) { + string[] args = { "gtkdoc-scan", + "--module", settings.pkg_name, + "--source-dir", realpath (settings.basedir ?? "."), + "--output-dir", output_dir, + "--rebuild-sections", "--rebuild-types" }; + string ignored = ""; + + if (ignore_headers != null) { + ignored = string.joinv (" ", ignore_headers); + } + + if (Config.ignore_headers != null) { + ignored = "%s %s".printf (ignored, Config.ignore_headers); + } + + if (ignored != "") { + args += "--ignore-headers"; + args += ignored; + } + + if (Config.deprecated_guards != null) { + args += "--deprecated-guards"; + args += Config.deprecated_guards; + } + + if (Config.ignore_decorators != null) { + args += "--ignore-decorators"; + args += Config.ignore_decorators; + } + + try { + Process.spawn_sync (settings.path, args, null, SpawnFlags.SEARCH_PATH, null, null, null); + } catch (Error e) { + warning ("gtkdoc-scan: %s", e.message); + return false; + } + + return true; + } + + private bool scangobj () { + if (Config.library_filename == null) { + return true; + } + + var library = realpath (Config.library_filename); + + string[] pc = { "pkg-config" }; + foreach (var package in tree.get_package_list()) { + if (package.is_package) + pc += package.name; + } + + var pc_cflags = pc; + pc_cflags += "--cflags"; + var pc_libs = pc; + pc_libs += "--libs"; + + try { + string stderr; + int status; + + string cflags; + Process.spawn_sync (null, pc_cflags, null, SpawnFlags.SEARCH_PATH, null, out cflags, out stderr, out status); + if (status != 0) { + warning ("GtkDoc: pkg-config cflags error: %s\n", stderr); + return false; + } + cflags = cflags.strip (); + + string libs; + Process.spawn_sync (null, pc_libs, null, SpawnFlags.SEARCH_PATH, null, out libs, out stderr, out status); + if (status != 0) { + warning ("GtkDoc: pkg-config libs error: %s\n", stderr); + return false; + } + + libs = libs.strip (); + + string[] args = { "gtkdoc-scangobj", + "--module", settings.pkg_name, + "--types", "%s.types".printf (settings.pkg_name), + "--output-dir", settings.path }; + + string[] env = { "CFLAGS=%s".printf (cflags), + "LDFLAGS=%s %s".printf (libs, library) }; + + foreach (var evar in Environment.list_variables()) { + env += "%s=%s".printf (evar, Environment.get_variable(evar)); + } + + Process.spawn_sync (settings.path, args, env, SpawnFlags.SEARCH_PATH, null, null, null); + } catch (Error e) { + warning ("gtkdoc-scangobj: %s", e.message); + return false; + } + + return true; + } + + private bool mkdb () { + var code_dir = Path.build_filename (settings.path, "ccomments"); + + try { + Process.spawn_sync (settings.path, + { "gtkdoc-mkdb", + "--module", settings.pkg_name, + "--source-dir", code_dir, + "--output-format", "xml", + "--sgml-mode", + "--main-sgml-file", "%s-docs.xml".printf (settings.pkg_name), + "--name-space", settings.pkg_name }, + null, SpawnFlags.SEARCH_PATH, null, null, null); + } catch (Error e) { + warning ("gtkdoc-mkdb: %s", e.message); + return false; + } + + return true; + } + + private bool mkhtml () { + if (Config.nohtml) { + return true; + } + + var html_dir = Path.build_filename (settings.path, "html"); + DirUtils.create_with_parents (html_dir, 0777); + + try { + Process.spawn_sync (html_dir, + {"gtkdoc-mkhtml", + settings.pkg_name, "../%s-docs.xml".printf (settings.pkg_name)}, + null, SpawnFlags.SEARCH_PATH, null, null, null); + } catch (Error e) { + warning ("gtkdoc-mkhtml: %s", e.message); + return false; + } + + /* fix xrefs for regenerated html */ + try { + Process.spawn_sync (settings.path, + { "gtkdoc-fixxref", + "--module", settings.pkg_name, + "--module-dir", html_dir, + "--html-dir", html_dir }, + null, SpawnFlags.SEARCH_PATH, null, null, null); + } catch (Error e) { + warning ("gtkdoc-fixxref: %s", e.message); + return false; + } + + return true; + } +} + +[ModuleInit] +public Type register_plugin ( ) { + return typeof ( Gtkdoc.Director ); +} + + diff --git a/src/doclets/gtkdoc/gcomment.vala b/src/doclets/gtkdoc/gcomment.vala new file mode 100644 index 000000000..3238abe0a --- /dev/null +++ b/src/doclets/gtkdoc/gcomment.vala @@ -0,0 +1,112 @@ +/* gcomment.vala +* +* Copyright (C) 2010 Luca Bruno +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Author: +* Luca Bruno +*/ + +public class Gtkdoc.Header { + public string name; + public string[]? annotations; + public string? value; + + public Header (string name, string? value = null) { + this.name = name; + this.value = value; + } +} + + +public class Gtkdoc.GComment { + public string symbol; + public string[] symbol_annotations; + public Gee.List
headers = new Gee.LinkedList
(); + public string body; + public string returns; + public string[] returns_annotations; + public Gee.List
versioning = new Gee.LinkedList
(); + + public string to_string () { + var builder = new StringBuilder (); + + builder.append_printf ("/**\n * %s", symbol); + if (symbol_annotations != null) { + if (symbol_annotations.length > 0) { + builder.append_c (':'); + } + + foreach (var annotation in symbol_annotations) { + builder.append_printf (" (%s)", annotation); + } + } + + foreach (var header in headers) { + builder.append_printf ("\n * %s:", header.name); + if (header.annotations != null) { + foreach (var annotation in header.annotations) { + builder.append_printf (" (%s)", annotation); + } + + if (header.annotations.length > 0) { + builder.append_c (':'); + } + } + + if (header.value != null) { + builder.append_c (' '); + builder.append (commentize (header.value)); + } + } + + if (body != null) { + builder.append ("\n * \n * "); + builder.append (commentize (body)); + } + + if (returns != null || returns_annotations.length > 0) { + builder.append ("\n * \n * Returns:"); + if (returns_annotations != null) { + foreach (var annotation in returns_annotations) { + builder.append_printf (" (%s)", annotation); + } + + if (returns_annotations.length > 0) { + builder.append_c (':'); + } + } + builder.append_c (' '); + + if (returns != null) { + builder.append (commentize (returns)); + } + } + + if (versioning.size > 0) { + builder.append ("\n *"); + foreach (var version in versioning) { + builder.append_printf ("\n * %s:", version.name); + if (version.value != null) { + builder.append_printf (" %s", commentize (version.value)); + } + } + } + builder.append ("\n */"); + return builder.str; + } +} + diff --git a/src/doclets/gtkdoc/generator.vala b/src/doclets/gtkdoc/generator.vala new file mode 100644 index 000000000..9783a367e --- /dev/null +++ b/src/doclets/gtkdoc/generator.vala @@ -0,0 +1,411 @@ +/* generator.vala + * + * Copyright (C) 2010 Luca Bruno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Luca Bruno + */ + +using Valadoc; +using Valadoc.Api; +using Valadoc.Content; + +public class Gtkdoc.Generator : Api.Visitor { + class FileData { + public string filename; + public Gee.List comments; + public Gee.List section_lines; + } + + private Gee.Map files_data = new Gee.HashMap(); + private string current_cname; + private Gee.List
current_headers; + private Class current_class; + + public bool execute (Settings settings, Api.Tree tree) { + tree.accept (this); + var code_dir = Path.build_filename (settings.path, "ccomments"); + var sections = Path.build_filename (settings.path, "%s-sections.txt".printf (settings.pkg_name)); + DirUtils.create_with_parents (code_dir, 0777); + + var sections_writer = new TextWriter (sections); + if (!sections_writer.open ()) { + warning ("GtkDoc: unable to open %s for writing", sections_writer.filename); + return false; + } + + foreach (var file_data in files_data.values) { + // C comments + var basename = get_section (file_data.filename); + var cwriter = new TextWriter (Path.build_filename (code_dir, "%s.c".printf (basename))); + + if (!cwriter.open ()) { + warning ("GtkDoc: unable to open %s for writing", cwriter.filename); + return false; + } + + foreach (var comment in file_data.comments) { + cwriter.write_line (comment); + } + cwriter.close (); + + // sections + sections_writer.write_line ("
"); + sections_writer.write_line ("%s".printf (basename)); + + foreach (var section_line in file_data.section_lines) { + sections_writer.write_line (section_line); + } + sections_writer.write_line ("
"); + } + sections_writer.close (); + + return true; + } + + private FileData get_file_data (string filename) { + var file_data = files_data[filename]; + if (file_data == null) { + file_data = new FileData (); + file_data.filename = filename; + file_data.comments = new Gee.LinkedList(); + file_data.section_lines = new Gee.LinkedList(); + files_data[filename] = file_data; + } + return file_data; + } + + private Gee.List
merge_headers (Gee.List
doc_headers, Gee.List
? lang_headers) { + if (lang_headers == null) { + return doc_headers; + } + + var headers = new Gee.LinkedList
(); + + foreach (var doc_header in doc_headers) { + var header = doc_header; + foreach (var lang_header in lang_headers) { + if (doc_header.name == lang_header.name) { + header.annotations = lang_header.annotations; + if (lang_header.value != null) { + header.value += "%s".printf (lang_header.value); + } + } + } + headers.add (header); + } + + // add remaining headers + foreach (var lang_header in lang_headers) { + bool found = false; + + foreach (var header in headers) { + if (header.name == lang_header.name) { + found = true; + break; + } + } + + if (!found) { + headers.add (lang_header); + } + } + return headers; + } + + private string format_comment (string symbol, Comment? comment, bool short_description = false, string[]? returns_annotations = null) { + var converter = new Gtkdoc.CommentConverter (); + if (comment != null) { + converter.convert (comment); + } + + var gcomment = new GComment (); + gcomment.symbol = symbol; + gcomment.returns = converter.returns; + gcomment.returns_annotations = returns_annotations; + if (converter.brief_comment != null) { + if (short_description) { + var header = new Header ("@short_description", converter.brief_comment); + gcomment.headers.add (header); + gcomment.body = converter.long_comment; + } else if (converter.long_comment == null) { + gcomment.body = converter.brief_comment; + } else { + gcomment.body = "%s\n\n%s".printf (converter.brief_comment, converter.long_comment); + } + } + + gcomment.headers.add_all (merge_headers (converter.headers, current_headers)); + gcomment.versioning.add_all (converter.versioning); + return gcomment.to_string (); + } + + private void add_comment (string filename, string symbol, Comment? comment, bool short_description = false) { + if (comment == null) { + return; + } + + var file_data = get_file_data (filename); + file_data.comments.add (format_comment (symbol, comment, short_description)); + } + + private void add_symbol (string filename, string cname, Comment? comment = null, string? symbol = null, bool title = false, bool short_description = false, string[]? returns_annotations = null) { + var file_data = get_file_data (filename); + if (title) { + file_data.section_lines.add ("%s".printf (cname)); + } + + file_data.section_lines.add (cname); + + if (comment != null || (current_headers != null && current_headers.size > 0)) { + file_data.comments.add (format_comment (symbol ?? cname, comment, short_description, returns_annotations)); + } + } + + private void add_header (string name, Comment? comment, string[]? annotations = null) { + if (comment == null && annotations == null) { + return; + } + + var converter = new Gtkdoc.CommentConverter (); + var header = new Header ("@"+name); + + if (comment != null) { + converter.convert (comment); + if (converter.long_comment != null) { + header.value = "%s%s".printf (converter.brief_comment, converter.long_comment); + } else { + header.value = converter.brief_comment; + } + } + + header.annotations = annotations; + current_headers.add (header); + } + + public override void visit_tree (Api.Tree tree) { + tree.accept_children (this); + } + + public override void visit_package (Api.Package package) { + /* we are not (yet?) interested in external packages */ + if (package.is_package) { + return; + } + + package.accept_all_children (this); + } + + public override void visit_namespace (Api.Namespace ns) { + if (ns.get_filename () != null) { + add_comment (ns.get_filename(), "SECTION:%s".printf (get_section (ns.get_filename ())), ns.documentation, true); + } + + ns.accept_all_children (this); + } + + public override void visit_interface (Api.Interface iface) { + var old_cname = current_cname; + var old_headers = current_headers; + current_cname = iface.get_cname (); + current_headers = new Gee.LinkedList
(); + + iface.accept_all_children (this); + add_symbol (iface.get_filename(), iface.get_cname(), iface.documentation, null, true); + + current_cname = old_cname; + current_headers = old_headers; + } + + public override void visit_class (Api.Class cl) { + var old_cname = current_cname; + var old_headers = current_headers; + var old_class = current_class; + current_cname = cl.get_cname (); + current_headers = new Gee.LinkedList
(); + current_class = cl; + + cl.accept_all_children (this); + add_symbol (cl.get_filename(), cl.get_cname(), cl.documentation, null, true); + + current_cname = old_cname; + current_headers = old_headers; + current_class = old_class; + + if (cl.is_fundamental && cl.base_type == null) { + var filename = cl.get_filename (); + add_symbol (filename, cl.get_ref_function_cname ()); + add_symbol (filename, cl.get_unref_function_cname ()); + add_symbol (filename, cl.get_param_spec_function_cname ()); + add_symbol (filename, cl.get_set_value_function_cname ()); + add_symbol (filename, cl.get_get_value_function_cname ()); + add_symbol (filename, cl.get_take_value_function_cname ()); + } + } + + public override void visit_struct (Api.Struct st) { + var old_cname = current_cname; + var old_headers = current_headers; + current_cname = st.get_cname (); + current_headers = new Gee.LinkedList
(); + + st.accept_all_children (this); + add_symbol (st.get_filename(), st.get_cname(), st.documentation); + + current_cname = old_cname; + current_headers = old_headers; + + add_symbol (st.get_filename(), st.get_dup_function_cname ()); + add_symbol (st.get_filename(), st.get_free_function_cname ()); + } + + public override void visit_error_domain (Api.ErrorDomain edomain) { + var old_headers = current_headers; + current_headers = new Gee.LinkedList
(); + + edomain.accept_all_children (this); + add_symbol (edomain.get_filename(), edomain.get_cname(), edomain.documentation); + + current_headers = old_headers; + } + + public override void visit_error_code (Api.ErrorCode ecode) { + add_header (ecode.get_cname (), ecode.documentation); + ecode.accept_all_children (this); + } + + public override void visit_enum (Api.Enum en) { + var old_headers = current_headers; + current_headers = new Gee.LinkedList
(); + + en.accept_all_children (this); + add_symbol (en.get_filename(), en.get_cname(), en.documentation); + + current_headers = old_headers; + } + + public override void visit_enum_value (Api.EnumValue eval) { + add_header (eval.get_cname (), eval.documentation); + eval.accept_all_children (this); + } + + public override void visit_property (Api.Property prop) { + if (prop.is_override || prop.is_private || (!prop.is_abstract && !prop.is_virtual && prop.base_property != null)) { + return; + } + + add_comment (prop.get_filename(), "%s:%s".printf (current_cname, prop.get_cname ()), prop.documentation); + prop.accept_all_children (this); + + if (prop.getter != null && !prop.getter.is_private) { + add_symbol (prop.get_filename(), prop.getter.get_cname ()); + } + + if (prop.setter != null && !prop.setter.is_private) { + add_symbol (prop.get_filename(), prop.setter.get_cname ()); + } + } + + public override void visit_field (Api.Field f) { + if (f.is_private) { + return; + } + + add_header (f.get_cname (), f.documentation); + f.accept_all_children (this); + } + + public override void visit_constant (Api.Constant c) { + add_symbol (c.get_filename(), c.get_cname(), c.documentation); + c.accept_all_children (this); + } + + public override void visit_delegate (Api.Delegate d) { + add_symbol (d.get_filename(), d.get_cname(), d.documentation); + d.accept_all_children (this); + } + + public override void visit_signal (Api.Signal sig) { + add_comment (sig.get_filename(), "%s::%s".printf (current_cname, sig.get_cname ()), sig.documentation); + sig.accept_all_children (this); + } + + public override void visit_creation_method (Api.Method m) { + // never called + } + + public override void visit_method (Api.Method m) { + if ((m.is_constructor && current_class.is_abstract) || m.is_override || m.is_private || (!m.is_abstract && !m.is_virtual && m.base_method != null)) { + return; + } + + var annotations = new string[] {}; + + if (m.return_type != null) { + if (m.return_type.data_type is Api.Array) { + annotations += "array length=result_length1"; + } + + if (m.return_type.is_unowned) { + annotations += "transfer none"; + } + } + + var old_headers = current_headers; + current_headers = new Gee.LinkedList
(); + + m.accept_all_children (this); + if (m.is_yields) { + add_header ("_callback_", null, {"scope async"}); + } + add_symbol (m.get_filename(), m.get_cname (), m.documentation, null, false, false, annotations); + + current_headers = old_headers; + + if (m.is_yields) { + add_symbol (m.get_filename(), m.get_finish_function_cname ()); + } + } + + public override void visit_formal_parameter (Api.FormalParameter param) { + var annotations = new string[]{}; + var direction = "in"; + + if (param.is_out) { + direction = "out"; + } else if (param.is_ref) { + direction = "inout"; + } + + annotations += direction; + + if (param.parameter_type.is_nullable) { + annotations += "allow-none"; + } + + if (param.parameter_type.is_owned) { + annotations += "transfer full"; + } + + if (param.parameter_type.data_type is Api.Array) { + annotations += "array length=%s".printf (param.name+"_length1"); + } + + add_header (param.name, param.documentation, annotations); + param.accept_all_children (this); + } +} diff --git a/src/doclets/gtkdoc/utils.vala b/src/doclets/gtkdoc/utils.vala new file mode 100644 index 000000000..500cdad7f --- /dev/null +++ b/src/doclets/gtkdoc/utils.vala @@ -0,0 +1,89 @@ +/* utils.vala + * + * Copyright (C) 2010 Luca Bruno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Luca Bruno + */ + +using Valadoc; +using Valadoc.Api; +using Valadoc.Content; + +namespace Gtkdoc { + public string get_section (string filename) { + long dot = filename.pointer_to_offset (filename.rchr (-1, '.')); + return Path.get_basename (filename.substring (0, dot)); + } + + public string commentize (string comment) { + return string.joinv ("\n * ", comment.split ("\n")); + } + + private string? get_reference (Api.Node symbol) { + if (symbol is Api.Method) { + return "%s()".printf (((Api.Method)symbol).get_cname ()); + } else if (symbol is Api.FormalParameter) { + return "@%s".printf (((Api.FormalParameter)symbol).name); + } else if (symbol is Api.Constant) { + return "%%%s".printf (((Api.Constant)symbol).get_cname ()); + } else if (symbol is Api.Signal) { + return "::%s".printf (((Api.Signal)symbol).get_cname ()); + } else if (symbol is Api.Class) { + return "#%s".printf (((Api.Class)symbol).get_cname ()); + } else if (symbol is Api.Struct) { + return "#%s".printf (((Api.Struct)symbol).get_cname ()); + } else if (symbol is Api.Interface) { + return "#%s".printf (((Api.Interface)symbol).get_cname ()); + } else if (symbol is Api.ErrorDomain) { + return "#%s".printf (((Api.ErrorDomain)symbol).get_cname ()); + } else if (symbol is Api.ErrorCode) { + return "#%s".printf (((Api.ErrorCode)symbol).get_cname ()); + } else if (symbol is Api.Delegate) { + return "#%s".printf (((Api.Delegate)symbol).get_cname ()); + } else if (symbol is Api.Enum) { + return "#%s".printf (((Api.Enum)symbol).get_cname ()); + } + return null; + } +} + + +public class Gtkdoc.TextWriter { + public string filename; + + private FileStream? stream; + + public TextWriter (string filename) { + this.filename = filename; + } + + public bool open () { + stream = FileStream.open (filename, "a"); + return stream != null; + } + + public void close () { + stream = null; + } + + public void write_line (string line) { + stream.puts (line); + stream.putc ('\n'); + } +} + diff --git a/src/libvaladoc/api/class.vala b/src/libvaladoc/api/class.vala index 37434c2d7..5cd03a0fb 100644 --- a/src/libvaladoc/api/class.vala +++ b/src/libvaladoc/api/class.vala @@ -49,6 +49,30 @@ public class Valadoc.Api.Class : TypeSymbol { return this.vclass.get_cname(); } + public string? get_ref_function_cname () { + return this.vclass.get_ref_function (); + } + + public string? get_unref_function_cname () { + return this.vclass.get_unref_function (); + } + + public string? get_param_spec_function_cname () { + return this.vclass.get_param_spec_function (); + } + + public string? get_set_value_function_cname () { + return this.vclass.get_set_value_function (); + } + + public string? get_get_value_function_cname () { + return this.vclass.get_get_value_function (); + } + + public string? get_take_value_function_cname () { + return this.vclass.get_take_value_function (); + } + public Collection get_implemented_interface_list () { return this.interfaces; } @@ -74,6 +98,12 @@ public class Valadoc.Api.Class : TypeSymbol { } } + public bool is_fundamental { + get { + return this.vclass.is_fundamental (); + } + } + public override NodeType node_type { get { return NodeType.CLASS; } } public override void accept (Visitor visitor) { diff --git a/src/libvaladoc/api/method.vala b/src/libvaladoc/api/method.vala index 243587db8..96087b864 100644 --- a/src/libvaladoc/api/method.vala +++ b/src/libvaladoc/api/method.vala @@ -33,6 +33,10 @@ public class Valadoc.Api.Method : Member { return ((Vala.Method) symbol).get_cname (); } + public string? get_finish_function_cname () { + return ((Vala.Method) symbol).get_finish_cname (); + } + public Method? base_method { private set; get; } public TypeReference? return_type { private set; get; } diff --git a/src/libvaladoc/api/propertyaccessor.vala b/src/libvaladoc/api/propertyaccessor.vala index 7df4dde51..593068a2c 100644 --- a/src/libvaladoc/api/propertyaccessor.vala +++ b/src/libvaladoc/api/propertyaccessor.vala @@ -33,6 +33,10 @@ public class Valadoc.Api.PropertyAccessor : Symbol { public override NodeType node_type { get { return NodeType.PROPERTY_ACCESSOR; } } + public string? get_cname () { + return vpropacc.get_cname (); + } + public override void accept (Visitor visitor) { } diff --git a/src/libvaladoc/api/struct.vala b/src/libvaladoc/api/struct.vala index 7b672e96e..0e01c17e1 100644 --- a/src/libvaladoc/api/struct.vala +++ b/src/libvaladoc/api/struct.vala @@ -34,6 +34,14 @@ public class Valadoc.Api.Struct : TypeSymbol { return ((Vala.Struct) symbol).get_cname(); } + public string? get_dup_function_cname () { + return ((Vala.Struct) symbol).get_dup_function (); + } + + public string? get_free_function_cname () { + return ((Vala.Struct) symbol).get_free_function (); + } + public override NodeType node_type { get { return NodeType.STRUCT; } } public override void accept (Visitor visitor) { diff --git a/src/libvaladoc/settings.vala b/src/libvaladoc/settings.vala index 58d367020..eca9f1c4a 100755 --- a/src/libvaladoc/settings.vala +++ b/src/libvaladoc/settings.vala @@ -25,6 +25,7 @@ public class Valadoc.Settings : Object { public string pkg_name = null; public string pkg_version; public string wiki_directory; + public string[] pluginargs; public bool _private = false; public bool _protected = false; diff --git a/src/valadoc/valadoc.vala b/src/valadoc/valadoc.vala index 672e82acd..02ff73022 100755 --- a/src/valadoc/valadoc.vala +++ b/src/valadoc/valadoc.vala @@ -31,6 +31,8 @@ public class ValaDoc : Object { private static string wikidirectory = null; private static string pkg_version = null; private static string pluginpath = null; + [CCode (array_length = false, array_null_terminated = true)] + private static string[] pluginargs; private static string directory = null; private static string pkg_name = null; @@ -80,6 +82,7 @@ public class ValaDoc : Object { { "wiki", 0, 0, OptionArg.FILENAME, ref wikidirectory, "Wiki directory", "DIRECTORY" }, { "deps", 0, 0, OptionArg.NONE, ref with_deps, "Adds packages to the documentation", null }, + { "doclet-arg", 'X', 0, OptionArg.STRING_ARRAY, ref pluginargs, "Pass arguments to the doclet", "ARG" }, { "doclet", 0, 0, OptionArg.STRING, ref pluginpath, "plugin", "Name of an included doclet or path to custom doclet" }, { "no-protected", 0, OptionFlags.REVERSE, OptionArg.NONE, ref _protected, "Removes protected elements from documentation", null }, @@ -143,6 +146,7 @@ public class ValaDoc : Object { settings.path = realpath (this.directory); settings.verbose = this.verbose; settings.wiki_directory = this.wikidirectory; + settings.pluginargs = this.pluginargs; settings.enable_checking = enable_checking; settings.deprecated = deprecated;