Code Monkey home page Code Monkey logo

tabspaces's Introduction

Tabspaces

GNU Emacs MELPA Buy Me A Coffee

Tabspaces leverages tab-bar.el and project.el (both built into emacs 27+) to create buffer-isolated workspaces (or “tabspaces”) that also integrate with your version-controlled projects. It should work with emacs 27+. It is tested to work with a single frame workflow, but should work with multiple frames as well.

While other great packages exist for managing workspaces, such as bufler, perspective and persp-mode, this package is less complex than those alternatives, and works entirely based on the built-in (to emacs 27+) tab-bar and project packages. If you like simple, this may be the workspace package for you. That said, bufler, perspective or persp-mode, etc. may better fit your needs.

NOTE: version 1.2 renames several functions and streamlines tab and project creation. Apologies if this breaks your workflow. Please update your configuration accordingly.

Basic Usage

Calling the minor-mode tabspaces-mode sets up newly created tabs as buffer-isolated workspaces using tab.el in the background. Calling tabspaces-mode does not itself create a new tabbed workspace.

Switch or create workspace via tabspaces-switch-or-create-workspace. Close a workspace by invoking tabspaces-close-workspace. Note that these two functions are simply wrappers around native tab-bar commands. You can close a workspace and kill all buffers associated with it using tabspaces-kill-buffers-close-workspace.

Open an existing version-controlled project in its own workspace using tabspaces-open-or-create-project-and-workspace. If no such project exists it will then create one in its own workspace for you.

See workspace buffers using tabspaces-switch-buffer (for consult integration see below), which will only show buffers in the workspace (but list-buffers, ibuffer, etc. will show all buffers). Setting tabspaces-use-filtered-buffers-as-default to t remaps switch-to-buffer to tabspaces-switch-to-buffer.

Adding buffers to a workspace is as simple as opening the buffer in the workspace. Delete buffers from a workspace either by killing them or using one of either tabspaces-remove-selected-buffer or tabspaces-remove-current-buffer. Removed buffers are still available from the default tabspace unless the variable tabspaces-remove-to-default is set to nil.

NOTE that other than tabbed buffer isolation for all created window tabs this package does not modify tab-bar, tab-line, or project in any way. It simply adds convenience functions for use with those packages. So it is still up to the user to configure tabs, etc., however they like.

Here are some screenshots of tabspaces (with my lambda-themes) and using consult-buffer (see below for instructions on that setup). You can see the workspace isolated buffers in each and the tabs at top:

screenshots/tab-notes.png

screenshots/tab-emacsd.png

Installation

You may install this package either from Melpa (M-x package-install tabspaces RET) or by cloning this repo and adding it to your load-path.

Setup

Here’s one possible way of setting up the package using use-package (and straight, if you use that).

