Skip to content

loveencounterflow/remarkably

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

42 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Table of Contents generated with DocToc

ReMarkably

ReMarkably is a collection of MarkDown syntax extension to be used with the truly remarkable Remarkable MarkDown parser.

Install with

npm install --save remarkably

What is it for?

Finally, it's possible to easily extend your all-time favorite liteweight markup language, MarkDown.

Heretofore, it has been difficult to extend the syntax of markup languages because their parsers were not often written with extensibility in mind—as in programming languages, many markup designers are caught in a frame of thinking that makes them want to create the best language with the best syntax, the final one to rule them all.

LISPers were never interested in syntax, because, hey, what can surpass a homoiconic language (yes, they really call it that) with gazillions of brackets, right?

ReStructuredText (also once known as ReStructuredTextNG) used to be a strong contender to MarkDown, but IMHO it lost out because it's just too complicated—too many rules, and to setup a parser and just parse means jumping through too many Java-Enterprisy hoops (at least when i last checked five years ago). Is it extensible? Don't try at home.

In the Python community, extending syntax is seen as something dangerous, as an activity that can't be left to users but must be firmely policed by an inner circle of senior contributors. Given the fairly traditional Lex-YACC-Bison-ish tooling of the parser, that's even true because you'd have to make changes in like four or six files to add a trivial tidbit to the language, and then re-compile the entire thing, which makes development feel like you're back to mainframes and please come back tomorrow to pick up your printouts. Assuming you took that month-long upfront training class so you sorta know-what'cherdoing. Sort of. It's really difficult. And, having done that, anyone interested in your extension must download the entire source tree and compile that themselves, or else you must provide binary packages. It's not least this factor that has caused a lot of digital rot in the Python world, because compiling C sources tends to be much more fragile than relying on an 'interpreted' (i.e. low-level-compilation-free) idiom. In order to implement the tiniest of changes, you have to submit to a month-long or year-long period of intense scrutiny and deliberation, and your proposal will likely get downvoted.

Such strict procedures are necessary to uphold the quality of monolithic languages. After all, uncounted numbers of users will be confronted with your changes, and any addition to the language will likely be kept indefinitely because even correcting a mistake may break backward compatibility. JavaScript programmers know this very well: they have to live with lots of 'original sins' because their language was born under a very swiftly wandering star, and there was no time to correct oversights. Now those flaws are baked into the language, and any change for the better in JS core could potentially break many millions of websites.

As Douglas Crockford convincingly demonstrates in his 2013 MLOCJS Syntaxation talk, those times should be over. Languages should be extensible, and given the right tools, parsing can be much easier than it used to be.

Sadly, as of this writing (2014-11-09), there is very little documentation on how to extend remarkable, so the foremost purpose of ReMarkably is filling that gap. Anyone interested can fork the repo, develop their own extensions, and issue pull requests to make more syntax extensions available to the masses.

Usage

It's quite simple (using CoffeeScript here):

log         = console.log
RMY         = require 'remarkably'

enable      = 'full'
settings    =
  html:           yes,            # Enable HTML tags in source
  xhtmlOut:       no,             # Use '/' to close single tags (<br />)
  breaks:         no,             # Convert '\n' in paragraphs into <br>
  langPrefix:     'language-',    # CSS language prefix for fenced blocks
  linkify:        yes,            # Autoconvert URL-like text to links
  typographer:    yes,
  quotes:         '“”‘’'

RM                    = new RMY.ReMarkable enable, settings
RMY.use RM, video     = RMY.get.examples.video()
RMY.use RM, emphasis  = RMY.get.examples.emphasis()
RMY.use RM, emphasis2 = RMY.get.examples.emphasis2()
RMY.use RM, braces    = RMY.get.examples.brackets opener: '{',  closer: '}', arity: 2, name: 'braces'
RMY.use RM, angles    = RMY.get.examples.brackets opener: '<',  closer: '>', arity: 2, name: 'angles'
RMY.use RM, brackets  = RMY.get.examples.brackets opener: '[',  closer: ']', arity: 3, name: 'brackets-3'
RMY.use RM, smh       = RMY.get.examples.brackets opener: '',  closer: '', arity: 1, name: 'book-title'
source        = """
  =This= ==is== ===very=== _awesome_(c): %[example movie](http://example.com)
  *A* **B** ***C*** ****D****

  Here are
  * <<double pointy brackets>>,
  * {{double braces}},
  * [[[triple square brackets]]],
  * 也可以用 《中文書名号》 。
  """.trim()
