Merge: Semicolumn
authorJean Privat <jean@pryen.org>
Thu, 7 May 2015 01:56:10 +0000 (21:56 -0400)
committerJean Privat <jean@pryen.org>
Thu, 7 May 2015 01:56:10 +0000 (21:56 -0400)
Propose that `;` is usable as a hard line break. While this is not really nit-ish, the only need is in fact to be able to write short one-line scripts in environment where linefeeds are not an option.

~~~sh
$ ./nit -e 'for line in stdin.each_line do; var xs = line.split(":"); if xs.not_empty then print xs.first; end' < /etc/passwd
~~~

Pull-Request: #1305
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

36 files changed:
VERSION
examples/langannot.nit [new file with mode: 0644]
examples/languages/en/LC_MESSAGES/langannot.po [new file with mode: 0644]
examples/languages/fr/LC_MESSAGES/langannot.po [new file with mode: 0644]
examples/languages/ja/LC_MESSAGES/langannot.po [new file with mode: 0644]
examples/languages/langannot.pot [new file with mode: 0644]
examples/rosettacode/balanced_brackets.nit [new file with mode: 0644]
lib/bucketed_game.nit
lib/gettext.nit [new file with mode: 0644]
lib/java/java.nit
lib/mnit/mnit_injected_input.nit
lib/standard/collection/abstract_collection.nit
lib/standard/environ.nit
lib/standard/math.nit
lib/standard/stream.nit
misc/vim/plugin/nit.vim
src/doc/doc_phases/doc_graphs.nit
src/doc/doc_phases/doc_html.nit
src/doc/doc_phases/doc_indexing.nit
src/doc/doc_phases/doc_lin.nit [new file with mode: 0644]
src/doc/doc_phases/doc_structure.nit
src/doc/html_templates/html_components.nit
src/doc/html_templates/html_model.nit
src/doc/html_templates/html_templates.nit
src/frontend/frontend.nit
src/frontend/i18n_phase.nit [new file with mode: 0644]
src/nitdoc.nit
src/nitpretty.nit
src/platform/android.nit
src/pretty.nit
src/transform.nit
tests/exec.skip
tests/sav/balanced_brackets.res [new file with mode: 0644]
tests/sav/test_bufferedfilereader.res [new file with mode: 0644]
tests/test_bufferedfilereader.nit [new file with mode: 0644]
tests/tests.sh

diff --git a/VERSION b/VERSION
index 3d105a6..520c9c2 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v0.7.3
+v0.7.4
diff --git a/examples/langannot.nit b/examples/langannot.nit
new file mode 100644 (file)
index 0000000..7d471f6
--- /dev/null
@@ -0,0 +1,37 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Sample module showing the use of the i18n annotation
+module langannot is i18n
+
+import gettext
+
+class X
+       fun foo: String do
+               return "This String is a test"
+       end
+end
+
+var t = new X
+print t.foo
+
+print "This String is a test"
+
+print """Multiline string
+
+example
+
+
+of the language annotation capacities
+"""
diff --git a/examples/languages/en/LC_MESSAGES/langannot.po b/examples/languages/en/LC_MESSAGES/langannot.po
new file mode 100644 (file)
index 0000000..e4e0d02
--- /dev/null
@@ -0,0 +1,9 @@
+#: langannot::langannot 22--10:32, langannot::langannot 29--7:29
+msgid "This String is a test"
+msgstr "This String is a test"
+
+#: langannot::langannot 31--7:3
+msgid "Multiline string\n\nexample\n\n\nof the language annotation capacities\n"
+msgstr "Multiline string\n\nexample\n\n\nof the language annotation capacities\n"
+
+# Generated file, do not modify
diff --git a/examples/languages/fr/LC_MESSAGES/langannot.po b/examples/languages/fr/LC_MESSAGES/langannot.po
new file mode 100644 (file)
index 0000000..78ed2e7
--- /dev/null
@@ -0,0 +1,9 @@
+#: langannot::langannot 22--10:32, langannot::langannot 29--7:29
+msgid "This String is a test"
+msgstr "Cette chaîne de caractères est un test"
+
+#: langannot::langannot 31--7:3
+msgid "Multiline string\n\nexample\n\n\nof the language annotation capacities\n"
+msgstr "Example de\n\nchaine multiligne\n\n\navec annotation de localisation\n"
+
+# Generated file, do not modify
diff --git a/examples/languages/ja/LC_MESSAGES/langannot.po b/examples/languages/ja/LC_MESSAGES/langannot.po
new file mode 100644 (file)
index 0000000..1a50944
--- /dev/null
@@ -0,0 +1,9 @@
+#: langannot::langannot 22--10:32, langannot::langannot 29--7:29
+msgid "This String is a test"
+msgstr "この文字列はてストです"
+
+#: langannot::langannot 31--7:3
+msgid "Multiline string\n\nexample\n\n\nof the language annotation capacities\n"
+msgstr "複数行の文字列\n\nなサンプルプログラム\n\n\n新しい国際化アノテーションとともに\n"
+
+# Generated file, do not modify
diff --git a/examples/languages/langannot.pot b/examples/languages/langannot.pot
new file mode 100644 (file)
index 0000000..f6c2096
--- /dev/null
@@ -0,0 +1,9 @@
+#: langannot::langannot 22--10:32, langannot::langannot 29--7:29
+msgid "This String is a test"
+msgstr ""
+
+#: langannot::langannot 31--7:3
+msgid "Multiline string\n\nexample\n\n\nof the language annotation capacities\n"
+msgstr ""
+
+# Generated file, do not modify
diff --git a/examples/rosettacode/balanced_brackets.nit b/examples/rosettacode/balanced_brackets.nit
new file mode 100644 (file)
index 0000000..a1ae476
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Balanced brackets
+# SEE: <http://rosettacode.org/wiki/Balanced_brackets>
+module balanced_brackets
+
+# Are `[` and `]` balanced?
+# Other characters are ignored.
+#
+#     assert is_balanced("[][[]]")
+#     assert is_balanced("")
+#     assert not is_balanced("[[]")
+#     assert not is_balanced("][][")
+fun is_balanced(s: String): Bool
+do
+       var l = 0
+       for x in s.chars do
+               if x == '[' then
+                       l += 1
+               else if x == ']' then
+                       l -= 1
+                       if l < 0 then return false
+               end
+       end
+       return l == 0
+end
+
+var n = 3
+if args.not_empty then n = args.first.to_i
+
+for i in [0..10[ do
+       var a = (['[', ']'] * n)
+       a.shuffle
+       var b = a.join("")
+       if is_balanced(b) then print "{b} is well-balanced" else print "{b} is not well-balanced"
+end
index ae0d792..eba5d69 100644 (file)
@@ -48,20 +48,16 @@ class Buckets[G: Game]
        # Bucket type used in this implementation.
        type BUCKET: HashSet[Bucketable[G]]
 
-       private var buckets: Array[BUCKET] is noinit
-
        private var next_bucket: nullable BUCKET = null
        private var current_bucket_key: Int = -1
 
-       init
-       do
-               var n_buckets = 100
-               buckets = new Array[BUCKET].with_capacity(n_buckets)
+       # Number of `buckets`, default at 100
+       #
+       # Must be set prior to using any other methods of this class.
+       var n_buckets = 100
 
-               for b in [0 .. n_buckets [do
-                       buckets[b] = new HashSet[Bucketable[G]]
-               end
-       end
+       private var buckets: Array[BUCKET] =
+               [for b in n_buckets.times do new HashSet[Bucketable[G]]] is lazy
 
        # Add the Bucketable event `e` at `at_tick`.
        fun add_at(e: Bucketable[G], at_tick: Int)
diff --git a/lib/gettext.nit b/lib/gettext.nit
new file mode 100644 (file)
index 0000000..946a87c
--- /dev/null
@@ -0,0 +1,76 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Internationalization of Strings using `gettext` library
+module gettext is
+       new_annotation i18n
+end
+
+in "C" `{
+       #include <libintl.h>
+       #include <locale.h>
+`}
+
+redef class Sys
+       redef fun run do
+               "".to_cstring.set_locale
+               super
+       end
+end
+
+redef class NativeString
+       # Sets the locale of the program running
+       #
+       # This can be set at different times in the program,
+       # if used with an empty string, the locale will
+       # be set to the user's environment locale.
+       #
+       # For more info, SEE setlocale manual
+       fun set_locale `{
+               setlocale(LC_ALL, recv);
+       `}
+end
+
+redef class String
+       # Gets the translation of `self` via gettext from `dir` using `domain`
+       #
+       # `domain`: Gettext domain, i.e. file/module name
+       # `dir`: Where the locale/LC_MESSAGES folder for this domain is located
+       fun get_translation(domain, dir: String): String do
+               bindtextdomain(domain, dir)
+               return dgettext(domain)
+       end
+
+       # Gettext `gettext`, SEE gettext manual for further info
+       fun gettext: String
+       import String.to_cstring, NativeString.to_s `{
+               return NativeString_to_s(gettext(String_to_cstring(recv)));
+       `}
+
+       # Gettext `dgettext`, SEE gettext manual for further info
+       fun dgettext(domain: String): String
+       import String.to_cstring, NativeString.to_s `{
+               return NativeString_to_s(dgettext(String_to_cstring(domain), String_to_cstring(recv)));
+       `}
+end
+
+# Gettext `bindtextdomain`, SEE gettext manual for further info
+fun bindtextdomain(domain, dir: String) import String.to_cstring `{
+       bindtextdomain(String_to_cstring(domain), String_to_cstring(dir));
+`}
+
+# Gettext `textdomain`, SEE gettext manual for further info
+fun textdomain(domain: String) import String.to_cstring `{
+       textdomain(String_to_cstring(domain));
+`}
index 2020e30..6358430 100644 (file)
@@ -165,7 +165,7 @@ redef extern class JavaObject
 
        # Pops the current local reference frame and return a valid reference to self
        #
-       # Similiar to `JavaVM::pop_local_frame` but returns a value.
+       # Similar to `JavaVM::pop_local_frame` but returns a value.
        fun pop_from_local_frame: SELF
        do
                var jni_env = sys.jni_env
index 4d6ddfa..da2e33d 100644 (file)
@@ -20,7 +20,7 @@
 # In order to reproduce executions, the behavior of the application must be deterministic
 # for a given sequence of inputs.
 # The main source of differences in executions is caused by the `rand` function,
-# Set the environment variable `MNIT_SRAND` to a value to force srand to be initialized with this value.
+# Set the environment variable `NIT_SRAND` to a value to force srand to be initialized with this value.
 #
 # The input event file is made of event descriptions, one event by line.
 #
@@ -75,13 +75,6 @@ redef class App
 
        redef fun setup
        do
-               var env = "MNIT_SRAND".environ
-               if env != "" then
-                       srand_from(env.to_i)
-               else
-                       srand_from(0)
-               end
-
                var input = "MNIT_READ_INPUT".environ
                if input != "" then
                        injected_input_stream = new FileReader.open(input)
index 1b1e71e..2f7e454 100644 (file)
@@ -179,8 +179,9 @@ interface Collection[E]
        end
 end
 
-# Instances of the Iterator class generates a series of elements, one at a time.
-# They are mainly used with collections.
+# Iterators generate a series of elements, one at a time.
+#
+# They are mainly used with collections and obtained from `Collection::iterator`.
 interface Iterator[E]
        # The current item.
        # Require `is_ok`.
index 2f6ae46..0abe7f3 100644 (file)
@@ -60,3 +60,11 @@ redef class NativeString
        private fun get_environ: NativeString is extern "string_NativeString_NativeString_get_environ_0"
        private fun setenv( v : NativeString ) is extern "string_NativeString_NativeString_setenv_1"
 end
+
+redef class Sys
+       redef init
+       do
+               var x = "NIT_SRAND".environ
+               if x != "" then srand_from(x.to_i)
+       end
+end
index 2b55dca..9e1744f 100644 (file)
@@ -241,6 +241,11 @@ end
 redef class Collection[ E ]
        # Return a random element form the collection
        # There must be at least one element in the collection
+       #
+       # ~~~
+       # var x = [1,2,3].rand
+       # assert x == 1 or x == 2 or x == 3
+       # ~~~
        fun rand: E
        do
                if is_empty then abort
@@ -252,6 +257,19 @@ redef class Collection[ E ]
                end
                abort
        end
+
+       # Return a new array made of elements in a random order.
+       #
+       # ~~~
+       # var a = [1,2,1].to_shuffle
+       # assert a == [1,1,2] or a == [1,2,1] or a == [2,1,1]
+       # ~~~
+       fun to_shuffle: Array[E]
+       do
+               var res = self.to_a
+               res.shuffle
+               return res
+       end
 end
 
 redef class SequenceRead[E]
@@ -263,6 +281,36 @@ redef class SequenceRead[E]
        end
 end
 
