Merge: rosetta_code: sha1
authorJean Privat <jean@pryen.org>
Thu, 11 Jun 2015 21:44:47 +0000 (17:44 -0400)
committerJean Privat <jean@pryen.org>
Thu, 11 Jun 2015 21:44:47 +0000 (17:44 -0400)
Signed-off-by: Jean Privat <jean@pryen.org>

Pull-Request: #1456
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: ArthurDelamare <arthur.delamare@viacesi.fr>

contrib/nitiwiki/src/wiki_base.nit
contrib/nitiwiki/src/wiki_html.nit
contrib/nitiwiki/src/wiki_links.nit
lib/ini.nit
lib/markdown/wikilinks.nit

index b250071..09b499b 100644 (file)
@@ -209,6 +209,26 @@ class Nitiwiki
                return tpl
        end
 
+       # Does a sideblock named `name` exists for this wiki?
+       fun has_sideblock(name: String): Bool do
+               name = "{name}.{config.md_ext}"
+               return expand_path(config.root_dir, config.sidebar_dir, name).file_exists
+       end
+
+       # Load a markdown block with `name` from `WikiConfig::sidebar_dir`.
+       private fun load_sideblock(name: String): nullable String do
+               if not has_sideblock(name) then
+                       message("Error: can't load sideblock `{name}`", 0)
+                       return null
+               end
+               name = "{name}.{config.md_ext}"
+               var path = expand_path(config.root_dir, config.sidebar_dir, name)
+               var file = new FileReader.open(path)
+               var res = file.read_all
+               file.close
+               return res
+       end
+
        # Join `parts` as a path and simplify it
        fun expand_path(parts: String...): String do
                var path = ""
@@ -324,6 +344,9 @@ abstract class WikiEntry
                return path.reversed
        end
 
+       # Sidebar relative to this wiki entry.
+       var sidebar = new WikiSidebar(self)
+
        # Relative path from `wiki.config.root_dir` to source if any.
        fun src_path: nullable String is abstract
 
@@ -560,6 +583,28 @@ class WikiArticle
        redef fun to_s do return "{name} ({parent or else "null"})"
 end
 
+# The sidebar is displayed in front of the main panel of a `WikiEntry`.
+class WikiSidebar
+
+       # Wiki used to parse sidebar blocks.
+       var wiki: Nitiwiki is lazy do return entry.wiki
+
+       # WikiEntry this panel is related to.
+       var entry: WikiEntry
+
+       # Blocks are ieces of markdown that will be rendered in the sidebar.
+       var blocks: Array[Text] is lazy do
+               var res = new Array[Text]
+               # TODO get blocks from the entry for more customization
+               for name in entry.wiki.config.sidebar_blocks do
+                       var block = wiki.load_sideblock(name)
+                       if block == null then continue
+                       res.add block
+               end
+               return res
+       end
+end
+
 # Wiki configuration class.
 #
 # This class provides services that ensure static typing when accessing the `config.ini` file.
@@ -702,6 +747,59 @@ class WikiConfig
                return value_or_default("wiki.footer", "footer.html")
        end
 
+       # Automatically add a summary.
+       #
+       # * key: `wiki.auto_summary`
+       # * default: `true`
+       var auto_summary: Bool is lazy do
+               return value_or_default("wiki.auto_summary", "true") == "true"
+       end
+
+       # Automatically add breadcrumbs.
+       #
+       # * key: `wiki.auto_breadcrumbs`
+       # * default: `true`
+       var auto_breadcrumbs: Bool is lazy do
+               return value_or_default("wiki.auto_breadcrumbs", "true") == "true"
+       end
+
+       # Sidebar position.
+       #
+       # Position of the sidebar between `left`, `right` and `none`. Any other value
+       # will be considered as `none`.
+       #
+       # * key: `wiki.sidebar`
+       # * default: `left`
+       var sidebar: String is lazy do
+               return value_or_default("wiki.sidebar", "left")
+       end
+
+       # Sidebar markdown block to include.
+       #
+       # Blocks are specified by their filename without the extension.
+       #
+       # * key: `wiki.sidebar.blocks`
+       # * default: `[]`
+       var sidebar_blocks: Array[String] is lazy do
+               var res = new Array[String]
+               if not has_key("wiki.sidebar.blocks") then return res
+               for val in at("wiki.sidebar.blocks").values do
+                       res.add val
+               end
+               return res
+       end
+
+       # Sidebar files directory.
+       #
+       # Directory where sidebar blocks are stored.
+       # **This path MUST be relative to `root_dir`.**
+       #
+       # * key: `wiki.sidebar_dir`
+       # * default: `sidebar/`
+       var sidebar_dir: String is lazy do
+               return value_or_default("wiki.sidebar_dir", "sidebar/").simplify_path
+       end
+
        # Directory used by rsync to upload wiki files.
        #
        # This information is used to update your distant wiki files (like the webserver).
