您的位置:首页 > 其它

Atom插件开发入门教程(三)

2017-01-09 16:24 609 查看


插件: 文本计数器

让我们开始写一个非常简单的而又实用的插件吧。文本计数插件:统计当前缓冲里有多少文字,并用一个小窗口把结果显示出来。


插件包生成器(Package
Generator)

创建插件最简单的方法是用Atom内置的插件包生成器( package generator)。 你可能已经猜到了, package generator 自己也是一个插件包。(详情参见:package-generator

你可以在command palette里搜索 "Generate Package"来运行它。这时会弹出一个对话框,让你输入插件的名字,建议输入: 
your-name-word-count
.
Atom 会创建一个目录并插入默认的插件代码。默认位置是:
%USERPROFILE%\.atom\packages
 ,这样重启Atom时,就会自动加载这个插件了。

注意: 如果你的插件包没有被加载。那很可能是因为有一个相同名称的插件已经被上传到了atom.io 。所以建议大家建立插件时,加上自己名字的缩写或者公司缩写,来保证插件名称的唯一性。



如上图, Atom 已经创建了插件包的基础代码。下面我们来介绍插件包的代码是怎样构成的:

最基础的路径结构如下:
my-package/
├─ grammars/
├─ keymaps/
├─ lib/
├─ menus/
├─ spec/
├─ snippets/
├─ styles/
├─ index.coffee
└─ package.json


不是每个插件都需要这些文件,自动创建的代码里,也没有
snippets
 和 
grammars
目录。

package.json


类似于 Node modules, Atom 插件包的根目录必须有一个 
package.json
 文件。这里包含了插件包的基础信息,比如
"main" 模块的路径,依赖库, 和指定的资源加载顺序。

下面介绍 
package.json
文件中各个参数的意义:

main
: CoffeeScript(JavaScript)代码文件的路径,如果不填写此项目, Atom 会自动寻找 
index.coffee
 或 
index.js。


styles
: 一个字符串数组,说明Styles文件的加载顺序,如果不填写,Atom会自动在styles目录下按文件名字母排序的方式去加载样式文件。注意,样式支持css和less文件。
keymaps
:
一个字符串数组,说明keymaps文件的加载顺序,如果不填,则Atom自动在
keymaps
目录下按字母排序的方式去加载。

menus
: 一个字符串数组,说明memus配置文件在哪里加载。如果不填, 则Atom自动在
menus
 目录下按字母排序的方式加载。

snippets
: 一个字符串数组,说明插件中使用的snippets从哪里加载。如果不填,则Atom自动在 
snippets
 目录下按字母排序的方式去加载。

activationCommands
: 一个对象, 说明哪个命令来激活你的插件。如果不填写, 则Atom会在插件激活时,自动调用 
activate()
函数。

activationHooks
: 一个字符串数组,说明使用哪种语法来解释脚本。一般来说,我们填写: 
language-package-name:grammar-used
 (e.g.,
language-javascript:grammar-used
)

下面是一个 
package.json
 的例子:
{
"name": "wordcount",
"main": "./lib/wordcount",
"version": "0.0.0",
"description": "A short description of your package",
"activationCommands": {
"atom-workspace": "wordcount:toggle"
},
"repository": "https://github.com/atom/your-name-word-count",
"license": "MIT",
"engines": {
"atom": ">=1.0.0 <2.0.0"
},
"dependencies": {
}
}


如果使用了 activationHooks, 代码是:
{
"name": "wordcount",
"main": "./lib/wordcount",
"version": "0.0.0",
"description": "A short description of your package",
"activationHooks": ["language-javascript:grammar-used", "language-coffee-script:grammar-used"],
"repository": "https://github.com/atom/your-name-word-count",
"license": "MIT",
"engines": {
"atom": ">=1.0.0 <2.0.0"
},
"dependencies": {
}
}


首先,要填写这些基本信息。

Warning: 切记要更新 repository URL. 否则你的更改将无法分享给他人。


源代码


首先你的插件必须包含一个[top-level]模块, 在 
package.json
 文件里的main字段下声明它。对于我们这个例子来说,[top-level]文件是 
lib/wordcount.coffee
.
你编写的源代码应该放在 
lib
 目录下,并且用[top-level]文件中required它。 如果
main
 没写,则Atom自动选择 
index.coffee
 或 
index.js
 作为[top-level]文件。

插件的[top-level] 模块是一个单例类。生命周期由 Atom来管理。 不论你创建多少Dom或者View, 它们全部由[top-level]来管理。

插件的[top-level] 模块可以实现下列的基本接口:
activate(state)
: 插件激活时被调用。如果你实现了 
serialize()
 接口,它会传递窗口上次的state.
一般用这个接口来初始化插件。
initialize(state)
: (Atom 1.14以后的版本支持) 与
activate()
 功能类似,不过被调用的更早,可以说在所有操作之前被调用。 如果你想等环境运行完毕了再初始化,请用 
activate()
 。如果需要在画面构造前执行什么,请用
initialize()
 。
serialize()
: 在窗口被关闭的时候,允许你返回 JSON 序列,来保存当前的状态。你保存的信息,可以传递给
activate()
 接口,来在下次启动时恢复你的窗口状态。
deactivate()
: 窗口关闭时会调用这个接口,如果你的插件正在使用某些资源,或关联着某些文件,请在这里释放他们。 

样式


之前说过,插件的样式应该放在 
styles
 目录里。 支持 CSS 或 Less,
推荐使用Less。

理想的来说, 你不需要这种方式来做样式。Atom提供了基础的UI样式。 打开 command palette Ctrl+Shift+P + Ctrl+Shift+G来查找Atom默认提供的插件UI样式吧。

如果你需要特别的UI样式,建议只保留结构样式。如果你要使用特别的颜色、字体等,建议使用Atom提供的主题样式。参见 ui-variables.less.

快捷键


插件可以绑定多个快捷键, 举例参见 
keymaps/wordcount.cson
 文件:
'atom-workspace':
'ctrl-alt-o': 'wordcount:toggle'


这将快捷键 Alt+Ctrl+O,
设置为运行 
your-name-word-count:toggle
 命令。创建插件时,Atom会自动分配一个快捷键给它,当然你也可以自行定义。

快捷键配置文件在 
keymaps
 子路径中。默认的所有快捷键配置文件按字母排序来加载。如果需要特别的加载顺序,请在 
package.json
 文件中配置。

快捷键的启动,也受到当前Dom元素(当前正在操作的View)是哪个的影响。在本例中 
your-name-word-count:toggle
 命令是在
atom-workspace
 元素上按下Alt+Ctrl+O 时会被执行。因为 
atom-workspace
 是整个
Atom UI的上级。这意味着,任何时候按下Alt+Ctrl+O 都会被执行。

如果想更深入的了解快捷键有关的内容,请参见 Keymaps in Depth.

菜单


菜单在 
menus
 子目录下。它定义了右键弹出菜单和工具栏菜单。

菜单默认按字母排序加载。 你可以在
package.json
 中编写自定义的加载顺序。

Application
菜单


如果你的插件不是对特定元素使用的,建议你在Packages菜单下创建一个application菜单项。 在
menus/your-name-word-count.cson
 文件中,我们能看到如下代码片段:
'menu': [
{
'label': 'Packages'
'submenu': [
'label': 'Word Count'
'submenu': [
{
'label': 'Toggle'
'command': 'your-name-word-count:toggle'
}
]
]
}
]


上述代码在Packages菜单下,添加了WorkCount子项,并在其中添加了Toggle项目。



如果选择这个选项,会执行 
your-name-word-count:toggle
 命令。

菜单项会自动和其他菜单项合并,他们按照加载的顺序来显示。

环境菜单(右键弹出的菜单)


建议为插件制作一些环境菜单。 在 
menus/your-name-word-count.cson
 文件中,你会看到一些默认的代码片段。
'context-menu':
'atom-text-editor': [
{
'label': 'Toggle Word Count'
'command': 'your-name-word-count:toggle'
}
]


在编辑页面点击右键,我们会看到这个菜单项。



点击它,会执行 
your-name-word-count:toggle
 方法。

环境菜单也可以添加子项目, 如下:
'context-menu':
'atom-workspace': [
{
label: 'Text'
submenu: [
{label: 'Inspect Element', command: 'core:inspect'}
{type: 'separator'}
{label: 'Selector All', command: 'core:select-all'}
{type: 'separator'}
{label: 'Deleted Selected Text', command: 'core:delete'}
]
}
]


开发插件

在我们之前创建的插件框架基础上,运行 
your-name-word-count:toggle
我们会看到一个弹窗: "The Wordcount package is Alive!
It's ALIVE!".




理解自动生成的代码


看一下 
lib
 目录。

在 
lib
 目录下,现在有两个文件。其中 (
lib/your-name-word-count.coffee
)是主文件,
在 
package.json
 文件中我们指定它为主文件。它负责整个插件的逻辑。

第二要注意的是 View class文件
lib/your-name-word-count-view.coffee
, 这里主要是插件的UI代码
module.exports =
class YourNameWordCountView
constructor: (serializedState) ->
# Create root element
@element = document.createElement('div')
@element.classList.add('your-name-word-count')

# Create message element
message = document.createElement('div')
message.textContent = "The Your Name Word Count package is Alive! It's ALIVE!"
message.classList.add('message')
@element.appendChild(message)

# Returns an object that can be retrieved when package is activated
serialize: ->

# Tear down any state and detach
destroy: ->
@element.remove()

getElement: ->
@element


代码的具体含义这里不在赘述。

这里重点要注意的是 DOM methods: 
createElement()
 和
appendChild()
.两个函数的用法。

第二个文件有两个主入口,它们是在 
package.json
 文件里配置的.文件代码如下:
YourNameWordCountView = require './your-name-word-count-view'
{CompositeDisposable} = require 'atom'

module.exports = YourNameWordCount =
yourNameWordCountView: null
modalPanel: null
subscriptions: null

activate: (state) ->
@yourNameWordCountView = new YourNameWordCountView(state.yourNameWordCountViewState)
@modalPanel = atom.workspace.addModalPanel(item: @yourNameWordCountView.getElement(), visible: false)

# Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
@subscriptions = new CompositeDisposable

# Register command that toggles this view
@subscriptions.add atom.commands.add 'atom-workspace',
'your-name-word-count:toggle': => @toggle()

deactivate: ->
@modalPanel.destroy()
@subscriptions.dispose()
@wordcountView.destroy()

serialize: ->
yourNameWordCountViewState: @yourNameWordCountView.serialize()

toggle: ->
console.log 'YourNameWordCount was toggled!'

if @modalPanel.isVisible()
@modalPanel.hide()
else
@modalPanel.show()


首先,我们看到这里定义了4个方法,其中只有 
activate
. 是必要的。 
deactivate
 是 
serialize
 Atom推荐但可选的。
 
toggle
 方法不是由Atom调用的,我们必须手动或者配置的方式去调用它。一般来说,都是在 
activationCommands
section
(
package.json文件中)
 或者菜单中。

 
deactivate
 函数只负责销毁,
serialize
 只负责序列化View的信息,建议不要在这两个函数里做其他事情。

activate
 函数不是在Atom启动时就被调用的。它仅在 
activationCommands(package.json中声明)
 中定义的方法被调用时
才会被调用。所以 
activate
 仅当 
toggle
command被调用时,才会被调用。如果没人点菜单,则它永远不会被调用。

对于本例来说,它做了两件事:一、创建了一个窗口并把这个窗口插入到 Atom workspace中。
@yourNameWordCountView = new YourNameWordCountView(state.yourNameWordCountViewState)
@modalPanel = atom.workspace.addModalPanel(
item: @yourNameWordCountView.getElement(),
visible: false
)


这里我们可以无视state 参数 本例中并没有用到它。

二、创建了一个CompositeDisposable 的实例,以注册插件中所有可以被调用的命令。
# Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
@subscriptions = new CompositeDisposable

# Register command that toggles this view
@subscriptions.add atom.commands.add 'atom-workspace', 'your-name-word-count:toggle': => @toggle()


下一步,我们来编写 
toggle
 函数。这个函数仅仅是把我们在
activate
 函数中声明的View做了显示/隐藏
处理。
toggle: ->
console.log 'YourNameWordCount was toggled!'

if @modalPanel.isVisible()
@modalPanel.hide()
else
@modalPanel.show()


现在,我们来验证一下插件是否有效。

The
Flow


现在,我们来回顾一下整个包的执行流程:
Atom 运行
Atom 开始加载插件
Atom 读取你编写的 
package.json

Atom 读取 keymaps, menus, styles 和main 模块
Atom 完成加载插件
用户通过 
your-name-word-count:toggle来运行你的插件

Atom 运行你main模块中的 
activate
 函数(本例中,插入了一个窗口)
用户再次点击 
your-name-word-count:toggle
 时,Atom隐藏了你的插件窗口。
用户再次点击
your-name-word-count:toggle
 command ,Atom再次显示你的插件窗口
Atom 中途不会释放你的插件
最终, Atom 关闭时会 调用所有的插件定义的serializations

提示: 注意,如果你用了
activationCommands
 ,流程会有所不同。


插件:单词计数器


现在,我们已经理解了Atom插件的基本工作原理,让我们来修改代码,实现一个小插件吧。

当插件被激活时,我们来数一数Atom当前正在编辑的文档窗口里,有多少个单词吧。
toggle: ->
if @modalPanel.isVisible()
@modalPanel.hide()
else
editor = atom.workspace.getActiveTextEditor()
words = editor.getText().split(/\s+/).length
@yourNameWordCountView.setCount(words)
@modalPanel.show()


 
atom.workspace.getActiveTextEditor()
.获得了当前Atom编辑窗口。

 
getText()
 获得了字符串。

最后,我们调用
setCount()(在view中我们稍后会声明它),来把结果显示出来。


在 
your-name-word-count-view.coffee
 文件中,我们声明了函数setCount()
setCount: (count) ->
displayText = "There are #{count} words."
@element.children[0].textContent = displayText


运行结果如下:




基础的Debugging

你应该注意到了代码中的 
console.log
  因为Atom是基于 Chromium 开发的,所以有一些常规的调试工具供你使用。

用快捷键 Ctrl+Shift+I打开开发者模式,激活开发者工具。



对于做过Web开发的同学们来说,这应该是很熟悉的。


测试

建议测试一下你的插件,如果把插件放置到 
spec
 目录下,Atom就可以运行它。

Jasmine v1.3 可以用来执行测试

运行测试


点击 Alt+Ctrl+P 或通过菜单的 View
> Developer > Run Package Specs 项目。



也可以在控制台用 
atom --test spec
 命令来运行它。


总结

现在,你已经完成了你的第一个插件,现在来发布它吧。详见 publish
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: