From 6cea8929e6a6033102110a23a12554337187baa7 Mon Sep 17 00:00:00 2001 From: John Andrews Date: Sun, 25 Jul 2021 14:58:21 -0400 Subject: [PATCH 01/17] Conditionally respect sclang-vars Only when compiling with cmake will sclang-vars.el be written. This commit moves relevant constants into customizable vars. Only if the `sclang-system-data-dir` exists is it respected, otherwise it will guess the proper location by looking at the system-type. The purpose of this is to make it possible to build and distribute a package independent of the SuperCollider build process, while ensuring that those users who have become accustomed to the current install process are not disrupted. --- .gitignore | 1 + el/sclang-help.el | 31 +++++++++++++++++++++++++------ el/sclang-vars.el.in | 20 ++++++++++++-------- 3 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/el/sclang-help.el b/el/sclang-help.el index e70a864..46e1c1a 100644 --- a/el/sclang-help.el +++ b/el/sclang-help.el @@ -24,15 +24,29 @@ (require 'sclang-interp) (require 'sclang-language) (require 'sclang-mode) -(require 'sclang-vars) +(require 'sclang-vars nil 'ignore-missing-file) (require 'sclang-minor-mode) -(defcustom sclang-help-directory "~/SuperCollider/Help" - "*Directory where the SuperCollider help files are kept. OBSOLETE." +(defun sclang-system-root () + "Find the common install location for the platform." + (cond + ((boundp 'sclang-system-data-dir) + sclang-system-data-dir) + + ((eql 'darwin system-type) + (expand-file-name "~/Library/Application Support/SuperCollider")) + + ((and (eql 'gnu/linux system-type) + (file-exists-p "/usr/local/share/SuperCollider")) + "/usr/local/share/SuperCollider") + + ((eql 'gnu/linux system-type) + "/usr/share/SuperCollider"))) + +(defcustom sclang-system-help-dir (expand-file-name "Help" (sclang-system-root)) + "Installation dependent help directory." :group 'sclang-interface - :version "21.3" - :type 'directory - :options '(:must-match)) + :type 'directory) (defcustom sclang-help-path (list sclang-system-help-dir "~/.local/share/SuperCollider/Help") @@ -41,6 +55,11 @@ :version "21.4" :type '(repeat directory)) +(defcustom sclang-system-extension-dir (expand-file-name "Extensions" (sclang-system-root)) + "Installation dependent extension directory." + :group 'sclang-interface + :type 'directory) + (defconst sclang-extension-path (list sclang-system-extension-dir "~/.local/share/SuperCollider/Extensions") "List of SuperCollider extension directories.") diff --git a/el/sclang-vars.el.in b/el/sclang-vars.el.in index 57fac1b..96b1f62 100644 --- a/el/sclang-vars.el.in +++ b/el/sclang-vars.el.in @@ -16,19 +16,23 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to -;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. +;;; Commentary: +;; +;; This file is included in the distribution in order to convey system +;; installation variables which are known at compile time, but only when +;; the library is installed with cmake along with SuperCollider source. +;; +;; When using the stand-alone package, this file can be ignored and +;; the constants it defines will have no effect. + ;;; Code: (defconst sclang-system-data-dir "@PKG_DATA_DIR@" - "Installation dependent data directory.") - -(defconst sclang-system-help-dir "@PKG_DATA_DIR@/Help" - "Installation dependent help directory.") - -(defconst sclang-system-extension-dir "@PKG_DATA_DIR@/Extensions" - "Installation dependent extension directory.") + "Installation dependent data directory. +Bound only when library is installed with SuperCollider.") (provide 'sclang-vars) ;;; sclang-vars.el ends here From 225e810bdd7bf5d8ecef02b7339d1738083c6040 Mon Sep 17 00:00:00 2001 From: John Andrews Date: Sun, 25 Jul 2021 19:54:35 -0400 Subject: [PATCH 02/17] Add quark file --- scel.quark | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 scel.quark diff --git a/scel.quark b/scel.quark new file mode 100644 index 0000000..7d1b71c --- /dev/null +++ b/scel.quark @@ -0,0 +1,5 @@ +( + name: "scel", + summary: "SuperCollider/Emacs interface", + version: "1.0.0", +) From 14157663b09da97c753ced8ce19ec364c8d5f48f Mon Sep 17 00:00:00 2001 From: John Andrews Date: Mon, 9 Aug 2021 11:57:27 -0400 Subject: [PATCH 03/17] Add test to expose a load-time issue The issue manifests when sclang is compiled. Steps to reproduce in real emacs config: 1. Install the package and compile it 2. Open a file with a `.sc` or `.scd` extension 3. Witness the error `(void-function sclang-make-buffer-name)` Steps to reproduce with the test: 0. install [eldev](https://github.com/doublep/eldev#installation) 1. `eldev compile` 2. `eldev test` --- .gitignore | 3 +++ el/Eldev | 7 +++++++ el/sclang.el | 11 ++++++++++- el/test/fixtures/super-boring.scd | 1 + el/test/sclang-mode-test.el | 7 +++++++ 5 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 el/Eldev create mode 100644 el/test/fixtures/super-boring.scd create mode 100644 el/test/sclang-mode-test.el diff --git a/.gitignore b/.gitignore index e43b0f9..4aee566 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ .DS_Store +*.elc +.eldev +/el/sclang-autoloads.el diff --git a/el/Eldev b/el/Eldev new file mode 100644 index 0000000..9c887bf --- /dev/null +++ b/el/Eldev @@ -0,0 +1,7 @@ +; -*- mode: emacs-lisp; lexical-binding: t -*- + +;; Uncomment some calls below as needed for your project. +;(eldev-use-package-archive 'gnu) +;(eldev-use-package-archive 'melpa) + +(eldev-use-plugin 'autoloads) diff --git a/el/sclang.el b/el/sclang.el index 3550287..9bbedcc 100644 --- a/el/sclang.el +++ b/el/sclang.el @@ -1,5 +1,7 @@ ;;; sclang.el --- IDE for working with the SuperCollider language -;; copyright 2003 stefan kersten +;; Copyright 2003 stefan kersten +;; Version: 1.0.0 +;; URL: https://github.com/supercollider/scel ;; ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as @@ -16,6 +18,13 @@ ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 ;; USA +;;; Commentary: +;; +;; This package provides code for interfacing with sclang and scsynth. +;; In order to be useful you need to install SuperCollider and the +;; sc-el Quark. See the README or https://github.com/supercollider/scel +;; for more information. + ;;; Code: (defgroup sclang nil "IDE for working with the SuperCollider language." diff --git a/el/test/fixtures/super-boring.scd b/el/test/fixtures/super-boring.scd new file mode 100644 index 0000000..a8b058e --- /dev/null +++ b/el/test/fixtures/super-boring.scd @@ -0,0 +1 @@ +{ WhiteNoise.ar(0.2!2); }.play; \ No newline at end of file diff --git a/el/test/sclang-mode-test.el b/el/test/sclang-mode-test.el new file mode 100644 index 0000000..1273019 --- /dev/null +++ b/el/test/sclang-mode-test.el @@ -0,0 +1,7 @@ +;; -*- no-byte-compile: t; lexical-binding: t; -*- +;;; test/sclang-mode-test.el + +(ert-deftest sclang-major-mode-init-test () + "Loading a file with an scd extension should init sclang-mode" + (find-file "fixtures/super-boring.scd") + (should (eq 'sclang-mode major-mode))) From 9b160ec102dce546d63d6da7e702aae749dc50db Mon Sep 17 00:00:00 2001 From: John Andrews Date: Mon, 9 Aug 2021 12:05:52 -0400 Subject: [PATCH 04/17] Fix the load issue by unwrapping requires --- el/sclang-dev.el | 9 ++------- el/sclang-interp.el | 5 ++--- el/sclang-minor-mode.el | 7 ++----- el/sclang-mode.el | 6 ++---- el/sclang-server.el | 10 ++++------ el/sclang-widgets.el | 6 +++--- 6 files changed, 15 insertions(+), 28 deletions(-) diff --git a/el/sclang-dev.el b/el/sclang-dev.el index 440409c..211082d 100644 --- a/el/sclang-dev.el +++ b/el/sclang-dev.el @@ -13,13 +13,8 @@ ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 ;; USA - -(eval-when-compile - (require 'sclang-util) - (require 'sclang-interp) - ) - - +(require 'sclang-util) +(require 'sclang-interp) (sclang-set-command-handler 'openDevSource diff --git a/el/sclang-interp.el b/el/sclang-interp.el index 1bf7308..04a9a41 100644 --- a/el/sclang-interp.el +++ b/el/sclang-interp.el @@ -15,9 +15,8 @@ ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 ;; USA -(eval-when-compile - (require 'sclang-util) - (require 'compile)) +(require 'sclang-util) +(require 'compile) ;; ===================================================================== ;; post buffer access diff --git a/el/sclang-minor-mode.el b/el/sclang-minor-mode.el index caf5416..c5e2a47 100644 --- a/el/sclang-minor-mode.el +++ b/el/sclang-minor-mode.el @@ -16,11 +16,8 @@ ;;; along with this program; if not, write to the Free Software ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -(eval-when-compile - (require 'sclang-util) - (require 'sclang-mode) - ) - +(require 'sclang-util) +(require 'sclang-mode) (easy-mmode-define-minor-mode sclang-minor-mode "Toggle sclang-minor-mode. diff --git a/el/sclang-mode.el b/el/sclang-mode.el index ad7b626..0f42491 100644 --- a/el/sclang-mode.el +++ b/el/sclang-mode.el @@ -24,10 +24,8 @@ (declare-function company-mode "ext:company") (defvar company-backends) -(eval-when-compile - (require 'font-lock) - (require 'sclang-util)) - +(require 'font-lock) +(require 'sclang-util) (require 'sclang-interp) (require 'sclang-language) (require 'sclang-dev) diff --git a/el/sclang-server.el b/el/sclang-server.el index 1f948db..375c492 100644 --- a/el/sclang-server.el +++ b/el/sclang-server.el @@ -16,12 +16,10 @@ ;; USA (require 'cl-lib) - -(eval-when-compile - (require 'sclang-util) - (require 'sclang-interp) - (require 'sclang-language) - (require 'sclang-mode)) +(require 'sclang-util) +(require 'sclang-interp) +(require 'sclang-language) +(require 'sclang-mode) (defcustom sclang-server-panel "Server.default.makeWindow" "Expression to execute when `sclang-show-server-panel' is invoked." diff --git a/el/sclang-widgets.el b/el/sclang-widgets.el index 45f9475..d5aa3c6 100644 --- a/el/sclang-widgets.el +++ b/el/sclang-widgets.el @@ -27,9 +27,9 @@ ;;; Code: (require 'cl-lib) -(eval-when-compile (require 'sclang-util) - (require 'sclang-language)) -(eval-and-compile (require 'sclang-interp)) +(require 'sclang-util) +(require 'sclang-language) +(require 'sclang-interp) (defvar sclang-widgets nil) (make-variable-buffer-local 'sclang-widgets) From bc06627f243d33d70f415ed70171804c733d3314 Mon Sep 17 00:00:00 2001 From: John Andrews Date: Mon, 9 Aug 2021 15:58:08 -0400 Subject: [PATCH 05/17] Enable github CI workflow --- .github/workflows/test.yml | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e7df509 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,42 @@ +name: CI + +on: + push: + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + emacs_version: + - 27.2 + - 26.3 + - 25.3 + + steps: + - name: Set up Emacs + uses: purcell/setup-emacs@master + with: + version: ${{matrix.emacs_version}} + + - name: Install Eldev + run: curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/github-eldev | sh + + - name: Check out the elisp source code + uses: actions/checkout@v2 + + - name: Test the uncompiled source + run: | + cd el + eldev -p -dtT test + + - name: Compile and run tests again + run: | + cd el + eldev compile + eldev -p -dtT test From c414b9c7abacd7b59ff9cae34e3334669b2d72cb Mon Sep 17 00:00:00 2001 From: John Andrews Date: Mon, 9 Aug 2021 22:34:47 -0400 Subject: [PATCH 06/17] Combine sclang-mode into the main sclang file Because the only autoloaded function is sclang-mode, the sclang file was not being evaluated. This made it impossible to use-package properly in order to configure the package. Combining the files makes the most sense as sclang.el wasn't doing much anyways. --- el/sclang-help.el | 1 - el/sclang-minor-mode.el | 1 - el/sclang-mode.el | 703 ---------------------------------------- el/sclang-server.el | 1 - el/sclang-util.el | 2 +- el/sclang.el | 702 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 687 insertions(+), 723 deletions(-) delete mode 100644 el/sclang-mode.el diff --git a/el/sclang-help.el b/el/sclang-help.el index 46e1c1a..8580d45 100644 --- a/el/sclang-help.el +++ b/el/sclang-help.el @@ -23,7 +23,6 @@ (require 'sclang-util) (require 'sclang-interp) (require 'sclang-language) -(require 'sclang-mode) (require 'sclang-vars nil 'ignore-missing-file) (require 'sclang-minor-mode) diff --git a/el/sclang-minor-mode.el b/el/sclang-minor-mode.el index c5e2a47..98b4e13 100644 --- a/el/sclang-minor-mode.el +++ b/el/sclang-minor-mode.el @@ -17,7 +17,6 @@ ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA (require 'sclang-util) -(require 'sclang-mode) (easy-mmode-define-minor-mode sclang-minor-mode "Toggle sclang-minor-mode. diff --git a/el/sclang-mode.el b/el/sclang-mode.el deleted file mode 100644 index 0f42491..0000000 --- a/el/sclang-mode.el +++ /dev/null @@ -1,703 +0,0 @@ -;; copyright 2003-2005 stefan kersten -;; -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License as -;; published by the Free Software Foundation; either version 2 of the -;; License, or (at your option) any later version. -;; -;; This program is distributed in the hope that it will be useful, but -;; WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -;; General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this program; if not, write to the Free Software -;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -;; USA - -;;; Code: - -(require 'cl-lib) - -;; Make byte-compiler happy by declaring external functions and -;; variables. -(declare-function company-mode "ext:company") -(defvar company-backends) - -(require 'font-lock) -(require 'sclang-util) -(require 'sclang-interp) -(require 'sclang-language) -(require 'sclang-dev) - -(defun sclang-fill-syntax-table (table) - ;; string - (modify-syntax-entry ?\" "\"" table) - (modify-syntax-entry ?\' "\"" table) ; no string syntax class for single quotes - ;; expression prefix - (modify-syntax-entry ?~ "'" table) - ;; escape - (modify-syntax-entry ?\\ "\\" table) - ;; character quote - (modify-syntax-entry ?$ "/" table) - ;; symbol - (modify-syntax-entry ?_ "_" table) - ;; symbol/punctuation - (modify-syntax-entry ?! "." table) - (modify-syntax-entry ?% "." table) - (modify-syntax-entry ?& "." table) - (modify-syntax-entry ?* ". 23n" table) - (modify-syntax-entry ?+ "." table) - (modify-syntax-entry ?- "." table) - (modify-syntax-entry ?/ ". 124b" table) - (modify-syntax-entry ?< "." table) - (modify-syntax-entry ?= "." table) - (modify-syntax-entry ?> "." table) - (modify-syntax-entry ?? "." table) - (modify-syntax-entry ?@ "." table) - (modify-syntax-entry ?| "." table) - ;; punctuation - (modify-syntax-entry ?: "." table) - (modify-syntax-entry ?\; "." table) - (modify-syntax-entry ?^ "." table) - ;; parenthesis - (modify-syntax-entry ?\( "()" table) - (modify-syntax-entry ?\) ")(" table) - (modify-syntax-entry ?\[ "(]" table) - (modify-syntax-entry ?\] ")[" table) - (modify-syntax-entry ?\{ "(}" table) - (modify-syntax-entry ?\} "){" table) - ;; comment end - (modify-syntax-entry ?\n "> b" table) - ;; Give CR the same syntax as newline, for selective-display - (modify-syntax-entry ?\^m "> b" table) - ;; return table - table) - -(defun sclang-mode-make-menu (title) - (easy-menu-create-menu - title - '( - ["Start Interpreter" sclang-start :included (not (sclang-library-initialized-p))] - ["Restart Interpreter" sclang-start :included (sclang-library-initialized-p)] - ["Recompile Class Library" sclang-recompile :included (sclang-library-initialized-p)] - ["Stop Interpreter" sclang-stop :included (sclang-get-process)] - ["Kill Interpreter" sclang-kill :included (sclang-get-process)] - "-" - ["Show Post Buffer" sclang-show-post-buffer] - ["Clear Post Buffer" sclang-clear-post-buffer] - "-" - ["Switch To Workspace" sclang-switch-to-workspace] - "-" - ["Evaluate Region" sclang-eval-region] - ["Evaluate Line" sclang-eval-region-or-line] - ["Evaluate Defun" sclang-eval-defun] - ["Evaluate Expression ..." sclang-eval-expression] - ["Evaluate Document" sclang-eval-document] - "-" - ["Find Definitions ..." sclang-find-definitions] - ["Find References ..." sclang-find-references] - ["Pop Mark" sclang-pop-definition-mark] - ["Show Method Arguments" sclang-show-method-args] - ["Complete keyword" sclang-complete-symbol] - ["Dump Interface" sclang-dump-interface] - ["Dump Full Interface" sclang-dump-full-interface] - "-" - ["Index Help Topics" sclang-index-help-topics] - ["Find Help ..." sclang-find-help] - ["Switch to Help Browser" sclang-goto-help-browser] - ["Open Help GUI" sclang-open-help-gui] - "-" - ["Run Main" sclang-main-run] - ["Stop Main" sclang-main-stop] - ["Show Server Panels" sclang-show-server-panel] - ))) - -(defun sclang-fill-mode-map (map) - ;; process control - (define-key map "\C-c\C-l" 'sclang-recompile) - (define-key map "\C-c\C-o" 'sclang-start) - ;; post buffer control - (define-key map "\C-c<" 'sclang-clear-post-buffer) - (define-key map "\C-c>" 'sclang-show-post-buffer) - ;; workspace access - (define-key map "\C-c\C-w" 'sclang-switch-to-workspace) - ;; code evaluation - (define-key map "\C-c\C-c" 'sclang-eval-region-or-line) - (define-key map "\C-c\C-d" 'sclang-eval-region) - (define-key map "\C-\M-x" 'sclang-eval-defun) - (define-key map "\C-c\C-e" 'sclang-eval-expression) - (define-key map "\C-c\C-f" 'sclang-eval-document) - ;; language information - (define-key map "\C-c\C-n" 'sclang-complete-symbol) - (define-key map "\C-c:" 'sclang-find-definitions) - (define-key map "\C-c;" 'sclang-find-references) - (define-key map "\C-c}" 'sclang-pop-definition-mark) - (define-key map "\C-c\C-m" 'sclang-show-method-args) - (define-key map "\C-c{" 'sclang-dump-full-interface) - (define-key map "\C-c[" 'sclang-dump-interface) - ;; documentation access - (define-key map "\C-c\C-h" 'sclang-find-help) - (define-key map "\C-\M-h" 'sclang-goto-help-browser) - (define-key map "\C-c\C-y" 'sclang-open-help-gui) - (define-key map "\C-ch" 'sclang-find-help-in-gui) - ;; language control - (define-key map "\C-c\C-r" 'sclang-main-run) - (define-key map "\C-c\C-s" 'sclang-main-stop) - (define-key map "\C-c\C-p" 'sclang-show-server-panel) - (define-key map "\C-c\C-k" 'sclang-edit-dev-source) - ;; electric characters - (define-key map "}" 'sclang-electric-brace) - (define-key map ")" 'sclang-electric-brace) - (define-key map "]" 'sclang-electric-brace) - (define-key map "/" 'sclang-electric-slash) - (define-key map "*" 'sclang-electric-star) - ;; menu - (let ((title "SCLang")) - (define-key map [menu-bar sclang] (cons title (sclang-mode-make-menu title)))) - ;; return map - map) - -;; ===================================================================== -;; font-lock support -;; ===================================================================== - -(defconst sclang-font-lock-keyword-list - '( - "arg" - "classvar" - "const" - "super" - "this" - "thisFunction" - "thisFunctionDef" - "thisMethod" - "thisProcess" - "thisThread" - "var" - ) - "*List of keywords to highlight in SCLang mode.") - -(defconst sclang-font-lock-builtin-list - '( - "false" - "inf" - "nil" - "true" - ) - "*List of builtins to highlight in SCLang mode.") - -(defconst sclang-font-lock-method-list - '( - "ar" - "for" - "forBy" - "if" - "ir" - "kr" - "tr" - "loop" - "while" - ) - "*List of methods to highlight in SCLang mode.") - -(defconst sclang-font-lock-error-list - '( - "die" - "error" - "exit" - "halt" - "verboseHalt" - "warn" - ) - "*List of methods signalling errors or warnings.") - -(defvar sclang-font-lock-class-keywords nil) - -(defvar sclang-font-lock-keywords-1 nil - "Subdued level highlighting for SCLang mode.") - -(defvar sclang-font-lock-keywords-2 nil - "Medium level highlighting for SCLang mode.") - -(defvar sclang-font-lock-keywords-3 nil - "Gaudy level highlighting for SCLang mode.") - -(defvar sclang-font-lock-keywords nil - "Default expressions to highlight in SCLang mode.") - -(defconst sclang-font-lock-defaults '((sclang-font-lock-keywords - sclang-font-lock-keywords-1 - sclang-font-lock-keywords-2 - sclang-font-lock-keywords-3 - ) - nil nil - nil - beginning-of-defun - )) - -(defun sclang-font-lock-syntactic-face (state) - (cond ((eq (nth 3 state) ?') - ;; symbol - 'font-lock-constant-face) - ((nth 3 state) - ;; string - 'font-lock-string-face) - ((nth 4 state) - ;; comment - 'font-lock-comment-face))) - -(defun sclang-font-lock-class-keyword-matcher (limit) - (let ((regexp (concat "\\<" sclang-class-name-regexp "\\>")) - (case-fold-search nil) - (continue t) - (res nil)) - (while continue - (setq res (re-search-forward regexp limit t)) - (if (or (null res) (null sclang-class-list)) - (setq continue nil) - (let ((thing (thing-at-point 'word))) - (if (null thing) - (setq res nil continue nil) - (when (cl-position (substring-no-properties thing) sclang-class-list :test 'equal) - (setq continue nil)))))) - res)) - -(defun sclang-set-font-lock-keywords () - (setq - ;; level 1 - sclang-font-lock-keywords-1 - (list - ;; keywords - (cons (regexp-opt sclang-font-lock-keyword-list 'words) - 'font-lock-keyword-face) - ;; builtins - (cons (regexp-opt sclang-font-lock-builtin-list 'words) - 'font-lock-builtin-face) - ;; pi is a special case - (cons "\\<\\([0-9]+\\(\\.\\)\\)pi\\>" 'font-lock-builtin-face) - ;; constants - (cons "\\s/\\s\\?." 'font-lock-constant-face) ; characters - (cons (concat "\\\\\\(" sclang-symbol-regexp "\\)") - 'font-lock-constant-face) ; symbols - ) - ;; level 2 - sclang-font-lock-keywords-2 - (append - sclang-font-lock-keywords-1 - (list - ;; variables - (cons (concat "\\s'\\(" sclang-identifier-regexp "\\)") - 'font-lock-variable-name-face) ; environment variables - (cons (concat "\\<\\(" sclang-identifier-regexp "\\)\\>:") ; keyword arguments - 'font-lock-variable-name-face) - ;; method definitions - (cons sclang-method-definition-regexp - (list 1 'font-lock-function-name-face)) - ;; methods - (cons (regexp-opt sclang-font-lock-method-list 'words) - 'font-lock-function-name-face) - ;; errors - (cons (regexp-opt sclang-font-lock-error-list 'words) - 'font-lock-warning-face) - )) - ;; level 3 - sclang-font-lock-keywords-3 - (append - sclang-font-lock-keywords-2 - (list - ;; classes - (cons 'sclang-font-lock-class-keyword-matcher 'font-lock-type-face) -;; (cons (concat "\\<" sclang-class-name-regexp "\\>") 'font-lock-type-face) - )) - ;; default level - sclang-font-lock-keywords sclang-font-lock-keywords-1 - )) - -(defun sclang-update-font-lock () - "Update font-lock information in all sclang-mode buffers." - ;; too expensive - ;; (dolist (buffer (buffer-list)) - ;; (with-current-buffer buffer - ;; (and (eq major-mode 'sclang-mode) - ;; (eq t (car font-lock-keywords)) - ;; (setq font-lock-keywords (cdr font-lock-keywords))))) - (if (eq major-mode 'sclang-mode) - (font-lock-fontify-buffer))) - -;; ===================================================================== -;; indentation -;; ===================================================================== - -(defcustom sclang-indent-level 4 - "*Indentation offset for SCLang statements." - :group 'sclang-mode - :type 'integer) - -(defun sclang-indent-line () - "Indent current line as sclang code. -Return the amount the indentation changed by." - (let ((indent (calculate-sclang-indent)) - beg shift-amt - (case-fold-search nil) - (pos (- (point-max) (point)))) - (beginning-of-line) - (setq beg (point)) - (skip-chars-forward " \t") - (setq shift-amt (- indent (current-column))) - (if (zerop shift-amt) - (if (> (- (point-max) pos) (point)) - (goto-char (- (point-max) pos))) - (delete-region beg (point)) - (indent-to indent) - ;; if initial point was within line's indentation, position - ;; after the indentation, else stay at same point in text. - (if (> (- (point-max) pos) (point)) - (goto-char (- (point-max) pos)))) - shift-amt)) - -(defun calculate-sclang-indent (&optional parse-start) - "Return appropriate indentation for current line as sclang code. -Returns the column to indent to." - (save-excursion - (beginning-of-line) - (let ((indent-point (point)) - (case-fold-search nil) - state) - (if parse-start - (goto-char parse-start) - (beginning-of-defun)) - (while (< (point) indent-point) - (setq state (parse-partial-sexp (point) indent-point 0))) - (let* ((containing-sexp (nth 1 state)) - (inside-string-p (nth 3 state)) - (inside-comment-p (nth 4 state))) - (cond (inside-string-p - ;; inside string: no change - (current-indentation)) - ((integerp inside-comment-p) - ;; inside comment - (let ((base (if containing-sexp - (save-excursion - (goto-char containing-sexp) - (+ (current-indentation) sclang-indent-level)) - 0)) - (offset (* sclang-indent-level - (- inside-comment-p - (if (save-excursion - (back-to-indentation) - (looking-at "\\*/")) - 1 0))))) - (+ base offset))) - ((null containing-sexp) - ;; top-level: no indentation - 0) - (t - (back-to-indentation) - (let ((open-paren (and (looking-at "\\s)") - (matching-paren (char-after)))) - (indent (current-indentation))) - (goto-char containing-sexp) - (if (or (not open-paren) (eq open-paren (char-after))) - (cond ((progn (beginning-of-line) (looking-at sclang-block-regexp)) 0) - (open-paren (current-indentation)) - (t (+ (current-indentation) sclang-indent-level))) - ;; paren mismatch: do nothing - indent)))))))) - -;; ===================================================================== -;; electric character commands -;; ===================================================================== - -(defun sclang-electric-brace (arg) - (interactive "*P") - (self-insert-command (prefix-numeric-value arg)) - (and (save-excursion - (beginning-of-line) - (looking-at "\\s *\\s)")) - (indent-according-to-mode))) - -(defun sclang-electric-slash (arg) - (interactive "*P") - (let* ((char (char-before)) - (indent-p (or (eq char ?/) - (eq char ?*)))) - (self-insert-command (prefix-numeric-value arg)) - (if indent-p (indent-according-to-mode)))) - -(defun sclang-electric-star (arg) - (interactive "*P") - (let ((indent-p (eq (char-before) ?/))) - (self-insert-command (prefix-numeric-value arg)) - (if indent-p (indent-according-to-mode)))) - -;; ===================================================================== -;; document interface -;; ===================================================================== - -(defvar sclang-document-id nil) -(defvar sclang-document-state nil) -(defvar sclang-document-envir nil) - -(defvar sclang-document-counter 0) -(defvar sclang-document-list nil) -(defvar sclang-current-document nil - "Currently active document.") - -(defvar sclang-document-idle-timer nil) - -(defconst sclang-document-property-map - '((sclang-document-name . (prSetTitle (buffer-name))) - (sclang-document-path . (prSetFileName (buffer-file-name))) - (sclang-document-listener-p . (prSetIsListener (eq (current-buffer) (sclang-get-post-buffer)))) - (sclang-document-editable-p . (prSetEditable (not buffer-read-only))) - (sclang-document-edited-p . (prSetEdited (buffer-modified-p))))) - -(defmacro sclang-next-document-id () - `(cl-incf sclang-document-counter)) - -(defun sclang-document-id (buffer) - (cdr (assq 'sclang-document-id (buffer-local-variables buffer)))) - -(defun sclang-document-p (buffer) - (integerp (sclang-document-id buffer))) - -(defmacro with-sclang-document (buffer &rest body) - `(when (sclang-document-p buffer) - (with-current-buffer buffer - ,@body))) - -(defun sclang-get-document (id) - (cl-find-if (lambda (buffer) (eq id (sclang-document-id buffer))) - sclang-document-list)) - -(defun sclang-init-document () - (set (make-local-variable 'sclang-document-id) (sclang-next-document-id)) - (set (make-local-variable 'sclang-document-envir) nil) - (dolist (assoc sclang-document-property-map) - (set (make-local-variable (car assoc)) nil)) - (cl-pushnew (current-buffer) sclang-document-list)) - -(defun sclang-document-update-property-1 (assoc &optional force) - (when (consp assoc) - (let* ((key (car assoc)) - (prop (cdr assoc)) - (prev-value (eval key)) - (cur-value (eval (cadr prop)))) - (when (or force (not (equal prev-value cur-value))) - (set key cur-value) - (sclang-perform-command-no-result - 'documentSetProperty sclang-document-id - (car prop) cur-value))))) - -(defun sclang-document-update-property (key &optional force) - (sclang-document-update-property-1 (assq key sclang-document-property-map) force)) - -(defun sclang-document-update-properties (&optional force) - (dolist (assoc sclang-document-property-map) - (sclang-document-update-property-1 assoc force))) - -(defun sclang-make-document () - (sclang-perform-command-no-result 'documentNew sclang-document-id) - (sclang-document-update-properties t)) - -(defun sclang-close-document (buffer) - (with-sclang-document - buffer - (setq sclang-document-list (delq buffer sclang-document-list)) - (sclang-perform-command-no-result - 'documentClosed sclang-document-id))) - -(defun sclang-set-current-document (buffer &optional force) - (when (or force (not (eq buffer sclang-current-document))) - (setq sclang-current-document buffer) - (sclang-perform-command-no-result 'documentSetCurrent (sclang-document-id buffer)) - t)) - -(defun sclang-document-library-startup-hook-function () - (dolist (buffer sclang-document-list) - (with-current-buffer buffer - (sclang-make-document))) - (sclang-set-current-document (current-buffer) t)) - -(defun sclang-document-kill-buffer-hook-function () - (sclang-close-document (current-buffer))) - -(defun sclang-document-post-command-hook-function () - (when (and (sclang-library-initialized-p) - (sclang-document-p (current-buffer))) - (sclang-document-update-properties)) - (sclang-set-current-document (current-buffer))) - -(defun sclang-document-change-major-mode-hook-function () - (sclang-close-document (current-buffer))) - -;; ===================================================================== -;; command handlers -;; ===================================================================== - -(sclang-set-command-handler - '_documentOpen - (lambda (arg) - (cl-multiple-value-bind (file-name region-start region-length) arg - (let ((buffer (get-file-buffer file-name))) - (unless buffer - (setf buffer (find-file-noselect file-name))) - (when buffer - (unless (sclang-document-p buffer) - (with-current-buffer buffer (sclang-mode))) - (goto-char (max (point-min) (min (point-max) region-start))) - ;; TODO: how to activate region in transient-mark-mode? - (sclang-document-id buffer)))))) - -(sclang-set-command-handler - '_documentNew - (lambda (arg) - (cl-multiple-value-bind (name str make-listener) arg - (let ((buffer (generate-new-buffer name))) - (with-current-buffer buffer - (insert str) - (set-buffer-modified-p nil) - (sclang-mode)) - (sclang-document-id buffer))))) - -(sclang-set-command-handler - '_documentClose - (lambda (arg) - (let ((doc (and (integerp arg) (sclang-get-document arg)))) - (and doc (kill-buffer doc))) - nil)) - -(sclang-set-command-handler - '_documentRename - (lambda (arg) - (cl-multiple-value-bind (id name) arg - (when (stringp name) - (let ((doc (and (integerp id) (sclang-get-document id)))) - (when doc - (with-current-buffer doc - (rename-buffer name t) - (sclang-document-update-property 'sclang-document-name)))))) - nil)) - -(sclang-set-command-handler - '_documentSetEditable - (lambda (arg) - (cl-multiple-value-bind (id flag) arg - (let ((doc (and (integerp id) (sclang-get-document id)))) - (when doc - (with-current-buffer doc - (setq buffer-read-only (not flag)) - (sclang-document-update-property 'sclang-editable-p))))) - nil)) - -(sclang-set-command-handler - '_documentSwitchTo - (lambda (arg) - (let ((doc (and (integerp arg) (sclang-get-document arg)))) - (and doc (switch-to-buffer doc))) - nil)) - -(sclang-set-command-handler - '_documentPutString -(lambda (arg) - (cl-multiple-value-bind (id str) arg - (let ((doc (and (integerp id) (sclang-get-document id)))) - (when doc - (with-current-buffer doc - (insert str) - ) - nil))))) - -(sclang-set-command-handler - '_documentPopTo - (lambda (arg) - (let ((doc (and (integerp arg) (sclang-get-document arg)))) - (and doc (display-buffer doc))) - nil)) - -;; ===================================================================== -;; sclang-mode -;; ===================================================================== - -(defun sclang-mode-set-local-variables () - (set (make-local-variable 'require-final-newline) nil) - ;; indentation - (set (make-local-variable 'indent-line-function) - 'sclang-indent-line) - (set (make-local-variable 'tab-width) 4) - (set (make-local-variable 'indent-tabs-mode) t) - ;; comment formatting - (set (make-local-variable 'comment-start) "// ") - (set (make-local-variable 'comment-end) "") - (set (make-local-variable 'comment-column) 40) - (set (make-local-variable 'comment-start-skip) "/\\*+ *\\|//+ *") - ;; "\\(^\\|\\s-\\);?// *") - (set (make-local-variable 'comment-multi-line) t) - ;; parsing and movement - (set (make-local-variable 'parse-sexp-ignore-comments) t) - (set (make-local-variable 'beginning-of-defun-function) - 'sclang-beginning-of-defun) - (set (make-local-variable 'end-of-defun-function) - 'sclang-end-of-defun) - ;; paragraph formatting - ;; (set (make-local-variable 'paragraph-start) (concat "$\\|" page-delimiter)) - ;; mostly copied from c++-mode, seems to work - (set (make-local-variable 'paragraph-start) - "[ \t]*\\(//+\\|\\**\\)[ \t]*$\\|^ ") - (set (make-local-variable 'paragraph-separate) paragraph-start) - (set (make-local-variable 'paragraph-ignore-fill-prefix) t) - (set (make-local-variable 'adaptive-fill-mode) t) - (set (make-local-variable 'adaptive-fill-regexp) - "[ \t]*\\(//+\\|\\**\\)[ \t]*\\([ \t]*\\([-|#;>*]+[ \t]*\\|(?[0-9]+[.)][ \t]*\\)*\\)") - ;; font lock - (set (make-local-variable 'font-lock-syntactic-face-function) - 'sclang-font-lock-syntactic-face) - (set (make-local-variable 'font-lock-defaults) - sclang-font-lock-defaults) - ;; --- - nil) - -(defvar sclang-mode-map (sclang-fill-mode-map (make-sparse-keymap)) - "Keymap used in SuperCollider mode.") - -(defvar sclang-mode-syntax-table (sclang-fill-syntax-table (make-syntax-table)) - "Syntax table used in SuperCollider mode.") - -(defcustom sclang-mode-hook nil - "*Hook run when entering SCLang mode." - :group 'sclang-mode - :type 'hook) - -;;;###autoload -(define-derived-mode sclang-mode prog-mode "SCLang" - "Major mode for editing SuperCollider language code. -\\{sclang-mode-map}" - :group 'sclang - :syntax-table sclang-mode-syntax-table - (sclang-mode-set-local-variables) - (sclang-set-font-lock-keywords) - (sclang-init-document) - (sclang-make-document) - - ;; Setup completion - (add-hook 'completion-at-point-functions - #'sclang-completion-at-point nil 'local) - (when (fboundp 'company-mode) - (add-to-list 'company-backends 'company-capf))) - -;; ===================================================================== -;; module initialization -;; ===================================================================== - -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.scd?\\'" . sclang-mode)) -(add-to-list 'interpreter-mode-alist '("sclang" . sclang-mode)) - -(add-hook 'sclang-library-startup-hook 'sclang-document-library-startup-hook-function) -(add-hook 'kill-buffer-hook 'sclang-document-kill-buffer-hook-function) -(add-hook 'post-command-hook 'sclang-document-post-command-hook-function) -(add-hook 'change-major-mode-hook 'sclang-document-change-major-mode-hook-function) - -(provide 'sclang-mode) -;;; sclang-mode ends here diff --git a/el/sclang-server.el b/el/sclang-server.el index 375c492..f2bfa27 100644 --- a/el/sclang-server.el +++ b/el/sclang-server.el @@ -19,7 +19,6 @@ (require 'sclang-util) (require 'sclang-interp) (require 'sclang-language) -(require 'sclang-mode) (defcustom sclang-server-panel "Server.default.makeWindow" "Expression to execute when `sclang-show-server-panel' is invoked." diff --git a/el/sclang-util.el b/el/sclang-util.el index ea104cb..7c33d35 100644 --- a/el/sclang-util.el +++ b/el/sclang-util.el @@ -83,4 +83,4 @@ (provide 'sclang-util) -;; EOF \ No newline at end of file +;; EOF diff --git a/el/sclang.el b/el/sclang.el index 9bbedcc..8f6d990 100644 --- a/el/sclang.el +++ b/el/sclang.el @@ -26,6 +26,24 @@ ;; for more information. ;;; Code: + +(require 'cl-lib) +(require 'font-lock) +(require 'sclang-util) +(require 'sclang-browser) +(require 'sclang-interp) +(require 'sclang-language) +(require 'sclang-document) +(require 'sclang-minor-mode) +(require 'sclang-help) +(require 'sclang-server) +(require 'sclang-widgets) + +;; Make byte-compiler happy by declaring external functions and +;; variables. +(declare-function company-mode "ext:company") +(defvar company-backends) + (defgroup sclang nil "IDE for working with the SuperCollider language." :group 'languages) @@ -55,22 +73,674 @@ (interactive) (customize-group 'sclang)) -(eval-and-compile - (let ((load-path - (if (and (boundp 'byte-compile-dest-file) - (stringp byte-compile-dest-file)) - (cons (file-name-directory byte-compile-dest-file) load-path) - load-path))) - (require 'sclang-util) - (require 'sclang-browser) - (require 'sclang-interp) - (require 'sclang-language) - (require 'sclang-document) - (require 'sclang-mode) - (require 'sclang-minor-mode) - (require 'sclang-help) - (require 'sclang-server) - (require 'sclang-widgets))) +(defun sclang-fill-syntax-table (table) + ;; string + (modify-syntax-entry ?\" "\"" table) + (modify-syntax-entry ?\' "\"" table) ; no string syntax class for single quotes + ;; expression prefix + (modify-syntax-entry ?~ "'" table) + ;; escape + (modify-syntax-entry ?\\ "\\" table) + ;; character quote + (modify-syntax-entry ?$ "/" table) + ;; symbol + (modify-syntax-entry ?_ "_" table) + ;; symbol/punctuation + (modify-syntax-entry ?! "." table) + (modify-syntax-entry ?% "." table) + (modify-syntax-entry ?& "." table) + (modify-syntax-entry ?* ". 23n" table) + (modify-syntax-entry ?+ "." table) + (modify-syntax-entry ?- "." table) + (modify-syntax-entry ?/ ". 124b" table) + (modify-syntax-entry ?< "." table) + (modify-syntax-entry ?= "." table) + (modify-syntax-entry ?> "." table) + (modify-syntax-entry ?? "." table) + (modify-syntax-entry ?@ "." table) + (modify-syntax-entry ?| "." table) + ;; punctuation + (modify-syntax-entry ?: "." table) + (modify-syntax-entry ?\; "." table) + (modify-syntax-entry ?^ "." table) + ;; parenthesis + (modify-syntax-entry ?\( "()" table) + (modify-syntax-entry ?\) ")(" table) + (modify-syntax-entry ?\[ "(]" table) + (modify-syntax-entry ?\] ")[" table) + (modify-syntax-entry ?\{ "(}" table) + (modify-syntax-entry ?\} "){" table) + ;; comment end + (modify-syntax-entry ?\n "> b" table) + ;; Give CR the same syntax as newline, for selective-display + (modify-syntax-entry ?\^m "> b" table) + ;; return table + table) + +(defun sclang-mode-make-menu (title) + (easy-menu-create-menu + title + '( + ["Start Interpreter" sclang-start :included (not (sclang-library-initialized-p))] + ["Restart Interpreter" sclang-start :included (sclang-library-initialized-p)] + ["Recompile Class Library" sclang-recompile :included (sclang-library-initialized-p)] + ["Stop Interpreter" sclang-stop :included (sclang-get-process)] + ["Kill Interpreter" sclang-kill :included (sclang-get-process)] + "-" + ["Show Post Buffer" sclang-show-post-buffer] + ["Clear Post Buffer" sclang-clear-post-buffer] + "-" + ["Switch To Workspace" sclang-switch-to-workspace] + "-" + ["Evaluate Region" sclang-eval-region] + ["Evaluate Line" sclang-eval-region-or-line] + ["Evaluate Defun" sclang-eval-defun] + ["Evaluate Expression ..." sclang-eval-expression] + ["Evaluate Document" sclang-eval-document] + "-" + ["Find Definitions ..." sclang-find-definitions] + ["Find References ..." sclang-find-references] + ["Pop Mark" sclang-pop-definition-mark] + ["Show Method Arguments" sclang-show-method-args] + ["Complete keyword" sclang-complete-symbol] + ["Dump Interface" sclang-dump-interface] + ["Dump Full Interface" sclang-dump-full-interface] + "-" + ["Index Help Topics" sclang-index-help-topics] + ["Find Help ..." sclang-find-help] + ["Switch to Help Browser" sclang-goto-help-browser] + ["Open Help GUI" sclang-open-help-gui] + "-" + ["Run Main" sclang-main-run] + ["Stop Main" sclang-main-stop] + ["Show Server Panels" sclang-show-server-panel] + ))) + +(defun sclang-fill-mode-map (map) + ;; process control + (define-key map "\C-c\C-l" 'sclang-recompile) + (define-key map "\C-c\C-o" 'sclang-start) + ;; post buffer control + (define-key map "\C-c<" 'sclang-clear-post-buffer) + (define-key map "\C-c>" 'sclang-show-post-buffer) + ;; workspace access + (define-key map "\C-c\C-w" 'sclang-switch-to-workspace) + ;; code evaluation + (define-key map "\C-c\C-c" 'sclang-eval-region-or-line) + (define-key map "\C-c\C-d" 'sclang-eval-region) + (define-key map "\C-\M-x" 'sclang-eval-defun) + (define-key map "\C-c\C-e" 'sclang-eval-expression) + (define-key map "\C-c\C-f" 'sclang-eval-document) + ;; language information + (define-key map "\C-c\C-n" 'sclang-complete-symbol) + (define-key map "\C-c:" 'sclang-find-definitions) + (define-key map "\C-c;" 'sclang-find-references) + (define-key map "\C-c}" 'sclang-pop-definition-mark) + (define-key map "\C-c\C-m" 'sclang-show-method-args) + (define-key map "\C-c{" 'sclang-dump-full-interface) + (define-key map "\C-c[" 'sclang-dump-interface) + ;; documentation access + (define-key map "\C-c\C-h" 'sclang-find-help) + (define-key map "\C-\M-h" 'sclang-goto-help-browser) + (define-key map "\C-c\C-y" 'sclang-open-help-gui) + (define-key map "\C-ch" 'sclang-find-help-in-gui) + ;; language control + (define-key map "\C-c\C-r" 'sclang-main-run) + (define-key map "\C-c\C-s" 'sclang-main-stop) + (define-key map "\C-c\C-p" 'sclang-show-server-panel) + (define-key map "\C-c\C-k" 'sclang-edit-dev-source) + ;; electric characters + (define-key map "}" 'sclang-electric-brace) + (define-key map ")" 'sclang-electric-brace) + (define-key map "]" 'sclang-electric-brace) + (define-key map "/" 'sclang-electric-slash) + (define-key map "*" 'sclang-electric-star) + ;; menu + (let ((title "SCLang")) + (define-key map [menu-bar sclang] (cons title (sclang-mode-make-menu title)))) + ;; return map + map) + +;; ===================================================================== +;; font-lock support +;; ===================================================================== + +(defconst sclang-font-lock-keyword-list + '( + "arg" + "classvar" + "const" + "super" + "this" + "thisFunction" + "thisFunctionDef" + "thisMethod" + "thisProcess" + "thisThread" + "var" + ) + "*List of keywords to highlight in SCLang mode.") + +(defconst sclang-font-lock-builtin-list + '( + "false" + "inf" + "nil" + "true" + ) + "*List of builtins to highlight in SCLang mode.") + +(defconst sclang-font-lock-method-list + '( + "ar" + "for" + "forBy" + "if" + "ir" + "kr" + "tr" + "loop" + "while" + ) + "*List of methods to highlight in SCLang mode.") + +(defconst sclang-font-lock-error-list + '( + "die" + "error" + "exit" + "halt" + "verboseHalt" + "warn" + ) + "*List of methods signalling errors or warnings.") + +(defvar sclang-font-lock-class-keywords nil) + +(defvar sclang-font-lock-keywords-1 nil + "Subdued level highlighting for SCLang mode.") + +(defvar sclang-font-lock-keywords-2 nil + "Medium level highlighting for SCLang mode.") + +(defvar sclang-font-lock-keywords-3 nil + "Gaudy level highlighting for SCLang mode.") + +(defvar sclang-font-lock-keywords nil + "Default expressions to highlight in SCLang mode.") + +(defconst sclang-font-lock-defaults '((sclang-font-lock-keywords + sclang-font-lock-keywords-1 + sclang-font-lock-keywords-2 + sclang-font-lock-keywords-3 + ) + nil nil + nil + beginning-of-defun + )) + +(defun sclang-font-lock-syntactic-face (state) + (cond ((eq (nth 3 state) ?') + ;; symbol + 'font-lock-constant-face) + ((nth 3 state) + ;; string + 'font-lock-string-face) + ((nth 4 state) + ;; comment + 'font-lock-comment-face))) + +(defun sclang-font-lock-class-keyword-matcher (limit) + (let ((regexp (concat "\\<" sclang-class-name-regexp "\\>")) + (case-fold-search nil) + (continue t) + (res nil)) + (while continue + (setq res (re-search-forward regexp limit t)) + (if (or (null res) (null sclang-class-list)) + (setq continue nil) + (let ((thing (thing-at-point 'word))) + (if (null thing) + (setq res nil continue nil) + (when (cl-position (substring-no-properties thing) sclang-class-list :test 'equal) + (setq continue nil)))))) + res)) + +(defun sclang-set-font-lock-keywords () + (setq + ;; level 1 + sclang-font-lock-keywords-1 + (list + ;; keywords + (cons (regexp-opt sclang-font-lock-keyword-list 'words) + 'font-lock-keyword-face) + ;; builtins + (cons (regexp-opt sclang-font-lock-builtin-list 'words) + 'font-lock-builtin-face) + ;; pi is a special case + (cons "\\<\\([0-9]+\\(\\.\\)\\)pi\\>" 'font-lock-builtin-face) + ;; constants + (cons "\\s/\\s\\?." 'font-lock-constant-face) ; characters + (cons (concat "\\\\\\(" sclang-symbol-regexp "\\)") + 'font-lock-constant-face) ; symbols + ) + ;; level 2 + sclang-font-lock-keywords-2 + (append + sclang-font-lock-keywords-1 + (list + ;; variables + (cons (concat "\\s'\\(" sclang-identifier-regexp "\\)") + 'font-lock-variable-name-face) ; environment variables + (cons (concat "\\<\\(" sclang-identifier-regexp "\\)\\>:") ; keyword arguments + 'font-lock-variable-name-face) + ;; method definitions + (cons sclang-method-definition-regexp + (list 1 'font-lock-function-name-face)) + ;; methods + (cons (regexp-opt sclang-font-lock-method-list 'words) + 'font-lock-function-name-face) + ;; errors + (cons (regexp-opt sclang-font-lock-error-list 'words) + 'font-lock-warning-face) + )) + ;; level 3 + sclang-font-lock-keywords-3 + (append + sclang-font-lock-keywords-2 + (list + ;; classes + (cons 'sclang-font-lock-class-keyword-matcher 'font-lock-type-face) +;; (cons (concat "\\<" sclang-class-name-regexp "\\>") 'font-lock-type-face) + )) + ;; default level + sclang-font-lock-keywords sclang-font-lock-keywords-1 + )) + +(defun sclang-update-font-lock () + "Update font-lock information in all sclang-mode buffers." + ;; too expensive + ;; (dolist (buffer (buffer-list)) + ;; (with-current-buffer buffer + ;; (and (eq major-mode 'sclang-mode) + ;; (eq t (car font-lock-keywords)) + ;; (setq font-lock-keywords (cdr font-lock-keywords))))) + (if (eq major-mode 'sclang-mode) + (font-lock-fontify-buffer))) + +;; ===================================================================== +;; indentation +;; ===================================================================== + +(defcustom sclang-indent-level 4 + "*Indentation offset for SCLang statements." + :group 'sclang-mode + :type 'integer) + +(defun sclang-indent-line () + "Indent current line as sclang code. +Return the amount the indentation changed by." + (let ((indent (calculate-sclang-indent)) + beg shift-amt + (case-fold-search nil) + (pos (- (point-max) (point)))) + (beginning-of-line) + (setq beg (point)) + (skip-chars-forward " \t") + (setq shift-amt (- indent (current-column))) + (if (zerop shift-amt) + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos))) + (delete-region beg (point)) + (indent-to indent) + ;; if initial point was within line's indentation, position + ;; after the indentation, else stay at same point in text. + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos)))) + shift-amt)) + +(defun calculate-sclang-indent (&optional parse-start) + "Return appropriate indentation for current line as sclang code. +Returns the column to indent to." + (save-excursion + (beginning-of-line) + (let ((indent-point (point)) + (case-fold-search nil) + state) + (if parse-start + (goto-char parse-start) + (beginning-of-defun)) + (while (< (point) indent-point) + (setq state (parse-partial-sexp (point) indent-point 0))) + (let* ((containing-sexp (nth 1 state)) + (inside-string-p (nth 3 state)) + (inside-comment-p (nth 4 state))) + (cond (inside-string-p + ;; inside string: no change + (current-indentation)) + ((integerp inside-comment-p) + ;; inside comment + (let ((base (if containing-sexp + (save-excursion + (goto-char containing-sexp) + (+ (current-indentation) sclang-indent-level)) + 0)) + (offset (* sclang-indent-level + (- inside-comment-p + (if (save-excursion + (back-to-indentation) + (looking-at "\\*/")) + 1 0))))) + (+ base offset))) + ((null containing-sexp) + ;; top-level: no indentation + 0) + (t + (back-to-indentation) + (let ((open-paren (and (looking-at "\\s)") + (matching-paren (char-after)))) + (indent (current-indentation))) + (goto-char containing-sexp) + (if (or (not open-paren) (eq open-paren (char-after))) + (cond ((progn (beginning-of-line) (looking-at sclang-block-regexp)) 0) + (open-paren (current-indentation)) + (t (+ (current-indentation) sclang-indent-level))) + ;; paren mismatch: do nothing + indent)))))))) + +;; ===================================================================== +;; electric character commands +;; ===================================================================== + +(defun sclang-electric-brace (arg) + (interactive "*P") + (self-insert-command (prefix-numeric-value arg)) + (and (save-excursion + (beginning-of-line) + (looking-at "\\s *\\s)")) + (indent-according-to-mode))) + +(defun sclang-electric-slash (arg) + (interactive "*P") + (let* ((char (char-before)) + (indent-p (or (eq char ?/) + (eq char ?*)))) + (self-insert-command (prefix-numeric-value arg)) + (if indent-p (indent-according-to-mode)))) + +(defun sclang-electric-star (arg) + (interactive "*P") + (let ((indent-p (eq (char-before) ?/))) + (self-insert-command (prefix-numeric-value arg)) + (if indent-p (indent-according-to-mode)))) + +;; ===================================================================== +;; document interface +;; ===================================================================== + +(defvar sclang-document-id nil) +(defvar sclang-document-state nil) +(defvar sclang-document-envir nil) + +(defvar sclang-document-counter 0) +(defvar sclang-document-list nil) +(defvar sclang-current-document nil + "Currently active document.") + +(defvar sclang-document-idle-timer nil) + +(defconst sclang-document-property-map + '((sclang-document-name . (prSetTitle (buffer-name))) + (sclang-document-path . (prSetFileName (buffer-file-name))) + (sclang-document-listener-p . (prSetIsListener (eq (current-buffer) (sclang-get-post-buffer)))) + (sclang-document-editable-p . (prSetEditable (not buffer-read-only))) + (sclang-document-edited-p . (prSetEdited (buffer-modified-p))))) + +(defmacro sclang-next-document-id () + `(cl-incf sclang-document-counter)) + +(defun sclang-document-id (buffer) + (cdr (assq 'sclang-document-id (buffer-local-variables buffer)))) + +(defun sclang-document-p (buffer) + (integerp (sclang-document-id buffer))) + +(defmacro with-sclang-document (buffer &rest body) + `(when (sclang-document-p buffer) + (with-current-buffer buffer + ,@body))) + +(defun sclang-get-document (id) + (cl-find-if (lambda (buffer) (eq id (sclang-document-id buffer))) + sclang-document-list)) + +(defun sclang-init-document () + (set (make-local-variable 'sclang-document-id) (sclang-next-document-id)) + (set (make-local-variable 'sclang-document-envir) nil) + (dolist (assoc sclang-document-property-map) + (set (make-local-variable (car assoc)) nil)) + (cl-pushnew (current-buffer) sclang-document-list)) + +(defun sclang-document-update-property-1 (assoc &optional force) + (when (consp assoc) + (let* ((key (car assoc)) + (prop (cdr assoc)) + (prev-value (eval key)) + (cur-value (eval (cadr prop)))) + (when (or force (not (equal prev-value cur-value))) + (set key cur-value) + (sclang-perform-command-no-result + 'documentSetProperty sclang-document-id + (car prop) cur-value))))) + +(defun sclang-document-update-property (key &optional force) + (sclang-document-update-property-1 (assq key sclang-document-property-map) force)) + +(defun sclang-document-update-properties (&optional force) + (dolist (assoc sclang-document-property-map) + (sclang-document-update-property-1 assoc force))) + +(defun sclang-make-document () + (sclang-perform-command-no-result 'documentNew sclang-document-id) + (sclang-document-update-properties t)) + +(defun sclang-close-document (buffer) + (with-sclang-document + buffer + (setq sclang-document-list (delq buffer sclang-document-list)) + (sclang-perform-command-no-result + 'documentClosed sclang-document-id))) + +(defun sclang-set-current-document (buffer &optional force) + (when (or force (not (eq buffer sclang-current-document))) + (setq sclang-current-document buffer) + (sclang-perform-command-no-result 'documentSetCurrent (sclang-document-id buffer)) + t)) + +(defun sclang-document-library-startup-hook-function () + (dolist (buffer sclang-document-list) + (with-current-buffer buffer + (sclang-make-document))) + (sclang-set-current-document (current-buffer) t)) + +(defun sclang-document-kill-buffer-hook-function () + (sclang-close-document (current-buffer))) + +(defun sclang-document-post-command-hook-function () + (when (and (sclang-library-initialized-p) + (sclang-document-p (current-buffer))) + (sclang-document-update-properties)) + (sclang-set-current-document (current-buffer))) + +(defun sclang-document-change-major-mode-hook-function () + (sclang-close-document (current-buffer))) + +;; ===================================================================== +;; command handlers +;; ===================================================================== + +(sclang-set-command-handler + '_documentOpen + (lambda (arg) + (cl-multiple-value-bind (file-name region-start region-length) arg + (let ((buffer (get-file-buffer file-name))) + (unless buffer + (setf buffer (find-file-noselect file-name))) + (when buffer + (unless (sclang-document-p buffer) + (with-current-buffer buffer (sclang-mode))) + (goto-char (max (point-min) (min (point-max) region-start))) + ;; TODO: how to activate region in transient-mark-mode? + (sclang-document-id buffer)))))) + +(sclang-set-command-handler + '_documentNew + (lambda (arg) + (cl-multiple-value-bind (name str make-listener) arg + (let ((buffer (generate-new-buffer name))) + (with-current-buffer buffer + (insert str) + (set-buffer-modified-p nil) + (sclang-mode)) + (sclang-document-id buffer))))) + +(sclang-set-command-handler + '_documentClose + (lambda (arg) + (let ((doc (and (integerp arg) (sclang-get-document arg)))) + (and doc (kill-buffer doc))) + nil)) + +(sclang-set-command-handler + '_documentRename + (lambda (arg) + (cl-multiple-value-bind (id name) arg + (when (stringp name) + (let ((doc (and (integerp id) (sclang-get-document id)))) + (when doc + (with-current-buffer doc + (rename-buffer name t) + (sclang-document-update-property 'sclang-document-name)))))) + nil)) + +(sclang-set-command-handler + '_documentSetEditable + (lambda (arg) + (cl-multiple-value-bind (id flag) arg + (let ((doc (and (integerp id) (sclang-get-document id)))) + (when doc + (with-current-buffer doc + (setq buffer-read-only (not flag)) + (sclang-document-update-property 'sclang-editable-p))))) + nil)) + +(sclang-set-command-handler + '_documentSwitchTo + (lambda (arg) + (let ((doc (and (integerp arg) (sclang-get-document arg)))) + (and doc (switch-to-buffer doc))) + nil)) + +(sclang-set-command-handler + '_documentPutString +(lambda (arg) + (cl-multiple-value-bind (id str) arg + (let ((doc (and (integerp id) (sclang-get-document id)))) + (when doc + (with-current-buffer doc + (insert str) + ) + nil))))) + +(sclang-set-command-handler + '_documentPopTo + (lambda (arg) + (let ((doc (and (integerp arg) (sclang-get-document arg)))) + (and doc (display-buffer doc))) + nil)) + +;; ===================================================================== +;; sclang-mode +;; ===================================================================== + +(defun sclang-mode-set-local-variables () + (set (make-local-variable 'require-final-newline) nil) + ;; indentation + (set (make-local-variable 'indent-line-function) + 'sclang-indent-line) + (set (make-local-variable 'tab-width) 4) + (set (make-local-variable 'indent-tabs-mode) t) + ;; comment formatting + (set (make-local-variable 'comment-start) "// ") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-column) 40) + (set (make-local-variable 'comment-start-skip) "/\\*+ *\\|//+ *") + ;; "\\(^\\|\\s-\\);?// *") + (set (make-local-variable 'comment-multi-line) t) + ;; parsing and movement + (set (make-local-variable 'parse-sexp-ignore-comments) t) + (set (make-local-variable 'beginning-of-defun-function) + 'sclang-beginning-of-defun) + (set (make-local-variable 'end-of-defun-function) + 'sclang-end-of-defun) + ;; paragraph formatting + ;; (set (make-local-variable 'paragraph-start) (concat "$\\|" page-delimiter)) + ;; mostly copied from c++-mode, seems to work + (set (make-local-variable 'paragraph-start) + "[ \t]*\\(//+\\|\\**\\)[ \t]*$\\|^ ") + (set (make-local-variable 'paragraph-separate) paragraph-start) + (set (make-local-variable 'paragraph-ignore-fill-prefix) t) + (set (make-local-variable 'adaptive-fill-mode) t) + (set (make-local-variable 'adaptive-fill-regexp) + "[ \t]*\\(//+\\|\\**\\)[ \t]*\\([ \t]*\\([-|#;>*]+[ \t]*\\|(?[0-9]+[.)][ \t]*\\)*\\)") + ;; font lock + (set (make-local-variable 'font-lock-syntactic-face-function) + 'sclang-font-lock-syntactic-face) + (set (make-local-variable 'font-lock-defaults) + sclang-font-lock-defaults) + ;; --- + nil) + +(defvar sclang-mode-map (sclang-fill-mode-map (make-sparse-keymap)) + "Keymap used in SuperCollider mode.") + +(defvar sclang-mode-syntax-table (sclang-fill-syntax-table (make-syntax-table)) + "Syntax table used in SuperCollider mode.") + +(defcustom sclang-mode-hook nil + "*Hook run when entering SCLang mode." + :group 'sclang-mode + :type 'hook) + +;;;###autoload +(define-derived-mode sclang-mode prog-mode "SCLang" + "Major mode for editing SuperCollider language code. +\\{sclang-mode-map}" + :group 'sclang + :syntax-table sclang-mode-syntax-table + (sclang-mode-set-local-variables) + (sclang-set-font-lock-keywords) + (sclang-init-document) + (sclang-make-document) + + ;; Setup completion + (add-hook 'completion-at-point-functions + #'sclang-completion-at-point nil 'local) + (when (fboundp 'company-mode) + (add-to-list 'company-backends 'company-capf))) + +;; ===================================================================== +;; module initialization +;; ===================================================================== + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.scd?\\'" . sclang-mode)) +(add-to-list 'interpreter-mode-alist '("sclang" . sclang-mode)) + +(add-hook 'sclang-library-startup-hook 'sclang-document-library-startup-hook-function) +(add-hook 'kill-buffer-hook 'sclang-document-kill-buffer-hook-function) +(add-hook 'post-command-hook 'sclang-document-post-command-hook-function) +(add-hook 'change-major-mode-hook 'sclang-document-change-major-mode-hook-function) (provide 'sclang) From cf3e692bb57a82a0b13659d3af4801ff45a88e1d Mon Sep 17 00:00:00 2001 From: John Andrews Date: Sun, 25 Jul 2021 19:55:00 -0400 Subject: [PATCH 07/17] Update installation docs --- CHANGELOG.md | 1 + README.md | 106 ++++++++++++++++++++++----------------------------- 2 files changed, 47 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02c3263..03618c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added + - Ability to distribute via package managers - Support for `completion-at-point-functions` and `company` via `company-capf` ### Changed diff --git a/README.md b/README.md index 74a9e36..4f49f8e 100644 --- a/README.md +++ b/README.md @@ -1,83 +1,80 @@ -Scel -==== +# scel - sclang-mode for emacs SuperCollider/Emacs interface +## Installation -Installation requirements -------------------------- +The repository contains two subprojects. `/sc` contains the SuperCollider code +required to implement the emacs interface. `/el` contains the emacs-lisp +implementation of the mode. Emacs and SuperCollider each have their own package +managers, so it is required to install each half separately. -For the HTML help system, you will need emacs-w3m support. +### Installing scel quark +The `scel` Quark is required for emacs to communicate with sclang. Evaluate this in the SuperCollider GUI: -Installation (default) ----------------------- - -By default emacs-lisp files are installed in - -`$prefix/share/emacs/site-lisp` - -SuperCollider files are put in +``` supercollider +Quarks.install("https://github.com/supercollider/scel"); +``` -`$prefix/share/SuperCollider/Extensions/scide_scel` +### Installing the emacs mode +Using straight.el +``` emacs-lisp +(straight-use-package + '(sclang :type git + :host github + :repo "supercollider/scel" + :files ("el/*.el"))) +``` -The only thing you need to do is loading the sclang interface in your `~/.emacs`: +Or download the repo directly to your user config directory +``` shell +git clone https://github.com/supercollider/scel.git ~/.emacs.d/scel ``` +``` emacs-lisp +(add-to-list 'load-path "~/.emacs.d/scel/el/") (require 'sclang) ``` -For the HTML help system to fully function also add -``` -(require 'w3m) +### On MacOS + +If `sclang` executable is not on your path, you may need to add it to your exec-path. + +``` emacs-lisp +(setq exec-path (append exec-path '("/Applications/SuperCollider.app/Contents/MacOS/"))) ``` +### On Linux +If you are building SuperCollider from source on Linux, this library (both .el +and .sc files) will be installed by default. To disable it pass the flag +`-DSC_EL=OFF` as a `cmake` option. See the supercollider readme for more info. -Installation (detailed) ------------------------ -Put all `*.el` files in emacs' load-path. e.g. if you put them in -`~/emacs/`, add the following lines to `~/.emacs` (or whatever your init -file is called): +## Installation requirements -``` -(add-to-list 'load-path "~/emacs") -(require 'sclang) -``` +For the HTML help system, you will need emacs-w3m support, but you can still use without that. -for the HTML help system to fully function also add -``` +```emacs-lisp (require 'w3m) ``` -now put all `*.sc` files in sclang's library path, e.g. if you put them -in a non-standard location, such as `~/SuperCollider/Emacs`, add the -following to `~/.config/SuperCollider/sclang_conf.yaml` (Linux) or `~/Library/Application Support/SuperCollider/sclang_conf.yaml` (macOS): +## Configuration -``` -includePaths: - [~/SuperCollider/Emacs] -``` +To fine-tune the installation from within emacs' graphical customization interface, type: -(note normally this is not needed as they are put into sclang's library -path during installation with scons). +`M-x sclang-customize` -Usage ------ +## Usage -In order to automatically start sclang when invoking emacs, use the following command line: +`M-x sclang-start` or open a `.scd` file and press `C-c C-o` -``` -$> emacs -sclang -``` - -you're now ready to edit, inspect and execute sclang code! +You're now ready to edit, inspect and execute sclang code! -Getting help ------------- +## Getting help Inside an sclang-mode buffer (e.g. by editing a .sc file), execute @@ -108,18 +105,7 @@ in your `~/.emacs`: This ensures that the arrow keys are just for moving through the document, and not from hyperlink to hyperlink, which is the default in w3m-mode. -Customization -------------- - -To fine-tune the installation from within emacs' graphical customization interface, type: - -`M-x sclang-customize` - -In particular, you will have to customize `sclang-runtime-directory'. - - -Server control --------------- +## Server control In the post buffer window, right-click on the server name; by default the two servers `internal` and `localhost` are available. You will get a menu with common server control operations. From 5ac51503972dbf345d8d9269cc6e6858aeae7c11 Mon Sep 17 00:00:00 2001 From: John Andrews Date: Sun, 15 Aug 2021 16:27:45 -0400 Subject: [PATCH 08/17] Rename sc directory so it only loads when calling from emacs --- CMakeLists.txt | 2 +- {sc => scide_scel}/CMakeLists.txt | 0 {sc => scide_scel}/Document.sc | 0 {sc => scide_scel}/Emacs.sc | 0 {sc => scide_scel}/EmacsBuffer.sc | 0 {sc => scide_scel}/EmacsDocument.sc | 0 {sc => scide_scel}/ScelDocument.sc | 0 {sc => scide_scel}/extBuffer.sc | 0 {sc => scide_scel}/extClassBrowser.sc | 0 {sc => scide_scel}/extString.sc | 0 {sc => scide_scel}/homeContext.sc | 0 {sc => scide_scel}/storeLispOn.sc | 0 12 files changed, 1 insertion(+), 1 deletion(-) rename {sc => scide_scel}/CMakeLists.txt (100%) rename {sc => scide_scel}/Document.sc (100%) rename {sc => scide_scel}/Emacs.sc (100%) rename {sc => scide_scel}/EmacsBuffer.sc (100%) rename {sc => scide_scel}/EmacsDocument.sc (100%) rename {sc => scide_scel}/ScelDocument.sc (100%) rename {sc => scide_scel}/extBuffer.sc (100%) rename {sc => scide_scel}/extClassBrowser.sc (100%) rename {sc => scide_scel}/extString.sc (100%) rename {sc => scide_scel}/homeContext.sc (100%) rename {sc => scide_scel}/storeLispOn.sc (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index e21cfa5..90d90d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(el) -add_subdirectory(sc) +add_subdirectory(scide_scel) install(DIRECTORY HelpSource DESTINATION share/SuperCollider/Extensions/scide_scel/) diff --git a/sc/CMakeLists.txt b/scide_scel/CMakeLists.txt similarity index 100% rename from sc/CMakeLists.txt rename to scide_scel/CMakeLists.txt diff --git a/sc/Document.sc b/scide_scel/Document.sc similarity index 100% rename from sc/Document.sc rename to scide_scel/Document.sc diff --git a/sc/Emacs.sc b/scide_scel/Emacs.sc similarity index 100% rename from sc/Emacs.sc rename to scide_scel/Emacs.sc diff --git a/sc/EmacsBuffer.sc b/scide_scel/EmacsBuffer.sc similarity index 100% rename from sc/EmacsBuffer.sc rename to scide_scel/EmacsBuffer.sc diff --git a/sc/EmacsDocument.sc b/scide_scel/EmacsDocument.sc similarity index 100% rename from sc/EmacsDocument.sc rename to scide_scel/EmacsDocument.sc diff --git a/sc/ScelDocument.sc b/scide_scel/ScelDocument.sc similarity index 100% rename from sc/ScelDocument.sc rename to scide_scel/ScelDocument.sc diff --git a/sc/extBuffer.sc b/scide_scel/extBuffer.sc similarity index 100% rename from sc/extBuffer.sc rename to scide_scel/extBuffer.sc diff --git a/sc/extClassBrowser.sc b/scide_scel/extClassBrowser.sc similarity index 100% rename from sc/extClassBrowser.sc rename to scide_scel/extClassBrowser.sc diff --git a/sc/extString.sc b/scide_scel/extString.sc similarity index 100% rename from sc/extString.sc rename to scide_scel/extString.sc diff --git a/sc/homeContext.sc b/scide_scel/homeContext.sc similarity index 100% rename from sc/homeContext.sc rename to scide_scel/homeContext.sc diff --git a/sc/storeLispOn.sc b/scide_scel/storeLispOn.sc similarity index 100% rename from sc/storeLispOn.sc rename to scide_scel/storeLispOn.sc From daeb0106c5f8f53bd252545cf54d14a3e02e93f1 Mon Sep 17 00:00:00 2001 From: John Andrews Date: Sun, 15 Aug 2021 20:20:18 -0400 Subject: [PATCH 09/17] Feedback from PR --- README.md | 97 +++++++++++++++++++++++++++++++++++++++++-------------- el/Eldev | 7 ++-- 2 files changed, 75 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 4f49f8e..218705a 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,44 @@ SuperCollider/Emacs interface ## Installation -The repository contains two subprojects. `/sc` contains the SuperCollider code -required to implement the emacs interface. `/el` contains the emacs-lisp -implementation of the mode. Emacs and SuperCollider each have their own package -managers, so it is required to install each half separately. +There are 3 options for installation: -### Installing scel quark +1. Using Emacs' and SuperCollider's own package managers (recommended) +2. From debian package `supercollider-emacs` +3. From source -The `scel` Quark is required for emacs to communicate with sclang. Evaluate this in the SuperCollider GUI: +Option #1 is the best cross-platform option, and is recommended. Whatever option +you choose, you should only install from a single source. + +### Install Option 1: Native Packages + +The repository contains two subprojects. `/el` contains the emacs-lisp +implementation. `/scide_scel` contains the SuperCollider code required to +implement the Emacs interface. Emacs and SuperCollider each have their own +package managers, so it is required to install each half separately. + +#### Installing scel quark + +The `scel` Quark is required for emacs to communicate with sclang. Evaluate this +code in the SuperCollider GUI: ``` supercollider Quarks.install("https://github.com/supercollider/scel"); ``` -### Installing the emacs mode +The scel repository will be downloaded to your local file system and the path +will be added to your default `sclang_conf.yaml` file. If you want to change the +yaml file used by your emacs sessions you can customize the +`sclang-library-configuration-file` variable, but make sure that file loads +the scel quark. + +#### Installing the emacs mode + +Using [straight.el](https://github.com/raxod502/straight.el), put the following +in your init file: -Using straight.el ``` emacs-lisp +;; in ~/.emacs (straight-use-package '(sclang :type git :host github @@ -33,43 +54,60 @@ Or download the repo directly to your user config directory ``` shell git clone https://github.com/supercollider/scel.git ~/.emacs.d/scel ``` + ``` emacs-lisp +;; in ~/.emacs (add-to-list 'load-path "~/.emacs.d/scel/el/") (require 'sclang) ``` -### On MacOS +#### On macOS -If `sclang` executable is not on your path, you may need to add it to your exec-path. +If `sclang` executable is not on your path, you may need to add it to your +exec-path. ``` emacs-lisp (setq exec-path (append exec-path '("/Applications/SuperCollider.app/Contents/MacOS/"))) ``` -### On Linux -If you are building SuperCollider from source on Linux, this library (both .el -and .sc files) will be installed by default. To disable it pass the flag -`-DSC_EL=OFF` as a `cmake` option. See the supercollider readme for more info. +### Install Option 2: Debian package + +There is a debian package which provides emacs integration called +`supercollider-emacs`. Option #1 will likely be more recent, but +if you prefer you can install the package with: + +``` shell +sudo apt install supercollider-emacs +``` + +### Install Option 3: Installing from source +If you are building SuperCollider from source, you can optionally compile and +install this library along with it. The cmake `-DSC_EL` flag controls whether +scel will be compiled. On Linux machines `-DSC_EL=ON` by default. See the +supercollider README files for more info. ## Installation requirements -For the HTML help system, you will need emacs-w3m support, but you can still use without that. +For the HTML help system, you will need emacs-w3m support, but you can still use +the rest of the functionality without the w3m dependency. ```emacs-lisp +;; in ~/.emacs (require 'w3m) ``` ## Configuration -To fine-tune the installation from within emacs' graphical customization interface, type: +To fine-tune the installation from within emacs' graphical customization +interface, type: `M-x sclang-customize` ## Usage -`M-x sclang-start` or open a `.scd` file and press `C-c C-o` +Open a `.scd` file and press `C-c C-o` to start the interpreter. You're now ready to edit, inspect and execute sclang code! @@ -84,11 +122,16 @@ and a window with key bindings in sclang-mode will pop up. `C-x C-h` lets you search for a help file -`C-M-h` opens or switches to the Help browser (if no Help file has been opened, the default Help file will be opened). +`C-M-h` opens or switches to the Help browser (if no Help file has been opened, +the default Help file will be opened). -`E` copies the buffer, puts it in text mode and sclang-minor-mode, to enable you to edit the code parts to try out variations of the provided code in the help file. With `C-M-h` you can then return to the Help browser and browse further from the Help file. +`E` copies the buffer, puts it in text mode and sclang-minor-mode, to enable you +to edit the code parts to try out variations of the provided code in the help +file. With `C-M-h` you can then return to the Help browser and browse further +from the Help file. -`C-c C-e` allows you to edit the source of the HTML file, for example if you want to improve it and commit it to the repository. +`C-c C-e` allows you to edit the source of the HTML file, for example if you +want to improve it and commit it to the repository. To enable moving around in the help file with arrow keys add the following in your `~/.emacs`: @@ -102,14 +145,18 @@ in your `~/.emacs`: (define-key w3m-mode-map [down] 'next-line))) ``` -This ensures that the arrow keys are just for moving through the document, and not from hyperlink to hyperlink, which is the default in w3m-mode. +This ensures that the arrow keys are just for moving through the document, and +not from hyperlink to hyperlink, which is the default in w3m-mode. ## Server control -In the post buffer window, right-click on the server name; by default the two servers `internal` and `localhost` are available. You will get a menu with common server control operations. +In the post buffer window, right-click on the server name; by default the two +servers `internal` and `localhost` are available. You will get a menu with +common server control operations. -To select another server, step through the server list by left-clicking on the server name. +To select another server, step through the server list by left-clicking on the +server name. -Servers instantiated from the language will automatically be available -in the mode line. +Servers instantiated from the language will automatically be available in the +mode line. diff --git a/el/Eldev b/el/Eldev index 9c887bf..9ff63c3 100644 --- a/el/Eldev +++ b/el/Eldev @@ -1,7 +1,6 @@ ; -*- mode: emacs-lisp; lexical-binding: t -*- - -;; Uncomment some calls below as needed for your project. -;(eldev-use-package-archive 'gnu) -;(eldev-use-package-archive 'melpa) +; +;; You can obtain eldev from https://github.com/doublep/eldev +;; We use it for package development and running tests (eldev-use-plugin 'autoloads) From 19dfecc8ca2ec5b58e761ef272845cbb19d6f968 Mon Sep 17 00:00:00 2001 From: John Andrews Date: Mon, 16 Aug 2021 19:57:43 -0400 Subject: [PATCH 10/17] Revert "Combine sclang-mode into the main sclang file" This reverts commit c414b9c7abacd7b59ff9cae34e3334669b2d72cb. --- el/sclang-help.el | 1 + el/sclang-minor-mode.el | 1 + el/sclang-mode.el | 703 ++++++++++++++++++++++++++++++++++++++++ el/sclang-server.el | 1 + el/sclang-util.el | 2 +- el/sclang.el | 702 +-------------------------------------- 6 files changed, 723 insertions(+), 687 deletions(-) create mode 100644 el/sclang-mode.el diff --git a/el/sclang-help.el b/el/sclang-help.el index 8580d45..46e1c1a 100644 --- a/el/sclang-help.el +++ b/el/sclang-help.el @@ -23,6 +23,7 @@ (require 'sclang-util) (require 'sclang-interp) (require 'sclang-language) +(require 'sclang-mode) (require 'sclang-vars nil 'ignore-missing-file) (require 'sclang-minor-mode) diff --git a/el/sclang-minor-mode.el b/el/sclang-minor-mode.el index 98b4e13..c5e2a47 100644 --- a/el/sclang-minor-mode.el +++ b/el/sclang-minor-mode.el @@ -17,6 +17,7 @@ ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA (require 'sclang-util) +(require 'sclang-mode) (easy-mmode-define-minor-mode sclang-minor-mode "Toggle sclang-minor-mode. diff --git a/el/sclang-mode.el b/el/sclang-mode.el new file mode 100644 index 0000000..0f42491 --- /dev/null +++ b/el/sclang-mode.el @@ -0,0 +1,703 @@ +;; copyright 2003-2005 stefan kersten +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 2 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +;; USA + +;;; Code: + +(require 'cl-lib) + +;; Make byte-compiler happy by declaring external functions and +;; variables. +(declare-function company-mode "ext:company") +(defvar company-backends) + +(require 'font-lock) +(require 'sclang-util) +(require 'sclang-interp) +(require 'sclang-language) +(require 'sclang-dev) + +(defun sclang-fill-syntax-table (table) + ;; string + (modify-syntax-entry ?\" "\"" table) + (modify-syntax-entry ?\' "\"" table) ; no string syntax class for single quotes + ;; expression prefix + (modify-syntax-entry ?~ "'" table) + ;; escape + (modify-syntax-entry ?\\ "\\" table) + ;; character quote + (modify-syntax-entry ?$ "/" table) + ;; symbol + (modify-syntax-entry ?_ "_" table) + ;; symbol/punctuation + (modify-syntax-entry ?! "." table) + (modify-syntax-entry ?% "." table) + (modify-syntax-entry ?& "." table) + (modify-syntax-entry ?* ". 23n" table) + (modify-syntax-entry ?+ "." table) + (modify-syntax-entry ?- "." table) + (modify-syntax-entry ?/ ". 124b" table) + (modify-syntax-entry ?< "." table) + (modify-syntax-entry ?= "." table) + (modify-syntax-entry ?> "." table) + (modify-syntax-entry ?? "." table) + (modify-syntax-entry ?@ "." table) + (modify-syntax-entry ?| "." table) + ;; punctuation + (modify-syntax-entry ?: "." table) + (modify-syntax-entry ?\; "." table) + (modify-syntax-entry ?^ "." table) + ;; parenthesis + (modify-syntax-entry ?\( "()" table) + (modify-syntax-entry ?\) ")(" table) + (modify-syntax-entry ?\[ "(]" table) + (modify-syntax-entry ?\] ")[" table) + (modify-syntax-entry ?\{ "(}" table) + (modify-syntax-entry ?\} "){" table) + ;; comment end + (modify-syntax-entry ?\n "> b" table) + ;; Give CR the same syntax as newline, for selective-display + (modify-syntax-entry ?\^m "> b" table) + ;; return table + table) + +(defun sclang-mode-make-menu (title) + (easy-menu-create-menu + title + '( + ["Start Interpreter" sclang-start :included (not (sclang-library-initialized-p))] + ["Restart Interpreter" sclang-start :included (sclang-library-initialized-p)] + ["Recompile Class Library" sclang-recompile :included (sclang-library-initialized-p)] + ["Stop Interpreter" sclang-stop :included (sclang-get-process)] + ["Kill Interpreter" sclang-kill :included (sclang-get-process)] + "-" + ["Show Post Buffer" sclang-show-post-buffer] + ["Clear Post Buffer" sclang-clear-post-buffer] + "-" + ["Switch To Workspace" sclang-switch-to-workspace] + "-" + ["Evaluate Region" sclang-eval-region] + ["Evaluate Line" sclang-eval-region-or-line] + ["Evaluate Defun" sclang-eval-defun] + ["Evaluate Expression ..." sclang-eval-expression] + ["Evaluate Document" sclang-eval-document] + "-" + ["Find Definitions ..." sclang-find-definitions] + ["Find References ..." sclang-find-references] + ["Pop Mark" sclang-pop-definition-mark] + ["Show Method Arguments" sclang-show-method-args] + ["Complete keyword" sclang-complete-symbol] + ["Dump Interface" sclang-dump-interface] + ["Dump Full Interface" sclang-dump-full-interface] + "-" + ["Index Help Topics" sclang-index-help-topics] + ["Find Help ..." sclang-find-help] + ["Switch to Help Browser" sclang-goto-help-browser] + ["Open Help GUI" sclang-open-help-gui] + "-" + ["Run Main" sclang-main-run] + ["Stop Main" sclang-main-stop] + ["Show Server Panels" sclang-show-server-panel] + ))) + +(defun sclang-fill-mode-map (map) + ;; process control + (define-key map "\C-c\C-l" 'sclang-recompile) + (define-key map "\C-c\C-o" 'sclang-start) + ;; post buffer control + (define-key map "\C-c<" 'sclang-clear-post-buffer) + (define-key map "\C-c>" 'sclang-show-post-buffer) + ;; workspace access + (define-key map "\C-c\C-w" 'sclang-switch-to-workspace) + ;; code evaluation + (define-key map "\C-c\C-c" 'sclang-eval-region-or-line) + (define-key map "\C-c\C-d" 'sclang-eval-region) + (define-key map "\C-\M-x" 'sclang-eval-defun) + (define-key map "\C-c\C-e" 'sclang-eval-expression) + (define-key map "\C-c\C-f" 'sclang-eval-document) + ;; language information + (define-key map "\C-c\C-n" 'sclang-complete-symbol) + (define-key map "\C-c:" 'sclang-find-definitions) + (define-key map "\C-c;" 'sclang-find-references) + (define-key map "\C-c}" 'sclang-pop-definition-mark) + (define-key map "\C-c\C-m" 'sclang-show-method-args) + (define-key map "\C-c{" 'sclang-dump-full-interface) + (define-key map "\C-c[" 'sclang-dump-interface) + ;; documentation access + (define-key map "\C-c\C-h" 'sclang-find-help) + (define-key map "\C-\M-h" 'sclang-goto-help-browser) + (define-key map "\C-c\C-y" 'sclang-open-help-gui) + (define-key map "\C-ch" 'sclang-find-help-in-gui) + ;; language control + (define-key map "\C-c\C-r" 'sclang-main-run) + (define-key map "\C-c\C-s" 'sclang-main-stop) + (define-key map "\C-c\C-p" 'sclang-show-server-panel) + (define-key map "\C-c\C-k" 'sclang-edit-dev-source) + ;; electric characters + (define-key map "}" 'sclang-electric-brace) + (define-key map ")" 'sclang-electric-brace) + (define-key map "]" 'sclang-electric-brace) + (define-key map "/" 'sclang-electric-slash) + (define-key map "*" 'sclang-electric-star) + ;; menu + (let ((title "SCLang")) + (define-key map [menu-bar sclang] (cons title (sclang-mode-make-menu title)))) + ;; return map + map) + +;; ===================================================================== +;; font-lock support +;; ===================================================================== + +(defconst sclang-font-lock-keyword-list + '( + "arg" + "classvar" + "const" + "super" + "this" + "thisFunction" + "thisFunctionDef" + "thisMethod" + "thisProcess" + "thisThread" + "var" + ) + "*List of keywords to highlight in SCLang mode.") + +(defconst sclang-font-lock-builtin-list + '( + "false" + "inf" + "nil" + "true" + ) + "*List of builtins to highlight in SCLang mode.") + +(defconst sclang-font-lock-method-list + '( + "ar" + "for" + "forBy" + "if" + "ir" + "kr" + "tr" + "loop" + "while" + ) + "*List of methods to highlight in SCLang mode.") + +(defconst sclang-font-lock-error-list + '( + "die" + "error" + "exit" + "halt" + "verboseHalt" + "warn" + ) + "*List of methods signalling errors or warnings.") + +(defvar sclang-font-lock-class-keywords nil) + +(defvar sclang-font-lock-keywords-1 nil + "Subdued level highlighting for SCLang mode.") + +(defvar sclang-font-lock-keywords-2 nil + "Medium level highlighting for SCLang mode.") + +(defvar sclang-font-lock-keywords-3 nil + "Gaudy level highlighting for SCLang mode.") + +(defvar sclang-font-lock-keywords nil + "Default expressions to highlight in SCLang mode.") + +(defconst sclang-font-lock-defaults '((sclang-font-lock-keywords + sclang-font-lock-keywords-1 + sclang-font-lock-keywords-2 + sclang-font-lock-keywords-3 + ) + nil nil + nil + beginning-of-defun + )) + +(defun sclang-font-lock-syntactic-face (state) + (cond ((eq (nth 3 state) ?') + ;; symbol + 'font-lock-constant-face) + ((nth 3 state) + ;; string + 'font-lock-string-face) + ((nth 4 state) + ;; comment + 'font-lock-comment-face))) + +(defun sclang-font-lock-class-keyword-matcher (limit) + (let ((regexp (concat "\\<" sclang-class-name-regexp "\\>")) + (case-fold-search nil) + (continue t) + (res nil)) + (while continue + (setq res (re-search-forward regexp limit t)) + (if (or (null res) (null sclang-class-list)) + (setq continue nil) + (let ((thing (thing-at-point 'word))) + (if (null thing) + (setq res nil continue nil) + (when (cl-position (substring-no-properties thing) sclang-class-list :test 'equal) + (setq continue nil)))))) + res)) + +(defun sclang-set-font-lock-keywords () + (setq + ;; level 1 + sclang-font-lock-keywords-1 + (list + ;; keywords + (cons (regexp-opt sclang-font-lock-keyword-list 'words) + 'font-lock-keyword-face) + ;; builtins + (cons (regexp-opt sclang-font-lock-builtin-list 'words) + 'font-lock-builtin-face) + ;; pi is a special case + (cons "\\<\\([0-9]+\\(\\.\\)\\)pi\\>" 'font-lock-builtin-face) + ;; constants + (cons "\\s/\\s\\?." 'font-lock-constant-face) ; characters + (cons (concat "\\\\\\(" sclang-symbol-regexp "\\)") + 'font-lock-constant-face) ; symbols + ) + ;; level 2 + sclang-font-lock-keywords-2 + (append + sclang-font-lock-keywords-1 + (list + ;; variables + (cons (concat "\\s'\\(" sclang-identifier-regexp "\\)") + 'font-lock-variable-name-face) ; environment variables + (cons (concat "\\<\\(" sclang-identifier-regexp "\\)\\>:") ; keyword arguments + 'font-lock-variable-name-face) + ;; method definitions + (cons sclang-method-definition-regexp + (list 1 'font-lock-function-name-face)) + ;; methods + (cons (regexp-opt sclang-font-lock-method-list 'words) + 'font-lock-function-name-face) + ;; errors + (cons (regexp-opt sclang-font-lock-error-list 'words) + 'font-lock-warning-face) + )) + ;; level 3 + sclang-font-lock-keywords-3 + (append + sclang-font-lock-keywords-2 + (list + ;; classes + (cons 'sclang-font-lock-class-keyword-matcher 'font-lock-type-face) +;; (cons (concat "\\<" sclang-class-name-regexp "\\>") 'font-lock-type-face) + )) + ;; default level + sclang-font-lock-keywords sclang-font-lock-keywords-1 + )) + +(defun sclang-update-font-lock () + "Update font-lock information in all sclang-mode buffers." + ;; too expensive + ;; (dolist (buffer (buffer-list)) + ;; (with-current-buffer buffer + ;; (and (eq major-mode 'sclang-mode) + ;; (eq t (car font-lock-keywords)) + ;; (setq font-lock-keywords (cdr font-lock-keywords))))) + (if (eq major-mode 'sclang-mode) + (font-lock-fontify-buffer))) + +;; ===================================================================== +;; indentation +;; ===================================================================== + +(defcustom sclang-indent-level 4 + "*Indentation offset for SCLang statements." + :group 'sclang-mode + :type 'integer) + +(defun sclang-indent-line () + "Indent current line as sclang code. +Return the amount the indentation changed by." + (let ((indent (calculate-sclang-indent)) + beg shift-amt + (case-fold-search nil) + (pos (- (point-max) (point)))) + (beginning-of-line) + (setq beg (point)) + (skip-chars-forward " \t") + (setq shift-amt (- indent (current-column))) + (if (zerop shift-amt) + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos))) + (delete-region beg (point)) + (indent-to indent) + ;; if initial point was within line's indentation, position + ;; after the indentation, else stay at same point in text. + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos)))) + shift-amt)) + +(defun calculate-sclang-indent (&optional parse-start) + "Return appropriate indentation for current line as sclang code. +Returns the column to indent to." + (save-excursion + (beginning-of-line) + (let ((indent-point (point)) + (case-fold-search nil) + state) + (if parse-start + (goto-char parse-start) + (beginning-of-defun)) + (while (< (point) indent-point) + (setq state (parse-partial-sexp (point) indent-point 0))) + (let* ((containing-sexp (nth 1 state)) + (inside-string-p (nth 3 state)) + (inside-comment-p (nth 4 state))) + (cond (inside-string-p + ;; inside string: no change + (current-indentation)) + ((integerp inside-comment-p) + ;; inside comment + (let ((base (if containing-sexp + (save-excursion + (goto-char containing-sexp) + (+ (current-indentation) sclang-indent-level)) + 0)) + (offset (* sclang-indent-level + (- inside-comment-p + (if (save-excursion + (back-to-indentation) + (looking-at "\\*/")) + 1 0))))) + (+ base offset))) + ((null containing-sexp) + ;; top-level: no indentation + 0) + (t + (back-to-indentation) + (let ((open-paren (and (looking-at "\\s)") + (matching-paren (char-after)))) + (indent (current-indentation))) + (goto-char containing-sexp) + (if (or (not open-paren) (eq open-paren (char-after))) + (cond ((progn (beginning-of-line) (looking-at sclang-block-regexp)) 0) + (open-paren (current-indentation)) + (t (+ (current-indentation) sclang-indent-level))) + ;; paren mismatch: do nothing + indent)))))))) + +;; ===================================================================== +;; electric character commands +;; ===================================================================== + +(defun sclang-electric-brace (arg) + (interactive "*P") + (self-insert-command (prefix-numeric-value arg)) + (and (save-excursion + (beginning-of-line) + (looking-at "\\s *\\s)")) + (indent-according-to-mode))) + +(defun sclang-electric-slash (arg) + (interactive "*P") + (let* ((char (char-before)) + (indent-p (or (eq char ?/) + (eq char ?*)))) + (self-insert-command (prefix-numeric-value arg)) + (if indent-p (indent-according-to-mode)))) + +(defun sclang-electric-star (arg) + (interactive "*P") + (let ((indent-p (eq (char-before) ?/))) + (self-insert-command (prefix-numeric-value arg)) + (if indent-p (indent-according-to-mode)))) + +;; ===================================================================== +;; document interface +;; ===================================================================== + +(defvar sclang-document-id nil) +(defvar sclang-document-state nil) +(defvar sclang-document-envir nil) + +(defvar sclang-document-counter 0) +(defvar sclang-document-list nil) +(defvar sclang-current-document nil + "Currently active document.") + +(defvar sclang-document-idle-timer nil) + +(defconst sclang-document-property-map + '((sclang-document-name . (prSetTitle (buffer-name))) + (sclang-document-path . (prSetFileName (buffer-file-name))) + (sclang-document-listener-p . (prSetIsListener (eq (current-buffer) (sclang-get-post-buffer)))) + (sclang-document-editable-p . (prSetEditable (not buffer-read-only))) + (sclang-document-edited-p . (prSetEdited (buffer-modified-p))))) + +(defmacro sclang-next-document-id () + `(cl-incf sclang-document-counter)) + +(defun sclang-document-id (buffer) + (cdr (assq 'sclang-document-id (buffer-local-variables buffer)))) + +(defun sclang-document-p (buffer) + (integerp (sclang-document-id buffer))) + +(defmacro with-sclang-document (buffer &rest body) + `(when (sclang-document-p buffer) + (with-current-buffer buffer + ,@body))) + +(defun sclang-get-document (id) + (cl-find-if (lambda (buffer) (eq id (sclang-document-id buffer))) + sclang-document-list)) + +(defun sclang-init-document () + (set (make-local-variable 'sclang-document-id) (sclang-next-document-id)) + (set (make-local-variable 'sclang-document-envir) nil) + (dolist (assoc sclang-document-property-map) + (set (make-local-variable (car assoc)) nil)) + (cl-pushnew (current-buffer) sclang-document-list)) + +(defun sclang-document-update-property-1 (assoc &optional force) + (when (consp assoc) + (let* ((key (car assoc)) + (prop (cdr assoc)) + (prev-value (eval key)) + (cur-value (eval (cadr prop)))) + (when (or force (not (equal prev-value cur-value))) + (set key cur-value) + (sclang-perform-command-no-result + 'documentSetProperty sclang-document-id + (car prop) cur-value))))) + +(defun sclang-document-update-property (key &optional force) + (sclang-document-update-property-1 (assq key sclang-document-property-map) force)) + +(defun sclang-document-update-properties (&optional force) + (dolist (assoc sclang-document-property-map) + (sclang-document-update-property-1 assoc force))) + +(defun sclang-make-document () + (sclang-perform-command-no-result 'documentNew sclang-document-id) + (sclang-document-update-properties t)) + +(defun sclang-close-document (buffer) + (with-sclang-document + buffer + (setq sclang-document-list (delq buffer sclang-document-list)) + (sclang-perform-command-no-result + 'documentClosed sclang-document-id))) + +(defun sclang-set-current-document (buffer &optional force) + (when (or force (not (eq buffer sclang-current-document))) + (setq sclang-current-document buffer) + (sclang-perform-command-no-result 'documentSetCurrent (sclang-document-id buffer)) + t)) + +(defun sclang-document-library-startup-hook-function () + (dolist (buffer sclang-document-list) + (with-current-buffer buffer + (sclang-make-document))) + (sclang-set-current-document (current-buffer) t)) + +(defun sclang-document-kill-buffer-hook-function () + (sclang-close-document (current-buffer))) + +(defun sclang-document-post-command-hook-function () + (when (and (sclang-library-initialized-p) + (sclang-document-p (current-buffer))) + (sclang-document-update-properties)) + (sclang-set-current-document (current-buffer))) + +(defun sclang-document-change-major-mode-hook-function () + (sclang-close-document (current-buffer))) + +;; ===================================================================== +;; command handlers +;; ===================================================================== + +(sclang-set-command-handler + '_documentOpen + (lambda (arg) + (cl-multiple-value-bind (file-name region-start region-length) arg + (let ((buffer (get-file-buffer file-name))) + (unless buffer + (setf buffer (find-file-noselect file-name))) + (when buffer + (unless (sclang-document-p buffer) + (with-current-buffer buffer (sclang-mode))) + (goto-char (max (point-min) (min (point-max) region-start))) + ;; TODO: how to activate region in transient-mark-mode? + (sclang-document-id buffer)))))) + +(sclang-set-command-handler + '_documentNew + (lambda (arg) + (cl-multiple-value-bind (name str make-listener) arg + (let ((buffer (generate-new-buffer name))) + (with-current-buffer buffer + (insert str) + (set-buffer-modified-p nil) + (sclang-mode)) + (sclang-document-id buffer))))) + +(sclang-set-command-handler + '_documentClose + (lambda (arg) + (let ((doc (and (integerp arg) (sclang-get-document arg)))) + (and doc (kill-buffer doc))) + nil)) + +(sclang-set-command-handler + '_documentRename + (lambda (arg) + (cl-multiple-value-bind (id name) arg + (when (stringp name) + (let ((doc (and (integerp id) (sclang-get-document id)))) + (when doc + (with-current-buffer doc + (rename-buffer name t) + (sclang-document-update-property 'sclang-document-name)))))) + nil)) + +(sclang-set-command-handler + '_documentSetEditable + (lambda (arg) + (cl-multiple-value-bind (id flag) arg + (let ((doc (and (integerp id) (sclang-get-document id)))) + (when doc + (with-current-buffer doc + (setq buffer-read-only (not flag)) + (sclang-document-update-property 'sclang-editable-p))))) + nil)) + +(sclang-set-command-handler + '_documentSwitchTo + (lambda (arg) + (let ((doc (and (integerp arg) (sclang-get-document arg)))) + (and doc (switch-to-buffer doc))) + nil)) + +(sclang-set-command-handler + '_documentPutString +(lambda (arg) + (cl-multiple-value-bind (id str) arg + (let ((doc (and (integerp id) (sclang-get-document id)))) + (when doc + (with-current-buffer doc + (insert str) + ) + nil))))) + +(sclang-set-command-handler + '_documentPopTo + (lambda (arg) + (let ((doc (and (integerp arg) (sclang-get-document arg)))) + (and doc (display-buffer doc))) + nil)) + +;; ===================================================================== +;; sclang-mode +;; ===================================================================== + +(defun sclang-mode-set-local-variables () + (set (make-local-variable 'require-final-newline) nil) + ;; indentation + (set (make-local-variable 'indent-line-function) + 'sclang-indent-line) + (set (make-local-variable 'tab-width) 4) + (set (make-local-variable 'indent-tabs-mode) t) + ;; comment formatting + (set (make-local-variable 'comment-start) "// ") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-column) 40) + (set (make-local-variable 'comment-start-skip) "/\\*+ *\\|//+ *") + ;; "\\(^\\|\\s-\\);?// *") + (set (make-local-variable 'comment-multi-line) t) + ;; parsing and movement + (set (make-local-variable 'parse-sexp-ignore-comments) t) + (set (make-local-variable 'beginning-of-defun-function) + 'sclang-beginning-of-defun) + (set (make-local-variable 'end-of-defun-function) + 'sclang-end-of-defun) + ;; paragraph formatting + ;; (set (make-local-variable 'paragraph-start) (concat "$\\|" page-delimiter)) + ;; mostly copied from c++-mode, seems to work + (set (make-local-variable 'paragraph-start) + "[ \t]*\\(//+\\|\\**\\)[ \t]*$\\|^ ") + (set (make-local-variable 'paragraph-separate) paragraph-start) + (set (make-local-variable 'paragraph-ignore-fill-prefix) t) + (set (make-local-variable 'adaptive-fill-mode) t) + (set (make-local-variable 'adaptive-fill-regexp) + "[ \t]*\\(//+\\|\\**\\)[ \t]*\\([ \t]*\\([-|#;>*]+[ \t]*\\|(?[0-9]+[.)][ \t]*\\)*\\)") + ;; font lock + (set (make-local-variable 'font-lock-syntactic-face-function) + 'sclang-font-lock-syntactic-face) + (set (make-local-variable 'font-lock-defaults) + sclang-font-lock-defaults) + ;; --- + nil) + +(defvar sclang-mode-map (sclang-fill-mode-map (make-sparse-keymap)) + "Keymap used in SuperCollider mode.") + +(defvar sclang-mode-syntax-table (sclang-fill-syntax-table (make-syntax-table)) + "Syntax table used in SuperCollider mode.") + +(defcustom sclang-mode-hook nil + "*Hook run when entering SCLang mode." + :group 'sclang-mode + :type 'hook) + +;;;###autoload +(define-derived-mode sclang-mode prog-mode "SCLang" + "Major mode for editing SuperCollider language code. +\\{sclang-mode-map}" + :group 'sclang + :syntax-table sclang-mode-syntax-table + (sclang-mode-set-local-variables) + (sclang-set-font-lock-keywords) + (sclang-init-document) + (sclang-make-document) + + ;; Setup completion + (add-hook 'completion-at-point-functions + #'sclang-completion-at-point nil 'local) + (when (fboundp 'company-mode) + (add-to-list 'company-backends 'company-capf))) + +;; ===================================================================== +;; module initialization +;; ===================================================================== + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.scd?\\'" . sclang-mode)) +(add-to-list 'interpreter-mode-alist '("sclang" . sclang-mode)) + +(add-hook 'sclang-library-startup-hook 'sclang-document-library-startup-hook-function) +(add-hook 'kill-buffer-hook 'sclang-document-kill-buffer-hook-function) +(add-hook 'post-command-hook 'sclang-document-post-command-hook-function) +(add-hook 'change-major-mode-hook 'sclang-document-change-major-mode-hook-function) + +(provide 'sclang-mode) +;;; sclang-mode ends here diff --git a/el/sclang-server.el b/el/sclang-server.el index f2bfa27..375c492 100644 --- a/el/sclang-server.el +++ b/el/sclang-server.el @@ -19,6 +19,7 @@ (require 'sclang-util) (require 'sclang-interp) (require 'sclang-language) +(require 'sclang-mode) (defcustom sclang-server-panel "Server.default.makeWindow" "Expression to execute when `sclang-show-server-panel' is invoked." diff --git a/el/sclang-util.el b/el/sclang-util.el index 7c33d35..ea104cb 100644 --- a/el/sclang-util.el +++ b/el/sclang-util.el @@ -83,4 +83,4 @@ (provide 'sclang-util) -;; EOF +;; EOF \ No newline at end of file diff --git a/el/sclang.el b/el/sclang.el index 8f6d990..9bbedcc 100644 --- a/el/sclang.el +++ b/el/sclang.el @@ -26,24 +26,6 @@ ;; for more information. ;;; Code: - -(require 'cl-lib) -(require 'font-lock) -(require 'sclang-util) -(require 'sclang-browser) -(require 'sclang-interp) -(require 'sclang-language) -(require 'sclang-document) -(require 'sclang-minor-mode) -(require 'sclang-help) -(require 'sclang-server) -(require 'sclang-widgets) - -;; Make byte-compiler happy by declaring external functions and -;; variables. -(declare-function company-mode "ext:company") -(defvar company-backends) - (defgroup sclang nil "IDE for working with the SuperCollider language." :group 'languages) @@ -73,674 +55,22 @@ (interactive) (customize-group 'sclang)) -(defun sclang-fill-syntax-table (table) - ;; string - (modify-syntax-entry ?\" "\"" table) - (modify-syntax-entry ?\' "\"" table) ; no string syntax class for single quotes - ;; expression prefix - (modify-syntax-entry ?~ "'" table) - ;; escape - (modify-syntax-entry ?\\ "\\" table) - ;; character quote - (modify-syntax-entry ?$ "/" table) - ;; symbol - (modify-syntax-entry ?_ "_" table) - ;; symbol/punctuation - (modify-syntax-entry ?! "." table) - (modify-syntax-entry ?% "." table) - (modify-syntax-entry ?& "." table) - (modify-syntax-entry ?* ". 23n" table) - (modify-syntax-entry ?+ "." table) - (modify-syntax-entry ?- "." table) - (modify-syntax-entry ?/ ". 124b" table) - (modify-syntax-entry ?< "." table) - (modify-syntax-entry ?= "." table) - (modify-syntax-entry ?> "." table) - (modify-syntax-entry ?? "." table) - (modify-syntax-entry ?@ "." table) - (modify-syntax-entry ?| "." table) - ;; punctuation - (modify-syntax-entry ?: "." table) - (modify-syntax-entry ?\; "." table) - (modify-syntax-entry ?^ "." table) - ;; parenthesis - (modify-syntax-entry ?\( "()" table) - (modify-syntax-entry ?\) ")(" table) - (modify-syntax-entry ?\[ "(]" table) - (modify-syntax-entry ?\] ")[" table) - (modify-syntax-entry ?\{ "(}" table) - (modify-syntax-entry ?\} "){" table) - ;; comment end - (modify-syntax-entry ?\n "> b" table) - ;; Give CR the same syntax as newline, for selective-display - (modify-syntax-entry ?\^m "> b" table) - ;; return table - table) - -(defun sclang-mode-make-menu (title) - (easy-menu-create-menu - title - '( - ["Start Interpreter" sclang-start :included (not (sclang-library-initialized-p))] - ["Restart Interpreter" sclang-start :included (sclang-library-initialized-p)] - ["Recompile Class Library" sclang-recompile :included (sclang-library-initialized-p)] - ["Stop Interpreter" sclang-stop :included (sclang-get-process)] - ["Kill Interpreter" sclang-kill :included (sclang-get-process)] - "-" - ["Show Post Buffer" sclang-show-post-buffer] - ["Clear Post Buffer" sclang-clear-post-buffer] - "-" - ["Switch To Workspace" sclang-switch-to-workspace] - "-" - ["Evaluate Region" sclang-eval-region] - ["Evaluate Line" sclang-eval-region-or-line] - ["Evaluate Defun" sclang-eval-defun] - ["Evaluate Expression ..." sclang-eval-expression] - ["Evaluate Document" sclang-eval-document] - "-" - ["Find Definitions ..." sclang-find-definitions] - ["Find References ..." sclang-find-references] - ["Pop Mark" sclang-pop-definition-mark] - ["Show Method Arguments" sclang-show-method-args] - ["Complete keyword" sclang-complete-symbol] - ["Dump Interface" sclang-dump-interface] - ["Dump Full Interface" sclang-dump-full-interface] - "-" - ["Index Help Topics" sclang-index-help-topics] - ["Find Help ..." sclang-find-help] - ["Switch to Help Browser" sclang-goto-help-browser] - ["Open Help GUI" sclang-open-help-gui] - "-" - ["Run Main" sclang-main-run] - ["Stop Main" sclang-main-stop] - ["Show Server Panels" sclang-show-server-panel] - ))) - -(defun sclang-fill-mode-map (map) - ;; process control - (define-key map "\C-c\C-l" 'sclang-recompile) - (define-key map "\C-c\C-o" 'sclang-start) - ;; post buffer control - (define-key map "\C-c<" 'sclang-clear-post-buffer) - (define-key map "\C-c>" 'sclang-show-post-buffer) - ;; workspace access - (define-key map "\C-c\C-w" 'sclang-switch-to-workspace) - ;; code evaluation - (define-key map "\C-c\C-c" 'sclang-eval-region-or-line) - (define-key map "\C-c\C-d" 'sclang-eval-region) - (define-key map "\C-\M-x" 'sclang-eval-defun) - (define-key map "\C-c\C-e" 'sclang-eval-expression) - (define-key map "\C-c\C-f" 'sclang-eval-document) - ;; language information - (define-key map "\C-c\C-n" 'sclang-complete-symbol) - (define-key map "\C-c:" 'sclang-find-definitions) - (define-key map "\C-c;" 'sclang-find-references) - (define-key map "\C-c}" 'sclang-pop-definition-mark) - (define-key map "\C-c\C-m" 'sclang-show-method-args) - (define-key map "\C-c{" 'sclang-dump-full-interface) - (define-key map "\C-c[" 'sclang-dump-interface) - ;; documentation access - (define-key map "\C-c\C-h" 'sclang-find-help) - (define-key map "\C-\M-h" 'sclang-goto-help-browser) - (define-key map "\C-c\C-y" 'sclang-open-help-gui) - (define-key map "\C-ch" 'sclang-find-help-in-gui) - ;; language control - (define-key map "\C-c\C-r" 'sclang-main-run) - (define-key map "\C-c\C-s" 'sclang-main-stop) - (define-key map "\C-c\C-p" 'sclang-show-server-panel) - (define-key map "\C-c\C-k" 'sclang-edit-dev-source) - ;; electric characters - (define-key map "}" 'sclang-electric-brace) - (define-key map ")" 'sclang-electric-brace) - (define-key map "]" 'sclang-electric-brace) - (define-key map "/" 'sclang-electric-slash) - (define-key map "*" 'sclang-electric-star) - ;; menu - (let ((title "SCLang")) - (define-key map [menu-bar sclang] (cons title (sclang-mode-make-menu title)))) - ;; return map - map) - -;; ===================================================================== -;; font-lock support -;; ===================================================================== - -(defconst sclang-font-lock-keyword-list - '( - "arg" - "classvar" - "const" - "super" - "this" - "thisFunction" - "thisFunctionDef" - "thisMethod" - "thisProcess" - "thisThread" - "var" - ) - "*List of keywords to highlight in SCLang mode.") - -(defconst sclang-font-lock-builtin-list - '( - "false" - "inf" - "nil" - "true" - ) - "*List of builtins to highlight in SCLang mode.") - -(defconst sclang-font-lock-method-list - '( - "ar" - "for" - "forBy" - "if" - "ir" - "kr" - "tr" - "loop" - "while" - ) - "*List of methods to highlight in SCLang mode.") - -(defconst sclang-font-lock-error-list - '( - "die" - "error" - "exit" - "halt" - "verboseHalt" - "warn" - ) - "*List of methods signalling errors or warnings.") - -(defvar sclang-font-lock-class-keywords nil) - -(defvar sclang-font-lock-keywords-1 nil - "Subdued level highlighting for SCLang mode.") - -(defvar sclang-font-lock-keywords-2 nil - "Medium level highlighting for SCLang mode.") - -(defvar sclang-font-lock-keywords-3 nil - "Gaudy level highlighting for SCLang mode.") - -(defvar sclang-font-lock-keywords nil - "Default expressions to highlight in SCLang mode.") - -(defconst sclang-font-lock-defaults '((sclang-font-lock-keywords - sclang-font-lock-keywords-1 - sclang-font-lock-keywords-2 - sclang-font-lock-keywords-3 - ) - nil nil - nil - beginning-of-defun - )) - -(defun sclang-font-lock-syntactic-face (state) - (cond ((eq (nth 3 state) ?') - ;; symbol - 'font-lock-constant-face) - ((nth 3 state) - ;; string - 'font-lock-string-face) - ((nth 4 state) - ;; comment - 'font-lock-comment-face))) - -(defun sclang-font-lock-class-keyword-matcher (limit) - (let ((regexp (concat "\\<" sclang-class-name-regexp "\\>")) - (case-fold-search nil) - (continue t) - (res nil)) - (while continue - (setq res (re-search-forward regexp limit t)) - (if (or (null res) (null sclang-class-list)) - (setq continue nil) - (let ((thing (thing-at-point 'word))) - (if (null thing) - (setq res nil continue nil) - (when (cl-position (substring-no-properties thing) sclang-class-list :test 'equal) - (setq continue nil)))))) - res)) - -(defun sclang-set-font-lock-keywords () - (setq - ;; level 1 - sclang-font-lock-keywords-1 - (list - ;; keywords - (cons (regexp-opt sclang-font-lock-keyword-list 'words) - 'font-lock-keyword-face) - ;; builtins - (cons (regexp-opt sclang-font-lock-builtin-list 'words) - 'font-lock-builtin-face) - ;; pi is a special case - (cons "\\<\\([0-9]+\\(\\.\\)\\)pi\\>" 'font-lock-builtin-face) - ;; constants - (cons "\\s/\\s\\?." 'font-lock-constant-face) ; characters - (cons (concat "\\\\\\(" sclang-symbol-regexp "\\)") - 'font-lock-constant-face) ; symbols - ) - ;; level 2 - sclang-font-lock-keywords-2 - (append - sclang-font-lock-keywords-1 - (list - ;; variables - (cons (concat "\\s'\\(" sclang-identifier-regexp "\\)") - 'font-lock-variable-name-face) ; environment variables - (cons (concat "\\<\\(" sclang-identifier-regexp "\\)\\>:") ; keyword arguments - 'font-lock-variable-name-face) - ;; method definitions - (cons sclang-method-definition-regexp - (list 1 'font-lock-function-name-face)) - ;; methods - (cons (regexp-opt sclang-font-lock-method-list 'words) - 'font-lock-function-name-face) - ;; errors - (cons (regexp-opt sclang-font-lock-error-list 'words) - 'font-lock-warning-face) - )) - ;; level 3 - sclang-font-lock-keywords-3 - (append - sclang-font-lock-keywords-2 - (list - ;; classes - (cons 'sclang-font-lock-class-keyword-matcher 'font-lock-type-face) -;; (cons (concat "\\<" sclang-class-name-regexp "\\>") 'font-lock-type-face) - )) - ;; default level - sclang-font-lock-keywords sclang-font-lock-keywords-1 - )) - -(defun sclang-update-font-lock () - "Update font-lock information in all sclang-mode buffers." - ;; too expensive - ;; (dolist (buffer (buffer-list)) - ;; (with-current-buffer buffer - ;; (and (eq major-mode 'sclang-mode) - ;; (eq t (car font-lock-keywords)) - ;; (setq font-lock-keywords (cdr font-lock-keywords))))) - (if (eq major-mode 'sclang-mode) - (font-lock-fontify-buffer))) - -;; ===================================================================== -;; indentation -;; ===================================================================== - -(defcustom sclang-indent-level 4 - "*Indentation offset for SCLang statements." - :group 'sclang-mode - :type 'integer) - -(defun sclang-indent-line () - "Indent current line as sclang code. -Return the amount the indentation changed by." - (let ((indent (calculate-sclang-indent)) - beg shift-amt - (case-fold-search nil) - (pos (- (point-max) (point)))) - (beginning-of-line) - (setq beg (point)) - (skip-chars-forward " \t") - (setq shift-amt (- indent (current-column))) - (if (zerop shift-amt) - (if (> (- (point-max) pos) (point)) - (goto-char (- (point-max) pos))) - (delete-region beg (point)) - (indent-to indent) - ;; if initial point was within line's indentation, position - ;; after the indentation, else stay at same point in text. - (if (> (- (point-max) pos) (point)) - (goto-char (- (point-max) pos)))) - shift-amt)) - -(defun calculate-sclang-indent (&optional parse-start) - "Return appropriate indentation for current line as sclang code. -Returns the column to indent to." - (save-excursion - (beginning-of-line) - (let ((indent-point (point)) - (case-fold-search nil) - state) - (if parse-start - (goto-char parse-start) - (beginning-of-defun)) - (while (< (point) indent-point) - (setq state (parse-partial-sexp (point) indent-point 0))) - (let* ((containing-sexp (nth 1 state)) - (inside-string-p (nth 3 state)) - (inside-comment-p (nth 4 state))) - (cond (inside-string-p - ;; inside string: no change - (current-indentation)) - ((integerp inside-comment-p) - ;; inside comment - (let ((base (if containing-sexp - (save-excursion - (goto-char containing-sexp) - (+ (current-indentation) sclang-indent-level)) - 0)) - (offset (* sclang-indent-level - (- inside-comment-p - (if (save-excursion - (back-to-indentation) - (looking-at "\\*/")) - 1 0))))) - (+ base offset))) - ((null containing-sexp) - ;; top-level: no indentation - 0) - (t - (back-to-indentation) - (let ((open-paren (and (looking-at "\\s)") - (matching-paren (char-after)))) - (indent (current-indentation))) - (goto-char containing-sexp) - (if (or (not open-paren) (eq open-paren (char-after))) - (cond ((progn (beginning-of-line) (looking-at sclang-block-regexp)) 0) - (open-paren (current-indentation)) - (t (+ (current-indentation) sclang-indent-level))) - ;; paren mismatch: do nothing - indent)))))))) - -;; ===================================================================== -;; electric character commands -;; ===================================================================== - -(defun sclang-electric-brace (arg) - (interactive "*P") - (self-insert-command (prefix-numeric-value arg)) - (and (save-excursion - (beginning-of-line) - (looking-at "\\s *\\s)")) - (indent-according-to-mode))) - -(defun sclang-electric-slash (arg) - (interactive "*P") - (let* ((char (char-before)) - (indent-p (or (eq char ?/) - (eq char ?*)))) - (self-insert-command (prefix-numeric-value arg)) - (if indent-p (indent-according-to-mode)))) - -(defun sclang-electric-star (arg) - (interactive "*P") - (let ((indent-p (eq (char-before) ?/))) - (self-insert-command (prefix-numeric-value arg)) - (if indent-p (indent-according-to-mode)))) - -;; ===================================================================== -;; document interface -;; ===================================================================== - -(defvar sclang-document-id nil) -(defvar sclang-document-state nil) -(defvar sclang-document-envir nil) - -(defvar sclang-document-counter 0) -(defvar sclang-document-list nil) -(defvar sclang-current-document nil - "Currently active document.") - -(defvar sclang-document-idle-timer nil) - -(defconst sclang-document-property-map - '((sclang-document-name . (prSetTitle (buffer-name))) - (sclang-document-path . (prSetFileName (buffer-file-name))) - (sclang-document-listener-p . (prSetIsListener (eq (current-buffer) (sclang-get-post-buffer)))) - (sclang-document-editable-p . (prSetEditable (not buffer-read-only))) - (sclang-document-edited-p . (prSetEdited (buffer-modified-p))))) - -(defmacro sclang-next-document-id () - `(cl-incf sclang-document-counter)) - -(defun sclang-document-id (buffer) - (cdr (assq 'sclang-document-id (buffer-local-variables buffer)))) - -(defun sclang-document-p (buffer) - (integerp (sclang-document-id buffer))) - -(defmacro with-sclang-document (buffer &rest body) - `(when (sclang-document-p buffer) - (with-current-buffer buffer - ,@body))) - -(defun sclang-get-document (id) - (cl-find-if (lambda (buffer) (eq id (sclang-document-id buffer))) - sclang-document-list)) - -(defun sclang-init-document () - (set (make-local-variable 'sclang-document-id) (sclang-next-document-id)) - (set (make-local-variable 'sclang-document-envir) nil) - (dolist (assoc sclang-document-property-map) - (set (make-local-variable (car assoc)) nil)) - (cl-pushnew (current-buffer) sclang-document-list)) - -(defun sclang-document-update-property-1 (assoc &optional force) - (when (consp assoc) - (let* ((key (car assoc)) - (prop (cdr assoc)) - (prev-value (eval key)) - (cur-value (eval (cadr prop)))) - (when (or force (not (equal prev-value cur-value))) - (set key cur-value) - (sclang-perform-command-no-result - 'documentSetProperty sclang-document-id - (car prop) cur-value))))) - -(defun sclang-document-update-property (key &optional force) - (sclang-document-update-property-1 (assq key sclang-document-property-map) force)) - -(defun sclang-document-update-properties (&optional force) - (dolist (assoc sclang-document-property-map) - (sclang-document-update-property-1 assoc force))) - -(defun sclang-make-document () - (sclang-perform-command-no-result 'documentNew sclang-document-id) - (sclang-document-update-properties t)) - -(defun sclang-close-document (buffer) - (with-sclang-document - buffer - (setq sclang-document-list (delq buffer sclang-document-list)) - (sclang-perform-command-no-result - 'documentClosed sclang-document-id))) - -(defun sclang-set-current-document (buffer &optional force) - (when (or force (not (eq buffer sclang-current-document))) - (setq sclang-current-document buffer) - (sclang-perform-command-no-result 'documentSetCurrent (sclang-document-id buffer)) - t)) - -(defun sclang-document-library-startup-hook-function () - (dolist (buffer sclang-document-list) - (with-current-buffer buffer - (sclang-make-document))) - (sclang-set-current-document (current-buffer) t)) - -(defun sclang-document-kill-buffer-hook-function () - (sclang-close-document (current-buffer))) - -(defun sclang-document-post-command-hook-function () - (when (and (sclang-library-initialized-p) - (sclang-document-p (current-buffer))) - (sclang-document-update-properties)) - (sclang-set-current-document (current-buffer))) - -(defun sclang-document-change-major-mode-hook-function () - (sclang-close-document (current-buffer))) - -;; ===================================================================== -;; command handlers -;; ===================================================================== - -(sclang-set-command-handler - '_documentOpen - (lambda (arg) - (cl-multiple-value-bind (file-name region-start region-length) arg - (let ((buffer (get-file-buffer file-name))) - (unless buffer - (setf buffer (find-file-noselect file-name))) - (when buffer - (unless (sclang-document-p buffer) - (with-current-buffer buffer (sclang-mode))) - (goto-char (max (point-min) (min (point-max) region-start))) - ;; TODO: how to activate region in transient-mark-mode? - (sclang-document-id buffer)))))) - -(sclang-set-command-handler - '_documentNew - (lambda (arg) - (cl-multiple-value-bind (name str make-listener) arg - (let ((buffer (generate-new-buffer name))) - (with-current-buffer buffer - (insert str) - (set-buffer-modified-p nil) - (sclang-mode)) - (sclang-document-id buffer))))) - -(sclang-set-command-handler - '_documentClose - (lambda (arg) - (let ((doc (and (integerp arg) (sclang-get-document arg)))) - (and doc (kill-buffer doc))) - nil)) - -(sclang-set-command-handler - '_documentRename - (lambda (arg) - (cl-multiple-value-bind (id name) arg - (when (stringp name) - (let ((doc (and (integerp id) (sclang-get-document id)))) - (when doc - (with-current-buffer doc - (rename-buffer name t) - (sclang-document-update-property 'sclang-document-name)))))) - nil)) - -(sclang-set-command-handler - '_documentSetEditable - (lambda (arg) - (cl-multiple-value-bind (id flag) arg - (let ((doc (and (integerp id) (sclang-get-document id)))) - (when doc - (with-current-buffer doc - (setq buffer-read-only (not flag)) - (sclang-document-update-property 'sclang-editable-p))))) - nil)) - -(sclang-set-command-handler - '_documentSwitchTo - (lambda (arg) - (let ((doc (and (integerp arg) (sclang-get-document arg)))) - (and doc (switch-to-buffer doc))) - nil)) - -(sclang-set-command-handler - '_documentPutString -(lambda (arg) - (cl-multiple-value-bind (id str) arg - (let ((doc (and (integerp id) (sclang-get-document id)))) - (when doc - (with-current-buffer doc - (insert str) - ) - nil))))) - -(sclang-set-command-handler - '_documentPopTo - (lambda (arg) - (let ((doc (and (integerp arg) (sclang-get-document arg)))) - (and doc (display-buffer doc))) - nil)) - -;; ===================================================================== -;; sclang-mode -;; ===================================================================== - -(defun sclang-mode-set-local-variables () - (set (make-local-variable 'require-final-newline) nil) - ;; indentation - (set (make-local-variable 'indent-line-function) - 'sclang-indent-line) - (set (make-local-variable 'tab-width) 4) - (set (make-local-variable 'indent-tabs-mode) t) - ;; comment formatting - (set (make-local-variable 'comment-start) "// ") - (set (make-local-variable 'comment-end) "") - (set (make-local-variable 'comment-column) 40) - (set (make-local-variable 'comment-start-skip) "/\\*+ *\\|//+ *") - ;; "\\(^\\|\\s-\\);?// *") - (set (make-local-variable 'comment-multi-line) t) - ;; parsing and movement - (set (make-local-variable 'parse-sexp-ignore-comments) t) - (set (make-local-variable 'beginning-of-defun-function) - 'sclang-beginning-of-defun) - (set (make-local-variable 'end-of-defun-function) - 'sclang-end-of-defun) - ;; paragraph formatting - ;; (set (make-local-variable 'paragraph-start) (concat "$\\|" page-delimiter)) - ;; mostly copied from c++-mode, seems to work - (set (make-local-variable 'paragraph-start) - "[ \t]*\\(//+\\|\\**\\)[ \t]*$\\|^ ") - (set (make-local-variable 'paragraph-separate) paragraph-start) - (set (make-local-variable 'paragraph-ignore-fill-prefix) t) - (set (make-local-variable 'adaptive-fill-mode) t) - (set (make-local-variable 'adaptive-fill-regexp) - "[ \t]*\\(//+\\|\\**\\)[ \t]*\\([ \t]*\\([-|#;>*]+[ \t]*\\|(?[0-9]+[.)][ \t]*\\)*\\)") - ;; font lock - (set (make-local-variable 'font-lock-syntactic-face-function) - 'sclang-font-lock-syntactic-face) - (set (make-local-variable 'font-lock-defaults) - sclang-font-lock-defaults) - ;; --- - nil) - -(defvar sclang-mode-map (sclang-fill-mode-map (make-sparse-keymap)) - "Keymap used in SuperCollider mode.") - -(defvar sclang-mode-syntax-table (sclang-fill-syntax-table (make-syntax-table)) - "Syntax table used in SuperCollider mode.") - -(defcustom sclang-mode-hook nil - "*Hook run when entering SCLang mode." - :group 'sclang-mode - :type 'hook) - -;;;###autoload -(define-derived-mode sclang-mode prog-mode "SCLang" - "Major mode for editing SuperCollider language code. -\\{sclang-mode-map}" - :group 'sclang - :syntax-table sclang-mode-syntax-table - (sclang-mode-set-local-variables) - (sclang-set-font-lock-keywords) - (sclang-init-document) - (sclang-make-document) - - ;; Setup completion - (add-hook 'completion-at-point-functions - #'sclang-completion-at-point nil 'local) - (when (fboundp 'company-mode) - (add-to-list 'company-backends 'company-capf))) - -;; ===================================================================== -;; module initialization -;; ===================================================================== - -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.scd?\\'" . sclang-mode)) -(add-to-list 'interpreter-mode-alist '("sclang" . sclang-mode)) - -(add-hook 'sclang-library-startup-hook 'sclang-document-library-startup-hook-function) -(add-hook 'kill-buffer-hook 'sclang-document-kill-buffer-hook-function) -(add-hook 'post-command-hook 'sclang-document-post-command-hook-function) -(add-hook 'change-major-mode-hook 'sclang-document-change-major-mode-hook-function) +(eval-and-compile + (let ((load-path + (if (and (boundp 'byte-compile-dest-file) + (stringp byte-compile-dest-file)) + (cons (file-name-directory byte-compile-dest-file) load-path) + load-path))) + (require 'sclang-util) + (require 'sclang-browser) + (require 'sclang-interp) + (require 'sclang-language) + (require 'sclang-document) + (require 'sclang-mode) + (require 'sclang-minor-mode) + (require 'sclang-help) + (require 'sclang-server) + (require 'sclang-widgets))) (provide 'sclang) From 8503b2a3e204d4eee700b8a560afa944635f7fff Mon Sep 17 00:00:00 2001 From: John Andrews Date: Mon, 16 Aug 2021 20:33:05 -0400 Subject: [PATCH 11/17] Better autoloads Add autoloads which will cause whole sclang package to be loaded. --- el/sclang-interp.el | 1 + el/sclang-mode.el | 2 +- el/sclang-util.el | 4 +++- el/sclang.el | 26 ++++++++++---------------- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/el/sclang-interp.el b/el/sclang-interp.el index 04a9a41..b836347 100644 --- a/el/sclang-interp.el +++ b/el/sclang-interp.el @@ -302,6 +302,7 @@ If EOB-P is non-nil, positions cursor at end of buffer." (list "-s")) (list "-iscel")))) +;;;###autoload (autoload 'sclang-start "sclang" "Start SuperCollider process." t) (defun sclang-start () "Start SuperCollider process." (interactive) diff --git a/el/sclang-mode.el b/el/sclang-mode.el index 0f42491..4263a54 100644 --- a/el/sclang-mode.el +++ b/el/sclang-mode.el @@ -669,7 +669,7 @@ Returns the column to indent to." :group 'sclang-mode :type 'hook) -;;;###autoload +;;;###autoload (autoload 'sclang-mode "sclang" "Major mode for editing SuperCollider language" t) (define-derived-mode sclang-mode prog-mode "SCLang" "Major mode for editing SuperCollider language code. \\{sclang-mode-map}" diff --git a/el/sclang-util.el b/el/sclang-util.el index ea104cb..8a0ea6f 100644 --- a/el/sclang-util.el +++ b/el/sclang-util.el @@ -1,3 +1,5 @@ +;;; package: sclang-util --- Utility helpers for sclang +;; ;; copyright 2003-2005 stefan kersten ;; ;; This program is free software; you can redistribute it and/or @@ -83,4 +85,4 @@ (provide 'sclang-util) -;; EOF \ No newline at end of file +;;; sclang-util.el ends here diff --git a/el/sclang.el b/el/sclang.el index 9bbedcc..9b6b95b 100644 --- a/el/sclang.el +++ b/el/sclang.el @@ -55,22 +55,16 @@ (interactive) (customize-group 'sclang)) -(eval-and-compile - (let ((load-path - (if (and (boundp 'byte-compile-dest-file) - (stringp byte-compile-dest-file)) - (cons (file-name-directory byte-compile-dest-file) load-path) - load-path))) - (require 'sclang-util) - (require 'sclang-browser) - (require 'sclang-interp) - (require 'sclang-language) - (require 'sclang-document) - (require 'sclang-mode) - (require 'sclang-minor-mode) - (require 'sclang-help) - (require 'sclang-server) - (require 'sclang-widgets))) +(require 'sclang-util) +(require 'sclang-browser) +(require 'sclang-interp) +(require 'sclang-language) +(require 'sclang-document) +(require 'sclang-mode) +(require 'sclang-minor-mode) +(require 'sclang-help) +(require 'sclang-server) +(require 'sclang-widgets) (provide 'sclang) From fcfa17b6be78eb1f08d1edacd2d3f324d249fa2f Mon Sep 17 00:00:00 2001 From: John Andrews Date: Thu, 19 Aug 2021 20:53:11 -0400 Subject: [PATCH 12/17] Update readme --- README.md | 90 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 218705a..4f196b0 100644 --- a/README.md +++ b/README.md @@ -6,70 +6,71 @@ SuperCollider/Emacs interface There are 3 options for installation: -1. Using Emacs' and SuperCollider's own package managers (recommended) +1. Using SuperCollider Quarks (recommended) 2. From debian package `supercollider-emacs` 3. From source Option #1 is the best cross-platform option, and is recommended. Whatever option -you choose, you should only install from a single source. +you choose, make sure not to mix installation methods. In particular, do not +install the Quark if you already have the supercollider-emacs package or if you +compiled SuperCollider with the `-DSC_EL=ON` option. Otherwise you will get an +error from SuperCollider about duplicated classes. -### Install Option 1: Native Packages +### Install Option 1: SuperCollider's own package manager The repository contains two subprojects. `/el` contains the emacs-lisp -implementation. `/scide_scel` contains the SuperCollider code required to -implement the Emacs interface. Emacs and SuperCollider each have their own -package managers, so it is required to install each half separately. +implementation. `/sc` contains the SuperCollider code required to +implement the Emacs interface. SuperCollider has its own package system +called Quarks, which we can use to install both halves. -#### Installing scel quark - -The `scel` Quark is required for emacs to communicate with sclang. Evaluate this -code in the SuperCollider GUI: +Evaluate this code in the SuperCollider GUI by pasting it and pressing +shift+enter: ``` supercollider Quarks.install("https://github.com/supercollider/scel"); ``` The scel repository will be downloaded to your local file system and the path -will be added to your default `sclang_conf.yaml` file. If you want to change the -yaml file used by your emacs sessions you can customize the -`sclang-library-configuration-file` variable, but make sure that file loads -the scel quark. +will be added to your default `sclang_conf.yaml` file. (You can find its +location by evaluating `Platform.userConfigDir`) -#### Installing the emacs mode +Next, find out where scel was installed. You will use this install-path in your +emacs config. -Using [straight.el](https://github.com/raxod502/straight.el), put the following -in your init file: +``` supercollider +Quarks.folder.postln; -``` emacs-lisp -;; in ~/.emacs -(straight-use-package - '(sclang :type git - :host github - :repo "supercollider/scel" - :files ("el/*.el"))) -``` - -Or download the repo directly to your user config directory - -``` shell -git clone https://github.com/supercollider/scel.git ~/.emacs.d/scel +// -> /Users/jxa/Library/Application Support/SuperCollider/downloaded-quarks ``` +Now in your emacs config, add the `/el` subdirectory to your load path ``` emacs-lisp ;; in ~/.emacs -(add-to-list 'load-path "~/.emacs.d/scel/el/") + +;; Paste path from above, appending "/scel/el" +(add-to-list 'load-path "/Users/jxa/Library/Application Support/SuperCollider/downloaded-quarks/scel/el") (require 'sclang) ``` - #### On macOS If `sclang` executable is not on your path, you may need to add it to your exec-path. ``` emacs-lisp +;; in ~/.emacs (setq exec-path (append exec-path '("/Applications/SuperCollider.app/Contents/MacOS/"))) ``` +#### Installing with an emacs package manager + +It's completely possible to install with +[straight.el](https://github.com/raxod502/straight.el), +[use-package](https://github.com/jwiegley/use-package), +[doom](https://github.com/hlissner/doom-emacs), etc. Instructions for doing so +are beyond the scope of this README, but note that `autoloads` are implemented +for entry-point functions so if you like to have a speedy start-up time you can +use the `:defer t` option. + ### Install Option 2: Debian package There is a debian package which provides emacs integration called @@ -97,20 +98,31 @@ the rest of the functionality without the w3m dependency. (require 'w3m) ``` -## Configuration +## Usage -To fine-tune the installation from within emacs' graphical customization -interface, type: +The main function which starts interacting with the sclang interpreter is +`sclang-start`. You can execute that anywhere with `M-x sclang-start`, or from +within a `.scd` buffer by pressing `C-c C-o`. -`M-x sclang-customize` +If you know you want to launch sclang when you start emacs you can use the `-f` +option to execute that function right away: +``` shell +# in your terminal +emacs -f sclang-start +``` -## Usage +## Configuration -Open a `.scd` file and press `C-c C-o` to start the interpreter. +To fine-tune the installation from within emacs' graphical customization +interface, type: -You're now ready to edit, inspect and execute sclang code! +`M-x sclang-customize` +If you want to change from the default `sclang_conf.yaml` file used by your +emacs sessions you can customize the `sclang-library-configuration-file` +variable, but make sure that file loads the scel quark, or you won't be +able to run sclang code. ## Getting help From 4e1c886b9851713a50437f46fe89cf3779908d17 Mon Sep 17 00:00:00 2001 From: John Andrews Date: Thu, 19 Aug 2021 20:54:46 -0400 Subject: [PATCH 13/17] Move files back under /sc --- CMakeLists.txt | 2 +- {scide_scel => sc/scide_scel}/CMakeLists.txt | 0 {scide_scel => sc/scide_scel}/Document.sc | 0 {scide_scel => sc/scide_scel}/Emacs.sc | 0 {scide_scel => sc/scide_scel}/EmacsBuffer.sc | 0 {scide_scel => sc/scide_scel}/EmacsDocument.sc | 0 {scide_scel => sc/scide_scel}/ScelDocument.sc | 0 {scide_scel => sc/scide_scel}/extBuffer.sc | 0 {scide_scel => sc/scide_scel}/extClassBrowser.sc | 0 {scide_scel => sc/scide_scel}/extString.sc | 0 {scide_scel => sc/scide_scel}/homeContext.sc | 0 {scide_scel => sc/scide_scel}/storeLispOn.sc | 0 12 files changed, 1 insertion(+), 1 deletion(-) rename {scide_scel => sc/scide_scel}/CMakeLists.txt (100%) rename {scide_scel => sc/scide_scel}/Document.sc (100%) rename {scide_scel => sc/scide_scel}/Emacs.sc (100%) rename {scide_scel => sc/scide_scel}/EmacsBuffer.sc (100%) rename {scide_scel => sc/scide_scel}/EmacsDocument.sc (100%) rename {scide_scel => sc/scide_scel}/ScelDocument.sc (100%) rename {scide_scel => sc/scide_scel}/extBuffer.sc (100%) rename {scide_scel => sc/scide_scel}/extClassBrowser.sc (100%) rename {scide_scel => sc/scide_scel}/extString.sc (100%) rename {scide_scel => sc/scide_scel}/homeContext.sc (100%) rename {scide_scel => sc/scide_scel}/storeLispOn.sc (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 90d90d4..9d77014 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(el) -add_subdirectory(scide_scel) +add_subdirectory(sc/scide_scel) install(DIRECTORY HelpSource DESTINATION share/SuperCollider/Extensions/scide_scel/) diff --git a/scide_scel/CMakeLists.txt b/sc/scide_scel/CMakeLists.txt similarity index 100% rename from scide_scel/CMakeLists.txt rename to sc/scide_scel/CMakeLists.txt diff --git a/scide_scel/Document.sc b/sc/scide_scel/Document.sc similarity index 100% rename from scide_scel/Document.sc rename to sc/scide_scel/Document.sc diff --git a/scide_scel/Emacs.sc b/sc/scide_scel/Emacs.sc similarity index 100% rename from scide_scel/Emacs.sc rename to sc/scide_scel/Emacs.sc diff --git a/scide_scel/EmacsBuffer.sc b/sc/scide_scel/EmacsBuffer.sc similarity index 100% rename from scide_scel/EmacsBuffer.sc rename to sc/scide_scel/EmacsBuffer.sc diff --git a/scide_scel/EmacsDocument.sc b/sc/scide_scel/EmacsDocument.sc similarity index 100% rename from scide_scel/EmacsDocument.sc rename to sc/scide_scel/EmacsDocument.sc diff --git a/scide_scel/ScelDocument.sc b/sc/scide_scel/ScelDocument.sc similarity index 100% rename from scide_scel/ScelDocument.sc rename to sc/scide_scel/ScelDocument.sc diff --git a/scide_scel/extBuffer.sc b/sc/scide_scel/extBuffer.sc similarity index 100% rename from scide_scel/extBuffer.sc rename to sc/scide_scel/extBuffer.sc diff --git a/scide_scel/extClassBrowser.sc b/sc/scide_scel/extClassBrowser.sc similarity index 100% rename from scide_scel/extClassBrowser.sc rename to sc/scide_scel/extClassBrowser.sc diff --git a/scide_scel/extString.sc b/sc/scide_scel/extString.sc similarity index 100% rename from scide_scel/extString.sc rename to sc/scide_scel/extString.sc diff --git a/scide_scel/homeContext.sc b/sc/scide_scel/homeContext.sc similarity index 100% rename from scide_scel/homeContext.sc rename to sc/scide_scel/homeContext.sc diff --git a/scide_scel/storeLispOn.sc b/sc/scide_scel/storeLispOn.sc similarity index 100% rename from scide_scel/storeLispOn.sc rename to sc/scide_scel/storeLispOn.sc From 4852752d67bb6378fe1b791e3bf6777ec2bec97e Mon Sep 17 00:00:00 2001 From: John Andrews Date: Thu, 19 Aug 2021 21:23:54 -0400 Subject: [PATCH 14/17] Add a test for the autoloading behavior --- el/test/sclang-mode-test.el | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/el/test/sclang-mode-test.el b/el/test/sclang-mode-test.el index 1273019..54d78d4 100644 --- a/el/test/sclang-mode-test.el +++ b/el/test/sclang-mode-test.el @@ -1,6 +1,11 @@ ;; -*- no-byte-compile: t; lexical-binding: t; -*- ;;; test/sclang-mode-test.el +(ert-deftest sclang-autoloaded-functions () + "Some functions should be callable interactively without requiring them" + (should (commandp 'sclang-start t)) + (should (commandp 'sclang-mode t))) + (ert-deftest sclang-major-mode-init-test () "Loading a file with an scd extension should init sclang-mode" (find-file "fixtures/super-boring.scd") From cbd89755e8e3cbdf36c9dae9e96724bfb7b118da Mon Sep 17 00:00:00 2001 From: John Andrews Date: Sat, 4 Sep 2021 09:46:59 -0400 Subject: [PATCH 15/17] README clarifications Also, autoload `sclang-customize`, as that should be able to be called interactively regardless of whether sclang is started --- README.md | 29 +++++++++++++++++++---------- el/sclang.el | 1 + 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 4f196b0..e8aa9f2 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ There are 3 options for installation: 3. From source Option #1 is the best cross-platform option, and is recommended. Whatever option -you choose, make sure not to mix installation methods. In particular, do not +you choose, *make sure not to mix installation methods*. In particular, do not install the Quark if you already have the supercollider-emacs package or if you compiled SuperCollider with the `-DSC_EL=ON` option. Otherwise you will get an error from SuperCollider about duplicated classes. @@ -40,7 +40,7 @@ emacs config. ``` supercollider Quarks.folder.postln; -// -> /Users/jxa/Library/Application Support/SuperCollider/downloaded-quarks +// -> /Users//Library/Application Support/SuperCollider/downloaded-quarks ``` Now in your emacs config, add the `/el` subdirectory to your load path @@ -48,7 +48,7 @@ Now in your emacs config, add the `/el` subdirectory to your load path ;; in ~/.emacs ;; Paste path from above, appending "/scel/el" -(add-to-list 'load-path "/Users/jxa/Library/Application Support/SuperCollider/downloaded-quarks/scel/el") +(add-to-list 'load-path "/Users//Library/Application Support/SuperCollider/downloaded-quarks/scel/el") (require 'sclang) ``` #### On macOS @@ -88,10 +88,18 @@ install this library along with it. The cmake `-DSC_EL` flag controls whether scel will be compiled. On Linux machines `-DSC_EL=ON` by default. See the supercollider README files for more info. -## Installation requirements +``` emacs-lisp +;; in ~/.emacs +(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/SuperCollider/") ;; path will depend on your compilation settings +(require 'sclang) +``` + +## Optional Installation Requirements -For the HTML help system, you will need emacs-w3m support, but you can still use -the rest of the functionality without the w3m dependency. +There are two options for SuperCollider help files. They can be opened in the +help browser that ships with SuperCollider, or if you prefer an emacs-only +workflow they can be opened using the w3m browser. The browse-in-emacs option +requires an additional dependency. ```emacs-lisp ;; in ~/.emacs @@ -119,10 +127,11 @@ interface, type: `M-x sclang-customize` -If you want to change from the default `sclang_conf.yaml` file used by your -emacs sessions you can customize the `sclang-library-configuration-file` -variable, but make sure that file loads the scel quark, or you won't be -able to run sclang code. +NOTE: If you use an sclang configuration file different from the default +`sclang_conf.yaml`, you need to specify it in scel by customizing the +`sclang-library-configuration-file `variable. Otherwise, even after installing +the Quark in SuperCollider, you won't be able to run sclang code in emacs. + ## Getting help diff --git a/el/sclang.el b/el/sclang.el index 9b6b95b..edae1fb 100644 --- a/el/sclang.el +++ b/el/sclang.el @@ -50,6 +50,7 @@ "Options for the SuperCollider process." :group 'sclang-interface) +;;;###autoload (defun sclang-customize () "Customize sclang variables." (interactive) From 057a1b30f2620c19d94e09ad1f7350dd2317023c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20P=C4=85czkowski?= Date: Sat, 4 Sep 2021 09:39:05 -0700 Subject: [PATCH 16/17] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e8aa9f2..2494657 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Quarks.install("https://github.com/supercollider/scel"); ``` The scel repository will be downloaded to your local file system and the path -will be added to your default `sclang_conf.yaml` file. (You can find its +will be added to your currently used `sclang_conf.yaml` file. (You can find its location by evaluating `Platform.userConfigDir`) Next, find out where scel was installed. You will use this install-path in your @@ -85,7 +85,8 @@ sudo apt install supercollider-emacs If you are building SuperCollider from source, you can optionally compile and install this library along with it. The cmake `-DSC_EL` flag controls whether -scel will be compiled. On Linux machines `-DSC_EL=ON` by default. See the +scel will be compiled. On Linux machines `-DSC_EL=ON` by +. See the supercollider README files for more info. ``` emacs-lisp From 35b972f1a79e2ed70ed055d0e4d834d79cbae5a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20P=C4=85czkowski?= Date: Sat, 4 Sep 2021 09:41:16 -0700 Subject: [PATCH 17/17] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2494657..21a949a 100644 --- a/README.md +++ b/README.md @@ -85,8 +85,7 @@ sudo apt install supercollider-emacs If you are building SuperCollider from source, you can optionally compile and install this library along with it. The cmake `-DSC_EL` flag controls whether -scel will be compiled. On Linux machines `-DSC_EL=ON` by -. See the +scel will be compiled. On Linux machines `-DSC_EL=ON` by default. See the supercollider README files for more info. ``` emacs-lisp