(use-package tabspaces
  ;; use this next line only if you also use straight, otherwise ignore it. 
  :straight (:type git :host github :repo "mclear-tools/tabspaces")
  :hook (after-init . tabspaces-mode) ;; use this only if you want the minor-mode loaded at startup. 
  :commands (tabspaces-switch-or-create-workspace
             tabspaces-open-or-create-project-and-workspace)
  :custom
  (tabspaces-use-filtered-buffers-as-default t)
  (tabspaces-default-tab "Default")
  (tabspaces-remove-to-default t)
  (tabspaces-include-buffers '("*scratch*"))
  (tabspaces-initialize-project-with-todo t)
  (tabspaces-todo-file-name "project-todo.org")
  ;; sessions
  (tabspaces-session t)
  (tabspaces-session-auto-restore t))
  

Keybindings

Workspace Keybindings are defined in the following variable:

(defvar tabspaces-command-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "C") 'tabspaces-clear-buffers)
    (define-key map (kbd "b") 'tabspaces-switch-to-buffer)
    (define-key map (kbd "d") 'tabspaces-close-workspace)
    (define-key map (kbd "k") 'tabspaces-kill-buffers-close-workspace)
    (define-key map (kbd "o") 'tabspaces-open-or-create-project-and-workspace)
    (define-key map (kbd "r") 'tabspaces-remove-current-buffer)
    (define-key map (kbd "R") 'tabspaces-remove-selected-buffer)
    (define-key map (kbd "s") 'tabspaces-switch-or-create-workspace)
    (define-key map (kbd "t") 'tabspaces-switch-buffer-and-tab)
    map)
  "Keymap for tabspace/workspace commands after `tabspaces-keymap-prefix'.")

The variable tabspaces-keymap-prefix sets a key prefix (default is C-c TAB) for the keymap, but this can be changed to anything the user prefers.

Buffer Filtering

When tabspaces-mode is enabled use tabspaces-switch-to-buffer to choose from a filtered list of only those buffers in the current tab/workspace. Though nil by default, when tabspaces-use-filtered-buffers-as-default is set to t and tabspaces-mode is enabled, switch-to-buffer is globally remapped to tabspaces-switch-to-buffer, and thus only shows those buffers in the current workspace. For use with consult-buffer, see below.

Switch Tabs via Buffer

Sometimes the user may wish to switch to some open buffer in a tabspace and switch to that tab as well. Use (=tabspaces-switch-buffer-and-tab) to achieve this. If the buffer is open in more than one tabspace the user will be prompted to choose which tab to switch to. If there is no such buffer user will be prompted on whether to create it in a new tabspace or the current one.

Persistent Tabspaces

Rudimentary support for saving tabspaces across sessions has been implemented. Setting tabspaces-session to t ensures that all open tabspaces and file-visiting buffers are saved. These may either be restored interactively via (tabspaces-restore-session), non-interactively via (tabspaces--restore-session-on-startup), or they can be automatically opened when (tabspaces-mode) is activated if tabspaces-session-auto-restore is set to t. In addition, a particular project tabspace may be saved via (tabspaces-save-current-project-session), and restored when the project is opened via (tabspaces-open-or-create-project-and-workspace).

Additional Customization

Consult

If you have consult installed you might want to implement the following in your config to have workspace buffers in consult-buffer:

;; Filter Buffers for Consult-Buffer

(with-eval-after-load 'consult
;; hide full buffer list (still available with "b" prefix)
(consult-customize consult--source-buffer :hidden t :default nil)
;; set consult-workspace buffer list
(defvar consult--source-workspace
  (list :name     "Workspace Buffers"
        :narrow   ?w
        :history  'buffer-name-history
        :category 'buffer
        :state    #'consult--buffer-state
        :default  t
        :items    (lambda () (consult--buffer-query
                         :predicate #'tabspaces--local-buffer-p
                         :sort 'visibility
                         :as #'buffer-name)))

  "Set workspace buffer list for consult-buffer.")
(add-to-list 'consult-buffer-sources 'consult--source-workspace))

This should seamlessly integrate workspace buffers into consult-buffer, displaying workspace buffers by default and all buffers when narrowing using “b”. Note that you can also see all project related buffers and files just by narrowing with “p” in a default consult setup.

NOTE: If you typically toggle between having tabspaces-mode active and inactive, you may want to also include a hook function to turn off the consult--source-workspace above and modify the visibility of consult--source-buffer. You can do that with something like the following:

(defun my--consult-tabspaces ()
  "Deactivate isolated buffers when not using tabspaces."
  (require 'consult)
  (cond (tabspaces-mode
         ;; hide full buffer list (still available with "b")
         (consult-customize consult--source-buffer :hidden t :default nil)
         (add-to-list 'consult-buffer-sources 'consult--source-workspace))
        (t
         ;; reset consult-buffer to show all buffers 
         (consult-customize consult--source-buffer :hidden nil :default t)
         (setq consult-buffer-sources (remove #'consult--source-workspace consult-buffer-sources)))))

(add-hook 'tabspaces-mode-hook #'my--consult-tabspaces)           

Ivy

If you use ivy you can use this function to limit your buffer search to only those in the tabspace.

(defun tabspaces-ivy-switch-buffer (buffer)
  "Display the local buffer BUFFER in the selected window.
This is the frame/tab-local equivilant to `switch-to-buffer'."
  (interactive
   (list
    (let ((blst (mapcar #'buffer-name (tabspaces-buffer-list))))
      (read-buffer
       "Switch to local buffer: " blst nil
       (lambda (b) (member (if (stringp b) b (car b)) blst))))))
  (ivy-switch-buffer buffer))

Alternatively, you may use the following function, which is basically a clone of ivy-switch-buffer (and thus uses ivy’s own implementation framework), but with an additional predicate that only allows showing buffers from the current tabspace.

(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))

Included Buffers

By default the *scratch* buffer is included in all workspaces. You can modify which buffers are included by default by changing the value of tabspaces-include-buffers.

If you want emacs to startup with a set of initial buffers in a workspace (something I find works well) you could do something like the following:

(defun my--tabspace-setup ()
  "Set up tabspace at startup."
  ;; Add *Messages* and *splash* to Tab \`Home\'
  (tabspaces-mode 1)
  (progn
    (tab-bar-rename-tab "Home")
    (when (get-buffer "*Messages*")
      (set-frame-parameter nil
                           'buffer-list
                           (cons (get-buffer "*Messages*")
                                 (frame-parameter nil 'buffer-list))))
    (when (get-buffer "*splash*")
      (set-frame-parameter nil
                           'buffer-list
                           (cons (get-buffer "*splash*")
                                 (frame-parameter nil 'buffer-list))))))

(add-hook 'after-init-hook #'my--tabspace-setup)

TODO file per project

By default Tabspaces will create a project-todo.org file at the root of the project when creating a new workspace using tabspaces-open-or-create-project-and-workspace.

Use tabspaces-todo-file-name to change the name of that file, or tabspaces-initialize-project-with-todo to disable this feature completely.

Acknowledgments

Code for this package is derived from, or inspired by, a variety of sources. These include:

tabspaces's People

Contributors

aaronjensen avatar abougouffa avatar bramadams avatar dalugm avatar gagbo avatar mclearc avatar rytswd avatar sethidden avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

tabspaces's Issues

[BUG] two projects with same name are considered as one workspace

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

emacs-workspaces/project-switch-project-open-file vs emacs-workspaces/open-existing-project-and-workspace

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!

Is it possible to create workspaces based on modes?

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.

advise tab-bar-new-tab?

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.

tabspaces-switch-or-create not creating new 'workspaces''

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.

[SUGGESTION] Enable tabspace duplication only when `tabspaces-open-or-create-project-and-workspace` is called with C-u prefix

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:

  1. 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

  2. (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

C-c TAB b to switch buffers always lists the current buffer at the top of the list

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.

image

error while installing from melpa

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..

Saving tabspaces isolated buffer lists with desktop.el

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:

  • Workspace 1: scratch
  • Workspace 2: test.org, scratch
  • Workspace 3: test.el, random.org, dired, scratch

After using desktop-save, quitting emacs, and using desktop-revert:

  • Workspace 1: scratch
  • Workspace 2: test.org, scratch
  • Workspace 3: test.el, test.org, scratch

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.

Switch between tabspaces

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

emacs-workspaces/kill-buffers-close-workspace is looping infinitely

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.

Support restoring project eshell and dired buffers in tabspaces

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.

question about your README

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'?

Unusable `tabspaces-open-or-create-project-and-workspace`

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!

Error when opening project tab space first time

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).

Submit to MELPA

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-open-or-create-project-and-workspace` always creates new workspace, switch buffer always includes the buffer opened from in new workspace

In emacs 31, following the install instructions I'm not having success with either of these features. When calling tabspaces-open-or-create-project-and-workspace always creates new workspace regardless if one exists for that project. Switch buffer will always include the buffer opened from in new workspace, i.e. I'm in project A, A.cpp, if I try to open project B and file b.html A.cpp will be listed in the workspace buffers for project B, nothing from B will be listed in the project buffers for A.

image

tabspaces-open-existing-project-and-workspace includes current buffer in new workspace

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))))))

startup error when tabspaces session file does not yet exist

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()

allow opting out of automatic keymap prefix binding

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.

[SUGGESTION] a nicer tabspaces-ivy-switch-buffer

(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.

Rename created tabspace

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.

Displays *Messages* buffer

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?

`tabspaces-session-auto-restore t` doesn't autorestore when switching to project

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 startup
  • Manually invoking tabspaces-restore-session loads the buffers I expect
  • Currently whenever I open-or-create-project-and-workspace I only get a find file pop up to find files in the project.

config

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)

image

emacs-workspaces--project-name potential problems

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.
  • the vc-call-backend branch seems to not have a value and is returning nil?
  • The catch-all t branch is returning the same string as the previous branch, making the previous branch un-necessary?

tabspaces with non-project buffers

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.

when switching buffer and tab to an existing buffer, it gets added to the current tabspace

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!

[Sharing code] Switching tabs via frog-menu

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:

  1. Create a list of tab names.
  2. Construct a list of strings with first character of each tab name.
  3. Make a new list where each repetition of a char is capitalized.
  4. Turn that list of strings into a list of characters.

There are potential issues:

  • when more than two tab names start with the same character
  • when a tab name starts with something other than an alphabetic character

Mixing tabspaces-switch-to-buffer with tab-bar-select-tab leaks buffer to another tab

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:

  • C-x tabspaces-switch-to-buffer
    (prompt opens, asking for a buffer)
  • C-x tab-bar-select-tab
  • <enter> to load default entry.

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?

Customise `magit-init` call when opening a project

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.

[SUGGESTION] Don't overwrite `project-switch-commands`

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:

  1. M-x tabspaces-open-or-create-project-and-workspace
  2. Hover over a project from the project--list (it has to be a project that doesn't have an existing tabspace yet)
  3. Hit enter

... 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

Add tabspaces-session-file to no-littering

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.

[BUG] two projects with same name are considered as one workspace

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).

tabspaces/tabspaces.el

Lines 402 to 409 in beab193

(defun tabspaces--generate-unique-tab-name (base-name existing-names)
"Generate a unique tab name based on BASE-NAME and the list of EXISTING-NAMES."
(let ((new-name base-name)
(count 1))
(while (member new-name existing-names)
(setq new-name (format "%s<%d>" base-name count))
(setq count (1+ count)))
new-name))

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).

Switch buffer and tab from ibuffer

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).

Integration with projectile

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.

tabspaces-switch-or-create doesn't create empty workspace

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.?

Missing support for tab-group / integration with desktop.el

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?

Question about tabspaces-exclude-buffers

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.

tabspaces/tabspaces.el

Lines 133 to 138 in beab193

(seq-filter (lambda (buffer)
(or (member buffer window-buffers)
(member (buffer-name buffer)
tabspaces-include-buffers)
(not (member (buffer-name buffer)
tabspaces-exclude-buffers))))

Is this behavior designed to be like this? Or would it be better to use and condition with line 137-138?

Pick a license

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.

Searching files in a workspace

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.

help understanding buffer-isolated tabspaces

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!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.