;; Elisp source code header -*- coding: utf-8 -*-
;; Created: [18-51:53 Февраль 25 2008]
;; Modified: [23-44:29 Май 31 2008]
;; Description: Development routines
;; Author: Stanislav M. Ivankin
;; Email: stas@concat.info
;; Tags: elisp, emacs, devel

;;;;;;;;;;;;;;;; Some usefull things ;;;;;;;;;;;;;;;;

;; Set execution bits to script files, idea was taken on emacswiki.org
(add-hook 'after-save-hook
          #'(lambda ()
              (save-excursion
                (save-restriction
                  (widen)
                  (goto-char (point-min))
                  (save-match-data
                    (when (looking-at "^#!")
                      (change-execution-bit)))))))

;; Timeclock thingy
(global-set-key (kbd "s-i") 'timeclock-in)
(global-set-key (kbd "s-o") 'timeclock-out)

;; Line numbers
(require 'linum)

;; Emacs tags
(require 'etags)

;;;;;;;;;;;;;;;; Add info header to a source file ;;;;;;;;;;;;;;;;

(defvar myself "Stanislav M. Ivankin")
(defvar myemail "stas@concat.info")
(defvar my-coding-system "utf-8")

;; TODO: If we cant guess file type, try to guess
;; it from activated mode, then prompt user
(defun* my-define-file-type ()
  (let* ((buf ((lambda (x)
                 (if (null x)
                     ""
                   x)) buffer-file-name))
         (infoheader
           ((lambda ()
              (cond ((string-match  "\\.el$" buf)
                     '("Elisp" ";; "))
                    ((string-match  "\\.lisp$" buf)
                     '("Lisp" ";; "))
                    ((string-match "\\.\\(hs\\|hi\\)$" buf)
                     '("Haskell" "-- "))
                    ((string-match "\\.\\(cpp\\|cc\\|hh\\|hpp\\)$" buf)
                     '("C++" "// "))
                    ((string-match "\\.\\(c\\|h\\)$" buf)
                     '("C" "// "))
                    ((string-match "\\.sh$" buf)
                     '("Shell" "## "))
                    ((string-match "\\.pl$" buf)
                     '("Perl" "## "))
                    (t '("File" "## ")))))))
      (list (concat (car infoheader) " source code header") (cadr infoheader))))

(defun update-my-info-header ()
  "If there is info header, changes its timestamp, when file is saved"
  (interactive)
  (let ((infoheader (my-define-file-type)))
    (unless (null infoheader)
      (save-excursion
        (save-restriction
          (save-match-data
            (widen)
            (goto-char (point-min))
            (when (search-forward (car infoheader) nil t)
              (goto-line (+ (line-number-at-pos) 2))
              (when (looking-at (concat (cadr (my-define-file-type)) "Modified: \\["))
                (let ((start (+ (point)
                                (+ (length (cadr (my-define-file-type)))
                                   (length "Modified: [")))))
                  (when (search-forward "]")
                    (delete-region start (- (point) 1))
                    (goto-char start)
                    (insert (format-time-string "%H-%M:%S %B %d %Y" (current-time)))))))))))))

(defun create-my-info-header ()
  "Creates info header for source code file"
  (interactive)
  (let ((exists nil) (infoheader (my-define-file-type)))
    (when infoheader
      (save-excursion
        (save-restriction
          (save-match-data
            (goto-char (point-min))
            (if (search-forward (car infoheader) nil t)
                (message "Header already exists.")
              (when (equal "y" (completing-read "Create info header?: " nil nil nil))
                (goto-char (point-min))
                (when (looking-at "#!")
                  (goto-line 2))
                (let ((comment (cadr infoheader)))
                  (insert (concat comment (car infoheader) " -*- coding: " my-coding-system " -*-" "\n"
                                  comment "Created: ["
                                  (format-time-string "%H-%M:%S %B %d %Y" (current-time)) "]\n"
                                  comment "Modified: [---]\n"
                                  comment "Description: \n"
                                  comment "Author: " myself "\n"
                                  comment "Email: " myemail "\n"
                                  comment "Tags: \n"
                                  comment "License: \n\n")))))))))))

(add-hook 'before-save-hook 'update-my-info-header)

;;;;;;;;;;;;;;;; Yasnippet ;;;;;;;;;;;;;;;;

(require 'yasnippet) ;; not yasnippet-bundle
(yas/initialize)
(yas/load-directory "/home/esgal/elisp/yasnippet/snippets/text-mode/")

;;;;;;;;;;;;;;;; Objective Caml ;;;;;;;;;;;;;;;;

;; Caml mode
;;(setq load-path (cons "~/caml-mode" load-path))
;;(setq auto-mode-alist (cons '("\\.ml\\w?" . caml-mode) auto-mode-alist))
;;(autoload 'caml-mode "caml" "Major mode for editing Caml code" t)
;;(autoload 'camldebug "camldebug" "Run the Caml debugger" t)

;; Tuareg mode
(add-to-list 'load-path "~/elisp/tuareg-mode")
(autoload 'tuareg-mode "tuareg" "Major mode for editing Caml code" t)
(autoload 'camldebug "camldebug" "Run the Caml debugger" t)
(autoload 'tuareg-imenu-set-imenu "tuareg-imenu" 
  "Configuration of imenu for tuareg" t) 

(load "~/elisp/tuareg-mode/custom-tuareg.el")

(add-hook 'tuareg-mode-hook 'tuareg-imenu-set-imenu)
(add-hook 'tuareg-mode-hook '(lambda ()
                               (local-set-key [return] 'newline-and-indent)
                               (local-set-key (kbd "C-x c") '(lambda ()
                                                               (interactive)
                                                               (insert "(* *)")))
                               (linum-mode 1)))

(setq auto-mode-alist 
      (append '(("\\.ml[ily]?$" . tuareg-mode)
                ("\\.topml$" . tuareg-mode))
              auto-mode-alist))

;;;;;;;;;;;;;;;; Sh/Bash  ;;;;;;;;;;;;;;;;

(add-hook 'sh-mode-hook '(lambda ()
                           (linum-mode 1)))

;;;;;;;;;;;;;;;; C/C++ code stuff ;;;;;;;;;;;;;;;;

(defconst use-backup-dir t)

(require 'cc-mode)

(defun my-c-mode-common-hook ()
       (c-set-offset 'member-init-intro '++)
;;       (add-to-list 'font-lock-defaults (list my-c-keywords . font-lock-builtin-face))
       (linum-mode 1)
       (setq tab-width 4)
       (setq indent-tabs-mode t)
       (setq c-default-style "k&r")
       (c-set-offset 'substatement-open 0)
       (setq c-basic-offset 4)
       (c-toggle-auto-hungry-state 1)
       (abbrev-mode 1)
       (auto-fill-mode 1)
       (local-set-key [delete]  'delete-char)
       (local-set-key [return] 'newline-and-indent)
       (define-key c-mode-base-map "\C-m" 'newline-and-indent)
       (local-set-key "\C-c:" 'uncomment-region)
       (local-set-key "\C-c;" 'comment-region))

(add-hook 'c-mode-common-hook 'my-c-mode-common-hook)

(defun fp-c-mode-routine ()
  (local-set-key "\M-q" 'rebox-comment))

(add-hook 'c-mode-hook 'fp-c-mode-routine)
(autoload 'rebox-comment "rebox" nil t)
(autoload 'rebox-region "rebox" nil t)

(setq user-mail-address "stas@concat.info")
(setq user-full-name "Stanislav M. Ivankin")
(setq tab-width 4)
;;; Always end a file with a newline
(setq require-final-newline t)

(setq auto-mode-alist (cons '("\\.h\\'" . c++-mode) auto-mode-alist))

(defun user-save-and-make-all ()
  "save and call compile as make all"
  (interactive)
  (save-buffer)
  (compile "make all")
  (message "make all executed!"))

(defun user-make-clean ()
  "save and clean build"
  (interactive)
  (compile "make clean")
  (message "make clean executed!"))

(defun call-shell ()
  "Call eshell for current build"
  (interactive)
  (eshell)
  (message "eshell started"))

;; cscope
(require 'xcscope)
(require 'xcscope+)

;;;;;;;;;;;;;;;; Perl mode ;;;;;;;;;;;;;;;

(fset 'perl-mode 'cperl-mode)
(setq cperl-tab-always-indent t
      cperl-indent-level 4
      cperl-close-paren-offset -4
      cperl-continued-statement-offset 4
      cperl-invalid-face nil
;      cperl-electric-keywords t
      cperl-auto-newline t
      cperl-indent-parens-as-block t)

(add-hook 'cperl-mode-hook
          (lambda ()
            (local-set-key (kbd "C-h f") 'cperl-perldoc)
            (local-set-key [return] 'newline-and-indent)
            (linum-mode 1)))

(custom-set-faces
 '(cperl-array-face ((((class color) (background light)) (:background "lightgrey" :foreground "Blue" :weight bold))))
 '(cperl-hash-face ((((class color) (background light)) (:background "lightgrey" :foreground "Red" :slant italic :weight bold)))))

;;;;;;;;;;;;;;;; ECB and stuff configs ;;;;;;;;;;;;;;;;

;; compilation window shall scroll down
(setq compilation-scroll-output 1)

(load-file "~/elisp/cedet/common/cedet.el")
(load-file "~/elisp/cedet/semantic/semantic-load.el")

(require 'ede)
(require 'gdb-ui)
(require 'cedet)
(require 'ecb)

(setq ecb-tip-of-the-day nil)

;; semantic stuff
(eval-after-load "semantic-load"
    '(progn
       (message "-> Initializing semantic stuff")
       (global-semantic-tag-folding-mode 1)
       (global-semantic-highlight-edits-mode -1)
       (global-semanticdb-minor-mode 1)
       (semantic-load-enable-guady-code-helpers)
       ;; Stiky thing on top of my editor - kill it!
       (global-semantic-stickyfunc-mode nil)
       (global-semantic-idle-completions-mode -1)
       (global-semantic-show-unmatched-syntax-mode -1)
       (global-semantic-decoration-mode 1)
       (global-semantic-idle-scheduler-mode 1)
       (global-semantic-idle-summary-mode 1)
       ;; Enable SRecode (Template management) minor-mode
       (global-srecode-minor-mode 1)
       ;; Enable EDE (Project Management) features
       (global-ede-mode 1)
;;       (ede-cpp-root-project "TTA" :file "~/Projects/CPP/tau/Makefile")
       (define-key global-map "\C-cc" 'semantic-analyze-possible-completions)))

;; code browser
(when (and (locate-library "ecb") (locate-library "cedet"))
  (require 'ecb)
  (setq ecb-compile-window-height 10
        ecb-compile-window-width (quote edit-window)
        ecb-layout-name "left10"
        ecb-layout-window-sizes     (quote (("left10" (0.22857142857142856 . 0.6585365853658537) (0.11428571428571428 . 0.3170731707317073) (0.11428571428571428 . 0.3170731707317073))))
        ecb-prescan-directories-for-emptyness t
        ecb-primary-secondary-mouse-buttons (quote mouse-1--C-mouse-1)))

;;;;;;;;;;;;;;;; Haskell mode ;;;;;;;;;;;;;;;;

(load "~/elisp/haskell-mode/haskell-site-file")

(add-hook 'haskell-mode-hook 'turn-on-haskell-indent)
(add-hook 'haskell-mode-hook 'turn-on-haskell-ghci)

(setq auto-mode-alist
      (append auto-mode-alist
              '(("\\.[hg]s$"  . haskell-mode)
                ("\\.hi$"     . haskell-mode)
                ("\\.l[hg]s$" . literate-haskell-mode))))

(autoload 'haskell-mode "haskell-mode"
  "Major mode for editing Haskell scripts." t)
(autoload 'literate-haskell-mode "haskell-mode"
  "Major mode for editing literate Haskell scripts." t)
;;(autoload 'hoogle-lookup "hoogle" t)

(setq haskell-program-name "ghci -i")

(add-hook 'haskell-mode-hook 'turn-on-haskell-font-lock)
(add-hook 'haskell-mode-hook 'turn-on-haskell-decl-scan)
(add-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode)
(add-hook 'haskell-mode-hook
          '(lambda ()
             (linum-mode 1)
             (local-set-key (kbd "C-h f") 'haskell-hoogle)
             (local-set-key [return] 'newline-and-indent)))

;;;;;;;;;;;;;;;; Lisp mode ;;;;;;;;;;;;;;;;

(require 'vnesting)

(add-hook 'slime-lisp-mode-hook 'vnest-mode)
(add-hook 'lisp-mode-hook 'vnest-mode)

(add-hook 'lisp-mode-hook
          '(lambda ()
             (linum-mode 1)
             (local-set-key [delete]  'delete-char)
             (local-set-key [return] 'newline-and-indent)))

(add-hook 'emacs-lisp-mode-hook
          '(lambda ()
             (linum-mode 1)
             (local-set-key [delete]  'delete-char)
             (local-set-key [return] 'newline-and-indent)))

;;(defun my-match-paren (arg)
;;  (interactive "p")
;;  (cond ((looking-at "\\s\(")
;;       (forward-list 1) (backward-char 1))
;;      ((looking-at "\\s\)")
;;       (forward-char 1) (backward-list 1))
;;      (t (self-insert-command (or arg1)))))

;;(global-set-key (kbd "s-a") 'my-match-paren)

(show-paren-mode 1)
(setq show-paren-style 'parenthesis)

;;;;;;;;;;;;;;;; Lisp ;;;;;;;;;;;;;;;;;;

;; Slime for Lisp

(add-to-list 'load-path "~/elisp/slime")
(add-to-list 'load-path "~/hacking/lisp/slime/")

(global-set-key [f12] 'slime)

(autoload 'slime "slime" t)

(eval-after-load "slime"
  '(progn
     (message "-> slime loaded")
     (setq slime-repl-history-size 1000
           slime-net-coding-system 'utf-8-unix
           inferior-lisp-program "/usr/bin/sbcl"
           slime-kill-without-query-p t
           slime-lisp-implementations `((sbcl ("sbcl") :coding-system utf-8-unix)))
     (slime-setup)
     (slime-mode)
     (global-set-key (kbd "C-c s") 'slime-indent-and-complete-symbol)))

;;;;;;;;;;;;;;;; Revision control systems ;;;;;;;;;;;;;;;;

(require 'darcsum)

(defun user-cvs-update ()
  "save and call pcl-cvs function cvs-update"
  (interactive)
  (save-buffer)
  (cvs-update "." nil)
  (message "pcl-cvs update executed!"))

(global-set-key (kbd "\C-cn") 'user-make-clean)
(global-set-key (kbd "\C-cm") 'user-save-and-make-all)
(global-set-key (kbd "\C-cu") 'user-cvs-update)

;;;;;;;;;;;;;;;; Fontlocking keywords ;;;;;;;;;;;;;;;;
;; Make special keywords colorfull, just like in vim.
;; TODO: Create more usable interface

(defvar warn-keywords '("FIXME" "WARN" "ERR" "BUG"))
(defvar notice-keywords '("TODO" "XXX"))

(make-my-face 'my-warn-face "red" "white" t)
(make-my-face 'my-notice-face "yellow" "black" t)

(dolist (mode '(c-mode c++-mode cperl-mode sh-mode
                slime-mode emacs-lisp-mode lisp-mode
                tuareg-mode haskell-mode python-mode))
  (add-fontlocked-keywords mode warn-keywords 'my-warn-face)
  (add-fontlocked-keywords mode notice-keywords 'my-notice-face))

;;;;;;;;;;;;;;;; Embedded changelog ;;;;;;;;;;;;;;;;
(autoload 'embedded-changelog-add-entry
  "~/elisp/embeddedchangelog.el" nil t)