├── LEGAL.mkd ├── README.mkd ├── install.sh ├── plugin ├── markdown-preview.css ├── vim-markdown-preview │ └── kramdown │ │ ├── COPYING │ │ ├── compatibility.rb │ │ ├── converter.rb │ │ ├── converter │ │ ├── base.rb │ │ ├── html.rb │ │ ├── kramdown.rb │ │ └── latex.rb │ │ ├── document.rb │ │ ├── error.rb │ │ ├── kramdown.rb │ │ ├── options.rb │ │ ├── parser.rb │ │ ├── parser │ │ ├── base.rb │ │ ├── html.rb │ │ ├── kramdown.rb │ │ └── kramdown │ │ │ ├── abbreviation.rb │ │ │ ├── attribute_list.rb │ │ │ ├── autolink.rb │ │ │ ├── blank_line.rb │ │ │ ├── blockquote.rb │ │ │ ├── codeblock.rb │ │ │ ├── codespan.rb │ │ │ ├── emphasis.rb │ │ │ ├── eob.rb │ │ │ ├── escaped_chars.rb │ │ │ ├── extension.rb │ │ │ ├── footnote.rb │ │ │ ├── header.rb │ │ │ ├── horizontal_rule.rb │ │ │ ├── html.rb │ │ │ ├── html_entity.rb │ │ │ ├── line_break.rb │ │ │ ├── link.rb │ │ │ ├── list.rb │ │ │ ├── math.rb │ │ │ ├── paragraph.rb │ │ │ ├── smart_quotes.rb │ │ │ ├── table.rb │ │ │ └── typographic_symbol.rb │ │ ├── utils.rb │ │ ├── utils │ │ ├── entities.rb │ │ └── html.rb │ │ └── version.rb └── vmp.vim └── screenshots └── preview.png /LEGAL.mkd: -------------------------------------------------------------------------------- 1 | ## LEGAL 2 | **vim-markdown-preview** 3 | vim-markdown-preview is released under the GPL v3. 4 | The terms and conditions of this license are described below: 5 | 6 | 7 | Copyright (C) 2009 Robert Gleeson 8 | 9 | vim-markdown-preview is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program. If not, see . 21 | 22 | This file(LEGAL.mkd) **must** be apart of any redistributable packages made 23 | from this software. No licenses(including the one pertaining to kramdown) should 24 | be removed from this software if you are making redistributable copies. 25 | 26 | **kramdown** 27 | The kramdown library is used for converting a markdown document into a 28 | HTML document, and it is bundled with this software. 29 | Thanks to `Thomas Leitner`, the author of the kramdown library. 30 | 31 | (The GPLv3 license for kramdown can be found at 32 | "./plugin/vim-markdown-preview/kramdown/COPYING" 33 | from the root of this project) 34 | 35 | -------------------------------------------------------------------------------- /README.mkd: -------------------------------------------------------------------------------- 1 | ## vim-markdown-preview 2 | 3 | _vim-markdown-preview_ adds the option to preview markdown documents in the 4 | browser from the vim editor. 5 | The web page it opens is loosely styled on how GitHub would render README.mkd. 6 | 7 | ## Install 8 | 9 | **Download** 10 | 11 | * Option 1 12 | Cloning this repository is one option, and the master branch should always be 13 | in a working state. 14 | 15 | * Option 2 16 | You can download versioned releases from the 17 | [downloads](http://github.com/robgleeson/vim-markdown-preview/downloads) page. 18 | 19 | **Automatic installation** 20 | vim-markdown-preview is bundled with an installer script which will install to 21 | $HOME/.vim/plugin/ 22 | 23 | sh install.sh 24 | 25 | ## Usage 26 | 27 | Send vim the command `:Mm` for a preview of your markdown document. 28 | Alternatively, bind `:Mm` to a key binding: 29 | `map p :Mm` 30 | 31 | 32 | ## Dependencies 33 | 34 | * vim 35 | * Ruby support for vim. 36 | * kramdown (bundled with this package) 37 | 38 | ## OSX 39 | 40 | _vim-markdown-preview_ was written to run on OSX, and it will work w/o 41 | problems. 42 | I recommend the safari browser because it will not open new tabs needlessly 43 | if a document is already loaded in an existing tab/window. 44 | 45 | ## Linux/Others 46 | 47 | _vim-markdown-preview_ is tied to the OSX command `open`. 48 | You will need to replace it with something else - firefox-bin, for example, 49 | for it to work on your operating system. 50 | 51 | ## Credit 52 | 53 | * Mathias Billman 54 | This is an enitrely rewritten version of the vim script originally written by 55 | Mathias Billman and found 56 | [here](http://mathias-biilmann.net/2009/1/markdown-preview-in-vim). 57 | 58 | * [moreweb](http://github.com/moreweb) 59 | Contributor. 60 | 61 | * [maba](http://github.com/maba) 62 | Contributor. 63 | 64 | 65 | ## Bugs 66 | 67 | No known bugs right now. 68 | [Found a bug?](http://github.com/robgleeson/vim-markdown-preview/issues). 69 | 70 | ## Contact 71 | 72 | * IRC 73 | irc.freenode.net/#flowof.info as 'robgleeson' 74 | 75 | * Mail 76 | rob [at] flowof.info 77 | 78 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -e $HOME/.vim/plugin/MKD.vim ]; then 4 | echo "Removing old version of vim-markdown-preview ..." 5 | rm $HOME/.vim/plugin/MKD.vim 6 | echo "DONE.." 7 | fi 8 | 9 | echo "Copying plugin/* to $HOME/.vim/plugin ... " 10 | cp -R plugin/* $HOME/.vim/plugin 11 | echo "DONE.." 12 | 13 | -------------------------------------------------------------------------------- /plugin/markdown-preview.css: -------------------------------------------------------------------------------- 1 | body div#content { 2 | margin : 0 auto; 3 | width : 920px; 4 | background-color : #f8f8f8; 5 | padding : .7em; 6 | font-size : 13.34px; 7 | font-family : verdana, sans-serif; 8 | border : 1px #E0E0E0 solid; 9 | } 10 | 11 | body div#content h2, body div#content h3, body div#content h4 { 12 | padding-top : 10px; 13 | border-top : 4px solid #E0E0E0; 14 | } 15 | 16 | body div#content pre { 17 | padding : 5px; 18 | border-style : solid; 19 | border-width : 1px; 20 | border-color : #E0E0E0; 21 | background-color : #F8F8FF; 22 | } 23 | 24 | body div#content pre code { 25 | padding : 5px; 26 | background-color : #F8F8FF; 27 | border : none; 28 | } 29 | 30 | body div#content code { 31 | font-family : courier, fixed; 32 | display : inline-block; 33 | padding : 0px 2px 0px 2px; 34 | background-color : #F8F8FF; 35 | border : 1px #E0E0E0 solid; 36 | } 37 | 38 | body h4#title { 39 | font-family : verdana, sans-serif; 40 | display : block; 41 | margin : 0 auto; 42 | width : 920px; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/COPYING: -------------------------------------------------------------------------------- 1 | kramdown - fast, pure-Ruby Markdown-superset converter 2 | Copyright (C) 2009 Thomas Leitner 3 | 4 | kramdown is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | 17 | 18 | Some test cases and the benchmark files are based on test cases from 19 | the MDTest test suite: 20 | 21 | MDTest 22 | Copyright (c) 2007 Michel Fortin 23 | 24 | 25 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/compatibility.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | # All the code in this file is backported from Ruby 1.8.7 sothat kramdown works under 1.8.5 24 | 25 | if RUBY_VERSION == '1.8.5' 26 | require 'rexml/parsers/baseparser' 27 | module REXML 28 | module Parsers 29 | class BaseParser 30 | UNAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}" 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/converter.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | 25 | # == Converter Module 26 | # 27 | # This module contains all available converters, i.e. classes that take a document and convert the 28 | # document tree to a specific output format, normally a string. For example, the Html module 29 | # converts the document tree into HTML. 30 | # 31 | # Converters use the Base class for common functionality (like applying a template to the output)- 32 | # see its API documentation for how to create a converter class. 33 | module Converter 34 | 35 | autoload :Base, 'kramdown/converter/base' 36 | autoload :Html, 'kramdown/converter/html' 37 | autoload :Latex, 'kramdown/converter/latex' 38 | autoload :Kramdown, 'kramdown/converter/kramdown' 39 | 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/converter/base.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'erb' 24 | 25 | module Kramdown 26 | 27 | module Converter 28 | 29 | # == Base class for converters 30 | # 31 | # This class serves as base class for all converters. It provides methods that can/should be 32 | # used by all converters (like #generate_id) as well as common functionality that is 33 | # automatically applied to the result (for example, embedding the output into a template). 34 | # 35 | # == Implementing a converter 36 | # 37 | # Implementing a new converter is rather easy: just create a new sub class from this class and 38 | # put it in the Kramdown::Converter module (the latter is only needed if auto-detection should 39 | # work properly). Then you need to implement the #convert(tree) method which takes a document 40 | # tree and should return the converted output. 41 | # 42 | # The document instance is automatically set as @doc in Base#initialize. Furthermore, the 43 | # document instance provides a hash called `conversion_infos` that is also automatically cleared 44 | # and can be used to store information about the conversion process. 45 | # 46 | # The actual transformation of the document tree can be done in any way. However, writing one 47 | # method per tree element type is a straight forward way to do it - this is how the Html and 48 | # Latex converters do the transformation. 49 | class Base 50 | 51 | # Initialize the converter with the given Kramdown document +doc+. 52 | def initialize(doc) 53 | @doc = doc 54 | @doc.conversion_infos.clear 55 | end 56 | private_class_method(:new, :allocate) 57 | 58 | # Convert the Kramdown document +doc+ to the output format implemented by a subclass. 59 | # 60 | # Initializes a new instance of the calling class and then calls the #convert method that must 61 | # be implemented by each subclass. If the +template+ option is specified and non-empty, the 62 | # result is rendered into the specified template. 63 | def self.convert(doc) 64 | result = new(doc).convert(doc.tree) 65 | result = apply_template(doc, result) if !doc.options[:template].empty? 66 | result 67 | end 68 | 69 | # Apply the template specified in the +doc+ options, using +body+ as the body string. 70 | def self.apply_template(doc, body) 71 | erb = ERB.new(get_template(doc.options[:template])) 72 | obj = Object.new 73 | obj.instance_variable_set(:@doc, doc) 74 | obj.instance_variable_set(:@body, body) 75 | erb.result(obj.instance_eval{binding}) 76 | end 77 | 78 | # Return the template specified by +template+. 79 | def self.get_template(template) 80 | format_ext = '.' + self.name.split(/::/).last.downcase 81 | shipped = File.join(::Kramdown.data_dir, template + format_ext) 82 | if File.exist?(template) 83 | File.read(template) 84 | elsif File.exist?(template + format_ext) 85 | File.read(template + format_ext) 86 | elsif File.exist?(shipped) 87 | File.read(shipped) 88 | else 89 | raise "The specified template file #{template} does not exist" 90 | end 91 | end 92 | 93 | 94 | # Generate an unique alpha-numeric ID from the the string +str+ for use as header ID. 95 | def generate_id(str) 96 | gen_id = str.gsub(/[^a-zA-Z0-9 -]/, '').gsub(/^[^a-zA-Z]*/, '').gsub(' ', '-').downcase 97 | gen_id = 'section' if gen_id.length == 0 98 | @used_ids ||= {} 99 | if @used_ids.has_key?(gen_id) 100 | gen_id += '-' + (@used_ids[gen_id] += 1).to_s 101 | else 102 | @used_ids[gen_id] = 0 103 | end 104 | @doc.options[:auto_id_prefix] + gen_id 105 | end 106 | 107 | end 108 | 109 | end 110 | 111 | end 112 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/converter/html.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'rexml/parsers/baseparser' 24 | 25 | module Kramdown 26 | 27 | module Converter 28 | 29 | # Converts a Kramdown::Document to HTML. 30 | class Html < Base 31 | 32 | include ::Kramdown::Utils::HTML 33 | 34 | # DEPRECATED: use #html_attributes 35 | def options_for_element(el) 36 | warn("DEPRECATION WARNING: this method will be deprecated in the next release, use #html_attributes instead") 37 | html_attributes(el) 38 | end 39 | 40 | # :stopdoc: 41 | 42 | # Defines the amount of indentation used when nesting HTML tags. 43 | INDENTATION = 2 44 | 45 | begin 46 | require 'coderay' 47 | 48 | # Highlighting via coderay is available if this constant is +true+. 49 | HIGHLIGHTING_AVAILABLE = true 50 | rescue LoadError => e 51 | HIGHLIGHTING_AVAILABLE = false 52 | end 53 | 54 | # Initialize the HTML converter with the given Kramdown document +doc+. 55 | def initialize(doc) 56 | super 57 | @footnote_counter = @footnote_start = @doc.options[:footnote_nr] 58 | @footnotes = [] 59 | @toc = [] 60 | @toc_code = nil 61 | end 62 | 63 | def convert(el, indent = -INDENTATION, opts = {}) 64 | send("convert_#{el.type}", el, indent, opts) 65 | end 66 | 67 | def inner(el, indent, opts) 68 | result = '' 69 | indent += INDENTATION 70 | el.children.each do |inner_el| 71 | result << send("convert_#{inner_el.type}", inner_el, indent, opts) 72 | end 73 | result 74 | end 75 | 76 | def convert_blank(el, indent, opts) 77 | "\n" 78 | end 79 | 80 | def convert_text(el, indent, opts) 81 | escape_html(el.value, :text) 82 | end 83 | 84 | def convert_p(el, indent, opts) 85 | if el.options[:transparent] 86 | "#{inner(el, indent, opts)}" 87 | else 88 | "#{' '*indent}#{inner(el, indent, opts)}