@@ -721,6 +819,18 @@ class WikiConfig
        # * key: `wiki.git_branch`
        # * default: `master`
        var git_branch: String is lazy do return value_or_default("wiki.git_branch", "master")
+
+       # URL to source versionning used to display last changes
+       #
+       # * key: `wiki.last_changes`
+       # * default: ``
+       var last_changes: String is lazy do return value_or_default("wiki.last_changes", "")
+
+       # URL to source edition.
+       #
+       # * key: `wiki.edit`
+       # * default: ``
+       var edit: String is lazy do return value_or_default("wiki.edit", "")
 end
 
 # WikiSection custom configuration.
index 46518aa..d5ccc7a 100644 (file)
@@ -211,14 +211,23 @@ redef class WikiArticle
        fun tpl_article: TplArticle do
                var article = new TplArticle
                article.body = content
-               article.breadcrumbs = new TplBreadcrumbs(self)
-               tpl_sidebar.blocks.add tpl_summary
+               if wiki.config.auto_breadcrumbs then
+                       article.breadcrumbs = new TplBreadcrumbs(self)
+               end
                article.sidebar = tpl_sidebar
+               article.sidebar_pos = wiki.config.sidebar
                return article
        end
 
        # Sidebar for this page.
-       var tpl_sidebar = new TplSidebar
+       var tpl_sidebar: TplSidebar is lazy do
+               var res = new TplSidebar
+               if wiki.config.auto_summary then
+                       res.blocks.add tpl_summary
+               end
+               res.blocks.add_all sidebar.blocks
+               return res
+       end
 
        # Generate the HTML summary for this article.
        #
@@ -284,6 +293,14 @@ redef class WikiArticle
                if tpl.has_macro("GEN_TIME") then
                        tpl.replace("GEN_TIME", time.to_s)
                end
+               if tpl.has_macro("LAST_CHANGES") then
+                       var url = "{wiki.config.last_changes}{src_path or else ""}"
+                       tpl.replace("LAST_CHANGES", url)
+               end
+               if tpl.has_macro("EDIT") then
+                       var url = "{wiki.config.edit}{src_path or else ""}"
+                       tpl.replace("EDIT", url)
+               end
                return tpl
        end
 end
@@ -327,6 +344,11 @@ class TplArticle
        # Sidebar of this article (if any).
        var sidebar: nullable TplSidebar = null
 
+       # Position of the sidebar.
+       #
+       # See `WikiConfig::sidebar`.
+       var sidebar_pos: String = "left"
+
        # Breadcrumbs from wiki root to this article.
        var breadcrumbs: nullable TplBreadcrumbs = null
 
@@ -336,13 +358,11 @@ class TplArticle
        end
 
        redef fun rendering do
-               if sidebar != null then
-                       add "<div class=\"col-sm-3 sidebar\">"
-                       add sidebar.as(not null)
-                       add "</div>"
-                       add "<div class=\"col-sm-9 content\">"
-               else
+               if sidebar_pos == "left" then render_sidebar
+               if sidebar == null then
                        add "<div class=\"col-sm-12 content\">"
+               else
+                       add "<div class=\"col-sm-9 content\">"
                end
                if body != null then
                        add "<article>"
@@ -358,6 +378,14 @@ class TplArticle
                        add " </article>"
                end
                add "</div>"
+               if sidebar_pos == "right" then render_sidebar
+       end
+
+       private fun render_sidebar do
+               if sidebar == null then return
+               add "<div class=\"col-sm-3 sidebar\">"
+               add sidebar.as(not null)
+               add "</div>"
        end
 end
 
@@ -370,9 +398,9 @@ class TplSidebar
 
        redef fun rendering do
                for block in blocks do
