mclear-tools / tabspaces Goto Github PK
View Code? Open in Web Editor NEWLicense: GNU General Public License v3.0
License: GNU General Public License v3.0
Hello. I'm very interested in this project as the goals line up well with my own attempts to minimize large external projects and make better use of emacs built-in functionality.
I just forked the repository to add a nix flake for my own consumption (since this is not present in MELPA). When writing the flake, I realized that I had no idea what the license for this project is, which leaves me in a bit of a pickle as, while I believe you intend for this to be available for public consumption and modification, not choosing a license means you have withheld exclusive copyright.
Picking a license is your decision as the maintainer. I do not have much of an opinion but, emacs being a GNU project, the obvious choice is the GPLv3. I would suggest having a read over https://choosealicense.com and picking one. There are a number of available options, but this site does a great job of presenting the options in a concise and clear manner.
The command tab-switch-or-create
adds the current buffers to the the new workspace when creating a new workspace. I expect it to create a new workspace with no buffers attached to it or maybe it should be a separate command like tabspaces-create-empty-workspace
.?
I didn't really know if I could figure out where in the code Tabspaces gets its current behavior since I don't know Emacs Lisp that well. But then I remembered the words of Kant that "I have no knowledge of myself as I am, but merely as I appear to myself." and tried anyway.
I noticed that if I follow these steps:
tabspaces-open-or-create-project-and-workspace
project--list
(it has to be a project that doesn't have an existing tabspace yet)... then Tabspaces will always run project-find-file
. This behavior is hardcoded here: https://github.com/mclear-tools/tabspaces/blob/main/tabspaces.el#L392
I was wondering - would it make sense to allow users to provide their own function in place of project-find-file
? That way, they could have e.g. magit-status
be called instead of project-find-file
.
Off the top of my head, the customization could come in the form of a new tabspaces-project-switch-commands
variable in place of the hardcoded project-find-file
.
On the other hand, it could potentially be more beautiful if project-switch-commands
were never set to just project-find-file
, and the unchanged value of project-switch-commands
was used (so the project.el minibuffer prompt appears asking "what do you wanna do with this"). That way, the customization would just come from the default behavior of project.el
This is different from how Emacs does buffer switching by default, which is to not list the current buffer in the list of candidates at all. This screenshot shows the behavior I'm describing. The default local buffer to switch to should not be the current buffer, but instead (in this case) it should be cus-edit.el.gz
instead.
Once #4 is resolved, I believe that this project would see a lot more usage if it were submitted to melpa (rather than requiring straight or manual cloning). I have not yet looked deeply into how to achieve this, but I do know that there will need to be a recipe created and a maintainer for that recipe selected. MELPA's repo suggests that the emacs package author take over the MELPA recipe author role for their particular packages.
If there's anything I can do to help in this effort, please don't hesitate to ask. Thank you.
tabspaces-switch-to-or-create-workspace
invokes both tabspaces-create-workspace
and tab-bar-new-tab
, which creates two tabs.
Hi, great package!
If I'm in a tabspace and I open a buffer that's already opened in another tabspace, and I've specified I want to switch to its tab (using tabspaces-switch-buffer-and-tab
), I expect the buffer not to be added to the current tab as well - or to be prompted if I want to deliberately add it.
With time, this totally defeats the point of segregating buffers between tabs, in my opinion.
Thanks!
I just installed tabspaces and set tabspaces-session
and tabspaces-session-auto-restore
in my configuration. Upon starting up a new session with emacs --debug-init
, I get the following error:
Debugger entered--Lisp error: (file-missing "Cannot open load file" "No such file or directory" "/home/cdom/.cache/ceamx/var/tabspaces-session.eld")
load-file("/home/cdom/.cache/ceamx/var/tabspaces-session.eld")
tabspaces--restore-session-on-startup()
run-hooks(emacs-startup-hook term-setup-hook)
#f(compiled-function () #<bytecode -0x8b2bbf2256dd1d2>)()
normal-top-level()
Hello,
Let's say that I have two projects at:
~/ws/proj1
~/ws/oldstuff/proj1
Opening the first project with tabspaces
will create a proj1
tab. When opening the second one, it will consider that the project doesn't change and uses the same proj1
tab.
As the directories aren't the same, it should rather create another tab with something like proj1<1>
or oldstuff/proj1
.
Thank you for this package!
Regards
Using the functions desktop-save and desktop-revert (or with desktop+, desktop+-create and desktop+-load) doesn't save the complete buffer lists isolated within workspaces. It does save the workspaces created across frames, and the visible buffers each workspaces--it just fails to save the list of buffers that are not opened. Additionally, each succeeding workspace includes the buffer in the previous workspaces, even if they were not included before saving the desktop.
This is what happens (the first/leftmost buffer is the visible buffer):
Before using desktop-save and desktop-revert:
After using desktop-save, quitting emacs, and using desktop-revert:
Is integration with desktop saving planned or possible? Since tabspaces work with builtin packages. I've tried it with burly.el too and similar problems persist.
--
Another thing, though I am not sure if I must open another issue for it: tab-bar-detach-tab and tab-bar-move-tab-to-frame has issues such that when a workspace is moved to another frame, deleting it with tabspaces-kill-buffers-close-workspace still acts as if it wasn't moved to another frame, and using the function repeatedly removes the buffers in the workspaces in the frame where the moved workspace came from.
This is related to #42
In the issue, the reporter says:
Also, the tabspaces--generate-unique-tab-name function causes opening the same project twice to create two tabs with names "project" and "project<1>".
And Colin replied:
Note though that the duplicate tab function is a feature not a bug. Some folks might want two different workspaces for the same project.
While the change supports a valid usecase, I miss how tabspaces-open-or-create-project-and-workspace
could've been used mindlessly, without having to think if the tabspace is already open. Sure, you can use C-C TAB s
now instead, but as I said, the "mindlessness" was a nice bonus imo. I frequently have so many tabspaces open that it's not immediately clear if the tabspace is already there or not.
Here are some ideas:
I was wondering if it'd be possible to have two modes of operation for tabspaces-open-or-create-project-and-workspace
. If a prefix C-u (https://www.emacswiki.org/emacs/PrefixArgument) is used, use the new "duplicate tabspace if already existing". If no prefix, use old "switch to tabspace if already exists". So: C-u C-c TAB o -> duplicate tabspace always; C-c TAB o -> switch to tabspace if it exists
(nitpick) If you prefer the current behavior to stay, the docstring at https://github.com/mclear-tools/tabspaces/blob/main/tabspaces.el#L399 should be changed to say that the function will duplicate the tabspace if it exists, not switch to it. I can do that if you want
This may be a case of https://xkcd.com/1172/, so if I'm being a baby about it and should now just use C-c TAB s instead of C-C TAB o I'll oblige :P
(defun tabspaces-ivy-switch-buffer ()
"Switch to another buffer in the current tabspace."
(interactive)
(ivy-read "Switch to buffer: " #'internal-complete-buffer
:predicate (when (tabspaces--current-tab-name)
(let ((local-buffers (tabspaces--buffer-list)))
(lambda (name-and-buffer)
(member (cdr name-and-buffer) local-buffers))))
:keymap ivy-switch-buffer-map
:preselect (buffer-name (other-buffer (current-buffer)))
:action #'ivy--switch-buffer-action
:matcher #'ivy--switch-buffer-matcher
:caller 'ivy-switch-buffer))
This is a straight clone of ivy-switch-buffer
, with added predicate that only allows buffers from the current tabspace. The :caller
is left as 'ivy-switch-buffer
to let Ivy do its thing of appending dead buffers from history to the list; it would be neat to limit those to the current tabspace's history also but I have no idea how.
But of course it uses Ivy implementation details, so there's that.
Hey there!
Thanks a lot for creating tabspaces. I wanted to ask if there is any interest from your side to support projectile?
Projectile is pretty big part of my personal Emacs config and I was looking into tabspaces source which changes would need to be made in order to use projectile instead of project.el.
For example, there could be a variable like
(defcustom tabspaces-projectile-integration nil
"Whether to use projectile instead of project.el.
This variable must be set before tabspaces is loaded."
:type 'boolean
:group 'tabspaces)
which then controls how projects are switched, etc.
If yes, I can prepare a PR as soon as I am done.
Forgive me, as I'm not an Emacs Lisp coder (Common Lisp actually), but I find some things possibly wrong with the cond
form here
(vc-root-dir)
could be lexically bound as to not call it twice.vc-call-backend
branch seems to not have a value and is returning nil
?t
branch is returning the same string as the previous branch, making the previous branch un-necessary?Hi,
I am trying to create a function which switches to the previously opened tabspace.
Suppose I have multiple tabspaces open (say, A,B,C). I am in A, and then navigate the tabspaces in the sequence A -> B -> C. I would like to make a function that switches from the current tabspace (C) to the previous one (B). Invoking this function twice in succession from C, would bring me back to C.
Any tips would be awesome. Sorry, this is not an issue, but I didn't know where else to reach out.
Cheers
Hello and thank you for your responsiveness on issue #38
The commit e937743 broke the workflow! Now, calling tabspaces-open-or-create-project-and-workspace
on existing projects asks each time for repo initialization as Git, Hg, ...
Also, the tabspaces--generate-unique-tab-name
function causes opening the same project twice to create two tabs with names "project" and "project<1>".
I think the commit should be revoked until this gets fixed, in my config, I'm explicitly using the commit before this one.
I will try to work on this the next weekend and open a PR.
Thank you again for this awesome package!
There does not appear to be a way to opt out of the automatic binding of tabspaces-command-map
to tabspaces-keymap-prefix
without causing a warning message to appear. I set tabspaces-keymap-prefix
to nil, which should prevent the binding, but then I get the following warning:
⛔ Warning (emacs): Value ‘nil’ does not match type string
I'm not sure how to get a trace for this message, however, since it is not an error. As a workaround, I've just set the prefix to a key inside my custom keymap.
Hi,
When I try to open a project tab space (C-c TAB o
) for the first time after loading emacs, it gives me a prompt to do a couple things including opening a file, but then when I open a file from that menu, I get an error and the file gets opened in the default tabspace.
Debug trace (replaced file name with FILE):
Debugger entered--Lisp error: (wrong-type-argument sequencep #<buffer FILE>)
call-interactively(tabspaces-open-or-create-project-and-workspace nil nil)
command-execute(tabspaces-open-or-create-project-and-workspace)
After this happens, going through the steps again works just fine (I run C-c TAB o
, select my project, it automatically opens the file picker, selected file then gets moved into a new tabspace).
Recently, when opening a project in a subdirectory of a git repo (when customising project-vc-extra-root-markers
to find projects in a monorepo), a new git repo is initialized in the project that then has to be deleted for magit to behave as intended.
Ideally it would be possible to customise this behaviour.
So whenever I want to switch to a project using tabspaces-open-or-create-project-and-workspace
I would like it to auto restore the session. But currently that is not happening.
tabspaces-mode
seems to be enabled on startuptabspaces-restore-session
loads the buffers I expectopen-or-create-project-and-workspace
I only get a find file
pop up to find files in the project.My current config for tabspaces using elpaca:
(use-package tabspaces
:ensure t
:hook (emacs-startup . tabspaces-mode)
:commands (tabspaces-switch-or-create-workspace
tabspaces-open-or-create-project-and-workspace)
:custom
(tabspaces-keymap-prefix "C-c p")
(tabspaces-use-filtered-buffers-as-default t)
(tabspaces-include-buffers '("*scratch*"))
(tabspaces-default-tab "Home")
;; sessions
(tabspaces-session t)
(tabspaces-session-auto-restore t))
(provide 'init-project)
Any insights in to how to integrate ivy?
Thanks for this package Colin! I've recently made the switch to using tabs (rather than perspectives), and I really like the simplicity of the package.
I'm sharing the code below as it might be useful to others. Hopefully this is the right place to do so.
When I have many tabs open, I like to switch between them quickly. One way to do this is via frog-menu
which pops up a posframe and uses avy keys to select a candidate. More on frog-menu here: https://elpa.gnu.org/packages/frog-menu.html
In short: the code below allows to (1) pop-up a list of tabs in a posframe, and (2) switch to a tab by typing a single key (based on the first letter of the key). It takes inspiration from https://github.com/waymondo/frog-jump-buffer
(require 'frog-menu)
(defun efls/frog-tab ()
"Pick a tab to switch to via `frog-menu'."
(interactive)
(let* ((frog-menu-avy-padding t)
(frog-menu-min-col-padding 1)
(frog-menu-display-option-alist
'((avy-posframe . posframe-poshandler-frame-center)
(avy-side-window display-buffer-in-side-window (side . bottom))))
(prompt "Switch tab")
(tabs (mapcar (lambda (tab) (alist-get 'name tab))
(tab-bar-tabs)))
(frog-menu-avy-keys (efls/frog-tabs-generate-keys tabs))
(actions '(("C-l" "List tabs"
(call-interactively tabspaces-switch-or-create-workspace))))
(res (frog-menu-read prompt tabs actions)))
(if (stringp res)
(progn (message res)
(tabspaces-switch-or-create-workspace res))
(apply res))))
Generating the keys for switching perspectives is done via a separate function.
(defun efls/frog-tabs-generate-keys (tabs)
"Generate keys for TABS (a list of tab names).
Returns a list of first chars from each tab. If two chars are
identical, make second capitalized. If more chars than two are
identical, then... nothing else."
(let ((new-chars))
(dolist (char (-map (lambda (s) (string (string-to-char s)))
tabs))
(if (member char new-chars)
(setq new-chars (append new-chars (list (capitalize char))))
(setq new-chars (append new-chars (list char)))))
(-map 'string-to-char new-chars)))
It works as follows:
There are potential issues:
I hope I'm not mistaken here but:
Testing out tabspaces-switch-or-create-workspace doesn't seem to create any new 'workspaces'. I took a look at tabspaces.el, and it is aliased to tab-bar-switch-to-tab. Using that function doesn't create new tabs too, and instead just tries to switch tabs despite no tabs existing.
However, I do see a separate function tab-bar-new-tab / tab-bar-new-tab-to, which creates new tabs. Using the tabspaces-switch-or-create-workspace / tab-bar-switch-to-tab functions switches between the created new tabs.
Am I missing something here? Is the function supposed to work for both switch and creation of 'workspaces'? Thank you so much.
Not sure if this is already possible somehow, but it would be awesome if we could use ibuffer, and simply jump to the correct tab by executing a command on a buffer from the ibuffer list.
You can do this manually with tabspaces-switch-buffer-and-tab
, but this does not seem to be integrated into ibuffer (yet).
Hi! Thank you for the great plugin. It's exactly the simplicity I was looking for.
I'm only missing one thing, and that is a command/function that searches through the current project(starting from the defined root and N folders deep), while respecting .gitignore.
I don't think this needs to be a function for tabspaces, but I'm new in Emacs-land and I was wondering what would be the best way.
I found that calling bury-buffer
on a buffer will also remove that buffer from the current tab ((tabspaces--buffer-list)
). Is this a bug or by design?
Feature request adding to #18.
I find that sometimes I'll only have a project-eshell buffer or dired buffer open from doing C-x p c
or C x p p RET
.
This means when I restore a tabspace there's not really a project open anymore ebcause those buffers aren't restored.
I then have the mild inconvenience of having to do C x p p
to select the project again and reopen the dired or eshell buffer.
This request doesn't include restoring eshell output or anything, just essentially adding "this project had only an eshell buffer open so we'll do C-x p c
on tab restore in that directory" logic.
Hello,
I didn't try your package yet but after skimming through the readme it seems it's pretty much for projects only.
I use Gnus and ERC, is it possible to isolate buffers related to these two modes with your package?
Thanks.
I'm just trying to figure out if this package will work for me and wanted to run a couple of scenarios by you.
I'm working in a project in one tab. I realize I need to do something in another project. I select the relevant buffer or file with tabsapces-switch-to-buffer
(can I even do that?). Does the buffer open for me in the appropriate tab for its own project?
I have one tab that I use for mail, using mu4e. mu4e creates a bunch of its own buffers, and I also have a dashboard buffer on the side (using mu4e-dashboard
). When I create the workspace for this tab, can I specify that all of those buffers should be included in the workspace filtered buffer list? That is, can I specify my own pseudo-project that holds things like mail, rss feeds, etc?
Maybe the answers to these questions are obvious, but I didn't immediately see how to do them when I looked at the code.
Thank you!
you wrote "Calling the minor-mode tabspaces-mode sets up newly created tabs as buffer-isolated workspaces using tab.el in the background". what is a 'buffer-isolated workspace'?
Once you have created a tabspace, is there a way to rename it? Specially, I would want to rename the first tabspace created in a session.
It tends to stay with the name of the first buffer within it, while on creating the rest of tabspaces I can pick a name that is meaningful to the tasks I intend to solve in it.
I have been using this excellent package daily for around a year, but I eventually need to stop to clean up tabs because they end up with unrelated buffers. Today, I started investigating a bit more and realized it happens when I'm quickly alternating between buffers and tabs:
If I do:
The buffer list will be loaded from the old tab, but the switch-to-buffer will happen in the new tab.
For sure, this is user error. But can we make the interface a bit more friendly?
ideally, the minibuffer would be per-tab, but that's quite hard/impossible. A simpler solution would be exiting the minibuffer when switching tabs, but that is not very emacsy. I think we could at least detect the tab changed during tabspaces-switch-to-buffer and error out. Alternatively, we could force the switch to happen in the old tab/window where the command was initiated. Thoughts?
Hi, thanks for the great package! I am enjoying it, and it was integrated into Centaur Emacs.
I have a quick question: How to display the "Messages" buffer in all workspaces? I tried customizing tabspaces-include-buffers
but seems not working. What was I missing?
I have submitted a PR to the no-littering emacs package to include configuration of tabspaces' tabspaces-session-file
variable.
No-littering package is an attempt to keep user-emacs-directory
and source directories tidy by assigning standard locations for data and config files. The files and directories are also expected to follow certain naming conventions.
In addition to informing you, I hope you can confirm that this is the only data file saved by tabspaces.
Thanks!
PS: A discussion thread would have been more appropriate for this. Please feel free to close this issue.
Hi, Thanks for this great package, this is so useful!
I wonder if there is way to create workspaces based on modes? For example, besides from having projects in different workspaces, I would also like to keep all help buffers on a different workspace, and all magit buffers on another workspace.
Hi,
I get the following when installing trough package-install
:
In tabspaces-switch-buffer-and-tab:
tabspaces.el:322:25: Error: ‘add-to-list’ can’t use lexical var ‘tabcand’; use
‘push’ or ‘cl-pushnew’
although somehow installation proceeds..
Hello @mclearc
The issue I've reported previously in #38 is raising again, I think after this commit a260cd8.
I think the idea behind the above commit is good (no duplicate tabs for the same project unless intended). However, it fails to distinguishes (given it uses only tab names) between two projects with the same name (with different paths), and two instances of the same project (with the same path).
Lines 402 to 409 in beab193
I think it would be more logical to store (somehow) the absolute path for each project of each tab. I don't know if there is an obvious way to add tab-local variables (like buffer local with defvar-local
and setq-local
), but I think this is the right way to do it (instead of relaying only on tab names).
Would it make sense to advise tab-bar-new-tab
, rather than providing a separate tabspaces-create-workspace
function? If that were done, then things like tab-bar-switch-to-tab
could be used out of the box, rather than tabspaces-switch-to-or-create-workspace
.
When saving / restoring tab details using tabspaces-save-session
and tabspaces-restore-session
, tab-group information does not seem to be handled at the moment. I started using desktop.el to save the frame setting instead, which does store the tab setup correctly including tab-group, but I won't get the correct buffer mapping with tabspaces.
What would be the approach I could take to restore all tabs and tab groups, while also keeping the buffers associated to tabs correctly? While both tabspaces-save-session
and tabspaces-restore-session
work pretty well on their own, is there any way to get them to work with desktop.el?
After 929e677, the default behavior of creating new tabs has changed. When creating a new tab, all the buffers in current tab would be kept in the new tab as these buffers are not in tabspaces-exclude-buffers
.
Lines 133 to 138 in beab193
and
condition with line 137-138?This is just a question, but could you perhaps explain what each function does? I can't tell what the difference is between these two functions from their names or by invoking them...they both seem to have the same effect for me, unless I'm missing something. It might be a good idea to write descriptions in the README, or to word the documentation a bit better to avoid any confusion. Thanks!
Hi, we can now filter buffers under specific tab. Taking a different approach, can we also have an option to switch corresponding tab automatically when switching between buffers?
With a workspace containing 3 buffers, space.lisp
, conditions.lisp
, and *scratch*
, this function loops infinitely until Emacs crashes.
I am not that great at debugging elisp code, but writing the current buffer name and the return values of kill-buffer
to a file results in the following until Emacs hard-crashes:
space.lisp t
space.lisp t
*Minibuf-1* t
space.lisp t
*Minibuf-1* t
conditions.lisp t
space.lisp t
*Minibuf-1* t
conditions.lisp t
*scratch* t
It seems that even though the buffer is successfully killed, it keeps coming back.
I am using project.el, and (project-kill-buffers t)
works flawlessly, and also seems to close the tab when the last buffer is killed, so I'm using that for now, as I couldn't debug the problem any further.
tabspaces-create-workspace
will include the current buffer in the newly created workspace. This means that tabspaces-open-existing-project-and-workspace
includes it as well. In my custom command, I have to remove it with tabspaces-remove-selected-buffer
:
(defun aj/tabspaces-switch-project (project-to-switch)
"Switch to project tab and find project file.
Only if the switched to buffer is not of that project."
(interactive (list (project-prompt-project-dir)))
(let* ((tab-names (mapcar (lambda (tab) (alist-get 'name tab)) (funcall tab-bar-tabs-function)))
(project-name (aj/project-name project-to-switch))
(tab-name project-name)
new-tab)
(if (member tab-name tab-names)
(tab-bar-select-tab-by-name tab-name)
(setq new-tab t)
(tabspaces-create-workspace)
(tab-bar-rename-tab tab-name))
(unless (string= project-name (aj/project-name))
(let ((default-directory project-to-switch)
(previous-buffer (current-buffer)))
(project-find-file)
(when new-tab
(tabspaces-remove-selected-buffer previous-buffer))))))
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.