On Fri, 25 Mar 2011 13:26:44 +0000, Alan Mackenzie wrote > Hi, Masatake. > > On Thu, Mar 24, 2011 at 09:07:33PM +0900, Masatake YAMATO wrote: >> Hi, > >> Yes, I'll write. [A patch for the menus] > > Thanks! > >> > One other thing: there doesn't seem to be a mechanism for dumping out >> > a guessed style so that it can be used in a future Emacs session. >> > Something like M-x c-dump-guessed-style which would print out the >> > style a bit like c-submit-but-report (C-c C-b) dumps the entire >> > configuration. Do you think something like this is needed? > >> I agree about the requirement if I understand the requirement correctly. > > >> `cc-guess-view-guessed-style' in my patch >> is not enough? It is just do `pp' guessed style like: > > My apologies: I hadn't seen cc-guess-view-guessed-style. I don't think > the function as it is does quite the right thing, though. > >> (with-output-to-temp-buffer "*Guessed Style*" >> (pp style)) > >> I can change the code like: > >> (with-output-to-temp-buffer "*Guessed Style*" >> (pp `(c-add-style ,(cc-guess-style-name) ,style ,t))) > > That would be much better. > >> Do you think this code satisfies the requirement? > > Not quite: the guessed style only includes two style variables. I think > it should be printed out as inheriting from the default style. Something > like: > > (c-add-style "*cc-guess*" > '("gnu" > > )) > > What do you think? Would it perhaps be better to make the *Guessed > Style* buffer R/W, allowing the user to edit the new style before copying > it to his .emacs? I'm not sure on this one. I agree. It is nice. I've replace all the prefix of function name "cc-guess-view-" with new prefix "cc-guess-dump-". Instead I've introduce new function named `cc-guess-view'. It does what you suggested. I've pasted the diff to this mail. To avoid a trouble in applying the patch, I attach cc-guess.el itself I've worked on to this mail. Masatake YAMATO The summary of changes: - Added Style... menu item to C pull-down menu. - `cc-guess-view' emits emacs lisp code which defines guessed style. Suggested by Alan Mackenzie. The changed files: - cc-guess.el: This is the main program file. - cc-lang.el: The menu definition is changes. - cc-style.el: `cc-choose-style-for-mode' is newly defined here. This function is used from both cc-guess.el and cc-mode.el. - cc-mode.el: Some lines are replaced with `cc-choose-style-for-mode'. - Makefile: This is part of official cc-mode source tree, not emacs source tree. cc-guess.el is added to ELISPFILES macro. Detail of changes: 2011-03-27 Masatake YAMATO * cc-guess.el (cc-guess-region-no-install) (cc-guess-make-basic-offset): Use `copy-tree' instead of `copy-list'. 2011-03-27 Masatake YAMATO * cc-langs.el (c-mode-menu): Use `cc-guess-view'. 2011-03-27 Masatake YAMATO * cc-styles.el (cc-choose-style-for-mode): New function dereived from `c-basic-common-init'. * cc-mode.el (c-basic-common-init): Use `cc-choose-style-for-mode'. * cc-guess.el (cc-styles): Require cc-styles to use `cc-choose-style-for-mode'. * cc-guess.el (cc-guess-dump-*): Renamed from cc-guess-view-*. (cc-guess-view-guessed-style): Take a printer function as a parameter. (cc-guess-view): New function for emitting .emacs ready code. Suggested by Alan Mackenzie. 2011-03-27 Masatake YAMATO * cc-langs.el (c-mode-menu): Added "Style..." menu entries. 2011-03-25 Masatake YAMATO * cc-guess.el (cc-guess-install): Report the name of installed style. 2011-03-25 Masatake YAMATO * cc-guess.el (cc-guess-empty-line-p): Define this subst befine it was used. (cc-guess-reset-accumulator): Fix a typo. Set `cc-guess-accumulator' to nil. 2011-03-25 Masatake YAMATO * cc-guess.el (cc-guess-guessed-offsets-alist): (cc-guess-guessed-basic-offset): (cc-guess): (cc-guess-no-install): (cc-guess-buffer): (cc-guess-buffer-no-install): (cc-guess-region): (cc-guess-region-no-install): (cc-guess-install): Added ;;;####autoload marks. * Makefile (ELISPFILES): Move cc-guess.el from EXTRAFILES dist to ELISPFILES. * cc-mode.el (cc-guess): Required. 2011-02-11 Masatake YAMATO * cc-guess.el (cc-guess): Don't limit the region if `cc-guess-region-max' is nil. (cc-guess-no-install): Ditto. 2011-01-18 Masatake YAMATO * cc-guess.el (cc-guess-make-basic-offset): Don't use sort to find the majority. 2011-01-18 Masatake YAMATO * cc-guess.el (cc-guess-style-name-p): New function. 2011-01-16 Masatake YAMATO * cc-guess.el: Use `+', `-', `++', `--', `*', or `/'. (cc-guess-symbolize-offsets-alist): New function. (cc-guess-symbolize-integer): New function. (cc-guess-make-style): New function. (cc-guess-view-guessed-style): New function. 2011-01-16 Masatake YAMATO * cc-guess.el (cc-guess-no-install): New function. (cc-guess-buffer-no-install): New function. (cc-guess-region-no-install): New function. (cc-guess-make-basic-offset): Ignore `c' syntax-symbol. (cc-guess-style-name): New function. (cc-guess-install): Don't set `c-offsets-alist'. Instead define a style and use it. (cc-guess-view-guessed-values): Renamed from `cc-guess-view-offsets-alist'. Print also `basic-offset'. 2011-01-16 Masatake YAMATO * cc-guess.el: Use term `offset' instead of `delta'. Temorary don't use the term `style'. Instead use term `basic-offset' or `offset-alist'. (cc-guess-offset-threshold): Renamed from `cc-guess-delta-threshold'. (cc-guess-accumulate-offset): Renamed from `cc-guess-accumulate-delta'. (cc-guess-guessed-style): Removed. (cc-guess-make-offsets-alist): Renamed from `cc-guess-make-style'. (cc-guess-merge-offsets-alists): Renamed from `cc-guess-merge-styles'. (cc-guess-make-basic-offset): New function. (cc-guess-guessed-offsets-alist): New variable. (cc-guess-guessed-basic-offset): New variable. (cc-guess-view-accumulator): New function for debugging. (cc-guess-reset-accumulator): New function. (cc-guess-view-offsets-alist): Renamed from `cc-guess-view-style'. 2011-01-13 Masatake YAMATO * cc-guess.el (cc-guess-region-max): New option. (cc-guess-buffer): New function. (cc-guess): Limit the region for examining indentation by `cc-guess-region-max'. 2011-01-13 Masatake YAMATO * cc-guess.el (cc-guess-accumulate): New function. (cc-guess-region): Use `cc-guess-accumulate'. 2011-01-13 Masatake YAMATO * cc-guess.el: s/delta-accumulator/accumulator/g. 2011-01-13 Masatake YAMATO * cc-guess.el (cc-guess-empty-line-p): New subroutine. (cc-guess-region): Handle multiple symbols returned from `c-guess-basic-syntax'. Use `cc-guess-empty-line-p'. 2011-01-13 Masatake YAMATO * cc-guess.el (cc-guess-guessed-style): Renamed from `cc-guessed-style'. 2011-01-13 Masatake YAMATO * cc-guess.el (cc-guess-delta-threshold): New option. (cc-guess-current-delta): New function derived from `cc-guess-region'. (cc-guess-region): Don't accumulate a sampled indentation if it is greater than `cc-guess-delta-threshold'. Local Variables: mode: change-log End: The patch: diff --git a/Makefile b/Makefile index 0730662..694d459 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ ELISPFILES=\ cc-defs.el \ cc-engine.el \ cc-fonts.el \ + cc-guess.el \ cc-langs.el \ cc-menus.el \ cc-mode.el \ @@ -26,7 +27,6 @@ EXTRAFILES=\ MANIFEST \ README \ COPYING \ - cc-guess.el \ cc-lobotomy.el \ cc-fix.el diff --git a/cc-guess.el b/cc-guess.el index 55c36c7..9891829 100644 --- a/cc-guess.el +++ b/cc-guess.el @@ -15,12 +15,12 @@ ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2 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; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, @@ -29,12 +29,41 @@ ;;; Commentary: ;; ;; This file contains routines that help guess the cc-mode style in a -;; particular region/buffer. It is provided for example and -;; experimentation only. It is not supported in anyway. Note that -;; style guessing is lossy! +;; particular region/buffer. Here style means `offsets-alist' and +;; `basic-offset'. +;; +;; The main entry point of this program is `cc-guess' command but there +;; are some variants. +;; +;; Consider the major mode for the current buffer is one of the modes +;; provided by cc-mode. `cc-guess' guesses the indentation style by +;; examining the indentation in a region which started from buffer +;; beginning to the point limited by `cc-guess-offset-threshold' and +;; installs the guessed style. The name for installed style is given +;; by `cc-guess-style-name'. +;; +;; `cc-guess-buffer' does the same but in a whole the buffer. +;; `cc-guess-region' does the same but in a region between the point +;; and the mark. ;; -;; The way this is intended to be run is for you to mark a region of -;; code to guess the style of, then run the command, `cc-guess-region'. +;; `cc-guess-no-install', `cc-guess-buffer-no-install' +;; and `cc-guess-region-no-install' guess the indentation style but +;; don't install. You can review a guessed style with `cc-guess-view'. +;; After reviewing use `cc-guess-install' to install the style +;; if you prefer it. +;; +;; If you want to reuse the guessed style in other buffer, +;; run `c-set-style' command with the name of the guessed style: +;; "*cc-guess*:". +;; Once guessed style is installed explicitly with `cc-guess-install' +;; or implicitly with `cc-guess', `cc-guess-buffer', or `cc-guess-region', +;; a style name is given by `cc-guess-style-name' with above form. +;; +;; If you want to reuse the guessed style in next emacs sessions, +;; you may want to put it to your .emacs. `cc-guess-view' is for +;; you. It emits emacs lisp code which defines the last guessed +;; style on a temporary buffer. You can put the emitted code to +;; your .emacs. This command is suggested by Alan Mackenzie. ;;; Code: @@ -48,24 +77,48 @@ (cc-require 'cc-defs) (cc-require 'cc-engine) +(cc-require 'cc-styles) -(defvar cc-guessed-style nil - "Currently guessed style.") -(defvar cc-guess-delta-accumulator nil) -;; Accumulated sampled indent information. Information is represented +(defcustom cc-guess-offset-threshold 10 + "Threshold of acceptable offset when examining indent information. +Discard a examined offset if its absolute value is greater than this. + +The offset of the a line included in the indent information returned +by `c-guess-basic-syntax'." + :type 'integer + :group 'c) + +(defcustom cc-guess-region-max 50000 + "The maximum point of region for examining indent information with `cc-guess'. +It takes long time for examining indent information from large region. +This option helps you limit the examining time. `nil' means no limit." + :type 'integer + :group 'c) + + +;;;###autoload +(defvar cc-guess-guessed-offsets-alist nil + "Currently guessed offsets-alist. Buffer local.") +;;;###autoload +(defvar cc-guess-guessed-basic-offset nil + "Currently guessed basic-offset. Buffer local.") + +(defvar cc-guess-accumulator nil) +;; Accumulated examined indent information. Information is represented ;; in a list. Each element in it has following structure: -;; -;; (syntactic-symbol ((indentation-delta1 . number-of-times1) -;; (indentation-delta2 . number-of-times2) -;; ...)) -;; -;; This structure is built by `cc-guess-accumulate-delta'. -;; -;; Here we call the pair (indentation-delta1 . number-of-times1) a -;; counter. `cc-guess-sort-delta-accumulator' sorts the order of +;; +;; (syntactic-symbol ((indentation-offset1 . number-of-times1) +;; (indentation-offset2 . number-of-times2) +;; ...)) +;; +;; This structure is built by `cc-guess-accumulate-offset'. +;; +;; Here we call the pair (indentation-offset1 . number-of-times1) a +;; counter. `cc-guess-sort-accumulator' sorts the order of ;; counters by number-of-times. +;; Use `cc-guess-dump-accumulator' to see the value. (defconst cc-guess-conversions '((c . c-lineup-C-comments) @@ -75,131 +128,194 @@ (arglist-cont-nonempty . c-lineup-arglist) (arglist-close . c-lineup-close-paren) (cpp-macro . -1000))) - + +;;;###autoload (defun cc-guess (&optional accumulate) - "Apply `cc-guess-region' on the whole current buffer. + "Apply `cc-guess-region' on the region limited by `cc-guess-region-max'. If given a prefix argument (or if the optional argument ACCUMULATE is non-nil) then the previous guess is extended, otherwise a new guess is made from scratch." (interactive "P") - (cc-guess-region (point-min) (point-max) accumulate)) + (cc-guess-region (point-min) + (min (point-max) (or cc-guess-region-max + (point-max))) + accumulate)) -(defun cc-guess-install () - "Set the indentation style from the last guessed style (`cc-guessed-style')." - (interactive) - (setq c-offsets-alist (cc-guess-merge-styles cc-guessed-style - c-offsets-alist))) +;;;###autoload +(defun cc-guess-no-install (&optional accumulate) + "Apply `cc-guess-region-no-install' on the region limited by `cc-guess-region-max'. + +If given a prefix argument (or if the optional argument ACCUMULATE is +non-nil) then the previous guess is extended, otherwise a new guess is +made from scratch." + (interactive "P") + (cc-guess-region-no-install (point-min) + (min (point-max) (or cc-guess-region-max + (point-max))) + accumulate)) + +;;;###autoload +(defun cc-guess-buffer (&optional accumulate) + "Apply `cc-guess-region' on the whole current buffer. + If given a prefix argument (or if the optional argument ACCUMULATE is + non-nil) then the previous guess is extended, otherwise a new guess is + made from scratch." + (interactive "P") + (cc-guess-region (point-min) + (point-max) + accumulate)) + +;;;###autoload +(defun cc-guess-buffer-no-install (&optional accumulate) + "Apply `cc-guess-region-no-install' on the whole current buffer. + + If given a prefix argument (or if the optional argument ACCUMULATE is + non-nil) then the previous guess is extended, otherwise a new guess is + made from scratch." + (interactive "P") + (cc-guess-region-no-install (point-min) + (point-max) + accumulate)) + +;;;###autoload (defun cc-guess-region (start end &optional accumulate) - "Set the indentation style by examining the indentation in a region of code. -Every line of code in the region is examined and the indentation -values of the various syntactic symbols in `c-offset-alist' are -guessed. Frequencies of use are taken into account, so minor -inconsistencies in the indentation style shouldn't produce wrong -guesses. + "Call `cc-guess-region-no-install' and install the guessed style." + (interactive "r\nP") + (cc-guess-region-no-install start end accumulate) + (cc-guess-install)) + + +(defsubst cc-guess-empty-line-p () + (eq (line-beginning-position) + (line-end-position))) -The guessed style is put into `cc-guessed-style'. It's also merged -into `c-offsets-alist'. Guessed offsets takes precedence over -existing ones on `c-offsets-alist'. +;;;###autoload +(defun cc-guess-region-no-install (start end &optional accumulate) + "Guess the indentation style by examining the indentation in a region of code. +Every line of code in the region is examined and the values for following +two variabels are guessed: + +* `c-basic-offset', and +* the indentation values of the various syntactic symbols in + `c-offsets-alist'. + +The guessed values are put into `cc-guess-guessed-basic-offset' and +`cc-guess-guessed-offsets-alist'. + +Frequencies of use are taken into account when guessing, so minor +inconsistencies in the indentation style shouldn't produce wrong guesses. If given a prefix argument (or if the optional argument ACCUMULATE is -non-nil) then the previous guess is extended, otherwise a new guess is -made from scratch. +non-nil) then the previous examination is extended, otherwise a new +guess is made from scratch. -Note that the larger the region to guess in, the slower the guessing." +Note that the larger the region to guess in, the slower the guessing. +So you can limit the region with `cc-guess-region-max'." (interactive "r\nP") - (let ((delta-accumulator (when accumulate cc-guess-delta-accumulator)) + ;; + ;; Examining stage + ;; + (let ((accumulator (when accumulate cc-guess-accumulator)) (reporter (when (fboundp 'make-progress-reporter) - (make-progress-reporter "Sampling Indentation " start end)))) - ;; - ;; Sampling stage - ;; + (make-progress-reporter "Examining Indentation " + start + end)))) (save-excursion (goto-char start) (while (< (point) end) - (c-save-buffer-state - ((syntax (c-guess-basic-syntax)) - (relpos (car (cdr (car syntax)))) - (symbol (car (car syntax)))) - ;; TBD: for now I can't guess indentation when more than 1 - ;; symbol is on the list, nor for symbols without relpos's - ;; - ;; I think it is too stricted for ((topmost-intro) (comment-intro)). - ;; -- Masatake - (unless (or ; (/= 1 (length syntax)) - (not (numberp relpos)) - (eq (line-beginning-position) - (line-end-position))) - (setq delta-accumulator (cc-guess-accumulate-delta - delta-accumulator - symbol - (- (progn (back-to-indentation) - (current-column) ) - (save-excursion - (goto-char relpos) - (current-column))))))) + (unless (cc-guess-empty-line-p) + (mapc (lambda (s) + (setq accumulator (or (cc-guess-accumulate accumulator s) + accumulator))) + (c-save-buffer-state () (c-guess-basic-syntax)))) (when reporter (progress-reporter-update reporter (point))) (forward-line 1))) (when reporter (progress-reporter-done reporter)) - ;; - ;; Guessing stage - ;; - (setq delta-accumulator (cc-guess-sort-delta-accumulator - delta-accumulator) - cc-guess-delta-accumulator delta-accumulator) - (let* ((typical-style (cc-guess-make-style delta-accumulator)) - (merged-style (cc-guess-merge-styles - (copy-list cc-guess-conversions) - typical-style))) - (setq cc-guessed-style merged-style - c-offsets-alist (cc-guess-merge-styles - merged-style - c-offsets-alist))))) - -(defun cc-guess-accumulate-delta (accumulator symbol delta) - ;; Added SYMBOL and DELTA to ACCUMULATOR. See - ;; `cc-guess-delta-accumulator' about the structure of ACCUMULATOR. + (setq cc-guess-accumulator (cc-guess-sort-accumulator accumulator))) + ;; + ;; Guessing stage + ;; + (let* ((basic-offset (cc-guess-make-basic-offset cc-guess-accumulator)) + (typical-offsets-alist (cc-guess-make-offsets-alist + cc-guess-accumulator)) + (symbolic-offsets-alist (cc-guess-symbolize-offsets-alist + typical-offsets-alist + basic-offset)) + (merged-offsets-alist (cc-guess-merge-offsets-alists + (copy-tree cc-guess-conversions) + symbolic-offsets-alist))) + (set (make-local-variable 'cc-guess-guessed-basic-offset) + basic-offset) + (set (make-local-variable 'cc-guess-guessed-offsets-alist) + merged-offsets-alist)) + ) + +(defun cc-guess-current-offset (relpos) + ;; Calculate relative indentation (point) to RELPOS. + (- (progn (back-to-indentation) + (current-column)) + (save-excursion + (goto-char relpos) + (current-column)))) + +(defun cc-guess-accumulate (accumulator syntax-element) + ;; Added SYNTAX-ELEMENT to ACCUMULATOR. + (let ((symbol (car syntax-element)) + (relpos (cadr syntax-element))) + (when (numberp relpos) + (let ((offset (cc-guess-current-offset relpos))) + (when (< (abs offset) cc-guess-offset-threshold) + (cc-guess-accumulate-offset accumulator + symbol + offset)))))) + +(defun cc-guess-accumulate-offset (accumulator symbol offset) + ;; Added SYMBOL and OFFSET to ACCUMULATOR. See + ;; `cc-guess-accumulator' about the structure of ACCUMULATOR. (let* ((entry (assoc symbol accumulator)) (counters (cdr entry)) counter) (if entry (progn - (setq counter (assoc delta counters)) + (setq counter (assoc offset counters)) (if counter (setcdr counter (1+ (cdr counter))) - (setq counters (cons (cons delta 1) counters)) + (setq counters (cons (cons offset 1) counters)) (setcdr entry counters)) accumulator) - (cons (cons symbol (cons (cons delta 1) nil)) accumulator)))) + (cons (cons symbol (cons (cons offset 1) nil)) accumulator)))) -(defun cc-guess-sort-delta-accumulator (accumulator) +(defun cc-guess-sort-accumulator (accumulator) ;; Sort the each element of ACCUMULATOR by the number-of-times. See - ;; `cc-guess-delta-accumulator' for more details. + ;; `cc-guess-accumulator' for more details. (mapcar (lambda (entry) (let ((symbol (car entry)) (counters (cdr entry))) - (cons symbol (sort counters + (cons symbol (sort counters (lambda (a b) (if (> (cdr a) (cdr b)) t - (and + (and (eq (cdr a) (cdr b)) (< (car a) (car b))))))))) accumulator)) - -(defun cc-guess-make-style (accumulator) - ;; Throw away the rare cases in accumulator and make a style structure. - (mapcar + +(defun cc-guess-make-offsets-alist (accumulator) + ;; Throw away the rare cases in accumulator + ;; and make a offsets-alist structure. + (mapcar (lambda (entry) - (cons (car entry) + (cons (car entry) (car (car (cdr entry))))) accumulator)) -(defun cc-guess-merge-styles (strong weak) - ;; Merge two styles into one. When two styles has the same symbol +(defun cc-guess-merge-offsets-alists (strong weak) + ;; Merge two offsets-alists into one. + ;; When two offsets-alists have the same symbol ;; entry, give STRONG priority over WEAK. (mapc (lambda (weak-elt) @@ -208,11 +324,184 @@ Note that the larger the region to guess in, the slower the guessing." weak) strong) -(defun cc-guess-view-style () - "Show `cc-guessed-style'." +(defun cc-guess-make-basic-offset (accumulator) + ;; As `basic-offset' find the most frequently appeared indentation-offset + ;; from ACCUMULATOR. + (let* (;; Drop the value related to `c' syntactic-symbol. + ;; (`c': Inside a multiline C style block comment.) + ;; The impact for values of `c' is too large for guessing + ;; `basic-offset' if the target source file is small and its license + ;; notice is at top of the file. + (accumulator (assq-delete-all 'c (copy-tree accumulator))) + ;; Drop syntactic-symbols from ACCUMULATOR. + (alist (apply #'append (mapcar (lambda (elts) + (mapcar (lambda (elt) + (cons (abs (car elt)) + (cdr elt))) + (cdr elts))) + accumulator))) + ;; Gather all indentation-offsets other than 0. + ;; 0 is meaningless as `basic-offset'. + (offset-list (delete 0 + (delete-dups (mapcar + (lambda (elt) (car elt)) + alist)))) + ;; Sum of number-of-times for offset: + ;; (offset . sum) + (summed (mapcar (lambda (offset) + (cons offset + (apply #'+ + (mapcar (lambda (a) + (if (eq (car a) offset) + (cdr a) + 0)) + alist)))) + offset-list))) + ;; + ;; Find the majority. + ;; + (let ((majority '(nil . 0))) + (while summed + (when (< (cdr majority) (cdr (car summed))) + (setq majority (car summed))) + (setq summed (cdr summed))) + (car majority)))) + +(defun cc-guess-symbolize-offsets-alist (offsets-alist basic-offset) + ;; Convert the representation of OFFSETS-ALIST to an alist using + ;; `+', `-', `++', `--', `*', or `/'. These symbols represents + ;; a value relative to BASIC-OFFSET. See info of CC mode about + ;; the detail of the symbols. + (mapcar + (lambda (elt) + (let ((s (car elt)) + (v (cdr elt))) + (cond + ((integerp v) + (cons s (cc-guess-symbolize-integer v + basic-offset))) + (t elt)))) + offsets-alist)) + +(defun cc-guess-symbolize-integer (int basic-offset) + (let ((aint (abs int))) + (cond + ((eq int basic-offset) '+) + ((eq aint basic-offset) '-) + ((eq int (* 2 basic-offset)) '++) + ((eq aint (* 2 basic-offset)) '--) + ((eq (* 2 int) basic-offset) '*) + ((eq (* 2 aint) basic-offset) '-) + (t int)))) + +(defun cc-guess-style-name-p (name) + "Return t if NAME is name of a style created by cc-guess." + (string-prefix-p "*cc-guess*:" name)) +(defun cc-guess-style-name () + ;; Make a style name for the guessed style. + (format "*cc-guess*:%s" (buffer-file-name))) + +(defun cc-guess-make-style () + ;; Make a style from guessed values. + (when cc-guess-guessed-offsets-alist + (let* ((basic-offset cc-guess-guessed-basic-offset) + (offsets-alist (cc-guess-merge-offsets-alists + cc-guess-guessed-offsets-alist + c-offsets-alist))) + `((c-basic-offset . ,basic-offset) + (c-offsets-alist . ,offsets-alist))))) + +;;;###autoload +(defun cc-guess-install () + "Define the indentation style from the last guessed values and use it. +Here guessed values mean `cc-guess-guessed-basic-offset' and +`cc-guess-guessed-offsets-alist'. + +When defining the style from `cc-guess-guessed-offsets-alist', +`c-offsets-alist' is also merged into the style. However, +`cc-guess-guessed-offsets-alist' takes precedence over +`c-offsets-alist'. + +The style name is given by `cc-guess-style-name'." + (interactive) + (let ((style (cc-guess-make-style))) + (if style + (let ((style-name (cc-guess-style-name))) + (c-add-style style-name style t) + (message "Style \"%s\" is installed" style-name)) + (error "Not yet guessed")))) + +(defun cc-guess-dump-accumulator () + "Show `cc-guess-accumulator'." (interactive) - (with-output-to-temp-buffer "*Indentation Guessing Result*" - (pp cc-guessed-style))) + (with-output-to-temp-buffer "*Accumulated Examined Indent Information*" + (pp cc-guess-accumulator))) + +(defun cc-guess-reset-accumulator () + "Reset `cc-guess-accumulator'." + (interactive) + (setq cc-guess-accumulator nil)) + +(defun cc-guess-dump-guessed-values () + "Show `cc-guess-guessed-basic-offset' and `cc-guess-guessed-offsets-alist'." + (interactive) + (with-output-to-temp-buffer "*Guessed Values*" + (princ "basic-offset: \n\t") + (pp cc-guess-guessed-basic-offset) + (princ "\n\n") + (princ "offsets-alist: \n") + (pp cc-guess-guessed-offsets-alist) + )) + +(defun cc-guess-dump-guessed-style (&optional printer) + "Show the guessed style. +`pp' is used to print the style but if PRINTER is given, +PRINTER is used instead. If PRINTER is not `nil', it +is called with one argument, the guessed style." + (interactive) + (let ((style (cc-guess-make-style))) + (if style + (with-output-to-temp-buffer "*Guessed Style*" + (funcall (if printer printer 'pp) style)) + (error "Not yet guessed")))) + +(defun cc-guess-view (&optional with-name) + "Emit emacs lisp code which defines the last guessed style. +So you can put the code into .emacs if you prefer the +guessed code. +\"STYLE NAME HERE\" is used as the name for the style in the +emitted code. If WITH-NAME is given, it is used instead. +WITH-NAME is expected as a string but if this function +called interactively with prefix argument, the value for +WITH-NAME is asked to the user." + (interactive "P") + (let* ((temporary-style-name (cond + ((stringp with-name) with-name) + (with-name (read-from-minibuffer + "Style name: ")) + (t + "STYLE NAME HERE"))) + (guessed-style-name (cc-guess-style-name)) + (current-style-name c-indentation-style) + (parent-style-name (if (string-equal guessed-style-name + current-style-name) + ;; The guessed style is already installed. + ;; It cannot be used as the parent style. + ;; Use the default style for the current + ;; major mode as the parent style. + (cc-choose-style-for-mode + major-mode + c-default-style) + ;; The guessed style is not installed yet. + current-style-name))) + (cc-guess-dump-guessed-style + (lambda (style) + (pp `(c-add-style ,temporary-style-name + ',(cons parent-style-name + style))) + (with-current-buffer standard-output + (lisp-interaction-mode)) + )))) (cc-provide 'cc-guess) diff --git a/cc-langs.el b/cc-langs.el index 0cf6131..0b92ab6 100644 --- a/cc-langs.el +++ b/cc-langs.el @@ -287,6 +287,19 @@ the evaluated constant value at compile time." ["Backslashify" c-backslash-region (c-fn-region-is-active-p)])) "----" + ("Style..." + ["Set Style..." c-set-style t] + ["Show Current Style Name" (message + "Style Name: %s" + c-indentation-style) t] + ["Guess Style from this Buffer" cc-guess-buffer-no-install t] + ["Install the Last Guessed Style" cc-guess-install + (and cc-guess-guessed-offsets-alist + cc-guess-guessed-basic-offset) ] + ["View the Last Guessed Style" cc-guess-view + (and cc-guess-guessed-offsets-alist + cc-guess-guessed-basic-offset) ]) + "----" ("Toggle..." ["Syntactic indentation" c-toggle-syntactic-indentation t] ["Electric mode" c-toggle-electric-state t] diff --git a/cc-mode.el b/cc-mode.el index fe35f28..6b59e6e 100644 --- a/cc-mode.el +++ b/cc-mode.el @@ -92,6 +92,7 @@ (cc-require 'cc-cmds) (cc-require 'cc-align) (cc-require 'cc-menus) +(cc-require 'cc-guess) ;; Silence the compiler. (cc-bytecomp-defvar adaptive-fill-first-line-regexp) ; Emacs @@ -563,11 +564,7 @@ that requires a literal mode spec at compile time." (c-clear-found-types) ;; now set the mode style based on default-style - (let ((style (if (stringp default-style) - default-style - (or (cdr (assq mode default-style)) - (cdr (assq 'other default-style)) - "gnu")))) + (let ((style (cc-choose-style-for-mode mode default-style))) ;; Override style variables if `c-old-style-variable-behavior' is ;; set. Also override if we are using global style variables, ;; have already initialized a style once, and are switching to a diff --git a/cc-styles.el b/cc-styles.el index 18f0afa..6c3218b 100644 --- a/cc-styles.el +++ b/cc-styles.el @@ -655,6 +655,15 @@ any reason to call this function directly." (setq c-style-variables-are-local-p t)) )) +(defun cc-choose-style-for-mode (mode default-style) + "Return suitable style for MODE from DEFAULT-STYLE. +DEFAULT-STYLE has the same format as `c-default-style'." + (if (stringp default-style) + default-style + (or (cdr (assq mode default-style)) + (cdr (assq 'other default-style)) + "gnu"))) + (cc-provide 'cc-styles)