Pull out markdown-preview package into a separate repo

This commit is contained in:
Kevin Sawicki
2013-08-13 10:13:14 -07:00
parent c1c50a0b4c
commit 7bb41f57b0
9 changed files with 1 additions and 937 deletions

View File

@@ -75,6 +75,7 @@
"go-to-line": "0.1.0",
"grammar-selector": "0.1.0",
"image-view": "0.1.0",
"markdown-preview": "0.1.0",
"spell-check": "0.1.0",
"symbols-view": "0.1.0",
"terminal": "0.3.0",

View File

@@ -1,2 +0,0 @@
'.editor':
'ctrl-m': 'markdown-preview:show'

View File

@@ -1,106 +0,0 @@
$ = require 'jquery'
_ = require 'underscore'
ScrollView = require 'scroll-view'
{$$$} = require 'space-pen'
roaster = require 'roaster'
Editor = require 'editor'
fenceNameToExtension =
'bash': 'sh'
'coffee': 'coffee'
'coffeescript': 'coffee'
'coffee-script': 'coffee'
'css': 'css'
'go': 'go'
'java': 'java'
'javascript': 'js'
'js': 'js'
'mustache': 'mustache'
'python': 'py'
'rb': 'rb'
'ruby': 'rb'
'sh': 'sh'
'toml': 'toml'
'xml': 'xml'
module.exports =
class MarkdownPreviewView extends ScrollView
registerDeserializer(this)
@deserialize: ({path}) ->
new MarkdownPreviewView(project.bufferForPath(path))
@content: ->
@div class: 'markdown-preview', tabindex: -1
initialize: (@buffer) ->
super
@renderMarkdown()
@subscribe syntax, 'grammar-added grammar-updated', _.debounce((=> @renderMarkdown()), 250)
@on 'core:move-up', => @scrollUp()
@on 'core:move-down', => @scrollDown()
afterAttach: (onDom) ->
@subscribe @buffer, 'saved reloaded', =>
@renderMarkdown()
pane = @getPane()
pane.showItem(this) if pane? and pane isnt rootView.getActivePane()
getPane: ->
@parent('.item-views').parent('.pane').view()
serialize: ->
deserializer: 'MarkdownPreviewView'
path: @buffer.getPath()
getTitle: ->
"#{@buffer.getBaseName()} Preview"
getUri: ->
"markdown-preview:#{@buffer.getPath()}"
getPath: ->
@buffer.getPath()
setErrorHtml: (result) ->
failureMessage = result?.message
@html $$$ ->
@h2 'Previewing Markdown Failed'
@h3 failureMessage if failureMessage?
setLoading: ->
@html($$$ -> @div class: 'markdown-spinner', 'Loading Markdown...')
tokenizeCodeBlocks: (html) =>
html = $(html)
preList = $(html.filter("pre"))
for preElement in preList.toArray()
$(preElement).addClass("editor-colors")
codeBlock = $(preElement.firstChild)
# go to next block unless this one has a class
continue unless className = codeBlock.attr('class')
fenceName = className.replace(/^lang-/, '')
# go to next block unless the class name matches `lang`
continue unless extension = fenceNameToExtension[fenceName]
text = codeBlock.text()
grammar = syntax.selectGrammar("foo.#{extension}", text)
codeBlock.empty()
for tokens in grammar.tokenizeLines(text)
codeBlock.append(Editor.buildLineHtml({ tokens, text }))
html
renderMarkdown: ->
@setLoading()
roaster @buffer.getText(), (err, html) =>
if err
@setErrorHtml(err)
else
@html(@tokenizeCodeBlocks(html))

View File

@@ -1,33 +0,0 @@
EditSession = require 'edit-session'
MarkdownPreviewView = require './markdown-preview-view'
module.exports =
activate: ->
rootView.command 'markdown-preview:show', '.editor', => @show()
show: ->
activePane = rootView.getActivePane()
editSession = activePane.activeItem
isEditSession = editSession instanceof EditSession
hasMarkdownGrammar = editSession.getGrammar().scopeName == "source.gfm"
if not isEditSession or not hasMarkdownGrammar
console.warn("Can not render markdown for '#{editSession.getUri() ? 'untitled'}'")
return
{previewPane, previewItem} = @getExistingPreview(editSession)
if previewItem?
previewPane.showItem(previewItem)
previewItem.renderMarkdown()
else if nextPane = activePane.getNextPane()
nextPane.showItem(new MarkdownPreviewView(editSession.buffer))
else
activePane.splitRight(new MarkdownPreviewView(editSession.buffer))
activePane.focus()
getExistingPreview: (editSession) ->
uri = "markdown-preview:#{editSession.getPath()}"
for previewPane in rootView.getPanes()
previewItem = previewPane.itemForUri(uri)
return {previewPane, previewItem} if previewItem?
{}

