Files
drunkendotfiles/vim/plugin/vim-markdown-preview/kramdown/parser/kramdown/link.rb
2011-11-17 16:00:49 -06:00

157 lines
5.0 KiB
Ruby
Executable File

# -*- coding: utf-8 -*-
#
#--
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
#
# This file is part of kramdown.
#
# kramdown is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#++
#
module Kramdown
module Parser
class Kramdown
PUNCTUATION_CHARS = "_.:,;!?-"
LINK_ID_CHARS = /[a-zA-Z0-9 #{PUNCTUATION_CHARS}]/
LINK_ID_NON_CHARS = /[^a-zA-Z0-9 #{PUNCTUATION_CHARS}]/
LINK_DEFINITION_START = /^#{OPT_SPACE}\[(#{LINK_ID_CHARS}+)\]:[ \t]*(?:<(.*?)>|([^\s]+))[ \t]*?(?:\n?[ \t]*?(["'])(.+?)\4[ \t]*?)?\n/
# Parse the link definition at the current location.
def parse_link_definition
@src.pos += @src.matched_size
link_id, link_url, link_title = @src[1].downcase, @src[2] || @src[3], @src[5]
warning("Duplicate link ID '#{link_id}' - overwriting") if @doc.parse_infos[:link_defs][link_id]
@doc.parse_infos[:link_defs][link_id] = [link_url, link_title]
true
end
define_parser(:link_definition, LINK_DEFINITION_START)
# This helper methods adds the approriate attributes to the element +el+ of type +a+ or +img+
# and the element itself to the <tt>@tree</tt>.
def add_link(el, href, title, alt_text = nil)
el.options[:attr] ||= {}
el.options[:attr]['title'] = title if title
if el.type == :a
el.options[:attr]['href'] = href
else
el.options[:attr]['src'] = href
el.options[:attr]['alt'] = alt_text
el.children.clear
end
@tree.children << el
end
LINK_TEXT_BRACKET_RE = /\\\[|\\\]|\[|\]/
LINK_INLINE_ID_RE = /\s*?\[(#{LINK_ID_CHARS}+)?\]/
LINK_INLINE_TITLE_RE = /\s*?(["'])(.+?)\1\s*?\)/
LINK_START = /!?\[(?=[^^])/
# Parse the link at the current scanner position. This method is used to parse normal links as
# well as image links.
def parse_link
result = @src.scan(LINK_START)
reset_pos = @src.pos
link_type = (result =~ /^!/ ? :img : :a)
# no nested links allowed
if link_type == :a && (@tree.type == :img || @tree.type == :a || @stack.any? {|t,s| t && (t.type == :img || t.type == :a)})
add_text(result)
return
end
el = Element.new(link_type)
stop_re = /\]|!?\[/
count = 1
found = parse_spans(el, stop_re) do
case @src.matched
when "[", "!["
count += 1
when "]"
count -= 1
end
count - el.children.select {|c| c.type == :img}.size == 0
end
if !found || (link_type == :a && el.children.empty?)
@src.pos = reset_pos
add_text(result)
return
end
alt_text = extract_string(reset_pos...@src.pos, @src)
conv_link_id = alt_text.gsub(/(\s|\n)+/m, ' ').gsub(LINK_ID_NON_CHARS, '').downcase
@src.scan(stop_re)
# reference style link or no link url
if @src.scan(LINK_INLINE_ID_RE) || !@src.check(/\(/)
link_id = (@src[1] || conv_link_id).downcase
if link_id.empty?
@src.pos = reset_pos
add_text(result)
elsif @doc.parse_infos[:link_defs].has_key?(link_id)
add_link(el, @doc.parse_infos[:link_defs][link_id].first, @doc.parse_infos[:link_defs][link_id].last, alt_text)
else
warning("No link definition for link ID '#{link_id}' found")
@src.pos = reset_pos
add_text(result)
end
return
end
# link url in parentheses
if @src.scan(/\(<(.*?)>/)
link_url = @src[1]
if @src.scan(/\)/)
add_link(el, link_url, nil, alt_text)
return
end
else
link_url = ''
re = /\(|\)|\s/
nr_of_brackets = 0
while temp = @src.scan_until(re)
link_url += temp
case @src.matched
when /\s/
break
when '('
nr_of_brackets += 1
when ')'
nr_of_brackets -= 1
break if nr_of_brackets == 0
end
end
link_url = link_url[1..-2]
if nr_of_brackets == 0
add_link(el, link_url, nil, alt_text)
return
end
end
if @src.scan(LINK_INLINE_TITLE_RE)
add_link(el, link_url, @src[2], alt_text)
else
@src.pos = reset_pos
add_text(result)
end
end
define_parser(:link, LINK_START, '!?\[')
end
end
end