;;;; -*- mode:emacs-lisp;coding:utf-8 -*-
;;;;******************************************************************************
;;;;FILE:               pjb-c.el
;;;;LANGUAGE:           emacs lisp
;;;;SYSTEM:             emacs
;;;;USER-INTERFACE:     emacs
;;;;DESCRIPTION
;;;;
;;;;    This module exports C indenting functions.
;;;;
;;;;AUTHORS
;;;;    <PJB> Pascal J. Bourguignon
;;;;MODIFICATIONS
;;;;    2003-02-25 <PJB> Updated, from pjb-objc-mode.el, for emacs 21.2.
;;;;    199?-??-?? <PJB> Creation.
;;;;BUGS
;;;;LEGAL
;;;;    LGPL
;;;;
;;;;    Copyright Pascal J. Bourguignon 1990 - 2011
;;;;
;;;;    This library is free software; you can redistribute it and/or
;;;;    modify it under the terms of the GNU Lesser General Public
;;;;    License as published by the Free Software Foundation; either
;;;;    version 2 of the License, or (at your option) any later version.
;;;;
;;;;    This library 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
;;;;    Lesser General Public License for more details.
;;;;
;;;;    You should have received a copy of the GNU Lesser General Public
;;;;    License along with this library; if not, write to the Free Software
;;;;    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
;;;;
;;;;******************************************************************************
(debug "Should not load this file!")
(provide 'pjb-c)
(require 'cc-mode)
(require 'pjb-utilities)
(error "should not be loaded...")


(defun pjb-c-syntax-of-previous-line ()
  "
RETURN: the syntax (in c-guess-basic-syntax sense) of the previous line.
"
  (save-excursion
    (forward-line -1)
    (end-of-line)
    (c-guess-basic-syntax))
  );;pjb-c-syntax-of-previous-line


(defun pjb-c-comment-of-method    (syntcont)
  "
RETURN: whether the previous line is a objc-method-intro
        and the current line is a comment-intro.
"
  (and (eq (caar (pjb-c-syntax-of-previous-line)) 'objc-method-intro)
       (eq (caar syntcont)                        'comment-intro))
  );;pjb-c-comment-of-method


(defun pjb-c-comment-of-function  (syntcont)
  "
RETURN: whether the current syntactic context is
            ((comment-intro) (knr-argdecl-intro . N))
"
  (and (= (length syntcont) 2)
       (eq (caar syntcont) 'comment-intro)
       (or (eq (caadr  syntcont) 'knr-argdecl-intro)
           (eq (caadr  syntcont) 'objc-method-args-cont)))
  );;pjb-c-comment-of-function


(defun pjb-c-comment-of-top-level (syntcont)
  "
RETURN: whether the current syntactic context is
            ((comment-intro) (topmost-intro . N))
"
  (and (= (length syntcont) 2)
       (eq (caar  syntcont) 'comment-intro)
       (eq (caadr syntcont) 'topmost-intro))
  );;pjb-c-comment-of-top-level


(defun pjb-c-previous-line-is-//-comment ()
  "
RETURN: whether the previous line is a // comment.
"
  (save-excursion
    (forward-line -1)
    (back-to-indentation)
    (looking-at "//"))
  );;pjb-c-previous-line-is-//-comment


(defun pjb-c-current-line-is-//-comment ()
  "
RETURN: whether the current line is a // comment.
"
  (save-excursion
    (back-to-indentation)
    (looking-at "//"))
  );;pjb-c-current-line-is-//-comment


(defun pjb-c-current-line-is-/*-comment ()
  "
RETURN: whether the current line is a /* comment.
"
  (save-excursion
    (back-to-indentation)
    (looking-at "/[*]"))
  );;pjb-c-current-line-is-/*-comment


(defun pjb-c-beginning-of-comment ()
  "
PRE:    (point) is inside a comment.
DO:     Move the point at the beginning of the comment.
"
  (interactive)
  (let* ((syntax  (c-guess-basic-syntax))
         (boc     (assoc 'c syntax)))
    (while boc
      (forward-line -1)
      (pjb-c-beginning-of-comment)
      (setq syntax  (c-guess-basic-syntax)
            boc     (assoc 'c syntax)))
    (if (eq (caar syntax) 'comment-intro)
        (back-to-indentation)
      (error "We must start from the inside of a comment!")))
  );;pjb-c-beginning-of-comment


(defun pjb-c-first-line-of-comment (syntcont)
  "
RETURN: whether the current line is on the first line of a comment,
        discounting the comment-intro on the previous line.
EXAMPLES:
     // YES comment-intro  /* NO  comment-intro  /*    NO  comment-intro
     // NO  comment-intro     YES c                 // YES comment-intro c
     // NO  comment-intro     NO  c                 // NO  comment-intro c
     // NO  comment-intro  */ NO  c              */    NO  c
"
  (let ((boc (assoc 'c syntcont))
        (curr-point (save-excursion (beginning-of-line) (point))))
    (save-excursion
      (if boc
        ;; within a /* comment.
        (progn
          (pjb-c-beginning-of-comment)
          (and (= (point) (cdr boc))
               (= 1 (count-lines (point) curr-point))))
        ;; within a range of // comments.
        (and (pjb-c-current-line-is-//-comment)
             (not (pjb-c-previous-line-is-//-comment))))))
  );;pjb-c-first-line-of-comment



(defun pjb-c-really-comment-intro (syntcont)
  "
RETURN: whether the given syntcont is really a comment intro, and not a mere
        // comment following a // comment or a comment inside a /* comment.
"
  (and (eq (caar syntcont) 'comment-intro)
       (not (assoc 'c syntcont)))
  );;pjb-c-really-comment-intro


(defun pjb-c-keyword-comment-line (syntcont)
  "
RETURN:         Wether we're in a ''KEYWORD:  comment'' line.
"
  (let ((result (progn
  (back-to-indentation)
  (let ((case-fold-search nil))
    (looking-at "[A-Z][-A-Z ]*[A-Z]: "))
  ))) (message "c-keyword-comment=%S" result) result)
  );;pjb-c-keyword-comment-line


(defvar pjb-c-keyword-comment-indent 16
  "Indentation of keyworded comments.");;pjb-c-keyword-comment-indent


(defun pjb-c-lineup-C-comments (langelem)
  "
PRE:            langelem = (comment-intro) or langelem = (c . N)
NOTE:           When langelem = (comment-intro), we're on the line '/*',
                when langelem = (c . N), we're inside the comment.
RETURN:         The relative indentation of the following line.
SEE ALSO:       c-indent-line
"
  (save-excursion
    (let ((c-syntactic-context (c-guess-basic-syntax)))
      (message "\n(c-guess-basic-syntax)=%S\nc-syntactic-context=%S\nlangelem=%S" (c-guess-basic-syntax) c-syntactic-context langelem)
      (if (pjb-c-really-comment-intro c-syntactic-context)
        (progn
          (message "really-comment-intro")
          ;; The indentation for "/*" depends on the previous line.
          (cond
           ((or (pjb-c-comment-of-method    c-syntactic-context)
                (pjb-c-comment-of-function  c-syntactic-context)
                (pjb-c-comment-of-top-level c-syntactic-context))
            (- c-basic-offset))
           (t
            0))
          )
        (progn
          (message "Body or end of comment")
          ;; Body or end of comment.
          (back-to-indentation)
          (cond
           ((looking-at "[*]+/")
            ;; The indentation of "*/" is the same as the indentation of "/*".
            (message "at end of comment")
            (goto-char (match-end 0))
            (forward-comment -1)
            0) ;;(- (current-column) (c-langelem-col langelem)))
           ((pjb-c-first-line-of-comment c-syntactic-context)
            (message "first line of comment")
            c-basic-offset)
           (t
            ;; The indentation of the other lines of the comment
            ;; is the same as that of the previous line.
            ;; TODO: We should check if the previous lines begins
            ;;       with a keyword.
            ;;         EXEMPLE:        comment tralala
            ;;                         comment tralala
            (message "In middle of comment")
            (cond
             ((pjb-c-keyword-comment-line c-syntactic-context)
              (message "keyword line")
              c-basic-offset)
             ((save-excursion
                (forward-line -1)
                (pjb-c-keyword-comment-line c-syntactic-context))
              (message "just after keyword line")
              (+ c-basic-offset pjb-c-keyword-comment-indent))
             (t
              (message "other comment line")
              ;; same indentation as previous line.
              (save-excursion
                (forward-line -1)
                (back-to-indentation)
                (- (current-column) (c-langelem-col langelem))) ))))))))
  );;pjb-c-lineup-C-comments



(defconst pjb-c-todo-regexp
  "\\(\\(//\\)? *\\(TODO\\|SEE\\):.*\\|/\\* *\\(TODO\\|SEE\\):\\(\\*[^/]\\|[^\\*]\\)* \\*/\\)"
  "regexp to match a TODO: (obsolete SEE:) comment.");;pjb-c-todo-regexp


(defun pjb-c-todo-hook ()
 "A hook for C-like modes to add specific font-lock to TODO comments."
  (font-lock-add-keywords
   major-mode (list (list pjb-c-todo-regexp 1 'font-lock-warning-face t))))


;; (defconst pjb-c-C++-method-key
;;   (concat
;;    "^\\s *"
;;    "\\(ABSTRACTMETHOD\\|CLASSMETHOD\\|METHOD"
;;    "\\|PROCEDURE\\|CONSTRUCTOR2?\\|DESTRUCTOR\\)"
;;    "([^)]*\\(([^)]*)\\)?[^)]*)\\s *"))
;;
;;
;; (defun pjb-c++-mode-hook ()
;;   "A hook for C++ mode where we install our own pjb-c-C++-method-key."
;;   (setq c-method-key pjb-c-C++-method-key))




;;; (setq tab-width                     4)  ; the true one!

;;; (setq c-indent-level                4) ;  Indentation of C statements with
;;;                                         ; respect to containing block.

;;; (setq c-brace-imaginary-offset      0) ;  Imagined indentation of a C open brace
;;;                                         ; that actually follows a statement.

;;; (setq c-brace-offset                0) ;  Extra indentation for braces, compared
;;;                                         ; with other text in same context.

;;; (setq c-argdecl-indent              4) ;  Indentation level of declarations of
;;;                                         ; C function arguments.

;;; (setq c-label-offset               -4) ;  Offset of C label lines and case
;;;                                         ; statements relative to usual
;;;                                         ; indentation.

;;; (setq c-continued-statement-offset  4) ;  Extra indent for lines not starting
;;;                                         ; new statements.

;;; (setq c-continued-brace-offset      0) ;  Extra indent for substatements that
;;;                                         ; start with open-braces.

;;; (setq c-basic-offset                4) ;  Amount of basic offset used by `+' and
;;;                                         ; `-' symbols in `c-offsets-alist'.

;;; (setq c-auto-newline                nil) ; Non-nil means automatically newline
;;;                                         ;  before and after braces, and after
;;;                                         ;  colons and semicolons, inserted in C
;;;                                         ;  code. If you do not want a leading
;;;                                         ;  newline before braces then use:
;;;                                         ;  (define-key c-mode-map \"{\"
;;;                                         ;           'electric-c-semi)"

;;; (setq c-tab-always-indent           nil) ; Non-nil means TAB in C mode should
;;;                                         ;  always reindent the current line,
;;;                                         ;  regardless of where in the line point
;;;                                         ;  is when the TAB command is used.


(defvar *pjb-c-word-size* 64 "Number of bits in a unsigned long long.")
(defun pjb-c-label-function (name enum-type enum-constants)
  (let* ((word-size   64)
         (max-width   (loop for constant in enum-constants
                            maximize (length (prin1-to-string constant))))
         (case-format (format "    case %%-%ds: return %%S;\n" max-width))
         (buffer-size (+ (length "Invalid  value: ")
                         (length (prin1-to-string enum-type))
                         (ceiling (* *pjb-c-word-size* (log 2 10)))
                         1 ; terminating null
                         16 ; safety.
                         )))
    (concat (format "const char* %s(%s value){
    switch(value){
" name enum-type)
            (mapconcat (lambda (constant) (format case-format constant (prin1-to-string constant)))
                       enum-constants "")
            (format "    default: {
        static char buffer[%d];
        sprintf(buffer,\"Invalid %s value: %%d\",value);
        return buffer;
    }}
}
" buffer-size enum-type))))


(assert (string= (pjb-c-label-function 'get_label 'foo '(foo_foo foo_bar foo_chiang))
         "const char* get_label(foo value){
    switch(value){
    case foo_foo   : return \"foo_foo\";
    case foo_bar   : return \"foo_bar\";
    case foo_chiang: return \"foo_chiang\";
    default: {
        static char buffer[56];
        sprintf(buffer,\"Invalid foo value: %d\",value);
        return buffer;
    }}
}
"))



(defun ensure-string (object)
  (typecase object
    (symbol (symbol-name object))
    (string object)
    (t (prin1-to-string object))))

(defun substitute-strings (substitutions string)
  (with-temp-buffer
      (loop
        for (old new) in substitutions
          initially (insert string)
        do (replace-string (ensure-string old)
                           (ensure-string new)
                           nil (point-min) (point-max))
        finally (return (buffer-substring-no-properties (point-min) (point-max))))))

(when nil
  (progn
    (loop
      for <T> in '("A" "W")
      for TCHAR in '("char" "WCHAR")
      do (insert (substitute-strings (list (list '<T> <T>) (list 'TCHAR TCHAR))
                                 "
BOOL contains<T> (TCHAR * string, char * substring)
{
    int wlen = strlen<T>(string);
    int slen = strlen(substring);
    int end = wlen - slen;
    int i = 0;
    for (i = 0; i < end; i ++ )
    {
        if (ncompare<T>(string + i, substring, slen) == 0)
        {
            return TRUE;
        }
    }
    return FALSE;
}
")))
    (insert "
BOOL contains (int widechar, BYTE * string, char * substring)
{
    if (widechar)
    {
        return containsW((WCHAR*)string,substring);
    }
    else
    {
        return containsA((char*)string,substring);
    }
}
")))


;;;; THE END ;;;;
ViewGit