What are you trying to do?
I often want to open some third-party code (eg a package in node_modules, or a ruby gem) and be able to easily search it or pop open files. Even though I have emacs as my main editor, I’d usually open these in TextMate or VSCode because I can just run code node_modules/bootstrap
and have a new self-contained window where ⌘P allows me to jump to any file in that directory, and ⌘⇧F searches that directory, and only that directory.
I’ve been trying to replicate that sort of workflow by opening a new emacs frame with a new workspace, and setting projectile-project-root to try and limit the scope of searches with SPC s p
and file-finding with SPC SPC
.
What happens instead?
It doesn’t work great, especially if I have two separate projects open at the same time where projectile-project-root gets overwritten - ideally I’d like each emacs frame to be dedicated to its own project.
What code do you want reviewed?
Here’s my bash script that I use to launch projects from the terminal. I have iterm2 set up so that command-clicking a file path runs it, or I can manually run something like edit ./node_modules/bootstrap
.
#!/bin/bash
if [ -d "$1" ]; then
dir=$(realpath "$1")/
target="$dir"
projectname=$(basename "$dir")
else
dir=$(realpath "$(dirname "$1")")/
# read filename and line number out of ./foo.rb:41
IFS=: read -r target linenumber <<< "$1"
target=$(realpath "$target")
fi
emacsclient --alternate-editor="" --no-wait --quiet --suppress-output --eval "
;; using make-frame rather than 'emacsclient --create-frame' because the latter is a bit weird on macOS,
;; creating a second dock icon with the generic file icon rather than an emacs icon, and doesn't allow cmd-~ to switch windows
(let ((frame (make-frame '((display . \":0\"))))
(frame-count (length (frame-list))))
;; center it, but offset successive frames so that they don't all open in the exact same spot
(modify-frame-parameters
frame
'((user-position . t) (top . 0.5) (left . 0.5)))
(set-frame-parameter frame 'top (+ (frame-parameter frame 'top) (* 10 frame-count)))
(set-frame-parameter frame 'left (+ (frame-parameter frame 'left) (* 10 frame-count)))
(select-frame-set-input-focus frame)
(if (file-directory-p \"$target\")
;; Try and set up the directory as if it's a project-root (eg for SPC-s-p searching)
(progn
(dir-locals-set-class-variables 'vendor-directory
'((nil . ((projectile-project-root . \"$dir\")))))
(dir-locals-set-directory-class \"$dir\" 'vendor-directory)
(add-to-list 'safe-local-variable-values '(projectile-project-root . \"$dir\"))
(+workspace/new \"$projectname\"))
;; Start a new workspace. Seems redundant for a single file, but I don't want to pollute the existing workspaces.
;; Plus it means that cmd-w will actually close the current frame.
(+workspace/new)
;; todo: try & figure out the project root if we're opening a single file
)
;; setup hook to delete workspace when the frame is closed
(set-frame-parameter frame 'workspace (+workspace-current-name))
(find-file \"$target\")
(goto-line ${linenumber:-1})
)"
or for ease of syntax-highlighting, here’s just the lispy bit:
(let ((frame (make-frame '((display . ":0"))))
(frame-count (length (frame-list))))
;; center it, but offset successive frames so that they don't all open in the exact same spot
(modify-frame-parameters
frame
'((user-position . t) (top . 0.5) (left . 0.5)))
(set-frame-parameter frame 'top (+ (frame-parameter frame 'top) (* 10 frame-count)))
(set-frame-parameter frame 'left (+ (frame-parameter frame 'left) (* 10 frame-count)))
(select-frame-set-input-focus frame)
(if (file-directory-p "$target")
;; Try and set up the directory as if it's a project-root (eg for SPC-s-p searching)
(progn
(dir-locals-set-class-variables 'vendor-directory
'((nil . ((projectile-project-root . "$dir")))))
(dir-locals-set-directory-class "$dir" 'vendor-directory)
(add-to-list 'safe-local-variable-values '(projectile-project-root . \"$dir\"))
(+workspace/new "$projectname"))
;; Start a new workspace. Seems redundant for a single file, but I don't want to pollute the existing workspaces.
;; Plus it means that cmd-w will actually close the current frame.
(+workspace/new)
;; todo: try & figure out the project root if we're opening a single file
)
;; setup hook to delete workspace when the frame is closed
(set-frame-parameter frame 'workspace (+workspace-current-name))
(find-file "$target")
(goto-line "${linenumber:-1}")
)
Would appreciate any tips on how to clean this up / alternate workflows.