Skip to content

Commit

Permalink
Finals are optional. PROS features are not. This contains the start o…
Browse files Browse the repository at this point in the history
…f the Conductor GUI
  • Loading branch information
edjubuh committed Dec 16, 2016
1 parent 11c285c commit a3afeca
Show file tree
Hide file tree
Showing 20 changed files with 985 additions and 137 deletions.
2 changes: 1 addition & 1 deletion coffeelint.json
@@ -1,6 +1,6 @@
{
"max_line_length": {
"value": "100",
"value": "110",
"level": "warn"
}
}
53 changes: 52 additions & 1 deletion lib/cli.coffee
@@ -1,5 +1,6 @@
path = require 'path'
utils = require './terminal-utilities'
semver = require 'semver'

module.exports=
execute: utils.execute
Expand Down Expand Up @@ -29,11 +30,17 @@ module.exports=
terminal: (args...) ->
return @baseCommand().concat(['terminal'].concat(args...))

infoProject: (args...) ->
return @baseCommand().concat(['conduct', 'info-project']).concat(args...)

addLib: (args...) ->
return @baseCommand().concat(['conduct', 'new-lib']).concat(args...)

executeParsed: (cb, command, params) ->
if '--machine-output' not in command
command.push '--machine-output'
callback = (c, o) ->
cb c, JSON.parse e for e in o.split(/\r?\n/).filter(Boolean)
cb c, JSON.parse e for e in o?.split(/\r?\n/).filter(Boolean)
return utils.execute callback, command, params

executeParsedSync: (command) ->
Expand All @@ -48,6 +55,14 @@ module.exports=
getTemplatesSync: (args...) ->
@executeParsedSync(@lstemplate(args...))

projectInfo: (cb, args...) ->
@executeParsed cb, @infoProject(args...), {}

projectInfoSync: (cb, args...) ->
@executeParsedSync @infoProject args...

addLibraryExecute: (cb, args...) ->
utils.execute cb, @addLib args...

createNewExecute: (cb, args...) ->
utils.execute cb, @createNew args...
Expand All @@ -72,3 +87,39 @@ module.exports=
# wrapper function for scoping reasons
runInTerminal: (command) ->
return utils.executeInTerminal command.split ' '

checkCli: (minVersion, cb) ->
# wait... maybe environment not yet properly loaded yet
process.nextTick ->
utils.execute(((c, o) ->
if c != 0
console.log o
cb 2, 'PROS CLI was not found on your PATH.'
else
utils.execute(((c, o) ->
if c != 0
console.log o
cb 3, 'PROS CLI is improperly configured.'
else
version = /pros, version (.*)/g.exec(o)?[1]
if not version or semver.lt(version, minVersion)
if version is undefined
# it's posssible that it failed by chance... try again
utils.execute(((c, o) ->
if c != 0
console.log o
cb 3, 'PROS CLI is improperly configured.'
else
version = /pros, version (.*)/g.exec(o)?[1]
if not version or semver.lt(version, minVersion)
console.log o
cb 1, "PROS CLI is out of date. (#{version} does not meet #{minVersion})"
else
cb 0, version
), ['pros', '--version'])
console.log o
cb 1, "PROS CLI is out of date. (#{version} does not meet #{minVersion})"
else
cb 0, version
), ['pros', '--version'])
), ['where', 'pros'])
72 changes: 48 additions & 24 deletions lib/main.coffee
Expand Up @@ -5,6 +5,7 @@
# {`TerminalView`} = require './views/terminal/terminal-view'
{Disposable} = require 'atom'
fs = require 'fs'
path = require 'path'
cli = require './cli'
terminal = require './terminal-utilities'
GA = require './ga'
Expand All @@ -15,19 +16,21 @@ universalConfig = require './universal-config'
autocomplete = require './autocomplete/autocomplete-clang'
buttons = require './buttons'
StatusBar = require './views/statusbar'
utils = require './utils'

WelcomeView = null
ConductorView = null
AddLibraryModal = null

createWelcomeView = (state) ->
WelcomeView = require './views/welcome/welcome-view'
new WelcomeView(state)
welcomeUri = 'pros://welcome'
conductorUri = 'pros://conductor'
conductorRegex = /conductor\/(.+)/i
addLibraryUri = 'pros://addlib'
addLibraryRegex = /addlib\/(.+)/i

