Syntax Highlighting
Not sure if this is a bug or expected behaviour. Someone suggested to ask better here than in the SC forum.
When I open a scd file in Emacs without starting the sclang interpreter it actually looks like expected. However blue are all words starting with capitals. So any word will be formatted in blue.
https://scsynth.org/uploads/default/original/2X/3/3c1ff462a80a9845ea0fd6b8ef49a33d4dd9523f.png
When I start the sclang interpreter the colors change, e.g. SinOsc becomes white (normal font), while ar stays yellow.
https://scsynth.org/uploads/default/original/2X/6/6d90225e9f7843f62c72bb2d94417331e05908c5.png
In this screenshot there's even a mixture between these two options: https://ariona.fr/posts/2024/supercollider_emacs/
I have scel installed. Sclang extension I don't have installed as it didn't work for me. Can someone tell me how the syntax highlighting should correctly look like?
Hmmm, funny, never really thought about thtat, but you're absolutely right! Running arch linux, and can confirm.
Hmmm, funny, never really thought about thtat, but you're absolutely right! Running arch linux, and can confirm.
Looks like I'm overthinking things :)
Not at all. This is definitely a bug...
I fiddled a bit with chatGPT. Could it be that maybe sclang-class-list is not used anymore and instead sclang-class-tree is used? That would explain that the classes are not found and formatted. But I have no clue (yet) about elisp.
A quick fix from chatGPT that formats all word with capitals blue. I put this in my init.el file. Was not able to correctly only color the Classes. Now it behaves exactly like before starting the interpreter.
;; Class Synthax highlighting fix
(add-hook 'sclang-library-startup-hook
(lambda ()
(let* ((classes (mapcar #'car (sclang-flatten-class-tree sclang-class-tree)))
(regexp (concat "\\_<" (regexp-opt classes) "\\_>"))) ;; case-sensitive
(setq-local font-lock-defaults
`(((
;; Classes
(,regexp . font-lock-type-face)
;; Keywords like arg/var
("\\<\\(var\\|arg\\)\\>" . font-lock-keyword-face)
;; Keyword arguments (foo:)
("\\_<\\([a-zA-Z0-9_]+:\\)" 1 font-lock-keyword-face)
;; Method calls (.foo)
("\\.[a-zA-Z_][a-zA-Z0-9_]*" . font-lock-function-name-face)
)))))))
Very nice, it does seem to fix the problem on the syntax side, but it's causing an unspecified error in emacs when I start sclang... Unfortunately I am not yet strong enough on elisp to crack this problem (yet).
Very nice, it does seem to fix the problem on the syntax side, but it's causing an unspecified error in emacs when I start sclang... Unfortunately I am not yet strong enough on elisp to crack this problem (yet).
What error do you get ? I think for me it was fine
I'm getting this:
SCLang: Error in command handler
... which is not a terribly helpful error message, unfortunately...
It also seems like no one is doing bug fixes if my impression is right. Maybe mnost people switched to SCIDE?
The development seems to have stalled, unfortunately. I do find scel quite useable, still. You might find some ideas on how to configure it to your liking here: https://gitlab.com/kflak/dots/-/blob/main/emacs/supercollider.el?ref_type=heads
Thanks, I will try this. Also thought about writing the class names, methods, etc. into a text file and read this in to highlight the fonts. Will give this a try with chatGPT just out of curiositiy. Similarily is it done in SCnvim with the export assests function.
I think this looks promising :) I bet chatGPT to write out the Classes, Methods, etc. into a text file. This can be done from a scd file by running this code with SuperCollider:
(
var outFile, stream;
var keywords, constants, arguments;
var classes, classMethods, instMethods;
outFile = "~/tmp/sc-syntax-export.txt".standardizePath;
stream = File(outFile, "w");
// Hardcoded language elements
keywords = ["arg", "var", "if", "while", "for", "case", "do", "return", "this", "super"];
constants = ["nil", "true", "false", "inf"];
arguments = ["freq:", "amp:", "bufnum:", "out:", "dur:", "pan:"];
// Collect classes
classes = Set.new;
Class.allClasses.do { |c|
classes.add(c.name.asString.replace("Meta_", ""));
};
classes = classes.asArray.sort;
// Collect class methods (defined directly on metaclass)
classMethods = Set.new;
Class.allClasses.do { |c|
c.class.methods.do { |m|
if (m.ownerClass == c.class) { classMethods.add(m.name.asString); };
};
};
classMethods = classMethods.asArray.sort;
// Collect instance methods (defined directly on class)
instMethods = Set.new;
Class.allClasses.do { |c|
c.methods.do { |m|
if (m.ownerClass == c) { instMethods.add(m.name.asString); };
};
};
instMethods = instMethods.asArray.sort;
// Write sections
stream.write("[CLASSES]\n");
classes.do { |name| stream.write(name ++ "\n") };
stream.write("\n[CLASS-METHODS]\n");
classMethods.do { |name| stream.write(name ++ "\n") };
stream.write("\n[INSTANCE-METHODS]\n");
instMethods.do { |name| stream.write(name ++ "\n") };
stream.write("\n[KEYWORDS]\n");
keywords.sort.do { |kw| stream.write(kw ++ "\n") };
stream.write("\n[CONSTANTS]\n");
constants.sort.do { |c| stream.write(c ++ "\n") };
stream.write("\n[ARGUMENTS]\n");
arguments.sort.do { |a| stream.write(a ++ "\n") };
stream.close;
"Exported SuperCollider syntax to %".format(outFile).postln;
)
Then in init.el add this and change the path to point to the file generated in the first step.
;;SuperCollider Syntax Highlighting
;; Path to your exported SC syntax file
;; Path to your exported SC syntax file
(defvar sclang-syntax-file
"~/tmp/sc-syntax-export.txt"
"File containing SC classes, methods, keywords, etc. for font-locking.")
(defun sclang-load-syntax-definitions ()
"Load SC syntax elements from file into an alist."
(with-temp-buffer
(insert-file-contents sclang-syntax-file)
(let ((section nil)
(classes '())
(class-methods '())
(instance-methods '())
(keywords '())
(constants '())
(arguments '()))
(while (not (eobp))
(let ((line (string-trim (thing-at-point 'line t))))
(cond
((string-match-p "^\\[CLASSES\\]" line) (setq section 'classes))
((string-match-p "^\\[CLASS-METHODS\\]" line) (setq section 'class-methods))
((string-match-p "^\\[INSTANCE-METHODS\\]" line) (setq section 'instance-methods))
((string-match-p "^\\[KEYWORDS\\]" line) (setq section 'keywords))
((string-match-p "^\\[CONSTANTS\\]" line) (setq section 'constants))
((string-match-p "^\\[ARGUMENTS\\]" line) (setq section 'arguments))
((or (string-empty-p line)
(string-prefix-p "#" line)) nil)
(t
(pcase section
('classes (push line classes))
('class-methods (push line class-methods))
('instance-methods (push line instance-methods))
('keywords (push line keywords))
('constants (push line constants))
('arguments (push line arguments))))))
(forward-line 1))
`((classes . ,classes)
(class-methods . ,class-methods)
(instance-methods . ,instance-methods)
(keywords . ,keywords)
(constants . ,constants)
(arguments . ,arguments)))))
(defun sclang-setup-extra-font-lock ()
"Add extra syntax highlighting from SC export file."
(let* ((defs (sclang-load-syntax-definitions))
;; Build regexps
(class-re (regexp-opt (alist-get 'classes defs) 'symbols))
(class-method-re (regexp-opt (alist-get 'class-methods defs) 'symbols))
(inst-method-re (regexp-opt (alist-get 'instance-methods defs) 'symbols))
(keyword-re (regexp-opt (alist-get 'keywords defs) 'symbols))
(const-re (regexp-opt (alist-get 'constants defs) 'symbols))
(arg-re (regexp-opt (alist-get 'arguments defs))))
;; Add font-lock rules
(font-lock-add-keywords
'sclang-mode
`((,class-re . font-lock-type-face)
;; (,class-method-re . font-lock-function-name-face)
;; (,inst-method-re . font-lock-function-name-face)
(,keyword-re . font-lock-keyword-face)
(,const-re . font-lock-constant-face)
(,arg-re 1 font-lock-variable-name-face))
'append))) ;; 'append ensures it overrides broken default highlighting
;; Hook into sclang-mode
(add-hook 'sclang-mode-hook #'sclang-setup-extra-font-lock)
In principal also the methods can be colored but it didn't look consistent to me and that's also not done in SCIDE. That's why I commented this out. Maybe someone who knows SuperCollider and Eslip better than me can comment on that if it makes sense.
Also thank's for the blink snippet. I had to divide the line and region part. Now it works like expected:
(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)
I have a PR with an appropriate solution, I do believe. It doesn't involve creating any extra files. I was also getting errors by modifying my init.el as suggested above; in particular, that solution was blocking the name completion feature from working. See the PR for the details on the fix.