+redef class AbstractArray[E]
+       # Reorder randomly the elements in self.
+       #
+       # ~~~
+       # var a = new Array[Int]
+       #
+       # a.shuffle
+       # assert a.is_empty
+       #
+       # a.add 1
+       # a.shuffle
+       # assert a == [1]
+       #
+       # a.add 2
+       # a.shuffle
+       # assert a == [1,2] or a == [2,1]
+       # ~~~
+       #
+       # ENSURE self.shuffle.has_exactly(old(self))
+       fun shuffle
+       do
+               for i in [0..length[ do
+                       var j = i + (length-i).rand
+                       var tmp = self[i]
+                       self[i] = self[j]
+                       self[j] = tmp
+               end
+       end
+end
+
 redef class Sys
        init
        do
index 7df1bd1..e0d3a67 100644 (file)
@@ -395,20 +395,18 @@ abstract class BufferedReader
        redef fun read(i)
        do
                if last_error != null then return ""
-               if _buffer.length == _buffer_pos then
-                       if not eof then
-                               return read(i)
-                       end
-                       return ""
-               end
-               if _buffer_pos + i >= _buffer.length then
-                       var from = _buffer_pos
-                       _buffer_pos = _buffer.length
-                       if from == 0 then return _buffer.to_s
-                       return _buffer.substring_from(from).to_s
+               if eof then return ""
+               var p = _buffer_pos
+               var bufsp = _buffer.length - p
+               if bufsp >= i then
+                       _buffer_pos += i
+                       return _buffer.substring(p, i).to_s
                end
-               _buffer_pos += i
-               return _buffer.substring(_buffer_pos - i, i).to_s
+               _buffer_pos = _buffer.length
+               var readln = _buffer.length - p
+               var s = _buffer.substring(p, readln).to_s
+               fill_buffer
+               return s + read(i - readln)
        end
 
        redef fun read_all
index 86d5e45..bb8dd13 100644 (file)
@@ -382,8 +382,10 @@ fun NitGitGrep()
        redraw!
 endfun
 
-" Activate the omnifunc on Nit files
-autocmd FileType nit set omnifunc=NitOmnifunc
+if !exists("g:nit_disable_omnifunc") || !g:nit_disable_omnifunc
+       " Activate the omnifunc on Nit files
+       autocmd FileType nit set omnifunc=NitOmnifunc
+endif
 
 " Define the user command Nitdoc for ease of use
 command -nargs=* Nitdoc call Nitdoc("<args>")
index 66ef564..0a011e8 100644 (file)
@@ -73,7 +73,7 @@ redef class MModulePage
                        end
                end
                op.append("\}\n")
-               return new GraphArticle(mentity, name, op)
+               return new GraphArticle(mentity, name, "Importation Graph", op)
        end
 end
 
@@ -107,7 +107,7 @@ redef class MClassPage
                        end
                end
                op.append("\}\n")
-               return new GraphArticle(mentity, name, op)
+               return new GraphArticle(mentity, name, "Inheritance Graph", op)
        end
 end
 
@@ -121,6 +121,9 @@ class GraphArticle
        # Graph ID (used for outputing file with names).
        var id: String
 
+       # Graph title to display.
+       var graph_title: String
+
        # Dot script of the graph.
        var dot: Text
 end
index 3329fc5..3ee3379 100644 (file)
@@ -133,8 +133,8 @@ class RenderHTMLPhase
 
        end
 
-       # A source link template for a given location
-       fun tpl_showsource(location: nullable Location): nullable String
+       # Returns a HTML link for a given `location`.
+       fun html_source_link(location: nullable Location): nullable String
        do
                if location == null then return null
                var source = ctx.opt_source.value
@@ -170,9 +170,9 @@ redef class DocPage
 
                # build page
                init_title(v, doc)
-               init_sidebar(v, doc)
                init_topmenu(v, doc)
                init_content(v, doc)
+               init_sidebar(v, doc)
 
                # piwik tracking
                var tracker_url = v.ctx.opt_piwik_tracker.value
@@ -211,11 +211,14 @@ redef class DocPage
 
        # Build page sidebar if any.
        fun init_sidebar(v: RenderHTMLPhase, doc: DocModel) do
-               sidebar = new TplSidebar
+               sidebar = new DocSideBar
+               sidebar.boxes.add new DocSideBox("Summary", html_toc)
        end
 
        # Build page content template.
-       fun init_content(v: RenderHTMLPhase, doc: DocModel) do end
+       fun init_content(v: RenderHTMLPhase, doc: DocModel) do
+               root.init_html_render(v, doc, self)
+       end
 end
 
 redef class OverviewPage
@@ -227,36 +230,6 @@ redef class OverviewPage
                        title = v.ctx.opt_custom_title.value.to_s
                end
        end
-
-       # TODO this should be done in StructurePhase.
-       redef fun init_content(v, doc) do
-               # intro text
-               var section = new TplSection.with_title("overview", title)
-               var article = new TplArticle("intro")
-               if v.ctx.opt_custom_intro.value != null then
-                       article.content = v.ctx.opt_custom_intro.value.to_s
-               end
-               section.add_child article
-               # Projects list
-               var mprojects = doc.model.mprojects.to_a
-               var sorter = new MConcernRankSorter
-               sorter.sort mprojects
-               var ssection = new TplSection.with_title("projects", "Projects")
-               for mproject in mprojects do
-                       var sarticle = mproject.tpl_article
-                       sarticle.subtitle = mproject.html_declaration
-                       sarticle.content = mproject.tpl_definition
-                       var comment = mproject.html_short_comment
-                       if comment != null then
-                               sarticle.content = comment
-                       end
-                       ssection.add_child sarticle
-               end
-               section.add_child ssection
-               self.add_section section
-       end
-
-       redef fun init_sidebar(v, doc) do sidebar = new TplSidebar
 end
 
 redef class SearchPage
@@ -269,64 +242,11 @@ redef class SearchPage
        end
 
        redef fun init_sidebar(v, doc) do end
-
-       # TODO this should be done in StructurePhase.
-       redef fun init_content(v, doc) do
-               var tpl = new TplSearchPage("search_all")
-               var section = new TplSection("search")
-               # title
-               tpl.title = "Index"
-               # modules list
-               for mmodule in modules_list(v, doc) do
-                       tpl.modules.add mmodule.html_link
-               end
-               # classes list
-               for mclass in classes_list(v, doc) do
-                       tpl.classes.add mclass.html_link
-               end
-               # properties list
-               for mproperty in mprops_list(v, doc) do
-                       var m = new Template
-                       m.add mproperty.intro.html_link
-                       m.add " ("
-                       m.add mproperty.intro.mclassdef.mclass.html_link
-                       m.add ")"
-                       tpl.props.add m
-               end
-               section.add_child tpl
-               self.add_section section
-       end
-
-       # Extract mmodule list to display (sorted by name)
-       private fun modules_list(v: RenderHTMLPhase, doc: DocModel): Array[MModule] do
-               var sorted = new Array[MModule]
-               for mmodule in doc.model.mmodule_importation_hierarchy do
-                       if mmodule.is_fictive or mmodule.is_test_suite then continue
-                       sorted.add mmodule
-               end
-               v.name_sorter.sort(sorted)
-               return sorted
-       end
-
-       # Extract mclass list to display (sorted by name)
-       private fun classes_list(v: RenderHTMLPhase, doc: DocModel): Array[MClass] do
-               var sorted = doc.mclasses.to_a
-               v.name_sorter.sort(sorted)
-               return sorted
-       end
-
-       # Extract mproperty list to display (sorted by name)
-       private fun mprops_list(v: RenderHTMLPhase, doc: DocModel): Array[MProperty] do
-               var sorted = doc.mproperties.to_a
-               v.name_sorter.sort(sorted)
-               return sorted
-       end
 end
 
 redef class MEntityPage
        redef var html_url is lazy do return mentity.nitdoc_url
        redef fun init_title(v, doc) do title = mentity.html_name
-       redef fun init_content(v, doc) do add_section root.start_rendering(v, doc, self)
 end
 
 # FIXME all clases below are roughly copied from `doc_pages` and adapted to new
@@ -350,27 +270,28 @@ redef class MGroupPage
                mclasses.add_all intros
                mclasses.add_all redefs
                if mclasses.is_empty then return
-               var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
-
+               var list = new UnorderedList
+               list.css_classes.add "list-unstyled list-labeled"
                var sorted = mclasses.to_a
                v.name_sorter.sort(sorted)
                for mclass in sorted do
                        list.add_li tpl_sidebar_item(mclass)
                end
-               sidebar.boxes.add new TplSideBox.with_content("All classes", list)
+               sidebar.boxes.add new DocSideBox("All classes", list)
+               sidebar.boxes.last.is_open = false
        end
 
-       private fun tpl_sidebar_item(def: MClass): TplListItem do
-               var classes = def.intro.tpl_css_classes.to_a
+       private fun tpl_sidebar_item(def: MClass): ListItem do
+               var classes = def.intro.css_classes
                if intros.has(def) then
                        classes.add "intro"
                else
                        classes.add "redef"
                end
                var lnk = new Template
-               lnk.add new TplLabel.with_classes(classes)
+               lnk.add new DocHTMLLabel.with_classes(classes)
                lnk.add def.html_link
-               return new TplListItem.with_content(lnk)
+               return new ListItem(lnk)
        end
 end
 
@@ -391,27 +312,29 @@ redef class MModulePage
                mclasses.add_all mentity.filter_intro_mclasses(v.ctx.min_visibility)
                mclasses.add_all mentity.filter_redef_mclasses(v.ctx.min_visibility)
                if mclasses.is_empty then return
-               var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
+               var list = new UnorderedList
+               list.css_classes.add "list-unstyled list-labeled"
 
                var sorted = mclasses.to_a
                v.name_sorter.sort(sorted)
                for mclass in sorted do
                        list.add_li tpl_sidebar_item(mclass)
                end
-               sidebar.boxes.add new TplSideBox.with_content("All classes", list)
+               sidebar.boxes.add new DocSideBox("All classes", list)
+               sidebar.boxes.last.is_open = false
        end
 
-       private fun tpl_sidebar_item(def: MClass): TplListItem do
-               var classes = def.intro.tpl_css_classes.to_a
+       private fun tpl_sidebar_item(def: MClass): ListItem do
+               var classes = def.intro.css_classes
                if def.intro_mmodule == self.mentity then
                        classes.add "intro"
                else
                        classes.add "redef"
                end
                var lnk = new Template
-               lnk.add new TplLabel.with_classes(classes)
+               lnk.add new DocHTMLLabel.with_classes(classes)
                lnk.add def.html_link
-               return new TplListItem.with_content(lnk)
+               return new ListItem(lnk)
        end
 end
 
@@ -428,47 +351,68 @@ redef class MClassPage
        redef fun init_sidebar(v, doc) do
                super
                var by_kind = new PropertiesByKind.with_elements(mclass_inherited_mprops(v, doc))
-               var summary = new TplList.with_classes(["list-unstyled"])
+               var summary = new UnorderedList
+               summary.css_classes.add "list-unstyled"
 
                by_kind.sort_groups(v.name_sorter)
                for g in by_kind.groups do tpl_sidebar_list(g, summary)
-               sidebar.boxes.add new TplSideBox.with_content("All properties", summary)
+               sidebar.boxes.add new DocSideBox("All properties", summary)
+               sidebar.boxes.last.is_open = false
        end
 
-       private fun tpl_sidebar_list(mprops: PropertyGroup[MProperty], summary: TplList) do
+       private fun tpl_sidebar_list(mprops: PropertyGroup[MProperty], summary: UnorderedList) do
                if mprops.is_empty then return
-               var entry = new TplListItem.with_content(mprops.title)
-               var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
+               var list = new UnorderedList
+               list.css_classes.add "list-unstyled list-labeled"
                for mprop in mprops do
                        list.add_li tpl_sidebar_item(mprop)
                end
-               entry.append list
-               summary.elts.add entry
+               var content = new Template
+               content.add mprops.title
+               content.add list
+               var li = new ListItem(content)
+               summary.add_li li
        end
 
-       private fun tpl_sidebar_item(mprop: MProperty): TplListItem do
-               var classes = mprop.intro.tpl_css_classes.to_a
+       private fun tpl_sidebar_item(mprop: MProperty): ListItem do
+               var classes = mprop.intro.css_classes
                if not mprop_is_local(mprop) then
                        classes.add "inherit"
                        var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url
-                       var def_url = "{cls_url}#{mprop.nitdoc_id}"
-                       var lnk = new TplLink(def_url, mprop.html_name)
+                       var def_url = "{cls_url}#article:{mprop.nitdoc_id}.definition"
+                       var lnk = new Link(def_url, mprop.html_name)
                        var mdoc = mprop.intro.mdoc_or_fallback
                        if mdoc != null then lnk.title = mdoc.short_comment
                        var item = new Template
-                       item.add new TplLabel.with_classes(classes)
+                       item.add new DocHTMLLabel.with_classes(classes)
                        item.add lnk
-                       return new TplListItem.with_content(item)
+                       return new ListItem(item)
                end
                if mpropdefs.has(mprop.intro) then
                        classes.add "intro"
                else
                        classes.add "redef"
                end
+               var def = select_mpropdef(mprop)
+               var anc = def.html_link_to_anchor
+               anc.href = "#article:{def.nitdoc_id}.definition"
                var lnk = new Template
-               lnk.add new TplLabel.with_classes(classes)
-               lnk.add mprop.html_link_to_anchor
-               return new TplListItem.with_content(lnk)
+               lnk.add new DocHTMLLabel.with_classes(classes)
+               lnk.add anc
+               return new ListItem(lnk)
+       end
+
+       # Get the mpropdef contained in `self` page for a mprop.
+       #
+       # FIXME this method is used to translate a mprop into a mpropdefs for
+       # section linking. A better page structure should avoid this...
+       private fun select_mpropdef(mprop: MProperty): MPropDef do
+               for mclassdef in mentity.mclassdefs do
+                       for mpropdef in mclassdef.mpropdefs do
+                               if mpropdef.mproperty == mprop then return mpropdef
+                       end
+               end
+               abort # FIXME is there a case where the prop is not found?
        end
 
        private fun mclass_inherited_mprops(v: RenderHTMLPhase, doc: DocModel): Set[MProperty] do
@@ -512,307 +456,150 @@ redef class MPropertyPage
 end
 
 redef class DocComposite
-       # Render this DocComposite as HTML.
+       # Prepares the HTML rendering for this element.
        #
-       # FIXME needed to maintain TplSection compatibility.
-       fun render(v: RenderHTMLPhase, doc: DocModel, page: MEntityPage, parent: TplSectionElt) is abstract
+       # This visit is mainly used to set template attributes before rendering.
+       fun init_html_render(v: RenderHTMLPhase, doc: DocModel, page: DocPage) do
+               for child in children do child.init_html_render(v, doc, page)
+       end
 end
 
-redef class DocRoot
-
-       # Start the rendering from root.
-       #
-       # FIXME needed to maintain TplSection compatibility.
-       fun start_rendering(v: RenderHTMLPhase, doc: DocModel, page: MEntityPage): TplSection do
-               var section = new TplSection("top")
-               var mentity = page.mentity
-               section.title = mentity.html_name
-               section.subtitle = mentity.html_declaration
-               # FIXME ugly hack to avoid diff
+# FIXME hideous hacks to avoid diff
+redef class MEntitySection
+       redef fun init_html_render(v, doc, page) do
+               if not page isa MEntityPage then return
+               var mentity = self.mentity
                if mentity isa MGroup and mentity.is_root then
-                       section.title = mentity.mproject.html_name
-                       section.subtitle = mentity.mproject.html_declaration
+                       html_title = mentity.mproject.html_name
+                       html_subtitle = mentity.mproject.html_declaration
                else if mentity isa MProperty then
-                       section.title = "{mentity.html_name}{mentity.intro.html_signature.write_to_string}"
-                       section.subtitle = mentity.html_namespace
-                       section.summary_title = mentity.html_name
-               end
-               render(v, doc, page, section)
-               return section
-       end
-
-       redef fun render(v, doc, page, parent) do
-               for child in children do
-                       child.render(v, doc, page, parent)
+                       var title = new Template
+                       title.add mentity.html_name
+                       title.add mentity.html_signature
+                       html_title = title
+                       html_subtitle = mentity.html_namespace
+                       toc_title = mentity.html_name
                end
+               super
        end
 end
 
+# FIXME hideous hacks to avoid diff
 redef class ConcernSection
-       redef fun render(v, doc, page, parent) do
-               var section = new TplSection(mentity.nitdoc_id)
+       redef fun init_html_render(v, doc, page) do
+               if not page isa MEntityPage then return
                var mentity = self.mentity
-               # FIXME hideous hacks to avoid diff
-               if page.mentity isa MModule and mentity isa MModule then
-                       render_concern_mmodule(page, section, mentity)
-               else if page.mentity isa MClass and mentity isa MModule then
-                       render_concern_other(page, section, mentity)
-               else if page.mentity isa MProperty and mentity isa MModule then
-                       render_concern_other(page, section, mentity)
-               end
-               for child in children do
-                       child.render(v, doc, page, section)
-               end
-               parent.add_child section
-       end
-
-       private fun render_concern_mmodule(page: MEntityPage, section: TplSection, mmodule: MModule) do
-               var title = new Template
-               if mmodule == page.mentity then
+               if page isa MGroupPage then
+                       html_title = null
+                       toc_title = mentity.html_name
+                       is_toc_hidden = false
+               else if page.mentity isa MModule and mentity isa MModule then
+                       var title = new Template
+                       if mentity == page.mentity then
+                               title.add "in "
+                               toc_title = "in {mentity.html_name}"
+                       else
+                               title.add "from "
+                               toc_title = "from {mentity.html_name}"
+                       end
+                       title.add mentity.html_namespace
+                       html_title = title
+               else if (page.mentity isa MClass and mentity isa MModule) or
+                               (page.mentity isa MProperty and mentity isa MModule) then
+                       var title = new Template
                        title.add "in "
-                       section.summary_title = "in {mmodule.html_name}"
-               else
-                       title.add "from "
-                       section.summary_title = "from {mmodule.html_name}"
+                       title.add mentity.html_namespace
+                       html_title = title
+                       toc_title = "in {mentity.html_name}"
                end
-               title.add mmodule.html_namespace
-               section.title = title
-       end
-
-       private fun render_concern_other(page: MEntityPage, section: TplSection, mmodule: MModule) do
-               var title = new Template
-               title.add "in "
-               title.add mmodule.html_namespace
-               section.title = title
-               section.summary_title = "in {mmodule.html_name}"
-       end
-end
-
-redef class MEntitySection
-       redef fun render(v, doc, page, parent) do
-               for child in children do child.render(v, doc, page, parent)
+               super
        end
 end
 
+# TODO redo showlink
 redef class IntroArticle
-       redef fun render(v, doc, page, parent) do
-               var article = new TplArticle("intro")
+       redef fun init_html_render(v, doc, page) do
                var mentity = self.mentity
                if mentity isa MModule then
-                       article.source_link = v.tpl_showsource(mentity.location)
+                       html_source_link = v.html_source_link(mentity.location)
                else if mentity isa MClassDef then
-                       article.source_link = v.tpl_showsource(mentity.location)
+                       html_source_link = v.html_source_link(mentity.location)
                else if mentity isa MPropDef then
-                       article.source_link = v.tpl_showsource(mentity.location)
+                       html_source_link = v.html_source_link(mentity.location)
                end
-               # article.subtitle = mentity.html_declaration
-               # FIXME diff hack
-               if mentity isa MProperty then
-                       # intro title
-                       var ns = mentity.intro.mclassdef.mmodule.html_namespace
-                       var section = new TplSection("intro")
-                       var title = new Template
-                       title.add "Introduction in "
-                       title.add ns
-                       section.title = title
-                       section.summary_title = "Introduction"
-                       var intro = mentity.intro.tpl_article
-                       intro.source_link = v.tpl_showsource(mentity.intro.location)
-                       section.add_child intro
-                       parent.add_child section
-               else
-                       article.content = mentity.tpl_definition
-                       parent.add_child article
-               end
-       end
-end
-
-redef class ConcernsArticle
-       redef fun render(v, doc, page, parent) do
-               # FIXME diff hack
-               var title = "concerns"
-               if page.mentity isa MProperty then title = "Concerns"
-               parent.add_child new TplArticle.
-                       with_content(title, "Concerns", concerns.to_tpl)
        end
 end
 
+# FIXME less hideous hacks...
 redef class DefinitionArticle
-       redef fun render(v, doc, page, parent) do
-               var article: TplArticle
+       redef fun init_html_render(v, doc, page) do
                var mentity = self.mentity
-               # FIXME hideous hacks...
-               if mentity isa MModule then
-                       article = mentity.tpl_article
-                       article.subtitle = mentity.html_declaration
-                       article.content = mentity.tpl_definition
+               if mentity isa MProject or mentity isa MModule then
+                       var title = new Template
+                       title.add mentity.html_icon
+                       title.add mentity.html_namespace
+                       html_title = title
+                       toc_title = mentity.html_name
+                       if mentity isa MModule then
+                               html_source_link = v.html_source_link(mentity.location)
+                       end
                else if mentity isa MClass then
-                       article = make_mclass_article(v, page)
+                       var title = new Template
+                       title.add mentity.html_icon
+                       title.add mentity.html_link
+                       html_title = title
+                       html_subtitle = mentity.html_namespace
+                       toc_title = mentity.html_name
+                       is_no_body = true
                else if mentity isa MClassDef then
-                       article = make_mclassdef_article(v, page)
-                       article.source_link = v.tpl_showsource(mentity.location)
-               else if mentity isa MPropDef and page.mentity isa MClass then
-                       article = make_mpropdef_article(v, doc, page)
-               else
-                       article = mentity.tpl_article
-                       article.subtitle = mentity.html_declaration
-                       if mentity isa MPropDef then
-                               article.source_link = v.tpl_showsource(mentity.location)
-                       end
-               end
-               for child in children do
-                       child.render(v, doc, page, article)
-               end
-               parent.add_child article
-       end
-
-       # FIXME avoid diff while preserving TplArticle compatibility.
-
-       private fun make_mclass_article(v: RenderHTMLPhase, page: MEntityPage): TplArticle do
-               var article = mentity.tpl_article
-               article.subtitle = mentity.html_namespace
-               article.content = null
-               return article
-       end
-
-       private fun make_mclassdef_article(v: RenderHTMLPhase, page: MEntityPage): TplArticle do
-               var mclassdef = mentity.as(MClassDef)
-               var article = mentity.tpl_article
-               if mclassdef.is_intro and mclassdef.mmodule != page.mentity then
-                       article = mentity.tpl_short_article
-               end
-               var title = new Template
-               title.add "in "
-               title.add mclassdef.mmodule.html_namespace
-               article.subtitle = title
-               return article
-       end
-
-       private fun make_mpropdef_article(v: RenderHTMLPhase, doc: DocModel, page: MEntityPage): TplArticle
-       do
-               var mpropdef = mentity.as(MPropDef)
-               var mprop = mpropdef.mproperty
-               var article = new TplArticle(mprop.nitdoc_id)
-               var title = new Template
-               title.add mprop.tpl_icon
-               title.add "<span id='{mpropdef.nitdoc_id}'></span>"
-               if mpropdef.is_intro then
-                       title.add mprop.html_link
-                       title.add mprop.intro.html_signature
-               else
-                       var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url
-                       var def_url = "{cls_url}#{mprop.nitdoc_id}"
-                       var lnk = new TplLink.with_title(def_url, mprop.html_name,
-                                       "Go to introduction")
-                       title.add "redef "
-                       title.add lnk
-               end
-               article.title = title
-               article.title_classes.add "signature"
-               article.summary_title = "{mprop.html_name}"
-               article.subtitle = mpropdef.html_namespace
-               var comment = mpropdef.html_comment
-               if comment != null then
-                       article.content = comment
-               end
-               # TODO move in its own phase? let's see after doc_template refactoring.
-               # Add linearization
-               var all_defs = new HashSet[MPropDef]
-               for local_def in local_defs(page.as(MClassPage), mprop) do
-                       all_defs.add local_def
-                       var smpropdef = local_def
-                       while not smpropdef.is_intro do
-                               smpropdef = smpropdef.lookup_next_definition(
-                                       doc.mainmodule, smpropdef.mclassdef.bound_mtype)
-                               all_defs.add smpropdef
-                       end
-               end
-               var lin = all_defs.to_a
-               doc.mainmodule.linearize_mpropdefs(lin)
-               if lin.length > 1 then
-                       var lin_article = new TplArticle("{mpropdef.nitdoc_id}.lin")
-                       lin_article.title = "Inheritance"
-                       var lst = new TplList.with_classes(["list-unstyled", "list-labeled"])
-                       for smpropdef in lin do
-                               lst.add_li smpropdef.tpl_inheritance_item
+                       var title = new Template
+                       title.add "in "
+                       title.add mentity.mmodule.html_namespace
+                       html_title = mentity.html_declaration
+                       html_subtitle = title
+                       toc_title = "in {mentity.html_name}"
+                       html_source_link = v.html_source_link(mentity.location)
+                       if page isa MEntityPage and mentity.is_intro and mentity.mmodule != page.mentity then
+                               is_short_comment = true
                        end
-                       lin_article.content = lst
-                       article.add_child lin_article
-               end
-               return article
-       end
-
-       # Filter `page.mpropdefs` for this `mpropertie`.
-       #
-       # FIXME compatability with current templates.
-       private fun local_defs(page: MClassPage, mproperty: MProperty): HashSet[MPropDef] do
-               var mpropdefs = new HashSet[MPropDef]
-               for mpropdef in page.mpropdefs do
-                       if mpropdef.mproperty == mproperty then
-                               mpropdefs.add mpropdef
+                       if page isa MModulePage then is_toc_hidden = true
+               else if mentity isa MPropDef then
+                       if page isa MClassPage then
+                               var title = new Template
+                               title.add mentity.html_icon
+                               title.add mentity.html_declaration
+                               html_title = title
+                               html_subtitle = mentity.html_namespace
+                               toc_title = mentity.html_name
+                       else
+                               var title = new Template
+                               title.add "in "
+                               title.add mentity.mclassdef.html_link
+                               html_title = title
+                               toc_title = "in {mentity.mclassdef.html_name}"
                        end
+                       html_source_link = v.html_source_link(mentity.location)
                end
-               return mpropdefs
-       end
-end
-
-redef class IntrosRedefsListArticle
-       redef fun render(v, doc, page, parent) do
-               if mentities.is_empty then return
-               var title = list_title
-               # FIXME diff hack
-               var id = "intros"
-               if title == "Redefines" then id = "redefs"
-               var article = new TplArticle.with_title("{mentity.nitdoc_id}.{id}", title)
-               var list = new TplList.with_classes(["list-unstyled", "list-labeled"])
-               for mentity in mentities do
-                       list.add_li mentity.tpl_list_item
-               end
-               article.content = list
-               parent.add_child article
-       end
-end
-
-# FIXME compatibility with doc_templates.
-redef class ImportationListSection
-       redef fun render(v, doc, page, parent) do
-               var section = new TplSection.with_title("dependencies", "Dependencies")
-               for child in children do
-                       child.render(v, doc, page, section)
-               end
-               parent.add_child section
-       end
-end
-
-# FIXME compatibility with doc_templates.
-redef class InheritanceListSection
-       redef fun render(v, doc, page, parent) do
-               var section = new TplSection.with_title("inheritance", "Inheritance")
-               for child in children do
-                       child.render(v, doc, page, section)
+               if page isa MGroupPage and mentity isa MModule then
+                       is_toc_hidden = true
                end
-               parent.add_child section
+               super
        end
 end
 
-# FIXME compatibility with doc_templates.
-redef class HierarchyListArticle
-       redef fun render(v, doc, page, parent) do
-               if mentities.is_empty then return
-               var title = list_title
-               var id = list_title.to_lower
-               var article = new TplArticle.with_title(id, title)
-               var list = new TplList.with_classes(["list-unstyled", "list-definition"])
-               for mentity in mentities do
-                       list.elts.add mentity.tpl_list_item
+redef class HomeArticle
+       redef fun init_html_render(v, doc, page) do
+               if v.ctx.opt_custom_title.value != null then
+                       self.html_title = v.ctx.opt_custom_title.value.to_s
+                       self.toc_title = v.ctx.opt_custom_title.value.to_s
                end
-               article.content = list
-               parent.add_child article
+               self.content = v.ctx.opt_custom_intro.value
+               super
        end
 end
 
 redef class GraphArticle
-       redef fun render(v, doc, page, parent) do
+       redef fun init_html_render(v, doc, page) do
                var output_dir = v.ctx.output_dir
                var path = output_dir / id
                var path_sh = path.escape_to_sh
@@ -821,336 +608,7 @@ redef class GraphArticle
                file.close
                sys.system("\{ test -f {path_sh}.png && test -f {path_sh}.s.dot && diff -- {path_sh}.dot {path_sh}.s.dot >/dev/null 2>&1 ; \} || \{ cp -- {path_sh}.dot {path_sh}.s.dot && dot -Tpng -o{path_sh}.png -Tcmapx -o{path_sh}.map {path_sh}.s.dot ; \}")
                var fmap = new FileReader.open("{path}.map")
-               var map = fmap.read_all
+               self.map = fmap.read_all
                fmap.close
-
-               var article = new TplArticle("graph")
-               var alt = ""
-               # FIXME diff hack
-               # if title != null then
-                       # article.title = title
-                       # alt = "alt='{title.html_escape}'"
-               # end
-               article.css_classes.add "text-center"
-               var content = new Template
-               var name_html = id.html_escape
-               content.add "<img src='{name_html}.png' usemap='#{name_html}' style='margin:auto' {alt}/>"
-               content.add map
-               article.content = content
-               parent.add_child article
-       end
-end
-
-redef class Location
-       # Github url based on this location
-       fun github(gitdir: String): String do
-               var base_dir = getcwd.join_path(gitdir).simplify_path
-               var file_loc = getcwd.join_path(file.filename).simplify_path
-               var gith_loc = file_loc.substring(base_dir.length + 1, file_loc.length)
-               return "{gith_loc}:{line_start},{column_start}--{line_end},{column_end}"
-       end
-end
-
-redef class MEntity
-       # A template article that briefly describe the entity
-       fun tpl_short_article: TplArticle do
-               var tpl = tpl_article
-               var comment = html_short_comment
-               if comment != null then
-                       tpl.content = comment
-               end
-               return tpl
-       end
-
-       # A template article that describe the entity
-       fun tpl_article: TplArticle do
-               var tpl = new TplArticle.with_title(nitdoc_id, tpl_title)
-               tpl.title_classes.add "signature"
-               tpl.subtitle = html_namespace
-               tpl.summary_title = html_name
-               return tpl
-       end
-
-       # A template definition of the mentity
-       # include name, sysnopsys, comment and namespace
-       fun tpl_definition: TplDefinition is abstract
-
-       # A li element that can go in a list
-       fun tpl_list_item: TplListItem do
-               var lnk = new Template
-               lnk.add new TplLabel.with_classes(tpl_css_classes)
-               lnk.add html_link
-               var comment = html_short_comment
-               if comment != null then
-                       lnk.add ": "
-                       lnk.add comment
-               end
-               return new TplListItem.with_content(lnk)
-       end
-
-       var tpl_css_classes = new Array[String]
-
-       # Box title for this mentity
-       fun tpl_title: Template do
-               var title = new Template
-               title.add tpl_icon
-               title.add html_namespace
-               return title
-       end
-
-       # Icon that will be displayed before the title
-       fun tpl_icon: TplIcon do
-               var icon = new TplIcon.with_icon("tag")
-               icon.css_classes.add_all(tpl_css_classes)
-               return icon
-       end
-end
-
-redef class MConcern
-       # Return a li element for `self` that can be displayed in a concern list
-       private fun tpl_concern_item: TplListItem do
-               var lnk = new Template
-               lnk.add html_link_to_anchor
-               var comment = html_short_comment
-               if comment != null then
-                       lnk.add ": "
-                       lnk.add comment
-               end
-               return new TplListItem.with_content(lnk)
-       end
-end
-
-redef class MProject
-       redef fun tpl_definition do
-               var tpl = new TplDefinition
-                       var comment = html_comment
-               if comment != null then
-                       tpl.comment = comment
-               end
-               return tpl
-       end
-
-       redef fun tpl_css_classes do return ["public"]
-end
-
-redef class MGroup
-       redef fun tpl_definition do
-               var tpl = new TplDefinition
-               var comment = html_comment
-               if comment != null then
-                       tpl.comment = comment
-               end
-               return tpl
-       end
-end
-
-redef class MModule
-       redef fun tpl_definition do
-               var tpl = new TplClassDefinition
-               var comment = html_comment
-               if comment != null then
-                       tpl.comment = comment
-               end
-               return tpl
-       end
-
-       redef fun tpl_css_classes do return ["public"]
-end
-
-redef class MClass
-       redef fun tpl_definition do return intro.tpl_definition
-
-       redef fun tpl_title do
-               var title = new Template
-               title.add tpl_icon
-               title.add html_link
-               return title
-       end
-
-       redef fun tpl_icon do return intro.tpl_icon
-       redef fun tpl_css_classes do return intro.tpl_css_classes
-end
-
-redef class MClassDef
-       redef fun tpl_article do
-               var tpl = new TplArticle(nitdoc_id)
-               tpl.summary_title = "in {mmodule.html_name}"
-               tpl.title = html_declaration
-               tpl.title_classes.add "signature"
-               var title = new Template
-               title.add "in "
-               title.add mmodule.html_namespace
-               tpl.subtitle = title
-               var comment = html_comment
-               if comment != null then
-                       tpl.content = comment
-               end
-               return tpl
-       end
-
-       redef fun tpl_title do
-               var title = new Template
-               title.add tpl_icon
-               title.add html_link
-               return title
-       end
-
-       redef fun tpl_definition do
-               var tpl = new TplClassDefinition
-               var comment = html_comment
-               if comment != null then
-                       tpl.comment = comment
-               end
-               return tpl
-       end
-
-       redef fun tpl_css_classes do
-               var set = new HashSet[String]
-               if is_intro then set.add "intro"
-               for m in mclass.intro.modifiers do set.add m.to_cmangle
-               for m in modifiers do set.add m.to_cmangle
-               return set.to_a
-       end
-
-       fun tpl_modifiers: Template do
-               var tpl = new Template
-               for modifier in modifiers do
-                       if modifier == "public" then continue
-                       tpl.add "{modifier.html_escape} "
-               end
-               return tpl
-       end
-end
-
-redef class MProperty
-       redef fun tpl_title do return intro.tpl_title
-       redef fun tpl_icon do return intro.tpl_icon
-       redef fun tpl_css_classes do return intro.tpl_css_classes
-end
-
-redef class MPropDef
-       redef fun tpl_article do
-               var tpl = new TplArticle(nitdoc_id)
-               tpl.summary_title = "in {mclassdef.html_name}"
-               var title = new Template
-               title.add "in "
-               title.add mclassdef.html_link
-               tpl.title = title
-               tpl.subtitle = html_declaration
-               var comment = html_comment
-               if comment != null then
-                       tpl.content = comment
-               end
-               return tpl
-       end
-
-       redef fun tpl_definition do
-               var tpl = new TplDefinition
-               var comment = html_comment
-               if comment != null then
-                       tpl.comment = comment
-               end
-               return tpl
-       end
-
-       redef fun tpl_css_classes do
-               var set = new HashSet[String]
-               if is_intro then set.add "intro"
-               for m in mproperty.intro.modifiers do set.add m.to_cmangle
-               for m in modifiers do set.add m.to_cmangle
-               return set.to_a
-       end
-
-       fun tpl_modifiers: Template do
-               var tpl = new Template
-               for modifier in modifiers do
-                       if modifier == "public" then continue
-                       tpl.add "{modifier.html_escape} "
-               end
-               return tpl
-       end
-
-       redef fun tpl_list_item do
-               var lnk = new Template
-               lnk.add new TplLabel.with_classes(tpl_css_classes.to_a)
-               var atext = html_link.text
-               var ahref = "{mclassdef.mclass.nitdoc_url}#{mproperty.nitdoc_id}"
-               var atitle = html_link.title
-               var anchor = new Link.with_title(ahref, atext, atitle)
-               lnk.add anchor
-               var comment = html_short_comment
-               if comment != null then
-                       lnk.add ": "
-                       lnk.add comment
-               end
-               return new TplListItem.with_content(lnk)
-       end
-
-       fun tpl_inheritance_item: TplListItem do
-               var lnk = new Template
-               lnk.add new TplLabel.with_classes(tpl_css_classes.to_a)
-               lnk.add mclassdef.mmodule.html_namespace
-               lnk.add "::"
-               var atext = mclassdef.html_link.text
-               var ahref = "{mclassdef.mclass.nitdoc_url}#{mproperty.nitdoc_id}"
-               var atitle = mclassdef.html_link.title
-               var anchor = new Link.with_title(ahref, atext, atitle)
-               lnk.add anchor
-               var comment = html_short_comment
-               if comment != null then
-                       lnk.add ": "
-                       lnk.add comment
-               end
-               var li = new TplListItem.with_content(lnk)
-               li.css_classes.add "signature"
-               return li
-       end
-end
-
-redef class ConcernsTree
-
-       private var seen = new HashSet[MConcern]
-
-       redef fun add(p, e) do
-               if seen.has(e) then return
-               seen.add e
-               super(p, e)
-       end
-
-       fun to_tpl: TplList do
-               var lst = new TplList.with_classes(["list-unstyled", "list-definition"])
-               for r in roots do
-                       var li = r.tpl_concern_item
-                       lst.add_li li
-                       build_list(r, li)
-               end
-               return lst
-       end
-
-       private fun build_list(e: MConcern, li: TplListItem) do
-               if not sub.has_key(e) then return
-               var subs = sub[e]
-               var lst = new TplList.with_classes(["list-unstyled", "list-definition"])
-               for e2 in subs do
-                       if e2 isa MGroup and e2.is_root then
-                               build_list(e2, li)
-                       else
-                               var sli = e2.tpl_concern_item
-                               lst.add_li sli
-                               build_list(e2, sli)
-                       end
-               end
-               li.append lst
-       end
-end
-
-redef class MInnerClassDef
-       redef fun tpl_definition do
-               var tpl = new TplClassDefinition
-               var comment = html_comment
-               if comment != null then
-                       tpl.comment = comment
-               end
-               return tpl
        end
 end
index 2f1151f..3e71df2 100644 (file)
@@ -43,7 +43,7 @@ class IndexingPhase
                                if not doc.mpropdefs.has(mpropdef) then continue
                                var full_name = mpropdef.mclassdef.mclass.full_name
                                var cls_url = mpropdef.mclassdef.mclass.nitdoc_url
-                               var def_url = "{cls_url}#{mpropdef.mproperty.nitdoc_id}"
+                               var def_url = "{cls_url}#article:{mpropdef.nitdoc_id}.definition"
                                add_result_for(mproperty.name, full_name, def_url)
                        end
                end
diff --git a/src/doc/doc_phases/doc_lin.nit b/src/doc/doc_phases/doc_lin.nit
new file mode 100644 (file)
index 0000000..eda76ba
--- /dev/null
@@ -0,0 +1,100 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Add linearization lists to DefinitionArticle found in MClass pages.
+module doc_lin
+
+import doc_structure
+
+# LinPhase populates the DocPage content with linearization data.
+class LinListPhase
+       super DocPhase
+
+       # Used to sort list by linearization order
+       private var lin_sorter = new MEntityNameSorter
+
+       redef fun apply do
+               for page in doc.pages do page.apply_linearization(self, doc)
+       end
+end
+
+redef class DocPage
+
+       # Populates `self` with linearization data.
+       #
+       # See `LinListPhase`.
+       fun apply_linearization(v: LinListPhase, doc: DocModel) do end
+end
+
+redef class MClassPage
+       redef fun apply_linearization(v, doc) do
+               root.apply_linearization(v, doc, self)
+       end
+end
+
+redef class DocComposite
+
+       # Populates `self` with linearization data.
+       #
+       # For now, it's only used for mpropdefs linearization in MClassPage.
+       #
+       # See `LinListPhase`.
+       private fun apply_linearization(v: LinListPhase, doc: DocModel, page: DocPage) do
+               for child in children do child.apply_linearization(v, doc, page)
+       end
+end
+
+redef class DefinitionArticle
+       redef fun apply_linearization(v, doc, page) do
+               var mentity = self.mentity
+               if not mentity isa MPropDef then return
+               # Add linearization
+               var all_defs = new HashSet[MPropDef]
+               for local_def in local_defs(page.as(MClassPage), mentity.mproperty) do
+                       all_defs.add local_def
+                       var smpropdef = local_def
+                       while not smpropdef.is_intro do
+                               smpropdef = smpropdef.lookup_next_definition(
+                                       doc.mainmodule, smpropdef.mclassdef.bound_mtype)
+                               all_defs.add smpropdef
+                       end
+               end
+               var lin = all_defs.to_a
+               doc.mainmodule.linearize_mpropdefs(lin)
+               if lin.length > 1 then
+                       add_child new DefinitionLinArticle(mentity, lin)
+               end
+       end
+
+       # Filter `page.mpropdefs` for this `mpropertie`.
+       #
+       # FIXME compatability with current templates.
+       private fun local_defs(page: MClassPage, mproperty: MProperty): HashSet[MPropDef] do
+               var mpropdefs = new HashSet[MPropDef]
+               for mpropdef in page.mpropdefs do
+                       if mpropdef.mproperty == mproperty then
+                               mpropdefs.add mpropdef
+                       end
+               end
+               return mpropdefs
+       end
+end
+
+# Display a linearized list of definitions.
+class DefinitionLinArticle
+       super MEntityArticle
+
+       # The linearized list to display.
+       var mentities: Array[MEntity]
+end
index 9c32c8c..1c6b432 100644 (file)
@@ -32,15 +32,11 @@ class StructurePhase
 
        # Populates the given DocModel.
        redef fun apply do
-               for page in doc.pages do
-                       if page isa MEntityPage then page.apply_structure(self, doc)
-               end
+               for page in doc.pages do page.apply_structure(self, doc)
        end
-
-       # TODO index and search page should also be structured here
 end
 
-redef class MEntityPage
+redef class DocPage
 
        # Populates `self` with structure elements like DocComposite ones.
        #
@@ -48,6 +44,34 @@ redef class MEntityPage
        fun apply_structure(v: StructurePhase, doc: DocModel) do end
 end
 
+redef class OverviewPage
+       redef fun apply_structure(v, doc) do
+               var article = new HomeArticle
+               root.add_child article
+               # Projects list
+               var mprojects = doc.model.mprojects.to_a
+               var sorter = new MConcernRankSorter
+               sorter.sort mprojects
+               var section = new ProjectsSection
+               for mproject in mprojects do
+                       section.add_child new DefinitionArticle(mproject)
+               end
+               article.add_child section
+       end
+end
+
+redef class SearchPage
+       redef fun apply_structure(v, doc) do
+               var mmodules = doc.mmodules.to_a
+               v.name_sorter.sort(mmodules)
+               var mclasses = doc.mclasses.to_a
+               v.name_sorter.sort(mclasses)
+               var mprops = doc.mproperties.to_a
+               v.name_sorter.sort(mprops)
+               root.add_child new IndexArticle(mmodules, mclasses, mprops)
+       end
+end
+
 redef class MGroupPage
        redef fun apply_structure(v, doc) do
                var section = new MEntitySection(mentity)
@@ -67,11 +91,11 @@ redef class MGroupPage
                mentity.booster_rank = 0
                section.add_child new ConcernsArticle(mentity, concerns)
                for mentity in concerns do
+                       var ssection = new ConcernSection(mentity)
                        if mentity isa MModule then
-                               section.add_child new DefinitionArticle(mentity)
-                       else
-                               section.add_child new ConcernSection(mentity)
+                               ssection.add_child new DefinitionArticle(mentity)
                        end
+                       section.add_child ssection
                end
        end
 end
@@ -294,3 +318,27 @@ end
 class DefinitionArticle
        super MEntityArticle
 end
+
+# The main project article.
+class HomeArticle
+       super DocArticle
+end
+
+# The project list.
+class ProjectsSection
+       super DocArticle
+end
+
+# An article that display an index of mmodules, mclasses and mproperties.
+class IndexArticle
+       super DocArticle
+
+       # List of mmodules to display.
+       var mmodules: Array[MModule]
+
+       # List of mclasses to display.
+       var mclasses: Array[MClass]
+
+       # List of mproperties to display.
+       var mprops: Array[MProperty]
+end
index e65059b..221162b 100644 (file)
 module html_components
 
 import doc_base
-import template
+import html::bootstrap
 import json::static
 
-#########################
-# general layout elements
-#########################
+# A label with a text content.
+class DocHTMLLabel
+       super BSLabel
 
-# A sidebar template
-class TplSidebar
-       super Template
-
-       # Sidebar contains sidebar element templates called boxes
-       var boxes = new Array[TplSidebarElt]
-
-       # Sort boxes by order priority
-       private fun order_boxes do
-               var sorter = new OrderComparator
-               sorter.sort(boxes)
-       end
-
-       redef fun rendering do
-               if boxes.is_empty then return
-               order_boxes
-               addn "<div id='sidebar'>"
-               for box in boxes do add box
-               addn "</div>"
-       end
-end
-
-# Comparator used to sort boxes by order
-private class OrderComparator
-       super Comparator
-
-       redef type COMPARED: TplSidebarElt
-
-       redef fun compare(a, b) do
-               if a.order < b.order then return -1
-               if a.order > b.order then return 1
-               return 0
-       end
-end
-
-# Something that can be put in the sidebar
-class TplSidebarElt
-       super Template
-
-       # Order of the box in the sidebar
-       var order: Int = 1
-
-       init with_order(order: Int) do self.order = order
-end
-
-# Agenericbox that can be added to sidebar
-class TplSideBox
-       super TplSidebarElt
-
-       # Title of the box to display
-       # Title is also a placeholder for the collapse link
-       var title: String
-
-       # Box HTML id
-       # equals to `title.to_cmangle` by default
-       # Used for collapsing
-       var id: String is noinit
-
-       # Content to display in the box
-       # box will not be rendered if the content is null
-       var content: nullable Writable = null is writable
-
-       # Is the box opened by default
-       # otherwise, the user will have to clic on the title to display the content
-       var is_open = false is writable
-
-       init do
-               self.id = title.to_cmangle
-       end
-
-       init with_content(title: String, content: Writable) do
-               init(title)
-               self.content = content
-       end
-
-       redef fun rendering do
-               if content == null then return
-               var open = ""
-               if is_open then open = "in"
-               addn "<div class='panel'>"
-               addn " <div class='panel-heading'>"
-               add "  <a data-toggle='collapse' data-parent='#sidebar' data-target='#box_{id}' href='#'>"
-               add title
-               addn "  </a>"
-               addn " </div>"
-               addn " <div id='box_{id}' class='panel-body collapse {open}'>"
-               add content.as(not null)
-               addn " </div>"
-               addn "</div>"
-       end
-end
-
-# Something that can go on a summary template
-class TplSummaryElt
-       super Template
-
-       # Add an element to the summary
-       fun add_child(child: TplSummaryElt) is abstract
-end
-
-# A summary that can go on the sidebar
-# If the page contains a sidebar, the summary is automatically placed
-# on top of the sidebarauto-generated
-# summary contains anchors to all sections displayed in the page
-class TplSummary
-       super TplSidebarElt
-       super TplSummaryElt
-
-       # Summary elements to display
-       var children = new Array[TplSummaryElt]
-
-       redef fun add_child(child) do children.add child
-
-       redef fun rendering do
-               if children.is_empty then return
-               addn "<div class='panel'>"
-               addn " <div class='panel-heading'>"
-               add "  <a data-toggle='collapse' data-parent='#sidebar' data-target='#box-sum' href='#'>"
-               add "Summary"
-               addn "  </a>"
-               addn " </div>"
-               addn " <div id='box-sum' class='summary collapse in'>"
-               addn " <ul class='nav'>"
-               for entry in children do add entry
-               addn " </ul>"
-               addn " </div>"
-               addn "</div>"
-       end
-end
-
-# A summary entry
-class TplSummaryEntry
-       super TplSummaryElt
-
-       # Text to display
-       var text: Writable
-
-       # Children of this entry
-       # Will be displayed as a tree
-       var children = new Array[TplSummaryElt]
-
-       redef fun add_child(child) do children.add child
-
-       redef fun rendering do
-               add "<li>"
-               add text
-               if not children.is_empty then
-                       addn "\n<ul class='nav'>"
-                       for entry in children do add entry
-                       addn "</ul>"
-               end
-               addn  "</li>"
-       end
-end
-
-# Something that can go in a section
-# Sections are automatically collected to populate the menu
-class TplSectionElt
-       super Template
-
-       # HTML anchor id
-       var id: String
-
-       # Title to display if any
-       # if both `title` and `summary_title` are null then
-       # the section will not appear in the summary
-       var title: nullable Writable = null is writable
-
-       # Subtitle to display if any
-       var subtitle: nullable Writable = null is writable
-
-       # Title that appear in the summary
-       # if null use `title` instead
-       var summary_title: nullable String = null is writable
-
-       # CSS classes to apply on the section element
-       var css_classes = new Array[String]
-
-       # CSS classes to apply on the title heading element
-       var title_classes = new Array[String]
-
-       # Parent article/section if any
-       var parent: nullable TplSectionElt = null
-
-       init with_title(id: String, title: Writable) do
-               init(id)
-               self.title = title
-       end
-
-       # Level <hX> for HTML heading
-       protected fun hlvl: Int do
-               if parent == null then return 1
-               return parent.hlvl + 1
-       end
-
-       # Elements contained by this section
-       var children = new Array[TplSectionElt]
-
-       # Add an element in this section
-       fun add_child(child: TplSectionElt) do
-               child.parent = self
-               children.add child
-       end
-
-       # Is the section empty (no content at all)
-       fun is_empty: Bool do return children.is_empty
-
-       # Render this section in the summary
-       fun render_summary(parent: TplSummaryElt) do
-               if is_empty then return
-               var title = summary_title
-               if title == null and self.title != null then title = self.title.write_to_string
-               if title == null then return
-               var lnk = new TplLink("#{id}", title)
-               var entry = new TplSummaryEntry(lnk)
-               for child in children do
-                       child.render_summary(entry)
-               end
-               parent.add_child entry
-       end
-end
-
-# A HTML <section> element
-class TplSection
-       super TplSectionElt
-
-       redef fun rendering do
-               addn "<section id='{id}' class='{css_classes.join(" ")}'>"
-               if title != null then
-                       var lvl = hlvl
-                       if lvl == 2 then title_classes.add "well well-sm"
-                       addn "<h{lvl} class='{title_classes.join(" ")}'>"
-                       addn title.as(not null)
-                       addn "</h{lvl}>"
-               end
-               if subtitle != null then
-                       addn "<div class='info subtitle'>"
-                       addn subtitle.as(not null)
-                       addn "</div>"
-               end
-               for child in children do
-                       add child
-               end
-               addn "</section>"
-       end
-end
-
-# A page article that can go in a section
-class TplArticle
-       super TplSectionElt
-
-       # Content for this article
-       var content: nullable Writable = null is writable
-       var source_link: nullable Writable = null is writable
-
-       init with_content(id: String, title: Writable, content: Writable) do
-               with_title(id, title)
-               self.content = content
-       end
-
-       redef fun render_summary(parent) do
-               if is_empty then return
-               var title = summary_title
-               if title == null and self.title != null then title = self.title.write_to_string
-               if title == null then return
-               var lnk = new TplLink("#{id}", title)
-               parent.add_child new TplSummaryEntry(lnk)
-       end
-
-       redef fun rendering do
-               if is_empty then return
-               addn "<article id='{id}' class='{css_classes.join(" ")}'>"
-               if source_link != null then
-                       add "<div class='source-link'>"
-                       add source_link.as(not null)
-                       addn "</div>"
-               end
-               if title != null then
-                       var lvl = hlvl
-                       if lvl == 2 then title_classes.add "well well-sm"
-                       add "<h{lvl} class='{title_classes.join(" ")}'>"
-                       add title.as(not null)
-                       addn "</h{lvl}>"
-               end
-               if subtitle != null then
-                       add "<div class='info subtitle'>"
-                       add subtitle.as(not null)
-                       addn "</div>"
-               end
-               if content != null then
-                       add content.as(not null)
-               end
-               for child in children do
-                       add child
-               end
-               addn """</article>"""
-       end
-
-       redef fun is_empty: Bool do
-               return title == null and subtitle == null and content == null and children.is_empty
-       end
-end
-
-# A module / class / prop definition
-class TplDefinition
-       super Template
-
-       # Comment to display
-       var comment: nullable Writable = null is writable
-
-       # Namespace for this definition
-       var namespace: nullable Writable = null is writable
-
-       # Location link to display
-       var location: nullable Writable = null is writable
-
-       private fun render_info do
-               addn "<div class='info text-right'>"
-               if namespace != null then
-                       if comment == null then
-                               add "<span class=\"noComment\">no comment for </span>"
-                       end
-                       add namespace.as(not null)
-               end
-               if location != null then
-                       add " "
-                       add location.as(not null)
-               end
-               addn "</div>"
-       end
-
-       private fun render_comment do
-               if comment != null then add comment.as(not null)
-       end
-
-       redef fun rendering do
-               addn "<div class='definition'>"
-               render_comment
-               render_info
-               addn "</div>"
-       end
-end
-
-# Class definition
-class TplClassDefinition
-       super TplDefinition
-
-       var intros = new Array[TplListElt]
-       var redefs = new Array[TplListElt]
-
-       redef fun rendering do
-               addn "<div class='definition'>"
-               render_comment
-               render_info
-               render_list("Introduces", intros)
-               render_list("Redefines", redefs)
-               addn "</div>"
+       redef init do
+               css_classes.clear
+               css_classes.add "label"
        end
 
-       private fun render_list(name: String, elts: Array[TplListElt]) do
-               if elts.is_empty then return
-               addn "<h5>{name.html_escape}</h5>"
-               addn "<ul class='list-unstyled list-definition'>"
-               for elt in elts do add elt
-               addn "</ul>"
-       end
-end
-
-# Layout for Search page
-class TplSearchPage
-       super TplSectionElt
-
-       var modules = new Array[Writable]
-       var classes = new Array[Writable]
-       var props = new Array[Writable]
-
-       redef fun rendering do
-               var title = self.title
-               if title != null then addn "<h1>{title.to_s.html_escape}</h1>"
-               addn "<div class='container-fluid'>"
-               addn " <div class='row'>"
-               if not modules.is_empty then
-                       addn "<div class='col-xs-4'>"
-                       addn "<h3>Modules</h3>"
-                       addn "<ul>"
-                       for m in modules do
-                               add "<li>"
-                               add m
-                               addn "</li>"
-                       end
-                       addn "</ul>"
-                       addn "</div>"
-               end
-               if not classes.is_empty then
-                       addn "<div class='col-xs-4'>"
-                       addn "<h3>Classes</h3>"
-                       addn "<ul>"
-                       for c in classes do
-                               add "<li>"
-                               add c
-                               addn "</li>"
-                       end
-                       addn "</ul>"
-                       addn "</div>"
-               end
-               if not props.is_empty then
-                       addn "<div class='col-xs-4'>"
-                       addn "<h3>Properties</h3>"
-                       addn "<ul>"
-                       for p in props do
-                               add "<li>"
-                               add p
-                               addn "</li>"
-                       end
-                       addn "</ul>"
-                       addn "</div>"
-               end
-               addn " </div>"
-               addn "</div>"
-       end
-end
-
-#####################
-# Basiv HTML elements
-#####################
-
-# A html link <a>
-class TplLink
-       super Template
-
-       # Link href
-       var href: String is writable
-
-       # The raw HTML content to display in the link
-       var text: Writable is writable
-
-       # The unescaped optional title.
-       var title: nullable String = null is writable
-
-       init with_title(href, text, title: String) do
-               init(href, text)
-               self.title = title
-       end
-
-       redef fun rendering do
-               add "<a href=\""
-               add href.html_escape
-               add "\""
-               if title != null then
-                       add " title=\""
-                       add title.as(not null).html_escape
-                       add "\""
-               end
-               add ">"
-               add text
-               add "</a>"
-       end
-end
-
-# A <ul> list
-class TplList
-       super TplListElt
-
-       # Elements contained in this list
-       # can be <li> or <ul> elements
-       var elts = new Array[TplListElt]
-
-       # CSS classes of the <ul> element
-       var css_classes = new Array[String]
-
-       # Add content wrapped in a <li> element
-       fun add_li(item: TplListItem) do elts.add item
-
-       init with_classes(classes: Array[String]) do self.css_classes = classes
-
-       fun is_empty: Bool do return elts.is_empty
-
-       redef fun rendering do
-               if elts.is_empty then return
-               addn "<ul class='{css_classes.join(" ")}'>"
-               for elt in elts do add elt
-               addn "</ul>"
-       end
-end
-
-# Something that can be added to a TplList
-class TplListElt
-       super Template
-end
-
-# A list item <li>
-class TplListItem
-       super TplListElt
-
-       # Content of the list item
-       var content = new Template
-
-       # CSS classes of the <li> element
-       var css_classes = new Array[String]
-
-       init with_content(content: Writable) do append(content)
-
-       init with_classes(content: Writable, classes: Array[String]) do
-               with_content(content)
-               css_classes = classes
-       end
-
-       # Append `content` to the item
-       # similar to `self.content.add`
-       fun append(content: Writable) do self.content.add content
-
-       redef fun rendering do
-               add "<li class='{css_classes.join(" ")}'>"
-               add content
-               addn "</li>"
-       end
-end
-
-# A Bootstrap tab component that contains `TplTabPanel`.
-class TplTab
-       super Template
-
-       # Panels contained in the tab.
-       var panels = new Array[TplTabPanel]
-
-       # Add a new panel.
-       fun add_panel(panel: TplTabPanel) do panels.add panel
-
-       # CSS classes of the tab component.
-       var css_classes = new Array[String]
-
-       redef fun rendering do
-               addn "<div class='tab-content'>"
-               for panel in panels do add panel
-               addn "</div>"
-       end
-end
-
-# A panel that goes in a `TplTab`.
-class TplTabPanel
-       super Template
-
-       # CSS classes of the pane element.
-       var css_classes = new Array[String]
-
-       # The panel id.
-       #
-       # Used to show/hide panel.
-       var id: String is noinit
-
-       # The panel name.
-       #
-       # Displayed in the tab header or in the pointing link.
-       var name: Writable
-
-       # Is the panel visible by default?
-       var is_active = false is writable
-
-       # Body of the panel
-       var content: nullable Writable = null is writable
-
-       # Get a link pointing to this panel.
-       fun tpl_link_to: Writable do
-               var lnk = new Template
-               lnk.add "<a data-target='#{id}' data-toggle='pill'>"
-               lnk.add name
-               lnk.add "</a>"
-               return lnk
-       end
-
-       redef fun rendering do
-               add "<div class='tab-pane {css_classes.join(" ")}"
-               if is_active then add "active"
-               addn "' id='{id}'>"
-               if content != null then add content.as(not null)
-               addn "</div>"
-       end
-end
-
-# A label with a text content
-class TplLabel
-       super Template
-
-       # Content of the label if any
-       var content: nullable Writable = null is writable
-
-       # CSS classes of the <span> element
-       var css_classes = new Array[String]
-
-       init with_content(content: Writable) do self.content = content
-       init with_classes(classes: Array[String]) do self.css_classes = classes
-
-       redef fun rendering do
-               add "<span class='label {css_classes.join(" ")}'>"
-               if content != null then add content.as(not null)
-               add "</span>"
-       end
-end
-
-# A label with an icon
-class TplIcon
-       super TplLabel
-
-       # Bootsrap icon name
-       # see: http://getbootstrap.com/components/#glyphicons
-       var icon: String
-
-       init with_icon(icon: String) do self.icon = icon
-
-       redef fun rendering do
-               add "<span class='glyphicon glyphicon-{icon} {css_classes.join(" ")}'>"
-               if content != null then add content.as(not null)
-               add "</span>"
+       # Init this label from css classes.
+       init with_classes(classes: Array[String]) do
+               init("label", "")
+               css_classes.add_all classes
        end
 end
 
@@ -669,17 +65,21 @@ class TagAttribute
        end
 end
 
-# Javacript template
+# Javacript template that can be added into a DocPage.
 class TplScript
        super Template
 
+       # HTML attributes to add in this tag.
        var attrs = new Array[TagAttribute]
+
+       # Text content of this script tag.
        var content: nullable Writable = null is writable
 
        init do
                attrs.add(new TagAttribute("type", "text/javascript"))
        end
 
+       # Render the content of this script.
        protected fun render_content do
                if content != null then add content.as(not null)
        end
@@ -697,7 +97,10 @@ end
 class TplPiwikScript
        super TplScript
 
+       # Piwik URL to use for this tracker.
        var tracker_url: String
+
+       # Site ID used on Piwik system.
        var site_id: String
 
        redef fun render_content do
index f3c2751..c112e25 100644 (file)
@@ -117,6 +117,31 @@ redef class MEntity
                if mdoc == null then return null
                return mdoc.tpl_short_comment
        end
+
+       # Icon that will be displayed before the title
+       fun html_icon: BSIcon do
+               var icon = new BSIcon("tag")
+               icon.css_classes.add_all(css_classes)
+               return icon
+       end
+
+       # CSS classes used to decorate `self`.
+       #
+       # Mainly used for icons.
+       var css_classes = new Array[String]
+
+       # A li element that can go in a `HTMLList`.
+       fun html_list_item: ListItem do
+               var tpl = new Template
+               tpl.add new DocHTMLLabel.with_classes(css_classes)
+               tpl.add html_link
+               var comment = html_short_comment
+               if comment != null then
+                       tpl.add ": "
+                       tpl.add comment
+               end
+               return new ListItem(tpl)
+       end
 end
 
 redef class MProject
@@ -124,6 +149,7 @@ redef class MProject
        redef fun nitdoc_url do return root.nitdoc_url
        redef var html_modifiers = ["project"]
        redef fun html_namespace do return html_link
+       redef var css_classes = ["public"]
 end
 
 redef class MGroup
@@ -150,6 +176,8 @@ redef class MGroup
                end
                return tpl
        end
+
+       redef var css_classes = ["public"]
 end
 
 redef class MModule
@@ -180,6 +208,8 @@ redef class MModule
                tpl.add html_link
                return tpl
        end
+
+       redef var css_classes = ["public"]
 end
 
 redef class MClass
@@ -221,6 +251,9 @@ redef class MClass
 
        # Returns `intro.html_signature`.
        fun html_signature: Template do return intro.html_signature
+
+       redef fun html_icon do return intro.html_icon
+       redef fun css_classes do return intro.css_classes
 end
 
 redef class MClassDef
@@ -249,14 +282,17 @@ redef class MClassDef
        #
        # For intro: `private abstract class Foo[E: Object]`
        # For redef: `redef class Foo[E]`
-       # TODO change the implementation to correspond to the comment.
        redef fun html_declaration do
                var tpl = new Template
                tpl.add "<span>"
                tpl.add html_modifiers.join(" ")
                tpl.add " "
                tpl.add html_link
-               tpl.add html_signature
+               if is_intro then
+                       tpl.add html_signature
+               else
+                       tpl.add html_short_signature
+               end
                tpl.add "</span>"
                return tpl
        end
@@ -301,6 +337,14 @@ redef class MClassDef
                end
                return tpl
        end
+
+       redef fun css_classes do
+               var set = new HashSet[String]
+               if is_intro then set.add "intro"
+               for m in mclass.intro.modifiers do set.add m.to_cmangle
+               for m in modifiers do set.add m.to_cmangle
+               return set.to_a
+       end
 end
 
 redef class MProperty
@@ -325,6 +369,8 @@ redef class MProperty
 
        # Returns `intro.html_signature`.
        fun html_signature: Template do return intro.html_signature
+
+       redef fun css_classes do return intro.css_classes
 end
 
 redef class MPropDef
@@ -352,14 +398,18 @@ redef class MPropDef
        #
        # For intro: `private fun foo(e: Object): Bar is abstract`
        # For redef: `redef fun foo(e) is cached`
-       # TODO change the implementation to correspond to the comment.
        redef fun html_declaration do
                var tpl = new Template
                tpl.add "<span>"
                tpl.add html_modifiers.join(" ")
                tpl.add " "
-               tpl.add html_link
-               tpl.add html_signature
+               if is_intro then
+                       tpl.add html_link
+                       tpl.add html_signature
+               else
+                       tpl.add mproperty.intro.html_link
+                       tpl.add html_short_signature
+               end
                tpl.add "</span>"
                return tpl
        end
@@ -378,6 +428,14 @@ redef class MPropDef
 
        # Returns the MPropDef signature with static types.
        fun html_signature: Template is abstract
+
+       redef fun css_classes do
+               var set = new HashSet[String]
+               if is_intro then set.add "intro"
+               for m in mproperty.intro.modifiers do set.add m.to_cmangle
+               for m in modifiers do set.add m.to_cmangle
+               return set.to_a
+       end
 end
 
 redef class MAttributeDef
@@ -571,6 +629,56 @@ redef class MParameter
        end
 end
 
+redef class ConcernsTree
+       # Render `self` as a hierarchical UnorderedList.
+       fun html_list: UnorderedList do
+               var lst = new UnorderedList
+               lst.css_classes.add "list-unstyled list-definition"
+               for r in roots do
+                       var li = r.html_concern_item
+                       lst.add_li li
+                       build_html_list(r, li)
+               end
+               return lst
+       end
+
+       # Build the html list recursively.
+       private fun build_html_list(e: MConcern, li: ListItem) do
+               if not sub.has_key(e) then return
+               var subs = sub[e]
+               var lst = new UnorderedList
+               lst.css_classes.add "list-unstyled list-definition"
+               for e2 in subs do
+                       if e2 isa MGroup and e2.is_root then
+                               build_html_list(e2, li)
+                       else
+                               var sli = e2.html_concern_item
+                               lst.add_li sli
+                               build_html_list(e2, sli)
+                       end
+               end
+               var text = new Template
+               text.add li.text
+               if not lst.is_empty then text.add lst
+               li.text = text
+       end
+end
+
+redef class MConcern
+       # Return a li element for `self` that can be displayed in a concern list
+       private fun html_concern_item: ListItem do
+               var lnk = html_link
+               var tpl = new Template
+               tpl.add new Link.with_title("#concern:{nitdoc_id}", lnk.text, lnk.title)
+               var comment = html_short_comment
+               if comment != null then
+                       tpl.add ": "
+                       tpl.add comment
+               end
+               return new ListItem(tpl)
+       end
+end
+
 ################################################################################
 # Additions to `model_ext`.
 
index 540ec24..83f6a36 100644 (file)
@@ -17,6 +17,11 @@ module html_templates
 
 import html_model
 import html::bootstrap
+import doc_phases::doc_structure
+import doc_phases::doc_hierarchies
+import doc_phases::doc_graphs
+import doc_phases::doc_intros_redefs
+import doc_phases::doc_lin
 
 # Renders the page as HTML.
 redef class DocPage
@@ -35,11 +40,7 @@ redef class DocPage
        var topmenu: DocTopMenu is writable, noinit
 
        # Sidebar template if any.
-       var sidebar: nullable TplSidebar = null is writable
-
-       # Content of the page in form a TplSection.
-       # TODO remove when other templates are migrated.
-       var sections = new Array[TplSection]
+       var sidebar: nullable DocSideBar = null is writable
 
        # Footer content if any.
        var footer: nullable Writable = null is writable
@@ -47,12 +48,6 @@ redef class DocPage
        # JS scripts to append at the end of the body
        var scripts = new Array[TplScript]
 
-       # Adds a section to this page.
-       # TODO remove when other templates are migrated.
-       fun add_section(section: TplSection) do
-               sections.add section
-       end
-
        # Renders the html `<head>`.
        private fun render_head do
                var css = (self.shareurl / "css").html_escape
@@ -75,23 +70,9 @@ redef class DocPage
                addn ">"
        end
 
-       # Renders the sidebar template.
-       #
-       # Sidebar is automatically populated with a summary of all sections
-       # TODO remove summary generation when other templates are migrated.
-       private fun render_sidebar do
-               if sidebar == null then return
-               var summary = new TplSummary.with_order(0)
-               for section in sections do
-                       section.render_summary summary
-               end
-               sidebar.boxes.add summary
-               add sidebar.as(not null)
-       end
-
        # Renders the footer and content.
        private fun render_content do
-               for section in sections do add section
+               add root
                if footer != null then
                        addn "<div class='well footer'>"
                        add footer.as(not null)
@@ -127,9 +108,10 @@ redef class DocPage
                add topmenu
                addn " </div>"
                addn " <div class='row' id='content'>"
+               var sidebar = self.sidebar
                if sidebar != null then
                        addn "<div class='col col-xs-3 col-lg-2'>"
-                       render_sidebar
+                       add sidebar
                        addn "</div>"
                        addn "<div class='col col-xs-9 col-lg-10' data-spy='scroll' data-target='.summary'>"
                        render_content
@@ -143,6 +125,16 @@ redef class DocPage
                addn "</div>"
                render_footer
        end
+
+       # Render table of content for this page.
+       fun html_toc: UnorderedList do
+               var lst = new UnorderedList
+               lst.css_classes.add "nav"
+               for child in root.children do
+                       child.render_toc_item(lst)
+               end
+               return lst
+       end
 end
 
 # Top menu bar template.
@@ -197,11 +189,65 @@ class DocTopMenu
        end
 end
 
+# Nitdoc sidebar template.
+class DocSideBar
+       super Template
+
+       # Sidebar contains `DocSideBox`.
+       var boxes = new Array[DocSideBox]
+
+       redef fun rendering do
+               if boxes.is_empty then return
+               addn "<div id='sidebar'>"
+               for box in boxes do add box
+               addn "</div>"
+       end
+end
+
+# Something that can be put in a DocSideBar.
+class DocSideBox
+       super Template
+
+       # Box HTML id, used for Bootstrap collapsing feature.
+       #
+       # Use `html_title.to_cmangle` by default.
+       var id: String is lazy do return title.write_to_string.to_cmangle
+
+       # Title of the box to display.
+       var title: Writable
+
+       # Content to display in the box.
+       var content: Writable
+
+       # Is the box opened by default?
+       #
+       # Otherwise, the user will have to clic on the title to display the content.
+       #
+       # Default is `true`.
+       var is_open = true is writable
+
+       redef fun rendering do
+               var open = ""
+               if is_open then open = "in"
+               addn "<div class='panel'>"
+               addn " <div class='panel-heading'>"
+               add "  <a data-toggle='collapse' data-parent='#sidebar'"
+               add "   data-target='#box_{id}' href='#'>"
+               add title
+               addn "  </a>"
+               addn " </div>"
+               addn " <div id='box_{id}' class='summary panel-body collapse {open}'>"
+               add content
+               addn " </div>"
+               addn "</div>"
+       end
+end
+
 redef class DocComposite
        super Template
 
        # HTML anchor id
-       var html_id: String is noinit
+       var html_id: String is noinit, writable
 
        # Title to display if any.
        #
@@ -214,7 +260,10 @@ redef class DocComposite
        # Render the element title and subtitle.
        private fun render_title do
                if html_title != null then
-                       addn new Header(hlvl, html_title.write_to_string)
+               var header = new Header(hlvl, html_title.write_to_string)
+               header.css_classes.add "signature"
+               if hlvl == 2 then header.css_classes.add "well well-sm"
+               addn header
                end
                if html_subtitle != null then
                        addn "<div class='info subtitle'>"
@@ -224,7 +273,9 @@ redef class DocComposite
        end
 
        # Render the element body.
-       private fun render_body do end
+       private fun render_body do
+               for child in children do addn child.write_to_string
+       end
 
        redef fun rendering do
                if is_hidden then return
@@ -234,7 +285,7 @@ redef class DocComposite
 
        # Level <hX> for HTML heading.
        private fun hlvl: Int do
-               if parent == null then return 1
+               if parent == null then return 0
                return parent.hlvl + 1
        end
 
@@ -242,6 +293,32 @@ redef class DocComposite
        #
        # By default, empty elements are hidden.
        fun is_hidden: Bool do return is_empty
+
+       # A short, undecorated title that goes in the table of contents.
+       #
+       # By default, returns `html_title.to_s`, subclasses should redefine it.
+       var toc_title: String is lazy, writable do return html_title.to_s
+
+       # Is `self` hidden in the table of content?
+       var is_toc_hidden = false is writable
+
+       # Render this element in a table of contents.
+       private fun render_toc_item(lst: UnorderedList) do
+               if is_toc_hidden then return
+
+               var content = new Template
+               content.add new Link("#{html_id}", toc_title)
+
+               if not children.is_empty then
+                       var sublst = new UnorderedList
+                       sublst.css_classes.add "nav"
+                       for child in children do
+                               child.render_toc_item(sublst)
+                       end
+                       content.add sublst
+               end
+               lst.add_li new ListItem(content)
+       end
 end
 
 redef class DocSection
@@ -252,16 +329,264 @@ redef class DocSection
                        addn "<a id=\"{html_id}\"></a>"
                        return
                end
+               addn "<section{render_css_classes} id=\"{html_id}\">"
+               render_title
                render_body
+               addn "</section>"
        end
 end
 
 redef class DocArticle
        super BSComponent
 
-       # Never displays the title for article.
+       redef fun rendering do
+               if is_hidden then return
+               addn "<article{render_css_classes} id=\"{html_id}\">"
+               render_title
+               render_body
+               addn "</article>"
+       end
+end
+
+redef class HomeArticle
+       redef var html_id = "article:home"
+       redef var html_title = "Overview"
+
+       # HTML content to display on the home page.
+       #
+       # This attribute is set by the `doc_render` phase who knows the context.
+       var content: nullable String is noinit, writable
+
+       redef fun render_body do
+               var content = self.content
+               if content != null then add content
+               super
+       end
+end
+
+redef class IndexArticle
+       redef var html_id = "article:index"
+       redef var html_title = "Index"
+       redef fun is_empty do
+               return mmodules.is_empty and mclasses.is_empty and mprops.is_empty
+       end
+
+       redef fun render_body do
+               addn "<div class='container-fluid'>"
+               addn " <div class='row'>"
+               render_list("Modules", mmodules)
+               render_list("Classes", mclasses)
+               render_list("Properties", mprops)
+               addn "</div>"
+               addn "</div>"
+       end
+
+       # Displays a list from the content of `mentities`.
+       private fun render_list(title: String, mentities: Array[MEntity]) do
+               if mentities.is_empty then return
+               addn "<div class='col-xs-4'>"
+               addn new Header(3, title)
+               var lst = new UnorderedList
+               for mentity in mentities do
+                       if mentity isa MProperty then
+                               var tpl = new Template
+                               tpl.add mentity.intro.html_link
+                               tpl.add " ("
+                               tpl.add mentity.intro.mclassdef.mclass.html_link
+                               tpl.add ")"
+                               lst.add_li new ListItem(tpl)
+                       else
+                               lst.add_li new ListItem(mentity.html_link)
+                       end
+               end
+               addn lst
+               addn "</div>"
+       end
+end
+
+redef class ProjectsSection
+       redef var html_id = "section:projects"
+       redef var html_title = "Projects"
+end
+
+redef class MEntityComposite
+       redef var html_id is lazy do return mentity.nitdoc_id
+       redef var html_title is lazy do return mentity.nitdoc_name
+end
+
+redef class MEntitySection
+       redef var html_id is lazy do return "section:{mentity.nitdoc_name}"
+       redef var html_title is lazy do return mentity.html_name
+       redef var html_subtitle is lazy do return mentity.html_declaration
+end
+
+redef class ConcernSection
+       redef var html_id is lazy do return "concern:{mentity.nitdoc_id}"
+       redef var html_title is lazy do return "in {mentity.nitdoc_name}"
+       redef fun is_toc_hidden do return is_empty
+end
+
+redef class ImportationListSection
+       redef var html_id is lazy do return "section:{mentity.nitdoc_id}.importation"
+       redef var html_title is lazy do return "Dependencies"
+end
+
+redef class InheritanceListSection
+       redef var html_id is lazy do return "section:{mentity.nitdoc_id}.inheritance"
+       redef var html_title is lazy do return "Inheritance"
+end
+
+redef class IntroArticle
+       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.intro"
+       redef var html_title = null
+       redef var is_hidden = false
+       redef var is_toc_hidden = true
+
+       # Link to source to display if any.
+       var html_source_link: nullable Writable is noinit, writable
+
+       redef fun render_title do
+               var lnk = html_source_link
+               if lnk != null then
+                       add "<div class='source-link'>"
+                       add lnk
+                       addn "</div>"
+               end
+               super
+       end
+
+       redef fun render_body do
+               var comment = mentity.html_comment
+               if comment != null then addn comment
+               super
+       end
+end
+
+redef class ConcernsArticle
+       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.concerns"
+       redef var html_title = "Concerns"
+       redef fun is_hidden do return concerns.is_empty
+       redef fun render_body do add concerns.html_list
+end
+
+redef class DefinitionArticle
+       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.definition"
+       redef var html_title is lazy do return mentity.html_name
+       redef var html_subtitle is lazy do return mentity.html_declaration
+       redef var is_hidden = false
+
+       # Does `self` display only it's title and no body?
+       #
+       # FIXME diff hack
+       var is_no_body: Bool = false is writable
+
+       # Does `self` display only the short content as definition?
        #
-       # This is to maintain compatibility with old components, this may change
-       # without notice in further version.
-       redef fun render_title do end
+       # FIXME diff hack
+       var is_short_comment: Bool = false is writable
+
+       # Link to source to display if any.
+       var html_source_link: nullable Writable is noinit, writable
+
+       redef fun render_title do
+               var lnk = html_source_link
+               if lnk != null then
+                       add "<div class='source-link'>"
+                       add lnk
+                       addn "</div>"
+               end
+               super
+       end
+
+       redef fun render_body do
+               if not is_no_body then
+                       var comment
+                       if is_short_comment then
+                               comment = mentity.html_short_comment
+                       else
+                               comment = mentity.html_comment
+                       end
+                       if comment != null then addn comment
+               end
+               super
+       end
+end
+
+redef class HierarchyListArticle
+       redef var html_id is lazy do return "article:{list_title}_{mentity.nitdoc_id}.hierarchy"
+       redef var html_title is lazy do return list_title
+       redef fun is_empty do return mentities.is_empty
+       redef fun is_toc_hidden do return mentities.is_empty
+
+       redef fun render_body do
+               var lst = new UnorderedList
+               lst.css_classes.add "list-unstyled list-definition"
+               for mentity in mentities do
+                       lst.add_li mentity.html_list_item
+               end
+               addn lst
+       end
+end
+
+redef class IntrosRedefsListArticle
+       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.intros_redefs"
+       redef var html_title is lazy do return list_title
+       redef fun is_hidden do return mentities.is_empty
+       redef var is_toc_hidden = true
+
+       redef fun render_body do
+               var lst = new UnorderedList
+               lst.css_classes.add "list-unstyled list-labeled"
+               for mentity in mentities do
+                       lst.add_li mentity.html_list_item
+               end
+               add lst
+       end
+end
+
+redef class DefinitionLinArticle
+       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.lin"
+       redef var html_title is lazy do return "Linearization"
+       redef fun is_hidden do return mentities.is_empty
+       redef var is_toc_hidden = true
+
+       redef fun render_body do
+               var lst = new UnorderedList
+               lst.css_classes.add "list-unstyled list-labeled"
+               for mentity in mentities do
+                       if not mentity isa MPropDef then continue # TODO handle all mentities
+                       var tpl = new Template
+                       tpl.add mentity.mclassdef.html_namespace
+                       var comment = mentity.mclassdef.html_short_comment
+                       if comment != null then
+                               tpl.add ": "
+                               tpl.add comment
+                       end
+                       var li = new ListItem(tpl)
+                       li.css_classes.add "signature"
+                       lst.add_li li
+               end
+               add lst
+       end
+end
+
+redef class GraphArticle
+       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.graph"
+       redef var html_title = null
+       redef var toc_title do return "Graph"
+       redef var is_hidden = false
+       redef var is_toc_hidden = true
+
+       # HTML map used to display link.
+       #
+       # This attribute is set by the `doc_render` phase who knows the context.
+       var map: String is noinit, writable
+
+       redef fun render_body do
+               addn "<div class=\"text-center\">"
+               addn " <img src='{id}.png' usemap='#{id}' style='margin:auto'"
+               addn "  alt='{graph_title}'/>"
+               add map
+               addn "</div>"
+       end
 end
index 28e36bd..68ea75b 100644 (file)
@@ -26,6 +26,7 @@ import deriving
 import check_annotation
 import glsl_validation
 import parallelization_phase
+import i18n_phase
 
 redef class ToolContext
        # FIXME: there is conflict in linex in nitc, so use this trick to force invocation
diff --git a/src/frontend/i18n_phase.nit b/src/frontend/i18n_phase.nit
new file mode 100644 (file)
index 0000000..f068dc1
--- /dev/null
@@ -0,0 +1,185 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Basic support of internationalization through the generation of id-to-string tables
+module i18n_phase
+
+intrude import literal
+private import annotation
+private import parser_util
+import astbuilder
+
+redef class ToolContext
+       # Main phase of `language`
+       var localize_phase: Phase = new I18NPhase(self, [literal_phase])
+end
+
+private class I18NPhase
+       super Phase
+
+       redef fun process_annotated_node(nmodule, nat) do
+               if not nat.name == "i18n" then return
+
+               if not nmodule isa AModuledecl then
+                       toolcontext.error(nmodule.location, "Error: The localized language can only be used on module declarations.")
+                       return
+               end
+
+               var domain = nmodule.n_name.n_id.text
+
+               var lang: nullable String = null
+               if nat.n_args.length > 0 then
+                       lang = nat.arg_as_string(toolcontext.modelbuilder)
+                       if lang == null then return
+               end
+
+               var module_dir = nmodule.location.file.filename.dirname.realpath
+               var locale_dir = module_dir / "languages"
+
+               if not locale_dir.file_exists then locale_dir.mkdir
+
+               var amodule = nmodule.parent.as(AModule)
+
+               var vi = new StringFinder(domain, locale_dir, toolcontext, amodule)
+               vi.enter_visit(amodule)
+
+               var module_name = nmodule.location.file.filename.basename(".nit")
+
+               var pot_path = locale_dir / module_name
+               var arr = vi.strings.values.to_a
+               var po = new POFile.with_strings(arr)
+               po.write_template(pot_path)
+
+               if lang != null then
+                       for i in po.strings do
+                               i.msgstr = i.msgid
+                       end
+
+                       var lang_dir = locale_dir / lang
+                       if not lang_dir.file_exists then lang_dir.mkdir
+
+                       var messages_dir = lang_dir / "LC_MESSAGES"
+                       if not messages_dir.file_exists then messages_dir.mkdir
+
+                       po.write_to_file(messages_dir / module_name)
+               end
+
+               var lit = new LiteralVisitor(toolcontext)
+               lit.enter_visit(amodule)
+       end
+end
+
+private class StringFinder
+       super Visitor
+
+       # Strings in the file, used to generate .pot and .po files
+       var strings = new HashMap[String, PObject]
+
+       # Domain of the strings to internationalize
+       var domain: String
+
+       # Location of the languages file
+       var languages_location: String
+
+       # Context for the visitor, used only for the parse_expr
+       var toolcontext: ToolContext
+
+       # The module we are working on
+       var amodule: AModule
+
+       redef fun visit(n)
+       do
+               n.accept_string_finder(self)
+               n.visit_all(self)
+       end
+
+       redef fun enter_visit(e) do
+               if e isa AAnnotation then return
+               super
+       end
+end
+
+redef class ANode
+       private fun accept_string_finder(v: StringFinder) do end
+end
+
+redef class AStringExpr
+
+       redef fun accept_string_finder(v) do
+               var str = value.as(not null).escape_to_c
+               var parse = v.toolcontext.parse_expr("\"{str}\".get_translation(\"{v.domain}\", \"{v.languages_location}\").unescape_nit")
+               var loc = location
+               var locstr = "{v.amodule.mmodule.mgroup.name}::{v.amodule.mmodule.name} {loc.line_start}--{loc.column_start}:{loc.column_end}"
+               if not v.strings.has_key(str) then
+                       var po = new PObject([locstr], str, "")
+                       v.strings[str] = po
+               else
+                       v.strings[str].locations.push locstr
+               end
+               replace_with(parse)
+       end
+end
+
+# .po file entry
+#
+# Locations are optional, they just serve for translation purposes
+# to help the translator with the context of the message if necessary
+#
+# msgid and msgstr are the map of translate to translated strings in the po file.
+class PObject
+       # Array since the same string can be encountered at several places
+       var locations: Array[String]
+       # Identifier of the string to translate (i.e. the string itself)
+       var msgid: String is writable
+       # Translation of the string
+       var msgstr: String is writable
+end
+
+# A GNU gettext .po/.pot file
+class POFile
+       super Writable
+
+       # Map of the strings's `msgid` and `msgstr`
+       #
+       # Read from a PO file
+       var strings: Array[PObject]
+
+       # Creates a PO file with strings built-in
+       init with_strings(sm: Array[PObject])do
+               strings = new Array[PObject].with_capacity(sm.length)
+               strings.add_all sm
+       end
+
+       redef fun write_to_file(path) do
+               if not path.has_suffix(".po") then path += ".po"
+               super path
+       end
+
+       redef fun write_to(ofs) do
+               for i in strings do
+                       ofs.write("#: {i.locations.join(", ")}\n")
+                       ofs.write("msgid \"{i.msgid}\"\n")
+                       ofs.write("msgstr \"{i.msgstr}\"\n\n")
+               end
+               ofs.write("# Generated file, do not modify\n")
+       end
+
+       # Writes the information of the POFile to a .pot template file
+       fun write_template(path: String) do
+               if not path.has_suffix(".pot") then path += ".pot"
+               var f = new FileWriter.open(path)
+               write_to(f)
+               f.close
+       end
+end
index 333fa96..e0c343a 100644 (file)
@@ -41,6 +41,7 @@ private class Nitdoc
                        new StructurePhase(toolcontext, doc),
                        new InheritanceListsPhase(toolcontext, doc),
                        new IntroRedefListPhase(toolcontext, doc),
+                       new LinListPhase(toolcontext, doc),
                        new GraphPhase(toolcontext, doc),
                        new RenderHTMLPhase(toolcontext, doc): DocPhase]
 
index 9667f81..17cc649 100644 (file)
@@ -32,6 +32,12 @@ redef class ToolContext
        var opt_meld = new OptionBool("Show diff between source and output using meld",
           "--meld")
 
+       # --line-width
+       var opt_line_width = new OptionInt("Maximum length of lines (use 0 to disable automatic line breaks)", 80, "--line-width")
+
+       # --no-inline
+       var opt_no_inline = new OptionBool("Disable automatic one-liners", "--no-inline")
+
        # Break too long string literals.
        var opt_break_str = new OptionBool("Break too long string literals", "--break-strings")
 
@@ -69,7 +75,8 @@ var toolcontext = new ToolContext
 var opts = toolcontext.option_context
 opts.add_option(toolcontext.opt_dir, toolcontext.opt_output)
 opts.add_option(toolcontext.opt_diff, toolcontext.opt_meld, toolcontext.opt_check)
-opts.add_option(toolcontext.opt_break_str, toolcontext.opt_inline_do)
+opts.add_option(toolcontext.opt_line_width, toolcontext.opt_break_str, toolcontext.opt_inline_do)
+opts.add_option(toolcontext.opt_no_inline)
 opts.add_option(toolcontext.opt_skip_empty)
 
 toolcontext.tooldescription = "Usage: nitpretty [OPTION]... <file.nit>\n" +
@@ -80,7 +87,7 @@ var arguments = toolcontext.option_context.rest
 # build model
 var model = new Model
 var mbuilder = new ModelBuilder(model, toolcontext)
-var mmodules = mbuilder.parse(arguments)
+var mmodules = mbuilder.parse_full(arguments)
 mbuilder.run_phases
 
 if mmodules.is_empty then
@@ -97,6 +104,7 @@ var dir = toolcontext.opt_dir.value or else ".nitpretty"
 if not dir.file_exists then dir.mkdir
 var v = new PrettyPrinterVisitor
 
+v.max_size = toolcontext.opt_line_width.value
 if toolcontext.opt_break_str.value then
        v.break_strings = true
 end
@@ -106,6 +114,9 @@ end
 if toolcontext.opt_skip_empty.value then
        v.skip_empty = true
 end
+if toolcontext.opt_no_inline.value then
+       v.no_inline = true
+end
 
 for mmodule in mmodules do
        var nmodule = mbuilder.mmodule2node(mmodule)
index 2958c4b..a543672 100644 (file)
@@ -197,8 +197,7 @@ $(call import-module,android/native_app_glue)
                android:label="@string/app_name"
                android:hasCode="true"
                android:debuggable="{{{not release}}}"
-               {{{icon_declaration}}}
-               android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation">
+               {{{icon_declaration}}}>
 """
 
                for activity in project.activities do
index 8da7bed..e4d848d 100644 (file)
@@ -99,7 +99,20 @@ class PrettyPrinterVisitor
                n.accept_pretty_printer self
        end
 
-       # Visit a list of `Anode`.
+       # Visit a list of arguments `ANode` with optional parentheses
+       fun visit_args(n: nullable ANodes[ANode]) do
+               if n == null or n.is_empty then return
+               if current_token isa TOpar then
+                       consume "("
+               else
+                       adds
+               end
+
+               visit_list n
+               if current_token isa TCpar then consume ")"
+       end
+
+       # Visit a list of `ANode`.
        fun visit_list(n: nullable ANodes[ANode]) do
                if n == null then return
                n.accept_pretty_printer self
@@ -108,10 +121,11 @@ class PrettyPrinterVisitor
        # Is the node inlinable and can fit on the line.
        fun can_inline(n: nullable ANode): Bool do
                if n == null then return true
+               if no_inline and n.location.line_start != n.location.line_end then return false
                if n.must_be_inline then return true
                if n.must_be_block then return false
                # check length
-               if n.collect_length + current_length > max_size then return false
+               if max_size > 0 and n.collect_length + current_length > max_size then return false
                # check block is inlinable
                return n.is_inlinable
        end
@@ -145,12 +159,22 @@ class PrettyPrinterVisitor
        # Skip `current_token` until `target` is reached.
        fun skip_to(target: nullable Token) do
                if target == null then return
-               while current_token != target do skip
+               while current_token != null and current_token != target do skip
+               if current_token == null then
+                       target.debug("Looked for, but not found :(")
+                       abort
+               end
+       end
+
+       # Consume comments and end of lines if any
+       fun consume_comments do
+               while current_token isa TEol or current_token isa TComment do visit current_token
        end
 
        # Visit `current_token`.
        fun consume(token: String) do
-               assert current_token.text == token
+               consume_comments
+               if current_token.text == token then else current_token.debug("Got `{current_token.text}`; expected `{token}`.")
                visit current_token
        end
 
@@ -196,7 +220,8 @@ class PrettyPrinterVisitor
        var tab_size = 8
 
        # Max line size.
-       var max_size = 80
+       # 0 (or negative) to disable.
+       var max_size = 80 is writable
 
        # Length of the current line.
        var current_length = 0
@@ -260,6 +285,9 @@ class PrettyPrinterVisitor
 
        # Do we force the deletion of empty lines?
        var skip_empty = false is public writable
+
+       # Disable automatic inlining.
+       var no_inline = false is writable
 end
 
 # Base framework redefs
@@ -269,6 +297,8 @@ redef class ANodes[E]
                for e in self do
                        var e_can_inline = v.can_inline(e)
 
+                       if v.current_token isa TComma then v.skip
+
                        if e != first then
                                if not e_can_inline then
                                        v.add ","
@@ -509,15 +539,7 @@ redef class AAnnotation
                        v.adds
                end
                v.visit n_atid
-               if not n_args.is_empty then
-                       if n_opar == null then
-                               v.adds
-                       else
-                               v.visit n_opar
-                       end
-                       v.visit_list n_args
-                       v.visit n_cpar
-               end
+               v.visit_args n_args
        end
 end
 
@@ -1041,7 +1063,7 @@ redef class AExternCalls
                        v.adds
                        v.visit_list n_extern_calls
                else
-                       v.addn
+                       v.forcen
                        v.indent += 1
                        v.addt
                        v.indent -= 1
@@ -1141,7 +1163,7 @@ redef class TExternCodeSegment
                                v.add "`\{"
 
                                if not lines.first.trim.is_empty then
-                                       v.addn
+                                       v.forcen
                                        lines.first.l_trim
                                        v.indent += 1
                                        v.addt
@@ -1218,7 +1240,7 @@ redef class AIfExpr
                        v.adds
                else
                        v.visit n_expr
-                       v.addn
+                       v.forcen
                        v.addt
                end
 
@@ -1269,10 +1291,11 @@ redef class AIfExpr
                                end
                        end
 
-                       if has_else(v) then
-                               while not v.current_token isa TKwelse do
-                                       v.consume v.current_token.text
-                               end
+                       v.consume_comments
+
+                       # FIXME: for some unknown reasons, has_else can be true even if
+                       # there is no `else` keyword but a `end` instead.
+                       if has_else(v) and v.current_token isa TKwelse then
 
                                v.indent -= 1
                                v.addt
@@ -1559,7 +1582,7 @@ redef class ACallExpr
                v.visit_recv n_expr
 
                if not n_expr isa AImplicitSelfExpr and not can_inline then
-                       v.addn
+                       v.forcen
                        v.addt
                end
 
@@ -1572,14 +1595,7 @@ redef class ACallExpr
                                v.visit n_args.n_exprs.first
                                if v.current_token isa TCpar then v.skip
                        else
-                               if v.current_token isa TOpar then
-                                       v.consume "("
-                               else
-                                       v.adds
-                               end
-
-                               v.visit_list n_args.n_exprs
-                               if v.current_token isa TCpar then v.consume ")"
+                               v.visit_args n_args.n_exprs
                        end
                end
        end
@@ -1700,12 +1716,7 @@ redef class AInitExpr
                end
 
                v.visit n_kwinit
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
+               v.visit_args n_args.n_exprs
        end
 end
 
@@ -1720,7 +1731,7 @@ redef class ANewExpr
                        v.consume "."
 
                        if not can_inline then
-                               v.addn
+                               v.forcen
                                v.indent += 1
                                v.addt
                                v.indent -= 1
@@ -1729,11 +1740,7 @@ redef class ANewExpr
                        v.visit n_id
                end
 
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
+               v.visit_args n_args.n_exprs
        end
 
        redef fun is_inlinable do return true
@@ -1842,7 +1849,7 @@ redef class AAssertExpr
                                else
                                        v.addt
                                        v.visit n_else
-                                       v.addn
+                                       v.forcen
                                        v.indent -= 1
                                        v.addt
                                        v.add "end"
@@ -1880,14 +1887,7 @@ redef class ASuperExpr
                                v.visit n_args.n_exprs.first
                                if v.current_token isa TCpar then v.skip
                        else
-                               if v.current_token isa TOpar then
-                                       v.consume "("
-                               else
-                                       v.adds
-                               end
-
-                               v.visit_list n_args.n_exprs
-                               if v.current_token isa TCpar then v.consume ")"
+                               v.visit_args n_args.n_exprs
                        end
                end
        end
@@ -1977,7 +1977,7 @@ private class ABinOpHelper
                        v.adds
                        v.visit bin_expr2
                else
-                       v.addn
+                       v.forcen
                        v.indent += 1
                        v.addt
                        v.indent -= 1
@@ -2068,6 +2068,11 @@ redef class AArrayExpr
        redef fun accept_pretty_printer(v) do
                v.consume "["
                v.visit_list n_exprs
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
                v.consume "]"
        end
 end
@@ -2111,7 +2116,7 @@ redef class AStringFormExpr
                        while i < text.length do
                                v.add text[i].to_s
 
-                               if v.current_length >= v.max_size and i <= text.length - 3 then
+                               if v.max_size > 0 and v.current_length >= v.max_size and i <= text.length - 3 then
                                        v.add "\" +"
                                        if was_inline then
                                                v.forcen
index 4fa28c0..ebe2c73 100644 (file)
@@ -26,7 +26,7 @@ redef class ToolContext
        var transform_phase: Phase = new TransformPhase(self, [typing_phase, auto_super_init_phase])
 
        # --no-shortcut-range
-       var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
+       var opt_no_shortcut_range: OptionBool = new OptionBool("Always instantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
 
        redef init
        do
index b4d2b33..49599a2 100644 (file)
@@ -3,3 +3,4 @@ leapfrog_curses
 websocket_server
 converter
 show_basedir
+langannot
diff --git a/tests/sav/balanced_brackets.res b/tests/sav/balanced_brackets.res
new file mode 100644 (file)
index 0000000..ea08659
--- /dev/null
@@ -0,0 +1,10 @@
+][[]][ is not well-balanced
+[[]][] is well-balanced
+[]][][ is not well-balanced
+[[[]]] is well-balanced
+[][]][ is not well-balanced
+]][[][ is not well-balanced
+[]]][[ is not well-balanced
+[]][][ is not well-balanced
+[][][] is well-balanced
+][]][[ is not well-balanced
diff --git a/tests/sav/test_bufferedfilereader.res b/tests/sav/test_bufferedfilereader.res
new file mode 100644 (file)
index 0000000..3c68ca8
--- /dev/null
@@ -0,0 +1,244 @@
+Read 4 chars: # Th
+Read 4 chars: is f
+Read 4 chars: ile 
+Read 4 chars: is p
+Read 4 chars: art 
+Read 4 chars: of N
+Read 4 chars: IT (
+Read 4 chars:  htt
+Read 4 chars: p://
+Read 4 chars: www.
+Read 4 chars: nitl
+Read 4 chars: angu
+Read 4 chars: age.
+Read 4 chars: org 
+Read 4 chars: ).
+#
+Read 4 chars: 
+# C
+Read 4 chars: opyr
+Read 4 chars: ight
+Read 4 chars:  201
+Read 4 chars: 3 Al
+Read 4 chars: exis
+Read 4 chars:  Laf
+Read 4 chars: erri
+Read 4 chars: ère
+Read 4 chars:  <al
+Read 4 chars: exis
+Read 4 chars: .laf
+Read 4 chars: @xym
+Read 4 chars: us.n
+Read 4 chars: et>
+
+Read 4 chars: #
+# 
+Read 4 chars: Lice
+Read 4 chars: nsed
+Read 4 chars:  und
+Read 4 chars: er t
+Read 4 chars: he A
+Read 4 chars: pach
+Read 4 chars: e Li
+Read 4 chars: cens
+Read 4 chars: e, V
+Read 4 chars: ersi
+Read 4 chars: on 2
+Read 4 chars: .0 (
+Read 4 chars: the 
+Read 4 chars: "Lic
+Read 4 chars: ense
+Read 4 chars: ");
+
+Read 4 chars: # yo
+Read 4 chars: u ma
+Read 4 chars: y no
+Read 4 chars: t us
+Read 4 chars: e th
+Read 4 chars: is f
+Read 4 chars: ile 
+Read 4 chars: exce
+Read 4 chars: pt i
+Read 4 chars: n co
+Read 4 chars: mpli
+Read 4 chars: ance
+Read 4 chars:  wit
+Read 4 chars: h th
+Read 4 chars: e Li
+Read 4 chars: cens
+Read 4 chars: e.
+#
+Read 4 chars:  You
+Read 4 chars:  may
+Read 4 chars:  obt
+Read 4 chars: ain 
+Read 4 chars: a co
+Read 4 chars: py o
+Read 4 chars: f th
+Read 4 chars: e Li
+Read 4 chars: cens
+Read 4 chars: e at
+Read 4 chars: 
+#
+#
+Read 4 chars:     
+Read 4 chars:  htt
+Read 4 chars: p://
+Read 4 chars: www.
+Read 4 chars: apac
+Read 4 chars: he.o
+Read 4 chars: rg/l
+Read 4 chars: icen
+Read 4 chars: ses/
+Read 4 chars: LICE
+Read 4 chars: NSE-
+Read 4 chars: 2.0
+
+Read 4 chars: #
+# 
+Read 4 chars: Unle
+Read 4 chars: ss r
+Read 4 chars: equi
+Read 4 chars: red 
+Read 4 chars: by a
+Read 4 chars: ppli
+Read 4 chars: cabl
+Read 4 chars: e la
+Read 4 chars: w or
+Read 4 chars:  agr
+Read 4 chars: eed 
+Read 4 chars: to i
+Read 4 chars: n wr
+Read 4 chars: itin
+Read 4 chars: g, s
+Read 4 chars: oftw
+Read 4 chars: are
+
+Read 4 chars: # di
+Read 4 chars: stri
+Read 4 chars: bute
+Read 4 chars: d un
+Read 4 chars: der 
+Read 4 chars: the 
+Read 4 chars: Lice
+Read 4 chars: nse 
+Read 4 chars: is d
+Read 4 chars: istr
+Read 4 chars: ibut
+Read 4 chars: ed o
+Read 4 chars: n an
+Read 4 chars:  "AS
+Read 4 chars:  IS"
+Read 4 chars:  BAS
+Read 4 chars: IS,
+
+Read 4 chars: # WI
+Read 4 chars: THOU
+Read 4 chars: T WA
+Read 4 chars: RRAN
+Read 4 chars: TIES
+Read 4 chars:  OR 
+Read 4 chars: COND
+Read 4 chars: ITIO
+Read 4 chars: NS O
+Read 4 chars: F AN
+Read 4 chars: Y KI
+Read 4 chars: ND, 
+Read 4 chars: eith
+Read 4 chars: er e
+Read 4 chars: xpre
+Read 4 chars: ss o
+Read 4 chars: r im
+Read 4 chars: plie
+Read 4 chars: d.
+#
+Read 4 chars:  See
+Read 4 chars:  the
+Read 4 chars:  Lic
+Read 4 chars: ense
+Read 4 chars:  for
+Read 4 chars:  the
+Read 4 chars:  spe
+Read 4 chars: cifi
+Read 4 chars: c la
+Read 4 chars: ngua
+Read 4 chars: ge g
+Read 4 chars: over
+Read 4 chars: ning
+Read 4 chars:  per
+Read 4 chars: miss
+Read 4 chars: ions
+Read 4 chars:  and
+Read 4 chars: 
+# l
+Read 4 chars: imit
+Read 4 chars: atio
+Read 4 chars: ns u
+Read 4 chars: nder
+Read 4 chars:  the
+Read 4 chars:  Lic
+Read 4 chars: ense
+Read 4 chars: .
+
+m
+Read 4 chars: odul
+Read 4 chars: e te
+Read 4 chars: st_a
+Read 4 chars: nnot
+Read 4 chars: _c_c
+Read 4 chars: ompi
+Read 4 chars: ler 
+Read 4 chars: is
+       
+Read 4 chars: cfla
+Read 4 chars: gs "
+Read 4 chars: -I /
+Read 4 chars: usr/
+Read 4 chars: incl
+Read 4 chars: ude"
+Read 4 chars: 
+       cf
+Read 4 chars: lags
+Read 4 chars:  exe
+Read 4 chars: c("p
+Read 4 chars: kg-c
+Read 4 chars: onfi
+Read 4 chars: g", 
+Read 4 chars: "--c
+Read 4 chars: flag
+Read 4 chars: s", 
+Read 4 chars: "sdl
+Read 4 chars: ")
+       
+Read 4 chars: ldfl
+Read 4 chars: ags 
+Read 4 chars: "-lm
+Read 4 chars: "
+       l
+Read 4 chars: dfla
+Read 4 chars: gs("
+Read 4 chars: -lm"
+Read 4 chars: , "-
+Read 4 chars: L /u
+Read 4 chars: sr/b
+Read 4 chars: in")
+Read 4 chars: 
+end
+Read 4 chars: 
+
+fu
+Read 4 chars: n du
+Read 4 chars: mmy 
+Read 4 chars: `{ p
+Read 4 chars: rint
+Read 4 chars: f("n
+Read 4 chars: othi
+Read 4 chars: ng..
+Read 4 chars: .\n"
+Read 4 chars: ); `
+Read 4 chars: }
+
+d
+Read 4 chars: ummy
+Read 1 chars: 
+
diff --git a/tests/test_bufferedfilereader.nit b/tests/test_bufferedfilereader.nit
new file mode 100644 (file)
index 0000000..f5ac80a
--- /dev/null
@@ -0,0 +1,22 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var f = new FileReader.open("test_annot_c_compiler.nit")
+
+while not f.eof do
+       var s = f.read(4)
+       print "Read {s.length} chars: {s}"
+end
+
+f.close
index 39e0cbd..2769eba 100755 (executable)
@@ -21,7 +21,7 @@
 export LANG=C
 export LC_ALL=C
 export NIT_TESTING=true
-export MNIT_SRAND=0
+export NIT_SRAND=0
 
 unset NIT_DIR