module.exports =
provideBuilder: provideBuilder

showWelcome: ->
atom.workspace.open 'pros://welcome'

activate: ->
@subscriptions = new CompositeDisposable
require('atom-package-deps').install('pros').then () =>
Expand Down Expand Up @@ -65,31 +68,51 @@ module.exports =
name: 'ProsWelcomeView'
deserialize: (state) -> createWelcomeView state

atom.commands.add 'atom-workspace', 'PROS:New-Project': -> new (require './views/new-project')
atom.commands.add 'atom-workspace', 'PROS:Upgrade-Project': => @upgradeProject()
atom.commands.add 'atom-workspace', 'PROS:Register-Project': => @registerProject()
atom.commands.add 'atom-workspace', 'PROS:Upload-Project': => @uploadProject()
atom.commands.add 'atom-workspace', 'PROS:Toggle-Terminal': => @toggleTerminal()
atom.commands.add 'atom-workspace', 'PROS:Show-Welcome': -> atom.workspace.open welcomeUri
atom.commands.add 'atom-workspace', 'PROS:Toggle-PROS': => @togglePROS()
atom.commands.add 'atom-workspace', 'PROS:Open-Conductor': ->
currentProject = atom.project.relativizePath atom.workspace.getActiveTextEditor()?.getPath()
if currentProject[0]
atom.workspace.open "#{conductorUri}/#{currentProject[0]}"
else
atom.workspace.open conductorUri
atom.commands.add 'atom-workspace',
'PROS:New-Project': => @newProject()
atom.commands.add 'atom-workspace',
'PROS:Upgrade-Project': => @upgradeProject()
atom.commands.add 'atom-workspace',
'PROS:Register-Project': => @registerProject()
atom.commands.add 'atom-workspace',
'PROS:Upload-Project': => @uploadProject()
atom.commands.add 'atom-workspace',
'PROS:Toggle-Terminal': => @toggleTerminal()
atom.commands.add 'atom-workspace',
'PROS:Show-Welcome': => @showWelcome()
atom.commands.add 'atom-workspace',
'PROS:Toggle-PROS': => @togglePROS()
'PROS:Add-Library': ->
new (require './views/add-library') path: atom.project.getPaths()?[0]

@subscriptions.add atom.workspace.addOpener (uri) ->
if uri is 'pros://welcome'
createWelcomeView uri: 'pros://welcome'
if uri is welcomeUri
WelcomeView ?= new (require './views/welcome') {welcomeUri}

@subscriptions.add atom.workspace.addOpener (uri) ->
if uri.startsWith conductorUri
ConductorView ?= new (require('./views/conductor')) {conductorUri}
if match = conductorRegex.exec uri
ConductorView.updateAvailableProjects()
ConductorView.updateSelectedPath match[1]
ConductorView

if atom.config.get 'pros.welcome.enabled'
@showWelcome()
atom.workspace.open welcomeUri

cli.execute(((c, o) -> console.log o if o),
cli.baseCommand().concat ['conduct', 'first-run', '--no-force', '--use-defaults'])
@PROSstatus = true

grammarSubscription = atom.grammars.onDidAddGrammar (grammar) ->
if grammar.scopeName is 'source.json'
grammarSubscription.dispose()
grammar.fileTypes.push 'pros'
process.nextTick ->
for e in atom.workspace.getTextEditors()
if path.extname(e.getPath()) == '.pros'
e.setGrammar grammar

deactivate: ->
# End client session
if atom.config.get('pros.googleAnalytics.enabled') and \
Expand Down Expand Up @@ -132,9 +155,7 @@ module.exports =

consumeToolbar: (getToolBar) =>
@toolBar = getToolBar('pros')

buttons.addButtons @toolBar

@toolBar.onDidDestroy => @toolBar = null

autocompleteProvider: ->
Expand All @@ -143,4 +164,7 @@ module.exports =
consumeStatusBar: (statusbar) ->
@statusBarTile = new StatusBar(statusbar)

deserializeConductorView: (data) ->
ConductorView ?= new (require('./views/conductor')) data

config: universalConfig.filterConfig config.config, 'atom'
2 changes: 1 addition & 1 deletion lib/terminal-utilities.coffee
Expand Up @@ -26,7 +26,7 @@ module.exports =
outBuf += data
params?.onstdout?(data)
proc.on 'exit', (c, o, e) ->
cb c, outBuf
cb c, outBuf or o
return proc