View File

@@ -1,5 +0,0 @@
'main': './lib/markdown-preview'
'description': 'Open a rendered version of the Markdown in the current editor with `ctrl-m`.'
'activationEvents':
'markdown-preview:show': '.editor'
'deferredDeserializers': ['MarkdownPreviewView']

View File

@@ -1,135 +0,0 @@
RootView = require 'root-view'
MarkdownPreviewView = require 'markdown-preview/lib/markdown-preview-view'
{$$} = require 'space-pen'
describe "Markdown preview package", ->
beforeEach ->
atom.activatePackage('gfm')
project.setPath(project.resolve('markdown'))
window.rootView = new RootView
atom.activatePackage("markdown-preview", immediate: true)
spyOn(MarkdownPreviewView.prototype, 'renderMarkdown')
describe "markdown-preview:show", ->
beforeEach ->
rootView.open("file.markdown")
describe "when the active item is an edit session", ->
beforeEach ->
rootView.attachToDom()
describe "when the edit session does not use the GFM grammar", ->
it "does not show a markdown preview", ->
spyOn(console, 'warn')
rootView.open()
expect(rootView.getPanes()).toHaveLength(1)
rootView.getActiveView().trigger 'markdown-preview:show'
expect(rootView.getPanes()).toHaveLength(1)
expect(console.warn).toHaveBeenCalled()
describe "when a preview item has not been created for the edit session's uri", ->
describe "when there is more than one pane", ->
it "shows a markdown preview for the current buffer on the next pane", ->
rootView.getActivePane().splitRight()
[pane1, pane2] = rootView.getPanes()
pane1.focus()
rootView.getActiveView().trigger 'markdown-preview:show'
preview = pane2.activeItem
expect(preview).toBeInstanceOf(MarkdownPreviewView)
expect(preview.buffer).toBe rootView.getActivePaneItem().buffer
expect(pane1).toMatchSelector(':has(:focus)')
describe "when there is only one pane", ->
it "splits the current pane to the right with a markdown preview for the current buffer", ->
expect(rootView.getPanes()).toHaveLength 1
rootView.getActiveView().trigger 'markdown-preview:show'
expect(rootView.getPanes()).toHaveLength 2
[pane1, pane2] = rootView.getPanes()
expect(pane2.items).toHaveLength 1
preview = pane2.activeItem
expect(preview).toBeInstanceOf(MarkdownPreviewView)
expect(preview.buffer).toBe rootView.getActivePaneItem().buffer
expect(pane1).toMatchSelector(':has(:focus)')
describe "when a buffer is saved", ->
it "does not show the markdown preview", ->
[pane] = rootView.getPanes()
pane.focus()
MarkdownPreviewView.prototype.renderMarkdown.reset()
pane.activeItem.buffer.trigger 'saved'
expect(MarkdownPreviewView.prototype.renderMarkdown).not.toHaveBeenCalled()
describe "when a buffer is reloaded", ->
it "does not show the markdown preview", ->
[pane] = rootView.getPanes()
pane.focus()
MarkdownPreviewView.prototype.renderMarkdown.reset()
pane.activeItem.buffer.trigger 'reloaded'
expect(MarkdownPreviewView.prototype.renderMarkdown).not.toHaveBeenCalled()
describe "when a preview item has already been created for the edit session's uri", ->
it "updates and shows the existing preview item if it isn't displayed", ->
rootView.getActiveView().trigger 'markdown-preview:show'
[pane1, pane2] = rootView.getPanes()
pane2.focus()
expect(rootView.getActivePane()).toBe pane2
preview = pane2.activeItem
expect(preview).toBeInstanceOf(MarkdownPreviewView)
rootView.open()
expect(pane2.activeItem).not.toBe preview
pane1.focus()
preview.renderMarkdown.reset()
rootView.getActiveView().trigger 'markdown-preview:show'
expect(preview.renderMarkdown).toHaveBeenCalled()
expect(rootView.getPanes()).toHaveLength 2
expect(pane2.getItems()).toHaveLength 2
expect(pane2.activeItem).toBe preview
expect(pane1).toMatchSelector(':has(:focus)')
describe "when a buffer is saved", ->
describe "when the preview is in the same pane", ->
it "updates the preview but does not make it active", ->
rootView.getActiveView().trigger 'markdown-preview:show'
[pane1, pane2] = rootView.getPanes()
pane2.moveItemToPane(pane2.activeItem, pane1, 1)
pane1.showItemAtIndex(1)
pane1.showItemAtIndex(0)
preview = pane1.itemAtIndex(1)
preview.renderMarkdown.reset()
pane1.activeItem.buffer.trigger 'saved'
expect(preview.renderMarkdown).toHaveBeenCalled()
expect(pane1.activeItem).not.toBe preview
describe "when the preview is not in the same pane", ->
it "updates the preview and makes it active", ->
rootView.getActiveView().trigger 'markdown-preview:show'
[pane1, pane2] = rootView.getPanes()
preview = pane2.activeItem
pane2.showItem($$ -> @div id: 'view', tabindex: -1, 'View')
expect(pane2.activeItem).not.toBe preview
pane1.focus()
preview.renderMarkdown.reset()
pane1.activeItem.buffer.trigger 'saved'
expect(preview.renderMarkdown).toHaveBeenCalled()
expect(pane2.activeItem).toBe preview
describe "when a new grammar is loaded", ->
it "reloads the view to colorize any fenced code blocks matching the newly loaded grammar", ->
rootView.getActiveView().trigger 'markdown-preview:show'
[pane1, pane2] = rootView.getPanes()
preview = pane2.activeItem
preview.renderMarkdown.reset()
jasmine.unspy(window, 'setTimeout')
atom.activatePackage('javascript-tmbundle', sync: true)
waitsFor -> preview.renderMarkdown.callCount > 0

