Activate on save event?


#1

I have a package in the works that needs to run something on every file save. It works fine. I register for the buffer:saved event at activation time. For testing I am activating with a keystroke (myapp:test).

I want to activate on the first save instead. As you can see I have tried to activate with a number of save events but none work …

"activationEvents": ["buffer:saved", "TextBuffer:saved", "textbuffer:saved", "text-buffer:saved"],

What am I doing wrong? Please don’t tell me I have to activate at load time.


#2

Check out go-plus for an example of a package that does a lot of stuff on file save. Admittedly nothing that is bound only to the first save of a file.

I’m guessing you might want to check out atom.workspaceView.eachEditorView (editorView) => @dowhateveritisyouwanttodowiththe(editorView).

Then you could bind an event for will-be-saved on a TextBuffer.

will-be-saved - Emitted before the buffer is saved to disk.

You can get to the buffer via: editorView?.getEditor()?.getBuffer().

Inside the function you bind to that event, you might want to:

If the result is false, you’re saving the file for the first time.


#3

Thank you for the informative reply. However, I wasn’t asking to do something on the first save. I want to activate on the first save. Once I am activated I can do anything on any save. My alternative is to activate on load, which I’m trying to avoid. I might be missing something basic here.

Maybe I should be asking what kind of events are allowed in the activationEvents property in package.json. I have tried to get an activation from textbuffer:save and others like it with no luck. Are only keybindings allowed?


#4

Packages activate when Atom loads. When you activate, you register for events you care about (e.g. by ensuring that when an EditorView is opened, that you do something). In your case, when an EditorView is opened, you want to take some action only after the file is first saved. My procedure describes a way to determine this; at that point you can ‘enable’ the functionality for that EditorView.

If you maintain another component that is stateful and is created for each EditorView, then you should be able to achieve what you want. You just need to make sure you destroy this / clean this up when an EditorView is destroyed.


#5

Any event (in other words, command) can be used as an activation event. So you could put core:save as your activation event in your package.json and that should activate your package on the first user-executed save.


#6

Packages activate when Atom loads if there is nothing in the activationEvents key in the package.json. If there is an array of command names, then the package loads when Atom loads, but does not activate until one of those commands is executed.


#7

I stand corrected. Thanks!


#8

@leedohm: If you have an activationEvent, does your package activate on the first instance of it and then stay activated indefinitely, or will you end up with multiple instances (?) of the package - one for each instance of the activation event?


#9

The package remains activated until explicitly deactivated by closing the window, disabling the package or the package being deactivated by code.


#10

That’s what I thought. So functionally, the two approaches are almost identical. The only major difference being when you run any code (which might be moderately expensive) required to activate the package.

I assume when you say closing the window you mean closing the entire workspace as opposed to a single EditorView?


#11
Go Plus 2.0.1
joefitzgerald/go-plus

Adds `gofmt`, `goimports`, `go vet`, `golint`, `go build` and `go test` functionality for the go language.

This package added 45ms to startup time.

My activation essentially wires up a whole bunch of functions to occur on events.


#12

Thanks once again. That worked. Why would an event sent out by textBuffer be listed as core:save? And is this by any chance documented anywhere? (he asks sheepishly)


#13

Commands are implemented as events at the low level. So while core:save is an event, it is also a command, i.e. the command that is executed when you press Ctrl+S. The TextBuffer class doesn’t actually raise the core:save event … you can find the implementation here:


#14

Ouch. Using core:save has a problem for me. My bindings for the buffer save event aren’t being bound until after the first save call. So I’m missing the first save event. Here is my code …

module.exports =
  activate: ->
    atom.workspace.eachEditor (editor) =>
      buffer = editor.getBuffer()
      buffer.on 'saved', =>
        console.log 'saved'

Is there some way for me to find the editor that caused that first core:save event inside my activation code? Then I could run the same code I run in the buffer:saved event.


#15

Yes.

Yes, there are circumstances when activating a package on Atom load makes sense, e.g. when you are writing a package that adds something to the status bar. (At least, so long as the status-bar package isn’t disabled.)

Maintaining the performance of Atom should be every package author’s responsibility. One suggestion is you might think about adding some code to quickly check if the EditorView in question is attached to an Editor that is using the Go grammar. If not, you can short-circuit a lot of those functions.


#16

I think I figured this out. I can find the editor by using getActiveEditor. I’ll give it a try.


#17

Ah … interesting point. Not that I know of. But you also don’t need to load and activate everything right away. Take a look at my (admittedly hacky) tabs-to-spaces package for an example. I’d point straight to it, but GitHub seems to be having an issue right now (or I seem to be having an issue connecting to GitHub).


#18

Unless I’m misreading the code, or missing something, that statement is incorrect. Only commands can be used as activation events, not any event. See Package::subscribeToActivationEvents for the relevant implementation details;


#19

Yes it is, I think the name event can be quite misleading as there’s basically two very different type of events:

  1. DOM events: These are the ones that can be also called commands, but all DOM events aren’t atom command (load, scroll, etc… are dom events but aren’t necessarily bound to a command). The documentation of the command method is quite clear in that it’s just an enhanced version of the jquery ::on method. The key point here is that these events are dispatched by views and bubbles up in the DOM tree, allowing for the selector syntax we know for binding commands to keystrokes.
  2. Emissary events: These are events sent by anything but views, they are not meant to become commands as they doesn’t traverse the DOM, so you have to explicitly register to them using the emissary ::on method or using the Subscriber mixin. In some way they are more like signals than events as the receiver has to know to the emitter to be able to register to the signal.

#20

Anyway … what I was trying to point people to last night before someone so rudely smashed into a telephone pole down the street and brought down my Internet connection was this:

https://github.com/lee-dohm/tabs-to-spaces/blob/master/lib/index.coffee#L13-15

My activate method on my package just sets up commands that loads the functionality on first call. Both my package load and activation times are under 5ms on even my slow laptop because all that’s loading is the one rather simple file. The functionality of the package is always available, but the full code is only loaded if it is actually used.

(This pattern was stolen from the settings-view package, by the way.)