\n" 89 | end 90 | end 91 | 92 | def convert_codeblock(el, indent, opts) 93 | if el.options[:attr] && el.options[:attr]['lang'] && HIGHLIGHTING_AVAILABLE 94 | el = Marshal.load(Marshal.dump(el)) # so that the original is not changed 95 | opts = {:wrap => @doc.options[:coderay_wrap], :line_numbers => @doc.options[:coderay_line_numbers], 96 | :line_number_start => @doc.options[:coderay_line_number_start], :tab_width => @doc.options[:coderay_tab_width], 97 | :bold_every => @doc.options[:coderay_bold_every], :css => @doc.options[:coderay_css]} 98 | result = CodeRay.scan(el.value, el.options[:attr].delete('lang').to_sym).html(opts).chomp + "\n" 99 | "#{' '*indent}#{result}#{' '*indent}\n" 100 | else 101 | result = escape_html(el.value) 102 | if el.options[:attr] && el.options[:attr].has_key?('class') && el.options[:attr]['class'] =~ /\bshow-whitespaces\b/ 103 | result.gsub!(/(?:(^[ \t]+)|([ \t]+$)|([ \t]+))/) do |m| 104 | suffix = ($1 ? '-l' : ($2 ? '-r' : '')) 105 | m.scan(/./).map do |c| 106 | case c 107 | when "\t" then "\t" 108 | when " " then "" 109 | end 110 | end.join('') 111 | end 112 | end 113 | "#{' '*indent}#{result}#{result =~ /\n\Z/ ? '' : "\n"}\n" 114 | end 115 | end 116 | 117 | def convert_blockquote(el, indent, opts) 118 | "#{' '*indent}\n#{inner(el, indent, opts)}#{' '*indent}\n" 119 | end 120 | 121 | def convert_header(el, indent, opts) 122 | el = Marshal.load(Marshal.dump(el)) # so that the original is not changed 123 | if @doc.options[:auto_ids] && !(el.options[:attr] && el.options[:attr]['id']) 124 | (el.options[:attr] ||= {})['id'] = generate_id(el.options[:raw_text]) 125 | end 126 | @toc << [el.options[:level], el.options[:attr]['id'], el.children] if el.options[:attr] && el.options[:attr]['id'] && within_toc_depth?(el) 127 | "#{' '*indent}#{inner(el, indent, opts)}\n" 128 | end 129 | 130 | def within_toc_depth?(el) 131 | @doc.options[:toc_depth] <= 0 || el.options[:level] <= @doc.options[:toc_depth] 132 | end 133 | 134 | def convert_hr(el, indent, opts) 135 | "#{' '*indent}
\n" 136 | end 137 | 138 | def convert_ul(el, indent, opts) 139 | if !@toc_code && (el.options[:ial][:refs].include?('toc') rescue nil) && (el.type == :ul || el.type == :ol) 140 | @toc_code = [el.type, el.options[:attr], (0..128).to_a.map{|a| rand(36).to_s(36)}.join] 141 | @toc_code.last 142 | else 143 | "#{' '*indent}<#{el.type}#{html_attributes(el)}>\n#{inner(el, indent, opts)}#{' '*indent}\n" 144 | end 145 | end 146 | alias :convert_ol :convert_ul 147 | alias :convert_dl :convert_ul 148 | 149 | def convert_li(el, indent, opts) 150 | output = ' '*indent << "<#{el.type}" << html_attributes(el) << ">" 151 | res = inner(el, indent, opts) 152 | if el.children.empty? || (el.children.first.type == :p && el.children.first.options[:transparent]) 153 | output << res << (res =~ /\n\Z/ ? ' '*indent : '') 154 | else 155 | output << "\n" << res << ' '*indent 156 | end 157 | output << "\n" 158 | end 159 | alias :convert_dd :convert_li 160 | 161 | def convert_dt(el, indent, opts) 162 | "#{' '*indent}#{inner(el, indent, opts)}\n" 163 | end 164 | 165 | HTML_TAGS_WITH_BODY=['div', 'script'] 166 | 167 | def convert_html_element(el, indent, opts) 168 | res = inner(el, indent, opts) 169 | if el.options[:category] == :span 170 | "<#{el.value}#{html_attributes(el)}" << (!res.empty? ? ">#{res}" : " />") 171 | else 172 | output = '' 173 | output << ' '*indent if !el.options[:parent_is_raw] 174 | output << "<#{el.value}#{html_attributes(el)}" 175 | if !res.empty? && el.options[:parse_type] != :block 176 | output << ">#{res}" 177 | elsif !res.empty? 178 | output << ">\n#{res}" << ' '*indent << "" 179 | elsif HTML_TAGS_WITH_BODY.include?(el.value) 180 | output << ">" 181 | else 182 | output << " />" 183 | end 184 | output << "\n" if el.options[:outer_element] || !el.options[:parent_is_raw] 185 | output 186 | end 187 | end 188 | 189 | def convert_xml_comment(el, indent, opts) 190 | if el.options[:category] == :block && !el.options[:parent_is_raw] 191 | ' '*indent + el.value + "\n" 192 | else 193 | el.value 194 | end 195 | end 196 | alias :convert_xml_pi :convert_xml_comment 197 | alias :convert_html_doctype :convert_xml_comment 198 | 199 | def convert_table(el, indent, opts) 200 | if el.options[:alignment].all? {|a| a == :default} 201 | alignment = '' 202 | else 203 | alignment = el.options[:alignment].map do |a| 204 | "#{' '*(indent + INDENTATION)}" + (a == :default ? "" : "") + "\n" 205 | end.join('') 206 | end 207 | "#{' '*indent}\n#{alignment}#{inner(el, indent, opts)}#{' '*indent}\n" 208 | end 209 | 210 | def convert_thead(el, indent, opts) 211 | "#{' '*indent}<#{el.type}#{html_attributes(el)}>\n#{inner(el, indent, opts)}#{' '*indent}\n" 212 | end 213 | alias :convert_tbody :convert_thead 214 | alias :convert_tfoot :convert_thead 215 | alias :convert_tr :convert_thead 216 | 217 | def convert_td(el, indent, opts) 218 | res = inner(el, indent, opts) 219 | "#{' '*indent}<#{el.type}#{html_attributes(el)}>#{res.empty? ? " " : res}\n" 220 | end 221 | alias :convert_th :convert_td 222 | 223 | def convert_comment(el, indent, opts) 224 | if el.options[:category] == :block 225 | "#{' '*indent}\n" 226 | else 227 | "" 228 | end 229 | end 230 | 231 | def convert_br(el, indent, opts) 232 | "
" 233 | end 234 | 235 | def convert_a(el, indent, opts) 236 | do_obfuscation = el.options[:attr]['href'] =~ /^mailto:/ 237 | if do_obfuscation 238 | el = Marshal.load(Marshal.dump(el)) # so that the original is not changed 239 | href = obfuscate(el.options[:attr]['href'].sub(/^mailto:/, '')) 240 | mailto = obfuscate('mailto') 241 | el.options[:attr]['href'] = "#{mailto}:#{href}" 242 | end 243 | res = inner(el, indent, opts) 244 | res = obfuscate(res) if do_obfuscation 245 | "#{res}" 246 | end 247 | 248 | def convert_img(el, indent, opts) 249 | "" 250 | end 251 | 252 | def convert_codespan(el, indent, opts) 253 | "#{escape_html(el.value)}" 254 | end 255 | 256 | def convert_footnote(el, indent, opts) 257 | number = @footnote_counter 258 | @footnote_counter += 1 259 | @footnotes << [el.options[:name], @doc.parse_infos[:footnotes][el.options[:name]]] 260 | "#{number}" 261 | end 262 | 263 | def convert_raw(el, indent, opts) 264 | el.value + (el.options[:category] == :block ? "\n" : '') 265 | end 266 | 267 | def convert_em(el, indent, opts) 268 | "<#{el.type}#{html_attributes(el)}>#{inner(el, indent, opts)}" 269 | end 270 | alias :convert_strong :convert_em 271 | 272 | def convert_entity(el, indent, opts) 273 | entity_to_str(el.value) 274 | end 275 | 276 | TYPOGRAPHIC_SYMS = { 277 | :mdash => [::Kramdown::Utils::Entities.entity('mdash')], 278 | :ndash => [::Kramdown::Utils::Entities.entity('ndash')], 279 | :hellip => [::Kramdown::Utils::Entities.entity('hellip')], 280 | :laquo_space => [::Kramdown::Utils::Entities.entity('laquo'), ::Kramdown::Utils::Entities.entity('nbsp')], 281 | :raquo_space => [::Kramdown::Utils::Entities.entity('nbsp'), ::Kramdown::Utils::Entities.entity('raquo')], 282 | :laquo => [::Kramdown::Utils::Entities.entity('laquo')], 283 | :raquo => [::Kramdown::Utils::Entities.entity('raquo')] 284 | } 285 | def convert_typographic_sym(el, indent, opts) 286 | TYPOGRAPHIC_SYMS[el.value].map {|e| entity_to_str(e)}.join('') 287 | end 288 | 289 | def convert_smart_quote(el, indent, opts) 290 | entity_to_str(::Kramdown::Utils::Entities.entity(el.value.to_s)) 291 | end 292 | 293 | def convert_math(el, indent, opts) 294 | el = Marshal.load(Marshal.dump(el)) # so that the original is not changed 295 | el.options[:attr] ||= {} 296 | el.options[:attr]['class'] ||= '' 297 | el.options[:attr]['class'] += (el.options[:attr]['class'].empty? ? '' : ' ') + 'math' 298 | type = 'span' 299 | type = 'div' if el.options[:category] == :block 300 | "<#{type}#{html_attributes(el)}>#{escape_html(el.value)}#{type == 'div' ? "\n" : ''}" 301 | end 302 | 303 | def convert_abbreviation(el, indent, opts) 304 | title = @doc.parse_infos[:abbrev_defs][el.value] 305 | title = nil if title.empty? 306 | "#{el.value}" 307 | end 308 | 309 | def convert_root(el, indent, opts) 310 | result = inner(el, indent, opts) 311 | result << footnote_content 312 | if @toc_code 313 | toc_tree = generate_toc_tree(@toc, @toc_code[0], @toc_code[1] || {}) 314 | text = if toc_tree.children.size > 0 315 | convert(toc_tree, 0) 316 | else 317 | '' 318 | end 319 | result.sub!(/#{@toc_code.last}/, text) 320 | end 321 | result 322 | end 323 | 324 | def generate_toc_tree(toc, type, attr) 325 | sections = Element.new(type, nil, {:attr => {'id' => 'markdown-toc'}.merge(attr)}) 326 | stack = [] 327 | toc.each do |level, id, children| 328 | li = Element.new(:li, nil, {:level => level}) 329 | li.children << Element.new(:p, nil, {:transparent => true}) 330 | a = Element.new(:a, nil, {:attr => {:href => "##{id}"}}) 331 | a.children += children 332 | li.children.last.children << a 333 | li.children << Element.new(type) 334 | 335 | success = false 336 | while !success 337 | if stack.empty? 338 | sections.children << li 339 | stack << li 340 | success = true 341 | elsif stack.last.options[:level] < li.options[:level] 342 | stack.last.children.last.children << li 343 | stack << li 344 | success = true 345 | else 346 | item = stack.pop 347 | item.children.pop unless item.children.last.children.size > 0 348 | end 349 | end 350 | end 351 | while !stack.empty? 352 | item = stack.pop 353 | item.children.pop unless item.children.last.children.size > 0 354 | end 355 | sections 356 | end 357 | 358 | # Helper method for obfuscating the +text+ by using HTML entities. 359 | def obfuscate(text) 360 | result = "" 361 | text.each_byte do |b| 362 | result += (b > 128 ? b.chr : "&#%03d;" % b) 363 | end 364 | result.force_encoding(text.encoding) if RUBY_VERSION >= '1.9' 365 | result 366 | end 367 | 368 | # Return a HTML list with the footnote content for the used footnotes. 369 | def footnote_content 370 | ol = Element.new(:ol) 371 | ol.options[:attr] = {'start' => @footnote_start} if @footnote_start != 1 372 | @footnotes.each do |name, data| 373 | li = Element.new(:li, nil, {:attr => {:id => "fn:#{name}"}, :first_is_block => true}) 374 | li.children = Marshal.load(Marshal.dump(data[:content].children)) #TODO: probably remove this!!!! 375 | ol.children << li 376 | 377 | ref = Element.new(:raw, "") 378 | if li.children.last.type == :p 379 | para = li.children.last 380 | else 381 | li.children << (para = Element.new(:p)) 382 | end 383 | para.children << ref 384 | end 385 | (ol.children.empty? ? '' : "
\n#{convert(ol, 2)}
\n") 386 | end 387 | 388 | end 389 | 390 | end 391 | end 392 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/converter/kramdown.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'rexml/parsers/baseparser' 24 | 25 | module Kramdown 26 | 27 | module Converter 28 | 29 | # Converts a Kramdown::Document to the kramdown format. 30 | class Kramdown < Base 31 | 32 | # :stopdoc: 33 | 34 | include ::Kramdown::Utils::HTML 35 | 36 | def initialize(doc) 37 | super 38 | @linkrefs = [] 39 | @footnotes = [] 40 | @abbrevs = [] 41 | @stack = [] 42 | end 43 | 44 | def convert(el, opts = {}) 45 | res = send("convert_#{el.type}", el, opts) 46 | if el.type != :html_element && el.type != :li && el.type != :dd && (ial = ial_for_element(el)) 47 | res << ial 48 | res << "\n\n" if el.options[:category] == :block 49 | end 50 | res 51 | end 52 | 53 | def inner(el, opts = {}) 54 | @stack.push([el, opts]) 55 | result = '' 56 | el.children.each_with_index do |inner_el, index| 57 | options = opts.dup 58 | #p [index, inner_el] 59 | options[:index] = index 60 | options[:prev] = (index == 0 ? nil : el.children[index-1]) 61 | options[:next] = (index == el.children.length - 1 ? nil : el.children[index+1]) 62 | result << convert(inner_el, options) 63 | end 64 | @stack.pop 65 | result 66 | end 67 | 68 | def convert_blank(el, opts) 69 | "\n" 70 | end 71 | 72 | ESCAPED_CHAR_RE = /(\$\$|[\\*_`\[\]\{\}"'])|^[ ]{0,3}(:)/ 73 | 74 | def convert_text(el, opts) 75 | if opts[:raw_text] 76 | el.value 77 | else 78 | nl = (el.value =~ /\n$/) 79 | el.value.gsub(/\s+/, ' ').gsub(ESCAPED_CHAR_RE) { "\\#{$1 || $2}" } + (nl ? "\n" : '') 80 | end 81 | end 82 | 83 | def convert_p(el, opts) 84 | res = inner(el, opts).strip.gsub(/\A(?:([#|])|(\d+)\.|([+-]\s))/) do 85 | $1 || $3 ? "\\#{$1 || $3}" : "#{$2}\\." 86 | end + "\n" 87 | if opts[:next] && opts[:next].type == :p && !ial_for_element(el) 88 | res += "\n" 89 | end 90 | res 91 | end 92 | 93 | CODEBLOCK_PREV_EL = [:ul, :ol, :dl, :codeblock] 94 | 95 | def convert_codeblock(el, opts) 96 | res = '' 97 | res << "^\n" if opts[:prev] && ((CODEBLOCK_PREV_EL.include?(opts[:prev].type) && !ial_for_element(opts[:prev])) || 98 | (opts[:prev].type == :blank && 99 | opts[:index]-2 >= 0 && 100 | (tmp = @stack.last.first.children[opts[:index]-2]) && 101 | CODEBLOCK_PREV_EL.include?(tmp.type) && !ial_for_element(tmp))) 102 | res << el.value.split(/\n/).map {|l| l.empty? ? " " : " #{l}"}.join("\n") + "\n" 103 | end 104 | 105 | def convert_blockquote(el, opts) 106 | res = '' 107 | res << "\n" if opts[:prev] && opts[:prev].type == :blockquote 108 | res << inner(el, opts).chomp.split(/\n/).map {|l| "> #{l}"}.join("\n") << "\n" 109 | end 110 | 111 | def convert_header(el, opts) 112 | res = '' 113 | res << "\n" if opts[:prev] && opts[:prev].type != :blank 114 | res << "#{'#' * el.options[:level]} #{inner(el, opts)}" 115 | res << " {##{el.options[:attr]['id']}}" if el.options[:attr] && el.options[:attr]['id'] 116 | res << "\n" if opts[:next] && opts[:next].type != :blank 117 | res << "\n" 118 | end 119 | 120 | def convert_hr(el, opts) 121 | "* * *\n" 122 | end 123 | 124 | def convert_ul(el, opts) 125 | res = '' 126 | res << "\n" if opts[:prev] && (opts[:prev].type == :p && !opts[:prev].options[:transparent]) 127 | res << "^\n" if opts[:prev] && ((opts[:prev].type == el.type && !ial_for_element(opts[:prev])) || 128 | (opts[:prev].type == :blank && opts[:index]-2 >= 0 && 129 | (tmp = @stack.last.first.children[opts[:index]-2]) && 130 | tmp.type == el.type && !ial_for_element(tmp))) 131 | res + inner(el, opts).sub(/\n+\Z/, "\n") 132 | end 133 | alias :convert_ol :convert_ul 134 | alias :convert_dl :convert_ul 135 | 136 | def convert_li(el, opts) 137 | sym, width = if @stack.last.first.type == :ul 138 | ['* ', el.children.first.type == :codeblock ? 4 : 2] 139 | else 140 | ["#{opts[:index] + 1}.".ljust(4), 4] 141 | end 142 | if ial = ial_for_element(el) 143 | sym += ial + " " 144 | end 145 | 146 | first, *last = inner(el, opts).chomp.split(/\n/) 147 | last = last.map {|l| " "*width + l}.join("\n") 148 | last = last.empty? ? "\n" : "\n#{last}\n" 149 | if el.children.first.type == :p && !el.children.first.options[:transparent] 150 | res = "#{sym}#{first}\n#{last}" 151 | res << "^\n" if el.children.size == 1 && @stack.last.first.children.last == el && 152 | (@stack.last.first.children.any? {|c| c.children.first.type != :p} || @stack.last.first.children.size == 1) 153 | res 154 | elsif el.children.first.type == :codeblock 155 | "#{sym}\n #{first}#{last}" 156 | else 157 | "#{sym}#{first}#{last}" 158 | end 159 | end 160 | 161 | def convert_dd(el, opts) 162 | sym, width = ": ", (el.children.first.type == :codeblock ? 4 : 2) 163 | if ial = ial_for_element(el) 164 | sym += ial + " " 165 | end 166 | 167 | first, *last = inner(el, opts).chomp.split(/\n/) 168 | last = last.map {|l| " "*width + l}.join("\n") 169 | text = first + (last.empty? ? '' : "\n" + last) 170 | if el.children.first.type == :p && !el.children.first.options[:transparent] 171 | "\n#{sym}#{text}\n" 172 | elsif el.children.first.type == :codeblock 173 | "#{sym}\n #{text}\n" 174 | else 175 | "#{sym}#{text}\n" 176 | end 177 | end 178 | 179 | def convert_dt(el, opts) 180 | res = '' 181 | res << inner(el, opts) << "\n" 182 | end 183 | 184 | HTML_TAGS_WITH_BODY=['div', 'script'] 185 | 186 | def convert_html_element(el, opts) 187 | markdown_attr = el.options[:category] == :block && el.children.any? do |c| 188 | c.type != :html_element && (c.type != :p || !c.options[:transparent]) && c.options[:category] == :block 189 | end 190 | opts[:force_raw_text] = true if %w{script pre code}.include?(el.value) 191 | opts[:raw_text] = opts[:force_raw_text] || opts[:block_raw_text] || (el.options[:category] != :span && !markdown_attr) 192 | opts[:block_raw_text] = true if el.options[:category] == :block && opts[:raw_text] 193 | res = inner(el, opts) 194 | if el.options[:category] == :span 195 | "<#{el.value}#{html_attributes(el)}" << (!res.empty? ? ">#{res}" : " />") 196 | else 197 | output = '' 198 | output << "<#{el.value}#{html_attributes(el)}" 199 | output << " markdown=\"1\"" if markdown_attr 200 | if !res.empty? && el.options[:parse_type] != :block 201 | output << ">#{res}" 202 | elsif !res.empty? 203 | output << ">\n#{res}" << "" 204 | elsif HTML_TAGS_WITH_BODY.include?(el.value) 205 | output << ">" 206 | else 207 | output << " />" 208 | end 209 | output << "\n" if el.options[:outer_element] || !el.options[:parent_is_raw] 210 | output 211 | end 212 | end 213 | 214 | def convert_xml_comment(el, opts) 215 | if el.options[:category] == :block && !el.options[:parent_is_raw] 216 | el.value + "\n" 217 | else 218 | el.value 219 | end 220 | end 221 | alias :convert_xml_pi :convert_xml_comment 222 | alias :convert_html_doctype :convert_xml_comment 223 | 224 | def convert_table(el, opts) 225 | opts[:alignment] = el.options[:alignment] 226 | inner(el, opts) 227 | end 228 | 229 | def convert_thead(el, opts) 230 | rows = inner(el, opts) 231 | if opts[:alignment].all? {|a| a == :default} 232 | "#{rows}|" + "-"*10 + "\n" 233 | else 234 | "#{rows}| " + opts[:alignment].map do |a| 235 | case a 236 | when :left then ":-" 237 | when :right then "-:" 238 | when :center then ":-:" 239 | when :default then "-" 240 | end 241 | end.join(' ') + "\n" 242 | end 243 | end 244 | 245 | def convert_tbody(el, opts) 246 | res = '' 247 | res << inner(el, opts) 248 | res << '|' << '-'*10 << "\n" if opts[:next] && opts[:next].type == :tbody 249 | res 250 | end 251 | 252 | def convert_tfoot(el, opts) 253 | "|" + "="*10 + "\n#{inner(el, opts)}" 254 | end 255 | 256 | def convert_tr(el, opts) 257 | "| " + el.children.map {|c| convert(c, opts)}.join(" | ") + " |\n" 258 | end 259 | 260 | def convert_td(el, opts) 261 | inner(el, opts).gsub(/\|/, '\\|') 262 | end 263 | alias :convert_th :convert_td 264 | 265 | def convert_comment(el, opts) 266 | if el.options[:category] == :block 267 | "{::comment}\n#{el.value}\n{:/}\n" 268 | else 269 | "{::comment}#{el.value}{:/}" 270 | end 271 | end 272 | 273 | def convert_br(el, opts) 274 | " \n" 275 | end 276 | 277 | def convert_a(el, opts) 278 | if el.options[:attr]['href'].empty? 279 | "[#{inner(el, opts)}]()" 280 | else 281 | @linkrefs << el 282 | "[#{inner(el, opts)}][#{@linkrefs.size}]" 283 | end 284 | end 285 | 286 | def convert_img(el, opts) 287 | title = (el.options[:attr]['title'] ? ' "' + el.options[:attr]['title'].gsub(/"/, """) + '"' : '') 288 | "![#{el.options[:attr]['alt']}](<#{el.options[:attr]['src']}>#{title})" 289 | end 290 | 291 | def convert_codespan(el, opts) 292 | delim = (el.value.scan(/`+/).max || '') + '`' 293 | "#{delim}#{' ' if delim.size > 1}#{el.value}#{' ' if delim.size > 1}#{delim}" 294 | end 295 | 296 | def convert_footnote(el, opts) 297 | @footnotes << [el.options[:name], @doc.parse_infos[:footnotes][el.options[:name]]] 298 | "[^#{el.options[:name]}]" 299 | end 300 | 301 | def convert_raw(el, opts) 302 | if @stack.last.first.type == :html_element 303 | el.value 304 | elsif el.options[:category] == :block 305 | "{::nomarkdown}\n#{el.value}\n{:/}\n" 306 | else 307 | "{::nomarkdown}#{el.value}{:/}" 308 | end 309 | end 310 | 311 | def convert_em(el, opts) 312 | "*#{inner(el, opts)}*" 313 | end 314 | 315 | def convert_strong(el, opts) 316 | "**#{inner(el, opts)}**" 317 | end 318 | 319 | def convert_entity(el, opts) 320 | entity_to_str(el.value) 321 | end 322 | 323 | TYPOGRAPHIC_SYMS = { 324 | :mdash => '---', :ndash => '--', :hellip => '...', 325 | :laquo_space => '<< ', :raquo_space => ' >>', 326 | :laquo => '<<', :raquo => '>>' 327 | } 328 | def convert_typographic_sym(el, opts) 329 | TYPOGRAPHIC_SYMS[el.value] 330 | end 331 | 332 | def convert_smart_quote(el, opts) 333 | el.value.to_s =~ /[rl]dquo/ ? "\"" : "'" 334 | end 335 | 336 | def convert_math(el, opts) 337 | (@stack.last.first.type == :p && opts[:prev].nil? ? "\\" : '') + "$$#{el.value}$$" + (el.options[:category] == :block ? "\n" : '') 338 | end 339 | 340 | def convert_abbreviation(el, opts) 341 | el.value 342 | end 343 | 344 | def convert_root(el, opts) 345 | res = inner(el, opts) 346 | res << create_link_defs 347 | res << create_footnote_defs 348 | res << create_abbrev_defs 349 | res 350 | end 351 | 352 | def create_link_defs 353 | res = '' 354 | res << "\n\n" if @linkrefs.size > 0 355 | @linkrefs.each_with_index do |el, i| 356 | link = (el.type == :a ? el.options[:attr]['href'] : el.options[:attr]['src']) 357 | link = "<#{link}>" if link =~ / / 358 | title = el.options[:attr]['title'] 359 | res << "[#{i+1}]: #{link} #{title ? '"' + title.gsub(/"/, """) + '"' : ''}\n" 360 | end 361 | res 362 | end 363 | 364 | def create_footnote_defs 365 | res = '' 366 | res = "\n" if @footnotes.size > 0 367 | @footnotes.each do |name, data| 368 | res << "\n[^#{name}]:\n" 369 | res << inner(data[:content]).chomp.split(/\n/).map {|l| " #{l}"}.join("\n") 370 | end 371 | res 372 | end 373 | 374 | def create_abbrev_defs 375 | return '' unless @doc.parse_infos[:abbrev_defs] 376 | res = '' 377 | @doc.parse_infos[:abbrev_defs].each do |name, text| 378 | res << "*[#{name}]: #{text}\n" 379 | end 380 | res 381 | end 382 | 383 | # Return the IAL containing the attributes of the element +el+. 384 | def ial_for_element(el) 385 | res = (el.options[:attr] || {}).map do |k,v| 386 | next if [:img, :a].include?(el.type) && ['href', 'src', 'alt', 'title'].include?(k) 387 | next if el.type == :header && k == 'id' 388 | v.nil? ? '' : " #{k}=\"#{v.to_s}\"" 389 | end.compact.sort.join('') 390 | res = "toc" + (res.strip.empty? ? '' : " #{res}") if (el.type == :ul || el.type == :ol) && 391 | (el.options[:ial][:refs].include?('toc') rescue nil) 392 | res.strip.empty? ? nil : "{:#{res}}" 393 | end 394 | 395 | end 396 | 397 | end 398 | end 399 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/converter/latex.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'set' 24 | 25 | module Kramdown 26 | 27 | module Converter 28 | 29 | # Converts a Kramdown::Document to LaTeX. This converter uses ideas from other Markdown-to-LaTeX 30 | # converters like Pandoc and Maruku. 31 | class Latex < Base 32 | 33 | # :stopdoc: 34 | 35 | # Initialize the LaTeX converter with the given Kramdown document +doc+. 36 | def initialize(doc) 37 | super 38 | #TODO: set the footnote counter at the beginning of the document 39 | @doc.options[:footnote_nr] 40 | @doc.conversion_infos[:packages] = Set.new 41 | end 42 | 43 | def convert(el, opts = {}) 44 | send("convert_#{el.type}", el, opts) 45 | end 46 | 47 | def inner(el, opts) 48 | result = '' 49 | el.children.each do |inner_el| 50 | result << send("convert_#{inner_el.type}", inner_el, opts) 51 | end 52 | result 53 | end 54 | 55 | def convert_root(el, opts) 56 | inner(el, opts) 57 | end 58 | 59 | def convert_blank(el, opts) 60 | "" 61 | end 62 | 63 | def convert_text(el, opts) 64 | escape(el.value) 65 | end 66 | 67 | def convert_p(el, opts) 68 | "#{inner(el, opts)}\n\n" 69 | end 70 | 71 | def convert_codeblock(el, opts) 72 | show_whitespace = el.options[:attr] && el.options[:attr]['class'].to_s =~ /\bshow-whitespaces\b/ 73 | lang = el.options[:attr] && el.options[:attr]['lang'] 74 | if show_whitespace || lang 75 | result = "\\lstset{showspaces=%s,showtabs=%s}\n" % (show_whitespace ? ['true', 'true'] : ['false', 'false']) 76 | result += "\\lstset{language=#{lang}}\n" if lang 77 | result += "\\lstset{basicstyle=\\ttfamily\\footnotesize}\\lstset{columns=fixed,frame=tlbr}\n" 78 | "#{result}\\begin{lstlisting}#{attribute_list(el)}\n#{el.value}\n\\end{lstlisting}\n" 79 | else 80 | "\\begin{verbatim}#{el.value}\\end{verbatim}\n" 81 | end 82 | end 83 | 84 | def latex_environment(type, el, text) 85 | "\\begin{#{type}}#{attribute_list(el)}\n#{text}\n\\end{#{type}}\n" 86 | end 87 | 88 | def convert_blockquote(el, opts) 89 | latex_environment('quote', el, inner(el, opts)) 90 | end 91 | 92 | HEADER_TYPES = { 93 | 1 => 'section', 94 | 2 => 'subsection', 95 | 3 => 'subsubsection', 96 | 4 => 'paragraph', 97 | 5 => 'subparagraph', 98 | 6 => 'subparagraph' 99 | } 100 | def convert_header(el, opts) 101 | type = HEADER_TYPES[el.options[:level]] 102 | if ((el.options[:attr] && (id = el.options[:attr]['id'])) || 103 | (@doc.options[:auto_ids] && (id = generate_id(el.options[:raw_text])))) && 104 | (@doc.options[:toc_depth] <= 0 || el.options[:level] <= @doc.options[:toc_depth]) 105 | "\\hypertarget{#{id}}{}\\#{type}{#{inner(el, opts)}}\\label{#{id}}\n\n" 106 | else 107 | "\\#{type}*{#{inner(el, opts)}}\n\n" 108 | end 109 | end 110 | 111 | def convert_hr(el, opts) 112 | "\\begin{center}#{attribute_list(el)}\n\\rule{3in}{0.4pt}\n\\end{center}\n" 113 | end 114 | 115 | def convert_ul(el, opts) 116 | if !@doc.conversion_infos[:has_toc] && (el.options[:ial][:refs].include?('toc') rescue nil) 117 | @doc.conversion_infos[:has_toc] = true 118 | '\tableofcontents' 119 | else 120 | latex_environment(el.type == :ul ? 'itemize' : 'enumerate', el, inner(el, opts)) 121 | end 122 | end 123 | alias :convert_ol :convert_ul 124 | 125 | def convert_dl(el, opts) 126 | latex_environment('description', el, inner(el, opts)) 127 | end 128 | 129 | def convert_li(el, opts) 130 | "\\item #{inner(el, opts).sub(/\n+\Z/, '')}\n" 131 | end 132 | 133 | def convert_dt(el, opts) 134 | "\\item[#{inner(el, opts)}] " 135 | end 136 | 137 | def convert_dd(el, opts) 138 | "#{inner(el, opts)}\n\n" 139 | end 140 | 141 | def convert_html_element(el, opts) 142 | if el.value == 'i' 143 | "\\emph{#{inner(el, opts)}}" 144 | elsif el.value == 'b' 145 | "\\emph{#{inner(el, opts)}}" 146 | else 147 | @doc.warnings << "Can't convert HTML element" 148 | '' 149 | end 150 | end 151 | 152 | def convert_xml_comment(el, opts) 153 | el.value.split(/\n/).map {|l| "% #{l}"}.join("\n") + "\n" 154 | end 155 | 156 | def convert_xml_pi(el, opts) 157 | @doc.warnings << "Can't convert XML PI/HTML document type" 158 | '' 159 | end 160 | alias :convert_html_doctype :convert_xml_pi 161 | 162 | TABLE_ALIGNMENT_CHAR = {:default => 'l', :left => 'l', :center => 'c', :right => 'r'} 163 | 164 | def convert_table(el, opts) 165 | align = el.options[:alignment].map {|a| TABLE_ALIGNMENT_CHAR[a]}.join('|') 166 | "\\begin{tabular}{|#{align}|}#{attribute_list(el)}\n\\hline\n#{inner(el, opts)}\\hline\n\\end{tabular}\n\n" 167 | end 168 | 169 | def convert_thead(el, opts) 170 | "#{inner(el, opts)}\\hline\n" 171 | end 172 | 173 | def convert_tbody(el, opts) 174 | inner(el, opts) 175 | end 176 | 177 | def convert_tfoot(el, opts) 178 | "\\hline \\hline \n#{inner(el, opts)}" 179 | end 180 | 181 | def convert_tr(el, opts) 182 | el.children.map {|c| send("convert_#{c.type}", c, opts)}.join(' & ') + "\\\\\n" 183 | end 184 | 185 | def convert_td(el, opts) 186 | inner(el, opts) 187 | end 188 | alias :convert_th :convert_td 189 | 190 | def convert_comment(el, opts) 191 | el.value.split(/\n/).map {|l| "% #{l}"}.join("\n") + "\n" 192 | end 193 | 194 | def convert_br(el, opts) 195 | "\\newline\n" 196 | end 197 | 198 | def convert_a(el, opts) 199 | url = el.options[:attr]['href'] 200 | if url =~ /^#/ 201 | "\\hyperlink{#{url[1..-1]}}{#{inner(el, opts)}}" 202 | else 203 | "\\href{#{url}}{#{inner(el, opts)}}" 204 | end 205 | end 206 | 207 | def convert_img(el, opts) 208 | if el.options[:attr]['src'] =~ /^(https?|ftps?):\/\// 209 | @doc.warnings << "Cannot include non-local image" 210 | '' 211 | elsif !el.options[:attr]['src'].empty? 212 | @doc.conversion_infos[:packages] << 'graphicx' 213 | "\\includegraphics{#{el.options[:attr]['src']}}" 214 | else 215 | @doc.warnings << "Cannot include image with empty path" 216 | '' 217 | end 218 | end 219 | 220 | def convert_codespan(el, opts) 221 | "{\\tt #{escape(el.value)}}" 222 | end 223 | 224 | def convert_footnote(el, opts) 225 | @doc.conversion_infos[:packages] << 'fancyvrb' 226 | "\\footnote{#{inner(@doc.parse_infos[:footnotes][el.options[:name]][:content], opts)}}" 227 | end 228 | 229 | def convert_raw(el, opts) 230 | escape(el.value) 231 | end 232 | 233 | def convert_em(el, opts) 234 | "\\emph{#{inner(el, opts)}}" 235 | end 236 | 237 | def convert_strong(el, opts) 238 | "\\textbf{#{inner(el, opts)}}" 239 | end 240 | 241 | # Inspired by Maruku: entity conversion table based on the one from htmltolatex 242 | # (http://sourceforge.net/projects/htmltolatex/), with some small adjustments/additions 243 | ENTITY_CONV_TABLE = { 244 | 913 => ['$A$'], 245 | 914 => ['$B$'], 246 | 915 => ['$\Gamma$'], 247 | 916 => ['$\Delta$'], 248 | 917 => ['$E$'], 249 | 918 => ['$Z$'], 250 | 919 => ['$H$'], 251 | 920 => ['$\Theta$'], 252 | 921 => ['$I$'], 253 | 922 => ['$K$'], 254 | 923 => ['$\Lambda$'], 255 | 924 => ['$M$'], 256 | 925 => ['$N$'], 257 | 926 => ['$\Xi$'], 258 | 927 => ['$O$'], 259 | 928 => ['$\Pi$'], 260 | 929 => ['$P$'], 261 | 931 => ['$\Sigma$'], 262 | 932 => ['$T$'], 263 | 933 => ['$Y$'], 264 | 934 => ['$\Phi$'], 265 | 935 => ['$X$'], 266 | 936 => ['$\Psi$'], 267 | 937 => ['$\Omega$'], 268 | 945 => ['$\alpha$'], 269 | 946 => ['$\beta$'], 270 | 947 => ['$\gamma$'], 271 | 948 => ['$\delta$'], 272 | 949 => ['$\epsilon$'], 273 | 950 => ['$\zeta$'], 274 | 951 => ['$\eta$'], 275 | 952 => ['$\theta$'], 276 | 953 => ['$\iota$'], 277 | 954 => ['$\kappa$'], 278 | 955 => ['$\lambda$'], 279 | 956 => ['$\mu$'], 280 | 957 => ['$\nu$'], 281 | 958 => ['$\xi$'], 282 | 959 => ['$o$'], 283 | 960 => ['$\pi$'], 284 | 961 => ['$\rho$'], 285 | 963 => ['$\sigma$'], 286 | 964 => ['$\tau$'], 287 | 965 => ['$\upsilon$'], 288 | 966 => ['$\phi$'], 289 | 967 => ['$\chi$'], 290 | 968 => ['$\psi$'], 291 | 969 => ['$\omega$'], 292 | 962 => ['$\varsigma$'], 293 | 977 => ['$\vartheta$'], 294 | 982 => ['$\varpi$'], 295 | 8230 => ['\ldots'], 296 | 8242 => ['$\prime$'], 297 | 8254 => ['-'], 298 | 8260 => ['/'], 299 | 8472 => ['$\wp$'], 300 | 8465 => ['$\Im$'], 301 | 8476 => ['$\Re$'], 302 | 8501 => ['$\aleph$'], 303 | 8226 => ['$\bullet$'], 304 | 8482 => ['$^{\rm TM}$'], 305 | 8592 => ['$\leftarrow$'], 306 | 8594 => ['$\rightarrow$'], 307 | 8593 => ['$\uparrow$'], 308 | 8595 => ['$\downarrow$'], 309 | 8596 => ['$\leftrightarrow$'], 310 | 8629 => ['$\hookleftarrow$'], 311 | 8657 => ['$\Uparrow$'], 312 | 8659 => ['$\Downarrow$'], 313 | 8656 => ['$\Leftarrow$'], 314 | 8658 => ['$\Rightarrow$'], 315 | 8660 => ['$\Leftrightarrow$'], 316 | 8704 => ['$\forall$'], 317 | 8706 => ['$\partial$'], 318 | 8707 => ['$\exists$'], 319 | 8709 => ['$\emptyset$'], 320 | 8711 => ['$\nabla$'], 321 | 8712 => ['$\in$'], 322 | 8715 => ['$\ni$'], 323 | 8713 => ['$\notin$'], 324 | 8721 => ['$\sum$'], 325 | 8719 => ['$\prod$'], 326 | 8722 => ['$-$'], 327 | 8727 => ['$\ast$'], 328 | 8730 => ['$\surd$'], 329 | 8733 => ['$\propto$'], 330 | 8734 => ['$\infty$'], 331 | 8736 => ['$\angle$'], 332 | 8743 => ['$\wedge$'], 333 | 8744 => ['$\vee$'], 334 | 8745 => ['$\cup$'], 335 | 8746 => ['$\cap$'], 336 | 8747 => ['$\int$'], 337 | 8756 => ['$\therefore$', 'amssymb'], 338 | 8764 => ['$\sim$'], 339 | 8776 => ['$\approx$'], 340 | 8773 => ['$\cong$'], 341 | 8800 => ['$\neq$'], 342 | 8801 => ['$\equiv$'], 343 | 8804 => ['$\leq$'], 344 | 8805 => ['$\geq$'], 345 | 8834 => ['$\subset$'], 346 | 8835 => ['$\supset$'], 347 | 8838 => ['$\subseteq$'], 348 | 8839 => ['$\supseteq$'], 349 | 8836 => ['$\nsubset$', 'amssymb'], 350 | 8853 => ['$\oplus$'], 351 | 8855 => ['$\otimes$'], 352 | 8869 => ['$\perp$'], 353 | 8901 => ['$\cdot$'], 354 | 8968 => ['$\rceil$'], 355 | 8969 => ['$\lceil$'], 356 | 8970 => ['$\lfloor$'], 357 | 8971 => ['$\rfloor$'], 358 | 9001 => ['$\rangle$'], 359 | 9002 => ['$\langle$'], 360 | 9674 => ['$\lozenge$', 'amssymb'], 361 | 9824 => ['$\spadesuit$'], 362 | 9827 => ['$\clubsuit$'], 363 | 9829 => ['$\heartsuit$'], 364 | 9830 => ['$\diamondsuit$'], 365 | 38 => ['\&'], 366 | 34 => ['"'], 367 | 39 => ['\''], 368 | 169 => ['\copyright'], 369 | 60 => ['\textless{}'], 370 | 62 => ['\textgreater{}'], 371 | 338 => ['\OE'], 372 | 339 => ['\oe'], 373 | 352 => ['\v{S}'], 374 | 353 => ['\v{s}'], 375 | 376 => ['\"Y'], 376 | 710 => ['\textasciicircum'], 377 | 732 => ['\textasciitilde'], 378 | 8211 => ['--'], 379 | 8212 => ['---'], 380 | 8216 => ['`'], 381 | 8217 => ['\''], 382 | 8220 => ['``'], 383 | 8221 => ['\'\''], 384 | 8224 => ['\dag'], 385 | 8225 => ['\ddag'], 386 | 8240 => ['\permil', 'wasysym'], 387 | 8364 => ['\euro', 'eurosym'], 388 | 8249 => ['\guilsinglleft'], 389 | 8250 => ['\guilsinglright'], 390 | 8218 => ['\quotesinglbase', 'mathcomp'], 391 | 8222 => ['\quotedblbase', 'mathcomp'], 392 | 402 => ['\textflorin', 'mathcomp'], 393 | 381 => ['\v{Z}'], 394 | 382 => ['\v{z}'], 395 | 160 => ['\nolinebreak'], 396 | 161 => ['\textexclamdown'], 397 | 163 => ['\pounds'], 398 | 164 => ['\currency', 'wasysym'], 399 | 165 => ['\textyen', 'textcomp'], 400 | 166 => ['\brokenvert', 'wasysym'], 401 | 167 => ['\S'], 402 | 171 => ['\guillemotleft'], 403 | 187 => ['\guillemotright'], 404 | 174 => ['\textregistered'], 405 | 170 => ['\textordfeminine'], 406 | 172 => ['$\neg$'], 407 | 176 => ['$\degree$', 'mathabx'], 408 | 177 => ['$\pm$'], 409 | 180 => ['\''], 410 | 181 => ['$\mu$'], 411 | 182 => ['\P'], 412 | 183 => ['$\cdot$'], 413 | 186 => ['\textordmasculine'], 414 | 162 => ['\cent', 'wasysym'], 415 | 185 => ['$^1$'], 416 | 178 => ['$^2$'], 417 | 179 => ['$^3$'], 418 | 189 => ['$\frac{1}{2}$'], 419 | 188 => ['$\frac{1}{4}$'], 420 | 190 => ['$\frac{3}{4}'], 421 | 192 => ['\`A'], 422 | 193 => ['\\\'A'], 423 | 194 => ['\^A'], 424 | 195 => ['\~A'], 425 | 196 => ['\"A'], 426 | 197 => ['\AA'], 427 | 198 => ['\AE'], 428 | 199 => ['\cC'], 429 | 200 => ['\`E'], 430 | 201 => ['\\\'E'], 431 | 202 => ['\^E'], 432 | 203 => ['\"E'], 433 | 204 => ['\`I'], 434 | 205 => ['\\\'I'], 435 | 206 => ['\^I'], 436 | 207 => ['\"I'], 437 | 208 => ['$\eth$', 'amssymb'], 438 | 209 => ['\~N'], 439 | 210 => ['\`O'], 440 | 211 => ['\\\'O'], 441 | 212 => ['\^O'], 442 | 213 => ['\~O'], 443 | 214 => ['\"O'], 444 | 215 => ['$\times$'], 445 | 216 => ['\O'], 446 | 217 => ['\`U'], 447 | 218 => ['\\\'U'], 448 | 219 => ['\^U'], 449 | 220 => ['\"U'], 450 | 221 => ['\\\'Y'], 451 | 222 => ['\Thorn', 'wasysym'], 452 | 223 => ['\ss'], 453 | 224 => ['\`a'], 454 | 225 => ['\\\'a'], 455 | 226 => ['\^a'], 456 | 227 => ['\~a'], 457 | 228 => ['\"a'], 458 | 229 => ['\aa'], 459 | 230 => ['\ae'], 460 | 231 => ['\cc'], 461 | 232 => ['\`e'], 462 | 233 => ['\\\'e'], 463 | 234 => ['\^e'], 464 | 235 => ['\"e'], 465 | 236 => ['\`i'], 466 | 237 => ['\\\'i'], 467 | 238 => ['\^i'], 468 | 239 => ['\"i'], 469 | 240 => ['$\eth$'], 470 | 241 => ['\~n'], 471 | 242 => ['\`o'], 472 | 243 => ['\\\'o'], 473 | 244 => ['\^o'], 474 | 245 => ['\~o'], 475 | 246 => ['\"o'], 476 | 247 => ['$\divide$'], 477 | 248 => ['\o'], 478 | 249 => ['\`u'], 479 | 250 => ['\\\'u'], 480 | 251 => ['\^u'], 481 | 252 => ['\"u'], 482 | 253 => ['\\\'y'], 483 | 254 => ['\thorn', 'wasysym'], 484 | 255 => ['\"y'], 485 | } 486 | ENTITY_CONV_TABLE.each {|k,v| ENTITY_CONV_TABLE[k] = v.unshift(v.shift + '{}')} 487 | 488 | def convert_entity(el, opts) 489 | text, package = ENTITY_CONV_TABLE[el.value.code_point] 490 | if text 491 | @doc.conversion_infos[:packages] << package if package 492 | text 493 | else 494 | @doc.warnings << "Couldn't find entity in substitution table!" 495 | '' 496 | end 497 | end 498 | 499 | TYPOGRAPHIC_SYMS = { 500 | :mdash => '---', :ndash => '--', :hellip => '\ldots{}', 501 | :laquo_space => '\guillemotleft{}~', :raquo_space => '~\guillemotright{}', 502 | :laquo => '\guillemotleft{}', :raquo => '\guillemotright{}' 503 | } 504 | def convert_typographic_sym(el, opts) 505 | TYPOGRAPHIC_SYMS[el.value] 506 | end 507 | 508 | SMART_QUOTE_SYMS = {:lsquo => '`', :rsquo => '\'', :ldquo => '``', :rdquo => '\'\''} 509 | def convert_smart_quote(el, opts) 510 | SMART_QUOTE_SYMS[el.value] 511 | end 512 | 513 | def convert_math(el, opts) 514 | @doc.conversion_infos[:packages] += %w[amssymb amsmath amsthm amsfonts] 515 | if el.options[:category] == :block 516 | if el.value =~ /\A\s*\\begin\{/ 517 | el.value 518 | else 519 | latex_environment('displaymath', el, el.value) 520 | end 521 | else 522 | "$#{el.value}$" 523 | end 524 | end 525 | 526 | def convert_abbreviation(el, opts) 527 | el.value 528 | end 529 | 530 | ESCAPE_MAP = { 531 | "^" => "\\^{}", 532 | "\\" => "\\textbackslash{}", 533 | "~" => "\\ensuremath{\\sim}", 534 | "|" => "\\textbar{}", 535 | "<" => "\\textless{}", 536 | ">" => "\\textgreater{}" 537 | }.merge(Hash[*("{}$%&_#".scan(/./).map {|c| [c, "\\#{c}"]}.flatten)]) 538 | ESCAPE_RE = Regexp.union(*ESCAPE_MAP.collect {|k,v| k}) 539 | 540 | def escape(str) 541 | str.gsub(ESCAPE_RE) {|m| ESCAPE_MAP[m]} 542 | end 543 | 544 | def attribute_list(el) 545 | attrs = (el.options[:attr] || {}).map {|k,v| v.nil? ? '' : " #{k}=\"#{v.to_s}\""}.compact.sort.join('') 546 | attrs = " % #{attrs}" if !attrs.empty? 547 | attrs 548 | end 549 | 550 | end 551 | 552 | end 553 | end 554 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/document.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'kramdown/compatibility' 24 | 25 | require 'kramdown/version' 26 | require 'kramdown/error' 27 | require 'kramdown/parser' 28 | require 'kramdown/converter' 29 | require 'kramdown/options' 30 | require 'kramdown/utils' 31 | 32 | module Kramdown 33 | 34 | # Return the data directory for kramdown. 35 | def self.data_dir 36 | unless defined?(@@data_dir) 37 | require 'rbconfig' 38 | @@data_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'kramdown')) 39 | @@data_dir = File.expand_path(File.join(Config::CONFIG["datadir"], "kramdown")) if !File.exists?(@@data_dir) 40 | raise "kramdown data directory not found! This is a bug, please report it!" unless File.directory?(@@data_dir) 41 | end 42 | @@data_dir 43 | end 44 | 45 | 46 | # The main interface to kramdown. 47 | # 48 | # This class provides a one-stop-shop for using kramdown to convert text into various output 49 | # formats. Use it like this: 50 | # 51 | # require 'kramdown' 52 | # doc = Kramdown::Document.new('This *is* some kramdown text') 53 | # puts doc.to_html 54 | # 55 | # The #to_html method is a shortcut for using the Converter::Html class. 56 | # 57 | # The second argument to the #new method is an options hash for customizing the behaviour of the 58 | # used parser and the converter. See Document#new for more information! 59 | class Document 60 | 61 | # The element tree of the document. It is immediately available after the #new method has been 62 | # called. 63 | attr_accessor :tree 64 | 65 | # The options hash which holds the options for parsing/converting the Kramdown document. It is 66 | # possible that these values get changed during the parsing phase. 67 | attr_reader :options 68 | 69 | # An array of warning messages. It is filled with warnings during the parsing phase (i.e. in 70 | # #new) and the conversion phase. 71 | attr_reader :warnings 72 | 73 | # Holds needed parse information which is dependent on the used parser, like ALDs, link 74 | # definitions and so on. This information may be used by converters afterwards. 75 | attr_reader :parse_infos 76 | 77 | # Holds conversion information which is dependent on the used converter. A converter clears this 78 | # variable before doing the conversion. 79 | attr_reader :conversion_infos 80 | 81 | 82 | # Create a new Kramdown document from the string +source+ and use the provided +options+. The 83 | # options that can be used are defined in the Options module. 84 | # 85 | # The special options key :input can be used to select the parser that should parse the 86 | # +source+. It has to be the name of a class in the Kramdown::Parser module. For example, to 87 | # select the kramdown parser, one would set the :input key to +Kramdown+. If this key 88 | # is not set, it defaults to +Kramdown+. 89 | # 90 | # The +source+ is immediately parsed by the selected parser so that the document tree is 91 | # immediately available and the output can be generated. 92 | def initialize(source, options = {}) 93 | @options = Options.merge(options) 94 | @warnings = [] 95 | @parse_infos = {} 96 | @parse_infos[:encoding] = source.encoding if RUBY_VERSION >= '1.9' 97 | @conversion_infos = {} 98 | parser = (options[:input] || 'kramdown').to_s 99 | parser = parser[0..0].upcase + parser[1..-1] 100 | if Parser.const_defined?(parser) 101 | @tree = Parser.const_get(parser).parse(source, self) 102 | else 103 | raise Kramdown::Error.new("kramdown has no parser to handle the specified input format: #{options[:input]}") 104 | end 105 | end 106 | 107 | # Check if a method is invoked that begins with +to_+ and if so, try to instantiate a converter 108 | # class (i.e. a class in the Kramdown::Converter module) and use it for converting the document. 109 | # 110 | # For example, +to_html+ would instantiate the Kramdown::Converter::Html class. 111 | def method_missing(id, *attr, &block) 112 | if id.to_s =~ /^to_(\w+)$/ 113 | Converter.const_get($1[0..0].upcase + $1[1..-1]).convert(self) 114 | else 115 | super 116 | end 117 | end 118 | 119 | def inspect #:nodoc: 120 | "" 121 | end 122 | 123 | end 124 | 125 | 126 | # Represents all elements in the parse tree. 127 | # 128 | # kramdown only uses this one class for representing all available elements in a parse tree 129 | # (paragraphs, headers, emphasis, ...). The type of element can be set via the #type accessor. 130 | class Element 131 | 132 | # A symbol representing the element type. For example, :p or :blockquote. 133 | attr_accessor :type 134 | 135 | # The value of the element. The interpretation of this field depends on the type of the element. 136 | # Many elements don't use this field. 137 | attr_accessor :value 138 | 139 | # The options hash for the element. It is used for storing arbitray options as well as the 140 | # following special contents: 141 | # 142 | # - *Attributes* of the element under the :attr key 143 | # - Category of the element, either :block or :span, under the 144 | # :category key. If this key is absent, it can be assumed that the element is in the 145 | # :span category. 146 | attr_accessor :options 147 | 148 | # The child elements of this element. 149 | attr_accessor :children 150 | 151 | 152 | # Create a new Element object of type +type+. The optional parameters +value+ and +options+ can 153 | # also be set in this constructor for convenience. 154 | def initialize(type, value = nil, options = {}) 155 | @type, @value, @options = type, value, options 156 | @children = [] 157 | end 158 | 159 | def inspect #:nodoc: 160 | "" 161 | end 162 | 163 | end 164 | 165 | end 166 | 167 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/error.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | 25 | class Error < RuntimeError; end 26 | 27 | end 28 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/kramdown.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'kramdown/document' 24 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/options.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | 25 | # This module defines all options that are used by parsers and/or converters as well as providing 26 | # methods to deal with the options. 27 | module Options 28 | 29 | # Helper class introducing a boolean type for specifying boolean values (+true+ and +false+) as 30 | # option types. 31 | class Boolean 32 | 33 | # Return +true+ if +other+ is either +true+ or +false+ 34 | def self.===(other) 35 | FalseClass === other || TrueClass === other 36 | end 37 | 38 | end 39 | 40 | # ---------------------------- 41 | # :section: Option definitions 42 | # 43 | # This sections informs describes the methods that can be used on the Options module. 44 | # ---------------------------- 45 | 46 | # Contains the definition of an option. 47 | Definition = Struct.new(:name, :type, :default, :desc) 48 | 49 | # Allowed option types. 50 | ALLOWED_TYPES = [String, Integer, Float, Symbol, Boolean, Array, Object] 51 | 52 | @options = {} 53 | 54 | # Define a new option called +name+ (a Symbol) with the given +type+ (String, Integer, Float, 55 | # Symbol, Boolean, Array, Object), default value +default+ and the description +desc+. 56 | # 57 | # The type 'Object' should only be used if none of the other types suffices because such an 58 | # option will be opaque and cannot be used, for example, by CLI command! 59 | def self.define(name, type, default, desc) 60 | raise ArgumentError, "Option name #{name} is already used" if @options.has_key?(name) 61 | raise ArgumentError, "Invalid option type #{type} specified" if !ALLOWED_TYPES.include?(type) 62 | raise ArgumentError, "Invalid type for default value" if !(type === default) && !default.nil? 63 | @options[name] = Definition.new(name, type, default, desc) 64 | end 65 | 66 | # Return all option definitions. 67 | def self.definitions 68 | @options 69 | end 70 | 71 | # Return +true+ if an option called +name+ is defined. 72 | def self.defined?(name) 73 | @options.has_key?(name) 74 | end 75 | 76 | # Return a Hash with the default values for all options. 77 | def self.defaults 78 | temp = {} 79 | @options.each {|n, o| temp[o.name] = o.default} 80 | temp 81 | end 82 | 83 | # Merge the #defaults Hash with the *parsed* options from the given Hash, i.e. only valid option 84 | # names are considered and their value is run through the #parse method. 85 | def self.merge(hash) 86 | temp = defaults 87 | hash.each do |k,v| 88 | next unless @options.has_key?(k) 89 | temp[k] = parse(k, v) 90 | end 91 | temp 92 | end 93 | 94 | # Parse the given value +data+ as if it was a value for the option +name+ and return the parsed 95 | # value with the correct type. 96 | # 97 | # If +data+ already has the correct type, it is just returned. Otherwise it is converted to a 98 | # String and then to the correct type. 99 | def self.parse(name, data) 100 | raise ArgumentError, "No option named #{name} defined" if !@options.has_key?(name) 101 | return data if @options[name].type === data 102 | data = data.to_s 103 | if @options[name].type == String 104 | data 105 | elsif @options[name].type == Integer 106 | Integer(data) 107 | elsif @options[name].type == Float 108 | Float(data) 109 | elsif @options[name].type == Symbol 110 | (data.strip.empty? ? nil : data.to_sym) 111 | elsif @options[name].type == Boolean 112 | data.downcase.strip != 'false' && !data.empty? 113 | elsif @options[name].type == Array 114 | data.split(/\s+/) 115 | end 116 | end 117 | 118 | # ---------------------------- 119 | # :section: Option Definitions 120 | # 121 | # This sections contains all option definitions that are used by the included 122 | # parsers/converters. 123 | # ---------------------------- 124 | 125 | define(:template, String, '', <hallo` the emphasis tag 195 | would normally be converted to an `:html` element with tag type `:em`. 196 | If `html_to_native` is `true`, then the emphasis would be converted to a 197 | native `:em` element. 198 | 199 | This is useful for converters that cannot deal with HTML elements. 200 | 201 | Default: false 202 | Used by: kramdown parser 203 | EOF 204 | 205 | define(:footnote_nr, Integer, 1, < 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | 25 | # == Parser Module 26 | # 27 | # This module contains all available parsers. Currently, there two parsers: 28 | # 29 | # * Kramdown for parsing documents in kramdown format 30 | # * Html for parsing HTML documents 31 | module Parser 32 | 33 | autoload :Base, 'kramdown/parser/base' 34 | autoload :Kramdown, 'kramdown/parser/kramdown' 35 | autoload :Html, 'kramdown/parser/html' 36 | 37 | end 38 | 39 | end 40 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/base.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | 25 | module Parser 26 | 27 | # == Base class for parsers 28 | # 29 | # This class serves as base class for parsers. It provides common methods that can/should be 30 | # used by all parsers, especially by those using StringScanner for parsing. 31 | # 32 | class Base 33 | 34 | # Initialize the parser with the given Kramdown document +doc+. 35 | def initialize(doc) 36 | @doc = doc 37 | @text_type = :text 38 | end 39 | private_class_method(:new, :allocate) 40 | 41 | # Parse the +source+ string into an element tree, using the information provided by the 42 | # Kramdown document +doc+. 43 | # 44 | # Initializes a new instance of the calling class and then calls the #parse method that must 45 | # be implemented by each subclass. 46 | def self.parse(source, doc) 47 | new(doc).parse(source) 48 | end 49 | 50 | 51 | # Add the given warning +text+ to the warning array of the Kramdown document. 52 | def warning(text) 53 | @doc.warnings << text 54 | #TODO: add position information 55 | end 56 | 57 | # Modify the string +source+ to be usable by the parser. 58 | def adapt_source(source) 59 | source.gsub(/\r\n?/, "\n").chomp + "\n" 60 | end 61 | 62 | # This helper method adds the given +text+ either to the last element in the +tree+ if it is a 63 | # +type+ element or creates a new text element with the given +type+. 64 | def add_text(text, tree = @tree, type = @text_type) 65 | if tree.children.last && tree.children.last.type == type 66 | tree.children.last.value << text 67 | elsif !text.empty? 68 | tree.children << Element.new(type, text) 69 | end 70 | end 71 | 72 | # Extract the part of the StringScanner +srcscan+ backed string specified by the +range+. This 73 | # method also works correctly under Ruby 1.9. 74 | def extract_string(range, strscan) 75 | result = nil 76 | if RUBY_VERSION >= '1.9' 77 | begin 78 | enc = strscan.string.encoding 79 | strscan.string.force_encoding('ASCII-8BIT') 80 | result = strscan.string[range].force_encoding(enc) 81 | ensure 82 | strscan.string.force_encoding(enc) 83 | end 84 | else 85 | result = strscan.string[range] 86 | end 87 | result 88 | end 89 | 90 | end 91 | 92 | end 93 | 94 | end 95 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/html.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'rexml/parsers/baseparser' 24 | require 'strscan' 25 | 26 | module Kramdown 27 | 28 | module Parser 29 | 30 | # Used for parsing a HTML document. 31 | class Html < Base 32 | 33 | # Contains all constants that are used when parsing. 34 | module Constants 35 | #:stopdoc: 36 | # The following regexps are based on the ones used by REXML, with some slight modifications. 37 | HTML_DOCTYPE_RE = //m 38 | HTML_COMMENT_RE = //m 39 | HTML_INSTRUCTION_RE = /<\?(.*?)\?>/m 40 | HTML_ATTRIBUTE_RE = /\s*(#{REXML::Parsers::BaseParser::UNAME_STR})\s*=\s*(["'])(.*?)\2/m 41 | HTML_TAG_RE = /<((?>#{REXML::Parsers::BaseParser::UNAME_STR}))\s*((?>\s+#{REXML::Parsers::BaseParser::UNAME_STR}\s*=\s*(["']).*?\3)*)\s*(\/)?>/m 42 | HTML_TAG_CLOSE_RE = /<\/(#{REXML::Parsers::BaseParser::NAME_STR})\s*>/m 43 | HTML_ENTITY_RE = /&([\w:][\-\w\d\.:]*);|&#(\d+);|&\#x([0-9a-fA-F]+);/ 44 | 45 | 46 | HTML_PARSE_AS_BLOCK = %w{applet button blockquote colgroup dd div dl fieldset form iframe li 47 | map noscript object ol table tbody thead tfoot tr td ul} 48 | HTML_PARSE_AS_SPAN = %w{a abbr acronym address b bdo big cite caption del dfn dt em 49 | h1 h2 h3 h4 h5 h6 i ins kbd label legend optgroup p q rb rbc 50 | rp rt rtc ruby samp select small span strong sub sup th tt var} 51 | HTML_PARSE_AS_RAW = %w{script math option textarea pre code} 52 | 53 | HTML_PARSE_AS = Hash.new {|h,k| h[k] = :raw} 54 | HTML_PARSE_AS_BLOCK.each {|i| HTML_PARSE_AS[i] = :block} 55 | HTML_PARSE_AS_SPAN.each {|i| HTML_PARSE_AS[i] = :span} 56 | HTML_PARSE_AS_RAW.each {|i| HTML_PARSE_AS[i] = :raw} 57 | 58 | # Some HTML elements like script belong to both categories (i.e. are valid in block and 59 | # span HTML) and don't appear therefore! 60 | HTML_SPAN_ELEMENTS = %w{a abbr acronym b big bdo br button cite code del dfn em i img input 61 | ins kbd label option q rb rbc rp rt rtc ruby samp select small span 62 | strong sub sup textarea tt var} 63 | HTML_BLOCK_ELEMENTS = %w{address article aside applet body button blockquote caption col colgroup dd div dl dt fieldset 64 | figcaption footer form h1 h2 h3 h4 h5 h6 header hgroup hr html head iframe legend listing menu 65 | li map nav ol optgroup p pre section summary table tbody td th thead tfoot tr ul} 66 | HTML_ELEMENTS_WITHOUT_BODY = %w{area base br col command embed hr img input keygen link meta param source track wbr} 67 | end 68 | 69 | 70 | # Contains the parsing methods. This module can be mixed into any parser to get HTML parsing 71 | # functionality. The only thing that must be provided by the class are instance variable 72 | # @stack for storing needed state and @src (instance of StringScanner) for 73 | # the actual parsing. 74 | module Parser 75 | 76 | include Constants 77 | 78 | # Process the HTML start tag that has already be scanned/checked. Does the common processing 79 | # steps and then yields to the caller for further processing. 80 | def handle_html_start_tag 81 | name = @src[1] 82 | closed = !@src[4].nil? 83 | attrs = {} 84 | @src[2].scan(HTML_ATTRIBUTE_RE).each {|attr,sep,val| attrs[attr] = val} 85 | 86 | el = Element.new(:html_element, name, :attr => attrs, :category => :block) 87 | @tree.children << el 88 | 89 | if !closed && HTML_ELEMENTS_WITHOUT_BODY.include?(el.value) 90 | warning("The HTML tag '#{el.value}' cannot have any content - auto-closing it") 91 | closed = true 92 | end 93 | if name == 'script' 94 | handle_html_script_tag 95 | yield(el, true) 96 | else 97 | yield(el, closed) 98 | end 99 | end 100 | 101 | def handle_html_script_tag 102 | curpos = @src.pos 103 | if result = @src.scan_until(/(?=<\/script\s*>)/m) 104 | add_text(extract_string(curpos...@src.pos, @src), @tree.children.last, :raw) 105 | @src.scan(HTML_TAG_CLOSE_RE) 106 | else 107 | add_text(@src.scan(/.*/m), @tree.children.last, :raw) 108 | warning("Found no end tag for 'script' - auto-closing it") 109 | end 110 | end 111 | 112 | HTML_RAW_START = /(?=<(#{REXML::Parsers::BaseParser::UNAME_STR}|\/|!--|\?))/ 113 | 114 | # Parse raw HTML from the current source position, storing the found elements in +el+. 115 | # Parsing continues until one of the following criteria are fulfilled: 116 | # 117 | # - The end of the document is reached. 118 | # - The matching end tag for the element +el+ is found (only used if +el+ is an HTML 119 | # element). 120 | # 121 | # When an HTML start tag is found, processing is deferred to #handle_html_start_tag, 122 | # providing the block given to this method. 123 | def parse_raw_html(el, &block) 124 | @stack.push(@tree) 125 | @tree = el 126 | 127 | done = false 128 | while !@src.eos? && !done 129 | if result = @src.scan_until(HTML_RAW_START) 130 | add_text(result, @tree, :text) 131 | if result = @src.scan(HTML_COMMENT_RE) 132 | @tree.children << Element.new(:xml_comment, result, :category => :block, :parent_is_raw => true) 133 | elsif result = @src.scan(HTML_INSTRUCTION_RE) 134 | @tree.children << Element.new(:xml_pi, result, :category => :block, :parent_is_raw => true) 135 | elsif @src.scan(HTML_TAG_RE) 136 | handle_html_start_tag(&block) 137 | elsif @src.scan(HTML_TAG_CLOSE_RE) 138 | if @tree.value == @src[1] 139 | done = true 140 | else 141 | warning("Found invalidly used HTML closing tag for '#{@src[1]}' - ignoring it") 142 | end 143 | else 144 | add_text(@src.scan(/./), @tree, :text) 145 | end 146 | else 147 | result = @src.scan(/.*/m) 148 | add_text(result, @tree, :text) 149 | warning("Found no end tag for '#{@tree.value}' - auto-closing it") if @tree.type == :html_element 150 | done = true 151 | end 152 | end 153 | 154 | @tree = @stack.pop 155 | end 156 | 157 | end 158 | 159 | 160 | # Converts HTML elements to native elements if possible. 161 | class ElementConverter 162 | 163 | include Constants 164 | include ::Kramdown::Utils::Entities 165 | 166 | REMOVE_TEXT_CHILDREN = %w{html head hgroup ol ul dl table colgroup tbody thead tfoot tr select optgroup} 167 | WRAP_TEXT_CHILDREN = %w{body section nav article aside header footer address div li dd blockquote figure 168 | figcaption fieldset form} 169 | REMOVE_WHITESPACE_CHILDREN = %w{body section nav article aside header footer address 170 | div li dd blockquote figure figcaption td th fieldset form} 171 | STRIP_WHITESPACE = %w{address article aside blockquote body caption dd div dl dt fieldset figcaption form footer 172 | header h1 h2 h3 h4 h5 h6 legend li nav p section td th} 173 | SIMPLE_ELEMENTS = %w{em strong blockquote hr br a img p thead tbody tfoot tr td th ul ol dl li dl dt dd} 174 | 175 | def initialize(doc) 176 | @doc = doc 177 | end 178 | 179 | # Convert the element +el+ and its children. 180 | def process(el, do_conversion = true, preserve_text = false, parent = nil) 181 | case el.type 182 | when :xml_comment, :xml_pi, :html_doctype 183 | ptype = if parent.nil? 184 | 'div' 185 | else 186 | case parent.type 187 | when :html_element then parent.value 188 | when :code_span then 'code' 189 | when :code_block then 'pre' 190 | when :header then 'h1' 191 | else parent.type.to_s 192 | end 193 | end 194 | el.options = {:category => HTML_PARSE_AS_SPAN.include?(ptype) ? :span : :block} 195 | return 196 | when :html_element 197 | else return 198 | end 199 | 200 | type = el.value 201 | remove_text_children(el) if REMOVE_TEXT_CHILDREN.include?(type) 202 | 203 | mname = "convert_#{el.value}" 204 | if do_conversion && self.class.method_defined?(mname) 205 | send(mname, el) 206 | elsif do_conversion && SIMPLE_ELEMENTS.include?(type) 207 | set_basics(el, type.intern, HTML_SPAN_ELEMENTS.include?(type) ? :span : :block) 208 | process_children(el, do_conversion, preserve_text) 209 | else 210 | process_html_element(el, do_conversion, preserve_text) 211 | end 212 | 213 | strip_whitespace(el) if STRIP_WHITESPACE.include?(type) 214 | remove_whitespace_children(el) if REMOVE_WHITESPACE_CHILDREN.include?(type) 215 | wrap_text_children(el) if WRAP_TEXT_CHILDREN.include?(type) 216 | end 217 | 218 | def process_children(el, do_conversion = true, preserve_text = false) 219 | el.children.map! do |c| 220 | if c.type == :text 221 | process_text(c.value, preserve_text) 222 | else 223 | process(c, do_conversion, preserve_text, el) 224 | c 225 | end 226 | end.flatten! 227 | end 228 | 229 | # Process the HTML text +raw+: compress whitespace (if +preserve+ is +false+) and convert 230 | # entities in entity elements. 231 | def process_text(raw, preserve = false) 232 | raw.gsub!(/\s+/, ' ') unless preserve 233 | src = StringScanner.new(raw) 234 | result = [] 235 | while !src.eos? 236 | if tmp = src.scan_until(/(?=#{HTML_ENTITY_RE})/) 237 | result << Element.new(:text, tmp) 238 | src.scan(HTML_ENTITY_RE) 239 | val = src[1] || (src[2] && src[2].to_i) || src[3].hex 240 | result << if %w{lsquo rsquo ldquo rdquo}.include?(val) 241 | Element.new(:smart_quote, val.intern) 242 | elsif %w{mdash ndash hellip laquo raquo}.include?(val) 243 | Element.new(:typographic_sym, val.intern) 244 | else 245 | Element.new(:entity, entity(val)) 246 | end 247 | else 248 | result << Element.new(:text, src.scan(/.*/m)) 249 | end 250 | end 251 | result 252 | end 253 | 254 | def process_html_element(el, do_conversion = true, preserve_text = false) 255 | el.options = {:category => HTML_SPAN_ELEMENTS.include?(el.value) ? :span : :block, 256 | :parse_type => HTML_PARSE_AS[el.value], 257 | :attr => el.options[:attr] 258 | } 259 | process_children(el, do_conversion, preserve_text) 260 | end 261 | 262 | def remove_text_children(el) 263 | el.children.delete_if {|c| c.type == :text} 264 | end 265 | 266 | SPAN_ELEMENTS = [:em, :strong, :br, :a, :img, :codespan, :entity, :smart_quote, :typographic_sym, :math] 267 | 268 | def wrap_text_children(el) 269 | tmp = [] 270 | last_is_p = false 271 | el.children.each do |c| 272 | if c.options[:category] != :block || c.type == :text 273 | if !last_is_p 274 | tmp << Element.new(:p, nil, :transparent => true) 275 | last_is_p = true 276 | end 277 | tmp.last.children << c 278 | tmp 279 | else 280 | tmp << c 281 | last_is_p = false 282 | end 283 | end 284 | el.children = tmp 285 | end 286 | 287 | def strip_whitespace(el) 288 | return if el.children.empty? 289 | if el.children.first.type == :text 290 | el.children.first.value.lstrip! 291 | end 292 | if el.children.last.type == :text 293 | el.children.last.value.rstrip! 294 | end 295 | end 296 | 297 | def remove_whitespace_children(el) 298 | i = -1 299 | el.children.delete_if do |c| 300 | i += 1 301 | c.type == :text && c.value.strip.empty? && 302 | (i == 0 || i == el.children.length - 1 || (el.children[i-1].options[:category] == :block && 303 | el.children[i+1].options[:category] == :block)) 304 | end 305 | end 306 | 307 | def set_basics(el, type, category, opts = {}) 308 | el.type = type 309 | el.options = {:category => category, :attr => el.options[:attr]}.merge(opts) 310 | el.value = nil 311 | end 312 | 313 | def extract_text(el, raw) 314 | raw << el.value.to_s if el.type == :text 315 | el.children.each {|c| extract_text(c, raw)} 316 | end 317 | 318 | def convert_h1(el) 319 | set_basics(el, :header, :block, :level => el.value[1..1].to_i) 320 | extract_text(el, el.options[:raw_text] = '') 321 | process_children(el) 322 | end 323 | %w{h2 h3 h4 h5 h6}.each do |i| 324 | alias_method("convert_#{i}".to_sym, :convert_h1) 325 | end 326 | 327 | def convert_code(el) 328 | raw = '' 329 | extract_text(el, raw) 330 | result = process_text(raw, true) 331 | begin 332 | str = result.inject('') do |mem, c| 333 | if c.type == :text 334 | mem << c.value 335 | elsif c.type == :entity 336 | if RUBY_VERSION >= '1.9' 337 | mem << c.value.char.encode(@doc.parse_infos[:encoding]) 338 | elsif [60, 62, 34, 38].include?(c.value.code_point) 339 | mem << c.value.code_point.chr 340 | end 341 | elsif c.type == :smart_quote || c.type == :typographic_sym 342 | mem << entity(c.value.to_s).char.encode(@doc.parse_infos[:encoding]) 343 | else 344 | raise "Bug - please report" 345 | end 346 | end 347 | result.clear 348 | result << Element.new(:text, str) 349 | rescue 350 | end 351 | if result.length > 1 || result.first.type != :text 352 | process_html_element(el, false, true) 353 | else 354 | if el.value == 'code' 355 | set_basics(el, :codespan, :span) 356 | else 357 | set_basics(el, :codeblock, :block) 358 | end 359 | el.value = result.first.value 360 | end 361 | end 362 | alias :convert_pre :convert_code 363 | 364 | def convert_table(el) 365 | if !is_simple_table?(el) 366 | process_html_element(el, false) 367 | return 368 | end 369 | process_children(el) 370 | set_basics(el, :table, :block) 371 | el.options[:alignment] = [] 372 | calc_alignment = lambda do |c| 373 | if c.type == :tr && el.options[:alignment].empty? 374 | el.options[:alignment] = [:default] * c.children.length 375 | break 376 | else 377 | c.children.each {|cc| calc_alignment.call(cc)} 378 | end 379 | end 380 | calc_alignment.call(el) 381 | if el.children.first.type == :tr 382 | tbody = Element.new(:tbody, nil, :category => :block) 383 | tbody.children = el.children 384 | el.children = [tbody] 385 | end 386 | end 387 | 388 | def is_simple_table?(el) 389 | only_phrasing_content = lambda do |c| 390 | c.children.all? do |cc| 391 | (cc.type == :text || !HTML_BLOCK_ELEMENTS.include?(cc.value)) && only_phrasing_content.call(cc) 392 | end 393 | end 394 | check_cells = Proc.new do |c| 395 | if c.value == 'th' || c.value == 'td' 396 | return false if !only_phrasing_content.call(c) 397 | else 398 | c.children.each {|cc| check_cells.call(cc)} 399 | end 400 | end 401 | check_cells.call(el) 402 | 403 | check_rows = lambda do |t, type| 404 | t.children.all? {|r| (r.value == 'tr' || r.type == :text) && r.children.all? {|c| c.value == type || c.type == :text}} 405 | end 406 | check_rows.call(el, 'td') || 407 | (el.children.all? do |t| 408 | t.type == :text || (t.value == 'thead' && check_rows.call(t, 'th')) || 409 | ((t.value == 'tfoot' || t.value == 'tbody') && check_rows.call(t, 'td')) 410 | end && el.children.any? {|t| t.value == 'tbody'}) 411 | end 412 | 413 | def convert_div(el) 414 | if !is_math_tag?(el) 415 | process_html_element(el) 416 | else 417 | handle_math_tag(el) 418 | end 419 | end 420 | alias :convert_span :convert_div 421 | 422 | def is_math_tag?(el) 423 | el.options[:attr] && el.options[:attr]['class'].to_s =~ /\bmath\b/ && 424 | el.children.size == 1 && el.children.first.type == :text 425 | end 426 | 427 | def handle_math_tag(el) 428 | set_basics(el, :math, (el.value == 'div' ? :block : :span)) 429 | el.value = el.children.shift.value 430 | if el.options[:attr]['class'] =~ /^\s*math\s*$/ 431 | el.options[:attr].delete('class') 432 | else 433 | el.options[:attr]['class'].sub!(/\s?math/, '') 434 | end 435 | el.value.gsub!(/&(amp|quot|gt|lt);/) do |m| 436 | case m 437 | when '&' then '&' 438 | when '"' then '"' 439 | when '>' then '>' 440 | when '<' then '<' 441 | end 442 | end 443 | end 444 | end 445 | 446 | include Parser 447 | 448 | # Parse +source+ as HTML document and return the created +tree+. 449 | def parse(source) 450 | @stack = [] 451 | @tree = Element.new(:root) 452 | @src = StringScanner.new(adapt_source(source)) 453 | 454 | while true 455 | if result = @src.scan(/\s*#{HTML_INSTRUCTION_RE}/) 456 | @tree.children << Element.new(:xml_pi, result.strip, :category => :block) 457 | elsif result = @src.scan(/\s*#{HTML_DOCTYPE_RE}/) 458 | @tree.children << Element.new(:html_doctype, result.strip, :category => :block) 459 | elsif result = @src.scan(/\s*#{HTML_COMMENT_RE}/) 460 | @tree.children << Element.new(:xml_comment, result.strip, :category => :block) 461 | else 462 | break 463 | end 464 | end 465 | 466 | tag_handler = lambda do |c, closed| 467 | parse_raw_html(c, &tag_handler) if !closed 468 | end 469 | parse_raw_html(@tree, &tag_handler) 470 | 471 | ec = ElementConverter.new(@doc) 472 | @tree.children.each {|c| ec.process(c)} 473 | ec.remove_whitespace_children(@tree) 474 | @tree 475 | end 476 | 477 | end 478 | 479 | end 480 | 481 | end 482 | 483 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'strscan' 24 | require 'stringio' 25 | 26 | #TODO: use [[:alpha:]] in all regexp to allow parsing of international values in 1.9.1 27 | #NOTE: use @src.pre_match only before other check/match?/... operations, otherwise the content is changed 28 | 29 | module Kramdown 30 | 31 | module Parser 32 | 33 | # Used for parsing a document in kramdown format. 34 | # 35 | # If you want to extend the functionality of the parser, you need to the following: 36 | # 37 | # * Create a new subclass 38 | # * add the needed parser methods 39 | # * modify the @block_parsers and @span_parsers variables and add the names of your parser 40 | # methods 41 | # 42 | # Here is a small example for an extended parser class that parses ERB style tags as raw text if 43 | # they are used as span level elements (an equivalent block level parser should probably also be 44 | # made to handle the block case): 45 | # 46 | # require 'kramdown/parser/kramdown' 47 | # 48 | # class Kramdown::Parser::ERBKramdown < Kramdown::Parser::Kramdown 49 | # 50 | # def initialize(doc) 51 | # super(doc) 52 | # @span_parsers.unshift(:erb_tags) 53 | # end 54 | # 55 | # ERB_TAGS_START = /<%.*?%>/ 56 | # 57 | # def parse_erb_tags 58 | # @src.pos += @src.matched_size 59 | # @tree.children << Element.new(:raw, @src.matched) 60 | # end 61 | # define_parser(:erb_tags, ERB_TAGS_START, '<%') 62 | # 63 | # end 64 | # 65 | # The new parser can be used like this: 66 | # 67 | # require 'kramdown/document' 68 | # # require the file with the above parser class 69 | # 70 | # Kramdown::Document.new(input_text, :input => 'ERBKramdown').to_html 71 | # 72 | class Kramdown < Base 73 | 74 | include ::Kramdown 75 | 76 | attr_reader :tree 77 | attr_reader :doc 78 | attr_reader :options 79 | 80 | # Create a new Kramdown parser object for the Kramdown::Document +doc+. 81 | def initialize(doc) 82 | super(doc) 83 | 84 | @src = nil 85 | @tree = nil 86 | @stack = [] 87 | @text_type = :raw_text 88 | @block_ial = nil 89 | 90 | @doc.parse_infos[:ald] = {} 91 | @doc.parse_infos[:link_defs] = {} 92 | @doc.parse_infos[:abbrev_defs] = {} 93 | @doc.parse_infos[:footnotes] = {} 94 | 95 | @block_parsers = [:blank_line, :codeblock, :codeblock_fenced, :blockquote, :table, :atx_header, 96 | :setext_header, :horizontal_rule, :list, :definition_list, :link_definition, :block_html, 97 | :footnote_definition, :abbrev_definition, :ald, :block_math, 98 | :block_extension, :block_ial, :eob_marker, :paragraph] 99 | @span_parsers = [:emphasis, :codespan, :autolink, :span_html, :footnote_marker, :link, :smart_quotes, :inline_math, 100 | :span_extension, :span_ial, :html_entity, :typographic_syms, :line_break, :escaped_chars] 101 | 102 | end 103 | private_class_method(:new, :allocate) 104 | 105 | 106 | # The source string provided on initialization is parsed and the created +tree+ is returned. 107 | def parse(source) 108 | configure_parser 109 | tree = Element.new(:root) 110 | parse_blocks(tree, adapt_source(source)) 111 | update_tree(tree) 112 | replace_abbreviations(tree) 113 | @doc.parse_infos[:footnotes].each do |name, data| 114 | update_tree(data[:content]) 115 | end 116 | tree 117 | end 118 | 119 | ####### 120 | protected 121 | ####### 122 | 123 | # Adapt the object to allow parsing like specified in the options. 124 | def configure_parser 125 | @parsers = {} 126 | (@block_parsers + @span_parsers).each do |name| 127 | if self.class.has_parser?(name) 128 | @parsers[name] = self.class.parser(name) 129 | else 130 | raise Kramdown::Error, "Unknown parser: #{name}" 131 | end 132 | end 133 | @span_start, @span_start_re = span_parser_regexps 134 | end 135 | 136 | # Create the needed span parser regexps. 137 | def span_parser_regexps(parsers = @span_parsers) 138 | span_start = /#{parsers.map {|name| @parsers[name].span_start}.join('|')}/ 139 | [span_start, /(?=#{span_start})/] 140 | end 141 | 142 | # Parse all block level elements in +text+ into the element +el+. 143 | def parse_blocks(el, text = nil) 144 | @stack.push([@tree, @src]) 145 | @tree, @src = el, (text.nil? ? @src : StringScanner.new(text)) 146 | 147 | status = catch(:stop_block_parsing) do 148 | while !@src.eos? 149 | block_ial_set = @block_ial 150 | @block_parsers.any? do |name| 151 | if @src.check(@parsers[name].start_re) 152 | send(@parsers[name].method) 153 | else 154 | false 155 | end 156 | end || begin 157 | warning('Warning: this should not occur - no block parser handled the line') 158 | add_text(@src.scan(/.*\n/)) 159 | end 160 | @block_ial = nil if block_ial_set 161 | end 162 | end 163 | 164 | @tree, @src = *@stack.pop 165 | status 166 | end 167 | 168 | # Update the tree by parsing all :raw_text elements with the span level parser 169 | # (resets +@tree+, +@src+ and the +@stack+) and by updating the attributes from the IALs. 170 | def update_tree(element) 171 | element.children.map! do |child| 172 | if child.type == :raw_text 173 | @stack, @tree, @text_type = [], nil, :text 174 | @src = StringScanner.new(child.value) 175 | parse_spans(child) 176 | child.children 177 | elsif child.type == :eob 178 | [] 179 | else 180 | update_tree(child) 181 | update_attr_with_ial(child.options[:attr] ||= {}, child.options[:ial]) if child.options[:ial] 182 | child 183 | end 184 | end.flatten! 185 | end 186 | 187 | # Parse all span level elements in the source string. 188 | def parse_spans(el, stop_re = nil, parsers = nil, text_type = @text_type) 189 | @stack.push([@tree, @text_type]) unless @tree.nil? 190 | @tree, @text_type = el, text_type 191 | 192 | span_start = @span_start 193 | span_start_re = @span_start_re 194 | span_start, span_start_re = span_parser_regexps(parsers) if parsers 195 | parsers = parsers || @span_parsers 196 | 197 | used_re = (stop_re.nil? ? span_start_re : /(?=#{Regexp.union(stop_re, span_start)})/) 198 | stop_re_found = false 199 | while !@src.eos? && !stop_re_found 200 | if result = @src.scan_until(used_re) 201 | add_text(result) 202 | if stop_re && (stop_re_matched = @src.check(stop_re)) 203 | stop_re_found = (block_given? ? yield : true) 204 | end 205 | processed = parsers.any? do |name| 206 | if @src.check(@parsers[name].start_re) 207 | send(@parsers[name].method) 208 | true 209 | else 210 | false 211 | end 212 | end unless stop_re_found 213 | add_text(@src.scan(/./)) if !processed && !stop_re_found 214 | else 215 | add_text(@src.scan(/.*/m)) unless stop_re 216 | break 217 | end 218 | end 219 | 220 | @tree, @text_type = @stack.pop 221 | 222 | stop_re_found 223 | end 224 | 225 | # Update the attributes with the information from the inline attribute list and all referenced ALDs. 226 | def update_attr_with_ial(attr, ial) 227 | ial[:refs].each do |ref| 228 | update_attr_with_ial(attr, ref) if ref = @doc.parse_infos[:ald][ref] 229 | end if ial[:refs] 230 | attr['class'] = ((attr['class'] || '') + " #{ial['class']}").lstrip if ial['class'] 231 | ial.each {|k,v| attr[k] = v if k.kind_of?(String) && k != 'class' } 232 | end 233 | 234 | # Create a new block level element, taking care of applying a preceding block IAL if it exists. 235 | def new_block_el(*args) 236 | el = Element.new(*args) 237 | el.options[:category] ||= :block 238 | el.options[:ial] = @block_ial if @block_ial && el.type != :blank && el.type != :eob 239 | el 240 | end 241 | 242 | @@parsers = {} 243 | 244 | # Holds all the needed data for one block/span level parser. 245 | Data = Struct.new(:name, :start_re, :span_start, :method) 246 | 247 | # Add a parser method 248 | # 249 | # * with the given +name+, 250 | # * using +start_re+ as start regexp 251 | # * and, for span parsers, +span_start+ as a String that can be used in a regexp and 252 | # which identifies the starting character(s) 253 | # 254 | # to the registry. The method name is automatically derived from the +name+ or can explicitly 255 | # be set by using the +meth_name+ parameter. 256 | def self.define_parser(name, start_re, span_start = nil, meth_name = "parse_#{name}") 257 | raise "A parser with the name #{name} already exists!" if @@parsers.has_key?(name) 258 | @@parsers[name] = Data.new(name, start_re, span_start, meth_name) 259 | end 260 | 261 | # Return the Data structure for the parser +name+. 262 | def self.parser(name = nil) 263 | @@parsers[name] 264 | end 265 | 266 | # Return +true+ if there is a parser called +name+. 267 | def self.has_parser?(name) 268 | @@parsers.has_key?(name) 269 | end 270 | 271 | INDENT = /^(?:\t| {4})/ 272 | OPT_SPACE = / {0,3}/ 273 | 274 | require 'kramdown/parser/kramdown/blank_line' 275 | require 'kramdown/parser/kramdown/eob' 276 | require 'kramdown/parser/kramdown/paragraph' 277 | require 'kramdown/parser/kramdown/header' 278 | require 'kramdown/parser/kramdown/blockquote' 279 | require 'kramdown/parser/kramdown/table' 280 | require 'kramdown/parser/kramdown/codeblock' 281 | require 'kramdown/parser/kramdown/horizontal_rule' 282 | require 'kramdown/parser/kramdown/list' 283 | require 'kramdown/parser/kramdown/link' 284 | require 'kramdown/parser/kramdown/attribute_list' 285 | require 'kramdown/parser/kramdown/extension' 286 | require 'kramdown/parser/kramdown/footnote' 287 | require 'kramdown/parser/kramdown/html' 288 | require 'kramdown/parser/kramdown/escaped_chars' 289 | require 'kramdown/parser/kramdown/html_entity' 290 | require 'kramdown/parser/kramdown/line_break' 291 | require 'kramdown/parser/kramdown/typographic_symbol' 292 | require 'kramdown/parser/kramdown/autolink' 293 | require 'kramdown/parser/kramdown/codespan' 294 | require 'kramdown/parser/kramdown/emphasis' 295 | require 'kramdown/parser/kramdown/smart_quotes' 296 | require 'kramdown/parser/kramdown/math' 297 | require 'kramdown/parser/kramdown/abbreviation' 298 | 299 | end 300 | 301 | end 302 | 303 | end 304 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/abbreviation.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | ABBREV_DEFINITION_START = /^#{OPT_SPACE}\*\[(.+?)\]:(.*?)\n/ 28 | 29 | # Parse the link definition at the current location. 30 | def parse_abbrev_definition 31 | @src.pos += @src.matched_size 32 | abbrev_id, abbrev_text = @src[1], @src[2].strip 33 | warning("Duplicate abbreviation ID '#{abbrev_id}' - overwriting") if @doc.parse_infos[:abbrev_defs][abbrev_id] 34 | @doc.parse_infos[:abbrev_defs][abbrev_id] = abbrev_text 35 | true 36 | end 37 | define_parser(:abbrev_definition, ABBREV_DEFINITION_START) 38 | 39 | # Replace the abbreviation text with elements. 40 | def replace_abbreviations(el, regexps = nil) 41 | return if @doc.parse_infos[:abbrev_defs].empty? 42 | if !regexps 43 | regexps = [Regexp.union(*@doc.parse_infos[:abbrev_defs].keys.map {|k| /#{Regexp.escape(k)}/})] 44 | regexps << /(?=(?:\W|^)#{regexps.first}(?!\w))/ # regexp should only match on word boundaries 45 | end 46 | el.children.map! do |child| 47 | if child.type == :text 48 | result = [] 49 | strscan = StringScanner.new(child.value) 50 | while temp = strscan.scan_until(regexps.last) 51 | temp += strscan.scan(/\W|^/) 52 | abbr = strscan.scan(regexps.first) 53 | result += [Element.new(:text, temp), Element.new(:abbreviation, abbr)] 54 | end 55 | result + [Element.new(:text, extract_string(strscan.pos..-1, strscan))] 56 | else 57 | replace_abbreviations(child, regexps) 58 | child 59 | end 60 | end.flatten! 61 | end 62 | 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/attribute_list.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | # Parse the string +str+ and extract all attributes and add all found attributes to the hash 28 | # +opts+. 29 | def parse_attribute_list(str, opts) 30 | str.scan(ALD_TYPE_ANY).each do |key, sep, val, id_attr, class_attr, ref| 31 | if ref 32 | (opts[:refs] ||= []) << ref 33 | elsif class_attr 34 | opts['class'] = ((opts['class'] || '') + " #{class_attr}").lstrip 35 | elsif id_attr 36 | opts['id'] = id_attr 37 | else 38 | opts[key] = val.gsub(/\\(\}|#{sep})/, "\\1") 39 | end 40 | end 41 | end 42 | 43 | # Update the +ial+ with the information from the inline attribute list +opts+. 44 | def update_ial_with_ial(ial, opts) 45 | (ial[:refs] ||= []) << opts[:refs] 46 | ial['class'] = ((ial['class'] || '') + " #{opts['class']}").lstrip if opts['class'] 47 | opts.each {|k,v| ial[k] = v if k != :refs && k != 'class' } 48 | end 49 | 50 | 51 | ALD_ID_CHARS = /[\w\d-]/ 52 | ALD_ANY_CHARS = /\\\}|[^\}]/ 53 | ALD_ID_NAME = /(?:\w|\d)#{ALD_ID_CHARS}*/ 54 | ALD_TYPE_KEY_VALUE_PAIR = /(#{ALD_ID_NAME})=("|')((?:\\\}|\\\2|[^\}\2])*?)\2/ 55 | ALD_TYPE_CLASS_NAME = /\.(#{ALD_ID_NAME})/ 56 | ALD_TYPE_ID_NAME = /#(#{ALD_ID_NAME})/ 57 | ALD_TYPE_REF = /(#{ALD_ID_NAME})/ 58 | ALD_TYPE_ANY = /(?:\A|\s)(?:#{ALD_TYPE_KEY_VALUE_PAIR}|#{ALD_TYPE_ID_NAME}|#{ALD_TYPE_CLASS_NAME}|#{ALD_TYPE_REF})(?=\s|\Z)/ 59 | ALD_START = /^#{OPT_SPACE}\{:(#{ALD_ID_NAME}):(#{ALD_ANY_CHARS}+)\}\s*?\n/ 60 | 61 | # Parse the attribute list definition at the current location. 62 | def parse_ald 63 | @src.pos += @src.matched_size 64 | parse_attribute_list(@src[2], @doc.parse_infos[:ald][@src[1]] ||= {}) 65 | @tree.children << Element.new(:eob) 66 | true 67 | end 68 | define_parser(:ald, ALD_START) 69 | 70 | 71 | IAL_BLOCK_START = /^#{OPT_SPACE}\{:(?!:)(#{ALD_ANY_CHARS}+)\}\s*?\n/ 72 | 73 | # Parse the inline attribute list at the current location. 74 | def parse_block_ial 75 | @src.pos += @src.matched_size 76 | if @tree.children.last && @tree.children.last.type != :blank && @tree.children.last.type != :eob 77 | parse_attribute_list(@src[1], @tree.children.last.options[:ial] ||= {}) 78 | else 79 | parse_attribute_list(@src[1], @block_ial = {}) 80 | end 81 | @tree.children << Element.new(:eob) unless @src.check(IAL_BLOCK_START) 82 | true 83 | end 84 | define_parser(:block_ial, IAL_BLOCK_START) 85 | 86 | 87 | IAL_SPAN_START = /\{:(#{ALD_ANY_CHARS}+)\}/ 88 | 89 | # Parse the inline attribute list at the current location. 90 | def parse_span_ial 91 | @src.pos += @src.matched_size 92 | if @tree.children.last && @tree.children.last.type != :text 93 | attr = {} 94 | parse_attribute_list(@src[1], attr) 95 | update_ial_with_ial(@tree.children.last.options[:ial] ||= {}, attr) 96 | update_attr_with_ial(@tree.children.last.options[:attr] ||= {}, attr) 97 | else 98 | warning("Ignoring span IAL because preceding element is just text") 99 | end 100 | end 101 | define_parser(:span_ial, IAL_SPAN_START, '\{:') 102 | 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/autolink.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | if RUBY_VERSION == '1.8.5' 28 | ACHARS = '\x80-\xFF' 29 | else 30 | ACHARS = '' 31 | end 32 | AUTOLINK_START = /<((mailto|https?|ftps?):.+?|[-.\w#{ACHARS}]+@[-\w#{ACHARS}]+(\.[-\w#{ACHARS}]+)*\.[a-z]+)>/u 33 | 34 | # Parse the autolink at the current location. 35 | def parse_autolink 36 | @src.pos += @src.matched_size 37 | href = @src[1] 38 | href= "mailto:#{href}" if @src[2].nil? 39 | el = Element.new(:a, nil, {:attr => {'href' => href}}) 40 | add_text(@src[1].sub(/^mailto:/, ''), el) 41 | @tree.children << el 42 | end 43 | define_parser(:autolink, AUTOLINK_START, '<') 44 | 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/blank_line.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | BLANK_LINE = /(?:^\s*\n)+/ 28 | 29 | # Parse the blank line at the current postition. 30 | def parse_blank_line 31 | @src.pos += @src.matched_size 32 | if @tree.children.last && @tree.children.last.type == :blank 33 | @tree.children.last.value += @src.matched 34 | else 35 | @tree.children << new_block_el(:blank, @src.matched) 36 | end 37 | true 38 | end 39 | define_parser(:blank_line, BLANK_LINE) 40 | 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/blockquote.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | BLOCKQUOTE_START = /^#{OPT_SPACE}> ?/ 28 | BLOCKQUOTE_MATCH = /(^#{OPT_SPACE}>.*?\n)+/ 29 | 30 | # Parse the blockquote at the current location. 31 | def parse_blockquote 32 | result = @src.scan(BLOCKQUOTE_MATCH).gsub(BLOCKQUOTE_START, '') 33 | el = new_block_el(:blockquote) 34 | @tree.children << el 35 | parse_blocks(el, result) 36 | true 37 | end 38 | define_parser(:blockquote, BLOCKQUOTE_START) 39 | 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/codeblock.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'kramdown/parser/kramdown/blank_line' 24 | 25 | module Kramdown 26 | module Parser 27 | class Kramdown 28 | 29 | CODEBLOCK_START = INDENT 30 | CODEBLOCK_LINE = /(?:#{INDENT}.*?\S.*?\n)+/ 31 | CODEBLOCK_MATCH = /(?:#{BLANK_LINE}?#{CODEBLOCK_LINE})*/ 32 | 33 | # Parse the indented codeblock at the current location. 34 | def parse_codeblock 35 | @tree.children << new_block_el(:codeblock, @src.scan(CODEBLOCK_MATCH).gsub!(INDENT, '')) 36 | true 37 | end 38 | define_parser(:codeblock, CODEBLOCK_START) 39 | 40 | 41 | FENCED_CODEBLOCK_START = /^~{3,}/ 42 | FENCED_CODEBLOCK_MATCH = /^(~{3,})\s*?\n(.*?)^\1~*\s*?\n/m 43 | 44 | # Parse the fenced codeblock at the current location. 45 | def parse_codeblock_fenced 46 | if @src.check(FENCED_CODEBLOCK_MATCH) 47 | @src.pos += @src.matched_size 48 | @tree.children << new_block_el(:codeblock, @src[2]) 49 | true 50 | else 51 | false 52 | end 53 | end 54 | define_parser(:codeblock_fenced, FENCED_CODEBLOCK_START) 55 | 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/codespan.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | CODESPAN_DELIMITER = /`+/ 28 | 29 | # Parse the codespan at the current scanner location. 30 | def parse_codespan 31 | result = @src.scan(CODESPAN_DELIMITER) 32 | simple = (result.length == 1) 33 | reset_pos = @src.pos 34 | 35 | if simple && @src.pre_match =~ /\s\Z/ && @src.match?(/\s/) 36 | add_text(result) 37 | return 38 | end 39 | 40 | text = @src.scan_until(/#{result}/) 41 | if text 42 | text.sub!(/#{result}\Z/, '') 43 | if !simple 44 | text = text[1..-1] if text[0..0] == ' ' 45 | text = text[0..-2] if text[-1..-1] == ' ' 46 | end 47 | @tree.children << Element.new(:codespan, text) 48 | else 49 | @src.pos = reset_pos 50 | add_text(result) 51 | end 52 | end 53 | define_parser(:codespan, CODESPAN_DELIMITER, '`') 54 | 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/emphasis.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | EMPHASIS_START = /(?:\*\*?|__?)/ 28 | 29 | # Parse the emphasis at the current location. 30 | def parse_emphasis 31 | result = @src.scan(EMPHASIS_START) 32 | element = (result.length == 2 ? :strong : :em) 33 | type = (result =~ /_/ ? '_' : '*') 34 | reset_pos = @src.pos 35 | 36 | if (type == '_' && @src.pre_match =~ /[[:alpha:]]\z/ && @src.check(/[[:alpha:]]/)) || @src.check(/\s/) || 37 | @tree.type == element || @stack.any? {|el, _| el.type == element} 38 | add_text(result) 39 | return 40 | end 41 | 42 | sub_parse = lambda do |delim, elem| 43 | el = Element.new(elem) 44 | stop_re = /#{Regexp.escape(delim)}/ 45 | found = parse_spans(el, stop_re) do 46 | (@src.pre_match[-1, 1] !~ /\s/) && 47 | (elem != :em || !@src.match?(/#{Regexp.escape(delim*2)}(?!#{Regexp.escape(delim)})/)) && 48 | (type != '_' || !@src.match?(/#{Regexp.escape(delim)}[[:alpha:]]/)) && el.children.size > 0 49 | end 50 | [found, el, stop_re] 51 | end 52 | 53 | found, el, stop_re = sub_parse.call(result, element) 54 | if !found && element == :strong && @tree.type != :em 55 | @src.pos = reset_pos - 1 56 | found, el, stop_re = sub_parse.call(type, :em) 57 | end 58 | if found 59 | @src.scan(stop_re) 60 | @tree.children << el 61 | else 62 | @src.pos = reset_pos 63 | add_text(result) 64 | end 65 | end 66 | define_parser(:emphasis, EMPHASIS_START, '\*|_') 67 | 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/eob.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | EOB_MARKER = /^\^\s*?\n/ 28 | 29 | # Parse the EOB marker at the current location. 30 | def parse_eob_marker 31 | @src.pos += @src.matched_size 32 | @tree.children << new_block_el(:eob) 33 | true 34 | end 35 | define_parser(:eob_marker, EOB_MARKER) 36 | 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/escaped_chars.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | ESCAPED_CHARS = /\\([\\.*_+`()\[\]{}#!:|"'\$-])/ 28 | 29 | # Parse the backslash-escaped character at the current location. 30 | def parse_escaped_chars 31 | @src.pos += @src.matched_size 32 | add_text(@src[1]) 33 | end 34 | define_parser(:escaped_chars, ESCAPED_CHARS, '\\\\') 35 | 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/extension.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'kramdown/parser/kramdown/attribute_list' 24 | 25 | module Kramdown 26 | module Parser 27 | class Kramdown 28 | 29 | def parse_extension_start_tag(type) 30 | @src.pos += @src.matched_size 31 | 32 | if @src[4] || @src.matched == '{:/}' 33 | name = (@src[4] ? "for '#{@src[4]}' " : '') 34 | warning("Invalid extension stop tag #{name}found - ignoring it") 35 | return 36 | end 37 | 38 | ext = @src[1] 39 | opts = {} 40 | body = nil 41 | parse_attribute_list(@src[2] || '', opts) 42 | 43 | if !@src[3] 44 | stop_re = (type == :block ? /#{EXT_BLOCK_STOP_STR % ext}/ : /#{EXT_STOP_STR % ext}/) 45 | if result = @src.scan_until(stop_re) 46 | body = result.sub!(stop_re, '') 47 | body.chomp! if type == :block 48 | else 49 | warning("No stop tag for extension '#{ext}' found - treating it as extension without body") 50 | end 51 | end 52 | 53 | handle_extension(ext, opts, body, type) 54 | end 55 | 56 | def handle_extension(name, opts, body, type) 57 | case name 58 | when 'comment' 59 | @tree.children << Element.new(:comment, body, :category => type) if body.kind_of?(String) 60 | when 'nomarkdown' 61 | @tree.children << Element.new(:raw, body, :category => type) if body.kind_of?(String) 62 | when 'options' 63 | opts.select do |k,v| 64 | k = k.to_sym 65 | if Kramdown::Options.defined?(k) 66 | @doc.options[k] = Kramdown::Options.parse(k, v) rescue @doc.options[k] 67 | false 68 | else 69 | true 70 | end 71 | end.each do |k,v| 72 | warning("Unknown kramdown option '#{k}'") 73 | end 74 | else 75 | warning("Invalid extension name '#{name}' specified - ignoring extension") 76 | end 77 | end 78 | 79 | 80 | EXT_STOP_STR = "\\{:/(%s)?\\}" 81 | EXT_START_STR = "\\{::(\\w+)(?:\\s(#{ALD_ANY_CHARS}*?)|)(\\/)?\\}" 82 | EXT_SPAN_START = /#{EXT_START_STR}|#{EXT_STOP_STR % ALD_ID_NAME}/ 83 | EXT_BLOCK_START = /^#{OPT_SPACE}(?:#{EXT_START_STR}|#{EXT_STOP_STR % ALD_ID_NAME})\s*?\n/ 84 | EXT_BLOCK_STOP_STR = "^#{OPT_SPACE}#{EXT_STOP_STR}\s*?\n" 85 | 86 | # Parse the extension block at the current location. 87 | def parse_block_extension 88 | parse_extension_start_tag(:block) 89 | true 90 | end 91 | define_parser(:block_extension, EXT_BLOCK_START) 92 | 93 | 94 | # Parse the extension span at the current location. 95 | def parse_span_extension 96 | parse_extension_start_tag(:span) 97 | end 98 | define_parser(:span_extension, EXT_SPAN_START, '\{:[:/]') 99 | 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/footnote.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'kramdown/parser/kramdown/attribute_list' 24 | require 'kramdown/parser/kramdown/blank_line' 25 | require 'kramdown/parser/kramdown/codeblock' 26 | 27 | module Kramdown 28 | module Parser 29 | class Kramdown 30 | 31 | FOOTNOTE_DEFINITION_START = /^#{OPT_SPACE}\[\^(#{ALD_ID_NAME})\]:\s*?(.*?\n(?:#{BLANK_LINE}?#{CODEBLOCK_LINE})*)/ 32 | 33 | # Parse the foot note definition at the current location. 34 | def parse_footnote_definition 35 | @src.pos += @src.matched_size 36 | 37 | el = Element.new(:footnote_def) 38 | parse_blocks(el, @src[2].gsub(INDENT, '')) 39 | warning("Duplicate footnote name '#{@src[1]}' - overwriting") if @doc.parse_infos[:footnotes][@src[1]] 40 | (@doc.parse_infos[:footnotes][@src[1]] = {})[:content] = el 41 | true 42 | end 43 | define_parser(:footnote_definition, FOOTNOTE_DEFINITION_START) 44 | 45 | 46 | FOOTNOTE_MARKER_START = /\[\^(#{ALD_ID_NAME})\]/ 47 | 48 | # Parse the footnote marker at the current location. 49 | def parse_footnote_marker 50 | @src.pos += @src.matched_size 51 | fn_def = @doc.parse_infos[:footnotes][@src[1]] 52 | if fn_def 53 | valid = fn_def[:marker] && fn_def[:marker].options[:stack][0..-2].zip(fn_def[:marker].options[:stack][1..-1]).all? do |par, child| 54 | par.children.include?(child) 55 | end 56 | if !fn_def[:marker] || !valid 57 | fn_def[:marker] = Element.new(:footnote, nil, :name => @src[1]) 58 | fn_def[:marker].options[:stack] = [@stack.map {|s| s.first}, @tree, fn_def[:marker]].flatten.compact 59 | @tree.children << fn_def[:marker] 60 | else 61 | warning("Footnote marker '#{@src[1]}' already appeared in document, ignoring newly found marker") 62 | add_text(@src.matched) 63 | end 64 | else 65 | warning("Footnote definition for '#{@src[1]}' not found") 66 | add_text(@src.matched) 67 | end 68 | end 69 | define_parser(:footnote_marker, FOOTNOTE_MARKER_START, '\[') 70 | 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/header.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | HEADER_ID=/(?:[ \t]\{#((?:\w|\d)[\w\d-]*)\})?/ 28 | SETEXT_HEADER_START = /^(#{OPT_SPACE}[^ \t].*?)#{HEADER_ID}[ \t]*?\n(-|=)+\s*?\n/ 29 | 30 | # Parse the Setext header at the current location. 31 | def parse_setext_header 32 | if @tree.children.last && @tree.children.last.type != :blank 33 | return false 34 | end 35 | @src.pos += @src.matched_size 36 | text, id, level = @src[1].strip, @src[2], @src[3] 37 | el = new_block_el(:header, nil, :level => (level == '-' ? 2 : 1), :raw_text => text) 38 | add_text(text, el) 39 | el.options[:attr] = {'id' => id} if id 40 | @tree.children << el 41 | true 42 | end 43 | define_parser(:setext_header, SETEXT_HEADER_START) 44 | 45 | 46 | ATX_HEADER_START = /^\#{1,6}/ 47 | ATX_HEADER_MATCH = /^(\#{1,6})(.+?)\s*?#*#{HEADER_ID}\s*?\n/ 48 | 49 | # Parse the Atx header at the current location. 50 | def parse_atx_header 51 | if @tree.children.last && @tree.children.last.type != :blank 52 | return false 53 | end 54 | result = @src.scan(ATX_HEADER_MATCH) 55 | level, text, id = @src[1], @src[2].strip, @src[3] 56 | el = new_block_el(:header, nil, :level => level.length, :raw_text => text) 57 | add_text(text, el) 58 | el.options[:attr] = {'id' => id} if id 59 | @tree.children << el 60 | true 61 | end 62 | define_parser(:atx_header, ATX_HEADER_START) 63 | 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/horizontal_rule.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | HR_START = /^#{OPT_SPACE}(\*|-|_)[ \t]*\1[ \t]*\1[ \t]*(\1|[ \t])*\n/ 28 | 29 | # Parse the horizontal rule at the current location. 30 | def parse_horizontal_rule 31 | @src.pos += @src.matched_size 32 | @tree.children << new_block_el(:hr) 33 | true 34 | end 35 | define_parser(:horizontal_rule, HR_START) 36 | 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/html.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'kramdown/parser/html' 24 | 25 | module Kramdown 26 | module Parser 27 | class Kramdown 28 | 29 | include Kramdown::Parser::Html::Parser 30 | 31 | def handle_kramdown_html_tag(el, closed) 32 | parse_type = if @tree.type != :html_element || @tree.options[:parse_type] != :raw 33 | (@doc.options[:parse_block_html] ? HTML_PARSE_AS[el.value] : :raw) 34 | else 35 | :raw 36 | end 37 | if val = html_parse_type(el.options[:attr].delete('markdown')) 38 | parse_type = (val == :default ? HTML_PARSE_AS[el.value] : val) 39 | end 40 | 41 | @src.scan(/[ \t]*\n/) if parse_type == :block 42 | el.options[:outer_element] = true if @tree.type != :html_element 43 | el.options[:parent_is_raw] = true if @tree.type == :html_element && @tree.options[:parse_type] == :raw 44 | el.options[:parse_type] = parse_type 45 | 46 | if !closed 47 | if parse_type == :block 48 | end_tag_found = parse_blocks(el) 49 | if !end_tag_found 50 | warning("Found no end tag for '#{el.value}' - auto-closing it") 51 | end 52 | elsif parse_type == :span 53 | curpos = @src.pos 54 | if result = @src.scan_until(/(?=<\/#{el.value}\s*>)/m) 55 | add_text(extract_string(curpos...@src.pos, @src), el) 56 | @src.scan(HTML_TAG_CLOSE_RE) 57 | else 58 | add_text(@src.scan(/.*/m), el) 59 | warning("Found no end tag for '#{el.value}' - auto-closing it") 60 | end 61 | else 62 | parse_raw_html(el, &method(:handle_kramdown_html_tag)) 63 | end 64 | @src.scan(/[ \t]*\n/) unless (@tree.type == :html_element && @tree.options[:parse_type] == :raw) 65 | end 66 | end 67 | 68 | # Return the HTML parse type defined by the string +val+, i.e. raw when "0", default parsing 69 | # (return value +nil+) when "1", span parsing when "span" and block parsing when "block". If 70 | # +val+ is nil, then the default parsing mode is used. 71 | def html_parse_type(val) 72 | case val 73 | when "0" then :raw 74 | when "1" then :default 75 | when "span" then :span 76 | when "block" then :block 77 | when NilClass then nil 78 | else 79 | warning("Invalid markdown attribute val '#{val}', using default") 80 | nil 81 | end 82 | end 83 | 84 | 85 | HTML_BLOCK_START = /^#{OPT_SPACE}<(#{REXML::Parsers::BaseParser::UNAME_STR}|\?|!--|\/)/ 86 | 87 | # Parse the HTML at the current position as block level HTML. 88 | def parse_block_html 89 | if result = @src.scan(HTML_COMMENT_RE) 90 | @tree.children << Element.new(:xml_comment, result, :category => :block) 91 | @src.scan(/[ \t]*\n/) 92 | true 93 | elsif result = @src.scan(HTML_INSTRUCTION_RE) 94 | @tree.children << Element.new(:xml_pi, result, :category => :block) 95 | @src.scan(/[ \t]*\n/) 96 | true 97 | else 98 | if result = @src.check(/^#{OPT_SPACE}#{HTML_TAG_RE}/) && !HTML_SPAN_ELEMENTS.include?(@src[1]) 99 | @src.pos += @src.matched_size 100 | handle_html_start_tag(&method(:handle_kramdown_html_tag)) 101 | Kramdown::Parser::Html::ElementConverter.new(@doc).process(@tree.children.last) if @doc.options[:html_to_native] 102 | true 103 | elsif result = @src.check(/^#{OPT_SPACE}#{HTML_TAG_CLOSE_RE}/) && !HTML_SPAN_ELEMENTS.include?(@src[1]) 104 | @src.pos += @src.matched_size 105 | name = @src[1] 106 | 107 | if @tree.type == :html_element && @tree.value == name 108 | throw :stop_block_parsing, :found 109 | else 110 | warning("Found invalidly used HTML closing tag for '#{name}' - ignoring it") 111 | true 112 | end 113 | else 114 | false 115 | end 116 | end 117 | end 118 | define_parser(:block_html, HTML_BLOCK_START) 119 | 120 | 121 | HTML_SPAN_START = /<(#{REXML::Parsers::BaseParser::UNAME_STR}|\?|!--|\/)/ 122 | 123 | # Parse the HTML at the current position as span level HTML. 124 | def parse_span_html 125 | if result = @src.scan(HTML_COMMENT_RE) 126 | @tree.children << Element.new(:xml_comment, result, :category => :span) 127 | elsif result = @src.scan(HTML_INSTRUCTION_RE) 128 | @tree.children << Element.new(:xml_pi, result, :category => :span) 129 | elsif result = @src.scan(HTML_TAG_CLOSE_RE) 130 | warning("Found invalidly used HTML closing tag for '#{@src[1]}' - ignoring it") 131 | elsif result = @src.scan(HTML_TAG_RE) 132 | return if HTML_BLOCK_ELEMENTS.include?(@src[1]) 133 | 134 | reset_pos = @src.pos 135 | attrs = {} 136 | @src[2].scan(HTML_ATTRIBUTE_RE).each {|name,sep,val| attrs[name] = val.gsub(/\n+/, ' ')} 137 | 138 | do_parsing = (HTML_PARSE_AS_RAW.include?(@src[1]) || @tree.options[:parse_type] == :raw ? false : @doc.options[:parse_span_html]) 139 | if val = html_parse_type(attrs.delete('markdown')) 140 | if val == :block 141 | warning("Cannot use block level parsing in span level HTML tag - using default mode") 142 | elsif val == :span 143 | do_parsing = true 144 | elsif val == :default 145 | do_parsing = !HTML_PARSE_AS_RAW.include?(@src[1]) 146 | elsif val == :raw 147 | do_parsing = false 148 | end 149 | end 150 | 151 | el = Element.new(:html_element, @src[1], :attr => attrs, :category => :span, :parse_type => (do_parsing ? :span : :raw)) 152 | @tree.children << el 153 | stop_re = /<\/#{Regexp.escape(@src[1])}\s*>/ 154 | if !@src[4] && HTML_ELEMENTS_WITHOUT_BODY.include?(el.value) 155 | warning("The HTML tag '#{el.value}' cannot have any content - auto-closing it") 156 | elsif !@src[4] 157 | if parse_spans(el, stop_re, (do_parsing ? nil : [:span_html])) 158 | @src.scan(stop_re) 159 | else 160 | warning("Found no end tag for '#{el.value}' - auto-closing it") 161 | add_text(@src.scan(/.*/m), el) 162 | end 163 | end 164 | Kramdown::Parser::Html::ElementConverter.new(@doc).process(el) if @doc.options[:html_to_native] 165 | else 166 | add_text(@src.scan(/./)) 167 | end 168 | end 169 | define_parser(:span_html, HTML_SPAN_START, '<') 170 | 171 | end 172 | end 173 | end 174 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/html_entity.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'kramdown/parser/html' 24 | 25 | module Kramdown 26 | module Parser 27 | class Kramdown 28 | 29 | # Parse the HTML entity at the current location. 30 | def parse_html_entity 31 | @src.pos += @src.matched_size 32 | @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity(@src[1] || (@src[2] && @src[2].to_i) || @src[3].hex)) 33 | end 34 | define_parser(:html_entity, Kramdown::Parser::Html::Constants::HTML_ENTITY_RE, '&') 35 | 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/line_break.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | LINE_BREAK = /( |\\\\)(?=\n)/ 28 | 29 | # Parse the line break at the current location. 30 | def parse_line_break 31 | @src.pos += @src.matched_size 32 | @tree.children << Element.new(:br) 33 | end 34 | define_parser(:line_break, LINE_BREAK, '( |\\\\)(?=\n)') 35 | 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/link.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | PUNCTUATION_CHARS = "_.:,;!?-" 28 | LINK_ID_CHARS = /[a-zA-Z0-9 #{PUNCTUATION_CHARS}]/ 29 | LINK_ID_NON_CHARS = /[^a-zA-Z0-9 #{PUNCTUATION_CHARS}]/ 30 | LINK_DEFINITION_START = /^#{OPT_SPACE}\[(#{LINK_ID_CHARS}+)\]:[ \t]*(?:<(.*?)>|([^\s]+))[ \t]*?(?:\n?[ \t]*?(["'])(.+?)\4[ \t]*?)?\n/ 31 | 32 | # Parse the link definition at the current location. 33 | def parse_link_definition 34 | @src.pos += @src.matched_size 35 | link_id, link_url, link_title = @src[1].downcase, @src[2] || @src[3], @src[5] 36 | warning("Duplicate link ID '#{link_id}' - overwriting") if @doc.parse_infos[:link_defs][link_id] 37 | @doc.parse_infos[:link_defs][link_id] = [link_url, link_title] 38 | true 39 | end 40 | define_parser(:link_definition, LINK_DEFINITION_START) 41 | 42 | 43 | # This helper methods adds the approriate attributes to the element +el+ of type +a+ or +img+ 44 | # and the element itself to the @tree. 45 | def add_link(el, href, title, alt_text = nil) 46 | el.options[:attr] ||= {} 47 | el.options[:attr]['title'] = title if title 48 | if el.type == :a 49 | el.options[:attr]['href'] = href 50 | else 51 | el.options[:attr]['src'] = href 52 | el.options[:attr]['alt'] = alt_text 53 | el.children.clear 54 | end 55 | @tree.children << el 56 | end 57 | 58 | LINK_TEXT_BRACKET_RE = /\\\[|\\\]|\[|\]/ 59 | LINK_INLINE_ID_RE = /\s*?\[(#{LINK_ID_CHARS}+)?\]/ 60 | LINK_INLINE_TITLE_RE = /\s*?(["'])(.+?)\1\s*?\)/ 61 | LINK_START = /!?\[(?=[^^])/ 62 | 63 | # Parse the link at the current scanner position. This method is used to parse normal links as 64 | # well as image links. 65 | def parse_link 66 | result = @src.scan(LINK_START) 67 | reset_pos = @src.pos 68 | 69 | link_type = (result =~ /^!/ ? :img : :a) 70 | 71 | # no nested links allowed 72 | if link_type == :a && (@tree.type == :img || @tree.type == :a || @stack.any? {|t,s| t && (t.type == :img || t.type == :a)}) 73 | add_text(result) 74 | return 75 | end 76 | el = Element.new(link_type) 77 | 78 | stop_re = /\]|!?\[/ 79 | count = 1 80 | found = parse_spans(el, stop_re) do 81 | case @src.matched 82 | when "[", "![" 83 | count += 1 84 | when "]" 85 | count -= 1 86 | end 87 | count - el.children.select {|c| c.type == :img}.size == 0 88 | end 89 | if !found || (link_type == :a && el.children.empty?) 90 | @src.pos = reset_pos 91 | add_text(result) 92 | return 93 | end 94 | alt_text = extract_string(reset_pos...@src.pos, @src) 95 | conv_link_id = alt_text.gsub(/(\s|\n)+/m, ' ').gsub(LINK_ID_NON_CHARS, '').downcase 96 | @src.scan(stop_re) 97 | 98 | # reference style link or no link url 99 | if @src.scan(LINK_INLINE_ID_RE) || !@src.check(/\(/) 100 | link_id = (@src[1] || conv_link_id).downcase 101 | if link_id.empty? 102 | @src.pos = reset_pos 103 | add_text(result) 104 | elsif @doc.parse_infos[:link_defs].has_key?(link_id) 105 | add_link(el, @doc.parse_infos[:link_defs][link_id].first, @doc.parse_infos[:link_defs][link_id].last, alt_text) 106 | else 107 | warning("No link definition for link ID '#{link_id}' found") 108 | @src.pos = reset_pos 109 | add_text(result) 110 | end 111 | return 112 | end 113 | 114 | # link url in parentheses 115 | if @src.scan(/\(<(.*?)>/) 116 | link_url = @src[1] 117 | if @src.scan(/\)/) 118 | add_link(el, link_url, nil, alt_text) 119 | return 120 | end 121 | else 122 | link_url = '' 123 | re = /\(|\)|\s/ 124 | nr_of_brackets = 0 125 | while temp = @src.scan_until(re) 126 | link_url += temp 127 | case @src.matched 128 | when /\s/ 129 | break 130 | when '(' 131 | nr_of_brackets += 1 132 | when ')' 133 | nr_of_brackets -= 1 134 | break if nr_of_brackets == 0 135 | end 136 | end 137 | link_url = link_url[1..-2] 138 | 139 | if nr_of_brackets == 0 140 | add_link(el, link_url, nil, alt_text) 141 | return 142 | end 143 | end 144 | 145 | if @src.scan(LINK_INLINE_TITLE_RE) 146 | add_link(el, link_url, @src[2], alt_text) 147 | else 148 | @src.pos = reset_pos 149 | add_text(result) 150 | end 151 | end 152 | define_parser(:link, LINK_START, '!?\[') 153 | 154 | end 155 | end 156 | end 157 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/list.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'kramdown/parser/kramdown/blank_line' 24 | require 'kramdown/parser/kramdown/eob' 25 | require 'kramdown/parser/kramdown/horizontal_rule' 26 | require 'kramdown/parser/kramdown/attribute_list' 27 | 28 | module Kramdown 29 | module Parser 30 | class Kramdown 31 | 32 | # Used for parsing the first line of a list item or a definition, i.e. the line with list item 33 | # marker or the definition marker. 34 | def parse_first_list_line(indentation, content) 35 | if content =~ /^\s*(#{IAL_SPAN_START})?\s*\n/ 36 | indentation = 4 37 | else 38 | while content =~ /^ *\t/ 39 | temp = content.scan(/^ */).first.length + indentation 40 | content.sub!(/^( *)(\t+)/) {$1 + " "*(4 - (temp % 4)) + " "*($2.length - 1)*4} 41 | end 42 | indentation += content.scan(/^ */).first.length 43 | end 44 | content.sub!(/^\s*/, '') 45 | 46 | indent_re = /^ {#{indentation}}/ 47 | content_re = /^(?:(?:\t| {4}){#{indentation / 4}} {#{indentation % 4}}|(?:\t| {4}){#{indentation / 4 + 1}}).*?\n/ 48 | [content, indentation, content_re, indent_re] 49 | end 50 | 51 | 52 | LIST_START_UL = /^(#{OPT_SPACE}[+*-])([\t| ].*?\n)/ 53 | LIST_START_OL = /^(#{OPT_SPACE}\d+\.)([\t| ].*?\n)/ 54 | LIST_START = /#{LIST_START_UL}|#{LIST_START_OL}/ 55 | 56 | # Parse the ordered or unordered list at the current location. 57 | def parse_list 58 | if @tree.children.last && @tree.children.last.type == :p # last element must not be a paragraph 59 | return false 60 | end 61 | 62 | type, list_start_re = (@src.check(LIST_START_UL) ? [:ul, LIST_START_UL] : [:ol, LIST_START_OL]) 63 | list = new_block_el(type) 64 | 65 | item = nil 66 | indent_re = nil 67 | content_re = nil 68 | eob_found = false 69 | nested_list_found = false 70 | while !@src.eos? 71 | if @src.check(HR_START) 72 | break 73 | elsif @src.scan(list_start_re) 74 | item = Element.new(:li) 75 | item.value, indentation, content_re, indent_re = parse_first_list_line(@src[1].length, @src[2]) 76 | list.children << item 77 | 78 | item.value.sub!(/^#{IAL_SPAN_START}\s*/) do |match| 79 | parse_attribute_list($~[1], item.options[:ial] ||= {}) 80 | '' 81 | end 82 | 83 | list_start_re = (type == :ul ? /^( {0,#{[3, indentation - 1].min}}[+*-])([\t| ].*?\n)/ : 84 | /^( {0,#{[3, indentation - 1].min}}\d+\.)([\t| ].*?\n)/) 85 | nested_list_found = false 86 | elsif result = @src.scan(content_re) 87 | result.sub!(/^(\t+)/) { " "*4*($1 ? $1.length : 0) } 88 | result.sub!(indent_re, '') 89 | if !nested_list_found && result =~ LIST_START 90 | parse_blocks(item, item.value) 91 | if item.children.length == 1 && item.children.first.type == :p 92 | item.value = '' 93 | else 94 | item.children.clear 95 | end 96 | nested_list_found = true 97 | end 98 | item.value << result 99 | elsif result = @src.scan(BLANK_LINE) 100 | nested_list_found = true 101 | item.value << result 102 | elsif @src.scan(EOB_MARKER) 103 | eob_found = true 104 | break 105 | else 106 | break 107 | end 108 | end 109 | 110 | @tree.children << list 111 | 112 | last = nil 113 | list.children.each do |it| 114 | temp = Element.new(:temp) 115 | parse_blocks(temp, it.value) 116 | it.children += temp.children 117 | it.value = nil 118 | next if it.children.size == 0 119 | 120 | if it.children.first.type == :p && (it.children.length < 2 || it.children[1].type != :blank || 121 | (it == list.children.last && it.children.length == 2 && !eob_found)) && 122 | (list.children.last != it || list.children.size == 1 || 123 | list.children[0..-2].any? {|cit| cit.children.first.type != :p || cit.children.first.options[:transparent]}) 124 | it.children.first.children.first.value += "\n" if it.children.size > 1 && it.children[1].type != :blank 125 | it.children.first.options[:transparent] = true 126 | end 127 | 128 | if it.children.last.type == :blank 129 | last = it.children.pop 130 | else 131 | last = nil 132 | end 133 | end 134 | 135 | @tree.children << last if !last.nil? && !eob_found 136 | 137 | true 138 | end 139 | define_parser(:list, LIST_START) 140 | 141 | 142 | DEFINITION_LIST_START = /^(#{OPT_SPACE}:)([\t| ].*?\n)/ 143 | 144 | # Parse the ordered or unordered list at the current location. 145 | def parse_definition_list 146 | children = @tree.children 147 | if !children.last || (children.length == 1 && children.last.type != :p ) || 148 | (children.length >= 2 && children[-1].type != :p && (children[-1].type != :blank || children[-1].value != "\n" || children[-2].type != :p)) 149 | return false 150 | end 151 | 152 | first_as_para = false 153 | deflist = new_block_el(:dl) 154 | para = @tree.children.pop 155 | if para.type == :blank 156 | para = @tree.children.pop 157 | first_as_para = true 158 | end 159 | para.children.first.value.split("\n").each do |term| 160 | el = Element.new(:dt) 161 | el.children << Element.new(:raw_text, term) 162 | deflist.children << el 163 | end 164 | 165 | item = nil 166 | indent_re = nil 167 | content_re = nil 168 | def_start_re = DEFINITION_LIST_START 169 | while !@src.eos? 170 | if @src.scan(def_start_re) 171 | item = Element.new(:dd) 172 | item.options[:first_as_para] = first_as_para 173 | item.value, indentation, content_re, indent_re = parse_first_list_line(@src[1].length, @src[2]) 174 | deflist.children << item 175 | 176 | item.value.sub!(/^#{IAL_SPAN_START}\s*/) do |match| 177 | parse_attribute_list($~[1], item.options[:ial] ||= {}) 178 | '' 179 | end 180 | 181 | def_start_re = /^( {0,#{[3, indentation - 1].min}}:)([\t| ].*?\n)/ 182 | first_as_para = false 183 | elsif result = @src.scan(content_re) 184 | result.sub!(/^(\t+)/) { " "*4*($1 ? $1.length : 0) } 185 | result.sub!(indent_re, '') 186 | item.value << result 187 | first_as_para = false 188 | elsif result = @src.scan(BLANK_LINE) 189 | first_as_para = true 190 | item.value << result 191 | else 192 | break 193 | end 194 | end 195 | 196 | last = nil 197 | deflist.children.each do |it| 198 | next if it.type == :dt 199 | 200 | parse_blocks(it, it.value) 201 | it.value = nil 202 | next if it.children.size == 0 203 | 204 | if it.children.last.type == :blank 205 | last = it.children.pop 206 | else 207 | last = nil 208 | end 209 | if it.children.first.type == :p && !it.options.delete(:first_as_para) 210 | it.children.first.children.first.value += "\n" if it.children.size > 1 211 | it.children.first.options[:transparent] = true 212 | end 213 | end 214 | 215 | if @tree.children.length >= 1 && @tree.children.last.type == :dl 216 | @tree.children[-1].children += deflist.children 217 | elsif @tree.children.length >= 2 && @tree.children[-1].type == :blank && @tree.children[-2].type == :dl 218 | @tree.children.pop 219 | @tree.children[-1].children += deflist.children 220 | else 221 | @tree.children << deflist 222 | end 223 | 224 | @tree.children << last if !last.nil? 225 | 226 | true 227 | end 228 | define_parser(:definition_list, DEFINITION_LIST_START) 229 | 230 | end 231 | end 232 | end 233 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/math.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | BLOCK_MATH_START = /^#{OPT_SPACE}(\\)?\$\$(.*?)\$\$\s*?\n/m 28 | 29 | # Parse the math block at the current location. 30 | def parse_block_math 31 | if @src[1] 32 | @src.scan(/^#{OPT_SPACE}\\/) 33 | return false 34 | end 35 | @src.pos += @src.matched_size 36 | @tree.children << new_block_el(:math, @src[2], :category => :block) 37 | true 38 | end 39 | define_parser(:block_math, BLOCK_MATH_START) 40 | 41 | 42 | INLINE_MATH_START = /\$\$(.*?)\$\$/ 43 | 44 | # Parse the inline math at the current location. 45 | def parse_inline_math 46 | @src.pos += @src.matched_size 47 | @tree.children << Element.new(:math, @src[1], :category => :span) 48 | end 49 | define_parser(:inline_math, INLINE_MATH_START, '\$') 50 | 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/paragraph.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | PARAGRAPH_START = /^#{OPT_SPACE}[^ \t].*?\n/ 28 | 29 | # Parse the paragraph at the current location. 30 | def parse_paragraph 31 | @src.pos += @src.matched_size 32 | if @tree.children.last && @tree.children.last.type == :p 33 | @tree.children.last.children.first.value << "\n" << @src.matched.chomp 34 | else 35 | @tree.children << new_block_el(:p) 36 | add_text(@src.matched.lstrip.chomp, @tree.children.last) 37 | end 38 | true 39 | end 40 | define_parser(:paragraph, PARAGRAPH_START) 41 | 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/smart_quotes.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | #-- 23 | # Parts of this file are based on code from Maruku by Andrea Censi. 24 | # The needed license statements follow: 25 | # 26 | # Copyright (C) 2006 Andrea Censi 27 | # 28 | # Maruku is free software; you can redistribute it and/or modify 29 | # it under the terms of the GNU General Public License as published by 30 | # the Free Software Foundation; either version 2 of the License, or 31 | # (at your option) any later version. 32 | # 33 | # Maruku is distributed in the hope that it will be useful, 34 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 35 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 36 | # GNU General Public License for more details. 37 | # 38 | # You should have received a copy of the GNU General Public License 39 | # along with Maruku; if not, write to the Free Software 40 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 41 | # 42 | # NOTA BENE: 43 | # 44 | # The following algorithm is a rip-off of RubyPants written by 45 | # Christian Neukirchen. 46 | # 47 | # RubyPants is a Ruby port of SmartyPants written by John Gruber. 48 | # 49 | # This file is distributed under the GPL, which I guess is compatible 50 | # with the terms of the RubyPants license. 51 | # 52 | # -- Andrea Censi 53 | # 54 | # = RubyPants -- SmartyPants ported to Ruby 55 | # 56 | # Ported by Christian Neukirchen 57 | # Copyright (C) 2004 Christian Neukirchen 58 | # 59 | # Incooporates ideas, comments and documentation by Chad Miller 60 | # Copyright (C) 2004 Chad Miller 61 | # 62 | # Original SmartyPants by John Gruber 63 | # Copyright (C) 2003 John Gruber 64 | # 65 | # 66 | # = RubyPants -- SmartyPants ported to Ruby 67 | # 68 | # 69 | # [snip] 70 | # 71 | # == Authors 72 | # 73 | # John Gruber did all of the hard work of writing this software in 74 | # Perl for Movable Type and almost all of this useful documentation. 75 | # Chad Miller ported it to Python to use with Pyblosxom. 76 | # 77 | # Christian Neukirchen provided the Ruby port, as a general-purpose 78 | # library that follows the *Cloth API. 79 | # 80 | # 81 | # == Copyright and License 82 | # 83 | # === SmartyPants license: 84 | # 85 | # Copyright (c) 2003 John Gruber 86 | # (http://daringfireball.net) 87 | # All rights reserved. 88 | # 89 | # Redistribution and use in source and binary forms, with or without 90 | # modification, are permitted provided that the following conditions 91 | # are met: 92 | # 93 | # * Redistributions of source code must retain the above copyright 94 | # notice, this list of conditions and the following disclaimer. 95 | # 96 | # * Redistributions in binary form must reproduce the above copyright 97 | # notice, this list of conditions and the following disclaimer in 98 | # the documentation and/or other materials provided with the 99 | # distribution. 100 | # 101 | # * Neither the name "SmartyPants" nor the names of its contributors 102 | # may be used to endorse or promote products derived from this 103 | # software without specific prior written permission. 104 | # 105 | # This software is provided by the copyright holders and contributors 106 | # "as is" and any express or implied warranties, including, but not 107 | # limited to, the implied warranties of merchantability and fitness 108 | # for a particular purpose are disclaimed. In no event shall the 109 | # copyright owner or contributors be liable for any direct, indirect, 110 | # incidental, special, exemplary, or consequential damages (including, 111 | # but not limited to, procurement of substitute goods or services; 112 | # loss of use, data, or profits; or business interruption) however 113 | # caused and on any theory of liability, whether in contract, strict 114 | # liability, or tort (including negligence or otherwise) arising in 115 | # any way out of the use of this software, even if advised of the 116 | # possibility of such damage. 117 | # 118 | # === RubyPants license 119 | # 120 | # RubyPants is a derivative work of SmartyPants and smartypants.py. 121 | # 122 | # Redistribution and use in source and binary forms, with or without 123 | # modification, are permitted provided that the following conditions 124 | # are met: 125 | # 126 | # * Redistributions of source code must retain the above copyright 127 | # notice, this list of conditions and the following disclaimer. 128 | # 129 | # * Redistributions in binary form must reproduce the above copyright 130 | # notice, this list of conditions and the following disclaimer in 131 | # the documentation and/or other materials provided with the 132 | # distribution. 133 | # 134 | # This software is provided by the copyright holders and contributors 135 | # "as is" and any express or implied warranties, including, but not 136 | # limited to, the implied warranties of merchantability and fitness 137 | # for a particular purpose are disclaimed. In no event shall the 138 | # copyright owner or contributors be liable for any direct, indirect, 139 | # incidental, special, exemplary, or consequential damages (including, 140 | # but not limited to, procurement of substitute goods or services; 141 | # loss of use, data, or profits; or business interruption) however 142 | # caused and on any theory of liability, whether in contract, strict 143 | # liability, or tort (including negligence or otherwise) arising in 144 | # any way out of the use of this software, even if advised of the 145 | # possibility of such damage. 146 | # 147 | # == Links 148 | # 149 | # John Gruber:: http://daringfireball.net 150 | # SmartyPants:: http://daringfireball.net/projects/smartypants 151 | # 152 | # Chad Miller:: http://web.chad.org 153 | # 154 | # Christian Neukirchen:: http://kronavita.de/chris 155 | # 156 | #++ 157 | # 158 | 159 | module Kramdown 160 | module Parser 161 | class Kramdown 162 | 163 | SQ_PUNCT = '[!"#\$\%\'()*+,\-.\/:;<=>?\@\[\\\\\]\^_`{|}~]' 164 | SQ_CLOSE = %![^\ \\\\\t\r\n\\[{(-]! 165 | 166 | SQ_RULES = [ 167 | [/("|')(?=#{SQ_PUNCT}\B)/, [:rquote1]], 168 | # Special case for double sets of quotes, e.g.: 169 | #

