Compare commits

...

11 commits

Author SHA1 Message Date
João Távora
a2df797f83 Add mechanism for escaping shorthand substitution
* src/lread.c (read1): Add skip_shorthand variable.  Add a '#\'
case.  Sometimes call oblookup instead of
oblookup_considering_shorthand.
2021-09-22 23:53:15 +01:00
João Távora
881478bca9 Consider shorthands in Elisp's elisp-completion-at-point
* lisp/progmodes/elisp-mode.el : new helper.
(elisp-completion-at-point): Use new helpers.
(elisp--completion-local-symbols)
(elisp--fboundp-considering-shorthands)
(elisp--bboundp-considering-shorthands): New helpers

* src/lread.c (intern_driver): Nullify Qobarray_cache.
(syms_of_lread): Add Qobarray_cache.

* test/lisp/progmodes/elisp-resources/magnars-string-user.el: New
file to play around with `magnars-string` library.
2021-09-22 18:05:35 +01:00
João Távora
39a63cda6d * lisp/emacs-lisp/magnars-string.el: New file. 2021-09-22 11:33:56 +01:00
João Távora
eed51f26c5 Adjust C style and add comments to shorthand code
* src/lread.c (oblookup_considering_shorthand): Adjust
declaration.
(read1, Fintern_soft): Adjust C style.
(oblookup_considering_shorthand): Adjust C style.  Add comments.
2021-09-22 11:33:56 +01:00
João Távora
d102e30618 Rework docstring of hack-elisp-shorthands
* lisp/international/mule.el (hack-elisp-shorthands): Rework
docstring.
2021-09-22 11:33:56 +01:00
João Távora
62523a0009 Rework elisp-shorthands to only allow only prefix substitution
This simplification in requirements makes for more complex C code but
that code is much less wasteful in Lisp strings than the previous
implementation.

* src/lread.c (read1): Rework.
(Fintern): Rework.
(Fintern_soft): Rework.
(Funintern): Rework.
(oblookup_considering_shorthand): Rewrite.

* test/lisp/progmodes/elisp-mode-tests.el (elisp-shorthand-read-buffer)
(elisp-shorthand-read-from-string): Use new format of
shorthand-longhand-.

* test/lisp/progmodes/elisp-resources/simple-shorthand-test.el (f-test)
(f-test2, f-test3): Use new form of elisp-shorthands.
2021-09-22 11:33:56 +01:00
João Távora
2f95a95041 Add a test for byte-compilation
* test/lisp/progmodes/elisp-mode-tests.el
(elisp-shorthand-byte-compile-a-file): New test.
(elisp-shorthand-load-a-file): Simplify.
2021-09-22 11:33:56 +01:00
João Távora
fe4e4c28ec Integrate shorthand functionality into elisp-mode.el
Also rename the main variable to elisp-shorthands, from the
silly pleonastic shorthand-shorthands.

For some reason, I had to stick the new source-file loading functions
in lisp/international/mule.el, otherwise lisp/loadup.el wouldn't see
them.  This should probably be fixed.

* lisp/shorthand.el: Remove.

* lisp/progmodes/elisp-mode.el (elisp--shorthand-load-wrapper):
Move here.

* src/lread.c (oblookup_considering_shorthand, syms_of_lread):
Rename variable elisp-shorthand, from shorthand-shorthands.

* test/lisp/shorthand-tests.el: Remove.

* test/lisp/progmodes/elisp-mode-tests.el (elisp-shorthand-read-buffer)
(elisp-shorthand-read-from-string)
(elisp-shorthand-load-a-file): New tests.

* test/lisp/progmodes/elisp-resources/simple-shorthand-test.el: New file

* lisp/loadup.el (load-source-file-function): Set to
  load-with-shorthands-and-code-conversion.

* lisp/international/mule.el (hack-elisp-shorthands): Move here.
(load-with-shorthands-and-code-conversion): And here.
2021-09-22 11:33:55 +01:00
João Távora
82528bcb59 Robustify checking of shorthand-shorthands
* src/lread.c (oblookup_considering_shorthand): Maybe warn when we
find fishy shorthand-shorthands.
2021-09-22 11:33:55 +01:00
João Távora
5811e055c0 Move most of the shorthand implementation to C code
This very likely isn't the final form of the implementation.  For one,
the reader is much slower and allocates a Lisp string for every atom
read, regardless if its already interned or not, which perhaps has the
potential to be catastrophic in terms of GC.

But it passes the tests.

The solution to this, is probably to simplify the semantics of
shorthand-shorthands.  Instead of making it a regexp-to-longhand
alist, make it just prefix-to-longhand.  Then we wouldn't need to call
Fstring_match in oblookup_considering_shorthand, meaning we wouldn't
need a Lisp string there.

* lisp/shorthand.el (shorthand-shorthands): Move to C code.
(shorthand--expand-shorthand): Remove.
(shorthand-read-wrapper): Remove.
(shorthand-intern-soft-wrapper): Remove.
(read, intern-soft): No longer advise.

* src/lread.c:
(read1, Fintern, Fintern_soft, Funintern): Use
oblookup_considering_shorthand.
(oblookup_considering_shorthand): New helper.
(syms_of_lread): Declare shorthand-shorthands.
2021-09-22 11:33:55 +01:00
João Távora
d9cab4177b First Elisp version of lisp/shorthand.el, failing some tests
* lisp/shorthand.el: New file

* test/lisp/shorthand-tests.el: New file
2021-09-22 11:33:55 +01:00
8 changed files with 1121 additions and 42 deletions

View file

