Ниже я приведу пример написания своего package для Atom.
Мы будем создавать package для генерирования marionettejs файлов на основе библиотеки о которой я писал ранее.
Для создания package я предлагаю использовать package-generator (должен быть установлен как package в вашем Atom)
- Идем в пункт меню
Packages->Package Generator->Generate Atom Package
- Вводим имя. В моем случае
atom-marionettejs-cli
- Enter
Открываем наш package для редактирования.
Preferences->Packages
-> находим свой package, открываем его, View Code
Отлично.
Теперь отредактируем меню.
Идем в ./menus/package-generator.cson
, смотрим в документацию и подгоняем под себя.
'context-menu':
'atom-text-editor': [
{
'label': 'Generate marionettejs application'
'command': 'atom-marionettejs-cli:generate-app'
}
]
'menu': [
{
'label': 'Packages'
'submenu': [
'label': 'MarionetteJS CLI'
'submenu': [
{
'label': 'Generate marionettejs application'
'command': 'atom-marionettejs-cli:generate-app'
}
{
'label': 'Generate marionettejs file'
'command': 'atom-marionettejs-cli:generate-file'
}
{
'label': 'Set type'
'command': 'atom-marionettejs-cli:set-type'
}
]
]
}
]
И так, мы хотим чтобы юзер имел возможность выбрать файл, который он хочет сгенерировать, из списка. Для этого используем SelectListView
{SelectListView} = require 'atom-space-pen-views'
items =
'type': [
{
label: 'ES6'
command: 'es6'
},
{
label: 'CommonJS'
command: 'cjs'
},
{
label: 'RequireJS'
command: 'rjs'
}
],
'file': [
{
label: 'Layout'
command: '--layout'
},
{
label: 'Collection'
command: '--collection'
},
{
label: 'Model'
command: '--model'
},
{
label: 'Router'
command: '--router'
},
{
label: 'Object'
command: '--object'
},
{
label: 'Item View'
command: '--itemView'
},
{
label: 'Collection View'
command: '--collectionView'
},
{
label: 'Composite View'
command: '--compositeView'
},
{
label: 'Behavior'
command: '--behavior'
},
]
module.exports =
class AtomMarionettejsCliView extends SelectListView
mode: null
viewForItem: (item) ->
"<li data-command='#{item.command}'>#{item.label}</li>"
showModalPanel: (@mode) ->
@panel ?= atom.workspace.addModalPanel(item: this, visible: false)
@addClass('overlay from-top')
@setItems(items[@mode])
@panel.show()
@focusFilterEditor()
cancelled: ->
@panel.hide()
getCommand: ->
selectedItem = this.getSelectedItemView();
return selectedItem.data().command;
Теперь рассмотрим наш главный файл (./lib/atom-marionettejs-cli
)
Кстати, если хотите, чтобы все работало, а главный файл лежал в другом месте или содержал другое имя, просто измените строчку в вашем package.json
"main": "./lib/atom-marionettejs-cli"
Во время активации package создадим наше view и навешаем подписки на события которые будут тригериться при клике на пункт меню
...
{
'label': 'Generate marionettejs application'
'command': 'atom-marionettejs-cli:generate-app'
}
...
activate: () ->
@modalPanel = new AtomMarionettejsCliView()
@subscriptions = new CompositeDisposable
@subscriptions = atom.commands.add 'atom-workspace',
'atom-marionettejs-cli:generate-app': => @attach('app')
'atom-marionettejs-cli:generate-file': => @attach('file')
'atom-marionettejs-cli:set-type': => @attach('type')
Теперь нужно как-то обрабатывать событие на клик в нашем листе.
@modalPanel.confirmed = ->
path = getPath()
command = @getCommand();
if @mode is 'file'
fileName = filePrefix + command
args = ['g', command, fileName, path]
else
args = ['s', command];
cli.run(args);
@panel.hide()
Метод confirmed
— это метод нашей SelectListView. Он вынесен сюда, чтобы не разносить вызов CLI (cli.run()
) по разным файлам.
AtomMarionettejsCliView = require './atom-marionettejs-cli-view'
{CompositeDisposable, BufferedNodeProcess} = require 'atom'
filePrefix = 'marionette-'
cli = require 'marionette-cli/lib/cli'
getPath = ->
editor = atom.workspace.getActivePaneItem()
file = editor?.buffer.file
projectPath = atom.project.getPaths()[0]
# if opened file or project doesn't exist
if !file && !projectPath
throw new Error ('Create project or open some file')
# get path of opened file
path = editor?.buffer.file.path
# if no opened tabs
if !path
return projectPath
regexp = /(.*/).*/g
# get path to file
regexp.exec(path)[1]
module.exports =
modalPanel: null
mode: null
subscriptions: null
activate: () ->
@modalPanel = new AtomMarionettejsCliView()
@subscriptions = new CompositeDisposable
@subscriptions = atom.commands.add 'atom-workspace',
'atom-marionettejs-cli:generate-app': => @attach('app')
'atom-marionettejs-cli:generate-file': => @attach('file')
'atom-marionettejs-cli:set-type': => @attach('type')
@modalPanel.confirmed = ->
path = getPath()
command = @getCommand();
if @mode is 'file'
fileName = filePrefix + command
args = ['g', command, fileName, path]
else
args = ['s', command];
cli.run(args);
@panel.hide()
deactivate: ->
@modalPanel.destroy()
@subscriptions.dispose()
attach: (@mode) ->
switch @mode
when 'app'
@generateApp()
when 'file', 'type'
@modalPanel.showModalPanel(@mode)
generateApp: ->
appPath = getPath() + '/app'
cli.run(['new', appPath]);
Несколько небольших советов
- для отладки используйте консоль (
View->Developer->Toggle Developer Tools
) - после внесенных изменений в файлы делайте перезагрузку окна (
View->Developer->Reload Window
), потом проверяйте работоспособность кода - при использовании сторонних библиотек возможна ошибка типа
Refused to evaluate a string as JavaScript because 'unsafe-eval'...
. Для борьбы с ней можно использовать loophole
Пишем документацию, приводим в порядок package.json
и можно релизить.
git commit -am 'first release'
apm publish --tag v0.1.0 minor
Репозиторий на Github
Package на atom.io
Спасибо за внимание.
P.S. Не судите строго, это был мой первый опыт с coffeescript
)
Автор: denar90