He said, "'Quoted' words in a larger quote."

170 | [/(\s?)"'(?=\w)/, [1, :ldquo, :lsquo]], 171 | [/(\s?)'"(?=\w)/, [1, :lsquo, :ldquo]], 172 | # Special case for decade abbreviations (the '80s): 173 | [/(\s?)'(?=\d\ds)/, [1, :rsquo]], 174 | 175 | # Get most opening single/double quotes: 176 | [/(\s)('|")(?=\w)/, [1, :lquote2]], 177 | # Single/double closing quotes: 178 | [/(#{SQ_CLOSE})('|")/, [1, :rquote2]], 179 | # Special case for e.g. "Custer's Last Stand." 180 | [/("|')(\s|s\b|$)/, [:rquote1, 2]], 181 | # Any remaining single quotes should be opening ones: 182 | [/(.?)'/m, [1, :lsquo]], 183 | [/(.?)"/m, [1, :ldquo]], 184 | ] #'" 185 | 186 | SQ_SUBSTS = { 187 | [:rquote1, '"'] => :rdquo, 188 | [:rquote1, "'"] => :rsquo, 189 | [:rquote2, '"'] => :rdquo, 190 | [:rquote2, "'"] => :rsquo, 191 | [:lquote1, '"'] => :ldquo, 192 | [:lquote1, "'"] => :lsquo, 193 | [:lquote2, '"'] => :ldquo, 194 | [:lquote2, "'"] => :lsquo, 195 | } 196 | SMART_QUOTES_RE = /[^\\]?["']/ 197 | 198 | # Parse the smart quotes at current location. 199 | def parse_smart_quotes 200 | regexp, substs = SQ_RULES.find {|reg, subst| @src.scan(reg)} 201 | substs.each do |subst| 202 | if subst.kind_of?(Integer) 203 | add_text(@src[subst].to_s) 204 | else 205 | val = SQ_SUBSTS[[subst, @src[subst.to_s[-1,1].to_i]]] || subst 206 | @tree.children << Element.new(:smart_quote, val) 207 | end 208 | end 209 | end 210 | define_parser(:smart_quotes, SMART_QUOTES_RE, '[^\\\\]?["\']') 211 | 212 | end 213 | end 214 | end 215 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/table.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | require 'kramdown/parser/kramdown/blank_line' 24 | require 'kramdown/parser/kramdown/eob' 25 | require 'kramdown/parser/kramdown/horizontal_rule' 26 | 27 | module Kramdown 28 | module Parser 29 | class Kramdown 30 | 31 | TABLE_SEP_LINE = /^#{OPT_SPACE}(?:\||\+)([ ]?:?-[+|: -]*)[ \t]*\n/ 32 | TABLE_HSEP_ALIGN = /[ ]?(:?)-+(:?)[ ]?/ 33 | TABLE_FSEP_LINE = /^#{OPT_SPACE}(\||\+)[ ]?:?=[+|: =]*[ \t]*\n/ 34 | TABLE_ROW_LINE = /^#{OPT_SPACE}\|(.*?)[ \t]*\n/ 35 | TABLE_START = /^#{OPT_SPACE}\|(?:-|(?!=))/ 36 | 37 | # Parse the table at the current location. 38 | def parse_table 39 | orig_pos = @src.pos 40 | table = new_block_el(:table, nil, :alignment => []) 41 | 42 | @src.scan(TABLE_SEP_LINE) 43 | 44 | rows = [] 45 | has_footer = false 46 | columns = 0 47 | 48 | add_container = lambda do |type, force| 49 | if force || type != :tbody || !has_footer 50 | cont = Element.new(type) 51 | cont.children, rows = rows, [] 52 | table.children << cont 53 | end 54 | end 55 | 56 | while !@src.eos? 57 | if @src.scan(TABLE_SEP_LINE) && !rows.empty? 58 | if table.options[:alignment].empty? && !has_footer 59 | add_container.call(:thead, false) 60 | table.options[:alignment] = @src[1].scan(TABLE_HSEP_ALIGN).map do |left, right| 61 | (left.empty? && right.empty? && :default) || (right.empty? && :left) || (left.empty? && :right) || :center 62 | end 63 | else # treat as normal separator line 64 | add_container.call(:tbody, false) 65 | end 66 | elsif @src.scan(TABLE_FSEP_LINE) 67 | add_container.call(:tbody, true) if !rows.empty? 68 | has_footer = true 69 | elsif @src.scan(TABLE_ROW_LINE) 70 | trow = Element.new(:tr) 71 | cells = (@src[1] + ' ').split(/\|/) 72 | i = 0 73 | while i < cells.length - 1 74 | backslashes = cells[i].scan(/\\+$/).first 75 | if backslashes && backslashes.length % 2 == 1 76 | cells[i] = cells[i].chop + '|' + cells[i+1] 77 | cells.delete_at(i+1) 78 | else 79 | i += 1 80 | end 81 | end 82 | cells.pop if cells.last.strip.empty? 83 | cells.each do |cell_text| 84 | tcell = Element.new(:td) 85 | tcell.children << Element.new(:raw_text, cell_text.strip) 86 | trow.children << tcell 87 | end 88 | columns = [columns, cells.length].max 89 | rows << trow 90 | else 91 | break 92 | end 93 | end 94 | 95 | add_container.call(has_footer ? :tfoot : :tbody, false) if !rows.empty? 96 | 97 | if !table.children.any? {|c| c.type == :tbody} 98 | warning("Found table without body - ignoring it") 99 | @src.pos = orig_pos 100 | return false 101 | end 102 | 103 | # adjust all table rows to have equal number of columns, same for alignment defs 104 | table.children.each do |kind| 105 | kind.children.each do |row| 106 | (columns - row.children.length).times do 107 | row.children << Element.new(:td) 108 | end 109 | row.children.each {|el| el.type = :th} if kind.type == :thead 110 | end 111 | end 112 | if table.options[:alignment].length > columns 113 | table.options[:alignment] = table.options[:alignment][0...columns] 114 | else 115 | table.options[:alignment] += [:default] * (columns - table.options[:alignment].length) 116 | end 117 | 118 | @tree.children << table 119 | 120 | true 121 | end 122 | define_parser(:table, TABLE_START) 123 | 124 | end 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/parser/kramdown/typographic_symbol.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | module Parser 25 | class Kramdown 26 | 27 | TYPOGRAPHIC_SYMS = [['---', :mdash], ['--', :ndash], ['...', :hellip], 28 | ['\\<<', '<<'], ['\\>>', '>>'], 29 | ['<< ', :laquo_space], [' >>', :raquo_space], 30 | ['<<', :laquo], ['>>', :raquo]] 31 | TYPOGRAPHIC_SYMS_SUBST = Hash[*TYPOGRAPHIC_SYMS.flatten] 32 | TYPOGRAPHIC_SYMS_RE = /#{TYPOGRAPHIC_SYMS.map {|k,v| Regexp.escape(k)}.join('|')}/ 33 | 34 | # Parse the typographic symbols at the current location. 35 | def parse_typographic_syms 36 | @src.pos += @src.matched_size 37 | val = TYPOGRAPHIC_SYMS_SUBST[@src.matched] 38 | if val.kind_of?(Symbol) 39 | @tree.children << Element.new(:typographic_sym, val) 40 | elsif @src.matched == '\\<<' 41 | @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('lt')) 42 | @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('lt')) 43 | else 44 | @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('gt')) 45 | @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('gt')) 46 | end 47 | end 48 | define_parser(:typographic_syms, TYPOGRAPHIC_SYMS_RE, '--|\\.\\.\\.|(?:\\\\| )?(?:<<|>>)') 49 | 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/utils.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | 25 | # == Utils Module 26 | # 27 | # This module contains utility class/modules/methods that can be used by both parsers and 28 | # converters. 29 | module Utils 30 | 31 | autoload :Entities, 'kramdown/utils/entities' 32 | autoload :HTML, 'kramdown/utils/html' 33 | 34 | end 35 | 36 | end 37 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/utils/entities.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | 25 | module Utils 26 | 27 | module Entities 28 | 29 | class Entity < Struct.new(:code_point, :name) 30 | 31 | def char 32 | [code_point].pack('U*') rescue nil 33 | end 34 | 35 | end 36 | 37 | ENTITY_TABLE = [ 38 | [913, 'Alpha'], 39 | [914, 'Beta'], 40 | [915, 'Gamma'], 41 | [916, 'Delta'], 42 | [917, 'Epsilon'], 43 | [918, 'Zeta'], 44 | [919, 'Eta'], 45 | [920, 'Theta'], 46 | [921, 'Iota'], 47 | [922, 'Kappa'], 48 | [923, 'Lambda'], 49 | [924, 'Mu'], 50 | [925, 'Nu'], 51 | [926, 'Xi'], 52 | [927, 'Omicron'], 53 | [928, 'Pi'], 54 | [929, 'Rho'], 55 | [931, 'Sigma'], 56 | [932, 'Tau'], 57 | [933, 'Upsilon'], 58 | [934, 'Phi'], 59 | [935, 'Chi'], 60 | [936, 'Psi'], 61 | [937, 'Omega'], 62 | [945, 'alpha'], 63 | [946, 'beta'], 64 | [947, 'gamma'], 65 | [948, 'delta'], 66 | [949, 'epsilon'], 67 | [950, 'zeta'], 68 | [951, 'eta'], 69 | [952, 'theta'], 70 | [953, 'iota'], 71 | [954, 'kappa'], 72 | [955, 'lambda'], 73 | [956, 'mu'], 74 | [957, 'nu'], 75 | [958, 'xi'], 76 | [959, 'omicron'], 77 | [960, 'pi'], 78 | [961, 'rho'], 79 | [963, 'sigma'], 80 | [964, 'tau'], 81 | [965, 'upsilon'], 82 | [966, 'phi'], 83 | [967, 'chi'], 84 | [968, 'psi'], 85 | [969, 'omega'], 86 | [962, 'sigmaf'], 87 | [977, 'thetasym'], 88 | [982, 'piv'], 89 | [8230, 'hellip'], 90 | [8242, 'prime'], 91 | [8254, 'oline'], 92 | [8260, 'frasl'], 93 | [8472, 'weierp'], 94 | [8465, 'image'], 95 | [8476, 'real'], 96 | [8501, 'alefsym'], 97 | [8226, 'bull'], 98 | [8482, 'trade'], 99 | [8592, 'larr'], 100 | [8594, 'rarr'], 101 | [8593, 'uarr'], 102 | [8595, 'darr'], 103 | [8596, 'harr'], 104 | [8629, 'crarr'], 105 | [8657, 'uArr'], 106 | [8659, 'dArr'], 107 | [8656, 'lArr'], 108 | [8658, 'rArr'], 109 | [8660, 'hArr'], 110 | [8704, 'forall'], 111 | [8706, 'part'], 112 | [8707, 'exist'], 113 | [8709, 'empty'], 114 | [8711, 'nabla'], 115 | [8712, 'isin'], 116 | [8715, 'ni'], 117 | [8713, 'notin'], 118 | [8721, 'sum'], 119 | [8719, 'prod'], 120 | [8722, 'minus'], 121 | [8727, 'lowast'], 122 | [8730, 'radic'], 123 | [8733, 'prop'], 124 | [8734, 'infin'], 125 | [8736, 'ang'], 126 | [8743, 'and'], 127 | [8744, 'or'], 128 | [8745, 'cup'], 129 | [8746, 'cap'], 130 | [8747, 'int'], 131 | [8756, 'there4'], 132 | [8764, 'sim'], 133 | [8776, 'asymp'], 134 | [8773, 'cong'], 135 | [8800, 'ne'], 136 | [8801, 'equiv'], 137 | [8804, 'le'], 138 | [8805, 'ge'], 139 | [8834, 'sub'], 140 | [8835, 'sup'], 141 | [8838, 'sube'], 142 | [8839, 'supe'], 143 | [8836, 'nsub'], 144 | [8853, 'oplus'], 145 | [8855, 'otimes'], 146 | [8869, 'perp'], 147 | [8901, 'sdot'], 148 | [8968, 'rceil'], 149 | [8969, 'lceil'], 150 | [8970, 'lfloor'], 151 | [8971, 'rfloor'], 152 | [9001, 'rang'], 153 | [9002, 'lang'], 154 | [9674, 'loz'], 155 | [9824, 'spades'], 156 | [9827, 'clubs'], 157 | [9829, 'hearts'], 158 | [9830, 'diams'], 159 | [38, 'amp'], 160 | [34, 'quot'], 161 | [39, 'apos'], 162 | [169, 'copy'], 163 | [60, 'lt'], 164 | [62, 'gt'], 165 | [338, 'OElig'], 166 | [339, 'oelig'], 167 | [352, 'Scaron'], 168 | [353, 'scaron'], 169 | [376, 'Yuml'], 170 | [710, 'circ'], 171 | [732, 'tilde'], 172 | [8211, 'ndash'], 173 | [8212, 'mdash'], 174 | [8216, 'lsquo'], 175 | [8217, 'rsquo'], 176 | [8220, 'ldquo'], 177 | [8221, 'rdquo'], 178 | [8224, 'dagger'], 179 | [8225, 'Dagger'], 180 | [8240, 'permil'], 181 | [8364, 'euro'], 182 | [8249, 'lsaquo'], 183 | [8250, 'rsaquo'], 184 | [160, 'nbsp'], 185 | [161, 'iexcl'], 186 | [163, 'pound'], 187 | [164, 'curren'], 188 | [165, 'yen'], 189 | [166, 'brvbar'], 190 | [167, 'sect'], 191 | [171, 'laquo'], 192 | [187, 'raquo'], 193 | [174, 'reg'], 194 | [170, 'ordf'], 195 | [172, 'not'], 196 | [176, 'deg'], 197 | [177, 'plusmn'], 198 | [180, 'acute'], 199 | [181, 'micro'], 200 | [182, 'para'], 201 | [183, 'middot'], 202 | [186, 'ordm'], 203 | [162, 'cent'], 204 | [185, 'sup1'], 205 | [178, 'sup2'], 206 | [179, 'sup3'], 207 | [189, 'frac12'], 208 | [188, 'frac14'], 209 | [190, 'frac34'], 210 | [192, 'Agrave'], 211 | [193, 'Aacute'], 212 | [194, 'Acirc'], 213 | [195, 'Atilde'], 214 | [196, 'Auml'], 215 | [197, 'Aring'], 216 | [198, 'AElig'], 217 | [199, 'Ccedil'], 218 | [200, 'Egrave'], 219 | [201, 'Eacute'], 220 | [202, 'Ecirc'], 221 | [203, 'Euml'], 222 | [204, 'Igrave'], 223 | [205, 'Iacute'], 224 | [206, 'Icirc'], 225 | [207, 'Iuml'], 226 | [208, 'ETH'], 227 | [209, 'Ntilde'], 228 | [210, 'Ograve'], 229 | [211, 'Oacute'], 230 | [212, 'Ocirc'], 231 | [213, 'Otilde'], 232 | [214, 'Ouml'], 233 | [215, 'times'], 234 | [216, 'Oslash'], 235 | [217, 'Ugrave'], 236 | [218, 'Uacute'], 237 | [219, 'Ucirc'], 238 | [220, 'Uuml'], 239 | [221, 'Yacute'], 240 | [222, 'THORN'], 241 | [223, 'szlig'], 242 | [224, 'agrave'], 243 | [225, 'aacute'], 244 | [226, 'acirc'], 245 | [227, 'atilde'], 246 | [228, 'auml'], 247 | [229, 'aring'], 248 | [230, 'aelig'], 249 | [231, 'ccedil'], 250 | [232, 'egrave'], 251 | [233, 'eacute'], 252 | [234, 'ecirc'], 253 | [235, 'euml'], 254 | [236, 'igrave'], 255 | [237, 'iacute'], 256 | [238, 'icirc'], 257 | [239, 'iuml'], 258 | [240, 'eth'], 259 | [241, 'ntilde'], 260 | [242, 'ograve'], 261 | [243, 'oacute'], 262 | [244, 'ocirc'], 263 | [245, 'otilde'], 264 | [246, 'ouml'], 265 | [247, 'divide'], 266 | [248, 'oslash'], 267 | [249, 'ugrave'], 268 | [250, 'uacute'], 269 | [251, 'ucirc'], 270 | [252, 'uuml'], 271 | [253, 'yacute'], 272 | [254, 'thorn'], 273 | [255, 'yuml'], 274 | 275 | [8218, 'sbquo'], 276 | [402, 'fnof'], 277 | [8222, 'bdquo'], 278 | [381, 'Zcaron'], 279 | [382, 'zcaron'], 280 | 281 | [128, 8364], 282 | [130, 8218], 283 | [131, 402], 284 | [132, 8222], 285 | [133, 8230], 286 | [134, 8224], 287 | [135, 8225], 288 | [136, 710], 289 | [137, 8240], 290 | [138, 352], 291 | [139, 8249], 292 | [140, 338], 293 | [142, 381], 294 | [145, 8216], 295 | [146, 8217], 296 | [147, 8220], 297 | [148, 8221], 298 | [149, 8226], 299 | [150, 8211], 300 | [151, 8212], 301 | [152, 732], 302 | [153, 8482], 303 | [154, 353], 304 | [155, 8250], 305 | [156, 339], 306 | [158, 382], 307 | [159, 376], 308 | ] 309 | ENTITY_MAP = Hash.new do |h,k| 310 | if k.kind_of?(Integer) 311 | h[k] = Entity.new(k, nil) 312 | else 313 | raise Kramdown::Error, "Can't handle generic non-integer character reference '#{k}'" 314 | end 315 | end 316 | 317 | ENTITY_TABLE.each do |code_point, data| 318 | if data.kind_of?(String) 319 | ENTITY_MAP[code_point] = ENTITY_MAP[data] = Entity.new(code_point, data) 320 | else 321 | raise "No entity object for code point #{data} found" unless ENTITY_MAP.has_key?(data) 322 | ENTITY_MAP[code_point] = ENTITY_MAP[data] 323 | end 324 | end 325 | 326 | # Return the entity for the given +point_or_name+. 327 | def entity(point_or_name) 328 | ENTITY_MAP[point_or_name] 329 | end 330 | 331 | module_function :entity 332 | 333 | end 334 | 335 | end 336 | 337 | end 338 | 339 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/utils/html.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | 25 | module Utils 26 | 27 | module HTML 28 | 29 | # Convert the +entity+ to a string. 30 | def entity_to_str(e) 31 | if RUBY_VERSION >= '1.9' && (c = e.char.encode(@doc.parse_infos[:encoding]) rescue nil) && !ESCAPE_MAP.has_key?(c) 32 | c 33 | elsif @doc.options[:numeric_entities] || e.name.nil? 34 | "&##{e.code_point};" 35 | else 36 | "&#{e.name};" 37 | end 38 | end 39 | 40 | # Return the string with the attributes of the element +el+. 41 | def html_attributes(el) 42 | (el.options[:attr] || {}).map {|k,v| v.nil? ? '' : " #{k}=\"#{escape_html(v.to_s, :no_entities)}\"" }.sort.join('') 43 | end 44 | 45 | ESCAPE_MAP = { 46 | '<' => '<', 47 | '>' => '>', 48 | '&' => '&', 49 | '"' => '"' 50 | } 51 | ESCAPE_ALL_RE = Regexp.union(*ESCAPE_MAP.collect {|k,v| k}) 52 | ESCAPE_NO_ENTITIES_RE = Regexp.union(REXML::Parsers::BaseParser::REFERENCE_RE, ESCAPE_ALL_RE) 53 | ESCAPE_NORMAL = Regexp.union(REXML::Parsers::BaseParser::REFERENCE_RE, /<|>|&/) 54 | ESCAPE_RE_FROM_TYPE = { 55 | :all => ESCAPE_ALL_RE, 56 | :no_entities => ESCAPE_NO_ENTITIES_RE, 57 | :text => ESCAPE_NORMAL 58 | } 59 | 60 | # Escape the special HTML characters in the string +str+. The parameter +type+ specifies what 61 | # is escaped: :all - all special HTML characters as well as entities, 62 | # :no_entities - all special HTML characters but no entities, :text - all 63 | # special HTML characters except the quotation mark but no entities. 64 | def escape_html(str, type = :all) 65 | str.gsub(ESCAPE_RE_FROM_TYPE[type]) {|m| ESCAPE_MAP[m] || m} 66 | end 67 | 68 | end 69 | 70 | end 71 | 72 | end 73 | -------------------------------------------------------------------------------- /plugin/vim-markdown-preview/kramdown/version.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | #-- 4 | # Copyright (C) 2009-2010 Thomas Leitner 5 | # 6 | # This file is part of kramdown. 7 | # 8 | # kramdown is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #++ 21 | # 22 | 23 | module Kramdown 24 | 25 | # The kramdown version. 26 | VERSION = '0.9.0' 27 | 28 | end 29 | -------------------------------------------------------------------------------- /plugin/vmp.vim: -------------------------------------------------------------------------------- 1 | function! PreviewMKD() 2 | ruby << RUBY 3 | 4 | 5 | VIM.evaluate('&runtimepath').split(',').each do |path| 6 | $LOAD_PATH.unshift(File.join(path, 'plugin', 'vim-markdown-preview')) 7 | end 8 | 9 | require('kramdown/kramdown') 10 | 11 | text = Array.new(VIM::Buffer.current.count) do |i| 12 | VIM::Buffer.current[i + 1] 13 | end.join("\n") 14 | 15 | VIM::Buffer.current.name.nil? ? (name = 'No Name.md') : (name = Vim::Buffer.current.name) 16 | 17 | preview_path = VIM.evaluate('&runtimepath').split(',').select{|path| path =~ /vim-markdown-preview/}.first 18 | cssfile = File.open("#{preview_path}/plugin/markdown-preview.css") 19 | style = cssfile.read 20 | 21 | layout = <<-LAYOUT 22 | 25 | 26 | 27 | 28 | 31 | 32 | #{File.basename(name)} 33 | 34 | 35 | 36 |

37 | #{File.basename(name)} 38 |

39 | 40 |
41 | #{Kramdown::Document.new(text).to_html} 42 |
43 | 44 | 45 | LAYOUT 46 | 47 | 48 | unless File.extname(name) =~ /\.(md|mkd|markdown)/ 49 | VIM.message('This file extension is not supported for Markdown previews') 50 | else 51 | file = File.join('/tmp', File.basename(name) + '.html') 52 | File.open('%s' % [ file ], 'w') { |f| f.write(layout) } 53 | Vim.command("silent !open '%s'" % [ file ]) 54 | end 55 | RUBY 56 | endfunction 57 | 58 | :command! Mm :call PreviewMKD() 59 | -------------------------------------------------------------------------------- /screenshots/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nelstrom/vim-markdown-preview/98844ac2d201fc4e9cfa0a43dfdba6f8dd39a181/screenshots/preview.png --------------------------------------------------------------------------------