-                       add "<div class=\"sideblock\">"
+                       add "<nav class=\"sideblock\">"
                        add block
-                       add "</div>"
+                       add "</nav>"
                end
        end
 end
index bfdeb86..43f354d 100644 (file)
@@ -29,7 +29,7 @@ redef class Nitiwiki
        #
        # Returns `null` if no article can be found.
        fun lookup_entry_by_name(context: WikiEntry, name: String): nullable WikiEntry do
-               var section = context.parent
+               var section: nullable WikiEntry = context.parent or else context
                var res = section.lookup_entry_by_name(name)
                if res != null then return res
                while section != null do
@@ -50,13 +50,13 @@ redef class Nitiwiki
        #
        # Returns `null` if no article can be found.
        fun lookup_entry_by_title(context: WikiEntry, title: String): nullable WikiEntry do
-               var section = context.parent
+               var section: nullable WikiEntry = context.parent or else context
                var res = section.lookup_entry_by_title(title)
                if res != null then return res
                while section != null do
-                       if section.title == title then return section
+                       if section.title.to_lower == title.to_lower then return section
                        for child in section.children.values do
-                               if child.title == title then return child
+                               if child.title.to_lower == title.to_lower then return child
                        end
                        section = section.parent
                end
@@ -70,7 +70,7 @@ redef class Nitiwiki
        #
        # Returns `null` if no article can be found.
        fun lookup_entry_by_path(context: WikiEntry, path: String): nullable WikiEntry do
-               var entry = context.parent
+               var entry = context.parent or else context
                var parts = path.split_with("/")
                if path.has_prefix("/") then
                        entry = root_section
@@ -80,6 +80,7 @@ redef class Nitiwiki
                while not parts.is_empty do
                        var name = parts.shift
                        if name.is_empty then continue
+                       if entry.name == name then continue
                        if not entry.children.has_key(name) then return null
                        entry = entry.children[name]
                end
@@ -95,6 +96,7 @@ redef class WikiEntry
        redef fun render do
                super
                if not is_dirty and not wiki.force_render then return
+               render_sidebar_wikilinks
        end
 
        # Search in `self` then `self.children` if an entry has the name `name`.
@@ -110,7 +112,7 @@ redef class WikiEntry
        # Search in `self` then `self.children` if an entry has the title `title`.
        fun lookup_entry_by_title(title: String): nullable WikiEntry do
                for child in children.values do
-                       if child.title == title then return child
+                       if child.title.to_lower == title.to_lower then return child
                end
                for child in children.values do
                        var res = child.lookup_entry_by_title(title)
@@ -118,6 +120,19 @@ redef class WikiEntry
                end
                return null
        end
