aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuri Linkov2009-08-04 23:52:06 +0000
committerJuri Linkov2009-08-04 23:52:06 +0000
commitb4d84ecfec33d4a2df59cb7d56052659c5cfe44f (patch)
treed512ef7f3787843dc6cd4cc680600c95f024d599
parent82941b5e55d656bb56efa4d60cd62644d34f52e7 (diff)
downloademacs-b4d84ecfec33d4a2df59cb7d56052659c5cfe44f.tar.gz
emacs-b4d84ecfec33d4a2df59cb7d56052659c5cfe44f.zip
Commands to add/delete file/directory-local variables.
(read-file-local-variable, read-file-local-variable-value) (read-file-local-variable-mode, modify-file-local-variable) (modify-file-local-variable-prop-line) (modify-dir-local-variable): New functions. (add-file-local-variable, delete-file-local-variable) (add-file-local-variable-prop-line, delete-file-local-variable-prop-line) (add-dir-local-variable, delete-dir-local-variable) (copy-file-locals-to-dir-locals, copy-dir-locals-to-file-locals) (copy-dir-locals-to-file-locals-prop-line): New commands.
-rw-r--r--lisp/ChangeLog13
-rw-r--r--lisp/files.el413
2 files changed, 426 insertions, 0 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 42b40e1442f..dea01f1eea3 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,16 @@
12009-08-04 Juri Linkov <juri@jurta.org>
2
3 * files.el: Commands to add/delete file/directory-local variables.
4 (read-file-local-variable, read-file-local-variable-value)
5 (read-file-local-variable-mode, modify-file-local-variable)
6 (modify-file-local-variable-prop-line)
7 (modify-dir-local-variable): New functions.
8 (add-file-local-variable, delete-file-local-variable)
9 (add-file-local-variable-prop-line, delete-file-local-variable-prop-line)
10 (add-dir-local-variable, delete-dir-local-variable)
11 (copy-file-locals-to-dir-locals, copy-dir-locals-to-file-locals)
12 (copy-dir-locals-to-file-locals-prop-line): New commands.
13
12009-08-04 Chong Yidong <cyd@stupidchicken.com> 142009-08-04 Chong Yidong <cyd@stupidchicken.com>
2 15
3 * abbrev.el (insert-abbrev-table-description): Prettify output. 16 * abbrev.el (insert-abbrev-table-description): Prettify output.
diff --git a/lisp/files.el b/lisp/files.el
index 5f256a016ca..ae28ee2610f 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -3407,6 +3407,419 @@ and `file-local-variables-alist', without applying them."
3407 (hack-local-variables-filter variables dir-name))))))) 3407 (hack-local-variables-filter variables dir-name)))))))
3408 3408
3409 3409
3410;;; Commands to add/delete file-local/directory-local variables.
3411
3412(defun read-file-local-variable (prompt)
3413 "Read file-local variable using PROMPT and completion.
3414Intended to be used in the `interactive' spec of
3415`add-file-local-variable', `delete-file-local-variable',
3416`add-dir-local-variable', `delete-dir-local-variable'."
3417 (let (default variable)
3418 (setq default (variable-at-point))
3419 (setq default (and (symbolp default) (boundp default)
3420 (symbol-name default)))
3421 (setq variable
3422 (completing-read
3423 (if default
3424 (format "%s (default %s): " prompt default)
3425 (format "%s: " prompt))
3426 obarray
3427 (lambda (sym)
3428 (or (user-variable-p sym)
3429 (memq sym '(mode eval coding unibyte))))
3430 nil nil nil default nil))
3431 (and (stringp variable) (intern variable))))
3432
3433(defun read-file-local-variable-value (variable)
3434 "Read value of file-local VARIABLE using completion.
3435Intended to be used in the `interactive' spec of
3436`add-file-local-variable' and `add-dir-local-variable'."
3437 (let (default value)
3438 (cond
3439 ((eq variable 'mode)
3440 (setq default (and (symbolp major-mode) (symbol-name major-mode)))
3441 (setq value
3442 (completing-read
3443 (if default
3444 (format "Add %s with value (default %s): " variable default)
3445 (format "Add %s with value: " variable))
3446 obarray
3447 (lambda (sym)
3448 (and (string-match-p "-mode\\'" (symbol-name sym))
3449 (not (string-match-p "-minor-mode\\'" (symbol-name sym)))))
3450 nil nil nil default nil))
3451 (and (stringp value)
3452 (intern (replace-regexp-in-string "-mode\\'" "" value))))
3453 ((eq variable 'eval)
3454 (let ((minibuffer-completing-symbol t))
3455 (read-from-minibuffer (format "Add %s with expression: " variable)
3456 nil read-expression-map t
3457 'read-expression-history)))
3458 ((eq variable 'coding)
3459 (setq default (and (symbolp buffer-file-coding-system)
3460 (symbol-name buffer-file-coding-system)))
3461 (read-coding-system
3462 (if default
3463 (format "Add %s with value (default %s): " variable default)
3464 (format "Add %s with value: " variable))
3465 default))
3466 (t
3467 (read (read-string (format "Add %s with value: " variable)
3468 nil 'set-variable-value-history
3469 (format "%S" (symbol-value variable))))))))
3470
3471(defun read-file-local-variable-mode ()
3472 "Read per-directory file-local variable's mode using completion.
3473Intended to be used in the `interactive' spec of
3474`add-dir-local-variable', `delete-dir-local-variable'."
3475 (let* ((default (and (symbolp major-mode) (symbol-name major-mode)))
3476 (mode
3477 (completing-read
3478 (if default
3479 (format "Mode or subdirectory (default %s): " default)
3480 (format "Mode or subdirectory: "))
3481 obarray
3482 (lambda (sym)
3483 (and (string-match-p "-mode\\'" (symbol-name sym))
3484 (not (string-match-p "-minor-mode\\'" (symbol-name sym)))))
3485 nil nil nil default nil)))
3486 (cond
3487 ((equal mode "nil") nil)
3488 ((and (stringp mode) (fboundp (intern mode))) (intern mode))
3489 (t mode))))
3490
3491(defun modify-file-local-variable (variable value op)
3492 "Modify file-local VARIABLE in Local Variables depending on operation OP.
3493
3494If OP is `add-or-replace' then delete all existing settings of
3495VARIABLE (except `mode' and `eval') and add a new file-local VARIABLE
3496with VALUE to the Local Variables list.
3497
3498If there is no Local Variables list in the current file buffer and OP
3499is not `delete' then this function adds the first line containing the
3500string `Local Variables:' and the last line containing the string `End:'.
3501
3502If OP is `delete' then delete all existing settings of VARIABLE
3503from the Local Variables list ignoring the input argument VALUE."
3504 (catch 'exit
3505 (let ((beg (point)) end replaced-pos)
3506 (unless enable-local-variables
3507 (throw 'exit (message "File-local variables are disabled")))
3508
3509 ;; Look for "Local variables:" line in last page.
3510 (widen)
3511 (goto-char (point-max))
3512 (search-backward "\n\^L" (max (- (point-max) 3000) (point-min)) 'move)
3513
3514 ;; Add "Local variables:" list if not found.
3515 (unless (let ((case-fold-search t))
3516 (search-forward "Local Variables:" nil t))
3517
3518 ;; Don't add "Local variables:" list for the deletion operation.
3519 (when (eq op 'delete)
3520 (throw 'exit (progn (goto-char beg)
3521 (message "Local Variables not found"))))
3522
3523 (goto-char (point-max))
3524 (let ((comment-style 'plain)
3525 (comment-start (or comment-start ";;; ")))
3526 (comment-region
3527 (prog1 (setq beg (point))
3528 (insert "\nLocal Variables:\nEnd:\n"))
3529 (point)))
3530
3531 (unless (let ((case-fold-search t))
3532 (goto-char beg)
3533 (search-forward "Local Variables:" nil t))
3534 (throw 'exit (message "Can't add file-local variables"))))
3535
3536 ;; prefix is what comes before "local variables:" in its line.
3537 ;; suffix is what comes after "local variables:" in its line.
3538 (let* ((prefix (buffer-substring (line-beginning-position)
3539 (match-beginning 0)))
3540 (suffix (buffer-substring (point) (line-end-position)))
3541 (prefix-re (concat "^" (regexp-quote prefix)))
3542 (suffix-re (concat (regexp-quote suffix) "$")))
3543
3544 ;; Find or add missing "End:".
3545 (forward-line 1)
3546 (setq beg (point))
3547 (save-excursion
3548 (unless (let ((case-fold-search t))
3549 (re-search-forward
3550 (concat prefix-re "[ \t]*End:[ \t]*" suffix-re)
3551 nil t))
3552 (save-excursion
3553 (insert (format "%sEnd:%s\n" prefix suffix))))
3554 (beginning-of-line)
3555 (setq end (point-marker)))
3556
3557 ;; Find and delete all existing variable/value pairs.
3558 (when (member op '(add-or-replace delete))
3559 (if (and (eq op 'add-or-replace) (memq variable '(mode eval)))
3560 (goto-char end)
3561 (goto-char beg)
3562 (while (re-search-forward
3563 (format "%s%S:.*%s" prefix-re variable suffix-re) end t)
3564 (delete-region (match-beginning 0) (1+ (match-end 0)))
3565 (setq replaced-pos (point)))))
3566
3567 ;; Add a new variable/value pair. Add `mode' to the start, add new
3568 ;; variable to the end, and add a replaced variable to its last location.
3569 (when (eq op 'add-or-replace)
3570 (cond
3571 ((eq variable 'mode) (goto-char beg))
3572 ((null replaced-pos) (goto-char end))
3573 (replaced-pos (goto-char replaced-pos)))
3574 (insert (format "%s%S: %S%s\n" prefix variable value suffix)))))))
3575
3576(defun add-file-local-variable (variable value)
3577 "Add file-local VARIABLE with its VALUE to the Local Variables list.
3578
3579This command deletes all existing settings of VARIABLE (except `mode'
3580and `eval') and adds a new file-local VARIABLE with VALUE to the
3581Local Variables list.
3582
3583If there is no Local Variables list in the current file buffer
3584then this function adds the first line containing the string
3585`Local Variables:' and the last line containing the string `End:'."
3586 (interactive
3587 (let ((variable (read-file-local-variable "Add file-local variable")))
3588 (list variable (read-file-local-variable-value variable))))
3589 (modify-file-local-variable variable value 'add-or-replace))
3590
3591(defun delete-file-local-variable (variable)
3592 "Delete all settings of file-local VARIABLE from the Local Variables list."
3593 (interactive
3594 (list (read-file-local-variable "Delete file-local variable")))
3595 (modify-file-local-variable variable nil 'delete))
3596
3597(defun modify-file-local-variable-prop-line (variable value op)
3598 "Modify file-local VARIABLE in the -*- line depending on operation OP.
3599
3600If OP is `add-or-replace' then delete all existing settings of
3601VARIABLE (except `mode' and `eval') and add a new file-local VARIABLE
3602with VALUE to the -*- line.
3603
3604If there is no -*- line at the beginning of the current file buffer
3605and OP is not `delete' then this function adds the -*- line.
3606
3607If OP is `delete' then delete all existing settings of VARIABLE
3608from the -*- line ignoring the input argument VALUE."
3609 (catch 'exit
3610 (let ((beg (point)) end replaced-pos)
3611 (unless enable-local-variables
3612 (throw 'exit (message "File-local variables are disabled")))
3613
3614 ;; Find the -*- line at the beginning of the current buffer.
3615 (widen)
3616 (goto-char (point-min))
3617 (setq end (set-auto-mode-1))
3618
3619 (if end
3620 (setq beg (point) end (copy-marker end))
3621
3622 ;; Add the -*- line if not found.
3623 ;; Don't add the -*- line for the deletion operation.
3624 (when (eq op 'delete)
3625 (throw 'exit (progn (goto-char beg)
3626 (message "The -*- line not found"))))
3627
3628 (goto-char (point-min))
3629
3630 ;; Skip interpreter magic line "#!"
3631 (when (looking-at "^\\(#!\\|'\\\\\"\\)")
3632 (forward-line 1))
3633
3634 (let ((comment-style 'plain)
3635 (comment-start (or comment-start ";;; ")))
3636 (comment-region
3637 (prog1 (point)
3638 (insert "-*-")
3639 (setq beg (point-marker))
3640 (setq end (point-marker))
3641 (insert "-*-\n"))
3642 (point))))
3643
3644 (cond
3645 ((looking-at "[ \t]*\\([^ \t\n\r:;]+\\)\\([ \t]*-\\*-\\)")
3646 ;; Simple form: "-*- MODENAME -*-".
3647 (if (eq variable 'mode)
3648 ;; Replace or delete MODENAME
3649 (progn
3650 (when (member op '(add-or-replace delete))
3651 (delete-region (match-beginning 1) (match-end 1)))
3652 (when (eq op 'add-or-replace)
3653 (goto-char (match-beginning 1))
3654 (insert (format "%S" value))))
3655 ;; Else, turn `MODENAME' into `mode:MODENAME'
3656 ;; and add `;VARIABLE:VALUE'
3657 (goto-char (match-beginning 2))
3658 (insert (format "; %S: %S " variable value))
3659 (goto-char (match-beginning 1))
3660 (insert " mode: ")))
3661
3662 (t
3663 ;; Hairy form: '-*-' [ <variable> ':' <value> ';' ]* '-*-'
3664 ;; Find and delete all existing variable/value pairs.
3665 (when (member op '(add-or-replace delete))
3666 (if (and (eq op 'add-or-replace) (memq variable '(mode eval)))
3667 (goto-char end)
3668 (goto-char beg)
3669 (while (< (point) end)
3670 (or (looking-at "[ \t]*\\([^ \t\n:]+\\)[ \t]*:[ \t]*")
3671 (throw 'exit (message "Malformed -*- line")))
3672 (goto-char (match-end 0))
3673 (let ((key (intern (match-string 1)))
3674 (val (save-restriction
3675 (narrow-to-region (point) end)
3676 (let ((read-circle nil))
3677 (read (current-buffer))))))
3678 (skip-chars-forward " \t;")
3679 (when (eq key variable)
3680 (delete-region (match-beginning 0) (point))
3681 (setq replaced-pos (point)))))))
3682 ;; Add a new variable/value pair. Add `mode' to the start, add new
3683 ;; variable to the end, and add a replaced variable to its last location.
3684 (when (eq op 'add-or-replace)
3685 (cond
3686 ((eq variable 'mode) (goto-char beg))
3687 ((null replaced-pos) (goto-char end) (insert "; "))
3688 (replaced-pos (goto-char replaced-pos)))
3689 (insert (format "%S: %S" variable value))
3690 (cond
3691 ((eq variable 'mode) (insert "; "))
3692 (replaced-pos (insert "; ") ))))))))
3693
3694(defun add-file-local-variable-prop-line (variable value)
3695 "Add file-local VARIABLE with its VALUE to the -*- line.
3696
3697This command deletes all existing settings of VARIABLE (except `mode'
3698and `eval') and adds a new file-local VARIABLE with VALUE to
3699the -*- line.
3700
3701If there is no -*- line at the beginning of the current file buffer
3702then this function adds it."
3703 (interactive
3704 (let ((variable (read-file-local-variable "Add -*- file-local variable")))
3705 (list variable (read-file-local-variable-value variable))))
3706 (modify-file-local-variable-prop-line variable value 'add-or-replace))
3707
3708(defun delete-file-local-variable-prop-line (variable)
3709 "Delete all settings of file-local VARIABLE from the -*- line."
3710 (interactive
3711 (list (read-file-local-variable "Delete -*- file-local variable")))
3712 (modify-file-local-variable-prop-line variable nil 'delete))
3713
3714(defun modify-dir-local-variable (mode variable value op)
3715 "Modify directory-local VARIABLE in .dir-locals.el depending on operation OP.
3716
3717If OP is `add-or-replace' then delete all existing settings of
3718VARIABLE (except `mode' and `eval') and add a new directory-local VARIABLE
3719with VALUE to the MODE alist where MODE can be a mode name symbol or
3720a subdirectory name.
3721
3722If .dir-locals.el was not found and OP is not `delete' then create
3723this file in the current directory.
3724
3725If OP is `delete' then delete all existing settings of VARIABLE
3726from the the MODE alist ignoring the input argument VALUE."
3727 (catch 'exit
3728 (unless enable-local-variables
3729 (throw 'exit (message "Directory-local variables are disabled")))
3730
3731 (let ((variables-file (or (and (buffer-file-name)
3732 (not (file-remote-p (buffer-file-name)))
3733 (dir-locals-find-file (buffer-file-name)))
3734 dir-locals-file))
3735 variables)
3736
3737 ;; Don't create ".dir-locals.el" for the deletion operation.
3738 (when (and (eq op 'delete)
3739 (not (file-exists-p variables-file)))
3740 (throw 'exit (message "File .dir-locals.el not found")))
3741
3742 (let ((auto-insert nil))
3743 (find-file variables-file))
3744 (widen)
3745 (goto-char (point-min))
3746
3747 ;; Read alist of directory-local variables.
3748 (ignore-errors
3749 (delete-region
3750 (prog1 (point)
3751 (setq variables (let ((read-circle nil))
3752 (read (current-buffer)))))
3753 (point)))
3754
3755 ;; Add or replace variable in alist of directory-local variables.
3756 (let ((mode-assoc (assoc mode variables)))
3757 (if mode-assoc
3758 (setq variables
3759 (cons (cons mode
3760 (if (eq op 'delete)
3761 (assq-delete-all variable (cdr mode-assoc))
3762 (cons
3763 (cons variable value)
3764 (if (memq variable '(mode eval))
3765 (cdr mode-assoc)
3766 (assq-delete-all variable (cdr mode-assoc))))))
3767 (assq-delete-all mode variables)))
3768 (setq variables
3769 (cons `(,mode . ((,variable . ,value)))
3770 variables))))
3771
3772 ;; Insert modified alist of directory-local variables.
3773 (insert ";;; Directory Local Variables\n")
3774 (insert ";;; See Info node `(emacs) Directory Variables' for more information.\n\n")
3775 (pp (sort variables
3776 (lambda (a b)
3777 (cond
3778 ((null (car a)) t)
3779 ((null (car b)) nil)
3780 ((and (symbolp (car a)) (stringp (car b))) t)
3781 ((and (symbolp (car b)) (stringp (car a))) nil)
3782 (t (string< (car a) (car b))))))
3783 (current-buffer)))))
3784
3785(defun add-dir-local-variable (mode variable value)
3786 "Add directory-local VARIABLE with its VALUE and MODE to .dir-locals.el."
3787 (interactive
3788 (let (variable)
3789 (list
3790 (read-file-local-variable-mode)
3791 (setq variable (read-file-local-variable "Add directory-local variable"))
3792 (read-file-local-variable-value variable))))
3793 (modify-dir-local-variable mode variable value 'add-or-replace))
3794
3795(defun delete-dir-local-variable (mode variable)
3796 "Delete all MODE settings of file-local VARIABLE from .dir-locals.el."
3797 (interactive
3798 (list
3799 (read-file-local-variable-mode)
3800 (read-file-local-variable "Delete directory-local variable")))
3801 (modify-dir-local-variable mode variable nil 'delete))
3802
3803(defun copy-file-locals-to-dir-locals ()
3804 "Copy file-local variables to .dir-locals.el."
3805 (interactive)
3806 (dolist (elt file-local-variables-alist)
3807 (unless (assq (car elt) dir-local-variables-alist)
3808 (add-dir-local-variable major-mode (car elt) (cdr elt)))))
3809
3810(defun copy-dir-locals-to-file-locals ()
3811 "Copy directory-local variables to the Local Variables list."
3812 (interactive)
3813 (dolist (elt dir-local-variables-alist)
3814 (add-file-local-variable (car elt) (cdr elt))))
3815
3816(defun copy-dir-locals-to-file-locals-prop-line ()
3817 "Copy directory-local variables to the the -*- line."
3818 (interactive)
3819 (dolist (elt dir-local-variables-alist)
3820 (add-file-local-variable-prop-line (car elt) (cdr elt))))
3821
3822
3410(defcustom change-major-mode-with-file-name t 3823(defcustom change-major-mode-with-file-name t
3411 "Non-nil means \\[write-file] should set the major mode from the file name. 3824 "Non-nil means \\[write-file] should set the major mode from the file name.
3412However, the mode will not be changed if 3825However, the mode will not be changed if