lib/markdown: remove some dead code
[nit.git] / lib / markdown / markdown.nit
index 336fc3d..5b73d3b 100644 (file)
@@ -189,7 +189,7 @@ class MarkdownProcessor
                current_line = line
                current_block = root
                while current_line != null do
-                       current_line.kind(self).process(self)
+                       line_kind(current_line.as(not null)).process(self)
                end
                self.in_list = old_mode
                self.current_block = old_root
@@ -206,6 +206,61 @@ class MarkdownProcessor
        # Is the current recursion in list mode?
        # Used when visiting blocks with `recurse`
        private var in_list = false
+
+       # The type of line.
+       # see: `md_line_*`
+       fun line_kind(md: MDLine): Line do
+               var value = md.value
+               var leading = md.leading
+               var trailing = md.trailing
+               if md.is_empty then return new LineEmpty
+               if md.leading > 3 then return new LineCode
+               if value[leading] == '#' then return new LineHeadline
+               if value[leading] == '>' then return new LineBlockquote
+
+               if value.length - leading - trailing > 2 then
+                       if value[leading] == '`' and md.count_chars_start('`') >= 3 then
+                               return new LineFence
+                       end
+                       if value[leading] == '~' and md.count_chars_start('~') >= 3 then
+                               return new LineFence
+                       end
+               end
+
+               if value.length - leading - trailing > 2 and
+                  (value[leading] == '*' or value[leading] == '-' or value[leading] == '_') then
+                  if md.count_chars(value[leading]) >= 3 then
+                               return new LineHR
+                  end
+               end
+
+               if value.length - leading >= 2 and value[leading + 1] == ' ' then
+                       var c = value[leading]
+                       if c == '*' or c == '-' or c == '+' then return new LineUList
+               end
+
+               if value.length - leading >= 3 and value[leading].is_digit then
+                       var i = leading + 1
+                       while i < value.length and value[i].is_digit do i += 1
+                       if i + 1 < value.length and value[i] == '.' and value[i + 1] == ' ' then
+                               return new LineOList
+                       end
+               end
+
+               if value[leading] == '<' and md.check_html then return new LineXML
+
+               var next = md.next
+               if next != null and not next.is_empty then
+                       if next.count_chars('=') > 0 then
+                               return new LineHeadline1
+                       end
+                       if next.count_chars('-') > 0 then
+                               return new LineHeadline2
+                       end
+               end
+               return new LineOther
+       end
+
 end
 
 # Emit output corresponding to blocks content.
@@ -332,6 +387,7 @@ class LinkRef
        # Is the link an abreviation?
        var is_abbrev = false
 
+       # Create a link with a title.
        init with_title(link: String, title: nullable String) do
                self.link = link
                self.title = title
@@ -398,18 +454,46 @@ interface Decorator
 
        # Render a line break
        fun add_line_break(v: MarkdownEmitter) is abstract
+
+       # Generate a new html valid id from a `String`.
+       fun strip_id(txt: String): String is abstract
+
+       # Found headlines during the processing labeled by their ids.
+       fun headlines: ArrayMap[String, HeadLine] is abstract
+end
+
+# Class representing a markdown headline.
+class HeadLine
+       # Unique identifier of this headline.
+       var id: String
+
+       # Text of the headline.
+       var title: String
+
+       # Level of this headline.
+       #
+       # According toe the markdown specification, level must be in `[1..6]`.
+       var level: Int
 end
 
 # `Decorator` that outputs HTML.
 class HTMLDecorator
        super Decorator
 
+       redef var headlines = new ArrayMap[String, HeadLine]
+
        redef fun add_ruler(v, block) do v.add "<hr/>\n"
 
        redef fun add_headline(v, block) do
-               v.add "<h{block.depth}>"
+               # save headline
+               var txt = block.block.first_line.value
+               var id = strip_id(txt)
+               var lvl = block.depth
+               headlines[id] = new HeadLine(id, txt, lvl)
+               # output it
+               v.add "<h{lvl} id=\"{id}\">"
                v.emit_in block
-               v.add "</h{block.depth}>\n"
+               v.add "</h{lvl}>\n"
        end
 
        redef fun add_paragraph(v, block) do
@@ -544,6 +628,35 @@ class HTMLDecorator
                        end
                end
        end
