Help with Emacs configuration

Hello all!

I want to switch to Emacs for my SuperCOllider programming, since i also do every other work in emacs.

I got SuperCollider running in Emacs, but i have a little bug, which i can’t solve.

The bug is related to code highlighting and appears like this:

  • when i open a .scd document the SCLang mode starts and everything looks fine. the code is highlighted as expected
  • then i start the interpreter (C-c C-o). The SCLang Workspace starts and the interpreter buffer is opened. but the highlighting of the UGens is disappearing. for example: ‘SinOsc’ is not highlighted anymore, but ‘var’, and ‘\symbols’ are still highlited.

I guess it has something to do with rescanning the SC database for UGens.

Can someone help?

Greetings,
PvN

I checked the Repo for the scel package and i noticed, that this is a known problem.
There is even a PR which solves this problem, so i copied the PR manually, since it is not merged yet.

This solved the problem.

But i have another question:

  • what is the best auto-complete package for emacs and supercollider? normally i use company, but it does not complete for my variables. what are you using?
1 Like

Company also. Can you give an example?

Thank you! I solved this shortly after writing…

But i found a bug and since the Emacs package is not maintained, i assume that other people have also problems with this and maybe found a solution:

i wanted to use the sclang-eval-region command in Emacs to evluate everything in between two panrenthesis. This does not seem to work, since i always get syntax error, but the code evaluates fine in the SuperCollider IDE.

How do you evaluate code bewteen two parnthesis?

I think it is C-M-x, but i have to look it up to be sure.

Yes! C-M-xis per default mapped to sclang-eval-defun and this works. sclang-eval-regionis leading to some parser problems of emacs.

I tried to be sure. It is still working with my setup using GNU Emacs 30.2.

This is my init.el file, but it should work out of the box:

;; ------------------------
;; Package Manager Setup
;; ------------------------
(require 'package)
(add-to-list 'package-archives
             '("melpa" . "https://melpa.org/packages/"))
(package-initialize)

;; Ensure use-package is installed
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))
(require 'use-package)

;; Lua Keybindings
(with-eval-after-load 'lua-mode
  ;; Remove old keys first (optional if reloading)
  (define-key lua-mode-map (kbd "C-c C-c") nil)
  (define-key lua-mode-map (kbd "C-c C-d") nil)
  (define-key lua-mode-map (kbd "C-c C-l") nil)
  (define-key lua-mode-map (kbd "C-c C-f") nil)

  ;; Now define them cleanly
  (define-key lua-mode-map (kbd "C-c C-c") 'lua-send-current-line)
  (define-key lua-mode-map (kbd "C-c C-d") 'lua-send-region)
  (define-key lua-mode-map (kbd "C-c C-l") 'lua-send-buffer)
  (define-key lua-mode-map (kbd "C-c C-f") 'lua-search-documentation))

;; Hide Toolbar
  (tool-bar-mode -1)

;; Enable mouse support in terminal
   (xterm-mouse-mode 1)

;; Clipboard support in terminal mode
   (xclip-mode 1)

;; deactivate C-z
 (global-unset-key (kbd "C-z"))
;; ------------------------
;; Startup UI
;; ------------------------
(setq inhibit-startup-screen t)   ;; disable welcome screen
(setq initial-scratch-message "") ;; empty scratch buffer


;; Ask for confirmation before quitting Emacs
(setq confirm-kill-emacs 'y-or-n-p)


;; ------------------------
;; Put backup files neatly away
;; ------------------------
(let ((backup-dir "~/tmp/emacs/backups")
      (auto-saves-dir "~/tmp/emacs/auto-saves/"))
  (dolist (dir (list backup-dir auto-saves-dir))
    (when (not (file-directory-p dir))
      (make-directory dir t)))
  (setq backup-directory-alist `(("." . ,backup-dir))
        auto-save-file-name-transforms `((".*" ,auto-saves-dir t))
        auto-save-list-file-prefix (concat auto-saves-dir ".saves-")
        tramp-backup-directory-alist `((".*" . ,backup-dir))
        tramp-auto-save-directory auto-saves-dir))

(setq backup-by-copying t    ; Don't delink hardlinks
      delete-old-versions t  ; Clean up the backups
      version-control t      ; Use version numbers on backups,
      kept-new-versions 5    ; keep some new versions
      kept-old-versions 2)   ; and some old ones, too


;; ------------------------
;; Global line numbers
;; ------------------------ 
(global-display-line-numbers-mode 1)
(setq display-line-numbers-type 'absolute)  ;; or 'relative

;; Force solid background for line numbers, this is used to supress colored artifact pixels when using relative line numbering
;; Reported the bug to the emacs bug tracker mailing list 
;;(set-face-attribute 'line-number nil
;;  :background "#282a36"   ;; Dracula background color
;;  :foreground "#6272a4") ;; Dracula line number color

;; (set-face-attribute 'line-number-current-line nil
;;  :background "#282a36"   ;; same as background, no transparency
;;  :foreground "#f8f8f2") ;; brighter for current line

;; (setq display-line-numbers-width 100)

;; ------------------------
;; Theme
;; ------------------------
(setq catppuccin-flavor 'frappe) ;; or 'latte, 'macchiato, or 'mocha
(use-package catppuccin-theme
  :ensure t
  :config
  (load-theme 'catppuccin :no-confirm))

 
;; ------------------------
;; Company-mode (autocomplete)
;; ------------------------
(use-package company
  :ensure t
  :hook (after-init . global-company-mode)
  :config
  (setq company-idle-delay 0.2
        company-minimum-prefix-length 1)

  ;; Filter out candidates that are purely numeric
  (push (apply-partially #'cl-remove-if
                         (lambda (c)
                           (string-match-p "^[0-9]+\\(?:\\.[0-9]*\\)?$" c)))
        company-transformers))

;; ------------------------
;; SuperCollider-specific configuration
;; ------------------------
(with-eval-after-load 'sclang
  (add-hook 'sclang-mode-hook
            (lambda ()
              (company-mode 1)
              ;; Only set backends after sclang is loaded
              (setq-local company-backends '(company-capf company-dabbrev-code))
              (setq sclang-autocomplete-enable t))))


;; ------------------------
;; SuperCollider Emacs Integration
;; ------------------------
;; Adjust this path to where you installed the SC Quark 'scel'
(add-to-list 'load-path "~/.local/share/SuperCollider/downloaded-quarks/scel/el")
(require 'sclang)


;; ------------------------
;; Associate .scd and .sc files automatically
  (add-to-list 'auto-mode-alist '("\\.scd$" . sclang-mode))
  (add-to-list 'auto-mode-alist '("\\.sc$" . sclang-mode))

;; Move PostBuffer to right side window
(add-to-list 'display-buffer-alist
            '("\\*SCLang:PostBuffer\\*"
              (display-buffer-in-side-window)
              (side . right)
              (window-width . 60)))


;; ------------------------
;; Custom-set Variables (generated by Emacs, keep as is)
;; ------------------------
(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(cua-mode t)
 '(custom-enabled-themes '(modus-operandi-tinted))
 '(custom-safe-themes
   '("36c5acdaf85dda0dad1dd3ad643aacd478fb967960ee1f83981d160c52b3c8ac"
     "fae5872ff90462502b3bedfe689c02d2fa281bc63d33cb007b94a199af6ccf24"
     "dfb1c8b5bfa040b042b4ef660d0aab48ef2e89ee719a1f24a4629a0c5ed769e8"
     "13096a9a6e75c7330c1bc500f30a8f4407bd618431c94aeab55c9855731a95e1"
     "19b62f442479efd3ca4c1cef81c2311579a98bbc0f3684b49cdf9321bd5dfdbf"
     "b9c002dc827fb75b825da3311935c9f505d48d7ee48f470f0aa7ac5d2a595ab2"
     "ffa78fc746f85d1c88a2d1691b1e37d21832e9a44a0eeee114a00816eabcdaf9"
     "4c16a8be2f20a68f0b63979722676a176c4f77e2216cc8fe0ea200f597ceb22e"
     "aff0396925324838889f011fd3f5a0b91652b88f5fd0611f7b10021cc76f9e09"
     "c038d994d271ebf2d50fa76db7ed0f288f17b9ad01b425efec09519fa873af53"
     "df39cc8ecf022613fc2515bccde55df40cb604d7568cb96cd7fe1eff806b863b"
     "d6b369a3f09f34cdbaed93eeefcc6a0e05e135d187252e01b0031559b1671e97"
     "b3ba955a30f22fe444831d7bc89f6466b23db8ce87530076d1f1c30505a4c23b"
     "ac893acecb0f1cf2b6ccea5c70ea97516c13c2b80c07f3292c21d6eb0cb45239"
     "d609d9aaf89d935677b04d34e4449ba3f8bbfdcaaeeaab3d21ee035f43321ff1"
     "59c36051a521e3ea68dc530ded1c7be169cd19e8873b7994bfc02a216041bf3b"
     "cd5f8f91cc2560c017cc9ec24a9ab637451e36afd22e00a03e08d7b1b87c29ca"
     "5a0ddbd75929d24f5ef34944d78789c6c3421aa943c15218bac791c199fc897d"
     "8363207a952efb78e917230f5a4d3326b2916c63237c1f61d7e5fe07def8d378"
     "5244ba0273a952a536e07abaad1fdf7c90d7ebb3647f36269c23bfd1cf20b0b8"
     "83550d0386203f010fa42ad1af064a766cfec06fc2f42eb4f2d89ab646f3ac01"
     "b5fd9c7429d52190235f2383e47d340d7ff769f141cd8f9e7a4629a81abc6b19"
     "166a2faa9dc5b5b3359f7a31a09127ebf7a7926562710367086fcc8fc72145da"
     "7de64ff2bb2f94d7679a7e9019e23c3bf1a6a04ba54341c36e7cf2d2e56e2bcc"
     "014cb63097fc7dbda3edf53eb09802237961cbb4c9e9abd705f23b86511b0a69"
     "e85a354f77ae6c2e47667370a8beddf02e8772a02e1f7edb7089e793f4762a45"
     "6af300029805f10970ebec4cea3134f381cd02f04c96acba083c76e2da23f3ec"
     "f9d423fcd4581f368b08c720f04d206ee80b37bfb314fa37e279f554b6f415e9"
     default))
 '(display-line-numbers-type 'absolute)
 '(global-display-line-numbers-mode t)
 '(package-selected-packages
   '(buffer-move catppuccin-theme company doom-themes doric-themes
		 dracula-theme ef-themes ement eradio evil fzf
		 gruvbox-theme lsp-pyright lua-mode marginalia
		 multi-vterm pyenv-mode pyvenv tidal vertico xclip))
 '(sclang-auto-scroll-post-buffer t)
 '(sclang-eval-line-forward nil)
 '(sclang-show-workspace-on-startup nil)
 '(tool-bar-mode nil))

(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(highlight ((t (:background "dark green" :foreground "#2e3436")))))



;; ------------------------
;; QWERTZ-friendly paragraph navigation
;; Rebind M-{ (backward-paragraph) and M-} (forward-paragraph) to easier keys
;; ------------------------
   (global-set-key (kbd "C-ü") 'backward-paragraph)
   (global-set-key (kbd "C-+") 'forward-paragraph)
 

;; Optional: also keep original M-{ and M-} if you want
 (global-set-key (kbd "M-{") 'backward-paragraph)
 (global-set-key (kbd "M-}") 'forward-paragraph)


;; Thank you, undltd!!! https://xn--w5d.cc/2024/07/28/supercollider-emacs-highlight-eval.html
(require 'pulse)
(defun my/sclang-highlight-defun (&optional silent-p)
  (cl-multiple-value-bind (beg end) (sclang-point-in-defun-p)
    (when (and beg end)
      (pulse-momentary-highlight-region beg end))))

(defun my/sclang-highlight-region (&optional silent-p)
  (when (use-region-p)
    (pulse-momentary-highlight-region (region-beginning) (region-end))))

(defun my/sclang-highlight-line (&optional silent-p)
  (pulse-momentary-highlight-one-line (point)))

;; Hook into evaluation commands
(advice-add 'sclang-eval-defun :before #'my/sclang-highlight-defun)
(advice-add 'sclang-eval-region :before #'my/sclang-highlight-region)
(advice-add 'sclang-eval-line :before #'my/sclang-highlight-line)



;; --- SuperCollider record setup ---
;; Set path where recordings should be stored
(setq kf/sclang-recording-path "~/sc-recordings")  ;; change this!

(defun kf/sclang-record ()
  "Toggle SuperCollider recording. Files go to `kf/sclang-recording-path`."
  (interactive)
  (sclang-eval-expression
   (format
    "if(s.isRecording) {
       s.stopRecording
     } {
       s.record(
         \"%s/\" ++ Date.localtime.stamp ++ \".wav\",
         numChannels: s.options.numOutputBusChannels
       )
     }"
    (expand-file-name kf/sclang-recording-path))))

;; Keybinding for sclang-mode
(with-eval-after-load 'sclang
  (define-key sclang-mode-map (kbd "<f5>") #'kf/sclang-record)
  (define-key sclang-mode-map (kbd "<f12>") #'sclang-main-stop))
  

;; Prot dired configuration -- see https://protesilaos.com/codelog/2023-06-26-emacs-file-dired-basics/
;; Install the `vertico' package to get a vertical view of the
;; minibuffer.  Vertico is optimised for performance and is also
;; highly configurable.  Here is my minimal setup:
(setq vertico-resize nil)
(vertico-mode 1)

;; Install the `marginalia' package.  This will display useful
;; annotations next to entries in the minibuffer.  For example, when
;; using M-x it will show a brief description of the command as well
;; as the keybinding associated with it (if any).
(marginalia-mode 1)

;; When you first call `find-file' (C-x C-f by default), you do not
;; need to clear the existing file path before adding the new one.
;; Just start typing the whole path and Emacs will "shadow" the
;; current one.  For example, you are at ~/Documents/notes/file.txt
;; and you want to go to ~/.emacs.d/init.el: type the latter directly
;; and Emacs will take you there.
(file-name-shadow-mode 1)

;; This works with `file-name-shadow-mode' enabled.  When you are in
;; a sub-directory and use, say, `find-file' to go to your home '~/'
;; or root '/' directory, Vertico will clear the old path to keep
;; only your current input.
(add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy)

;; Do not outright delete files.  Move them to the system trash
;; instead.  The `trashed' package can act on them in a Dired-like
;; fashion.  I use it and can recommend it to either restore (R) or
;; permanently delete (D) the files.
(setq delete-by-moving-to-trash t)

;; When there are two Dired buffers side-by-side make Emacs
;; automatically suggest the other one as the target of copy or rename
;; operations.  Remember that you can always use M-p and M-n in the
;; minibuffer to cycle through the history, regardless of what this
;; does.  (The "dwim" stands for "Do What I Mean".)
(setq dired-dwim-target t)

;; Automatically hide the detailed listing when visiting a Dired
;; buffer.  This can always be toggled on/off by calling the
;; `dired-hide-details-mode' interactively with M-x or its keybindings
;; (the left parenthesis by default).
(add-hook 'dired-mode-hook #'dired-hide-details-mode)

;; Teach Dired to use a specific external program with either the
;; `dired-do-shell-command' or `dired-do-async-shell-command' command
;; (with the default keys, those are bound to `!' `&', respectively).
;; The first string is a pattern match against file names.  The
;; remaining strings are external programs that Dired will provide as
;; suggestions.  Of course, you can always type an arbitrary program
;; despite these defaults.
;;
;; Note that the * can be added to a program to instruct it to open
;; all the files as a set rather than all as separate instances.  You
;; write the name of the program, then space followed by the asterisk
;; (thanks to @mac68tm on YouTube for pointing this out).
(setq dired-guess-shell-alist-user
      '(("\\.\\(png\\|jpe?g\\|tiff\\)" "feh *" "xdg-open")
        ("\\.\\(mp[34]\\|m4a\\|ogg\\|flac\\|webm\\|mkv\\)" "mpv *" "xdg-open")
		(".*" "xdg-open")))

;; Set folders first
(setq dired-listing-switches "-al --group-directories-first")


;; Change keybinding to go up in directory
(eval-after-load "dired" '(progn
  (define-key dired-mode-map (kbd "M-u") 'dired-up-directory) ))


;; Hide dotfiles
(use-package dired
  :init
  (require 'dired-x) ;; needed for dired-omit-mode

  :hook
  (dired-mode . (lambda () (dired-omit-mode))) ;; hide .dot files by default

  :config
  (setq dired-omit-files
        (concat dired-omit-files "\\|^\\..+$")))


;; Enable Evil mode
(require 'evil)
(evil-mode 0)

;; Python configuration
(use-package lsp-pyright
  :ensure t
  :hook (python-mode . (lambda ()
                         (require 'lsp-pyright)
                         (lsp-deferred)))
  :custom
  (lsp-pyright-python-executable-cmd "~/python/myenv/bin/python")
  (lsp-pyright-langserver-command "~/python/myenv/bin/pyright"))

;; Python REPL
(setq python-shell-interpreter "~/python/myenv/bin/python")
(setq python-shell-interpreter-args "-i")

;; Lua configuration
(use-package lsp-mode
  :ensure t
  :hook (lua-mode . lsp)
  :commands lsp)

(setq lsp-clients-lua-language-server-bin
      "~/builds/luals/lua-language-server/bin/lua-language-server")


;; Stuff to get a better Tidal experience
(with-eval-after-load 'cua-base
  ;; disable C-Enter from triggering CUA rectangle select
  (define-key cua-global-keymap (kbd "C-<return>") nil))

;; Fixing C-c C-c error
(add-hook 'haskell-interactive-mode-hook
          (lambda ()
            (define-key interactive-haskell-mode-map
              (kbd "C-c C-c") nil)))

;; Tidal autocompletion
(add-to-list 'load-path "~/.emacs.d/tidal_completion")
(require 'tidal-completion)

(setq tidal-completion-sample-directories
      '("~/.local/share/SuperCollider/downloaded-quarks/Dirt-Samples"))

;; only init after tidal/haskell are loaded
(with-eval-after-load 'tidal
  (tidal-completion-init)

  (defun my-tidal-completion-add-hook ()
    (add-hook 'completion-at-point-functions
              #'tidal-completion-at-point nil t))

  (add-hook 'tidal-mode-hook #'my-tidal-completion-add-hook))

;; company integration (buffer-local)
(with-eval-after-load 'company
  (add-hook 'tidal-mode-hook
            (lambda ()
              (company-mode 1)
              (setq-local company-backends
                          (cons #'company-capf company-backends)))))
1 Like

Yes it does!

The problem was, i assumed to use sclang-eval-regionfor this and not the sclang-eval-defun command, which does not work.

Also thank you for the 'pulse snippet! I copied it to my configuration!

1 Like