View File

@@ -1,44 +0,0 @@
MarkdownPreviewView = require 'markdown-preview/lib/markdown-preview-view'
$ = require 'jquery'
{$$$} = require 'space-pen'
describe "MarkdownPreviewView", ->
[buffer, preview] = []
beforeEach ->
project.setPath(project.resolve('markdown'))
buffer = project.bufferForPath('file.markdown')
atom.activatePackage('ruby-tmbundle', sync: true)
preview = new MarkdownPreviewView(buffer)
afterEach ->
buffer.release()
describe "on construction", ->
it "shows a loading spinner and renders the markdown", ->
preview.setLoading()
expect(preview.find('.markdown-spinner')).toExist()
expect(preview.buffer.getText()).toBe buffer.getText()
preview.renderMarkdown()
expect(preview.find(".emoji")).toExist()
it "shows an error message on error", ->
preview.setErrorHtml("Not a real file")
expect(preview.text()).toContain "Failed"
describe "serialization", ->
it "reassociates with the same buffer when deserialized", ->
newPreview = deserialize(preview.serialize())
expect(newPreview.buffer).toBe buffer
describe "code block tokenization", ->
describe "when the code block's fence name has a matching grammar", ->
it "tokenizes the code block with the grammar", ->
expect(preview.find("pre span.entity.name.function.ruby")).toExist()
describe "when the code block's fence name doesn't have a matching grammar", ->
it "does not tokenize the code block", ->
expect(preview.find("pre code:not([class])").children().length).toBe 0
expect(preview.find("pre code.lang-kombucha").children().length).toBe 0

View File

@@ -1,411 +0,0 @@
.markdown-preview {
font-family: "Helvetica Neue", Helvetica, sans-serif;
font-size: 14px;
line-height: 1.6;
background-color: #fff;
overflow: scroll;
box-sizing: border-box;
padding: 20px;
:not(.editor-colors) > code {
color: #333;
}
}
.markdown-spinner {
margin: auto;
background-image: url(images/octocat-spinner-128.gif);
background-repeat: no-repeat;
background-size: 64px;
background-position: top center;
padding-top: 70px;
text-align: center;
}
// This is styling for generic markdownized text. Anything you put in a
// container with .markdown-preview on it should render generally well. It also
// includes some GitHub Flavored Markdown specific styling (like @mentions)
.markdown-preview {
pre,
pre div.editor,
code,
tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
a {
color: #4183c4;
}
ol > li {
list-style-type: decimal;
}
ul > li {
list-style-type: disc;
}
& > *:first-child {
margin-top: 0 !important;
}
& > *:last-child {
margin-bottom: 0 !important;
}
// Link Colors
a.absent {
color: #c00;
}
a.anchor {
display: block;
padding-left: 30px;
margin-left: -30px;
cursor: pointer;
position: absolute;
top: 0;
left: 0;
bottom: 0;
}
// Headings
h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
cursor: text;
position: relative;
.octicon-link {
display: none;
color: #000;
}
&:hover a.anchor {
text-decoration: none;
line-height: 1;
padding-left: 0;
margin-left: -22px;
top: 15%;
.octicon-link {
display: inline-block;
}
}
tt, code {
font-size: inherit;
}
}
h1 {
font-size: 28px;
color: #000;
}
h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: #777;
font-size: 14px;
}
p,
blockquote,
ul, ol, dl,
table,
pre {
margin: 15px 0;
}
hr {
background: transparent;
border: 0 none;
color: #ccc;
height: 4px;
padding: 0;
}
& > h2:first-child,
& > h1:first-child,
& > h1:first-child + h2,
& > h3:first-child,
& > h4:first-child,
& > h5:first-child,
& > h6:first-child {
margin-top: 0;
padding-top: 0;
}
// fixes margin on shit like:
// <a name='the-heading'>
// <h1>The Heading</h1></a>
a:first-child {
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
padding-top: 0;
}
}
h1 + p,
h2 + p,
h3 + p,
h4 + p,
h5 + p,
h6 + p {
margin-top: 0;
}
// ReST first graf in nested list
li p.first {
display: inline-block;
}
// Lists, Blockquotes & Such
ul, ol {
padding-left: 30px;
&.no-list {
list-style-type: none;
padding: 0;
}
li > :first-child,
li ul:first-of-type {
margin-top: 0;
}
}
ul ul,
ul ol,
ol ol,
ol ul {
margin-bottom: 0;
}
dl {
padding: 0;
}
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
&:first-child {
padding: 0;
}
& > :first-child {
margin-top: 0;
}
& > :last-child {
margin-bottom: 0;
}
}
dl dd {
margin: 0 0 15px;
padding: 0 15px;
& > :first-child {
margin-top: 0;
}
& > :last-child {
margin-bottom: 0;
}
}
blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
& > :first-child {
margin-top: 0;
}
& > :last-child {
margin-bottom: 0;
}
}
// Tables
table {
th {
font-weight: bold;
}
th, td {
border: 1px solid #ccc;
padding: 6px 13px;
}
tr {
border-top: 1px solid #ccc;
background-color: #fff;
&:nth-child(2n) {
background-color: #f8f8f8;
}
}
}
// Images & Stuff
img {
max-width: 100%;
}
// Gollum Image Tags
// Framed
span.frame {
display: block;
overflow: hidden;
& > span {
border: 1px solid #ddd;
display: block;
float: left;
overflow: hidden;
margin: 13px 0 0;
padding: 7px;
width: auto;
}
span img {
display: block;
float: left;
}
span span {
clear: both;
color: #333;
display: block;
padding: 5px 0 0;
}
}
span.align-center {
display: block;
overflow: hidden;
clear: both;
& > span {
display: block;
overflow: hidden;
margin: 13px auto 0;
text-align: center;
}
span img {
margin: 0 auto;
text-align: center;
}
}
span.align-right {
display: block;
overflow: hidden;
clear: both;
& > span {
display: block;
overflow: hidden;
margin: 13px 0 0;
text-align: right;
}
span img {
margin: 0;
text-align: right;
}
}
span.float-left {
display: block;
margin-right: 13px;
overflow: hidden;
float: left;
span {
margin: 13px 0 0;
}
}
span.float-right {
display: block;
margin-left: 13px;
overflow: hidden;
float: right;
& > span {
display: block;
overflow: hidden;
margin: 13px auto 0;
text-align: right;
}
}
// Inline code snippets
code, tt {
margin: 0 2px;
padding: 0 5px;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius:3px;
}
code { white-space: nowrap; }
// Code tags within code blocks (<pre>s)
pre > code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}
.highlight pre, pre {
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius:3px;
}
pre code, pre tt {
margin: 0;
padding: 0;
background-color: transparent;
border: none;
}
.emoji {
height: 20px;
width: 20px;
}
}

