Package: Active editor info
We saw in our Word Count package how we could show information in a modal panel. However, panels aren’t the only way to extend Pulsar’s UI: you can also add items to the workspace. These items can be dragged to new locations (for example, one of the docks on the edges of the window), and Pulsar will restore them the next time you open the project.
For this package, we’ll define a workspace item that tells us some information about our active text editor. The final package can be viewed at https://github.com/pulsar-edit/active-editor-info.
Create the package
To begin, press Ctrl+Shift+P Cmd+Shift+P to bring up the command palette. Type “generate package” and select the Package Generator: Generate Package command, just as we covered earlier. Enter active-editor-info as the name of the package.
Add an opener
Now let’s edit the package files to show our view in a workspace item instead of a modal panel. The way we do this is by registering an opener with Pulsar. Openers are just functions that accept a URI and return a view (if it’s a URI that the opener knows about). When you call atom.workspace.open(), Pulsar will go through all of its openers until it finds one that can handle the URI you passed.
Let’s open lib/active-editor-info.js and edit our activate() method to register an opener:
"use babel";
import ActiveEditorInfoView from "./active-editor-info-view";
import { CompositeDisposable, Disposable } from "atom";
export default {
subscriptions: null,
activate(state) {
this.subscriptions = new CompositeDisposable(
// Add an opener for our view.
atom.workspace.addOpener((uri) => {
if (uri === "atom://active-editor-info") {
return new ActiveEditorInfoView();
}
}),
// Register command that toggles this view
atom.commands.add("atom-workspace", {
"active-editor-info:toggle": () => this.toggle(),
}),
// Destroy any ActiveEditorInfoViews when the package is deactivated.
new Disposable(() => {
atom.workspace.getPaneItems().forEach((item) => {
if (item instanceof ActiveEditorInfoView) {
item.destroy();
}
});
})
);
},
deactivate() {
this.subscriptions.dispose();
},
toggle() {
console.log("Toggle it!");
},
};
You’ll notice we also removed the activeEditorInfoView property and the serialize() method. That’s because, with workspace items, it’s possible to have more than one instance of a given view. Since each instance can have its own state, each should do its own serialization instead of relying on a package-level serialize() method. We’ll come back to that later.
You probably also noticed that our toggle() implementation just logs the text Toggle it! to the console. Let’s make it actually toggle our view:
toggle() {
atom.workspace.toggle('atom://active-editor-info');
}
Updating the view
Pulsar uses the same view abstractions everywhere, so we can almost use the generated ActiveEditorInfoView class as-is. We just need to add two small methods:
getTitle() {
// Used by Pulsar for tab text
return 'Active Editor Info';
}
getURI() {
// Used by Pulsar to identify the view when toggling.
return 'atom://active-editor-info';
}
Now reload the window and run the Active Editor Info: Toggle command from the command palette! Our view will appear in a new tab in the center of the workspace. If you want, you can drag it into one of the docks. Toggling it again will then hide that dock. If you close the tab and run the toggle command again, it will appear in the last place you had it.
Constraining our item’s locations
The purpose of our view is to show information about the active text editor, so it doesn’t really make sense to show our item in the center of the workspace (where the text editor will be). Let’s add some methods to our view class to influence where its opened:
getDefaultLocation() {
// This location will be used if the user hasn't overridden it by dragging
// the item elsewhere. Valid values are "left", "right", "bottom", and
// "center" (the default).
return 'right';
}
getAllowedLocations() {
// The locations into which the item can be moved.
return ['left', 'right', 'bottom'];
}
Now our item will appear in the right dock initially and users will only be able to drag it to one of the other docks.
Show active editor info
Now that we have our view all wired up, let’s update it to show some information about the active text editor. Add this to the constructor:
this.subscriptions = atom.workspace
.getCenter()
.observeActivePaneItem((item) => {
if (!atom.workspace.isTextEditor(item)) {
message.innerText = "Open a file to see important information about it.";
return;
}
message.innerHTML = `
<h2>${item.getFileName() || "untitled"}</h2>
<ul>
<li><b>Soft Wrap:</b> ${item.softWrapped}</li>
<li><b>Tab Length:</b> ${item.getTabLength()}</li>
<li><b>Encoding:</b> ${item.getEncoding()}</li>
<li><b>Line Count:</b> ${item.getLineCount()}</li>
</ul>
`;
});
Now whenever you open a text editor in the center, the view will update with some information about it.
Also, don’t forget to clean up the subscription in the destroy() method:
destroy() {
this.element.remove();
this.subscriptions.dispose();
}
Serialization
If you were to reload Atom now, you’d see that our item had disappeared. That’s because we haven’t told Pulsar how to serialize it yet. Let’s do that now.
The first step is to implement a serialize() method on our
ActiveEditorInfoView class. Atom will call the serialize() method on every
item in the workspace periodically to save its state.
serialize() {
return {
// This is used to look up the deserializer function. It can be any
// string, but it needs to be unique across all packages!
deserializer: 'active-editor-info/ActiveEditorInfoView'
};
}
Next we need to register a deserializer function that Pulsar can use to recreate the real object when it starts up. The best way to do that is to add a deserializers object to our package.json file:
{
"name": "active-editor-info",
...
"deserializers": {
"active-editor-info/ActiveEditorInfoView": "deserializeActiveEditorInfoView"
}
}
Notice that the key ("active-editor-info/ActiveEditorInfoView") matches the string we used in our serialize() method above. The value ("deserializeActiveEditorInfoView") refers to a function in our main module, which we still need to add. Go back to active-editor-info.js and do that now:
deserializeActiveEditorInfoView(serialized) {
return new ActiveEditorInfoView();
}
The value returned from our serialize() method will be passed to this function. Since our serialized object didn’t include any state, we can just return a new ActiveEditorInfoView instance.
Reload Pulsar and toggle the view with the Active Editor Info: Toggle command. Then reload Pulsar again. Your view should be just where you left it!
Summary
In this section, we’ve made a toggleable workspace item whose placement can be controlled by the user. This could be helpful when creating all sorts of visual tools for working with code!