Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve devtools integration #282

Merged
merged 27 commits into from
Jan 28, 2016
Merged

Improve devtools integration #282

merged 27 commits into from
Jan 28, 2016

Conversation

lionel-
Copy link
Member

@lionel- lionel- commented Dec 7, 2015

This improves devtools integration in several ways.

Additional functions:
  • loading package with a prefix forces a recompile. With a double prefix, the package is unloaded.
  • new checking method binded to C-c C-t c. With no prefix, run tests. With single prefix, run R CMD check. With double prefix, check only documentation
  • new documentation method. I didn't find a good keybinding for it though.

All these commands run through (ess-developer-send-process). I thus deprecated the variable ess-developer-load-package-command since users can use the function to send any custom commands.

Keeping track of packages

A new buffer local variable ess-developer-local-package can be set to a cons cell with package info: (name . path). This way we can use devtools commands without activating developer mode. It also works in files outside a package hierarchy.

Furthermore, using a devtools command will set this local variable in the inferior buffer as well. This way the commands work when called from that buffer and it is useful to create functions using this information. For example I use it to reload R and the current package automatically (useful when debugging a crashing function).

Smarter package selection

This one is not related to devtools. When adding a package, the user is no longer prompted for the path when the selected package is the same as the package containing the current file.

In addition, I'd like to never ask for the path when we call a command from a package file. Would there be objections for this?

@vspinu
Copy link
Member

vspinu commented Dec 7, 2015

