├── .gitignore
├── 1.coffee
├── 1.js
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── keymaps
└── pp.cson
├── lib
├── coffeeToJS.coffee
├── eval.coffee
├── pp-status-view.coffee
├── pp.coffee
├── quick-view.coffee
├── runCoffee.js
└── worker.coffee
├── menus
└── pp.cson
├── package.json
├── styles
└── pp.less
└── todo
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | npm-debug.log
3 | node_modules
4 | todo
5 |
--------------------------------------------------------------------------------
/1.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | {bare: true}
4 |
5 | ###
6 |
7 | console.log 'hello'
8 |
--------------------------------------------------------------------------------
/1.js:
--------------------------------------------------------------------------------
1 | console.log('hello');
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.1.0 - First Release
2 | * Every feature added
3 | * Every bug fixed
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pp package
2 |
3 | Basic framework for preview packages
4 | offers the following features
5 | 0. Easy Plugin options for previews with all necessary parameter passed for executing the previews
6 | 1. dropdown for the list of applicable preview for each editor based on file types/alias/patterns
7 | 2. Live - Preview On Save
8 | 3. HyperLive - As the changes happens - Preview
9 | 4. Quick Preview Panel
10 | 5. user defined requires to quickly get preview
11 | 6. Run Options/Kill the process
12 | 7. scroll sync
13 | 8. super
14 |
15 | Comes default with the coffee to js preview implementation.
16 |
17 | ]
18 |
--------------------------------------------------------------------------------
/keymaps/pp.cson:
--------------------------------------------------------------------------------
1 | # Keybindings require three things to be fully defined: A selector that is
2 | # matched against the focused element, the keystroke and the command to
3 | # execute.
4 | #
5 | # Below is a basic keybinding which registers on all platforms by applying to
6 | # the root workspace element.
7 |
8 | # For more detailed documentation see
9 | # https://atom.io/docs/latest/behind-atom-keymaps-in-depth
10 |
--------------------------------------------------------------------------------
/lib/coffeeToJS.coffee:
--------------------------------------------------------------------------------
1 | # {bare:true}
2 | coffee = require 'coffee-script'
3 | jQuery = require 'jquery'
4 | loophole = require './eval'
5 | fs = require 'fs'
6 | module.exports =
7 |
8 | fileTypes: do ->
9 | types = atom.config.get('pp.coffee-types') or []
10 | types.concat ['coff','coffee'] #filetypes against which this compileTo Option will show
11 |
12 | names: do ->
13 | names = atom.config.get('pp.coffee-names') or []
14 | names.concat ['CoffeeScript (Literate)'] #filetypes against which this compileTo Option will show
15 |
16 | scopeNames: do ->
17 | scopes = atom.config.get('pp.coffee-scope') or []
18 | scopes.concat ['source.litcoffee'] #filetypes against which this compileTo Option will show
19 |
20 | # js:
21 | # scopes: scopes.concat ['source.litcoffee'] #scopes this applicable for
22 | # names: names.concat ['CoffeeScript (Literate)'] # name as in grammar this applicable for
23 | # fileTypes: fileTypes.concat ['coffee'] #file Extensions this applicable for, No need for .
24 | # ext: 'js' #extension of the resulting preview
25 | # name: 'js' # name as it appears in the dropdown
26 | # options: bare:true #default options to pass with the compiler
27 | # hyperLive: true #on change to editor code will show preview updates
28 | # quickPreview: true #quick preview of few lines
29 | # # actual compile function
30 | # # exe: (editor,fileName,src,quickPreview,hyperLive,params,previewPane)->
31 | # exe: (src,options,data,fileName,quickPreview,hyperLive,editor)->
32 | # text : coffee.compile src, options
33 | # # scroll syncing option between source code and target possible through source maps
34 | # scrollSync : ->
35 |
36 | js:
37 | ext: 'js'
38 | hyperLive: true
39 | quickPreview: true
40 | exe: (src,options,data,fileName,quickPreview,hyperLive,editor,view)->
41 | options.filename = fileName
42 | if quickPreview or hyperLive or fileName.indexOf '~pp~coffee~'
43 | text : coffee.compile src, options
44 | else
45 | dfd = new jQuery.Deferred()
46 | fs.readFile fileName,'utf-8',(err,data)=>
47 | if err
48 | console.log dfd.fail err
49 | text : coffee.compile src, options
50 | else
51 | dfd.resolve text: coffee.compile data, options
52 | dfd.promise()
53 |
54 | run:
55 | hyperLive: true
56 | quickPreview: true
57 | exe: (src,options={},data,fileName,quickPreview,hyperLive,editor,view)->
58 |
59 | if quickPreview or hyperLive or fileName.indexOf('~pp~')
60 | args = atom.config.get('pp.coffee-cli-args').concat(src)
61 | program: 'runCoffee.js'
62 | args: args
63 | # process: loophole.runCommand 'coffee', [args].concat(src),options,editor
64 | else
65 | args = atom.config.get('pp.coffee-args').concat(fileName)
66 | # process: loophole.runCommand 'coffee', [args].concat(fileName),options,view
67 | command: 'coffee'
68 | args: args
69 |
70 | html:
71 | hyperLive: true
72 | quickPreview: true
73 | ext: 'html'
74 | exe: (src,options={},data,fileName,quickPreview,hyperLive,editor,view)->
75 | options.filename = fileName
76 | coffeeSrc = src
77 | unless (quickPreview or hyperLive or fileName.startsWith('browserplus~'))
78 | try
79 | coffeeSrc = fs.readFileSync(fileName,'utf-8').toString()
80 | catch e
81 |
82 | js = coffee.compile coffeeSrc, options
83 | js = "\n"
84 | atom.packages.getActivePackage('pp').mainModule.makeHTML
85 | js: js
86 | jsURL: data.js
87 | cssURL: data.css
88 |
89 |
90 | browser:
91 | hyperLive: true
92 | quickPreview: true
93 | browserPlus: true
94 | exe: (src,options={},data,fileName,quickPreview,hyperLive,editor,view)=>
95 | coffeeSrc = src
96 | unless (quickPreview or hyperLive or fileName.startsWith('browserplus~'))
97 | try
98 | coffeeSrc = fs.readFileSync(fileName,'utf-8').toString()
99 | catch e
100 | js = coffee.compile coffeeSrc, options
101 | js = ""
102 | html: atom.packages.getActivePackage('pp').mainModule.makeHTML
103 | js: js
104 | jsURL: data.js
105 | cssURL: data.css
106 |
--------------------------------------------------------------------------------
/lib/eval.coffee:
--------------------------------------------------------------------------------
1 | vm = require("vm")
2 | exports.allowUnsafeEval = (fn) ->
3 | previousEval = global["eval"]
4 | try
5 | global["eval"] = (source) -> vm.runInThisContext source
6 | fn()
7 | finally
8 | global["eval"] = previousEval
9 |
10 | exports.allowUnsafeNewFunction = (fn) ->
11 | previousFunction = global.Function
12 | try
13 | global.Function = exports.Function
14 | fn()
15 | finally
16 | global.Function = previousFunction
17 |
18 | exports.allowUnsafe = (fn) ->
19 | previousFunction = global.Function
20 | previousEval = global["eval"]
21 | try
22 | global.Function = exports.Function
23 | global["eval"] = (source) -> vm.runInThisContext source
24 | fn()
25 | finally
26 | global["eval"] = previousEval
27 | global.Function = previousFunction
28 |
29 | exports.Function = (paramLists..., body) ->
30 | params = []
31 | for paramList in paramLists
32 | if typeof paramList is 'string'
33 | paramList = paramList.split(/\s*,\s*/)
34 | params.push(paramList...)
35 |
36 | vm.runInThisContext """
37 | (function(#{params.join(', ')}) {
38 | #{body}
39 | })
40 | """
41 |
42 |
43 | exports.Function:: = global.Function::
44 |
--------------------------------------------------------------------------------
/lib/pp-status-view.coffee:
--------------------------------------------------------------------------------
1 | {View,SelectListView} = require 'atom-space-pen-views'
2 | $ = require 'jquery'
3 | _ = require 'lodash'
4 |
5 | class PPStatusView extends View
6 | @content: ->
7 | @div class:'pp-status inline-block', =>
8 | @span "Live",class:"live off ",outlet:"live", click:'toggleLive'
9 | @span class:"compileTo",outlet:"compileTo", click:'compile'
10 | @span "▼", class:"enums",outlet:"enums", click:'previewList'
11 |
12 | initialize: (@model,@editor)->
13 | @statusBarTile = @model.statusBar.addRightTile {item:@, priority:9999}
14 | @clicks = 0
15 |
16 | setLive: (live)->
17 | edStatus = @model.editors[@editor.id][@_id]
18 | if edStatus.live?.disposalAction
19 | @live.removeClass('off').removeClass('hyper').addClass('on')
20 | else if edStatus.hyperLive?.disposalAction
21 | @live.removeClass('off').removeClass('on').addClass('hyper')
22 | else
23 | @live.removeClass('on').removeClass('hyper').addClass('off')
24 |
25 | showStatusBar: (@editor,fresh)->
26 | return unless @editor
27 | @hide()
28 | delete @_id if fresh
29 | try
30 | status = @model.getStatus(@editor)
31 | @_id = status._id
32 | if status.vw
33 | # preview = _.find @model.previews,(preview)->
34 | # preview._id is status._id
35 | # preview.statusView = status.vw.clone(true) unless preview.statusView
36 | @compileTo.empty().append status.vw
37 | else
38 | @compileTo.empty().text status.name
39 |
40 | @show()
41 | @setLive()
42 | if status.enum
43 | @enums.show()
44 | else
45 | @enums.hide()
46 | catch e
47 | # console.log 'Not a Preview-Plus Editor',e.message,e.stack
48 |
49 | compile: ->
50 | @model.compile(@editor,@_id)
51 |
52 | setToggleLive: (click)->
53 | @model.setLiveListener(@editor,@_id,click)
54 | @setLive()
55 |
56 | toggleLive: (evt)->
57 | edStatus = @model.editors[@editor.id][@_id]
58 | preview = _.find @model.previews, (preview)->
59 | preview._id is edStatus._id
60 | if @model.processes[edStatus._id]?[@_id]
61 | @model.processes[edStatus._id][@_id]?.kill()
62 | @compileTo.replace('(kill)','')
63 | return true
64 | @clicks++
65 | if @clicks is 1
66 | @timer = setTimeout =>
67 | @clicks = 0
68 | @setToggleLive(1)
69 | ,300
70 | else
71 | @clicks = 0
72 | if ( typeof preview.hyperLive is 'boolean' and preview.hyperLive ) or (typeof preview.hyperLive is 'function' and preview.hyperLive())
73 | @setToggleLive(2)
74 | else
75 | atom.notifications.addInfo('HyperLive Not available')
76 |
77 | clearTimeout @timer
78 |
79 | previewList: ->
80 | previews = @model.getPreviews(@editor)
81 | defaults = @model.getDefaultStatus(@editor)
82 | if defaults
83 | for preview in previews
84 | if preview._id is defaults._id
85 | preview.default = true
86 | else
87 | preview.default = false
88 | else
89 | previews[0].default = true
90 |
91 | new CompilerView previews,@
92 |
93 | updateStatusBar: (item)->
94 | status = @model.getStatus(@editor,item._id)
95 | if status.vw
96 | # item.statusView = item.vw.clone(true) unless item.statusView
97 | @compileTo.empty().append status.vw
98 | else
99 | @compileTo.empty().text status.name
100 | @_id = item._id
101 | @setLive()
102 | @model.compile(@editor,item._id)
103 |
104 | show: ->
105 | super
106 |
107 | hide: ->
108 | super
109 | destroy: ->
110 |
111 | class CompilerView extends SelectListView
112 | initialize: (items,@statusView)->
113 | super
114 | @addClass 'overlay from-top'
115 | @setItems items
116 | atom.workspace.addModalPanel item:@
117 | @focusFilterEditor()
118 | # if @statusView.compileTo.children()?.length > 0
119 | # @selectItemView @list.find("li").has('span')
120 | # else
121 | # compileTo = @statusView.compileTo.text()
122 | # @selectItemView @list.find("li:contains('#{compileTo}')")
123 |
124 | viewForItem: (item)->
125 | if item.viewClass
126 | li = $("")
127 | unless item.vw
128 | if item.viewArgs
129 | item.vw = new item.viewClass(item.viewArgs)
130 | else
131 | item.vw = new item.viewClass
132 | li.append item.vw
133 | item.vw.selectList = @
134 | else
135 | li = $("#{item.fname}")
136 | # li = $("#{item.fname}")
137 | if item.default
138 | radio = $("")
139 | else
140 | radio = $("")
141 | fn = (e)=>
142 | $(`this `).closest('ol').find('span').removeClass('on')
143 | $(`this `).addClass('on')
144 | model = _this.statusView.model
145 | ext = model.getExt(_this.statusView.editor)
146 | item.enum = true
147 | model.defaults[ext] = item
148 | item.default = true
149 | e.stopPropagation()
150 | return false
151 | radio.on 'mouseover', fn
152 | li.append(radio)
153 | return li
154 | # "#{item.fname} <\li>"
155 | # if typeof item is 'string'
156 | # "#{item}"
157 | # else
158 | # $li = $('').append item.element
159 | # $li.data('selectList',@)
160 | # item
161 | confirmed: (item)->
162 | @statusView.updateStatusBar(item)
163 | # if item.vw
164 | # for i in [1..10000]
165 | # console.log i
166 | # else
167 | @cancel()
168 |
169 | cancelled: ->
170 | @parent().remove()
171 |
172 | module.exports = { PPStatusView, CompilerView }
173 |
--------------------------------------------------------------------------------
/lib/pp.coffee:
--------------------------------------------------------------------------------
1 | path = require 'path'
2 | _ = require 'lodash'
3 | uuid = require 'node-uuid'
4 | jQuery = require 'jquery'
5 | loophole = require './eval'
6 | {CompositeDisposable,BufferedProcess} = require 'atom'
7 | PPError = (@name,@message)->
8 | PPError.prototype = new Error()
9 | QuickView = require './quick-view'
10 |
11 | module.exports =
12 | subscriptions: null
13 |
14 | config:
15 | require:
16 | title: 'NPM/Require'
17 | type: 'array'
18 | default:['./coffeeToJs']
19 |
20 | 'coffee-types':
21 | title: 'Coffee File Types'
22 | type: 'array'
23 | default: []
24 |
25 | 'coffee-cli-args':
26 | title: 'Coffee CLI Arguments'
27 | type: 'array'
28 | default: ['-e']
29 |
30 | 'coffee-args':
31 | title: 'Coffee Arguments'
32 | type: 'array'
33 | default: []
34 |
35 | cursorFocusBack:
36 | default: true
37 | type: 'boolean'
38 | title: 'Set Cursor Focus Back'
39 |
40 | liveMilliseconds:
41 | title: 'MilliSeconds'
42 | type: 'number'
43 | default: 1200
44 | min: 600
45 |
46 | promptForSave:
47 | title: 'Prompt for save for previewed pane'
48 | type: 'boolean'
49 | default: false
50 |
51 | liveOff: ->
52 | for editor in @editors
53 | for key,ele of editor
54 | editor[key].dispose()
55 | @pp.setLive()
56 |
57 | activate: (state) ->
58 | # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
59 | @subscriptions = new CompositeDisposable
60 | @previews = []
61 | @editors = {}
62 | @processes = []
63 | @defaults = {}
64 | requires = atom.config.get('pp.require')
65 | atom.commands.add 'atom-text-editor', 'pp:preview': => @compile()
66 | atom.commands.add 'atom-text-editor', 'pp:liveOff': => @liveOff()
67 | atom.commands.add 'atom-text-editor', 'pp:killAll': => @killProcesses()
68 | @addPreviews(requires,false)
69 |
70 | idx = null
71 | itemSets = atom.contextMenu.itemSets
72 | contextMenu = _.find itemSets, (item,itemIdx)->
73 | idx = itemIdx
74 | item.items[0]?.command is 'pp:preview'
75 |
76 | if contextMenu?
77 | itemSets.splice idx,1
78 | itemSets.unshift contextMenu
79 |
80 | atom.contextMenu.itemSets = itemSets
81 |
82 | atom.workspace.onDidChangeActivePaneItem (activePane)=>
83 | return unless activePane
84 | @pp?.showStatusBar activePane
85 | subscribe?.dispose?()
86 | subscribe = activePane.onDidChangeGrammar? (grammar)->
87 | _this.pp?._id = null
88 | _this.pp?.showStatusBar activePane,true
89 |
90 | atom.workspace.onDidDestroyPaneItem (pane)=>
91 | if @editors
92 | for key,ele of @editors[pane.item.id]
93 | @editors[pane.item.id][key].live?.dispose?()
94 | @editors[pane.item.id][key].hyperLive?.dispose?()
95 | delete @editors[pane.item.id]
96 |
97 | setLiveListener: (editor,_id,clicks)->
98 | status = @getStatus(editor,_id)
99 | if clicks is 1
100 | if status.live?.disposalAction or status.hyperLive?.disposalAction
101 | status.live?.dispose?()
102 | status.hyperLive?.dispose?()
103 | else
104 | status.live = editor.onDidSave =>
105 | @compile(editor,_id)
106 | @editors[editor.id][_id] = status
107 | @compile(editor,_id)
108 | else
109 | editor.buffer.stoppedChangingDelay = atom.config.get('pp.liveMilliseconds')
110 | status.live?.dispose?()
111 | status.hyperLive = editor.onDidStopChanging =>
112 | @compile(editor,_id)
113 | @editors[editor.id][_id] = status
114 | @compile(editor,_id)
115 |
116 | consumeStatusBar: (statusBar)->
117 | @statusBar = statusBar
118 | {PPStatusView} = require './pp-status-view'
119 | editor = atom.workspace.getActiveTextEditor()
120 | @pp = new PPStatusView(@,editor)
121 | @pp.showStatusBar editor
122 |
123 | getPreviews: (editor,ext=@getExt(editor))->
124 | grammar = editor.getGrammar()
125 | previews = _.filter @previews, (preview)->
126 | return true unless (preview.fileTypes.length or preview.names.length or preview.scopeNames.length)
127 | return true if ext in preview.fileTypes
128 | # if it is not available
129 | for name in preview.names
130 | return true if name in grammar.name
131 | for scopeName in preview.scopeNames
132 | return true if scopeName in grammar.scopeName
133 |
134 | for fileType in preview.fileTypes
135 | return true if fileType in grammar.fileTypes
136 | throw new PPError 'alert','Set the Grammar for the Editor' unless previews.length
137 | previews
138 |
139 | getExt: (editor)->
140 | editorPath = editor.getPath()
141 | ext = path.extname(editorPath)[1...]
142 |
143 | fillStatus: (preview)->
144 | status = {}
145 | status._id = preview._id
146 | status.name = preview.name
147 | if preview.viewClass
148 | if preview.viewArgs
149 | preview.vw = new preview.viewClass(preview.viewArgs)
150 | else
151 | preview.vw= new preview.viewClass
152 | status.vw = preview.vw
153 | status
154 |
155 | getEnum: (editor)->
156 | ext = @getExt(editor)
157 | @defaults[ext].enum
158 |
159 | getStatus: (editor,_id)->
160 | _id = _id or @editors[editor.id]?.current
161 | status = @editors[editor.id]?[_id] if _id
162 | return status if status
163 | if _id
164 | preview = _.find @previews, (preview)->
165 | preview._id is _id
166 | status = @fillStatus(preview)
167 | status.enum = @getEnum(editor)
168 | @setCurrentStatus(editor,status)
169 | return status
170 | @getDefaultStatus(editor)
171 |
172 | setCurrentStatus: (editor,preview)->
173 | if @editors[editor.id]?[preview._id]
174 | return @editors[editor.id]?[preview._id]
175 | status = jQuery.extend {}, preview
176 | unless @editors[editor.id]
177 | @editors[editor.id] = {}
178 | @editors[editor.id][status._id] = status
179 | @editors[editor.id].current = status._id
180 | return status
181 |
182 | getDefaultStatus: (editor,fresh)->
183 | ext = @getExt(editor) or ''
184 | if fresh or not @defaults[ext]
185 | previews = @getPreviews editor,ext
186 | throw new PPError 'alert','No Previews Available' if previews.length is 0
187 | @defaults[ext] = @fillStatus previews[0],editor
188 | @defaults[ext].enum = true if previews.length > 1
189 | @setCurrentStatus(editor,@defaults[ext])
190 |
191 | compilePath: (path,_id)->
192 | panes = atom.workspace.getPaneItems()
193 | ed = atom.workspace.paneForURI(path)?.getItems()?.find (pane)-> pane.getURI() is path
194 | # ed = panes.find (pane)->
195 | # return if pane.constructor.name is 'HTMLEditor'
196 | # pane.getURI() is path or "file:///"+pane.getURI() is path
197 | if ed
198 | @compile(ed,_id)
199 | else
200 | if path.startsWith('file:///')
201 | path = path.split('file:///')[1]
202 | atom.workspace.open(path)
203 | .then (vw)=>
204 | @compile(vw,_id)
205 |
206 | compile: (editor = atom.workspace.getActiveTextEditor(),_id)->
207 |
208 | status = @getStatus(editor,_id)
209 | @editors[editor.id].current = status._id
210 | {text,fpath,quickPreview} = @getText editor
211 | preview = _.find @previews,{_id:status._id}
212 | settings = @project?.props?.settings?[preview.fname] or {}
213 | options = jQuery.extend {},settings['pp-options'] ,@getContent('options',text)
214 | data = jQuery.extend {},settings['pp-data'],@getContent('data',text)
215 | @previewPane(preview,text,options,data,fpath,quickPreview,status.hyperLive?.disposalAction,editor,_id)
216 |
217 | killProcesses: ->
218 | for proc in @processes
219 | proc.kill()
220 |
221 |
222 | runProgram: (program,code,fpath, args, options,preview, editor) ->
223 | workerFile = "#{atom.packages.getActivePackage('pp').path}\\lib\\#{program}"
224 | onFinish = =>
225 |
226 | stdout = (output) =>
227 | editor.insertText output
228 |
229 | stderr = (output) =>
230 | editor.insertText output
231 |
232 | exit = =>
233 | duration = (@startTime - new Date())/1000
234 | switch
235 | when duration > 3600
236 | editor.insertText("[Completed in #{duration/3600} hrs]")
237 | when duration > 60
238 | editor.insertText("[Completed in #{duration/60} minutes]")
239 | else
240 | editor.insertText("[Completed in #{duration} seconds]")
241 |
242 | # process to be removed once the process get complete
243 | delete @processes[preview._id]
244 | compileTo = @pp.compileTo
245 | compileTo.text compileTo.text().replace('(kill)','')
246 | args = [workerFile,fpath]
247 | options = {
248 | stdio: 'pipe'
249 | }
250 | command = 'node'
251 | @startTime = new Date()
252 | coffee = require('coffee-script')
253 | js = coffee.compile(code)
254 | console.log js
255 | vm = require('vm')
256 | context = vm.createContext({
257 | require: require,
258 | # register:require('coffee-script/register'),
259 | console: console })
260 | vm.runInContext(js,context, fpath)
261 |
262 | # child = new BufferedProcess {command, args, options, stdout, stderr, exit}
263 | # # keep track of all process
264 | # @processes[preview._id] = child
265 | # # # update the status bar text add (kill)
266 | # @pp.compileTo.text @pp.compileTo.text() + "(kill)"
267 | # child.process.stdin.write(code)
268 | # child.process.stdin.end()
269 |
270 |
271 | # cp = require 'child_process'
272 | # # child = cp.fork workerFile
273 | # # atom = require('atom-shell');
274 | # child = cp.spawn atom,["file://c/sub.js"]
275 | # # child = cp.fork "./sub.js",[] ,{cwd:"file:c://"} # "#{atom.packages.getActivePackage('pp').path}"}
276 | # # ,{cwd:__dirname}, (err,stdout,stderr)->
277 | # # debugger
278 | # # console.log 'stdout'
279 | # # console.log 'std'
280 | # # console.log workerFile
281 | # # child = cp.fork workerFile
282 | # # console.log 'seinding to the child'
283 | # # # child.send {program,args,options,preview,editor}
284 | # # debugger
285 | # child.on 'message', (data)->
286 | # # debugger
287 | # console.log 'received mes',data
288 | #
289 | # child.send('hlloo')
290 |
291 | # n.on 'message', (m) ->
292 | # console.log('PARENT got message:', m);
293 | #
294 | # n.send({ hello: 'world' });
295 | runCommand: (command, args, options,preview, editor) ->
296 |
297 | onFinish = =>
298 |
299 | stdout = (output) =>
300 | editor.insertText output
301 |
302 | stderr = (output) =>
303 | editor.insertText output
304 |
305 | exit = =>
306 | duration = (@startTime - new Date())/1000
307 | switch
308 | when duration > 3600
309 | editor.insertText("[Completed in #{duration/3600} hrs]")
310 | when duration > 60
311 | editor.insertText("[Completed in #{duration/60} minutes]")
312 | else
313 | editor.insertText("[Completed in #{duration} seconds]")
314 |
315 | # process to be removed once the process get complete
316 | delete @processes[preview._id]
317 | compileTo = @pp.compileTo
318 | compileTo.text compileTo.text().replace('(kill)','')
319 |
320 | child = new BufferedProcess {command, args, options, stdout, stderr, exit}
321 | # update the status bar text add (kill)
322 | @startTime = new Date()
323 | @pp.compileTo.text @pp.compileTo.text() + "(kill)"
324 | # keep track of all process
325 | @processes[preview._id] = child
326 |
327 | previewPane: (preview,text,options,data,fpath,quickPreview,live,editor,_id)->
328 | # grammar = if not err then preview.ext else syntax = editor.getGrammar()
329 | syntax = atom.grammars.selectGrammar(preview.ext)
330 | view = undefined
331 | compile = =>
332 | try
333 | result = preview.exe(text,options,data,fpath,quickPreview,live,editor,view)
334 | unless result
335 | view?.destroy()
336 | return true
337 | formatResult = (res)=>
338 | if res.text or typeof res is 'string' or res.code
339 | @previewText(editor,view,res.text or res.code or res)
340 | if res.command
341 | @runCommand res.command, res.args ,res.options or options,preview,view
342 | if res.program
343 | @runProgram res.program,text,fpath, res.args,res.options or options, preview,view
344 |
345 | if res.html or res.htmlURL
346 | view?.destroy()
347 | uri = res.htmlURL or "browser-plus~#{preview.name}~#{preview._id}://#{editor.getURI()}"
348 | pane = atom.workspace.paneForURI(uri)
349 | if pane
350 | htmlEditor = pane.getItems()?.find (itm)-> itm.getURI() is uri
351 | htmlEditor.setText(res.html) if res.html
352 | pane = atom.workspace.paneForItem htmlEditor
353 | pane.setActiveItem(htmlEditor)
354 | # htmlEditor.refresh()
355 | else
356 | atom.workspace.open uri,{src:res.html,split:@getPosition(editor),orgURI:fpath,_id:_id}
357 |
358 | if result.promise
359 | result.done (res)=>
360 | formatResult(res)
361 | result.fail (text)->
362 | e = new Error()
363 | e.name = 'console'
364 | e.message = text
365 | throw e
366 | else
367 | formatResult(result)
368 | #
369 | catch e
370 | console.log e.message
371 | if e.name is 'alert'
372 | alert e.message
373 | else
374 | if e.location
375 | {first_line} = e.location
376 | error = text.split('\n')[0...first_line].join('\n')
377 | error += '\n'+e.toString().split('\n')[1..-1].join('\n')+'\n'+e.message
378 | @previewText editor,view,error,true
379 |
380 | if preview.noPreview or preview.browserPlus
381 | compile()
382 | else
383 |
384 | if quickPreview
385 | unless @qView
386 | @qView = new QuickView(title,text,syntax)
387 | atom.workspace.addBottomPanel item: @qView
388 | else
389 | @qView.editor.setText('')
390 | view = @qView.showPanel(text,syntax)
391 | view.setGrammar syntax if syntax
392 | compile()
393 | else
394 | split = @getPosition editor
395 | title = editor.getTitle()
396 | if preview.ext
397 | title = title.substr(0, title.lastIndexOf('.'))
398 | title = "#{title}.#{preview.ext}~pp~#{preview.name}~#{preview._id}.#{preview.ext}"
399 | else
400 | title = "#{title}.~pp~#{preview.name}~#{preview._id}"
401 |
402 | atom.workspace.open title,
403 | searchAllPanes:true
404 | split: split
405 | # src: text
406 | .then (vw)=>
407 | view = vw
408 | vw.shouldPromptToSave = ->
409 | atom.config.get('pp.promptForSave')
410 |
411 | view.setText('')
412 | view.disposables.add editor.onDidDestroy =>
413 | view.destroy()
414 | view.setGrammar syntax if syntax
415 | # view.moveToTop()
416 | compile()
417 |
418 | previewText: (editor,view,text,err)->
419 | if view
420 | if err
421 | view.emitter.emit 'did-change-title', "#{view.getTitle()}.err"
422 | syntax = editor.getGrammar()
423 | view.setGrammar(syntax)
424 |
425 | view.setText(text)
426 | view.moveToTop()
427 | activePane = atom.workspace.paneForItem(editor)
428 | activePane.activate() if atom.config.get('pp.cursorFocusBack')
429 |
430 | consumeProjectManager: (PM) ->
431 | PM.projects.getCurrent (@project) =>
432 | # if project
433 |
434 | consumeBrowserPlus: (@bp) ->
435 |
436 |
437 | provideAddPreview: ->
438 | @addPreviews.bind(@)
439 |
440 | addPreviews: (requires,pkg=true)->
441 | if pkg
442 | if requires.deactivate
443 | return
444 | # pkage = _.find atom.packages.loadedPackages,(pkg)->
445 | # pkg.mainModulePath is module.id
446 | # return unless pkage
447 | # requires = [].push(requires) unless $.isArray(requires)
448 | return unless requires['pkgName']
449 | requires = [requires]
450 | _ids = []
451 | for req in requires
452 | try
453 | preview = if pkg then req else require req
454 | fileTypes = preview['fileTypes'] or []
455 | names = preview['names'] or []
456 | scopeNames = preview['scopeNames'] or []
457 | for key,obj of preview
458 | continue if key in ['fileTypes', 'names', 'scopeNames','pkgName']
459 | continue unless obj['exe']
460 | obj.name = key unless obj.name
461 | if pkg
462 | obj.fname = "#{obj.name} (#{preview['pkgName']})"
463 | else
464 | obj.fname = "#{obj.name} (#{req})"
465 | obj.fileTypes = (obj['fileTypes'] or []).concat fileTypes
466 | obj.names = (obj['names'] or []).concat names
467 | obj.scopeNames = (obj['scopeNames'] or []).concat scopeNames
468 | obj._id = uuid.v1()
469 | _ids.push obj._id
470 | @previews.push obj
471 | _ids
472 | catch e
473 | console.log 'check the requires setting in PP package',e
474 | atom.notifications.addInfo 'check the require settings in PP Package'+e
475 |
476 | getContent: (tag,text)->
477 | regex = new RegExp("([\\s\\S]*?)")
478 | match = text.match(regex)
479 | if match? and match[1].trim()
480 | data = loophole.allowUnsafeEval ->
481 | eval "(#{match[1]})"
482 |
483 | deactivate: ->
484 | @pp.destroy()
485 | @subscriptions.dispose()
486 |
487 | serialize: ->
488 | viewState = []
489 | for view in @views
490 | viewState.push if view.serialize?()
491 | previewState: @pp.serialize()
492 | viewState : viewState
493 | projectState: @project
494 |
495 | listen: ->
496 | textEditor = atom.workspace.getActiveTextEditor()
497 | if textEditor
498 | view = atom.views.getView(textEditor)
499 | atom.commands.dispatch(view,'pp:preview') if view
500 |
501 | getPosition: (editor)->
502 | activePane = atom.workspace.paneForItem(editor)
503 | paneAxis = activePane.getParent()
504 | paneIndex = paneAxis.getPanes().indexOf(activePane)
505 | orientation = paneAxis.orientation ? 'horizontal'
506 | if orientation is 'horizontal'
507 | if paneIndex is 0 then 'right' else 'left'
508 | else
509 | if paneIndex is 0 then 'down' else 'top'
510 |
511 | getText: (editor)->
512 | selected = editor.getSelectedText()
513 | quickPreview = true if selected
514 | fpath = editor.getPath() # unless ( selected or editor['preview-plus.livePreview'] )
515 | text = selected or editor.getText()
516 |
517 | if text.length is 0 or !text.trim() then throw new PPError 'alert','No Code to Compile' else { text, fpath, quickPreview}
518 |
519 | makeHTML: (obj = {})->
520 | cssURL = ''
521 | jsURL = ''
522 | obj.html or= ''
523 | obj.js or= ''
524 | obj.css or= ''
525 | for css in ( obj.cssURL or [] )
526 | cssURL = cssURL+ ""
527 |
528 | for js in ( obj.jsURL or [] )
529 | jsURL = jsURL+ ""
530 |
531 | """
532 |
533 |
534 | #{obj.js}
535 | #{jsURL}
536 | #{obj.css}
537 | #{cssURL}
538 |
539 |
540 | #{obj.html}
541 |
542 |
543 | """
544 |
--------------------------------------------------------------------------------
/lib/quick-view.coffee:
--------------------------------------------------------------------------------
1 | {TextEditor} = require 'atom'
2 | {$, ScrollView} = require 'atom-space-pen-views'
3 |
4 | module.exports =
5 | class QuickView extends ScrollView
6 | constructor: (@title,@src,@grammar)->
7 | @editor = new TextEditor mini:false
8 | @attach()
9 | super
10 |
11 | @content: ()->
12 | @div class: 'atom-text-panel qv-scroll', =>
13 | @div class:'pp-resizer-drag', mousedown: 'dragStart'
14 | @tag 'atom-text-editor'
15 |
16 | initialize: ->
17 | @editor = @view().find('atom-text-editor')[0].getModel()
18 | # @editor.setText(@src)
19 | # view = @view()
20 | @editor.setGrammar(@grammar)
21 | @view().find('atom-text-editor').css 'overflow','auto'
22 | # @view().find('atom-text-editor').parent().css 'overflow','auto'
23 | # @view().find('atom-text-editor').parent().parent().css 'overflow','auto'
24 |
25 | showPanel: (src,grammar)->
26 | @show()
27 | view = @view()
28 | # view.css 'max-height', '200px'
29 | view.css 'height', '200px'
30 | # @editor.setText(src)
31 | # @editor.setGrammar(grammar)
32 | # @editor.setCursorScreenPosition([1,1])
33 | @editor
34 |
35 | getTitle: ->
36 | @title
37 |
38 | getModel: ->
39 | @editor
40 |
41 | dragStart: (evt,ele)->
42 | view = @view()
43 | editorHeight = view.find('atom-text-editor').height()
44 | top = view.parent().position().top
45 | ht = view.height()
46 | width = view.width()
47 | view.css position :'fixed'
48 | view.css top: evt.pageY
49 | view.css width: width
50 | view.css 'max-height', ''
51 | view.css height: ht
52 | $(document).mousemove (evt,ele)=>
53 | view = @view()
54 | view.css top: evt.pageY
55 | height = ht + top - evt.pageY
56 | height = height * -1 if height < 0
57 | textEditorHeight = editorHeight + top - evt.pageY
58 | textEditorHeight = textEditorHeight * -1 if textEditorHeight < 0
59 | view.find('atom-text-editor').css height: textEditorHeight
60 | view.css height: height
61 | $(document).mouseup (evt,ele)=>
62 | view = @view().view()
63 | view.css position :'static'
64 | $(document).unbind('mousemove')
65 |
66 | attach: ->
67 | $(document).keyup (e)=>
68 | if e.keyCode == 27 # Escape Key
69 | @detach()
70 |
71 | detach: (hide=true)->
72 | # super
73 | # @unsubscribe()
74 | @hide() if hide
75 |
--------------------------------------------------------------------------------
/lib/runCoffee.js:
--------------------------------------------------------------------------------
1 | fpath = process.argv[2]
2 | path = require('path')
3 | process.stdin.setEncoding('utf8');
4 | var code = '';
5 | process.stdin.on('readable', function() {
6 | var chunk = process.stdin.read();
7 | if (chunk !== null) {
8 | code = code.concat(chunk)
9 | }
10 | });
11 |
12 | process.stdin.on('end', function() {
13 | coffee = require('coffee-script');
14 | js = coffee.compile(code)
15 | vm = require('vm');
16 | context = vm.createContext({
17 | require: require,
18 | register:require('coffee-script/register'),
19 | console: console,
20 | module:module,
21 | __filename: fpath
22 | });
23 | vm.runInContext(js,context, fpath);
24 | });
25 |
--------------------------------------------------------------------------------
/lib/worker.coffee:
--------------------------------------------------------------------------------
1 | debugger
2 | # console.log 'in the worker.js'
3 | process.on 'message', (data)->
4 | debugger
5 | console.log('@obj',args)
6 | process.send('foo')
7 | # @obj.program @obj.args,@obj.options
8 | # process.kill('SIGHUP')
9 |
10 | # process.stdout.on 'data',(data)->
11 | # console.log data
12 | # debugger
13 | # @obj.editor.insertText data.toString()
14 |
15 | process.stderr.on 'data',(data)->
16 | debugger
17 | console.log data
18 | # @obj.editor.insertText data.toString()
19 |
--------------------------------------------------------------------------------
/menus/pp.cson:
--------------------------------------------------------------------------------
1 | 'context-menu':
2 | 'atom-text-editor': [
3 | {
4 | 'label': 'Preview-Plus'
5 | 'command': 'pp:preview'
6 | }
7 | ]
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pp",
3 | "main": "./lib/pp",
4 | "version": "0.0.1",
5 | "description": "PP-PreviewPlus~framework for all previews",
6 | "repository": "https://github.com/skandasoft/pp",
7 | "license": "MIT",
8 | "engines": {
9 | "atom": ">=1.0.0 <2.0.0"
10 | },
11 | "dependencies": {
12 | "coffee-script": ">=1.8.0",
13 | "jquery": ">=2.1.3",
14 | "tmp": ">=0.0.24",
15 | "lodash": ">=3.10.1",
16 | "theorist": ">=1.0.2",
17 | "atom-space-pen-views": ">=2.0.3",
18 | "node-uuid": ">=1.4.7"
19 | },
20 | "providedServices": {
21 | "pp": {
22 | "description": "Add Your Preview",
23 | "versions": {
24 | "1.0.0": "provideAddPreview"
25 | }
26 | }
27 | },
28 | "consumedServices": {
29 | "status-bar": {
30 | "versions": {
31 | ">=0.0.0": "consumeStatusBar"
32 | }
33 | },
34 | "browser-plus": {
35 | "versions": {
36 | ">=0.0.0": "consumeBrowserPlus"
37 | }
38 | },
39 | "project-manager": {
40 | "versions": {
41 | "^2.2.1": "consumeProjectManager"
42 | }
43 | }
44 | },
45 | "keywords": [
46 | "preview",
47 | "preview-plus",
48 | "coffee2js",
49 | "jade",
50 | "markdown",
51 | "quickpreview"
52 | ]
53 | }
54 |
--------------------------------------------------------------------------------
/styles/pp.less:
--------------------------------------------------------------------------------
1 | // The ui-variables file is provided by base themes provided by Atom.
2 | //
3 | // See https://github.com/atom/atom-dark-ui/blob/master/styles/ui-variables.less
4 | // for a full listing of what's available.
5 | @import "ui-variables";
6 | @import "octicon-utf-codes";
7 | .pp {
8 | }
9 |
10 | .pp-resizer-drag {
11 | height:2px;
12 | top:0;
13 | left: 0;
14 | width: 100%;
15 | }
16 |
17 | .qv-scroll{
18 | overflow: auto;
19 | }
20 | .pp-resizer-drag:hover, .pp-resizer-drag:focus{
21 | height: 6px;
22 | cursor: ns-resize;
23 | width: 100%;
24 | height: 6px;
25 | border: 1px solid cornflowerblue;
26 | background-color: cornflowerblue;
27 | }
28 |
29 | .pp-status span{
30 | font-weight: bold;
31 | }
32 |
33 | .pp-status .compileTo{
34 | color: blueviolet;
35 | }
36 |
37 | .pp-status .live.hyper{
38 | color: green;
39 | }
40 |
41 | .pp-status .live.hyper::before{
42 | content: '*'
43 | }
44 |
45 | .pp-status .live.on{
46 | color: green;
47 | }
48 |
49 | .pp-status .live.off{
50 | color: red;
51 | }
52 | .pp-status span{
53 | padding: 0 10px;
54 | }
55 |
56 | .pp-status span.enums{
57 | padding: 0 3px;
58 | content: "▼";
59 | color: green;
60 | // display: none;
61 | }
62 |
63 | // .pp-space {
64 | // padding: 0 25px;
65 | // }
66 |
67 | span.pp-default{
68 | position: absolute !important;
69 | right: 25px;
70 | }
71 | span.mega-octicon{
72 | font-family: "Octicons Regular";
73 | font-size: 18px;
74 | }
75 | .octicon-star::before{
76 | content: '\f02a';
77 | }
78 |
79 | .pp-default.on{
80 | color: green;
81 | }
82 |
--------------------------------------------------------------------------------
/todo:
--------------------------------------------------------------------------------
1 | check if browser plus / options?? as a string
2 | js - run
3 | jade - with data
4 | react - pick js/css - html
5 | facebook reactsjsx
6 |
7 | quick scss/sass/less/stylus - APM
8 | typescript
9 | handlebars
10 | cjsx
11 | ruby/erb
12 | slim/haml... - APM
13 |
14 | save defaults
15 | riotjs
16 |
17 |
18 | kill all the process/defaults on change of grammar
19 | run/kill --> live updating result/kill all process
20 |
21 | mark down(in flavors) - APM
22 | jade as APM implementation
23 | js/script/css/resources
24 | history
25 | handle bars/others
26 |
27 |
28 | get the previews with name..
29 | each editor maintained in the browser-plus has an editor status...
30 | if the file type changes ...change the editor status
31 | editor status has the live status../enum/tool bar (name/view )/ has the preview object...
32 | everytime call the getList of previews..when clicked on enum...on selection update the editor status with
33 | preview object and call the compile to/run/method...
34 | check if it is the default or update default...
35 |
36 |
37 | on compile -->what all things flows..
38 | what is the file name starts with..../ends with
39 | code cleanup/with support for source maps..
40 |
41 | pick executable and show on statusbar
42 | execute from click of statusbar -> preview
43 | live/ultra live
44 | switch editor
45 | pick list(with full name)
46 | set default
47 | quick preview / fix drag panel
48 | get parameters
49 | global(on the read file)/ fileType
50 | try read project level data/options/ at the start of the activations...
51 | project level/ ,data,options,...
52 |
53 | check if hyper live possible/quick preview
54 | should prompt for save/ esc key
55 |
56 | html implmentation - done/ unformatted
57 |
58 | * addition / live preview html/output status bar template/save the last guy....
59 | make open-in-browsers as PP with URL support (compileView)
60 | file implementation as NPM
61 | browser fix for documentation
62 |
63 |
64 | scroll sync..
65 | quick pdf ?
66 |
--------------------------------------------------------------------------------