@ -0,0 +1,752 @@
;;; magnars-string.el --- Namespace-clean s.el, "The long lost Emacs string manipulation library." -*- lexical-binding: t -*-
;; Copyright (C) 2012-2015 Magnar Sveen
;; Author: Magnar Sveen <magnars@gmail.com>
;; Version: 1.12.0
;; Keywords: strings
;; 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 3 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, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; A minimal adaptation of s.el, "The long lost Emacs string
;; manipulation library".
;;
;; This derivative file is identical to the original, except that it
;; defines symbol shorthands so that the symbols it would normally
;; intern without these shorthands are intered with prefixes matching
;; the fila name of the derivative file.
;;
;; See documentation on https://github.com/magnars/s.el#functions
;;; Code:
;; Silence byte-compiler
(defvar ucs-normalize-combining-chars) ; Defined in `ucs-normalize'
(autoload 'slot-value "eieio")
(defun s-trim-left (s)
"Remove whitespace at the beginning of S."
(declare (pure t) (side-effect-free t))
(save-match-data
(if (string-match "\\`[ \t\n\r]+" s)
(replace-match "" t t s)
s)))
(defun s-trim-right (s)
"Remove whitespace at the end of S."
(save-match-data
(declare (pure t) (side-effect-free t))
(if (string-match "[ \t\n\r]+\\'" s)
(replace-match "" t t s)
s)))
(defun s-trim (s)
"Remove whitespace at the beginning and end of S."
(declare (pure t) (side-effect-free t))
(s-trim-left (s-trim-right s)))
(defun s-collapse-whitespace (s)
"Convert all adjacent whitespace characters to a single space."
(declare (pure t) (side-effect-free t))
(replace-regexp-in-string "[ \t\n\r]+" " " s))
(defun s-split (separator s &optional omit-nulls)
"Split S into substrings bounded by matches for regexp SEPARATOR.
If OMIT-NULLS is non-nil, zero-length substrings are omitted.
This is a simple wrapper around the built-in `split-string'."
(declare (side-effect-free t))
(save-match-data
(split-string s separator omit-nulls)))
(defun s-split-up-to (separator s n &optional omit-nulls)
"Split S up to N times into substrings bounded by matches for regexp SEPARATOR.
If OMIT-NULLS is non-nil, zero-length substrings are omitted.
See also `s-split'."
(declare (side-effect-free t))
(save-match-data
(let ((op 0)
(r nil))
(with-temp-buffer
(insert s)
(setq op (goto-char (point-min)))
(while (and (re-search-forward separator nil t)
(< 0 n))
(let ((sub (buffer-substring op (match-beginning 0))))
(unless (and omit-nulls
(equal sub ""))
(push sub r)))
(setq op (goto-char (match-end 0)))
(setq n (1- n)))
(let ((sub (buffer-substring op (point-max))))
(unless (and omit-nulls
(equal sub ""))
(push sub r))))
(nreverse r))))
(defun s-lines (s)
"Splits S into a list of strings on newline characters."
(declare (pure t) (side-effect-free t))
(s-split "\\(\r\n\\|[\n\r]\\)" s))
(defun s-join (separator strings)
"Join all the strings in STRINGS with SEPARATOR in between."
(declare (pure t) (side-effect-free t))
(mapconcat 'identity strings separator))
(defun s-concat (&rest strings)
"Join all the string arguments into one string."
(declare (pure t) (side-effect-free t))
(apply 'concat strings))
(defun s-prepend (prefix s)
"Concatenate PREFIX and S."
(declare (pure t) (side-effect-free t))
(concat prefix s))
(defun s-append (suffix s)
"Concatenate S and SUFFIX."
(declare (pure t) (side-effect-free t))
(concat s suffix))
(defun s-repeat (num s)
"Make a string of S repeated NUM times."
(declare (pure t) (side-effect-free t))
(let (ss)
(while (> num 0)
(setq ss (cons s ss))
(setq num (1- num)))
(apply 'concat ss)))
(defun s-chop-suffix (suffix s)
"Remove SUFFIX if it is at end of S."
(declare (pure t) (side-effect-free t))
(let ((pos (- (length suffix))))
(if (and (>= (length s) (length suffix))
(string= suffix (substring s pos)))
(substring s 0 pos)
s)))
(defun s-chop-suffixes (suffixes s)
"Remove SUFFIXES one by one in order, if they are at the end of S."
(declare (pure t) (side-effect-free t))
(while suffixes
(setq s (s-chop-suffix (car suffixes) s))
(setq suffixes (cdr suffixes)))
s)
(defun s-chop-prefix (prefix s)
"Remove PREFIX if it is at the start of S."
(declare (pure t) (side-effect-free t))
(let ((pos (length prefix)))
(if (and (>= (length s) (length prefix))
(string= prefix (substring s 0 pos)))
(substring s pos)
s)))
(defun s-chop-prefixes (prefixes s)
"Remove PREFIXES one by one in order, if they are at the start of S."
(declare (pure t) (side-effect-free t))
(while prefixes
(setq s (s-chop-prefix (car prefixes) s))
(setq prefixes (cdr prefixes)))
s)
(defun s-shared-start (s1 s2)
"Returns the longest prefix S1 and S2 have in common."
(declare (pure t) (side-effect-free t))
(let ((cmp (compare-strings s1 0 (length s1) s2 0 (length s2))))
(if (eq cmp t) s1 (substring s1 0 (1- (abs cmp))))))
(defun s-shared-end (s1 s2)
"Returns the longest suffix S1 and S2 have in common."
(declare (pure t) (side-effect-free t))
(let* ((l1 (length s1))
(l2 (length s2))
(search-length (min l1 l2))
(i 0))
(while (and (< i search-length)
(= (aref s1 (- l1 i 1)) (aref s2 (- l2 i 1))))
(setq i (1+ i)))
;; If I is 0, then it means that there's no common suffix between
;; S1 and S2.
;;
;; However, since (substring s (- 0)) will return the whole
;; string, `s-shared-end' should simply return the empty string
;; when I is 0.
(if (zerop i)
""
(substring s1 (- i)))))
(defun s-chomp (s)
"Remove one trailing `\\n`, `\\r` or `\\r\\n` from S."
(declare (pure t) (side-effect-free t))
(s-chop-suffixes '("\n" "\r") s))
(defun s-truncate (len s &optional ellipsis)
"If S is longer than LEN, cut it down and add ELLIPSIS to the end.
The resulting string, including ellipsis, will be LEN characters
long.
When not specified, ELLIPSIS defaults to ...."
(declare (pure t) (side-effect-free t))
(unless ellipsis
(setq ellipsis "..."))
(if (> (length s) len)
(format "%s%s" (substring s 0 (- len (length ellipsis))) ellipsis)
s))
(defun s-word-wrap (len s)
"If S is longer than LEN, wrap the words with newlines."
(declare (side-effect-free t))
(save-match-data
(with-temp-buffer
(insert s)
(let ((fill-column len))
(fill-region (point-min) (point-max)))
(buffer-substring (point-min) (point-max)))))
(defun s-center (len s)
"If S is shorter than LEN, pad it with spaces so it is centered."
(declare (pure t) (side-effect-free t))
(let ((extra (max 0 (- len (length s)))))
(concat
(make-string (ceiling extra 2) ? )
s
(make-string (floor extra 2) ? ))))
(defun s-pad-left (len padding s)
"If S is shorter than LEN, pad it with PADDING on the left."
(declare (pure t) (side-effect-free t))
(let ((extra (max 0 (- len (length s)))))
(concat (make-string extra (string-to-char padding))
s)))
(defun s-pad-right (len padding s)
"If S is shorter than LEN, pad it with PADDING on the right."
(declare (pure t) (side-effect-free t))
(let ((extra (max 0 (- len (length s)))))
(concat s
(make-string extra (string-to-char padding)))))
(defun s-left (len s)
"Returns up to the LEN first chars of S."
(declare (pure t) (side-effect-free t))
(if (> (length s) len)
(substring s 0 len)
s))
(defun s-right (len s)
"Returns up to the LEN last chars of S."
(declare (pure t) (side-effect-free t))
(let ((l (length s)))
(if (> l len)
(substring s (- l len) l)
s)))
(defun s-ends-with? (suffix s &optional ignore-case)
"Does S end with SUFFIX?
If IGNORE-CASE is non-nil, the comparison is done without paying
attention to case differences.
Alias: `s-suffix?'"
(declare (pure t) (side-effect-free t))
(let ((start-pos (- (length s) (length suffix))))
(and (>= start-pos 0)
(eq t (compare-strings suffix nil nil
s start-pos nil ignore-case)))))
(defun s-starts-with? (prefix s &optional ignore-case)
"Does S start with PREFIX?
If IGNORE-CASE is non-nil, the comparison is done without paying
attention to case differences.
Alias: `s-prefix?'. This is a simple wrapper around the built-in
`string-prefix-p'."
(declare (pure t) (side-effect-free t))
(string-prefix-p prefix s ignore-case))
(defun s--truthy? (val)
(declare (pure t) (side-effect-free t))
(not (null val)))
(defun s-contains? (needle s &optional ignore-case)
"Does S contain NEEDLE?
If IGNORE-CASE is non-nil, the comparison is done without paying
attention to case differences."
(declare (pure t) (side-effect-free t))
(let ((case-fold-search ignore-case))
(s--truthy? (string-match-p (regexp-quote needle) s))))
(defun s-equals? (s1 s2)
"Is S1 equal to S2?
This is a simple wrapper around the built-in `string-equal'."
(declare (pure t) (side-effect-free t))
(string-equal s1 s2))
(defun s-less? (s1 s2)
"Is S1 less than S2?
This is a simple wrapper around the built-in `string-lessp'."
(declare (pure t) (side-effect-free t))
(string-lessp s1 s2))
(defun s-matches? (regexp s &optional start)
"Does REGEXP match S?
If START is non-nil the search starts at that index.
This is a simple wrapper around the built-in `string-match-p'."
(declare (side-effect-free t))
(s--truthy? (string-match-p regexp s start)))
(defun s-blank? (s)
"Is S nil or the empty string?"
(declare (pure t) (side-effect-free t))
(or (null s) (string= "" s)))
(defun s-blank-str? (s)
"Is S nil or the empty string or string only contains whitespace?"
(declare (pure t) (side-effect-free t))
(or (s-blank? s) (s-blank? (s-trim s))))
(defun s-present? (s)
"Is S anything but nil or the empty string?"
(declare (pure t) (side-effect-free t))
(not (s-blank? s)))
(defun s-presence (s)
"Return S if it's `s-present?', otherwise return nil."
(declare (pure t) (side-effect-free t))
(and (s-present? s) s))
(defun s-lowercase? (s)
"Are all the letters in S in lower case?"
(declare (side-effect-free t))
(let ((case-fold-search nil))
(not (string-match-p "[[:upper:]]" s))))
(defun s-uppercase? (s)
"Are all the letters in S in upper case?"
(declare (side-effect-free t))
(let ((case-fold-search nil))
(not (string-match-p "[[:lower:]]" s))))
(defun s-mixedcase? (s)
"Are there both lower case and upper case letters in S?"
(let ((case-fold-search nil))
(s--truthy?
(and (string-match-p "[[:lower:]]" s)
(string-match-p "[[:upper:]]" s)))))
(defun s-capitalized? (s)
"In S, is the first letter upper case, and all other letters lower case?"
(declare (side-effect-free t))
(let ((case-fold-search nil))
(s--truthy?
(string-match-p "^[[:upper:]][^[:upper:]]*$" s))))
(defun s-numeric? (s)
"Is S a number?"
(declare (pure t) (side-effect-free t))
(s--truthy?
(string-match-p "^[0-9]+$" s)))
(defun s-replace (old new s)
"Replaces OLD with NEW in S."
(declare (pure t) (side-effect-free t))
(replace-regexp-in-string (regexp-quote old) new s t t))
(defalias 's-replace-regexp 'replace-regexp-in-string)
(defun s--aget (alist key)
(declare (pure t) (side-effect-free t))
(cdr (assoc-string key alist)))
(defun s-replace-all (replacements s)
"REPLACEMENTS is a list of cons-cells. Each `car` is replaced with `cdr` in S."
(declare (pure t) (side-effect-free t))
(replace-regexp-in-string (regexp-opt (mapcar 'car replacements))
(lambda (it) (s--aget replacements it))
s t t))
(defun s-downcase (s)
"Convert S to lower case.
This is a simple wrapper around the built-in `downcase'."
(declare (side-effect-free t))
(downcase s))
(defun s-upcase (s)
"Convert S to upper case.
This is a simple wrapper around the built-in `upcase'."
(declare (side-effect-free t))
(upcase s))
(defun s-capitalize (s)
"Convert the first word's first character to upper case and the rest to lower case in S."
(declare (side-effect-free t))
(concat (upcase (substring s 0 1)) (downcase (substring s 1))))
(defun s-titleize (s)
"Convert each word's first character to upper case and the rest to lower case in S.
This is a simple wrapper around the built-in `capitalize'."
(declare (side-effect-free t))
(capitalize s))
(defmacro s-with (s form &rest more)
"Threads S through the forms. Inserts S as the last item
in the first form, making a list of it if it is not a list
already. If there are more forms, inserts the first form as the
last item in second form, etc."
(declare (debug (form &rest [&or (function &rest form) fboundp])))
(if (null more)
(if (listp form)
`(,(car form) ,@(cdr form) ,s)
(list form s))
`(s-with (s-with ,s ,form) ,@more)))
(put 's-with 'lisp-indent-function 1)
(defun s-index-of (needle s &optional ignore-case)
"Returns first index of NEEDLE in S, or nil.
If IGNORE-CASE is non-nil, the comparison is done without paying
attention to case differences."
(declare (pure t) (side-effect-free t))
(let ((case-fold-search ignore-case))
(string-match-p (regexp-quote needle) s)))
(defun s-reverse (s)
"Return the reverse of S."
(declare (pure t) (side-effect-free t))
(save-match-data
(if (multibyte-string-p s)
(let ((input (string-to-list s))
output)
(require 'ucs-normalize)
(while input
;; Handle entire grapheme cluster as a single unit
(let ((grapheme (list (pop input))))
(while (memql (car input) ucs-normalize-combining-chars)
(push (pop input) grapheme))
(setq output (nconc (nreverse grapheme) output))))
(concat output))
(concat (nreverse (string-to-list s))))))
(defun s-match-strings-all (regex string)
"Return a list of matches for REGEX in STRING.
Each element itself is a list of matches, as per
`match-string'. Multiple matches at the same position will be
ignored after the first."
(declare (side-effect-free t))
(save-match-data
(let ((all-strings ())
(i 0))
(while (and (< i (length string))
(string-match regex string i))
(setq i (1+ (match-beginning 0)))
(let (strings
(num-matches (/ (length (match-data)) 2))
(match 0))
(while (/= match num-matches)
(push (match-string match string) strings)
(setq match (1+ match)))
(push (nreverse strings) all-strings)))
(nreverse all-strings))))
(defun s-matched-positions-all (regexp string &optional subexp-depth)
"Return a list of matched positions for REGEXP in STRING.
SUBEXP-DEPTH is 0 by default."
(declare (side-effect-free t))
(if (null subexp-depth)
(setq subexp-depth 0))
(save-match-data
(let ((pos 0) result)
(while (and (string-match regexp string pos)
(< pos (length string)))
(let ((m (match-end subexp-depth)))
(push (cons (match-beginning subexp-depth) (match-end subexp-depth)) result)
(setq pos (match-end 0))))
(nreverse result))))
(defun s-match (regexp s &optional start)
"When the given expression matches the string, this function returns a list
of the whole matching string and a string for each matched subexpressions.
If it did not match the returned value is an empty list (nil).
When START is non-nil the search will start at that index."
(declare (side-effect-free t))
(save-match-data
(if (string-match regexp s start)
(let ((match-data-list (match-data))
result)
(while match-data-list
(let* ((beg (car match-data-list))
(end (cadr match-data-list))
(subs (if (and beg end) (substring s beg end) nil)))
(setq result (cons subs result))
(setq match-data-list
(cddr match-data-list))))
(nreverse result)))))
(defun s-slice-at (regexp s)
"Slices S up at every index matching REGEXP."
(declare (side-effect-free t))
(if (= 0 (length s)) (list "")
(save-match-data
(let (i)
(setq i (string-match regexp s 1))
(if i
(cons (substring s 0 i)
(s-slice-at regexp (substring s i)))
(list s))))))
(defun s-split-words (s)
"Split S into list of words."
(declare (side-effect-free t))
(s-split
"[^[:word:]0-9]+"
(let ((case-fold-search nil))
(replace-regexp-in-string
"\\([[:lower:]]\\)\\([[:upper:]]\\)" "\\1 \\2"
(replace-regexp-in-string "\\([[:upper:]]\\)\\([[:upper:]][0-9[:lower:]]\\)" "\\1 \\2" s)))
t))
(defun s--mapcar-head (fn-head fn-rest list)
"Like MAPCAR, but applies a different function to the first element."
(if list
(cons (funcall fn-head (car list)) (mapcar fn-rest (cdr list)))))
(defun s-lower-camel-case (s)
"Convert S to lowerCamelCase."
(declare (side-effect-free t))
(s-join "" (s--mapcar-head 'downcase 'capitalize (s-split-words s))))
(defun s-upper-camel-case (s)
"Convert S to UpperCamelCase."
(declare (side-effect-free t))
(s-join "" (mapcar 'capitalize (s-split-words s))))
(defun s-snake-case (s)
"Convert S to snake_case."
(declare (side-effect-free t))
(s-join "_" (mapcar 'downcase (s-split-words s))))
(defun s-dashed-words (s)
"Convert S to dashed-words."
(declare (side-effect-free t))
(s-join "-" (mapcar 'downcase (s-split-words s))))
(defun s-capitalized-words (s)
"Convert S to Capitalized words."
(declare (side-effect-free t))
(let ((words (s-split-words s)))
(s-join " " (cons (capitalize (car words)) (mapcar 'downcase (cdr words))))))
(defun s-titleized-words (s)
"Convert S to Titleized Words."
(declare (side-effect-free t))
(s-join " " (mapcar 's-titleize (s-split-words s))))
(defun s-word-initials (s)
"Convert S to its initials."
(declare (side-effect-free t))
(s-join "" (mapcar (lambda (ss) (substring ss 0 1))
(s-split-words s))))
;; Errors for s-format
(progn
(put 's-format-resolve
'error-conditions
'(error s-format s-format-resolve))
(put 's-format-resolve
'error-message
"Cannot resolve a template to values"))
(defun s-format (template replacer &optional extra)
"Format TEMPLATE with the function REPLACER.
REPLACER takes an argument of the format variable and optionally
an extra argument which is the EXTRA value from the call to
`s-format'.
Several standard `s-format' helper functions are recognized and
adapted for this:
(s-format \"${name}\" 'gethash hash-table)
(s-format \"${name}\" 'aget alist)
(s-format \"$0\" 'elt sequence)
The REPLACER function may be used to do any other kind of
transformation."
(let ((saved-match-data (match-data)))
(unwind-protect
(replace-regexp-in-string
"\\$\\({\\([^}]+\\)}\\|[0-9]+\\)"
(lambda (md)
(let ((var
(let ((m (match-string 2 md)))
(if m m
(string-to-number (match-string 1 md)))))
(replacer-match-data (match-data)))
(unwind-protect
(let ((v
(cond
((eq replacer 'gethash)
(funcall replacer var extra))
((eq replacer 'aget)
(funcall 's--aget extra var))
((eq replacer 'elt)
(funcall replacer extra var))
((eq replacer 'oref)
(funcall #'slot-value extra (intern var)))
(t
(set-match-data saved-match-data)
(if extra
(funcall replacer var extra)
(funcall replacer var))))))
(if v (format "%s" v) (signal 's-format-resolve md)))
(set-match-data replacer-match-data))))
template
;; Need literal to make sure it works
t t)
(set-match-data saved-match-data))))
(defvar s-lex-value-as-lisp nil
"If `t' interpolate lisp values as lisp.
`s-lex-format' inserts values with (format \"%S\").")
(defun s-lex-fmt|expand (fmt)
"Expand FMT into lisp."
(declare (side-effect-free t))
(list 's-format fmt (quote 'aget)
(append '(list)
(mapcar
(lambda (matches)
(list
'cons
(cadr matches)
`(format
(if s-lex-value-as-lisp "%S" "%s")
,(intern (cadr matches)))))
(s-match-strings-all "${\\([^}]+\\)}" fmt)))))
(defmacro s-lex-format (format-str)
"`s-format` with the current environment.
FORMAT-STR may use the `s-format' variable reference to refer to
any variable:
(let ((x 1))
(s-lex-format \"x is: ${x}\"))
The values of the variables are interpolated with \"%s\" unless
the variable `s-lex-value-as-lisp' is `t' and then they are
interpolated with \"%S\"."
(declare (debug (form)))
(s-lex-fmt|expand format-str))
(defun s-count-matches (regexp s &optional start end)
"Count occurrences of `regexp' in `s'.
`start', inclusive, and `end', exclusive, delimit the part of `s' to
match. `start' and `end' are both indexed starting at 1; the initial
character in `s' is index 1.
This function starts looking for the next match from the end of the
previous match. Hence, it ignores matches that overlap a previously
found match. To count overlapping matches, use
`s-count-matches-all'."
(declare (side-effect-free t))
(save-match-data
(with-temp-buffer
(insert s)
(goto-char (point-min))
(count-matches regexp (or start 1) (or end (point-max))))))
(defun s-count-matches-all (regexp s &optional start end)
"Count occurrences of `regexp' in `s'.
`start', inclusive, and `end', exclusive, delimit the part of `s' to
match. `start' and `end' are both indexed starting at 1; the initial
character in `s' is index 1.
This function starts looking for the next match from the second
character of the previous match. Hence, it counts matches that
overlap a previously found match. To ignore matches that overlap a
previously found match, use `s-count-matches'."
(declare (side-effect-free t))
(let* ((anchored-regexp (format "^%s" regexp))
(match-count 0)
(i 0)
(narrowed-s (substring s
(when start (1- start))
(when end (1- end)))))
(save-match-data
(while (< i (length narrowed-s))
(when (s-matches? anchored-regexp (substring narrowed-s i))
(setq match-count (1+ match-count)))
(setq i (1+ i))))
match-count))
(defun s-wrap (s prefix &optional suffix)
"Wrap string S with PREFIX and optionally SUFFIX.
Return string S with PREFIX prepended. If SUFFIX is present, it
is appended, otherwise PREFIX is used as both prefix and
suffix."
(declare (pure t) (side-effect-free t))
(concat prefix s (or suffix prefix)))
;;; Aliases
(defalias 's-blank-p 's-blank?)
(defalias 's-blank-str-p 's-blank-str?)
(defalias 's-capitalized-p 's-capitalized?)
(defalias 's-contains-p 's-contains?)
(defalias 's-ends-with-p 's-ends-with?)
(defalias 's-equals-p 's-equals?)
(defalias 's-less-p 's-less?)
(defalias 's-lowercase-p 's-lowercase?)
(defalias 's-matches-p 's-matches?)
(defalias 's-mixedcase-p 's-mixedcase?)
(defalias 's-numeric-p 's-numeric?)
(defalias 's-prefix-p 's-starts-with?)
(defalias 's-prefix? 's-starts-with?)
(defalias 's-present-p 's-present?)
(defalias 's-starts-with-p 's-starts-with?)
(defalias 's-suffix-p 's-ends-with?)
(defalias 's-suffix? 's-ends-with?)
(defalias 's-uppercase-p 's-uppercase?)
(provide 'magnars-string)
;;; magnars-string.el ends here
;; Local Variables:
;; elisp-shorthands: (("s-" . "magnars-string-"))
;; End:

View file

@ -294,6 +294,31 @@ attribute."
(apply 'define-charset-internal name (mapcar 'cdr attrs))))
(defun hack-elisp-shorthands (fullname)
"Return value of the `elisp-shorthands' file-local variable in FULLNAME.
FULLNAME is the full name of an Elisp file which potentially
specifies a file-local value for `elisp-shorthands'. The Elisp
code isn't read or evaluated in any way, we merely extract what
the buffer-local value of `elisp-shorthands' would be if the file
had been found by `find-file'."
(let ((size (nth 7 (file-attributes fullname))))
(with-temp-buffer
(insert-file-contents fullname nil (max 0 (- size 3000)) size)
(goto-char (point-max))
(let* ((found (search-backward-regexp "elisp-shorthands:[ \t]*" 0 t))
(val (and found
(goto-char (match-end 0))
(ignore-errors (read (current-buffer)))))
(probe val)
aux)
(catch 'done
(when (consp probe)
(while (setq aux (pop probe))
(unless (and (consp aux)
(stringp (car aux))
(stringp (cdr aux)))
(throw 'done nil)))
val))))))
(defun load-with-code-conversion (fullname file &optional noerror nomessage)
"Execute a file of Lisp code named FILE whose absolute name is FULLNAME.
@ -354,6 +379,11 @@ Return t if file exists."
(message "Loading %s...done" file)))
t)))
(defun load-with-shorthands-and-code-conversion (fullname file noerror nomessage)
"As `load-with-code-conversion', also considering Elisp shorthands."
(let ((elisp-shorthands (hack-elisp-shorthands fullname)))
(load-with-code-conversion fullname file noerror nomessage)))
(defun charset-info (charset)
"Return a vector of information of CHARSET.
This function is provided for backward compatibility.

View file

@ -151,7 +151,7 @@
;; variable its advertised default value (it starts as nil, see
;; xdisp.c).
(setq resize-mini-windows 'grow-only)
(setq load-source-file-function #'load-with-code-conversion)
(setq load-source-file-function #'load-with-shorthands-and-code-conversion)
(load "files")
;; Load-time macro-expansion can only take effect after setting

View file

@ -535,6 +535,45 @@ It can be quoted, or be inside a quoted form."
0))
((facep sym) (find-definition-noselect sym 'defface)))))
(defun elisp--completion-local-symbols ()
"Compute list all Elisp symbols for completion purposes."
(let* ((calculate
(lambda ()
(let (retval)
(mapatoms (lambda (s)
(push s retval)
(cl-loop for (shorthand . longhand) in elisp-shorthands
for full-name = (symbol-name s)
when (string-prefix-p longhand full-name)
do (let ((sym (make-symbol
(concat shorthand
(substring full-name
(length longhand))))))
(put sym 'shorthand t)
(push sym retval)
retval))))
retval)))
(probe
(and obarray-cache
(gethash (cons (current-buffer) elisp-shorthands)
obarray-cache))))
(cond (probe)
(obarray-cache
(puthash (cons (current-buffer) elisp-shorthands)
(funcall calculate)
obarray-cache))
(t
(setq obarray-cache (make-hash-table :test #'equal))
(puthash (cons (current-buffer) elisp-shorthands)
(funcall calculate)
obarray-cache)))))
(defun elisp--shorthand-aware-fboundp (sym)
(fboundp (intern-soft (symbol-name sym))))
(defun elisp--shorthand-aware-boundp (sym)
(boundp (intern-soft (symbol-name sym))))
(defun elisp-completion-at-point ()
"Function used for `completion-at-point-functions' in `emacs-lisp-mode'.
If the context at point allows only a certain category of
@ -582,24 +621,27 @@ functions are annotated with \"<f>\" via the
;; the current form and use it to provide a more
;; specific completion table in more cases.
((eq fun-sym 'ignore-error)
(list t obarray
(list t (elisp--completion-local-symbols)
:predicate (lambda (sym)
(get sym 'error-conditions))))
((elisp--expect-function-p beg)
(list nil obarray
:predicate #'fboundp
(list nil (elisp--completion-local-symbols)
:predicate
#'elisp--shorthand-aware-fboundp
:company-kind #'elisp--company-kind
:company-doc-buffer #'elisp--company-doc-buffer
:company-docsig #'elisp--company-doc-string
:company-location #'elisp--company-location))
(quoted
(list nil obarray
(list nil (elisp--completion-local-symbols)
;; Don't include all symbols (bug#16646).
:predicate (lambda (sym)
(or (boundp sym)
(fboundp sym)
(featurep sym)
(symbol-plist sym)))
;; shorthand-aware
(let ((sym (intern-soft (symbol-name sym))))
(or (boundp sym)
(fboundp sym)
(featurep sym)
(symbol-plist sym))))
:annotation-function
(lambda (str) (if (fboundp (intern-soft str)) " <f>"))
:company-kind #'elisp--company-kind
@ -610,8 +652,8 @@ functions are annotated with \"<f>\" via the
(list nil (completion-table-merge
elisp--local-variables-completion-table
(apply-partially #'completion-table-with-predicate
obarray
#'boundp
(elisp--completion-local-symbols)
#'elisp--shorthand-aware-boundp
'strict))
:company-kind
(lambda (s)
@ -648,11 +690,11 @@ functions are annotated with \"<f>\" via the
(ignore-errors
(forward-sexp 2)
(< (point) beg)))))
(list t obarray
(list t (elisp--completion-local-symbols)
:predicate (lambda (sym) (get sym 'error-conditions))))
;; `ignore-error' with a list CONDITION parameter.
('ignore-error
(list t obarray
(list t (elisp--completion-local-symbols)
:predicate (lambda (sym)
(get sym 'error-conditions))))
((and (or ?\( 'let 'let*)
@ -662,14 +704,14 @@ functions are annotated with \"<f>\" via the
(up-list -1))
(forward-symbol -1)
(looking-at "\\_<let\\*?\\_>"))))
(list t obarray
:predicate #'boundp
(list t (elisp--completion-local-symbols)
:predicate #'elisp--shorthand-aware-boundp
:company-kind (lambda (_) 'variable)
:company-doc-buffer #'elisp--company-doc-buffer
:company-docsig #'elisp--company-doc-string
:company-location #'elisp--company-location))
(_ (list nil obarray
:predicate #'fboundp
(_ (list nil (elisp--completion-local-symbols)
:predicate #'elisp--shorthand-aware-fboundp
:company-kind #'elisp--company-kind
:company-doc-buffer #'elisp--company-doc-buffer
:company-docsig #'elisp--company-doc-string
@ -686,6 +728,9 @@ functions are annotated with \"<f>\" via the
" " (cadr table-etc)))
(cddr table-etc)))))))))
(defun elisp--fboundp-considering-shorthands (sym)
(fboundp (intern-soft (symbol-name sym))))
(defun elisp--company-kind (str)
(let ((sym (intern-soft str)))
(cond
@ -2078,5 +2123,8 @@ Runs in a batch-mode Emacs. Interactively use variable
(terpri)
(pp collected)))
(put 'elisp-shorthands 'safe-local-variable #'consp)
(provide 'elisp-mode)
;;; elisp-mode.el ends here

View file

@ -2956,6 +2956,10 @@ read_integer (Lisp_Object readcharfun, int radix,
return unbind_to (count, string_to_number (read_buffer, radix, NULL));
}
Lisp_Object oblookup_considering_shorthand
(Lisp_Object obarray,
const char *in, ptrdiff_t size, ptrdiff_t size_byte,
char **out, ptrdiff_t *size_out, ptrdiff_t *size_byte_out);
/* If the next token is ')' or ']' or '.', we store that character
in *PCH and the return value is not interesting. Else, we store
@ -2968,6 +2972,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
{
int c;
bool uninterned_symbol = false;
bool skip_shorthand = false;
bool multibyte;
char stackbuf[stackbufsize];
current_thread->stack_top = stackbuf;
@ -3363,6 +3368,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
if (c == ':')
{
uninterned_symbol = true;
read_hash_prefixed_symbol:
c = READCHAR;
if (!(c > 040
&& c != NO_BREAK_SPACE
@ -3376,6 +3382,12 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
}
goto read_symbol;
}
/* #/foo is the shorthand-oblivious symbol named foo. */
if (c == '\\')
{
skip_shorthand = true;
goto read_hash_prefixed_symbol;
}
/* ## is the empty symbol. */
if (c == '#')
return Fintern (empty_unibyte_string, Qnil);
@ -3756,7 +3768,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
ptrdiff_t nbytes = p - read_buffer;
UNREAD (c);
if (!quoted && !uninterned_symbol)
if (!quoted && !uninterned_symbol && !skip_shorthand)
{
ptrdiff_t len;
Lisp_Object result = string_to_number (read_buffer, 10, &len);
@ -3786,18 +3798,35 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
Like intern_1 but supports multibyte names. */
Lisp_Object obarray = check_obarray (Vobarray);
Lisp_Object tem = oblookup (obarray, read_buffer,
nchars, nbytes);
char* longhand = NULL;
ptrdiff_t longhand_chars = 0;
ptrdiff_t longhand_bytes = 0;
Lisp_Object tem;
if (skip_shorthand)
tem = oblookup (obarray, read_buffer, nchars, nbytes);
else {
tem = oblookup_considering_shorthand
(obarray, read_buffer, nchars, nbytes,
&longhand, &longhand_chars, &longhand_bytes);
}
if (SYMBOLP (tem))
result = tem;
else
{
Lisp_Object name
= make_specified_string (read_buffer, nchars, nbytes,
multibyte);
result = intern_driver (name, obarray, tem);
}
else if (longhand) {
Lisp_Object name
= make_specified_string (longhand, longhand_chars,
longhand_bytes,
multibyte);
xfree (longhand);
result = intern_driver (name, obarray, tem);
} else {
Lisp_Object name
= make_specified_string (read_buffer, nchars, nbytes,
multibyte);
result = intern_driver (name, obarray, tem);
}
}
if (EQ (Vread_with_symbol_positions, Qt)
@ -4339,6 +4368,7 @@ intern_sym (Lisp_Object sym, Lisp_Object obarray, Lisp_Object index)
Lisp_Object
intern_driver (Lisp_Object string, Lisp_Object obarray, Lisp_Object index)
{
SET_SYMBOL_VAL (XSYMBOL (Qobarray_cache), Qnil);
return intern_sym (Fmake_symbol (string), obarray, index);
}
@ -4407,10 +4437,29 @@ it defaults to the value of `obarray'. */)
obarray = check_obarray (NILP (obarray) ? Vobarray : obarray);
CHECK_STRING (string);
tem = oblookup (obarray, SSDATA (string), SCHARS (string), SBYTES (string));
char* longhand = NULL;
ptrdiff_t longhand_chars = 0;
ptrdiff_t longhand_bytes = 0;
tem = oblookup_considering_shorthand
(obarray, SSDATA (string), SCHARS (string), SBYTES (string),
&longhand, &longhand_chars, &longhand_bytes);
if (!SYMBOLP (tem))
tem = intern_driver (NILP (Vpurify_flag) ? string : Fpurecopy (string),
obarray, tem);
{
if (longhand)
{
tem = intern_driver (make_specified_string (longhand, longhand_chars,
longhand_bytes, true),
obarray, tem);
xfree (longhand);
}
else
{
tem = intern_driver (NILP (Vpurify_flag) ? string : Fpurecopy (string),
obarray, tem);
}
}
return tem;
}
@ -4422,7 +4471,8 @@ A second optional argument specifies the obarray to use;
it defaults to the value of `obarray'. */)
(Lisp_Object name, Lisp_Object obarray)
{
register Lisp_Object tem, string;
register Lisp_Object tem;
Lisp_Object string;
if (NILP (obarray)) obarray = Vobarray;
obarray = check_obarray (obarray);
@ -4431,15 +4481,24 @@ it defaults to the value of `obarray'. */)
{
CHECK_STRING (name);
string = name;
char* longhand = NULL;
ptrdiff_t longhand_chars = 0;
ptrdiff_t longhand_bytes = 0;
tem = oblookup_considering_shorthand
(obarray, SSDATA (string), SCHARS (string), SBYTES (string),
&longhand, &longhand_chars, &longhand_bytes);
if (longhand) xfree (longhand);
if (FIXNUMP (tem)) return Qnil; else return tem;
}
else
string = SYMBOL_NAME (name);
tem = oblookup (obarray, SSDATA (string), SCHARS (string), SBYTES (string));
if (FIXNUMP (tem) || (SYMBOLP (name) && !EQ (name, tem)))
return Qnil;
else
return tem;
{
// If already a symbol, we do no shorthand-longhand translation,
// as promised in docstring.
string = SYMBOL_NAME (name);
tem
= oblookup (obarray, SSDATA (string), SCHARS (string), SBYTES (string));
if (EQ (name, tem)) return tem; else return Qnil;
}
}
DEFUN ("unintern", Funintern, Sunintern, 1, 2, 0,
@ -4451,7 +4510,8 @@ OBARRAY, if nil, defaults to the value of the variable `obarray'.
usage: (unintern NAME OBARRAY) */)
(Lisp_Object name, Lisp_Object obarray)
{
register Lisp_Object string, tem;
register Lisp_Object tem;
Lisp_Object string;
size_t hash;
if (NILP (obarray)) obarray = Vobarray;
@ -4465,9 +4525,14 @@ usage: (unintern NAME OBARRAY) */)
string = name;
}
tem = oblookup (obarray, SSDATA (string),
SCHARS (string),
SBYTES (string));
char* longhand = NULL;
ptrdiff_t longhand_chars = 0;
ptrdiff_t longhand_bytes = 0;
tem = oblookup_considering_shorthand
(obarray, SSDATA (string), SCHARS (string), SBYTES (string),
&longhand, &longhand_chars, &longhand_bytes);
if (longhand) free(longhand);
if (FIXNUMP (tem))
return Qnil;
/* If arg was a symbol, don't delete anything but that symbol itself. */
@ -4554,6 +4619,67 @@ oblookup (Lisp_Object obarray, register const char *ptr, ptrdiff_t size, ptrdiff
XSETINT (tem, hash);
return tem;
}
/* Like oblookup, but considers Velisp_shorthands, potentially
transforming the symbol name coded in IN into a longhand version
that is placed in OUT. It no such substitution occurs, OUT is set
to point to NULL. Else, memory is malloc'ed for OUT (which the
caller must free) while SIZE_OUT and SIZE_BYTE_OUT respectively
hold the character and byte sizes of the transformed symbol
name. */
Lisp_Object
oblookup_considering_shorthand
(Lisp_Object obarray,
const char *in, ptrdiff_t size, ptrdiff_t size_byte,
char **out, ptrdiff_t *size_out, ptrdiff_t *size_byte_out)
{
// First, assume no transformation will take place.
*out = NULL;
Lisp_Object tail = Velisp_shorthands;
// Then, iterate each pair in Velisp_shorthands.
FOR_EACH_TAIL_SAFE (tail)
{
Lisp_Object pair = XCAR (tail);
// Be lenient to Velisp_shorthands: if some element isn't a cons
// or some member of that cons isn't a string, just skip to the
// next element.
if (!CONSP (pair)) continue;
Lisp_Object sh_prefix = XCAR (pair);
Lisp_Object lh_prefix = XCDR (pair);
if (!STRINGP (sh_prefix) || !STRINGP (lh_prefix)) continue;
ptrdiff_t sh_prefix_size = SBYTES (sh_prefix);
// Compare the prefix of the transformation pair to the symbol
// name. If a match occurs, do the renaming and exit the loop.
// In other words, only one such transformation may take place.
// Calculate the amount of memory to allocate for the longhand
// version of the symbol name with realloc(). This isn't
// strictly needed, but it could later be used as a way for
// multiple transformations on a single symbol name.
if (sh_prefix_size <= size_byte &&
memcmp(SSDATA(sh_prefix), in, sh_prefix_size) == 0)
{
ptrdiff_t lh_prefix_size = SBYTES (lh_prefix);
ptrdiff_t suffix_size = size_byte - sh_prefix_size;
*out = xrealloc (*out, lh_prefix_size + suffix_size);
memcpy (*out, SSDATA(lh_prefix), lh_prefix_size);
memcpy (*out + lh_prefix_size, in + sh_prefix_size, suffix_size);
*size_out = SCHARS (lh_prefix) - SCHARS (sh_prefix) + size;
*size_byte_out = lh_prefix_size + suffix_size;
break;
}
}
// Now, as promised, call oblookup() with the "final" symbol name to
// lookup. That function remains oblivious to whether a
// transformation happened here or not, but the caller of this
// function can tell by inspecting the OUT parameter.
if (*out)
return oblookup (obarray, *out, *size_out, *size_byte_out);
else
return oblookup (obarray, in, size, size_byte);
}
void
map_obarray (Lisp_Object obarray, void (*fn) (Lisp_Object, Lisp_Object), Lisp_Object arg)
@ -5310,4 +5436,10 @@ that are loaded before your customizations are read! */);
DEFSYM (Qrehash_threshold, "rehash-threshold");
DEFSYM (Qchar_from_name, "char-from-name");
DEFVAR_LISP ("elisp-shorthands", Velisp_shorthands,
doc: /* Alist of known symbol name shorthands*/);
Velisp_shorthands = Qnil;
DEFSYM (Qelisp_shorthands, "elisp-shorthands");
DEFSYM (Qobarray_cache, "obarray-cache");
}

View file

@ -1021,5 +1021,64 @@ evaluation of BODY."
(should (equal (elisp--xref-infer-namespace p3) 'any))
(should (equal (elisp--xref-infer-namespace p4) 'any))))
(ert-deftest elisp-shorthand-read-buffer ()
(let* ((gsym (downcase (symbol-name (cl-gensym "sh-"))))
(shorthand-sname (format "s-%s" gsym))
(expected (intern (format "shorthand-longhand-%s" gsym))))
(cl-assert (not (intern-soft shorthand-sname)))
(should (equal (let ((elisp-shorthands
'(("s-" . "shorthand-longhand-"))))
(with-temp-buffer
(insert shorthand-sname)
(goto-char (point-min))
(read (current-buffer))))
expected))
(should (not (intern-soft shorthand-sname)))))
(ert-deftest elisp-shorthand-read-from-string ()
(let* ((gsym (downcase (symbol-name (cl-gensym "sh-"))))
(shorthand-sname (format "s-%s" gsym))
(expected (intern (format "shorthand-longhand-%s" gsym))))
(cl-assert (not (intern-soft shorthand-sname)))
(should (equal (let ((elisp-shorthands
'(("s-" . "shorthand-longhand-"))))
(car (read-from-string shorthand-sname)))
expected))
(should (not (intern-soft shorthand-sname)))))
(defvar elisp--test-resources-dir
(expand-file-name "elisp-resources/"
(file-name-directory
(or load-file-name
(error "this file needs to be loaded")))))
(ert-deftest elisp-shorthand-load-a-file ()
(let ((test-file (expand-file-name "simple-shorthand-test.el"
elisp--test-resources-dir)))
(mapatoms (lambda (s)
(when (string-match "^elisp--foo-" (symbol-name s))
(unintern s obarray))))
(load test-file)
(should (intern-soft "elisp--foo-test"))
(should-not (intern-soft "f-test"))))
(ert-deftest elisp-shorthand-byte-compile-a-file ()
(let ((test-file (expand-file-name "simple-shorthand-test.el"
elisp--test-resources-dir))
(byte-compiled (expand-file-name "simple-shorthand-test.elc"
elisp--test-resources-dir)))
(mapatoms (lambda (s)
(when (string-match "^elisp--foo-" (symbol-name s))
(unintern s obarray))))
(byte-compile-file test-file)
(should-not (intern-soft "f-test"))
(should (intern-soft "elisp--foo-test"))
(should-not (fboundp (intern-soft "elisp--foo-test")))
(load byte-compiled)
(should (intern-soft "elisp--foo-test"))
(should-not (intern-soft "f-test"))))
(provide 'elisp-mode-tests)
;;; elisp-mode-tests.el ends here

View file

@ -0,0 +1,33 @@
;;; magnars-string-user.el --- playground file that uses magnars-string.el and shorthands -*- lexical-binding: t; -*-
;; require this library
(require 'magnars-string)
;; can't live without these
(show-paren-mode 1)
(electric-pair-mode 1)
;; will be useful later
(flymake-mode 1)
(add-hook 'emacs-lisp-mode-hook 'flymake-mode)
;; just for geeks, watch the echo area when I eval this
(benchmark-run 1 (elisp--completion-local-symbols))
(intern (symbol-name (gensym)))
(benchmark-run 1 (elisp--completion-local-symbols))
(defun silly ()
)
;; Things to demo:
;; * C-M-i completion
;; * Eldoc
;; * M-. and then M-,
;; * C-h f
;; * Changing the shorthand, reload with M-x revert-buffer
;; * Flymake
;; Local Variables:
;; elisp-shorthands: (("s-" . "magnars-string-"))
;; End:

View file

@ -0,0 +1,25 @@
(defun f-test ()
(let ((elisp-shorthands '(("foo-" . "bar-"))))
(with-temp-buffer
(insert "(foo-bar)")
(goto-char (point-min))
(read (current-buffer)))))
(defun f-test2 ()
(let ((elisp-shorthands '(("foo-" . "bar-"))))
(read-from-string "(foo-bar)")))
(defun f-test3 ()
(let ((elisp-shorthands '(("foo-" . "bar-"))))
(intern "foo-bar")))
(when nil
(f-test3)
(f-test2)
(f-test))
;; Local Variables:
;; elisp-shorthands: (("f-" . "elisp--foo-"))
;; End: