From 89cc852af3c7a17684b0d3083eca1ef2731f1f41 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 30 May 2016 16:33:07 -0400 Subject: * lisp/emacs-lisp/pcase.el (pcase-mutually-exclusive-predicates): Add `atom'. --- lisp/emacs-lisp/pcase.el | 51 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) (limited to 'lisp/emacs-lisp') diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el index 7e164c0fe5c..b18472d7e3d 100644 --- a/lisp/emacs-lisp/pcase.el +++ b/lisp/emacs-lisp/pcase.el @@ -105,6 +105,8 @@ specs))))) (edebug-match cursor (cons '&or specs)))) +(fset 'pcase--canon #'identity) + ;;;###autoload (defmacro pcase (exp &rest cases) "Evaluate EXP and attempt to match it against structural patterns. @@ -332,7 +334,8 @@ any kind of error." ;; to a separate function if that number is too high. ;; ;; We've already used this branch. So it is shared. - (let* ((code (car prev)) (cdrprev (cdr prev)) + (let* (;; (code (car prev)) + (cdrprev (cdr prev)) (prevvars (car cdrprev)) (cddrprev (cdr cdrprev)) (res (car cddrprev))) (unless (symbolp res) @@ -434,8 +437,10 @@ to this macro." ;; Don't use let*, otherwise macroexp-let* may merge it with some surrounding ;; let* which might prevent the setcar/setcdr in pcase--expand's fancy ;; codegen from later metamorphosing this let into a funcall. - `(let ,(mapcar (lambda (b) (list (car b) (cdr b))) vars) - ,@code)) + (if vars + `(let ,(mapcar (lambda (b) (list (car b) (cdr b))) vars) + ,@code) + `(progn ,@code))) (defun pcase--small-branch-p (code) (and (= 1 (length code)) @@ -451,7 +456,36 @@ to this macro." (cond ((eq else :pcase--dontcare) then) ((eq then :pcase--dontcare) (debug) else) ;Can/should this ever happen? - (t (macroexp-if test then else)))) + ;; FIXME: The code below shows that there are some opportunities for sharing, + ;; but it's rarely useful to do it here, since almost all sharing found + ;; shares a trivial expression. + ;; But among the common trivial expressions are those of the form + ;; (funcall pcase-0). For this case, there could be a significant payoff + ;; if we could find the sharing-opportunity earlier so as to avoid + ;; the creation of pcase-0. + ;; ((and (eq 'if (car-safe then)) + ;; (equal (macroexp-unprogn (macroexp-progn (nthcdr 3 then))) + ;; (macroexp-unprogn else))) + ;; (let ((res (macroexp-if `(and ,test ,(nth 1 then)) + ;; (nth 2 then) else))) + ;; (message "if+if => if-and: sharing %S" else) + ;; res)) + ;; ((and (eq 'if (car-safe else)) + ;; (equal (nth 2 else) then)) + ;; (let ((res (macroexp-if `(or ,test ,(nth 1 else)) + ;; then (macroexp-progn (nthcdr 3 else))))) + ;; (message "if+if => if-or: sharing %S" then) + ;; res)) + (t + ;; (cond + ;; ((and (eq 'cond (car-safe then)) + ;; (equal `(cond ,@(nthcdr 2 then)) else)) + ;; (message "if+cond => cond-and: sharing %S" else)) + ;; ((and (eq 'cond (car-safe else)) + ;; (equal (macroexp-unprogn (macroexp-progn (cdr (nth 1 else)))) + ;; (macroexp-unprogn then))) + ;; (message "if+cond => cond-or: sharing %S" then))) + (macroexp-if test then else)))) ;; Note about MATCH: ;; When we have patterns like `(PAT1 . PAT2), after performing the `consp' @@ -509,6 +543,7 @@ MATCH is the pattern that needs to be matched, of the form: (numberp . stringp) (numberp . byte-code-function-p) (consp . arrayp) + (consp . atom) (consp . vectorp) (consp . stringp) (consp . byte-code-function-p) @@ -918,6 +953,14 @@ QPAT can take the following forms: ((or (stringp qpat) (integerp qpat) (symbolp qpat)) `',qpat) (t (error "Unknown QPAT: %S" qpat)))) +;;; Extra definitions that use pcase. + +(defun pcase--canon (e) + (pcase e + (`(progn ,e) (pcase--canon e)) + (`(cond (,test . ,then) (t . ,else)) + `(if ,test ,(macroexp-progn then) ,(macroexp-progn else))))) + (provide 'pcase) ;;; pcase.el ends here -- cgit v1.2.3 From 13411853b25f3c861d9364961f8ca0b18a9b5ed4 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 30 May 2016 16:35:00 -0400 Subject: * lisp/emacs-lisp/pcase.el: Undo last change's spurious changes --- lisp/emacs-lisp/pcase.el | 50 ++++-------------------------------------------- 1 file changed, 4 insertions(+), 46 deletions(-) (limited to 'lisp/emacs-lisp') diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el index b18472d7e3d..0b8dddfacc9 100644 --- a/lisp/emacs-lisp/pcase.el +++ b/lisp/emacs-lisp/pcase.el @@ -105,8 +105,6 @@ specs))))) (edebug-match cursor (cons '&or specs)))) -(fset 'pcase--canon #'identity) - ;;;###autoload (defmacro pcase (exp &rest cases) "Evaluate EXP and attempt to match it against structural patterns. @@ -334,8 +332,7 @@ any kind of error." ;; to a separate function if that number is too high. ;; ;; We've already used this branch. So it is shared. - (let* (;; (code (car prev)) - (cdrprev (cdr prev)) + (let* ((code (car prev)) (cdrprev (cdr prev)) (prevvars (car cdrprev)) (cddrprev (cdr cdrprev)) (res (car cddrprev))) (unless (symbolp res) @@ -437,10 +434,8 @@ to this macro." ;; Don't use let*, otherwise macroexp-let* may merge it with some surrounding ;; let* which might prevent the setcar/setcdr in pcase--expand's fancy ;; codegen from later metamorphosing this let into a funcall. - (if vars - `(let ,(mapcar (lambda (b) (list (car b) (cdr b))) vars) - ,@code) - `(progn ,@code))) + `(let ,(mapcar (lambda (b) (list (car b) (cdr b))) vars) + ,@code)) (defun pcase--small-branch-p (code) (and (= 1 (length code)) @@ -456,36 +451,7 @@ to this macro." (cond ((eq else :pcase--dontcare) then) ((eq then :pcase--dontcare) (debug) else) ;Can/should this ever happen? - ;; FIXME: The code below shows that there are some opportunities for sharing, - ;; but it's rarely useful to do it here, since almost all sharing found - ;; shares a trivial expression. - ;; But among the common trivial expressions are those of the form - ;; (funcall pcase-0). For this case, there could be a significant payoff - ;; if we could find the sharing-opportunity earlier so as to avoid - ;; the creation of pcase-0. - ;; ((and (eq 'if (car-safe then)) - ;; (equal (macroexp-unprogn (macroexp-progn (nthcdr 3 then))) - ;; (macroexp-unprogn else))) - ;; (let ((res (macroexp-if `(and ,test ,(nth 1 then)) - ;; (nth 2 then) else))) - ;; (message "if+if => if-and: sharing %S" else) - ;; res)) - ;; ((and (eq 'if (car-safe else)) - ;; (equal (nth 2 else) then)) - ;; (let ((res (macroexp-if `(or ,test ,(nth 1 else)) - ;; then (macroexp-progn (nthcdr 3 else))))) - ;; (message "if+if => if-or: sharing %S" then) - ;; res)) - (t - ;; (cond - ;; ((and (eq 'cond (car-safe then)) - ;; (equal `(cond ,@(nthcdr 2 then)) else)) - ;; (message "if+cond => cond-and: sharing %S" else)) - ;; ((and (eq 'cond (car-safe else)) - ;; (equal (macroexp-unprogn (macroexp-progn (cdr (nth 1 else)))) - ;; (macroexp-unprogn then))) - ;; (message "if+cond => cond-or: sharing %S" then))) - (macroexp-if test then else)))) + (t (macroexp-if test then else)))) ;; Note about MATCH: ;; When we have patterns like `(PAT1 . PAT2), after performing the `consp' @@ -953,14 +919,6 @@ QPAT can take the following forms: ((or (stringp qpat) (integerp qpat) (symbolp qpat)) `',qpat) (t (error "Unknown QPAT: %S" qpat)))) -;;; Extra definitions that use pcase. - -(defun pcase--canon (e) - (pcase e - (`(progn ,e) (pcase--canon e)) - (`(cond (,test . ,then) (t . ,else)) - `(if ,test ,(macroexp-progn then) ,(macroexp-progn else))))) - (provide 'pcase) ;;; pcase.el ends here -- cgit v1.2.3 From 0c26f14b7e200b39134ec70c77fab8c467cf3290 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 30 May 2016 23:22:49 -0400 Subject: * lisp/emacs-lisp/autoload.el: Use radix-tree. (autoload--make-defs-autoload): Rewrite. (autoload--split-prefixes-1): Remove. (autoload-def-prefixes-max-entries): Rename from autoload-defs-autoload-max-size. (autoload-popular-prefixes): Remove. (autoload-def-prefixes-max-length): New const. * lisp/emacs-lisp/radix-tree.el: New file. --- etc/NEWS | 2 + lisp/emacs-lisp/autoload.el | 86 +++++++------------ lisp/emacs-lisp/radix-tree.el | 188 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+), 54 deletions(-) create mode 100644 lisp/emacs-lisp/radix-tree.el (limited to 'lisp/emacs-lisp') diff --git a/etc/NEWS b/etc/NEWS index 185b1a4f644..80b8036bbd1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -360,6 +360,8 @@ See the 'vc-faces' customization group. * New Modes and Packages in Emacs 25.2 +** New Elisp data-structure library `radix-tree'. + * Incompatible Lisp Changes in Emacs 25.2 diff --git a/lisp/emacs-lisp/autoload.el b/lisp/emacs-lisp/autoload.el index 11316f1d9d6..afd8e4ee03e 100644 --- a/lisp/emacs-lisp/autoload.el +++ b/lisp/emacs-lisp/autoload.el @@ -500,41 +500,26 @@ Return non-nil in the case where no autoloads were added at point." (let ((generated-autoload-file buffer-file-name)) (autoload-generate-file-autoloads file (current-buffer)))) -(defun autoload--split-prefixes-1 (strs) - (let ((prefixes ())) - (dolist (str strs) - (string-match "\\`[^-:/_]*[-:/_]*" str) - (let* ((prefix (match-string 0 str)) - (tail (substring str (match-end 0))) - (cell (assoc prefix prefixes))) - (cond - ((null cell) (push (list prefix tail) prefixes)) - ((equal (cadr cell) tail) nil) - (t (setcdr cell (cons tail (cdr cell))))))) - prefixes)) - (defvar autoload-compute-prefixes t "If non-nil, autoload will add code to register the prefixes used in a file. Standard prefixes won't be registered anyway. I.e. if a file \"foo.el\" defines variables or functions that use \"foo-\" as prefix, that will not be registered. But all other prefixes will be included.") -(defconst autoload-defs-autoload-max-size 5 +(defconst autoload-def-prefixes-max-entries 5 "Target length of the list of definition prefixes per file. If set too small, the prefixes will be too generic (i.e. they'll use little memory, we'll end up looking in too many files when we need a particular prefix), and if set too large, they will be too specific (i.e. they will cost more memory use).") -(defvar autoload-popular-prefixes nil) +(defconst autoload-def-prefixes-max-length 12 + "Target size of definition prefixes. +Don't try to split prefixes that are already longer than that.") + +(require 'radix-tree) (defun autoload--make-defs-autoload (defs file) - ;; FIXME: avoid redundant entries. E.g. opascal currently has - ;; "opascal-" "opascal--literal-start-re" "opascal--syntax-propertize" - ;; where only the first one should be kept. - ;; FIXME: Avoid keeping too-long-prefixes. E.g. ob-scheme currently has - ;; "org-babel-scheme-" "org-babel-default-header-args:scheme" - ;; "org-babel-expand-body:scheme" "org-babel-execute:scheme". ;; Remove the defs that obey the rule that file foo.el (or ;; foo-mode.el) uses "foo-" as prefix. @@ -548,39 +533,32 @@ cost more memory use).") ;; Then compute a small set of prefixes that cover all the ;; remaining definitions. - (let ((prefixes (autoload--split-prefixes-1 defs)) - (again t)) - ;; (message "Initial prefixes %s : %S" file (mapcar #'car prefixes)) - (while again - (setq again nil) - (let ((newprefixes - (sort - (mapcar (lambda (cell) - (cons cell - (autoload--split-prefixes-1 (cdr cell)))) - prefixes) - (lambda (x y) (< (length (cdr x)) (length (cdr y))))))) - (setq prefixes nil) - (while newprefixes - (let ((x (pop newprefixes))) - (if (or (equal '("") (cdar x)) - (and (cddr x) - (not (member (caar x) - autoload-popular-prefixes)) - (> (+ (length prefixes) (length newprefixes) - (length (cdr x))) - autoload-defs-autoload-max-size))) - ;; Nothing to split or would split too deep. - (push (car x) prefixes) - ;; (message "Expand %S to %S" (caar x) (cdr x)) - (setq again t) - (setq prefixes - (nconc (mapcar (lambda (cell) - (cons (concat (caar x) - (car cell)) - (cdr cell))) - (cdr x)) - prefixes))))))) + (let* ((tree (let ((tree radix-tree-empty)) + (dolist (def defs) + (setq tree (radix-tree-insert tree def t))) + tree)) + (prefixes (list (cons "" tree)))) + (while + (let ((newprefixes nil) + (changes nil)) + (dolist (pair prefixes) + (let ((prefix (car pair))) + (if (or (> (length prefix) autoload-def-prefixes-max-length) + (radix-tree-lookup (cdr pair) "")) + ;; No point splitting it any further. + (push pair newprefixes) + (setq changes t) + (radix-tree-iter-subtrees + (cdr pair) (lambda (sprefix subtree) + (push (cons (concat prefix sprefix) subtree) + newprefixes)))))) + (and changes + (or (and (null (cdr prefixes)) (equal "" (caar prefixes))) + (<= (length newprefixes) + autoload-def-prefixes-max-entries)) + (setq prefixes newprefixes) + (< (length prefixes) autoload-def-prefixes-max-entries)))) + ;; (message "Final prefixes %s : %S" file (mapcar #'car prefixes)) (when prefixes `(if (fboundp 'register-definition-prefixes) diff --git a/lisp/emacs-lisp/radix-tree.el b/lisp/emacs-lisp/radix-tree.el new file mode 100644 index 00000000000..a6984b8034c --- /dev/null +++ b/lisp/emacs-lisp/radix-tree.el @@ -0,0 +1,188 @@ +;;; radix-tree.el --- A simple library of radix trees -*- lexical-binding: t; -*- + +;; Copyright (C) 2016 Free Software Foundation, Inc. + +;; Author: Stefan Monnier +;; Keywords: + +;; This file is part of GNU Emacs. + +;; GNU Emacs 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 Emacs 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 GNU Emacs. If not, see . + +;;; Commentary: + +;; There are many different options for how to represent radix trees +;; in Elisp. Here I chose a very simple one. A radix-tree can be either: +;; - a node, of the form ((PREFIX . PTREE) . RTREE) where PREFIX is a string +;; meaning that everything that starts with PREFIX is in PTREE, +;; and everything else in RTREE. It also has the property that +;; everything that starts with the first letter of PREFIX but not with +;; that whole PREFIX is not in RTREE (i.e. is not in the tree at all). +;; - anything else is taken as the value to associate with the empty string. +;; So every node is basically an (improper) alist where each mapping applies +;; to a different leading letter. +;; +;; The main downside of this representation is that the lookup operation +;; is slower because each level of the tree is an alist rather than some kind +;; of array, so every level's lookup is O(N) rather than O(1). We could easily +;; solve this by using char-tables instead of alists, but that would make every +;; level take up a lot more memory, and it would make the resulting +;; datastructure harder to read (by a human) when printed out. + +;;; Code: + +(defun radix-tree--insert (tree key val i) + (pcase tree + (`((,prefix . ,ptree) . ,rtree) + (let* ((ni (+ i (length prefix))) + (cmp (compare-strings prefix nil nil key i ni))) + (if (eq t cmp) + (let ((nptree (radix-tree--insert ptree key val ni))) + `((,prefix . ,nptree) . ,rtree)) + (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1)))) + (if (zerop n) + (let ((nrtree (radix-tree--insert rtree key val i))) + `((,prefix . ,ptree) . ,nrtree)) + (let* ((nprefix (substring prefix 0 n)) + (kprefix (substring key (+ i n))) + (pprefix (substring prefix n)) + (ktree (if (equal kprefix "") val + `((,kprefix . ,val))))) + `((,nprefix + . ((,pprefix . ,ptree) . ,ktree)) + . ,rtree))))))) + (_ + (if (= (length key) i) val + (let ((prefix (substring key i))) + `((,prefix . ,val) . ,tree)))))) + +(defun radix-tree--remove (tree key i) + (pcase tree + (`((,prefix . ,ptree) . ,rtree) + (let* ((ni (+ i (length prefix))) + (cmp (compare-strings prefix nil nil key i ni))) + (if (eq t cmp) + (pcase (radix-tree--remove ptree key ni) + (`nil rtree) + (`((,pprefix . ,pptree)) + `((,(concat prefix pprefix) . ,pptree) . ,rtree)) + (nptree `((,prefix . ,nptree) . ,rtree))) + (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1)))) + (if (zerop n) + (let ((nrtree (radix-tree--remove rtree key i))) + `((,prefix . ,ptree) . ,nrtree)) + tree))))) + (_ + (if (= (length key) i) nil tree)))) + + +(defun radix-tree--lookup (tree string i) + (pcase tree + (`((,prefix . ,ptree) . ,rtree) + (let* ((ni (+ i (length prefix))) + (cmp (compare-strings prefix nil nil string i ni))) + (if (eq t cmp) + (radix-tree--lookup ptree string ni) + (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1)))) + (if (zerop n) + (radix-tree--lookup rtree string i) + (+ i n)))))) + (val + (if (and val (equal (length string) i)) + (if (integerp val) `(t . ,val) val) + i)))) + +(defun radix-tree--subtree (tree string i) + (if (equal (length string) i) tree + (pcase tree + (`((,prefix . ,ptree) . ,rtree) + (let* ((ni (+ i (length prefix))) + (cmp (compare-strings prefix nil nil string i ni))) + (if (eq t cmp) + (radix-tree--subtree ptree string ni) + (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1)))) + (cond + ((zerop n) (radix-tree--subtree rtree string i)) + ((equal (+ n i) (length string)) + (let ((nprefix (substring prefix n))) + `((,nprefix . ,ptree)))) + (t nil)))))) + (_ nil)))) + +;;; Entry points + +(defconst radix-tree-empty nil + "The empty radix-tree.") + +(defun radix-tree-insert (tree key val) + "Insert a mapping from KEY to VAL in radix TREE." + (when (consp val) (setq val `(t . ,val))) + (if val (radix-tree--insert tree key val 0) + (radix-tree--remove tree key 0))) + +(defun radix-tree-lookup (tree key) + "Return the value associated to KEY in radix TREE. +If not found, return nil." + (pcase (radix-tree--lookup tree key 0) + (`(t . ,val) val) + ((pred numberp) nil) + (val val))) + +(defun radix-tree-subtree (tree string) + "Return the subtree of TREE rooted at the prefix STRING." + (radix-tree--subtree tree string 0)) + +(eval-and-compile + (pcase-defmacro radix-tree-leaf (vpat) + ;; FIXME: We'd like to use a negative pattern (not consp), but pcase + ;; doesn't support it. Using `atom' works but generates sub-optimal code. + `(or `(t . ,,vpat) (and (pred atom) ,vpat)))) + +(defun radix-tree-iter-subtrees (tree fun) + "Apply FUN to every immediate subtree of radix TREE. +FUN is called with two arguments: PREFIX and SUBTREE. +You can test if SUBTREE is a leaf (and extract its value) with the +pcase pattern (radix-tree-leaf PAT)." + (while tree + (pcase tree + (`((,prefix . ,ptree) . ,rtree) + (funcall fun prefix ptree) + (setq tree rtree)) + (_ (funcall fun "" tree) + (setq tree nil))))) + +(defun radix-tree-iter-mappings (tree fun &optional prefix) + "Apply FUN to every mapping in TREE. +FUN is called with two arguments: KEY and VAL. +PREFIX is only used internally." + (radix-tree-iter-subtrees + tree + (lambda (p s) + (let ((nprefix (concat prefix p))) + (pcase s + ((radix-tree-leaf v) (funcall fun nprefix v)) + (_ (radix-tree-iter-mappings s fun nprefix))))))) + +;; (defun radix-tree->alist (tree) +;; (let ((al nil)) +;; (radix-tree-iter-mappings tree (lambda (p v) (push (cons p v) al))) +;; al)) + +(defun radix-tree-count (tree) + (let ((i 0)) + (radix-tree-iter-mappings tree (lambda (_ _) (setq i (1+ i)))) + i)) + +(provide 'radix-tree) +;;; radix-tree.el ends here -- cgit v1.2.3 From 01030eed9395f5004e7d0721394697d1ca90cc2f Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Mon, 30 May 2016 23:19:54 -0700 Subject: ; Spelling fixes --- lisp/emacs-lisp/autoload.el | 2 +- lisp/emacs-lisp/radix-tree.el | 2 +- lisp/net/tramp-gvfs.el | 2 +- lisp/net/tramp-sh.el | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'lisp/emacs-lisp') diff --git a/lisp/emacs-lisp/autoload.el b/lisp/emacs-lisp/autoload.el index afd8e4ee03e..424b8e31936 100644 --- a/lisp/emacs-lisp/autoload.el +++ b/lisp/emacs-lisp/autoload.el @@ -967,7 +967,7 @@ write its autoloads into the specified file instead." t files-re)) dirs))) (done ()) ;Files processed; to remove duplicates. - (changed nil) ;Non-nil if some change occured. + (changed nil) ;Non-nil if some change occurred. (last-time) ;; Files with no autoload cookies or whose autoloads go to other ;; files because of file-local autoload-generated-file settings. diff --git a/lisp/emacs-lisp/radix-tree.el b/lisp/emacs-lisp/radix-tree.el index a6984b8034c..d4b5cd211e4 100644 --- a/lisp/emacs-lisp/radix-tree.el +++ b/lisp/emacs-lisp/radix-tree.el @@ -38,7 +38,7 @@ ;; of array, so every level's lookup is O(N) rather than O(1). We could easily ;; solve this by using char-tables instead of alists, but that would make every ;; level take up a lot more memory, and it would make the resulting -;; datastructure harder to read (by a human) when printed out. +;; data structure harder to read (by a human) when printed out. ;;; Code: diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el index 96773928068..ac390e5d5a6 100644 --- a/lisp/net/tramp-gvfs.el +++ b/lisp/net/tramp-gvfs.el @@ -410,7 +410,7 @@ Every entry is a list (NAME ADDRESS).") (defconst tramp-gvfs-file-attributes '("type" "standard::display-name" - ;; We don't need this one. It is used as delimeter in case the + ;; We don't need this one. It is used as delimiter in case the ;; display name contains spaces, which is hard to parse. "standard::icon" "standard::symlink-target" diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index bfa3cc62ae2..e9f78b7d1ce 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -4717,7 +4717,7 @@ connection if a previous connection has died for some reason." (options (tramp-ssh-controlmaster-options vec)) (process-connection-type tramp-process-connection-type) (process-adaptive-read-buffering nil) - ;; There are unfortune settings for "cmdproxy" on + ;; There are unfortunate settings for "cmdproxy" on ;; W32 systems. (process-coding-system-alist nil) (coding-system-for-read nil) -- cgit v1.2.3