View File

@@ -1,201 +0,0 @@
.highlight {
background: #ffffff;
// Comment
.c { color: #999988; font-style: italic }
// Error
.err { color: #a61717; background-color: #e3d2d2 }
// Keyword
.k { font-weight: bold }
// Operator
.o { font-weight: bold }
// Comment.Multiline
.cm { color: #999988; font-style: italic }
// Comment.Preproc
.cp { color: #999999; font-weight: bold }
// Comment.Single
.c1 { color: #999988; font-style: italic }
// Comment.Special
.cs { color: #999999; font-weight: bold; font-style: italic }
// Generic.Deleted
.gd { color: #000000; background-color: #ffdddd }
// Generic.Deleted.Specific
.gd .x { color: #000000; background-color: #ffaaaa }
// Generic.Emph
.ge { font-style: italic }
// Generic.Error
.gr { color: #aa0000 }
// Generic.Heading
.gh { color: #999999 }
// Generic.Inserted
.gi { color: #000000; background-color: #ddffdd }
// Generic.Inserted.Specific
.gi .x { color: #000000; background-color: #aaffaa }
// Generic.Output
.go { color: #888888 }
// Generic.Prompt
.gp { color: #555555 }
// Generic.Strong
.gs { font-weight: bold }
// Generic.Subheading
.gu { color: #800080; font-weight: bold; }
// Generic.Traceback
.gt { color: #aa0000 }
// Keyword.Constant
.kc { font-weight: bold }
// Keyword.Declaration
.kd { font-weight: bold }
// Keyword.Namespace
.kn { font-weight: bold }
// Keyword.Pseudo
.kp { font-weight: bold }
// Keyword.Reserved
.kr { font-weight: bold }
// Keyword.Type
.kt { color: #445588; font-weight: bold }
// Literal.Number
.m { color: #009999 }
// Literal.String
.s { color: #d14 }
// Name
.n { color: #333333 }
// Name.Attribute
.na { color: #008080 }
// Name.Builtin
.nb { color: #0086B3 }
// Name.Class
.nc { color: #445588; font-weight: bold }
// Name.Constant
.no { color: #008080 }
// Name.Entity
.ni { color: #800080 }
// Name.Exception
.ne { color: #990000; font-weight: bold }
// Name.Function
.nf { color: #990000; font-weight: bold }
// Name.Namespace
.nn { color: #555555 }
// Name.Tag
.nt { color: #000080 }
// Name.Variable
.nv { color: #008080 }
// Operator.Word
.ow { font-weight: bold }
// Text.Whitespace
.w { color: #bbbbbb }
// Literal.Number.Float
.mf { color: #009999 }
// Literal.Number.Hex
.mh { color: #009999 }
// Literal.Number.Integer
.mi { color: #009999 }
// Literal.Number.Oct
.mo { color: #009999 }
// Literal.String.Backtick
.sb { color: #d14 }
// Literal.String.Char
.sc { color: #d14 }
// Literal.String.Doc
.sd { color: #d14 }
// Literal.String.Double
.s2 { color: #d14 }
// Literal.String.Escape
.se { color: #d14 }
// Literal.String.Heredoc
.sh { color: #d14 }
// Literal.String.Interpol
.si { color: #d14 }
// Literal.String.Other
.sx { color: #d14 }
// Literal.String.Regex
.sr { color: #009926 }
// Literal.String.Single
.s1 { color: #d14 }
// Literal.String.Symbol
.ss { color: #990073 }
// Name.Builtin.Pseudo
.bp { color: #999999 }
// Name.Variable.Class
.vc { color: #008080 }
// Name.Variable.Global
.vg { color: #008080 }
// Name.Variable.Instance
.vi { color: #008080 }
// Literal.Number.Integer.Long
.il { color: #009999 }
.gc {
color: #999;
background-color: #EAF2F5;
}
}
.type-csharp .highlight {
.k { color: #0000FF }
.kt { color: #0000FF }
.nf { color: #000000; font-weight: normal }
.nc { color: #2B91AF }
.nn { color: #000000 }
.s { color: #A31515 }
.sc { color: #A31515 }
}