executeSync: (command) ->
Expand Down
13 changes: 13 additions & 0 deletions lib/utils.coffee
@@ -1,6 +1,7 @@
fs = require 'fs-plus'
path = require 'path'
cp = require 'child_process'
async = require 'async'
@cliVersion = ''
@pkgVersion = ''

Expand Down Expand Up @@ -48,3 +49,15 @@ module.exports =
@cliVersion = stdout.replace 'pros, version ', ''

# cb0 @cliVersion

findOpenPROSProjectsSync: ->
results = []
traversal = (dir) ->
fs.traverseTreeSync dir,
((file) ->
if path.basename(file) == 'project.pros'
results.push path.dirname file
),
((d) ->)
traversal project for project in atom.project.getPaths()
return results
123 changes: 123 additions & 0 deletions lib/views/add-library.coffee
@@ -0,0 +1,123 @@
{CompositeDisposable, Disposable} = require 'atom'
{$, View, TextEditorView} = require 'atom-space-pen-views'
fs = require 'fs'
path = require 'path'
cli = require '../cli'

module.exports =
class AddLibraryModal extends View
@content: ->
@div class: 'pros-add-library', tabindex: -1, =>
@h1 'Add Library to PROS Project'
@h4 'Choose a project:'
@div class: 'select-list', id: 'projectPathPicker', outlet: 'projectPathPicker', =>
@div style: 'display: flex; flex-direction: row-reverse;', =>
@button class: 'btn btn-default', outlet: 'openDir', =>
@span class: 'icon icon-ellipsis'
@button class: 'btn btn-default', outlet: 'toggleListButton', =>
@span class: 'icon icon-three-bars'
@subview 'projectPathEditor', new TextEditorView mini: true
@ol class: 'list-group', =>
for p in atom.project.getPaths()
if fs.existsSync path.join p, 'project.pros'
@li p
@h4 'Choose a library:'
@div class: 'library-picker select-list', =>
@ol class: 'list-group', outlet: 'libraryList', =>
@li 'Loading...'
@div class: 'actions', =>
@div class: 'btn-group', =>
@button outlet: 'cancelButton', class: 'btn', 'Cancel'
@button outlet: 'addButton', class: 'btn btn-primary icon icon-rocket', 'Add Library'
@span class: 'loading loading-spinner-tiny'


initialize: ({_path, @cb}={}) ->
atom.keymaps.add 'add-library-keymap',
'.pros-add-library':
'escape': 'core:cancel'
atom.commands.add @element, 'core:cancel', => @cancel()
@panel ?= atom.workspace.addModalPanel item: this, visible: false

@cancelButton.click => @cancel()

@openDir.click => atom.pickFolder (paths) =>
if paths?[0]
@projectPathEditor.setText paths[0]
$('#projectPathPicker ol').removeClass 'enabled'

@toggleListButton.click -> $('#projectPathPicker ol').toggleClass 'enabled'

$('#projectPathPicker ol li').on 'click', (e) =>
@projectPathEditor.setText e.target.innerText
$('#projectPathPicker ol').removeClass 'enabled'

updateDisable = =>
if !!!@projectPathEditor.getText()
@addButton.prop 'disabled', true
else if !fs.existsSync path.join @projectPathEditor.getText(), 'project.pros'
@addButton.prop 'disabled', true
else if not @selected
@addButton.prop 'disabled', true
else
@addButton.prop 'disabled', false

@projectPathEditor.getModel().onDidChange =>
updateDisable()
if fs.existsSync path.join @projectPathEditor.getText(), 'project.pros'
cli.projectInfo(((c, info) =>
if Object.keys(info.libraries).some((k) -> info.libraries.hasOwnProperty k)
for n, v of info.libraries
@libraryList.children('.primary-line.icon-check').removeClass 'icon icon-check'
for child in @libraryList.children()
value = $(child).data 'value'
if value?.library == n and value?.version == v.version
$(child).children('.primary-line').addClass 'icon icon-check'
), @projectPathEditor.getText())

@addButton.click =>
if dir = @projectPathEditor.getText()
if template = @selected.data 'value'
$(@element).find('.actions').addClass 'working'
cli.addLibraryExecute(((c, o) =>
@cancel(true) # destroy the modal
if c is 0
atom.notifications.addSuccess "Added #{template.library} to #{path.basename dir}", detail: o
atom.project.addPath dir
else
atom.notifications.addError "Failed to add #{template.library} to #{path.basename dir}",
detail: o
dismissable: true
), "\"#{dir}\"", template.library, template.version, template.depot)

