[valadoc] doclets/gtkdoclet: Add support for dbus interfaces



commit 0366700b3fb2e984e44739882a80a3f62a6c5b3c
Author: Luca Bruno <lethalman88 gmail com>
Date:   Tue May 4 22:28:36 2010 +0200

    doclets/gtkdoclet: Add support for dbus interfaces

 src/doclets/gtkdoc/Makefile.am           |    1 +
 src/doclets/gtkdoc/commentconverter.vala |   36 +++--
 src/doclets/gtkdoc/dbus.vala             |  270 ++++++++++++++++++++++++++++++
 src/doclets/gtkdoc/doclet.vala           |   93 ++++++++---
 src/doclets/gtkdoc/gcomment.vala         |   87 ++++++++--
 src/doclets/gtkdoc/generator.vala        |  144 +++++++++++++----
 src/doclets/gtkdoc/utils.vala            |   61 ++++++--
 7 files changed, 599 insertions(+), 93 deletions(-)
---
diff --git a/src/doclets/gtkdoc/Makefile.am b/src/doclets/gtkdoc/Makefile.am
index be0e1fc..6a1c76c 100644
--- a/src/doclets/gtkdoc/Makefile.am
+++ b/src/doclets/gtkdoc/Makefile.am
@@ -27,6 +27,7 @@ doclet_LTLIBRARIES =  \
 
 libdoclet_la_VALASOURCES = \
 	commentconverter.vala	\
+	dbus.vala		\
 	doclet.vala           	\
 	gcomment.vala		\
 	generator.vala		\
diff --git a/src/doclets/gtkdoc/commentconverter.vala b/src/doclets/gtkdoc/commentconverter.vala
index 9fedcb3..3f183ee 100644
--- a/src/doclets/gtkdoc/commentconverter.vala
+++ b/src/doclets/gtkdoc/commentconverter.vala
@@ -24,29 +24,37 @@ using Valadoc;
 using Valadoc.Api;
 using Valadoc.Content;
 