+
+       redef fun strip_id(txt) do
+               # strip id
+               var b = new FlatBuffer
+               for c in txt do
+                       if c == ' ' then
+                               b.add '_'
+                       else
+                               if not c.is_letter and
+                                  not c.is_digit and
+                                  not allowed_id_chars.has(c) then continue
+                               b.add c
+                       end
+               end
+               var res = b.to_s
+               var key = res
+               # check for multiple id definitions
+               if headlines.has_key(key) then
+                       var i = 1
+                       key = "{res}_{i}"
+                       while headlines.has_key(key) do
+                               i += 1
+                               key = "{res}_{i}"
+                       end
+               end
+               return key
+       end
+
+       private var allowed_id_chars: Array[Char] = ['-', '_', ':', '.']
 end
 
 # A block of markdown lines.
@@ -689,7 +802,7 @@ class MDBlock
                var line = first_line
                while line != null do
                        if not line.is_empty then
-                               var kind = line.kind(v)
+                               var kind = v.line_kind(line)
                                if kind isa LineList then
                                        line.value = kind.extract_value(line)
                                else
@@ -802,13 +915,18 @@ end
 class BlockCode
        super Block
 
+       # Number of char to skip at the beginning of the line.
+       #
+       # Block code lines start at 4 spaces.
+       protected var line_start = 4
+
        redef fun emit(v) do v.decorator.add_code(v, self)
 
        redef fun emit_lines(v) do
                var line = block.first_line
                while line != null do
                        if not line.is_empty then
-                               v.decorator.append_code(v, line.value, 4, line.value.length)
+                               v.decorator.append_code(v, line.value, line_start, line.value.length)
                        end
                        v.addn
                        line = line.next
@@ -822,6 +940,9 @@ end
 # this class is only used for typing purposes.
 class BlockFence
        super BlockCode
+
+       # Fence code lines start at 0 spaces.
+       redef var line_start = 0
 end
 
 # A markdown headline.
@@ -878,7 +999,7 @@ abstract class BlockList
                var line = block.first_line
                line = line.next
                while line != null do
-                       var t = line.kind(v)
+                       var t = v.line_kind(line)
                        if t isa LineList or
                           (not line.is_empty and (line.prev_empty and line.leading == 0 and
                           not (t isa LineList))) then
@@ -990,6 +1111,7 @@ class MDLine
        # Is the next line empty?
        var next_empty: Bool = false is writable
 
+       # Initialize a new MDLine from its string value
        init(value: String) do
                self.value = value
                self.leading = process_leading
@@ -1009,57 +1131,6 @@ class MDLine
                if next != null then next.prev_empty = true
        end
 
-       # The type of line.
-       # see `md_line_*`
-       fun kind(v: MarkdownProcessor): Line do
-               var value = self.value
-               if is_empty then return new LineEmpty
-               if leading > 3 then return new LineCode
-               if value[leading] == '#' then return new LineHeadline
-               if value[leading] == '>' then return new LineBlockquote
-
-               if value.length - leading - trailing > 2 then
-                       if value[leading] == '`' and count_chars_start('`') >= 3 then
-                               return new LineFence
-                       end
-                       if value[leading] == '~' and count_chars_start('~') >= 3 then
-                               return new LineFence
-                       end
-               end
-
-               if value.length - leading - trailing > 2 and
-                  (value[leading] == '*' or value[leading] == '-' or value[leading] == '_') then
-                  if count_chars(value[leading]) >= 3 then
-                               return new LineHR
-                  end
-               end
-
-               if value.length - leading >= 2 and value[leading + 1] == ' ' then
-                       var c = value[leading]
-                       if c == '*' or c == '-' or c == '+' then return new LineUList
-               end
-
-               if value.length - leading >= 3 and value[leading].is_digit then
-                       var i = leading + 1
-                       while i < value.length and value[i].is_digit do i += 1
-                       if i + 1 < value.length and value[i] == '.' and value[i + 1] == ' ' then
-                               return new LineOList
-                       end
-               end
-
-               if value[leading] == '<' and check_html then return new LineXML
-
-               if next != null and not next.is_empty then
-                       if next.count_chars('=') > 0 then
-                               return new LineHeadline1
-                       end
-                       if next.count_chars('-') > 0 then
-                               return new LineHeadline2
-                       end
-               end
-               return new LineOther
-       end
-
        # Number or leading spaces on this line.
        var leading: Int = 0 is writable
 
@@ -1148,7 +1219,7 @@ class MDLine
                                        pos += 1
                                end
                                if pos >= line.value.length then
-                                       if line.value[pos - 2] == '/' then
+                                       if pos - 2 >= 0 and line.value[pos - 2] == '/' then
                                                tags.pop
                                                if tags.is_empty then
                                                        xml_end_line = line
@@ -1247,7 +1318,7 @@ class LineOther
                # go to block end
                var was_empty = line.prev_empty
                while line != null and not line.is_empty do
-                       var t = line.kind(v)
+                       var t = v.line_kind(line)
                        if v.in_list and t isa LineList then
                                break
                        end
@@ -1261,7 +1332,6 @@ class LineOther
                        line = line.next
                end
                # build block
-               var bk: Block
                if line != null and not line.is_empty then
                        var block = v.current_block.split(line.prev.as(not null))
                        if v.in_list and not was_empty then
@@ -1295,7 +1365,7 @@ class LineCode
        redef fun process(v) do
                var line = v.current_line
                # lookup block end
-               while line != null and (line.is_empty or line.kind(v) isa LineCode) do
+               while line != null and (line.is_empty or v.line_kind(line) isa LineCode) do
                        line = line.next
                end
                # split at block end line
@@ -1336,7 +1406,7 @@ class LineBlockquote
                while line != null do
                        if not line.is_empty and (line.prev_empty and
                           line.leading == 0 and
-                          not line.kind(v) isa LineBlockquote) then break
+                          not v.line_kind(line) isa LineBlockquote) then break
                        line = line.next
                end
                # build sub block
@@ -1378,7 +1448,7 @@ class LineFence
                # go to fence end
                var line = v.current_line.next
                while line != null do
-                       if line.kind(v) isa LineFence then break
+                       if v.line_kind(line) isa LineFence then break
                        line = line.next
                end
                if line != null then
@@ -1393,7 +1463,8 @@ class LineFence
                end
                block.kind = new BlockFence(block)
                block.first_line.clear
-               if block.last_line.kind(v) isa LineFence then
+               var last = block.last_line
+               if last != null and v.line_kind(last) isa LineFence then
                        block.last_line.clear
                end
                block.remove_surrounding_empty_lines
@@ -1412,8 +1483,6 @@ class LineHeadline
                var block = v.current_block.split(line.as(not null))
                var kind = new BlockHeadline(block)
                block.kind = kind
-               # TODO block ID
-               # block.id = block.first_line.strip_id
                kind.transform_headline(block)
                v.current_block.remove_leading_empty_lines
                v.current_line = v.current_block.first_line
@@ -1432,8 +1501,6 @@ class LineHeadline1
                var block = v.current_block.split(line.as(not null))
                var kind = new BlockHeadline(block)
                kind.depth = 1
-               # TODO block ID
-               # block.id = block.first_line.strip_id
                kind.transform_headline(block)
                block.kind = kind
                v.current_block.remove_leading_empty_lines
@@ -1453,8 +1520,6 @@ class LineHeadline2
                var block = v.current_block.split(line.as(not null))
                var kind = new BlockHeadline(block)
                kind.depth = 2
-               # TODO block ID
-               # block.id = block.first_line.strip_id
                kind.transform_headline(block)
                block.kind = kind
                v.current_block.remove_leading_empty_lines
@@ -1471,7 +1536,7 @@ class LineList
                var line = v.current_line
                # go to list end
                while line != null do
-                       var t = line.kind(v)
+                       var t = v.line_kind(line)
                        if not line.is_empty and (line.prev_empty and line.leading == 0 and
                           not t isa LineList) then break
                        line = line.next
@@ -1504,6 +1569,7 @@ class LineList
        # Create a new block kind based on this line.
        protected fun block_kind(block: MDBlock): BlockList is abstract
 
+       # Extract string value from `MDLine`.
        protected fun extract_value(line: MDLine): String is abstract
 end
 
@@ -1906,7 +1972,6 @@ redef class Text
                var c0: Char
                var c1: Char
                var c2: Char
-               var c3: Char
 
                if pos > 0 then
                        c0 = self[pos - 1]
@@ -1925,11 +1990,6 @@ redef class Text
                else
                        c2 = ' '
                end
-               if pos + 3 < length then
-                       c3 = self[pos + 3]
-               else
-                       c3 = ' '
-               end
 
                if c == '*' then
                        if c1 == '*' then