if !!_path then @projectPathEditor.setText _path
@panel.show()
@projectPathEditor.focus()

cli.getTemplates(((c, o) =>
@libraryList.empty()
for {library, version, depot} in o
li = document.createElement 'li'
li.className = 'two-lines library-option'
li.setAttribute 'data-value', JSON.stringify {library, version, depot}
li.innerHTML = "
<div class='primary-line'>#{library}</div>
<div class='secondary-line'><em>version</em> #{version} <em>from</em>
#{depot}</div>"
@libraryList.append li

$('li.library-option').on 'click', (e) =>
@selected?.removeClass 'selected'
@selected?.children('.primary-line').removeClass 'icon icon-chevron-right'
@selected = $(e.target).closest 'li.library-option'
updateDisable()
@selected.addClass 'selected'
@selected.children('.primary-line').addClass 'icon icon-chevron-right'
), '--offline-only --libraries')

cancel: (complete=false)->
@panel?.hide()
@panel?.destroy()
@panel = null
atom.workspace.getActivePane().activate()
@cb?(complete)
8 changes: 8 additions & 0 deletions lib/views/brand.coffee
@@ -0,0 +1,8 @@
module.exports =
# coffeelint: disable=max_line_length
tuxFullColor: "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 89 89.6'><style>.a{fill:#D2AB67;}.b{fill:#E7BC70;}.c{fill:#060500;}.d{fill:#2F2D29;}.e{fill:#7E868C;}</style><polygon points='73.4 37.6 88.6 50.7 44.4 89.2 44.4 89.1 44.4 89.2 0.4 50.9 15.7 37.7 44.5 79.9 ' class='a'/><polygon points='44.5 89.1 88.6 50.7 73.4 37.6 44.5 79.9 ' class='b'/><polygon points='81.9 21.6 44.5 76.5 7.1 21.6 44.5 0.4 ' class='a'/><path d='M44.5 76.4l37.3-54.8 -6 8.6H44.5V76.4zM56.5 52.9l-0.3-19.6 19.4-2.8L56.5 52.9z' class='b'/><polygon points='44.5 0.4 44.5 10.7 47.4 12.1 47.4 15 44.5 16.5 44.5 21.6 81.9 21.6 ' class='b'/><polygon points='81.9 21.6 44.5 67.7 7.1 21.6 44.5 4.8 ' class='c'/><polygon points='44.5 4.8 44.5 4.8 44.5 67.6 44.5 67.7 81.9 21.6 ' class='d'/><polygon points='13.1 30.3 75.8 30.3 81.9 21.6 7.1 21.6 ' class='a'/><polygon points='44.5 21.6 44.5 30.3 75.8 30.3 81.9 21.6 ' class='b'/><polygon points='65.6 25.9 23.4 25.9 20.4 21.5 68.5 21.5 ' class='c'/><polygon points='44.5 21.5 44.5 25.9 65.6 25.9 68.6 21.5 ' class='d'/><polygon points='22.5 24.4 23.4 25.9 65.6 25.9 66.5 24.4 ' class='e'/><circle cx='44.5' cy='33.7' r='1.2' class='e'/><circle cx='44.5' cy='38.8' r='1.2' class='e'/><circle cx='44.5' cy='43.8' r='1.2' class='e'/><polygon points='32.5 52.7 32.7 33.2 13.3 30.3 ' class='a'/><polygon points='32.5 52.7 27.5 38.2 13.3 30.3 ' class='c'/><polygon points='56.5 52.9 56.2 33.2 75.6 30.4 ' class='a'/><polygon points='56.2 33.2 56.5 52.9 75.6 30.4 ' class='b'/><polygon points='56.5 52.9 61.6 38.2 75.6 30.4 ' class='d'/><polygon points='47.4 15 44.5 16.5 41.5 15 41.5 12.1 44.5 10.7 47.4 12.1 ' class='a'/><polygon points='44.5 10.7 44.5 16.5 47.4 15 47.4 12.1 ' class='b'/><path d='M45.2 70.8c0 0.3-0.3 0.6-0.6 0.6h-0.3c-0.3 0-0.6-0.3-0.6-0.6v-22.3c0-0.3 0.3-0.6 0.6-0.6h0.3c0.3 0 0.6 0.3 0.6 0.6V70.8z' class='e'/></svg>"

