diff options
author | Rico Tzschichholz <ricotz@ubuntu.com> | 2017-06-27 12:21:44 +0200 |
---|---|---|
committer | Rico Tzschichholz <ricotz@ubuntu.com> | 2017-06-27 12:21:44 +0200 |
commit | 93d9fe647be1f2effc0bfeeec903b5e030182f6c (patch) | |
tree | d28aab5aa5c0e4a149e40b22246246dd2b2d5957 /libvaladoc | |
parent | b4f3f73a01b86fe7f9edde7a3991b493346eea23 (diff) | |
download | vala-93d9fe647be1f2effc0bfeeec903b5e030182f6c.tar.gz |
Prepare valadoc source-tree for merge
Diffstat (limited to 'libvaladoc')
143 files changed, 25485 insertions, 0 deletions
diff --git a/libvaladoc/Makefile.am b/libvaladoc/Makefile.am new file mode 100644 index 000000000..a53b08892 --- /dev/null +++ b/libvaladoc/Makefile.am @@ -0,0 +1,232 @@ +NULL = + +DEFAULT_DRIVER = $(shell $(VALAC) --api-version >/dev/null 2>&1; if [ $$? = 0 ]; then $(VALAC) --api-version; else $(VALAC) --version; fi) + +AM_CFLAGS = \ + -DPACKAGE_ICONDIR=\"$(datadir)/valadoc/icons/\" \ + -DPACKAGE_DATADIR=\"$(libdir)/valadoc\" \ + -DPACKAGE_VERSION=\"$(VERSION)\" \ + -DDEFAULT_DRIVER=\"$(DEFAULT_DRIVER)\" \ + $(LIBGVC_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(LIBGEE_CFLAGS) \ + $(GMODULE_CFLAGS) \ + -g \ + -w \ + $(NULL) + +AM_VALAFLAGS = \ + $(VALAFLAGS) \ + --vapidir $(top_srcdir)/src/vapi/ \ + --basedir $(srcdir) \ + --directory $(builddir) \ + -C \ + -g \ + $(NULL) + +BUILT_SOURCES = \ + libvaladoc.vala.stamp \ + valadoc-1.0.h \ + $(NULL) + +lib_LTLIBRARIES = libvaladoc.la + +libvaladoc_la_VALASOURCES = \ + doclet.vala \ + errorreporter.vala \ + filehelper.vala \ + moduleloader.vala \ + settings.vala \ + markupwriter.vala \ + gtkdocmarkupwriter.vala \ + devhelp-markupwriter.vala \ + ctyperesolver.vala \ + markupsourcelocation.vala \ + markuptokentype.vala \ + markupreader.vala \ + gtkdocrenderer.vala \ + documentation/commentscanner.vala \ + documentation/documentation.vala \ + documentation/documentationparser.vala \ + documentation/wiki.vala \ + documentation/wikiscanner.vala \ + documentation/gtkdoccommentparser.vala \ + documentation/gtkdoccommentscanner.vala \ + documentation/gtkdocmarkdownparser.vala \ + documentation/gtkdocmarkdownscanner.vala \ + documentation/importerhelper.vala \ + documentation/girmetadata.vala \ + importer/documentationimporter.vala \ + importer/valadocdocumentationimporter.vala \ + importer/valadocdocumentationimporterscanner.vala \ + importer/girdocumentationimporter.vala \ + importer/internalidregistrar.vala \ + api/symbolaccessibility.vala \ + api/sourcecomment.vala \ + api/girsourcecomment.vala \ + api/attributeargument.vala \ + api/attribute.vala \ + api/array.vala \ + api/callable.vala \ + api/childsymbolregistrar.vala \ + api/class.vala \ + api/constant.vala \ + api/delegate.vala \ + api/enum.vala \ + api/enumvalue.vala \ + api/errorcode.vala \ + api/errordomain.vala \ + api/field.vala \ + api/formalparameter.vala \ + api/formalparametertype.vala \ + api/interface.vala \ + api/item.vala \ + api/member.vala \ + api/method.vala \ + api/methodbindingtype.vala \ + api/namespace.vala \ + api/node.vala \ + api/nodetype.vala \ + api/ownership.vala \ + api/package.vala \ + api/pointer.vala \ + api/property.vala \ + api/propertyaccessor.vala \ + api/propertyaccessortype.vala \ + api/propertybindingtype.vala \ + api/signal.vala \ + api/signaturebuilder.vala \ + api/sourcefile.vala \ + api/struct.vala \ + api/symbol.vala \ + api/tree.vala \ + api/typeparameter.vala \ + api/typereference.vala \ + api/typesymbol.vala \ + api/browsable.vala \ + api/visitor.vala \ + api/driver.vala \ + content/block.vala \ + content/blockcontent.vala \ + content/comment.vala \ + content/contentfactory.vala \ + content/contentelement.vala \ + content/contentrenderer.vala \ + content/contentvisitor.vala \ + content/embedded.vala \ + content/headline.vala \ + content/inline.vala \ + content/inlinetaglet.vala \ + content/inlinecontent.vala \ + content/wikilink.vala \ + content/link.vala \ + content/list.vala \ + content/listitem.vala \ + content/page.vala \ + content/paragraph.vala \ + content/warning.vala \ + content/note.vala \ + content/resourcelocator.vala \ + content/run.vala \ + content/sourcecode.vala \ + content/styleattributes.vala \ + content/symbollink.vala \ + content/table.vala \ + content/tablecell.vala \ + content/tablerow.vala \ + content/taglet.vala \ + content/text.vala \ + charts/chart.vala \ + charts/chartfactory.vala \ + charts/hierarchychart.vala \ + charts/simplechartfactory.vala \ + parser/manyrule.vala \ + parser/oneofrule.vala \ + parser/optionalrule.vala \ + parser/parser.vala \ + parser/parsercallback.vala \ + parser/rule.vala \ + parser/scanner.vala \ + parser/sequencerule.vala \ + parser/sourcelocation.vala \ + parser/stubrule.vala \ + parser/token.vala \ + parser/tokentype.vala \ + taglets/tagletdeprecated.vala \ + taglets/tagletinheritdoc.vala \ + taglets/tagletinit.vala \ + taglets/tagletlink.vala \ + taglets/tagletparam.vala \ + taglets/tagletreturn.vala \ + taglets/tagletsee.vala \ + taglets/tagletsince.vala \ + taglets/tagletthrows.vala \ + highlighter/scanner.vala \ + highlighter/codescanner.vala \ + highlighter/xmlscanner.vala \ + highlighter/codetoken.vala \ + highlighter/highlighter.vala \ + html/basicdoclet.vala \ + html/htmlchartfactory.vala \ + html/linkhelper.vala \ + html/cssclassresolver.vala \ + html/htmlmarkupwriter.vala \ + html/htmlrenderer.vala \ + $(NULL) + +nodist_libvaladoc_la_SOURCES = \ + $(libvaladoc_la_VALASOURCES:.vala=.c) \ + $(NULL) + +valadoc-1.0.vapi valadoc-1.0.h: libvaladoc.vala.stamp +libvaladoc.vala.stamp: $(libvaladoc_la_VALASOURCES) Makefile + $(VALAC) \ + $(AM_VALAFLAGS) \ + -H valadoc-1.0.h \ + --library valadoc-1.0 \ + --vapi valadoc-1.0.vapi \ + --pkg config \ + --pkg gee-0.8 \ + --pkg gmodule-2.0 \ + --pkg libgvc \ + $(filter %.vala %.c,$^) + touch $@ + +libvaladoc_la_LDFLAGS = -no-undefined + +libvaladoc_la_LIBADD = \ + $(LIBGVC_LIBS) \ + $(LIBGEE_LIBS) \ + $(GLIB_LIBS) \ + $(GMODULE_LIBS) \ + $(NULL) + +libvaladocincludedir = $(includedir)/ +nodist_libvaladocinclude_HEADERS = \ + valadoc-1.0.h \ + $(NULL) + +pkgconfigdir = $(libdir)/pkgconfig +nodist_pkgconfig_DATA = valadoc-1.0.pc + +vapidir = $(datadir)/vala/vapi +nodist_vapi_DATA = \ + valadoc-1.0.vapi \ + valadoc-1.0.deps \ + $(NULL) + +EXTRA_DIST = \ + $(libvaladoc_la_VALASOURCES) \ + valadoc-1.0.deps.in \ + valadoc-1.0.pc.in \ + $(NULL) + +CLEANFILES = \ + $(BUILT_SOURCES) \ + $(nodist_libvaladoc_la_SOURCES) \ + valadoc-1.0.deps \ + valadoc-1.0.pc \ + valadoc-1.0.vapi \ + valadoc-1.0.h \ + $(NULL) + diff --git a/libvaladoc/api/array.vala b/libvaladoc/api/array.vala new file mode 100644 index 000000000..cfbd64291 --- /dev/null +++ b/libvaladoc/api/array.vala @@ -0,0 +1,70 @@ +/* array.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents an array declaration. + */ +public class Valadoc.Api.Array : Item { + + /** + * The element type. + */ + public Item data_type { + set; + get; + } + + public Array (Item parent, void* data) { + base (data); + + this.parent = parent; + } + + private inline bool element_is_owned () { + TypeReference reference = data_type as TypeReference; + if (reference == null) { + return true; + } + + return !reference.is_unowned && !reference.is_weak; + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + SignatureBuilder builder = new SignatureBuilder (); + if (element_is_owned ()) { + builder.append_content (data_type.signature); + } else { + builder.append ("(", false); + builder.append_content (data_type.signature, false); + builder.append (")", false); + } + builder.append ("[]", false); + return builder.get (); + } +} diff --git a/libvaladoc/api/attribute.vala b/libvaladoc/api/attribute.vala new file mode 100644 index 000000000..04fac9b18 --- /dev/null +++ b/libvaladoc/api/attribute.vala @@ -0,0 +1,109 @@ +/* attribute.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Valadoc.Content; +using Gee; + + +public class Valadoc.Api.Attribute : Item { + private ArrayList<AttributeArgument> args = new ArrayList<AttributeArgument> (); + private SourceFile file; + + public string name { + private set; + get; + } + + public Attribute (Node parent, SourceFile file, string name, void* data) { + base (data); + + this.parent = parent; + this.name = name; + this.file = file; + } + + public AttributeArgument? get_argument (string name) { + if (args != null) { + foreach (AttributeArgument arg in args) { + if (arg.name == name) { + return arg; + } + } + } + + return null; + } + + public AttributeArgument add_boolean (string name, bool value, void* data = null) { + AttributeArgument arg = new AttributeArgument.boolean (this, file, name, value, data); + args.add (arg); + return arg; + } + + public AttributeArgument add_integer (string name, int value, void* data = null) { + AttributeArgument arg = new AttributeArgument.integer (this, file, name, value, data); + args.add (arg); + return arg; + } + + public AttributeArgument add_double (string name, double value, void* data = null) { + AttributeArgument arg = new AttributeArgument.double (this, file, name, value, data); + args.add (arg); + return arg; + } + + public AttributeArgument add_string (string name, string value, void* data = null) { + AttributeArgument arg = new AttributeArgument.string (this, file, name, value, data); + args.add (arg); + return arg; + } + + public SourceFile get_source_file () { + return file; + } + + protected override Inline build_signature () { + SignatureBuilder builder = new SignatureBuilder (); + + builder.append_attribute ("["); + builder.append_type_name (name); + + if (args.size > 0) { + builder.append_attribute ("("); + bool first = true; + + foreach (AttributeArgument arg in args) { + if (first == false) { + builder.append_attribute (", "); + } + builder.append_content (arg.signature); + first = false; + } + builder.append_attribute (")"); + } + + builder.append_attribute ("]"); + + return builder.get (); + } +} + diff --git a/libvaladoc/api/attributeargument.vala b/libvaladoc/api/attributeargument.vala new file mode 100644 index 000000000..7bcd0c6d8 --- /dev/null +++ b/libvaladoc/api/attributeargument.vala @@ -0,0 +1,135 @@ +/* attributeargument.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +using Valadoc.Content; +using Gee; + + + +public class Valadoc.Api.AttributeArgument : Item { + public enum Type { + BOOLEAN, + INTEGER, + DOUBLE, + STRING + } + + private SourceFile file; + + public string name { + private set; + get; + } + + public AttributeArgument.Type argument_type { + private set; + get; + } + + public string value { + private set; + get; + } + + public AttributeArgument.boolean (Attribute parent, SourceFile file, string name, bool value, void* data) { + this (parent, file, name, Type.BOOLEAN, value.to_string (), data); + } + + public AttributeArgument.integer (Attribute parent, SourceFile file, string name, int value, void* data) { + this (parent, file, name, Type.INTEGER, value.to_string (), data); + } + + public AttributeArgument.double (Attribute parent, SourceFile file, string name, double value, void* data) { + this (parent, file, name, Type.DOUBLE, value.to_string (), data); + } + + public AttributeArgument.string (Attribute parent, SourceFile file, string name, string value, void* data) { + this (parent, file, name, Type.STRING, value, data); + } + + private AttributeArgument (Attribute parent, SourceFile file, string name, Type type, string value, void* data) { + base (data); + + this.argument_type = type; + this.parent = parent; + this.value = value; + this.file = file; + this.name = name; + } + + public SourceFile get_source_file () { + return file; + } + + public bool get_value_as_boolean () { + assert (argument_type == Type.BOOLEAN); + + bool tmp; + + if (bool.try_parse (value, out tmp)) { + return tmp; + } + + assert_not_reached (); + } + + public int get_value_as_integer () { + assert (argument_type == Type.INTEGER); + + double tmp; + + if (global::double.try_parse (value, out tmp) && tmp >= int.MIN && tmp <= int.MAX) { + return (int) tmp; + } + + assert_not_reached (); + } + + public double get_value_as_double () { + assert (argument_type == Type.DOUBLE); + + double tmp; + + if (global::double.try_parse (value, out tmp)) { + return tmp; + } + + assert_not_reached (); + } + + public string get_value_as_string () { + assert (argument_type == Type.STRING); + + return value; + } + + protected override Inline build_signature () { + SignatureBuilder builder = new SignatureBuilder (); + + builder.append_attribute (name); + builder.append_attribute ("="); + builder.append_literal (value); + + return builder.get (); + } +} diff --git a/libvaladoc/api/browsable.vala b/libvaladoc/api/browsable.vala new file mode 100644 index 000000000..498ba0dd8 --- /dev/null +++ b/libvaladoc/api/browsable.vala @@ -0,0 +1,35 @@ +/* browsable.vala + * + * Copyright (C) 2008 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; + + +/** + * Specifies whether users are able to browse the item. + */ +public interface Valadoc.Api.Browsable : Item { + + /** + * Specifies whether users are able to browse the item. + */ + public abstract bool is_browsable (Settings settings); +} diff --git a/libvaladoc/api/callable.vala b/libvaladoc/api/callable.vala new file mode 100644 index 000000000..60515ee1b --- /dev/null +++ b/libvaladoc/api/callable.vala @@ -0,0 +1,51 @@ +/* callable.vala + * + * Copyright (C) 2012 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +using Valadoc; +using GLib; +using Gee; + + +/** + * Used to translate imported C-documentation + */ +public interface Valadoc.Api.Callable : Symbol { + /** + * The return type of this symbol. + * + * @return The return type of this symbol or null for void + */ + public abstract TypeReference? return_type { + set; + get; + } + + /** + * Used to avoid warnings for implicit parameters + */ + internal abstract string? implicit_array_length_cparameter_name { + get; + set; + } +} + diff --git a/libvaladoc/api/childsymbolregistrar.vala b/libvaladoc/api/childsymbolregistrar.vala new file mode 100644 index 000000000..e044327b0 --- /dev/null +++ b/libvaladoc/api/childsymbolregistrar.vala @@ -0,0 +1,99 @@ +/* childsymbolregistrar.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Valadoc.Api; +using Gee; + + +public class Valadoc.Api.ChildSymbolRegistrar : Visitor { + /** + * {@inheritDoc} + */ + public override void visit_tree (Api.Tree item) { + item.accept_children (this); + } + + /** + * {@inheritDoc} + */ + public override void visit_package (Package item) { + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_namespace (Namespace item) { + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_interface (Interface item) { + Collection<TypeReference> interfaces = item.get_implemented_interface_list (); + foreach (var type_ref in interfaces) { + ((Interface) type_ref.data_type).register_related_interface (item); + } + + if (item.base_type != null) { + ((Class) item.base_type.data_type).register_derived_interface (item); + } + + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_class (Class item) { + Collection<TypeReference> interfaces = item.get_implemented_interface_list (); + foreach (TypeReference type_ref in interfaces) { + ((Interface) type_ref.data_type).register_implementation (item); + } + + if (item.base_type != null) { + ((Class) item.base_type.data_type).register_child_class (item); + } + + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_struct (Struct item) { + if (item.base_type != null) { + ((Struct) item.base_type.data_type).register_child_struct (item); + } + + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_property (Property item) { + item.accept_all_children (this, false); + } +} + diff --git a/libvaladoc/api/class.vala b/libvaladoc/api/class.vala new file mode 100644 index 000000000..8d792a75a --- /dev/null +++ b/libvaladoc/api/class.vala @@ -0,0 +1,361 @@ +/* class.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents a class declaration. + */ +public class Valadoc.Api.Class : TypeSymbol { + private ArrayList<TypeReference> interfaces; + + private string? dbus_name; + private string? take_value_function_cname; + private string? get_value_function_cname; + private string? set_value_function_cname; + private string? unref_function_name; + private string? ref_function_name; + private string? free_function_name; + private string? finalize_function_name; + private string? param_spec_function_name; + private string? type_id; + private string? is_class_type_macro_name; + private string? class_type_macro_name; + private string? class_macro_name; + private string? private_cname; + private string? cname; + + public Class (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? cname, string? private_cname, string? class_macro_name, + string? type_macro_name, string? is_type_macro_name, string? type_cast_macro_name, + string? type_function_name, string? class_type_macro_name, string? is_class_type_macro_name, + string? dbus_name, string? type_id, string? param_spec_function_name, string? ref_function_name, + string? unref_function_name, string? free_function_name, string? finalize_function_name, + string? take_value_function_cname, string? get_value_function_cname, string? set_value_function_cname, + bool is_fundamental, bool is_abstract, bool is_basic_type, void* data) + { + base (parent, file, name, accessibility, comment, type_macro_name, + is_type_macro_name, type_cast_macro_name, type_function_name, is_basic_type, data); + + this.interfaces = new ArrayList<TypeReference> (); + + this.is_class_type_macro_name = is_class_type_macro_name; + this.class_type_macro_name = class_type_macro_name; + this.class_macro_name = class_macro_name; + this.private_cname = private_cname; + this.dbus_name = dbus_name; + this.type_id = type_id; + this.cname = cname; + + this.param_spec_function_name = param_spec_function_name; + + this.unref_function_name = unref_function_name; + this.ref_function_name = ref_function_name; + this.finalize_function_name = finalize_function_name; + this.free_function_name = free_function_name; + + this.take_value_function_cname = take_value_function_cname; + this.get_value_function_cname = get_value_function_cname; + this.set_value_function_cname = set_value_function_cname; + + this.is_fundamental = is_fundamental; + this.is_abstract = is_abstract; + } + + /** + * Specifies the base class. + */ + public TypeReference? base_type { + set; + get; + } + + /** + * Returns the name of this class as it is used in C. + */ + public string? get_cname () { + return cname; + } + + /** + * Returns the name of this class' private data structure as it is used in C. + */ + public string? get_private_cname () { + return private_cname; + } + + /** + * Returns the C symbol representing the runtime type id for this data type. + */ + public string? get_type_id () { + return type_id; + } + + /** + * Returns the C function name that increments the reference count of + * instances of this data type. + * + * @return the name of the C function or null if this data type does not + * support reference counting + */ + public string? get_ref_function_cname () { + return ref_function_name; + } + + /** + * Returns the C function name that decrements the reference count of + * instances of this data type. + * + * @return the name of the C function or null if this data type does not + * support reference counting + */ + public string? get_unref_function_cname () { + return unref_function_name; + } + + /** + * Returns the C function name that frees the + * instances of this data type. + * + * @return the name of the C function or null + */ + public string? get_free_function_name () { + return free_function_name; + } + + /** + * Returns the C function name that finalizes the + * instances of this data type. + * + * @return the name of the C function or null + */ + public string? get_finalize_function_name () { + return finalize_function_name; + } + + /** + * Returns the cname of the GValue parameter spec function. + */ + public string? get_param_spec_function_cname () { + return param_spec_function_name; + } + + /** + * Returns the cname of the GValue setter function. + */ + public string? get_set_value_function_cname () { + return set_value_function_cname; + } + + /** + * Returns the cname of the GValue getter function. + */ + public string? get_get_value_function_cname () { + return get_value_function_cname; + } + + /** + * Returns the cname of the GValue taker function. + */ + public string? get_take_value_function_cname () { + return take_value_function_cname; + } + + /** + * Returns the dbus-name. + */ + public string? get_dbus_name () { + return dbus_name; + } + + /** + * Gets the name of the GType macro which returns the class struct. + */ + public string get_class_macro_name () { + return class_macro_name; + } + + /** + * Gets the name of the GType macro which returns the type of the class. + */ + public string get_class_type_macro_name () { + return class_type_macro_name; + } + + /** + * Gets the name of the GType macro which returns whether a class instance is of a given type. + */ + public string get_is_class_type_macro_name () { + return is_class_type_macro_name; + } + + /** + * Returns a list of all newly implemented interfaces. + * + * @see get_full_implemented_interface_list + */ + public Collection<TypeReference> get_implemented_interface_list () { + return this.interfaces; + } + + private Collection<TypeReference> _full_implemented_interfaces = null; + + /** + * Returns a list of all implemented interfaces. + * + * @see get_implemented_interface_list + */ + public Collection<TypeReference> get_full_implemented_interface_list () { + if (_full_implemented_interfaces == null) { + _full_implemented_interfaces = new LinkedList<TypeReference> (); + _full_implemented_interfaces.add_all (this.interfaces); + + if (base_type != null) { + _full_implemented_interfaces.add_all (((Class) base_type.data_type).get_full_implemented_interface_list ()); + } + } + + return _full_implemented_interfaces; + } + + public void add_interface (TypeReference iface) { + interfaces.add (iface); + } + + /** + * Specifies whether this class is abstract. + */ + public bool is_abstract { + private set; + get; + } + + /** + * Specifies whether this class is fundamental. + */ + public bool is_fundamental { + private set; + get; + } + + public bool is_compact { + get { + return base_type == null && get_attribute ("Compact") != null; + } + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { get { return NodeType.CLASS; } } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_class (this); + } + + private Set<Interface> _known_derived_interfaces = new TreeSet<Interface> (); + private Set<Class> _known_child_classes = new TreeSet<Class> (); + + /** + * Returns a list of all known classes based on this class + */ + public Collection<Class> get_known_child_classes () { + return _known_child_classes.read_only_view; + } + + /** + * Returns a list of all known interfaces based on this class + */ + public Collection<Interface> get_known_derived_interfaces () { + return _known_derived_interfaces.read_only_view; + } + + public void register_derived_interface (Interface iface) { + _known_derived_interfaces.add (iface); + } + + public void register_child_class (Class cl) { + if (this.base_type != null) { + ((Class) this.base_type.data_type).register_child_class (cl); + } + + _known_child_classes.add (cl); + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var signature = new SignatureBuilder (); + + signature.append_keyword (accessibility.to_string ()); + if (is_abstract) { + signature.append_keyword ("abstract"); + } + signature.append_keyword ("class"); + signature.append_symbol (this); + + var type_parameters = get_children_by_type (NodeType.TYPE_PARAMETER, false); + if (type_parameters.size > 0) { + signature.append ("<", false); + bool first = true; + foreach (Item param in type_parameters) { + if (!first) { + signature.append (",", false); + } + signature.append_content (param.signature, false); + first = false; + } + signature.append (">", false); + } + + bool first = true; + if (base_type != null) { + signature.append (":"); + + signature.append_content (base_type.signature); + first = false; + } + + if (interfaces.size > 0) { + if (first) { + signature.append (":"); + } + + foreach (Item implemented_interface in interfaces) { + if (!first) { + signature.append (",", false); + } + signature.append_content (implemented_interface.signature); + first = false; + } + } + + return signature.get (); + } +} + diff --git a/libvaladoc/api/constant.vala b/libvaladoc/api/constant.vala new file mode 100644 index 000000000..351584be4 --- /dev/null +++ b/libvaladoc/api/constant.vala @@ -0,0 +1,81 @@ +/* constant.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + +/** + * Represents a type member with a constant value. + */ +public class Valadoc.Api.Constant : Member { + private string? cname; + + /** + * The data type of this constant. + */ + public TypeReference constant_type { + set; + get; + } + + public Constant (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? cname, void* data) + { + base (parent, file, name, accessibility, comment, data); + + this.cname = cname; + } + + /** + * Returns the name of this constant as it is used in C. + */ + public string get_cname () { + return cname; + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + return new SignatureBuilder () + .append_keyword (accessibility.to_string ()) + .append_keyword ("const") + .append_content (constant_type.signature) + .append_symbol (this) + .get (); + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.CONSTANT; } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_constant (this); + } +} + diff --git a/libvaladoc/api/delegate.vala b/libvaladoc/api/delegate.vala new file mode 100644 index 000000000..cd7e7c552 --- /dev/null +++ b/libvaladoc/api/delegate.vala @@ -0,0 +1,142 @@ +/* delegate.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents a Delegate. + */ +public class Valadoc.Api.Delegate : TypeSymbol, Callable { + private string? cname; + + /** + * {@inheritDoc} + */ + internal string? implicit_array_length_cparameter_name { + get; + set; + } + + + public Delegate (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? cname, bool is_static, void* data) + { + base (parent, file, name, accessibility, comment, null, null, null, null, false, data); + + this.is_static = is_static; + this.cname = cname; + } + + /** + * Returns the name of this delegate as it is used in C. + */ + public string? get_cname () { + return cname; + } + + /** + * {@inheritDoc} + */ + public TypeReference? return_type { + set; + get; + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.DELEGATE; } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_delegate (this); + } + + /** + * Specifies whether this delegate is static + */ + public bool is_static { + private set; + get; + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var signature = new SignatureBuilder (); + + signature.append_keyword (accessibility.to_string ()); + signature.append_keyword ("delegate"); + signature.append_content (return_type.signature); + signature.append_symbol (this); + + var type_parameters = get_children_by_type (NodeType.TYPE_PARAMETER); + if (type_parameters.size > 0) { + signature.append ("<", false); + bool first = true; + foreach (Item param in type_parameters) { + if (!first) { + signature.append (",", false); + } + signature.append_content (param.signature, false); + first = false; + } + signature.append (">", false); + } + + signature.append ("("); + + bool first = true; + foreach (Node param in get_children_by_type (NodeType.FORMAL_PARAMETER, false)) { + if (!first) { + signature.append (",", false); + } + signature.append_content (param.signature, !first); + first = false; + } + + signature.append (")", false); + + var exceptions = get_children_by_types ({NodeType.ERROR_DOMAIN, NodeType.CLASS}); + if (exceptions.size > 0) { + signature.append_keyword ("throws"); + first = true; + foreach (Node param in exceptions) { + if (!first) { + signature.append (",", false); + } + signature.append_type (param); + first = false; + } + } + + return signature.get (); + } +} + diff --git a/libvaladoc/api/driver.vala b/libvaladoc/api/driver.vala new file mode 100644 index 000000000..a43abcff0 --- /dev/null +++ b/libvaladoc/api/driver.vala @@ -0,0 +1,43 @@ +/* driver.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Valadoc.Api; +using Gee; + + +/** + * A plugin register function for drivers + * + * @see ModuleLoader + */ +[CCode (has_target = false)] +public delegate Type Valadoc.DriverRegisterFunction (ModuleLoader module_loader); + + + +public interface Valadoc.Driver : Object { + public abstract void write_gir (Settings settings, ErrorReporter reporter); + + public abstract Api.Tree? build (Settings settings, ErrorReporter reporter); +} + + diff --git a/libvaladoc/api/enum.vala b/libvaladoc/api/enum.vala new file mode 100644 index 000000000..230c0bb64 --- /dev/null +++ b/libvaladoc/api/enum.vala @@ -0,0 +1,72 @@ +/* enum.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents an enum declaration. + */ +public class Valadoc.Api.Enum : TypeSymbol { + private string cname; + + public Enum (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? cname, string? type_macro_name, + string? type_function_name, void* data) + { + base (parent, file, name, accessibility, comment, type_macro_name, null, null, + type_function_name, false, data); + this.cname = cname; + } + + /** + * Returns the name of this enum as it is used in C. + */ + public string? get_cname () { + return cname; + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { get { return NodeType.ENUM; } } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_enum (this); + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + return new SignatureBuilder () + .append_keyword (accessibility.to_string ()) + .append_keyword ("enum") + .append_symbol (this) + .get (); + } +} + diff --git a/libvaladoc/api/enumvalue.vala b/libvaladoc/api/enumvalue.vala new file mode 100644 index 000000000..34804ff55 --- /dev/null +++ b/libvaladoc/api/enumvalue.vala @@ -0,0 +1,115 @@ +/* enumvalue.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents an enum member. + */ +public class Valadoc.Api.EnumValue: Symbol { + private SourceComment? source_comment; + private string? cname; + + public Content.Run default_value { + get; + set; + } + + /** + * Specifies whether the parameter has a default value + */ + public bool has_default_value { + get { + return default_value != null; + } + } + + public EnumValue (Enum parent, SourceFile file, string name, SourceComment? comment, string? cname, void* data) { + base (parent, file, name, parent.accessibility, data); + + this.source_comment = comment; + this.cname = cname; + } + + /** + * {@inheritDoc} + */ + internal override void parse_comments (Settings settings, DocumentationParser parser) { + if (documentation != null) { + return ; + } + + if (source_comment != null) { + documentation = parser.parse (this, source_comment); + } + + base.parse_comments (settings, parser); + } + + /** + * {@inheritDoc} + */ + internal override void check_comments (Settings settings, DocumentationParser parser) { + if (documentation != null) { + parser.check (this, documentation); + } + + base.check_comments (settings, parser); + } + + /** + * Returns the name of this enum value as it is used in C. + */ + public string get_cname () { + return cname; + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { get { return NodeType.ENUM_VALUE; } } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_enum_value (this); + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var builder = new SignatureBuilder () + .append_symbol (this); + + if (has_default_value) { + builder.append ("="); + builder.append_content (default_value); + } + + return builder.get (); + } +} + diff --git a/libvaladoc/api/errorcode.vala b/libvaladoc/api/errorcode.vala new file mode 100644 index 000000000..d7cf55128 --- /dev/null +++ b/libvaladoc/api/errorcode.vala @@ -0,0 +1,108 @@ +/* errorcode.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents an errordomain member in the source code. + */ +public class Valadoc.Api.ErrorCode : Symbol { + private SourceComment? source_comment; + private string? dbus_name; + private string? cname; + + public ErrorCode (ErrorDomain parent, SourceFile file, string name, SourceComment? comment, + string? cname, string? dbus_name, void* data) + { + base (parent, file, name, parent.accessibility, data); + + this.source_comment = comment; + this.dbus_name = dbus_name; + this.cname = cname; + } + + /** + * {@inheritDoc} + */ + internal override void parse_comments (Settings settings, DocumentationParser parser) { + if (documentation != null) { + return ; + } + + if (source_comment != null) { + documentation = parser.parse (this, source_comment); + } + + base.parse_comments (settings, parser); + } + + /** + * {@inheritDoc} + */ + internal override void check_comments (Settings settings, DocumentationParser parser) { + if (documentation != null) { + parser.check (this, documentation); + } + + base.check_comments (settings, parser); + } + + /** + * Returns the name of this class as it is used in C. + */ + public string get_cname () { + return cname; + } + + /** + * Returns the dbus-name. + */ + public string get_dbus_name () { + return dbus_name; + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.ERROR_CODE; } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_error_code (this); + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + return new SignatureBuilder () + .append_symbol (this) + .get (); + } +} + diff --git a/libvaladoc/api/errordomain.vala b/libvaladoc/api/errordomain.vala new file mode 100644 index 000000000..969f294a3 --- /dev/null +++ b/libvaladoc/api/errordomain.vala @@ -0,0 +1,101 @@ +/* errordomain.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents an error domain declaration. + */ +public class Valadoc.Api.ErrorDomain : TypeSymbol { + private string? quark_function_name; + private string? quark_macro_name; + private string? dbus_name; + private string? cname; + + public ErrorDomain (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? cname, string? quark_macro_name, + string? quark_function_name, string? dbus_name, void* data) + { + base (parent, file, name, accessibility, comment, null, null, null, null, false, data); + + this.quark_function_name = quark_function_name; + this.quark_macro_name = quark_macro_name; + this.dbus_name = dbus_name; + this.cname = cname; + } + + /** + * Returns the name of this errordomain as it is used in C. + */ + public string? get_cname () { + return this.cname; + } + + /** + * Returns the dbus-name. + */ + public string? get_dbus_name () { + return dbus_name; + } + + /** + * Gets the name of the quark() function which represents the error domain + */ + public string get_quark_function_name () { + return quark_function_name; + } + + /** + * Gets the name of the quark macro which represents the error domain + */ + public string get_quark_macro_name () { + return quark_macro_name; + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.ERROR_DOMAIN; } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_error_domain (this); + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + return new SignatureBuilder () + .append_keyword (accessibility.to_string ()) + .append_keyword ("errordomain") + .append_symbol (this) + .get (); + } +} + diff --git a/libvaladoc/api/field.vala b/libvaladoc/api/field.vala new file mode 100644 index 000000000..a167c35b8 --- /dev/null +++ b/libvaladoc/api/field.vala @@ -0,0 +1,111 @@ +/* field.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents a field. + */ +public class Valadoc.Api.Field : Member { + private string? cname; + + public Field (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? cname, bool is_static, bool is_volatile, + void* data) + { + base (parent, file, name, accessibility, comment, data); + + this.is_static = !(parent is Namespace) && is_static; + this.is_volatile = is_volatile; + + this.cname = cname; + } + + /** + * Returns the name of this field as it is used in C. + */ + public string? get_cname () { + return cname; + } + + /** + * The field type. + * + * @return The field type or null for void + */ + public TypeReference? field_type { + set; + get; + } + + /** + * Specifies whether the field is static. + */ + public bool is_static { + private set; + get; + } + + /** + * Specifies whether the field is volatile. + */ + public bool is_volatile { + private set; + get; + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var signature = new SignatureBuilder (); + + signature.append_keyword (accessibility.to_string ()); + if (is_static) { + signature.append_keyword ("static"); + } + if (is_volatile) { + signature.append_keyword ("volatile"); + } + + signature.append_content (field_type.signature); + signature.append_symbol (this); + return signature.get (); + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.FIELD; } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_field (this); + } +} + diff --git a/libvaladoc/api/formalparameter.vala b/libvaladoc/api/formalparameter.vala new file mode 100644 index 000000000..82ed872a2 --- /dev/null +++ b/libvaladoc/api/formalparameter.vala @@ -0,0 +1,155 @@ +/* formalparameter.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + +/** + * Represents a formal parameter in method, signal and delegate signatures. + */ +public class Valadoc.Api.FormalParameter : Symbol { + public Content.Run default_value { + get; + set; + } + + /** + * Used to translate imported C-documentation + */ + internal string? implicit_array_length_cparameter_name { + get; + set; + } + + /** + * Used to translate imported C-documentation + */ + internal string? implicit_closure_cparameter_name { + get; + set; + } + + /** + * Used to translate imported C-documentation + */ + internal string? implicit_destroy_cparameter_name { + get; + set; + } + + private FormalParameterType type; + + public FormalParameter (Node parent, SourceFile file, string? name, SymbolAccessibility accessibility, FormalParameterType type, bool ellipsis, void* data) { + base (parent, file, name, accessibility, data); + assert ((name == null && ellipsis) || (name != null && !ellipsis)); + + this.ellipsis = ellipsis; + this.type = type; + } + + /** + * Specifies whether the parameter direction is out + */ + public bool is_out { + get { + return type == FormalParameterType.OUT; + } + } + + /** + * Specifies whether the parameter direction is ref + */ + public bool is_ref { + get { + return type == FormalParameterType.REF; + } + } + + /** + * Specifies whether the parameter has a default value + */ + public bool has_default_value { + get { + return default_value != null; + } + } + + /** + * The parameter type. + * + * @return The parameter type or null for void + */ + public TypeReference? parameter_type { + set; + get; + } + + /** + * Specifies whether the methods accepts a variable number of arguments + */ + public bool ellipsis { + private set; + get; + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.FORMAL_PARAMETER; } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_formal_parameter (this); + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var signature = new SignatureBuilder (); + + if (ellipsis) { + signature.append ("..."); + } else { + if (is_out) { + signature.append_keyword ("out"); + } else if (is_ref) { + signature.append_keyword ("ref"); + } + + signature.append_content (parameter_type.signature); + signature.append (name); + + if (has_default_value) { + signature.append ("="); + signature.append_content (default_value); + } + } + + return signature.get (); + } +} + diff --git a/libvaladoc/api/formalparametertype.vala b/libvaladoc/api/formalparametertype.vala new file mode 100644 index 000000000..d95cc0f5f --- /dev/null +++ b/libvaladoc/api/formalparametertype.vala @@ -0,0 +1,44 @@ +/* formalparametertype.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +public enum Valadoc.Api.FormalParameterType { + IN, + OUT, + REF; + + public string to_string () { + switch (this) { + case FormalParameterType.OUT: + return "out"; + + case FormalParameterType.REF: + return "ref"; + + case FormalParameterType.IN: + return ""; + + } + + assert_not_reached (); + } +} + diff --git a/libvaladoc/api/girsourcecomment.vala b/libvaladoc/api/girsourcecomment.vala new file mode 100644 index 000000000..c935ba39e --- /dev/null +++ b/libvaladoc/api/girsourcecomment.vala @@ -0,0 +1,60 @@ +/* sourcecomment.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +using Gee; + + +/** + * A documentation comment used by valadoc + */ +public class Valadoc.Api.GirSourceComment : SourceComment { + private Map<string, SourceComment> parameters = new HashMap<string, SourceComment> (); + + public string? instance_param_name { set; get; } + public SourceComment? return_comment { set; get; } + public SourceComment? deprecated_comment { set; get; } + public SourceComment? version_comment { get; set; } + public SourceComment? stability_comment { get; set; } + + + public MapIterator<string, SourceComment> parameter_iterator () { + return parameters.map_iterator (); + } + + public void add_parameter_content (string param_name, SourceComment comment) { + this.parameters.set (param_name, comment); + } + + public SourceComment? get_parameter_comment (string param_name) { + if (parameters == null) { + return null; + } + + return parameters.get (param_name); + } + + public GirSourceComment (string content, SourceFile file, int first_line, int first_column, int last_line, int last_column) { + base (content, file, first_line, first_column, last_line, last_column); + } +} + diff --git a/libvaladoc/api/interface.vala b/libvaladoc/api/interface.vala new file mode 100644 index 000000000..c75846ed8 --- /dev/null +++ b/libvaladoc/api/interface.vala @@ -0,0 +1,215 @@ +/* interface.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + + +/** + * Represents a interface declaration in the source code. + */ +public class Valadoc.Api.Interface : TypeSymbol { + private string? interface_macro_name; + private string? dbus_name; + private string? cname; + + + public Interface (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? cname, string? type_macro_name, string? is_type_macro_name, + string? type_cast_macro_name, string? type_function_name, string interface_macro_name, + string? dbus_name, void* data) + { + base (parent, file, name, accessibility, comment, type_macro_name, is_type_macro_name, + type_cast_macro_name, type_function_name, false, data); + + this.interface_macro_name = interface_macro_name; + this.dbus_name = dbus_name; + this.cname = cname; + } + + /** + * A list of preconditioned interfaces + */ + private ArrayList<TypeReference> interfaces = new ArrayList<TypeReference> (); + + /** + * Add a newpreconditioned interface to the list + */ + public void add_interface (TypeReference iface) { + interfaces.add (iface); + } + + /** + * Returns a list of newly preconditioned interfaces + */ + public Collection<TypeReference> get_implemented_interface_list () { + return this.interfaces; + } + + + /** + * A list of all preconditioned interfaces + */ + private Collection<TypeReference> _full_implemented_interfaces = null; + + /** + * Returns a list of all preconditioned interfaces + */ + public Collection<TypeReference> get_full_implemented_interface_list () { + if (_full_implemented_interfaces == null) { + _full_implemented_interfaces = new LinkedList<TypeReference> (); + _full_implemented_interfaces.add_all (this.interfaces); + + if (base_type != null) { + _full_implemented_interfaces.add_all (((Class) base_type.data_type).get_full_implemented_interface_list ()); + } + } + + return _full_implemented_interfaces; + } + + /** + * Returns the name of this interface as it is used in C. + */ + public string? get_cname () { + return cname; + } + + /** + * Returns the dbus-name. + */ + public string? get_dbus_name () { + return dbus_name; + } + + /** + * Gets the name of the GType macro which returns the interface struct. + */ + public string get_interface_macro_name () { + return interface_macro_name; + } + + /** + * A preconditioned class or null + */ + public TypeReference? base_type { + set; + get; + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.INTERFACE; } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_interface (this); + } + + /** + * A list of all known related (sub-)interfaces + */ + private Set<Interface> _known_related_interfaces = new TreeSet<Interface> (); + + /** + * A list of all known implementations of this interface + */ + private Set<Class> _known_implementations = new TreeSet<Class> (); + + /** + * Returns a list of all known implementations of this interface + */ + public Collection<Class> get_known_implementations () { + return _known_implementations; + } + + /** + * Returns a list of all known related (sub-)interfaces + */ + public Collection<Interface> get_known_related_interfaces () { + return _known_related_interfaces; + } + + public void register_related_interface (Interface iface) { + _known_related_interfaces.add (iface); + } + + public void register_implementation (Class cl) { + _known_implementations.add (cl); + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var signature = new SignatureBuilder (); + + signature.append_keyword (accessibility.to_string ()); + signature.append_keyword ("interface"); + signature.append_symbol (this); + + var type_parameters = get_children_by_type (NodeType.TYPE_PARAMETER, false); + if (type_parameters.size > 0) { + signature.append ("<", false); + bool first = true; + foreach (Item param in type_parameters) { + if (!first) { + signature.append (",", false); + } + signature.append_content (param.signature, false); + first = false; + } + signature.append (">", false); + } + + bool first = true; + if (base_type != null) { + signature.append (":"); + + signature.append_content (base_type.signature); + first = false; + } + + if (interfaces.size > 0) { + if (first) { + signature.append (":"); + } + + foreach (Item implemented_interface in interfaces) { + if (!first) { + signature.append (",", false); + } + signature.append_content (implemented_interface.signature); + first = false; + } + } + + return signature.get (); + } +} + diff --git a/libvaladoc/api/item.vala b/libvaladoc/api/item.vala new file mode 100644 index 000000000..63b994736 --- /dev/null +++ b/libvaladoc/api/item.vala @@ -0,0 +1,71 @@ +/* item.vala + * + * Copyright (C) 2008 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Valadoc.Content; +using Gee; + + +/** + * Represents a node in the api tree. + */ +public abstract class Valadoc.Api.Item : Object { + private Inline _signature; + + public void* data { + private set; + get; + } + + /** + * The parent of this item. + */ + public Item parent { + protected set; + get; + } + + public Item (void* data) { + this.data = data; + } + + internal virtual void parse_comments (Settings settings, DocumentationParser parser) { + } + + internal virtual void check_comments (Settings settings, DocumentationParser parser) { + } + + + /** + * The signature of this item. + */ + public Inline signature { + get { + if (_signature == null) { + _signature = build_signature (); + } + return _signature; + } + } + + protected abstract Inline build_signature (); +} + diff --git a/libvaladoc/api/member.vala b/libvaladoc/api/member.vala new file mode 100644 index 000000000..b8271c37b --- /dev/null +++ b/libvaladoc/api/member.vala @@ -0,0 +1,63 @@ +/* member.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +public abstract class Valadoc.Api.Member : Symbol { + private SourceComment? source_comment; + + public Member (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, void* data) + { + base (parent, file, name, accessibility, data); + + this.source_comment = comment; + } + + + /** + * {@inheritDoc} + */ + internal override void parse_comments (Settings settings, DocumentationParser parser) { + if (documentation != null) { + return ; + } + + if (source_comment != null) { + documentation = parser.parse (this, source_comment); + } + + base.parse_comments (settings, parser); + } + + /** + * {@inheritDoc} + */ + internal override void check_comments (Settings settings, DocumentationParser parser) { + if (documentation != null) { + parser.check (this, documentation); + } + + base.check_comments (settings, parser); + } +} diff --git a/libvaladoc/api/method.vala b/libvaladoc/api/method.vala new file mode 100644 index 000000000..ce891e112 --- /dev/null +++ b/libvaladoc/api/method.vala @@ -0,0 +1,266 @@ +/* method.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Valadoc.Content; +using Gee; + + +/** + * Represents a function or a method. + */ +public class Valadoc.Api.Method : Member, Callable { + private string? finish_function_cname; + private string? dbus_result_name; + private string? dbus_name; + private string? cname; + + private MethodBindingType binding_type; + + /** + * {@inheritDoc} + */ + internal string? implicit_array_length_cparameter_name { + get; + set; + } + + + public Method (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? cname, string? dbus_name, string? dbus_result_name, + string? finish_function_cname, MethodBindingType binding_type, bool is_yields, + bool is_dbus_visible, bool is_constructor, void* data) + { + base (parent, file, name, accessibility, comment, data); + + this.finish_function_cname = finish_function_cname; + this.dbus_result_name = dbus_result_name; + this.dbus_name = dbus_name; + this.cname = cname; + + this.binding_type = binding_type; + this.is_dbus_visible = is_dbus_visible; + this.is_constructor = is_constructor; + this.is_yields = is_yields; + } + + /** + * Returns the name of this method as it is used in C. + */ + public string? get_cname () { + return cname; + } + + /** + * Returns the name of the finish function as it is used in C. + */ + public string? get_finish_function_cname () { + return finish_function_cname; + } + + /** + * Returns the dbus-name. + */ + public string get_dbus_name () { + return dbus_name; + } + + public string get_dbus_result_name () { + return dbus_result_name; + } + + /** + * Specifies the virtual or abstract method this method overrides. + */ + public weak Method? base_method { + set; + get; + } + + /** + * {@inheritDoc} + */ + public TypeReference? return_type { + set; + get; + } + + /** + * Specifies whether this method is asynchronous + */ + public bool is_yields { + private set; + get; + } + + /** + * Specifies whether this method is abstract + */ + public bool is_abstract { + get { + return binding_type == MethodBindingType.ABSTRACT; + } + } + + /** + * Specifies whether this method is virtual + */ + public bool is_virtual { + get { + return binding_type == MethodBindingType.VIRTUAL; + } + } + + /** + * Specifies whether this method overrides another one + */ + public bool is_override { + get { + return binding_type == MethodBindingType.OVERRIDE; + } + } + + /** + * Specifies whether this method is static + */ + public bool is_static { + get { + return !is_constructor && binding_type == MethodBindingType.STATIC + && parent is Namespace == false; + } + } + + /** + * Specifies whether this method is a creation method + */ + public bool is_constructor { + private set; + get; + } + + /** + * Specifies whether this method is inline + */ + public bool is_inline { + get { + return binding_type == MethodBindingType.INLINE; + } + } + + /** + * Specifies whether this method is visible for dbus + */ + public bool is_dbus_visible { + private set; + get; + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var signature = new SignatureBuilder (); + + signature.append_keyword (accessibility.to_string ()); + + if (!is_constructor) { + if (is_static) { + signature.append_keyword ("static"); + } else if (is_abstract) { + signature.append_keyword ("abstract"); + } else if (is_override) { + signature.append_keyword ("override"); + } else if (is_virtual) { + signature.append_keyword ("virtual"); + } + if (is_inline) { + signature.append_keyword ("inline"); + } + if (is_yields) { + signature.append_keyword ("async"); + } + + signature.append_content (return_type.signature); + } + + signature.append_symbol (this); + + var type_parameters = get_children_by_type (NodeType.TYPE_PARAMETER, false); + if (type_parameters.size > 0) { + signature.append ("<", false); + bool first = true; + foreach (Item param in type_parameters) { + if (!first) { + signature.append (",", false); + } + signature.append_content (param.signature, false); + first = false; + } + signature.append (">", false); + } + + signature.append ("("); + + bool first = true; + foreach (Node param in get_children_by_type (NodeType.FORMAL_PARAMETER, false)) { + if (!first) { + signature.append (",", false); + } + signature.append_content (param.signature, !first); + first = false; + } + + signature.append (")", false); + + var exceptions = get_children_by_types ({NodeType.ERROR_DOMAIN, NodeType.CLASS}); + if (exceptions.size > 0) { + signature.append_keyword ("throws"); + first = true; + foreach (Node param in exceptions) { + if (!first) { + signature.append (",", false); + } + signature.append_type (param); + first = false; + } + } + + return signature.get (); + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { + return is_constructor ? NodeType.CREATION_METHOD : + is_static ? NodeType.STATIC_METHOD : NodeType.METHOD; + } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_method (this); + } +} + diff --git a/libvaladoc/api/methodbindingtype.vala b/libvaladoc/api/methodbindingtype.vala new file mode 100644 index 000000000..c340c30b6 --- /dev/null +++ b/libvaladoc/api/methodbindingtype.vala @@ -0,0 +1,55 @@ +/* methodbindingtype.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Brosch Florian <flo.brosch@gmail.com> + */ + + +public enum Valadoc.MethodBindingType { + UNMODIFIED, + OVERRIDE, + ABSTRACT, + VIRTUAL, + INLINE, + STATIC; + + public string to_string () { + switch (this) { + case OVERRIDE: + return "override"; + + case ABSTRACT: + return "abstract"; + + case VIRTUAL: + return "virtual"; + + case INLINE: + return "inline"; + + case STATIC: + return "static"; + + case UNMODIFIED: + return ""; + } + + assert_not_reached (); + } +} diff --git a/libvaladoc/api/namespace.vala b/libvaladoc/api/namespace.vala new file mode 100644 index 000000000..c2cba30d4 --- /dev/null +++ b/libvaladoc/api/namespace.vala @@ -0,0 +1,97 @@ +/* namespace.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents a namespace declaration. + */ +public class Valadoc.Api.Namespace : Symbol { + private SourceComment? source_comment; + + public Namespace (Api.Node parent, SourceFile file, string? name, SourceComment? comment, void* data) { + base (parent, file, name, SymbolAccessibility.PUBLIC, data); + + this.source_comment = comment; + } + + + /** + * {@inheritDoc} + */ + internal override void parse_comments (Settings settings, DocumentationParser parser) { + if (documentation != null) { + return ; + } + + if (source_comment != null) { + documentation = parser.parse (this, source_comment); + } + + base.parse_comments (settings, parser); + } + + /** + * {@inheritDoc} + */ + internal override void check_comments (Settings settings, DocumentationParser parser) { + if (documentation != null) { + parser.check (this, documentation); + } + + base.check_comments (settings, parser); + } + + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + return new SignatureBuilder () + .append_keyword (accessibility.to_string ()) + .append_keyword ("namespace") + .append_symbol (this) + .get (); + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { get { return NodeType.NAMESPACE; } } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_namespace (this); + } + + /** + * {@inheritDoc} + */ + public override bool is_browsable (Settings settings) { + return has_visible_children (settings); + } +} + diff --git a/libvaladoc/api/node.vala b/libvaladoc/api/node.vala new file mode 100644 index 000000000..580828ff6 --- /dev/null +++ b/libvaladoc/api/node.vala @@ -0,0 +1,725 @@ +/* node.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * Copyright (C) 20011 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +/** + * Represents a node in the api tree. + */ +public abstract class Valadoc.Api.Node : Item, Browsable, Documentation, Comparable<Node> { + protected bool do_document = false; + private SourceFile file; + + /** + * The name of the node + */ + public string? name { + private set; + get; + } + + public SourceFile get_source_file () { + return file; + } + + /** + * Returns the type of this node + */ + public abstract NodeType node_type { get; } + + private Map<string, Node> per_name_children; + private Map<NodeType, Gee.List<Node>> per_type_children; + + + public Node (Node? parent, SourceFile? file, string? name, void* data) { + base (data); + + per_name_children = new HashMap<string, Node> (); + per_type_children = new HashMap<NodeType, Gee.List<Node>> (); + + if (name != null && (is_keyword (name) || name[0].isdigit ())) { + this.name = "@" + name; + } else { + this.name = name; + } + + this.parent = parent; + this.file = file; + } + + /** + * Visits this node with the specified Visitor. + * + * @param visitor the visitor to be called while traversing + */ + public abstract void accept (Visitor visitor); + + /** + * {@inheritDoc} + */ + // TODO: rename to is_visible + public abstract bool is_browsable (Settings settings); + + /** + * {@inheritDoc} + */ + public string? get_filename () { + if (file == null) { + return null; + } + + return file.relative_path; + } + + public void add_child (Symbol child) { + if (child.name != null) { + if (child.name[0] == '@') { + per_name_children.set (child.name.next_char (), child); + } else { + per_name_children.set (child.name, child); + } + } else { + // Special case for the root namespace + per_name_children.set ("", child); + } + + Gee.List<Node> children = per_type_children.get (child.node_type); + if (children == null) { + children = new ArrayList<Node> (); + per_type_children.set (child.node_type, children); + } + + children.add (child); + } + + /** + * {@inheritDoc} + */ + internal override void parse_comments (Settings settings, DocumentationParser parser) { + do_document = true; + + foreach (Node node in per_name_children.values) { + if (node.is_browsable (settings)) { + node.parse_comments (settings, parser); + } + } + } + + /** + * {@inheritDoc} + */ + internal override void check_comments (Settings settings, DocumentationParser parser) { + + foreach (Node node in per_name_children.values) { + if (node.is_browsable (settings)) { + node.check_comments (settings, parser); + } + } + } + + + /** + * Specifies whether this node has at least one visible child with the given type + * + * @param type a node type + */ + public bool has_visible_children_by_type (NodeType type, Settings settings) { + Gee.List<Node>? all_children = per_type_children.get (type); + if (all_children != null) { + foreach (Node node in all_children) { + if (node.is_browsable (settings)) { + return true; + } + } + } + + return false; + } + + /** + * Specifies whether this node has at least one visible child with the given types + * + * @param types a list of node types + */ + public bool has_visible_children_by_types (NodeType[] types, Settings settings) { + foreach (NodeType type in types) { + if (has_visible_children_by_type (type, settings)) { + return true; + } + } + + return false; + } + + /** + * Specifies whether this node has at least one visible child + */ + public bool has_visible_children (Settings settings) { + return has_visible_children_by_types (per_type_children.keys.to_array (), settings); + } + + /** + * Specifies whether this node has at least one child with the given type + * + * @param type a node type + */ + public bool has_children_by_type (NodeType type) { + Gee.List<Node>? all_children = per_type_children.get (type); + return all_children != null && !all_children.is_empty; + } + + /** + * Specifies whether this node has at least one child with the given types + * + * @param types a list of node types + */ + public bool has_children (NodeType[] types) { + foreach (NodeType type in types) { + if (has_children_by_type (type)) { + return true; + } + } + return false; + } + + /** + * Returns a list of all children with the given type. + * + * @param type a node type + * @param filtered specifies whether nodes which are not browsable should appear in the list + */ + public Gee.List<Node> get_children_by_type (NodeType type, bool filtered = true) { + var children = new ArrayList<Node> (); + + Gee.List<Node> all_children = per_type_children.get (type); + if (all_children != null) { + foreach (Node node in all_children) { + if (node.do_document || !filtered) { + children.add (node); + } + } + } + + return children; + } + + /** + * Returns a list of all children with the given types. + * + * @param types a list of node types + * @param filtered specifies whether nodes which are not browsable should appear in the list + */ + public Gee.List<Node> get_children_by_types (NodeType[] types, bool filtered = true) { + var children = new ArrayList<Node> (); + + foreach (NodeType type in types) { + children.add_all (get_children_by_type (type, filtered)); + } + + return children; + } + + /** + * Visits all children of this node with the given type with the specified Visitor. + * + * @param type a node type + * @param visitor the visitor to be called while traversing + * @param filtered specifies whether nodes which are not browsable should appear in the list + */ + public void accept_children_by_type (NodeType type, Visitor visitor, bool filtered = true) { + Gee.List<Node> all_children = per_type_children.get (type); + if (all_children != null) { + foreach (Node node in all_children) { + if (node.do_document || !filtered) { + node.accept (visitor); + } + } + } + } + + /** + * Visits all children of this node with the given types with the specified Visitor. + * + * @param types a list of node types + * @param visitor the visitor to be called while traversing + * @param filtered specifies whether nodes which are not browsable should appear in the list + */ + public void accept_children (NodeType[] types, Visitor visitor, bool filtered = true) { + foreach (NodeType type in types) { + accept_children_by_type (type, visitor, filtered); + } + } + + /** + * Visits all children of this node with the specified Visitor. + * + * @param visitor the visitor to be called while traversing + * @param filtered specifies whether nodes which are not browsable should appear in the list + */ + public void accept_all_children (Visitor visitor, bool filtered = true) { + foreach (Gee.List<Node> children in per_type_children.values) { + foreach (Node node in children) { + if (node.do_document || !filtered) { + node.accept (visitor); + } + } + } + } + + public Node? find_by_name (string name) { + if (name[0] == '@') { + return per_name_children.get (name.next_char ()); + } else { + return per_name_children.get (name); + } + } + + private Namespace? _nspace = null; + private Package? _package = null; + private string _full_name = null; + + /** + * The corresponding namespace + */ + public Namespace? nspace { + get { + if (this._nspace == null) { + Api.Item ast = this; + while (ast is Valadoc.Api.Namespace == false) { + ast = ast.parent; + if (ast == null) { + return null; + } + } + this._nspace = (Valadoc.Api.Namespace)ast; + } + return this._nspace; + } + } + + /** + * The corresponding package such as a vapi or gir file + */ + public Package? package { + get { + if (this._package == null) { + Api.Item ast = this; + while (ast is Valadoc.Api.Package == false) { + ast = ast.parent; + if (ast == null) { + return null; + } + } + this._package = (Valadoc.Api.Package)ast; + } + return this._package; + } + } + + public Content.Comment? documentation { + internal set; + get; + } + + /** + * Returns canonicalized absolute name (GLib.FileStream for instance) + */ + public string? get_full_name () { + if (this._full_name == null) { + if (this.name == null) { + return null; + } + + GLib.StringBuilder full_name = new GLib.StringBuilder (this.name); + + if (this.parent != null) { + for (Item pos = this.parent; pos is Package == false ; pos = pos.parent) { + string name = ((Node)pos).name; + if (name != null) { + full_name.prepend_unichar ('.'); + full_name.prepend (name); + } + } + } + this._full_name = full_name.str; + } + return this._full_name; + } + + /** + * A comparison function used to sort nodes in alphabetical order + */ + public int compare_to (Node node) { + return strcmp (name, node.name); + } + + private bool is_keyword (string name) { + switch (name[0]) { + case 'a': + switch (name[1]) { + case 'b': + return name == "abstract"; + + case 's': + if (name[2] == '\0') { + return true; + } + + return name == "async"; + } + break; + + case 'b': + switch (name[1]) { + case 'a': + return name == "base"; + + case 'r': + return name == "break"; + } + break; + + case 'c': + switch (name[1]) { + case 'a': + switch (name[2]) { + case 's': + return name == "case"; + + case 't': + return name == "catch"; + } + break; + + case 'l': + return name == "class"; + + case 'o': + if (name[2] != 'n') { + return false; + } + + switch (name[3]) { + case 's': + if (name[4] == 't') { + switch (name[5]) { + case '\0': + return true; + + case 'r': + return name == "construct"; + } + } + break; + + case 't': + return name == "continue"; + } + break; + } + break; + + case 'd': + switch (name[1]) { + case 'e': + switch (name[2]) { + case 'f': + return name == "default"; + + case 'l': + if (name[3] != 'e') { + return false; + } + + switch (name[4]) { + case 'g': + return name == "delegate"; + + case 't': + return name == "delete"; + } + break; + } + break; + + case 'o': + return name[2] == '\0'; + + case 'y': + return name == "dynamic"; + } + break; + + case 'e': + switch (name[1]) { + case 'l': + return name == "else"; + + case 'n': + switch (name[2]) { + case 's': + return name == "ensures"; + + case 'u': + return name == "enum"; + } + break; + + case 'r': + return name == "errordomain"; + + case 'x': + return name == "extern"; + } + break; + + case 'f': + switch (name[1]) { + case 'a': + return name == "false"; + + case 'i': + return name == "finally"; + + case 'o': + if (name[2] != 'r') { + return false; + } + + switch (name[3]) { + case '\0': + return true; + + case 'e': + return name == "foreach"; + } + break; + } + break; + + case 'g': + return name == "get"; + + case 'i': + switch (name[1]) { + case 'f': + return name[2] == '\0'; + + case 'n': + switch (name[2]) { + case '\0': + return true; + + case 'l': + return name == "inline"; + + case 't': + return name == "interface" || name == "internal"; + } + break; + + case 's': + return name[2] == '\0'; + } + break; + + case 'l': + return name == "lock"; + + case 'n': + switch (name[1]) { + case 'a': + return name == "namespace"; + + case 'e': + return name == "new"; + + case 'u': + return name == "null"; + } + break; + + case 'o': + switch (name[1]) { + case 'u': + return name == "out"; + + case 'v': + return name == "override"; + + case 'w': + return name == "owned"; + } + break; + + case 'p': + switch (name[1]) { + case 'a': + return name == "params"; + + case 'r': + switch (name[2]) { + case 'i': + return name == "private"; + + case 'o': + return name == "protected"; + } + break; + case 'u': + return name == "public"; + } + break; + + case 'r': + if (name[1] != 'e') { + return false; + } + + switch (name[2]) { + case 'f': + return name[3] == '\0'; + + case 'q': + return name == "requires"; + + case 't': + return name == "return"; + } + break; + + case 's': + switch (name[1]) { + case 'e': + switch (name[2]) { + case 'a': + return name == "sealed"; + + case 't': + return name[3] == '\0'; + } + break; + + case 'i': + switch (name[2]) { + case 'g': + return name == "signal"; + case 'z': + return name == "sizeof"; + } + break; + + case 't': + switch (name[2]) { + case 'a': + return name == "static"; + + case 'r': + return name == "struct"; + } + break; + + case 'w': + return name == "switch"; + } + break; + + case 't': + switch (name[1]) { + case 'h': + switch (name[2]) { + case 'i': + return name == "this"; + + case 'r': + if (name[3] == 'o' && name[4] == 'w') { + return name[5] == '\0' || (name[5] == 's' && name[6] == '\0'); + } + break; + } + break; + + case 'r': + switch (name[2]) { + case 'u': + return name == "true"; + + case 'y': + return name[3] == '\0'; + } + break; + + case 'y': + return name == "typeof"; + } + break; + + case 'u': + switch (name[1]) { + case 'n': + return name == "unowned"; + + case 's': + return name == "using"; + } + break; + + case 'v': + switch (name[1]) { + case 'a': + return name == "var"; + + case 'i': + return name == "virtual"; + + case 'o': + switch (name[2]) { + case 'i': + return name == "void"; + + case 'l': + return name == "volatile"; + } + break; + } + break; + + case 'w': + switch (name[1]) { + case 'e': + return name == "weak"; + + case 'h': + return name == "while"; + } + break; + + case 'y': + return name == "yield"; + } + + return false; + } +} + diff --git a/libvaladoc/api/nodetype.vala b/libvaladoc/api/nodetype.vala new file mode 100644 index 000000000..1fbc22e66 --- /dev/null +++ b/libvaladoc/api/nodetype.vala @@ -0,0 +1,119 @@ +/* node.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2007-20012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +/** + * Specifies the context of a node. + */ +public enum Valadoc.Api.NodeType { + CLASS, + CONSTANT, + CREATION_METHOD, + DELEGATE, + ENUM, + ENUM_VALUE, + ERROR_CODE, + ERROR_DOMAIN, + FIELD, + FORMAL_PARAMETER, + INTERFACE, + METHOD, + NAMESPACE, + PACKAGE, + PROPERTY, + PROPERTY_ACCESSOR, + SIGNAL, + STATIC_METHOD, + STRUCT, + TYPE_PARAMETER; + + public string to_string () { + switch (this) { + case CLASS: + return "CLASS"; + + case CONSTANT: + return "CONSTANT"; + + case CREATION_METHOD: + return "CREATION_METHOD"; + + case DELEGATE: + return "DELEGATE"; + + case ENUM: + return "ENUM"; + + case ENUM_VALUE: + return "ENUM_VALUE"; + + case ERROR_CODE: + return "ERROR_CODE"; + + case ERROR_DOMAIN: + return "ERROR_DOMAIN"; + + case FIELD: + return "FIELD"; + + case FORMAL_PARAMETER: + return "FORMAL_PARAMETER"; + + case INTERFACE: + return "INTERFACE"; + + case METHOD: + return "METHOD"; + + case NAMESPACE: + return "NAMESPACE"; + + case PACKAGE: + return "PACKAGE"; + + case PROPERTY: + return "PROPERTY"; + + case PROPERTY_ACCESSOR: + return "PROPERTY_ACCESSOR"; + + case SIGNAL: + return "SIGNAL"; + + case STATIC_METHOD: + return "STATIC_METHOD"; + + case STRUCT: + return "STRUCT"; + + case TYPE_PARAMETER: + return "TYPE_PARAMETER"; + + default: + assert_not_reached (); + } + } +} + diff --git a/libvaladoc/api/ownership.vala b/libvaladoc/api/ownership.vala new file mode 100644 index 000000000..eb4f5b0fc --- /dev/null +++ b/libvaladoc/api/ownership.vala @@ -0,0 +1,47 @@ +/* ownership.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +public enum Valadoc.Api.Ownership { + DEFAULT, + UNOWNED, + OWNED, + WEAK; + + public string to_string () { + switch (this) { + case Ownership.UNOWNED: + return "unowned"; + + case Ownership.OWNED: + return "owned"; + + case Ownership.WEAK: + return "weak"; + + case Ownership.DEFAULT: + return ""; + } + + assert_not_reached (); + } +} diff --git a/libvaladoc/api/package.vala b/libvaladoc/api/package.vala new file mode 100644 index 000000000..8c2cad184 --- /dev/null +++ b/libvaladoc/api/package.vala @@ -0,0 +1,139 @@ +/* package.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; +using Valadoc.Importer; + +public class Valadoc.Api.Package : Node { + + /** + * Specifies whether this package is a dependency + */ + public bool is_package { + private set; + get; + } + + internal void set_dependency_list (ArrayList<Package> list) { + this._dependencies = list; + } + + private ArrayList<Package> _dependencies; + + /** + * Returns a list with all dependencies + */ + public Collection<Package> get_full_dependency_list () { + ArrayList<Package> list = new ArrayList<Package> (); + + if (this._dependencies == null) { + return list.read_only_view; + } + + foreach (Package pkg in this._dependencies) { + if (list.contains ( pkg ) == false) { + list.add (pkg); + } + + var pkg_list = pkg.get_full_dependency_list (); + foreach (Package pkg2 in pkg_list) { + if (list.contains (pkg2) == false) { + list.add (pkg2); + } + } + } + return list.read_only_view; + } + + public Collection<Package> get_dependency_list () { + if (this._dependencies == null) { + return Collection.empty<Package> (); + } + + return this._dependencies.read_only_view; + } + + public Package (string name, bool is_package, void* data) { + base (null, null, name, data); + + this.is_package = is_package; + this.parent = null; + } + + // <version, symbols> + private HashMap<string?, ArrayList<Symbol>> deprecated; + + internal void register_deprecated_symbol (Symbol symbol, string? version) { + if (deprecated == null) { + deprecated = new HashMap<string?, ArrayList<Symbol>> (); + } + + ArrayList<Symbol> list = deprecated.get (version); + if (list == null) { + list = new ArrayList<Symbol> (); + deprecated.set (version, list); + } + + list.add (symbol); + } + + public Map<string?, Collection<Symbol>> get_deprecated_symbols () { + if (deprecated == null) { + return Map<string?, Collection<Symbol>>.empty<string?, Collection<Symbol>> (); + } + + return deprecated; + } + + /** + * {@inheritDoc} + */ + public override bool is_browsable (Settings settings) { + return !(this.is_package && settings.with_deps == false); + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.PACKAGE; } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_package (this); + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + return new SignatureBuilder () + .append_keyword ("package") + .append (name) + .get (); + } +} + diff --git a/libvaladoc/api/pointer.vala b/libvaladoc/api/pointer.vala new file mode 100644 index 000000000..aec541bb8 --- /dev/null +++ b/libvaladoc/api/pointer.vala @@ -0,0 +1,55 @@ +/* pointer.vala + * + * Copyright (C) 2008 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents a pointer declaration. + */ +public class Valadoc.Api.Pointer : Item { + + /** + * The type the pointer is referring to. + */ + public Item data_type { + set; + get; + } + + public Pointer (Item parent, void* data) { + base (data); + + this.parent = parent; + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + return new SignatureBuilder () + .append_content (data_type.signature) + .append ("*", false) + .get (); + } +} diff --git a/libvaladoc/api/property.vala b/libvaladoc/api/property.vala new file mode 100644 index 000000000..556c7c9bf --- /dev/null +++ b/libvaladoc/api/property.vala @@ -0,0 +1,202 @@ +/* property.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents a property declaration. + */ +public class Valadoc.Api.Property : Member { + private PropertyBindingType binding_type; + private string? dbus_name; + private string? cname; + + public Property (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? cname, string? dbus_name, bool is_dbus_visible, + PropertyBindingType binding_type, void* data) + { + base (parent, file, name, accessibility, comment, data); + + this.is_dbus_visible = is_dbus_visible; + this.binding_type = binding_type; + + this.dbus_name = dbus_name; + this.cname = cname; + } + + /** + * Returns the name of this method as it is used in C. + */ + public string? get_cname () { + return cname; + } + + /** + * Returns the dbus-name. + */ + public string get_dbus_name () { + return dbus_name; + } + + /** + * The property type. + * + * @return The property type or null for void + */ + public TypeReference? property_type { + set; + get; + } + + /** + * Specifies whether the property is virtual. + */ + public bool is_virtual { + get { + return binding_type == PropertyBindingType.VIRTUAL; + } + } + + /** + * Specifies whether the property is abstract. + */ + public bool is_abstract { + get { + return binding_type == PropertyBindingType.ABSTRACT; + } + } + + /** + * Specifies whether the property is override. + */ + public bool is_override { + get { + return binding_type == PropertyBindingType.OVERRIDE; + } + } + + /** + * Specifies whether the property is visible. + */ + public bool is_dbus_visible { + private set; + get; + } + + public PropertyAccessor? setter { + internal set; + get; + } + + public PropertyAccessor? getter { + internal set; + get; + } + + /** + * Specifies the virtual or abstract property this property overrides. + */ + public Property base_property { + set; + get; + } + + /** + * {@inheritDoc} + */ + internal override void parse_comments (Settings settings, DocumentationParser parser) { + if (getter != null && getter.is_browsable (settings)) { + getter.parse_comments (settings, parser); + } + + if (setter != null && setter.is_browsable (settings)) { + setter.parse_comments (settings, parser); + } + + base.parse_comments (settings, parser); + } + + /** + * {@inheritDoc} + */ + internal override void check_comments (Settings settings, DocumentationParser parser) { + if (getter != null && getter.is_browsable (settings)) { + getter.check_comments (settings, parser); + } + + if (setter != null && setter.is_browsable (settings)) { + setter.check_comments (settings, parser); + } + + base.check_comments (settings, parser); + } + + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var signature = new SignatureBuilder (); + + signature.append_keyword (accessibility.to_string ()); + if (is_abstract) { + signature.append_keyword ("abstract"); + } else if (is_override) { + signature.append_keyword ("override"); + } else if (is_virtual) { + signature.append_keyword ("virtual"); + } + + signature.append_content (property_type.signature); + signature.append_symbol (this); + signature.append ("{"); + + if (setter != null && setter.do_document) { + signature.append_content (setter.signature); + } + + if (getter != null && getter.do_document) { + signature.append_content (getter.signature); + } + + signature.append ("}"); + + return signature.get (); + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.PROPERTY; } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_property (this); + } +} + diff --git a/libvaladoc/api/propertyaccessor.vala b/libvaladoc/api/propertyaccessor.vala new file mode 100644 index 000000000..0a79dd3fc --- /dev/null +++ b/libvaladoc/api/propertyaccessor.vala @@ -0,0 +1,134 @@ +/* propertyaccessor.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents a get or set accessor of a property. + */ +public class Valadoc.Api.PropertyAccessor : Symbol { + private PropertyAccessorType type; + private Ownership ownership; + private string? cname; + + public PropertyAccessor (Property parent, SourceFile file, string name, SymbolAccessibility accessibility, + string? cname, PropertyAccessorType type, Ownership ownership, void* data) + { + base (parent, file, name, accessibility, data); + + this.ownership = ownership; + this.cname = cname; + this.type = type; + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.PROPERTY_ACCESSOR; } + } + + /** + * Returns the name of this property accessor as it is used in C. + */ + public string? get_cname () { + return cname; + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + } + + /** + * Specifies whether this accessor may be used to construct the property. + */ + public bool is_construct { + get { + return (type & PropertyAccessorType.CONSTRUCT) != 0; + } + } + + /** + * Specifies whether this accessor is a setter. + */ + public bool is_set { + get { + return (type & PropertyAccessorType.SET) != 0; + } + } + + /** + * Specifies whether this accessor is a getter. + */ + public bool is_get { + get { + return (type & PropertyAccessorType.GET) != 0; + } + } + + /** + * Specifies whether the property is owned. + */ + public bool is_owned { + get { + return ownership == Ownership.OWNED; + } + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var signature = new SignatureBuilder (); + + // FIXME + if (!do_document) { + return signature.get (); + } + + if (((Property) parent).accessibility != accessibility) { + signature.append_keyword (accessibility.to_string ()); + } + + if (is_set || is_construct) { + if (is_construct) { + signature.append_keyword ("construct"); + } + if (is_set) { + signature.append_keyword ("set"); + } + } else if (is_get) { + if (is_owned) { + signature.append_keyword ("owned"); + } + signature.append_keyword ("get"); + } + signature.append (";", false); + + return signature.get (); + } +} + diff --git a/libvaladoc/api/propertyaccessortype.vala b/libvaladoc/api/propertyaccessortype.vala new file mode 100644 index 000000000..609815ae0 --- /dev/null +++ b/libvaladoc/api/propertyaccessortype.vala @@ -0,0 +1,42 @@ +/* propertyaccessor.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +public enum Valadoc.Api.PropertyAccessorType { + CONSTRUCT = 1 << 0, + SET = 1 << 1, + GET = 1 << 2; + + public string to_string () { + if ((this & PropertyAccessorType.CONSTRUCT) != 0) { + if ((this & PropertyAccessorType.SET) != 0) { + return "construct set"; + } + return "construct"; + } else if ((this & PropertyAccessorType.SET) != 0) { + return "set"; + } else if ((this & PropertyAccessorType.GET) != 0) { + return "get"; + } + + assert_not_reached (); + } +} diff --git a/libvaladoc/api/propertybindingtype.vala b/libvaladoc/api/propertybindingtype.vala new file mode 100644 index 000000000..a8ce1617b --- /dev/null +++ b/libvaladoc/api/propertybindingtype.vala @@ -0,0 +1,48 @@ +/* propertybindingtype.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +public enum Valadoc.Api.PropertyBindingType { + UNMODIFIED, + OVERRIDE, + ABSTRACT, + VIRTUAL; + + public string to_string () { + switch (this) { + case OVERRIDE: + return "override"; + + case ABSTRACT: + return "abstract"; + + case VIRTUAL: + return "virtual"; + + case UNMODIFIED: + return ""; + } + + assert_not_reached (); + } +} + diff --git a/libvaladoc/api/signal.vala b/libvaladoc/api/signal.vala new file mode 100644 index 000000000..4bbbb2320 --- /dev/null +++ b/libvaladoc/api/signal.vala @@ -0,0 +1,146 @@ +/* signal.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents an signal. + */ +public class Valadoc.Api.Signal : Member, Callable { + private string? default_impl_cname; + private string? dbus_name; + private string? cname; + + + /** + * {@inheritDoc} + */ + internal string? implicit_array_length_cparameter_name { + get; + set; + } + + + public Signal (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? cname, string? default_impl_cname, string? dbus_name, bool is_dbus_visible, + bool is_virtual, void* data) + { + base (parent, file, name, accessibility, comment, data); + + this.default_impl_cname = default_impl_cname; + this.dbus_name = dbus_name; + this.cname = cname; + + this.is_dbus_visible = is_dbus_visible; + this.is_virtual = is_virtual; + } + + /** + * Returns the name of this signal as it is used in C. + */ + public string? get_cname () { + return cname; + } + + public string? get_default_impl_cname () { + return default_impl_cname; + } + + /** + * Returns the dbus-name. + */ + public string get_dbus_name () { + return dbus_name; + } + + /** + * {@inheritDoc} + */ + public TypeReference? return_type { + set; + get; + } + + /** + * Specifies whether this signal is virtual + */ + public bool is_virtual { + private set; + get; + } + + /** + * Specifies whether this signal is visible for dbus + */ + public bool is_dbus_visible { + private set; + get; + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var signature = new SignatureBuilder (); + + signature.append_keyword (accessibility.to_string ()); + if (is_virtual) { + signature.append_keyword ("virtual"); + } + + signature.append_keyword ("signal"); + + signature.append_content (return_type.signature); + signature.append_symbol (this); + signature.append ("("); + + bool first = true; + foreach (Node param in get_children_by_type (NodeType.FORMAL_PARAMETER, false)) { + if (!first) { + signature.append (",", false); + } + signature.append_content (param.signature, !first); + first = false; + } + + signature.append (")", false); + + return signature.get (); + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.SIGNAL; } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_signal (this); + } +} + diff --git a/libvaladoc/api/signaturebuilder.vala b/libvaladoc/api/signaturebuilder.vala new file mode 100644 index 000000000..158bd6e41 --- /dev/null +++ b/libvaladoc/api/signaturebuilder.vala @@ -0,0 +1,180 @@ +/* signaturebuilder.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Valadoc.Content; + + +/** + * Builds up a signature from the given items. + */ +public class Valadoc.Api.SignatureBuilder { + private Run run; + private Inline last_appended; + + /** + * Creates a new SignatureBuilder + */ + public SignatureBuilder () { + run = new Run (Run.Style.NONE); + } + + private void append_text (string text) { + if (last_appended is Text) { + ((Text) last_appended).content += text; + } else { + run.content.add (last_appended = new Text (text)); + } + } + + /** + * Adds text onto the end of the builder. + * + * @param text a string + * @param spaced add a space at the front of the string if necessary + * @return this + */ + public SignatureBuilder append (string text, bool spaced = true) { + string content = (last_appended != null && spaced ? " " : "") + text; + append_text (content); + return this; + } + + /** + * Adds text onto the end of the builder. + * + * @param text a string + * @param spaced add a space at the front of the string if necessary + * @return this + */ + public SignatureBuilder append_attribute (string text, bool spaced = true) { + string content = (last_appended != null && spaced ? " " : "") + text; + append_text (content); + return this; + } + + /** + * Adds highlighted text onto the end of the builder. + * + * @param text a string + * @param spaced add a space at the front of the string if necessary + * @return this + */ + public SignatureBuilder append_highlighted (string text, bool spaced = true) { + string content = (last_appended != null && spaced ? " " : "") + text; + Run inner = new Run (Run.Style.ITALIC); + inner.content.add (new Text (content)); + return append_content (inner, spaced); + } + + /** + * Adds a Inline onto the end of the builder. + * + * @param content a content + * @param spaced add a space at the front of the inline if necessary + * @return this + */ + public SignatureBuilder append_content (Inline content, bool spaced = true) { + if (last_appended != null && spaced) { + append_text (" "); + } + run.content.add (last_appended = content); + return this; + } + + /** + * Adds a keyword onto the end of the builder. + * + * @param keyword a keyword + * @param spaced add a space at the front of the keyword if necessary + * @return this + */ + public SignatureBuilder append_keyword (string keyword, bool spaced = true) { + Run inner = new Run (Run.Style.LANG_KEYWORD); + inner.content.add (new Text (keyword)); + return append_content (inner, spaced); + } + + /** + * Adds a symbol onto the end of the builder. + * + * @param node a node + * @param spaced add a space at the front of the node if necessary + * @return this + */ + public SignatureBuilder append_symbol (Node node, bool spaced = true) { + Run inner = new Run (Run.Style.BOLD); + inner.content.add (new SymbolLink (node, node.name)); + return append_content (inner, spaced); + } + + /** + * Adds a type onto the end of the builder. + * + * @param node a node + * @param spaced add a space at the front of the node if necessary + * @return this + */ + public SignatureBuilder append_type (Node node, bool spaced = true) { + Run.Style style = Run.Style.LANG_TYPE; + if (node is TypeSymbol && ((TypeSymbol)node).is_basic_type) { + style = Run.Style.LANG_BASIC_TYPE; + } + + Run inner = new Run (style); + inner.content.add (new SymbolLink (node, node.name)); + return append_content (inner, spaced); + } + + /** + * Adds a type name onto the end of the builder. + * + * @param name a type name + * @param spaced add a space at the front of the type name if necessary + * @return this + */ + public SignatureBuilder append_type_name (string name, bool spaced = true) { + Run inner = new Run (Run.Style.LANG_TYPE); + inner.content.add (new Text (name)); + return append_content (inner, spaced); + } + + /** + * Adds a literal onto the end of the builder. + * + * @param literal a literal + * @param spaced add a space at the front of the literal if necessary + * @return this + */ + public SignatureBuilder append_literal (string literal, bool spaced = true) { + Run inner = new Run (Run.Style.LANG_LITERAL); + inner.content.add (new Text (literal)); + return append_content (inner, spaced); + } + + /** + * The content + */ + public new Run get () { + return run; + } +} + diff --git a/libvaladoc/api/sourcecomment.vala b/libvaladoc/api/sourcecomment.vala new file mode 100644 index 000000000..df6d1e1da --- /dev/null +++ b/libvaladoc/api/sourcecomment.vala @@ -0,0 +1,86 @@ +/* sourcecomment.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + + +/** + * A documentation comment used by valadoc + */ +public class Valadoc.Api.SourceComment { + public SourceFile file { + private set; + get; + } + + /** + * The text describing the referenced source code. + */ + public string content { + private set; + get; + } + + /** + * The first line number of the referenced source code. + */ + public int first_line { + private set; + get; + } + + /** + * The first column number of the referenced source code. + */ + public int first_column { + private set; + get; + } + + /** + * The last line number of the referenced source code. + */ + public int last_line { + private set; + get; + } + + /** + * The last column number of the referenced source code. + */ + public int last_column { + private set; + get; + } + + public SourceComment (string content, SourceFile file, int first_line, int first_column, + int last_line, int last_column) + { + this.first_column = first_column; + this.last_column = last_column; + this.first_line = first_line; + this.last_line = last_line; + this.content = content; + this.file = file; + } +} + + diff --git a/libvaladoc/api/sourcefile.vala b/libvaladoc/api/sourcefile.vala new file mode 100644 index 000000000..46d1801be --- /dev/null +++ b/libvaladoc/api/sourcefile.vala @@ -0,0 +1,60 @@ +/* sourcefile.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Brosch Florian <flo.brosch@gmail.com> + */ + +/** + * Represents a source file + */ +public class Valadoc.Api.SourceFile : Object { + + public Package package { + private set; + get; + } + + public string relative_path { + private set; + get; + } + + public string? relative_c_path { + private set; + get; + } + + public string get_name () { + return Path.get_basename (relative_path); + } + + public void* data { + private set; + get; + } + + public SourceFile (Package package, string relative_path, string? relative_c_path, void* data) { + this.relative_c_path = relative_c_path; + this.relative_path = relative_path; + this.package = package; + this.data = data; + } +} + + diff --git a/libvaladoc/api/struct.vala b/libvaladoc/api/struct.vala new file mode 100644 index 000000000..e1df479c3 --- /dev/null +++ b/libvaladoc/api/struct.vala @@ -0,0 +1,174 @@ +/* struct.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents a struct declaration. + */ +public class Valadoc.Api.Struct : TypeSymbol { + private string? dup_function_cname; + private string? copy_function_cname; + private string? free_function_cname; + private string? destroy_function_cname; + private string? type_id; + private string? cname; + + public Struct (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? cname, string? type_macro_name, + string? type_function_name, string? type_id, string? dup_function_cname, + string? copy_function_cname, string? destroy_function_cname, + string? free_function_cname, bool is_basic_type, void* data) + { + base (parent, file, name, accessibility, comment, type_macro_name, null, null, + type_function_name, is_basic_type, data); + + this.dup_function_cname = dup_function_cname; + this.copy_function_cname = copy_function_cname; + this.free_function_cname = free_function_cname; + this.destroy_function_cname = destroy_function_cname; + + this.cname = cname; + } + + /** + * Specifies the base struct. + */ + public TypeReference? base_type { + set; + get; + } + + + /** + * Returns the name of this struct as it is used in C. + */ + public string? get_cname () { + return cname; + } + + /** + * Returns the C symbol representing the runtime type id for this data type. + */ + public string? get_type_id () { + return type_id; + } + + /** + * Returns the C function name that duplicates instances of this data + * type. + */ + public string? get_dup_function_cname () { + return dup_function_cname; + } + + /** + * Returns the C function name that copies instances of this data + * type. + */ + public string? get_copy_function_cname () { + return copy_function_cname; + } + + /** + * Returns the C function name that frees instances of this data type. + */ + public string? get_free_function_cname () { + return free_function_cname; + } + + /** + * Returns the C function name that destroys instances of this data type. + */ + public string? get_destroy_function_cname () { + return destroy_function_cname; + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { + get { return NodeType.STRUCT; } + } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_struct (this); + } + + + private Set<Struct> _known_child_structs = new TreeSet<Struct> (); + + /** + * Returns a list of all known structs based on this struct + */ + public Collection<Struct> get_known_child_structs () { + return _known_child_structs.read_only_view; + } + + public void register_child_struct (Struct stru) { + if (this.base_type != null) { + ((Struct) this.base_type.data_type).register_child_struct (stru); + } + + _known_child_structs.add (stru); + } + + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var signature = new SignatureBuilder (); + + signature.append_keyword (accessibility.to_string ()); + signature.append_keyword ("struct"); + signature.append_symbol (this); + + var type_parameters = get_children_by_type (NodeType.TYPE_PARAMETER, false); + if (type_parameters.size > 0) { + signature.append ("<", false); + bool first = true; + foreach (Item param in type_parameters) { + if (!first) { + signature.append (",", false); + } + signature.append_content (param.signature, false); + first = false; + } + signature.append (">", false); + } + + if (base_type != null) { + signature.append (":"); + + signature.append_content (base_type.signature); + } + + return signature.get (); + } +} + diff --git a/libvaladoc/api/symbol.vala b/libvaladoc/api/symbol.vala new file mode 100644 index 000000000..aec2bdd75 --- /dev/null +++ b/libvaladoc/api/symbol.vala @@ -0,0 +1,158 @@ +/* symbol.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +/** + * Represents a node in the symbol tree. + */ +public abstract class Valadoc.Api.Symbol : Node { + private ArrayList<Attribute> attributes; + + public bool is_deprecated { + default = false; + private set; + get; + } + + public Symbol (Node parent, SourceFile file, string? name, SymbolAccessibility accessibility, + void* data) + { + base (parent, file, name, data); + + this.accessibility = accessibility; + } + + public void add_attribute (Attribute att) { + if (attributes == null) { + attributes = new ArrayList<Attribute> (); + } + + // register deprecated symbols: + if (att.name == "Version") { + AttributeArgument? deprecated = att.get_argument ("deprecated"); + AttributeArgument? version = att.get_argument ("deprecated_since"); + if ((deprecated != null && deprecated.get_value_as_boolean ()) || version != null) { + string? version_str = (version != null) ? version.get_value_as_string () : null; + + package.register_deprecated_symbol (this, version_str); + is_deprecated = true; + } + } else if (att.name == "Deprecated") { + AttributeArgument? version = att.get_argument ("version"); + string? version_str = (version != null) ? version.get_value_as_string () : null; + + package.register_deprecated_symbol (this, version_str); + is_deprecated = true; + } + + attributes.add (att); + } + + public Collection<Attribute> get_attributes () { + if (attributes == null) { + return Collection<Attribute>.empty<Attribute> (); + } else { + return attributes; + } + } + + public Attribute? get_attribute (string name) { + if (attributes != null) { + foreach (Attribute att in attributes) { + if (att.name == name) { + return att; + } + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public override bool is_browsable (Settings settings) { + if (!settings._private && this.is_private) { + return false; + } + if (!settings._internal && this.is_internal) { + return false; + } + if (!settings._protected && this.is_protected) { + return false; + } + + Item? pos = parent; + while (pos != null && pos is Symbol && pos is Namespace == false) { + if (((Symbol) pos).is_browsable (settings) == false) { + return false; + } + pos = pos.parent; + } + + return true; + } + + public SymbolAccessibility accessibility { + private set; + get; + } + + /** + * Specifies whether this symbol is public. + */ + public bool is_public { + get { + return accessibility == SymbolAccessibility.PUBLIC; + } + } + + /** + * Specifies whether this symbol is protected. + */ + public bool is_protected { + get { + return accessibility == SymbolAccessibility.PROTECTED; + } + } + + /** + * Specifies whether this symbol is internal. + */ + public bool is_internal { + get { + return accessibility == SymbolAccessibility.INTERNAL; + } + } + + /** + * Specifies whether this symbol is private. + */ + public bool is_private { + get { + return accessibility == SymbolAccessibility.PRIVATE; + } + } +} + diff --git a/libvaladoc/api/symbolaccessibility.vala b/libvaladoc/api/symbolaccessibility.vala new file mode 100644 index 000000000..9b78aa671 --- /dev/null +++ b/libvaladoc/api/symbolaccessibility.vala @@ -0,0 +1,51 @@ +/* SymbolAccessibility.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +/** + * The access modifier + */ +public enum Valadoc.Api.SymbolAccessibility { + PROTECTED, + INTERNAL, + PRIVATE, + PUBLIC; + + public string to_string () { + switch (this) { + case SymbolAccessibility.PROTECTED: + return "protected"; + + case SymbolAccessibility.INTERNAL: + return "internal"; + + case SymbolAccessibility.PRIVATE: + return "private"; + + case SymbolAccessibility.PUBLIC: + return "public"; + + default: + assert_not_reached (); + } + } +} diff --git a/libvaladoc/api/tree.vala b/libvaladoc/api/tree.vala new file mode 100644 index 000000000..a5ae3c54b --- /dev/null +++ b/libvaladoc/api/tree.vala @@ -0,0 +1,352 @@ +/* tree.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Valadoc.Importer; +using Gee; + + + +/** + * The root of the code tree. + */ +public class Valadoc.Api.Tree { + private Deque<InheritDocContainer> inheritdocs = new LinkedList<InheritDocContainer> (); + private ArrayList<string> external_c_files = new ArrayList<string>(); + private ArrayList<Package> packages = new ArrayList<Package>(); + private Package source_package = null; + private Settings settings; + private ErrorReporter reporter; + private Highlighter.Highlighter _highlighter; + private CTypeResolver _cresolver = null; + private Package _source_package; + + + private class InheritDocContainer { + public unowned Taglets.InheritDoc taglet; + public unowned Api.Node taglet_container; + + public InheritDocContainer (Api.Node taglet_container, Taglets.InheritDoc taglet) { + this.taglet_container = taglet_container; + this.taglet = taglet; + } + } + + + public void add_package(Package package) { + this.packages.add (package); + } + + public void* data { + set; + get; + } + + public Highlighter.Highlighter highlighter { + get { + if (_highlighter == null) { + _highlighter = new Highlighter.Highlighter (); + } + + return _highlighter; + } + } + + /** + * The root of the wiki tree. + */ + public WikiPageTree? wikitree { + private set; + get; + } + + /** + * Returns a list of C source files. + * + * @return list of C source files + */ + public Collection<string> get_external_c_files () { + return external_c_files.read_only_view; + } + + public void add_external_c_files (string name) { + external_c_files.add (name); + } + + + /** + * Returns a list of all packages in the tree + * + * @return list of all packages + */ + public Collection<Package> get_package_list () { + return this.packages.read_only_view; + } + + private void add_dependencies_to_source_package () { + if ( this.source_package != null ) { + ArrayList<Package> deplst = new ArrayList<Package> (); + foreach (Package pkg in this.packages) { + if (pkg != this.source_package) { + deplst.add (pkg); + } + } + this.source_package.set_dependency_list (deplst); + } + } + + /** + * Visits this node with the specified Visitor. + * + * @param visitor the visitor to be called while traversing + */ + public void accept (Visitor visitor) { + visitor.visit_tree (this); + } + + /** + * Visits all children of this node with the given types with the specified Visitor. + * + * @param visitor the visitor to be called while traversing + */ + public void accept_children (Visitor visitor) { + foreach (Node node in packages) { + node.accept (visitor); + } + } + + private Node? search_relative_to (Node element, string[] path) { + Api.Node? node = element; + + foreach (string name in path) { + node = node.find_by_name (name); + if (node == null) { + break; + } + } + + if (node == null && element.parent != null) { + node = search_relative_to ((Node) element.parent, path); + } + + return node; + } + + public Node? search_symbol_path (Node? element, string[] path) { + Api.Node? node = null; + + // relative to element + if (element != null) { + node = search_relative_to (element, path); + if (node != null) { + return node; + } + } + + + // absolute + foreach (Package package in packages) { + // search in root namespace + + Node? global = package.find_by_name (""); + if (global != null) { + node = search_relative_to (global, path); + if (node != null) { + return node; + } + } + } + + return null; + } + + public TypeSymbol? search_symbol_type_cstr (string cname) { + if (_cresolver == null) { + _cresolver = new CTypeResolver (this); + } + + return _cresolver.resolve_symbol_type (cname); + } + + public Node? search_symbol_cstr (Node? element, string cname) { + if (_cresolver == null) { + _cresolver = new CTypeResolver (this); + } + + return _cresolver.resolve_symbol (element, cname); + } + + public Node? search_symbol_str (Node? element, string symname) { + string[] path = split_name (symname); + + var node = search_symbol_path (element, path); + if (node != null) { + return node; + } + + if (path.length >= 2 && path[path.length-2] == path[path.length-2]) { + path[path.length-2] = path[path.length-2]+"."+path[path.length-1]; + path.resize (path.length-1); + return search_symbol_path (element, path); + } + + return null; + } + + private string[] split_name (string full_name) { + string[] params = (full_name).split (".", -1); + int i = 0; while (params[i] != null) i++; + params.length = i; + return params; + } + + public Tree (ErrorReporter reporter, Settings settings, void* data = null) { + this.settings = settings; + this.reporter = reporter; + this.data = data; + } + + // copied from valacodecontext.vala + private string? get_file_path (string basename, string[] directories) { + string filename = null; + + if (directories != null) { + foreach (string dir in directories) { + filename = Path.build_filename (dir, basename); + if (FileUtils.test (filename, FileTest.EXISTS)) { + return filename; + } + } + } + + foreach (string dir in Environment.get_system_data_dirs ()) { + filename = Path.build_filename (dir, basename); + if (FileUtils.test (filename, FileTest.EXISTS)) { + return filename; + } + } + + return null; + } + + public bool create_tree ( ) { + this.add_dependencies_to_source_package (); + return true; + } + + private Package? get_source_package () { + if (_source_package == null) { + foreach (Package pkg in packages) { + if (!pkg.is_package) { + _source_package = pkg; + break; + } + } + } + + return _source_package; + } + + private void parse_wiki (DocumentationParser docparser) { + this.wikitree = new WikiPageTree (); + var pkg = get_source_package (); + if (pkg != null) { + wikitree.parse (settings, docparser, pkg, reporter); + } + } + + private void check_wiki (DocumentationParser docparser) { + Package pkg = get_source_package (); + if (pkg != null) { + wikitree.check (settings, docparser, pkg); + } + } + + public void parse_comments (DocumentationParser docparser) { + parse_wiki (docparser); + + foreach (Package pkg in this.packages) { + if (pkg.is_browsable (settings)) { + pkg.parse_comments (settings, docparser); + } + } + } + + public void check_comments (DocumentationParser docparser) { + check_wiki (docparser); + + foreach (Package pkg in this.packages) { + if (pkg.is_browsable (settings)) { + pkg.check_comments (settings, docparser); + postprocess_inheritdoc (docparser); + } + } + } + + internal void register_inheritdoc (Api.Node container, Taglets.InheritDoc taglet) { + inheritdocs.add (new InheritDocContainer (container, taglet)); + } + + private void postprocess_inheritdoc (DocumentationParser docparser) { + while (!this.inheritdocs.is_empty) { + InheritDocContainer container = this.inheritdocs.poll_head (); + + docparser.transform_inheritdoc (container.taglet_container, container.taglet); + } + } + + + /** + * Import documentation from various sources + * + * @param importers a list of importers + * @param packages sources + * @param import_directories List of directories where to find the files + */ + public void import_comments (DocumentationImporter[] importers, string[] packages, + string[] import_directories) + { + HashSet<string> processed = new HashSet<string> (); + foreach (string pkg_name in packages) { + bool imported = false; + foreach (DocumentationImporter importer in importers) { + string? path = get_file_path ("%s.%s".printf (pkg_name, importer.file_extension), + import_directories); + if (path == null) { + continue; + } + + path = realpath (path); + imported = true; + + if (!processed.contains (path)) { + importer.process (path); + processed.add (path); + } + } + + if (imported == false) { + reporter.simple_error (null, "'%s' not found in specified import directories", pkg_name); + } + } + } +} + diff --git a/libvaladoc/api/typeparameter.vala b/libvaladoc/api/typeparameter.vala new file mode 100644 index 000000000..b5f33b860 --- /dev/null +++ b/libvaladoc/api/typeparameter.vala @@ -0,0 +1,62 @@ +/* typeparameter.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * Represents a generic type parameter in the source code. + */ +public class Valadoc.Api.TypeParameter : Symbol { + + public TypeParameter (Node parent, SourceFile file, string name, void* data) { + base (parent, file, name, SymbolAccessibility.PUBLIC, data); + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + return new SignatureBuilder () + .append_symbol (this) + .get (); + } + + /** + * {@inheritDoc} + */ + public override NodeType node_type { get { return NodeType.TYPE_PARAMETER; } } + + /** + * {@inheritDoc} + */ + public override void accept (Visitor visitor) { + visitor.visit_type_parameter (this); + } + + public override bool is_browsable (Settings settings) { + return false; + } + +} + diff --git a/libvaladoc/api/typereference.vala b/libvaladoc/api/typereference.vala new file mode 100644 index 000000000..0d5bffc1f --- /dev/null +++ b/libvaladoc/api/typereference.vala @@ -0,0 +1,168 @@ +/* typereference.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; +using Valadoc.Content; + + +/** + * A reference to a data type. + */ +public class Valadoc.Api.TypeReference : Item { + private ArrayList<TypeReference> type_arguments = new ArrayList<TypeReference> (); + private string? dbus_type_signature; + private Ownership ownership; + + public TypeReference (Item parent, Ownership ownership, bool pass_ownership, bool is_dynamic, + bool is_nullable, string? dbus_type_signature, void* data) + { + base (data); + + this.dbus_type_signature = dbus_type_signature; + this.pass_ownership = pass_ownership; + this.is_nullable = is_nullable; + this.is_dynamic = is_dynamic; + this.ownership = ownership; + this.parent = parent; + } + + /** + * Returns a copy of the list of generic type arguments. + * + * @return type argument list + */ + public Gee.Collection<TypeReference> get_type_arguments () { + return this.type_arguments.read_only_view; + } + + public void add_type_argument (TypeReference type_ref) { + type_arguments.add (type_ref); + } + + /** + * The referred data type. + */ + public Item? data_type { + set; + get; + } + + public bool pass_ownership { + private set; + get; + } + + /** + * Specifies that the expression is owned. + */ + public bool is_owned { + get { + return ownership == Ownership.OWNED; + } + } + + /** + * Specifies that the expression is weak. + */ + public bool is_weak { + get { + return ownership == Ownership.WEAK; + } + } + + /** + * Specifies that the expression is unwoned. + */ + public bool is_unowned { + get { + return ownership == Ownership.UNOWNED; + } + } + + + /** + * Specifies that the expression is dynamic. + */ + public bool is_dynamic { + private set; + get; + } + + /** + * Specifies that the expression may be null. + */ + public bool is_nullable { + private set; + get; + } + + public string? get_dbus_type_signature () { + return dbus_type_signature; + } + + /** + * {@inheritDoc} + */ + protected override Inline build_signature () { + var signature = new SignatureBuilder (); + + if (is_dynamic) { + signature.append_keyword ("dynamic"); + } + + if (is_weak) { + signature.append_keyword ("weak"); + } else if (is_owned) { + signature.append_keyword ("owned"); + } else if (is_unowned) { + signature.append_keyword ("unowned"); + } + + if (data_type == null) { + signature.append_keyword ("void"); + } else if (data_type is Symbol) { + signature.append_type ((Symbol) data_type); + } else { + signature.append_content (data_type.signature); + } + + if (type_arguments.size > 0) { + signature.append ("<", false); + bool first = true; + foreach (Item param in type_arguments) { + if (!first) { + signature.append (",", false); + } + signature.append_content (param.signature, false); + first = false; + } + signature.append (">", false); + } + + if (is_nullable) { + signature.append ("?", false); + } + + return signature.get (); + } +} + diff --git a/libvaladoc/api/typesymbol.vala b/libvaladoc/api/typesymbol.vala new file mode 100644 index 000000000..3574688e6 --- /dev/null +++ b/libvaladoc/api/typesymbol.vala @@ -0,0 +1,114 @@ +/* typesymbol.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +/** + * Represents a runtime data type. + */ +public abstract class Valadoc.Api.TypeSymbol : Symbol { + private SourceComment? source_comment; + private string? type_macro_name; + private string? is_type_macro_name; + private string? type_cast_macro_name; + private string? type_function_name; + + public TypeSymbol (Node parent, SourceFile file, string name, SymbolAccessibility accessibility, + SourceComment? comment, string? type_macro_name, string? is_type_macro_name, + string? type_cast_macro_name, string? type_function_name, bool is_basic_type, + void* data) + { + base (parent, file, name, accessibility, data); + + this.type_cast_macro_name = type_cast_macro_name; + this.is_type_macro_name = is_type_macro_name; + this.type_function_name = type_function_name; + this.type_macro_name = type_macro_name; + + this.is_basic_type = is_basic_type; + this.source_comment = comment; + } + + /** + * Specifies whether this symbol is a basic type (string, int, char, etc) + */ + public bool is_basic_type { + private set; + get; + } + + /** + * Gets the name of the GType macro which represents the type symbol + */ + public string get_type_macro_name () { + return type_macro_name; + } + + /** + * Gets the name of the GType macro which casts a type instance to the given type. + */ + public string get_type_cast_macro_name () { + return type_cast_macro_name; + } + + /** + * Gets the name of the GType macro which determines whether a type instance is of a given type. + */ + public string get_is_type_macro_name () { + return is_type_macro_name; + } + + /** + * Gets the name of the get_type() function which represents the type symbol + */ + public string get_type_function_name () { + return type_function_name; + } + + /** + * {@inheritDoc} + */ + internal override void parse_comments (Settings settings, DocumentationParser parser) { + if (documentation != null) { + return ; + } + + if (source_comment != null) { + documentation = parser.parse (this, source_comment); + } + + base.parse_comments (settings, parser); + } + + /** + * {@inheritDoc} + */ + internal override void check_comments (Settings settings, DocumentationParser parser) { + if (documentation != null) { + parser.check (this, documentation); + } + + base.check_comments (settings, parser); + } +} diff --git a/libvaladoc/api/visitor.vala b/libvaladoc/api/visitor.vala new file mode 100644 index 000000000..bf66a456b --- /dev/null +++ b/libvaladoc/api/visitor.vala @@ -0,0 +1,171 @@ +/* visitor.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + + +/** + * Abstract visitor for traversing API. + */ +public abstract class Valadoc.Api.Visitor : GLib.Object { + /** + * Visit operation called for api trees. + * + * @param item a tree + */ + public virtual void visit_tree (Tree item) { + } + + /** + * Visit operation called for packages like gir-files and vapi-files. + * + * @param item a package + */ + public virtual void visit_package (Package item) { + } + + /** + * Visit operation called for namespaces + * + * @param item a namespace + */ + public virtual void visit_namespace (Namespace item) { + } + + /** + * Visit operation called for interfaces. + * + * @param item a interface + */ + public virtual void visit_interface (Interface item) { + } + + /** + * Visit operation called for classes. + * + * @param item a class + */ + public virtual void visit_class (Class item) { + } + + /** + * Visit operation called for structs. + * + * @param item a struct + */ + public virtual void visit_struct (Struct item) { + } + + /** + * Visit operation called for properties. + * + * @param item a property + */ + public virtual void visit_property (Property item) { + } + + /** + * Visit operation called for fields. + * + * @param item a field + */ + public virtual void visit_field (Field item) { + } + + /** + * Visit operation called for constants. + * + * @param item a constant + */ + public virtual void visit_constant (Constant item) { + } + + /** + * Visit operation called for delegates. + * + * @param item a delegate + */ + public virtual void visit_delegate (Delegate item) { + } + + /** + * Visit operation called for signals. + * + * @param item a signal + */ + public virtual void visit_signal (Signal item) { + } + + /** + * Visit operation called for methods. + * + * @param item a method + */ + public virtual void visit_method (Method item) { + } + + /** + * Visit operation called for type parameters. + * + * @param item a type parameter + */ + public virtual void visit_type_parameter (TypeParameter item) { + } + + /** + * Visit operation called for formal parameters. + * + * @param item a formal parameter + */ + public virtual void visit_formal_parameter (FormalParameter item) { + } + + /** + * Visit operation called for error domains. + * + * @param item a error domain + */ + public virtual void visit_error_domain (ErrorDomain item) { + } + + /** + * Visit operation called for error codes. + * + * @param item a error code + */ + public virtual void visit_error_code (ErrorCode item) { + } + + /** + * Visit operation called for enums. + * + * @param item a enum + */ + public virtual void visit_enum (Enum item) { + } + + /** + * Visit operation called for enum values. + * + * @param item a enum value + */ + public virtual void visit_enum_value (EnumValue item) { + } +} diff --git a/libvaladoc/charts/chart.vala b/libvaladoc/charts/chart.vala new file mode 100644 index 000000000..a6307d7ff --- /dev/null +++ b/libvaladoc/charts/chart.vala @@ -0,0 +1,73 @@ +/* chart.vala + * + * Copyright (C) 2008 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +public class Valadoc.Charts.Chart : Api.Visitor { + protected Gvc.Context context; + protected Gvc.Graph graph; + protected Factory factory; + + static construct { + #if !WITH_CGRAPH + Gvc.init (); + #endif + } + + public Chart (Factory factory, Api.Node node) { + graph = factory.create_graph (node); + this.factory = factory; + node.accept (this); + } + + public void save (string file_name, string file_type = "png") { + if (context == null) { + context = factory.create_context (graph); + } + context.render_filename (graph, file_type, file_name); + } + + public void write (GLib.FileStream file, string file_type) { + if (context == null) { + context = factory.create_context (graph); + } + context.render (graph, file_type, file); + } + + public uint8[]? write_buffer (string file_type) { + if (context == null) { + context = factory.create_context (graph); + } + + uint8[]? data; + + /* This will return null in data if it fails. */ + context.render_data (graph, file_type, out data); + return data; + } + + ~Chart () { + if (context != null) { + context.free_layout (graph); + } + } +} + diff --git a/libvaladoc/charts/chartfactory.vala b/libvaladoc/charts/chartfactory.vala new file mode 100644 index 000000000..c2582ab9f --- /dev/null +++ b/libvaladoc/charts/chartfactory.vala @@ -0,0 +1,53 @@ +/* chartfactory.vala + * + * Copyright (C) 2008 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +public abstract class Valadoc.Charts.Factory : Object { + protected Gvc.Node create_type (Gvc.Graph graph, Api.Node item) { + #if WITH_CGRAPH + return graph.create_node (item.get_full_name (), 1); + #else + return graph.create_node (item.get_full_name ()); + #endif + } + + public abstract Gvc.Graph create_graph (Api.Node item); + + public abstract Gvc.Context create_context (Gvc.Graph graph); + + public abstract Gvc.Node create_class (Gvc.Graph graph, Api.Class item); + + public abstract Gvc.Node create_struct (Gvc.Graph graph, Api.Struct item); + + public abstract Gvc.Node create_interface (Gvc.Graph graph, Api.Interface item); + + public abstract Gvc.Node create_enum (Gvc.Graph graph, Api.Enum item); + + public abstract Gvc.Node create_delegate (Gvc.Graph graph, Api.Delegate item); + + public abstract Gvc.Node create_errordomain (Gvc.Graph graph, Api.ErrorDomain item); + + public abstract Gvc.Node create_namespace (Gvc.Graph graph, Api.Namespace item); + + public abstract Gvc.Edge add_children (Gvc.Graph graph, Gvc.Node parent, Gvc.Node child); +} + diff --git a/libvaladoc/charts/hierarchychart.vala b/libvaladoc/charts/hierarchychart.vala new file mode 100644 index 000000000..ce3613488 --- /dev/null +++ b/libvaladoc/charts/hierarchychart.vala @@ -0,0 +1,82 @@ +/* hierarchychart.vala + * + * Copyright (C) 2008 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; + + +public class Valadoc.Charts.Hierarchy : Charts.Chart { + public Hierarchy (Factory factory, Api.Node node) { + base (factory, node); + } + + private void draw_implemented_interfaces (Gvc.Node child, Collection<Api.TypeReference> interfaces) { + foreach (Api.TypeReference typeref in interfaces) { + var parent = factory.create_interface (graph, (Api.Interface) typeref.data_type); + factory.add_children (graph, parent, child); + } + } + + private void draw_parent_classes (Api.Class item, Gvc.Node? child = null) { + var parent = factory.create_class (graph, item); + + if (child != null) { + factory.add_children (graph, parent, child); + } + + if (item.base_type != null) { + draw_parent_classes ((Api.Class) item.base_type.data_type, parent); + } + + draw_implemented_interfaces (parent, item.get_implemented_interface_list ()); + } + + private void draw_parent_structs (Api.Struct item, Gvc.Node? child = null) { + var parent = factory.create_struct (graph, item); + + if (child != null) { + factory.add_children (graph, parent, child); + } + + if (item.base_type != null) { + draw_parent_structs ((Api.Struct) item.base_type.data_type, parent); + } + } + + public override void visit_interface (Api.Interface item) { + var iface = factory.create_interface (graph, item); + + if (item.base_type != null) { + draw_parent_classes ((Api.Class) item.base_type.data_type, iface); + } + + draw_implemented_interfaces (iface, item.get_implemented_interface_list ()); + } + + public override void visit_class (Api.Class item) { + draw_parent_classes (item); + } + + public override void visit_struct (Api.Struct item) { + draw_parent_structs (item); + } +} + diff --git a/libvaladoc/charts/simplechartfactory.vala b/libvaladoc/charts/simplechartfactory.vala new file mode 100644 index 000000000..06da97e13 --- /dev/null +++ b/libvaladoc/charts/simplechartfactory.vala @@ -0,0 +1,87 @@ +/* simplechartfactory.vala + * + * Copyright (C) 2008 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + + +public class Valadoc.Charts.SimpleFactory : Charts.Factory { + protected virtual Gvc.Node configure_type (Gvc.Node node, Api.Node item) { + node.safe_set ("shape", "box", ""); + node.safe_set ("fontname", "Times", ""); + node.safe_set ("label", item.get_full_name (), ""); + return node; + } + + public override Gvc.Graph create_graph (Api.Node item) { + #if WITH_CGRAPH + var graph = new Gvc.Graph (item.get_full_name (), Gvc.Agdirected, 0); + #else + var graph = new Gvc.Graph (item.get_full_name (), Gvc.GraphKind.AGDIGRAPH); + #endif + return graph; + } + + public override Gvc.Context create_context (Gvc.Graph graph) { + var context = new Gvc.Context (); + context.layout_jobs (graph); + context.layout (graph, "dot"); + return context; + } + + public override Gvc.Node create_class (Gvc.Graph graph, Api.Class item) { + var node = configure_type (create_type (graph, item), item); + node.safe_set ("style", "bold", ""); + return node; + } + + public override Gvc.Node create_struct (Gvc.Graph graph, Api.Struct item) { + var node = configure_type (create_type (graph, item), item); + node.safe_set ("style", "bold", ""); + return node; + } + + public override Gvc.Node create_interface (Gvc.Graph graph, Api.Interface item) { + return configure_type (create_type (graph, item), item); + } + + public override Gvc.Node create_enum (Gvc.Graph graph, Api.Enum item) { + return configure_type (create_type (graph, item), item); + } + + public override Gvc.Node create_delegate (Gvc.Graph graph, Api.Delegate item) { + return configure_type (create_type (graph, item), item); + } + + public override Gvc.Node create_errordomain (Gvc.Graph graph, Api.ErrorDomain item) { + return configure_type (create_type (graph, item), item); + } + + public override Gvc.Node create_namespace (Gvc.Graph graph, Api.Namespace item) { + return configure_type (create_type (graph, item), item); + } + + public override Gvc.Edge add_children (Gvc.Graph graph, Gvc.Node parent, Gvc.Node child) { + var edge = graph.create_edge (parent, child); + edge.safe_set ("dir", "back", ""); + return edge; + } +} + diff --git a/libvaladoc/content/block.vala b/libvaladoc/content/block.vala new file mode 100644 index 000000000..99273aee1 --- /dev/null +++ b/libvaladoc/content/block.vala @@ -0,0 +1,25 @@ +/* block.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +public interface Valadoc.Content.Block : ContentElement { +} + diff --git a/libvaladoc/content/blockcontent.vala b/libvaladoc/content/blockcontent.vala new file mode 100644 index 000000000..47e99a217 --- /dev/null +++ b/libvaladoc/content/blockcontent.vala @@ -0,0 +1,66 @@ +/* blockcontent.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +public abstract class Valadoc.Content.BlockContent : ContentElement { + public Gee.List<Block> content { get { return _content; } } + + private Gee.List<Block> _content; + + construct { + _content = new ArrayList<Block> (); + } + + internal BlockContent () { + } + + public override void configure (Settings settings, ResourceLocator locator) { + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + foreach (Block element in _content) { + element.parent = this; + element.check (api_root, container, file_path, reporter, settings); + } + } + + public override void accept_children (ContentVisitor visitor) { + foreach (Block element in _content) { + element.accept (visitor); + } + } + + public override bool is_empty () { + foreach (Block item in content) { + if (!item.is_empty ()) { + return false; + } + } + + return true; + } +} + diff --git a/libvaladoc/content/comment.vala b/libvaladoc/content/comment.vala new file mode 100644 index 000000000..f4ba4a09f --- /dev/null +++ b/libvaladoc/content/comment.vala @@ -0,0 +1,106 @@ +/* comment.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Valadoc.Taglets; +using Gee; + + +public class Valadoc.Content.Comment : BlockContent { + public Gee.List<Taglet> taglets { get { return _taglets; } } + private Gee.List<Taglet> _taglets; + + private bool checked = false; + + + internal Comment () { + base (); + _taglets = new ArrayList<Taglet> (); + } + + public override void configure (Settings settings, ResourceLocator locator) { + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + if (checked == true) { + return ; + } + + checked = true; + + + base.check (api_root, container, file_path, reporter, settings); + + foreach (Taglet element in _taglets) { + element.parent = this; + element.check (api_root, container, file_path, reporter, settings); + } + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_comment (this); + } + + public override void accept_children (ContentVisitor visitor) { + base.accept_children (visitor); + + foreach (Taglet element in _taglets) { + element.accept (visitor); + } + } + + public Gee.List<Taglet> find_taglets (Api.Node? container, Type taglet_type) { + Gee.List<Taglet> selected_taglets = new ArrayList<Taglet> (); + + // TODO inherit stuff if needed + + foreach (Taglet taglet in _taglets) { + if (taglet.get_type () == taglet_type) { + selected_taglets.add (taglet); + } + } + + return selected_taglets; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + assert (new_parent == null); + + Comment comment = new Comment (); + comment.parent = new_parent; + + foreach (Block element in content) { + Block copy = element.copy (comment) as Block; + comment.content.add (copy); + } + + foreach (Taglet taglet in _taglets) { + Taglet copy = taglet.copy (comment) as Taglet; + comment.taglets.add (copy); + } + + return comment; + } +} + diff --git a/libvaladoc/content/contentelement.vala b/libvaladoc/content/contentelement.vala new file mode 100644 index 000000000..6e2c65934 --- /dev/null +++ b/libvaladoc/content/contentelement.vala @@ -0,0 +1,46 @@ +/* contentelement.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using GLib; + + +public abstract class Valadoc.Content.ContentElement : Object { + public ContentElement parent { get; internal set; } + + public abstract ContentElement copy (ContentElement? new_parent = null); + + + public virtual void configure (Settings settings, ResourceLocator locator) { + } + + public abstract void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings); + + public abstract void accept (ContentVisitor visitor); + + public abstract bool is_empty (); + + public virtual void accept_children (ContentVisitor visitor) { + } +} + diff --git a/libvaladoc/content/contentfactory.vala b/libvaladoc/content/contentfactory.vala new file mode 100644 index 000000000..dda8a8763 --- /dev/null +++ b/libvaladoc/content/contentfactory.vala @@ -0,0 +1,124 @@ +/* contentfactory.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.ContentFactory : Object { + + public ContentFactory (Settings settings, ResourceLocator locator, ModuleLoader modules) { + _settings = settings; + _locator = locator; + _modules = modules; + } + + private Settings _settings; + private ResourceLocator _locator; + private ModuleLoader _modules; + + private inline ContentElement configure (ContentElement element) { + element.configure (_settings, _locator); + return element; + } + + public Comment create_comment () { + return (Comment) configure (new Comment ()); + } + + public Embedded create_embedded () { + return (Embedded) configure (new Embedded ()); + } + + public Headline create_headline () { + return (Headline) configure (new Headline ()); + } + + public Link create_link () { + return (Link) configure (new Link ()); + } + + public WikiLink create_wiki_link () { + return (WikiLink) configure (new WikiLink ()); + } + + public List create_list () { + return (List) configure (new List ()); + } + + public ListItem create_list_item () { + return (ListItem) configure (new ListItem ()); + } + + public Page create_page () { + return (Page) configure (new Page ()); + } + + public Paragraph create_paragraph () { + return (Paragraph) configure (new Paragraph ()); + } + + public Warning create_warning () { + return (Warning) configure (new Warning ()); + } + public Note create_note () { + return (Note) configure (new Note ()); + } + + public Run create_run (Run.Style style) { + return (Run) configure (new Run (style)); + } + + public SourceCode create_source_code () { + return (SourceCode) configure (new SourceCode ()); + } + + public Table create_table () { + return (Table) configure (new Table ()); + } + + public TableCell create_table_cell () { + return (TableCell) configure (new TableCell ()); + } + + public TableRow create_table_row () { + return (TableRow) configure (new TableRow ()); + } + + public Taglet? create_taglet (string name) { + return _modules.create_taglet (name); + } + + public Text create_text (string? text = null) { + return (Text) configure (new Text (text)); + } + + public ContentElement set_style_attributes (StyleAttributes element, + VerticalAlign? valign, + HorizontalAlign? halign, + string? style) { + element.vertical_align = valign; + element.horizontal_align = halign; + element.style = style; + return element; + } +} diff --git a/libvaladoc/content/contentrenderer.vala b/libvaladoc/content/contentrenderer.vala new file mode 100644 index 000000000..153e35deb --- /dev/null +++ b/libvaladoc/content/contentrenderer.vala @@ -0,0 +1,32 @@ +/* contentrenderer.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using GLib; + + +public abstract class Valadoc.Content.ContentRenderer : ContentVisitor { + + public abstract void render (ContentElement element); + + public abstract void render_children (ContentElement element); +} + diff --git a/libvaladoc/content/contentvisitor.vala b/libvaladoc/content/contentvisitor.vala new file mode 100644 index 000000000..d1b14c453 --- /dev/null +++ b/libvaladoc/content/contentvisitor.vala @@ -0,0 +1,86 @@ +/* contentvisitor.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using GLib; + + +public abstract class Valadoc.Content.ContentVisitor : Object { + + public virtual void visit_comment (Comment element) { + } + + public virtual void visit_embedded (Embedded element) { + } + + public virtual void visit_headline (Headline element) { + } + + public virtual void visit_link (Link element) { + } + + public virtual void visit_wiki_link (WikiLink element) { + } + + public virtual void visit_symbol_link (SymbolLink element) { + } + + public virtual void visit_list (List element) { + } + + public virtual void visit_list_item (ListItem element) { + } + + public virtual void visit_paragraph (Paragraph element) { + } + + public virtual void visit_warning (Warning element) { + } + + public virtual void visit_note (Note element) { + } + + public virtual void visit_page (Page element) { + } + + public virtual void visit_run (Run element) { + } + + public virtual void visit_source_code (SourceCode element) { + } + + public virtual void visit_table (Table element) { + } + + public virtual void visit_table_cell (TableCell element) { + } + + public virtual void visit_table_row (TableRow element) { + } + + public virtual void visit_taglet (Taglet element) { + } + + public virtual void visit_text (Text element) { + } +} + diff --git a/libvaladoc/content/embedded.vala b/libvaladoc/content/embedded.vala new file mode 100644 index 000000000..b1d7d9525 --- /dev/null +++ b/libvaladoc/content/embedded.vala @@ -0,0 +1,125 @@ +/* embedded.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.Embedded : ContentElement, Inline, StyleAttributes { + public string url { + get; + set; + } + + public string? caption { + get; + set; + } + + public HorizontalAlign? horizontal_align { + get; + set; + } + + public VerticalAlign? vertical_align { + get; + set; + } + + public string? style { + get; + set; + } + + public Api.Package package; + + private ResourceLocator _locator; + + internal Embedded () { + base (); + } + + public override void configure (Settings settings, ResourceLocator locator) { + _locator = locator; + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // search relative to our file + if (!Path.is_absolute (url)) { + string relative_to_file = Path.build_path (Path.DIR_SEPARATOR_S, + Path.get_dirname (file_path), + url); + if (FileUtils.test (relative_to_file, FileTest.EXISTS | FileTest.IS_REGULAR)) { + url = (owned) relative_to_file; + package = container.package; + return ; + } + } + + // search relative to the current directory / absoulte path + if (!FileUtils.test (url, FileTest.EXISTS | FileTest.IS_REGULAR)) { + string base_name = Path.get_basename (url); + + foreach (unowned string dir in settings.alternative_resource_dirs) { + string alternative_path = Path.build_path (Path.DIR_SEPARATOR_S, + dir, + base_name); + if (FileUtils.test (alternative_path, FileTest.EXISTS | FileTest.IS_REGULAR)) { + url = (owned) alternative_path; + package = container.package; + return ; + } + } + + string node_segment = (container is Api.Package)? "" : container.get_full_name () + ": "; + reporter.simple_error ("%s: %s{{".printf (file_path, node_segment), + "'%s' does not exist", url); + } else { + package = container.package; + } + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_embedded (this); + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Embedded embedded = new Embedded (); + embedded.parent = new_parent; + + embedded.horizontal_align = horizontal_align; + embedded.vertical_align = vertical_align; + embedded._locator = _locator; + embedded.caption = caption; + embedded.package = package; + embedded.style = style; + embedded.url = url; + + return embedded; + } +} diff --git a/libvaladoc/content/headline.vala b/libvaladoc/content/headline.vala new file mode 100644 index 000000000..56a4a443e --- /dev/null +++ b/libvaladoc/content/headline.vala @@ -0,0 +1,66 @@ +/* headline.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.Headline : InlineContent, Block { + public int level { get; set; } + + internal Headline () { + base (); + _level = 0; + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // TODO report error if level == 0 ? + // TODO: content.size == 0? + + // Check inline content + base.check (api_root, container, file_path, reporter, settings); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_headline (this); + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Headline headline = new Headline (); + headline.parent = new_parent; + headline.level = level; + + foreach (Inline element in content) { + Inline copy = element.copy (headline) as Inline; + headline.content.add (copy); + } + + return headline; + } +} + diff --git a/libvaladoc/content/inline.vala b/libvaladoc/content/inline.vala new file mode 100644 index 000000000..3a6af7564 --- /dev/null +++ b/libvaladoc/content/inline.vala @@ -0,0 +1,25 @@ +/* inline.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + + +public interface Valadoc.Content.Inline : ContentElement { +} diff --git a/libvaladoc/content/inlinecontent.vala b/libvaladoc/content/inlinecontent.vala new file mode 100644 index 000000000..f67ee4e1a --- /dev/null +++ b/libvaladoc/content/inlinecontent.vala @@ -0,0 +1,75 @@ +/* inlinecontent.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public abstract class Valadoc.Content.InlineContent : ContentElement { + public Gee.List<Inline> content { + get { + return _content; + } + } + + private Gee.List<Inline> _content; + + construct { + _content = new ArrayList<Inline> (); + } + + internal InlineContent () { + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + foreach (Inline element in _content) { + element.parent = this; + element.check (api_root, container, file_path, reporter, settings); + } + } + + public override void accept_children (ContentVisitor visitor) { + foreach (Inline element in _content) { + element.accept (visitor); + } + } + + public override bool is_empty () { + foreach (Inline item in content) { + if (!item.is_empty ()) { + return false; + } + } + + return true; + } + + internal void replace_node (Inline old, Inline replacement) { + int index = _content.index_of (old); + assert (index >= 0); + + _content.set (index, replacement); + } +} + diff --git a/libvaladoc/content/inlinetaglet.vala b/libvaladoc/content/inlinetaglet.vala new file mode 100644 index 000000000..812eab39a --- /dev/null +++ b/libvaladoc/content/inlinetaglet.vala @@ -0,0 +1,71 @@ +/* taglet.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public abstract class Valadoc.Content.InlineTaglet : ContentElement, Taglet, Inline { + protected Settings settings; + protected ResourceLocator locator; + private ContentElement _content; + + public InlineTaglet () { + base (); + } + + public abstract Rule? get_parser_rule (Rule run_rule); + + public abstract ContentElement produce_content (); + + private ContentElement get_content () { + if (_content == null) { + _content = produce_content (); + } + return _content; + } + + public override void configure (Settings settings, ResourceLocator locator) { + this.settings = settings; + this.locator = locator; + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + ContentElement element = get_content (); + element.parent = this; + + element.check (api_root, container, file_path, reporter, settings); + } + + public override void accept (ContentVisitor visitor) { + ContentElement element = get_content (); + element.accept (visitor); + } + + public override bool is_empty () { + // taglets are not empty by default + return false; + } +} + diff --git a/libvaladoc/content/link.vala b/libvaladoc/content/link.vala new file mode 100644 index 000000000..ba641e7e5 --- /dev/null +++ b/libvaladoc/content/link.vala @@ -0,0 +1,115 @@ +/* link.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2014 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.Link : InlineContent, Inline { + public string url { + set; + get; + } + + /** + * Used by importers to transform internal URLs + */ + public Importer.InternalIdRegistrar id_registrar { + internal set; + get; + } + + + internal Link () { + base (); + } + + public override void configure (Settings settings, ResourceLocator locator) { + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + + // Internal gktdoc-id? (gir-importer) + if (id_registrar != null) { + Api.Node? node = id_registrar.map_symbol_id (url); + if (node != null) { + InlineContent _parent = parent as InlineContent; + assert (_parent != null); + + SymbolLink replacement = new SymbolLink (node); + replacement.content.add_all (content); + + replacement.check (api_root, container, file_path, reporter, settings); + _parent.replace_node (this, replacement); + return ; + } + + + string _url = id_registrar.map_url_id (url); + if (_url == null) { + string node_segment = (container is Api.Package)? "" : container.get_full_name () + ": "; + reporter.simple_warning ("%s: %s[[".printf (file_path, node_segment), + "unknown imported internal id '%s'", url); + + InlineContent _parent = parent as InlineContent; + assert (_parent != null); + + Run replacement = new Run (Run.Style.ITALIC); + replacement.content.add_all (content); + replacement.check (api_root, container, file_path, reporter, settings); + + _parent.replace_node (this, replacement); + return ; + } + + url = _url; + } + + + //TODO: check url + base.check (api_root, container, file_path, reporter, settings); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_link (this); + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Link link = new Link (); + link.id_registrar = id_registrar; + link.parent = new_parent; + link.url = url; + + foreach (Inline element in content) { + Inline copy = element.copy (link) as Inline; + link.content.add (copy); + } + + return link; + } +} diff --git a/libvaladoc/content/list.vala b/libvaladoc/content/list.vala new file mode 100644 index 000000000..b23452648 --- /dev/null +++ b/libvaladoc/content/list.vala @@ -0,0 +1,156 @@ +/* list.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.List : ContentElement, Block { + public enum Bullet { + NONE, + UNORDERED, + ORDERED, + ORDERED_NUMBER, + ORDERED_LOWER_CASE_ALPHA, + ORDERED_UPPER_CASE_ALPHA, + ORDERED_LOWER_CASE_ROMAN, + ORDERED_UPPER_CASE_ROMAN; + + public static Bullet? from_string (string? str) { + switch (str) { + case "none": + return Bullet.NONE; + + case "unordered": + return Bullet.UNORDERED; + + case "ordered": + return Bullet.ORDERED; + + case "ordered-number": + return Bullet.ORDERED_NUMBER; + + case "ordered-lower-case-alpa": + return Bullet.ORDERED_LOWER_CASE_ALPHA; + + case "ordered-upper-case-alpha": + return Bullet.ORDERED_UPPER_CASE_ALPHA; + + case "ordered-lower-case-roman": + return Bullet.ORDERED_LOWER_CASE_ROMAN; + + case "ordered-upper-case-roman": + return Bullet.ORDERED_UPPER_CASE_ROMAN; + } + + return null; + } + + public unowned string to_string () { + switch (this) { + case Bullet.NONE: + return "none"; + + case Bullet.UNORDERED: + return "unordered"; + + case Bullet.ORDERED: + return "ordered"; + + case Bullet.ORDERED_NUMBER: + return "ordered-number"; + + case Bullet.ORDERED_LOWER_CASE_ALPHA: + return "ordered-lower-case-alpa"; + + case Bullet.ORDERED_UPPER_CASE_ALPHA: + return "ordered-upper-case-alpha"; + + case Bullet.ORDERED_LOWER_CASE_ROMAN: + return "ordered-lower-case-roman"; + + case Bullet.ORDERED_UPPER_CASE_ROMAN: + return "ordered-upper-case-roman"; + } + + assert (true); + return ""; + } + } + + public Bullet bullet { + get; + set; + } + + // TODO add initial value (either a number or some letters) + public Gee.List<ListItem> items { + get { + return _items; + } + } + + private Gee.List<ListItem> _items; + + internal List () { + base (); + _bullet = Bullet.NONE; + _items = new ArrayList<ListItem> (); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // Check individual list items + foreach (ListItem element in _items) { + element.parent = this; + element.check (api_root, container, file_path, reporter, settings); + } + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_list (this); + } + + public override void accept_children (ContentVisitor visitor) { + foreach (ListItem element in _items) { + element.accept (visitor); + } + } + + public override bool is_empty () { + return _items.size == 0; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Content.List list = new Content.List (); + list.parent = new_parent; + list.bullet = bullet; + + foreach (ListItem item in items) { + ListItem copy = item.copy (list) as ListItem; + list.items.add (copy); + } + + return list; + } +} diff --git a/libvaladoc/content/listitem.vala b/libvaladoc/content/listitem.vala new file mode 100644 index 000000000..299ce4e4e --- /dev/null +++ b/libvaladoc/content/listitem.vala @@ -0,0 +1,59 @@ +/* listitem.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.ListItem : BlockContent { + + internal ListItem () { + base (); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // Check block content + base.check (api_root, container, file_path, reporter, settings); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_list_item (this); + } + + public override void accept_children (ContentVisitor visitor) { + base.accept_children (visitor); + } + + public override ContentElement copy (ContentElement? new_parent = null) { + ListItem item = new ListItem (); + item.parent = new_parent; + + foreach (Block block in content) { + Block copy = block.copy (item) as Block; + item.content.add (copy); + } + + return item; + } +} diff --git a/libvaladoc/content/note.vala b/libvaladoc/content/note.vala new file mode 100644 index 000000000..d35347e05 --- /dev/null +++ b/libvaladoc/content/note.vala @@ -0,0 +1,55 @@ +/* note.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.Note : BlockContent, Block { + internal Note () { + base (); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // Check inline content + base.check (api_root, container, file_path, reporter, settings); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_note (this); + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Note note = new Note (); + note.parent = new_parent; + + foreach (Block block in content) { + Block copy = block.copy (note) as Block; + note.content.add (copy); + } + + return note; + } +} + diff --git a/libvaladoc/content/page.vala b/libvaladoc/content/page.vala new file mode 100644 index 000000000..f180be84f --- /dev/null +++ b/libvaladoc/content/page.vala @@ -0,0 +1,65 @@ +/* page.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.Page : BlockContent { + private bool checked = false; + + + internal Page () { + base (); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_page (this); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + if (checked == true) { + return ; + } + + checked = true; + + + base.check (api_root, container, file_path, reporter, settings); + } + + public override ContentElement copy (ContentElement? new_parent = null) { + assert (new_parent == null); + + Content.Page page = new Content.Page (); + page.parent = new_parent; + + foreach (Block block in content) { + Block copy = block.copy (page) as Block; + page.content.add (copy); + } + + return page; + } +} + diff --git a/libvaladoc/content/paragraph.vala b/libvaladoc/content/paragraph.vala new file mode 100644 index 000000000..6d474b940 --- /dev/null +++ b/libvaladoc/content/paragraph.vala @@ -0,0 +1,74 @@ +/* paragraph.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.Paragraph : InlineContent, Block, StyleAttributes { + public HorizontalAlign? horizontal_align { + get; + set; + } + + public VerticalAlign? vertical_align { + get; + set; + } + + public string? style { + get; + set; + } + + internal Paragraph () { + base (); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // Check inline content + base.check (api_root, container, file_path, reporter, settings); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_paragraph (this); + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Paragraph p = new Paragraph (); + p.parent = new_parent; + + p.horizontal_align = horizontal_align; + p.vertical_align = vertical_align; + p.style = style; + + foreach (Inline element in content) { + Inline copy = element.copy (p) as Inline; + p.content.add (copy); + } + + return p; + } +} + diff --git a/libvaladoc/content/resourcelocator.vala b/libvaladoc/content/resourcelocator.vala new file mode 100644 index 000000000..beffdfd0e --- /dev/null +++ b/libvaladoc/content/resourcelocator.vala @@ -0,0 +1,29 @@ +/* resourcelocator.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using GLib; + + +public interface Valadoc.ResourceLocator : Object { + public abstract string resolve (string path); +} + diff --git a/libvaladoc/content/run.vala b/libvaladoc/content/run.vala new file mode 100644 index 000000000..01eace6b2 --- /dev/null +++ b/libvaladoc/content/run.vala @@ -0,0 +1,208 @@ +/* run.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.Run : InlineContent, Inline { + public enum Style { + NONE, + BOLD, + ITALIC, + UNDERLINED, + MONOSPACED, + STROKE, + LANG_KEYWORD, + LANG_LITERAL, + LANG_BASIC_TYPE, + LANG_TYPE, + LANG_PREPROCESSOR, + LANG_COMMENT, + LANG_ESCAPE, + + XML_ESCAPE, + XML_ELEMENT, + XML_ATTRIBUTE, + XML_ATTRIBUTE_VALUE, + XML_COMMENT, + XML_CDATA; + + public static Style? from_string (string str) { + switch (str) { + case "none": + return Style.NONE; + + case "bold": + return Style.BOLD; + + case "italic": + return Style.ITALIC; + + case "underlined": + return Style.UNDERLINED; + + case "monospaced": + return Style.MONOSPACED; + + case "stroke": + return Style.STROKE; + + case "lang-escape": + return Style.LANG_ESCAPE; + + case "lang-keyword": + return Style.LANG_KEYWORD; + + case "lang-literal": + return Style.LANG_LITERAL; + + case "lang-basic-type": + return Style.LANG_BASIC_TYPE; + + case "lang-type": + return Style.LANG_TYPE; + + case "lang-preprocessor": + return Style.LANG_PREPROCESSOR; + + case "lang-comment": + return Style.LANG_COMMENT; + + case "xml-escape": + return Style.XML_ESCAPE; + + case "xml-element": + return Style.XML_ELEMENT; + + case "xml-attribute": + return Style.XML_ATTRIBUTE; + + case "xml-attribute-value": + return Style.XML_ATTRIBUTE_VALUE; + + case "xml-comment": + return Style.XML_COMMENT; + + case "xml-cdata": + return Style.XML_CDATA; + } + + return null; + } + + public unowned string to_string () { + switch (this) { + case Style.NONE: + return "none"; + + case Style.BOLD: + return "bold"; + + case Style.ITALIC: + return "italic"; + + case Style.UNDERLINED: + return "underlined"; + + case Style.MONOSPACED: + return "monospaced"; + + case Style.STROKE: + return "stroke"; + + case Style.LANG_ESCAPE: + return "lang-escape"; + + case Style.LANG_KEYWORD: + return "lang-keyword"; + + case Style.LANG_LITERAL: + return "lang-literal"; + + case Style.LANG_BASIC_TYPE: + return "lang-basic-type"; + + case Style.LANG_TYPE: + return "lang-type"; + + case Style.LANG_PREPROCESSOR: + return "lang-preprocessor"; + + case Style.LANG_COMMENT: + return "lang-comment"; + + case Style.XML_ESCAPE: + return "xml-escape"; + + case Style.XML_ELEMENT: + return "xml-element"; + + case Style.XML_ATTRIBUTE: + return "xml-attribute"; + + case Style.XML_ATTRIBUTE_VALUE: + return "xml-attribute-value"; + + case Style.XML_COMMENT: + return "xml-comment"; + + case Style.XML_CDATA: + return "xml-cdata"; + } + + assert (true); + return ""; + } + } + + public Style style { get; set; } + + internal Run (Style style) { + base (); + _style = style; + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // Check inline content + base.check (api_root, container, file_path, reporter, settings); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_run (this); + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Run run = new Run (style); + run.parent = new_parent; + + foreach (Inline element in content) { + Inline copy = element.copy (run) as Inline; + run.content.add (copy); + } + + return run; + } +} + diff --git a/libvaladoc/content/sourcecode.vala b/libvaladoc/content/sourcecode.vala new file mode 100644 index 000000000..31803f026 --- /dev/null +++ b/libvaladoc/content/sourcecode.vala @@ -0,0 +1,240 @@ +/* sourcecode.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +public class Valadoc.Content.SourceCode : ContentElement, Inline { + public enum Language { + GENIE, + VALA, + XML, + C; + + public static Language? from_path (string path) { + int pos = path.last_index_of ("."); + if (pos < 0) { + return null; + } + + string ext = path.substring (pos + 1); + return from_string (ext, true); + } + + public static Language? from_string (string str, bool is_extension = false) { + switch (str) { + case "genie": + if (is_extension) { + return null; + } + return Language.GENIE; + + case "gs": + return Language.GENIE; + + case "xml": + return Language.XML; + + case "vala": + return Language.VALA; + + case "c": + case "h": + return Language.C; + } + + return null; + } + + public unowned string to_string () { + switch (this) { + case Language.GENIE: + return "genie"; + + case Language.VALA: + return "vala"; + + case Language.XML: + return "xml"; + + case Language.C: + return "c"; + } + + assert (true); + return ""; + } + } + + + public string code { + get; + set; + } + + public Run? highlighted_code { + get; + private set; + } + + public Language? language { + get; + set; + } + + internal SourceCode () { + base (); + _language = Language.VALA; + } + + private string? get_path (string path, Api.Node container, string source_file_path, + ErrorReporter reporter) + { + // search relative to our file + if (!Path.is_absolute (path)) { + string relative_to_file = Path.build_path (Path.DIR_SEPARATOR_S, + Path.get_dirname (source_file_path), + path); + if (FileUtils.test (relative_to_file, FileTest.EXISTS | FileTest.IS_REGULAR)) { + return (owned) relative_to_file; + } + } + + // search relative to the current directory / absoulte path + if (!FileUtils.test (path, FileTest.EXISTS | FileTest.IS_REGULAR)) { + string node_segment = (container is Api.Package)? "" : container.get_full_name () + ": "; + code = "File '%s' does not exist".printf (path); + reporter.simple_warning ("%s: %s{{{".printf (source_file_path, node_segment), + "%s", code); + return null; + } + + return path; + } + + private void load_source_code (string _path, Api.Node container, string source_file_path, + ErrorReporter reporter) + { + string? path = get_path (_path, container, source_file_path, reporter); + if (path == null) { + return ; + } + + try { + string content = null; + FileUtils.get_contents (path, out content); + _language = Language.from_path (path); + code = (owned) content; + } catch (FileError err) { + string node_segment = (container is Api.Package)? "" : container.get_full_name () + ": "; + reporter.simple_error ("%s: %s{{{".printf (source_file_path, node_segment), + "Can't read file '%s': %s", path, err.message); + } + } + + private inline bool is_empty_string (string line) { + for (int i = 0; line[i] != '\0'; i++) { + if (line[i].isspace () == false) { + return false; + } + } + + return true; + } + + private string strip_code (string code) { + string[] lines = code.split ("\n"); + for (int i = lines.length - 1; i >= 0 && is_empty_string (lines[i]); i--) { + lines[i] = null; + } + + string** _lines = lines; + for (int i = 0; lines[i] != null && is_empty_string (lines[i]); i++) { + _lines = &lines[i + 1]; + } + + return string.joinv ("\n", (string[]) _lines); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + string[] splitted = code.split ("\n", 2); + if (splitted[0].strip () == "") { + code = splitted[1] ?? ""; + } else if (splitted[0].has_prefix ("#!")) { + unowned string start = (string) (((char*) splitted[0]) + 2); + if (start.has_prefix ("include:")) { + start = (string) (((char*) start) + 8); + string path = start.strip (); + load_source_code (path, container, file_path, reporter); + } else { + string name = start._strip ().down (); + _language = Language.from_string (name); + code = splitted[1] ?? ""; + if (_language == null && name != "none") { + string node_segment = (container is Api.Package)? "" : container.get_full_name () + ": "; + reporter.simple_warning ("%s: %s{{{".printf (file_path, node_segment), + "Unsupported programming language '%s'", name); + } + } + } + + code = strip_code (code); + + if (_language == Language.VALA) { + highlighted_code = api_root.highlighter.highlight_vala (code); + } else if (_language == Language.XML) { + highlighted_code = api_root.highlighter.highlight_xml (code); + } else if (_language == Language.C) { + highlighted_code = api_root.highlighter.highlight_c (code); + } else { + highlighted_code = new Run (Run.Style.MONOSPACED); + highlighted_code.content.add (new Text (code)); + } + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_source_code (this); + } + + public override void accept_children (ContentVisitor visitor) { + if (highlighted_code != null) { + highlighted_code.accept (visitor); + } + } + + public override bool is_empty () { + // empty source blocks are visible as well + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + SourceCode source_code = new SourceCode (); + source_code.parent = new_parent; + + source_code.language = language; + source_code.code = code; + + return source_code; + } +} diff --git a/libvaladoc/content/styleattributes.vala b/libvaladoc/content/styleattributes.vala new file mode 100644 index 000000000..c24893aaf --- /dev/null +++ b/libvaladoc/content/styleattributes.vala @@ -0,0 +1,116 @@ +/* styleattributes.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public enum Valadoc.Content.HorizontalAlign { + LEFT, + RIGHT, + CENTER; + + public static HorizontalAlign? from_string (string str) { + switch (str) { + case "left": + return HorizontalAlign.LEFT; + + case "right": + return HorizontalAlign.RIGHT; + + case "center": + return HorizontalAlign.CENTER; + } + + return null; + } + + public unowned string to_string () { + switch (this) { + case HorizontalAlign.LEFT: + return "left"; + + case HorizontalAlign.RIGHT: + return "right"; + + case HorizontalAlign.CENTER: + return "center"; + } + + assert (true); + return ""; + } +} + +public enum Valadoc.Content.VerticalAlign { + TOP, + MIDDLE, + BOTTOM; + + public static VerticalAlign? from_string (string str) { + switch (str) { + case "top": + return VerticalAlign.TOP; + + case "middle": + return VerticalAlign.MIDDLE; + + case "bottom": + return VerticalAlign.BOTTOM; + } + + return null; + } + + public unowned string to_string () { + switch (this) { + case VerticalAlign.TOP: + return "top"; + + case VerticalAlign.MIDDLE: + return "middle"; + + case VerticalAlign.BOTTOM: + return "bottom"; + } + + assert (true); + return ""; + } +} + +public interface Valadoc.Content.StyleAttributes : ContentElement { + public abstract HorizontalAlign? horizontal_align { + get; + set; + } + + public abstract VerticalAlign? vertical_align { + get; + set; + } + + public abstract string? style { + get; + set; + } +} + diff --git a/libvaladoc/content/symbollink.vala b/libvaladoc/content/symbollink.vala new file mode 100644 index 000000000..70e528495 --- /dev/null +++ b/libvaladoc/content/symbollink.vala @@ -0,0 +1,72 @@ +/* symbollink.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2014 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.SymbolLink : InlineContent, Inline { + public Api.Node symbol { + get; + set; + } + + public string given_symbol_name { + get; + set; + } + + internal SymbolLink (Api.Node? symbol = null, string? given_symbol_name = null) { + base (); + _symbol = symbol; + _given_symbol_name = given_symbol_name; + } + + public override void configure (Settings settings, ResourceLocator locator) { + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_symbol_link (this); + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + SymbolLink link = new SymbolLink (symbol, _given_symbol_name); + link.parent = new_parent; + + foreach (Inline element in content) { + Inline copy = element.copy (link) as Inline; + link.content.add (copy); + } + + return link; + } +} + diff --git a/libvaladoc/content/table.vala b/libvaladoc/content/table.vala new file mode 100644 index 000000000..0a7941a04 --- /dev/null +++ b/libvaladoc/content/table.vala @@ -0,0 +1,79 @@ +/* table.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.Table : ContentElement, Block { + public Gee.List<TableRow> rows { + get { + return _rows; + } + } + + private Gee.List<TableRow> _rows; + + internal Table () { + base (); + _rows = new ArrayList<TableRow> (); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // Check the table consistency in term of row/column number + + // Check individual rows + foreach (var row in _rows) { + row.parent = this; + row.check (api_root, container, file_path, reporter, settings); + } + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_table (this); + } + + public override void accept_children (ContentVisitor visitor) { + foreach (TableRow element in _rows) { + element.accept (visitor); + } + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Table table = new Table (); + table.parent = new_parent; + + foreach (var row in _rows) { + TableRow copy = row.copy (table) as TableRow; + table.rows.add (copy); + } + + return table; + } +} + diff --git a/libvaladoc/content/tablecell.vala b/libvaladoc/content/tablecell.vala new file mode 100644 index 000000000..50c7fd4ba --- /dev/null +++ b/libvaladoc/content/tablecell.vala @@ -0,0 +1,93 @@ +/* tablecell.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.TableCell : InlineContent, StyleAttributes { + public HorizontalAlign? horizontal_align { + get; + set; + } + + public VerticalAlign? vertical_align { + get; + set; + } + + public string? style { + get; + set; + } + + public int colspan { + get; + set; + } + + public int rowspan { + get; + set; + } + + internal TableCell () { + base (); + _colspan = 1; + _rowspan = 1; + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // Check inline content + base.check (api_root, container, file_path, reporter, settings); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_table_cell (this); + } + + public override bool is_empty () { + // empty cells are displayed as well + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + TableCell cell = new TableCell (); + cell.parent = new_parent; + + cell.horizontal_align = horizontal_align; + cell.vertical_align = vertical_align; + cell.colspan = colspan; + cell.rowspan = rowspan; + cell.style = style; + + foreach (Inline element in content) { + Inline copy = element.copy (cell) as Inline; + cell.content.add (copy); + } + + return cell; + } +} + diff --git a/libvaladoc/content/tablerow.vala b/libvaladoc/content/tablerow.vala new file mode 100644 index 000000000..d8759caac --- /dev/null +++ b/libvaladoc/content/tablerow.vala @@ -0,0 +1,77 @@ +/* tablerow.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.TableRow : ContentElement { + public Gee.List<TableCell> cells { + get { + return _cells; + } + } + + private Gee.List<TableCell> _cells; + + internal TableRow () { + base (); + _cells = new ArrayList<TableCell> (); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // Check individual cells + foreach (var cell in _cells) { + cell.parent = this; + cell.check (api_root, container, file_path, reporter, settings); + } + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_table_row (this); + } + + public override void accept_children (ContentVisitor visitor) { + foreach (TableCell element in _cells) { + element.accept (visitor); + } + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + TableRow row = new TableRow (); + row.parent = new_parent; + + foreach (TableCell cell in _cells) { + TableCell copy = cell.copy (row) as TableCell; + row.cells.add (copy); + } + + return row; + } +} + diff --git a/libvaladoc/content/taglet.vala b/libvaladoc/content/taglet.vala new file mode 100644 index 000000000..c492621a1 --- /dev/null +++ b/libvaladoc/content/taglet.vala @@ -0,0 +1,38 @@ +/* taglet.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public interface Valadoc.Content.Taglet : ContentElement { + + public abstract Rule? get_parser_rule (Rule run_rule); + + public virtual Gee.List<ContentElement>? get_inheritable_documentation () { + return null; + } + + public virtual bool inheritable (Taglet taglet) { + return false; + } +} + diff --git a/libvaladoc/content/text.vala b/libvaladoc/content/text.vala new file mode 100644 index 000000000..42ce1e15f --- /dev/null +++ b/libvaladoc/content/text.vala @@ -0,0 +1,63 @@ +/* text.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.Text : ContentElement, Inline { + public string content { + get; + set; + } + + construct { + _content = ""; + } + + internal Text (string? text = null) { + if (text != null) { + _content = text; + } + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_text (this); + } + + + public override bool is_empty () { + return content == ""; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Text text = new Text (content); + text.parent = new_parent; + return text; + } +} + diff --git a/libvaladoc/content/warning.vala b/libvaladoc/content/warning.vala new file mode 100644 index 000000000..445a447f8 --- /dev/null +++ b/libvaladoc/content/warning.vala @@ -0,0 +1,55 @@ +/* warning.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.Warning : BlockContent, Block { + internal Warning () { + base (); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // Check inline content + base.check (api_root, container, file_path, reporter, settings); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_warning (this); + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Warning warning = new Warning (); + warning.parent = new_parent; + + foreach (Block block in content) { + Block copy = block.copy (warning) as Block; + warning.content.add (copy); + } + + return warning; + } +} + diff --git a/libvaladoc/content/wikilink.vala b/libvaladoc/content/wikilink.vala new file mode 100644 index 000000000..1c180316b --- /dev/null +++ b/libvaladoc/content/wikilink.vala @@ -0,0 +1,78 @@ +/* link.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + + +public class Valadoc.Content.WikiLink : InlineContent, Inline { + public WikiPage page { + internal set; + get; + } + + public string name { + get; + set; + } + + internal WikiLink () { + base (); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + base.check (api_root, container, file_path, reporter, settings); + + page = api_root.wikitree.search (name); + if (page == null) { + string node_segment = (container is Api.Package)? "" : container.get_full_name () + ": "; + reporter.simple_warning ("%s: %s[[".printf (file_path, node_segment), + "'%s' does not exist", name); + return ; + } + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_wiki_link (this); + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + WikiLink link = new WikiLink (); + link.parent = new_parent; + + link.page = page; + link.name = name; + + foreach (Inline element in content) { + Inline copy = element.copy (link) as Inline; + link.content.add (copy); + } + + return link; + } +} diff --git a/libvaladoc/ctyperesolver.vala b/libvaladoc/ctyperesolver.vala new file mode 100644 index 000000000..6901d960e --- /dev/null +++ b/libvaladoc/ctyperesolver.vala @@ -0,0 +1,402 @@ +/* ctyperesolver.vala + * + * Copyright (C) 2010 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Valadoc.Api; +using Gee; + + +/** + * Resolves symbols by C-names + */ +public class Valadoc.CTypeResolver : Visitor { + private Map<string, Api.TypeSymbol> types = new HashMap<string, Api.TypeSymbol> (); + private Map<string, Api.Node> nodes = new HashMap<string, Api.Node> (); + private Api.Tree tree; + + public CTypeResolver (Api.Tree tree) { + tree.accept (this); + this.tree = tree; + } + + private string convert_array_to_camelcase (string[] elements) { + StringBuilder builder = new StringBuilder (); + + foreach (string element in elements) { + builder.append_c (((char[])element)[0].toupper ()); + builder.append (element.next_char ().down ()); + } + + return (owned) builder.str; + } + + private bool is_capitalized_and_underscored (string name) { + unowned string pos; + + unichar c = name.get_char (); + + + if (c < 'A' || c > 'Z') { + return false; + } + + bool last_was_underscore = false; + for (c = (pos = name).get_char (); c != '\0' ; c = (pos = pos.next_char ()).get_char ()) { + if ((c != '_' && !(c >= 'A' && c <= 'Z')) || (last_was_underscore && c == '_')) { + return false; + } + + last_was_underscore = (c == '_'); + } + + return !last_was_underscore; + } + + private string? translate_cname_to_g (string name) { + if (is_capitalized_and_underscored (name)) { + string[] segments = name.split ("_"); + unowned string last_segment = segments[segments.length - 1]; + if (last_segment != "ERROR") { + return null; + } + + return convert_array_to_camelcase (segments); + } + + int length = name.length; + if (length > 5 && name.has_suffix ("Iface")) { + return name.substring (0, length - 5); + } else if (length > 5 && name.has_suffix ("Class")) { + return name.substring (0, length - 5); + } + + return null; + } + + public Api.TypeSymbol? resolve_symbol_type (string name) { + TypeSymbol? symbol = types.get (name); + if (symbol != null) { + return symbol; + } + + if (is_capitalized_and_underscored (name)) { + string[] segments = name.split ("_"); + + if (segments[segments.length - 1] == "TYPE") { + segments.resize (segments.length - 1); + return types.get (convert_array_to_camelcase (segments)); + } else if (segments.length > 2 && segments[1] == "TYPE") { + string[] _segments = segments[1:segments.length]; + _segments[0] = segments[0]; + return types.get (convert_array_to_camelcase (_segments)); + } + } + + return null; + } + + /** + * Resolves symbols by C-names + * + * @param _name a C-name + * @return the resolved node or null + */ + public Api.Node? resolve_symbol (Api.Node? element, string _name) { + string name = _name.replace ("->", ".").replace ("-", "_"); + + if (element != null && name.has_prefix (":")) { + Item parent = element; + while (parent != null && !(parent is Class || parent is Interface)) { + parent = parent.parent; + } + + if (parent is Class && ((Class) parent).get_cname () != null) { + name = ((Class) parent).get_cname () + name; + } else if (parent is Interface && ((Interface) parent).get_cname () != null) { + name = ((Interface) parent).get_cname () + name; + } else { + return null; + } + + } + + Api.Node? node = nodes.get (name); + if (node != null) { + return node; + } + + string? alternative = translate_cname_to_g (name); + if (alternative != null) { + return nodes.get (alternative); + } + + if (element != null && name.has_prefix (":")) { + if (element is Class && ((Class) element).get_cname () != null) { + return nodes.get (((Class) element).get_cname () + "." + name); + } else if (element is Struct && ((Struct) element).get_cname () != null) { + return nodes.get (((Struct) element).get_cname () + "." + name); + } + } + + if (name == "dgettext") { + return nodes.get ("g_dgettext"); + } else if (name == "printf") { + return this.tree.search_symbol_str (null, "GLib.FileStream.printf"); + } + + int dotpos = name.index_of_char ('.'); + if (dotpos > 0) { + string fst = name.substring (0, dotpos); + string snd = name.substring (dotpos + 1); + return nodes.get (fst + ":" + snd); + } + + return null; + } + + private void register_symbol_type (string? name, Api.TypeSymbol symbol) { + if (name != null) { + types.set (name, symbol); + } + } + + private void register_symbol (string? name, Api.Node node) { + if (name != null) { + nodes.set (name.replace ("-", "_"), node); + } + } + + private string? get_parent_type_cname (Item item) { + string parent_cname = null; + if (item.parent is Class) { + parent_cname = ((Class) item.parent).get_cname (); + } else if (item.parent is Interface) { + parent_cname = ((Interface) item.parent).get_cname (); + } else if (item.parent is Struct) { + parent_cname = ((Struct) item.parent).get_cname (); + } else if (item.parent is ErrorDomain) { + parent_cname = ((ErrorDomain) item.parent).get_cname (); + } else if (item.parent is Api.Enum) { + parent_cname = ((Api.Enum) item.parent).get_cname (); + } else { + assert (true); + } + return parent_cname; + } + + /** + * {@inheritDoc} + */ + public override void visit_tree (Api.Tree item) { + item.accept_children (this); + } + + /** + * {@inheritDoc} + */ + public override void visit_package (Package item) { + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_namespace (Namespace item) { + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_interface (Interface item) { + register_symbol (item.get_cname (), item); + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_class (Class item) { + register_symbol_type (item.get_type_id (), item); + register_symbol (item.get_cname (), item); + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_struct (Struct item) { + register_symbol_type (item.get_type_id (), item); + register_symbol (item.get_cname (), item); + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_property (Property item) { + string parent_cname = get_parent_type_cname (item); + assert (parent_cname != null); + + string cname = item.get_cname (); + register_symbol (parent_cname+":"+cname, item); + + + Collection<Interface> interfaces = null; + Collection<Class> classes = null; + + if (item.parent is Interface) { + interfaces = ((Api.Interface) item.parent).get_known_related_interfaces (); + classes = ((Api.Interface) item.parent).get_known_implementations (); + } else if (item.parent is Class) { + interfaces = ((Api.Class) item.parent).get_known_derived_interfaces (); + classes = ((Api.Class) item.parent).get_known_child_classes (); + } + + foreach (Interface iface in interfaces) { + register_symbol (iface.get_cname () + ":" + cname, item); + } + + foreach (Class cl in classes) { + register_symbol (cl.get_cname () + ":" + cname, item); + } + } + + /** + * {@inheritDoc} + */ + public override void visit_field (Field item) { + if (item.parent is Namespace || item.is_static) { + register_symbol (item.get_cname (), item); + } else { + string parent_cname = get_parent_type_cname (item); + if (parent_cname != null) { + register_symbol (parent_cname + "." + item.get_cname (), item); + } + } + } + + /** + * {@inheritDoc} + */ + public override void visit_constant (Constant item) { + register_symbol (item.get_cname (), item); + } + + /** + * {@inheritDoc} + */ + public override void visit_delegate (Delegate item) { + register_symbol (item.get_cname (), item); + } + + /** + * {@inheritDoc} + */ + public override void visit_signal (Api.Signal item) { + string parent_cname = get_parent_type_cname (item); + assert (parent_cname != null); + + string? default_impl_cname = item.get_default_impl_cname (); + string cname = item.get_cname (); + register_symbol (parent_cname+"::"+cname, item); + + if (item.is_virtual) { + // only supported by classes + register_symbol (parent_cname + "Class." + item.name, item); + } + + Collection<Interface> interfaces = null; + Collection<Class> classes = null; + + if (item.parent is Interface) { + interfaces = ((Api.Interface) item.parent).get_known_related_interfaces (); + classes = ((Api.Interface) item.parent).get_known_implementations (); + } else if (item.parent is Class) { + interfaces = ((Api.Class) item.parent).get_known_derived_interfaces (); + classes = ((Api.Class) item.parent).get_known_child_classes (); + } + + foreach (Interface iface in interfaces) { + register_symbol (iface.get_cname () + "::" + cname, item); + } + + foreach (Class cl in classes) { + register_symbol (cl.get_cname () + "::" + cname, item); + } + + if (default_impl_cname != null) { + register_symbol (default_impl_cname, item); + } + } + + /** + * {@inheritDoc} + */ + public override void visit_method (Method item) { + if (item.is_abstract || item.is_virtual || item.is_override) { + string parent_cname = get_parent_type_cname (item); + + if (item.parent is Class) { + register_symbol (parent_cname + "Class." + item.name, item); + } else { + register_symbol (parent_cname + "Iface." + item.name, item); + } + + // Allow to resolve invalid links: + register_symbol (parent_cname + "." + item.name, item); + } + + register_symbol (item.get_cname (), item); + } + + /** + * {@inheritDoc} + */ + public override void visit_error_domain (ErrorDomain item) { + register_symbol (item.get_cname (), item); + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_error_code (ErrorCode item) { + register_symbol (item.get_cname (), item); + } + + /** + * {@inheritDoc} + */ + public override void visit_enum (Api.Enum item) { + register_symbol (item.get_cname (), item); + item.accept_all_children (this, false); + } + + /** + * {@inheritDoc} + */ + public override void visit_enum_value (Api.EnumValue item) { + register_symbol (item.get_cname (), item); + } +} + + diff --git a/libvaladoc/devhelp-markupwriter.vala b/libvaladoc/devhelp-markupwriter.vala new file mode 100644 index 000000000..ae524384c --- /dev/null +++ b/libvaladoc/devhelp-markupwriter.vala @@ -0,0 +1,93 @@ +/* devhelp-markupwriter.vala + * + * Copyright (C) 2008-2009 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +public class Valadoc.Devhelp.MarkupWriter : Valadoc.MarkupWriter { + + public MarkupWriter (FileStream stream, bool xml_declaration = true) { + // avoid broken implicit copy + unowned FileStream _stream = stream; + + base ((str) => { _stream.printf (str); }, xml_declaration); + } + + protected override bool inline_element (string name) { + return name != "book"; + } + + protected override bool content_inline_element (string name) { + return name == "keyword" + || name == "sub"; + } + + public MarkupWriter start_book (string title, string lang, string link, string name, string version, string author) { + this.start_tag ("book", {"xmlns", "http://www.devhelp.net/book", + "title", title, + "language", lang, + "name", name, + "version", version, + "author", author, + "link", link}); + return this; + } + + public MarkupWriter end_book () { + this.end_tag ("book"); + return this; + } + + public MarkupWriter start_functions () { + this.start_tag ("functions"); + return this; + } + + public MarkupWriter end_functions () { + this.end_tag ("functions"); + return this; + } + + public MarkupWriter start_chapters () { + this.start_tag ("chapters"); + return this; + } + + public MarkupWriter end_chapters () { + this.end_tag ("chapters"); + return this; + } + + public MarkupWriter start_sub (string name, string link) { + this.start_tag ("sub", {"name", name, "link", link}); + return this; + } + + public MarkupWriter end_sub () { + this.end_tag ("sub"); + return this; + } + + public MarkupWriter keyword (string name, string type, string link) { + this.start_tag ("keyword", {"type", type, "name", name, "link", link}); + this.end_tag ("keyword"); + return this; + } +} + diff --git a/libvaladoc/doclet.vala b/libvaladoc/doclet.vala new file mode 100644 index 000000000..c8c580444 --- /dev/null +++ b/libvaladoc/doclet.vala @@ -0,0 +1,52 @@ +/* doclet.vala + * + * Copyright (C) 2008-2009 Florian Brosch + * + * 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: + * Brosch Florian <flo.brosch@gmail.com> + */ + +using Gee; + +/** + * A plugin register function for doclets + * + * @see ModuleLoader + */ +[CCode (has_target = false)] +public delegate Type Valadoc.DocletRegisterFunction (ModuleLoader module_loader); + + + +/** + * Provides a mechanism to inspect the API & documentation of programs and libraries + */ +public interface Valadoc.Doclet : GLib.Object { + + /** + * Allows the doclet to inspect the given {@link Api.Tree} + * + * @param settings various configurations + * @param tree the tree to inspect + * @param reporter the reporter to use + * @see Content.ContentVisitor + * @see Api.Visitor + */ + public abstract void process (Settings settings, Api.Tree tree, ErrorReporter reporter); +} + + diff --git a/libvaladoc/documentation/commentscanner.vala b/libvaladoc/documentation/commentscanner.vala new file mode 100644 index 000000000..ad0160167 --- /dev/null +++ b/libvaladoc/documentation/commentscanner.vala @@ -0,0 +1,69 @@ +/* commentscanner.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +public class Valadoc.CommentScanner : WikiScanner { + + public CommentScanner (Settings settings) { + base (settings); + } + + private bool in_line_start; + private bool past_star; + private int start_column; + + public override void reset () { + base.reset (); + + in_line_start = true; + past_star = false; + start_column = 0; + } + + public override int get_line_start_column () { + return start_column; + } + + protected override void accept (unichar c) throws ParserError { + if (in_line_start) { + start_column++; + if (c == '*') { + past_star = true; + } else if (past_star) { + past_star = false; + if (c == '\n') { + base.accept (c); + in_line_start = true; + start_column = 0; + } else { + in_line_start = false; + } + } + } else { + base.accept (c); + if (c == '\n') { + in_line_start = true; + start_column = 0; + } + } + } +} + diff --git a/libvaladoc/documentation/documentation.vala b/libvaladoc/documentation/documentation.vala new file mode 100644 index 000000000..6df57eaa4 --- /dev/null +++ b/libvaladoc/documentation/documentation.vala @@ -0,0 +1,39 @@ +/* documentation.vala + * + * Copyright (C) 2008-2009 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; + +public interface Valadoc.Documentation : Object { + + /** + * The corresponding package + */ + public abstract Api.Package? package { + get; + } + + /** + * The corresponding file name + */ + public abstract string? get_filename (); +} + diff --git a/libvaladoc/documentation/documentationparser.vala b/libvaladoc/documentation/documentationparser.vala new file mode 100644 index 000000000..fe6d68505 --- /dev/null +++ b/libvaladoc/documentation/documentationparser.vala @@ -0,0 +1,897 @@ +/* documentationparser.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Valadoc.Content; +using Valadoc.Importer; +using Gee; + + +public class Valadoc.DocumentationParser : Object, ResourceLocator { + private HashMap<Api.SourceFile, GirMetaData> metadata; + private Importer.InternalIdRegistrar id_registrar; + + + public DocumentationParser (Settings settings, ErrorReporter reporter, + Api.Tree tree, ModuleLoader modules) + { + _settings = settings; + _reporter = reporter; + _tree = tree; + _modules = modules; + + _factory = new ContentFactory (_settings, this, _modules); + + _wiki_scanner = new WikiScanner (_settings); + _wiki_parser = new Parser (_settings, _wiki_scanner, _reporter); + _wiki_scanner.set_parser (_wiki_parser); + + _comment_scanner = new CommentScanner (_settings); + _comment_parser = new Parser (_settings, _comment_scanner, _reporter); + _comment_scanner.set_parser (_comment_parser); + + gtkdoc_parser = new Gtkdoc.Parser (settings, reporter, tree, modules); + gtkdoc_markdown_parser = new Gtkdoc.MarkdownParser (settings, reporter, tree, modules); + + + metadata = new HashMap<Api.SourceFile, GirMetaData> (); + id_registrar = new Importer.InternalIdRegistrar (); + + init_valadoc_rules (); + } + + private Gtkdoc.Parser gtkdoc_parser; + private Gtkdoc.MarkdownParser gtkdoc_markdown_parser; + + private Settings _settings; + private ErrorReporter _reporter; + private Api.Tree _tree; + private ModuleLoader _modules; + + private ContentFactory _factory; + private WikiScanner _wiki_scanner; + private CommentScanner _comment_scanner; + private Parser _wiki_parser; + private Parser _comment_parser; + + private Parser _parser; + private Scanner _scanner; + + public Comment? parse (Api.Node element, Api.SourceComment comment) { + if (comment is Api.GirSourceComment) { + Api.GirSourceComment gir_comment = (Api.GirSourceComment) comment; + GirMetaData metadata = get_metadata_for_comment (gir_comment); + + if (metadata.is_docbook) { + Comment doc_comment = gtkdoc_parser.parse (element, gir_comment, metadata, id_registrar); + return doc_comment; + } else { + Comment doc_comment = gtkdoc_markdown_parser.parse (element, gir_comment, metadata, id_registrar); + return doc_comment; + } + } else { + return parse_comment_str (element, comment.content, comment.file.get_name (), + comment.first_line, comment.first_column); + } + } + + public Comment? parse_comment_str (Api.Node element, string content, string filename, + int first_line, int first_column) + { + try { + Comment doc_comment = parse_comment (content, filename, first_line, first_column); + return doc_comment; + } catch (ParserError error) { + return null; + } + } + + public Page? parse_wikipage (Api.Package pkg, WikiPage page) { + if (page.documentation != null) { + return page.documentation; + } + + if (page.documentation_str == null) { + return null; + } + + try { + Page documentation = parse_wiki (page.documentation_str, page.get_filename ()); + return documentation; + } catch (ParserError error) { + return null; + } + } + + private Comment parse_comment (string content, string filename, int first_line, int first_column) + throws ParserError + { + _parser = _comment_parser; + _scanner = _comment_scanner; + _stack.clear (); + _comment_parser.parse (content, filename, first_line, first_column); + return (Comment) pop (); + } + + private Page parse_wiki (string content, string filename) throws ParserError { + _parser = _wiki_parser; + _scanner = _wiki_scanner; + _stack.clear (); + _wiki_parser.parse (content, filename, 0, 0); + return (Page) pop (); + } + + public void check (Api.Node element, Comment comment) { + comment.check (_tree, element, element.get_source_file ().relative_path, _reporter, _settings); + } + + public void check_wikipage (Api.Package package, WikiPage page) { + page.documentation.check (_tree, package, page.path, _reporter, _settings); + } + + public void transform_inheritdoc (Api.Node taglet_owner, Taglets.InheritDoc taglet) { + if (taglet.inherited == null) { + return ; + } + + + taglet.inherited.parse_comments (_settings, this); + if (taglet.inherited.documentation == null) { + return ; + } + + + taglet.inherited.check_comments (_settings, this); + + taglet.transform (_tree, taglet_owner, taglet_owner.get_source_file ().get_name (), _reporter, _settings); + } + + private GirMetaData get_metadata_for_comment (Api.GirSourceComment gir_comment) { + GirMetaData metadata = metadata.get (gir_comment.file); + if (metadata != null) { + return metadata; + } + + metadata = new GirMetaData (gir_comment.file.relative_path, _settings.metadata_directories, _reporter); + if (metadata.index_sgml != null) { + id_registrar.read_index_sgml_file (metadata.index_sgml, metadata.index_sgml_online, _reporter); + } + + this.metadata.set (gir_comment.file, metadata); + return metadata; + } + + public string resolve (string path) { + return path; + } + + private ArrayList<Object> _stack = new ArrayList<Object> (); + + private void push (Object element) { + _stack.add (element); + } + + private Object peek (int offset = -1) { + assert (_stack.size >= - offset); + return _stack.get (_stack.size + offset); + } + + private Object pop () { + Object node = peek (); + _stack.remove_at (_stack.size - 1); + return node; + } + + private Rule multiline_block_run; + private Rule multiline_run; + private int current_level = 0; + private int[] levels = new int[0]; + + private void new_list_item (Content.List.Bullet bullet) throws ParserError { + var new_item = _factory.create_list_item (); + + Content.List list = null; + if (levels.length >= 1) { + if (current_level > levels[levels.length - 1]) { + list = _factory.create_list (); + list.bullet = bullet; + + var current_item = peek () as ListItem; + current_item.content.add (list); + push (list); + + levels += current_level; + } else { + bool poped_some_lists = false; + while (current_level < levels[levels.length - 1]) { + // Pop current item and list + pop (); + pop (); + levels.resize (levels.length - 1); + poped_some_lists = true; + } + list = peek (-2) as Content.List; + + if (!poped_some_lists && bullet == Content.List.Bullet.NONE) { + ((Paragraph) ((ListItem) peek ()).content[0]).content.add (_factory.create_text (" ")); + return; + } else if (list.bullet != bullet) { + _parser.error (null, "Invalid bullet type '%s': expected '%s'" + .printf (bullet_type_string (bullet), bullet_type_string (list.bullet))); + return; + } + + pop (); + } + } else { + list = _factory.create_list (); + list.bullet = bullet; + + ((BlockContent) peek ()).content.add (list); + push (list); + + levels = new int[0]; + levels += current_level; + } + + list.items.add (new_item); + push (new_item); + } + + private string bullet_type_string (Content.List.Bullet bullet) { + switch (bullet) { + case Content.List.Bullet.NONE: + return "."; + case Content.List.Bullet.UNORDERED: + return "*"; + case Content.List.Bullet.ORDERED_NUMBER: + return "1."; + case Content.List.Bullet.ORDERED_LOWER_CASE_ALPHA: + return "a."; + case Content.List.Bullet.ORDERED_UPPER_CASE_ALPHA: + return "A."; + case Content.List.Bullet.ORDERED_LOWER_CASE_ROMAN: + return "i."; + case Content.List.Bullet.ORDERED_UPPER_CASE_ROMAN: + return "I."; + } + return ""; + } + + private void finish_list () { + while (peek () is ListItem) { + pop (); + pop (); + levels.resize (levels.length - 1); + } + } + + private void add_content_string (string str) { + var text = peek () as Text; + if (text == null) { + push (text = _factory.create_text ()); + } + text.content += str; + } + + private void add_content_space () { + // avoid double spaces + var head = peek (); + Text text_node = null; + + if (head is Text) { + text_node = (Text) head; + } else if (head is InlineContent && ((InlineContent) head).content.size > 0 + && ((InlineContent) head).content.last () is Text) + { + text_node = (Text) ((InlineContent) head).content.last (); + } else { + text_node = _factory.create_text (); + ((InlineContent) peek ()).content.add (text_node); + } + + if (!text_node.content.has_suffix (" ")) { + text_node.content += " "; + } + } + + private void init_valadoc_rules () { + // Inline rules + + StubRule run = new StubRule (); + run.set_name ("Run"); + + TokenType.Action add_text = (token) => { + add_content_string (token.to_string ()); + }; + + TokenType space = TokenType.SPACE.action ((token) => { add_content_space (); }); + TokenType word = TokenType.any_word ().action (add_text); + + Rule optional_invisible_spaces = + Rule.option ({ + Rule.many ({ TokenType.SPACE }) + }); + + Rule optional_spaces = + Rule.option ({ + Rule.many ({ + TokenType.SPACE.action ((token) => { add_content_space (); }) + }) + }); + + + Rule text = + Rule.many ({ + Rule.one_of ({ + TokenType.BREAK.action ((token) => { add_content_string ("\n"); }), + TokenType.CLOSED_BRACE.action (add_text), + TokenType.MINUS.action (add_text), + TokenType.ALIGN_BOTTOM.action (add_text), + TokenType.ALIGN_TOP.action (add_text), + TokenType.GREATER_THAN.action (add_text), + TokenType.LESS_THAN.action (add_text), + TokenType.DOUBLE_PIPE.action (add_text), + TokenType.PIPE.action (add_text), + TokenType.ALIGN_RIGHT.action (add_text), + TokenType.ALIGN_CENTER.action (add_text), + TokenType.EQUAL_1.action (add_text), + TokenType.EQUAL_2.action (add_text), + TokenType.EQUAL_3.action (add_text), + TokenType.EQUAL_4.action (add_text), + TokenType.EQUAL_5.action (add_text), + word + }), + Rule.option ({ space }) + }) + .set_name ("Text") + .set_start (() => { push (_factory.create_text ()); }); + + Rule run_with_spaces = + Rule.seq ({ + Rule.many ({ + Rule.one_of ({ + optional_invisible_spaces, + run + }) + }) + }) + .set_name ("RunWithSpaces"); + + multiline_run = Rule.many ({ + run_with_spaces, + TokenType.EOL.action (() => { add_content_space (); }) + }) + .set_name ("MultiLineRun"); + + Rule inline_taglet = + Rule.seq ({ + TokenType.OPEN_BRACE, + Rule.option ({ + TokenType.AROBASE, + TokenType.any_word ().action ((token) => { + var taglet = _factory.create_taglet (token.to_string ()); + if (!(taglet is Inline)) { + _parser.error (null, "Invalid taglet in this context: %s".printf (token.to_string ())); + } + push (taglet); + Rule? taglet_rule = taglet.get_parser_rule (multiline_run); + if (taglet_rule != null) { + _parser.push_rule (Rule.seq ({ TokenType.SPACE, taglet_rule })); + } + }), + TokenType.CLOSED_BRACE + }) + .set_skip (() => { add_content_string ("{"); }) + }) + .set_name ("InlineTaglet"); + + //TODO: Find a nicer way to allow empty tags (''run?'' won't work) + Rule bold = + Rule.seq ({ + TokenType.SINGLE_QUOTE_2, + Rule.one_of ({ + TokenType.SINGLE_QUOTE_2, + Rule.seq ({ optional_spaces, run, TokenType.SINGLE_QUOTE_2 }) + }) + }) + .set_name ("Bold") + .set_start (() => { push (_factory.create_run (Run.Style.BOLD)); }); + + Rule italic = + Rule.seq ({ + TokenType.SLASH_2, + Rule.one_of ({ + TokenType.SLASH_2, + Rule.seq ({ optional_spaces, run, TokenType.SLASH_2 }) + }) + }) + .set_name ("Italic") + .set_start (() => { push (_factory.create_run (Run.Style.ITALIC)); }); + + Rule underlined = + Rule.seq ({ + TokenType.UNDERSCORE_2, + Rule.one_of ({ + TokenType.UNDERSCORE_2, + Rule.seq ({ optional_spaces, run, TokenType.UNDERSCORE_2 }) + }) + }) + .set_name ("Underlined") + .set_start (() => { push (_factory.create_run (Run.Style.UNDERLINED)); }); + + Rule monospace = + Rule.seq ({ + TokenType.BACK_QUOTE_2, + Rule.one_of ({ + TokenType.BACK_QUOTE_2, + Rule.seq ({ optional_spaces, run, TokenType.BACK_QUOTE_2 }) + }) + }) + .set_name ("Monospace") + .set_start (() => { push (_factory.create_run (Run.Style.MONOSPACED)); }); + + Rule embedded = + Rule.seq ({ + TokenType.DOUBLE_OPEN_BRACE.action (() => { ((WikiScanner) _scanner).set_url_escape_mode (true); }), + TokenType.any_word ().action ((token) => { ((Embedded) peek ()).url = token.to_string (); }), + Rule.option ({ + TokenType.PIPE.action (() => { ((WikiScanner) _scanner).set_url_escape_mode (false); }), + text + }) + .set_reduce (() => { var caption = pop () as Text; ((Embedded) peek ()).caption = caption.content; }), + TokenType.DOUBLE_CLOSED_BRACE.action (() => { ((WikiScanner) _scanner).set_url_escape_mode (false); }) + }) + .set_name ("Embedded") + .set_start (() => { push (_factory.create_embedded ()); }); + + Rule link = + Rule.seq ({ + TokenType.DOUBLE_OPEN_BRACKET.action (() => { ((WikiScanner) _scanner).set_url_escape_mode (true); }), + TokenType.any_word ().action ((token) => { + var url = token.to_string (); + if (url.has_suffix (".valadoc")) { + var link = _factory.create_wiki_link (); + link.name = url; + push (link); + } else { + var link = _factory.create_link (); + link.url = url; + push (link); + } + }), + Rule.option ({ + TokenType.PIPE.action (() => { ((WikiScanner) _scanner).set_url_escape_mode (false); }), + run + }), + TokenType.DOUBLE_CLOSED_BRACKET.action (() => { ((WikiScanner) _scanner).set_url_escape_mode (false); }) + }) + .set_name ("Link"); + Rule source_code = + Rule.seq ({ + TokenType.TRIPLE_OPEN_BRACE.action ((token) => { ((WikiScanner) _scanner).set_code_escape_mode (true); }), + TokenType.any_word ().action ((token) => { ((SourceCode) peek ()).code = token.to_string (); }), + TokenType.TRIPLE_CLOSED_BRACE.action ((token) => { ((WikiScanner) _scanner).set_code_escape_mode (false); }) + }) + .set_name ("SourceCode") + .set_start (() => { push (_factory.create_source_code ()); }); + + Rule.Action append_head_to_head2 = () => { + var head = (Inline) pop (); + ((InlineContent) peek ()).content.add (head); + }; + + Rule run_subrules = + Rule.one_of ({ + Rule.seq ({ + text + }) + .set_reduce (append_head_to_head2), + Rule.seq ({ + Rule.one_of ({ + inline_taglet, bold, italic, underlined, monospace, embedded, link, source_code + }) + .set_reduce (append_head_to_head2), + optional_spaces + }) + }); + + Rule run_arobase = + Rule.seq ({ + TokenType.AROBASE.action (add_text) + }) + .set_reduce (append_head_to_head2); + + run.set_rule ( + Rule.seq ({ + run_subrules, + optional_spaces, + Rule.option ({ + Rule.many ({ + Rule.one_of ({ + run_arobase, + run_subrules, + optional_spaces + }) + }) + }) + }) + .set_name ("Run") + ); + + + // Block rules + + Rule paragraph = + Rule.seq ({ + Rule.option ({ + Rule.one_of ({ + TokenType.ALIGN_CENTER.action (() => { ((Paragraph) peek ()).horizontal_align = HorizontalAlign.CENTER; }), + TokenType.ALIGN_RIGHT.action (() => { ((Paragraph) peek ()).horizontal_align = HorizontalAlign.RIGHT; }) + }) + }), + Rule.many ({ + run, + TokenType.EOL.action (() => { add_content_space (); }) + }) + }) + .set_name ("Paragraph") + .set_start (() => { push (_factory.create_paragraph ()); }) + .set_reduce (() => { + var head = (Paragraph) pop (); + ((BlockContent) peek ()).content.add (head); + + Text last_element = head.content.last () as Text; + if (last_element != null) { + last_element.content._chomp (); + } + }); + + Rule warning = + Rule.seq ({ + TokenType.str ("Warning:"), + optional_invisible_spaces, + Rule.many ({ + Rule.seq({optional_invisible_spaces, run}), + TokenType.EOL.action (() => { add_content_space (); }) + }) + }) + .set_name ("Warning") + .set_start (() => { push (_factory.create_paragraph ()); }) + .set_reduce (() => { + var head = _factory.create_warning (); + head.content.add ((Paragraph) pop ()); + ((BlockContent) peek ()).content.add (head); + + Text last_element = head.content.last () as Text; + if (last_element != null) { + last_element.content._chomp (); + } + }); + + Rule note = + Rule.seq ({ + TokenType.str ("Note:"), + optional_invisible_spaces, + Rule.many ({ + Rule.seq({optional_invisible_spaces, run}), + TokenType.EOL.action (() => { add_content_space (); }) + }) + }) + .set_name ("Note") + .set_start (() => { push (_factory.create_paragraph ()); }) + .set_reduce (() => { + var head = _factory.create_note (); + head.content.add ((Paragraph) pop ()); + ((BlockContent) peek ()).content.add (head); + + Text last_element = head.content.last () as Text; + if (last_element != null) { + last_element.content._chomp (); + } + }); + + Rule indented_item = + Rule.seq ({ + Rule.many ({ + TokenType.SPACE.action ((token) => { current_level++; }) + }), + Rule.option ({ + Rule.one_of ({ + TokenType.str (".").action ((token) => { new_list_item (Content.List.Bullet.NONE); }), + TokenType.str ("*").action ((token) => { new_list_item (Content.List.Bullet.UNORDERED); }), + TokenType.str ("#").action ((token) => { new_list_item (Content.List.Bullet.ORDERED); }), + TokenType.str ("1.").action ((token) => { new_list_item (Content.List.Bullet.ORDERED_NUMBER); }), + TokenType.str ("a.").action ((token) => { new_list_item (Content.List.Bullet.ORDERED_LOWER_CASE_ALPHA); }), + TokenType.str ("A.").action ((token) => { new_list_item (Content.List.Bullet.ORDERED_UPPER_CASE_ALPHA); }), + TokenType.str ("i.").action ((token) => { new_list_item (Content.List.Bullet.ORDERED_LOWER_CASE_ROMAN); }), + TokenType.str ("I.").action ((token) => { new_list_item (Content.List.Bullet.ORDERED_UPPER_CASE_ROMAN); }) + }), + optional_invisible_spaces + }) + .set_skip (() => { new_list_item (Content.List.Bullet.NONE); }), + Rule.seq ({ run }) + .set_start (() => { + var content = _factory.create_paragraph (); + ((ListItem) peek ()).content.add (content); + push (content); + }) + .set_reduce (() => { pop (); }), + TokenType.EOL + }) + .set_name ("IndentedItem") + .set_start (() => { current_level = 0; }) + .set_reduce (() => { + var content_list = ((ListItem) peek ()).content; + if (content_list.size > 0 && content_list.last () is Text) { + ((Text) content_list.last ()).content._chomp (); + } + }); + + Rule indented_blocks = + Rule.many ({ + indented_item + }) + .set_name ("IndentedBlocks") + .set_reduce (() => { finish_list (); }); + + Rule table_cell_attributes = + Rule.seq ({ + TokenType.LESS_THAN, + Rule.option ({ + Rule.one_of ({ + Rule.seq ({ + Rule.option ({ + Rule.one_of ({ + TokenType.ALIGN_RIGHT.action ((token) => { ((TableCell) peek ()).horizontal_align = HorizontalAlign.RIGHT; }), + TokenType.ALIGN_CENTER.action ((token) => { ((TableCell) peek ()).horizontal_align = HorizontalAlign.CENTER; }) + }) + }), + Rule.option ({ + Rule.one_of ({ + TokenType.ALIGN_TOP.action ((token) => { ((TableCell) peek ()).vertical_align = VerticalAlign.TOP; }), + TokenType.ALIGN_BOTTOM.action ((token) => { ((TableCell) peek ()).vertical_align = VerticalAlign.BOTTOM; }) + }) + }) + }), + TokenType.any_word ().action ((token) => { ((TableCell) peek ()).style = token.to_string (); }) + }) + }), + Rule.option ({ + Rule.one_of ({ + Rule.seq ({ + TokenType.PIPE, + TokenType.any_number ().action ((token) => { ((TableCell) peek ()).rowspan = token.to_int (); }) + }), + Rule.seq ({ + TokenType.MINUS, + TokenType.any_number ().action ((token) => { ((TableCell) peek ()).colspan = token.to_int (); }) + }) + }) + }), + TokenType.GREATER_THAN + }) + .set_name ("CellAttributes"); + Rule table_cell = + Rule.seq ({ + Rule.seq ({ + Rule.option ({ + table_cell_attributes + }), + optional_invisible_spaces, + run + }), + TokenType.DOUBLE_PIPE + }) + .set_name ("Cell") + .set_start (() => { push (_factory.create_table_cell ()); }) + .set_reduce (() => { + var head = (TableCell) pop (); + ((TableRow) peek ()).cells.add (head); + + if (head.content.size > 0 && head.content.last () is Text) { + ((Text) head.content.last ()).content._chomp (); + } + }); + Rule table_row = + Rule.seq ({ + TokenType.DOUBLE_PIPE, + Rule.many ({ + table_cell + }), + TokenType.EOL + }) + .set_name ("Row") + .set_start (() => { push (_factory.create_table_row ()); }) + .set_reduce (() => { + var head = (TableRow) pop (); + ((Table) peek ()).rows.add (head); + }); + Rule table = + Rule.seq ({ + Rule.many ({ + table_row + }) + }) + .set_name ("Table") + .set_start (() => { push (_factory.create_table ()); }) + .set_reduce (() => { + var head = (Block) pop (); + ((BlockContent) peek ()).content.add (head); + }); + + Rule headline = + Rule.one_of ({ + Rule.seq ({ + TokenType.EQUAL_1.action ((token) => { ((Headline) peek ()).level = 1; }), + optional_invisible_spaces, + run, + optional_invisible_spaces, + TokenType.EQUAL_1, + TokenType.EOL + }), + Rule.seq ({ + TokenType.EQUAL_2.action ((token) => { ((Headline) peek ()).level = 2; }), + optional_invisible_spaces, + run, + optional_invisible_spaces, + TokenType.EQUAL_2, + TokenType.EOL + }), + Rule.seq ({ + TokenType.EQUAL_3.action ((token) => { ((Headline) peek ()).level = 3; }), + optional_invisible_spaces, + run, + optional_invisible_spaces, + TokenType.EQUAL_3, + TokenType.EOL + }), + Rule.seq ({ + TokenType.EQUAL_4.action ((token) => { ((Headline) peek ()).level = 4; }), + optional_invisible_spaces, + run, + optional_invisible_spaces, + TokenType.EQUAL_4, + TokenType.EOL + }), + Rule.seq ({ + TokenType.EQUAL_5.action ((token) => { ((Headline) peek ()).level = 5; }), + optional_invisible_spaces, + run, + optional_invisible_spaces, + TokenType.EQUAL_5, + TokenType.EOL + }) + }) + .set_name ("Headline") + .set_start (() => { push (_factory.create_headline ()); }) + .set_reduce (() => { + var head = (Block) pop (); + ((BlockContent) peek ()).content.add (head); + }); + + Rule blocks = + Rule.one_of ({ + indented_blocks, + table, + headline, + warning, + note, + paragraph + }) + .set_name ("Blocks"); + + Rule page = + Rule.seq ({ + blocks, + Rule.option ({ + Rule.many ({ + TokenType.EOL, + Rule.option ({ blocks }) + }) + }) + }) + .set_name ("Page") + .set_start (() => { push (_factory.create_page ()); }); + + Rule description = + Rule.seq ({ + blocks, + Rule.option ({ + Rule.many ({ + TokenType.EOL, + Rule.option ({ blocks }) + }) + }) + }) + .set_name ("Description"); + + multiline_block_run = + Rule.seq ({ + multiline_run + }) + .set_start (() => { push (_factory.create_paragraph ()); }) + .set_reduce (() => { + Paragraph p = (Paragraph) pop (); + ((BlockContent) peek ()).content.add (p); + }) + .set_name ("BlockMultilineRun"); + + Rule taglet = + Rule.seq ({ + TokenType.AROBASE, + TokenType.any_word ().action ((token) => { + + string tag_name = token.to_string (); + var taglet = _factory.create_taglet (tag_name); + if (!(taglet is Block)) { + _parser.error (token, "Invalid taglet in this context"); + } + push (taglet); + + Rule? taglet_rule; + if (taglet is BlockContent) { + taglet_rule = taglet.get_parser_rule (multiline_block_run); + } else { + taglet_rule = taglet.get_parser_rule (multiline_run); + } + + if (taglet_rule != null) { + _parser.push_rule (Rule.seq ({ TokenType.SPACE, taglet_rule })); + } + }), + Rule.option ({ + Rule.many ({ TokenType.EOL }) + }) + }) + .set_name ("Taglet") + .set_reduce (() => { + var head = (Taglet) pop (); + ((Comment) peek ()).taglets.add (head); + }); + + Rule comment = + Rule.seq ({ + TokenType.EOL, + Rule.option ({ + description + }), + Rule.option ({ + Rule.many ({ taglet }) + }) + }) + .set_name ("Comment") + .set_start (() => { push (_factory.create_comment ()); }); + + _comment_parser.set_root_rule (comment); + _wiki_parser.set_root_rule (page); + } + +#if DEBUG + private void dump_stack (string title) { + message ("=== Dumping stack: %s ===", title); + foreach (Object object in _stack) { + message ("%s", object.get_type ().name ()); + } + } +#endif +} diff --git a/libvaladoc/documentation/girmetadata.vala b/libvaladoc/documentation/girmetadata.vala new file mode 100644 index 000000000..2dbae65e4 --- /dev/null +++ b/libvaladoc/documentation/girmetadata.vala @@ -0,0 +1,156 @@ +/* girmetadata.vala + * + * Copyright (C) 2012-2014 Florian Brosch + * + * 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: + * Brosch Florian <flo.brosch@gmail.com> + */ + +using Gee; + + +/** + * Metadata reader for GIR files + */ +public class Valadoc.GirMetaData : Object { + private string? metadata_path = null; + private string? resource_dir = null; + + + public bool is_docbook { private set; get; default = false; } + public string index_sgml { private set; get; default = null; } + public string index_sgml_online { private set; get; default = null; } + + /** + * Used to manipulate paths to resources inside gir-files + */ + public string get_resource_path (string resource) { + if (resource_dir == null || metadata_path == null) { + return resource; + } + + if (Path.is_absolute (resource_dir)) { + return Path.build_filename (resource_dir, resource); + } + + return Path.build_filename (Path.get_dirname (metadata_path), resource_dir, resource); + } + + private string? get_metadata_file_name (string gir_file_path) { + string metadata_file_name = Path.get_basename (gir_file_path); + int last_dot_pos = metadata_file_name.last_index_of ("."); + if (last_dot_pos < 0) { + return null; + } + + metadata_file_name = metadata_file_name.substring (0, last_dot_pos); + return metadata_file_name + ".valadoc.metadata"; + } + + private string? get_metadata_path (string gir_file_path, string[] metadata_dirs) { + string? metadata_file_name = get_metadata_file_name (gir_file_path); + if (metadata_file_name == null) { + return null; + } + + // search for metatada at the same location as the gir file + string metadata_path = Path.build_filename (Path.get_dirname (gir_file_path), metadata_file_name); + if (FileUtils.test (metadata_path, FileTest.IS_REGULAR)) { + return metadata_path; + } + + foreach (string metadata_dir in metadata_dirs) { + metadata_path = Path.build_filename (metadata_dir, metadata_file_name); + if (FileUtils.test (metadata_path, FileTest.IS_REGULAR)) { + return metadata_path; + } + } + + return null; + } + + private void load_general_metadata (KeyFile key_file, ErrorReporter reporter) throws KeyFileError { + foreach (string key in key_file.get_keys ("General")) { + switch (key) { + case "resources": + this.resource_dir = key_file.get_string ("General", "resources"); + break; + + case "is_docbook": + this.is_docbook = key_file.get_boolean ("General", "is_docbook"); + break; + + case "index_sgml": + string tmp = key_file.get_string ("General", "index_sgml"); + this.index_sgml = Path.build_filename (Path.get_dirname (metadata_path), tmp); + break; + + case "index_sgml_online": + this.index_sgml_online = key_file.get_string ("General", "index_sgml_online"); + break; + + default: + reporter.simple_warning (metadata_path, "Unknown key 'General.%s'", key); + break; + } + } + } + + public GirMetaData (string gir_file_path, string[] metadata_dirs, ErrorReporter reporter) { + if (!FileUtils.test (gir_file_path, FileTest.IS_REGULAR)) { + return ; + } + + metadata_path = get_metadata_path (gir_file_path, metadata_dirs); + if (metadata_path == null) { + return ; + } + + KeyFile key_file; + + try { + key_file = new KeyFile (); + key_file.load_from_file (metadata_path, KeyFileFlags.NONE); + } catch (KeyFileError e) { + reporter.simple_error (metadata_path, "%s", e.message); + return ; + } catch (FileError e) { + reporter.simple_error (metadata_path, "%s", e.message); + return ; + } + + try { + foreach (string group in key_file.get_groups ()) { + switch (group) { + case "General": + load_general_metadata (key_file, reporter); + break; + + default: + reporter.simple_warning (metadata_path, "Unknown group '%s'", group); + break; + } + } + } catch (KeyFileError e) { + reporter.simple_error (null, "Unable to read file '%s': %s", metadata_path, e.message); + } + + + // Load internal link lut: + } +} + diff --git a/libvaladoc/documentation/gtkdoccommentparser.vala b/libvaladoc/documentation/gtkdoccommentparser.vala new file mode 100644 index 000000000..81dc52a8f --- /dev/null +++ b/libvaladoc/documentation/gtkdoccommentparser.vala @@ -0,0 +1,1668 @@ +/* gtkcommentparser.vala + * + * Copyright (C) 2011-2014 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +using Valadoc.Content; +using Valadoc.Gtkdoc; +using Gee; + + + +public class Valadoc.Gtkdoc.Parser : Object, ResourceLocator { + private Scanner scanner = new Scanner (); + private Token current; + + private LinkedList<string> stack = new LinkedList<string> (); + private LinkedList<LinkedList<Block>> footnotes = new LinkedList<LinkedList<Block>> (); + + private ContentFactory factory; + private ErrorReporter reporter; + private Settings settings; + private Api.Tree tree; + private Api.Node? element; + + private bool show_warnings; + private Api.SourceComment comment; + private unowned string instance_param_name; + + private string[]? comment_lines; + + private Regex? is_numeric_regex = null; + private Regex? normalize_regex = null; + private Regex regex_source_lang = null; + + private Importer.InternalIdRegistrar id_registrar = null; + private GirMetaData? current_metadata = null; + + private inline string fix_resource_path (string path) { + return this.current_metadata.get_resource_path (path); + } + + private void reset (Api.SourceComment comment) { + this.scanner.reset (comment.content); + this.show_warnings = !comment.file.package.is_package; + this.comment_lines = null; + this.footnotes.clear (); + this.comment = comment; + this.current = null; + this.stack.clear (); + } + + private string normalize (string text) { + try { + return normalize_regex.replace (text, -1, 0, " "); + } catch (RegexError e) { + assert_not_reached (); + } + } + + private bool is_numeric (string str) { + return is_numeric_regex.match (str); + } + + private void report_unexpected_token (Token got, string expected) { + report_warning (got, "Unexpected Token: %s (Expected: %s)".printf (got.to_string (), expected)); + } + + private void report_warning (Token got, string message) { + if (!this.show_warnings) { + return ; + } + + int startpos = (got.line == 0)? comment.first_column + got.first_column : got.first_column; + int endpos = (got.line == 0)? comment.first_column + got.last_column : got.last_column; + + if (this.comment_lines == null) { + this.comment_lines = this.comment.content.split ("\n"); + } + + this.reporter.warning (this.comment.file.get_name (), + comment.first_line + got.line, + startpos + 1, + endpos + 1, + this.comment_lines[got.line], + message); + } + + public Parser (Settings settings, ErrorReporter reporter, Api.Tree tree, ModuleLoader modules) { + this.factory = new ContentFactory (settings, this, modules); + this.reporter = reporter; + this.settings = settings; + this.tree = tree; + + try { + is_numeric_regex = new Regex ("^[+-]?([0-9]*\\.?[0-9]+|[0-9]+\\.?[0-9]*)([eE][+-]?[0-9]+)?$", + RegexCompileFlags.OPTIMIZE); + normalize_regex = new Regex ("( |\n|\t)+", RegexCompileFlags.OPTIMIZE); + regex_source_lang = new Regex ("^<!--[ \t]+language=\"([A-Za-z]*)\"[ \t]+-->"); + } catch (RegexError e) { + assert_not_reached (); + } + } + + private Note? _parse_note (Api.SourceComment comment) { + Comment? cmnt = parse_root_content (comment); + if (cmnt == null) { + return null; + } + + Note note = factory.create_note (); + note.content.add_all (cmnt.content); + + return note; + } + + private void add_note (ref Comment? comment, Note? note) { + if (note == null) { + return ; + } + + if (comment == null) { + comment = factory.create_comment (); + } + + if (comment.content.size == 0) { + comment.content.add (factory.create_paragraph ()); + } + + comment.content.insert (1, note); + } + + private void add_taglet (ref Comment? comment, Taglet? taglet) { + if (taglet == null) { + return ; + } + + if (comment == null) { + comment = factory.create_comment (); + } + + comment.taglets.add (taglet); + } + + public Comment? parse (Api.Node element, Api.GirSourceComment gir_comment, GirMetaData gir_metadata, Importer.InternalIdRegistrar id_registrar) { + this.instance_param_name = gir_comment.instance_param_name; + this.current_metadata = gir_metadata; + this.id_registrar = id_registrar; + this.element = element; + + + Comment? cmnt = parse_root_content (gir_comment); + if (cmnt != null) { + ImporterHelper.extract_short_desc (cmnt, factory); + } + + + // deprecated: + if (gir_comment.deprecated_comment != null) { + Note? note = _parse_note (gir_comment.deprecated_comment); + add_note (ref cmnt, note); + } + + + // version: + if (gir_comment.version_comment != null) { + Note? note = _parse_note (gir_comment.version_comment); + add_note (ref cmnt, note); + } + + // stability: + if (gir_comment.stability_comment != null) { + Note? note = _parse_note (gir_comment.stability_comment); + add_note (ref cmnt, note); + } + + + // return: + if (gir_comment.return_comment != null) { + Taglet? taglet = parse_block_taglet (gir_comment.return_comment, "return"); + add_taglet (ref cmnt, taglet); + } + + + // parameters: + MapIterator<string, Api.SourceComment> iter = gir_comment.parameter_iterator (); + for (bool has_next = iter.next (); has_next; has_next = iter.next ()) { + Taglets.Param? taglet = parse_block_taglet (iter.get_value (), "param") as Taglets.Param; + string param_name = iter.get_key (); + + taglet.is_c_self_param = (param_name == gir_comment.instance_param_name); + taglet.parameter_name = param_name; + add_taglet (ref cmnt, taglet); + } + + + bool first = true; + foreach (LinkedList<Block> note in this.footnotes) { + if (first == true && note.size > 0) { + Paragraph p = note.first () as Paragraph; + if (p == null) { + p = factory.create_paragraph (); + cmnt.content.add (p); + } + + p.content.insert (0, factory.create_text ("\n")); + } + cmnt.content.add_all (note); + first = false; + } + + return cmnt; + } + + private Taglet? parse_block_taglet (Api.SourceComment gir_comment, string taglet_name) { + this.reset (gir_comment); + current = null; + next (); + + parse_docbook_spaces (false); + var ic = parse_inline_content (); + parse_docbook_spaces (false); + + if (current.type != TokenType.EOF) { + this.report_unexpected_token (current, "<EOF>"); + return null; + } + + BlockContent? taglet = factory.create_taglet (taglet_name) as BlockContent; + assert (taglet != null); + Paragraph paragraph = factory.create_paragraph (); + paragraph.content.add (ic); + taglet.content.add (paragraph); + return taglet as Taglet; + } + + private Comment? parse_root_content (Api.SourceComment gir_comment) { + this.reset (gir_comment); + current = null; + + next (); + + Token tmp = null; + parse_docbook_spaces (false); + + Comment comment = factory.create_comment (); + while (current.type != TokenType.EOF && tmp != current) { + tmp = current; + var ic = parse_inline_content (); + if (ic != null && ic.content.size > 0) { + Paragraph p = factory.create_paragraph (); + p.content.add (ic); + comment.content.add (p); + } + + var bc = parse_block_content (); + if (bc != null && bc.size > 0) { + comment.content.add_all (bc); + } + } + + if (current.type != TokenType.EOF) { + this.report_unexpected_token (current, "<INLINE|BLOCK>"); + return null; + } + + ImporterHelper.extract_short_desc (comment, factory); + + return comment; + } + + + + // + // Common: + // + + private Token next () { + current = scanner.next (); + return current; + } + + private bool ignore_current_xml_close () { + if (current.type != TokenType.XML_CLOSE) { + return false; + } + + string name = current.content; + if ((name in stack) == false) { + return true; + } + + return false; + } + + private bool check_xml_open_tag (string tagname) { + if ((current.type == TokenType.XML_OPEN && current.content != tagname) + || current.type != TokenType.XML_OPEN) + { + return false; + } + + stack.offer_head (tagname); + return true; + } + + private bool check_xml_close_tag (string tagname) { + if ((current.type == TokenType.XML_CLOSE && current.content != tagname) + || current.type != TokenType.XML_CLOSE) + { + return false; + } + + if (stack.poll_head () == tagname) { + stack.peek_head (); + } + + return true; + } + + + private void parse_docbook_spaces (bool accept_paragraphs = true) { + while (true) { + if (current.type == TokenType.SPACE) { + next (); + } else if (current.type == TokenType.NEWLINE) { + next (); + } else if (accept_paragraphs && current.type == TokenType.GTKDOC_PARAGRAPH) { + next (); + } else { + break; + } + } + } + + + + // + // Rules, Ground: + // + + private Inline? parse_docbook_link_tempalte (string tagname, bool is_internal) { + if (!check_xml_open_tag (tagname)) { + this.report_unexpected_token (current, "<%s>".printf (tagname)); + return null; + } + + StringBuilder builder = new StringBuilder (); + string url = current.attributes.get ("linkend"); + next (); + + // TODO: check xml + while (!(current.type == TokenType.XML_CLOSE && current.content == tagname) + && current.type != TokenType.EOF) + { + if (current.type == TokenType.XML_OPEN) { + } else if (current.type == TokenType.XML_CLOSE) { + } else if (current.type == TokenType.XML_COMMENT) { + } else { + builder.append (current.content); + } + + next (); + } + + var link = factory.create_link (); + if (is_internal) { + link.id_registrar = id_registrar; + } + link.url = url; + + if (builder.len == 0) { + link.content.add (factory.create_text (url)); + } else { + link.content.add (factory.create_text (normalize (builder.str))); + } + + if (!check_xml_close_tag (tagname)) { + this.report_unexpected_token (current, "</%s>".printf (tagname)); + return link; + } + + next (); + return link; + } + + private InlineTaglet? parse_symbol_link (string tagname) { + if (!check_xml_open_tag (tagname)) { + this.report_unexpected_token (current, "<%s>".printf (tagname)); + return null; + } + + if (next ().type == TokenType.SPACE) { + next (); + } + + InlineTaglet? taglet = null; + + if (current.type == TokenType.WORD && current.content == "struct") { + next (); + + if (next ().type == TokenType.SPACE) { + next (); + } + } + + + if (current.type == TokenType.GTKDOC_FUNCTION || current.type == TokenType.GTKDOC_CONST + || current.type == TokenType.GTKDOC_TYPE || current.type == TokenType.WORD + || current.type == TokenType.GTKDOC_PROPERTY || current.type == TokenType.GTKDOC_SIGNAL) + { + taglet = this.create_type_link (current.content) as InlineTaglet; + assert (taglet != null); + } + + if (next ().type == TokenType.SPACE) { + next (); + } + + if (!check_xml_close_tag (tagname)) { + this.report_unexpected_token (current, "</%s>".printf (tagname)); + return taglet; + } + + next (); + return taglet; + } + + private void parse_anchor () { + if (!check_xml_open_tag ("anchor")) { + this.report_unexpected_token (current, "<anchor>"); + return; + } + + string id = current.attributes.get ("id"); + if (id != null) { + id_registrar.register_symbol (id, element); + } + next (); + + if (!check_xml_close_tag ("anchor")) { + this.report_unexpected_token (current, "</anchor>"); + return; + } + + next (); + } + + private Link? parse_xref () { + if (!check_xml_open_tag ("xref")) { + this.report_unexpected_token (current, "<xref>"); + return null; + } + + string linkend = current.attributes.get ("linkend"); + next (); + + Link link = factory.create_link (); + link.content.add (factory.create_text (linkend)); + link.id_registrar = id_registrar; + link.url = linkend; + + if (!check_xml_close_tag ("xref")) { + this.report_unexpected_token (current, "</xref>"); + return link; + } + + next (); + return link; + } + + private Run? parse_highlighted_template (string tag_name, Run.Style style) { + if (!check_xml_open_tag (tag_name)) { + this.report_unexpected_token (current, "<%s>".printf (tag_name)); + return null; + } + + next (); + Run run = parse_inline_content (); + if (run.style != Run.Style.NONE && run.style != style) { + Run tmp = factory.create_run (style); + tmp.content.add (run); + run = tmp; + } else { + run.style = style; + } + + if (!check_xml_close_tag (tag_name)) { + this.report_unexpected_token (current, "</%s>".printf (tag_name)); + return run; + } + + next (); + return run; + } + + private ListItem? parse_docbook_listitem () { + if (!check_xml_open_tag ("listitem")) { + this.report_unexpected_token (current, "<listitem>"); + return null; + } + + next (); + + ListItem item = factory.create_list_item (); + + item.content.add_all (parse_mixed_content ()); + + if (!check_xml_close_tag ("listitem")) { + this.report_unexpected_token (current, "</listitem>"); + return item; + } + + next (); + return item; + } + + private BlockContent? parse_docbook_information_box_template (string tagname, BlockContent container) { + if (!check_xml_open_tag (tagname)) { + this.report_unexpected_token (current, "<%s>".printf (tagname)); + return null; + } + + next (); + parse_docbook_spaces (); + + Token tmp = null; + while (current.type != TokenType.XML_CLOSE && current.type != TokenType.EOF) { + tmp = current; + var ic = parse_inline_content (); + if (ic != null && ic.content.size > 0) { + Paragraph p = factory.create_paragraph (); + p.content.add (ic); + container.content.add (p); + } + + var bc = parse_block_content (); + if (bc != null && bc.size > 0) { + container.content.add_all (bc); + } + } + + parse_docbook_spaces (); + + if (!check_xml_close_tag (tagname)) { + this.report_unexpected_token (current, "</%s>".printf (tagname)); + return container; + } + + next (); + return container; + } + + private Note? parse_docbook_important () { + return (Note?) parse_docbook_information_box_template ("important", factory.create_note ()); + } + + private Note? parse_docbook_note () { + return (Note?) parse_docbook_information_box_template ("note", factory.create_note ()); + } + + private Warning? parse_docbook_warning () { + return (Warning?) parse_docbook_information_box_template ("warning", factory.create_warning ()); + } + + private inline LinkedList<Block>? parse_docbook_orderedlist () { + return parse_docbook_itemizedlist ("orderedlist", Content.List.Bullet.ORDERED); + } + + private LinkedList<Block>? parse_docbook_itemizedlist (string tag_name = "itemizedlist", + Content.List.Bullet bullet_type = Content.List.Bullet.UNORDERED) + { + if (!check_xml_open_tag (tag_name)) { + this.report_unexpected_token (current, "<%s>".printf (tag_name)); + return null; + } + next (); + + + LinkedList<Block> content = new LinkedList<Block> (); + parse_docbook_spaces (); + + if (current.type == TokenType.XML_OPEN && current.content == "title") { + append_block_content_not_null (content, parse_docbook_title ()); + parse_docbook_spaces (); + } + + Content.List list = factory.create_list (); + list.bullet = bullet_type; + content.add (list); + + while (current.type == TokenType.XML_OPEN) { + if (current.content == "listitem") { + list.items.add (parse_docbook_listitem ()); + } else { + break; + } + + parse_docbook_spaces (); + } + + if (!check_xml_close_tag (tag_name)) { + this.report_unexpected_token (current, "</%s>".printf (tag_name)); + return content; + } + + next (); + return content; + } + + private Paragraph? parse_gtkdoc_paragraph () { + if (current.type != TokenType.GTKDOC_PARAGRAPH) { + this.report_unexpected_token (current, "<GTKDOC-PARAGRAPH>"); + return null; + } + + next (); + + Paragraph p = factory.create_paragraph (); + + Run? run = parse_inline_content (); + p.content.add (run); + return p; + } + + private LinkedList<Block> parse_mixed_content () { + LinkedList<Block> content = new LinkedList<Block> (); + Token tmp = null; + + while (tmp != current) { + tmp = current; + parse_docbook_spaces (); + + Run? run = parse_inline_content (); + if (run != null && run.content.size > 0) { + Paragraph p = factory.create_paragraph (); + p.content.add (run); + content.add (p); + continue; + } + + LinkedList<Block> lst = parse_block_content (); + if (lst != null && lst.size > 0) { + content.add_all (lst); + continue; + } + } + + return content; + } + + private inline LinkedList<Block>? parse_docbook_simpara () { + return parse_docbook_para ("simpara"); + } + + private LinkedList<Block>? parse_docbook_para (string tag_name = "para") { + if (!check_xml_open_tag (tag_name)) { + this.report_unexpected_token (current, "<%s>".printf (tag_name)); + return null; + } + + next (); + + LinkedList<Block> content = parse_mixed_content (); + + // ignore missing </para> to match gtkdocs behaviour + if (!check_xml_close_tag (tag_name) && current.type != TokenType.EOF) { + this.report_unexpected_token (current, "</%s>".printf (tag_name)); + return content; + } + + next (); + return content; + } + + private Paragraph? parse_gtkdoc_source () { + if (current.type != TokenType.GTKDOC_SOURCE_OPEN) { + this.report_unexpected_token (current, "|["); + return null; + } + + StringBuilder builder = new StringBuilder (); + Token source_token = current; + + for (next (); current.type != TokenType.EOF && current.type != TokenType.GTKDOC_SOURCE_CLOSE; next ()) { + if (current.type == TokenType.WORD) { + builder.append (current.content); + } else if (current.type != TokenType.XML_COMMENT) { + builder.append_len (current.start, current.length); + } + } + + SourceCode code = factory.create_source_code (); + MatchInfo info; + + unowned string source = builder.str; + if (regex_source_lang.match (source, 0, out info)) { + string lang_name = info.fetch (1).down (); + SourceCode.Language? lang = SourceCode.Language.from_string (lang_name); + code.language = lang; + + if (lang == null) { + report_warning (source_token, "Unknown language `%s' in source code block |[<!-- language=\"\"".printf (lang_name)); + } + + source = source.offset (source.index_of_char ('>') + 1); + } else { + code.language = (Highlighter.XmlScanner.is_xml (source)) + ? SourceCode.Language.XML + : SourceCode.Language.C; + } + code.code = source; + + Paragraph p = factory.create_paragraph (); + p.content.add (code); + + if (current.type != TokenType.GTKDOC_SOURCE_CLOSE) { + this.report_unexpected_token (current, "|]"); + return p; + } + + next (); + return p; + } + + private Paragraph? parse_docbook_title () { + if (!check_xml_open_tag ("title")) { + this.report_unexpected_token (current, "<title>"); + return null; + } + + next (); + + Paragraph p = factory.create_paragraph (); + Run content = parse_inline_content (); + content.content.add (factory.create_text (":")); + content.style = Run.Style.BOLD; + p.content.add (content); + + if (!check_xml_close_tag ("title")) { + this.report_unexpected_token (current, "</title>"); + return p; + } + + next (); + return p; + } + + private Paragraph? parse_docbook_graphic () { + var tmp = parse_docbook_inlinegraphic ("graphic"); + if (tmp == null) { + return null; + } + + Paragraph? p = factory.create_paragraph (); + p.content.add (tmp); + return p; + } + + private Embedded? parse_docbook_inlinegraphic (string tag_name = "inlinegraphic") { + if (!check_xml_open_tag (tag_name)) { + this.report_unexpected_token (current, "<%s>".printf (tag_name)); + return null; + } + + Embedded e = factory.create_embedded (); + e.url = fix_resource_path (current.attributes.get ("fileref")); + + next (); + parse_docbook_spaces (); + + if (!check_xml_close_tag (tag_name)) { + this.report_unexpected_token (current, "</%s>".printf (tag_name)); + return e; + } + + next (); + return e; + } + + private Paragraph? parse_docbook_programlisting () { + if (!check_xml_open_tag ("programlisting")) { + this.report_unexpected_token (current, "<programlisting>"); + return null; + } + + StringBuilder builder = new StringBuilder (); + + for (next (); current.type != TokenType.EOF && !(current.type == TokenType.XML_CLOSE + && current.content == "programlisting"); next ()) + { + if (current.type == TokenType.WORD) { + builder.append (current.content); + } else if (current.type != TokenType.XML_COMMENT) { + builder.append_len (current.start, current.length); + } + } + + SourceCode src = factory.create_source_code (); + src.language = SourceCode.Language.C; + src.code = builder.str; + + Paragraph p = factory.create_paragraph (); + p.content.add (src); + + if (!check_xml_close_tag ("programlisting")) { + this.report_unexpected_token (current, "</programlisting>"); + return p; + } + + next (); + return p; + } + +/* + private LinkedList<Block>? parse_docbook_informalexample () { + if (!check_xml_open_tag ("informalexample")) { + this.report_unexpected_token (current, "<informalexample>"); + return null; + } + + next (); + + parse_docbook_spaces (); + + LinkedList<Block> content = new LinkedList<Block> (); + + if (current.type == TokenType.XML_OPEN && current.content == "title") { + append_block_content_not_null (content, parse_docbook_title ()); + parse_docbook_spaces (); + } + + if (current.type == TokenType.XML_OPEN && current.content == "programlisting") { + append_block_content_not_null (content, parse_docbook_programlisting ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "inlinegraphic") { + Embedded? img = parse_docbook_inlinegraphic (); + Paragraph p = factory.create_paragraph (); + append_block_content_not_null (content, p); + p.content.add (img); + + } + + parse_docbook_spaces (); + + if (!check_xml_close_tag ("informalexample")) { + this.report_unexpected_token (current, "</informalexample>"); + return content; + } + + next (); + return content; + } +*/ + + private inline LinkedList<Block>? parse_docbook_informalexample () { + return parse_docbook_example ("informalexample"); + } + + private LinkedList<Block>? parse_docbook_example (string tag_name = "example") { + if (!check_xml_open_tag (tag_name)) { + this.report_unexpected_token (current, "<%s>".printf (tag_name)); + return null; + } + + next (); + + parse_docbook_spaces (); + + LinkedList<Block> content = new LinkedList<Block> (); + + content.add_all (parse_mixed_content ()); + + /* + while (current.type == TokenType.XML_OPEN) { + if (current.type == TokenType.XML_OPEN && current.content == "inlinegraphic") { + Paragraph p = factory.create_paragraph (); + while (current.type == TokenType.XML_OPEN && current.content == "inlinegraphic") { + p.content.add (parse_docbook_inlinegraphic ()); + parse_docbook_spaces (); + } + } else if (current.type == TokenType.XML_OPEN && current.content == "programlisting") { + append_block_content_not_null (content, parse_docbook_programlisting ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "para") { + this.append_block_content_not_null_all (content, parse_docbook_para ()); + } else { + break; + } + + parse_docbook_spaces (); + } */ + + if (!check_xml_close_tag (tag_name)) { + this.report_unexpected_token (current, "</%s>".printf (tag_name)); + return content; + } + + next (); + return content; + } + + private LinkedList<Block>? parse_docbook_refsect2 (int nr = 2) { + if (!check_xml_open_tag ("refsect%d".printf (nr))) { + this.report_unexpected_token (current, "<refsect%d>".printf (nr)); + return null; + } + + string id = current.attributes.get ("id"); + if (id != null) { + id_registrar.register_symbol (id, element); + } + next (); + + parse_docbook_spaces (); + + LinkedList<Block> content = new LinkedList<Block> (); + + this.append_block_content_not_null_all (content, parse_mixed_content ()); + + if (!check_xml_close_tag ("refsect%d".printf (nr))) { + this.report_unexpected_token (current, "</refsect%d>".printf (nr)); + return content; + } + + next (); + return content; + } + + private LinkedList<Block>? parse_docbook_figure () { + if (!check_xml_open_tag ("figure")) { + this.report_unexpected_token (current, "<figure>"); + return null; + } + next (); + + LinkedList<Block> content = new LinkedList<Block> (); + parse_docbook_spaces (); + + if (current.type == TokenType.XML_OPEN && current.content == "title") { + append_block_content_not_null (content, parse_docbook_title ()); + parse_docbook_spaces (); + } + + while (current.type == TokenType.XML_OPEN) { + if (current.content == "inlinegraphic") { + Paragraph p = (content.size > 0)? content[0] as Paragraph : null; + if (p == null) { + p = factory.create_paragraph (); + } + + while (current.type == TokenType.XML_OPEN && current.content == "inlinegraphic") { + p.content.add (parse_docbook_inlinegraphic ()); + parse_docbook_spaces (); + } + } else if (current.content == "graphic") { + append_block_content_not_null (content, parse_docbook_graphic ()); + } else { + break; + } + + parse_docbook_spaces (); + } + + if (!check_xml_close_tag ("figure")) { + this.report_unexpected_token (current, "</figure>"); + return content; + } + + next (); + return content; + } + + private Run? parse_docbook_footnote () { + if (!check_xml_open_tag ("footnote")) { + this.report_unexpected_token (current, "<footnote>"); + return null; + } + next (); + + int counter = this.footnotes.size + 1; + Run? nr = factory.create_run (Run.Style.ITALIC); + nr.content.add (factory.create_text ("[%d] ".printf (counter))); + LinkedList<Block> content = new LinkedList<Block> (); + this.footnotes.add (content); + + content.add_all (parse_mixed_content ()); + + Paragraph? first = (content.is_empty)? null : content.first () as Paragraph; + if (first == null) { + first = factory.create_paragraph (); + content.insert (0, first); + } + + Run entry = factory.create_run (Run.Style.ITALIC); + entry.content.add (factory.create_text (counter.to_string () + ": ")); + first.content.insert (0, entry); + + if (!check_xml_close_tag ("footnote")) { + this.report_unexpected_token (current, "</footnote>"); + return nr; + } + + next (); + return nr; + } + + private inline void append_block_content_not_null_all (LinkedList<Block> run, LinkedList<Block>? elements) { + if (elements != null) { + run.add_all (elements); + } + } + + private inline void append_block_content_not_null (LinkedList<Block> run, Block? element) { + if (element != null) { + run.add (element); + } + } + + private TableRow? parse_docbook_thead () { + if (!check_xml_open_tag ("thead")) { + this.report_unexpected_token (current, "<thead>"); + return null; + } + next (); + + parse_docbook_spaces (); + TableRow? row = parse_docbook_row (Run.Style.BOLD); + parse_docbook_spaces (); + + if (!check_xml_close_tag ("thead")) { + this.report_unexpected_token (current, "</thead>"); + return row; + } + + next (); + return row; + } + + private TableCell? parse_docbook_entry (Run.Style default_style = Run.Style.NONE) { + if (!check_xml_open_tag ("entry")) { + this.report_unexpected_token (current, "<entry>"); + return null; + } + next (); + + TableCell cell = factory.create_table_cell (); + Run run = factory.create_run (default_style); + run.content.add (parse_inline_content ()); + cell.content.add (run); + + if (!check_xml_close_tag ("entry")) { + this.report_unexpected_token (current, "</entry>"); + return cell; + } + + next (); + return cell; + } + + private TableRow? parse_docbook_row (Run.Style default_style = Run.Style.NONE) { + if (!check_xml_open_tag ("row")) { + this.report_unexpected_token (current, "<row>"); + return null; + } + next (); + + TableRow row = factory.create_table_row (); + parse_docbook_spaces (); + + while (current.type == TokenType.XML_OPEN && current.content == "entry") { + TableCell? table_cell = parse_docbook_entry (default_style); + if (table_cell == null) { + break; + } + + row.cells.add (table_cell); + parse_docbook_spaces (); + } + + if (!check_xml_close_tag ("row")) { + this.report_unexpected_token (current, "</row>"); + return row; + } + + next (); + return row; + } + + private LinkedList<TableRow>? parse_docbook_tbody () { + if (!check_xml_open_tag ("tbody")) { + this.report_unexpected_token (current, "<tbody>"); + return null; + } + next (); + + parse_docbook_spaces (); + + LinkedList<TableRow> rows = new LinkedList<TableRow> (); + while (current.type == TokenType.XML_OPEN && current.content == "row") { + TableRow? row = parse_docbook_row (); + if (row == null) { + break; + } + + parse_docbook_spaces (); + rows.add (row); + } + + + if (!check_xml_close_tag ("tbody")) { + this.report_unexpected_token (current, "</tbody>"); + return rows; + } + + next (); + return rows; + } + + private Table? parse_docbook_tgroup () { + if (!check_xml_open_tag ("tgroup")) { + this.report_unexpected_token (current, "<tgroup>"); + return null; + } + next (); + + Table table = factory.create_table (); + parse_docbook_spaces (); + + if (current.type == TokenType.XML_OPEN && current.content == "thead") { + TableRow? row = parse_docbook_thead (); + if (row != null) { + parse_docbook_spaces (); + table.rows.add (row); + } + + parse_docbook_spaces (); + } + + if (current.type == TokenType.XML_OPEN && current.content == "tbody") { + LinkedList<TableRow>? rows = parse_docbook_tbody (); + if (rows != null) { + table.rows.add_all (rows); + } + + parse_docbook_spaces (); + } + + if (!check_xml_close_tag ("tgroup")) { + this.report_unexpected_token (current, "</tgroup>"); + return table; + } + + next (); + return table; + } + + private Table? parse_docbook_informaltable () { + if (!check_xml_open_tag ("informaltable")) { + this.report_unexpected_token (current, "<informaltable>"); + return null; + } + next (); + + parse_docbook_spaces (); + Table? table = this.parse_docbook_tgroup (); + parse_docbook_spaces (); + + if (!check_xml_close_tag ("informaltable")) { + this.report_unexpected_token (current, "</informaltable>"); + return table; + } + + next (); + return table; + } + + private LinkedList<Block>? parse_docbook_section () { + if (!check_xml_open_tag ("section")) { + this.report_unexpected_token (current, "<section>"); + return null; + } + + string id = current.attributes.get ("id"); + if (id != null) { + id_registrar.register_symbol (id, element); + } + next (); + + LinkedList<Block> content = parse_mixed_content (); + + if (!check_xml_close_tag ("section")) { + this.report_unexpected_token (current, "</section>"); + return content; + } + + next (); + return content; + } + + private ListItem? parse_docbook_member () { + if (!check_xml_open_tag ("member")) { + this.report_unexpected_token (current, "<member>"); + return null; + } + next (); + + parse_docbook_spaces (); + + ListItem item = factory.create_list_item (); + Paragraph para = factory.create_paragraph (); + item.content.add (para); + + para.content.add (parse_inline_content ()); + + parse_docbook_spaces (); + + if (!check_xml_close_tag ("member")) { + this.report_unexpected_token (current, "</member>"); + return item; + } + + next (); + return item; + } + + private Content.List? parse_docbook_simplelist () { + if (!check_xml_open_tag ("simplelist")) { + this.report_unexpected_token (current, "<simplelist>"); + return null; + } + next (); + + parse_docbook_spaces (); + + Content.List list = factory.create_list (); + + while (current.type == TokenType.XML_OPEN && current.content == "member") { + ListItem item = parse_docbook_member (); + if (item == null) { + break; + } + + list.items.add (item); + parse_docbook_spaces (); + } + + + if (!check_xml_close_tag ("simplelist")) { + this.report_unexpected_token (current, "</simplelist>"); + return list; + } + + next (); + return list; + } + + private Paragraph? parse_docbook_term () { + if (!check_xml_open_tag ("term")) { + this.report_unexpected_token (current, "<term>"); + return null; + } + next (); + + parse_docbook_spaces (); + + Paragraph? p = factory.create_paragraph (); + Run run = parse_inline_content (); + run.style = Run.Style.ITALIC; + p.content.add (run); + + if (!check_xml_close_tag ("term")) { + this.report_unexpected_token (current, "</term>"); + return p; + } + + next (); + return p; + } + + private ListItem? parse_docbook_varlistentry () { + if (!check_xml_open_tag ("varlistentry")) { + this.report_unexpected_token (current, "<varlistentry>"); + return null; + } + next (); + + parse_docbook_spaces (); + + if (current.type != TokenType.XML_OPEN || current.content != "term") { + return null; + } + + Paragraph? term = parse_docbook_term (); + if (term == null) { + return null; + } + + parse_docbook_spaces (); + ListItem? desc = parse_docbook_listitem (); + if (desc == null) { + return null; + } + + parse_docbook_spaces (); + + Content.ListItem listitem = factory.create_list_item (); + Content.List list = factory.create_list (); + + listitem.content.add (term); + listitem.content.add (list); + list.items.add (desc); + + if (!check_xml_close_tag ("varlistentry")) { + this.report_unexpected_token (current, "</varlistentry>"); + return listitem; + } + + next (); + return listitem; + } + + private LinkedList<Block>? parse_docbook_variablelist () { + if (!check_xml_open_tag ("variablelist")) { + this.report_unexpected_token (current, "<variablelist>"); + return null; + } + next (); + + parse_docbook_spaces (); + + LinkedList<Block> content = new LinkedList<Block> (); + + if (current.type == TokenType.XML_OPEN && current.content == "title") { + append_block_content_not_null (content, parse_docbook_title ()); + parse_docbook_spaces (); + } + + Content.List list = factory.create_list (); + content.add (list); + + while (current.type == TokenType.XML_OPEN && current.content == "varlistentry") { + ListItem item = parse_docbook_varlistentry (); + if (item == null) { + break; + } + + list.items.add (item); + parse_docbook_spaces (); + } + + if (!check_xml_close_tag ("variablelist")) { + this.report_unexpected_token (current, "</variablelist>"); + return content; + } + + next (); + return content; + } + + private LinkedList<Block> parse_block_content () { + LinkedList<Block> content = new LinkedList<Block> (); + + while (current.type != TokenType.EOF) { + parse_docbook_spaces (false); + + if (current.type == TokenType.XML_OPEN && current.content == "itemizedlist") { + this.append_block_content_not_null_all (content, parse_docbook_itemizedlist ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "orderedlist") { + this.append_block_content_not_null_all (content, parse_docbook_orderedlist ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "variablelist") { + this.append_block_content_not_null_all (content, parse_docbook_variablelist ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "simplelist") { + this.append_block_content_not_null (content, parse_docbook_simplelist ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "informaltable") { + this.append_block_content_not_null (content, parse_docbook_informaltable ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "programlisting") { + this.append_block_content_not_null (content, parse_docbook_programlisting ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "para") { + this.append_block_content_not_null_all (content, parse_docbook_para ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "simpara") { + this.append_block_content_not_null_all (content, parse_docbook_simpara ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "informalexample") { + this.append_block_content_not_null_all (content, parse_docbook_informalexample ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "example") { + this.append_block_content_not_null_all (content, parse_docbook_example ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "warning") { + this.append_block_content_not_null (content, parse_docbook_warning ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "note") { + this.append_block_content_not_null (content, parse_docbook_note ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "important") { + this.append_block_content_not_null (content, parse_docbook_important ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "refsect3") { + this.append_block_content_not_null_all (content, parse_docbook_refsect2 (3)); + } else if (current.type == TokenType.XML_OPEN && current.content == "refsect2") { + this.append_block_content_not_null_all (content, parse_docbook_refsect2 ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "figure") { + this.append_block_content_not_null_all (content, parse_docbook_figure ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "title") { + this.append_block_content_not_null (content, parse_docbook_title ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "section") { + this.append_block_content_not_null_all (content, parse_docbook_section ()); + } else if (current.type == TokenType.GTKDOC_PARAGRAPH) { + this.append_block_content_not_null (content, parse_gtkdoc_paragraph ()); + } else if (current.type == TokenType.GTKDOC_SOURCE_OPEN) { + this.append_block_content_not_null (content, parse_gtkdoc_source ()); + } else { + break; + } + } + + return content; + } + + private Run? parse_xml_tag () { + if (!check_xml_open_tag ("tag")) { + this.report_unexpected_token (current, "<tag>"); + return null; + } + string? _class = current.attributes.get ("class"); + next (); + + parse_docbook_spaces (false); + + if (current.type != TokenType.WORD) { + this.report_unexpected_token (current, "<WORD>"); + return null; + } + + Run run = factory.create_run (Run.Style.MONOSPACED); + if (_class == null || _class == "starttag") { + run.content.add (factory.create_text ("<" + current.content + ">")); + next (); + } else if (_class == "endtag") { + run.content.add (factory.create_text ("</" + current.content + ">")); + next (); + } else { + this.report_unexpected_token (current, "<tag class=\"%s\">".printf (_class)); + return run; + } + + parse_docbook_spaces (false); + + + if (!check_xml_close_tag ("tag")) { + this.report_unexpected_token (current, "</tag>"); + return run; + } + + next (); + return run; + } + + private void append_inline_content_string (Run run, string current) { + Text last_as_text = null; + + if (run.content.size > 0) { + last_as_text = run.content.last () as Text; + } + + if (last_as_text == null) { + run.content.add (factory.create_text (current)); + } else if (current.has_prefix (" ") && last_as_text.content.has_suffix (" ")) { + last_as_text.content += current.chug (); + } else { + last_as_text.content += current; + } + } + + private Inline create_type_link (string name, bool c_accept_plural = false) { + if (name == "TRUE" || name == "FALSE" || name == "NULL" || is_numeric (name)) { + var monospaced = factory.create_run (Run.Style.MONOSPACED); + monospaced.content.add (factory.create_text (name.down ())); + return monospaced; + } else { + Taglets.Link? taglet = factory.create_taglet ("link") as Taglets.Link; + assert (taglet != null); + taglet.c_accept_plural = c_accept_plural; + taglet.symbol_name = "c::"+name; + return taglet; + } + } + + private inline void append_inline_content_not_null (Run run, Inline element) { + if (element != null) { + run.content.add (element); + } + } + + private Run parse_inline_content () { + Run run = factory.create_run (Run.Style.NONE); + + while (current.type != TokenType.EOF) { + if (current.type == TokenType.XML_OPEN && current.content == "firstterm") { + append_inline_content_not_null (run, parse_highlighted_template ("firstterm", Run.Style.ITALIC)); + } else if (current.type == TokenType.XML_OPEN && current.content == "abbrev") { + append_inline_content_not_null (run, parse_highlighted_template ("abbrev", Run.Style.ITALIC)); + } else if (current.type == TokenType.XML_OPEN && current.content == "term") { + append_inline_content_not_null (run, parse_highlighted_template ("term", Run.Style.ITALIC)); + } else if (current.type == TokenType.XML_OPEN && current.content == "literal") { + append_inline_content_not_null (run, parse_highlighted_template ("literal", Run.Style.ITALIC)); + } else if (current.type == TokenType.XML_OPEN && current.content == "literallayout") { + append_inline_content_not_null (run, parse_highlighted_template ("literallayout", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "application") { + append_inline_content_not_null (run, parse_highlighted_template ("application", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "varname") { + append_inline_content_not_null (run, parse_highlighted_template ("varname", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "computeroutput") { + append_inline_content_not_null (run, parse_highlighted_template ("computeroutput", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "emphasis") { + append_inline_content_not_null (run, parse_highlighted_template ("emphasis", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "pre") { + append_inline_content_not_null (run, parse_highlighted_template ("pre", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "code") { + append_inline_content_not_null (run, parse_highlighted_template ("code", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "guimenuitem") { + append_inline_content_not_null (run, parse_highlighted_template ("guimenuitem", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "command") { + append_inline_content_not_null (run, parse_highlighted_template ("command", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "option") { + append_inline_content_not_null (run, parse_highlighted_template ("option", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "keycap") { + append_inline_content_not_null (run, parse_highlighted_template ("keycap", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "keycombo") { + append_inline_content_not_null (run, parse_highlighted_template ("keycombo", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "envar") { + append_inline_content_not_null (run, parse_highlighted_template ("envar", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "filename") { + append_inline_content_not_null (run, parse_highlighted_template ("filename", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "parameter") { + append_inline_content_not_null (run, parse_highlighted_template ("parameter", Run.Style.MONOSPACED)); + } else if (current.type == TokenType.XML_OPEN && current.content == "replaceable") { + append_inline_content_not_null (run, parse_highlighted_template ("replaceable", Run.Style.ITALIC)); + } else if (current.type == TokenType.XML_OPEN && current.content == "quote") { + run.content.add (factory.create_text ("“")); + append_inline_content_not_null (run, parse_highlighted_template ("quote", Run.Style.NONE)); + run.content.add (factory.create_text ("”")); + } else if (current.type == TokenType.XML_OPEN && current.content == "footnote") { + append_inline_content_not_null (run, parse_docbook_footnote ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "type") { + append_inline_content_not_null (run, parse_symbol_link ("type")); + } else if (current.type == TokenType.XML_OPEN && current.content == "function") { + append_inline_content_not_null (run, parse_symbol_link ("function")); + } else if (current.type == TokenType.XML_OPEN && current.content == "classname") { + append_inline_content_not_null (run, parse_symbol_link ("classname")); + } else if (current.type == TokenType.XML_OPEN && current.content == "structname") { + append_inline_content_not_null (run, parse_symbol_link ("structname")); + } else if (current.type == TokenType.XML_OPEN && current.content == "structfield") { + append_inline_content_not_null (run, parse_symbol_link ("structfield")); + } else if (current.type == TokenType.XML_OPEN && current.content == "errorcode") { + append_inline_content_not_null (run, parse_symbol_link ("errorcode")); + } else if (current.type == TokenType.XML_OPEN && current.content == "constant") { + append_inline_content_not_null (run, parse_symbol_link ("constant")); + } else if (current.type == TokenType.XML_OPEN && current.content == "inlinegraphic") { + append_inline_content_not_null (run, parse_docbook_inlinegraphic ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "anchor") { + parse_anchor (); + } else if (current.type == TokenType.XML_OPEN && current.content == "link") { + append_inline_content_not_null (run, parse_docbook_link_tempalte ("link", true)); + } else if (current.type == TokenType.XML_OPEN && current.content == "ulink") { + append_inline_content_not_null (run, parse_docbook_link_tempalte ("ulink", false)); + } else if (current.type == TokenType.XML_OPEN && current.content == "xref") { + append_inline_content_not_null (run, parse_xref ()); + } else if (current.type == TokenType.XML_OPEN && current.content == "tag") { + append_inline_content_not_null (run, parse_xml_tag ()); + } else if (current.type == TokenType.GTKDOC_FUNCTION) { + run.content.add (this.create_type_link (current.content)); + next (); + } else if (current.type == TokenType.GTKDOC_PARAM) { + if (current.content == instance_param_name) { + Content.Run keyword_run = factory.create_run (Content.Run.Style.LANG_KEYWORD); + Content.Text text = factory.create_text ("this"); + keyword_run.content.add (text); + run.content.add (keyword_run); + } else { + string? param_array_name; + bool is_return_type_len; + string? param_name; + + string? cname = ImporterHelper.resolve_parameter_ctype ( + this.tree, + this.element, + current.content, + out param_name, + out param_array_name, + out is_return_type_len); + + Run current_run = factory.create_run (Run.Style.MONOSPACED); + run.content.add (current_run); + + if (is_return_type_len) { + Run keyword_run = factory.create_run (Run.Style.LANG_KEYWORD); + keyword_run.content.add (factory.create_text ("return")); + current_run.content.add (keyword_run); + + current_run.content.add (factory.create_text (".length")); + } else if (param_array_name != null) { + current_run.content.add (factory.create_text (param_array_name + ".length")); + } else { + current_run.content.add (factory.create_text (param_name)); + } + + if (cname != null) { + run.content.add (factory.create_text (".")); + + Taglets.Link link = factory.create_taglet ("link") as Taglets.Link; + link.symbol_name = cname; + run.content.add (link); + } + } + next (); + } else if (current.type == TokenType.GTKDOC_SIGNAL) { + run.content.add (this.create_type_link ("::" + current.content, true)); + next (); + } else if (current.type == TokenType.GTKDOC_PROPERTY) { + run.content.add (this.create_type_link (":" + current.content, true)); + next (); + } else if (current.type == TokenType.GTKDOC_CONST) { + run.content.add (this.create_type_link (current.content, true)); + next (); + } else if (current.type == TokenType.GTKDOC_TYPE) { + run.content.add (this.create_type_link (current.content, true)); + next (); + } else if (current.type == TokenType.NEWLINE || current.type == TokenType.SPACE) { + append_inline_content_string (run, " "); + next (); + } else if (current.type == TokenType.WORD) { + append_inline_content_string (run, current.content); + next (); + } else if (current.type == TokenType.XML_CLOSE && ignore_current_xml_close ()) { + next (); + } else if (current.type == TokenType.XML_COMMENT) { + next (); + } else { + break; + } + } + + return run; + } + + + + // + // Resource Locator: + // + + public string resolve (string path) { + return path; + } +} + + diff --git a/libvaladoc/documentation/gtkdoccommentscanner.vala b/libvaladoc/documentation/gtkdoccommentscanner.vala new file mode 100644 index 000000000..75f2d58ed --- /dev/null +++ b/libvaladoc/documentation/gtkdoccommentscanner.vala @@ -0,0 +1,890 @@ +/* gtkcommentscanner.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +using Valadoc.Gtkdoc; +using Gee; + + + +public enum Valadoc.Gtkdoc.TokenType { + XML_OPEN, + XML_CLOSE, + XML_COMMENT, + GTKDOC_FUNCTION, + GTKDOC_CONST, + GTKDOC_TYPE, + GTKDOC_PARAM, + GTKDOC_SOURCE_OPEN, + GTKDOC_SOURCE_CLOSE, + GTKDOC_SIGNAL, + GTKDOC_PROPERTY, + GTKDOC_PARAGRAPH, + NEWLINE, + SPACE, + WORD, + EOF +} + + +public class Valadoc.Gtkdoc.Token { + public TokenType type; + public string content; + public HashMap<string, string>? attributes; + public unowned string start; + public int length; + public int line; + public int first_column; + public int last_column; + + public Token (TokenType type, string content, HashMap<string, string>? attributes, string start, + int length, int line, int first_column, int last_column) + { + this.attributes = attributes; + this.content = content; + this.length = length; + this.start = start; + this.type = type; + this.line = line; + this.first_column = first_column; + this.last_column = last_column; + } + + public string to_string () { + switch (this.type) { + case TokenType.XML_OPEN: + return "`<%s>'".printf (this.content); + + case TokenType.XML_CLOSE: + return "`</%s>'".printf (this.content); + + case TokenType.XML_COMMENT: + return "<XML-COMMENT>"; + + case TokenType.GTKDOC_FUNCTION: + return "`%s ()'".printf (this.content); + + case TokenType.GTKDOC_CONST: + return "`%%%s'".printf (this.content); + + case TokenType.GTKDOC_TYPE: + return "`#%s'".printf (this.content); + + case TokenType.GTKDOC_PARAM: + return "<GTKDOC-PARAM>"; + + case TokenType.GTKDOC_SOURCE_OPEN: + return "[|"; + + case TokenType.GTKDOC_SOURCE_CLOSE: + return "|]"; + + case TokenType.GTKDOC_SIGNAL: + return "`::%s'".printf (this.content); + + case TokenType.GTKDOC_PROPERTY: + return "`:%s'".printf (this.content); + + case TokenType.GTKDOC_PARAGRAPH: + return "<GKTDOC-PARAGRAPH>"; + + case TokenType.NEWLINE: + return "<NEWLNIE>"; + + case TokenType.SPACE: + return "<SPACE>"; + + case TokenType.WORD: + return "`%s'".printf (this.content); + + case TokenType.EOF: + return "<EOF>"; + + default: + assert_not_reached (); + } + } +} + + +public class Valadoc.Gtkdoc.Scanner { + private unowned string content; + private unowned string pos; + private int column; + private int line; + private Token tmp_token; + + public Scanner () { + } + + public static string unescape (string txt) { + StringBuilder builder = new StringBuilder (); + unowned string start = txt; + unowned string pos; + unichar c; + + for (pos = txt; (c = pos.get_char ()) != '\0'; pos = pos.next_char ()) { + if (c == '&') { + if (pos.has_prefix ("&solidus;")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 9); + pos = (string) ((char*) pos + 8); + builder.append_unichar ('⁄'); + } else if (pos.has_prefix ("%")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 8); + pos = (string) ((char*) pos + 7); + builder.append_c ('%'); + } else if (pos.has_prefix ("@")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 8); + pos = (string) ((char*) pos + 7); + builder.append_c ('@'); + } else if (pos.has_prefix (" ")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 6); + pos = (string) ((char*) pos + 5); + builder.append_c (' '); + } else if (pos.has_prefix (""")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 6); + pos = (string) ((char*) pos + 5); + builder.append_c ('"'); + } else if (pos.has_prefix ("'")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 6); + pos = (string) ((char*) pos + 5); + builder.append_c ('\''); + } else if (pos.has_prefix ("(")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 6); + pos = (string) ((char*) pos + 5); + builder.append_c ('('); + } else if (pos.has_prefix (")")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 6); + pos = (string) ((char*) pos + 5); + builder.append_c (')'); + } else if (pos.has_prefix ("#")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 5); + pos = (string) ((char*) pos + 4); + builder.append_c ('#'); + } else if (pos.has_prefix ("&")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 5); + pos = (string) ((char*) pos + 4); + builder.append_c ('&'); + } else if (pos.has_prefix ("*")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 5); + pos = (string) ((char*) pos + 4); + builder.append_c ('*'); + } else if (pos.has_prefix ("π")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 4); + pos = (string) ((char*) pos + 3); + builder.append_unichar ('π'); + } else if (pos.has_prefix ("<")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 4); + pos = (string) ((char*) pos + 3); + builder.append_c ('<'); + } else if (pos.has_prefix (">")) { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + start = (string) ((char*) pos + 4); + pos = (string) ((char*) pos + 3); + builder.append_c ('>'); + } + } + } + + if (&txt == &start) { + return txt; + } else { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + return (owned) builder.str; + } + } + + public void reset (string content) { + this.content = content; + this.tmp_token = null; + this.pos = content; + this.column = 0; + this.line = 0; + } + + private inline unichar next_char () { + this.pos = this.pos.next_char (); + this.column++; + + return this.pos.get_char (); + } + + private inline unichar get () { + return this.pos.get_char (); + } + + private inline bool letter (unichar c) { + return (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z'); + } + + private inline bool letter_or_number (unichar c) { + return (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9'); + } + + private inline bool space (unichar c) { + return c == ' ' || c == '\t'; + } + + private inline bool space_or_newline (unichar c) { + if (c == '\n') { + this.line++; + this.column = 0; + return true; + } + + return space (c); + } + + private inline int offset (string a, string b) { + return (int) ((char*) a - (char*) b); + } + + private inline int vararg_prefix () { + if (this.pos.has_prefix ("...")) { + next_char (); + next_char (); + next_char (); + return 3; + } + + return 0; + } + + private inline int id_prefix () { + unichar c = get (); + + if (!letter (c) && c != '_') { + return 0; + } + + int start = this.column; + while ((c = next_char ()) == '_' || letter_or_number (c)); + return this.column - start; + } + + private inline int g_id_prefix () { + unowned string start = this.pos; + unichar c = get (); + + if (!letter (c)) { + return 0; + } + + while ((c = next_char ()) == '_' || c == '-' || letter_or_number (c)); + return offset (this.pos, start); + } + + private inline int skip_spaces_and_newlines () { + unowned string start = this.pos; + if (space_or_newline (get ())) { + while (space_or_newline (next_char ())); + } + + return offset (this.pos, start); + } + + private inline Token? function_prefix () { + unowned string start = this.pos; + int column_start = this.column; + int id_len = 0; + + if ((id_len = id_prefix ()) == 0) { + return null; + } + + space_prefix (); + + if (get () != '(') { + this.column = column_start; + this.pos = start; + return null; + } + + next_char (); + space_prefix (); + + if (get () != ')') { + this.column = column_start; + this.pos = start; + return null; + } + + next_char (); + return new Token (TokenType.GTKDOC_FUNCTION, + start.substring (0, id_len), + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + + private inline Token? gtkdoc_symbolic_link_prefix (unichar c, TokenType type) { + if (get () != c) { + return null; + } + + unowned string start = this.pos; + int column_start = this.column; + next_char (); + + int id_len = 0; + + if ((id_len = id_prefix ()) == 0) { + if (type == TokenType.GTKDOC_PARAM && (id_len = vararg_prefix ()) == 0) { + this.column = column_start; + this.pos = start; + return null; + } + } + + unowned string separator = this.pos; + if (get () == ':') { + int separator_len = 1; + if (next_char () == ':') { + next_char (); + separator_len++; + } + + int id_len2; + if ((id_len2 = g_id_prefix ()) == 0) { + this.pos = separator; + } else { + id_len += id_len2 + separator_len; + } + } else if (this.pos.has_prefix ("->") || this.pos.has_prefix (".")) { + unowned string sep_start = this.pos; + int sep_column_start = this.column; + int separator_len = 1; + + if (this.pos.has_prefix ("->")) { + separator_len = 2; + next_char (); + } + + next_char (); + + Token? func_token = function_prefix (); + if (func_token == null) { + int id_len2; + + if ((id_len2 = id_prefix ()) > 0) { + id_len += separator_len + id_len2; + } else { + this.column = sep_column_start; + this.pos = sep_start; + } + } else { + id_len += separator_len + func_token.content.length; + } + } + + return new Token (type, + start.substring (1, id_len), + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + + private inline Token? gtkdoc_property_prefix () { + if (get () != ':') { + return null; + } + + unowned string start = this.pos; + int column_start = this.column; + next_char (); + + int id_len = 0; + + if ((id_len = g_id_prefix ()) == 0) { + this.column = column_start; + this.pos = start; + return null; + } + + return new Token (TokenType.GTKDOC_PROPERTY, + start.substring (1, id_len), + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + + private inline Token? gtkdoc_signal_prefix () { + if (get () != ':') { + return null; + } + + unowned string start = this.pos; + int column_start = this.column; + if (next_char () != ':') { + this.column = column_start; + this.pos = start; + return null; + } + + + start = this.pos; + next_char (); + + int id_len = 0; + + if ((id_len = g_id_prefix ()) == 0) { + this.column = column_start; + this.pos = start; + return null; + } + + return new Token (TokenType.GTKDOC_SIGNAL, + start.substring (1, id_len), + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + + private inline Token? gtkdoc_const_prefix () { + return gtkdoc_symbolic_link_prefix ('%', TokenType.GTKDOC_CONST); + } + + private inline Token? gtkdoc_param_prefix () { + return gtkdoc_symbolic_link_prefix ('@', TokenType.GTKDOC_PARAM); + } + + private inline Token? gtkdoc_type_prefix () { + return gtkdoc_symbolic_link_prefix ('#', TokenType.GTKDOC_TYPE); + } + + private inline Token? xml_prefix () { + if (get () != '<') { + return null; + } + + unowned string start = this.pos; + int line_start = this.line; + int column_start = this.column; + next_char (); + + if (get () == '!') { + // comment + if (next_char () == '-') { + if (next_char () != '-') { + this.column = column_start; + this.pos = start; + return null; + } + + for (unichar c = next_char (); c != '\0'; c = next_char ()) { + if (c == '\n') { + this.line++; + this.column = 0; + } else if (this.pos.has_prefix ("-->")) { + next_char (); + next_char (); + next_char (); + return new Token (TokenType.XML_COMMENT, + "", + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + } + } else if (this.pos.has_prefix ("[CDATA[")) { + next_char (); + next_char (); + next_char (); + next_char (); + next_char (); + next_char (); + + for (unichar c = next_char (); c != '\0'; c = next_char ()) { + if (c == '\n') { + this.line++; + this.column = 0; + } else if (this.pos.has_prefix ("]]>")) { + string content = start.substring (9, offset (this.pos, start) - 9); + next_char (); + next_char (); + next_char (); + return new Token (TokenType.WORD, + unescape (content), + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + } + } + + this.pos = start; + this.column = column_start; + this.line = line_start; + return null; + } + + bool close = false; + if (get () == '/') { + next_char (); + close = true; + } + + unowned string id_start = this.pos; + + int id_len = 0; + if ((id_len = id_prefix ()) == 0) { + this.column = column_start; + this.pos = start; + return null; + } + + HashMap<string, string> map = new HashMap<string, string> (); + + while (close == false && skip_spaces_and_newlines () > 0) { + string name; + string val; + + unowned string att_pos = this.pos; + int att_id_len = 0; + if ((att_id_len = id_prefix ()) == 0) { + break; + } + + name = att_pos.substring (0, att_id_len); + + if (get() != '=') { + break; + } + + next_char (); + skip_spaces_and_newlines (); + + if (get() != '"') { + break; + } + + unichar c = next_char (); + att_pos = this.pos; + for (; c != '\0' && c != '\n' && c != '"' ; c = next_char ()); + + val = att_pos.substring (0, offset (this.pos, att_pos)); + + if (get() != '"') { + break; + } + + next_char (); + + map.set (name, val); + } + + skip_spaces_and_newlines (); + + bool open_and_close = false; + + if (!close && get () == '/') { + open_and_close = true; + next_char (); + } + + if (get () != '>') { + this.line = line_start; + this.column = column_start; + this.pos = start; + return null; + } + + next_char (); + + if (open_and_close) { + this.tmp_token = new Token (TokenType.XML_CLOSE, + id_start.substring (0, id_len), + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + + if (close) { + return new Token (TokenType.XML_CLOSE, + id_start.substring (0, id_len), + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } else { + return new Token (TokenType.XML_OPEN, + id_start.substring (0, id_len), + map, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + } + + private Token? newline_prefix () { + if (get () != '\n') { + return null; + } + + unowned string start = this.pos; + this.line++; + this.column = 0; + + for (unichar c = next_char (); c == ' ' || c == '\t' ; c = next_char ()); + + if (get () == '\n') { + next_char (); + this.line++; + this.column = 0; + return new Token (TokenType.GTKDOC_PARAGRAPH, + "\n\n", + null, + start, + offset (this.pos, start), + this.line, + this.column, + this.column); + } else { + return new Token (TokenType.NEWLINE, + "\n", + null, + start, + offset (this.pos, start), + this.line, + this.column, + this.column); + } + } + + private Token? eof_prefix () { + if (get () != '\0') { + return null; + } + + return new Token (TokenType.EOF, + "", + null, + this.pos, + 1, + this.line, + this.column, + this.column); + } + + private Token? space_prefix () { + unowned string start = this.pos; + int column_start = this.column; + for (unichar c = get (); c == ' ' || c == '\t'; c = next_char ()); + int len = offset (this.pos, start); + if (len == 0) { + this.column = column_start; + this.pos = start; + return null; + } + + return new Token (TokenType.SPACE, + start.substring (0, len), + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + + private Token? word_prefix () { + unowned string start = this.pos; + int column_start = this.column; + unichar c = get (); + if (c == '<' || c == '@') { + next_char (); + } + + for (c = get (); c != ' ' && c != '\t' && c != '\n' && c != '\0' && c != '<' && c != '@'; c = next_char ()); + int len = offset (this.pos, start); + if (len == 0) { + this.column = column_start; + this.pos = start; + return null; + } + + return new Token (TokenType.WORD, + unescape (start.substring (0, len)), + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + + private Token? gtkdoc_source_open_prefix () { + if (!this.pos.has_prefix ("|[")) { + return null; + } + + unowned string start = this.pos; + int column_start = this.column; + next_char (); + next_char (); + + return new Token (TokenType.GTKDOC_SOURCE_OPEN, + "|[", + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + + private Token? gtkdoc_source_close_prefix () { + if (!this.pos.has_prefix ("]|")) { + return null; + } + + unowned string start = this.pos; + int column_start = this.column; + next_char (); + next_char (); + + return new Token (TokenType.GTKDOC_SOURCE_CLOSE, "]|", + null, + start, + offset (this.pos, start), + this.line, + column_start, + this.column); + } + + public Token next () { + if (tmp_token != null) { + var tmp = tmp_token; + tmp_token = null; + return tmp; + } + + Token? token = function_prefix (); + if (token != null) { + return token; + } + + token = xml_prefix (); + if (token != null) { + return token; + } + + token = gtkdoc_param_prefix (); + if (token != null) { + return token; + } + + token = gtkdoc_const_prefix (); + if (token != null) { + return token; + } + + token = gtkdoc_type_prefix (); + if (token != null) { + return token; + } + + token = space_prefix (); + if (token != null) { + return token; + } + + token = newline_prefix (); + if (token != null) { + return token; + } + + token = gtkdoc_signal_prefix (); + if (token != null) { + return token; + } + + token = gtkdoc_property_prefix (); + if (token != null) { + return token; + } + + token = gtkdoc_source_open_prefix (); + if (token != null) { + return token; + } + + token = gtkdoc_source_close_prefix (); + if (token != null) { + return token; + } + + token = eof_prefix (); + if (token != null) { + return token; + } + + token = word_prefix (); + if (token != null) { + return token; + } + + assert_not_reached (); + } +} + diff --git a/libvaladoc/documentation/gtkdocmarkdownparser.vala b/libvaladoc/documentation/gtkdocmarkdownparser.vala new file mode 100644 index 000000000..ff52e9542 --- /dev/null +++ b/libvaladoc/documentation/gtkdocmarkdownparser.vala @@ -0,0 +1,840 @@ +/* gtkdocmarkdownparser.vala + * + * Copyright (C) 2014 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +using Valadoc.Content; +using Valadoc; +using Gee; + + + +public class Valadoc.Gtkdoc.MarkdownParser : Object, ResourceLocator { + private Valadoc.Parser parser; + private Content.ContentFactory _factory; + + private Settings _settings; + private ErrorReporter _reporter; + private Api.Tree _tree; + + private ArrayList<Object> _stack = new ArrayList<Object> (); + + private Valadoc.Token preserved_token = null; + private Regex regex_source_lang; + + private Importer.InternalIdRegistrar id_registrar; + private GirMetaData metadata; + private Api.GirSourceComment gir_comment; + private Api.Node element; + + + public MarkdownParser (Settings settings, ErrorReporter reporter, Api.Tree? tree, ModuleLoader _modules) { + MarkdownScanner scanner = new MarkdownScanner (settings); + parser = new Valadoc.Parser (settings, scanner, reporter); + scanner.set_parser (parser); + + + _factory = new Content.ContentFactory (settings, this, _modules); + _settings = settings; + _reporter = reporter; + _tree = tree; + + init_rules (); + + try { + regex_source_lang = new Regex ("^<!--[ \t]+language=\"([A-Za-z]*)\"[ \t]+-->"); + } catch (Error e) { + assert_not_reached (); + } + } + + public void init_rules () { + Valadoc.TokenType word = Valadoc.TokenType.any_word ().action (add_text); + + StubRule content = new StubRule (); + content.set_name ("Content"); + + StubRule run = new StubRule (); + run.set_name ("Run"); + + + Rule param = Rule.seq ({ + Valadoc.TokenType.MARKDOWN_PARAMETER.action ((token) => { + Run _run = null; + + if (token.value == gir_comment.instance_param_name) { + _run = _factory.create_run (Run.Style.LANG_KEYWORD); + _run.content.add (_factory.create_text ("this")); + } else if (is_error_parameter (token.value)) { + _run = _factory.create_run (Run.Style.LANG_KEYWORD); + _run.content.add (_factory.create_text ("throws")); + } else { + string? param_name; + string? param_array_name; + bool is_return_type_len; + + ImporterHelper.resolve_parameter_ctype ( + this._tree, + this.element, + token.value, + out param_name, + out param_array_name, + out is_return_type_len); + + _run = _factory.create_run (Run.Style.MONOSPACED); + + if (is_return_type_len) { + Run keyword_run = _factory.create_run (Run.Style.LANG_KEYWORD); + keyword_run.content.add (_factory.create_text ("return")); + _run.content.add (keyword_run); + + _run.content.add (_factory.create_text (".length")); + } else if (param_array_name != null) { + _run.content.add (_factory.create_text (param_array_name + ".length")); + } else { + _run.content.add (_factory.create_text (param_name)); + } + + + } + + push (_run); + }) + }) + .set_name ("Parameter"); + + + Rule constant = Rule.seq ({ + Valadoc.TokenType.MARKDOWN_CONSTANT.action ((token) => { + if (is_literal (token.value)) { + var _run = _factory.create_run (Run.Style.LANG_LITERAL); + _run.content.add (_factory.create_text (token.value.down ())); + push (_run); + } else { + add_symbol_link ("c::" + token.value, true); + } + }) + }) + .set_name ("Constant"); + + + Rule gmember = Rule.seq ({ + Valadoc.TokenType.MARKDOWN_LOCAL_GMEMBER.action ((token) => { + Api.Item gtype = element; + while (gtype is Api.Class == false && gtype is Api.Interface == false && gtype != null) { + gtype = gtype.parent; + } + + string parent_cname; + if (gtype is Api.Class) { + parent_cname = ((Api.Class) gtype).get_cname (); + } else if (gtype is Api.Interface) { + parent_cname = ((Api.Interface) gtype).get_cname (); + } else { + parent_cname = ""; + } + + add_symbol_link ("c::" + parent_cname + token.value, true); + }) + }) + .set_name ("GLocalMember"); + + + Rule symbol = Rule.seq ({ + Valadoc.TokenType.MARKDOWN_SYMBOL.action ((token) => { + add_symbol_link ("c::" + token.value, true); + }) + }) + .set_name ("Symbol"); + + + Rule function = Rule.seq ({ + Valadoc.TokenType.MARKDOWN_FUNCTION.action ((token) => { + add_symbol_link ("c::" + token.value, false); + }) + }) + .set_name ("Function"); + + + Rule link_short = Rule.seq ({ + Valadoc.TokenType.MARKDOWN_LESS_THAN, + Rule.option ({ + Rule.one_of ({ + Valadoc.TokenType.MARKDOWN_MAIL.action (preserve_token), + Valadoc.TokenType.MARKDOWN_LINK.action (preserve_token) + }), + Rule.option ({ + Valadoc.TokenType.MARKDOWN_GREATER_THAN, + }) + .set_reduce (() => { + Link url = _factory.create_link (); + url.url = pop_preserved_link (); + push (url); + }) + .set_skip (() => { + add_content_string ("<"); + add_value (preserved_token); + preserved_token = null; + }) + }) + .set_skip (() => { + add_content_string ("<"); + }) + }) + .set_name ("Link"); + + + Rule link = Rule.seq ({ + Valadoc.TokenType.MARKDOWN_OPEN_BRACKET, + Rule.option ({ + Rule.option ({ + run + }), + Valadoc.TokenType.MARKDOWN_CLOSE_BRACKET, + Rule.option ({ + Rule.one_of ({ + // External link: + Rule.seq ({ + Valadoc.TokenType.MARKDOWN_OPEN_PARENS, + Rule.option ({ + Rule.one_of ({ + Valadoc.TokenType.MARKDOWN_LINK.action (preserve_token), + Valadoc.TokenType.MARKDOWN_MAIL.action (preserve_token) + }), + Rule.option ({ + Valadoc.TokenType.MARKDOWN_CLOSE_PARENS + }) + .set_reduce (() => { + Link url = _factory.create_link (); + url.url = pop_preserved_link (); + + Run label = (Run) peek (); + url.content.add_all (label.content); + label.content.clear (); + label.content.add (url); + }) + .set_skip (() => { + Run _run = (Run) peek (); + _run.content.insert (0, _factory.create_text ("[")); + _run.content.add (_factory.create_text ("](" + pop_preserved_link ())); + }) + }) + .set_skip (() => { + Run _run = (Run) peek (); + _run.content.insert (0, _factory.create_text ("[")); + _run.content.add (_factory.create_text ("](")); + }) + }), + // Internal link: + Rule.seq ({ + Valadoc.TokenType.MARKDOWN_OPEN_BRACKET, + Rule.option ({ + Valadoc.TokenType.any_word ().action (preserve_token), + Rule.option ({ + Valadoc.TokenType.MARKDOWN_CLOSE_BRACKET + }) + .set_reduce (() => { + Link url = _factory.create_link (); + url.url = pop_preserved_link (); + url.id_registrar = id_registrar; + + Run label = (Run) peek (); + url.content.add_all (label.content); + label.content.clear (); + label.content.add (url); + }) + .set_skip (() => { + Run _run = (Run) peek (); + _run.content.insert (0, _factory.create_text ("[")); + + _run.content.add (_factory.create_text ("][" + pop_preserved_link ())); + }) + }) + .set_skip (() => { + Run _run = (Run) peek (); + _run.content.insert (0, _factory.create_text ("[")); + _run.content.add (_factory.create_text ("][")); + }) + }) + }) + }) + .set_skip (() => { + Run _run = (Run) peek (); + _run.content.insert (0, _factory.create_text ("[")); + _run.content.add (_factory.create_text ("]")); + }) + }) + .set_skip (() => { + Run _run = (Run) peek (); + _run.content.insert (0, _factory.create_text ("[")); + }) + }) + .set_start (() => { push (_factory.create_run (Run.Style.NONE)); }) + .set_name ("Link"); + + + Rule image = Rule.seq ({ + Valadoc.TokenType.MARKDOWN_EXCLAMATION_MARK, + Rule.option ({ + Valadoc.TokenType.MARKDOWN_OPEN_BRACKET, + Rule.option ({ + run + }), + Valadoc.TokenType.MARKDOWN_CLOSE_BRACKET, + Rule.option ({ + Valadoc.TokenType.MARKDOWN_OPEN_BRACKET, + Rule.option ({ + Rule.one_of ({ + Valadoc.TokenType.any_word ().action (preserve_token), + Valadoc.TokenType.MARKDOWN_LINK.action (preserve_token), + Valadoc.TokenType.MARKDOWN_MAIL.action (preserve_token) + }), + Rule.option ({ + Valadoc.TokenType.MARKDOWN_CLOSE_BRACKET, + }) + .set_reduce (() => { + Run label = (Run) peek (); + + string label_str; + try { + label_str = run_to_string (label, "<image>"); + } catch (Error e) { + parser.warning (preserved_token, e.message); + label_str = null; + } + + Embedded embedded = _factory.create_embedded (); + embedded.url = fix_resource_path (pop_preserved_path ()); + embedded.caption = label_str; + + label.content.clear (); + label.content.add (embedded); + }) + .set_skip (() => { + Run _run = (Run) peek (); + _run.content.insert (0, _factory.create_text ("![")); + _run.content.add (_factory.create_text ("][" + pop_preserved_link ())); + }) + }) + .set_skip (() => { + Run _run = (Run) peek (); + _run.content.insert (0, _factory.create_text ("![")); + _run.content.add (_factory.create_text ("][")); + }) + }) + .set_skip (() => { + Run _run = (Run) peek (); + _run.content.insert (0, _factory.create_text ("![")); + _run.content.add (_factory.create_text ("]")); + }) + }) + .set_skip (() => { + Run _run = (Run) peek (); + _run.content.insert (0, _factory.create_text ("!")); + }) + }) + .set_start (() => { push (_factory.create_run (Run.Style.NONE)); }) + .set_name ("Image"); + + + Rule source = Rule.seq ({ + Valadoc.TokenType.MARKDOWN_SOURCE.action ((token) => { + SourceCode code = _factory.create_source_code (); + MatchInfo info; + + unowned string source = token.value; + if (regex_source_lang.match (source, 0, out info)) { + string lang_name = info.fetch (1).down (); + SourceCode.Language? lang = SourceCode.Language.from_string (lang_name); + code.language = lang; + + if (lang == null) { + parser.warning (token, "Unknown language `%s' in source code block |[<!-- language=\"\"".printf (lang_name)); + } + + source = source.offset (source.index_of_char ('>') + 1); + } else { + code.language = (Highlighter.XmlScanner.is_xml (source)) + ? SourceCode.Language.XML + : SourceCode.Language.C; + } + + code.code = source; + push (code); + }) + }) + .set_name ("Source"); + + + Rule text = Rule.many ({ + Rule.one_of ({ + word, + Valadoc.TokenType.MARKDOWN_SPACE.action (add_text), + Valadoc.TokenType.MARKDOWN_MAIL.action (add_value), + Valadoc.TokenType.MARKDOWN_LINK.action (add_value), + Valadoc.TokenType.MARKDOWN_OPEN_PARENS.action (add_text), + Valadoc.TokenType.MARKDOWN_CLOSE_PARENS.action (add_text), + Valadoc.TokenType.MARKDOWN_CLOSE_BRACKET.action (add_text), + Valadoc.TokenType.MARKDOWN_GREATER_THAN.action (add_text) + }) + }) + .set_start (() => { push (_factory.create_text ()); }) + .set_name ("Text"); + + + run.set_rule ( + Rule.many ({ + Rule.one_of ({ + text, + link, + link_short, + image, + function, + constant, + param, + symbol, + gmember, + source + }) + .set_reduce (() => { + var head = (Inline) pop (); + ((InlineContent) peek ()).content.add (head); + }) + }) + ); + + + Rule unordered_list = Rule.seq ({ + Rule.seq ({ + Valadoc.TokenType.MARKDOWN_UNORDERED_LIST_ITEM_START, + content, + Valadoc.TokenType.MARKDOWN_UNORDERED_LIST_ITEM_END + }) + .set_start (() => { push (_factory.create_list_item ()); }) + .set_reduce (() => { + var head = (ListItem) pop (); + ((Content.List) peek ()).items.add (head); + }) + }) + .set_start (() => { + var siblings = ((BlockContent) peek ()).content; + if (siblings.size > 0 && siblings.last () is Content.List) { + push (siblings.last ()); + } else { + Content.List list = _factory.create_list (); + list.bullet = Content.List.Bullet.UNORDERED; + siblings.add (list); + push (list); + } + }) + .set_reduce (() => { + (Content.List) pop (); + }) + .set_name ("UnorderedList"); + + + Rule ordered_list = Rule.seq ({ + Rule.seq ({ + Valadoc.TokenType.MARKDOWN_ORDERED_LIST_ITEM_START, + content, + Valadoc.TokenType.MARKDOWN_ORDERED_LIST_ITEM_END + }) + .set_start (() => { push (_factory.create_list_item ()); }) + .set_reduce (() => { + var head = (ListItem) pop (); + ((Content.List) peek ()).items.add (head); + }) + }) + .set_start (() => { + var siblings = ((BlockContent) peek ()).content; + if (siblings.size > 0 && siblings.last () is Content.List) { + push (siblings.last ()); + } else { + Content.List list = _factory.create_list (); + list.bullet = Content.List.Bullet.ORDERED_NUMBER; + siblings.add (list); + push (list); + } + }) + .set_reduce (() => { + (Content.List) pop (); + }) + .set_name ("OrderedList"); + + + Rule paragraph = Rule.seq ({ + Valadoc.TokenType.MARKDOWN_PARAGRAPH, + Rule.option ({ + Valadoc.TokenType.MARKDOWN_SPACE + }), + Rule.option ({ + run + }) + }) + .set_start (() => { push (_factory.create_paragraph ()); }) + .set_reduce (() => { + var head = (Paragraph) pop (); + ((BlockContent) peek ()).content.add (head); + }) + .set_name ("Paragraph"); + + + Rule block = Rule.seq ({ + Valadoc.TokenType.MARKDOWN_BLOCK_START, + content, + Valadoc.TokenType.MARKDOWN_BLOCK_END + }) + .set_start (() => { push (_factory.create_note ()); }) + .set_reduce (() => { + var head = (Note) pop (); + ((BlockContent) peek ()).content.add (head); + }) + .set_name ("Block"); + + + Rule headline = Rule.seq ({ + Rule.one_of ({ + Valadoc.TokenType.MARKDOWN_HEADLINE_1.action ((token) => { + Headline h = (Headline) peek (); + h.level = 1; + }), + Valadoc.TokenType.MARKDOWN_HEADLINE_2.action ((token) => { + Headline h = (Headline) peek (); + h.level = 2; + }) + }), + run, + Rule.option ({ + Valadoc.TokenType.MARKDOWN_HEADLINE_HASH.action ((token) => { + id_registrar.register_symbol (token.value, element); + }) + }), + Valadoc.TokenType.MARKDOWN_HEADLINE_END + }) + .set_start (() => { push (_factory.create_headline ()); }) + .set_reduce (() => { + Headline h = (Headline) pop (); + ((BlockContent) peek ()).content.add (h); + }) + .set_name ("Headline"); + + content.set_rule ( + Rule.many ({ + Rule.one_of ({ + paragraph, + unordered_list, + ordered_list, + headline, + block + }) + }) + ); + + + Rule comment = Rule.seq ({ + content, + Valadoc.TokenType.MARKDOWN_EOC + }) + .set_start (() => { push (_factory.create_comment ()); }) + .set_name ("Comment"); + + parser.set_root_rule (comment); + } + + private Comment? _parse (Api.SourceComment comment) { + try { + _stack.clear (); + parser.parse (comment.content, comment.file.get_name (), comment.first_line, comment.first_column); + return (Comment) pop (); + } catch (ParserError e) { + return null; + } + } + + private Taglet? _parse_block_taglet (Api.SourceComment comment, string taglet_name) { + Comment? cmnt = _parse (comment); + if (cmnt == null) { + return null; + } + + Taglet? taglet = _factory.create_taglet (taglet_name); + BlockContent block = (BlockContent) taglet; + assert (taglet != null && block != null); + + block.content.add_all (cmnt.content); + + return taglet; + } + + private Note? _parse_note (Api.SourceComment comment) { + Comment? cmnt = _parse (comment); + if (cmnt == null) { + return null; + } + + Note note = _factory.create_note (); + note.content.add_all (cmnt.content); + + return note; + } + + private void add_taglet (ref Comment? comment, Taglet? taglet) { + if (taglet == null) { + return ; + } + + if (comment == null) { + comment = _factory.create_comment (); + } + + comment.taglets.add (taglet); + } + + private void add_note (ref Comment? comment, Note? note) { + if (note == null) { + return ; + } + + if (comment == null) { + comment = _factory.create_comment (); + } + + if (comment.content.size == 0) { + comment.content.add (_factory.create_paragraph ()); + } + + comment.content.insert (1, note); + } + + public Comment? parse (Api.Node element, Api.GirSourceComment gir_comment, GirMetaData metadata, Importer.InternalIdRegistrar id_registrar, string? this_name = null) { + this.metadata = metadata; + this.id_registrar = id_registrar; + this.gir_comment = gir_comment; + this.element = element; + + // main: + Comment? cmnt = _parse (gir_comment); + if (cmnt != null) { + ImporterHelper.extract_short_desc (cmnt, _factory); + } + + + // deprecated: + if (gir_comment.deprecated_comment != null) { + Note? note = _parse_note (gir_comment.deprecated_comment); + add_note (ref cmnt, note); + } + + + // version: + if (gir_comment.version_comment != null) { + Note? note = _parse_note (gir_comment.version_comment); + add_note (ref cmnt, note); + } + + + // stability: + if (gir_comment.stability_comment != null) { + Note? note = _parse_note (gir_comment.stability_comment); + add_note (ref cmnt, note); + } + + + // return: + if (gir_comment.return_comment != null) { + Taglet? taglet = _parse_block_taglet (gir_comment.return_comment, "return"); + add_taglet (ref cmnt, taglet); + } + + + // parameters: + MapIterator<string, Api.SourceComment> iter = gir_comment.parameter_iterator (); + for (bool has_next = iter.next (); has_next; has_next = iter.next ()) { + Taglets.Param? taglet = _parse_block_taglet (iter.get_value (), "param") as Taglets.Param; + string param_name = iter.get_key (); + + taglet.is_c_self_param = (param_name == gir_comment.instance_param_name); + taglet.parameter_name = param_name; + add_taglet (ref cmnt, taglet); + } + + + this.metadata = null; + this.gir_comment = null; + this.id_registrar = null; + this.element = null; + + return cmnt; + } + + + private void add_text (Valadoc.Token token) { + add_content_string (token.to_string ()); + } + + private void add_value (Valadoc.Token token) { + assert (token.value != null); + + add_content_string (token.value); + } + + private void add_content_string (string str) { + var text = peek () as Text; + if (text == null) { + push (text = _factory.create_text ()); + } + + text.content += str; + } + + private void add_symbol_link (string symbol, bool accept_plural) { + var taglet = new Taglets.Link (); + taglet.c_accept_plural = accept_plural; + taglet.symbol_name = symbol; + + var run = _factory.create_run (Run.Style.NONE); + run.content.add (taglet); + + push (run); + } + + private void preserve_token (Valadoc.Token token) { + assert (preserved_token == null); + preserved_token = token; + } + + private string pop_preserved_link () { + assert (preserved_token != null); + + Valadoc.Token _link_token = (owned) preserved_token; + + // email: + if (_link_token.token_type == Valadoc.TokenType.MARKDOWN_MAIL) { + return "mailto:" + _link_token.value; + } + + // http or https link: + if (_link_token.value != null) { + return _link_token.value; + } + + // GTKDOC-ID: + return _link_token.word; + } + + private string pop_preserved_path () { + assert (preserved_token != null); + + Valadoc.Token _path_token = (owned) preserved_token; + return _path_token.word ?? _path_token.value; + } + + private inline string run_to_string (Run run, string rule_name) throws Error { + StringBuilder builder = new StringBuilder (); + inline_to_string (run, rule_name, builder); + return (owned) builder.str; + } + + private void inline_to_string (Inline element, string rule_name, StringBuilder? builder) throws Error { + if (element is Run) { + Run run = (Run) element; + + foreach (Inline item in run.content) { + inline_to_string (item, rule_name, builder); + } + } else if (element is Text) { + Text text = (Text) element; + builder.append (text.content); + } else if (element is Embedded) { + throw new ContentToStringError.UNEXPECTED_ELEMENT ("Unexpected tag: <image> in `%s'", rule_name); + } else if (element is Link) { + throw new ContentToStringError.UNEXPECTED_ELEMENT ("Unexpected tag: <link> in `%s'", rule_name); + } else if (element is SourceCode) { + throw new ContentToStringError.UNEXPECTED_ELEMENT ("Unexpected tag: `|[' in `%s'", rule_name); + } else { + throw new ContentToStringError.UNEXPECTED_ELEMENT ("Unexpected tag in `%s''", rule_name); + } + } + + private bool is_literal (string str) { + if (str == "TRUE") { + return true; + } + + if (str == "FALSE") { + return true; + } + + if (str == "NULL") { + return true; + } + + if (str[0].isdigit ()) { + return true; + } + + return true; + } + + private bool is_error_parameter (string name) { + if (element == null || name != "error") { + return false; + } + + if (!(element is Api.Method || element is Api.Delegate)) { + return false; + } + + return element.get_children_by_types ({ + Api.NodeType.ERROR_DOMAIN, + Api.NodeType.CLASS}).size > 0; + } + + + public string resolve (string path) { + return path; + } + + private void push (Object element) { + _stack.add (element); + } + + private Object peek (int offset = -1) { + assert (_stack.size >= - offset); + return _stack.get (_stack.size + offset); + } + + private Object pop () { + Object node = peek (); + _stack.remove_at (_stack.size - 1); + return node; + } + + private inline string fix_resource_path (string path) { + return metadata.get_resource_path (path); + } +} + + +private errordomain Valadoc.Gtkdoc.ContentToStringError { + UNEXPECTED_ELEMENT +} + diff --git a/libvaladoc/documentation/gtkdocmarkdownscanner.vala b/libvaladoc/documentation/gtkdocmarkdownscanner.vala new file mode 100644 index 000000000..1908957d6 --- /dev/null +++ b/libvaladoc/documentation/gtkdocmarkdownscanner.vala @@ -0,0 +1,761 @@ +/* gtkdocmarkdownscanner.vala + * + * Copyright (C) 2014 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +using Valadoc.Content; +using Valadoc; +using Gee; + + + +public class Valadoc.Gtkdoc.MarkdownScanner : GLib.Object, Valadoc.Scanner { + private enum State { + NORMAL, + UNORDERED_LIST, + ORDERED_LIST, + BLOCK + } + + private Settings _settings; + private Valadoc.Parser parser; + + private unowned string _content; + private int _skip; + + private StringBuilder _current_string = new StringBuilder (); + private unowned string _index; + private bool contains_at; + + private int _line; + private int _column; + private int _last_line; + private int _last_column; + private bool _stop; + + private string? headline_end; + + private Regex regex_mail; + + private LinkedList<State> states = new LinkedList<State> (); + + private inline void push_state (State state) { + states.offer_head (state); + } + + private inline State pop_state () { + return states.poll_head (); + } + + private inline State peek_state () { + return states.peek_head (); + } + + + public MarkdownScanner (Settings settings) { + _settings = settings; + + try { + regex_mail = new Regex ("^[A-Za-z0-9._-]+@[A-Za-z0-9._-]+$"); + } catch (Error e) { + assert_not_reached (); + } + } + + public void set_parser (Valadoc.Parser parser) { + this.parser = parser; + } + + public void reset () { + _stop = false; + _last_line = 0; + _last_column = 0; + _line = 0; + _column = 0; + _skip = 0; + _current_string.erase (0, -1); + contains_at = false; + + states.clear (); + push_state (State.NORMAL); + } + + public void scan (string content) throws ParserError { + _content = content; + _index = _content; + + + // Accept block taglets: + if (handle_newline (_index, true)) { + _index = _index.next_char (); + } else { + // Empty string + emit_token (Valadoc.TokenType.MARKDOWN_PARAGRAPH); + } + + + while (!_stop && _index.get_char () != 0) { + unichar c = _index.get_char (); + accept (c); + + _index = _index.next_char (); + } + + + // Close open blocks: + while (peek_state () != State.NORMAL) { + if (peek_state () == State.BLOCK) { + emit_token (Valadoc.TokenType.MARKDOWN_BLOCK_END); + pop_state (); + } else { + close_block (); + } + } + + + emit_token (Valadoc.TokenType.MARKDOWN_EOC); + } + + private void accept (unichar c) throws ParserError { + _column++; + if (_skip > 0) { + _skip--; + return ; + } + + // In headline: + string? hash = null; + + if (headline_end != null && is_headline_end (ref _index, headline_end, out hash)) { + if (hash != null) { + emit_token (Valadoc.TokenType.MARKDOWN_HEADLINE_HASH, hash); + } + emit_token (Valadoc.TokenType.MARKDOWN_HEADLINE_END); + headline_end = null; + + handle_newline (_index, true); + + return ; + } + + switch (c) { + case '\\': + switch (get_next_char ()) { + case '(': + if (get_next_char (2) == ')') { + _current_string.append ("()"); + _skip += 2; + break; + } + + _current_string.append ("\\("); + _skip++; + break; + + case '<': + append_char ('<'); + _skip++; + break; + + case '>': + append_char ('>'); + _skip++; + break; + + case '@': + append_char ('@'); + _skip++; + break; + + case '%': + append_char ('%'); + _skip++; + break; + + case '#': + append_char ('#'); + _skip++; + break; + + default: + append_char ('\\'); + break; + } + + break; + + case ':': + unichar next_char = get_next_char (); + unichar next2_char = get_next_char (2); + + // :id or ::id + if ((_current_string.len == 0 || !_current_string.str[_current_string.len - 1].isalpha ()) + && (next_char.isalpha () || (next_char == ':' && next2_char.isalpha ()))) { + + unowned string _iter; + if (next_char == ':') { + _iter = _index.offset (2); + _skip++; + } else { + _iter = _index.offset (1); + } + + while (_iter[0].isalnum () || _iter[0] == '_' || (_iter[0] == '-' && _iter[1].isalnum ())) { + _iter = _iter.offset (1); + _skip++; + } + + emit_token (Valadoc.TokenType.MARKDOWN_LOCAL_GMEMBER, _index.substring (0, _skip + 1)); + break; + } + + append_char (c); + break; + + case '%': + // " %foo", "-%foo" but not " %", "a%foo", ... + if ((_current_string.len == 0 || !_current_string.str[_current_string.len - 1].isalpha ()) && get_next_char ().isalpha ()) { + unowned string _iter = _index.offset (1); + + while (_iter[0].isalnum () || _iter[0] == '_') { + _iter = _iter.offset (1); + _skip++; + } + + emit_token (Valadoc.TokenType.MARKDOWN_CONSTANT, _index.substring (1, _skip)); + break; + } + // %numeric: + if ((_current_string.len == 0 || !_current_string.str[_current_string.len - 1].isalpha ()) && get_next_char ().isdigit ()) { + unowned string _iter = _index.offset (1); + + while (_iter[0].isdigit ()) { + _iter = _iter.offset (1); + _skip++; + } + + // Integers: + if (_iter[0].tolower () == 'u' && _iter[0].tolower () == 'l') { + _iter = _iter.offset (1); + _skip += 2; + + emit_token (Valadoc.TokenType.MARKDOWN_CONSTANT, _index.substring (1, _skip)); + break; + } else if (_iter[0].tolower () == 'u' || _iter[0].tolower () == 'l') { + _iter = _iter.offset (1); + _skip++; + + emit_token (Valadoc.TokenType.MARKDOWN_CONSTANT, _index.substring (1, _skip)); + break; + } + + + // Float, double: + if (_iter[0] == '.' && _iter[1].isdigit ()) { + _iter = _iter.offset (2); + _skip += 2; + } + + while (_iter[0].isdigit ()) { + _iter = _iter.offset (1); + _skip++; + } + + if (_iter[0].tolower () == 'f' || _iter[0].tolower () == 'l') { + _iter = _iter.offset (1); + _skip++; + } + + emit_token (Valadoc.TokenType.MARKDOWN_CONSTANT, _index.substring (1, _skip)); + break; + } + + append_char (c); + break; + + case '#': + // " #foo", "-#foo" but not " #"", "a#""foo", ... + if ((_current_string.len == 0 || !_current_string.str[_current_string.len - 1].isalpha ()) && get_next_char ().isalpha ()) { + unowned string _iter = _index.offset (1); + + while (_iter[0].isalnum () || _iter[0] == '_') { + _iter = _iter.offset (1); + _skip++; + } + + // signals, fields, properties + bool is_field = false; + if (((_iter[0] == ':' || _iter[0] == '.') && _iter[1].isalpha ()) + || (_iter.has_prefix ("::") && _iter[2].isalpha ())) { + + is_field = (_iter[0] == '.'); + _iter = _iter.offset (2); + _skip += 2; + + while (_iter[0].isalnum () || _iter[0] == '_' || (!is_field && _iter[0] == '-')) { + _iter = _iter.offset (1); + _skip++; + } + } + + if (is_field && _iter.has_prefix ("()")) { + _skip += 2; + + emit_token (Valadoc.TokenType.MARKDOWN_SYMBOL, _index.substring (1, _skip - 2)); + } else { + emit_token (Valadoc.TokenType.MARKDOWN_SYMBOL, _index.substring (1, _skip)); + } + + break; + } + + append_char (c); + break; + + case '@': + // " @foo", "-@foo" but not " @", "a@foo", ... + if ((_current_string.len == 0 || !_current_string.str[_current_string.len - 1].isalpha ())) { + if (get_next_char ().isalpha ()) { + unowned string _iter = _index.offset (1); + + while (_iter[0].isalnum () || _iter[0] == '_') { + _iter = _iter.offset (1); + _skip++; + } + + emit_token (Valadoc.TokenType.MARKDOWN_PARAMETER, _index.substring (1, _skip)); + break; + } else if (_index.has_prefix ("@...")) { + _skip += 3; + emit_token (Valadoc.TokenType.MARKDOWN_PARAMETER, "..."); + break; + } + } + + append_char (c); + contains_at = true; + break; + + case '(': + if (get_next_char () == ')' && is_id ()) { + string id = _current_string.str; + _current_string.erase (0, -1); + contains_at = false; + + emit_token (Valadoc.TokenType.MARKDOWN_FUNCTION, id); + _skip++; + break; + } + + emit_token (Valadoc.TokenType.MARKDOWN_OPEN_PARENS); + break; + + case ')': + emit_token (Valadoc.TokenType.MARKDOWN_CLOSE_PARENS); + break; + + case '[': + unowned string iter = _index; + int count = 1; + while (iter[0] != '\n' && iter[0] != '\0' && count > 0) { + iter = iter.offset (1); + switch (iter[0]) { + case '[': + count++; + break; + + case ']': + count--; + break; + } + } + + if (iter[0] == ']') { + emit_token (Valadoc.TokenType.MARKDOWN_OPEN_BRACKET); + } else { + append_char ('['); + } + break; + + case ']': + emit_token (Valadoc.TokenType.MARKDOWN_CLOSE_BRACKET); + break; + + case '<': + emit_token (Valadoc.TokenType.MARKDOWN_LESS_THAN); + break; + + case '>': + emit_token (Valadoc.TokenType.MARKDOWN_GREATER_THAN); + break; + + case '!': + emit_token (Valadoc.TokenType.MARKDOWN_EXCLAMATION_MARK); + break; + + case '|': + if (get_next_char () == '[') { + unowned string _iter = _index.offset (2); + int end = _iter.index_of ("]|"); + if (end < 0) { + append_char ('|'); + } else { + emit_token (Valadoc.TokenType.MARKDOWN_SOURCE, _index.substring (2, end)); + _skip = end + 3; + } + + break; + } + + append_char (c); + break; + + case '\t': + case ' ': + unowned string _iter = _index.offset (1); + _skip += skip_spaces (ref _iter); + + if (_iter[0] != '\n' && _iter[0] != '\0') { + emit_token (Valadoc.TokenType.MARKDOWN_SPACE); + } + break; + + case '\r': + // Ignore + break; + + case '\n': + unowned string _iter = _index.offset (1); + + _line++; + _column = 0; + _last_column = 0; + handle_newline (_iter, false); + break; + + default: + append_char (c); + break; + } + } + + private bool handle_newline (string _iter, bool is_paragraph) throws ParserError { + int leading_spaces; + + leading_spaces = skip_spaces (ref _iter); + + if (_iter[0] == '\0') { + return false; + } + + // Do not emit paragraphs twice: + if (is_paragraph) { + while (_iter[0] == '\n') { + _line++; + _iter = _iter.offset (1); + leading_spaces = skip_spaces (ref _iter); + } + } + + bool in_block = states.contains (State.BLOCK); + if (_iter[0] == '>') { + if (!in_block) { + close_block (); + + if (is_paragraph) { + _column += (int) ((char*) _iter - (char*) _index); + _index = _iter.offset (1); + emit_token (Valadoc.TokenType.MARKDOWN_BLOCK_START); + push_state (State.BLOCK); + } + } + + if (in_block || is_paragraph) { + _column++; + _index = _iter; + + _iter = _iter.offset (1); + skip_spaces (ref _iter); + } + } else if (in_block && is_paragraph) { + _column += (int) ((char*) _iter - (char*) _index); + _index = _iter; + + close_block (); + + emit_token (Valadoc.TokenType.MARKDOWN_BLOCK_END); + pop_state (); + } + + + int list_token_len = 0; + bool is_unsorted_list = _iter[0] == '-' && _iter[1].isspace (); + bool is_sorted_list = is_ordered_list (_iter, out list_token_len); + if ((is_unsorted_list || is_sorted_list) && (is_paragraph || states.contains (State.UNORDERED_LIST) || states.contains (State.ORDERED_LIST))) { + Valadoc.TokenType start_token = Valadoc.TokenType.MARKDOWN_ORDERED_LIST_ITEM_START; + State new_state = State.ORDERED_LIST; + + if (is_unsorted_list) { + start_token = Valadoc.TokenType.MARKDOWN_UNORDERED_LIST_ITEM_START; + new_state = State.UNORDERED_LIST; + list_token_len = 2; + } + + + _iter = _iter.offset (list_token_len); + close_block (); + + skip_spaces (ref _iter); + + _column += (int) ((char*) _iter - (char*) _index); + _index = _iter.offset (-1); + emit_token (start_token); + push_state (new_state); + + emit_token (Valadoc.TokenType.MARKDOWN_PARAGRAPH); + return true; + } + + if ((_iter[0] == '#' && _iter[1].isspace ()) || (_iter[0] == '#' && _iter[1] == '#' && _iter[2].isspace ()) && is_paragraph) { + close_block (); + + if (_iter[1] != '#') { + _iter = _iter.offset (1); + emit_token (Valadoc.TokenType.MARKDOWN_HEADLINE_1); + headline_end = "#"; + } else { + emit_token (Valadoc.TokenType.MARKDOWN_HEADLINE_2); + _iter = _iter.offset (2); + headline_end = "##"; + } + + _column += (int) ((char*) _iter - (char*) _index); + _index = _iter.offset (-1); + + return true; + } + + if (is_paragraph) { + if (leading_spaces == 0) { + close_block (); + } + + _column += (int) ((char*) _iter - (char*) _index); + _index = _iter.offset (-1); + emit_token (Valadoc.TokenType.MARKDOWN_PARAGRAPH); + } else if (_iter[0] == '\n') { + _line++; + _column = 0; + _last_column = 0; + + handle_newline (_iter.offset (1), true); + } else { + emit_token (Valadoc.TokenType.MARKDOWN_SPACE); + } + + return true; + } + + private bool is_headline_end (ref unowned string iter, string separator, out string? hash) { + unowned string _iter = iter; + hash = null; + + + if (_iter[0] == '\n' || _iter[0] == '\0') { + _line++; + return true; + } + if (!_iter.has_prefix (separator)) { + return false; + } + + _iter = _iter.offset (separator.length); + + + skip_spaces (ref _iter); + if (_iter[0] == '\n' || _iter[0] == '\0') { + iter = _iter; + _line++; + return true; + } else if (!_iter.has_prefix ("{#")) { + return false; + } + _iter = _iter.offset (2); + + unowned string id_start = _iter; + int hash_len = 0; + + while (_iter[0] != '}' && _iter[0] != '\n' && _iter[0] != '\0') { + _iter = _iter.offset (1); + hash_len++; + } + + if (_iter[0] != '}') { + return false; + } + + _iter = _iter.offset (1); + + skip_spaces (ref _iter); + + if (_iter[0] == '\n' || _iter[0] == '\0') { + hash = id_start.substring (0, hash_len); + iter = _iter; + _line++; + return true; + } + + return false; + } + + private bool is_ordered_list (string iter, out int numeric_prefix_count) { + numeric_prefix_count = 0; + while (iter[0] >= '0' && iter[0] <= '9') { + numeric_prefix_count++; + iter = iter.offset (1); + } + + if (numeric_prefix_count > 0 && iter[0] == '.' && iter[1].isspace ()) { + numeric_prefix_count++; + return true; + } + + return false; + } + + private inline int skip_spaces (ref unowned string _iter) { + int count = 0; + while (_iter[0] == ' ' || _iter[0] == '\t' || _iter[0] == '\r') { + _iter = _iter.offset (1); + count++; + } + + return count; + } + + private bool close_block () throws ParserError { + if (states.peek () == State.UNORDERED_LIST) { + emit_token (Valadoc.TokenType.MARKDOWN_UNORDERED_LIST_ITEM_END); + pop_state (); + return true; + } else if (states.peek () == State.ORDERED_LIST) { + emit_token (Valadoc.TokenType.MARKDOWN_ORDERED_LIST_ITEM_END); + pop_state (); + return true; + } + + return false; + } + + public void end () throws ParserError { + emit_token (Valadoc.TokenType.EOF); + } + + public void stop () { + _stop = true; + } + + public string get_line_content () { + StringBuilder builder = new StringBuilder (); + weak string line_start = _index; + unichar c; + + while ((char*) line_start > (char*) _content && line_start.prev_char ().get_char () != '\n') { + line_start = line_start.prev_char (); + } + + while ((c = line_start.get_char ()) != '\n' && c != '\0') { + if (c == '\t') { + builder.append_c (' '); + } else { + builder.append_unichar (c); + } + line_start = line_start.next_char (); + } + + return builder.str; + } + + private void emit_token (Valadoc.TokenType type, string? value = null) throws ParserError { + emit_current_word (); + + parser.accept_token (new Valadoc.Token.from_type (type, get_begin (), get_end (_skip), value)); + } + + private void emit_current_word () throws ParserError { + if (_current_string.len > 0) { + if (is_mail ()) { + parser.accept_token (new Valadoc.Token.from_type (Valadoc.TokenType.MARKDOWN_MAIL, get_begin (), get_end (_skip), _current_string.str)); + } else if (_current_string.str.has_prefix ("http://") || _current_string.str.has_prefix ("https://")) { + // TODO: (https?:[\/]{2}[^\s]+?) + parser.accept_token (new Valadoc.Token.from_type (Valadoc.TokenType.MARKDOWN_LINK, get_begin (), get_end (_skip), _current_string.str)); + } else { + parser.accept_token (new Valadoc.Token.from_word (_current_string.str, get_begin (), get_end (-1))); + } + + _current_string.erase (0, -1); + contains_at = false; + } + } + + private SourceLocation get_begin () { + return SourceLocation (_last_line, get_line_start_column () + _last_column); + } + + private SourceLocation get_end (int offset = 0) { + return SourceLocation (_line, get_line_start_column () + _column + offset); + } + + public int get_line_start_column () { + return 0; + } + + private void append_char (unichar c) { + _current_string.append_unichar (c); + } + + private unichar get_next_char (int offset = 1) { + return _index.get_char (_index.index_of_nth_char (offset)); + } + + private inline bool is_mail () { + return contains_at && regex_mail.match (_current_string.str); + } + + private bool is_id () { + if (_current_string.len == 0) { + return false; + } + + if (_current_string.str[0].isalpha () == false && _current_string.str[0] != '_') { + return false; + } + + for (int i = 1; i < _current_string.len ; i++) { + if (_current_string.str[i].isalnum () == false && _current_string.str[i] != '_') { + return false; + } + } + + return true; + } +} + diff --git a/libvaladoc/documentation/importerhelper.vala b/libvaladoc/documentation/importerhelper.vala new file mode 100644 index 000000000..3f7501836 --- /dev/null +++ b/libvaladoc/documentation/importerhelper.vala @@ -0,0 +1,266 @@ +/* importhelper.vala + * + * Copyright (C) 2014 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +using Valadoc.Content; +using Gee; + + +namespace Valadoc.ImporterHelper { + + // + // resolve-parameter-ctype: + // + + internal string? resolve_parameter_ctype (Api.Tree tree, Api.Node element, string parameter_name, + out string? param_name, out string? param_array_name, out bool is_return_type_len) + { + string[]? parts = split_type_name (parameter_name); + is_return_type_len = false; + param_array_name = null; + + Api.FormalParameter? param = null; // type parameter or formal parameter + foreach (Api.Node node in element.get_children_by_type (Api.NodeType.FORMAL_PARAMETER, false)) { + if (node.name == parts[0]) { + param = node as Api.FormalParameter; + break; + } + + if (((Api.FormalParameter) node).implicit_array_length_cparameter_name == parts[0]) { + param_array_name = ((Api.FormalParameter) node).name; + break; + } + } + + if (element is Api.Callable + && ((Api.Callable) element).implicit_array_length_cparameter_name == parts[0]) + { + is_return_type_len = true; + } + + if (parts.length == 1) { + param_name = parameter_name; + return null; + } + + + Api.Item? inner = null; + + if (param_array_name != null || is_return_type_len) { + inner = tree.search_symbol_str (null, "int"); + } else if (param != null) { + inner = param.parameter_type; + } + + while (inner != null) { + if (inner is Api.TypeReference) { + inner = ((Api.TypeReference) inner).data_type; + } else if (inner is Api.Pointer) { + inner = ((Api.Pointer) inner).data_type; + } else if (inner is Api.Array) { + inner = ((Api.Array) inner).data_type; + } else { + break ; + } + } + + + if (inner == null) { + param_name = parameter_name; + return null; + } + + string? cname = null; + if (inner is Api.ErrorDomain) { + cname = ((Api.ErrorDomain) inner).get_cname (); + } else if (inner is Api.Struct) { + cname = ((Api.Struct) inner).get_cname (); + } else if (inner is Api.Class) { + cname = ((Api.Class) inner).get_cname (); + } else if (inner is Api.Enum) { + cname = ((Api.Enum) inner).get_cname (); + } else { + assert_not_reached (); + } + + param_name = (owned) parts[0]; + return "c::" + cname + parts[1] + parts[2]; + } + + + private string[]? split_type_name (string id) { + unichar c; + + for (unowned string pos = id; (c = pos.get_char ()) != '\0'; pos = pos.next_char ()) { + switch (c) { + case '-': // -> + return {id.substring (0, (long) (((char*) pos) - ((char*) id))), "->", (string) (((char*) pos) + 2)}; + + case ':': // : or :: + string sep = (pos.next_char ().get_char () == ':')? "::" : ":"; + return {id.substring (0, (long) (((char*) pos) - ((char*) id))), sep, (string) (((char*) pos) + sep.length)}; + + case '.': + return {id.substring (0, (long) (((char*) pos) - ((char*) id))), ".", (string) (((char*) pos) + 1)}; + } + } + + return {id}; + } + + + + // + // extract-short-desc: + // + + internal void extract_short_desc (Comment comment, ContentFactory factory) { + if (comment.content.size == 0) { + return ; + } + + Paragraph? first_paragraph = comment.content[0] as Paragraph; + if (first_paragraph == null) { + // add empty paragraph to avoid non-text as short descriptions + comment.content.insert (1, factory.create_paragraph ()); + return ; + } + + + // avoid fancy stuff in short descriptions: + first_paragraph.horizontal_align = null; + first_paragraph.vertical_align = null; + first_paragraph.style = null; + + + Paragraph? second_paragraph = split_paragraph (first_paragraph, factory); + if (second_paragraph == null) { + return ; + } + + if (second_paragraph.is_empty () == false) { + comment.content.insert (1, second_paragraph); + } + } + + private inline Text? split_text (Text text, ContentFactory factory) { + int offset = 0; + while ((offset = text.content.index_of_char ('.', offset)) >= 0) { + if (offset >= 2) { + // ignore "e.g." + unowned string cmp4 = ((string) (((char*) text.content) + offset - 2)); + if (cmp4.has_prefix (" e.g.") || cmp4.has_prefix ("(e.g.")) { + offset = offset + 3; + continue; + } + + // ignore "i.e." + if (cmp4.has_prefix (" i.e.") || cmp4.has_prefix ("(i.e.")) { + offset = offset + 3; + continue; + } + } + + unowned string cmp0 = ((string) (((char*) text.content) + offset)); + + // ignore ... (varargs) + if (cmp0.has_prefix ("...")) { + offset = offset + 3; + continue; + } + + // ignore numbers + if (cmp0.get (1).isdigit ()) { + offset = offset + 2; + continue; + } + + + Text sec = factory.create_text (text.content.substring (offset+1, -1)); + text.content = text.content.substring (0, offset+1); + return sec; + } + + return null; + } + + private inline Run? split_run (Run run, ContentFactory factory) { + if (run.style != Run.Style.NONE) { + return null; + } + + Run? sec = null; + + + Iterator<Inline> iter = run.content.iterator (); + for (bool has_next = iter.next (); has_next; has_next = iter.next ()) { + Inline item = iter.get (); + if (sec == null) { + Inline? tmp = split_inline (item, factory); + if (tmp != null) { + sec = factory.create_run (run.style); + sec.content.add (tmp); + } + } else { + sec.content.add (item); + iter.remove (); + } + } + + return sec; + } + + private inline Inline? split_inline (Inline item, ContentFactory factory) { + if (item is Text) { + return split_text ((Text) item, factory); + } else if (item is Run) { + return split_run ((Run) item, factory); + } + + return null; + } + + private inline Paragraph? split_paragraph (Paragraph p, ContentFactory factory) { + Paragraph? sec = null; + + Iterator<Inline> iter = p.content.iterator (); + for (bool has_next = iter.next (); has_next; has_next = iter.next ()) { + Inline item = iter.get (); + if (sec == null) { + Inline? tmp = split_inline (item, factory); + if (tmp != null) { + sec = factory.create_paragraph (); + sec.horizontal_align = p.horizontal_align; + sec.vertical_align = p.vertical_align; + sec.style = p.style; + sec.content.add (tmp); + } + } else { + sec.content.add (item); + iter.remove (); + } + } + + return sec; + } + +} diff --git a/libvaladoc/documentation/wiki.vala b/libvaladoc/documentation/wiki.vala new file mode 100644 index 000000000..0f8f4929e --- /dev/null +++ b/libvaladoc/documentation/wiki.vala @@ -0,0 +1,159 @@ +/* wiki.vala + * + * Copyright (C) 2008-2009 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; + + +public class Valadoc.WikiPage : Object, Documentation { + public Content.Page documentation { + protected set; + get; + } + + public string documentation_str { + private set; + get; + } + + public string path { + private set; + get; + } + + public string name { + private set; + get; + } + + + public Api.Package? package { + get { + return _package; + } + } + + private Api.Package _package; + + /** + * The corresponding file name + */ + public string? get_filename () { + return Path.get_basename(this.path); + } + + public WikiPage (string name, string path, Api.Package package) { + this._package = package; + this.name = name; + this.path = path; + } + + public void read (ErrorReporter reporter) { + try { + string content; + FileUtils.get_contents (this.path, out content); + this.documentation_str = content; + } catch (FileError err) { + reporter.simple_error (null, "Unable to read file '%s': %s", this.path, err.message); + } + } + + public void parse (DocumentationParser docparser, Api.Package pkg) { + documentation = docparser.parse_wikipage (pkg, this); + } +} + + +public class Valadoc.WikiPageTree : Object { + private ArrayList<WikiPage> wikipages; + + + public WikiPageTree () { + } + + public Collection<WikiPage> get_pages () { + return this.wikipages == null? Collection.empty<WikiPage> () : this.wikipages.read_only_view; + } + + public WikiPage? search (string name) { + if (this.wikipages == null) { + return null; + } + + foreach (WikiPage page in this.wikipages ) { + if (page.name == name) { + return page; + } + } + return null; + } + + private void create_tree_from_path (DocumentationParser docparser, Api.Package package, + ErrorReporter reporter, string path, string? nameoffset = null) + { + try { + Dir dir = Dir.open (path); + + for (string? curname = dir.read_name (); curname!=null ; curname = dir.read_name ()) { + string filename = Path.build_filename (path, curname); + if (curname.has_suffix (".valadoc") && FileUtils.test (filename, FileTest.IS_REGULAR)) { + WikiPage wikipage = new WikiPage ((nameoffset!=null) + ? Path.build_filename (nameoffset, curname) + : curname, filename, package); + this.wikipages.add(wikipage); + wikipage.read (reporter); + } else if (FileUtils.test (filename, FileTest.IS_DIR)) { + this.create_tree_from_path (docparser, package, reporter, filename, (nameoffset!=null) + ? Path.build_filename (nameoffset, curname) + : curname); + } + } + } catch (FileError err) { + reporter.simple_error (null, "Unable to open directory '%s': %s", path, err.message); + } + } + + public void parse (Settings settings, DocumentationParser docparser, Api.Package package, ErrorReporter reporter) { + weak string path = settings.wiki_directory; + if (path == null) { + return ; + } + + this.wikipages = new ArrayList<WikiPage> (); + this.create_tree_from_path (docparser, package, reporter, path); + + foreach (WikiPage page in this.wikipages) { + page.parse (docparser, package); + } + } + + public void check (Settings settings, DocumentationParser docparser, Api.Package pkg) { + if (this.wikipages == null) { + return ; + } + + foreach (WikiPage page in this.wikipages) { + docparser.check_wikipage (pkg, page); + } + } +} + + diff --git a/libvaladoc/documentation/wikiscanner.vala b/libvaladoc/documentation/wikiscanner.vala new file mode 100644 index 000000000..81f6ae8e2 --- /dev/null +++ b/libvaladoc/documentation/wikiscanner.vala @@ -0,0 +1,395 @@ +/* wikiscanner.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +public class Valadoc.WikiScanner : Object, Scanner { + + public WikiScanner (Settings settings) { + _settings = settings; + } + + private Settings _settings; + private Parser _parser; + + private string _content; + private unowned string _index; + private bool _stop; + private int _last_line; + private int _last_column; + private int _line; + private int _column; + private bool _url_escape_mode; + private bool _code_escape_mode; + private unichar _last_char; + private int _skip; + private StringBuilder _current_string = new StringBuilder (); + + public void set_parser (Parser parser) { + _parser = parser; + } + + public virtual void reset () { + _stop = false; + _last_line = 0; + _last_column = 0; + _line = 0; + _column = 0; + _url_escape_mode = false; + _code_escape_mode = false; + _last_char = 0; + _skip = 0; + _current_string.erase (0, -1); + } + + public void scan (string content) throws ParserError { + this._content = content; + + for (_index = _content; !_stop && _index.get_char () != 0; _index = _index.next_char ()) { + unichar c = _index.get_char (); + accept (c); + } + } + + public void end () throws ParserError { + emit_token (TokenType.EOF); + } + + public virtual void stop () { + _stop = true; + } + + public void set_url_escape_mode (bool escape_mode) { + _url_escape_mode = escape_mode; + } + + public void set_code_escape_mode (bool escape_mode) { + _code_escape_mode = escape_mode; + } + + public int get_line () { + return _line; + } + + public virtual string get_line_content () { + StringBuilder builder = new StringBuilder (); + weak string line_start = _index; + unichar c; + + while ((char*) line_start > (char*) _content && line_start.prev_char ().get_char () != '\n') { + line_start = line_start.prev_char (); + } + + while ((c = line_start.get_char ()) != '\n' && c != '\0') { + if (c == '\t') { + builder.append_c (' '); + } else { + builder.append_unichar (c); + } + line_start = line_start.next_char (); + } + + return builder.str; + } + + protected unichar get_next_char (int offset = 1) { + return _index.get_char (_index.index_of_nth_char (offset)); + } + + protected virtual void accept (unichar c) throws ParserError { + _column++; + if (_skip == 0) { + if (_code_escape_mode) { + if (c == '}' && get_next_char (1) == '}' && get_next_char (2) == '}') { + _code_escape_mode = false; // This is a temporary hack + emit_token (TokenType.TRIPLE_CLOSED_BRACE); + _skip = 2; + } else { + append_char (c); + } + return; + } else if (_url_escape_mode) { + switch (c) { + // Reserved characters + case ';': + case '/': + case '?': + case ':': + case '@': + case '#': + case '=': + case '&': + // Special characters + case '$': + case '-': + case '_': + case '.': + case '+': + case '!': + case '*': + case '\'': + case '(': + case ')': + case ',': + append_char (c); + return; + default: + break; + } + } + + switch (c) { + case '@': + emit_token (TokenType.AROBASE); + break; + + case '{': + look_for_three (c, + TokenType.OPEN_BRACE, + TokenType.DOUBLE_OPEN_BRACE, + TokenType.TRIPLE_OPEN_BRACE); + break; + + case '}': + look_for_three (c, + TokenType.CLOSED_BRACE, + TokenType.DOUBLE_CLOSED_BRACE, + TokenType.TRIPLE_CLOSED_BRACE); + break; + + case '[': + look_for_two_or_append (c, TokenType.DOUBLE_OPEN_BRACKET); + break; + + case ']': + look_for_two_or_append (c, TokenType.DOUBLE_CLOSED_BRACKET); + break; + + case '|': + look_for_two (c, + TokenType.PIPE, + TokenType.DOUBLE_PIPE); + break; + + case ')': + if (get_next_char () == ')') { + emit_token (TokenType.ALIGN_RIGHT); + _skip = 1; + } else if (get_next_char () == '(') { + emit_token (TokenType.ALIGN_CENTER); + _skip = 1; + } else { + append_char (c); + } + break; + + case '-': + emit_token (TokenType.MINUS); + break; + + case '=': + look_for_five (c, + TokenType.EQUAL_1, + TokenType.EQUAL_2, + TokenType.EQUAL_3, + TokenType.EQUAL_4, + TokenType.EQUAL_5); + break; + + case '<': + if (!look_for ("<<BR>>", TokenType.BREAK)) { + emit_token (TokenType.LESS_THAN); + } + break; + + case '>': + emit_token (TokenType.GREATER_THAN); + break; + + case '^': + emit_token (TokenType.ALIGN_TOP); + break; + + case 'v': + unichar next_char = get_next_char (); + if (_last_char.isalnum () || _last_char == ' ' + || next_char.isalnum () || next_char == ' ') { + append_char (c); + } else { + emit_token (TokenType.ALIGN_BOTTOM); + } + break; + + case '\'': + look_for_two_or_append (c, TokenType.SINGLE_QUOTE_2); + break; + + case '/': + look_for_two_or_append (c, TokenType.SLASH_2); + break; + + case '_': + look_for_two_or_append (c, TokenType.UNDERSCORE_2); + break; + + case '`': + if (get_next_char () == '`') { + emit_token (TokenType.BACK_QUOTE_2); + _skip = 1; + } else { + append_char (c); + } + break; + + case '\t': + emit_token (TokenType.TAB); + break; + + case ' ': + emit_token (TokenType.SPACE); + break; + + case '\r': + break; + + case '\n': + emit_token (TokenType.EOL); + _line++; + _column = 0; + _last_column = 0; + break; + + default: + append_char (c); + break; + } + } else { + _skip--; + } + _last_char = c; + } + + private void append_char (unichar c) { + _current_string.append_unichar (c); + } + + public virtual int get_line_start_column () { + return 0; + } + + private SourceLocation get_begin () { + return SourceLocation (_last_line, get_line_start_column () + _last_column); + } + + private SourceLocation get_end (int offset = 0) { + return SourceLocation (_line, get_line_start_column () + _column + offset); + } + + private void emit_current_word () throws ParserError { + if (_current_string.len > 0) { + _parser.accept_token (new Token.from_word (_current_string.str, get_begin (), get_end (-1))); + _current_string.erase (0, -1); + + _last_line = _line; + _last_column = _column - 1; + } + } + + private void emit_token (TokenType type) throws ParserError { + emit_current_word (); + + _parser.accept_token (new Token.from_type (type, get_begin (), get_end (_skip))); + + _last_line = _line; + _last_column = _column; + } + + private void look_for_two_or_append (unichar c, TokenType type) throws ParserError { + if (get_next_char () == c) { + emit_token (type); + _skip = 1; + } else { + append_char (c); + } + } + + private void look_for_two (unichar c, TokenType one, TokenType two) throws ParserError { + if (get_next_char (1) == c) { + emit_token (two); + _skip = 1; + } else { + emit_token (one); + } + } + + private void look_for_three (unichar c, TokenType one, TokenType two, TokenType three) + throws ParserError + { + if (get_next_char (1) == c) { + if (get_next_char (2) == c) { + emit_token (three); + _skip = 2; + } else { + emit_token (two); + _skip = 1; + } + } else { + emit_token (one); + } + } + + private void look_for_five (unichar c, TokenType one, TokenType two, TokenType three, + TokenType four, TokenType five) throws ParserError + { + if (get_next_char (1) == c) { + if (get_next_char (2) == c) { + if (get_next_char (3) == c) { + if (get_next_char (4) == c) { + emit_token (five); + _skip = 4; + } else { + emit_token (four); + _skip = 3; + } + } else { + emit_token (three); + _skip = 2; + } + } else { + emit_token (two); + _skip = 1; + } + } else { + emit_token (one); + } + } + + private bool look_for (string str, TokenType type) throws ParserError { + for (int i = 1; i < str.length; i++) { + if (get_next_char (i) != str[i]) { + return false; + } + } + + emit_token (type); + _skip = (int) (str.length - 1); + return true; + } +} diff --git a/libvaladoc/errorreporter.vala b/libvaladoc/errorreporter.vala new file mode 100644 index 000000000..5b9543e62 --- /dev/null +++ b/libvaladoc/errorreporter.vala @@ -0,0 +1,392 @@ +/* errorreporter.vala + * + * Copyright (C) 2008-2011 Florian Brosch + * + * 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: + * Brosch Florian <flo.brosch@gmail.com> + */ + +using Gee; + + +public class Valadoc.ErrorReporter : Object { + private int _warnings = 0; + private int _errors = 0; + + /** + * SGR end tag + */ + private const string ANSI_COLOR_END = "\x1b[0m"; + + /** + * SGR start tag for source location + */ + private string locus_color_start = ""; + + /** + * SGR end tag for source location + */ + private unowned string locus_color_end = ""; + + /** + * SGR start tag for warning titles + */ + private string warning_color_start = ""; + + /** + * SGR end tag for warning titles + */ + private unowned string warning_color_end = ""; + + /** + * SGR start tag for error titles + */ + private string error_color_start = ""; + + /** + * SGR end tag for error titles + */ + private unowned string error_color_end = ""; + + /** + * SGR start tag for note titles + */ + private string note_color_start = ""; + + /** + * SGR end tag for note titles + */ + private unowned string note_color_end = ""; + + /** + * SGR start tag for caret line (^^^) + */ + private string caret_color_start = ""; + + /** + * SGR end tag for caret line (^^^) + */ + private unowned string caret_color_end = ""; + + /** + * SGR start tag for quotes line ('', ``, `') + */ + private string quote_color_start = ""; + + /** + * SGR end tag for quotes line ('', ``, `') + */ + private unowned string quote_color_end = ""; + + + public int warnings_offset { + get; + set; + } + + public int errors_offset { + get; + set; + } + + public unowned GLib.FileStream stream { + get; + set; + } + + public Settings? settings { + get; + set; + } + + public int errors { + get { + return this._errors + errors_offset; + } + } + + public int warnings { + get { + return this._warnings + warnings_offset; + } + } + + + public ErrorReporter (Settings? settings = null) { + this.stream = GLib.stderr; + this.settings = settings; + } + + /** + * Set all colors by string + * + * {{{ + * "error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01" + * }}} + */ + public bool set_colors (string str) { + Regex val_regex; + try { + val_regex = new Regex ("^\\s*[0-9]+(;[0-9]*)*\\s*$"); + } catch (RegexError e) { + assert_not_reached (); + } + + string error_color = null; + string warning_color = null; + string note_color = null; + string caret_color = null; + string locus_color = null; + string quote_color = null; + + string[] fragments = str.split (":"); + foreach (unowned string fragment in fragments) { + string[] eq = fragment.split ("=", 2); + if (eq.length != 2) { + return false; + } + + if (!val_regex.match (eq[1])) { + return false; + } + + + unowned string checked_value = eq[1]._strip (); + switch (eq[0]._strip ()) { + case "error": + error_color = checked_value; + break; + + case "warning": + warning_color = checked_value; + break; + + case "note": + note_color = checked_value; + break; + + case "caret": + caret_color = checked_value; + break; + + case "locus": + locus_color = checked_value; + break; + + case "quote": + quote_color = checked_value; + break; + + default: + return false; + } + } + + if (is_atty (this.stream.fileno ())) { + if (error_color != null) { + this.error_color_start = "\x1b[0" + error_color + "m"; + this.error_color_end = ANSI_COLOR_END; + } + + if (warning_color != null) { + this.warning_color_start = "\x1b[0" + warning_color + "m"; + this.warning_color_end = ANSI_COLOR_END; + } + + if (note_color != null) { + this.note_color_start = "\x1b[0" + note_color + "m"; + this.note_color_end = ANSI_COLOR_END; + } + + if (caret_color != null) { + this.caret_color_start = "\x1b[0" + caret_color + "m"; + this.caret_color_end = ANSI_COLOR_END; + } + + if (locus_color != null) { + this.locus_color_start = "\x1b[0" + locus_color + "m"; + this.locus_color_end = ANSI_COLOR_END; + } + + if (quote_color != null) { + this.quote_color_start = "\x1b[0" + quote_color + "m"; + this.quote_color_end = ANSI_COLOR_END; + } + } + return true; + } + + [CCode (has_target = false)] + private delegate int AttyFunc (int fd); + + private bool is_atty (int fd) { + Module module = Module.open (null, ModuleFlags.BIND_LAZY); + if (module == null) { + return false; + } + + void* _func; + module.symbol ("isatty", out _func); + if (_func == null) { + return false; + } + + AttyFunc? func = (AttyFunc) _func; + return func (fd) == 1; + } + + [PrintfFormat] + private inline void msg (string type, string type_color_start, string type_color_end, string file, long line, long startpos, long endpos, + string errline, string msg_format, va_list args) + { + this.stream.printf ("%s%s:%lu.%lu-%lu.%lu:%s %s%s:%s ", + locus_color_start, file, line, startpos, line, endpos, locus_color_end, + type_color_start, type, type_color_end); + print_highlighted_message (msg_format.vprintf (args)); + this.stream.putc ('\n'); + + if (startpos <= endpos) { + this.stream.printf ("%s\n", errline); + + this.stream.puts (caret_color_start); + for (int i = 0; i < errline.char_count ()+1; i++) { + if (errline[i] == '\t') { + this.stream.printf ("\t"); + } else if (i >= startpos - 1 && i < endpos - 1) { + this.stream.printf ("^"); + } else { + this.stream.printf (" "); + } + } + this.stream.puts (caret_color_end); + this.stream.printf ("\n"); + } + } + + private void print_highlighted_message (string message) { + int start = 0; + int cur = 0; + + while (message[cur] != '\0') { + if (message[cur] == '\'' || message[cur] == '`') { + unowned string end_chars = (message[cur] == '`')? "`'" : "'"; + this.stream.puts (message.substring (start, cur - start)); + start = cur; + cur++; + + while (message[cur] != '\0' && end_chars.index_of_char (message[cur]) < 0) { + cur++; + } + if (message[cur] == '\0') { + this.stream.puts (message.substring (start, cur - start)); + start = cur; + } else { + cur++; + this.stream.printf ("%s%s%s", quote_color_start, message.substring (start, cur - start), quote_color_end); + start = cur; + } + } else { + cur++; + } + } + + this.stream.puts (message.offset (start)); + } + + [PrintfFormat] + public void simple_warning (string? location, string msg_format, ...) { + var args = va_list(); + + if (location != null) { + this.stream.puts (locus_color_start); + this.stream.puts (location); + this.stream.puts (": "); + this.stream.puts (locus_color_end); + } + + this.stream.puts (warning_color_start); + this.stream.puts ("warning: "); + this.stream.puts (warning_color_end); + + print_highlighted_message (msg_format.vprintf (args)); + this.stream.putc ('\n'); + this._warnings++; + } + + [PrintfFormat] + public void simple_error (string? location, string msg_format, ...) { + var args = va_list(); + + if (location != null) { + this.stream.puts (locus_color_start); + this.stream.puts (location); + this.stream.puts (": "); + this.stream.puts (locus_color_end); + this.stream.putc (' '); + } + + this.stream.puts (error_color_start); + this.stream.puts ("error: "); + this.stream.puts (error_color_end); + + print_highlighted_message (msg_format.vprintf (args)); + this.stream.putc ('\n'); + this._errors++; + } + + [PrintfFormat] + public void simple_note (string? location, string msg_format, ...) { + if (_settings == null || _settings.verbose) { + var args = va_list(); + + if (location != null) { + this.stream.puts (locus_color_start); + this.stream.puts (location); + this.stream.puts (": "); + this.stream.puts (locus_color_end); + this.stream.putc (' '); + } + + this.stream.puts (note_color_start); + this.stream.puts ("note: "); + this.stream.puts (note_color_end); + + print_highlighted_message (msg_format.vprintf (args)); + this.stream.putc ('\n'); + this._warnings++; + } + } + + [PrintfFormat] + public void error (string file, long line, long startpos, long endpos, string errline, + string msg_format, ...) + { + var args = va_list(); + this.msg ("error", error_color_start, error_color_end, file, line, startpos, endpos, errline, msg_format, args); + this._errors++; + } + + [PrintfFormat] + public void warning (string file, long line, long startpos, long endpos, string errline, + string msg_format, ...) + { + var args = va_list(); + this.msg ("warning", warning_color_start, warning_color_end, file, line, startpos, endpos, errline, msg_format, args); + this._warnings++; + } +} + diff --git a/libvaladoc/filehelper.vala b/libvaladoc/filehelper.vala new file mode 100644 index 000000000..bc2369b36 --- /dev/null +++ b/libvaladoc/filehelper.vala @@ -0,0 +1,200 @@ +/* filehelper.vala + * + * Copyright (C) 2008-2009 Florian Brosch + * + * 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: + * Brosch Florian <flo.brosch@gmail.com> + */ +using Gee; + + +namespace Valadoc { + [CCode (cprefix = "", cname = "PACKAGE_ICONDIR")] + public extern const string icons_dir; + + /** + * Makes a copy of the file src to dest. + * + * @param src the file to copy + * @param dest the destination path + */ + public bool copy_file (string src, string dest) { + GLib.FileStream fsrc = GLib.FileStream.open (src, "rb"); + if (fsrc == null) { + return false; + } + + GLib.FileStream fdest = GLib.FileStream.open (dest, "wb"); + if (fdest == null) { + return false; + } + + for (int c = fsrc.getc() ; !fsrc.eof() ; c = fsrc.getc()) { + fdest.putc ((char)c); + } + + return true; + } + + /** + * Makes a copy of the directory src to dest. + * + * @param src the directory to copy + * @param dest the destination path + */ + public bool copy_directory (string src, string dest) { + try { + GLib.Dir dir = GLib.Dir.open (src); + for (string? file = dir.read_name (); file != null; file = dir.read_name ()) { + string src_file_path = GLib.Path.build_filename (src, file); + string dest_file_path = GLib.Path.build_filename (dest, file); + if (GLib.FileUtils.test (src_file_path, GLib.FileTest.IS_DIR)) { + GLib.DirUtils.create (dest_file_path, 0755); // mkdir if necessary + if (!copy_directory (src_file_path, dest_file_path)) { // copy directories recursively + return false; + } + } else { + if (!copy_file (src_file_path, dest_file_path)) { + return false; + } + } + } + } + catch (GLib.FileError err) { + return false; + } + return true; + } + + /** + * A recursive directory delete function + * + * @param rpath the directory to remove + */ + public bool remove_directory (string rpath) { + try { + GLib.Dir dir = GLib.Dir.open ( rpath ); + if (dir == null) + return false; + + for (weak string entry = dir.read_name(); entry != null ; entry = dir.read_name()) { + string path = rpath + entry; + + bool is_dir = GLib.FileUtils.test (path, GLib.FileTest.IS_DIR); + if (is_dir == true) { + bool tmp = remove_directory (path); + if (tmp == false) { + return false; + } + } else { + int tmp = GLib.FileUtils.unlink (path); + if (tmp > 0) { + return false; + } + } + } + } catch (GLib.FileError err) { + return false; + } + + return true; + } + + + private inline bool ends_with_dir_separator (string s) { + // --- ported from libvala --- + return Path.is_dir_separator (s.get_char (s.length - 1)); + } + + /** + * Returns canonicalized absolute pathname + * + * @param name the path being checked + * @return a canonicalized absolute pathname + */ + public string realpath (string name) { + // --- ported from libvala --- + + string rpath; + + // start of path component + weak string start; + // end of path component + weak string end; + + if (!Path.is_absolute (name)) { + // relative path + rpath = Environment.get_current_dir (); + + start = end = name; + } else { + // set start after root + start = end = Path.skip_root (name); + + // extract root + rpath = name.substring (0, (int) ((char*) start - (char*) name)); + } + + long root_len = (long) ((char*) Path.skip_root (rpath) - (char*) rpath); + + for (; start.get_char () != 0; start = end) { + // skip sequence of multiple path-separators + while (Path.is_dir_separator (start.get_char ())) { + start = start.next_char (); + } + + // find end of path component + long len = 0; + for (end = start; end.get_char () != 0 && !Path.is_dir_separator (end.get_char ()); end = end.next_char ()) { + len++; + } + + if (len == 0) { + break; + } else if (len == 1 && start.get_char () == '.') { + // do nothing + } else if (len == 2 && start.has_prefix ("..")) { + // back up to previous component, ignore if at root already + if (rpath.length > root_len) { + do { + rpath = rpath.substring (0, rpath.length - 1); + } while (!ends_with_dir_separator (rpath)); + } + } else { + if (!ends_with_dir_separator (rpath)) { + rpath += Path.DIR_SEPARATOR_S; + } + + rpath += start.substring (0, len); + } + } + + if (rpath.length > root_len && ends_with_dir_separator (rpath)) { + rpath = rpath.substring (0, rpath.length - 1); + } + + if (Path.DIR_SEPARATOR != '/') { + // don't use backslashes internally, + // to avoid problems in #include directives + string[] components = rpath.split ("\\"); + rpath = string.joinv ("/", components); + } + + return rpath; + } +} + diff --git a/libvaladoc/gtkdocmarkupwriter.vala b/libvaladoc/gtkdocmarkupwriter.vala new file mode 100644 index 000000000..1e7276789 --- /dev/null +++ b/libvaladoc/gtkdocmarkupwriter.vala @@ -0,0 +1,73 @@ +/* gtkdocmarkupwriter.vala + * + * Copyright (C) 2012 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + + +public class Valadoc.GtkDocMarkupWriter : Valadoc.MarkupWriter { + private unowned StringBuilder builder; + + public void reset () { + last_was_tag = true; + current_column = 0; + builder.erase (); + indent = -1; + } + + public unowned string content { + get { return builder.str; } + } + + public GtkDocMarkupWriter () { + StringBuilder builder = new StringBuilder (); + base ((str) => { builder.append (str); }, false); + this.builder = builder; + } + + protected override bool inline_element (string name) { + return name != "para" + && name != "programlisting" + && name != "table" + && name != "example" + && name != "figure" + && name != "tr" + && name != "td" + && name != "mediaobject" + && name != "imageobject" + && name != "textobject" + && name != "listitem" + && name != "orderedlist" + && name != "itemizedlist" + && name != "title"; + } + + protected override bool content_inline_element (string name) { + return name == "para" + || name == "programlisting" + || name == "emphasis" + || name == "blockquote" + || name == "ulink" + || name == "listitem" + || name == "title"; + } +} + + diff --git a/libvaladoc/gtkdocrenderer.vala b/libvaladoc/gtkdocrenderer.vala new file mode 100644 index 000000000..877d7c5c9 --- /dev/null +++ b/libvaladoc/gtkdocrenderer.vala @@ -0,0 +1,503 @@ +/* gtkdocrenderer.vala + * + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using GLib; +using Valadoc.Content; + +public class Valadoc.GtkdocRenderer : ContentRenderer { + private GtkDocMarkupWriter writer = new GtkDocMarkupWriter (); + protected Settings settings; + private bool separated; + + private string? get_cname (Api.Item item) { + if (item is Api.Method) { + return ((Api.Method)item).get_cname (); + } else if (item is Api.FormalParameter) { + return ((Api.FormalParameter)item).name; + } else if (item is Api.Constant) { + return ((Api.Constant)item).get_cname (); + } else if (item is Api.Property) { + return ((Api.Property)item).get_cname (); + } else if (item is Api.Signal) { + var name = ((Api.Signal)item).get_cname (); + return name.replace ("_", "-"); + } else if (item is Api.Class) { + return ((Api.Class)item).get_cname (); + } else if (item is Api.Struct) { + return ((Api.Struct)item).get_cname (); + } else if (item is Api.Interface) { + return ((Api.Interface)item).get_cname (); + } else if (item is Api.ErrorDomain) { + return ((Api.ErrorDomain)item).get_cname (); + } else if (item is Api.ErrorCode) { + return ((Api.ErrorCode)item).get_cname (); + } else if (item is Api.Delegate) { + return ((Api.Delegate)item).get_cname (); + } else if (item is Api.Enum) { + return ((Api.Enum)item).get_cname (); + } else if (item is Api.EnumValue) { + return ((Api.EnumValue)item).get_cname (); + } + + return null; + } + + public void write_docbook_link (Api.Item item) { + writer.set_wrap (false); + + if (item is Api.Method) { + writer.start_tag ("function") + .text (((Api.Method)item).get_cname ()) + .end_tag ("function"); + } else if (item is Api.FormalParameter) { + writer.start_tag ("parameter"). + text (((Api.FormalParameter)item).name ?? "...") + .end_tag ("parameter"); + } else if (item is Api.Constant) { + writer.start_tag ("constant").text (((Api.Constant)item) + .get_cname ()) + .end_tag ("constant"); + } else if (item is Api.Property) { + // TODO: use docbook-tags instead + writer.text ("#").text (get_cname(item.parent)) + .text (":") + .text (((Api.Property)item) + .get_cname ().replace ("_", "-")); + } else if (item is Api.Signal) { + // TODO: use docbook-tags instead + writer.text ("#").text (get_cname(item.parent)) + .text ("::") + .text (((Api.Signal)item).get_cname ().replace ("_", "-")); + } else if (item is Api.Namespace) { + writer.text (((Api.Namespace) item).get_full_name ()); + } else { + writer.start_tag ("type") + .text (get_cname (item)) + .end_tag ("type"); + } + + writer.set_wrap (true); + } + + public GtkdocRenderer () { + } + + public override void render (ContentElement element) { + reset (); + element.accept (this); + } + + public void render_symbol (Content.Comment? documentation) { + render (documentation); + + append_exceptions (documentation.find_taglets (null, typeof(Taglets.Throws))); + append_see (documentation.find_taglets (null, typeof(Taglets.See))); + append_since (documentation.find_taglets (null, typeof(Taglets.Since))); + } + + public override void render_children (ContentElement element) { + reset (); + element.accept_children (this); + } + + private void reset () { + separated = false; + writer.reset (); + } + + public unowned string content { + get { + if (writer.content.has_prefix ("\n")) { + return writer.content.next_char (); + } + + return writer.content; + } + } + + public override void visit_comment (Comment element) { + element.accept_children (this); + } + + public override void visit_embedded (Embedded element) { + writer.start_tag ("figure"); + if (element.caption != null) { + writer.start_tag ("title") + .text (element.caption) + .end_tag ("title"); + } + + writer.start_tag ("mediaobject"); + + writer.start_tag ("imageobject") + .simple_tag ("imagedata", {"fileref", element.url}) + .end_tag ("imageobject"); + + if (element.caption != null) { + writer.start_tag ("textobject") + .start_tag ("phrase") + .text (element.caption) + .end_tag ("phrase") + .end_tag ("textobject"); + } + + writer.end_tag ("mediaobject"); + writer.end_tag ("figure"); + } + + public override void visit_headline (Headline element) { + assert_not_reached (); + } + + public override void visit_wiki_link (WikiLink element) { + // wiki pages are not supported by gir + if (element.content.size > 0) { + element.accept_children (this); + } else { + write_string (element.name); + } + } + + public override void visit_link (Link element) { + writer.start_tag ("ulink", {"url", element.url}); + element.accept_children (this); + writer.end_tag ("ulink"); + } + + public override void visit_symbol_link (SymbolLink element) { + if (element.content.size > 0) { + writer.text ("\""); + element.accept_children (this); + writer.text ("\" ("); + visit_symbol_link (element); + writer.text (")"); + } else { + visit_symbol_link (element); + } + } + + public void write_symbol_link (SymbolLink element) { + if (element.symbol == null) { + writer.text (element.given_symbol_name); + } else { + write_docbook_link (element.symbol); + } + } + + public override void visit_list (Content.List element) { + string tag = "orderedlist"; + switch (element.bullet) { + case Content.List.Bullet.NONE: + writer.start_tag ("itemizedlist", {"mark", "none"}); + tag = "itemizedlist"; + break; + + case Content.List.Bullet.UNORDERED: + writer.start_tag ("itemizedlist"); + tag = "itemizedlist"; + break; + + case Content.List.Bullet.ORDERED: + writer.start_tag ("orderedlist"); + break; + + case Content.List.Bullet.ORDERED_NUMBER: + writer.start_tag ("orderedlist", {"numeration", "arabic"}); + break; + + case Content.List.Bullet.ORDERED_LOWER_CASE_ALPHA: + writer.start_tag ("orderedlist", {"numeration", "loweralpha"}); + break; + + case Content.List.Bullet.ORDERED_UPPER_CASE_ALPHA: + writer.start_tag ("orderedlist", {"numeration", "upperalpha"}); + break; + + case Content.List.Bullet.ORDERED_LOWER_CASE_ROMAN: + writer.start_tag ("orderedlist", {"numeration", "lowerroman"}); + break; + + case Content.List.Bullet.ORDERED_UPPER_CASE_ROMAN: + writer.start_tag ("orderedlist", {"numeration", "upperroman"}); + break; + + default: + assert_not_reached (); + } + + element.accept_children (this); + + writer.end_tag (tag); + } + + public override void visit_list_item (ListItem element) { + writer.start_tag ("listitem"); + element.accept_children (this); + writer.end_tag ("listitem"); + } + + public override void visit_page (Page element) { + element.accept_children (this); + } + + public override void visit_paragraph (Paragraph element) { + writer.start_tag ("para"); + element.accept_children (this); + writer.end_tag ("para"); + } + + public override void visit_warning (Warning element) { + writer.start_tag ("warning"); + element.accept_children (this); + writer.end_tag ("warning"); + } + + public override void visit_note (Note element) { + writer.start_tag ("note"); + element.accept_children (this); + writer.end_tag ("note"); + } + + public override void visit_run (Run element) { + string? tag = null; + + switch (element.style) { + case Run.Style.BOLD: + writer.start_tag ("emphasis", {"role", "bold"}); + tag = "emphasis"; + break; + + case Run.Style.ITALIC: + writer.start_tag ("emphasis"); + tag = "emphasis"; + break; + + case Run.Style.UNDERLINED: + writer.start_tag ("emphasis", {"role", "underline"}); + tag = "emphasis"; + break; + + case Run.Style.MONOSPACED: + writer.start_tag ("blockquote"); + tag = "blockquote"; + break; + } + + element.accept_children (this); + + if (tag != null) { + writer.end_tag (tag); + } + } + + public override void visit_source_code (SourceCode element) { + writer.start_tag ("example") + .start_tag ("programlisting"); + writer.text (element.code); + writer.end_tag ("programlisting") + .end_tag ("example"); + } + + public override void visit_table (Table element) { + writer.start_tag ("table", {"align", "center"}); + element.accept_children (this); + writer.end_tag ("table"); + } + + public override void visit_table_cell (TableCell element) { + writer.start_tag ("td", {"colspan", element.colspan.to_string (), "rowspan", element.rowspan.to_string ()}); + element.accept_children (this); + writer.end_tag ("td"); + } + + public override void visit_table_row (TableRow element) { + writer.start_tag ("tr"); + element.accept_children (this); + writer.end_tag ("tr"); + } + + public override void visit_text (Text element) { + write_string (element.content); + } + + private void write_string (string content) { + unichar chr = content[0]; + long lpos = 0; + int i = 0; + + for (i = 0; chr != '\0' ; i++, chr = content[i]) { + switch (chr) { + case '<': + writer.raw_text (content.substring (lpos, i-lpos)); + writer.raw_text ("<"); + lpos = i+1; + break; + + case '>': + writer.raw_text (content.substring (lpos, i-lpos)); + writer.raw_text (">"); + lpos = i+1; + break; + + case '"': + writer.raw_text (content.substring (lpos, i-lpos)); + writer.raw_text ("""); + lpos = i+1; + break; + + case '\'': + writer.raw_text (content.substring (lpos, i-lpos)); + writer.raw_text ("'"); + lpos = i+1; + break; + + case '&': + writer.raw_text (content.substring (lpos, i-lpos)); + writer.raw_text ("&"); + lpos = i+1; + break; + + case '#': + writer.raw_text (content.substring (lpos, i-lpos)); + writer.raw_text ("#"); + lpos = i+1; + break; + + case '%': + writer.raw_text (content.substring (lpos, i-lpos)); + writer.raw_text ("%"); + lpos = i+1; + break; + + case '@': + writer.raw_text (content.substring (lpos, i-lpos)); + writer.raw_text ("@"); + lpos = i+1; + break; + + case '(': + writer.raw_text (content.substring (lpos, i-lpos)); + writer.raw_text ("("); + lpos = i+1; + break; + + case ')': + writer.raw_text (content.substring (lpos, i-lpos)); + writer.raw_text (")"); + lpos = i+1; + break; + + case '\n': + writer.raw_text (content.substring (lpos, i-lpos)); + writer.simple_tag ("br"); + lpos = i+1; + break; + } + } + + writer.raw_text (content.substring (lpos, i-lpos)); + } + + public void append_since (Gee.List<Content.Taglet> taglets) { + foreach (Content.Taglet _taglet in taglets) { + Taglets.Since taglet = _taglet as Taglets.Since; + if (taglet == null || taglet.version == null) { + // ignore unexpected taglets + continue ; + } + + if (separated == false) { + writer.text ("\n"); + } + + writer.set_wrap (false); + writer.text ("\nSince: ") + .text (taglet.version); + writer.set_wrap (true); + separated = true; + + // ignore multiple occurrences + return ; + } + } + + public void append_see (Gee.List<Content.Taglet> taglets) { + bool first = true; + foreach (Content.Taglet _taglet in taglets) { + Taglets.See taglet = _taglet as Taglets.See; + if (taglet == null || taglet.symbol == null) { + // ignore unexpected taglets + continue ; + } + + if (first) { + writer.start_tag ("para").text ("See also: "); + } else { + writer.text (", "); + } + + write_docbook_link (taglet.symbol); + first = false; + } + + if (first == false) { + writer.end_tag ("para"); + } + } + + public void append_exceptions (Gee.List<Content.Taglet> taglets) { + bool first = true; + foreach (Content.Taglet _taglet in taglets) { + Taglets.Throws taglet = _taglet as Taglets.Throws; + if (taglet == null || taglet.error_domain == null) { + // ignore unexpected taglets + continue ; + } + + if (first) { + writer.start_tag ("para") + .text ("This function may throw:") + .end_tag ("para"); + writer.start_tag ("table"); + } + + writer.start_tag ("tr"); + + writer.start_tag ("td"); + write_docbook_link (taglet.error_domain); + writer.end_tag ("td"); + + writer.start_tag ("td"); + taglet.accept_children (this); + writer.end_tag ("td"); + + writer.end_tag ("tr"); + + first = false; + } + + if (first == false) { + writer.end_tag ("table"); + } + } +} + diff --git a/libvaladoc/highlighter/codescanner.vala b/libvaladoc/highlighter/codescanner.vala new file mode 100644 index 000000000..8b15ee755 --- /dev/null +++ b/libvaladoc/highlighter/codescanner.vala @@ -0,0 +1,572 @@ +/* codescanner.vala + * + * Copyright (C) 2015 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using GLib; + + +/** + * A cheap scanner used to highlight C and Vala source code. + */ +public class Valadoc.Highlighter.CodeScanner : Object, Scanner { + private Gee.HashMap<string, CodeTokenType?> keywords; + private bool enable_string_templates; + private bool enabel_verbatim_string; + private bool enable_preprocessor_define; + private bool enable_preprocessor_include; + private bool enable_keyword_escape; + + + private Queue<CodeToken> token_queue = new Queue<CodeToken> (); + private unowned string content; + private unowned string pos; + + + public CodeScanner (string content, bool enable_string_templates, bool enabel_verbatim_string, + bool enable_preprocessor_define, bool enable_preprocessor_include, bool enable_keyword_escape, + Gee.HashMap<string, CodeTokenType?> keywords) + { + this.content = content; + this.pos = content; + + this.enable_string_templates = enable_string_templates; + this.enabel_verbatim_string = enabel_verbatim_string; + this.enable_preprocessor_define = enable_preprocessor_define; + this.enable_preprocessor_include = enable_preprocessor_include; + this.enable_keyword_escape = enable_keyword_escape; + + this.keywords = keywords; + } + + public CodeToken next () { + if (!token_queue.is_empty ()) { + return token_queue.pop_head (); + } + + + unowned string start; + + for (start = pos; pos[0] != '\0'; pos = pos.next_char ()) { + if (((char*) pos) == ((char*) content) || pos[0] == '\n') { + unowned string line_start = pos; + + while (pos[0] == ' ' || pos[0] == '\t' || pos[0] == '\n') { + pos = pos.offset (1); + } + + if (pos[0] == '\0') { + break; + } else if (enable_preprocessor_include && pos.has_prefix ("#include")) { + unowned string end = pos; + if (queue_c_include ()) { + return dispatch (start, end); + } else { + pos = line_start; + continue; + } + } else if (pos.has_prefix ("#if") || pos.has_prefix ("#else") || pos.has_prefix ("#elif") || pos.has_prefix ("#endif") + || (enable_preprocessor_define && (pos.has_prefix ("#defined") || pos.has_prefix ("#ifdef")))) { + + unowned string end = pos; + queue_until ('\n', CodeTokenType.PREPROCESSOR); + return dispatch (start, end); + } + } + + if (pos[0] == '\'') { + unowned string end = pos; + queue_string_literal ("\'"); + return dispatch (start, end); + } + + if (pos[0] == '"' || (enable_string_templates && pos[0] == '@' && pos[1] == '"')) { + unowned string end = pos; + if (enabel_verbatim_string && (pos.has_prefix ("\"\"\"") || (enable_string_templates && pos.has_prefix ("@\"\"\"")))) { + queue_string_literal ("\"\"\""); + } else { + queue_string_literal ("\""); + } + return dispatch (start, end); + } + + if (pos[0] >= '0' && pos[0] <= '9') { + unowned string end = pos; + queue_numeric_literal (); + return dispatch (start, end); + } + + if (pos.has_prefix ("/*")) { + unowned string end = pos; + queue_multiline_comment (); + return dispatch (start, end); + } + + if (pos.has_prefix ("//")) { + unowned string end = pos; + queue_until ('\n', CodeTokenType.COMMENT); + return dispatch (start, end); + } + + if ((((char*) pos) == ((char*) content) || !isidstartchar (pos[-1])) && isidstartchar (pos[0])) { + unowned string end = pos; + if (queue_keyword ()) { + return dispatch (start, end); + } else { + continue; + } + } + } + + token_queue.push_tail (new CodeToken (CodeTokenType.EOF, "")); + return dispatch (start, pos); + } + + private bool queue_c_include () { + unowned string include_start = pos; + unowned string start = pos; + pos = pos.offset (8); + + while (pos[0] == ' ' || pos[0] == '\t') { + pos = pos.offset (1); + } + + char? end_char = null; + if (pos[0] == '"') { + end_char = '"'; + } else if (pos[0] == '<') { + end_char = '>'; + } + + if (end_char != null) { + queue_token (start, pos, CodeTokenType.PREPROCESSOR); + + unowned string literal_start = pos; + pos = pos.offset (1); + + while (pos[0] != end_char && pos[0] != '\n' && pos[0] != '\0') { + pos = pos.offset (1); + } + + if (pos[0] == end_char) { + pos = pos.offset (1); + + queue_token (literal_start, pos, CodeTokenType.LITERAL); + start = pos; + } else { + pos = include_start; + token_queue.clear (); + return false; + } + } + + while (pos[0] == ' ' || pos[0] == '\t') { + pos = pos.offset (1); + } + + if (pos[0] == '\n' || pos[0] == '\0') { + queue_token (start, pos, CodeTokenType.PREPROCESSOR); + return true; + } else { + pos = include_start; + token_queue.clear (); + return false; + } + } + + private bool queue_keyword () { + unowned string start = pos; + if (pos[0] == '@') { + pos = pos.offset (1); + } + while (isidchar (pos[0])) { + pos = pos.offset (1); + } + + long length = start.pointer_to_offset (pos); + string word = start.substring (0, length); + CodeTokenType? token_type = keywords.get (word); + if (token_type == null) { + pos = start; + return false; + } + + token_queue.push_tail (new CodeToken (token_type, word)); + return true; + } + + private void queue_multiline_comment () { + unowned string start = pos; + pos = pos.offset (2); + + while (!(pos[0] == '*' && pos[1] == '/') && pos[0] != '\0') { + pos = pos.offset (1); + } + + if (pos[0] != '\0') { + pos = pos.offset (2); + } + + queue_token (start, pos, CodeTokenType.COMMENT); + } + + private void queue_until (char end_char, CodeTokenType token_type) { + unowned string start = pos; + pos = pos.offset (1); + + while (pos[0] != end_char && pos[0] != '\0') { + pos = pos.offset (1); + } + + if (pos[0] != '\0' && pos[0] != '\n') { + pos = pos.offset (1); + } + + queue_token (start, pos, token_type); + } + + private void queue_string_literal (string end_chars) { + unowned string start = pos; + bool is_template = false; + + if (pos[0] == '@') { + pos = pos.offset (end_chars.length + 1); + is_template = true; + } else { + pos = pos.offset (end_chars.length); + } + + while (!pos.has_prefix (end_chars) && pos[0] != '\0') { + long skip = 0; + + if ((pos[0] == '%' && has_printf_format_prefix (out skip)) + || (pos[0] == '\\' && has_escape_prefix (out skip)) + || (is_template && pos[0] == '$' && has_template_literal_prefix (out skip))) + { + queue_token (start, pos, CodeTokenType.LITERAL); + + unowned string sub_start = pos; + pos = pos.offset (skip); + queue_token (sub_start, pos, CodeTokenType.ESCAPE); + start = pos; + } else { + pos = pos.offset (1); + } + } + + if (pos[0] != '\0') { + pos = pos.offset (end_chars.length); + } + + queue_token (start, pos, CodeTokenType.LITERAL); + } + + private bool has_template_literal_prefix (out long skip) { + if (isidchar (pos[1])) { + skip = 1; + while (isidchar (pos[skip])) { + skip++; + } + return true; + } + + if (pos[1] == '(') { + int level = 1; + skip = 2; + + while (level > 0) { + switch (pos[skip]) { + case '(': + level++; + break; + case ')': + level--; + break; + case '\0': + skip = 0; + return false; + } + skip++; + } + return true; + } + + skip = 0; + return false; + } + + private bool has_escape_prefix (out long skip) { + switch (pos[1]) { + case 'a': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + case '\\': + case '\'': + case '\"': + case '?': + skip = 2; + return true; + + case 'x': + if (pos[2].isxdigit ()) { + for (skip = 2; pos[skip].isxdigit (); skip++) { + skip++; + } + + skip++; + return true; + } + + skip = 0; + return false; + + default: + if (pos[1].isdigit ()) { + skip = 2; + + if (pos[2].isdigit ()) { + skip++; + + if (pos[3].isdigit ()) { + skip++; + } + } + + return true; + } + + skip = 0; + return false; + } + } + + private bool has_printf_format_prefix (out long skip) { + // %[flag][min width][precision][length modifier][conversion specifier] + unowned string pos = this.pos; + unowned string start = pos; + + // '%' + pos = pos.offset (1); + + if (pos[0] == '%') { + pos = pos.offset (1); + skip = 2; + return true; + } + + + // flags: + while ("#0+- ".index_of_char (pos[0]) > 0) { + pos = pos.offset (1); + } + + // min width: + while (pos[0].isdigit ()) { + pos = pos.offset (1); + } + + // precision + if (pos[0] == '.' && pos[1].isdigit ()) { + pos = pos.offset (2); + while (pos[0].isdigit ()) { + pos = pos.offset (1); + } + } + + // length: + switch (pos[0]) { + case 'h': + pos = pos.offset (1); + if (pos[0] == 'h') { + pos = pos.offset (1); + } + break; + + case 'l': + pos = pos.offset (1); + if (pos[0] == 'l') { + pos = pos.offset (1); + } + break; + + case 'j': + case 'z': + case 't': + case 'L': + pos = pos.offset (1); + break; + } + + // conversion specifier: + switch (pos[0]) { + case 'd': + case 'i': + case 'u': + case 'o': + case 'x': + case 'X': + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + case 'c': + case 's': + case 'p': + case 'n': + pos = pos.offset (1); + break; + + default: + skip = 0; + return false; + } + + skip = start.pointer_to_offset (pos); + return true; + } + + private enum NumericType { + INTEGER, + REAL, + NONE + } + + // based on libvala + private void queue_numeric_literal () { + NumericType numeric_type = NumericType.INTEGER; + unowned string start = pos; + + + // integer part + if (pos[0] == '0' && pos[1] == 'x' && pos[2].isxdigit ()) { + // hexadecimal integer literal + pos = pos.offset (2); + while (pos[0].isxdigit ()) { + pos = pos.offset (1); + } + } else { + // decimal number + while (pos[0].isdigit ()) { + pos = pos.offset (1); + } + } + + + // fractional part + if (pos[0] == '.' && pos[1].isdigit ()) { + numeric_type = NumericType.REAL; + pos = pos.offset (1); + while (pos[0].isdigit ()) { + pos = pos.offset (1); + } + } + + + // exponent part + if (pos[0] == 'e' || pos[0] == 'E') { + numeric_type = NumericType.REAL; + pos = pos.offset (1); + if (pos[0] == '+' || pos[0] == '-') { + pos = pos.offset (1); + } + while (pos[0].isdigit ()) { + pos = pos.offset (1); + } + } + + + // type suffix + switch (pos[0]) { + case 'l': + case 'L': + if (numeric_type == NumericType.INTEGER) { + pos = pos.offset (1); + if (pos[0] == 'l' || pos[0] == 'L') { + pos = pos.offset (1); + } + } + break; + + case 'u': + case 'U': + if (numeric_type == NumericType.INTEGER) { + pos = pos.offset (1); + if (pos[0] == 'l' || pos[0] == 'L') { + pos = pos.offset (1); + if (pos[0] == 'l' || pos[0] == 'L') { + pos = pos.offset (1); + } + } + } + break; + + case 'f': + case 'F': + case 'd': + case 'D': + numeric_type = NumericType.REAL; + pos = pos.offset (1); + break; + } + + if (pos[0].isalnum ()) { + numeric_type = NumericType.NONE; + } + + queue_token (start, pos, (numeric_type != NumericType.NONE) + ? CodeTokenType.LITERAL + : CodeTokenType.PLAIN); + } + + private CodeToken dispatch (string start, string end) { + assert (token_queue.is_empty () == false); + + if (((char*) start) == ((char*) end)) { + return token_queue.pop_head (); + } + + long length = start.pointer_to_offset (end); + string content = start.substring (0, length); + return new CodeToken (CodeTokenType.PLAIN, content); + } + + private void queue_token (string start, string end, CodeTokenType token_type) { + long length = start.pointer_to_offset (end); + string content = start.substring (0, length); + token_queue.push_tail (new CodeToken (token_type, content)); + } + + private inline bool isidchar (char c) { + return c.isalnum () || c == '_'; + } + + private inline bool isidstartchar (char c) { + return c.isalnum () || c == '_' || (c == '@' && enable_keyword_escape); + } +} + diff --git a/libvaladoc/highlighter/codetoken.vala b/libvaladoc/highlighter/codetoken.vala new file mode 100644 index 000000000..1a02195ea --- /dev/null +++ b/libvaladoc/highlighter/codetoken.vala @@ -0,0 +1,58 @@ +/* codetoken.vala + * + * Copyright (C) 2015 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +public class Valadoc.Highlighter.CodeToken { + public CodeTokenType token_type { get; private set; } + public string content { get; private set;} + + public CodeToken (CodeTokenType type, string content) { + this.token_type = type; + this.content = content; + } +} + + +public enum Valadoc.Highlighter.CodeTokenType { + XML_ESCAPE, + XML_ELEMENT, + XML_ATTRIBUTE, + XML_ATTRIBUTE_VALUE, + XML_COMMENT, + XML_CDATA, + + PREPROCESSOR, + COMMENT, + KEYWORD, + LITERAL, + ESCAPE, + PLAIN, + TYPE, + EOF; + + public unowned string to_string () { + EnumClass enumc = (EnumClass) typeof (CodeTokenType).class_ref (); + unowned EnumValue? eval = enumc.get_value (this); + return_val_if_fail (eval != null, null); + return eval.value_nick; + } +} diff --git a/libvaladoc/highlighter/highlighter.vala b/libvaladoc/highlighter/highlighter.vala new file mode 100644 index 000000000..3c2578033 --- /dev/null +++ b/libvaladoc/highlighter/highlighter.vala @@ -0,0 +1,366 @@ +/* codehighlighter.vala + * + * Copyright (C) 2015 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using GLib; +using Valadoc.Content; + + +public class Valadoc.Highlighter.Highlighter : Object { + private Gee.HashMap<string, CodeTokenType?> vala_keywords; + private Gee.HashMap<string, CodeTokenType?> c_keywords; + + + /** + * Used to highlight vala source code. + */ + public Run highlight_vala (string source_code) { + if (vala_keywords == null) { + vala_keywords = new Gee.HashMap<string, CodeTokenType?> (); + + // ** Types: ** + vala_keywords.set ("string", CodeTokenType.TYPE); + vala_keywords.set ("bool", CodeTokenType.TYPE); + vala_keywords.set ("void", CodeTokenType.TYPE); + + vala_keywords.set ("double", CodeTokenType.TYPE); + vala_keywords.set ("float", CodeTokenType.TYPE); + + vala_keywords.set ("char", CodeTokenType.TYPE); + vala_keywords.set ("uchar", CodeTokenType.TYPE); + vala_keywords.set ("unichar", CodeTokenType.TYPE); + + vala_keywords.set ("short", CodeTokenType.TYPE); + vala_keywords.set ("ushort", CodeTokenType.TYPE); + + vala_keywords.set ("long", CodeTokenType.TYPE); + vala_keywords.set ("ulong", CodeTokenType.TYPE); + + vala_keywords.set ("size_t", CodeTokenType.TYPE); + vala_keywords.set ("ssize_t", CodeTokenType.TYPE); + + vala_keywords.set ("int", CodeTokenType.TYPE); + vala_keywords.set ("int8", CodeTokenType.TYPE); + vala_keywords.set ("int16", CodeTokenType.TYPE); + vala_keywords.set ("int32", CodeTokenType.TYPE); + vala_keywords.set ("int64", CodeTokenType.TYPE); + + vala_keywords.set ("uint", CodeTokenType.TYPE); + vala_keywords.set ("uint8", CodeTokenType.TYPE); + vala_keywords.set ("uint16", CodeTokenType.TYPE); + vala_keywords.set ("uint32", CodeTokenType.TYPE); + vala_keywords.set ("uint64", CodeTokenType.TYPE); + + + // ** Literals: ** + vala_keywords.set ("null", CodeTokenType.LITERAL); + vala_keywords.set ("true", CodeTokenType.LITERAL); + vala_keywords.set ("false", CodeTokenType.LITERAL); + + + // ** Keywords: ** + vala_keywords.set ("return", CodeTokenType.KEYWORD); + vala_keywords.set ("lock", CodeTokenType.KEYWORD); + vala_keywords.set ("var", CodeTokenType.KEYWORD); + vala_keywords.set ("yield", CodeTokenType.KEYWORD); + vala_keywords.set ("global", CodeTokenType.KEYWORD); + vala_keywords.set ("construct", CodeTokenType.KEYWORD); + + vala_keywords.set ("value", CodeTokenType.KEYWORD); + vala_keywords.set ("get", CodeTokenType.KEYWORD); + vala_keywords.set ("set", CodeTokenType.KEYWORD); + + vala_keywords.set ("owned", CodeTokenType.KEYWORD); + vala_keywords.set ("unowned", CodeTokenType.KEYWORD); + vala_keywords.set ("const", CodeTokenType.KEYWORD); + vala_keywords.set ("weak", CodeTokenType.KEYWORD); + vala_keywords.set ("dynamic", CodeTokenType.KEYWORD); + + vala_keywords.set ("out", CodeTokenType.KEYWORD); + vala_keywords.set ("ref", CodeTokenType.KEYWORD); + + vala_keywords.set ("break", CodeTokenType.KEYWORD); + vala_keywords.set ("continue", CodeTokenType.KEYWORD); + vala_keywords.set ("return", CodeTokenType.KEYWORD); + + vala_keywords.set ("if", CodeTokenType.KEYWORD); + vala_keywords.set ("else", CodeTokenType.KEYWORD); + vala_keywords.set ("switch", CodeTokenType.KEYWORD); + vala_keywords.set ("case", CodeTokenType.KEYWORD); + vala_keywords.set ("default", CodeTokenType.KEYWORD); + + vala_keywords.set ("do", CodeTokenType.KEYWORD); + vala_keywords.set ("while", CodeTokenType.KEYWORD); + vala_keywords.set ("for", CodeTokenType.KEYWORD); + vala_keywords.set ("foreach", CodeTokenType.KEYWORD); + vala_keywords.set ("in", CodeTokenType.KEYWORD); + + vala_keywords.set ("try", CodeTokenType.KEYWORD); + vala_keywords.set ("catch", CodeTokenType.KEYWORD); + vala_keywords.set ("finally", CodeTokenType.KEYWORD); + vala_keywords.set ("throw", CodeTokenType.KEYWORD); + + vala_keywords.set ("class", CodeTokenType.KEYWORD); + vala_keywords.set ("interface", CodeTokenType.KEYWORD); + vala_keywords.set ("struct", CodeTokenType.KEYWORD); + vala_keywords.set ("enum", CodeTokenType.KEYWORD); + vala_keywords.set ("delegate", CodeTokenType.KEYWORD); + vala_keywords.set ("errordomain", CodeTokenType.KEYWORD); + + vala_keywords.set ("abstract", CodeTokenType.KEYWORD); + vala_keywords.set ("virtual", CodeTokenType.KEYWORD); + vala_keywords.set ("override", CodeTokenType.KEYWORD); + vala_keywords.set ("signal", CodeTokenType.KEYWORD); + vala_keywords.set ("extern", CodeTokenType.KEYWORD); + vala_keywords.set ("static", CodeTokenType.KEYWORD); + vala_keywords.set ("async", CodeTokenType.KEYWORD); + vala_keywords.set ("inline", CodeTokenType.KEYWORD); + vala_keywords.set ("new", CodeTokenType.KEYWORD); + + vala_keywords.set ("public", CodeTokenType.KEYWORD); + vala_keywords.set ("private", CodeTokenType.KEYWORD); + vala_keywords.set ("protected", CodeTokenType.KEYWORD); + vala_keywords.set ("internal", CodeTokenType.KEYWORD); + + vala_keywords.set ("throws", CodeTokenType.KEYWORD); + vala_keywords.set ("requires", CodeTokenType.KEYWORD); + vala_keywords.set ("ensures", CodeTokenType.KEYWORD); + vala_keywords.set ("assert", CodeTokenType.KEYWORD); + + vala_keywords.set ("namespace", CodeTokenType.KEYWORD); + vala_keywords.set ("using", CodeTokenType.KEYWORD); + + vala_keywords.set ("as", CodeTokenType.KEYWORD); + vala_keywords.set ("is", CodeTokenType.KEYWORD); + vala_keywords.set ("in", CodeTokenType.KEYWORD); + vala_keywords.set ("new", CodeTokenType.KEYWORD); + vala_keywords.set ("delete", CodeTokenType.KEYWORD); + vala_keywords.set ("sizeof", CodeTokenType.KEYWORD); + vala_keywords.set ("typeof", CodeTokenType.KEYWORD); + + vala_keywords.set ("this", CodeTokenType.KEYWORD); + vala_keywords.set ("base", CodeTokenType.KEYWORD); + } + + bool enable_string_templates = true; + bool enable_preprocessor_define = false; + bool enable_preprocessor_include = false; + bool enable_keyword_escape = true; + bool enabel_verbatim_string = true; + + CodeScanner scanner = new CodeScanner (source_code, enable_string_templates, enabel_verbatim_string, + enable_preprocessor_define, enable_preprocessor_include, enable_keyword_escape, + vala_keywords); + + return highlight_code (scanner); + } + + /** + * Used to highlight C source code. + */ + public Run highlight_c (string source_code) { + if (c_keywords == null) { + c_keywords = new Gee.HashMap<string, CodeTokenType?> (); + + // ** Types: ** + c_keywords.set ("auto", CodeTokenType.TYPE); + c_keywords.set ("char", CodeTokenType.TYPE); + c_keywords.set ("const", CodeTokenType.TYPE); + c_keywords.set ("double", CodeTokenType.TYPE); + c_keywords.set ("extern", CodeTokenType.TYPE); + c_keywords.set ("int", CodeTokenType.TYPE); + c_keywords.set ("float", CodeTokenType.TYPE); + c_keywords.set ("long", CodeTokenType.TYPE); + c_keywords.set ("register", CodeTokenType.TYPE); + c_keywords.set ("short", CodeTokenType.TYPE); + c_keywords.set ("signed", CodeTokenType.TYPE); + c_keywords.set ("static", CodeTokenType.TYPE); + c_keywords.set ("unsigned", CodeTokenType.TYPE); + c_keywords.set ("void", CodeTokenType.TYPE); + c_keywords.set ("volatile", CodeTokenType.TYPE); + + c_keywords.set ("gboolean", CodeTokenType.TYPE); + c_keywords.set ("gpointer", CodeTokenType.TYPE); + c_keywords.set ("gconstpointer", CodeTokenType.TYPE); + c_keywords.set ("gchar", CodeTokenType.TYPE); + c_keywords.set ("guchar", CodeTokenType.TYPE); + c_keywords.set ("gint", CodeTokenType.TYPE); + c_keywords.set ("guint", CodeTokenType.TYPE); + c_keywords.set ("gshort", CodeTokenType.TYPE); + c_keywords.set ("gushort", CodeTokenType.TYPE); + c_keywords.set ("glong", CodeTokenType.TYPE); + c_keywords.set ("gulong", CodeTokenType.TYPE); + c_keywords.set ("gint8", CodeTokenType.TYPE); + c_keywords.set ("guint8", CodeTokenType.TYPE); + c_keywords.set ("gint16", CodeTokenType.TYPE); + c_keywords.set ("guint16", CodeTokenType.TYPE); + c_keywords.set ("gint32", CodeTokenType.TYPE); + c_keywords.set ("guint32", CodeTokenType.TYPE); + c_keywords.set ("gint64", CodeTokenType.TYPE); + c_keywords.set ("guint64", CodeTokenType.TYPE); + c_keywords.set ("gfloat", CodeTokenType.TYPE); + c_keywords.set ("gdouble", CodeTokenType.TYPE); + c_keywords.set ("gsize", CodeTokenType.TYPE); + c_keywords.set ("gssize", CodeTokenType.TYPE); + c_keywords.set ("goffset", CodeTokenType.TYPE); + c_keywords.set ("gintptr", CodeTokenType.TYPE); + c_keywords.set ("guintptr", CodeTokenType.TYPE); + + + // ** Literals: ** + c_keywords.set ("NULL", CodeTokenType.LITERAL); + c_keywords.set ("TRUE", CodeTokenType.LITERAL); + c_keywords.set ("FALSE", CodeTokenType.LITERAL); + + + // ** Keywords: ** + c_keywords.set ("break", CodeTokenType.KEYWORD); + c_keywords.set ("case", CodeTokenType.KEYWORD); + c_keywords.set ("continue", CodeTokenType.KEYWORD); + c_keywords.set ("default", CodeTokenType.KEYWORD); + c_keywords.set ("do", CodeTokenType.KEYWORD); + c_keywords.set ("else", CodeTokenType.KEYWORD); + c_keywords.set ("enum", CodeTokenType.KEYWORD); + c_keywords.set ("for", CodeTokenType.KEYWORD); + c_keywords.set ("goto", CodeTokenType.KEYWORD); + c_keywords.set ("if", CodeTokenType.KEYWORD); + c_keywords.set ("return", CodeTokenType.KEYWORD); + c_keywords.set ("sizeof", CodeTokenType.KEYWORD); + c_keywords.set ("struct", CodeTokenType.KEYWORD); + c_keywords.set ("switch", CodeTokenType.KEYWORD); + c_keywords.set ("typedef", CodeTokenType.KEYWORD); + c_keywords.set ("union", CodeTokenType.KEYWORD); + c_keywords.set ("while", CodeTokenType.KEYWORD); + c_keywords.set ("assert", CodeTokenType.KEYWORD); + } + + bool enable_string_templates = false; + bool enable_preprocessor_define = true; + bool enable_preprocessor_include = true; + bool enable_keyword_escape = false; + bool enabel_verbatim_string = false; + + CodeScanner scanner = new CodeScanner (source_code, enable_string_templates, enabel_verbatim_string, + enable_preprocessor_define, enable_preprocessor_include, enable_keyword_escape, + c_keywords); + + return highlight_code (scanner); + } + + /** + * Used to highlight C source code. + */ + public Run highlight_xml (string source_code) { + XmlScanner scanner = new XmlScanner (source_code); + return highlight_code (scanner); + } + + /** + * Used to highlight source code. + */ + private Run highlight_code (Scanner scanner) { + Run code = new Run (Run.Style.MONOSPACED); + + for (CodeToken token = scanner.next (); token.token_type != CodeTokenType.EOF; token = scanner.next ()) { + switch (token.token_type) { + case CodeTokenType.PREPROCESSOR: + Run run = new Run (Run.Style.LANG_PREPROCESSOR); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + case CodeTokenType.COMMENT: + Run run = new Run (Run.Style.LANG_COMMENT); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + case CodeTokenType.KEYWORD: + Run run = new Run (Run.Style.LANG_KEYWORD); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + case CodeTokenType.LITERAL: + Run run = new Run (Run.Style.LANG_LITERAL); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + case CodeTokenType.TYPE: + Run run = new Run (Run.Style.LANG_BASIC_TYPE); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + case CodeTokenType.ESCAPE: + Run run = new Run (Run.Style.LANG_ESCAPE); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + case CodeTokenType.XML_ESCAPE: + Run run = new Run (Run.Style.XML_ESCAPE); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + case CodeTokenType.XML_ELEMENT: + Run run = new Run (Run.Style.XML_ELEMENT); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + case CodeTokenType.XML_ATTRIBUTE: + Run run = new Run (Run.Style.XML_ATTRIBUTE); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + case CodeTokenType.XML_ATTRIBUTE_VALUE: + Run run = new Run (Run.Style.XML_ATTRIBUTE_VALUE); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + case CodeTokenType.XML_COMMENT: + Run run = new Run (Run.Style.XML_COMMENT); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + case CodeTokenType.XML_CDATA: + Run run = new Run (Run.Style.XML_CDATA); + run.content.add (new Text (token.content)); + code.content.add (run); + break; + + default: + code.content.add (new Text (token.content)); + break; + } + } + + return code; + } +} + + diff --git a/libvaladoc/highlighter/scanner.vala b/libvaladoc/highlighter/scanner.vala new file mode 100644 index 000000000..20eedcfb2 --- /dev/null +++ b/libvaladoc/highlighter/scanner.vala @@ -0,0 +1,32 @@ +/* scanner.vala + * + * Copyright (C) 2015 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using GLib; + + +/** + * Scanner interface used to highlight source code. + */ +public interface Valadoc.Highlighter.Scanner : Object { + + public abstract CodeToken next (); +} diff --git a/libvaladoc/highlighter/xmlscanner.vala b/libvaladoc/highlighter/xmlscanner.vala new file mode 100644 index 000000000..38b87c5ee --- /dev/null +++ b/libvaladoc/highlighter/xmlscanner.vala @@ -0,0 +1,374 @@ +/* xmlscanner.vala + * + * Copyright (C) 2015 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using GLib; + + +/** + * A cheap scanner used to highlight XML. + */ +public class Valadoc.Highlighter.XmlScanner : Object, Scanner { + private Queue<CodeToken> token_queue = new Queue<CodeToken> (); + private unowned string content; + private unowned string pos; + + + public XmlScanner (string content) { + this.content = content; + this.pos = content; + } + + public CodeToken next () { + if (!token_queue.is_empty ()) { + return token_queue.pop_head (); + } + + unowned string start; + + for (start = pos; pos[0] != '\0'; pos = pos.next_char ()) { + if (pos[0] == '&') { + unowned string begin = pos; + if (queue_escape ()) { + return dispatch (start, begin); + } + } else if (pos[0] == '<') { + if (pos[1] == '/') { + unowned string end = pos; + if (queue_end_element ()) { + return dispatch (start, end); + } + } else if (pos[1] == '!' && pos[2] == '-' && pos[3] == '-') { + unowned string end = pos; + if (queue_comment ()) { + return dispatch (start, end); + } + } else if (pos[1] == '!' && pos[2] == '[' && pos[3] == 'C' && pos[4] == 'D' && pos[5] == 'A' && pos[6] == 'T' && pos[7] == 'A' && pos[8] == '[') { + unowned string end = pos; + pos = pos.offset (9); + token_queue.push_tail (new CodeToken (CodeTokenType.XML_CDATA, "<![CDATA[")); + return dispatch (start, end); + } else { + unowned string end = pos; + if (queue_start_element (start, pos[1] == '?')) { + return dispatch (start, end); + } else { + continue; + } + } + } else if (pos[0] == ']' && pos[1] == ']' && pos[2] == '>') { + unowned string end = pos; + pos = pos.offset (3); + token_queue.push_tail (new CodeToken (CodeTokenType.XML_CDATA, "]]>")); + return dispatch (start, end); + } + } + + token_queue.push_tail (new CodeToken (CodeTokenType.EOF, "")); + return dispatch (start, pos); + } + + private bool queue_start_element (string dispatch_start, bool xml_decl) { + assert (token_queue.is_empty ()); + + unowned string element_start = pos; + if (xml_decl) { + pos = pos.offset (2); + } else { + pos = pos.offset (1); + } + + skip_optional_spaces (ref pos); + + if (skip_id (ref pos) == false) { + token_queue.clear (); + pos = element_start; + return false; + } + + skip_optional_spaces (ref pos); + + queue_token (element_start, pos, CodeTokenType.XML_ELEMENT); + + if (queue_attributes () == false) { + token_queue.clear (); + pos = element_start; + return false; + } + + unowned string element_end_start = pos; + + if (!xml_decl && pos[0] == '>') { + pos = pos.offset (1); + } else if (!xml_decl && pos[0] == '/' && pos[1] == '>') { + pos = pos.offset (2); + } else if (xml_decl && pos[0] == '?' && pos[1] == '>') { + pos = pos.offset (2); + } else { + token_queue.clear (); + pos = element_start; + return false; + } + + queue_token (element_end_start, pos, CodeTokenType.XML_ELEMENT); + return true; + } + + private bool queue_attributes () { + while (is_id_char (pos[0])) { + unowned string begin = pos; + + if (skip_id (ref pos) == false) { + return false; + } + + skip_optional_spaces (ref pos); + + if (pos[0] == '=') { + pos = pos.offset (1); + } else { + return false; + } + + skip_optional_spaces (ref pos); + + queue_token (begin, pos, CodeTokenType.XML_ATTRIBUTE); + begin = pos; + + if (pos[0] == '"') { + pos = pos.offset (1); + } else { + return false; + } + + while (pos[0] != '"' && pos[0] != '\0') { + pos = pos.offset (1); + } + + if (pos[0] == '"') { + pos = pos.offset (1); + } else { + return false; + } + + skip_optional_spaces (ref pos); + + queue_token (begin, pos, CodeTokenType.XML_ATTRIBUTE_VALUE); + } + + return true; + } + + private bool queue_end_element () { + unowned string start = pos; + pos = pos.offset (2); + + skip_optional_spaces (ref pos); + + if (skip_id (ref pos) == false) { + pos = start; + return false; + } + + skip_optional_spaces (ref pos); + + if (pos[0] == '>') { + pos = pos.offset (1); + } else { + pos = start; + return false; + } + + queue_token (start, pos, CodeTokenType.XML_ELEMENT); + return true; + } + + private bool queue_escape () { + unowned string start = pos; + pos = pos.offset (1); + + if (skip_id (ref pos) == false) { + pos = start; + return false; + } + + if (pos[0] == ';') { + pos = pos.offset (1); + } else { + pos = start; + return false; + } + + queue_token (start, pos, CodeTokenType.XML_ESCAPE); + return true; + } + + private bool queue_comment () { + unowned string start = pos; + pos = pos.offset (4); + + while (pos[0] != '>' && pos[0] != '\0') { + pos = pos.offset (1); + } + + if (pos[0] == '>') { + pos = pos.offset (1); + } else { + pos = start; + return false; + } + + queue_token (start, pos, CodeTokenType.XML_COMMENT); + return true; + } + + private static bool skip_id (ref unowned string pos) { + bool has_next_segment = true; + bool has_id = false; + + while (has_next_segment) { + has_id = false; + + while (is_id_char (pos[0])) { + pos = pos.offset (1); + has_id = true; + } + + if (pos[0] == ':' && has_id) { + has_next_segment = true; + pos = pos.offset (1); + } else { + has_next_segment = false; + } + } + + return has_id; + } + + private static bool skip_optional_spaces (ref unowned string pos) { + bool skipped = false; + + while (pos[0].isspace ()) { + pos = pos.offset (1); + skipped = true; + } + + return skipped; + } + + private CodeToken dispatch (string start, string end) { + assert (token_queue.is_empty () == false); + + if (((char*) start) == ((char*) end)) { + return token_queue.pop_head (); + } + + long length = start.pointer_to_offset (end); + string content = start.substring (0, length); + return new CodeToken (CodeTokenType.PLAIN, content); + } + + private void queue_token (string start, string end, CodeTokenType token_type) { + long length = start.pointer_to_offset (end); + string content = start.substring (0, length); + token_queue.push_tail (new CodeToken (token_type, content)); + } + + private static inline bool is_id_char (char c) { + return c.isalnum () || c == '_' || c == '-'; + } + + internal static bool is_xml (string source) { + unowned string pos = source; + + skip_optional_spaces (ref pos); + + if (pos[0] == '<') { + // Comment: + if (pos.has_prefix ("<!--")) { + return true; + } + + // CDATA: + if (pos.has_prefix ("<![CDATA[")) { + return true; + } + + + // Start Tag: + bool proc_instr = false; + pos = pos.offset (1); + + if (pos[0] == '?') { + pos = pos.offset (1); + proc_instr = true; + } + + // ID: + if (skip_id (ref pos) == false) { + return false; + } + + skip_optional_spaces (ref pos); + + while (skip_id (ref pos)) { + if (pos[0] == '=') { + pos = pos.offset (1); + } else { + return false; + } + + skip_optional_spaces (ref pos); + + if (pos[0] == '"') { + pos = pos.offset (1); + } else { + return false; + } + + while (pos[0] != '\0' && pos[0] != '\n' && pos[0] != '"') { + pos = pos.offset (1); + } + + if (pos[0] == '"') { + pos = pos.offset (1); + } else { + return false; + } + + skip_optional_spaces (ref pos); + } + + if (proc_instr && pos[0] == '?' && pos[1] == '>') { + return true; + } + + if (!proc_instr && (pos[0] == '>' || (pos[0] == '/' && pos[1] == '>'))) { + return true; + } + + return false; + } else { + return false; + } + } +} + diff --git a/libvaladoc/html/basicdoclet.vala b/libvaladoc/html/basicdoclet.vala new file mode 100644 index 000000000..8e51234a2 --- /dev/null +++ b/libvaladoc/html/basicdoclet.vala @@ -0,0 +1,1159 @@ +/* basicdoclet.vala + * + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Valadoc.Content; +using Valadoc.Api; + + + +public abstract class Valadoc.Html.BasicDoclet : Api.Visitor, Doclet { + public Html.LinkHelper linker { + protected set; + get; + } + + public Settings settings { + protected set; + get; + } + + public string wiki_index_name { + default = "index.valadoc"; + protected set; + get; + } + + protected Api.Tree tree; + protected HtmlRenderer _renderer; + protected Html.MarkupWriter writer; + protected Html.CssClassResolver cssresolver; + protected Charts.Factory image_factory; + protected ErrorReporter reporter; + protected string package_list_link = "../index.html"; + + // CSS: + private const string css_inline_navigation = "navi_inline"; + private const string css_package_index = "package_index"; + private const string css_brief_description = "brief_description"; + private const string css_description = "description"; + private const string css_known_list = "known_nodes"; + private const string css_leaf_brief_description = "leaf_brief_description"; + private const string css_leaf_code_definition = "leaf_code_definition"; + + private const string css_box_headline_text = "text"; + private const string css_box_headline_toggle = "toggle"; + private const string css_box_headline = "headline"; + private const string css_box_content = "content"; + private const string css_box_column = "column"; + private const string css_box = "box"; + + private const string css_namespace_note = "namespace_note"; + private const string css_package_note = "package_note"; + + private const string css_site_header = "site_header"; + private const string css_navi = "navi_main"; + private const string css_navi_hr = "navi_hr"; + private const string css_errordomain_table_name = "main_errordomain_table_name"; + private const string css_errordomain_table_text = "main_errordomain_table_text"; + private const string css_errordomain_table = "main_errordomain_table"; + private const string css_enum_table_name = "main_enum_table_name"; + private const string css_enum_table_text = "main_enum_table_text"; + private const string css_enum_table = "main_enum_table"; + private const string css_diagram = "main_diagram"; + private const string css_see_list = "main_see_list"; + private const string css_wiki_table = "main_table"; + private const string css_notification_area = "main_notification"; + private const string css_source_sample = "main_sourcesample"; + private const string css_exception_table = "main_parameter_table"; + private const string css_parameter_table_text = "main_parameter_table_text"; + private const string css_parameter_table_name = "main_parameter_table_name"; + private const string css_parameter_table = "main_parameter_table"; + private const string css_title = "main_title"; + private const string css_other_type = "main_other_type"; + private const string css_basic_type = "main_basic_type"; + private const string css_keyword = "main_keyword"; + private const string css_optional_parameter = "main_optional_parameter"; + private const string css_code_definition = "main_code_definition"; + private const string css_headline_hr = "main_hr"; + private const string css_hr = "main_hr"; + private const string css_list_errdom = "main_list_errdom"; + private const string css_list_en = "main_list_en"; + private const string css_list_ns = "main_list_ns"; + private const string css_list_cl = "main_list_cl"; + private const string css_list_iface = "main_list_iface"; + private const string css_list_stru = "main_list_stru"; + private const string css_list_field = "main_list_field"; + private const string css_list_prop = "main_list_prop"; + private const string css_list_del = "main_list_del"; + private const string css_list_sig = "main_list_sig"; + private const string css_list_m = "main_list_m"; + private const string css_style_navigation = "site_navigation"; + private const string css_style_content = "site_content"; + private const string css_style_body = "site_body"; + private const string css_deprecated = "deprecated"; + + public virtual void process (Settings settings, Api.Tree tree, ErrorReporter reporter) { + this.reporter = reporter; + this.settings = settings; + this.tree = tree; + + this.cssresolver = new CssClassResolver (); + this.linker = new LinkHelper (); + + _renderer = new HtmlRenderer (settings, this.linker, this.cssresolver); + this.image_factory = new SimpleChartFactory (settings, linker); + } + + + // paths: + protected string? get_link (Api.Node to, Api.Node from) { + return linker.get_relative_link (from, to, settings); + } + + protected virtual string get_img_path_html (Api.Node element, string type) { + return Path.build_filename ("img", element.get_full_name () + "." + type); + } + + protected virtual string get_img_path (Api.Node element, string type) { + return Path.build_filename (settings.path, element.package.name, "img", + element.get_full_name () + "." + type); + } + + protected virtual string get_icon_directory () { + return ".."; + } + + + private TypeSymbol? unpack_type_reference (TypeReference? type_reference) { + Api.Item pos = type_reference; + + while (pos != null) { + if (pos is TypeReference) { + pos = ((TypeReference) pos).data_type; + } else if (pos is Api.Array) { + pos = ((Api.Array) pos).data_type; + } else if (pos is Pointer) { + pos = ((Pointer) pos).data_type; + } else { + assert (pos is TypeSymbol); + return (TypeSymbol) pos; + } + } + + return null; + } + + + protected void write_navi_entry_html_template (string style, string content, bool is_deprecated) { + writer.start_tag ("li", {"class", style}); + + if (is_deprecated) { + writer.start_tag ("span", {"class", css_deprecated}); + writer.text (content); + writer.end_tag ("span"); + } else { + writer.text (content); + } + + writer.end_tag ("li"); + } + + protected void write_navi_entry_html_template_with_link (string style, string link, + string content, bool is_deprecated) + { + writer.start_tag ("li", {"class", style}); + + if (is_deprecated) { + writer.start_tag ("span", {"class", css_deprecated}); + writer.link (link, content); + writer.end_tag ("span"); + } else { + writer.link (link, content); + } + + writer.end_tag ("li"); + } + + protected void write_navi_entry (Api.Node element, Api.Node? pos, string style, + bool link, bool full_name = false) + { + string name; + + if (full_name == true && element is Namespace) { + string tmp = element.get_full_name(); + name = (tmp == null)? "Global Namespace" : tmp; + } else { + string tmp = element.name; + name = (tmp == null)? "Global Namespace" : tmp; + } + + bool is_deprecated = element is Symbol && ((Symbol) element).is_deprecated; + + if (link == true) { + this.write_navi_entry_html_template_with_link (style, + this.get_link (element, pos), + name, + is_deprecated); + } else { + this.write_navi_entry_html_template (style, name, is_deprecated); + } + } + + protected void write_wiki_pages (Api.Tree tree, string css_path_wiki, string js_path_wiki, + string contentp) + { + if (tree.wikitree == null) { + return ; + } + + if (tree.wikitree == null) { + return ; + } + + Gee.Collection<WikiPage> pages = tree.wikitree.get_pages(); + if (pages.size == 0) { + return ; + } + + DirUtils.create (contentp, 0777); + + DirUtils.create (Path.build_filename (contentp, "img"), 0777); + + foreach (WikiPage page in pages) { + if (page.name != wiki_index_name) { + write_wiki_page (page, contentp, css_path_wiki, js_path_wiki, this.settings.pkg_name); + } + } + } + + protected virtual void write_wiki_page (WikiPage page, string contentp, string css_path, + string js_path, string pkg_name) + { + GLib.FileStream file = GLib.FileStream.open ( + Path.build_filename (contentp, page.name.substring (0, page.name.length-7).replace ("/", ".")+"htm"), + "w"); + + writer = new MarkupWriter (file); + _renderer.set_writer (writer); + this.write_file_header (css_path, js_path, pkg_name); + _renderer.set_container (page); + _renderer.render (page.documentation); + this.write_file_footer (); + } + + protected void write_navi_top_entry (Api.Node element, Api.Node? parent) { + string style = cssresolver.resolve (element); + + writer.start_tag ("ul", {"class", css_navi}); + + if (element == parent || parent == null) { + this.write_navi_entry (element, parent, style, false); + } else { + this.write_navi_entry (element, parent, style, true); + } + + writer.end_tag ("ul"); + writer.simple_tag ("hr", {"class", css_navi_hr}); + } + + protected void write_top_element_template (string link) { + writer.start_tag ("ul", {"class", css_navi}); + writer.start_tag ("li", {"class", css_package_index}); + writer.link (link, "Packages"); + writer.end_tag ("li"); + writer.end_tag ("ul"); + writer.simple_tag ("hr", {"class", css_navi_hr}); + } + + protected void write_top_elements (Api.Node element, Api.Node? parent) { + Gee.ArrayList<Api.Node> lst = new Gee.ArrayList<Api.Node> (); + Api.Node pos = element; + + this.write_top_element_template (package_list_link); + + while (pos != null) { + lst.add (pos); + pos = (Api.Node)pos.parent; + } + + for (int i = lst.size-1; i >= 0 ; i--) { + Api.Node el = lst.get (i); + + if (el.name != null) { + this.write_navi_top_entry (el, parent); + } + } + } + + protected void fetch_subnamespace_names (Api.Node node, Gee.ArrayList<Namespace> namespaces) { + Gee.ArrayList<Api.Node> sorted_list = new Gee.ArrayList<Api.Node> (); + sorted_list.add_all (node.get_children_by_type (Api.NodeType.NAMESPACE)); + sorted_list.sort (); + + foreach (Api.Node child in sorted_list) { + namespaces.add ((Namespace) child); + this.fetch_subnamespace_names (child, namespaces); + } + } + + protected void write_navi_package (Package package) { + Gee.ArrayList<Namespace> ns_list = new Gee.ArrayList<Namespace> (); + this.fetch_subnamespace_names (package, ns_list); + + writer.start_tag ("div", {"class", css_style_navigation}); + write_top_elements (package, package); + writer.start_tag ("ul", {"class", css_navi}); + + Namespace globals = null; + + foreach (Namespace ns in ns_list) { + if (ns.name == null) { + globals = ns; + } else { + this.write_navi_entry (ns, package, cssresolver.resolve (ns), true, true); + } + } + + if (globals != null) { + write_navi_children (globals, Api.NodeType.ERROR_CODE, package); + write_navi_children (globals, Api.NodeType.ENUM_VALUE, package); + write_navi_children (globals, Api.NodeType.ENUM, package); + write_navi_children (globals, Api.NodeType.INTERFACE, package); + write_navi_children (globals, Api.NodeType.CLASS, package); + write_navi_children (globals, Api.NodeType.STRUCT, package); + write_navi_children (globals, Api.NodeType.CONSTANT, package); + write_navi_children (globals, Api.NodeType.PROPERTY, package); + write_navi_children (globals, Api.NodeType.DELEGATE, package); + write_navi_children (globals, Api.NodeType.STATIC_METHOD, package); + write_navi_children (globals, Api.NodeType.CREATION_METHOD, package); + write_navi_children (globals, Api.NodeType.METHOD, package); + write_navi_children (globals, Api.NodeType.SIGNAL, package); + write_navi_children (globals, Api.NodeType.FIELD, package); + } + + writer.end_tag ("ul"); + writer.end_tag ("div"); + } + + protected void write_navi_symbol (Api.Node node) { + writer.start_tag ("div", {"class", css_style_navigation}); + write_top_elements (node, node); + write_navi_symbol_inline (node, node); + writer.end_tag ("div"); + } + + protected void write_navi_leaf_symbol (Api.Node node) { + writer.start_tag ("div", {"class", css_style_navigation}); + write_top_elements ((Api.Node) node.parent, node); + write_navi_symbol_inline ((Api.Node) node.parent, node); + writer.end_tag ("div"); + } + + protected void write_navi_symbol_inline (Api.Node node, Api.Node? parent) { + writer.start_tag ("ul", {"class", css_navi}); + write_navi_children (node, Api.NodeType.NAMESPACE, parent); + write_navi_children (node, Api.NodeType.ERROR_CODE, parent); + write_navi_children (node, Api.NodeType.ENUM_VALUE, parent); + write_navi_children (node, Api.NodeType.ENUM, parent); + write_navi_children (node, Api.NodeType.INTERFACE, parent); + write_navi_children (node, Api.NodeType.CLASS, parent); + write_navi_children (node, Api.NodeType.STRUCT, parent); + write_navi_children (node, Api.NodeType.CONSTANT, parent); + write_navi_children (node, Api.NodeType.PROPERTY, parent); + write_navi_children (node, Api.NodeType.DELEGATE, parent); + write_navi_children (node, Api.NodeType.STATIC_METHOD, parent); + write_navi_children (node, Api.NodeType.CREATION_METHOD, parent); + write_navi_children (node, Api.NodeType.METHOD, parent); + write_navi_children (node, Api.NodeType.SIGNAL, parent); + write_navi_children (node, Api.NodeType.FIELD, parent); + writer.end_tag ("ul"); + } + + protected void write_navi_children (Api.Node node, Api.NodeType type, Api.Node? parent) { + var children = node.get_children_by_type (type); + children.sort (); + foreach (Api.Node child in children) { + write_navi_entry (child, parent, cssresolver.resolve (child), child != parent); + } + } + + protected void write_package_note (Api.Node element) { + string package = element.package.name; + if (package == null) { + return; + } + + writer.start_tag ("div", {"class", css_package_note}); + writer.start_tag ("b") + .text ("Package:") + .end_tag ("b"); + writer.text (" ") + .start_tag ("a", {"href", get_link (element.package, element)}) + .text (package) + .end_tag ("a"); + writer.end_tag ("div"); + } + + protected void write_namespace_note (Api.Node element) { + Namespace? ns = element.nspace; + if (ns == null) { + return; + } + + if (ns.name == null) { + return; + } + + writer.start_tag ("div", {"class", css_namespace_note}); + writer.start_tag ("b") + .text ("Namespace:") + .end_tag ("b"); + writer.text (" ") + .start_tag ("a", {"href", get_link (ns, element)}) + .text (ns.get_full_name()) + .end_tag ("a"); + writer.end_tag ("div"); + } + + private bool has_brief_description (Api.Node element) { + return element.documentation != null; + } + + private void write_brief_description (Api.Node element , Api.Node? pos) { + Content.Comment? doctree = element.documentation; + if (doctree == null) { + return; + } + + Gee.List<Block> description = doctree.content; + if (description.size > 0) { + writer.start_tag ("span", {"class", css_brief_description}); + + _renderer.set_container (pos); + _renderer.set_owner (element); + _renderer.render_children (description.get (0)); + _renderer.set_owner (null); + + writer.end_tag ("span"); + } + } + + private void write_documentation (Api.Node element , Api.Node? pos) { + Content.Comment? doctree = element.documentation; + bool is_deprecated = (element is Symbol && ((Symbol) element).is_deprecated); + + // avoid empty divs + if (doctree == null && !is_deprecated) { + return; + } + + + writer.start_tag ("div", {"class", css_description}); + _renderer.set_owner (element); + + // deprecation warning: + if (is_deprecated) { + Symbol symbol = (Symbol) element; + Attribute? version; + Attribute? deprecated; + AttributeArgument? replacement; + AttributeArgument? since; + if ((version = symbol.get_attribute ("Version")) != null) { + replacement = version.get_argument ("replacement"); + since = version.get_argument ("deprecated_since"); + } else if ((deprecated = symbol.get_attribute ("Deprecated")) != null) { + replacement = deprecated.get_argument ("replacement"); + since = deprecated.get_argument ("version"); + } else { + assert_not_reached (); + } + + writer.start_tag ("p"); + writer.start_tag ("b"); + writer.text ("Warning:"); + writer.end_tag ("b"); + writer.text (" %s is deprecated".printf (element.name)); + + if (since != null) { + writer.text (" since %s".printf (since.get_value_as_string ())); + } + + writer.text ("."); + + if (replacement != null) { + string replacement_name = replacement.get_value_as_string (); + Api.Node? replacement_node = tree.search_symbol_str (pos, + replacement_name.substring (1, replacement_name.length - 2)); + + writer.text (" Use "); + if (replacement_node == null) { + writer.text (replacement_name); + } else { + string? link = get_link (replacement_node, pos); + if (link != null) { + string css = cssresolver.resolve (replacement_node); + writer.link (link, replacement_node.get_full_name (), css); + } else { + writer.start_tag ("code") + .text (replacement_node.get_full_name ()) + .end_tag ("code"); + } + } + writer.text ("."); + } + + writer.end_tag ("p"); + } + + if (doctree != null) { + _renderer.set_container (pos); + _renderer.render (doctree); + } + + + _renderer.set_owner (null); + writer.end_tag ("div"); + } + + private void write_attributes (Api.Symbol element, Api.Node? pos) { + writer.set_wrap (false); + _renderer.set_container (pos); + foreach (Attribute att in element.get_attributes ()) { + _renderer.render (att.signature); + writer.simple_tag ("br"); + } + writer.set_wrap (true); + } + + private void write_signature (Api.Node element , Api.Node? pos) { + writer.set_wrap (false); + _renderer.set_container (pos); + _renderer.render (element.signature); + writer.set_wrap (true); + } + + protected bool is_internal_node (Api.Node node) { + return node is Package + || node is Api.Namespace + || node is Api.Interface + || node is Api.Class + || node is Api.Struct + || node is Api.Enum + || node is Api.EnumValue + || node is Api.ErrorDomain + || node is Api.ErrorCode; + } + + public void write_navi_packages_inline (Api.Tree tree) { + writer.start_tag ("ul", {"class", css_navi}); + foreach (Package pkg in tree.get_package_list()) { + if (pkg.is_browsable (settings)) { + writer.start_tag ("li", {"class", cssresolver.resolve (pkg)}); + writer.link (linker.get_package_link (pkg, settings), pkg.name); + // brief description + writer.end_tag ("li"); + } else { + writer.start_tag ("li", {"class", cssresolver.resolve (pkg)}); + writer.text (pkg.name); + writer.end_tag ("li"); + } + } + writer.end_tag ("ul"); + } + + public void write_navi_packages (Api.Tree tree) { + writer.start_tag ("div", {"class", css_style_navigation}); + this.write_navi_packages_inline (tree); + writer.end_tag ("div"); + } + + public void write_package_index_content (Api.Tree tree) { + writer.start_tag ("div", {"class", css_style_content}); + writer.start_tag ("h1", {"class", css_title}) + .text ("Packages:") + .end_tag ("h1"); + writer.simple_tag ("hr", {"class", css_headline_hr}); + + WikiPage? wikiindex = (tree.wikitree == null) + ? null + : tree.wikitree.search (wiki_index_name); + if (wikiindex != null) { + _renderer.set_container (wikiindex); + _renderer.render (wikiindex.documentation); + } + + writer.start_tag ("h2", {"class", css_title}) + .text ("Content:") + .end_tag ("h2"); + writer.start_tag ("h3", {"class", css_title}) + .text ("Packages:") + .end_tag ("h3"); + this.write_navi_packages_inline (tree); + writer.end_tag ("div"); + } + + private uint html_id_counter = 0; + + private inline Gee.Collection<Api.Node> get_accessible_nodes_from_list (Gee.Collection<Api.Node> nodes) { + var list = new Gee.ArrayList<Api.Node> (); + + foreach (var node in nodes) { + if (node.is_browsable(_settings)) { + list.add (node); + } + } + + return list; + } + + private void write_known_symbols_note (Gee.Collection<Api.Node> nodes2, Api.Node container, string headline) { + var nodes = get_accessible_nodes_from_list (nodes2); + if (nodes.size == 0) { + return ; + } + + // Box: + var html_id = "box-content-" + html_id_counter.to_string (); + html_id_counter++; + + + writer.start_tag ("div", {"class", css_box}); + + // headline: + writer.start_tag ("div", {"class", css_box_headline, "onclick", "toggle_box (this, '%s')".printf (html_id)}) + .text (headline) + .end_tag ("div"); + //writer.start_tag ("div", {"class", css_box_headline_text, "onclick", "toggle_box (this, '%s')".printf (html_id)}) + // .text (headline) + // .end_tag ("div"); + //writer.start_tag ("div", {"class", css_box_headline_toggle}); + //writer.start_tag ("img", {"onclick", + // "toggle_box (this, '" + html_id + "')", + // "src", + // Path.build_filename (get_icon_directory (), + // "coll_open.png")}); + //writer.raw_text (" "); + //writer.end_tag ("div"); + //writer.end_tag ("div"); + + + // content: + int[] list_sizes = {0, 0, 0}; + list_sizes[0] = nodes.size; + list_sizes[2] = list_sizes[0]/3; + list_sizes[0] -= list_sizes[2]; + list_sizes[1] = list_sizes[0]/2; + list_sizes[0] -= list_sizes[1]; + + writer.start_tag ("div", {"class", css_box_content, "id", html_id}); + + var iter = nodes.iterator (); + + for (int i = 0; i < list_sizes.length; i++) { + writer.start_tag ("div", {"class", css_box_column}); + writer.start_tag ("ul", {"class", css_inline_navigation}); + + for (int p = 0; p < list_sizes[i] && iter.next (); p++) { + var node = iter.get (); + writer.start_tag ("li", {"class", cssresolver.resolve (node)}); + string link = get_link (node, container); + if (link == null) { + writer.text (node.name); + } else { + writer.link (link, node.name); + } + writer.end_tag ("li"); + } + + writer.end_tag ("ul"); + writer.end_tag ("div"); + } + + writer.end_tag ("div"); // end content + + writer.end_tag ("div"); // end box + } + + public void write_symbol_content (Api.Node node) { + writer.start_tag ("div", {"class", css_style_content}); + writer.start_tag ("h1", {"class", css_title}) + .text (node.name) + .end_tag ("h1"); + writer.simple_tag ("hr", {"class", css_headline_hr}); + this.write_image_block (node); + writer.start_tag ("h2", {"class", css_title}) + .text ("Description:") + .end_tag ("h2"); + writer.start_tag ("div", {"class", css_code_definition}); + if (node is Symbol) { + this.write_attributes ((Symbol) node, node); + } + this.write_signature (node, node); + writer.end_tag ("div"); + this.write_documentation (node, node); + + if (node is Class) { + var cl = node as Class; + write_known_symbols_note (cl.get_known_child_classes (), + cl, + "All known sub-classes:"); + write_known_symbols_note (cl.get_known_derived_interfaces (), + cl, + "Required by:"); + } else if (node is Interface) { + var iface = node as Interface; + write_known_symbols_note (iface.get_known_implementations (), + iface, + "All known implementing classes:"); + write_known_symbols_note (iface.get_known_related_interfaces (), + iface, + "All known sub-interfaces:"); + } else if (node is Struct) { + var stru = node as Struct; + write_known_symbols_note (stru.get_known_child_structs (), + stru, + "All known sub-structs:"); + } + + if (node.parent is Namespace) { + writer.simple_tag ("br"); + write_namespace_note (node); + write_package_note (node); + } + + if (!(node is Method || node is Delegate || node is Api.Signal)) { + // avoids exception listings & implementations + + if (node.has_children ({ + Api.NodeType.ERROR_CODE, + Api.NodeType.ENUM_VALUE, + Api.NodeType.CREATION_METHOD, + Api.NodeType.STATIC_METHOD, + Api.NodeType.CLASS, + Api.NodeType.STRUCT, + Api.NodeType.ENUM, + Api.NodeType.DELEGATE, + Api.NodeType.METHOD, + Api.NodeType.SIGNAL, + Api.NodeType.PROPERTY, + Api.NodeType.FIELD, + Api.NodeType.CONSTANT + })) + { + writer.start_tag ("h2", {"class", css_title}).text ("Content:").end_tag ("h2"); + write_children (node, Api.NodeType.ERROR_CODE, "Error codes", node); + write_children (node, Api.NodeType.ENUM_VALUE, "Enum values", node); + write_children (node, Api.NodeType.CLASS, "Classes", node); + write_children (node, Api.NodeType.STRUCT, "Structs", node); + write_children (node, Api.NodeType.ENUM, "Enums", node); + write_children (node, Api.NodeType.CONSTANT, "Constants", node); + write_children (node, Api.NodeType.PROPERTY, "Properties", node); + write_children (node, Api.NodeType.DELEGATE, "Delegates", node); + write_children (node, Api.NodeType.STATIC_METHOD, "Static methods", node); + write_children (node, Api.NodeType.CREATION_METHOD, "Creation methods", node); + write_children (node, Api.NodeType.METHOD, "Methods", node); + write_children (node, Api.NodeType.SIGNAL, "Signals", node); + write_children (node, Api.NodeType.FIELD, "Fields", node); + } + } + + if (node is Class) { + write_inherited_symbols_note_for_class ((Class) node, node); + } else if (node is Interface) { + write_inherited_symbols_note_for_interface ((Interface) node, node); + } else if (node is Struct) { + write_inherited_symbols_note_for_struct ((Struct) node, node); + } + + writer.end_tag ("div"); + } + + private static NodeType[] inheritable_members = { + NodeType.CONSTANT, + NodeType.PROPERTY, + NodeType.DELEGATE, + NodeType.STATIC_METHOD, + NodeType.METHOD, + NodeType.SIGNAL, + NodeType.FIELD + }; + + private inline bool has_visible_inheritable_children (TypeSymbol symbol) { + return symbol.has_visible_children_by_types (inheritable_members, _settings); + } + + private void write_inherited_members_headline () { + writer.start_tag ("h3", {"class", css_title}) + .text ("Inherited Members:") + .end_tag ("h3"); + } + + private void write_inherited_symbols_note_for_class (Class cl, Api.Node container) { + bool headline_printed = false; + + // class hierarchy: + Class base_class = unpack_type_reference (cl.base_type) as Class; + while (base_class != null) { + if (!headline_printed && has_visible_inheritable_children (base_class)) { + write_inherited_members_headline (); + headline_printed = true; + } + + write_inherited_symbols_note (base_class, "class", container); + base_class = unpack_type_reference (base_class.base_type) as Class; + } + + + // implemented interfaces + Gee.LinkedList<Interface> printed_interfaces = new Gee.LinkedList<Interface> (); + foreach (TypeReference iface_ref in cl.get_full_implemented_interface_list ()) { + Interface iface = (Interface) unpack_type_reference (iface_ref); + + if (!headline_printed && has_visible_inheritable_children (iface)) { + write_inherited_members_headline (); + headline_printed = true; + } else if (printed_interfaces.contains (iface)) { + continue ; + } + + write_inherited_symbols_note (iface, "interface", container); + printed_interfaces.add (iface); + } + } + + private void write_inherited_symbols_note_for_interface (Interface iface, Api.Node container) { + bool headline_printed = false; + + // class hierarchy: + Class base_class = unpack_type_reference (iface.base_type) as Class; + while (base_class != null) { + if (!headline_printed && has_visible_inheritable_children (base_class)) { + write_inherited_members_headline (); + headline_printed = true; + } + + write_inherited_symbols_note (base_class, "class", container); + base_class = unpack_type_reference (base_class.base_type) as Class; + } + + + // interfaces: + Gee.LinkedList<Interface> printed_interfaces = new Gee.LinkedList<Interface> (); + foreach (TypeReference pre_ref in iface.get_full_implemented_interface_list ()) { + Interface pre = (Interface) unpack_type_reference (pre_ref); + + if (!headline_printed && has_visible_inheritable_children (pre)) { + write_inherited_members_headline (); + headline_printed = true; + } else if (printed_interfaces.contains (pre)) { + continue ; + } + + write_inherited_symbols_note (pre, "interface", container); + printed_interfaces.add (pre); + } + } + + private void write_inherited_symbols_note_for_struct (Struct str, Api.Node container) { + Struct base_struct = unpack_type_reference (str.base_type) as Struct; + if (base_struct != null && has_visible_inheritable_children (base_struct)) { + write_inherited_members_headline (); + write_inherited_symbols_note (base_struct, "struct", container); + } + } + + private void write_inherited_symbols_note (TypeSymbol symbol, string type, Api.Node container) { + write_known_symbols_note (symbol.get_children_by_types (inheritable_members, false), + container, + "All known members inherited from %s %s".printf (type, symbol.get_full_name ())); + + /* + write_known_symbols_note (symbol.get_children_by_type (NodeType.CONSTANT, false), + container, + "All known constants inherited from %s %s".printf (type, symbol.get_full_name ())); + write_known_symbols_note (symbol.get_children_by_type (NodeType.PROPERTY, false), + container, + "All known properties inherited from %s %s".printf (type, symbol.get_full_name ())); + write_known_symbols_note (symbol.get_children_by_type (NodeType.DELEGATE, false), + container, + "All known delegates inherited from %s %s".printf (type, symbol.get_full_name ())); + write_known_symbols_note (symbol.get_children_by_type (NodeType.STATIC_METHOD, false), + container, + "All known static methods inherited from %s %s".printf (type, symbol.get_full_name ())); + write_known_symbols_note (symbol.get_children_by_type (NodeType.METHOD, false), + container, + "All known methods inherited from %s %s".printf (type, symbol.get_full_name ())); + write_known_symbols_note (symbol.get_children_by_type (NodeType.SIGNAL, false), + container, + "All known signals inherited from %s %s".printf (type, symbol.get_full_name ())); + write_known_symbols_note (symbol.get_children_by_type (NodeType.FIELD, false), + container, + "All known fields inherited from %s %s".printf (type, symbol.get_full_name ())); + */ + } + + protected void write_child_namespaces (Api.Node node, Api.Node? parent) { + Gee.ArrayList<Namespace> namespaces = new Gee.ArrayList<Namespace> (); + this.fetch_subnamespace_names (node, namespaces); + + if (namespaces.size == 0) { + return; + } + + if (namespaces.size == 1) { + if (namespaces.get(0).name == null) { + return; + } + } + + bool with_childs = parent != null && parent is Package; + + writer.start_tag ("h3", {"class", css_title}) + .text ("Namespaces:") + .end_tag ("h3"); + writer.start_tag ("ul", {"class", css_inline_navigation}); + foreach (Namespace child in namespaces) { + if (child.name != null) { + writer.start_tag ("li", {"class", cssresolver.resolve (child)}); + writer.link (get_link (child, parent), child.name); + if (has_brief_description (child)) { + writer.text (" - "); + this.write_brief_description (child, parent); + } + writer.end_tag ("li"); + if (with_childs == true) { + write_children (child, Api.NodeType.INTERFACE, "Interfaces", parent); + write_children (child, Api.NodeType.CLASS, "Classes", parent); + write_children (child, Api.NodeType.STRUCT, "Structs", parent); + write_children (child, Api.NodeType.ENUM, "Enums", parent); + write_children (child, Api.NodeType.ERROR_DOMAIN, "Error domains", parent); + write_children (child, Api.NodeType.CONSTANT, "Constants", parent); + write_children (child, Api.NodeType.DELEGATE, "Delegates", parent); + write_children (child, Api.NodeType.METHOD, "Methods", parent); + write_children (child, Api.NodeType.FIELD, "Fields", parent); + } + } + } + writer.end_tag ("ul"); + } + + protected void write_child_dependencies (Package package, Api.Node? parent) { + Gee.Collection<Package>? deps = package.get_full_dependency_list (); + if (deps.size == 0) { + return; + } + + writer.start_tag ("h2", {"class", css_title}) + .text ("Dependencies:") + .end_tag ("h2"); + writer.start_tag ("ul", {"class", css_inline_navigation}); + foreach (Package p in deps) { + string? link = this.get_link (p, parent); + if (link == null) { + writer.start_tag ("li", {"class", cssresolver.resolve (p), "id", p.name}) + .text (p.name) + .end_tag ("li"); + } else { + writer.start_tag ("li", {"class", cssresolver.resolve (p)}); + writer.link (get_link (p, parent), p.name); + writer.end_tag ("li"); + } + } + writer.end_tag ("ul"); + } + + protected void write_children (Api.Node node, Api.NodeType type, string type_string, Api.Node? container) { + var children = node.get_children_by_type (type); + if (children.size > 0) { + writer.start_tag ("h3", {"class", css_title}) + .text (type_string) + .text (":") + .end_tag ("h3"); + writer.start_tag ("ul", {"class", css_inline_navigation}); + foreach (Api.Node child in children) { + writer.start_tag ("li", {"class", cssresolver.resolve (child)}); + if (is_internal_node (child)) { + if (child is Symbol && ((Symbol) child).is_deprecated) { + writer.start_tag ("span", {"class", css_deprecated}); + writer.link (get_link (child, container), child.name); + writer.end_tag ("span"); + } else { + writer.link (get_link (child, container), child.name); + } + if (has_brief_description (child)) { + writer.text (" - "); + write_brief_description (child, container); + } + } else { + writer.start_tag ("span", {"class", css_leaf_code_definition}); + if (child is Symbol && ((Symbol) child).is_deprecated) { + writer.start_tag ("span", {"class", css_deprecated}); + write_signature (child, container); + writer.end_tag ("span"); + } else { + write_signature (child, container); + } + writer.end_tag ("span"); + + writer.start_tag ("div", {"class", css_leaf_brief_description}); + write_brief_description (child, container); + writer.end_tag ("div"); + } + writer.end_tag ("li"); + } + writer.end_tag ("ul"); + } + } + + protected void write_image_block (Api.Node element) { + if (element is Class || element is Interface || element is Struct) { + unowned string format = (settings.use_svg_images ? "svg" : "png"); + var chart = new Charts.Hierarchy (image_factory, element); + chart.save (this.get_img_path (element, format), format); + + writer.start_tag ("h2", {"class", css_title}) + .text ("Object Hierarchy:") + .end_tag ("h2"); + + writer.simple_tag ("img", {"class", + css_diagram, + "usemap", + "#"+element.get_full_name (), + "alt", + "Object hierarchy for %s".printf (element.name), + "src", + this.get_img_path_html (element, format)}); + writer.add_usemap (chart); + } + } + + public void write_namespace_content (Namespace node, Api.Node? parent) { + writer.start_tag ("div", {"class", css_style_content}); + writer.start_tag ("h1", {"class", css_title}) + .text (node.name == null ? "Global Namespace" : node.get_full_name ()) + .end_tag ("h1"); + writer.simple_tag ("hr", {"class", css_hr}); + writer.start_tag ("h2", {"class", css_title}) + .text ("Description:") + .end_tag ("h2"); + + this.write_documentation (node, parent); + + writer.start_tag ("h2", {"class", css_title}) + .text ("Content:") + .end_tag ("h2"); + + if (node.name == null) { + this.write_child_namespaces ((Package) node.parent, parent); + } else { + this.write_child_namespaces (node, parent); + } + + write_children (node, Api.NodeType.INTERFACE, "Interfaces", parent); + write_children (node, Api.NodeType.CLASS, "Classes", parent); + write_children (node, Api.NodeType.STRUCT, "Structs", parent); + write_children (node, Api.NodeType.ENUM, "Enums", parent); + write_children (node, Api.NodeType.ERROR_DOMAIN, "Error domains", parent); + write_children (node, Api.NodeType.CONSTANT, "Constants", parent); + write_children (node, Api.NodeType.DELEGATE, "Delegates", parent); + write_children (node, Api.NodeType.METHOD, "Functions", parent); + write_children (node, Api.NodeType.FIELD, "Fields", parent); + writer.end_tag ("div"); + } + + protected void write_package_content (Package node, Api.Node? parent) { + writer.start_tag ("div", {"class", css_style_content}); + writer.start_tag ("h1", {"class", css_title, "id", node.name}) + .text (node.name) + .end_tag ("h1"); + writer.simple_tag ("hr", {"class", css_headline_hr}); + writer.start_tag ("h2", {"class", css_title}) + .text ("Description:") + .end_tag ("h2"); + + + WikiPage? wikipage = (tree.wikitree == null)? null : tree.wikitree.search (wiki_index_name); + if (wikipage != null) { + _renderer.set_container (parent); + _renderer.render (wikipage.documentation); + } + + writer.start_tag ("h2", {"class", css_title}) + .text ("Content:") + .end_tag ("h2"); + + this.write_child_namespaces (node, parent); + + foreach (Api.Node child in node.get_children_by_type (Api.NodeType.NAMESPACE)) { + if (child.name == null) { + write_children (child, Api.NodeType.INTERFACE, "Interfaces", parent); + write_children (child, Api.NodeType.CLASS, "Classes", parent); + write_children (child, Api.NodeType.STRUCT, "Structs", parent); + write_children (child, Api.NodeType.ENUM, "Enums", parent); + write_children (child, Api.NodeType.ERROR_DOMAIN, "Error domains", parent); + write_children (child, Api.NodeType.CONSTANT, "Constants", parent); + write_children (child, Api.NodeType.DELEGATE, "Delegates", parent); + write_children (child, Api.NodeType.METHOD, "Functions", parent); + write_children (child, Api.NodeType.FIELD, "Fields", parent); + } + } + + this.write_child_dependencies (node, parent); + writer.end_tag ("div"); + } + + protected void write_file_header (string css, string js, string? title) { + writer.start_tag ("html"); + writer.start_tag ("head"); + writer.simple_tag ("meta", {"charset", "UTF-8"}); + if (title == null) { + writer.start_tag ("title") + .text ("Vala Binding Reference") + .end_tag ("title"); + } else { + writer.start_tag ("title") + .text (title) + .text (" – Vala Binding Reference") + .end_tag ("title"); + } + writer.stylesheet_link (css); + writer.javascript_link (js); + writer.end_tag ("head"); + writer.start_tag ("body"); + writer.start_tag ("div", {"class", css_site_header}); + writer.text ("%s Reference Manual".printf (title == null ? "" : title)); + writer.end_tag ("div"); + writer.start_tag ("div", {"class", css_style_body}); + } + + protected void write_file_footer () { + writer.end_tag ("div"); + writer.simple_tag ("br"); + writer.start_tag ("div", {"class", "site_footer"}); + writer.text ("Generated by "); + writer.link ("http://www.valadoc.org/", "Valadoc"); + writer.end_tag ("div"); + writer.end_tag ("body"); + writer.end_tag ("html"); + } +} + diff --git a/libvaladoc/html/cssclassresolver.vala b/libvaladoc/html/cssclassresolver.vala new file mode 100644 index 000000000..e8961aeeb --- /dev/null +++ b/libvaladoc/html/cssclassresolver.vala @@ -0,0 +1,117 @@ +/* globals.vala + * + * Copyright (C) 2008-2009 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Valadoc.Api; + + +namespace Valadoc.Html { + public class CssClassResolver : Api.Visitor { + private string? css_class = null; + + public string resolve (Api.Node node) { + node.accept (this); + return (owned) css_class; + } + + public override void visit_package (Api.Package item) { + css_class = "package"; + } + + public override void visit_namespace (Api.Namespace item) { + css_class = "namespace"; + } + + public override void visit_interface (Api.Interface item) { + css_class = "interface"; + } + + public override void visit_class (Api.Class item) { + if (item.is_abstract) { + css_class = "abstract_class"; + } else { + css_class = "class"; + } + } + + public override void visit_struct (Api.Struct item) { + css_class = "struct"; + } + + public override void visit_property (Api.Property item) { + if (item.is_virtual || item.is_override) { + css_class = "virtual_property"; + } else if (item.is_abstract) { + css_class = "abstract_property"; + } else { + css_class = "property"; + } + } + + public override void visit_field (Api.Field item) { + css_class = "field"; + } + + public override void visit_constant (Api.Constant item) { + css_class = "constant"; + } + + public override void visit_delegate (Api.Delegate item) { + css_class = "delegate"; + } + + public override void visit_signal (Api.Signal item) { + css_class = "signal"; + } + + public override void visit_method (Api.Method item) { + if (item.is_static) { + css_class = "static_method"; + } else if (item.is_abstract) { + css_class = "abstract_method"; + } else if (item.is_virtual || item.is_override) { + css_class = "virtual_method"; + } else if (item.is_constructor) { + css_class = "creation_method"; + } else { + css_class = "method"; + } + } + + public override void visit_error_domain (Api.ErrorDomain item) { + css_class = "errordomain"; + } + + public override void visit_error_code (Api.ErrorCode item) { + css_class = "errorcode"; + } + + public override void visit_enum (Api.Enum item) { + css_class = "enum"; + } + + public override void visit_enum_value (Api.EnumValue item) { + css_class = "enumvalue"; + } + } +} + + diff --git a/libvaladoc/html/htmlchartfactory.vala b/libvaladoc/html/htmlchartfactory.vala new file mode 100644 index 000000000..1b63b4e13 --- /dev/null +++ b/libvaladoc/html/htmlchartfactory.vala @@ -0,0 +1,54 @@ +/* simplechartfactory.vala + * + * Copyright (C) 2008 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + + +public class Valadoc.Html.SimpleChartFactory : Charts.SimpleFactory { + private Settings _settings; + private Api.Node _container; + private LinkHelper _linker; + + public SimpleChartFactory (Settings settings, LinkHelper linker) { + _settings = settings; + _linker = linker; + } + + public override Gvc.Graph create_graph (Api.Node item) { + var graph = base.create_graph (item); + _container = item; + return graph; + } + + protected override Gvc.Node configure_type (Gvc.Node node, Api.Node item) { + base.configure_type (node, item); + + if (_container != null) { + var link = _linker.get_relative_link (_container, item, _settings); + if (link != null) { + node.safe_set ("URL", link, ""); + } + } + + return node; + } +} + diff --git a/libvaladoc/html/htmlmarkupwriter.vala b/libvaladoc/html/htmlmarkupwriter.vala new file mode 100644 index 000000000..15ed9efd1 --- /dev/null +++ b/libvaladoc/html/htmlmarkupwriter.vala @@ -0,0 +1,132 @@ +/* markupwriter.vala + * + * Copyright (C) 2008-2014 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using GLib; +using Valadoc.Content; + +public class Valadoc.Html.MarkupWriter : Valadoc.MarkupWriter { + + public MarkupWriter (FileStream stream, bool xml_declaration = true) { + // avoid broken implicit copy + unowned FileStream _stream = stream; + + base ((str) => { + _stream.printf (str); + }, xml_declaration); + } + + public MarkupWriter.builder (StringBuilder builder, bool xml_declaration = true) { + // avoid broken implicit copy + unowned StringBuilder _builder = builder; + + base ((str) => { + _builder.append (str); + }, xml_declaration); + } + + public MarkupWriter add_usemap (Charts.Chart chart) { + string? buf = (string?) chart.write_buffer ("cmapx"); + if (buf != null) { + raw_text ("\n"); + raw_text ((!) buf); + } + + return this; + } + + // edit + public MarkupWriter link (string url, string label, string? css_class = null) { + if (css_class == null) { + start_tag ("a", {"href", url}); + } else { + start_tag ("a", {"href", url, "class", css_class}); + } + + text (label); + end_tag ("a"); + return this; + } + + public MarkupWriter image (string src, string? caption = null, string? css_class = null) { + if (css_class == null) { + simple_tag ("img", {"src", src, "alt", caption}); + } else { + simple_tag ("img", {"src", src, "alt", caption, "class", css_class}); + } + return this; + } + + public MarkupWriter stylesheet_link (string url) { + simple_tag ("link", {"href", url, "rel", "stylesheet", "type", "text/css"}); + return this; + } + + public MarkupWriter javascript_link (string url) { + start_tag ("script", {"src", url, "type", "text/javascript"}); + end_tag ("script"); + return this; + } + + protected override bool inline_element (string name) { + return name != "html" + && name != "head" + && name != "title" + && name != "meta" + && name != "link" + && name != "body" + && name != "div" + && name != "p" + && name != "table" + && name != "tr" + && name != "td" + && name != "ul" + && name != "ol" + && name != "li" + && name != "h1" + && name != "h2" + && name != "h3" + && name != "h4" + && name != "h5" + && name != "hr" + && name != "img"; + } + + protected override bool content_inline_element (string name) { + return name == "title" + || name == "meta" + || name == "p" + || name == "a" + || name == "h1" + || name == "h2" + || name == "h3" + || name == "h4" + || name == "h5" + || name == "li" + || name == "span" + || name == "code" + || name == "b" + || name == "i" + || name == "u" + || name == "stoke"; + } +} + diff --git a/libvaladoc/html/htmlrenderer.vala b/libvaladoc/html/htmlrenderer.vala new file mode 100644 index 000000000..bdd78a09d --- /dev/null +++ b/libvaladoc/html/htmlrenderer.vala @@ -0,0 +1,632 @@ +/* htmlrenderer.vala + * + * Copyright (C) 2008-20014 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using GLib; +using Valadoc.Content; + +public class Valadoc.Html.HtmlRenderer : ContentRenderer { + + protected Documentation? _container; + protected Documentation? _owner; + protected unowned MarkupWriter writer; + protected Html.CssClassResolver cssresolver; + protected LinkHelper linker; + protected Settings settings; + + public HtmlRenderer (Settings settings, LinkHelper linker, CssClassResolver cssresolver) { + this.cssresolver = cssresolver; + this.settings = settings; + this.linker = linker; + } + + public void set_container (Documentation? container) { + _container = container; + } + + public void set_owner (Documentation? owner) { + _owner = owner; + } + + public void set_writer (MarkupWriter writer) { + this.writer = writer; + } + + public override void render (ContentElement element) { + element.accept (this); + } + + public override void render_children (ContentElement element) { + element.accept_children (this); + } + + private string get_url (Documentation symbol) { + return linker.get_relative_link (_container, symbol, settings); + } + + private void write_unresolved_symbol_link (string given_symbol_name, InlineContent? label_owner = null) { + if (label_owner == null || label_owner.content.size == 0) { + writer.start_tag ("code"); + writer.text (given_symbol_name); + writer.end_tag ("code"); + } else { + writer.start_tag ("i"); + label_owner.accept_children (this); + writer.end_tag ("i"); + } + } + + private void write_resolved_symbol_link (Api.Node symbol, string? given_symbol_name, InlineContent? label_owner = null) { + var symbol_name = (given_symbol_name == null || given_symbol_name == "") ? symbol.get_full_name () : given_symbol_name; + string href = (symbol == _container || symbol == _owner)? null : get_url (symbol); + string css_class = cssresolver.resolve (symbol); + string end_tag_name; + + + // Start Tag: + if (href != null) { + writer.start_tag ("a", {"href", href, "class", css_class}); + end_tag_name = "a"; + } else { + writer.start_tag ("span", {"class", css_class}); + end_tag_name = "span"; + } + + + // Content: + if (label_owner != null && label_owner.content.size > 0) { + label_owner.accept_children (this); + } else { + writer.text (symbol_name); + } + + + // End Tag: + writer.end_tag (end_tag_name); + } + + private delegate void Write (); + private delegate void TagletWrite (Taglet taglet); + + private void write_taglets (Write header, Write footer, Write separator, + Gee.List<Taglet> taglets, TagletWrite write) { + if (taglets.size > 0) { + header (); + bool first = true; + foreach (var taglet in taglets) { + if (!first) { + separator (); + } + write (taglet); + first = false; + } + footer (); + } + } + + public override void visit_comment (Comment element) { + Gee.List<Taglet> taglets; + + taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.Deprecated)); + write_taglets ( + () => { + writer.start_tag ("p", {"class", "main_title"}); + writer.start_tag ("b") + .text ("Deprecated: ") + .end_tag ("b"); + }, + () => { + writer.end_tag ("p"); + }, + () => {}, + taglets, + (taglet) => { + var deprecated = taglet as Taglets.Deprecated; + deprecated.accept_children (this); + }); + + // Write description + element.accept_children (this); + + taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.Param)); + taglets.sort ((_a, _b) => { + Taglets.Param a = _a as Taglets.Param; + Taglets.Param b = _b as Taglets.Param; + + if (a.position < 0 && b.position < 0) { + int cmp = a.parameter_name.ascii_casecmp (b.parameter_name); + if (cmp == 0) { + return 0; + } + + if (a.parameter_name == "...") { + return 1; + } + + if (b.parameter_name == "...") { + return -1; + } + + return cmp; + } + + if (a.position < 0) { + return 1; + } + + if (b.position < 0) { + return -1; + } + + return a.position - b.position; + }); + + write_taglets ( + () => { + writer.start_tag ("h2", {"class", "main_title"}) + .text ("Parameters:") + .end_tag ("h2"); + writer.start_tag ("table", {"class", "main_parameter_table"}); + }, + () => { + writer.end_tag ("table"); + }, + () => {}, + taglets, + (taglet) => { + var param = taglet as Taglets.Param; + string[]? unknown_parameter_css = null; + if (param.parameter == null && !param.is_this) { + unknown_parameter_css = {"class", "main_parameter_table_unknown_parameter"}; + } + + writer.start_tag ("tr", unknown_parameter_css); + writer.start_tag ("td", {"class", "main_parameter_table_name"}) + .text (param.parameter_name) + .end_tag ("td"); + writer.start_tag ("td"); + param.accept_children (this); + writer.end_tag ("td"); + writer.end_tag ("tr"); + }); + + taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.Return)); + write_taglets ( + () => { + writer.start_tag ("h2", {"class", "main_title"}) + .text ("Returns:") + .end_tag ("h2"); + writer.start_tag ("table", {"class", "main_parameter_table"}); + }, + () => { + writer.end_tag ("table"); + }, + () => {}, + taglets, + (taglet) => { + var param = taglet as Taglets.Return; + writer.start_tag ("tr"); + writer.start_tag ("td"); + param.accept_children (this); + writer.end_tag ("td"); + writer.end_tag ("tr"); + }); + + taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.Throws)); + write_taglets ( + () => { + writer.start_tag ("h2", {"class", "main_title"}) + .text ("Exceptions:") + .end_tag ("h2"); + writer.start_tag ("table", {"class", "main_parameter_table"}); + }, + () => { + writer.end_tag ("table"); + }, + () => {}, + taglets, + (taglet) => { + var exception = taglet as Taglets.Throws; + writer.start_tag ("tr"); + writer.start_tag ("td", {"class", "main_parameter_table_name"}) + .text (exception.error_domain_name) + .end_tag ("td"); + writer.start_tag ("td"); + exception.accept_children (this); + writer.end_tag ("td"); + writer.end_tag ("tr"); + }); + + taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.Since)); + write_taglets ( + () => { + writer.start_tag ("h2", {"class", "main_title"}) + .text ("Since:") + .end_tag ("h2"); + writer.start_tag ("p"); + }, + () => { + writer.end_tag ("p"); + }, + () => {}, + taglets, + (taglet) => { + var since = taglet as Taglets.Since; + writer.text (since.version); + }); + + taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.See)); + write_taglets ( + () => { + writer.start_tag ("h2", {"class", "main_title"}) + .text ("See also:") + .end_tag ("h2"); + writer.start_tag ("p"); + }, + () => { + writer.end_tag ("p"); + }, + () => { + writer.text (", "); + }, + taglets, + (taglet) => { + var see = taglet as Taglets.See; + if (see.symbol == null) { + write_unresolved_symbol_link (see.symbol_name); + } else { + write_resolved_symbol_link (see.symbol, see.symbol_name); + } + }); + } + + public override void visit_embedded (Embedded element) { + var caption = element.caption; + + var absolute_path = Path.build_filename (settings.path, element.package.name, "img", + Path.get_basename (element.url)); + var relative_path = Path.build_filename ("img", Path.get_basename (element.url)); + + copy_file (element.url, absolute_path); + + writer.image (relative_path, (caption == null || caption == "") ? "" : caption); + } + + public override void visit_headline (Headline element) { + writer.start_tag ("h%d".printf (element.level)); + element.accept_children (this); + writer.end_tag ("h%d".printf (element.level)); + } + + public override void visit_wiki_link (WikiLink element) { + if (element.page != null) { + writer.start_tag ("a", {"href", get_url (element.page)}); + } + + if (element.content.size > 0) { + element.accept_children (this); + } else { + writer.text (element.name.substring (0, element.name.last_index_of_char ('.'))); + } + + if (element.page != null) { + writer.end_tag ("a"); + } + } + + public override void visit_link (Link element) { + if (Uri.parse_scheme (element.url) != null) { + writer.start_tag ("a", {"href", element.url, "target", "_blank"}); + } else { + writer.start_tag ("a", {"href", element.url}); + } + + if (element.content.size > 0) { + element.accept_children (this); + } else { + writer.text (element.url); + } + + writer.end_tag ("a"); + } + + public override void visit_symbol_link (SymbolLink element) { + if (element.symbol == null) { + write_unresolved_symbol_link (element.given_symbol_name, element); + } else { + write_resolved_symbol_link (element.symbol, element.given_symbol_name, element); + } + } + + public override void visit_list (Content.List element) { + string list_type = null; + string bullet_type = null; + string css_class = null; + switch (element.bullet) { + case Content.List.Bullet.NONE: + list_type = "ul"; + css_class = "no_bullet"; + break; + case Content.List.Bullet.UNORDERED: + list_type = "ul"; + break; + case Content.List.Bullet.ORDERED: + list_type = "ol"; + break; + case Content.List.Bullet.ORDERED_NUMBER: + list_type = "ol"; + bullet_type = "1"; + break; + case Content.List.Bullet.ORDERED_LOWER_CASE_ALPHA: + list_type = "ol"; + bullet_type = "a"; + break; + case Content.List.Bullet.ORDERED_UPPER_CASE_ALPHA: + list_type = "ol"; + bullet_type = "A"; + break; + case Content.List.Bullet.ORDERED_LOWER_CASE_ROMAN: + list_type = "ol"; + bullet_type = "i"; + break; + case Content.List.Bullet.ORDERED_UPPER_CASE_ROMAN: + list_type = "ol"; + bullet_type = "I"; + break; + } + writer.start_tag (list_type, {"class", css_class, "type", bullet_type}); + element.accept_children (this); + writer.end_tag (list_type); + } + + public override void visit_list_item (ListItem element) { + writer.start_tag ("li"); + Paragraph? first_para = (element.content.size > 0)? element.content[0] as Paragraph : null; + if (first_para != null) { + // We do not pick up alignments in gir-files. + first_para.accept_children (this); + bool first_entry = true; + foreach (var item in element.content) { + if (!first_entry) { + item.accept (this); + } + first_entry = false; + } + } else { + element.accept_children (this); + } + writer.end_tag ("li"); + } + + public override void visit_page (Page element) { + element.accept_children (this); + } + + public override void visit_paragraph (Paragraph element) { + //FIXME: the extra-field is just a workarround for the current codegen ... + if (element.horizontal_align == null) { + writer.start_tag ("p"); + } else { + HorizontalAlign tmp = element.horizontal_align; + switch (tmp) { + case HorizontalAlign.CENTER: + writer.start_tag ("p", {"style", "text-align: center;"}); + break; + + case HorizontalAlign.RIGHT: + writer.start_tag ("p", {"style", "text-align: right;"}); + break; + + default: + writer.start_tag ("p"); + break; + } + } + element.accept_children (this); + writer.end_tag ("p"); + } + + private void visit_notification_block (BlockContent element, string headline) { + writer.start_tag ("div", {"class", "main_notification_block"}); + writer.start_tag ("span", {"class", "main_block_headline"}) + .text (headline) + .end_tag ("span") + .text (" "); + writer.start_tag ("div", {"class", "main_block_content"}); + element.accept_children (this); + writer.end_tag ("div"); + writer.end_tag ("div"); + } + + public override void visit_warning (Warning element) { + visit_notification_block (element, "Warning:"); + } + + public override void visit_note (Note element) { + visit_notification_block (element, "Note:"); + } + + public override void visit_run (Run element) { + string tag = null; + string css_type = null; + switch (element.style) { + case Run.Style.BOLD: + tag = "b"; + break; + case Run.Style.ITALIC: + tag = "i"; + break; + case Run.Style.UNDERLINED: + tag = "u"; + break; + case Run.Style.MONOSPACED: + tag = "code"; + break; + case Run.Style.STROKE: + tag = "stroke"; + break; + case Run.Style.LANG_KEYWORD: + tag = "span"; + css_type = "main_keyword"; + break; + case Run.Style.LANG_ESCAPE: + tag = "span"; + css_type = "main_escape"; + break; + case Run.Style.LANG_LITERAL: + tag = "span"; + css_type = "main_literal"; + break; + case Run.Style.LANG_BASIC_TYPE: + tag = "span"; + css_type = "main_basic_type"; + break; + case Run.Style.LANG_TYPE: + tag = "span"; + css_type = "main_type"; + break; + case Run.Style.LANG_COMMENT: + tag = "span"; + css_type = "main_comment"; + break; + case Run.Style.LANG_PREPROCESSOR: + tag = "span"; + css_type = "main_preprocessor"; + break; + + case Run.Style.XML_ESCAPE: + tag = "span"; + css_type = "xml_escape"; + break; + + case Run.Style.XML_ELEMENT: + tag = "span"; + css_type = "xml_element"; + break; + + case Run.Style.XML_ATTRIBUTE: + tag = "span"; + css_type = "xml_attribute"; + break; + + case Run.Style.XML_ATTRIBUTE_VALUE: + tag = "span"; + css_type = "xml_attribute_value"; + break; + + case Run.Style.XML_COMMENT: + tag = "span"; + css_type = "xml_comment"; + break; + + case Run.Style.XML_CDATA: + tag = "span"; + css_type = "xml_cdata"; + break; + } + if (tag != null) { + writer.start_tag (tag, {"class", css_type}); + } + element.accept_children (this); + if (tag != null) { + writer.end_tag (tag); + } + } + + public override void visit_source_code (SourceCode element) { + writer.set_wrap (false); + writer.start_tag ("pre", {"class", "main_source"}); + element.accept_children (this); + writer.end_tag ("pre"); + writer.set_wrap (true); + } + + public override void visit_table (Table element) { + writer.start_tag ("table", {"class", "main_table"}); + element.accept_children (this); + writer.end_tag ("table"); + } + + public override void visit_table_cell (TableCell element) { + string style = ""; + + if (element.horizontal_align != null) { + style += "text-align: "+element.horizontal_align.to_string ()+"; "; + } + + if (element.vertical_align != null) { + style += "vertical-align: "+element.vertical_align.to_string ()+"; "; + } + + writer.start_tag ("td", {"class", "main_table", + "colspan", element.colspan.to_string (), + "rowspan", element.rowspan.to_string (), + "style", style}); + element.accept_children (this); + writer.end_tag ("td"); + } + + public override void visit_table_row (TableRow element) { + writer.start_tag ("tr"); + element.accept_children (this); + writer.end_tag ("tr"); + } + + public override void visit_taglet (Taglet element) { + } + + public override void visit_text (Text element) { + write_string (element.content); + } + + private void write_string (string content) { + unichar chr = content[0]; + long lpos = 0; + int i = 0; + + for (i = 0; chr != '\0' ; i++, chr = content[i]) { + switch (chr) { + case '\n': + writer.text (content.substring (lpos, i-lpos)); + writer.simple_tag ("br"); + lpos = i+1; + break; + case '<': + writer.text (content.substring (lpos, i-lpos)); + writer.text ("<"); + lpos = i+1; + break; + case '>': + writer.text (content.substring (lpos, i-lpos)); + writer.text (">"); + lpos = i+1; + break; + case '&': + writer.text (content.substring (lpos, i-lpos)); + writer.text ("&"); + lpos = i+1; + break; + } + } + writer.text (content.substring (lpos, i-lpos)); + } +} + diff --git a/libvaladoc/html/linkhelper.vala b/libvaladoc/html/linkhelper.vala new file mode 100644 index 000000000..0f990bb43 --- /dev/null +++ b/libvaladoc/html/linkhelper.vala @@ -0,0 +1,189 @@ +/* linkhelper.vala + * + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Gee; + + + +public class Valadoc.Html.LinkHelper : Object { + protected Settings _settings = null; + + public bool enable_browsable_check { + default = true; + get; + set; + } + + public virtual string? get_package_link (Api.Package package, Settings settings) { + if (enable_browsable_check && !package.is_browsable (settings)) { + return null; + } + + return Path.build_filename (package.name, "index.htm"); + } + + public string? get_relative_link (Documentation from, Documentation to, Settings settings) { + _settings = settings; + + //TODO: find a better solution which does not require too much code ... + if (from is Api.Package) { + if (to is Api.Package) { + return from_package_to_package ((Api.Package) from, (Api.Package) to); + } else if (to is Api.Node) { + return from_package_to_node ((Api.Package) from, (Api.Node) to); + } else if (to is WikiPage) { + return from_package_to_wiki ((Api.Package) from, (WikiPage) to); + } else { + assert (true); + } + } else if (from is Api.Node) { + if (to is Api.Package) { + return from_node_to_package ((Api.Node) from, (Api.Package) to); + } else if (to is Api.Node) { + return from_node_to_node ((Api.Node) from, (Api.Node) to); + } else if (to is WikiPage) { + return from_node_to_wiki ((Api.Node) from, (WikiPage) to); + } else { + assert (true); + } + } else if (from is WikiPage) { + if (to is Api.Package) { + return from_wiki_to_package ((WikiPage) from, (Api.Package) to); + } else if (to is Api.Node) { + return from_wiki_to_node ((WikiPage) from, (Api.Node) to); + } else if (to is WikiPage) { + return from_wiki_to_wiki ((WikiPage) from, (WikiPage) to); + } else { + assert (true); + } + } else { + assert (true); + } + + return null; + } + + protected string translate_wiki_name (WikiPage page) { + var name = page.name; + return name.substring (0, name.last_index_of_char ('.')).replace ("/", ".") + ".htm"; + } + + + + + protected virtual string? from_package_to_package (Api.Package from, Api.Package to) { + if (enable_browsable_check && !to.is_browsable(_settings)) { + return null; + } + + if (from == to) { + return "#"; + } else { + return Path.build_filename ("..", to.name, "index.htm"); + } + } + + protected virtual string? from_package_to_wiki (Api.Package from, WikiPage to) { + if (from.is_package) { + return Path.build_filename ("..", _settings.pkg_name, translate_wiki_name (to)); + } else { + return translate_wiki_name (to); + } + } + + protected virtual string? from_package_to_node (Api.Package from, Api.Node to) { + if (enable_browsable_check && (!to.is_browsable(_settings) || !to.package.is_browsable (_settings))) { + return null; + } + + if (from == to.package) { + return Path.build_filename (to.get_full_name () + ".html"); + } else { + return Path.build_filename ("..", to.package.name, to.get_full_name () + ".html"); + } + } + + + + protected virtual string? from_wiki_to_package (WikiPage from, Api.Package to) { + if (enable_browsable_check && !to.is_browsable(_settings)) { + return null; + } + + if (to.is_package) { + return Path.build_filename ("..", to.name, "index.htm"); + } else { + return "index.htm"; + } + } + + protected virtual string? from_wiki_to_wiki (WikiPage from, WikiPage to) { + return translate_wiki_name (to); + } + + protected virtual string? from_wiki_to_node (WikiPage from, Api.Node to) { + if (enable_browsable_check && (!to.is_browsable(_settings) || !to.package.is_browsable (_settings))) { + return null; + } + + if (to.package.is_package) { + return Path.build_filename ("..", to.package.name, to.get_full_name () + ".html"); + } else { + return to.get_full_name () + ".html"; + } + } + + + + protected virtual string? from_node_to_package (Api.Node from, Api.Package to) { + if (enable_browsable_check && !to.is_browsable (_settings)) { + return null; + } + + if (from.package == to) { + return "index.htm"; + } else { + return Path.build_filename ("..", to.name, "index.htm"); + } + } + + protected virtual string? from_node_to_wiki (Api.Node from, WikiPage to) { + if (from.package.is_package) { + return Path.build_filename ("..", _settings.pkg_name, translate_wiki_name (to)); + } else { + return translate_wiki_name (to); + } + } + + protected virtual string? from_node_to_node (Api.Node from, Api.Node to) { + if (enable_browsable_check && (!to.is_browsable(_settings) || !to.package.is_browsable (_settings))) { + return null; + } + + if (from.package == to.package) { + return Path.build_filename (to.get_full_name() + ".html"); + } else { + return Path.build_filename ("..", to.package.name, to.get_full_name() + ".html"); + } + } + } + diff --git a/libvaladoc/importer/documentationimporter.vala b/libvaladoc/importer/documentationimporter.vala new file mode 100644 index 000000000..1a0f0bab5 --- /dev/null +++ b/libvaladoc/importer/documentationimporter.vala @@ -0,0 +1,47 @@ +/* resourcelocator.vala + * + * Copyright (C) 2008-2009 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +using Gee; + + +public abstract class Valadoc.Importer.DocumentationImporter : Object, ResourceLocator { + protected ModuleLoader modules; + protected Settings settings; + protected Api.Tree tree; + + public abstract string file_extension { get; } + + public DocumentationImporter (Api.Tree tree, ModuleLoader modules, Settings settings) { + this.settings = settings; + this.modules = null; + this.tree = tree; + } + + public virtual string resolve (string path) { + return path; + } + + public abstract void process (string filename); +} + + diff --git a/libvaladoc/importer/girdocumentationimporter.vala b/libvaladoc/importer/girdocumentationimporter.vala new file mode 100644 index 000000000..3228dbec1 --- /dev/null +++ b/libvaladoc/importer/girdocumentationimporter.vala @@ -0,0 +1,863 @@ +/* girdocumentationimporter.vala + * + * Copyright (C) 2008-2010 Jürg Billeter + * Copyright (C) 2011 Luca Bruno + * Copyright (C) 2011-2014 Florian Brosch + * + * 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: + * Jürg Billeter <j@bitron.ch> + * Luca Bruno <lucabru@src.gnome.org> + * Florian Brosch <flo.brosch@gmail.com> + */ + + +using Valadoc; +using GLib; +using Gee; + + +public class Valadoc.Importer.GirDocumentationImporter : DocumentationImporter { + public override string file_extension { + get { + return "gir"; + } + } + + private MarkupTokenType current_token; + private MarkupSourceLocation begin; + private MarkupSourceLocation end; + private MarkupReader reader; + + private DocumentationParser parser; + private ErrorReporter reporter; + private Api.SourceFile file; + + private string parent_c_identifier; + + private struct ImplicitParameterPos { + public int parameter; + public int position; + + public ImplicitParameterPos (int parameter, int position) { + this.parameter = parameter; + this.position = position; + } + } + + public GirDocumentationImporter (Api.Tree tree, DocumentationParser parser, + ModuleLoader modules, Settings settings, + ErrorReporter reporter) + { + base (tree, modules, settings); + this.reporter = reporter; + this.parser = parser; + } + + public override void process (string source_file) { + this.file = new Api.SourceFile (new Api.Package (Path.get_basename (source_file), true, null), + source_file, null, null); + this.reader = new MarkupReader (source_file, reporter); + + // xml prolog + next (); + next (); + + next (); + parse_repository (); + + reader = null; + file = null; + } + + private Api.FormalParameter? find_parameter (Api.Node node, string name) { + Gee.List<Api.Node> parameters = node.get_children_by_type (Api.NodeType.FORMAL_PARAMETER, false); + foreach (Api.Node param in parameters) { + if (((Api.FormalParameter) param).name == name) { + return (Api.FormalParameter) param; + } + } + + return null; + } + + private inline string? get_cparameter_name (string[] param_names, int length_pos) { + if (length_pos < 0 || param_names.length < length_pos) { + return null; + } + + return param_names[length_pos]; + } + + private void attach_comment (string cname, + Api.GirSourceComment? comment, + string[]? param_names = null, + ImplicitParameterPos[]? destroy_notifies = null, + ImplicitParameterPos[]? closures = null, + ImplicitParameterPos[]? array_lengths = null, + int array_length_ret = -1) + { + if (comment == null) { + return ; + } + + Api.Node? node = this.tree.search_symbol_cstr (null, cname); + if (node == null) { + return; + } + + if (param_names != null) { + foreach (ImplicitParameterPos pos in destroy_notifies) { + Api.FormalParameter? param = find_parameter (node, param_names[pos.parameter]); + if (param == null) { + continue ; + } + + param.implicit_destroy_cparameter_name + = get_cparameter_name (param_names, pos.position); + } + + foreach (ImplicitParameterPos pos in closures) { + Api.FormalParameter? param = find_parameter (node, param_names[pos.parameter]); + if (param == null) { + continue ; + } + + param.implicit_closure_cparameter_name + = get_cparameter_name (param_names, pos.position); + } + + foreach (ImplicitParameterPos pos in array_lengths) { + Api.FormalParameter? param = find_parameter (node, param_names[pos.parameter]); + if (param == null) { + continue ; + } + + param.implicit_array_length_cparameter_name + = get_cparameter_name (param_names, pos.position); + } + + if (node is Api.Callable) { + ((Api.Callable) node).implicit_array_length_cparameter_name + = get_cparameter_name (param_names, array_length_ret); + } + } + + Content.Comment? content = this.parser.parse (node, comment); + if (content == null) { + return; + } + + node.documentation = content; + } + + private void warning (string message) { + reporter.warning (this.file.relative_path, this.begin.line, this.begin.column, this.end.column, + this.reader.get_line_content (this.begin.line), message); + } + + private void error (string message) { + reporter.error (this.file.relative_path, this.begin.line, this.begin.column, this.end.column, + this.reader.get_line_content (this.begin.line), message); + } + + private void next () { + current_token = reader.read_token (out begin, out end); + + // Skip <annotation /> (only generated by valac) and <attribute /> + if (current_token == MarkupTokenType.START_ELEMENT + && (reader.name == "annotation" || reader.name == "attribute")) { + next (); // MarkupTokenType.END_ELEMENT, annotation / attribute + next (); + } + } + + private void start_element (string name) { + if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) { + // error + error ("expected start element of `%s'".printf (name)); + } + } + + private void end_element (string name) { + if (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) { + // error + error ("expected end element of `%s'".printf (name)); + } + next (); + } + + private const string GIR_VERSION = "1.2"; + + private void parse_repository () { + start_element ("repository"); + if (reader.get_attribute ("version") != GIR_VERSION) { + error ("unsupported GIR version %s (supported: %s)" + .printf (reader.get_attribute ("version"), GIR_VERSION)); + return; + } + next (); + + while (current_token == MarkupTokenType.START_ELEMENT) { + if (reader.name == "namespace") { + parse_namespace (); + } else if (reader.name == "include") { + parse_include (); + } else if (reader.name == "package") { + parse_package (); + } else if (reader.name == "c:include") { + parse_c_include (); + } else { + // error + error ("unknown child element `%s' in `repository'".printf (reader.name)); + skip_element (); + } + } + end_element ("repository"); + } + + private void parse_include () { + start_element ("include"); + next (); + + end_element ("include"); + } + + private void parse_package () { + start_element ("package"); + next (); + + end_element ("package"); + } + + private void parse_c_include () { + start_element ("c:include"); + next (); + + end_element ("c:include"); + } + + private void skip_element () { + next (); + + int level = 1; + while (level > 0) { + if (current_token == MarkupTokenType.START_ELEMENT) { + level++; + } else if (current_token == MarkupTokenType.END_ELEMENT) { + level--; + } else if (current_token == MarkupTokenType.EOF) { + error ("unexpected end of file"); + break; + } + next (); + } + } + + private void parse_namespace () { + start_element ("namespace"); + + next (); + while (current_token == MarkupTokenType.START_ELEMENT) { + if (reader.name == "alias") { + parse_alias (); + } else if (reader.name == "enumeration") { + parse_enumeration (); + } else if (reader.name == "bitfield") { + parse_bitfield (); + } else if (reader.name == "function") { + parse_method ("function"); + } else if (reader.name == "callback") { + parse_callback (); + } else if (reader.name == "record") { + parse_record (); + } else if (reader.name == "class") { + parse_class (); + } else if (reader.name == "interface") { + parse_interface (); + } else if (reader.name == "glib:boxed") { + parse_boxed ("glib:boxed"); + } else if (reader.name == "union") { + parse_union (); + } else if (reader.name == "constant") { + parse_constant (); + } else { + // error + error ("unknown child element `%s' in `namespace'".printf (reader.name)); + skip_element (); + } + } + + end_element ("namespace"); + } + + private void parse_alias () { + start_element ("alias"); + string c_identifier = reader.get_attribute ("c:type"); + next (); + + Api.GirSourceComment? comment = parse_symbol_doc (); + attach_comment (c_identifier, comment); + + parse_type (); + + end_element ("alias"); + } + + private Api.GirSourceComment? parse_symbol_doc () { + Api.GirSourceComment? comment = null; + + if (reader.name == "doc") { + start_element ("doc"); + next (); + + + if (current_token == MarkupTokenType.TEXT) { + comment = new Api.GirSourceComment (reader.content, file, begin.line, + begin.column, end.line, end.column); + next (); + } + + end_element ("doc"); + } + + while (true) { + if (reader.name == "doc-deprecated") { + Api.SourceComment? doc_deprecated = parse_doc ("doc-deprecated"); + if (doc_deprecated != null) { + if (comment == null) { + comment = new Api.GirSourceComment ("", file, begin.line, end.line, + begin.line, end.line); + } + + comment.deprecated_comment = doc_deprecated; + } + } else if (reader.name == "doc-version") { + Api.SourceComment? doc_version = parse_doc ("doc-version"); + if (doc_version != null) { + if (comment == null) { + comment = new Api.GirSourceComment ("", file, begin.line, end.line, + begin.line, end.line); + } + + comment.version_comment = doc_version; + } + } else if (reader.name == "doc-stability") { + Api.SourceComment? doc_stability = parse_doc ("doc-stability"); + if (doc_stability != null) { + if (comment == null) { + comment = new Api.GirSourceComment ("", file, begin.line, end.line, + begin.line, end.line); + } + + comment.stability_comment = doc_stability; + } + } else { + break; + } + } + + return comment; + } + + private Api.SourceComment? parse_doc (string element_name = "doc") { + if (reader.name != element_name) { + return null; + } + + start_element (element_name); + next (); + + Api.SourceComment? comment = null; + + if (current_token == MarkupTokenType.TEXT) { + comment = new Api.SourceComment (reader.content, file, begin.line, + begin.column, end.line, end.column); + next (); + } + + end_element (element_name); + return comment; + } + + private void parse_enumeration (string element_name = "enumeration") { + start_element (element_name); + this.parent_c_identifier = reader.get_attribute ("c:type"); + next (); + + Api.GirSourceComment? comment = parse_symbol_doc (); + attach_comment (this.parent_c_identifier, comment); + + while (current_token == MarkupTokenType.START_ELEMENT) { + if (reader.name == "member") { + parse_enumeration_member (); + } else if (reader.name == "function") { + skip_element (); + } else { + // error + error ("unknown child element `%s' in `%s'".printf (reader.name, element_name)); + skip_element (); + } + } + + this.parent_c_identifier = null; + end_element (element_name); + } + + private void parse_bitfield () { + parse_enumeration ("bitfield"); + } + + private void parse_enumeration_member () { + start_element ("member"); + string c_identifier = reader.get_attribute ("c:identifier"); + next (); + + Api.GirSourceComment? comment = parse_symbol_doc (); + attach_comment (c_identifier, comment); + + end_element ("member"); + } + + private void parse_return_value (out Api.SourceComment? comment, out int array_length_ret) { + start_element ("return-value"); + next (); + + comment = parse_doc (); + + parse_type (out array_length_ret); + + end_element ("return-value"); + } + + private void parse_parameter (out Api.SourceComment? comment, out string param_name, + out int destroy_pos, + out int closure_pos, out int array_length_pos) { + start_element ("parameter"); + param_name = reader.get_attribute ("name"); + array_length_pos = -1; + destroy_pos = -1; + closure_pos = -1; + + string? closure = reader.get_attribute ("closure"); + if (closure != null) { + closure_pos = int.parse (closure); + if (closure_pos < 0) { + warning ("invalid closure position"); + } + } + + string? destroy = reader.get_attribute ("destroy"); + if (destroy != null) { + destroy_pos = int.parse (destroy); + if (destroy_pos < 0) { + warning ("invalid destroy position"); + } + } + next (); + + comment = parse_doc (); + + if (reader.name == "varargs") { + start_element ("varargs"); + param_name = "..."; + next (); + + end_element ("varargs"); + } else { + parse_type (out array_length_pos); + } + + end_element ("parameter"); + } + + private void parse_type (out int array_length_pos = null) { + array_length_pos = -1; + + if (reader.name == "array") { + string? length = reader.get_attribute ("length"); + if (length != null) { + array_length_pos = int.parse (length); + if (array_length_pos < 0) { + warning ("invalid array lenght position"); + } + } + + skip_element (); + } else { + skip_element (); + } + } + + private void parse_record () { + start_element ("record"); + this.parent_c_identifier = reader.get_attribute ("c:type"); + if (this.parent_c_identifier.has_suffix ("Private")) { + this.parent_c_identifier = null; + skip_element (); + return ; + } + + bool is_type_struct = (reader.get_attribute ("glib:is-gtype-struct-for") != null); + next (); + + Api.GirSourceComment? comment = parse_symbol_doc (); + if (is_type_struct == false) { + attach_comment (this.parent_c_identifier, comment); + } + + while (current_token == MarkupTokenType.START_ELEMENT) { + if (reader.name == "field") { + parse_field (); + } else if (reader.name == "constructor") { + parse_constructor (); + } else if (reader.name == "method") { + parse_method ("method"); + } else if (reader.name == "function") { + skip_element (); + } else if (reader.name == "union") { + parse_union (); + } else { + // error + error ("unknown child element `%s' in `record'".printf (reader.name)); + skip_element (); + } + } + + this.parent_c_identifier = null; + end_element ("record"); + } + + private void parse_class () { + start_element ("class"); + this.parent_c_identifier = reader.get_attribute ("c:type"); + next (); + + Api.GirSourceComment? comment = parse_symbol_doc (); + attach_comment (this.parent_c_identifier, comment); + + while (current_token == MarkupTokenType.START_ELEMENT) { + if (reader.name == "implements") { + skip_element (); + } else if (reader.name == "constant") { + parse_constant (); + } else if (reader.name == "field") { + parse_field (); + } else if (reader.name == "property") { + parse_property (); + } else if (reader.name == "constructor") { + parse_constructor (); + } else if (reader.name == "function") { + parse_method ("function"); + } else if (reader.name == "method") { + parse_method ("method"); + } else if (reader.name == "virtual-method") { + parse_method ("virtual-method"); + } else if (reader.name == "union") { + parse_union (); + } else if (reader.name == "glib:signal") { + parse_signal (); + } else { + // error + error ("unknown child element `%s' in `class'".printf (reader.name)); + skip_element (); + } + } + + this.parent_c_identifier = null; + end_element ("class"); + } + + private void parse_interface () { + start_element ("interface"); + this.parent_c_identifier = reader.get_attribute ("c:type"); + next (); + + Api.GirSourceComment? comment = parse_symbol_doc (); + attach_comment (this.parent_c_identifier, comment); + + while (current_token == MarkupTokenType.START_ELEMENT) { + if (reader.name == "prerequisite") { + skip_element (); + } else if (reader.name == "field") { + parse_field (); + } else if (reader.name == "property") { + parse_property (); + } else if (reader.name == "virtual-method") { + parse_method ("virtual-method"); + } else if (reader.name == "function") { + parse_method ("function"); + } else if (reader.name == "method") { + parse_method ("method"); + } else if (reader.name == "glib:signal") { + parse_signal (); + } else { + // error + error ("unknown child element `%s' in `interface'".printf (reader.name)); + skip_element (); + } + } + + this.parent_c_identifier = null; + end_element ("interface"); + } + + private void parse_field () { + start_element ("field"); + string c_identifier = reader.get_attribute ("name"); + if (this.parent_c_identifier != null) { + c_identifier = this.parent_c_identifier + "." + c_identifier; + } + next (); + + parse_symbol_doc (); + + parse_type (); + + end_element ("field"); + } + + private void parse_property () { + start_element ("property"); + string c_identifier = "%s:%s".printf (parent_c_identifier, reader.get_attribute ("name") + .replace ("-", "_")); + next (); + + Api.GirSourceComment? comment = parse_symbol_doc (); + attach_comment (c_identifier, comment); + + parse_type (); + + end_element ("property"); + } + + private void parse_callback () { + parse_function ("callback"); + } + + private void parse_constructor () { + parse_function ("constructor"); + } + + private void parse_function (string element_name) { + start_element (element_name); + + string? c_identifier = null; + switch (element_name) { + case "constructor": + case "function": + case "method": + c_identifier = reader.get_attribute ("c:identifier"); + break; + + case "callback": + c_identifier = reader.get_attribute ("c:type"); + break; + + case "virtual-method": + c_identifier = "%s->%s".printf (this.parent_c_identifier, reader.get_attribute ("name") + .replace ("-", "_")); + break; + + case "glib:signal": + c_identifier = "%s::%s".printf (this.parent_c_identifier, reader.get_attribute ("name") + .replace ("-", "_")); + break; + + default: + skip_element (); + return ; + } + + next (); + + Api.GirSourceComment? comment = parse_symbol_doc (); + + ImplicitParameterPos[] destroy_notifies = new ImplicitParameterPos[0]; + ImplicitParameterPos[] array_lengths = new ImplicitParameterPos[0]; + ImplicitParameterPos[] closures = new ImplicitParameterPos[0]; + string[] param_names = new string[0]; + int array_length_ret = -1; + + if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") { + Api.SourceComment? return_comment; + parse_return_value (out return_comment, out array_length_ret); + if (return_comment != null) { + if (comment == null) { + comment = new Api.GirSourceComment ("", file, begin.line, begin.column, + end.line, end.column); + } + comment.return_comment = return_comment; + } + } + + + if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") { + start_element ("parameters"); + next (); + + if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "instance-parameter") { + string instance_param_name = reader.get_attribute ("name"); + next (); + + Api.SourceComment? param_comment = parse_doc (); + parse_type (null); + end_element ("instance-parameter"); + + if (param_comment != null) { + if (comment == null) { + comment = new Api.GirSourceComment ("", file, begin.line, begin.column, + end.line, end.column); + } + + comment.add_parameter_content (instance_param_name, param_comment); + comment.instance_param_name = instance_param_name; + } + } + + for (int pcount = 0; current_token == MarkupTokenType.START_ELEMENT; pcount++) { + Api.SourceComment? param_comment; + int array_length_pos; + int destroy_pos; + int closure_pos; + string? param_name; + + parse_parameter (out param_comment, out param_name, out destroy_pos, + out closure_pos, out array_length_pos); + param_names += param_name; + + if (destroy_pos >= 0 && pcount != destroy_pos) { + destroy_notifies += ImplicitParameterPos (pcount, destroy_pos); + } + + if (closure_pos >= 0 && pcount != closure_pos) { + closures += ImplicitParameterPos (pcount, closure_pos); + } + + if (array_length_pos >= 0 && pcount != destroy_pos) { + array_lengths += ImplicitParameterPos (pcount, array_length_pos); + } + + if (param_comment != null) { + if (comment == null) { + comment = new Api.GirSourceComment ("", file, begin.line, begin.column, + end.line, end.column); + } + + comment.add_parameter_content (param_name, param_comment); + } + } + end_element ("parameters"); + } + + attach_comment (c_identifier, comment, param_names, destroy_notifies, closures, + array_lengths, array_length_ret); + + end_element (element_name); + } + + private void parse_method (string element_name) { + parse_function (element_name); + } + + private void parse_signal () { + parse_function ("glib:signal"); + } + + private void parse_boxed (string element_name) { + start_element (element_name); + + this.parent_c_identifier = reader.get_attribute ("name"); + if (this.parent_c_identifier == null) { + this.parent_c_identifier = reader.get_attribute ("glib:name"); + } + + next (); + + parse_symbol_doc (); + + // TODO: process comments + + while (current_token == MarkupTokenType.START_ELEMENT) { + if (reader.name == "field") { + parse_field (); + } else if (reader.name == "constructor") { + parse_constructor (); + } else if (reader.name == "method") { + parse_method ("method"); + } else if (reader.name == "function") { + skip_element (); + } else if (reader.name == "union") { + parse_union (); + } else { + // error + error ("unknown child element `%s' in `class'".printf (reader.name)); + skip_element (); + } + } + + this.parent_c_identifier = null; + end_element (element_name); + } + + private void parse_union () { + start_element ("union"); + this.parent_c_identifier = reader.get_attribute ("c:type"); + if (this.parent_c_identifier == null) { + skip_element (); + return ; + } + next (); + + Api.GirSourceComment? comment = parse_symbol_doc (); + attach_comment (this.parent_c_identifier, comment); + + while (current_token == MarkupTokenType.START_ELEMENT) { + if (reader.name == "field") { + parse_field (); + } else if (reader.name == "constructor") { + parse_constructor (); + } else if (reader.name == "method") { + parse_method ("method"); + } else if (reader.name == "function") { + skip_element (); + } else if (reader.name == "record") { + parse_record (); + } else { + // error + error ("unknown child element `%s' in `union'".printf (reader.name)); + skip_element (); + } + } + + this.parent_c_identifier = null; + end_element ("union"); + } + + private void parse_constant () { + start_element ("constant"); + string c_identifier = reader.get_attribute ("c:type"); + next (); + + Api.GirSourceComment? comment = parse_symbol_doc (); + attach_comment (c_identifier, comment); + + parse_type (); + + end_element ("constant"); + } +} + diff --git a/libvaladoc/importer/internalidregistrar.vala b/libvaladoc/importer/internalidregistrar.vala new file mode 100644 index 000000000..a9ece0117 --- /dev/null +++ b/libvaladoc/importer/internalidregistrar.vala @@ -0,0 +1,88 @@ +/* gtkdocindexsgmlreader.vala + * + * Copyright (C) 2014 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +using Valadoc; +using Gee; + + +public class Valadoc.Importer.InternalIdRegistrar { + private HashMap<string, Api.Node> symbol_map; + private HashMap<string, string> map; + + + public InternalIdRegistrar () { + map = new HashMap<string, string> (); + symbol_map = new HashMap<string, Api.Node> (); + } + + + public void register_symbol (string id, Api.Node symbol) { + this.symbol_map.set (id, symbol); + } + + public string? map_url_id (string id) { + return map.get (id); + } + + public Api.Node? map_symbol_id (string id) { + return symbol_map.get (id); + } + + + public void read_index_sgml_file (string filename, string? index_sgml_online, ErrorReporter reporter) { + MarkupSourceLocation begin; + MarkupSourceLocation end; + MarkupTokenType token; + + string base_path = index_sgml_online ?? realpath (filename); + var reader = new MarkupReader (filename, reporter); + + while ((token = reader.read_token (out begin, out end)) != MarkupTokenType.EOF) { + if (token == MarkupTokenType.START_ELEMENT && reader.name == "ONLINE") { + if (index_sgml_online == null) { + base_path = reader.get_attribute ("href"); + if (base_path == null) { + reporter.error (filename, begin.line, begin.column, end.column, reader.get_line_content (begin.line), "missing attribute `href' in <ONLINE>"); + } + } + } else if (token == MarkupTokenType.START_ELEMENT && reader.name == "ANCHOR") { + string id = reader.get_attribute ("id"); + if (id == null) { + reporter.error (filename, begin.line, begin.column, end.column, reader.get_line_content (begin.line), "missing attribute `id' in <ANCHOR>"); + } + + string href = reader.get_attribute ("href"); + if (href == null) { + reporter.error (filename, begin.line, begin.column, end.column, reader.get_line_content (begin.line), "missing attribute `href' in <ANCHOR>"); + } else if (index_sgml_online != null) { + href = Path.get_basename (href); + } + + map.set (id, Path.build_path ("/", base_path, href)); + } else { + reporter.error (filename, begin.line, begin.column, end.column, reader.get_line_content (begin.line), "expected element of <ONLINE> or <ANCHOR>"); + } + } + } +} + + diff --git a/libvaladoc/importer/valadocdocumentationimporter.vala b/libvaladoc/importer/valadocdocumentationimporter.vala new file mode 100644 index 000000000..b74b430f6 --- /dev/null +++ b/libvaladoc/importer/valadocdocumentationimporter.vala @@ -0,0 +1,199 @@ +/* resourcelocator.vala + * + * Copyright (C) 2010 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +using Gee; +using Valadoc; +using Valadoc.Content; + +public class Valadoc.Importer.ValadocDocumentationImporter : DocumentationImporter, ResourceLocator { + public override string file_extension { get { return "valadoc"; } } + + private ValadocDocumentationScanner _scanner; + private DocumentationParser _doc_parser; + private Parser _parser; + + private MappedFile _mapped_file; + private string _filename; + private string _cname; + private StringBuilder _comment; + private SourceLocation _comment_location; + protected Content.ContentFactory factory; + + + private ErrorReporter reporter; + + public ValadocDocumentationImporter (Api.Tree tree, DocumentationParser parser, ModuleLoader modules, + Settings settings, ErrorReporter reporter) + { + base (tree, modules, settings); + this.factory = new Content.ContentFactory (settings, this, modules); + this.reporter = reporter; + + _scanner = new ValadocDocumentationScanner (settings); + _doc_parser = parser; + + _scanner = new ValadocDocumentationScanner (settings); + _parser = new Parser (settings, _scanner, reporter); + _scanner.set_parser (_parser); + + _comment = new StringBuilder (); + + // init parser rules: + Rule unprinted_spaces = Rule.many ({ + Rule.one_of ({ + TokenType.VALADOC_SPACE, + TokenType.VALADOC_TAB + }) + }); + + Rule empty_lines = Rule.many ({ + Rule.one_of ({ + unprinted_spaces, + TokenType.VALADOC_EOL + }) + }) + .set_name ("EmptyLines"); + + Rule optional_empty_lines = Rule.option ({ + empty_lines + }); + + Rule documentation = Rule.one_of ({ + Rule.seq ({ + TokenType.VALADOC_COMMENT_START.action ((token) => { _comment_location = token.end; }), + Rule.many ({ + Rule.one_of ({ + TokenType.ANY_WORD.action ((token) => { _comment.append (token.to_string ()); }), + TokenType.VALADOC_COMMENT_START.action ((token) => { _comment.append (token.to_string ()); }), + TokenType.VALADOC_SPACE.action ((token) => { _comment.append (token.to_string ()); }), + TokenType.VALADOC_TAB.action ((token) => { _comment.append (token.to_string ()); }), + TokenType.VALADOC_EOL.action ((token) => { _comment.append (token.to_string ()); }) + }) + }), + TokenType.VALADOC_COMMENT_END, + optional_empty_lines, + TokenType.ANY_WORD.action ((token) => { _cname = token.to_string (); }) + }) + .set_reduce (() => { + add_documentation (_cname, _comment, _filename, _comment_location); + _comment.erase (); + _cname = null; + }), + + TokenType.ANY_WORD.action ((token) => { + add_documentation (token.to_string (), null, _filename, _comment_location); + }) + }) + .set_name ("Documentation"); + + Rule file = Rule.many ({ + Rule.one_of ({ + documentation, + optional_empty_lines + }) + }) + .set_name ("ValadocFile"); + + _parser.set_root_rule (file); + } + + private enum InsertionMode { + APPEND, + PREPEND, + REPLACE + } + + private void add_documentation (string _symbol_name, StringBuilder? comment, string filename, + SourceLocation src_ref) + { + Api.Node? symbol = null; + + InsertionMode insertion_mode; + string symbol_name; + if (_symbol_name.has_suffix ("::append")) { + symbol_name = _symbol_name.substring (0, _symbol_name.length - 8); + insertion_mode = InsertionMode.APPEND; + } else if (_symbol_name.has_suffix ("::prepend")) { + symbol_name = _symbol_name.substring (0, _symbol_name.length - 9); + insertion_mode = InsertionMode.PREPEND; + } else { + symbol_name = _symbol_name; + insertion_mode = InsertionMode.REPLACE; + } + + if (symbol_name.has_prefix ("c::")) { + symbol = tree.search_symbol_cstr (null, symbol_name.substring (3)); + } else { + symbol = tree.search_symbol_str (null, symbol_name); + } + + if (symbol == null) { + if (settings.verbose) { + reporter.simple_warning (filename, "Node `%s' does not exist", symbol_name); + } + + return ; + } + + if (comment != null) { + var docu = _doc_parser.parse_comment_str (symbol, comment.str, filename, src_ref.line, src_ref.column); + if (docu != null) { + docu.check (tree, symbol, filename, reporter, settings); + + if (symbol.documentation == null || insertion_mode == InsertionMode.REPLACE) { + if (insertion_mode == InsertionMode.APPEND) { + docu.content.insert (0, factory.create_paragraph ()); + } + symbol.documentation = docu; + } else if (insertion_mode == InsertionMode.APPEND) { + symbol.documentation.content.add_all (docu.content); + merge_taglets (symbol.documentation, docu); + } else if (insertion_mode == InsertionMode.PREPEND) { + symbol.documentation.content.insert_all (0, docu.content); + merge_taglets (symbol.documentation, docu); + } + } + } + } + + private void merge_taglets (Comment comment, Comment imported) { + foreach (Taglet taglet in imported.taglets) { + imported.taglets.add (taglet); + } + } + + public override void process (string filename) { + try { + _filename = filename; + _mapped_file = new MappedFile (filename, false); + var content = _mapped_file.get_contents (); + if (content != null) { + _parser.parse ((string) content, filename, 0, 0); + } + } catch (FileError err) { + reporter.simple_error (null, "Unable to map file `%s': %s", filename, err.message); + } catch (ParserError err) { + } + } +} + diff --git a/libvaladoc/importer/valadocdocumentationimporterscanner.vala b/libvaladoc/importer/valadocdocumentationimporterscanner.vala new file mode 100644 index 000000000..5c4a4428d --- /dev/null +++ b/libvaladoc/importer/valadocdocumentationimporterscanner.vala @@ -0,0 +1,187 @@ +/* valadodocumentationscanner.vala + * + * Copyright (C) 2010 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + +public class Valadoc.Importer.ValadocDocumentationScanner : Object, Scanner { + + public ValadocDocumentationScanner (Settings settings) { + _settings = settings; + } + + private Settings _settings; + private Parser _parser; + + private string _content; + private unowned string _index; + private bool _stop; + private int _last_line; + private int _last_column; + private int _line; + private int _column; + private unichar _last_char; + private int _skip; + private StringBuilder _current_string = new StringBuilder (); + + public void set_parser (Parser parser) { + _parser = parser; + } + + public virtual void reset () { + _stop = false; + _last_line = 0; + _last_column = 0; + _line = 0; + _column = 0; + _last_char = 0; + _skip = 0; + _current_string.erase (0, -1); + } + + public void scan (string content) throws ParserError { + this._content = content; + + for (_index = _content; !_stop && _index.get_char () != 0; _index = _index.next_char ()) { + unichar c = _index.get_char (); + accept (c); + } + } + + public void end () throws ParserError { + emit_token (TokenType.EOF); + } + + public virtual void stop () { + _stop = true; + } + + public int get_line () { + return _line; + } + + public virtual string get_line_content () { + StringBuilder builder = new StringBuilder (); + weak string line_start = _index; + unichar c; + + while ((char*) line_start > (char*) _content && line_start.prev_char ().get_char () != '\n') { + line_start = line_start.prev_char (); + } + + while ((c = line_start.get_char ()) != '\n' && c != '\0') { + if (c == '\t') { + builder.append_c (' '); + } else { + builder.append_unichar (c); + } + line_start = line_start.next_char (); + } + + return builder.str; + } + + protected unichar get_next_char (int offset = 1) { + return _index.get_char (_index.index_of_nth_char (offset)); + } + + + protected void accept (unichar c) throws ParserError { + _column++; + if (_skip == 0) { + switch (c) { + case '/': + if (get_next_char (1) == '*') { + emit_token (TokenType.VALADOC_COMMENT_START); + _skip = 1; + } else { + append_char (c); + } + break; + + case '*': + if (get_next_char (1) == '/') { + emit_token (TokenType.VALADOC_COMMENT_END); + _skip = 1; + } else { + append_char (c); + } + break; + + case '\t': + emit_token (TokenType.VALADOC_TAB); + break; + + case ' ': + emit_token (TokenType.VALADOC_SPACE); + break; + + case '\n': + emit_token (TokenType.VALADOC_EOL); + _line++; + _column = 0; + _last_column = 0; + break; + + default: + append_char (c); + break; + } + } else { + _skip--; + } + _last_char = c; + } + + private void append_char (unichar c) { + _current_string.append_unichar (c); + } + + public virtual int get_line_start_column () { + return 0; + } + + private SourceLocation get_begin () { + return SourceLocation (_last_line, get_line_start_column () + _last_column); + } + + private SourceLocation get_end (int offset = 0) { + return SourceLocation (_line, get_line_start_column () + _column + offset); + } + + private void emit_current_word () throws ParserError { + if (_current_string.len > 0) { + _parser.accept_token (new Token.from_word (_current_string.str, get_begin (), get_end (-1))); + _current_string.erase (0, -1); + + _last_line = _line; + _last_column = _column - 1; + } + } + + private void emit_token (TokenType type) throws ParserError { + emit_current_word (); + + _parser.accept_token (new Token.from_type (type, get_begin (), get_end (_skip))); + + _last_line = _line; + _last_column = _column; + } + +} diff --git a/libvaladoc/markupreader.vala b/libvaladoc/markupreader.vala new file mode 100644 index 000000000..9f24177e4 --- /dev/null +++ b/libvaladoc/markupreader.vala @@ -0,0 +1,337 @@ +/* markupreader.vala + * + * Copyright (C) 2008-2009 Jürg Billeter + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Jürg Billeter <j@bitron.ch> + */ + +using GLib; +using Gee; + + +/** + * Simple reader for a subset of XML. + */ +public class Valadoc.MarkupReader : Object { + public string filename { + private set; + get; + } + + public string name { + private set; + get; + } + + public string content { + private set; + get; + } + + private MappedFile mapped_file; + + private string[] lines; + private char* begin; + private char* current; + private char* end; + + private int line; + private int column; + + private Map<string, string> attributes = new HashMap<string, string> (); + private bool empty_element; + + private ErrorReporter reporter; + + public MarkupReader.from_string (string filename, string content, ErrorReporter reporter) { + this.filename = filename; + this.reporter = reporter; + + lines = content.split ("\n"); + begin = content; + end = begin + content.length; + current = begin; + + column = 1; + line = 1; + } + + public MarkupReader (string filename, ErrorReporter reporter) { + this.filename = filename; + this.reporter = reporter; + + try { + mapped_file = new MappedFile (filename, false); + begin = mapped_file.get_contents (); + lines = ((string) begin).split ("\n"); + end = begin + mapped_file.get_length (); + + current = begin; + + line = 1; + column = 1; + } catch (FileError e) { + reporter.simple_error (null, "Unable to map file '%s': %s", filename, e.message); + } + } + + public string? get_line_content (int line_nr) { + if (this.lines.length > line_nr) { + return this.lines[line_nr]; + } + + return null; + } + + public string? get_attribute (string attr) { + return attributes[attr]; + } + + /* + * Returns a copy of the current attributes. + * + * @return map of current attributes + */ + public Map<string,string> get_attributes () { + var result = new HashMap<string, string> (); + foreach (var key in attributes.keys) { + result.set (key, attributes.get (key)); + } + return result; + } + + private string read_name () { + char* begin = current; + while (current < end) { + if (current[0] == ' ' || current[0] == '\t' || current[0] == '>' + || current[0] == '/' || current[0] == '=' || current[0] == '\n') { + break; + } + unichar u = ((string) current).get_char_validated ((long) (end - current)); + if (u != (unichar) (-1)) { + current += u.to_utf8 (null); + } else { + reporter.simple_error ("%s:%d".printf (filename, line), + "invalid UTF-8 character"); + } + } + if (current == begin) { + // syntax error: invalid name + } + return ((string) begin).substring (0, (int) (current - begin)); + } + + public MarkupTokenType read_token (out MarkupSourceLocation token_begin, out MarkupSourceLocation token_end) { + attributes.clear (); + + if (empty_element) { + empty_element = false; + token_begin = MarkupSourceLocation (begin, line, column); + token_end = MarkupSourceLocation (begin, line, column); + return MarkupTokenType.END_ELEMENT; + } + + content = null; + name = null; + + space (); + + MarkupTokenType type = MarkupTokenType.NONE; + char* begin = current; + token_begin = MarkupSourceLocation (begin, line, column); + + if (current >= end) { + type = MarkupTokenType.EOF; + } else if (current[0] == '<') { + current++; + if (current >= end) { + // error + } else if (current[0] == '?') { + // processing instruction + } else if (current[0] == '!') { + // comment or doctype + current++; + if (current < end - 1 && current[0] == '-' && current[1] == '-') { + // comment + current += 2; + while (current < end - 2) { + if (current[0] == '-' && current[1] == '-' && current[2] == '>') { + // end of comment + current += 3; + break; + } else if (current[0] == '\n') { + line++; + column = 0; + } + current++; + } + + // ignore comment, read next token + return read_token (out token_begin, out token_end); + } + } else if (current[0] == '/') { + type = MarkupTokenType.END_ELEMENT; + current++; + name = read_name (); + if (current >= end || current[0] != '>') { + // error + } + current++; + } else { + type = MarkupTokenType.START_ELEMENT; + name = read_name (); + space (); + while (current < end && current[0] != '>' && current[0] != '/') { + string attr_name = read_name (); + if (current >= end || current[0] != '=') { + // error + } + current++; + // FIXME allow single quotes + if (current >= end || current[0] != '"') { + // error + } + current++; + + string attr_value = text ('"', false); + + if (current >= end || current[0] != '"') { + // error + } + current++; + attributes.set (attr_name, attr_value); + space (); + } + if (current[0] == '/') { + empty_element = true; + current++; + space (); + } else { + empty_element = false; + } + if (current >= end || current[0] != '>') { + // error + } + current++; + } + } else { + space (); + + if (current[0] != '<') { + content = text ('<', true); + } else { + // no text + // read next token + return read_token (out token_begin, out token_end); + } + + type = MarkupTokenType.TEXT; + } + + token_end = MarkupSourceLocation (current, line, column - 1); + + return type; + } + + private string text (char end_char, bool rm_trailing_whitespace) { + StringBuilder content = new StringBuilder (); + char* text_begin = current; + char* last_linebreak = current; + + while (current < end && current[0] != end_char) { + unichar u = ((string) current).get_char_validated ((long) (end - current)); + if (u == (unichar) (-1)) { + reporter.simple_error ("%s:%d".printf (filename, line), + "invalid UTF-8 character"); + } else if (u == '&') { + char* next_pos = current + u.to_utf8 (null); + if (((string) next_pos).has_prefix ("amp;")) { + content.append (((string) text_begin).substring (0, (int) (current - text_begin))); + content.append_c ('&'); + current += 5; + text_begin = current; + } else if (((string) next_pos).has_prefix ("quot;")) { + content.append (((string) text_begin).substring (0, (int) (current - text_begin))); + content.append_c ('"'); + current += 6; + text_begin = current; + } else if (((string) next_pos).has_prefix ("apos;")) { + content.append (((string) text_begin).substring (0, (int) (current - text_begin))); + content.append_c ('\''); + current += 6; + text_begin = current; + } else if (((string) next_pos).has_prefix ("lt;")) { + content.append (((string) text_begin).substring (0, (int) (current - text_begin))); + content.append_c ('<'); + current += 4; + text_begin = current; + } else if (((string) next_pos).has_prefix ("gt;")) { + content.append (((string) text_begin).substring (0, (int) (current - text_begin))); + content.append_c ('>'); + current += 4; + text_begin = current; + } else if (((string) next_pos).has_prefix ("percnt;")) { + content.append (((string) text_begin).substring (0, (int) (current - text_begin))); + content.append_c ('>'); + current += 8; + text_begin = current; + } else { + current += u.to_utf8 (null); + } + } else { + if (u == '\n') { + line++; + column = 0; + last_linebreak = current; + } + + current += u.to_utf8 (null); + column++; + } + } + + if (text_begin != current) { + content.append (((string) text_begin).substring (0, (int) (current - text_begin))); + } + + column += (int) (current - last_linebreak); + + // Removes trailing whitespace + if (rm_trailing_whitespace) { + char* str_pos = ((char*)content.str) + content.len; + for (str_pos--; str_pos > ((char*)content.str) && str_pos[0].isspace(); str_pos--); + content.erase ((ssize_t) (str_pos-((char*) content.str) + 1), -1); + } + + return content.str; + } + + private void space () { + while (current < end && current[0].isspace ()) { + if (current[0] == '\n') { + line++; + column = 0; + } + current++; + column++; + } + } +} + + diff --git a/libvaladoc/markupsourcelocation.vala b/libvaladoc/markupsourcelocation.vala new file mode 100644 index 000000000..af7dff1b2 --- /dev/null +++ b/libvaladoc/markupsourcelocation.vala @@ -0,0 +1,39 @@ +/* valasourcelocation.vala + * + * Copyright (C) 2008 Jürg Billeter + * + * 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: + * Jürg Billeter <j@bitron.ch> + */ + +using GLib; + +/** + * Represents a position in a source file. + */ +public struct Valadoc.MarkupSourceLocation { + public char* pos; + public int line; + public int column; + + public MarkupSourceLocation (char* _pos, int _line, int _column) { + pos = _pos; + line = _line; + column = _column; + } +} + diff --git a/libvaladoc/markuptokentype.vala b/libvaladoc/markuptokentype.vala new file mode 100644 index 000000000..d2dcad74f --- /dev/null +++ b/libvaladoc/markuptokentype.vala @@ -0,0 +1,51 @@ +/* markuptokentype.vala + * + * Copyright (C) 2008-2009 Jürg Billeter + * + * 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: + * Jürg Billeter <j@bitron.ch> + */ + + +public enum Valadoc.MarkupTokenType { + NONE, + COMMENT, + START_ELEMENT, + END_ELEMENT, + TEXT, + EOF; + + public string to_string () { + switch (this) { + case START_ELEMENT: + return "start element"; + + case END_ELEMENT: + return "end element"; + + case TEXT: + return "text"; + + case EOF: + return "end of file"; + + default: + return "unknown token type"; + } + } +} + diff --git a/libvaladoc/markupwriter.vala b/libvaladoc/markupwriter.vala new file mode 100644 index 000000000..28a61ffe7 --- /dev/null +++ b/libvaladoc/markupwriter.vala @@ -0,0 +1,272 @@ +/* markupwriter.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + + +/** + * Writes markups and text to a file. + */ +public class Valadoc.MarkupWriter { + protected WriteFunc write; + protected int indent; + + protected long current_column = 0; + protected bool last_was_tag; + private bool wrap = true; + + public static string escape (string txt) { + StringBuilder builder = new StringBuilder (); + unowned string start = txt; + unowned string pos; + unichar c; + + for (pos = txt; (c = pos.get_char ()) != '\0'; pos = pos.next_char ()) { + switch (c) { + case '"': + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + builder.append ("""); + start = pos.next_char (); + break; + + case '<': + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + builder.append ("<"); + start = pos.next_char (); + break; + + case '>': + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + builder.append (">"); + start = pos.next_char (); + break; + + case '&': + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + builder.append ("&"); + start = pos.next_char (); + break; + + case '\'': + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + builder.append ("'"); + start = pos.next_char (); + break; + } + } + + if (&txt == &start) { + return txt; + } else { + builder.append_len (start, (ssize_t) ((char*) pos - (char*) start)); + return (owned) builder.str; + } + } + + /** + * Writes text to a desination like a {@link GLib.StringBuilder} or a {@link GLib.FileStream} + */ + public delegate void WriteFunc (string text); + + private const int MAX_COLUMN = 150; + + /** + * Initializes a new instance of the MarkupWriter + * + * @param write stream a WriteFunc + * @param xml_declaration specifies whether this file starts with an xml-declaration + */ + public MarkupWriter (owned WriteFunc write, bool xml_declaration = true) { + this.write = (owned) write; + if (xml_declaration) { + do_write ("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); + } + indent = -1; + last_was_tag = true; + } + + /** + * Writes an start tag of a markup element to the file + * + * @param name the name of the markup + * @param attributes a list of name/value pairs + * @return this + */ + public MarkupWriter start_tag (string name, string[]? attributes=null) { + indent++; + check_column (name); + + if (attributes.length % 2 != 0) { + attributes.resize (attributes.length+1); + attributes[attributes.length-1] = ""; + } + + var content = new StringBuilder ("<"); + content.append (name); + for (int i = 0; i < attributes.length; i=i+2) { + if (attributes[i+1] != null) { + content.append_printf (" %s=\"%s\"", attributes[i], attributes[i+1]); + } + } + content.append (">"); + + do_write (content.str); + last_was_tag = true; + return this; + } + + /** + * Writes a simple tag (<name />) to the file + * + * @param name the name of the markup + * @param attributes a list of name/value pairs + * @return this + */ + public MarkupWriter simple_tag (string name, string[]? attributes=null) { + indent++; + check_column (name); + + if (attributes.length % 2 != 0) { + attributes.resize (attributes.length+1); + attributes[attributes.length-1] = ""; + } + + var content = new StringBuilder ("<"); + content.append (name); + for (int i = 0; i < attributes.length; i=i+2) { + if (attributes[i+1] != null) { + content.append_printf (" %s=\"%s\"", attributes[i], attributes[i+1]); + } + } + content.append ("/>"); + + do_write (content.str); + indent--; + last_was_tag = true; + return this; + } + + /** + * Writes an end tag of a markup element to the file + * + * @param name the name of the markup + * @return this + */ + public MarkupWriter end_tag (string name) { + check_column (name, true); + do_write ("</%s>".printf (name)); + indent--; + last_was_tag = true; + return this; + } + + /** + * Writes the specified string to the output stream + * + * @see raw_text + * @return this + */ + public MarkupWriter text (string text) { + if (wrap && text.length + current_column > MAX_COLUMN) { + long wrote = 0; + while (wrote < text.length) { + long space_pos = -1; + for (long i = wrote + 1; i < text.length; i++) { + if (text[i] == ' ') { + if (i - wrote + current_column > MAX_COLUMN) { + break; + } + space_pos = i; + } + } + if (text.length - wrote + current_column <= MAX_COLUMN) { + do_write (text.substring (wrote)); + wrote = text.length + 1; + } else if (space_pos == -1) { + // Force line break + } else { + do_write (text.substring (wrote, space_pos - wrote)); + wrote = space_pos + 1; + } + if (wrote < text.length) { + break_line (); + do_write (" "); + } + } + } else { + do_write (text); + } + last_was_tag = false; + return this; + } + + /** + * Writes the specified string to the output stream + * + * @see text + * @return this + */ + public MarkupWriter raw_text (string text) { + do_write (text); + last_was_tag = false; + return this; + } + + public void set_wrap (bool wrap) { + this.wrap = wrap; + } + + private void break_line () { + write ("\n"); + write (string.nfill (indent * 2, ' ')); + current_column = indent * 2; + } + + protected void do_write (string text) { + if (wrap && current_column + text.length > MAX_COLUMN) { + break_line (); + } + write (text); + current_column += text.length; + } + + private void check_column (string name, bool end_tag = false) { + if (!wrap) { + return; + } else if (!end_tag && inline_element (name) /*&& !last_was_tag*/) { + return; + } else if (end_tag && content_inline_element (name)) { + return; + } else if (end_tag && !last_was_tag) { + return; + } + break_line (); + } + + protected virtual bool inline_element (string name) { + return false; + } + + protected virtual bool content_inline_element (string name) { + return true; + } +} + + diff --git a/libvaladoc/moduleloader.vala b/libvaladoc/moduleloader.vala new file mode 100644 index 000000000..911be76f7 --- /dev/null +++ b/libvaladoc/moduleloader.vala @@ -0,0 +1,254 @@ +/* moduleloader.vala + * + * Copyright (C) 2008-2009 Florian Brosch + * Copyright (C) 2011 Florian Brosch + * + * 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: + * Brosch Florian <flo.brosch@gmail.com> + */ + +using Gee; + + +[CCode (has_target = false)] +public delegate void Valadoc.TagletRegisterFunction (ModuleLoader loader); + + + + +public class Valadoc.ModuleLoader : Object { + private HashMap<string, ModuleData> doclets = new HashMap<string, ModuleData> (); + private HashMap<string, ModuleData> drivers = new HashMap<string, ModuleData> (); + private HashMap<string, GLib.Type> taglets = new HashMap<string, GLib.Type> (); + + private static ModuleLoader instance; + + public static ModuleLoader get_instance () { + if (instance == null) { + instance = new ModuleLoader (); + Taglets.init (instance); + } + return instance; + } + + private ModuleLoader () { + } + + private class ModuleData : Object { + public Module module; + public Type type; + } + + + // + // driver path helpers: + // + + private struct DriverMetaData { + public int64[] segments_min; + public int64[] segments_max; + public string driver_name; + + public DriverMetaData (int64 min_a, int64 min_b, int64 max_a, int64 max_b, string driver_name) { + this.segments_min = {min_a, min_b}; + this.segments_max = {max_a, max_b}; + this.driver_name = driver_name; + } + } + + public static bool is_driver (string path) { + string library_path = Path.build_filename (path, "libdriver." + Module.SUFFIX); + return FileUtils.test (path, FileTest.EXISTS) && FileUtils.test (library_path, FileTest.EXISTS); + } + + public static bool is_doclet (string path) { + string library_path = Path.build_filename (path, "libdoclet." + Module.SUFFIX); + return FileUtils.test (path, FileTest.EXISTS) && FileUtils.test (library_path, FileTest.EXISTS); + } + + private static string get_plugin_path (string pluginpath, string pluginsubdir) { + if (Path.is_absolute (pluginpath) == false) { + // Test to see if the plugin exists in the expanded path and then fallback + // to using the configured plugin directory + string local_path = Path.build_filename (Environment.get_current_dir(), pluginpath); + if (is_doclet(local_path)) { + return local_path; + } else { + return Path.build_filename (Config.plugin_dir, pluginsubdir, pluginpath); + } + } + + return pluginpath; + } + + public static string get_doclet_path (string? docletpath, ErrorReporter reporter) { + if (docletpath == null) { + return Path.build_filename (Config.plugin_dir, "doclets", "html"); + } + + return get_plugin_path (docletpath, "doclets"); + } + + public static string? get_driver_path (string? _driverpath, ErrorReporter reporter) { + string? driverpath = _driverpath; + // no driver selected + if (driverpath == null) { + driverpath = Config.default_driver; + } + + + // selected string is a plugin directory + string extended_driver_path = get_plugin_path (driverpath, "drivers"); + if (is_driver (extended_driver_path)) { + return extended_driver_path; + } + + + // selected string is a `valac --version` number: + if (driverpath.has_prefix ("Vala ")) { + if (driverpath.has_suffix ("-dirty")) { + driverpath = driverpath.substring (5, driverpath.length - 6 - 5); + } else { + driverpath = driverpath.substring (5); + } + } + + string[] segments = driverpath.split ("."); + if (segments.length != 2 && // e.g. 0.20, --pkg-version + segments.length != 3 && // e.g. 0.20.3, --version + segments.length != 4) // e.g. Vala 0.18.0.60-a4cdb, --version + { + reporter.simple_error (null, "Invalid driver version format."); + return null; + } + + + int64 segment_a; + int64 segment_b; + int64 segment_c = 0; + bool tmp; + + tmp = int64.try_parse (segments[0], out segment_a); + tmp &= int64.try_parse (segments[1], out segment_b); + + if (segments.length > 2) { + tmp &= int64.try_parse (segments[2], out segment_c); + } + + if (!tmp) { + reporter.simple_error (null, "Invalid driver version format."); + return null; + } + + DriverMetaData[] lut = { + DriverMetaData (0, 19, 0, 20, "0.20.x"), + DriverMetaData (0, 21, 0, 22, "0.22.x"), + DriverMetaData (0, 23, 0, 24, "0.24.x"), + DriverMetaData (0, 25, 0, 26, "0.26.x"), + DriverMetaData (0, 27, 0, 28, "0.28.x"), + DriverMetaData (0, 29, 0, 30, "0.30.x"), + DriverMetaData (0, 31, 0, 32, "0.32.x"), + DriverMetaData (0, 33, 0, 34, "0.34.x"), + DriverMetaData (0, 35, 0, 36, "0.36.x") + }; + + + for (int i = 0; i < lut.length ; i++) { + bool frst_seg = lut[i].segments_min[0] <= segment_a && lut[i].segments_max[0] >= segment_a; + bool scnd_seg = lut[i].segments_min[1] <= segment_b && lut[i].segments_max[1] >= segment_b; + if (frst_seg && scnd_seg) { + return Path.build_filename (Config.plugin_dir, "drivers", lut[i].driver_name); + } + } + + + reporter.simple_error (null, "No suitable driver found for libvala version " + + "%" + int64.FORMAT_MODIFIER + "d.%" + int64.FORMAT_MODIFIER + "d.%" + int64.FORMAT_MODIFIER + "d. " + + "Ensure the selected vala version was installed while building valadoc or use --driver to select another one.", + segment_a, segment_b, segment_c); + return null; + } + + // + // Creation methods: + // + + public Content.Taglet? create_taglet (string keyword) { + return (taglets.has_key (keyword))? (Content.Taglet) GLib.Object.new (taglets.get (keyword)) : null; + } + + public void register_taglet (string keyword, Type type) { + taglets.set (keyword, type); + } + + public Doclet? create_doclet (string _path) { + string path = realpath (_path); + + ModuleData? data = doclets.get (path); + if (data == null) { + void* function; + + Module? module = Module.open (Module.build_path (path, "libdoclet"), ModuleFlags.BIND_LAZY | ModuleFlags.BIND_LOCAL); + if (module == null) { + return null; + } + + module.symbol("register_plugin", out function); + if (function == null) { + return null; + } + + Valadoc.DocletRegisterFunction register_func = (Valadoc.DocletRegisterFunction) function; + data = new ModuleData (); + doclets.set (path, data); + + data.type = register_func (this); + data.module = (owned) module; + } + + return (Doclet) GLib.Object.new (data.type); + } + + public Driver? create_driver (string _path) { + string path = realpath (_path); + + ModuleData? data = drivers.get (path); + if (data == null) { + void* function; + + Module? module = Module.open (Module.build_path (path, "libdriver"), ModuleFlags.BIND_LAZY | ModuleFlags.BIND_LOCAL); + if (module == null) { + return null; + } + + module.symbol("register_plugin", out function); + if (function == null) { + return null; + } + + Valadoc.DriverRegisterFunction register_func = (Valadoc.DriverRegisterFunction) function; + data = new ModuleData (); + drivers.set (path, data); + + data.type = register_func (this); + data.module = (owned) module; + } + + return (Driver) GLib.Object.new (data.type); + } +} + diff --git a/libvaladoc/parser/manyrule.vala b/libvaladoc/parser/manyrule.vala new file mode 100644 index 000000000..093223ac9 --- /dev/null +++ b/libvaladoc/parser/manyrule.vala @@ -0,0 +1,112 @@ +/* manyrule.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +internal class Valadoc.ManyRule : Rule { + + public ManyRule (Object scheme) { + _scheme = scheme; + } + + private Object _scheme; + + private class State : Object { + public bool started = false; + public bool done_one = false; + } + + public override bool is_optional () { + return is_optional_rule (_scheme); + } + + public override bool starts_with_token (Token token) { + if (has_start_token (_scheme, token)) { + return true; + } + return false; + } + + public override bool accept_token (Token token, ParserCallback parser, Rule.Forward forward) + throws ParserError + { + var state = parser.get_rule_state () as State; + if (state == null) { + state = new State (); + parser.set_rule_state (state); + } + + if (!state.started) { + do_start (parser); + state.started = true; + } + + if (state.done_one && parser.would_parent_accept_token (token)) { + do_reduce (parser); + return false; + } + if (parser.would_parent_reduce_to_rule (token, this)) { + do_reduce (parser); + return false; + } + + bool handled; + if (try_to_apply (_scheme, token, parser, out handled)) { + state.done_one = true; + return handled; + } + if (parser.would_parent_accept_token (token)) { + do_reduce (parser); + return false; + } + + if (_scheme is TokenType) { + parser.error (null, "expected %s".printf (((TokenType) _scheme).to_pretty_string ())); + } else { + parser.error (token, "unexpected token"); + } + assert_not_reached (); + } + + public override bool would_accept_token (Token token, Object? state) { + if (has_start_token (_scheme, token)) { + return true; + } + return false; + } + + public override bool would_reduce (Token token, Object? rule_state) { + var state = rule_state as State; + return state.done_one || is_optional_rule (_scheme); + } + + public override string to_string (Object? rule_state) { + var state = rule_state as State; + if (state == null) { + state = new State (); + } + return "%-15s%-15s(started=%s;done_one=%s)".printf (name != null ? name : " ", + "[many]", + state.started.to_string (), + state.done_one.to_string ()); + } +} diff --git a/libvaladoc/parser/oneofrule.vala b/libvaladoc/parser/oneofrule.vala new file mode 100644 index 000000000..946335233 --- /dev/null +++ b/libvaladoc/parser/oneofrule.vala @@ -0,0 +1,98 @@ +/* oneofrule.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +internal class Valadoc.OneOfRule : Rule { + + public OneOfRule (Object[] scheme) { + _scheme = scheme; + } + + private Object[] _scheme; + + private class State : Object { + public int selected = -1; + } + + public override bool is_optional () { + return false; + } + + public override bool starts_with_token (Token token) { + foreach (Object? scheme_element in _scheme) { + if (has_start_token (scheme_element, token)) { + return true; + } + } + return false; + } + + public override bool accept_token (Token token, ParserCallback parser, Rule.Forward forward) + throws ParserError + { + var state = parser.get_rule_state () as State; + if (state == null) { + state = new State (); + parser.set_rule_state (state); + } + + if (state.selected == -1) { + do_start (parser); + + bool handled; + for (int i = 0; i < _scheme.length; i++) { + Object? scheme_element = _scheme[i]; + if (try_to_apply (scheme_element, token, parser, out handled)) { + state.selected = i; + return handled; + } + } + } else { + do_reduce (parser); + return false; + } + + parser.error (token, "unexpected token"); + assert_not_reached (); + } + + public override bool would_accept_token (Token token, Object? state) { + return false; + } + + public override bool would_reduce (Token token, Object? rule_state) { + var state = rule_state as State; + return state.selected != -1; + } + + public override string to_string (Object? rule_state) { + var state = rule_state as State; + if (state == null) { + state = new State (); + } + return "%-15s%-15s(selected=%d/%d)".printf (name != null ? name : " ", + "[one-of]", + state.selected, + _scheme.length); + } +} diff --git a/libvaladoc/parser/optionalrule.vala b/libvaladoc/parser/optionalrule.vala new file mode 100644 index 000000000..6945afa38 --- /dev/null +++ b/libvaladoc/parser/optionalrule.vala @@ -0,0 +1,88 @@ +/* optionalrule.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +internal class Valadoc.OptionalRule : Rule { + + public OptionalRule (Object scheme) { + _scheme = scheme; + } + + private Object _scheme; + + private class State : Object { + public bool started = false; + } + + public override bool is_optional () { + return true; + } + + public override bool starts_with_token (Token token) { + return has_start_token (_scheme, token); + } + + public override bool accept_token (Token token, ParserCallback parser, Rule.Forward forward) + throws ParserError + { + var state = parser.get_rule_state () as State; + if (state == null) { + state = new State (); + parser.set_rule_state (state); + } + + if (!state.started) { + do_start (parser); + state.started = true; + + bool handled; + if (try_to_apply (_scheme, token, parser, out handled)) { + return handled; + } else { + do_skip (parser); + return false; + } + } else { + do_reduce (parser); + return false; + } + } + + public override bool would_accept_token (Token token, Object? state) { + return false; + } + + public override bool would_reduce (Token token, Object? state) { + return true; + } + + public override string to_string (Object? rule_state) { + var state = rule_state as State; + if (state == null) { + state = new State (); + } + return "%-15s%-15s(started=%s)".printf (name != null ? name : " ", + "[option]", + state.started.to_string ()); + } +} diff --git a/libvaladoc/parser/parser.vala b/libvaladoc/parser/parser.vala new file mode 100644 index 000000000..bf3a59889 --- /dev/null +++ b/libvaladoc/parser/parser.vala @@ -0,0 +1,306 @@ +/* parser.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +public errordomain Valadoc.ParserError { + INTERNAL_ERROR, + UNEXPECTED_TOKEN +} + +public class Valadoc.Parser : ParserCallback { + + public Parser (Settings settings, Scanner scanner, ErrorReporter reporter) { + _settings = settings; + _scanner = scanner; + _reporter = reporter; + + TokenType.init_token_types (); + } + + private Settings _settings; + private Scanner _scanner; + private ErrorReporter _reporter; + private Rule _root_rule; + + private string _filename; + private int _first_line; + private int _first_column; + private Token _current_token; + + private ArrayList<Rule> rule_stack = new ArrayList<Rule> (); + private ArrayList<Object?> rule_state_stack = new ArrayList<Object?> (); + + public void set_root_rule (Rule root_rule) { + _root_rule = root_rule; + } + + public void parse (string content, string filename, int first_line, int first_column) + throws ParserError + { + _filename = filename; + _first_line = first_line; + _first_column = first_column; + + rule_stack.clear (); + rule_state_stack.clear (); + + try { + push_rule (_root_rule); + _scanner.reset (); + _scanner.scan (content); + _scanner.end (); + + if (rule_stack.size != 0) { + error (null, "Rule stack is not empty!"); + } + } catch (ParserError e) { + #if DEBUG + log_error (e.message); + #endif + // Set an in_error boolean, try to recover + // And only throw the error at the end of parse ? + throw e; + } + } + + public void accept_token (Token token) throws ParserError { + #if HARD_DEBUG + debug ("Incomming token: %s", token.to_pretty_string ()); + #endif + + _current_token = token; + int rule_depth = rule_stack.size; + Rule.Forward forward = Rule.Forward.NONE; + Rule? rule = peek_rule (); + if (rule == null) { + throw new ParserError.INTERNAL_ERROR ("Rule stack is empty!"); + } + while (rule != null) { + if (rule.accept_token (token, this, forward)) { + break; + } + + // Check for invalid recursion + if (rule_depth != rule_stack.size && peek_rule () == rule) { + error (null, "Parser state error"); + break; + } + rule = peek_rule (); + + // Rule stack size have changed + // Check for propagation + forward = rule_depth > rule_stack.size ? Rule.Forward.CHILD + : Rule.Forward.PARENT; + rule_depth = rule_stack.size; + } + } + + private Rule? peek_rule (int offset = -1) { + assert (offset < 0); + if (rule_stack.size + offset < 0) { + return null; + } + return rule_stack.get (rule_stack.size + offset); + } + + private Rule pop_rule () { + int last_index = rule_stack.size - 1; + Rule rule = rule_stack.get (last_index); + rule_stack.remove_at (last_index); + rule_state_stack.remove_at (last_index); + return rule; + } + + public void push_rule (Rule rule) { + rule_stack.add (rule); + rule_state_stack.add (null); + + #if HARD_DEBUG + debug ("Pushed at %2d: %s", rule_stack.size - 1, rule.to_string (null)); + #endif + } + + private Object? peek_state (int offset = -1) { + assert (offset < 0); + if (rule_state_stack.size + offset < 0) { + return null; + } + return rule_state_stack.get (rule_state_stack.size + offset); + } + + public Object? get_rule_state () { + return peek_state (); + } + + public void set_rule_state (Object state) { + int last_index = rule_stack.size - 1; + rule_state_stack.set (last_index, state); + } + + public void reduce () { + pop_rule (); + + #if HARD_DEBUG + Rule? parent_rule = peek_rule (); + if (parent_rule != null) { + debug ("Reduced to %2d: %s", rule_stack.size - 1, + parent_rule.to_string (peek_state ())); + } + #endif + } + + public bool would_parent_accept_token (Token token) { + int offset = -2; + Rule? parent_rule = peek_rule (offset); + Object? state = peek_state (offset); + while (parent_rule != null) { + #if VERY_HARD_DEBUG + debug ("WouldAccept - Offset %d; Index %d: %s", offset, + rule_stack.size + offset, parent_rule.to_string (state)); + #endif + if (parent_rule.would_accept_token (token, state)) { + #if VERY_HARD_DEBUG + debug ("WouldAccept - Yes"); + #endif + return true; + } + if (!parent_rule.would_reduce (token, state)) { + #if VERY_HARD_DEBUG + debug ("WouldAccept - No"); + #endif + return false; + } + offset--; + parent_rule = peek_rule (offset); + state = peek_state (offset); + } + #if VERY_HARD_DEBUG + debug ("WouldAccept - No"); + #endif + return false; + } + + public bool would_parent_reduce_to_rule (Token token, Rule rule) { + int offset = -2; + Rule? parent_rule = peek_rule (offset); + Object? state = peek_state (offset); + while (parent_rule != null) { + #if VERY_HARD_DEBUG + debug ("WouldReduce - Offset %d; Index %d: %s", offset, + rule_stack.size + offset, parent_rule.to_string (state)); + #endif + if (!parent_rule.would_reduce (token, state)) { + break; + } + offset--; + parent_rule = peek_rule (offset); + state = peek_state (offset); + } + if ((parent_rule != null && parent_rule.would_accept_token (token, state)) + || (parent_rule == null && TokenType.EOF.matches (token))) { + #if VERY_HARD_DEBUG + debug ("WouldReduce - Yes"); + #endif + return true; + } + #if VERY_HARD_DEBUG + debug ("WouldReduce - No"); + #endif + return false; + } + + public void warning (Token? token, string message) { + string error_message; + + if (token != null) { + error_message = message + ": " + token.to_pretty_string (); + } else { + error_message = message; + } + + _reporter.warning (_filename, + get_line (token), + get_start_column (token), + get_end_column (token), + _scanner.get_line_content (), + error_message); + } + + public void error (Token? token, string message) throws ParserError { + string error_message; + + if (token != null) { + error_message = message + ": " + token.to_pretty_string (); + } else { + error_message = message; + } + + _reporter.error (_filename, + get_line (token), + get_start_column (token), + get_end_column (token), + _scanner.get_line_content (), + error_message); + + throw new ParserError.UNEXPECTED_TOKEN (error_message); + } + + private int get_line (Token? token) { + if (token == null) { + token = _current_token; + } + return token.begin.line + _first_line; + } + + private int get_start_column (Token? token) { + if (token == null) { + token = _current_token; + } + if (token.begin.line == 0) { + return token.begin.column + _first_column + 1; + } else { + return token.begin.column + 1; + } + } + + private int get_end_column (Token? token) { + if (token == null) { + token = _current_token; + } + if (token.end.line == 0) { + return token.end.column + _first_column + 1; + } else { + return token.end.column + 1; + } + } + +#if DEBUG + private void log_error (string message) { + stderr.printf ("An error occured while parsing: %s\n", message); + stderr.printf ("\nDumping rule stack:\n"); + for (int i = 0; i < rule_stack.size; i++) { + stderr.printf ("\t%2d: %s\n", i, rule_stack[i].to_string (rule_state_stack[i])); + } + } +#endif +} diff --git a/libvaladoc/parser/parsercallback.vala b/libvaladoc/parser/parsercallback.vala new file mode 100644 index 000000000..c6fef181f --- /dev/null +++ b/libvaladoc/parser/parsercallback.vala @@ -0,0 +1,37 @@ +/* parsercallback.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +public interface Valadoc.ParserCallback { + public abstract Object? get_rule_state (); + public abstract void set_rule_state (Object state); + + public abstract void push_rule (Rule rule); + public abstract void reduce (); + + public abstract bool would_parent_accept_token (Token token); + public abstract bool would_parent_reduce_to_rule (Token token, Rule rule); + + public abstract void warning (Token? token, string message); + public abstract void error (Token? token, string message) throws ParserError; +} diff --git a/libvaladoc/parser/rule.vala b/libvaladoc/parser/rule.vala new file mode 100644 index 000000000..7ecbc3563 --- /dev/null +++ b/libvaladoc/parser/rule.vala @@ -0,0 +1,173 @@ +/* rule.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +public abstract class Valadoc.Rule : Object { + + public static Rule seq (Object[] scheme) { + return new SequenceRule (scheme); + } + + public static Rule one_of (Object[] scheme) { + return new OneOfRule (scheme); + } + + public static Rule many (Object[] scheme) { + if (scheme.length == 1) { + return new ManyRule (scheme[0]); + } else { + return new ManyRule (new SequenceRule (scheme)); + } + } + + public static Rule option (Object[] scheme) { + if (scheme.length == 1) { + return new OptionalRule (scheme[0]); + } else { + return new OptionalRule (new SequenceRule (scheme)); + } + } + + protected Rule () { + } + + private string? _name = null; + private Action _start_action; + private Action _reduce_action; + private Action _skip_action; + + public string name { get { return _name; } } + + public Rule set_name (string name) { + _name = name; + return this; + } + + public delegate void Action () throws ParserError; + + public Rule set_start (Action action) { + //TODO: Ownership Transfer + _start_action = () => { action (); }; + return this; + } + + public Rule set_reduce (Action action) { + //TODO: Ownership Transfer + _reduce_action = () => { action (); }; + return this; + } + + public Rule set_skip (Action action) { + //TODO: Ownership Transfer + _skip_action = () => { action (); }; + return this; + } + + public enum Forward { + NONE, + PARENT, + CHILD + } + + public abstract bool is_optional (); + public abstract bool starts_with_token (Token token); + public abstract bool accept_token (Token token, ParserCallback parser, Rule.Forward forward) throws ParserError; + public abstract bool would_accept_token (Token token, Object? state); + public abstract bool would_reduce (Token token, Object? state); + + public abstract string to_string (Object? state); + + protected bool is_optional_rule (Object? scheme_element) { + Rule? scheme_rule = scheme_element as Rule; + if (scheme_rule != null) { + return scheme_rule.is_optional (); + } + return false; + } + + protected bool has_start_token (Object? scheme_element, Token token) { + TokenType? scheme_token_type = scheme_element as TokenType; + if (scheme_token_type != null) { + return scheme_token_type.matches (token); + } + Rule? scheme_rule = scheme_element as Rule; + if (scheme_rule != null) { + return scheme_rule.starts_with_token (token); + } + return false; + } + + protected bool try_to_apply (Object? scheme_element, Token token, ParserCallback parser, + out bool handled) throws ParserError + { + #if VERY_HARD_DEBUG + { + TokenType? scheme_token = scheme_element as TokenType; + Rule? scheme_rule = scheme_element as Rule; + if (scheme_token != null) { + message ("TryToApply: token='%s'; scheme_token='%s'", token.to_string (), + scheme_token.to_string ()); + } else if (scheme_rule != null) { + message ("TryToApply: token='%s'; scheme_rule='%s'", token.to_string (), + scheme_rule.to_string (parser.get_rule_state ())); + } else { + assert (scheme_element != null); + } + } + #endif + TokenType? scheme_token_type = scheme_element as TokenType; + if (scheme_token_type != null && scheme_token_type.matches (token)) { + scheme_token_type.do_action (token); + handled = true; + return true; + } + Rule? scheme_rule = scheme_element as Rule; + if (scheme_rule != null && scheme_rule.starts_with_token (token)) { + parser.push_rule (scheme_rule); + handled = false; + return true; + } + + handled = false; + return false; + } + + protected void do_start (ParserCallback parser) throws ParserError { + if (_start_action != null) { + _start_action (); + } + } + + protected void do_reduce (ParserCallback parser) throws ParserError { + if (_reduce_action != null) { + _reduce_action (); + } + parser.reduce (); + } + + protected void do_skip (ParserCallback parser) throws ParserError { + if (_skip_action != null) { + _skip_action (); + } + } +} diff --git a/libvaladoc/parser/scanner.vala b/libvaladoc/parser/scanner.vala new file mode 100644 index 000000000..86acd23af --- /dev/null +++ b/libvaladoc/parser/scanner.vala @@ -0,0 +1,36 @@ +/* scanner.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +public interface Valadoc.Scanner : Object { + + public abstract void set_parser (Parser parser); + + public abstract void reset (); + + public abstract void scan (string content) throws ParserError; + + public abstract void end () throws ParserError; + + public abstract void stop (); + + public abstract string get_line_content (); +} diff --git a/libvaladoc/parser/sequencerule.vala b/libvaladoc/parser/sequencerule.vala new file mode 100644 index 000000000..f14cf070d --- /dev/null +++ b/libvaladoc/parser/sequencerule.vala @@ -0,0 +1,132 @@ +/* sequencerule.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +internal class Valadoc.SequenceRule : Rule { + + public SequenceRule (Object[] scheme) { + _scheme = scheme; + } + + private Object[] _scheme; + + private class State : Object { + public int index = 0; + } + + public override bool is_optional () { + return false; + } + + public override bool starts_with_token (Token token) { + return test_token (0, token); + } + + private bool test_token (int from_index, Token token) { + int i = from_index; + while (i < _scheme.length) { + if (has_start_token (_scheme[i], token)) { + return true; + } + if (!is_optional_rule (_scheme[i])) { + break; + } + i++; + } + return false; + } + + private bool test_reduce (int from_index, Token token) { + int i = from_index; + while (i < _scheme.length) { + if (!is_optional_rule (_scheme[i])) { + return false; + } + i++; + } + return true; + } + + public override bool accept_token (Token token, ParserCallback parser, + Rule.Forward forward) throws ParserError + { + var state = parser.get_rule_state () as State; + if (state == null) { + state = new State (); + parser.set_rule_state (state); + } + + if (state.index == 0) { + do_start (parser); + } else if (state.index == _scheme.length) { + do_reduce (parser); + return false; + } + + Object? scheme_element = null; + bool handled; + do { + scheme_element = _scheme[state.index]; + if (try_to_apply (scheme_element, token, parser, out handled)) { + state.index++; + return handled; + } + if (!is_optional_rule (scheme_element)) { + break; + } else { + ((Rule) scheme_element).do_skip (parser); + } + state.index++; + } while (state.index < _scheme.length); + + if (state.index == _scheme.length) { + do_reduce (parser); + return false; + } + + if (scheme_element is TokenType) { + parser.error (token, "expected %s".printf (((TokenType) scheme_element).to_pretty_string ())); + } else { + parser.error (token, "unexpected token"); + } + assert_not_reached (); + } + + public override bool would_accept_token (Token token, Object? rule_state) { + var state = rule_state as State; + return test_token (state.index, token); + } + + public override bool would_reduce (Token token, Object? rule_state) { + var state = rule_state as State; + return state.index == _scheme.length || test_reduce (state.index, token); + } + + public override string to_string (Object? rule_state) { + var state = rule_state as State; + if (state == null) { + state = new State (); + } + return "%-15s%-15s(index=%d/%d)".printf (name != null ? name : " ", "[seq]", state.index, _scheme.length); + } +} diff --git a/libvaladoc/parser/sourcelocation.vala b/libvaladoc/parser/sourcelocation.vala new file mode 100644 index 000000000..51b5bc1f9 --- /dev/null +++ b/libvaladoc/parser/sourcelocation.vala @@ -0,0 +1,35 @@ +/* sourcelocation.vala + * + * Copyright (C) 2008 Jürg Billeter + * + * 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: + * Jürg Billeter <j@bitron.ch> + */ + + +/** + * Represents a position in a source file. + */ +public struct Valadoc.SourceLocation { + public int line; + public int column; + + public SourceLocation (int _line, int _column) { + line = _line; + column = _column; + } +} diff --git a/libvaladoc/parser/stubrule.vala b/libvaladoc/parser/stubrule.vala new file mode 100644 index 000000000..0f6f97930 --- /dev/null +++ b/libvaladoc/parser/stubrule.vala @@ -0,0 +1,62 @@ +/* stubrule.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +public class Valadoc.StubRule : Rule { + + public StubRule () { + } + + private Rule _rule; + + public Rule set_rule (Rule rule) { + _rule = rule; + return this; + } + + public override bool is_optional () { + return _rule.is_optional (); + } + + public override bool starts_with_token (Token token) { + return _rule.starts_with_token (token); + } + + public override bool accept_token (Token token, ParserCallback parser, Rule.Forward forward) + throws ParserError + { + return _rule.accept_token (token, parser, forward); + } + + public override bool would_accept_token (Token token, Object? state) { + return _rule.would_accept_token (token, state); + } + + public override bool would_reduce (Token token, Object? state) { + return _rule.would_reduce (token, state); + } + + public override string to_string (Object? state) { + return _rule.to_string (state); + } +} diff --git a/libvaladoc/parser/token.vala b/libvaladoc/parser/token.vala new file mode 100644 index 000000000..6cbff5ed1 --- /dev/null +++ b/libvaladoc/parser/token.vala @@ -0,0 +1,110 @@ +/* token.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +public class Valadoc.Token : Object { + + public Token.from_type (TokenType type, SourceLocation begin, SourceLocation end, string? val = null) { + _type = type; + _begin = begin; + _end = end; + _value = val; + } + + public Token.from_word (string word, SourceLocation begin, SourceLocation end) { + _word = word; + _begin = begin; + _end = end; + } + + private TokenType? _type = null; + private string? _word = null; + private SourceLocation _begin; + private SourceLocation _end; + private string? _value; + + public bool is_word { + get { + return _word != null; + } + } + + public bool is_number { + get { + if (_word == null || _word.length == 0) { + return false; + } else if (_word[0] == '0' && _word.length > 1) { + return false; + } + for (int i = 0; i < _word.length; i++) { + if (_word[i] < '0' || _word[i] > '9') { + return false; + } + } + return true; + } + } + + public string? word { + get { + return _word; + } + } + + public string? value { + get { + return _value; + } + } + + public TokenType? token_type { + get { + return _type; + } + } + + public SourceLocation begin { + get { + return _begin; + } + } + + public SourceLocation end { + get { + return _end; + } + } + + public string to_string () { + return _word == null ? _type.to_string () : _word; + } + + public string to_pretty_string () { + return _word == null ? _type.to_pretty_string () : _word; + } + + public int to_int () { + assert (is_number); + return int.parse (_word); + } +} diff --git a/libvaladoc/parser/tokentype.vala b/libvaladoc/parser/tokentype.vala new file mode 100644 index 000000000..3cad35ea7 --- /dev/null +++ b/libvaladoc/parser/tokentype.vala @@ -0,0 +1,271 @@ +/* tokentype.vala + * + * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; + +public class Valadoc.TokenType : Object { + // valadoc-comments: + public static TokenType ANY; + public static TokenType ANY_WORD; + public static TokenType ANY_NUMBER; + public static TokenType EOF; + public static TokenType EOL; + public static TokenType BREAK; + public static TokenType AROBASE; + public static TokenType SPACE; + public static TokenType TAB; + public static TokenType EQUAL_1; + public static TokenType EQUAL_2; + public static TokenType EQUAL_3; + public static TokenType EQUAL_4; + public static TokenType EQUAL_5; + public static TokenType MINUS; + public static TokenType LESS_THAN; + public static TokenType GREATER_THAN; + public static TokenType ALIGN_TOP; + public static TokenType ALIGN_BOTTOM; + public static TokenType SINGLE_QUOTE_2; + public static TokenType SLASH_2; + public static TokenType UNDERSCORE_2; + public static TokenType BACK_QUOTE_2; + public static TokenType OPEN_BRACE; + public static TokenType CLOSED_BRACE; + public static TokenType DOUBLE_OPEN_BRACE; + public static TokenType DOUBLE_CLOSED_BRACE; + public static TokenType TRIPLE_OPEN_BRACE; + public static TokenType TRIPLE_CLOSED_BRACE; + public static TokenType DOUBLE_OPEN_BRACKET; + public static TokenType DOUBLE_CLOSED_BRACKET; + public static TokenType PIPE; + public static TokenType DOUBLE_PIPE; + public static TokenType ALIGN_RIGHT; + public static TokenType ALIGN_CENTER; + + + // .valadoc (importer) + public static TokenType VALADOC_COMMENT_START; + public static TokenType VALADOC_COMMENT_END; + public static TokenType VALADOC_ANY_WORD; + public static TokenType VALADOC_SPACE; + public static TokenType VALADOC_TAB; + public static TokenType VALADOC_EOL; + + + // markdown: + public static TokenType MARKDOWN_PARAGRAPH; + public static TokenType MARKDOWN_ANY_WORD; + public static TokenType MARKDOWN_SPACE; + public static TokenType MARKDOWN_SOURCE; + public static TokenType MARKDOWN_PARAMETER; + public static TokenType MARKDOWN_CONSTANT; + public static TokenType MARKDOWN_SYMBOL; + public static TokenType MARKDOWN_LOCAL_GMEMBER; + public static TokenType MARKDOWN_FUNCTION; + public static TokenType MARKDOWN_MAIL; + public static TokenType MARKDOWN_LINK; + public static TokenType MARKDOWN_GREATER_THAN; + public static TokenType MARKDOWN_LESS_THAN; + public static TokenType MARKDOWN_OPEN_BRACKET; + public static TokenType MARKDOWN_CLOSE_BRACKET; + public static TokenType MARKDOWN_OPEN_PARENS; + public static TokenType MARKDOWN_CLOSE_PARENS; + public static TokenType MARKDOWN_EXCLAMATION_MARK; + public static TokenType MARKDOWN_HEADLINE_1; + public static TokenType MARKDOWN_HEADLINE_2; + public static TokenType MARKDOWN_HEADLINE_HASH; + public static TokenType MARKDOWN_HEADLINE_END; + public static TokenType MARKDOWN_UNORDERED_LIST_ITEM_START; + public static TokenType MARKDOWN_UNORDERED_LIST_ITEM_END; + public static TokenType MARKDOWN_ORDERED_LIST_ITEM_START; + public static TokenType MARKDOWN_ORDERED_LIST_ITEM_END; + public static TokenType MARKDOWN_BLOCK_START; + public static TokenType MARKDOWN_BLOCK_END; + public static TokenType MARKDOWN_EOC; + + + private static bool initialized = false; + + internal static void init_token_types () { + if (!initialized) { + // valadoc-comments: + ANY = new TokenType.basic ("<any>"); + ANY_WORD = new TokenType.basic ("<any-word>"); + ANY_NUMBER = new TokenType.basic ("<any-number>"); + EOF = new TokenType.basic ("\0", "<end-of-file>"); + EOL = new TokenType.basic ("\n", "<end-of-line>"); + BREAK = new TokenType.basic ("<<BR>>"); + AROBASE = new TokenType.basic ("@"); + SPACE = new TokenType.basic (" ", "<space>"); + TAB = new TokenType.basic ("\t", "<tab>"); + EQUAL_1 = new TokenType.basic ("="); + EQUAL_2 = new TokenType.basic ("=="); + EQUAL_3 = new TokenType.basic ("===="); + EQUAL_4 = new TokenType.basic ("====="); + EQUAL_5 = new TokenType.basic ("======"); + MINUS = new TokenType.basic ("-"); + LESS_THAN = new TokenType.basic ("<"); + GREATER_THAN = new TokenType.basic (">"); + ALIGN_TOP = new TokenType.basic ("^"); + ALIGN_BOTTOM = new TokenType.basic ("v"); + SINGLE_QUOTE_2 = new TokenType.basic ("''"); + SLASH_2 = new TokenType.basic ("//"); + UNDERSCORE_2 = new TokenType.basic ("__"); + BACK_QUOTE_2 = new TokenType.basic ("``"); + OPEN_BRACE = new TokenType.basic ("{"); + CLOSED_BRACE = new TokenType.basic ("}"); + DOUBLE_OPEN_BRACE = new TokenType.basic ("{{"); + DOUBLE_CLOSED_BRACE = new TokenType.basic ("}}"); + TRIPLE_OPEN_BRACE = new TokenType.basic ("{{{"); + TRIPLE_CLOSED_BRACE = new TokenType.basic ("}}}"); + DOUBLE_OPEN_BRACKET = new TokenType.basic ("[["); + DOUBLE_CLOSED_BRACKET = new TokenType.basic ("]]"); + PIPE = new TokenType.basic ("|"); + DOUBLE_PIPE = new TokenType.basic ("||"); + ALIGN_RIGHT = new TokenType.basic ("))"); + ALIGN_CENTER = new TokenType.basic (")("); + + // .valadoc (importer) + VALADOC_COMMENT_START = new TokenType.basic ("/*"); + VALADOC_COMMENT_END = new TokenType.basic ("*/"); + VALADOC_ANY_WORD = ANY_WORD; + VALADOC_SPACE = SPACE; + VALADOC_TAB = TAB; + VALADOC_EOL = EOL; + + initialized = true; + + + // Markdown: (importer) + MARKDOWN_PARAGRAPH = new TokenType.basic ("<paragraph>"); + MARKDOWN_BLOCK_START = new TokenType.basic ("<block>"); + MARKDOWN_BLOCK_END = new TokenType.basic ("</block>"); + MARKDOWN_UNORDERED_LIST_ITEM_START = new TokenType.basic ("<unordered-list>"); + MARKDOWN_UNORDERED_LIST_ITEM_END = new TokenType.basic ("</unordered-list>"); + MARKDOWN_ORDERED_LIST_ITEM_START = new TokenType.basic ("<ordered-list>"); + MARKDOWN_ORDERED_LIST_ITEM_END = new TokenType.basic ("</ordered-list>"); + + MARKDOWN_HEADLINE_1 = new TokenType.basic ("<headline-1>"); + MARKDOWN_HEADLINE_2 = new TokenType.basic ("<headline-2>"); + MARKDOWN_HEADLINE_HASH = new TokenType.basic ("<hash>"); + MARKDOWN_HEADLINE_END = new TokenType.basic ("</headline>"); + MARKDOWN_SOURCE = new TokenType.basic ("<source>"); + MARKDOWN_PARAMETER = new TokenType.basic ("<parameter>"); + MARKDOWN_CONSTANT = new TokenType.basic ("<constant>"); + MARKDOWN_FUNCTION = new TokenType.basic ("<function>"); + MARKDOWN_SYMBOL = new TokenType.basic ("<symbol>"); + MARKDOWN_LOCAL_GMEMBER = new TokenType.basic ("<local-gmember>"); + MARKDOWN_MAIL = new TokenType.basic ("<mail>"); + MARKDOWN_LINK = new TokenType.basic ("<link>"); + + MARKDOWN_OPEN_BRACKET = new TokenType.basic ("["); + MARKDOWN_CLOSE_BRACKET = new TokenType.basic ("]"); + MARKDOWN_OPEN_PARENS = new TokenType.basic ("("); + MARKDOWN_CLOSE_PARENS = new TokenType.basic (")"); + MARKDOWN_EXCLAMATION_MARK = new TokenType.basic ("!"); + MARKDOWN_GREATER_THAN = GREATER_THAN; + MARKDOWN_LESS_THAN = LESS_THAN; + + MARKDOWN_ANY_WORD = ANY_WORD; + MARKDOWN_SPACE = SPACE; + MARKDOWN_EOC = EOL; + } + } + + private static int EXACT_WORD = -1; + + public static TokenType str (string str) { + return new TokenType (str, EXACT_WORD, null); + } + + public static TokenType any () { + return ANY; + } + + public static TokenType any_word () { + return ANY_WORD; + } + + public static TokenType any_number () { + return ANY_NUMBER; + } + + private TokenType (string string_value, int basic_value, Action? __action) { + _string_value = string_value; + _basic_value = basic_value; + if (__action != null) { + _action = (token) => { __action (token); }; + } else { + _action = null; + } + } + + private TokenType.basic (string string_value, string? pretty_string = null) { + _string_value = string_value; + _pretty_string = pretty_string; + _basic_value = ++_last_basic_value; + } + + private static int _last_basic_value = -1; + + private string _string_value; + private string? _pretty_string; + private int _basic_value = -1; + private Action? _action = null; + + public delegate void Action (Token token) throws ParserError; + + public TokenType action (Action action) { + return new TokenType (_string_value, _basic_value, action); + } + + public void do_action (Token matched_token) throws ParserError { + if (_action != null) { + _action (matched_token); + } + } + + public bool matches (Token token) { + if (_basic_value == ANY._basic_value) { + return true; + } else if (_basic_value == ANY_WORD._basic_value && token.is_word) { + return true; + } else if (_basic_value == ANY_NUMBER._basic_value && token.is_number) { + return true; + } else if (_basic_value == EXACT_WORD && token.is_word && token.word == _string_value) { + return true; + } else if (token.token_type != null && token.token_type._basic_value == _basic_value) { + return true; + } + return false; + } + + public string to_string () { + return _string_value; + } + + public string to_pretty_string () { + if (_pretty_string != null) { + return _pretty_string; + } + return _string_value; + } +} diff --git a/libvaladoc/settings.vala b/libvaladoc/settings.vala new file mode 100644 index 000000000..823dd5b20 --- /dev/null +++ b/libvaladoc/settings.vala @@ -0,0 +1,169 @@ +/* settings.vala + * + * Copyright (C) 2008-2009 Florian Brosch + * + * 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: + * Brosch Florian <flo.brosch@gmail.com> + */ + + +/** + * Contains information about output settings configuration + */ +public class Valadoc.Settings : Object { + /** + * Output directory/file name. + */ + public string path = "documentation/"; + + /** + * Package name + */ + public string pkg_name = null; + + /** + * Package version + */ + public string pkg_version; + + /** + * Wiki directory + */ + public string wiki_directory; + + /** + * Plugin-specific command line arguments + */ + public string[] pluginargs; + + + /** + * Add private elements to documentation + */ + public bool _private = false; + + /** + * Add protected elements to documentation + */ + public bool _protected = false; + + /** + * Add internal elements to documentation + */ + public bool _internal = false; + + /** + * Add dependencies to the documentation + */ + public bool with_deps = false; + + public bool add_inherited = false; + + /** + * Show all warnings + */ + public bool verbose = false; + + + + /** + * Do not warn when using experimental features. + */ + public bool experimental; + + /** + * Enable experimental enhancements for non-null types. + */ + public bool experimental_non_null; + + /** + * Use the given profile (dova, gobject, posix, ...) instead of the defaul + */ + public string? profile; + + /** + * Base source directory. + */ + public string? basedir; + + /** + * Output directory/file name. + */ + public string? directory; + + + /** + * A list of defined symbols. + */ + public string[] defines; + + /** + * List of directories where to find .vapi files. + */ + public string[] vapi_directories; + + /** + * A list of all packages + */ + public string[] packages; + + /** + * A list of all source files. + */ + public string[] source_files; + + /** + * GObject-Introspection directory + */ + public string? gir_directory; + + /** + * GObject-Introspection repository file name + */ + public string? gir_name; + + /** + * A list of all metadata directories + */ + public string[] metadata_directories; + + /** + * Alternative paths for resources + */ + public string[] alternative_resource_dirs; + + /** + * A list of all gir directories. + */ + public string[] gir_directories; + + /** + * GLib version to target. + */ + public string target_glib; + + public string gir_namespace; + + public string gir_version; + + /** + * Use SVG as chart images + */ + public bool use_svg_images = false; +} + + diff --git a/libvaladoc/taglets/tagletdeprecated.vala b/libvaladoc/taglets/tagletdeprecated.vala new file mode 100644 index 000000000..c77457935 --- /dev/null +++ b/libvaladoc/taglets/tagletdeprecated.vala @@ -0,0 +1,61 @@ +/* tagletdeprecated.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; +using Valadoc.Content; + + +public class Valadoc.Taglets.Deprecated : BlockContent, Taglet, Block { + public Rule? get_parser_rule (Rule run_rule) { + return run_rule; + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + base.check (api_root, container, file_path, reporter, settings); + reporter.simple_warning ("%s: %s: @deprecated".printf (file_path, container.get_full_name ()), + "@deprecated is deprecated. Use [Version (deprecated = true)]"); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_taglet (this); + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Deprecated deprecated = new Deprecated (); + deprecated.parent = new_parent; + + foreach (Block element in content) { + Block copy = element.copy (deprecated) as Block; + deprecated.content.add (copy); + } + + return deprecated; + } +} + diff --git a/libvaladoc/taglets/tagletinheritdoc.vala b/libvaladoc/taglets/tagletinheritdoc.vala new file mode 100644 index 000000000..d8b288545 --- /dev/null +++ b/libvaladoc/taglets/tagletinheritdoc.vala @@ -0,0 +1,232 @@ +/* tagletinheritdoc.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; +using Valadoc.Content; + +public class Valadoc.Taglets.InheritDoc : InlineTaglet { + private Taglet? parent_taglet = null; + + public Api.Node? inherited { + private set; + get; + } + + + public override Rule? get_parser_rule (Rule run_rule) { + return null; + } + + private Taglet? find_parent_taglet () { + if (_inherited == null || _inherited.documentation == null) { + return null; + } + + ContentElement pos; + for (pos = this.parent; pos != null && pos is Taglet == false; pos = pos.parent); + if (pos is Taglet) { + return (Taglet) pos; + } + + return null; + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // TODO Check that the container is an override of an abstract symbol + // Also retrieve that abstract symbol _inherited + + if (container is Api.Method) { + _inherited = ((Api.Method) container).base_method; + } else if (container is Api.Property) { + _inherited = ((Api.Property) container).base_property; + } else if (container is Api.Class && ((Api.Class) container).base_type != null) { + _inherited = (Api.Node) ((Api.Class) container).base_type.data_type; + } else if (container is Api.Struct && ((Api.Struct) container).base_type != null) { + _inherited = (Api.Node) ((Api.Struct) container).base_type.data_type; + } + + parent_taglet = find_parent_taglet (); + if (parent_taglet == null && _inherited != null) { + api_root.register_inheritdoc (container, this); + } + + + // TODO report error if inherited is null + + // TODO postpone check after complete parse of the api tree comments + // And reenable that check + //base.check (api_root, container, reporter); + } + + private Run[]? split_run (Inline? separator) { + if (separator == null) { + return null; + } + + ContentElement parent = separator.parent; + Gee.List<Inline> parent_content = null; + + if (parent is Run && ((Run) parent).style == Run.Style.NONE) { + parent_content = ((Run) parent).content; + } else if (parent is Paragraph) { + parent_content = ((Paragraph) parent).content; + } + + if (parent_content != null) { + Run right_run = new Run (Run.Style.NONE); + Run left_run = new Run (Run.Style.NONE); + bool separated = false; + + foreach (var current in parent_content) { + if (current == separator) { + separated = true; + } else if (separated) { + right_run.content.add (current); + current.parent = right_run; + } else { + left_run.content.add (current); + current.parent = left_run; + } + } + + return { left_run, right_run }; + } + + return null; + } + + internal void transform (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + ContentElement separator = this; + Run right_run = null; + Run left_run = null; + Run[]? parts; + + while ((parts = split_run (separator as Inline)) != null) { + if (left_run != null) { + parts[0].content.add (left_run); + left_run.parent = parts[0]; + } + + if (right_run != null) { + parts[1].content.insert (0, right_run); + right_run.parent = parts[1]; + } + + separator = separator.parent; + right_run = parts[1]; + left_run = parts[0]; + } + + if (separator is Paragraph == false || separator.parent is Comment == false) { + reporter.simple_error ("%s: %s: @inheritDoc".printf (file_path, container.get_full_name ()), + "Parent documentation can't be copied to this location."); + return ; + } + + Comment comment = separator.parent as Comment; + assert (comment != null); + + int insert_pos = comment.content.index_of ((Paragraph) separator); + int start_pos = insert_pos; + assert (insert_pos >= 0); + + foreach (Block block in _inherited.documentation.content) { + comment.content.insert (insert_pos, (Block) block.copy (comment)); + insert_pos++; + } + + if (right_run != null) { + if (comment.content[insert_pos - 1] is Paragraph) { + ((Paragraph) comment.content[insert_pos - 1]).content.add (right_run); + right_run.parent = comment.content[insert_pos - 1]; + } else { + Paragraph p = new Paragraph (); + p.content.add (right_run); + right_run.parent = p; + p.parent = comment; + comment.content.insert (insert_pos, p); + } + } + + if (left_run != null) { + if (comment.content[start_pos] is Paragraph) { + ((Paragraph) comment.content[start_pos]).content.insert (0, left_run); + left_run.parent = comment.content[start_pos]; + } else { + Paragraph p = new Paragraph (); + p.content.add (left_run); + left_run.parent = p; + p.parent = comment; + comment.content.insert (start_pos, p); + } + } + + comment.content.remove ((Paragraph) separator); + } + + private Run content_copy (Gee.List<ContentElement>? content) { + Run run = new Run (Run.Style.NONE); + run.parent = this; + + if (content != null) { + foreach (ContentElement item in content) { + run.content.add (item.copy (this) as Inline); + } + } + + return run; + } + + public override ContentElement produce_content () { + if (_inherited != null && _inherited.documentation != null && parent_taglet != null) { + Gee.List<Taglet> parent_taglets = _inherited.documentation.find_taglets (null, parent_taglet.get_type ()); + foreach (Taglet parent in parent_taglets) { + // we only care about the first match: + if (parent.inheritable (parent_taglet)) { + return content_copy (parent.get_inheritable_documentation ()); + } + } + } + return new Text (""); + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + InheritDoc doc = new InheritDoc (); + doc.parent = new_parent; + + doc.settings = settings; + doc.locator = locator; + + doc._inherited = _inherited; + + return doc; + } +} diff --git a/libvaladoc/taglets/tagletinit.vala b/libvaladoc/taglets/tagletinit.vala new file mode 100644 index 000000000..7e88126aa --- /dev/null +++ b/libvaladoc/taglets/tagletinit.vala @@ -0,0 +1,35 @@ +/* tagletinit.vala + * + * Copyright (C) 2008-2009 Florian Brosch + * + * 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: + * Florian Brosch <flo.brosch@gmail.com> + */ + + +namespace Valadoc.Taglets { + public void init (ModuleLoader loader) { + loader.register_taglet ("see", typeof (Valadoc.Taglets.See)); + loader.register_taglet ("since", typeof (Valadoc.Taglets.Since)); + loader.register_taglet ("link", typeof (Valadoc.Taglets.Link)); + loader.register_taglet ("throws", typeof (Valadoc.Taglets.Throws)); + loader.register_taglet ("return", typeof (Valadoc.Taglets.Return)); + loader.register_taglet ("param", typeof (Valadoc.Taglets.Param)); + loader.register_taglet ("deprecated", typeof (Valadoc.Taglets.Deprecated)); + loader.register_taglet ("inheritDoc", typeof (Valadoc.Taglets.InheritDoc)); + } +} diff --git a/libvaladoc/taglets/tagletlink.vala b/libvaladoc/taglets/tagletlink.vala new file mode 100644 index 000000000..b489a5898 --- /dev/null +++ b/libvaladoc/taglets/tagletlink.vala @@ -0,0 +1,186 @@ +/* taglet.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2014 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; +using Valadoc.Content; + + +public class Valadoc.Taglets.Link : InlineTaglet { + public string symbol_name { internal set; get; } + + /** + * Accept leading 's', e.g. #Widgets + */ + public bool c_accept_plural { internal set; get; } + + /** + * True if symbol_name could only be resolved after removing 's' + * + * E.g. true or #Widgets, false for #Widget + */ + public bool c_is_plural { private set; get; } + + + private enum SymbolContext { + NORMAL, + FINISH, + TYPE + } + + private SymbolContext _context = SymbolContext.NORMAL; + private Api.Node _symbol; + + public override Rule? get_parser_rule (Rule run_rule) { + return Rule.seq ({ + Rule.option ({ Rule.many ({ TokenType.SPACE }) }), + TokenType.any_word ().action ((token) => { symbol_name = token.to_string (); }), + Rule.option ({ + Rule.many ({ + Rule.one_of ({ + TokenType.any_word ().action ((token) => { symbol_name += token.to_string (); }), + TokenType.MINUS.action ((token => { symbol_name += token.to_string (); })) + }) + }) + }) + }); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) { + + if (symbol_name.has_prefix ("c::")) { + _symbol_name = _symbol_name.substring (3); + string? singular_symbol_name = (c_accept_plural && _symbol_name.has_suffix ("s")) + ? symbol_name.substring (0, _symbol_name.length - 1) + : null; + + _symbol = api_root.search_symbol_cstr (container, symbol_name); + if (_symbol == null && singular_symbol_name != null) { + _symbol = api_root.search_symbol_cstr (container, singular_symbol_name); + c_is_plural = true; + } + _context = SymbolContext.NORMAL; + + if (_symbol == null && _symbol_name.has_suffix ("_finish")) { + string tmp = _symbol_name.substring (0, _symbol_name.length - 7); + + _symbol = api_root.search_symbol_cstr (container, tmp + "_async") as Api.Method; + if (_symbol != null && ((Api.Method) _symbol).is_yields) { + _context = SymbolContext.FINISH; + } else { + _symbol = api_root.search_symbol_cstr (container, tmp) as Api.Method; + if (_symbol != null && ((Api.Method) _symbol).is_yields) { + _context = SymbolContext.FINISH; + } else { + _symbol = null; + } + } + } + + if (_symbol == null) { + _symbol = api_root.search_symbol_type_cstr (symbol_name); + if (_symbol == null && singular_symbol_name != null) { + _symbol = api_root.search_symbol_type_cstr (singular_symbol_name); + c_is_plural = true; + } + if (_symbol != null) { + _context = SymbolContext.TYPE; + } + } + + if (_symbol != null) { + symbol_name = _symbol.name; + } + } else { + _symbol = api_root.search_symbol_str (container, symbol_name); + } + + if (_symbol == null && symbol_name != "main") { + string node_segment = (container is Api.Package)? "" : container.get_full_name () + ": "; + reporter.simple_warning ("%s: %s@link".printf (file_path, node_segment), + "`%s' does not exist", symbol_name); + } + + base.check (api_root, container, file_path, reporter, settings); + } + + public override ContentElement produce_content () { + var link = new Content.SymbolLink (); + link.symbol = _symbol; + link.given_symbol_name = symbol_name; + + Content.Inline content; + switch (_context) { + case SymbolContext.FINISH: + link.given_symbol_name += ".end"; + content = link; + break; + + case SymbolContext.TYPE: + Run run = new Content.Run (Run.Style.MONOSPACED); + content = run; + + Content.Run keyword = new Content.Run (Run.Style.LANG_KEYWORD); + keyword.content.add (new Content.Text ("typeof")); + run.content.add (keyword); + + run.content.add (new Content.Text (" (")); + run.content.add (link); + run.content.add (new Content.Text (")")); + break; + + default: + content = link; + break; + } + + if (c_is_plural == true) { + Run run = new Content.Run (Run.Style.NONE); + run.content.add (content); + run.content.add (new Content.Text ("s")); + return run; + } + + return content; + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Link link = new Link (); + link.parent = new_parent; + + link.settings = settings; + link.locator = locator; + + link.symbol_name = symbol_name; + link.c_accept_plural = c_accept_plural; + link.c_is_plural = c_is_plural; + link._context = _context; + link._symbol = _symbol; + + return link; + } +} diff --git a/libvaladoc/taglets/tagletparam.vala b/libvaladoc/taglets/tagletparam.vala new file mode 100644 index 000000000..651a9ed3f --- /dev/null +++ b/libvaladoc/taglets/tagletparam.vala @@ -0,0 +1,166 @@ +/* taglet.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Valadoc.Content; +using Gee; + + +public class Valadoc.Taglets.Param : BlockContent, Taglet, Block { + public string parameter_name { internal set; get; } + + public weak Api.Symbol? parameter { private set; get; } + + public int position { private set; get; default = -1; } + + public bool is_c_self_param { internal set; get; } + + public bool is_this { private set; get; } + + + public Rule? get_parser_rule (Rule run_rule) { + return Rule.seq ({ + Rule.option ({ Rule.many ({ TokenType.SPACE }) }), + TokenType.any_word ().action ((token) => { parameter_name = token.to_string (); }), + run_rule + }); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // Check for the existence of such a parameter + unowned string? implicit_return_array_length = null; + bool has_instance = has_instance (container); + bool is_implicit = false; + this.parameter = null; + + if (container is Api.Callable) { + implicit_return_array_length = ((Api.Callable) container).implicit_array_length_cparameter_name; + } else { + reporter.simple_warning ("%s: %s: @param".printf (file_path, container.get_full_name ()), + "@param used outside method/delegate/signal context"); + base.check (api_root, container, file_path, reporter, settings); + return ; + } + + if (is_c_self_param == true && has_instance) { + this.parameter_name = "this"; + this.is_this = true; + this.position = 0; + } else if (parameter_name == "...") { + Gee.List<Api.Node> params = container.get_children_by_type (Api.NodeType.FORMAL_PARAMETER, false); + foreach (Api.Node param in params) { + if (((Api.FormalParameter) param).ellipsis) { + this.parameter = (Api.Symbol) param; + this.position = (has_instance)? params.size : params.size - 1; + break; + } + } + } else { + Gee.List<Api.Node> params = container.get_children_by_types ({Api.NodeType.FORMAL_PARAMETER, + Api.NodeType.TYPE_PARAMETER}, + false); + int pos = (has_instance)? 1 : 0; + + foreach (Api.Node param in params) { + if (param.name == parameter_name) { + this.parameter = (Api.Symbol) param; + this.position = pos; + break; + } + + Api.FormalParameter formalparam = param as Api.FormalParameter; + if (formalparam != null && (formalparam.implicit_array_length_cparameter_name == parameter_name + || formalparam.implicit_closure_cparameter_name == parameter_name + || formalparam.implicit_destroy_cparameter_name == parameter_name)) + { + is_implicit = true; + break; + } + + pos++; + } + + if (this.parameter == null + && (parameter_name == "error" + && container.has_children ({Api.NodeType.ERROR_DOMAIN, Api.NodeType.CLASS}) + || parameter_name == implicit_return_array_length)) + { + is_implicit = true; + } + } + + if (this.parameter == null) { + if (is_implicit) { + reporter.simple_note ("%s: %s: @param".printf (file_path, container.get_full_name ()), + "Implicit parameter `%s' exposed in documentation", parameter_name); + } else if (!is_c_self_param) { + reporter.simple_warning ("%s: %s: @param".printf (file_path, container.get_full_name ()), + "Unknown parameter `%s'", parameter_name); + } + } + + base.check (api_root, container, file_path, reporter, settings); + } + + private bool has_instance (Api.Item element) { + if (element is Api.Method) { + return !((Api.Method) element).is_static; + } + + return false; + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_taglet (this); + } + + public Gee.List<ContentElement>? get_inheritable_documentation () { + return content; + } + + public bool inheritable (Taglet taglet) { + if (taglet is Taglets.Param == false) { + return false; + } + + Taglets.Param t = (Taglets.Param) taglet; + return (parameter == t.parameter || parameter_name == t.parameter_name); + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Param param = new Param (); + param.parent = new_parent; + + param.parameter_name = parameter_name; + param.parameter = parameter; + param.position = position; + + foreach (Block element in content) { + Block copy = element.copy (param) as Block; + param.content.add (copy); + } + + return param; + } +} diff --git a/libvaladoc/taglets/tagletreturn.vala b/libvaladoc/taglets/tagletreturn.vala new file mode 100644 index 000000000..ef3e4ecf4 --- /dev/null +++ b/libvaladoc/taglets/tagletreturn.vala @@ -0,0 +1,79 @@ +/* taglet.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; +using Valadoc.Content; + + +public class Valadoc.Taglets.Return : BlockContent, Taglet, Block { + public Rule? get_parser_rule (Rule run_rule) { + return run_rule; + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) { + Api.TypeReference? type_ref = null; + bool creation_method = false; + + if (container is Api.Method) { + creation_method = ((Api.Method) container).is_constructor; + type_ref = ((Api.Method) container).return_type; + } else if (container is Api.Callable) { + type_ref = ((Api.Callable) container).return_type; + } else { + reporter.simple_warning ("%s: %s: @return".printf (file_path, container.get_full_name ()), + "@return used outside method/delegate/signal context"); + } + + if (type_ref != null && type_ref.data_type == null && !creation_method) { + reporter.simple_warning ("%s: %s: @return".printf (file_path, container.get_full_name ()), + "Return description declared for void function"); + } + + base.check (api_root, container, file_path, reporter, settings); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_taglet (this); + } + + public Gee.List<ContentElement>? get_inheritable_documentation () { + return content; + } + + public bool inheritable (Taglet taglet) { + return taglet is Taglets.Return; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Return ret = new Return (); + ret.parent = new_parent; + + foreach (Block element in content) { + Block copy = element.copy (ret) as Block; + ret.content.add (copy); + } + + return ret; + } +} diff --git a/libvaladoc/taglets/tagletsee.vala b/libvaladoc/taglets/tagletsee.vala new file mode 100644 index 000000000..1e096fda7 --- /dev/null +++ b/libvaladoc/taglets/tagletsee.vala @@ -0,0 +1,77 @@ +/* tagletsee.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; +using Valadoc.Content; + + +public class Valadoc.Taglets.See : ContentElement, Taglet, Block { + public string symbol_name { private set; get; } + public Api.Node symbol { private set; get; } + + public Rule? get_parser_rule (Rule run_rule) { + Rule optional_spaces = Rule.option ({ Rule.many ({ TokenType.SPACE }) }); + + return Rule.seq ({ + optional_spaces, + TokenType.any_word ().action ((token) => { symbol_name = token.to_string (); }), + optional_spaces + }); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) { + if (symbol_name.has_prefix ("c::")) { + symbol_name = symbol_name.substring (3); + symbol = api_root.search_symbol_cstr (container, symbol_name); + if (symbol != null) { + symbol_name = _symbol.name; + } + } else { + symbol = api_root.search_symbol_str (container, symbol_name); + } + + if (symbol == null) { + // TODO use ContentElement's source reference + reporter.simple_warning ("%s: %s: @see".printf (file_path, container.get_full_name ()), + "`%s' does not exist", symbol_name); + } + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_taglet (this); + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + See see = new See (); + see.parent = new_parent; + + see.symbol_name = symbol_name; + see.symbol = symbol; + + return see; + }} diff --git a/libvaladoc/taglets/tagletsince.vala b/libvaladoc/taglets/tagletsince.vala new file mode 100644 index 000000000..fccc063ef --- /dev/null +++ b/libvaladoc/taglets/tagletsince.vala @@ -0,0 +1,63 @@ +/* tagletsince.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; +using Valadoc.Content; + + +public class Valadoc.Taglets.Since : ContentElement, Taglet, Block { + public string version { get; internal set; } + + public Rule? get_parser_rule (Rule run_rule) { + Rule optional_spaces = Rule.option ({ Rule.many ({ TokenType.SPACE }) }); + + return Rule.seq ({ + optional_spaces, + TokenType.any_word ().action ((token) => { version = token.to_string (); }), + optional_spaces + }); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_taglet (this); + } + + public override bool is_empty () { + return false; + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Since since = new Since (); + since.parent = new_parent; + + since.version = version; + + return since; + } +} + diff --git a/libvaladoc/taglets/tagletthrows.vala b/libvaladoc/taglets/tagletthrows.vala new file mode 100644 index 000000000..6d3631c7f --- /dev/null +++ b/libvaladoc/taglets/tagletthrows.vala @@ -0,0 +1,125 @@ +/* tagletthrows.vala + * + * Copyright (C) 2008-2009 Didier Villevalois + * Copyright (C) 2008-2012 Florian Brosch + * + * 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: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +using Gee; +using Valadoc.Content; + + +public class Valadoc.Taglets.Throws : BlockContent, Taglet, Block { + // TODO: rename + public string error_domain_name { private set; get; } + + /** + * Thrown Error domain or Error code + */ + // TODO: rename + public Api.Node error_domain { private set; get; } + + public Rule? get_parser_rule (Rule run_rule) { + return Rule.seq ({ + Rule.option ({ Rule.many ({ TokenType.SPACE }) }), + TokenType.any_word ().action ((token) => { error_domain_name = token.to_string (); }), + run_rule + }); + } + + public override void check (Api.Tree api_root, Api.Node container, string file_path, + ErrorReporter reporter, Settings settings) + { + // context check: + if (container is Api.Method == false && container is Api.Delegate == false) { + reporter.simple_warning ("%s: %s: @throws".printf (file_path, container.get_full_name ()), + "@throws used outside method/delegate context"); + base.check (api_root, container, file_path, reporter, settings); + return ; + } + + + // type check: + error_domain = api_root.search_symbol_str (container, error_domain_name); + if (error_domain == null) { + // TODO use ContentElement's source reference + reporter.simple_error ("%s: %s: @throws".printf (file_path, container.get_full_name ()), + "`%s' does not exist", error_domain_name); + base.check (api_root, container, file_path, reporter, settings); + return ; + } + + + // Check if the method is allowed to throw the given type or error code: + Gee.List<Api.Node> exceptions = container.get_children_by_types ({Api.NodeType.ERROR_DOMAIN, + Api.NodeType.CLASS}, + false); + Api.Item expected_error_domain = (error_domain is Api.ErrorCode) + ? error_domain.parent + : error_domain; + bool report_warning = true; + foreach (Api.Node exception in exceptions) { + if (exception == expected_error_domain + || (exception is Api.Class && expected_error_domain is Api.ErrorDomain)) + { + report_warning = false; + break; + } + } + if (report_warning) { + reporter.simple_warning ("%s: %s: @throws".printf (file_path, container.get_full_name ()), + "`%s' does not exist in exception list", error_domain_name); + } + + base.check (api_root, container, file_path, reporter, settings); + } + + public override void accept (ContentVisitor visitor) { + visitor.visit_taglet (this); + } + + public Gee.List<ContentElement>? get_inheritable_documentation () { + return content; + } + + public bool inheritable (Taglet taglet) { + if (taglet is Taglets.Throws == false) { + return false; + } + + Taglets.Throws t = (Taglets.Throws) taglet; + return (error_domain == t.error_domain || error_domain_name == t.error_domain_name); + } + + public override ContentElement copy (ContentElement? new_parent = null) { + Throws tr = new Throws (); + tr.parent = new_parent; + + tr.error_domain_name = error_domain_name; + tr.error_domain = error_domain; + + foreach (Block element in content) { + Block copy = element.copy (tr) as Block; + tr.content.add (copy); + } + + return tr; + } +} + diff --git a/libvaladoc/valadoc-1.0.deps.in b/libvaladoc/valadoc-1.0.deps.in new file mode 100644 index 000000000..485c5a5ce --- /dev/null +++ b/libvaladoc/valadoc-1.0.deps.in @@ -0,0 +1,3 @@ +libgvc +gee-0.8 +gmodule-2.0 diff --git a/libvaladoc/valadoc-1.0.pc.in b/libvaladoc/valadoc-1.0.pc.in new file mode 100644 index 000000000..8cf9ab02e --- /dev/null +++ b/libvaladoc/valadoc-1.0.pc.in @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/valadoc/ +includedir=@includedir@ +datarootdir=@datarootdir@ +datadir=@datadir@ +vapidir=@datadir@/vala/vapi + +Name: Valadoc +Description: The Vala documentation compiler library +Version: @VERSION@ +Requires: libgvc gee-0.8 gmodule-2.0 +Libs: -L${libdir} -lvaladoc +Cflags: -I${includedir}/valadoc-1.0 |