+
+       private var md_proc: NitiwikiMdProcessor is lazy do
+               return new NitiwikiMdProcessor(wiki, self)
+       end
+
+       # Process wikilinks from sidebar.
+       private fun render_sidebar_wikilinks do
+               var blocks = sidebar.blocks
+               for i in [0..blocks.length[ do
+                       blocks[i] = md_proc.process(blocks[i].to_s).write_to_string
+                       md_proc.emitter.decorator.headlines.clear
+               end
+       end
 end
 
 redef class WikiSection
@@ -155,7 +170,6 @@ redef class WikiArticle
        redef fun render do
                super
                if not is_dirty and not wiki.force_render or not has_source then return
-               var md_proc = new NitiwikiMdProcessor(wiki, self)
                content = md_proc.process(md.as(not null))
                headlines.recover_with(md_proc.emitter.decorator.headlines)
        end
@@ -183,7 +197,7 @@ class NitiwikiMdProcessor
        # Article parsed by `self`.
        #
        # Used to contextualize links.
-       var context: WikiArticle
+       var context: WikiEntry
 
        init do
                emitter = new MarkdownEmitter(self)
@@ -198,32 +212,35 @@ private class NitiwikiDecorator
        var wiki: Nitiwiki
 
        # Article used to contextualize links.
-       var context: WikiArticle
+       var context: WikiEntry
 
        redef fun add_wikilink(v, link, name, comment) do
-               var wiki = v.processor.as(NitiwikiMdProcessor).wiki
-               var target: nullable WikiEntry = null
                var anchor: nullable String = null
-               if link.has("#") then
-                       var parts = link.split_with("#")
-                       link = parts.first
-                       anchor = parts.subarray(1, parts.length - 1).join("#")
-               end
-               if link.has("/") then
-                       target = wiki.lookup_entry_by_path(context, link.to_s)
-               else
-                       target = wiki.lookup_entry_by_name(context, link.to_s)
-                       if target == null then
-                               target = wiki.lookup_entry_by_title(context, link.to_s)
-                       end
-               end
                v.add "<a "
-               if target != null then
-                       if name == null then name = target.title
-                       link = target.url
-               else
-                       wiki.message("Warning: unknown wikilink `{link}` (in {context.src_path.as(not null)})", 0)
-                       v.add "class=\"broken\" "
+               if not link.has_prefix("http://") and not link.has_prefix("https://") then
+                       var wiki = v.processor.as(NitiwikiMdProcessor).wiki
+                       var target: nullable WikiEntry = null
+                       if link.has("#") then
+                               var parts = link.split_with("#")
+                               link = parts.first
+                               anchor = parts.subarray(1, parts.length - 1).join("#")
+                       end
+                       if link.has("/") then
+                               target = wiki.lookup_entry_by_path(context, link.to_s)
+                       else
+                               target = wiki.lookup_entry_by_name(context, link.to_s)
+                               if target == null then
+                                       target = wiki.lookup_entry_by_title(context, link.to_s)
+                               end
+                       end
+                       if target != null then
+                               if name == null then name = target.title
+                               link = target.url
+                       else
+                               var loc = context.src_path or else context.name
+                               wiki.message("Warning: unknown wikilink `{link}` (in {loc})", 0)
+                               v.add "class=\"broken\" "
+                       end
                end
                v.add "href=\""
                append_value(v, link)
index 241d22c..bf1c652 100644 (file)
@@ -184,12 +184,29 @@ class ConfigTree
        #     assert config["foo.bar.baz"] == "foobarbaz"
        #     assert config["goo.boo.bar"] == "gooboobar"
        #     assert config["goo.boo.baz.bar"] == "gooboobazbar"
+       #
+       # Using the array notation
+       #
+       #     str = """
+       #     foo[]=a
+       #     foo[]=b
+       #     foo[]=c"""
+       #     str.write_to_file("config4.ini")
+       #     # load file
+       #     config = new ConfigTree("config4.ini")
+       #     print config.to_map.join(":", ",")
+       #     assert config["foo.0"] == "a"
+       #     assert config["foo.1"] == "b"
+       #     assert config["foo.2"] == "c"
+       #     assert config.at("foo").values.join(",") == "a,b,c"
        fun load do
                roots.clear
                var stream = new FileReader.open(ini_file)
                var path: nullable String = null
+               var line_number = 0
                while not stream.eof do
                        var line = stream.read_line
+                       line_number += 1
                        if line.is_empty then
                                continue
                        else if line.has_prefix(";") then
@@ -201,12 +218,16 @@ class ConfigTree
                                set_node(path, null)
                        else
                                var parts = line.split("=")
+                               assert parts.length > 1 else
+                                       print "Error: malformed ini at line {line_number}"
+                               end
                                var key = parts[0].trim
                                var val = parts[1].trim
-                               if path == null then
-                                       set_node(key, val)
+                               if path != null then key = "{path}.{key}"
+                               if key.has_suffix("[]") then
+                                       set_array(key, val)
                                else
-                                       set_node("{path}.{key}", val)
+                                       set_node(key,val)
                                end
                        end
                end
@@ -218,6 +239,16 @@ class ConfigTree
 
        private var roots = new Array[ConfigNode]
 
+       # Append `value` to array at `key`
+       private fun set_array(key: String, value: nullable String) do
+               key = key.substring(0, key.length - 2)
+               var len = 0
+               if has_key(key) then
+                       len = get_node(key).children.length
+               end
+               set_node("{key}.{len.to_s}", value)
+       end
+
        private fun set_node(key: String, value: nullable String) do
                var parts = key.split(".").reversed
                var k = parts.pop
index 22b67c0..28fd1e0 100644 (file)
@@ -88,7 +88,6 @@ class TokenWikiLink
                pos += 1
                pos = md.skip_spaces(pos)
                if pos < start then return -1
-               pos += 1
                return pos
        end
 end