Atom Manual
Atom Manual
Atom Manual
3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
Introduction
Welcome to the Flight Manual for Atom, the Hackable Text Editor of the 21st
Century.
In the very simplest terms, Atom is a hackable text editor. It is like any other
text editor in that you generally use it to open plain text files like computer programs, modify them and save them again. It is also much more in that its built
on well known web technologies such as HTML and CoffeeScript and is quite
easy to customize, extend and modify.
In this book, well cover everything from the basics that you need to know to
edit files like an elementary school student all the way to building brand new
Atom-like applications with the shell that underlies Atom. Well also cover everything in between.
Strap into your seat and get your oxygen tank ready, were about to take off.
iii
Table of Contents
Introduction
iii
11
Why Atom
11
11
12
Installing Atom
13
Atom on Mac
14
Atom on Windows
14
Atom on Linux
15
16
Setting up a Proxy
16
Atom Basics
16
Basic Terminology
17
Command Palette
18
19
22
Opening Directories
24
Summary
27
29
Table of Contents
Atom Packages
Package Settings
30
Atom Themes
31
Command Line
33
Moving in Atom
34
Navigating by Symbols
35
Atom Bookmarks
36
Atom Selections
37
38
Basic Manipulation
39
39
40
Whitespace
41
Brackets
42
Encoding
43
44
Snippets
46
48
Autocomplete
50
Folding
51
Panes
52
Grammar
53
54
54
55
Commit editor
56
57
Line diffs
58
Open on GitHub
58
Writing in Atom
Spell Checking
vi
29
59
60
Table of Contents
Previews
61
Snippets
62
Basic Customization
63
Style Tweaks
63
64
65
67
Summary
69
71
71
72
73
Package Generator
73
81
Basic Debugging
86
Testing
87
Publishing
88
Summary
89
90
90
92
Summary
93
Creating a Theme
93
Getting Started
93
94
95
Development workflow
95
Theme Variables
97
Debugging
Update to the latest version
100
100
vii
Table of Contents
100
101
TimeCop
102
102
103
104
104
105
106
Writing specs
Create a new spec
106
Asynchronous specs
107
Running specs
108
109
109
110
Summary
110
111
Configuration API
111
111
112
Keymaps In-Depth
112
112
Removing Bindings
114
115
115
116
viii
106
116
116
Scope Selectors
117
Table of Contents
Scope Descriptors
117
Serialization in Atom
118
118
Serialization Methods
119
Versioning
120
120
120
121
122
Unpublish a Version
122
Adding a Collaborator
122
Transferring Ownership
123
123
123
Summary
124
125
Index
145
ix
Getting Started
Why Atom
There are a lot of text editors out there, why should you spend your time learning about and using Atom?
Editors like Sublime and TextMate offer convenience but only limited extensibility. On the other end of the spectrum, Emacs and Vim offer extreme flexibility, but they arent very approachable and can only be customized with specialpurpose scripting languages.
We think we can do better. Our goal is a zero-compromise combination of
hackability and usability: an editor that will be welcoming to an elementary
school student on their first day learning to code, but also a tool they wont outgrow as they develop into seasoned hackers.
As weve used Atom to build Atom, what began as an experiment has gradually matured into a tool we cant live without. On the surface, Atom is the
modern desktop text editor youve come to expect. Pop the hood, however, and
youll discover a system begging to be hacked on.
11
verely restricts access to the local system for security reasons, and for us, a text
editor that couldnt write files or run local subprocesses was a non-starter.
For this reason, we didnt build Atom as a traditional web application. Instead, Atom is a specialized variant of Chromium designed to be a text editor
rather than a web browser. Every Atom window is essentially a locally-rendered
web page.
All the APIs available to a typical Node.js application are also available to
the code running in each windows JavaScript context. This hybrid provides a
really unique client-side development experience.
Since everything is local, you dont have to worry about asset pipelines,
script concatenation, and asynchronous module definitions. If you want to load
some code, just require it at the top of your file. Nodes module system makes it
easy to break the system down into lots of small, focused packages.
JAVASCRIPT, MEET C++
Interacting with native code is also really simple. For example, we wrote a
wrapper around the Oniguruma regular expression engine for our TextMate
grammar support. In a browser, that would have required adventures with NaCl
or Esprima. Node integration made it easy.
In addition to the Node APIs, we also expose APIs for native dialogs, adding
application and context menu items, manipulating the window dimensions,
etc.
WEB TECH: THE FUN PARTS
Another great thing about writing code for Atom is the guarantee that its running on the newest version of Chromium. That means we can ignore issues like
browser compatibility and polyfills. We can use all the webs shiny features of
tomorrow, today.
For example, the layout of our workspace and panes is based on flexbox. Its
an emerging standard and has gone through a lot of change since we started
using it, but none of that mattered as long as it worked.
With the entire industry pushing web technology forward, were confident
that were building Atom on fertile ground. Native UI technologies come and go,
but the web is a standard that becomes more capable and ubiquitous with
every passing year. Were excited to dig deeper into its toolbox.
12
Installing Atom
Hub will continue to support its development with a dedicated team going forward. But we also know that we cant achieve our vision for Atom alone. As
Emacs and Vim have demonstrated over the past three decades, if you want to
build a thriving, long-lasting community around a text editor, it has to be open
source.
The entire Atom editor is free and open source and is available under the
https://github.com/atom organization.
Installing Atom
To get started with Atom, well need to get it on our system. This section will go
over installing Atom on Mac, Windows and Linux, as well as the basics of how to
build it from source.
Installing Atom should be fairly simple on any of these systems. Generally
you can simply go to https://atom.io and at the top of the page you should see
a download button as in Figure 1-1.
FIGURE 1-1
Download buttons
on atom.io
The buttons should be specific to your platform and easily installable. However, lets go over them here in a bit of detail.
13
Atom on Mac
Atom was originally built for Mac and should be a simple setup process. You can
either hit the download button from the atom.io site or you can go to the Atom
releases page at:
https://github.com/atom/atom/releases/latest
Here you can download the atom-mac.zip file explicitly.
Once you have that file, you can click on it to extract the binary and then
drag the new Atom application into your Applications folder.
We also recommend that you run Window: Install Shell Commands from
the Command Palette so that you can use atom and apm commands from the
terminal.
Atom on Windows
Atom comes with a windows installer. You can download the installer from
https://atom.io or from:
https://github.com/atom/atom/releases/latest
This will install Atom, add the atom and apm commands to your PATH, create
shortcuts on the desktop and in the start menu, and also add an Open with
Atom context menu in the Explorer.
14
Installing Atom
FIGURE 1-2
Atom on Windows
If you just want to download a .zip of the latest Atom release for Windows,
you can also get it from the Atom releases page at https://github.com/atom/
atom/releases.
Atom on Linux
To install Atom on Linux, you can download a Debian package or RPM package
either from the main Atom website at atom.io or from the Atom project releases page at https://github.com/atom/atom/releases.
On Debian, you would install the Debian package with dpkg -i:
$ sudo dpkg -i atom-amd64.deb
On RedHat or another RPM based system, you would use the rpm -i command:
$ rpm -i atom.x86_64.rpm
15
Setting up a Proxy
If youre using a proxy, you can configure apm (Atom Package Manager) to use it
by setting the https-proxy config in your ~/.atom/.apmrc file:
https-proxy = https://9.0.2.1:0
If you are behind a firewall and seeing SSL errors when installing packages,
you can disable strict SSL by putting the following in your ~/.atom/.apmrc
file:
strict-ssl = false
You can run apm config get https-proxy to verify it has been set correctly, and running apm config list lists all custom config settings.
Atom Basics
Now that Atom is installed on your system, lets fire it up, configure it and get
acquainted with the editor.
When you launch Atom for the first time, you should get a screen that looks
like this:
16
Atom Basics
FIGURE 1-3
Atoms welcome
screen
This is the Atom welcome screen and gives you a pretty good starting point
for how to get started with the editor.
Basic Terminology
First of all, lets get acquainted with some of the terminology well be using in
this manual.
Buffer
A buffer is the text content of a file in Atom. Its basically the same as a file for
most descriptions, but its the version Atom has in memory. For instance,
you can change the text of a buffer and it isnt written to its associated file
until you save it.
Pane
A pane is a visual section of Atom. If you look at the welcome screen we just
launched, you can see four Panes - the tab bar, the gutter (which has line
numbers in it), the status bar at the bottom and finally the text editor.
17
Command Palette
In that welcome screen, we are introduced to probably the most important
command in Atom, the Command Palette. If you hit cmd-shift-P while focused in an editor pane, the command palette will pop up.
Throughout the book we will use shortcut keybindings like cmd-shift-P to
demonstrate how to run a command. These are the default keybindings
for Atom on Mac. They may occasionally be slightly different depending
on your platform.
You can use the Command Palette to look up the correct keybindings if it
doesnt work for some reason.
This search-driven menu can do just about any major task that is possible in
Atom. Instead of clicking around all the application menus to look for something, you can just hit cmd-shift-P and search for the command.
FIGURE 1-4
The Command
Palette
Not only can you see and quickly search through thousands of possible commands, but you can also see if there is a keybinding associated with it. This is
great because it means you can guess your way to doing interesting things
while also learning the shortcut key strokes for doing it.
18
Atom Basics
For the rest of the book, we will try to be clear as to the text you can search
for in the Command Palette in addition to the keybinding for different commands.
FIGURE 1-5
Atoms settings
screen
This includes things like changing the color scheme or theme, specifying
how to handle wrapping, font settings, tab size, scroll speed and much more.
You can also use this screen to install new packages and themes, which well
cover in Atom Packages.
To open the Settings screen, you can go to the Preferences menu item under
the main Atom menu in the menu bar. You can also search for settingsview:open in the command palette or use the cmd-, keybinding.
19
FIGURE 1-6
Changing the theme
from Settings
The UI themes modify the color of UI elements like the tabs and the tree
view, while the syntax themes modify the syntax highlighting of text you load
into the editor. To change the theme, simply pick something different in the
dropdowns.
There are also dozens of themes on Atom.io that you can choose from if you
want something different. We will also cover customizing a theme in Style
Tweaks and creating your own theme in Creating a Theme.
SOFT WRAP
You can also use the Settings view to specify your whitespace and wrapping
preferences.
20
Atom Basics
FIGURE 1-7
Changing the theme
from Settings
Enabling Soft Tabs will insert spaces instead of actual tab characters when
you hit the tab key and the Tab Length specifies how many spaces to insert
when you do so, or how many spaces to represent a tab as if Soft Tabs is disabled.
The Soft Wrap option will wrap lines that are too long to fit in your current
window. If soft wrapping is disabled, the lines will simply run off the side of the
screen and you will have to scroll the window to see the rest of the content. If
Soft Wrap At Preferred Line Length is toggled, the lines will wrap at 80 characters instead of the end of the screen. You can also change the default line length
to a value other than 80 on this screen.
In Basic Customization we will see how to set different wrap preferences
for different types of files (for example, if you want to wrap Markdown files but
not code files).
BETA FEATURES
As Atom is developed, there are occasionally new features that are tested before they are mainlined for everyone. In some cases, those changes are shipped
turned off by default but can be enabled in the Settings view should you wish to
try them out.
21
FIGURE 1-8
Beta features in the
Settings view
22
Atom Basics
FIGURE 1-9
Open file by dialog
This is useful for opening a file that is not contained in the project youre currently in (more on that next), or if youre starting from a new window for some
reason.
Another way to open a file in Atom is from the command line. If youre on a
Mac, the Atom menu bar will have a command named Install Shell Commands which installs two new commands in your Terminal: atom and apm. On
Windows and Linux, those two commands will be set up automatically as a part
of Atoms installation process. You can run the atom command with one or
more file paths to open up those files in Atom.
$ atom -h
Atom Editor v0.152.0
Usage: atom [options] [path ...]
One or more paths to files or folders may be specified. If there is an
existing Atom window that contains all of the given folders, the paths
will be opened in that window. Otherwise, they will be opened in a new
window.
...
23
This is a great tool if youre used to the terminal or you work from the terminal a lot. Just fire off atom [files] and youre ready to start editing.
EDITING AND SAVING A FILE
Editing a file is pretty straightforward. You can click around and scroll with your
mouse and type to change the content. There is no special editing mode or key
commands.
To save a file you can choose File >> Save from the menu bar or cmd-S to
save the file. If you choose Save As or hit cmd-shift-S then you can save the
current content in your editor under a different file name. Finally, you can
choose ctrl-shift-S to save all the open files in Atom.
Opening Directories
Atom doesnt just work with single files though; you will most likely spend most
of your time working on projects with multiple files. To open a directory, choose
File >> Open from the menu bar and select a directory from the dialog. You
can also add more than one directory to your current Atom window, by choosing File >> Add Project Folder from the menu bar or hitting cmd-shift-O.
You can open any number of directories from the command line by passing
their paths to the atom command line tool. For example, you could run the
command atom ./hopes ./dreams to open both the hopes and the dreams
directories at the same time.
When you open Atom with one or more directories, you will automatically
get a Tree view on the side of your window.
24
Atom Basics
FIGURE 1-10
Tree view in an open
project
The Tree view allows you to explore and modify the file and directory structure of your project. You can open, rename, delete and create new files from
this view.
You can also hide and show it with cmd-\ or the tree-view:toggle command from the Palette, and ctrl-0 will focus it. When the Tree view has focus
you can press a, m, or delete to add, move or delete files and folders. You can
also simply right-click on a file or folder in the Tree view to see many of the various options, including all of these plus showing the file in your native filesystem
or copying the file path to your system clipboard.
ATOM MODULES
Like many parts of Atom, the Tree view is not built directly into the editor, but is its own standalone package that is simply shipped with Atom
by default.
You can find the source code to the Tree view here: https://github.com/
atom/tree-view
This is one of the interesting things about Atom. Many of its core features are actually just packages implemented the same way you would
implement any other functionality. This means that if you dont like the
Tree view for example, its fairly simple to write your own implementation of that functionality and replace it entirely.
25
FIGURE 1-11
Opening files with
the Fuzzy Finder
You can also search through only the files currently opened (rather than
every file in your project) with cmd-B. This searches through your buffers or
open files. You can also limit this fuzzy search with cmd-shift-B, which
searches only through the files which are new or have been modified since your
last Git commit.
The fuzzy finder uses both the core.ignoredNames and fuzzyfinder.ignoredNames config settings to filter out files and folders that will
not be shown. If you have a project with tons of files you dont want it to search
through, you can add patterns or paths to either of these config settings. Well
learn more about config settings in Global Configuration Settings, but for
now you can easily set these in the Settings view under Core Settings.
Both of those config settings are interpreted as glob patterns as implemented by the minimatch Node.js library.
You can read more about minimatch here: https://github.com/isaacs/minimatch
26
Summary
This package also will also not show Git ignored files when the core.excludeVcsIgnoredPaths is enabled. You can easily toggle this in the Settings
view, its one of the top options.
Summary
You should now have a basic understanding of what Atom is and what you want
to do with it. You should also have it installed on your system and able to use it
for the most basic text editing operations.
Now youre ready to start digging into the fun stuff.
27
Using Atom
Now that weve covered the very basics of Atom, we are ready to see how to
really start getting the most out of using it. In this chapter well look at how to
find and install new packages in order to add new functionality, how to find and
install new themes, how to work with and manipulate text in a more advanced
way, how to customize the editor in any way you wish, how to work with Git for
version control and more.
At the end of this chapter, you should have a customized environment that
youre comfortable in and should be able to change, create and move through
your text like a master.
Atom Packages
First well start with the Atom package system. As we mentioned previously,
Atom itself is a very basic core of functionality that ships with a number of useful packages that add new features like the Tree View and the Settings View.
In fact, there are more than 70 packages that comprise all of the functionality that is available in Atom by default. For example: the Welcome dialog that
you see when you first start it, the spell checker, the themes and the fuzzy
finder are all packages that are separately maintained and all use the same
APIs that you have access to, as well see in great detail in Chapter 3.
This means that packages can be incredibly powerful and can change everything from the very look and feel of the entire interface to the basic operation of
even core functionality.
In order to install a new package, you can use the Packages tab in the now
familiar Settings view. Simply open up the Settings view (cmd-,), click on the
Packages tab and type your search query into the box under Install Packages
that hints Search Packages.
The packages listed here have been published to atom.io which is the official registry for Atom packages. Searching on the settings pane will go to the
atom.io package registry and pull in anything that matches your search terms.
29
FIGURE 2-1
Package install
screen
All of the packages will come up with an Install button. Clicking that will
download the package and install it. Your editor will now have the functionality
that the package provides.
Package Settings
Once a package is installed in Atom, it will show up in the side pane in your setting screen, along with all the preinstalled packages that come with Atom. To
filter the list in order to find one, you can type into the Filter packages textbox.
30
Atom Packages
FIGURE 2-2
Package settings
screen
Clicking on one of the package names will give you the settings screen for
the package. Here you have the option of changing some of the default variables for the package, seeing what all the command keybindings are, disabling
the package temporarily, looking at the source code, seeing the current version
of the package, reporting issues and uninstalling the package.
If a new version of any of your packages is released, Atom will automatically
detect it and you can upgrade the package from either this screen or from the
main package search tab. This helps you easily keep all your installed packages
up to date.
Atom Themes
You can also find and install new themes for Atom from the Settings view. These
can be either UI themes or syntax highlighting themes and you can search for
them from the Themes tab, just like searching for new packages.
31
FIGURE 2-3
Theme search screen
Clicking on the Learn More button on any of the themes will take you to a
profile page for the theme on atom.io, which usually has a screenshot of the
theme. This way you can see what it looks like before installing it.
Clicking on Install will install the theme and make it available in the
Theme dropdowns as we saw in Changing the Color Theme.
32
Atom Packages
FIGURE 2-4
Example of the Unity
UI theme with
Monokai syntax
theme
Command Line
You can also install packages or themes from the command line using apm.
Check that you have apm installed by running the following command in
your terminal:
$ apm help install
You should see a message print out with details about the apm install
command.
If you do not, launch Atom and run the Atom > Install Shell Commands menu
to install the apm and atom commands.
You can also install packages by using the apm install command:
apm install <package_name> to install the latest version.
apm install <package_name>@<package_version> to install a specific version.
For example apm install [email protected] installs the 0.1.5 release of the
Emmet package.
You can also use apm to find new packages to install. If you run apm search,
you can search the package registry for a search term.
$ apm search coffee
Search Results For 'coffee' (5)
33
coffee-trace Add smart trace statements to coffee files with one keypress each.
coffee-navigator Code navigation panel for Coffee Script (557 downloads, 8 stars
atom-compile-coffee This Atom.io Package compiles .coffee Files on save to .js f
coffee-lint CoffeeScript linter (3336 downloads, 18 stars)
git-grep `git grep` in atom editor (1224 downloads, 9 stars)
You can use apm view to see more information about a specific package.
$ apm view git-grep
git-grep
0.7.0
git://github.com/mizchi/atom-git-grep
`git grep` in atom editor
1224 downloads
9 stars
Run `apm install git-grep` to install this package.
Moving in Atom
While its pretty easy to move around Atom by clicking with the mouse or using
the arrow keys, there are some keybindings that may help you keep your hands
on the keyboard and navigate around a little faster.
First of all, Atom ships with many of the basic Emacs keybindings for navigating a document. To go up and down a single character, you can use ctrl-P
and ctrl-N. To go left and right a single character, you can use ctrl-B and
ctrl-F. These are the equivalent of using the arrow keys, though some people
prefer not having to move their hands to where the arrow keys are located on
their keyboard.
In addition to single character movement, there are a number of other
movement keybindings.
alt-B, alt-left
Move to beginning of word
alt-F, alt-right
Move to end of word
cmd-right, ctrl-E
Move to end of line
cmd-left, ctrl-A
Move to first character of line
34
Moving in Atom
cmd-up
Move to top of file
cmd-down
Move to bottom of file
You can also move directly to a specific line (and column) number with
ctrl-G. This will bring up a dialog that asks which line you would like to jump
to. You can also use the row:column syntax to jump to a character in that line
as well.
FIGURE 2-5
Go directly to a line
Navigating by Symbols
You can also jump around a little more informatively. To jump to a symbol such
as a method definition, press cmd-r. This opens a list of all symbols in the current file, which you can fuzzy filter similarly to cmd-t. To search for symbols
across your project, use cmd-shift-r.
35
FIGURE 2-6
Search by symbol
across your project
You can also use ctrl-alt-down to jump directly to the declaration of the
method or function under the cursor.
First youll need to make sure you have tags (or TAGS) file generated for
your project. This can be done by installing ctags and running a command such
as ctags -R src/ from the command line in your projects root directory.
If youre on a Mac using Homebrew, you can just run brew install
ctags.
You can customize how tags are generated by creating your own .ctags file
in your home directory (~/.ctags). An example can be found here.
The symbols navigation functionality is implemented in the atom/symbolsview package.
Atom Bookmarks
Atom also has a great way to bookmark specific lines in your project so you can
jump back to them quickly.
If you press cmd-F2, Atom will toggle a bookmark on the current line. You
can set these throughout your project and use them to quickly find and jump to
important lines of your project. A small bookmark symbol is added to the line
gutter, like on line 73 of Figure 2-7.
36
Atom Selections
If you hit F2, Atom will jump to the next bookmark in the file you currently
have focused. If you use shift-F2 it will cycle backwards through them instead.
You can also see a list of all your projects current bookmarks and quickly filter them and jump to any of them by hitting ctrl-F2.
FIGURE 2-7
View and filter
bookmarks.
Atom Selections
Selecting ranges of text in Atom can be useful for a number of things. It can be
for scoping certain actions like deleting, searching or indenting. It can also be
helpful for things like quoting or bracketing text.
Selections mirror many of the movement commands. Theyre actually exactly the same keybindings as the movement commands, but with a shift key
added in.
ctrl-shift-P
Select up
ctrl-shift-N
Select down
37
ctrl-shift-B
Select previous character
ctrl-shift-F
Select next character
alt-shift-B, alt-shift-left
Select to beginning of word
alt-shift-F, alt-shift-right
Select to end of word
ctrl-shift-E, cmd-shift-right
Select to end of line
ctrl-shift-A, cmd-shift-left
Select to first character of line
cmd-shift-up
Select to top of file
cmd-shift-down
Select to bottom of file
In addition to the cursor movement selection commands, there are also a
few commands that help with selecting specific areas of content.
cmd-A
Select the entire buffer
cmd-L
Select entire line
ctrl-shift-W
Select current word
38
Basic Manipulation
There are a handful of cool keybindings for basic text manipulation that might
come in handy. These range from moving around lines of text and duplicating
lines to changing the case.
ctrl-T
Transpose characters. This swaps the two characters on either side of the
cursor.
cmd-J
Join the next line to the end of the current line
ctrl-cmd-up, ctrl-cmd-down
Move the current line up or down
cmd-shift-D
Duplicate the current line
cmd-K, cmd-U
Upper case the current word
cmd-K, cmd-L
Lower case the current word
Atom also has built in functionality to re-flow a paragraph to hard-wrap at a
given maximum line length. You can format the current selection to have lines
no longer than 80 (or whatever number editor.preferredLineLength is set
to) characters using cmd-alt-Q. If nothing is selected, the current paragraph
will be reflowed.
ctrl-shift-K
Delete current line
cmd-delete
Delete to end of line (cmd-fn-backspace on mac)
ctrl-K
Cut to end of line
39
cmd-backspace
Delete to beginning of line
alt-backspace, alt-H
Delete to beginning of word
alt-delete, alt-D
Delete to end of word
cmd-click
Add new cursor
cmd-shift-L
Convert a multi-line selection into multiple cursors
ctrl-shift-up, ctrl-shift-down
Add another cursor above/below the current cursor
cmd-D
Select the next word in the document that is the same as the currently selected word
ctrl-cmd-G
Select all words in document that are the same as the one under the current
cursor(s)
Using these commands you can place cursors in multiple places in your
document and effectively execute the same commands in multiple places at
once.
40
FIGURE 2-8
Using multiple
cursors
This can be incredibly helpful in doing many type of repetitive tasks such as
renaming variables or changing the format of some text. You can use this with
almost any plugin or command - for example, changing case and moving or duplicating lines.
You can also use the mouse to select text with the command key pressed
down to select multiple regions of your text simultaneously.
Whitespace
Atom comes with a couple of tools to help you manage the whitespace in your
document. These tools are implemented in the atom/whitespace package.
The first is a command that converts leading spaces to tabs and the equivalent for changing tabs into spaces. If youre working with a document that has
mixed whitespace, these commands can be great for helping to normalize the
file. There are no keybindings for these, so you will have to search your command palette for Convert Spaces to Tabs (or vice versa) to run one of these
commands.
The whitespace helper tools are maintained as a separate package and so
the settings for it can be managed from the page for the whitespace package.
41
FIGURE 2-9
Managing your
whitespace settings
Brackets
Atom ships with intelligent and easy to use bracket handling.
It will by default highlight [], (), and {} style brackets when your cursor is over
them. It will also highlight matching XML and HTML tags.
Atom will also automatically autocomplete [], (), and {}, , '', , , , , and
backticks when you type the leading one. If you have a selection and you type
any of these opening brackets or quotes, Atom will enclose the selection with
the opening and closing brackets or quotes.
There are a few other interesting bracket related commands that you can
use.
ctrl-m
Jump to the bracket matching the one adjacent to the cursor. It jumps to the
nearest enclosing bracket when theres no adjacent bracket.
42
ctrl-cmd-m
Select all the text inside the current brackets
alt-cmd-.
Close the current XML/HTML tag
The brackets functionality is implemented in the atom/bracket-matcher
package. Like all of these packages, to change defaults related to brackets handling, or to disable it entirely, you can navigate to this package in the Settings
view.
Encoding
Atom also ships with some basic file encoding support should you find yourself
working with non-UTF-8 encoded files, or should you wish to create one.
ctrl-shift-U
Toggle menu to change file encoding
If you pull up the file encoding dialog, you can choose an alternate file encoding to save your file in. Normally it will auto-detect the encoding if it can,
otherwise it will default to UTF-8. New files will also be UTF-8 files by default.
43
FIGURE 2-10
Changing your file
encoding
If you pull up the encoding menu and change the active encoding to something else, the file will be written out in that encoding the next time that you
save the file.
The encoding selector is implemented in the atom/encoding-selector package.
cmd-F
Search within a buffer
cmd-shift-f
Search the entire project
If you launch either of those commands, youll be greeted with the Find and
Replace pane at the bottom of your screen.
44
FIGURE 2-11
Find and replace text
in the current file
To search within your current file you can hit cmd-F, type in a search string
and hit enter (or cmd-G or the Find Next button) multiple times to cycle
through all the matches in that file. There are also buttons to toggle case sensitivity, regular expression matching and selection scoping.
If you type a string in the Replace in current buffer text box, you can replace matches with a different string. For example, if you wanted to replace
every instance of the string Scott with the string Dragon, you would enter
those values in the two text boxes and hit the Replace All button to execute
the replacements.
You can also do this throughout your entire project if you invoke the panel
with cmd-shift-F.
45
FIGURE 2-12
Find and replace text
in your project
This is a great way to find out where in your project a function is called, an
anchor is linked to or a specific misspelling is located. Clicking on the matching
line will jump you to that location in that file.
You can search only a subset of the files in your project by entering a glob
pattern into the File/Directory pattern text box. When you have multiple
project folders open, this feature can also be used to search in only one of those
folders. For example, if you had the folders /path1/folder1 and /path2/
folder2 open, you could enter a pattern starting with folder1 to search only
in the first folder.
Hit escape while focused on the Find and Replace pane to clear the pane
from your workspace.
The Find and Replace functionality is implemented in the atom/find-andreplace package and uses the atom/scandal package to do the actual searching.
Snippets
Snippets are an incredibly powerful way to quickly generate commonly needed
code syntax from a shortcut.
The idea is that you can type something like habtm and then hit the tab key
and that will expand into has_and_belongs_to_many.
46
Snippets
Many of the packages come bundled with their own snippets that are specific to that mode. For example, the language-html package that provides support for HTML syntax highlighting and grammar comes with dozens of snippets
to create many of the various HTML tags you might want to use. If you create a
new HTML file in Atom, you can type html and then hit tab and it will expand
to:
<html>
<head>
<title></title>
</head>
<body>
</body>
</html>
It will also position the cursor in the middle of the title tag so you can immediately start filling out the tag. Many snippets have multiple focus points
that you can move through with the tab key as well - for instance, in the case of
this HTML snippet, once youve filled out the title tag you can hit tab and the
cursor will move to the middle of the body tag.
To see all the available snippets for the file type that you currently have
open, you can type alt-shift-S.
FIGURE 2-13
View all available
snippets
47
You can also use fuzzy search to filter this list down by typing in the selection
box. Selecting one of them will execute the snippet where your cursor is (or
multiple cursors are).
The outermost keys are the selectors where these snippets should be active.
The easiest way to determine what this should be is to go to the language package of the language you want to add a snippet for and look for the Scope
string.
For example, if we wanted to add a snippet that would work for Java files,
we would look up the language-java package in our Settings view and we can
see the Scope is source.java. Then the top level snippet key would be that
prepended by a period (like a CSS class selector would do).
48
Snippets
FIGURE 2-14
Finding the selector
scope for a snippet
The next level of keys are the snippet names. These are used for describing
the snippet in a more readable way in the snippet menu. Its generally best to
use some sort of sort human readable string here.
Under each snippet name is a prefix that should trigger the snippet and a
body to insert when the snippet is triggered.
$ followed by a number are the tabs stops which can be cycled between by
pressing tab once a snippet has been triggered.
The above example adds a log snippet to JavaScript files that would expand
to.
console.log("crash");
The string "crash" would be initially selected and pressing tab again would
place the cursor after the ;
MULTI-LINE SNIPPET BODY
You can also use multi-line syntax using """ for larger templates:
'.source.js':
'if, else if, else':
49
'prefix': 'ieie'
'body': """
if (${1:true}) {
$2
} else if (${3:false}) {
$4
} else {
$5
}
"""
Bam, just fill that bad boy out and you have yourself a snippet. As soon as
you save the file, Atom should reload the snippets and you will immediately be
able to try it out.
The snippets functionality is implemented in the atom/snippets package.
Autocomplete
If youre still looking to save some typing time, Atom also ships with simple autocompletion functionality.
The autocompleter lets you view and insert possible completions in the editor using ctrl-space.
50
Folding
FIGURE 2-15
Autocomplete menu
By default, the completer will look through the current open file for strings
that match what youre starting to type.
If you want more options, in the Settings panel for the Autocomplete package you can toggle a setting to make the autocompleter look for strings in all
your open buffers rather than just the current file.
The Autocomplete functionality is implemented in the atom/autocomplete
package.
Folding
If you want to see an overview of the structure of the code file youre working
on, folding can be a helpful tool. Folding hides blocks of code such as functions
or looping blocks in order to simplify what is on your screen.
You can fold blocks of code by clicking the arrows that appear when you
hover your mouse cursor over the gutter. You can also fold and unfold from the
keyboard with the alt-cmd-[ and alt-cmd-] keybindings.
51
FIGURE 2-16
Code folding
example
Panes
You can split any editor pane horizontally or vertically by using cmd-k arrow
where the arrow is the direction to split the pane. Once you have a split pane,
you can move focus between them with cmd-k cmd-arrow where the arrow is
the direction the focus should move to.
52
Grammar
FIGURE 2-17
Multiple panes
Each pane has its own items or files, which are represented by tabs. You
can move the files from pane to pane by dragging them with the mouse and
dropping them in the pane you want that file to be in.
To close a pane, close all its editors with cmd-w, then press cmd-w one more
time to close the pane. You can configure panes to auto-close when empty in
the Settings view.
Grammar
The grammar of a buffer is what language Atom thinks that file content is.
Types of grammars would be Java or Markdown. We looked at this a bit when
we created some snippets in Snippets.
If you load up a file, Atom does a little work to try to figure out what type of
file it is. Largely this is accomplished by looking at its file extension (.md is generally a Markdown file, etc), though sometimes it has to inspect the content a
bit to figure it out if its ambiguous.
If you load up a file and Atom cant determine a grammar for the file, it will
default to Plain Text, which is the simplest one. If it does default to Plain Text or
miscategorize a file, or if for any reason you wish to change the active grammar
of a file, you can pull up the Grammar selector with ctrl-shift-L.
53
FIGURE 2-18
Grammar selector
Once the grammar of a file is changed manually, Atom will remember that
until you set it back to auto-detect or choose a different grammar manually.
The Grammar selector functionality is implemented in the atom/grammarselector package.
54
same as running git checkout HEAD -- <path> and git reset HEAD -<path> from the command line for that path.
FIGURE 2-19
Git checkout HEAD
This command goes onto the undo stack so you can use cmd-Z afterwards to
restore the previous contents.
55
FIGURE 2-20
Git status list
An octicon will appear to the right of each file letting you know whether it is
untracked or modified.
Commit editor
Atom can be used as your Git commit editor and ships with the language-git
package which adds syntax highlighting to edited commit, merge, and rebase
messages.
56
FIGURE 2-21
Git commit message
highlighting
You can configure Atom to be your Git commit editor with the following command:
$ git config --global core.editor "atom --wait"
The language-git package will help you with your brevity by colorizing the
first lines of commit messages when theyre longer than 50 and 65 characters.
FIGURE 2-22
Git Status Bar
The currently checked out branch name is shown with the number of commits the branch is ahead of or behind its upstream branch.
57
Line diffs
The included git-diff package colorizes the gutter next to lines that have been
added, edited, or removed.
FIGURE 2-23
Git line diffs
This package also adds alt-g down and alt-g up keybindings that allow
you to move the cursor to the next/previous diff hunk in the current editor.
Open on GitHub
If the project youre working on is on GitHub, there are also some very useful
integrations you can use. Most of the commands will take the current file youre
viewing and open a view of that file on GitHub - for instance, the blame or commit history of that file.
alt-G O
Open file on GitHub
58
Writing in Atom
alt-G B
Open blame of file on GitHub
alt-G H
Open history of file on GitHub
alt-G C
Copy the URL of the current file on GitHub
alt-G R
Branch compare on GitHub
The branch comparison simply shows you the commits that are on the
branch youre currently working on locally that are not on the mainline branch.
FIGURE 2-24
Open Blame of file
on GitHub
Writing in Atom
Though it is probably most common to use Atom to write software code, Atom
can also be used to write prose quite effectively. Most often this is done in some
sort of markup language such as Markdown or Asciidoc (which this manual is
written in). Here well quickly cover a few of the tools Atom provides for helping
you write prose.
In these docs, well concentrate on writing in Markdown; however, other
prose markup languages like Asciidoc have packages that provide similar functionality.
59
Spell Checking
If youre working in text (which includes plain text files, GitHub markdown and
Git commit messages by default), Atom will automatically try to check your
spelling.
Any misspelled words will be highlighted (by default with a dashed red line
beneath the word) and you can pull up a menu of possible corrections by hitting cmd-: (or by choosing Correct Spelling from the right-click context menu
or from the Command Palette).
FIGURE 2-25
Checking your
spelling
To add more types of files to the list of what Atom will try to spell check, go
to the Spell Check package settings in your Settings view and add any grammars you want to spell check.
By default its set to text.plain, source.gfm, text.git-commit but you can
add something like source.asciidoc if you wish to check those types of files
too.
The Atom spell checker uses the system dictionary, so if you want it to check
your spelling in another language or locale, you can change it easily.
60
Writing in Atom
FIGURE 2-26
Changing your spell
checking dictionary
Previews
When writing prose in a markup language, its often very useful to get an idea of
what the content will look like when its rendered. Atom ships with a markdown
preview plugin by default.
ctrl-shift-M
Will toggle Preview mode for Markdown.
61
FIGURE 2-27
Preview your prose
As you edit the text, the preview will also update by default. This makes it
fairly easy to check your syntax as you type.
You can also copy the rendered HTML from the preview pane into your system clipboard. There is no keybinding for it, but you can find it in the Command
Palette by searching for Markdown Preview Copy HTML.
Markdown preview is implemented in the atom/markdown-preview package.
Snippets
There are also a number of great snippets available for writing Markdown
quickly.
If you type img and hit tab you get Markdown formatted image embed code
like ![](). If you type table and hit tab you get a nice example table to fill
out.
| Header One
| Header Two
|
| :------------- | :------------- |
| Item One
| Item Two
|
There are only a handful of them (b for bold, i for italic, code for a code
block, etc), but it can easily save you time from having to look up the more obscure syntaxes. Again, you can easily see a list of all available snippets for the
type of file youre currently in by hitting alt-shift-S.
62
Basic Customization
Basic Customization
Now that we are feeling comfortable with just about everything built into Atom,
lets look at how to tweak it. Perhaps there is a keybinding that you use a lot but
feels wrong or a color that isnt quite right for you. Atom is amazingly flexible,
so lets go over some of the simpler flexes it can do.
Style Tweaks
If you want to apply quick-and-dirty personal styling changes without creating
an entire theme that you intend to publish, you can add styles to the
styles.less file in your ~/.atom directory.
You can open this file in an editor from the Atom > Open Your Stylesheet
menu.
FIGURE 2-28
Open your stylesheet
For example, to change the color of the cursor, you could add the following
rule to your ~/.atom/styles.less file:
63
atom-text-editor.is-focused .cursor {
border-color: pink;
}
To see what classes are available to style the easiest thing to do is to inspect
the DOM manually via the developer tools. Well go over the developer tools in
great detail in the next chapter, but for now lets take a simple look.
You can open the Developer Tools by hitting alt-cmd-I, which will bring up
the Chrome developer tools panel.
FIGURE 2-29
Developer Tools
You can now easily inspect all the elements in your current editor. If you
want to update the style of something, you simply need to figure out what
classes it has and write a Less rule into your styles file to modify it.
If you are unfamiliar with Less, it is a basic CSS preprocessor, making some
things in CSS a bit easier. You can learn more about it at lesscss.org. If you prefer to use CSS instead, this file can also be named styles.css and contain CSS.
64
Basic Customization
'atom-text-editor':
'enter': 'editor:newline'
'atom-text-editor[mini] input':
'enter': 'core:confirm'
The configuration itself is grouped by the package name or one of the two
core namespaces: core and editor.
You can open this file in an editor from the Atom > Open Your Config menu.
CONFIGURATION KEY REFERENCE
core
disabledPackages: An array of package names to disable
excludeVcsIgnoredPaths: Dont search within files specified
by .gitignore
ignoredNames: File names to ignore across all of Atom
projectHome: The directory where projects are assumed to be located
65
Enable/disable
soft
line
66
Basic Customization
67
FIGURE 2-30
Python specific
settings
68
Summary
'editor':
'softWrap': true
'.source.ruby': # ruby overrides
'editor':
'tabLength': 2
'.source.python': # python overrides
'editor':
'tabLength': 4
FIGURE 2-31
Finding a language
grammar
Summary
At this point you should be something of an Atom master user. You should be
able to navigate and manipulate your text and files like a wizard. You should
also be able to customize Atom backwards and forwards to make it look and
act just how you want it to.
In the next chapter, were going to kick it up a notch: well take a look at
changing and adding new functionality to the core of Atom itself. Were going to
start creating packages for Atom. If you can dream it, you can build it.
69
Hacking Atom
Now its time to come to the Hackable part of the Hackable Editor. As weve
seen throughout the second section, a huge part of Atom is made up of bundled
packages. If you wish to add some functionality to Atom, you have access to the
same APIs and tools that the core features of Atom has. From the tree view to
the command palette to find and replace functionality, even the most core
features of Atom are implemented as packages.
In this chapter, were going to learn how to extend the functionality of Atom
through writing packages. This will be everything from new user interfaces to
new language grammars to new themes. Well learn this by writing a series of
increasingly complex packages together, introducing you to new APIs and tools
and techniques as we need them.
If youre looking for an example using a specific API or feature, you can skip
to the end of the chapter where weve indexed all the examples that way.
71
Well go over examples like this in a bit, but this is what the language looks
like.
Just about everything you can do with CoffeeScript in Atom is also doable in
JavaScript, but as most of the community uses CoffeeScript, you will probably
want to write your packages in it. This will help you get contributions from the
community and in many instances write simpler code.
You can brush up on CoffeeScript at coffeescript.org.
Less is an even simpler transition from CSS. It adds a number of useful things
like variables and functions to CSS. You can brush up on your Less skills at
lesscss.org. Our usage of Less wont get too complex in this book however, so
as long as you know basic CSS you should be fine.
Because init.coffee provides access to Atoms API, you can use it to implement useful commands without creating a new package or extending an existing one. Heres a command which uses the Selection API and Clipboard API
to construct a Markdown link from the selected text and the clipboard contents
as the URL:
72
Now, reload Atom and use the Command Palette to execute the new
command by name (i.e., Markdown: Paste As Link). And if youd like to trigger
the command via a keyboard shortcut, you can define a keymap for the command.
Package Generator
The simplest way to start a package is to use the built-in package generator that
ships with Atom. As you might expect by now, this generator is itself a separate
package implemented in atom/package-generator.
You can run the generator by invoking the command palette and searching
for Generate Package. A dialog will appear asking you to name your new
project. Atom will then create that directory and fill it out with a skeleton
project and link it into your .atom directory so its loaded when you launch
your editor next time.
73
FIGURE 3-1
Basic generated
Atom package
You can see that Atom has created about a dozen files that make up the
package. Lets take a look at each of them to get an idea of how a package is
structured, then we can modify them to get our word count functionality.
The basic package layout is as follows:
my-package/
grammars/
keymaps/
lib/
menus/
spec/
snippets/
styles/
index.coffee
package.json
Not every package will have (or need) all of these directories and the package generator doesnt create snippets or grammars. Lets see what some of
these are so we can start messing with them.
74
PACKAGE.JSON
Similar to npm packages, Atom packages contain a package.json file in their
top-level directory. This file contains metadata about the package, such as the
path to its main module, library dependencies, and manifests specifying the
order in which its resources should be loaded.
In addition to the regular npm package.json keys available, Atom package.json files have their own additions.
main: the path to the CoffeeScript file thats the entry point to your package. If this is missing, Atom will default to looking for an index.coffee
or index.js.
styles: an Array of Strings identifying the order of the style sheets your
package needs to load. If not specified, style sheets in the styles directory
are added alphabetically.
keymaps: an Array of Strings identifying the order of the key mappings
your package needs to load. If not specified, mappings in the keymaps
directory are added alphabetically.
menus: an Array of Strings identifying the order of the menu mappings
your package needs to load. If not specified, mappings in the menus directory are added alphabetically.
snippets: an Array of Strings identifying the order of the snippets your
package needs to load. If not specified, snippets in the snippets directory
are added alphabetically.
activationCommands: an Object identifying commands that trigger your
packages activation. The keys are CSS selectors, the values are Arrays of
Strings identifying the command. The loading of your package is delayed
until one of these events is triggered within the associated scope defined
by the CSS selector.
The package.json in the package weve just generated looks like this currently:
{
"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/wordcount",
"license": "MIT",
75
"engines": {
"atom": ">0.50.0"
},
"dependencies": {
}
}
One of the first things you should do is ensure that this information is filled
out. The name, description, repository URL the project will be at, and the license can all be filled out immediately. The other information well get into
more detail on as we go.
SOURCE CODE
If you want to extend Atoms behavior, your package should contain a single
top-level module, which you export from whichever file is indicated by the main
key in your package.json file. In the package we just generated, the main package file is lib/wordcount.coffee. The remainder of your code should be
placed in the lib directory, and required from your top-level file. If the main
key is not in your package.json file, it will look for index.coffee or index.js
as the main entry point.
Your packages top-level module is a singleton object that manages the lifecycle of your extensions to Atom. Even if your package creates ten different
views and appends them to different parts of the DOM, its all managed from
your top-level object.
Your packages top-level module should implement the following methods:
activate(state): This required method is called when your package is
activated. It is passed the state data from the last time the window was
serialized if your module implements the serialize() method. Use this
to do initialization work when your package is started (like setting up
DOM elements or binding events).
serialize(): This optional method is called when the window is shutting down, allowing you to return JSON to represent the state of your
component. When the window is later restored, the data you returned is
passed to your modules activate method so you can restore your view
to where the user left off.
deactivate(): This optional method is called when the window is shutting down. If your package is watching any files or holding external resources in any other way, release them here. If youre just subscribing to
things on window, you dont need to worry because thats getting torn
down anyway.
76
STYLE SHEETS
Style sheets for your package should be placed in the styles directory. Any style
sheets in this directory will be loaded and attached to the DOM when your package is activated. Style sheets can be written as CSS or Less, but Less is recommended.
Ideally, you wont need much in the way of styling. Atom provides a standard
set of components which define both the colors and UI elements for any package that fits into Atom seamlessly. You can view all of Atoms UI components by
opening the styleguide: open the command palette (cmd-shift-P) and search
for styleguide, or just type cmd-ctrl-shift-G.
If you do need special styling, try to keep only structural styles in the package style sheets. If you must specify colors and sizing, these should be taken
from the active themes ui-variables.less.
An optional styleSheets array in your package.json can list the style sheets
by name to specify a loading order; otherwise, style sheets are loaded alphabetically.
KEYMAPS
Its recommended that you provide key bindings for commonly used actions for
your extension, especially if youre also adding a new command. In our new
package, we have a keymap filled in for us already in the keymaps/wordcount.cson file:
'atom-workspace':
'ctrl-alt-o': 'wordcount:toggle'
This means that if you hit ctrl-alt-o, our package will run the toggle
command. Well look at that code next, but if you want to change the default
key mapping, you can do that in this file.
Keymaps are placed in the keymaps subdirectory. By default, all keymaps
are loaded in alphabetical order. An optional keymaps array in your package.json can specify which keymaps to load and in what order.
Keybindings are executed by determining which element the keypress occurred on. In the example above, the wordcount:toggle command is executed
when pressing ctrl-alt-o only on the atom-workspace element. This means
that if youre focused on something else like the Tree View or Settings pane for
example, this key mapping wont work.
Well cover more advanced keymapping stuff a bit later in Keymaps InDepth.
77
MENUS
Menus are placed in the menus subdirectory. This defines menu elements like
what pops up when you right click (a context-menu) or would go in the menu
bar (application menu) to trigger functionality in your plugin.
By default, all menus are loaded in alphabetical order. An optional menus
array in your package.json can specify which menus to load and in what order.
Application Menu
Its recommended that you create an application menu item for common actions with your package that arent tied to a specific element. If we look in the
menus/wordcount.cson file that was generated for us, well see a section that
looks like this:
'menu': [
{
'label': 'Packages'
'submenu': [
'label': 'Word Count'
'submenu': [
{
'label': 'Toggle'
'command': 'wordcount:toggle'
}
]
]
}
]
This section puts a Toggle menu item under a menu group named Word
Count in the Packages menu.
78
FIGURE 3-2
Application menu
item
When you select that menu item, it will run the wordcount:toggle command, which well look at in a bit.
The menu templates you specify are merged with all other templates provided by other packages in the order which they were loaded.
Context Menu
Its recommended to specify a context menu item for commands that are
linked to specific parts of the interface. In our menus/wordcount.cson file, we
can see an auto-generated section that looks like this:
'context-menu':
'atom-text-editor': [
{
'label': 'Toggle Word Count'
'command': 'wordcount:toggle'
}
]
79
This adds a Toggle Word Count menu option to the menu that pops up
when you right-click in an Atom text editor pane.
FIGURE 3-3
Context menu entry
When you click that it will again run the wordcount:toggle method in your
code.
Context menus are created by determining which element was selected and
then adding all of the menu items whose selectors match that element (in the
order which they were loaded). The process is then repeated for the elements
until reaching the top of the DOM tree.
You can also add separators and submenus to your context menus. To add a
submenu, provide a submenu key instead of a command. To add a separator,
add an item with a single type: 'separator' key/value pair. For instance,
you could do something like this:
'context-menu':
'atom-workspace': [
{
label: 'Text'
submenu: [
{label: 'Inspect Element', command: 'core:inspect'}
{type: 'separator'}
80
FIGURE 3-4
Wordcount Package
is Alive dialog
81
module.exports =
class WordcountView
constructor: (serializedState) ->
# Create root element
@element = document.createElement('div')
@element.classList.add('wordcount')
# Create message element
message = document.createElement('div')
message.textContent = "The Wordcount 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
Basically the only thing happening here is that when the View class is created, it creates a simple div element and adds the wordcount class to it (so we
can find or style it later) and then adds the ``Wordcount package is Alive!``
text to it. There is also a getElement method which returns that div. The serialize and destroy methods dont do anything and we wont have to worry
about that until another example.
Notice that were simply using the basic browser DOM methods (ie, createElement(), appendChild()).
The second file we have is the main entry point to the package (again, because its referenced in the package.json file). Lets take a look at that file.
WordcountView = require './wordcount-view'
{CompositeDisposable} = require 'atom'
module.exports = Wordcount =
wordcountView: null
modalPanel: null
subscriptions: null
82
There is a bit more going on here. First of all we can see that we are defining
four methods. The only required one is activate. The deactivate and serialize methods are expected by Atom but optional. The toggle method is one
Atom is not looking for, so well have to invoke it somewhere for it to be called,
which you may recall we do both in the activationCommands section of the
package.json file and in the action we have in the menu file.
The deactivate method simply destroys the various class instances weve
created and the serialize method simply passes on the serialization to the
View class. Nothing too exciting here.
The activate command does a number of things. For one, it is not called
automatically when Atom starts up, it is first called when one of the activationCommands as defined in the package.json file are called. In this case, activate is only called the first time the toggle command is called. If nobody
ever invokes the menu item or hotkey, this code is never called.
This method does two things. The first is that it creates an instance of the
View class we have and adds the element that it creates to a hidden modal panel in the Atom workspace.
@wordcountView = new WordcountView(state.wordcountViewState)
@modalPanel = atom.workspace.addModalPanel(
item: @wordcountView.getElement(),
visible: false
)
83
Well ignore the state stuff for now, since its not important for this simple
plugin. The rest should be fairly straightforward.
The next thing this method does is create an instance of the CompositDisposable class so it can register all the commands that can be called from the
plugin so other plugins could subscribe to these events.
Next we have the toggle method. This method simply toggles the visibility
of the modal panel that we created in the activate method.
toggle: ->
console.log 'Wordcount was toggled!'
if @modalPanel.isVisible()
@modalPanel.hide()
else
@modalPanel.show()
This should be fairly simple to understand. Were looking to see if the modal
element is visible and hiding or showing it depending on its current state.
THE FLOW
So, lets review the actual flow in this package.
Atom starts up
Atom reads the package.json
Atom reads keymaps, menus, main file
User runs a package command
Atom executes the activate method
Creates a WordCount view, which creates a div
Grabs that div and sticks it in a hidden modal
Atom executes the package command
Sees that the modal is hidden, makes it visible
User runs a package command again
Atom executes the package command
Sees that the modal is visible, makes it hidden
84
Lets look at the 3 lines weve added. First we get an instance of the current
editor object (where our text to count is) by calling atom.workspace.getActiveTextEditor().
Next we get the number of words by calling getText() on our new editor
object, then splitting that text on whitespace with a regular expression and
then getting the length of that array.
Finally, we tell our view to update the word count it displays by calling the
setCount() method on our view and then showing the modal again. Since
that method doesnt yet exist, lets create it now.
We can add this code to the end of our wordcount-view.coffee file:
setCount: (count) ->
displayText = "There are #{count} words."
@element.children[0].textContent = displayText
Pretty simple - we take the count number that was passed in and place it
into a string that we then stick into the element that our view is controlling.
85
FIGURE 3-5
Word Count Working
Basic Debugging
Youll notice a few console.log statements in the code. One of the cool things
about Atom being built on Chromium is that you can use some of the same debugging tools available to you that you have when doing web development.
To open up the Developer Console, hit alt-cmd-I, or choose the menu option View > Developer > Toggle Developer Tools.
FIGURE 3-6
Developer Tools
Debugging
From here you can inspect objects, run code and view console output just as
though you were debugging a web site.
86
Testing
Your package should have tests, and if theyre placed in the spec directory, they
can be run by Atom.
Under the hood, Jasmine executes your tests, so you can assume that any
DSL available there is also available to your package.
RUNNING TESTS
Once youve got your test suite written, you can run it by pressing cmd-altctrl-p or via the Developer > Run Package Specs menu. Our generated package
comes with an example test suite, so you can run this right now to see what
happens.
FIGURE 3-7
Spec Suite Results
You can also use the apm test command to run them from the command
line. It prints the test output and results to the console and returns the proper
status code depending on whether the tests passed or failed.
87
Publishing
Now that our simple plugin is working and tested, lets go ahead and publish it
so its available to the world.
Atom bundles a command line utility called apm which we first used back in
Command Line to search for and install packages via the command line. The
apm command can also be used to publish Atom packages to the public registry
and update them.
PREPARE YOUR PACKAGE
There are a few things you should double check before publishing:
Your package.json file has name, description, and repository fields.
Your package.json file has a version field with a value of "0.0.0".
Your package.json file has an engines field that contains an entry for
Atom such as: "engines": {"atom": ">=0.50.0"}.
Your package has a README.md file at the root.
Your package is in a Git repository that has been pushed to GitHub. Follow this guide if your package isnt already on GitHub.
PUBLISH YOUR PACKAGE
Before you publish a package it is a good idea to check ahead of time if a package with the same name has already been published to atom.io. You can do
that by visiting https://atom.io/packages/my-package to see if the package already exists. If it does, update your packages name to something that is
available before proceeding.
Now lets review what the apm publish command does:
1. Registers the package name on atom.io if it is being published for the first
time.
2. Updates the version field in the package.json file and commits it.
3. Creates a new Git tag for the version being published.
4. Pushes the tag and current branch up to GitHub.
88
If this is the first package you are publishing, the apm publish command
may prompt you for your GitHub username and password. This is required to
publish and you only need to enter this information the first time you publish.
The credentials are stored securely in your keychain once you login.
Your package is now published and available on atom.io. Head on over to
https://atom.io/packages/my-package to see your packages page.
With apm publish, you can bump the version and publish by using
apm publish <version-type>
Summary
Weve now generated, customized and published our first plugin for Atom. Congratulations! Now anyone can install our masterpiece from directly within Atom
as we did in Atom Packages.
89
90
convert: ->
console.log 'Convert text!'
CREATE A COMMAND
Now lets add a command. It is recommended that you namespace your commands with the package name followed by a :, so as you can see in the code,
we called our command ascii-art:convert and it will call the convert()
method when its called.
So far, that will simply log to the console. Lets start by making it insert
something into the text buffer.
convert: ->
if editor = atom.workspace.getActiveTextEditor()
editor.insertText('Hello, World!')
As in Counting the Words, were using atom.workspace.getActiveTextEditor() to get the object that represents the active text editor. If this
convert() method is called when not focused on a text editor, this will simply
return a blank string, so we can skip the next line.
Next we insert a string into the current text editor with the insertText()
method. This will insert the text wherever the cursor currently is in the current
editor. If there is a selection, it will replace all selections with the Hello, World!
text.
RELOAD THE PACKAGE
Before we can trigger ascii-art:convert, we need to load the latest code for
our package by reloading the window. Run the command Window: Reload
from the command palette or by pressing ctrl-alt-cmd-l.
TRIGGER THE COMMAND
Now open the command panel and search for the Ascii Art: Convert command. But its not there! To fix this, open package.json and find the property
called activationCommands. Activation commands speed up load time by allowing Atom to delay a packages activation until its needed. So remove the existing command and use ascii-art:convert in activationCommands:
"activationCommands": {
"atom-workspace": "ascii-art:convert"
}
91
First, reload the window by running the command Window: Reload from
the command palette. Now when you run the Ascii Art: Convert command it
will output Hello, World!
ADD A KEY BINDING
Now lets add a key binding to trigger the ascii-art:convert command.
Open keymaps/ascii-art.cson and add a key binding linking ctrl-alt-a to the
ascii-art:convert command. You can delete the pre-existing key binding
since you dont need it anymore.
When finished, the file should look like this:
'atom-text-editor':
'ctrl-alt-a': 'ascii-art:convert'
Now reload the window and verify that the key binding works.
After saving the file, run the command Update Package Dependencies: Update from the Command Palette. This will install the packages node module
dependencies, only figlet in this case. You will need to run Update Package Dependencies: Update whenever you update the dependencies field in your
package.json file.
If for some reason this doesnt work, youll see a message saying Failed to
update package dependencies and you will find a new npm-debug.log file in
your directory. That file should give you some idea as to what went wrong.
Now require the figlet node module in lib/ascii-art.coffee and instead of inserting Hello, World!, convert the selected text to ASCII art.
convert: ->
if editor = atom.workspace.getActiveTextEditor()
selection = editor.getSelectedText()
figlet = require 'figlet'
font = "o8"
92
Creating a Theme
Now reload the editor, select some text in an editor window and hit ctrlalt-a. It should be replaced with a ridiculous ASCII art version instead.
There are a couple of new things in this example we should look at quickly.
The first is the editor.getSelectedText() which, as you might guess, returns the text that is currently selected.
We then call the Figlet code to convert that into something else and replace
the current selection with it with the editor.insertText() call.
Summary
In this section, weve made a UI-less package that takes selected text and replaces it with a processed version. This could be helpful in creating linters or
checkers for your code.
Creating a Theme
Atoms interface is rendered using HTML, and its styled via Less which is a superset of CSS. Dont worry if you havent heard of Less before; its just like CSS,
but with a few handy extensions.
Atom supports two types of themes: UI and syntax. UI themes style elements
such as the tree view, the tabs, drop-down lists, and the status bar. Syntax
themes style the code inside the editor.
Themes can be installed and changed from the settings view which you can
open by selecting the Atom > Preferences menu and navigating to the Install
section and the Themes section on the left hand side.
Getting Started
Themes are pretty straightforward but its still helpful to be familiar with a few
things before starting:
Less is a superset of CSS, but it has some really handy features like variables. If you arent familiar with its syntax, take a few minutes to familiarize yourself.
93
You may also want to review the concept of a package.json (as covered in
package.json). This file is used to help distribute your theme to Atom
users.
Your themes package.json must contain a "theme" key with a value of
"ui" or "syntax" for Atom to recognize and load it as a theme.
You can find existing themes to install or fork on atom.io.
94
Creating a Theme
Development workflow
There are a few of tools to help make theme development faster and easier.
LIVE RELOAD
Reloading by hitting cmd-alt-ctrl-L after you make changes to your theme is
less than ideal. Atom supports live updating of styles on Dev Mode Atom windows.
To enable a Dev Mode window:
1. Open your theme directory in a dev window by either going to the View >
Developer > Open in Dev Mode menu or by hitting the cmd-shift-o shortcut
2. Make a change to your theme file and save it. Your change should be immediately applied!
95
If youd like to reload all the styles at any time, you can use the shortcut
cmd-ctrl-shift-r.
DEVELOPER TOOLS
Atom is based on the Chrome browser, and supports Chromes Developer Tools.
You can open them by selecting the View > Toggle Developer Tools menu, or by
using the cmd-alt-i shortcut.
The dev tools allow you to inspect elements and take a look at their CSS
properties.
FIGURE 3-8
Dev Tools
96
Creating a Theme
FIGURE 3-9
Style Guide
Theme Variables
Atoms UI provides a set of variables you can use in your own themes and packages.
USE IN THEMES
Each custom theme must specify a ui-variables.less file with all of the following variables defined. The top-most theme specified in the theme settings
will be loaded and available for import.
USE IN PACKAGES
In any of your packages .less files, you can access the theme variables by importing the ui-variables file from Atom.
Your package should generally only specify structural styling, and these
should come from the style guide. Your package shouldnt specify colors, padding sizes, or anything in absolute pixels. You should instead use the theme
variables. If you follow this guideline, your package will look good out of the
box with any theme!
Heres an example .less file that a package can define using theme variables:
97
@import "ui-variables";
.my-selector {
background-color: @base-background-color;
padding: @component-padding;
}
VARIABLES
Text colors
@text-color
@text-color-subtle
@text-color-highlight
@text-color-selected
@text-color-info - A blue
@text-color-success- A green
@text-color-warning- An orange or yellow
@text-color-error - A red
Background colors
@background-color-info - A blue
@background-color-success - A green
@background-color-warning - An orange or yellow
@background-color-error - A red
@background-color-highlight
@background-color-selected
@app-background-color - The apps background under all the editor
components
Component colors
@base-background-color @base-border-color @pane-item-background-color @pane-item-border-color @input-background-color @input-border-color @tool-panel-background-color -
98
Creating a Theme
@tool-panel-border-color @inset-panel-background-color @inset-panel-border-color @panel-heading-background-color @panel-heading-border-color @overlay-background-color @overlay-border-color @button-background-color @button-background-color-hover @button-background-color-selected @button-border-color @tab-bar-background-color @tab-bar-border-color @tab-background-color @tab-background-color-active @tab-border-color @tree-view-background-color @tree-view-border-color @ui-site-color-1 @ui-site-color-2 @ui-site-color-3 @ui-site-color-4 @ui-site-color-5 -
Component sizes
@disclosure-arrow-size @component-padding @component-icon-padding @component-icon-size @component-line-height @component-border-radius @tab-height -
99
Fonts
@font-size @font-family -
Debugging
Atom provides several tools to help you understand unexpected behavior and
debug problems. This guide describes some of those tools and a few approaches to help you debug and provide more helpful information when submitting issues:
Head on over to the list of releases and see if theres a more recent release.
You can update to the most recent release by downloading Atom from the releases page, or with the in-app auto-updater. The in-app auto-updater checks
for and downloads a new version after you restart Atom, or if you use the Atom
> Check for Update menu option.
to list all linked development packages. You can remove the links using the
apm unlink command. See apm unlink --help for details.
100
Debugging
FIGURE 3-10
Settings View
Check Atoms settings in the Settings pane, theres a description of each configuration option here. For example, if you want Atom to use hard tabs (real
tabs) and not soft tabs (spaces), disable the Soft Tabs option.
Since Atom ships with a set of packages and you can install additional packages yourself, check the list of packages and their settings. For example, if youd
like to get rid of the vertical line in the middle of the editor, disable the Wrap
Guide package. And if you dont like it when Atom strips trailing whitespace or
ensures that theres a single trailing newline in the file, you can configure that in
the Whitespace packages settings.
101
FIGURE 3-11
Package Settings
TimeCop
TODO: Document TimeCop
FIGURE 3-12
Keybinding Resolver
The keybinding resolver shows you a list of keybindings that exist for the
keystroke, where each item in the list has the following:
102
Debugging
103
This starts Atom, but does not load packages from ~/.atom/packages or
~/.atom/dev/packages. If you can no longer reproduce the problem in safe
mode, its likely it was caused by one of the packages.
To figure out which package is causing trouble, start Atom normally again
and open Settings (cmd-,). Since Settings allow you to disable each installed
package, you can disable packages one by one until you can no longer reproduce the issue. Restart (cmd-q) or reload (cmd-ctrl-alt-l) Atom after you
disable each package to make sure its completely gone.
When you find the problematic package, you can disable or uninstall the
package, and consider creating an issue on the packages GitHub repository.
FIGURE 3-13
DevTools Error
If you can reproduce the error, use this approach to get the full stack trace.
The stack trace might point to a problem in your Init script or a specific package
you installed, which you can then disable and report an issue on its GitHub
repository.
104
Debugging
FIGURE 3-14
Then refocus Atom and perform the slow action to capture a recording.
When finished, click Stop. Switch to the Chart view, and a graph of the recorded actions will appear. Try to zoom in on the slow area, then take a screenshot
to include with your report. You can also save and post the profile data by clicking Save next to the profiles name (e.g. Profile 1) in the left panel.
FIGURE 3-15
105
Writing specs
Weve looked at and written a few specs through the examples already. Now its
time to take a closer look at the spec framework itself. How exactly do you write
tests in Atom?
Atom uses Jasmine as its spec framework. Any new functionality should
have specs to guard against regressions.
or
describe "Editor::moveUp", ->
# contents
106
Writing specs
Asynchronous specs
Writing Asynchronous specs can be tricky at first. Some examples.
PROMISES
Working with promises is rather easy in Atom. You can use our waitsForPromise function.
describe "when we open a file", ->
it "should be opened in an editor", ->
waitsForPromise ->
atom.workspace.open('c.coffee').then (editor) ->
expect(editor.getPath()).toContain 'c.coffee'
This method can be used in the describe, it, beforeEach and afterEach
functions.
describe "when we open a file", ->
beforeEach ->
waitsForPromise ->
atom.workspace.open 'c.coffee'
107
If you need to wait for multiple promises use a new waitsForPromise function for each promise. (Caution: Without beforeEach this example will fail!)
describe "waiting for the packages to load", ->
beforeEach ->
waitsForPromise ->
atom.workspace.open('sample.js')
waitsForPromise ->
atom.packages.activatePackage('tabs')
waitsForPromise ->
atom.packages.activatePackage('tree-view')
it 'should have waited long enough', ->
expect(atom.packages.isPackageActive('tabs')).toBe true
expect(atom.packages.isPackageActive('tree-view')).toBe true
Running specs
Most of the time youll want to run specs by triggering the window:runpackage-specs command. This command is not only to run package specs, it
is also for Atom core specs. This will run all the specs in the current projects
108
spec directory. If you want to run the Atom core specs and all the default package specs trigger the window:run-all-specs command.
To run a limited subset of specs use the fdescribe or fit methods. You can
use those to focus a single spec or several specs. In the example above, focusing an individual spec looks like this:
describe "when a test is written", ->
fit "has some expectations that should pass", ->
expect("apples").toEqual("apples")
expect("oranges").not.toEqual("apples")
RUNNING ON CI
It is now easy to run the specs in a CI environment like Travis and AppVeyor. See
the Travis CI For Your Packages and AppVeyor CI For Your Packages posts for
more details.
109
Summary
( List of topics we covered / appendix? )
Summary.
110
Behind Atom
Now that weve written a number of packages and themes, lets take minute to
take a closer look at some of the ways that Atom works in greater depth. Here
well go into more of a deep dive on individual internal APIs and systems of
Atom, even looking at some Atom source to see how things are really getting
done.
Configuration API
Reading Config Settings
If you are writing a package that you want to make configurable, youll need to
read config settings via the atom.config global. You can read the current value
of a namespaced config key with atom.config.get:
# read a value with `config.get`
@showInvisibles() if atom.config.get "editor.showInvisibles"
111
The atom.config.observe method will call the given callback immediately with the current value for the specified key path, and it will also call it in the
future whenever the value of that key path changes. If you only want to invoke
the callback when the next time the value changes, use atom.config.onDidChange instead.
Subscription methods return disposable subscription objects. Note in the
example above how we save the subscription to the @fontSizeObserveSubscription instance variable and dispose of it when the view is detached. To
group multiple subscriptions together, you can add them all to a CompositeDisposable that you dispose when the view is detached.
If youre exposing package configuration via specific key paths, youll want
to associate them with a schema in your packages main module. Read more
about schemas in the config API docs.
Keymaps In-Depth
Structure of a Keymap File
Keymap files are encoded as JSON or CSON files containing nested hashes.
They work much like style sheets, but instead of applying style properties to elements matching the selector, they specify the meaning of keystrokes on elements matching the selector. Here is an example of some bindings that apply
when keystrokes pass through atom-text-editor elements:
'atom-text-editor':
'cmd-delete': 'editor:delete-to-beginning-of-line'
'alt-backspace': 'editor:delete-to-beginning-of-word'
'ctrl-A': 'editor:select-to-first-character-of-line'
'ctrl-shift-e': 'editor:select-to-end-of-line'
'cmd-left': 'editor:move-to-first-character-of-line'
'atom-text-editor:not([mini])':
112
Keymaps In-Depth
'cmd-alt-[': 'editor:fold-current-row'
'cmd-alt-]': 'editor:unfold-current-row'
Beneath the first selector are several bindings, mapping specific keystroke
patterns to commands. When an element with the atom-text-editor class is
focused and cmd-delete is pressed, an custom DOM event called
editor:delete-to-beginning-of-line is emitted on the atom-texteditor element.
The second selector group also targets editors, but only if they dont have
the mini attribute. In this example, the commands for code folding dont really
make sense on mini-editors, so the selector restricts them to regular editors.
KEYSTROKE PATTERNS
Keystroke patterns express one or more keystrokes combined with optional
modifier keys. For example: ctrl-w v, or cmd-shift-up. A keystroke is composed of the following symbols, separated by a -. A multi-keystroke pattern can
be expressed as keystroke patterns separated by spaces.
Type
Examples
Character literals
a4$
Modifier keys
Special keys
COMMANDS
Commands are custom DOM events that are triggered when a keystroke matches a binding. This allows user interface code to listen for named commands
without specifying the specific keybinding that triggers it. For example, the following code creates a command to insert the current date in an editor:
atom.commands.add 'atom-text-editor',
'user:insert-date': (event) ->
editor = @getModel()
editor.insertText(new Date().toLocaleString())
113
When you are looking to bind new keys, it is often useful to use the command palette (ctrl-shift-p) to discover what commands are being listened
for in a given focus context. Commands are humanized following a simple algorithm, so a command like editor:fold-current-row would appear as
Editor: Fold Current Row.
COMPOSED COMMANDS
A common question is, How do I make a single keybinding execute two or
more commands? There isnt any direct support for this in Atom, but it can be
achieved by creating a custom command that performs the multiple actions
you desire and then creating a keybinding for that command. For example, lets
say I want to create a composed command that performs a Select Line followed by Cut. You could add the following to your init.coffee:
atom.commands.add 'atom-text-editor', 'custom:cut-line', ->
editor = atom.workspace.getActiveTextEditor()
editor.selectLinesContainingCursors()
editor.cutSelectedText()
Then lets say we want to map this custom command to alt-ctrl-z, you
could add the following to your keymap:
'atom-text-editor':
'alt-ctrl-z': 'custom:cut-line'
Removing Bindings
When the keymap system encounters a binding with the unset! directive as its
command, it will treat the current element as if it had no key bindings matching
114
Keymaps In-Depth
the current keystroke sequence and continue searching from its parent. If you
want to remove a binding from a keymap you dont control, such as keymaps in
Atom core or in packages, use the unset! directive.
For example, the following code removes the keybinding for a in the Tree
View, which is normally used to trigger the tree-view:add-file command:
'.tree-view':
'a': 'unset!'
FIGURE 4-1
Keybinding resolver
115
When the event handler observes that the cursor does not follow a valid prefix, it calls e.abortKeyBinding(), telling the keymap system to continue
searching for another matching binding.
116
function functionName() {
console.log('Log it out');
}
In the dev tools, the first lines markup looks like this.
FIGURE 4-2
Markup
All the class names on the spans are scope names. Any scope name can be
used to target a settings value.
Scope Selectors
Scope selectors allow you to target specific tokens just like a CSS selector targets specific nodes in the DOM. Some examples:
'.source.js' # selects all javascript tokens
'.source.js .function.name' # selects all javascript function names
'.function.name' # selects all function names in any language
Scope Descriptors
A scope descriptor is an Object that wraps an Array of `String`s. The Array describes a path from the root of the syntax tree to a token including all scope
names for the entire path.
In our JavaScript example above, a scope descriptor for the function name
token would be:
117
Config::get accepts a scopeDescriptor. You can get the value for your
setting scoped to JavaScript function names via:
scopeDescriptor = ['source.js', 'meta.function.js', 'entity.name.function.js']
value = atom.config.get(scopeDescriptor, 'my-package.my-setting')
But, you do not need to generate scope descriptors by hand. There are a
couple methods available to get the scope descriptor from the editor:
Editor::getRootScopeDescriptor to get the languages descriptor.
eg. [".source.js"]
Editor::scopeDescriptorForBufferPosition to get the descriptor
at a specific position in the buffer.
Cursor::getScopeDescriptor to get a cursors descriptor based on
position. eg. if the cursor were in the name of the method in our example
it would return ["source.js", "meta.function.js", "enti-
ty.name.function.js"]
Lets revisit our example using these methods:
editor = atom.workspace.getActiveTextEditor()
cursor = editor.getLastCursor()
valueAtCursor = atom.config.get(cursor.getScopeDescriptor(), 'my-package.my-setting'
valueForLanguage = atom.config.get(editor.getRootScopeDescriptor(), 'my-package.my-s
Serialization in Atom
When a window is refreshed or restored from a previous session, the view and
its associated objects are deserialized from a JSON representation that was
stored during the windows previous shutdown. For your own views and objects
to be compatible with refreshing, youll need to make them play nicely with the
serializing and deserializing.
118
Serialization in Atom
module.exports =
activate: (state) ->
@myObject =
if state
atom.deserializers.deserialize(state)
else
new MyObject("Hello")
serialize: ->
@myObject.serialize()
Serialization Methods
class MyObject
atom.deserializers.add(this)
@deserialize: ({data}) -> new MyObject(data)
constructor: (@data) ->
serialize: -> { deserializer: 'MyObject', data: @data }
.SERIALIZE()
Objects that you want to serialize should implement .serialize(). This
method should return a serializable object, and it must contain a key named
deserializer whose value is the name of a registered deserializer that can
convert the rest of the data to an object. Its usually just the name of the class
itself.
@DESERIALIZE(DATA)
The other side of the coin is the deserialize method, which is usually a classlevel method on the same class that implements serialize. This methods job
is to convert a state object returned from a previous call serialize back into a
genuine object.
ATOM.DESERIALIZERS.ADD(KLASS)
You need to call the atom.deserializers.add method with your class in order to make it available to the deserialization system. Now you can call the
global deserialize method with state returned from serialize, and your
classs deserialize method will be selected automatically.
119
Versioning
class MyObject
atom.deserializers.add(this)
@version: 2
@deserialize: (state) -> ...
serialize: -> { version: @constructor.version, ... }
After this, youll have to npm install and apm rebuild when you make a
change to the node modules code.
120
121
These methods will be called any time a package is activated that provides
their corresponding service. They will receive the service object as an argument. You will usually need to perform some kind of cleanup in the event that
the package providing the service is deactivated. To do this, return a Disposable from your service-consuming method:
{Disposable} = require 'atom'
module.exports =
activate: -> # ...
consumeAnotherServiceV1: (service) ->
useService(adaptServiceFromLegacyAPI(service))
new Disposable -> stopUsingService(service)
consumeAnotherServiceV2: (service) ->
useService(service)
new Disposable -> stopUsingService(service)
Unpublish a Version
If you mistakenly published a version of your package or perhaps you find a
glaring bug or security hole, you may want to unpublish just that version of
your package. For example, if your package is named package-name and the
bad version of your package is v1.2.3 then the command you would execute is:
apm unpublish [email protected]
This will remove just this particular version from the https://atom.io package registry. Anyone who has already downloaded this version will still have it,
but it will no longer be available for installation by others.
Adding a Collaborator
Some packages get too big for one person. Sometimes priorities change and
someone else wants to help out. You can let others help or create co-owners by
adding them as a collaborator on the GitHub repository for your package.
122
Note: Anyone that has push access to your repository will have the ability to
publish new versions of the package that belongs to that repository.
You can also have packages that are owned by a GitHub organization. Anyone who is a member of an organizations team which has push access to the
packages repository will be able to publish new versions of the package.
Transferring Ownership
This is a permanent change. There is no going back!
If you want to hand off support of your package to someone else, you can do
that by transferring the packages repository to the new owner.
If you no longer want to support your package and cannot find anyone to
take it over, you can unpublish your package from https://atom.io. For example, if your package is named package-name then the command you would execute is:
apm unpublish package-name
This will remove your package from the https://atom.io package registry.
Anyone who has already downloaded a copy of your package will still have it
and be able to use it, but it will no longer be available for installation by others.
123
Summary
You should now have a better understanding of some of the core Atom APIs and
systems.
124
Atom is rapidly approaching 1.0. Much of the effort leading up to the 1.0 has
been cleaning up APIs in an attempt to future proof, and make a more pleasant
experience developing packages. If you have developed packages or syntaxes
for Atom before the 1.0 API, you can find some tips on upgrading your work in
this appendix.
TL;DR
Weve set deprecation messages and errors in strategic places to help make
sure you dont miss anything. You should be able to get 95% of the way to an
updated package just by fixing errors and deprecations. There are a couple of
things you can do to get the full effect of all the errors and deprecations.
USE ATOM-SPACE-PEN-VIEWS
If you use any class from require 'atom' with a $ or View in the name, add
the atom-space-pen-views module to your packages package.json files
dependencies:
{
"dependencies": {
"atom-space-pen-views": "^2.0.3"
}
}
125
So this:
# Old way
{$, TextEditorView, View, GitRepository} = require 'atom'
126
EXAMPLES
We have upgraded all the core packages. Please see this issue for a link to all
the upgrade PRs.
Deprecations
All of the methods in Atom core that have changes will emit deprecation messages when called. These messages are shown in two places: your package
specs, and in Deprecation Cop.
SPECS
Just run your specs, and all the deprecations will be displayed in yellow.
Figure 1-1.
Deprecations in
specs
127
DEPRECATION COP
Run an atom window in dev mode (atom -d) with your package loaded, and
open Deprecation Cop (search for deprecation in the command palette). Deprecated methods will be appear in Deprecation Cop only after they have been
called.
Figure 1-2.
Deprecation Cop
When deprecation cop is open, and deprecated methods are called, a Refresh button will appear in the top right of the Deprecation Cop interface. So
exercise your package, then come back to Deprecation Cop and click the Refresh button.
128
@content: ->
@div class: 'find-and-replace', =>
@div class: 'block', =>
@subview 'myEditor', new TextEditorView(mini: true)
#...
THE NEW
require 'atom' no longer provides view helpers or jQuery. Atom core is now
view agnostic. The preexisting view system is available from a new npm package: atom-space-pen-views.
atom-space-pen-views now provides jQuery, space-pen views, and Atom
specific views:
# These are now provided by atom-space-pen-views
$
$$
$$$
View
TextEditorView
ScrollView
SelectListView
129
130
131
This attaches and detaches itself from the dom when toggled, canceling
magically detaches it from the DOM, and it uses the classes overlay and fromtop.
The new SelectListView no longer automatically detaches itself from the
DOM when cancelled. Its up to you to implement whatever cancel behavior you
want. Using the new APIs to mimic the semantics of the old class, it should look
like this:
# New!
class CommandPaletteView extends SelectListView
initialize: ->
super()
# no more need for the `overlay` and `from-top` classes
@addClass('command-palette')
atom.commands.add 'atom-workspace', 'command-palette:toggle', => @toggle()
# You need to implement the `cancelled` method and hide.
cancelled: ->
@hide()
confirmed: ({name, jQuery}) ->
@cancel()
# do something with the result
132
toggle: ->
# Toggling now checks panel visibility,
# and hides / shows rather than attaching to / detaching from the DOM.
if @panel?.isVisible()
@cancel()
else
@show()
show: ->
# Now you will add your select list as a modal panel to the workspace
@panel ?= atom.workspace.addModalPanel(item: this)
@panel.show()
@storeFocusedElement()
items = [] # TODO: build items
@setItems(items)
@focusFilterEditor()
hide: ->
@panel?.hide()
For actions that still require the view, such as dispatching commands or
munging css classes, youll access the view via the atom.views.getView()
133
method. This will return a subclass of HTMLElement rather than a jQuery object
or an instance of a deprecated view class (e.g. WorkspaceView).
# Old!
workspaceView = atom.workspaceView
editorView = workspaceView.getActiveEditorView()
paneView = editorView.getPaneView()
# New!
# Generally, just use the models
workspace = atom.workspace
editor = workspace.getActiveTextEditor()
pane = editor.getPane()
# If you need views, get them with `getView`
workspaceElement = atom.views.getView(atom.workspace)
editorElement = atom.views.getView(editor)
paneElement = atom.views.getView(pane)
Updating Specs
atom.workspaceView, the WorkspaceView class and the EditorView class
have been deprecated. These two objects are used heavily throughout specs,
mostly to dispatch events and commands. This section will explain how to remove them while still retaining the ability to dispatch events and commands.
REMOVING WORKSPACEVIEW REFERENCES
134
# New!
describe 'FindView', ->
workspaceElement = null
beforeEach ->
workspaceElement = atom.views.getView(atom.workspace)
Change it to:
# New!
jasmine.attachToDOM(workspaceElement)
135
editor = atom.workspace.getActiveTextEditor()
editorElement = atom.views.getView(editor)
DISPATCHING COMMANDS
Since the editorElement objects are no longer jQuery objects, they no longer
support trigger(). Additionally, Atom has a new command dispatcher,
atom.commands, that we use rather than commandeering jQuerys trigger
method.
From this:
# Old!
workspaceView.trigger 'a-package:toggle'
editorView.trigger 'find-and-replace:show'
To this:
# New!
atom.commands.dispatch workspaceElement, 'a-package:toggle'
atom.commands.dispatch editorElement, 'find-and-replace:show'
136
Using a CompositeDisposable
You can group multiple disposables into a single disposable with a Compo-
siteDisposable.
{CompositeDisposable} = require 'atom'
class Something
constructor: ->
editor = atom.workspace.getActiveTextEditor()
@disposables = new CompositeDisposable
@disposables.add editor.onDidChange ->
@disposables.add editor.onDidChangePath ->
destroy: ->
@disposables.dispose()
subscribe(unsubscribable)
# Old!
@subscribe editor.on 'changed', ->
# New!
disposables = new CompositeDisposable
disposables.add editor.onDidChange ->
When the modelObject is an Atom model object, the change is very simple.
Just use the correct event method, and add it to your CompositeDisposable.
# Old!
@subscribe editor, 'changed', ->
# New!
disposables = new CompositeDisposable
disposables.add editor.onDidChange ->
137
138
Subscribing To Commands
$.fn.command and View::subscribeToCommand are no longer available.
Now we use atom.commands.add, and collect the results in a CompositeDisposable. See the docs for more info.
# Old!
atom.workspaceView.command 'core:close core:cancel', ->
# When inside a View class, you might see this
@subscribeToCommand 'core:close core:cancel', ->
# New!
@disposables.add atom.commands.add 'atom-workspace',
'core:close': ->
'core:cancel': ->
# You can register commands directly on individual DOM elements in addition to
# using selectors. When in a View class, you should have a `@element` object
# available. `@element` is a plain HTMLElement object
@disposables.add atom.commands.add @element,
'core:close': ->
'core:cancel': ->
139
Deprecation Cop
Deprecation cop will list usages of deprecated selector patterns to guide you.
You can access it via the command palette (cmd-shift-p, then search for Deprecation). It breaks the deprecations down by package:
Figure 1-3.
Deprecation Cop
Custom Tags
Rather than adding classes to standard HTML elements to indicate their role,
Atom now uses custom element names. For example, <div class="workspace"> has now been replaced with <atom-workspace>. Selectors should be
updated accordingly. Note that tag names have lower specificity than classes in
CSS, so youll need to take care in converting things.
Old Selector
New Selector
.editor
atom-text-editor
.editor.mini
atom-text-editor[mini]
.workspace
atom-workspace
140
Old Selector
New Selector
.horizontal
atom-workspaceaxis.horizontal
.vertical
atom-workspace-axis.vertical
.pane-container
atom-pane-container
.pane
atom-pane
.tool-panel
atom-panel
.panel-top
atom-panel.top
.panel-bottom
atom-panel.bottom
.panel-left
atom-panel.left
.panel-right
atom-panel.right
.overlay
atom-panel.modal
141
::shadow
The ::shadow pseudo-element allows you to bypass a single shadow root.
For example, say you want to update a highlight decoration for a linter package.
Initially, the style looks as follows:
// Without shadow DOM support
atom-text-editor .highlight.my-linter {
background: hotpink;
}
In order for this style to apply with the shadow DOM enabled, you will need
to add a second selector with the ::shadow pseudo-element. You should leave
the original selector in place so your theme continues to work with the shadow
DOM disabled during the transition period.
// With shadow DOM support
atom-text-editor .highlight.my-linter,
atom-text-editor::shadow .highlight.my-linter {
background: hotpink;
}
Check out the find-and-replace package for another example of using ::shadow to pierce the shadow DOM.
/deep/
The /deep/ combinator overrides all shadow boundaries, making it useful
for rules you want to apply globally such as scrollbar styling. Heres a snippet
containing scrollbar styling for the Atom Dark UI theme before shadow DOM
support:
// Without shadow DOM support
.scrollbars-visible-always {
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-corner {
background: @scrollbar-background-color;
}
142
::-webkit-scrollbar-thumb {
background: @scrollbar-color;
border-radius: 5px;
box-shadow: 0 0 1px black inset;
}
}
To style scrollbars even inside of the shadow DOM, each rule needs to be
prefixed with /deep/. We use /deep/ instead of ::shadow because we dont
care about the selector of the host element in this case. We just want our styling
to apply everywhere.
// With shadow DOM support using /deep/
.scrollbars-visible-always {
/deep/ ::-webkit-scrollbar {
width: 8px;
height: 8px;
}
/deep/ ::-webkit-scrollbar-track,
/deep/ ::-webkit-scrollbar-corner {
background: @scrollbar-background-color;
}
/deep/ ::-webkit-scrollbar-thumb {
background: @scrollbar-color;
border-radius: 5px;
box-shadow: 0 0 1px black inset;
}
}
# loaded globally
# loaded in the text editor shadow DOM
Check out this style sheet from the decoration-example package for an example of context-targeting.
143
144
Index
145