X-Git-Url: http://nitlanguage.org diff --git a/lib/markdown/markdown.nit b/lib/markdown/markdown.nit index eac11c2..d63f3e4 100644 --- a/lib/markdown/markdown.nit +++ b/lib/markdown/markdown.nit @@ -133,6 +133,14 @@ class MarkdownProcessor # ~~~ var ext_mode = true + # Disable attaching MDLocation to Tokens + # + # Locations are useful for some tools but they may + # cause an important time and space overhead. + # + # Default = `false` + var no_location = false is writable + init do self.emitter = new MarkdownEmitter(self) # Process the mardown `input` string and return the processed output. @@ -168,8 +176,9 @@ class MarkdownProcessor var c = input[i] if c == '\n' then eol = true + else if c == '\r' then else if c == '\t' then - var np = pos + (4 - (pos.bin_and(3))) + var np = pos + (4 - (pos & 3)) while pos < np do value.add ' ' pos += 1 @@ -396,7 +405,16 @@ class MarkdownProcessor c2 = ' ' end - var loc = text.pos_to_loc(pos) + var loc + if no_location then + loc = null + else + loc = new MDLocation( + current_loc.line_start, + current_loc.column_start + pos, + current_loc.line_start, + current_loc.column_start + pos) + end if c == '*' then if c1 == '*' then @@ -413,7 +431,7 @@ class MarkdownProcessor end else if c == '_' then if c1 == '_' then - if c0 != ' ' or c2 != ' 'then + if c0 != ' ' or c2 != ' ' then return new TokenStrongUnderscore(loc, pos, c) else return new TokenEmUnderscore(loc, pos, c) @@ -476,6 +494,12 @@ class MarkdownProcessor end return -1 end + + # Location used for next parsed token. + # + # This location can be changed by the emitter to adjust with `\n` found + # in the input. + private fun current_loc: MDLocation do return emitter.current_loc end # Emit output corresponding to blocks content. @@ -519,15 +543,19 @@ class MarkdownEmitter # Transform and emit mardown text fun emit_text(text: Text) do emit_text_until(text, 0, null) - # Transform and emit mardown text starting at `from` and + # Transform and emit mardown text starting at `start` and # until a token with the same type as `token` is found. - # Go until the end of text if `token` is null. + # Go until the end of `text` if `token` is null. fun emit_text_until(text: Text, start: Int, token: nullable Token): Int do var old_text = current_text var old_pos = current_pos current_text = text current_pos = start while current_pos < text.length do + if text[current_pos] == '\n' then + current_loc.line_start += 1 + current_loc.column_start = -current_pos + end var mt = processor.token_at(text, current_pos) if (token != null and not token isa TokenNone) and (mt.is_same_type(token) or @@ -570,6 +598,21 @@ class MarkdownEmitter return buffer_stack.last end + # Stacked locations. + private var loc_stack = new List[MDLocation] + + # Push a new MDLocation on the stack. + private fun push_loc(location: MDLocation) do loc_stack.add location + + # Pop the last buffer. + private fun pop_loc: MDLocation do return loc_stack.pop + + # Current output buffer. + private fun current_loc: MDLocation do + assert not loc_stack.is_empty + return loc_stack.last + end + # Append `e` to current buffer. fun add(e: Writable) do if e isa Text then @@ -580,10 +623,12 @@ class MarkdownEmitter end # Append `c` to current buffer. - fun addc(c: Char) do add c.to_s + fun addc(c: Char) do + current_buffer.add c + end # Append a "\n" line break. - fun addn do add "\n" + fun addn do addc '\n' end # A Link Reference. @@ -605,7 +650,7 @@ class LinkRef # Create a link with a title. init with_title(link: String, title: nullable String) do - self.link = link + init(link) self.title = title end end @@ -617,6 +662,11 @@ interface Decorator # Kind of emitter used for decoration. type EMITTER: MarkdownEmitter + # Render a single plain char. + # + # Redefine this method to add special escaping for plain text. + fun add_char(v: EMITTER, c: Char) do v.addc c + # Render a ruler block. fun add_ruler(v: EMITTER, block: BlockRuler) is abstract @@ -724,8 +774,11 @@ class HTMLDecorator end redef fun add_code(v, block) do - if block isa BlockFence and block.meta != null then - v.add "
"
+ var meta = block.meta
+ if meta != null then
+ v.add ""
else
v.add ""
end
@@ -900,6 +953,11 @@ class MDLocation
var column_end: Int
redef fun to_s do return "{line_start},{column_start}--{line_end},{column_end}"
+
+ # Return a copy of `self`.
+ fun copy: MDLocation do
+ return new MDLocation(line_start, column_start, line_end, column_end)
+ end
end
# A block of markdown lines.
@@ -1128,10 +1186,32 @@ abstract class Block
fun emit_blocks(v: MarkdownEmitter) do
var block = self.block.first_block
while block != null do
+ v.push_loc(block.location)
block.kind.emit(v)
+ v.pop_loc
block = block.next
end
end
+
+ # The raw content of the block as a multi-line string.
+ fun raw_content: String do
+ var infence = self isa BlockFence
+ var text = new FlatBuffer
+ var line = self.block.first_line
+ while line != null do
+ if not line.is_empty then
+ var str = line.value
+ if not infence and str.has_prefix(" ") then
+ text.append str.substring(4, str.length - line.trailing)
+ else
+ text.append str
+ end
+ end
+ text.append "\n"
+ line = line.next
+ end
+ return text.write_to_string
+ end
end
# A block without any markdown specificities.
@@ -1172,6 +1252,9 @@ end
class BlockCode
super Block
+ # Any string found after fence token.
+ var meta: nullable Text
+
# Number of char to skip at the beginning of the line.
#
# Block code lines start at 4 spaces.
@@ -1198,9 +1281,6 @@ end
class BlockFence
super BlockCode
- # Any string found after fence token.
- var meta: nullable Text
-
# Fence code lines start at 0 spaces.
redef var line_start = 0
end
@@ -1209,7 +1289,15 @@ end
class BlockHeadline
super Block
- redef fun emit(v) do v.decorator.add_headline(v, self)
+ redef fun emit(v) do
+ var loc = block.location.copy
+ loc.column_start += start
+ v.push_loc(loc)
+ v.decorator.add_headline(v, self)
+ v.pop_loc
+ end
+
+ private var start = 0
# Depth of the headline used to determine the headline level.
var depth = 0
@@ -1238,6 +1326,7 @@ class BlockHeadline
line.leading = 0
line.trailing = 0
end
+ self.start = start
depth = level.min(6)
end
end
@@ -1793,7 +1882,7 @@ end
# A markdown list line.
# Mainly used to factorize code between ordered and unordered lists.
-class LineList
+abstract class LineList
super Line
redef fun process(v) do
@@ -1864,7 +1953,7 @@ end
abstract class Token
# Location of `self` in the original input.
- var location: MDLocation
+ var location: nullable MDLocation
# Position of `self` in input independant from lines.
var pos: Int
@@ -1873,7 +1962,7 @@ abstract class Token
var char: Char
# Output that token using `MarkdownEmitter::decorator`.
- fun emit(v: MarkdownEmitter) do v.addc char
+ fun emit(v: MarkdownEmitter) do v.decorator.add_char(v, char)
end
# A token without a specific meaning.
@@ -2057,6 +2146,7 @@ abstract class TokenLinkOrImage
if pos == -1 then return -1
end
end
+ if pos < start then return -1
if md[pos] != ')' then return -1
else if md[pos] == '[' then
pos += 1
@@ -2255,18 +2345,11 @@ redef class Text
if c == '\\' and pos + 1 < length then
pos = escape(out, self[pos + 1], pos)
else
- var end_reached = false
- for n in nend do
- if c == n then
- end_reached = true
- break
- end
- end
- if end_reached then break
+ for n in nend do if c == n then break label
out.add c
end
pos += 1
- end
+ end label
if pos == length then return -1
return pos
end
@@ -2495,24 +2578,6 @@ redef class Text
return null
end
- # Init a `MDLocation` instance at `pos` in `self`.
- private fun pos_to_loc(pos: Int): MDLocation do
- assert pos <= length
- var line = 1
- var col = 0
- var i = 0
- while i <= pos do
- col += 1
- var c = self[i]
- if c == '\n' then
- line +=1
- col = 0
- end
- i +=1
- end
- return new MDLocation(line, col, line, col)
- end
-
# Is `self` an unsafe HTML element?
private fun is_html_unsafe: Bool do return html_unsafe_tags.has(self.write_to_string)