log RM.render source

You can run the above with

remarkably/build && node remarkably/lib/main.js

which should output

<p><i>This</i> <b>is</b> <b><i>very</i></b> <em>awesome</em>©: <video href='http://example.com'>example movie</video>
<em>A</em> <strong>B</strong> <strong><em>C</em></strong> ****D****</p>
<p>Here are</p>
<ul>
<li><span class='angles'>double pointy brackets</span>,</li>
<li><span class='braces'>double braces</span>,</li>
<li><span class='brackets-3'>triple square brackets</span>,</li>
<li>也可以用 <span class='book-title'>中文書名号</span></li>
</ul>

Writing Your Own Extension

Paste and copy one of the existing sources. A dynamic extension (one that accepts parameters) should return an object with a get method. I'm in the middle of developing this, so details may change without notice; you probably want to copy from the avaible sources.

Here is an example for a dynamic extension that accepts an opening and a closing bracket character, an 'arity' (number of repetitions), and a rule name, and turns those into a rule to render markup like [[[this]]] as <span class='yournamehere'>this</span>:

#===========================================================================================================
@get = ( settings ) ->

  #---------------------------------------------------------------------------------------------------------
  rule                = {}
  rule._opener        = settings?[ 'opener'  ] ? '<'
  rule._closer        = settings?[ 'closer'  ] ? '>'
  rule.terminators    = rule._opener
  rule._arity         = settings?[ 'arity'   ] ? 2
  rule._class_name    = settings?[ 'name' ] ? 'angles'
  rule.name           = 'REMARKABLY/examples/' + rule._class_name

  #---------------------------------------------------------------------------------------------------------
  rule.about = """$name$ recognizes text stretches enclosed by multiple brackets."""

  #---------------------------------------------------------------------------------------------------------
  rule._get_multiple_bracket_pattern = ( opener, closer, arity = 2, anchor = no ) ->
    opener      = "(?:#{BNP.escape_regex opener})"
    closer      = "(?:#{BNP.escape_regex closer})"
    anchor      = if anchor then '^' else ''
    repeat_all  = if arity is 1 then '' else "{#{arity}}"
    repeat_some = if arity is 1 then '' else "{1,#{arity}}"
    #.......................................................................................................
    return """
      #{anchor}
      (#{opener}#{repeat_all}(?!#{opener}))
        ((?:
          \\\\#{closer}|
          [^#{closer}]|
          #{closer}#{repeat_some}(?!#{closer})
        )*)
        (#{closer}#{repeat_all})(?!#{closer})
      """.replace /\n\s*/g, ''

  #---------------------------------------------------------------------------------------------------------
  rule._pattern = rule._get_multiple_bracket_pattern rule._opener, rule._closer, rule._arity, no
  rule._re      = new RegExp rule._pattern, 'g' # need `g` for `lastIndex`

  #---------------------------------------------------------------------------------------------------------
  rule.parse = ( state, silent ) ->
    #.......................................................................................................
    { src, pos, }       = state
    rule._re.lastIndex  = pos
    return false if ( not ( match = rule._re.exec src )? ) or match[ 'index' ] isnt pos
    [ all, opener, content, closer, ] = match
    unless silent
      state.push
        type:     rule.name
        opener:   opener
        closer:   closer
        content:  content
        block:    false
        level:    state.level
    #.......................................................................................................
    state.pos += all.length
    return true

  #---------------------------------------------------------------------------------------------------------
  rule.render = ( tokens, idx ) -> # options
    { content, opener, closer, } = tokens[ idx ]
    return "<span class='#{rule._class_name}'>#{content}</span>"

  #---------------------------------------------------------------------------------------------------------
  rule.extend = ( self ) ->
    self.inline.ruler.before self.inline.ruler[ 'rules' ][ 0 ][ 'name' ], rule.name, rule.parse
    self.renderer.rules[ rule.name ] = rule.render
    return null

  #---------------------------------------------------------------------------------------------------------
  return rule

remarkable Compatibility

ReMarkably is comaptible with remarkable@1.4.0.

About

a collection of MarkDown syntax extensions for https://github.com/jonschlinkert/remarkable

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published