From 269cec13a2fc6ac18b675d0dadd07a3d4e074a72 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Fri, 25 Dec 2020 05:16:46 +0100 Subject: Remove `string-slice' -- it's not very well defined * doc/lispref/strings.texi (Creating Strings): Ditto. * lisp/emacs-lisp/subr-x.el (string-slice): Remove. --- test/lisp/emacs-lisp/subr-x-tests.el | 8 -------- 1 file changed, 8 deletions(-) (limited to 'test/lisp/emacs-lisp') diff --git a/test/lisp/emacs-lisp/subr-x-tests.el b/test/lisp/emacs-lisp/subr-x-tests.el index 3fc5f1d3ed3..2ae492ecf15 100644 --- a/test/lisp/emacs-lisp/subr-x-tests.el +++ b/test/lisp/emacs-lisp/subr-x-tests.el @@ -604,14 +604,6 @@ (should (equal (string-lines "foo") '("foo"))) (should (equal (string-lines "foo \nbar") '("foo " "bar")))) -(ert-deftest subr-string-slice () - (should (equal (string-slice "foo-bar" "-") '("foo" "-bar"))) - (should (equal (string-slice "foo-bar-" "-") '("foo" "-bar" "-"))) - (should (equal (string-slice "-foo-bar-" "-") '("-foo" "-bar" "-"))) - (should (equal (string-slice "ooo" "lala") '("ooo"))) - (should (equal (string-slice "foo bar" "\\b") '("foo" " " "bar" ""))) - (should (equal (string-slice "foo bar" "\\b\\|a") '("foo" " " "b" "ar" "")))) - (ert-deftest subr-string-pad () (should (equal (string-pad "foo" 5) "foo ")) (should (equal (string-pad "foo" 5 ?-) "foo--")) -- cgit v1.2.3 From af359de91772478587f768300ca61d64a693fedb Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Fri, 25 Dec 2020 05:58:09 +0100 Subject: Allow `string-limit' to work on encoded strings * doc/lispref/strings.texi (Creating Strings): Document it. * lisp/emacs-lisp/subr-x.el (string-limit): Allow limiting on encoded strings. --- doc/lispref/strings.texi | 9 ++++++++- lisp/emacs-lisp/shortdoc.el | 3 ++- lisp/emacs-lisp/subr-x.el | 34 +++++++++++++++++++++++++++++----- test/lisp/emacs-lisp/subr-x-tests.el | 20 ++++++++++++++++++++ 4 files changed, 59 insertions(+), 7 deletions(-) (limited to 'test/lisp/emacs-lisp') diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index 19b91471ed3..1e5f52ddfdd 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -394,12 +394,19 @@ there are individual words that are longer than @var{length}, these will not be shortened. @end defun -@defun string-limit string length &optional end +@defun string-limit string length &optional end coding-system If @var{string} is shorter than @var{length}, @var{string} is returned as is. Otherwise, return a substring of @var{string} consisting of the first @var{length} characters. If the optional @var{end} parameter is given, return a string of the @var{length} last characters instead. + +If @var{coding-system} is non-@code{nil}, @var{string} will be encoded +before limiting, and the result will be a unibyte string that's +shorter than @code{length}. If @var{string} contains characters that +are encoded into several bytes (for instance, when using +@code{utf-8}), the resulting unibyte string is never truncated in the +middle of a character representation. @end defun @defun string-lines string &optional omit-nulls diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el index 618465513da..9d183e0d4e9 100644 --- a/lisp/emacs-lisp/shortdoc.el +++ b/lisp/emacs-lisp/shortdoc.el @@ -146,7 +146,8 @@ There can be any number of :example/:result elements." (string-limit :eval (string-limit "foobar" 3) :eval (string-limit "foobar" 3 t) - :eval (string-limit "foobar" 10)) + :eval (string-limit "foobar" 10) + :eval (string-limit "fo好" 3 nil 'utf-8)) (truncate-string-to-width :eval (truncate-string-to-width "foobar" 3) :eval (truncate-string-to-width "你好bar" 5)) diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index dc5840a0865..9fbb0351af4 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -286,7 +286,7 @@ result will have lines that are longer than LENGTH." (fill-region (point-min) (point-max))) (buffer-string))) -(defun string-limit (string length &optional end) +(defun string-limit (string length &optional end coding-system) "Return (up to) a LENGTH substring of STRING. If STRING is shorter than or equal to LENGTH, the entire string is returned unchanged. @@ -295,15 +295,39 @@ If STRING is longer than LENGTH, return a substring consisting of the first LENGTH characters of STRING. If END is non-nil, return the last LENGTH characters instead. +If CODING-SYSTEM is non-nil, STRING will be encoded before +limiting, and LENGTH is interpreted as the number of bytes to +limit the string to. The result will be a unibyte string that is +shorter than LENGTH, but will not contain \"partial\" characters, +even if CODING-SYSTEM encodes characters with several bytes per +character. + When shortening strings for display purposes, `truncate-string-to-width' is almost always a better alternative than this function." (unless (natnump length) (signal 'wrong-type-argument (list 'natnump length))) - (cond - ((<= (length string) length) string) - (end (substring string (- (length string) length))) - (t (substring string 0 length)))) + (if coding-system + (let ((result nil) + (result-length 0) + (index (if end (1- (length string)) 0))) + (while (let ((encoded (encode-coding-char + (aref string index) coding-system))) + (and (<= (+ (length encoded) result-length) length) + (progn + (push encoded result) + (cl-incf result-length (length encoded)) + (setq index (if end (1- index) + (1+ index)))) + (if end (> index -1) + (< index (length string))))) + ;; No body. + ) + (apply #'concat (if end result (nreverse result)))) + (cond + ((<= (length string) length) string) + (end (substring string (- (length string) length))) + (t (substring string 0 length))))) (defun string-lines (string &optional omit-nulls) "Split STRING into a list of lines. diff --git a/test/lisp/emacs-lisp/subr-x-tests.el b/test/lisp/emacs-lisp/subr-x-tests.el index 2ae492ecf15..b17185ab0d3 100644 --- a/test/lisp/emacs-lisp/subr-x-tests.el +++ b/test/lisp/emacs-lisp/subr-x-tests.el @@ -600,6 +600,26 @@ (should (equal (string-limit "foo" 0) "")) (should-error (string-limit "foo" -1))) +(ert-deftest subr-string-limit-coding () + (should (not (multibyte-string-p (string-limit "foó" 10 nil 'utf-8)))) + (should (equal (string-limit "foó" 10 nil 'utf-8) "fo\303\263")) + (should (equal (string-limit "foó" 3 nil 'utf-8) "fo")) + (should (equal (string-limit "foó" 4 nil 'utf-8) "fo\303\263")) + (should (equal (string-limit "foóa" 4 nil 'utf-8) "fo\303\263")) + (should (equal (string-limit "foóá" 4 nil 'utf-8) "fo\303\263")) + (should (equal (string-limit "foóa" 4 nil 'iso-8859-1) "fo\363a")) + (should (equal (string-limit "foóá" 4 nil 'iso-8859-1) "fo\363\341")) + (should (equal (string-limit "foóá" 4 nil 'utf-16) "\376\377\000f")) + + (should (equal (string-limit "foó" 10 t 'utf-8) "fo\303\263")) + (should (equal (string-limit "foó" 3 t 'utf-8) "o\303\263")) + (should (equal (string-limit "foó" 4 t 'utf-8) "fo\303\263")) + (should (equal (string-limit "foóa" 4 t 'utf-8) "o\303\263a")) + (should (equal (string-limit "foóá" 4 t 'utf-8) "\303\263\303\241")) + (should (equal (string-limit "foóa" 4 t 'iso-8859-1) "fo\363a")) + (should (equal (string-limit "foóá" 4 t 'iso-8859-1) "fo\363\341")) + (should (equal (string-limit "foóá" 4 t 'utf-16) "\376\377\000\341"))) + (ert-deftest subr-string-lines () (should (equal (string-lines "foo") '("foo"))) (should (equal (string-lines "foo \nbar") '("foo " "bar")))) -- cgit v1.2.3 From 80420faf4921ffe5e9d4c4f9595941acf3156e50 Mon Sep 17 00:00:00 2001 From: Daniel Martín Date: Sun, 27 Dec 2020 09:04:56 +0100 Subject: Improve "find definition" in *Help* buffers * lisp/emacs-lisp/find-func.el (find-function-search-for-symbol): If our regexp algorithm could not find a location for the symbol definition, resort to find-function--search-by-expanding-macros. * test/lisp/emacs-lisp/find-func-tests.el: Add a automatic test for a function and variable generated by a macro. * etc/NEWS: Advertise the improved functionality (bug#45443). --- etc/NEWS | 6 +++ lisp/emacs-lisp/find-func.el | 65 ++++++++++++++++++++++++++++++++- test/lisp/emacs-lisp/find-func-tests.el | 10 +++++ 3 files changed, 80 insertions(+), 1 deletion(-) (limited to 'test/lisp/emacs-lisp') diff --git a/etc/NEWS b/etc/NEWS index 9ae8cc91d63..4f072df31c5 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -114,6 +114,12 @@ choosing a group, or clicking a button in the "*Help*" buffers when looking at the doc string of a function that belongs to one of these groups. +--- +** Improved "find definition" feature of *Help* buffers. +Now clicking on the link to find the definition of functions generated +by 'cl-defstruct', or variables generated by 'define-derived-mode', +for example, will go to the exact place where they are defined. + ** New variable 'redisplay-skip-initial-frame' to enable batch redisplay tests. Setting it to nil forces the redisplay to do its job even in the initial frame used in batch mode. diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el index 074e7db295b..7796a72ecfd 100644 --- a/lisp/emacs-lisp/find-func.el +++ b/lisp/emacs-lisp/find-func.el @@ -389,7 +389,70 @@ The search is done in the source for library LIBRARY." (progn (beginning-of-line) (cons (current-buffer) (point))) - (cons (current-buffer) nil))))))))) + ;; If the regexp search didn't find the location of + ;; the symbol (for example, because it is generated by + ;; a macro), try a slightly more expensive search that + ;; expands macros until it finds the symbol. + (cons (current-buffer) + (find-function--search-by-expanding-macros + (current-buffer) symbol type)))))))))) + +(defun find-function--try-macroexpand (form) + "Try to macroexpand FORM in full or partially. +This is a best-effort operation in which if macroexpansion fails, +this function returns FORM as is." + (ignore-errors + (or + (macroexpand-all form) + (macroexpand-1 form) + form))) + +(defun find-function--any-subform-p (form pred) + "Walk FORM and apply PRED to its subexpressions. +Return t if any PRED returns t." + (cond + ((not (consp form)) nil) + ((funcall pred form) t) + (t + (cl-destructuring-bind (left-child . right-child) form + (or + (find-function--any-subform-p left-child pred) + (find-function--any-subform-p right-child pred)))))) + +(defun find-function--search-by-expanding-macros (buf symbol type) + "Expand macros in BUF to search for the definition of SYMBOL of TYPE." + (catch 'found + (with-current-buffer buf + (save-excursion + (goto-char (point-min)) + (condition-case nil + (while t + (let ((form (read (current-buffer))) + (expected-symbol-p + (lambda (form) + (cond + ((null type) + ;; Check if a given form is a `defalias' to + ;; SYM, the function name we are searching + ;; for. All functions in Emacs Lisp + ;; ultimately expand to a `defalias' form + ;; after several steps of macroexpansion. + (and (eq (car-safe form) 'defalias) + (equal (car-safe (cdr form)) + `(quote ,symbol)))) + ((eq type 'defvar) + ;; Variables generated by macros ultimately + ;; expand to `defvar'. + (and (eq (car-safe form) 'defvar) + (eq (car-safe (cdr form)) symbol))) + (t nil))))) + (when (find-function--any-subform-p + (find-function--try-macroexpand form) + expected-symbol-p) + ;; We want to return the location at the beginning + ;; of the macro, so move back one sexp. + (throw 'found (progn (backward-sexp) (point)))))) + (end-of-file nil)))))) (defun find-function-library (function &optional lisp-only verbose) "Return the pair (ORIG-FUNCTION . LIBRARY) for FUNCTION. diff --git a/test/lisp/emacs-lisp/find-func-tests.el b/test/lisp/emacs-lisp/find-func-tests.el index d77eb6757ff..03df4bb9ff4 100644 --- a/test/lisp/emacs-lisp/find-func-tests.el +++ b/test/lisp/emacs-lisp/find-func-tests.el @@ -43,5 +43,15 @@ (concat data-directory (kbd "n x / TAB RET")) (read-library-name))))) +;; Avoid a byte-compilation warning that may confuse people reading +;; the result of the following test. +(declare-function compilation--message->loc nil "compile") + +(ert-deftest find-func-tests--locate-macro-generated-symbols () ;bug#45443 + (should (cdr (find-function-search-for-symbol + #'compilation--message->loc nil "compile"))) + (should (cdr (find-function-search-for-symbol + 'c-mode-hook 'defvar "cc-mode")))) + (provide 'find-func-tests) ;;; find-func-tests.el ends here -- cgit v1.2.3