summaryrefslogtreecommitdiff
path: root/emacs/guix-utils.el
diff options
context:
space:
mode:
Diffstat (limited to 'emacs/guix-utils.el')
-rw-r--r--emacs/guix-utils.el609
1 files changed, 0 insertions, 609 deletions
diff --git a/emacs/guix-utils.el b/emacs/guix-utils.el
deleted file mode 100644
index 3e4ecc36ab..0000000000
--- a/emacs/guix-utils.el
+++ /dev/null
@@ -1,609 +0,0 @@
-;;; guix-utils.el --- General utility functions -*- lexical-binding: t -*-
-
-;; Copyright © 2014, 2015, 2016 Alex Kost <alezost@gmail.com>
-
-;; This file is part of GNU Guix.
-
-;; GNU Guix 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 3 of the License, or
-;; (at your option) any later version.
-
-;; GNU Guix 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, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This file provides auxiliary general functions for guix.el package.
-
-;;; Code:
-
-(require 'cl-lib)
-
-(defvar guix-true-string "Yes")
-(defvar guix-false-string "–")
-(defvar guix-list-separator ", ")
-
-(defvar guix-time-format "%F %T"
- "String used to format time values.
-For possible formats, see `format-time-string'.")
-
-(defun guix-get-string (val &optional face)
- "Convert VAL into a string and return it.
-
-VAL can be an expression of any type.
-If VAL is t/nil, it is replaced with
-`guix-true-string'/`guix-false-string'.
-If VAL is list, its elements are concatenated using
-`guix-list-separator'.
-
-If FACE is non-nil, propertize returned string with this FACE."
- (let ((str (cond
- ((stringp val) val)
- ((null val) guix-false-string)
- ((eq t val) guix-true-string)
- ((numberp val) (number-to-string val))
- ((listp val) (mapconcat #'guix-get-string
- val guix-list-separator))
- (t (prin1-to-string val)))))
- (if (and val face)
- (propertize str 'font-lock-face face)
- str)))
-
-(defun guix-get-time-string (seconds)
- "Return formatted time string from SECONDS.
-Use `guix-time-format'."
- (format-time-string guix-time-format (seconds-to-time seconds)))
-
-(defun guix-get-one-line (str)
- "Return one-line string from a multi-line STR."
- (replace-regexp-in-string "\n" " " str))
-
-(defmacro guix-with-indent (indent &rest body)
- "Evaluate BODY and indent inserted text by INDENT number of spaces."
- (declare (indent 1) (debug t))
- (let ((region-beg-var (make-symbol "region-beg"))
- (indent-var (make-symbol "indent")))
- `(let ((,region-beg-var (point))
- (,indent-var ,indent))
- ,@body
- (unless (zerop ,indent-var)
- (indent-rigidly ,region-beg-var (point) ,indent-var)))))
-
-(defun guix-format-insert (val &optional face format)
- "Convert VAL into a string and insert it at point.
-If FACE is non-nil, propertize VAL with FACE.
-If FORMAT is non-nil, format VAL with FORMAT."
- (let ((str (guix-get-string val face)))
- (insert (if format
- (format format str)
- str))))
-
-(cl-defun guix-mapinsert (function sequence separator &key indent column)
- "Like `mapconcat' but for inserting text.
-Apply FUNCTION to each element of SEQUENCE, and insert SEPARATOR
-at point between each FUNCTION call.
-
-If INDENT is non-nil, it should be a number of spaces used to
-indent each line of the inserted text.
-
-If COLUMN is non-nil, it should be a column number which
-shouldn't be exceeded by the inserted text."
- (pcase sequence
- (`(,first . ,rest)
- (let* ((indent (or indent 0))
- (max-column (and column (- column indent))))
- (guix-with-indent indent
- (funcall function first)
- (dolist (element rest)
- (let ((before-sep-pos (and column (point))))
- (insert separator)
- (let ((after-sep-pos (and column (point))))
- (funcall function element)
- (when (and column
- (> (current-column) max-column))
- (save-excursion
- (delete-region before-sep-pos after-sep-pos)
- (goto-char before-sep-pos)
- (insert "\n")))))))))))
-
-(defun guix-insert-button (label &optional type &rest properties)
- "Make button of TYPE with LABEL and insert it at point.
-See `insert-text-button' for the meaning of PROPERTIES."
- (if (null label)
- (guix-format-insert nil)
- (apply #'insert-text-button label
- :type (or type 'button)
- properties)))
-
-(defun guix-buttonize (value button-type separator &rest properties)
- "Make BUTTON-TYPE button(s) from VALUE.
-Return a string with button(s).
-
-VALUE should be a string or a list of strings. If it is a list
-of strings, buttons are separated with SEPARATOR string.
-
-PROPERTIES are passed to `guix-insert-button'."
- (with-temp-buffer
- (let ((labels (if (listp value) value (list value))))
- (guix-mapinsert (lambda (label)
- (apply #'guix-insert-button
- label button-type properties))
- labels
- separator))
- (buffer-substring (point-min) (point-max))))
-
-(defun guix-button-type? (symbol)
- "Return non-nil, if SYMBOL is a button type."
- (and symbol
- (get symbol 'button-category-symbol)))
-
-(defun guix-split-insert (val &optional face col separator)
- "Convert VAL into a string, split it and insert at point.
-
-If FACE is non-nil, propertize returned string with this FACE.
-
-If COL is non-nil and result string is a one-line string longer
-than COL, split it into several short lines.
-
-Separate inserted lines with SEPARATOR."
- (if (null val)
- (guix-format-insert nil)
- (let ((strings (guix-split-string (guix-get-string val) col)))
- (guix-mapinsert (lambda (str) (guix-format-insert str face))
- strings
- (or separator "")))))
-
-(defun guix-split-string (str &optional col)
- "Split string STR by lines and return list of result strings.
-If COL is non-nil, fill STR to this column."
- (let ((str (if col
- (guix-get-filled-string str col)
- str)))
- (split-string str "\n *" t)))
-
-(defun guix-get-filled-string (str col)
- "Return string by filling STR to column COL."
- (with-temp-buffer
- (insert str)
- (let ((fill-column col))
- (fill-region (point-min) (point-max)))
- (buffer-string)))
-
-(defun guix-concat-strings (strings separator &optional location)
- "Return new string by concatenating STRINGS with SEPARATOR.
-If LOCATION is a symbol `head', add another SEPARATOR to the
-beginning of the returned string; if `tail' - add SEPARATOR to
-the end of the string; if nil, do not add SEPARATOR; otherwise
-add both to the end and to the beginning."
- (let ((str (mapconcat #'identity strings separator)))
- (cond ((null location)
- str)
- ((eq location 'head)
- (concat separator str))
- ((eq location 'tail)
- (concat str separator))
- (t
- (concat separator str separator)))))
-
-(defun guix-hexify (value)
- "Convert VALUE to string and hexify it."
- (url-hexify-string (guix-get-string value)))
-
-(defun guix-number->bool (number)
- "Convert NUMBER to boolean value.
-Return nil, if NUMBER is 0; return t otherwise."
- (not (zerop number)))
-
-(defun guix-shell-quote-argument (argument)
- "Quote shell command ARGUMENT.
-This function is similar to `shell-quote-argument', but less strict."
- (if (equal argument "")
- "''"
- (replace-regexp-in-string
- "\n" "'\n'"
- (replace-regexp-in-string
- (rx (not (any alnum "-=,./\n"))) "\\\\\\&" argument))))
-
-(defun guix-symbol-title (symbol)
- "Return SYMBOL's name, a string.
-This is like `symbol-name', but fancier."
- (if (eq symbol 'id)
- "ID"
- (let ((str (replace-regexp-in-string "-" " " (symbol-name symbol))))
- (concat (capitalize (substring str 0 1))
- (substring str 1)))))
-
-(defun guix-command-symbol (&optional args)
- "Return symbol by concatenating 'guix' and ARGS (strings)."
- (intern (guix-concat-strings (cons "guix" args) "-")))
-
-(defun guix-command-string (&optional args)
- "Return 'guix ARGS ...' string with quoted shell arguments."
- (let ((args (mapcar #'guix-shell-quote-argument args)))
- (guix-concat-strings (cons "guix" args) " ")))
-
-(defun guix-copy-as-kill (string &optional no-message?)
- "Put STRING into `kill-ring'.
-If NO-MESSAGE? is non-nil, do not display a message about it."
- (kill-new string)
- (unless no-message?
- (message "'%s' has been added to kill ring." string)))
-
-(defun guix-copy-command-as-kill (args &optional no-message?)
- "Put 'guix ARGS ...' string into `kill-ring'.
-See also `guix-copy-as-kill'."
- (guix-copy-as-kill (guix-command-string args) no-message?))
-
-(defun guix-compose-buffer-name (base-name postfix)
- "Return buffer name by appending BASE-NAME and POSTFIX.
-
-In a simple case the result is:
-
- BASE-NAME: POSTFIX
-
-If BASE-NAME is wrapped by '*', then the result is:
-
- *BASE-NAME: POSTFIX*"
- (let ((re (rx string-start
- (group (? "*"))
- (group (*? any))
- (group (? "*"))
- string-end)))
- (or (string-match re base-name)
- (error "Unexpected error in defining buffer name"))
- (let ((first* (match-string 1 base-name))
- (name-body (match-string 2 base-name))
- (last* (match-string 3 base-name)))
- ;; Handle the case when buffer name is wrapped by '*'.
- (if (and (string= "*" first*)
- (string= "*" last*))
- (concat "*" name-body ": " postfix "*")
- (concat base-name ": " postfix)))))
-
-(defun guix-completing-read (prompt table &optional predicate
- require-match initial-input
- hist def inherit-input-method)
- "Same as `completing-read' but return nil instead of an empty string."
- (let ((res (completing-read prompt table predicate
- require-match initial-input
- hist def inherit-input-method)))
- (unless (string= "" res) res)))
-
-(defun guix-completing-read-multiple (prompt table &optional predicate
- require-match initial-input
- hist def inherit-input-method)
- "Same as `completing-read-multiple' but remove duplicates in result."
- (cl-remove-duplicates
- (completing-read-multiple prompt table predicate
- require-match initial-input
- hist def inherit-input-method)
- :test #'string=))
-
-(declare-function org-read-date "org" t)
-
-(defun guix-read-date (prompt)
- "Prompt for a date or time using `org-read-date'.
-Return time value."
- (require 'org)
- (org-read-date nil t nil prompt))
-
-(defun guix-read-file-name (prompt &optional dir default-filename
- mustmatch initial predicate)
- "Read file name.
-This function is similar to `read-file-name' except it also
-expands the file name."
- (expand-file-name (read-file-name prompt dir default-filename
- mustmatch initial predicate)))
-
-(defcustom guix-find-file-function #'find-file
- "Function used to find a file.
-The function is called by `guix-find-file' with a file name as a
-single argument."
- :type '(choice (function-item find-file)
- (function-item org-open-file)
- (function :tag "Other function"))
- :group 'guix)
-
-(defun guix-find-file (file)
- "Find FILE if it exists."
- (if (file-exists-p file)
- (funcall guix-find-file-function file)
- (message "File '%s' does not exist." file)))
-
-(defvar url-handler-regexp)
-
-(defun guix-find-file-or-url (file-or-url)
- "Find FILE-OR-URL."
- (require 'url-handlers)
- (let ((file-name-handler-alist
- (cons (cons url-handler-regexp 'url-file-handler)
- file-name-handler-alist)))
- (find-file file-or-url)))
-
-(defmacro guix-while-search (regexp &rest body)
- "Evaluate BODY after each search for REGEXP in the current buffer."
- (declare (indent 1) (debug t))
- `(save-excursion
- (goto-char (point-min))
- (while (re-search-forward ,regexp nil t)
- ,@body)))
-
-(defmacro guix-while-null (&rest body)
- "Evaluate BODY until its result becomes non-nil."
- (declare (indent 0) (debug t))
- (let ((result-var (make-symbol "result")))
- `(let (,result-var)
- (while (null ,result-var)
- (setq ,result-var ,@body))
- ,result-var)))
-
-(defun guix-modify (object modifiers)
- "Apply MODIFIERS to OBJECT.
-OBJECT is passed as an argument to the first function from
-MODIFIERS list, the returned result is passed to the second
-function from the list and so on. Return result of the last
-modifier call."
- (if (null modifiers)
- object
- (guix-modify (funcall (car modifiers) object)
- (cdr modifiers))))
-
-(defmacro guix-keyword-args-let (args varlist &rest body)
- "Parse ARGS, bind variables from VARLIST and eval BODY.
-
-Find keyword values in ARGS, bind them to variables according to
-VARLIST, then evaluate BODY.
-
-ARGS is a keyword/value property list.
-
-Each element of VARLIST has a form:
-
- (SYMBOL KEYWORD [DEFAULT-VALUE])
-
-SYMBOL is a varible name. KEYWORD is a symbol that will be
-searched in ARGS for an according value. If the value of KEYWORD
-does not exist, bind SYMBOL to DEFAULT-VALUE or nil.
-
-The rest arguments (that present in ARGS but not in VARLIST) will
-be bound to `%foreign-args' variable.
-
-Example:
-
- (guix-keyword-args-let '(:two 8 :great ! :guix is)
- ((one :one 1)
- (two :two 2)
- (foo :smth))
- (list one two foo %foreign-args))
-
- => (1 8 nil (:guix is :great !))"
- (declare (indent 2))
- (let ((args-var (make-symbol "args")))
- `(let (,@(mapcar (lambda (spec)
- (pcase-let ((`(,name ,_ ,val) spec))
- (list name val)))
- varlist)
- (,args-var ,args)
- %foreign-args)
- (while ,args-var
- (pcase ,args-var
- (`(,key ,val . ,rest-args)
- (cl-case key
- ,@(mapcar (lambda (spec)
- (pcase-let ((`(,name ,key ,_) spec))
- `(,key (setq ,name val))))
- varlist)
- (t (setq %foreign-args
- (cl-list* key val %foreign-args))))
- (setq ,args-var rest-args))))
- ,@body)))
-
-
-;;; Alist procedures
-
-(defmacro guix-define-alist-accessor (name assoc-fun)
- "Define NAME function to access alist values using ASSOC-FUN."
- `(defun ,name (alist &rest keys)
- ,(format "Return value from ALIST by KEYS using `%s'.
-ALIST is alist of alists of alists ... which can be consecutively
-accessed with KEYS."
- assoc-fun)
- (if (or (null alist) (null keys))
- alist
- (apply #',name
- (cdr (,assoc-fun (car keys) alist))
- (cdr keys)))))
-
-(guix-define-alist-accessor guix-assq-value assq)
-(guix-define-alist-accessor guix-assoc-value assoc)
-
-(defun guix-alist-put (value alist &rest keys)
- "Put (add or replace if exists) VALUE to ALIST using KEYS.
-Return the new alist.
-
-ALIST is alist of alists of alists ... which can be consecutively
-accessed with KEYS.
-
-Example:
-
- (guix-alist-put
- 'foo
- '((one (a . 1) (b . 2))
- (two (m . 7) (n . 8)))
- 'one 'b)
-
- => ((one (a . 1) (b . foo))
- (two (m . 7) (n . 8)))"
- (or keys (error "Keys should be specified"))
- (guix-alist-put-1 value alist keys))
-
-(defun guix-alist-put-1 (value alist keys)
- "Subroutine of `guix-alist-put'."
- (cond
- ((null keys)
- value)
- ((null alist)
- (list (cons (car keys)
- (guix-alist-put-1 value nil (cdr keys)))))
- ((eq (car keys) (caar alist))
- (cons (cons (car keys)
- (guix-alist-put-1 value (cdar alist) (cdr keys)))
- (cdr alist)))
- (t
- (cons (car alist)
- (guix-alist-put-1 value (cdr alist) keys)))))
-
-(defun guix-alist-put! (value variable &rest keys)
- "Modify alist VARIABLE (symbol) by putting VALUE using KEYS.
-See `guix-alist-put' for details."
- (set variable
- (apply #'guix-alist-put value (symbol-value variable) keys)))
-
-
-;;; Diff
-
-(defvar guix-diff-switches "-u"
- "A string or list of strings specifying switches to be passed to diff.")
-
-(defun guix-diff (old new &optional switches no-async)
- "Same as `diff', but use `guix-diff-switches' as default."
- (diff old new (or switches guix-diff-switches) no-async))
-
-
-;;; Completing readers definers
-
-(defmacro guix-define-reader (name read-fun completions prompt)
- "Define NAME function to read from minibuffer.
-READ-FUN may be `completing-read', `completing-read-multiple' or
-another function with the same arguments."
- `(defun ,name (&optional prompt initial-contents)
- (,read-fun ,(if prompt
- `(or prompt ,prompt)
- 'prompt)
- ,completions nil nil initial-contents)))
-
-(defmacro guix-define-readers (&rest args)
- "Define reader functions.
-
-ARGS should have a form [KEYWORD VALUE] ... The following
-keywords are available:
-
- - `completions-var' - variable used to get completions.
-
- - `completions-getter' - function used to get completions.
-
- - `single-reader', `single-prompt' - name of a function to read
- a single value, and a prompt for it.
-
- - `multiple-reader', `multiple-prompt' - name of a function to
- read multiple values, and a prompt for it.
-
- - `multiple-separator' - if specified, another
- `<multiple-reader-name>-string' function returning a string
- of multiple values separated the specified separator will be
- defined."
- (guix-keyword-args-let args
- ((completions-var :completions-var)
- (completions-getter :completions-getter)
- (single-reader :single-reader)
- (single-prompt :single-prompt)
- (multiple-reader :multiple-reader)
- (multiple-prompt :multiple-prompt)
- (multiple-separator :multiple-separator))
- (let ((completions
- (cond ((and completions-var completions-getter)
- `(or ,completions-var
- (setq ,completions-var
- (funcall ',completions-getter))))
- (completions-var
- completions-var)
- (completions-getter
- `(funcall ',completions-getter)))))
- `(progn
- ,(when (and completions-var
- (not (boundp completions-var)))
- `(defvar ,completions-var nil))
-
- ,(when single-reader
- `(guix-define-reader ,single-reader guix-completing-read
- ,completions ,single-prompt))
-
- ,(when multiple-reader
- `(guix-define-reader ,multiple-reader completing-read-multiple
- ,completions ,multiple-prompt))
-
- ,(when (and multiple-reader multiple-separator)
- (let ((name (intern (concat (symbol-name multiple-reader)
- "-string"))))
- `(defun ,name (&optional prompt initial-contents)
- (guix-concat-strings
- (,multiple-reader prompt initial-contents)
- ,multiple-separator))))))))
-
-
-;;; Memoizing
-
-(defun guix-memoize (function)
- "Return a memoized version of FUNCTION."
- (let ((cache (make-hash-table :test 'equal)))
- (lambda (&rest args)
- (let ((result (gethash args cache 'not-found)))
- (if (eq result 'not-found)
- (let ((result (apply function args)))
- (puthash args result cache)
- result)
- result)))))
-
-(defmacro guix-memoized-defun (name arglist docstring &rest body)
- "Define a memoized function NAME.
-See `defun' for the meaning of arguments."
- (declare (doc-string 3) (indent 2))
- `(defalias ',name
- (guix-memoize (lambda ,arglist ,@body))
- ;; Add '(name args ...)' string with real arglist to the docstring,
- ;; because *Help* will display '(name &rest ARGS)' for a defined
- ;; function (since `guix-memoize' returns a lambda with '(&rest
- ;; args)').
- ,(format "(%S %s)\n\n%s"
- name
- (mapconcat #'symbol-name arglist " ")
- docstring)))
-
-(defmacro guix-memoized-defalias (symbol definition &optional docstring)
- "Set SYMBOL's function definition to memoized version of DEFINITION."
- (declare (doc-string 3) (indent 1))
- `(defalias ',symbol
- (guix-memoize #',definition)
- ,(or docstring
- (format "Memoized version of `%S'." definition))))
-
-
-(defvar guix-utils-font-lock-keywords
- (eval-when-compile
- `((,(rx "(" (group (or "guix-define-reader"
- "guix-define-readers"
- "guix-keyword-args-let"
- "guix-while-null"
- "guix-while-search"
- "guix-with-indent"))
- symbol-end)
- . 1)
- (,(rx "("
- (group "guix-memoized-" (or "defun" "defalias"))
- symbol-end
- (zero-or-more blank)
- (zero-or-one
- (group (one-or-more (or (syntax word) (syntax symbol))))))
- (1 font-lock-keyword-face)
- (2 font-lock-function-name-face nil t)))))
-
-(font-lock-add-keywords 'emacs-lisp-mode guix-utils-font-lock-keywords)
-
-(provide 'guix-utils)
-
-;;; guix-utils.el ends here