├── .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 | ![pp(preview-plus)](https://raw.github.com/skandasoft/pp/master/PP.png)] 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 | --------------------------------------------------------------------------------