tuxSingleColor: "<svg xmlns='http://www.w3.org/2000/svg' width='89' height='90' viewBox='0 0 89 89.6'><polygon points='73.4 35.4 88.6 48.5 44.4 87 44.4 86.9 44.4 87 0.4 48.7 15.7 35.5 44.5 77.7 ' class='a'/><polygon points='22.5 22.2 23.4 23.7 65.6 23.7 66.5 22.2 ' class='a'/><polygon points='32.5 50.5 27.5 36 13.3 28.1 ' class='a'/><polygon points='56.5 50.7 61.6 36 75.6 28.2 ' class='a'/><path d='M44.5 2.6L7.1 19.4h13.4l2.8 4.3h42.2l2.8-4.3h13.4L44.5 2.6zM47.4 12.9l-2.9 1.5 -2.9-1.5V9.9l2.9-1.5 2.9 1.5V12.9z' class='a'/><path d='M74.8 28.1H14.1l0.1 0.1 18.5 2.8 -0.3 19.5 -2.4-2.8 13.7 16.8v-18.3c0-0.3 0.3-0.6 0.6-0.6h0.3c0.3 0 0.6 0.3 0.6 0.6v18.3l11.4-14 -0.1 0.1 -0.3-19.6L74.6 28.4 74.8 28.1zM44.5 42.8c-0.6 0-1.2-0.5-1.2-1.2 0-0.6 0.5-1.2 1.2-1.2 0.6 0 1.2 0.5 1.2 1.2C45.6 42.2 45.1 42.8 44.5 42.8zM44.5 37.8c-0.6 0-1.2-0.5-1.2-1.2 0-0.6 0.5-1.2 1.2-1.2 0.6 0 1.2 0.5 1.2 1.2C45.6 37.2 45.1 37.8 44.5 37.8zM44.5 32.6c-0.6 0-1.2-0.5-1.2-1.2s0.5-1.2 1.2-1.2c0.6 0 1.2 0.5 1.2 1.2S45.1 32.6 44.5 32.6z' class='a'/></svg>"

text: "<svg xmlns='http://www.w3.org/2000/svg' width='160' height='35' viewBox='0 0 160 35'><path d='M29.9 12.6c0 7.8-4.9 12.4-13.4 12.4h-5.9v9.2H1.9V0.9h14.6C25 0.9 29.9 5.2 29.9 12.6zM21.6 12.8c0-3.3-2-5-5.5-5h-5.5v10.2h5.5C19.6 18 21.6 16.2 21.6 12.8z'/><path d='M62.5 34.1l-4.8-9.2h-0.2 -6.3v9.2h-8.7V0.9h14.9c8.8 0 13.8 4.3 13.8 11.7 0 5-2.1 8.7-5.9 10.7l6.9 10.8H62.5zM51.3 18h6.3c3.5 0 5.5-1.8 5.5-5.2 0-3.3-2-5-5.5-5h-6.3V18z'/><path d='M119.1 17.5c0 9.7-7.7 17-18 17 -10.3 0-17.9-7.3-17.9-17 0-9.7 7.7-16.8 18-16.8C111.4 0.7 119.1 7.9 119.1 17.5zM92.1 17.5c0 5.4 4.2 9.6 9.1 9.6 5 0 9-4.2 9-9.6s-4-9.5-9-9.5C96.3 8.1 92.1 12.1 92.1 17.5z'/><path d='M144 7.6c-2.1 0-3.5 0.8-3.5 2.3 0 5.5 17.5 2.4 17.5 14.3 0 6.8-6 10.2-13.4 10.2 -5.5 0-11.3-2-15.3-5.3l3.4-6.8c3.4 2.9 8.6 5 12 5 2.6 0 4.2-0.9 4.2-2.7 0-5.6-17.5-2.2-17.5-14 0-6.2 5.3-10.1 13.3-10.1 4.9 0 9.8 1.5 13.3 3.7l-3.3 6.9C151.2 9.1 146.8 7.6 144 7.6z'/></svg>"
# coffeelint: enabled=max_line_length

0 comments on commit a3afeca

Please sign in to comment.