+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.CommentConverter : ContentVisitor {
+	public bool is_dbus;
 	public string brief_comment;
 	public string long_comment;
-	public Gee.List<Header> headers = new Gee.LinkedList<Header> ();
 	public string returns;
+	public Gee.List<Header> headers = new Gee.LinkedList<Header> ();
 	public Gee.List<Header> versioning = new Gee.LinkedList<Header> ();
 
 	private StringBuilder current_builder = new StringBuilder ();
 	private bool in_brief_comment = true;
 	private string[] see_also = new string[]{};
 
-	public void convert (Comment comment) {
+	public void convert (Comment comment, bool is_dbus = false) {
+		this.is_dbus = is_dbus;
 		comment.accept (this);
-		if (brief_comment != null) {
-			brief_comment = brief_comment.strip ();
-		}
 
 		if (see_also.length > 0) {
-			current_builder.append_printf ("\n<emphasis>See Also</emphasis>: %s",
-										   string.joinv (", ", see_also));
+			current_builder.append_printf ("<para><emphasis>See also</emphasis>: %s</para>", string.joinv (", ", see_also));
 		}
+
 		long_comment = current_builder.str.strip ();
 		if (long_comment == "") {
 			long_comment = null;
@@ -89,7 +97,7 @@ public class Gtkdoc.CommentConverter : ContentVisitor {
 	}
 
 	public override void visit_symbol_link (SymbolLink sl) {
-		current_builder.append (get_creference (sl.symbol) ?? sl.label);
+		current_builder.append (get_docbook_link (sl.symbol, is_dbus) ?? sl.label);
 	}
   
 	public override void visit_list (Content.List list) {
@@ -133,7 +141,7 @@ public class Gtkdoc.CommentConverter : ContentVisitor {
 			warning ("GtkDoc: unsupported list type: %s", list.bullet.to_string ());
 			break;
 		}
-		
+
 		list.accept_children (this);
 		current_builder.append_printf ("</%s>", tag);
 	}
@@ -145,13 +153,17 @@ public class Gtkdoc.CommentConverter : ContentVisitor {
 	}
   
 	public override void visit_paragraph (Paragraph para) {
-		current_builder.append ("\n");
+		if (!in_brief_comment) {
+			current_builder.append ("<para>");
+		}
 		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;
+		} else {
+			current_builder.append ("</para>");
 		}
 	}
   
@@ -233,7 +245,7 @@ public class Gtkdoc.CommentConverter : ContentVisitor {
 			versioning.add (header);
 		} else if (t is Taglets.See) {
 			var see = (Taglets.See)t;
-			see_also += get_creference (see.symbol) ?? see.symbol_name;
+			see_also += get_docbook_link (see.symbol, is_dbus) ?? see.symbol_name;
 		} else if (t is Taglets.Link) {
 			((Taglets.Link)t).produce_content().accept (this);
 		} else {
diff --git a/src/doclets/gtkdoc/dbus.vala b/src/doclets/gtkdoc/dbus.vala
new file mode 100644
index 0000000..992dc4a
--- /dev/null
+++ b/src/doclets/gtkdoc/dbus.vala
@@ -0,0 +1,270 @@
+/* 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 <lethalman88 gmail com>
+ */
+
+using Valadoc;
+using Valadoc.Api;
+using Valadoc.Content;
+
+namespace Gtkdoc.DBus {
+	public class Parameter {
+		public enum Direction {
+			NONE,
+			IN,
+			OUT;
+
+			public string to_string () {
+				switch (this) {
+				case NONE:
+					return "";
+				case IN:
+					return "in";
+				case OUT:
+					return "out";
+				default:
+					assert_not_reached ();
+				}
+			}
+		}
+
+		public string name;
+		public string signature;
+		public Direction direction;
+
+		public Parameter (string name, string signature, Direction direction = Direction.NONE) {
+			this.name = name;
+			this.signature = signature;
+			this.direction = direction;
+		}
+
+		public string to_string () {
+			if (direction == Direction.NONE) {
+				return """<parameter><type>'%s'</type> %s</parameter>""".printf (signature, name);
+			} else {
+				return """<parameter>%s <type>'%s'</type> %s</parameter>""".printf (direction.to_string(), signature, name);
+			}
+		}
+	}
+
+	public class Member {
+		public string name;
+		public Gee.List<Parameter> parameters = new Gee.LinkedList<Parameter>();
+		public GComment comment;
+
+		internal DBus.Interface iface;
+
+		public Member (string name) {
+			this.name = name;
+		}
+
+		public void add_parameter (Parameter parameter) {
+			parameters.add (parameter);
+		}
+
+		public string get_docbook_id () {
+			return to_docbook_id (name);
+		}
+
+		public string to_string (int indent, bool link) {
+			var builder = new StringBuilder ();
+
+			if (link) {
+				builder.append_printf ("""
+<link linkend="%s-%s">%s</link>%s(""", iface.get_docbook_id (), get_docbook_id (), name, string.nfill (indent-name.length, ' '));
+			} else {
+				builder.append_printf ("\n%s%s(", name, string.nfill (indent-name.length, ' '));
+			}
+
+			if (parameters.size > 0) {
+				builder.append (parameters[0].to_string ());
+			}
+			for (int i=1; i < parameters.size; i++) {
+				builder.append (",\n");
+				builder.append (string.nfill (indent+1, ' '));
+				builder.append (parameters[i].to_string ());
+			}
+			builder.append_c (')');
+			return builder.str;
+		}
+	}
+
+	public class Interface {
+		public string package_name;
+		public string name;
+		public string purpose;
+		public string description;
+		public Gee.List<Member> methods = new Gee.LinkedList<Member>();
+		public Gee.List<Member> signals = new Gee.LinkedList<Member>();
+
+		public Interface (string package_name, string name, string purpose = "", string description = "") {
+			this.package_name = package_name;
+			this.name = name;
+			this.purpose = purpose;
+			this.description = description;
+		}
+
+		public void add_method (Member member) {
+			member.iface = this;
+			methods.add (member);
+		}
+
+		public void add_signal (Member member) {
+			member.iface = this;
+			signals.add (member);
+		}
+
+		public string get_docbook_id () {
+			return to_docbook_id (name);
+		}
+
+		public bool write (Settings settings) {
+			var xml_dir = Path.build_filename (settings.path, "xml");
+			DirUtils.create_with_parents (xml_dir, 0777);
+
+			var xml_file = Path.build_filename (xml_dir, "%s.xml".printf (to_docbook_id (name)));
+			var writer = new TextWriter (xml_file, "w");
+			if (!writer.open ()) {
+				warning ("GtkDoc: unable to open %s for writing", writer.filename);
+				return false;
+			}
+			writer.write_line (to_string ());
+			writer.close ();
+			return true;
+		}
+
+		public string to_string () {
+			/* compute minimum indent for methods */
+			var method_indent = 0;
+			foreach (var method in methods) {
+				method_indent = int.max (method_indent, (int)method.name.length);
+			}
+			method_indent += 5;
+
+			/* compute minimum indent for signals */
+			var signal_indent = 0;
+			foreach (var sig in signals) {
+				signal_indent = int.max (signal_indent, (int)sig.name.length);
+			}
+			signal_indent += 5;
+
+			var builder = new StringBuilder ();
+			var docbook_id = get_docbook_id ();
+
+			builder.append ("<?xml version=\"1.0\"?><!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.3//EN\" \"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd\"";);
+			builder.append_printf ("""
+[<!ENTITY %% local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">]>
+<refentry id="docs-%s">
+<refmeta>
+<refentrytitle role="top_of_page" id="docs-%s.top_of_page">%s</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>
+%s D-Bus API
+</refmiscinfo>
+</refmeta>
+<refnamediv>
+<refname>%s</refname>
+<refpurpose>%s</refpurpose>
+</refnamediv>""", docbook_id, docbook_id, name, package_name.up (), name, purpose ?? "");
+
+			/*
+			 * Methods
+			 */
+			if (methods.size > 0) {
+				builder.append_printf ("""
+<refsynopsisdiv id="docs-%s.synopsis" role="synopsis">
+<title role="synopsis.title">Methods</title>
+<synopsis>""", docbook_id);
+				foreach (var method in methods) {
+					builder.append (method.to_string (method_indent, true));
+				}
+				builder.append ("</synopsis></refsynopsisdiv>");
+			}
+
+			/*
+			 * Signals
+			 */
+			if (signals.size > 0) {
+				builder.append_printf ("""
+<refsynopsisdiv id="docs-%s.signals" role="signal_proto">
+<title role="signal_proto.title">Signals</title>
+<synopsis>""", docbook_id);
+				foreach (var sig in signals) {
+					builder.append (sig.to_string (signal_indent, true));
+				}
+				builder.append ("</synopsis></refsynopsisdiv>");
+			}
+
+			/*
+			 * Description
+			 */
+			builder.append_printf ("""
+<refsect1 id="docs-%s.description" role="desc">
+<title role="desc.title">Description</title>
+%s
+</refsect1>""", docbook_id, description);
+
+			/*
+			 * Methods details
+			 */
+			if (methods.size > 0) {
+				builder.append_printf ("""
+<refsect1 id="docs-%s.details" role="details">
+<title role="details.title">Details</title>""", docbook_id);
+
+				foreach (var method in methods) {
+					builder.append_printf ("""
+<refsect2 id="%s-%s" role="function">
+<title>%s ()</title>
+<programlisting>%s
+</programlisting>
+%s
+</refsect2>""", docbook_id, method.get_docbook_id (), method.name, method.to_string (method_indent, false), method.comment != null ? method.comment.to_docbook () : "");
+				}
+
+				builder.append ("</refsect1>");
+			}
+
+			/*
+			 * Signals details
+			 */
+			if (signals.size > 0) {
+				builder.append_printf ("""
+<refsect1 id="docs-%s.signal-details" role="signals">
+<title role="signals.title">Signal Details</title>""", docbook_id);
+
+				foreach (var sig in signals) {
+					builder.append_printf ("""
+<refsect2 id="%s-%s" role="signal">
+<title>The <literal>%s</literal> signal</title>
+<programlisting>%s
+</programlisting>
+%s
+</refsect2>""", docbook_id, sig.get_docbook_id (), sig.name, sig.to_string (signal_indent, false), sig.comment != null ? sig.comment.to_docbook () : "");
+				}
+
+				builder.append ("</refsect1>");
+			}
+
+			builder.append ("</refentry>");
+			return builder.str;
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/doclets/gtkdoc/doclet.vala b/src/doclets/gtkdoc/doclet.vala
index c79da12..f67b9a9 100644
--- a/src/doclets/gtkdoc/doclet.vala
+++ b/src/doclets/gtkdoc/doclet.vala
@@ -66,17 +66,18 @@ namespace Gtkdoc.Config {
 public class Gtkdoc.Director : Valadoc.Doclet, Object {
 	private Settings settings;
 	private Api.Tree tree;
+	private Gtkdoc.Generator generator;
 	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.
-	*/
+	 * 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)) {
@@ -91,7 +92,7 @@ public class Gtkdoc.Director : Valadoc.Doclet, Object {
 			warning ("GtkDoc: No vala header found");
 			return;
 		}
-	   
+
 		if (!scan (settings.path)) {
 			return;
 		}
@@ -104,11 +105,12 @@ public class Gtkdoc.Director : Valadoc.Doclet, Object {
 		}
 
 		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)));
+						  Path.build_filename (settings.path, "%s-sections.txt".printf (settings.pkg_name)));
 
-		var generator = new Gtkdoc.Generator ();
-		if (!generator.execute (settings, tree))
-		return;
+		generator = new Gtkdoc.Generator ();
+		if (!generator.execute (settings, tree)) {
+			return;
+		}
 
 		if (!scangobj ()) {
 			return;
@@ -212,8 +214,9 @@ public class Gtkdoc.Director : Valadoc.Doclet, Object {
 
 		string[] pc = { "pkg-config" };
 		foreach (var package in tree.get_package_list()) {
-			if (package.is_package)
-			pc += package.name;
+			if (package.is_package) {
+				pc += package.name;
+			}
 		}
 
 		var pc_cflags = pc;
@@ -268,23 +271,69 @@ public class Gtkdoc.Director : Valadoc.Doclet, Object {
 	}
 
 	private bool mkdb () {
+		var main_file = Path.build_filename (settings.path, "%s-docs.xml".printf (settings.pkg_name));
 		var code_dir = Path.build_filename (settings.path, "ccomments");
+		var must_update_main_file = !FileUtils.test (main_file, FileTest.EXISTS);
+
+		var args = new string[] { "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 };
 
 		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 },
+			Process.spawn_sync (settings.path, args,
 								null, SpawnFlags.SEARCH_PATH, null, null, null);
 		} catch (Error e) {
 			warning ("gtkdoc-mkdb: %s", e.message);
 			return false;
 		}
 
+		if (must_update_main_file) {
+			// gtkdoc-mkdb created a template main file, but users expect it to be a bit more complete
+			string contents;
+			try {
+				FileUtils.get_contents (main_file, out contents);
+			} catch (Error e) {
+				warning ("GtkDoc: Error while reading main file '%s' contents: %s", main_file, e.message);
+				return false;
+			}
+
+			if (settings.pkg_version != null) {
+				contents = contents.replace ("[VERSION]", settings.pkg_version);
+			}
+			contents = contents.replace ("[Insert title here]", "%s API Reference".printf (settings.pkg_name));
+
+			if (generator.dbus_interfaces.size > 0) {
+				// hackish but prevents us from re-creating the whole main file,
+				// which would more even more hackish
+				var builder = new StringBuilder ();
+				builder.append_printf ("\n<chapter>\n<title>%s D-Bus API Reference</title>\n", settings.pkg_name);
+				foreach (var iface in generator.dbus_interfaces) {
+					builder.append_printf ("<xi:include href=\"xml/%s.xml\"/>\n", to_docbook_id (iface.name));
+				}
+
+				var hierarchy_file = Path.build_filename (settings.path, "%s.hierarchy".printf (settings.pkg_name));
+				if (FileUtils.test (hierarchy_file, FileTest.EXISTS)) {
+					// if hierarchy exists, gtkdoc-mkdb will output it
+					builder.append ("</chapter>\n<chapter id=\"object-tree\">");
+					contents = contents.replace ("<chapter id=\"object-tree\">", builder.str);
+				} else {
+					builder.append ("</chapter>\n<index id=\"api-index-full\">");
+					contents = contents.replace ("<index id=\"api-index-full\">", builder.str);
+				}
+			}
+
+			try {
+				FileUtils.set_contents (main_file, contents);
+			} catch (Error e) {
+				warning ("GtkDoc: Error while writing main file '%s' contents: %s", main_file, e.message);
+				return false;
+			}
+		}
+
 		return true;
 	}
 
diff --git a/src/doclets/gtkdoc/gcomment.vala b/src/doclets/gtkdoc/gcomment.vala
index c1af819..0ad68c2 100644
--- a/src/doclets/gtkdoc/gcomment.vala
+++ b/src/doclets/gtkdoc/gcomment.vala
@@ -20,26 +20,15 @@
 * 	Luca Bruno <lethalman88 gmail com>
 */
 
-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<Header> headers = new Gee.LinkedList<Header>();
-	public string body;
+	public Gee.List<Header> headers = new Gee.LinkedList<Header> ();
+	public string brief_comment;
+	public string long_comment;
 	public string returns;
 	public string[] returns_annotations;
-	public Gee.List<Header> versioning = new Gee.LinkedList<Header>();
+	public Gee.List<Header> versioning = new Gee.LinkedList<Header> ();
 
 	public string to_string () {
 		var builder = new StringBuilder ();
@@ -60,16 +49,18 @@ public class Gtkdoc.GComment {
 				}
 				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 (brief_comment != null) {
+			builder.append_printf  ("\n * \n * %s", commentize (brief_comment));
+		}
+		if (long_comment != null) {
+			builder.append_printf  ("\n * \n * %s", commentize (long_comment));
 		}
 
 		if (returns != null || returns_annotations.length > 0) {
@@ -102,5 +93,63 @@ public class Gtkdoc.GComment {
 		builder.append ("\n */");
 		return builder.str;
 	}
+
+	public string to_docbook () {
+		/*
+		 * FIXME: this is not how it should be.
+		 * The real solution is to create a comment like gtkdoc-mkdb does.
+		 * This implies replacing the work of gtkdoc-mkdb and have a more accurate management of headers,
+		 * (i.e. differentiate between parameters, short_description, see_also, etc.).
+		 *
+		 * For now we'll assume all headers are parameters.
+		 * This is enough for a manually generated xml file only for D-Bus API.
+		 *
+		 * In other words, we are converting C/gtkdoc comment to a docbook comment.
+		 */
+
+		string? deprecated = null;
+		string? since = null;
+		foreach (var header in versioning) {
+			if (header.name == "Deprecated") {
+				deprecated = header.value;
+			} else if (header.name == "Since") {
+				since = header.value;
+			} else {
+				warning ("GtkDoc: Unknown versioning tag '%s'", header.name);
+			}
+		}
+
+		var builder = new StringBuilder ();
+		if (deprecated != null) {
+			builder.append_printf ("""<warning><para><literal>%s</literal> is deprecated and should not be used in newly-written code. %s</para></warning>""", symbol, deprecated);
+		}
+
+		if (brief_comment != null) {
+			builder.append_printf ("<para>%s</para>", brief_comment);
+		}
+		if (long_comment != null) {
+			builder.append (long_comment);
+		}
+
+		if (headers.size > 0 || returns != null) {
+			builder.append ("""<variablelist role="params">""");
+			foreach (var header in headers) {
+				builder.append_printf ("""<varlistentry><term><parameter>%s</parameter>&#160;:</term>
+<listitem><simpara> %s </simpara></listitem></varlistentry>""",
+									   header.name.offset (1), header.value);
+			}
+			if (returns != null) {
+				builder.append_printf ("""<varlistentry><term><emphasis>Returns</emphasis>&#160;:</term>
+<listitem><simpara> %s </simpara></listitem></varlistentry>""", returns);
+			}
+			builder.append ("</variablelist>");
+		}
+
+		if (since != null) {
+			builder.append_printf ("""<para role="since">Since %s</para>""", since);
+		}
+
+		return builder.str;
+	}
 }
 
diff --git a/src/doclets/gtkdoc/generator.vala b/src/doclets/gtkdoc/generator.vala
index 142d6f9..f0f4189 100644
--- a/src/doclets/gtkdoc/generator.vala
+++ b/src/doclets/gtkdoc/generator.vala
@@ -31,6 +31,9 @@ public class Gtkdoc.Generator : Api.Visitor {
 		public Gee.List<string> section_lines;
 	}
 
+	public Gee.List<DBus.Interface> dbus_interfaces = new Gee.LinkedList<DBus.Interface>();
+
+	private Settings settings;
 	private Gee.Map<string, FileData> files_data = new Gee.HashMap<string, FileData>();
 	private string current_cname;
 	private Gee.List<Header> current_headers;
@@ -38,8 +41,11 @@ public class Gtkdoc.Generator : Api.Visitor {
 	private Method current_method;
 	private Delegate current_delegate;
 	private Api.Signal current_signal;
+	private DBus.Interface current_dbus_interface;
+	private DBus.Member current_dbus_member;
 
 	public bool execute (Settings settings, Api.Tree tree) {
+		this.settings = settings;
 		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));
@@ -80,6 +86,10 @@ public class Gtkdoc.Generator : Api.Visitor {
 		return true;
 	}
 
+	public Gee.Set<string> get_filenames () {
+		return files_data.keys.read_only_view;
+	}
+
 	private FileData get_file_data (string filename) {
 		var file_data = files_data[filename];
 		if (file_data == null) {
@@ -97,7 +107,7 @@ public class Gtkdoc.Generator : Api.Visitor {
 			return doc_headers;
 		}
 
-		var headers = new Gee.LinkedList<Header> ();
+		var headers = new Gee.LinkedList<Header>();
 
 		foreach (var doc_header in doc_headers) {
 			var header = doc_header;
@@ -130,31 +140,28 @@ public class Gtkdoc.Generator : Api.Visitor {
 		return headers;
 	}
 
-	private string format_comment (string symbol, Comment? comment, bool short_description = false, string[]? returns_annotations = null) {
+	private GComment create_gcomment (string symbol, Comment? comment, bool short_description = false, string[]? returns_annotations = null, bool is_dbus = false) {
 		var converter = new Gtkdoc.CommentConverter ();
 		if (comment != null) {
-			converter.convert (comment);
+			converter.convert (comment, is_dbus);
 		}
 
 		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);
-			}
+
+		if (converter.brief_comment != null && short_description) {
+			var header = new Header ("@short_description", converter.brief_comment);
+			gcomment.headers.add (header);
+		} else {
+			gcomment.brief_comment = converter.brief_comment;
 		}
+		gcomment.long_comment = converter.long_comment;
 
 		gcomment.headers.add_all (merge_headers (converter.headers, current_headers));
 		gcomment.versioning.add_all (converter.versioning);
-		return gcomment.to_string ();
+		return gcomment;
 	}
 
 	private void add_comment (string filename, string symbol, Comment? comment, bool short_description = false) {
@@ -163,10 +170,10 @@ public class Gtkdoc.Generator : Api.Visitor {
 		}
 
 		var file_data = get_file_data (filename);
-		file_data.comments.add (format_comment (symbol, comment, short_description));
+		file_data.comments.add (create_gcomment(symbol, comment, short_description).to_string ());
 	}
 
-	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) {
+	private GComment? 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 ("<TITLE>%s</TITLE>".printf (cname));
@@ -175,11 +182,14 @@ public class Gtkdoc.Generator : Api.Visitor {
 		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));
+			var gcomment = create_gcomment(symbol ?? cname, comment, short_description, returns_annotations);
+			file_data.comments.add (gcomment.to_string ());
+			return gcomment;
 		}
+		return null;
 	}
 
-	private Header? add_manual_header (string name, string? comment, string[]? annotations = null) {
+	private Header? add_custom_header (string name, string? comment, string[]? annotations = null) {
 		if (comment == null && annotations == null) {
 			return null;
 		}
@@ -191,6 +201,18 @@ public class Gtkdoc.Generator : Api.Visitor {
 		return header;
 	}
 
+	private void remove_custom_header (string name) {
+		var header_name = "@%s".printf (name);
+		var it = current_headers.iterator();
+		while (it.next ()) {
+			var header = it  get ();
+			if (header.name == header_name) {
+				it.remove ();
+				break;
+			}
+		}
+	}
+
 	private Header? add_header (string name, Comment? comment, string[]? annotations = null) {
 		if (comment == null && annotations == null) {
 			return null;
@@ -201,10 +223,11 @@ public class Gtkdoc.Generator : Api.Visitor {
 
 		if (comment != null) {
 			converter.convert (comment);
-			if (converter.long_comment != null) {
-			  header.value = "%s<para>%s</para>".printf (converter.brief_comment, converter.long_comment);
-			} else {
-			  header.value = converter.brief_comment;
+			if (converter.brief_comment != null) {
+				header.value = converter.brief_comment;
+				if (converter.long_comment != null) {
+					header.value += converter.long_comment;
+				}
 			}
 		}
 
@@ -237,30 +260,50 @@ public class Gtkdoc.Generator : Api.Visitor {
 	public override void visit_interface (Api.Interface iface) {
 		var old_cname = current_cname;
 		var old_headers = current_headers;
+		var old_dbus_interface = current_dbus_interface;
 		current_cname = iface.get_cname ();
 		current_headers = new Gee.LinkedList<Header>();
+		current_dbus_interface = null;
 
+		if (iface.get_dbus_name () != null) {
+			current_dbus_interface = new DBus.Interface (settings.pkg_name, iface.get_dbus_name ());
+		}
 		iface.accept_all_children (this);
 		add_symbol (iface.get_filename(), iface.get_cname(), iface.documentation, null, true);
+		if (current_dbus_interface != null) {
+			current_dbus_interface.write (settings);
+			dbus_interfaces.add (current_dbus_interface);
+		}
 
 		current_cname = old_cname;
 		current_headers = old_headers;
+		current_dbus_interface = old_dbus_interface;
 	}
 
 	public override void visit_class (Api.Class cl) {
 		var old_cname = current_cname;
 		var old_headers = current_headers;
 		var old_class = current_class;
+		var old_dbus_interface = current_dbus_interface;
 		current_cname = cl.get_cname ();
 		current_headers = new Gee.LinkedList<Header>();
 		current_class = cl;
+		current_dbus_interface = null;
 
+		if (cl.get_dbus_name () != null) {
+			current_dbus_interface = new DBus.Interface (settings.pkg_name, cl.get_dbus_name ());
+		}
 		cl.accept_all_children (this);
 		add_symbol (cl.get_filename(), cl.get_cname(), cl.documentation, null, true);
+		if (current_dbus_interface != null) {
+			current_dbus_interface.write (settings);
+			dbus_interfaces.add (current_dbus_interface);
+		}
 
 		current_cname = old_cname;
 		current_headers = old_headers;
 		current_class = old_class;
+		current_dbus_interface = old_dbus_interface;
 
 		if (cl.is_fundamental && cl.base_type == null) {
 			var filename = cl.get_filename ();
@@ -300,7 +343,7 @@ public class Gtkdoc.Generator : Api.Visitor {
 				}
 			}
 			if (param_header == null) {
-				add_manual_header ("error", "location to store the error occuring, or %NULL to ignore", {"error-domains %s".printf (edomain.get_cname ())});
+				add_custom_header ("error", "location to store the error occuring, or %NULL to ignore", {"error-domains %s".printf (edomain.get_cname ())});
 			} else {
 				// assume the only annotation is error-domains
 				var annotation = param_header.annotations[0];
@@ -391,17 +434,32 @@ public class Gtkdoc.Generator : Api.Visitor {
 	public override void visit_signal (Api.Signal sig) {
 		var old_headers = current_headers;
 		var old_signal = current_signal;
+		var old_dbus_member = current_dbus_member;
 		current_headers = new Gee.LinkedList<Header>();
 		current_signal = sig;
+		current_dbus_member = null;
+
+		if (current_dbus_interface != null && sig.is_dbus_visible) {
+			current_dbus_member = new DBus.Member (sig.get_dbus_name ());
+		}
+		// gtkdoc maps parameters by their ordering, so let's customly add the first parameter
+		add_custom_header (to_lower_case (((Api.Node)sig.parent).name), "", null);
 
-		// gtkdoc maps parameters by their ordering, so let's manually add the first parameter
-		add_manual_header (to_lower_case (((Api.Node)sig.parent).name), "", null);
 		sig.accept_all_children (this);
+
 		var name = sig.get_cname().replace ("_", "-");
 		add_comment (sig.get_filename(), "%s::%s".printf (current_cname, name), sig.documentation);
+		if (current_dbus_interface != null && sig.is_dbus_visible) {
+			// remove the custom header
+			remove_custom_header (to_lower_case (((Api.Node)sig.parent).name));
+			var gcomment = create_gcomment (sig.get_dbus_name (), sig.documentation, false, null, true);
+			current_dbus_member.comment = gcomment;
+			current_dbus_interface.add_signal (current_dbus_member);
+		}
 
 		current_headers = old_headers;
 		current_signal = old_signal;
+		current_dbus_member = old_dbus_member;
 	}
 
 	public override void visit_creation_method (Api.Method m) {
@@ -413,7 +471,7 @@ public class Gtkdoc.Generator : Api.Visitor {
 			return;
 		}
 
-		var annotations = new string[] {};
+		var annotations = new string[]{};
 
 		if (m.return_type != null) {
 			if (m.return_type.data_type is Api.Array) {
@@ -427,18 +485,35 @@ public class Gtkdoc.Generator : Api.Visitor {
 
 		var old_headers = current_headers;
 		var old_method = current_method;
+		var old_dbus_member = current_dbus_member;
 		current_headers = new Gee.LinkedList<Header>();
 		current_method = m;
+		current_dbus_member = null;
+
+		if (current_dbus_interface != null && m.is_dbus_visible && !m.is_constructor) {
+			current_dbus_member = new DBus.Member (m.get_dbus_name ());
+		}
 
 		m.accept_all_children (this);
+
 		if (m.is_yields) {
-			add_manual_header ("_callback_", "callback to call when the request is satisfied", {"scope async"});
-			add_manual_header ("user_data", "the data to pass to callback function", {"closure"});
+			add_custom_header ("_callback_", "callback to call when the request is satisfied", {"scope async"});
+			add_custom_header ("user_data", "the data to pass to callback function", {"closure"});
 		}
 		add_symbol (m.get_filename(), m.get_cname (), m.documentation, null, false, false, annotations);
+		if (current_dbus_interface != null && m.is_dbus_visible && !m.is_constructor) {
+			if (m.return_type != null && m.return_type.data_type != null) {
+				var dresult = new DBus.Parameter (m.get_dbus_result_name (), m.return_type.get_dbus_type_signature (), DBus.Parameter.Direction.OUT);
+				current_dbus_member.add_parameter (dresult);
+			}
+			var gcomment = create_gcomment (m.get_dbus_name (), m.documentation, false, null, true);
+			current_dbus_member.comment = gcomment;
+			current_dbus_interface.add_method (current_dbus_member);
+		}
 
 		current_headers = old_headers;
 		current_method = old_method;
+		current_dbus_member = old_dbus_member;
 
 		if (m.is_yields) {
 			add_symbol (m.get_filename(), m.get_finish_function_cname ());
@@ -474,10 +549,21 @@ public class Gtkdoc.Generator : Api.Visitor {
 
 		if (current_signal != null && param.documentation == null) {
 			// gtkdoc writes arg0, arg1 which is ugly. As a workaround, we always add an header for them.
-			add_manual_header (param.name, "", null);
+			add_custom_header (param.name, "", null);
 		} else {
 			add_header (param.name, param.documentation, annotations);
 		}
+
+		if (current_dbus_member != null) {
+			var ddirection = DBus.Parameter.Direction.IN;
+			if (current_signal != null) {
+				ddirection = DBus.Parameter.Direction.NONE;
+			} else if (param.is_out) {
+				ddirection = DBus.Parameter.Direction.OUT;
+			}
+			var dparam = new DBus.Parameter (param.name, param.parameter_type.get_dbus_type_signature (), ddirection);
+			current_dbus_member.add_parameter (dparam);
+		}
 		param.accept_all_children (this);
 	}
 }
diff --git a/src/doclets/gtkdoc/utils.vala b/src/doclets/gtkdoc/utils.vala
index f50b2a9..6ba4175 100644
--- a/src/doclets/gtkdoc/utils.vala
+++ b/src/doclets/gtkdoc/utils.vala
@@ -65,23 +65,59 @@ namespace Gtkdoc {
 		return null;
 	}
 
-	public string? get_creference (Api.Item item) {
+	public string? get_dbus_interface (Api.Item item) {
+		if (item is Api.Class) {
+			return ((Api.Class)item).get_dbus_name ();
+		} else if (item is Api.Interface) {
+			return ((Api.Interface)item).get_dbus_name ();
+		}
+		return null;
+	}
+
+	public string? get_docbook_link (Api.Item item, bool is_dbus = false) {
 		if (item is Api.Method) {
-			return "%s()".printf (((Api.Method)item).get_cname ());
+			string name;
+			string parent;
+			if (is_dbus) {
+				name = ((Api.Method)item).get_dbus_name ();
+				parent = "%s-".printf (get_dbus_interface (item.parent));
+			} else {
+				name = ((Api.Method)item).get_cname ();
+				parent = "";
+			}
+			return """<link linkend="%s%s"><function>%s()</function></link>""".printf (to_docbook_id (parent), to_docbook_id (name), name);
 		} else if (item is Api.FormalParameter) {
-			return "@%s".printf (((Api.FormalParameter)item).name);
+			return "<parameter>%s</parameter>".printf (((Api.FormalParameter)item).name);
 		} else if (item is Api.Constant) {
-			return "%%%s".printf (((Api.Constant)item).get_cname ());
+			var cname = ((Api.Constant)item).get_cname ();
+			return """<link linkend="%s:CAPS"><literal>%s</literal></link>""".printf (to_docbook_id (cname), cname);
 		} else if (item is Api.Property) {
-			return "#%s:%s".printf (get_cname (item.parent), ((Api.Property)item).get_cname ());
+			string name;
+			string parent;
+			if (is_dbus) {
+				name = ((Api.Property)item).get_dbus_name ();
+				parent = get_dbus_interface (item.parent);
+			} else {
+				name = ((Api.Property)item).get_cname ();
+				parent = get_cname (item.parent);
+			}
+			return """<link linkend="%s--%s"><type>"%s"</type></link>""".printf (to_docbook_id (parent), to_docbook_id (name), name);
 		} else if (item is Api.Signal) {
-			var name = ((Api.Signal)item).get_cname ();
-			name = name.replace ("_", "-");
-			return "#%s::%s".printf (get_cname (item.parent), name);
+			string name;
+			string parent;
+			if (is_dbus) {
+				name = ((Api.Signal)item).get_dbus_name ();
+				parent = get_dbus_interface (item.parent);
+			} else {
+				name = ((Api.Signal)item).get_cname ();
+				name = name.replace ("_", "-");
+				parent = get_cname (item.parent);
+			}
+			return """<link linkend="%s-%s"><type>"%s"</type></link>""".printf (to_docbook_id (parent), to_docbook_id (name), name);
 		} else {
 			var cname = get_cname (item);
 			if (cname != null) {
-				return "#%s".printf (cname);
+				return """<link linkend="%s"><type>%s</type></link>""".printf (to_docbook_id (cname), cname);
 			}
 		}
 		return null;
@@ -104,6 +140,10 @@ namespace Gtkdoc {
 		}
 		return builder.str;
 	}
+
+	public string to_docbook_id (string name) {
+		return name.replace(".", "-").replace("_", "-");
+	}
 }
 
 
@@ -113,7 +153,7 @@ public class Gtkdoc.TextWriter {
 
 	private FileStream? stream;
 
-  public TextWriter (string filename, string mode) {
+	public TextWriter (string filename, string mode) {
 		this.filename = filename;
 		this.mode = mode;
 	}
@@ -132,4 +172,3 @@ public class Gtkdoc.TextWriter {
 		stream.putc ('\n');
 	}
 }
-



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]