Hi Lionel, All these looks truly great. Unfortunately I am just starting my very long trip to Asia. I will be able to have a closer look at it in a couple of days only. Same holds for your other patch (#281). (But I can also look at them even you have merged them.)

Is the plan to keep all shortcuts in dev-map? I would like that but I am afraid we don't have that much free space there. We will need a lot of devtools related keys. So maybe keep everything in one devtools map? Where to hook it then?

@lionel-
Copy link
Member Author

lionel- commented Dec 7, 2015

Don't worry Vitalie.

I will hold those PR here until the new patch release has come out. I'm not really qualified to come up with good Emacs-style bindings because as an Evil user I don't use them.

I wish you a very nice trip!

@lionel- lionel- force-pushed the dev-path branch 2 times, most recently from 612d4f3 to f5196f2 Compare December 12, 2015 12:25
Useful when using a scratch file outside the R package directory.
Don't ask for package path if the selected package is the same as the
one containing the current file.
* More loading options
* Function to perform checks on the package
* Function to document the package

`ess-developer-load-package-command' is now obsolete. Custom commands
can be send through (ess-developer-send-process) instead.
Previously, only "R" directories were detected. Now, a whole ranges of
directories are detected. The challenge is to avoid recursive
algorithms because they are slow in remote environments.

The new system uses a new variable `ess-developer-package-dirs` which
contains an alist of directory names and their depth in the package
hierarchy. For example the "testthat" folder is usually at depth 2
compared to the package root. This can be user-customised.
Check if `ess-developer-local-package` is set in the attached process
buffer, in which case use it. This way we can use devtools with last
used package in files that are not connected to a package.
@lionel-
Copy link
Member Author

lionel- commented Jan 26, 2016

@vspinu I wonder if the whole ess-developer-packages list is really necessary. I don't use the code injection mechanism so I'm not sure about this, but I've been using the devtools integration (which doesn't rely on ess-developer-packages) for a few months and it works quite well. Packages are determined with:

  • whether we are in a typical package folder (R, src, man, etc). I'm currently adding a ess-developer-check-all-dirs variable that can be switched off when working on remotes. When t the whole folder hierarchy will be checked instead of just the typical folders. Edit: Now implemented.
  • A file or dir local variable ess-developer-local-package.

This allows ESS to select the right package pretty much all the time.

By comparison, maintaining a list of known packages and switching manually between them seems cumbersome. Should we deprecate this?

@lionel-
Copy link
Member Author

lionel- commented Jan 26, 2016

@vspinu @mmaechler

Would C-c d be a good prefix for r-devtools-map? I'm asking as an Emacs keybindings neophyte ;) Seems to be unused.

@vspinu
Copy link
Member

vspinu commented Jan 26, 2016

@vspinu I wonder if the whole ess-developer-packages list is really necessary.

I have started working on improving the workflow some time ago (devUI branch). And it goes in line with your desire. But ess-developer-packages` is still needed if you want to source into a foreign package on the fly. You will need to add a package into the development list and activate ess-developer. I often do that with core packages or packages which I don't want to clone.

whether we are in a typical package folder (R, src, man, etc). I'm currently adding a ess-developer-check-all-dirs

If possible, we need to strive for the same behavior on remotes and locals. Why is this needed? For ess-developer--get-package-path?

Would C-c d be a good prefix for r-devtools-map?

Not really. C-c LETTER bindings are reserved for users. We cannot use those.

@lionel-
Copy link
Member Author

lionel- commented Jan 26, 2016

If possible, we need to strive for the same behavior on remotes and locals. Why is this needed? For ess-developer--get-package-path?

Yes, you had a note in the code that checking all folders was slow on remotes. But it'd still be nice to check all folders when working locally.

But ess-developer-packages` is still needed if you want to source into a foreign package on the fly.

How about a (ess-developer-select-package) function that would wrap (setq-local ess-developer-local-package ...)?

@vspinu
Copy link
Member

vspinu commented Jan 26, 2016

How about a (ess-developer-select-package) function that would wrap (setq-local ess-developer-local-package ...)?

I am not sure what's the added benefit of it? It seems less general than the current state but I a open to suggestions. If we can streamline UI and simplify the code I would be happy to have it.

Just to be clear, the current situation is like follows. There is C-c C-t C-a runs the command ess-developer-add-package. You add a package on the fly with C-c C-t C-a and toggle developer mode with C-c C-t C-t. We can determine which package the function belongs to so we can have multiple packages in that list.

We can in principle merge those two steps in one. The user can be asked for package when the developer mode is turned on on a file which could not be associated with a package.

I don't use the code injection mechanism

My idea was to make code injection automatic for in-package code, even if the package is not listed in ess-developer-packages. devtools is good, but it's complementary to the original goal of ess-developer - to eliminate the distinction between script and in-package development. I think currently we do quite ok on that.

@vspinu
Copy link
Member

vspinu commented Jan 26, 2016

I have looked through the code. Looks good. Could you please merge it in?

Do you have other ideas on the short cut? We can in principle try to fit all devtools commands in C-c C-t. It will be hard but it's not impossible.

(ess-developer-send-process "devtools::unload('%s')\n"
"Unloading %s"))

(defun ess-developer-check-package ()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about having a prompt with completion on C-u? It would be nice to have various options for check: with/out manuals, vignetes, CRAN etc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... test() (normal) and the full check() (C-u) are the most common operations. Maybe have the prompt on C-u C-u? And then have check_revdep() on another keybinding.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or have three functions, test, check (prompt on C-u) and check_revdep, and we bind those on a dedicated keymap r-devtools-check-map.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My common operation is to check without the vignette. I poropose that we have an allist of possible check commands with the default being the first one in the list. It's one extra RET after C-u but it saves us a lot of trouble.

A major benefit is that users can add their own check commands with whatever additional build args they need.

It might be good to extract all devtools commands into user vars for UI consistency.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to extract all devtools commands into user vars for UI consistency.

On the other hand it's easy enough for a user to bind her own functions wrapping (ess-developer-send-process). Is it really necessary to complicate the code/UI?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid point, but that would also involve binding to a key. Let's see what we end up with. It's more about the consistent code than UI, IMO. It's not good to leave some commands with custom vars and others without.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the following consistent scheme:

  • C-u → alternative command
  • C-u C-u → main command with custom args

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so for checking:

  • nil → check()
  • C-u → check(vignettes = FALSE)
  • C-u C-u → check() with prompt

For loading:

  • nil → load()
  • C-u → load(recompile = TRUE)
  • C-u C-u → load() with prompt

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually meant:

- nil → load()
- C-u → load() with prompt

We place alternative command as the default in the list (press RET and get it). Otherwise the system becomes too complex. User has to remember all these C-u things and the alternative command. We would also need to add configuration both for the alternative (C-u) and the list (C-u C-u) of commands. Not all users care about Vignetes.

For one RET speedup it's not worth it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One problem is that the shortcuts will be too long: C-u C-c C-t C-t c. I am always forgetting C-u and typing the command twice. We can have the with prompt command on the shifted last key C-c C-t C-t C, so that when you get to the last key you will decide if you want the default "check" or prompted "check".

@lionel-
Copy link
Member Author

lionel- commented Jan 26, 2016

Do you have other ideas on the short cut? We can in principle try to fit all devtools commands in C-c C-t. It will be hard but it's not impossible.

I use my own shortcuts with evil-leader so I'm not sure. I think it's important to have good mnemonics so fitting everything on C-c C-t doesn't seem right.

@lionel-
Copy link
Member Author

lionel- commented Jan 26, 2016

We can in principle merge those two steps in one. The user can be asked for package when the developer mode is turned on on a file which could not be associated with a package.

Yes I think just setting the package in the file-local variable + smart package detection should be enough. Then we can simplify the UI and the code. But it's entirely your call :)

devtools is good, but it's complementary to the original goal of ess-developer

Yup that's what I also think.

@vspinu
Copy link
Member

vspinu commented Jan 26, 2016

One option is to hook the whole devtools on C-c M-t or C-c M-d.

Or obsolete C-c C-t C-t as we discussed above. This one as it's very fast to type.

I use my own shortcuts with evil-leader so I'm not sure.

So you rebind keys for all modes you use?

Always wanted to ask, have you switched from VIM to emacs?

@vspinu
Copy link
Member

vspinu commented Jan 31, 2016

BTW, developer mode is not really a mode, it's just an entry and exit hook which users can customize. It might be worth to make it a proper minor mode.

@lionel-
Copy link
Member Author

lionel- commented Jan 31, 2016

It might be worth to make it a proper minor mode.

Ok, I'll restore it as a minor mode. Sorry for the confusion.

I think it's a complication. In each file there could be only one current environment/package active. So having one buffer local variable to indicate the environment should be enough in principle.

But that means that injecting code to *current* would prevent us from using devtools without setting back to the package... That could lead to a lot of back-and-forth, which is why I think it's better to keep both separate.

@lionel-
Copy link
Member Author

lionel- commented Jan 31, 2016

Or we just have two commands, one for injecting to current env, the other for injecting to current package? This would work with a unique package variable while avoiding the problem I just mentioned.

@vspinu
Copy link
Member

vspinu commented Jan 31, 2016

Ok, I'll restore it as a minor mode. Sorry for the confusion.

It wasm't a minor mode before. No need to restore. Let's think what's the best and most concise way to achieve what we want.

But that means that injecting code to current would prevent us from using devtools without setting back to the package..

Right. That's a problem.

Or we just have two commands, one for injecting to current env, the other for injecting to current package?

I actually was thinking to extend this functionality and to allow sourcing code into any visible user defined environment. Maybe having two variables is indeed unavoidable.

It will be hard to display both current dev package and current environment in the modeline.

Ok. Let sleep on these things. I started going through the code and making small changes here and there.

Are you working on the right now? Would be good not to clash.

@vspinu
Copy link
Member

vspinu commented Jan 31, 2016

BTW, with new names the mnemonics for shortcuts are lost. It feels awkward quite frankly.

@lionel-
Copy link
Member Author

lionel- commented Jan 31, 2016

Are you working on the right now?

No, I have a deadline tomorrow, and will be on vacation for a week after that.

BTW, with new names the mnemonics for shortcuts are lost

You mean for C-c C-t C-a / C-c ?

@vspinu
Copy link
Member

vspinu commented Jan 31, 2016

No, I have a deadline tomorrow, and will be on vacation for a week after that.

Ah. Ok. Then go to work. Don't bother with this. It can wait.

You mean for C-c C-t C-a / C-c ?

Yes. Also C-c C-t C-t xx is mind blowing. I find myself forffgetting how many times I pressed C-t and already confusing C-t and C-t C-t map. I think it's a bad key :(

@lionel-
Copy link
Member Author

lionel- commented Jan 31, 2016

well it was your suggestions ;) I think of C-a as "Adding" a package to the file-local vars section. But yes C-c is good for injecting to current but not for package. I had these on C-j initially, which I somehow find a good mnemonic for injection, but that could be just me.

Also C-c C-t C-t xx is mind blowing

If we don't find a better key, we should at least suggest in the documentation that devtools users should bind the keymap to C-c d or something like that.

Then go to work.

Aye :)

@vspinu
Copy link
Member

vspinu commented Jan 31, 2016

well it was your suggestions ;)

Yeh, and it sucks. My hope was that it won't surprise existing users. But now it looks to me that the functionality is so different that this argument doesn't hold anymore.

BTW, why do we need to set the package once again? I think we can do just fine without. Then have one command to set an injection environment in a broad sense (either a package or current environment or user defined environment).

@lionel-
Copy link
Member Author

lionel- commented Jan 31, 2016

BTW, why do we need to set the package once again?

It's for files outside a package. I often have a scratch file for working on packages and so it's nice to have devtools commands always work correctly while I'm visiting that file. So C-c C-t C-a adds the information to the file-local section of that file. But it should be a rarely used command.

@vspinu
Copy link
Member

vspinu commented Jan 31, 2016

For this use pattern we can do without the second command. You can select the source environment for that buffer. If environment is a package, we activate the developer mode there. Problem solved.

We will need a real developer mode in order to automatically activate devtools and some parts of ESS functionality in C/RCpp and other relevant files.

@lionel-
Copy link
Member Author

lionel- commented Jan 31, 2016

For this use pattern we can do without the second command. You can select the source environment for that buffer. If environment is a package, we activate the developer mode there. Problem solved.

Hmm I'm not sure I get it. The point is to set what package should be used for a given file or directory once and for all.

@lionel-
Copy link
Member Author

lionel- commented Jan 31, 2016

Btw, I have other good stuff that I will integrate in the future, like automatic detection of R process for gud debugging.

@vspinu
Copy link
Member

vspinu commented Feb 1, 2016

Hmm I'm not sure I get it. The point is to set what package should be used for
a given file or directory once and for all.

If you are developing from external file you are likely to want to source in the
package, no? What's the point of developing from outside? Even if you don't want
to develop, injection happens only for known objects. All others are assigned
into GlobalEnv. So it's hardly makes any difference. I think it's a small price
to pay for avoiding duplicated vars and user level commands.

Btw, I have other good stuff that I will integrate in the future, like
automatic detection of R process for gud debugging.

What would it bring over the tracebug? You cannot go much further existing
facilities in R, and tracebug already exploits almost all of those.

It's not really a visual debugger, but I don't know much about it. It's not well
documented.

@lionel-
Copy link
Member Author

lionel- commented Feb 2, 2016

Even if you don't want to develop, injection happens only for known objects

I didn't know that. Nice!

So it's hardly makes any difference.

I still think that such side-effecty functions should be optional. One user var does not cost a lot. In my current workflow I rely entirely on devtools — load_all(), install(), and the occasional restart of R session. I'll try code injection the next time I work on a package but I think it should be optional.

What would it bring over the tracebug?

I meant for debugging compiled C/C++ code ;)

@vspinu
Copy link
Member

vspinu commented Feb 3, 2016

One user var does not cost a lot. I

Indeed. But one more confusing comand does. And I am not sure if your use case is worth it. It doesn't make much sense to use load_all() and install on a package unless you are developing that package.

One easy solution would be to prompt for package in load_all when you are outside of the package. It would cost you one RET per load, but would result in a cleaner code and UI.

@lionel-
Copy link
Member Author

lionel- commented Feb 3, 2016

It doesn't make much sense to use load_all() and install on a package unless you are developing that package.

It makes sense that, when you trigger load_all() or any devtool command in a file belonging to a package, it uses automatically the relevant package path. I use those devtools commands like 50 times a day (if not a 100), I don't want to enter RET each time when there is no need...

But I'm not sure I understand what you're saying concretely. If I open a package file, I'm developing that package indeed.

One easy solution would be to prompt for package in load_all when you are outside of the package. It would cost you one RET per load, but would result in a cleaner code and UI.

Specifying one buffer-local var does not make the UI dirty. Minimalism has its virtues but at the same time Emacs needs to be flexible and do the right action with minimal hassle.

@vspinu
Copy link
Member

vspinu commented Feb 3, 2016

I don't want to enter RET each time when there is no need...

I was not proposing that.

But I'm not sure I understand what you're saying concretely. If I open a package file, I'm developing that package indeed.

I was reffering to your corner case example when you said you want to have devtools in a file which doesn't belong to a package. I said that's rare and there is no point to have a separate command to set some local var just for that corner case.

@lionel-
Copy link
Member Author

lionel- commented Feb 7, 2016

I said that's rare and there is no point to have a separate command to set some local var just for that corner case.

In my workflow it's not a corner case. I have one such file (sometimes several) for every package I develop / contribute to.

It's not a big deal not to have the command, but it's not a big deal providing it either. Maybe just don't provide a keybinding for it?

@vspinu
Copy link
Member

vspinu commented Feb 7, 2016

After sleeping on it a bit, I think you are right. We won't be able to isolate these two things. They are quite distinct conceptually, and accessing devtools need not even be from the R sources.

This is how I am seeing it right now. First we need a minor mode ess-developer-mode. We will activate this mode in all files inside an R package and will place all the functionality which is meaningful ouside R source on ess-developer-mode-map. We can use the prefix that you proposed - C-c C-w. The injection functionality makes sense in R buffers and is tightly connected with evaluaiton mechanism, so let's keep it on C-c C-t.

I propse to rename it to ess-set-source-environment and hook it on C-c C-t C-s. It's easier to type.

The package selection command could be named ess-set-developer-package and maybe hooked C-c C-w C-s in ess-devloper-mode-map. This way C-s will be responsible for similar actions in these two maps.

What do you think?

@lionel-
Copy link
Member Author

lionel- commented Feb 8, 2016

This sounds good, especially binding the map within the minor mode. We'll have the map activated inside Rcpp files, which is a huge improvement.

Where would we bind the devtools map? C-c C-w C-d? Or do you want to integrate all devtools commands in ess-developer-map? I see two problems with the latter option: devtools command are R only; and we already have a lot of devtools commands and we're not even finished integrating them all.

Otherwise this all sounds great.

@vspinu
Copy link
Member

vspinu commented Feb 8, 2016

Where would we bind the devtools map? C-c C-w C-d

All devtools will be on C-c C-w directly. They operate per package not per file. What do you mean by "devtools command are R only"? If you are concerned about non R dialects in ESS, then we can call it ess-R-developer-mode. ess-developer is R only anyways.

and we already have a lot of devtools commands and we're not even finished integrating them all.

They are a lot because you needlessly made two commands for each task. Once we have one command per task, we will be fine. We have full map available; it should be doable with good mnemonics.

I think there will be very few non devtools commands on that map.

@lionel-
Copy link
Member Author

lionel- commented Feb 8, 2016

They are a lot because you needlessly made two commands for each task.

I doubled the commands on your request because you didn't like to use C-u ....

If you are concerned about non R dialects in ESS, then we can call it ess-R-developer-mode. ess-developer is R only anyways

Or ess-r-developer-mode as we agreed above to use non-capitalised r prefix.

@vspinu
Copy link
Member

vspinu commented Feb 8, 2016

didn't like to use C-u ....

I think it was a miscommunication. I proposed to put optional forms of the command on C-u. C-u should ask for alternative arguments with completion. Not like now, when only one alternative is executed.

Or ess-r-developer-mode

A bit of an inconvenience is that we will have ess-dev-map and ess-r-developer-mode-map. This is confusing. Better names for these? How about ess-r-package-dev-map and thus ess-r-package-dev-mode?

@lionel-
Copy link
Member Author

lionel- commented Feb 8, 2016

I proposed to put optional forms of the command on C-u. C-u should ask for alternative arguments with completion.

No, that's how it worked before. Since the map prefix was a bit long (C-c C-t C-t), you found it inconvenient to have alternative prompts on C-u.

How about ess-r-package-dev-map and thus ess-r-package-dev-mode?

I like these.

@lionel-
Copy link
Member Author

lionel- commented Feb 8, 2016

Maybe the shorter ess-r-package-mode is good enough?

@vspinu
Copy link
Member

vspinu commented Feb 8, 2016

ess-r-packdev-mode?

@vspinu
Copy link
Member

vspinu commented Feb 8, 2016

I think ess-r-package-mode is not suggestive enough. package could mean anything.

@lionel-
Copy link
Member Author

lionel- commented Feb 8, 2016

ess-r-packdev-mode

I prefer your former suggestion then, ess-r-package-dev-mode. But will we have to prefix all functions like this? In this case we're better off with a short prefix like ess-r-dev-.

@vspinu
Copy link
Member

vspinu commented Feb 8, 2016

In this case we're better off with a short prefix like ess-r-dev-.

I thought to keep ess-r-devtools prefix for functions even if they reside in package-dev map. We already have ess-dev-map with heterogeneous functions in it. I think having ess-r-dev-map would be too confusing to users who would like to re-assign keys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants