Atom core development workflow?


#1

I’ve been trying to hack on Atom core a bit lately, and I’ve run into somewhat of a workflow issue. As far as I can tell, if you change a CoffeeScript file in core (let’s say it’s src/pane-element.coffee), changes aren’t reflected in Atom unless you run script/build. In other words, the CoffeeScript doesn’t get re-compiled on save, like Less does, and it doesn’t get re-compiled on Window:Reload either.

Am I doing it right? Do you have to run script/build every time you make a change to a CoffeeScript source file in core?

EDIT: Judging from Do you use Atom while hacking on the core editor? I may actually be doing it right after all, never mind


Workflow to work on atom core
How can I edit core packages?
Develop core module without rebuild
Is it possible to run atom without packaging it?
#2

Am I doing it right? Do you have to run script/build every time you make a change to a CoffeeScript source file in core?

The short answer is yes. However running the test suite is always done with the uncompiled sources, so while you have to run script/build to update the code of the app itself, you don’t need to do so to run the tests.
This is important to note because if you hack the core classes (like pane), it’s either because:

  1. You want to fix or add something, and probably submit a PR at some point where you’ll be asked to write tests for the fix/addition anyway, so it’s preferable to test drive the work sooner than later.
  2. You just want to patch something for your own usage, without caring about merging the changes in core. In that case, you can always rely on JS monkey-patching ability, and so you don’t really need to do that in the source file. As long as you know how to require the file, you can patch any methods from your init script or from a package. Using a package can be seen as an intermediary solution: You’re not sure if this patch should make it to the core but you want other people to have the ability to use it. This is, for instance, how I patched the multi-cursor copy/paste behavior until the PR I made was merged in core.

#3

Hi see the point of test driving a feature. But when you want to quickly hack the code to see how it works, to check behavior, to investigate you wanna try the app itself. And wainting several minutes between two modifications is very long in this kind of work.

Is it possible to run atom from its repository (I mean running electron on the atom source repository)?
How main atom developer work on the project?


#4

It is! In dev mode Atom loads its resources from your project path (~/github by default) from the atom folder in that directory. (You can also supply a custom resources path with -r, but I find it easiest to just stick with the defaults and open a dev mode window.)

So once you’ve built and bootstrapped Atom once, you need only load a window in dev mode and reload it (with the command Window: Reload in Atom) when you’ve made changes (e.g. atom -d .).

However, if you change anything in package.json you will need to run script/build first.


#5

@abe: I’d like to do some experimentation on the core editor (I’m specifically looking at the TextEditor class), and based on your description I think monkey patching should work for me, but I haven’t done it in JS before (did a bit in Python…). Do you mind expanding a bit on how to monkey patch in Atom? Or provide a link to an example?

Thanks!


#6

It’s easy. You can override a method on a core class by replacing the function in the class (prototype). You may need to keep a copy of the original to call before or after. I’ve done this several times.

BTW, this is rarely needed. The API is pretty good.


#7

Does this process still work? I’ve tried it on a Mac, and on Windows and cannot get atom to load the code from ~/github/atom. ‘atom -d’ opens the atom menu bar, but no code is loaded. If I run the build version, it works fine. ‘atom -r’ does the same thing. I’ve tried pointing -r at many different directories, but it does not pick up anything. The only way I’ve been able to start a development version is by doing a full build and running the built version.


#8

As @mark_hahn said, you can just override whatever you need.

The basic principle here is that if you need to rewrite a method of a class you’ll have to do so on its prototype. For instance, let’s say we want to rewrite the getSelectedBufferRange method of the TextEditor class, we’ll do something like this:

'use babel';
import { TextEditor } from 'atom';

var oldMethod = TextEditor.prototype.getSelectedBufferRange;

TextEditor.prototype.getSelectedBufferRange = function () {
  // you can invoke the previous method like this: 
  oldMethod.call(this);
  // or this: 
  oldMethod.apply(this);
};

I had a package in the early days of Atom that was monkey patching the editor and the selection class to support pasting multiple selection (this allowed me to test and distribute a fix while making a PR in core).
Here’s the code I wrote at that time: https://github.com/abe33/atom-multiple-cursors-clipboard/blob/master/lib/multiple-cursors-clipboard.coffee

It’s really old so don’t rely too much on the API I used, and also, it’s written in CoffeeScript.


#9

Thanks @abe and @mark_hahn for the input.

abe, both examples are extremely helpful, and the CoffeeScript approach looks potentially better for me. Now let’s see if I can actually make something work :wink:

Thanks!


#10

This is good advice, except for my use case I want to add something that is not in the exports. Specifically, I am adding ClojureScript support to compile-cache.js. I don’t have access to the COMPILERS var via monkey-patching.

How do the atom developers manage their environment?


#11

I was having the same issue, I needed to call the correct atom script however — the default atom was my normal, binary-installed application — so, instead I did, ./atom.sh -d or ./atom.sh -r <PATH> and that seems to work.


#12

@abe, so where would you put this file (for example), to get it to override this behavior?


#13

If it was in CoffeeScript you could just put that in your init script. Otherwise, you can create a new package and put that in its activate method (and taking care of restoring the previous function in deactivate).