;;;; init.el                                     -*- lexical-binding: t; -*-

;;; Packages.

(require 'package)
;;; either the stable version:

(add-to-list 'package-archives
;  ;; choose either the stable or the latest git version:
;  ;; '("melpa-stable" . "http://melpa-stable.org/packages/")
  '("melpa-unstable" . "http://melpa.org/packages/"))

(package-initialize)

;; Use the development version of Gnus
;(add-to-list 'load-path "/home/xyz/Code/gnus/lisp")
;(require 'gnus-load)
;(require 'info)
;(add-to-list 'Info-default-directory-list "/home/xyz/Code/gnus/texi/")

;; Define C-z as a new prefix key; because it usually is a standard Emacs
;; keybinding, no mode uses it, so we can have it for ourselves without fear
;; of conflicts. (Also, C-z is very convenient to type.) Suspending can still
;; be done with C-x C-z.
(define-prefix-command 'ctl-z-map)
(global-set-key (kbd "C-z") 'ctl-z-map)

;;;; Appearance and stuff.
(setq default-frame-alist
      '((foreground-color . "white")
        (background-color . "black")
        ;; It just so happens you can fit two 80-column windows side-by-side with this font size.
        (font . "-unknown-DejaVu Sans Mono-normal-normal-normal-*-14-*-*-*-m-0-iso10646-1")))

(tool-bar-mode 0)
(menu-bar-mode 0)

(setq history-length 500)
(savehist-mode 1)

;; Show clock in mode line, but only in full-screen mode.
(setq display-time-default-load-average nil)
(setq display-time-mail-file 'none)
(setq display-time-24hr-format t)

(defun toggle-frame-fullscreen* ()
  (interactive)
  (toggle-frame-fullscreen)
  (if (eq (cdr (assoc 'fullscreen (frame-parameters))) 'fullboth)
      (display-time-mode 1)
    (display-time-mode 0)))

(global-set-key (kbd "<f11>") 'toggle-frame-fullscreen*)

;; Save backup files in ~/backups instead of the file's directory.
(mkdir "~/.emacs.d/backups" t)
(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))
;; Likewise for autosave files.
(mkdir "~/.emacs.d/autosave" t)
(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/autosave/" t)))

;; More mode line stuff
(column-number-mode 1)
(size-indication-mode 1)

;; Custom file (where Emacs's Customize feature saves settings).
(load (setq custom-file "~/.emacs.d/custom.el"))

;; Enable some commands which are disabled by default.
;; (These enabling-lines get strewn in by Emacs in ~/.emacs,
;; but we move them here from time to time.)
(put 'narrow-to-region 'disabled nil)

;; Load dired-x, which makes C-x C-j available (opens dired positioned on
;; current file). This is especially useful to rename (with R) the current
;; file.
(load-library "dired-x")
;; Alas, jabber.el steals C-x C-j to itself. :-(
(define-key ctl-z-map (kbd "C-j") 'dired-jump)

;;;; Indentation, syntax highlighing, completion and the likes.

;; Indent Scheme 'if' like Emacs Lisp (I'm a barbarian like that).
(put 'if 'scheme-indent-function 2)
(put 'match 'scheme-indent-function 1)

;; Don't start Geiser automatically.
(setq geiser-mode-auto-p nil)

(defun newline-and-indent-body ()
  (interactive)
  (let ((lisp-indent-offset lisp-body-indent))
    (newline-and-indent)))

(global-set-key (kbd "M-RET") 'newline-and-indent-body)

;; Use spaces instead of tabs.
(setq-default indent-tabs-mode nil)


;; Dim parentheses in Lisp mode.
(setq paren-face-regexp "[][()]") ;; TODO: Don't match #\( et al.
;;(add-hook 'lisp-mode-hook 'elmord-lisp-mode-hook-function)
;;(add-hook 'scheme-mode-hook 'elmord-lisp-mode-hook-function)

(show-paren-mode 1)

;; AutoComplete package (I ended up not using it).
;; (add-hook 'prog-mode-hook
;; 	  (lambda ()
;; 	    (show-paren-mode)
;; 	    (when (featurep 'auto-complete)
;; 	      (auto-complete-mode)
;; 	      (ac-set-trigger-key "TAB"))))
;;
;; (setq ac-auto-start nil)
;; (setq ac-delay 0)

(setq tab-always-indent 'complete)

;;;; Compiling stuff.

(defun compile-prompt-only-once (&optional read-command-p)
  "Invoke `compile' on the current buffer. Only ask for the compilation command
the first time it is called, or if a prefix argument is used."
  (interactive "P")
  (when read-command-p
    (setq-local compilation-read-command t))
  (call-interactively 'compile)
  (setq-local compilation-read-command nil))

(global-set-key (kbd "C-z C-z") 'compile-prompt-only-once)

(setq compilation-scroll-output t)
(setq compilation-ask-about-save nil)


(global-set-key (kbd "M-p") 'previous-error)
(global-set-key (kbd "M-n") 'next-error)

(global-set-key (kbd "M-P") 'scroll-down-command)
(global-set-key (kbd "M-N") 'scroll-up-command)

;;;; Keybindings.

;; C-z C-q to save and quit.
;; TODO: I'd like to use the same prompt as save-buffers-kill-*, in
;; which SPC cancels, for example.

(require 'cl)

(defun elmord-ask-save-buffer (&optional backup-if-not-save)
  (when (buffer-modified-p)
    (case (y-or-n-p (format "Save file %s?" (buffer-file-name)))
      ((t) (save-buffer))
      ((nil) (when (and backup-if-not-save (buffer-file-name) make-backup-files)
	       (let ((backup-file (make-backup-file-name (buffer-file-name))))
		 (write-file backup-file)
		 (message "Backup saved to %s" backup-file)))))))

(defun save-buffer-kill-window ()
  "Kill current buffer and window. If the buffer is modified, prompt if the
user wants to save it. If user replies 'no' but backups are enabled for the
current file, save the modified buffer to a backup file. **OBSOLETE**"
  (interactive)
  (elmord-ask-save-buffer t)
  ;; Kill window; ignore the "sole window" error, but not other kinds of error.
  (condition-case err
      (kill-buffer-and-window)
    ((error) (unless (equal (cadr err) "Attempt to delete minibuffer or sole ordinary window")
	       (signal (car err) (cdr err))))))

(define-key ctl-z-map (kbd "C-q") 'save-buffer-kill-window)

;; Ach, I ended up never using the above. We can do better.

(defun quit-window-maybe-frame (&optional kill-p)
  "Delete current window and bury its buffer. With a prefix, kill instead of
burying. If the buffer is visiting a file (and it is modified), offer to save
it. If the buffer is to be killed, offer to save regardless of whether it is
associated with a file.

If there are Emacs clients waiting on this buffer, call `server-done' on it."
  (interactive "P")
  (if server-buffer-clients
      ;; If it's a client buffer, call `server-done' to save and quit.
      (server-done)
    ;; Otherwise, we handle the quitting ourselves.
    (when (or kill-p (buffer-file-name))
      (elmord-ask-save-buffer))
    (if kill-p (kill-buffer) (bury-buffer)))
  ;; In any case, delete the window after processing.
  (if (null (cdr (window-list))) 	; sole window?
      (delete-frame)
    (delete-window)))

(global-set-key (kbd "s-q") 'quit-window-maybe-frame)
(global-set-key (kbd "M-Q") 'quit-window-maybe-frame)

;; M-S to save, so we can save and quit quickly with M-S M-Q.
(global-set-key (kbd "M-S") 'save-buffer)

;; Kill all unmodified buffers.

(defun kill-unmodified-buffers ()
  (interactive)
  (let ((killed nil))
    (dolist (buffer (buffer-list))
      (when (not (buffer-modified-p buffer))
	(push (buffer-name buffer) killed)
	(kill-buffer buffer)))
    (message "%d buffers killed: %s" (length killed) killed)))

(global-set-key (kbd "C-x K") 'kill-unmodified-buffers)


;; Shift+Insert pastes from primary selection, like xterm.

(defun primary-selection-yank ()
  "Yanks text from the primary selection (the text you just marked with
the mouse in a graphical environment), as opposed to the clipboard (the
place things end up in when you copy with Ctrl+C)."
  (interactive)
  (let ((x-select-enable-clipboard nil)
	(x-select-enable-primary t))
    (yank)))

(global-set-key (kbd "S-<insert>") 'primary-selection-yank)
(define-key ctl-z-map (kbd "C-y") 'primary-selection-yank)


(defun kill-help-buffer ()
  (interactive)
  (kill-buffer "*Help*"))

(define-key ctl-z-map (kbd "h") 'kill-help-buffer)

(define-key ctl-z-map (kbd "p") 'previous-buffer)
(define-key ctl-z-map (kbd "n") 'next-buffer)

(global-set-key (kbd "C-.") 'complete-symbol)


(defun insert-current-date ()
  (interactive)
  (insert (format-time-string "%Y-%m-%d %H:%M %z")))

(define-key ctl-z-map (kbd "d") 'insert-current-date)

(defun visit-init-file ()
  (interactive)
  (find-file user-init-file))

(define-key ctl-z-map (kbd "e") 'visit-init-file)

;;;; ESC to quit temporary windows.

;; Keep track of which buffers are temporary.
(add-hook 'temp-buffer-window-setup-hook
	  (defun elmord/mark-buffer-as-temporary ()
	    (setq-local buffer-temporary-p t)))

(defvar temporary-buffer-names
  '("*Messages*" "*Completions*" "*Occur*")
  "Names of buffers which are always considered temporary, regardless of
the value of their value of `buffer-temporary-p'.")

(defun window-temporary-p (window)
  "Returns whether WINDOW is associated with a temporary buffer."
  (with-current-buffer (window-buffer window)
    (or (and (boundp 'buffer-temporary-p) buffer-temporary-p)
	(member (buffer-name) temporary-buffer-names))))

(defun quit-temp-windows ()
  "Quit all temporary buffers in the current frame.
 Returns t is any window was quitted, nil otherwise."
  (interactive)
  (let ((some-quitted-p nil))
    (dolist (window (window-list))
      (when (window-temporary-p window)
	(quit-window nil window)
	(setq some-quitted-p t)))
    some-quitted-p))

;; This function is no longer used.
(defun quit-window-with-buffer (name)
  "Quits a window displaying the specified buffer in the current frame.
Returns t if such a window existed, nil otherwise."
  (let ((buffer (get-buffer name)))
    (when buffer
      (let ((window (get-window-with-predicate
		     (lambda (window)
		       (eq (window-buffer window) buffer)))))
	(when window
	  (quit-window nil window)
	  t)))))

(defvar quit-temp-windows-previous-configuration nil)

(defun quit-temp-windows-or-delete-last-window (undo-p)
  "Quit all temporary buffers in the current frame. If no such buffer exists,
delete the last window in the current frame (defined by the order of
`window-list'), excluding the current one. With a prefix, undo the efect of
the last call. Returns t if a window was quitted/deleted, nil otherwise."
  (interactive "P")
  (if undo-p
      (set-window-configuration quit-temp-windows-previous-configuration)
    (let ((winconf (current-window-configuration)))
      (cond ((or (quit-temp-windows) (delete-last-window))
	     (setq quit-temp-windows-previous-configuration winconf)
	     t)
	    (t (message "No window to delete")
	       nil)))))

(defun delete-last-window ()
  "Delete the last window in the current frame (defined by the order of
`window-list', excluding the current window. Returns t if a window was
deleted, nil otherwise."
  (interactive)
  (let* ((windows (remove (get-buffer-window) (window-list)))
	 (victim (car (last windows))))
    (when victim
      (delete-window victim)
      t)))

(global-set-key (kbd "<escape>") 'quit-temp-windows-or-delete-last-window)

;; In minibuffer, ESC behaves the same as C-g.
(define-key minibuffer-local-map (kbd "<escape>") 'abort-recursive-edit)

;; C-u kills line backward (like in terminal/Vim).

(defvar backward-kill-line-preserve-indentation t
  "Whether `backward-kill-line' should avoid deleting indentation.")

(defun backward-kill-line (&optional prefix)
  "Kill from point to the beginning of the line.

If  `backward-kill-line-preserve-indentation' is non-nil, only kill up to
indentation, unless point is already at indentation, in which case kill to the
beginning of the line.

With a prefix N, just calls (`kill-line' -N)."
  (interactive "P")
  (if prefix
      (kill-line (- prefix))
    (if (bolp)
	;; What to do if we are at the beginning of line?
	;; Currently, delete the previous newline, like Vim.
	(backward-delete-char 1 t)
      (if backward-kill-line-preserve-indentation
	  (backward-kill-line-preserving-indentation)
	(kill-line 0)))))

(defun backward-kill-line-preserving-indentation ()
  (let ((indentation (save-excursion (back-to-indentation) (point))))
    (if (<= (point) indentation)
	(kill-line 0)
      (kill-region (point) indentation))))


(global-set-key "\C-u" 'backward-kill-line)

(global-set-key (kbd "C-M-<backspace>") 'backward-kill-sexp)

;; Move universal argument to C-v (daring proposition!).
(global-set-key "\C-v" 'universal-argument)
(define-key universal-argument-map "\C-u" nil)
(define-key universal-argument-map "\C-v" 'universal-argument-more)

;;;; Old stuff.
;(eval-after-load "/usr/share/emacs/23.4/lisp/emacs-lisp/cl-indent"
;  '(progn
;     (put 'if 'common-lisp-indent-function '(4 4 &body))))

;(require 'slime)
;(slime-setup '(slime-fancy slime-banner))
;
;(add-to-list 'load-path "/var/lib/chicken/7/")
;(autoload 'chicken-slime "chicken-slime" "SWANK backend for Chicken" t)
;
;(add-hook 'scheme-mode-hook
;          (lambda ()
;            (slime-mode t)))
;

;;;; Screen-like window management.

(defvar screenlike-current-screen 1)

(defun screenlike-switch-screen (&optional number)
  (interactive)
  (when (not number)
    (setq number (- last-command-event ?0)))
  (window-configuration-to-register screenlike-current-screen)
  (setq screenlike-current-screen number)
  (let ((new-config (get-register number)))
    (if (window-configuration-p (car new-config))
	(jump-to-register number)
      (delete-other-windows)
      (switch-to-buffer "*scratch*")))
  (message "Switched to screen %d" screenlike-current-screen))

(defun screenlike-next-screen (&optional count)
  (interactive)
  (let ((new-scr (+ screenlike-current-screen (or count 1))))
    (if (>= new-scr 1)
	(screenlike-switch-screen new-scr)
      (message "You're already at lowest screen"))))

(defun screenlike-previous-screen (&optional count)
  (interactive)
  (screenlike-next-screen (- (or count 1))))

(define-key ctl-z-map (kbd "1") 'screenlike-switch-screen)
(define-key ctl-z-map (kbd "2") 'screenlike-switch-screen)
(define-key ctl-z-map (kbd "3") 'screenlike-switch-screen)
(define-key ctl-z-map (kbd "4") 'screenlike-switch-screen)
(define-key ctl-z-map (kbd "5") 'screenlike-switch-screen)
(define-key ctl-z-map (kbd "6") 'screenlike-switch-screen)
(define-key ctl-z-map (kbd "7") 'screenlike-switch-screen)
(define-key ctl-z-map (kbd "8") 'screenlike-switch-screen)
(define-key ctl-z-map (kbd "9") 'screenlike-switch-screen)

(define-key ctl-z-map (kbd "C-p") 'screenlike-previous-screen)
(define-key ctl-z-map (kbd "C-n") 'screenlike-next-screen)


;;;; word-wrap-mode: like visual-line-mode, but without the visual line part.

(define-minor-mode word-wrap-mode
  "Wrap lines at word boundaries."
  nil nil nil
  (cond (word-wrap-mode ;; enable
	 (setq word-wrap t)
	 (set (make-local-variable 'fringe-indicator-alist)
	      (copy-alist fringe-indicator-alist))
	 (rplacd (assoc 'continuation fringe-indicator-alist) nil)
	 (message "Word-Wrap mode enabled"))
	(t ;;disable
	 (setq word-wrap nil)
	 (kill-local-variable 'fringe-indicator-alist)
	 (message "Word-Wrap mode disabled"))))

(add-hook 'text-mode-hook 'word-wrap-mode)

;;;; copy-indent-line: copy the indentation of the last non-blank line.

(defun huangho/remove-indentation ()
  "Remove the indentation of the current line."
  (interactive)
  (beginning-of-line)
  (when (looking-at "[ \t]")
    (let ((begpos (point)))
      (skip-chars-forward " \t")
      (delete-region begpos (point)))))

(defun huangho/copy-indent-line ()
  "Indent the current line like the last non-blank line. This function copies
the exact characters that make up the last non-blank line's indentation, i.e.,
it preserves tabs and spaces as-is."
  (interactive)
  ;; Position relative to end of buffer won't change, so we can use that
  ;; to restore point position after indenting (from `lisp-indent-line').
  (let ((oldpos (- (point-max) (point))))
    (huangho/remove-indentation)
    (let (beg end)
      ;; Find the indentation to copy.
      (save-excursion
	(when (re-search-backward "^[^\n]" nil t)
	  (setq beg (point))
	  (when (looking-at "[ \t]")
	    (skip-chars-forward " \t"))
	  (setq end (point))))
      ;; Only insert indentation if there was a previous nonblank line.
      (when beg
	(insert-buffer-substring (current-buffer) beg end))
      ;; If old position was after the indentation, restore it.
      (let ((newpos (- (point-max) oldpos)))
	(when (> newpos (point))
	  (goto-char newpos))))))

;;;;;; Indentation...
;;(load "~/unclever-indent.el")

;; Another attempt at unclever indent: changing what RET does

(defun elmord/newline-and-unclever-indent ()
  (interactive)
  (let (indent-start indent-end)
    (save-excursion
      (beginning-of-line)
      (setq indent-start (point))
      (back-to-indentation)
      (setq indent-end (point)))
    (newline)
    (insert-buffer-substring (current-buffer) indent-start indent-end)
    ;; Special case: if RET is type twice in sequence, remove the
    ;; indentation automatically added by the previous RET.
    (when (eq last-command 'elmord/newline-and-unclever-indent)
      (delete-region indent-start indent-end))
    ))

(define-minor-mode unclever-indent-mode
  ""					; docstring
  nil					; default value
  " Unclever"				; mode line message
  '(("\t" . tab-to-tab-stop)
    ("\r" . elmord/newline-and-unclever-indent)
    ("\d" . backward-delete-char))
  (cond
   (unclever-indent-mode		; enable
    (setq-local electric-indent-inhibit t)
    (message "Unclever Indent mode enabled"))
   (t					; disable
    (kill-local-variable 'electric-indent-inhibit)
    (message "Unclever Indent mode disabled"))))

(global-set-key (kbd "C-z i") 'unclever-indent-mode)



(defun elmord/real-time-indent (beg end len)
  (ignore-errors
    (save-excursion
      (backward-up-list)
      (indent-pp-sexp))))

;;(add-hook 'after-change-functions 'elmord/real-time-indent)
;;; Edit Blognir posts in html-mode.
(add-to-list 'auto-mode-alist '("/\\.data/entries/" . html-mode))

;;; Cache GPG passphrases (questionable! should use gpg-agent instead)
(setq epa-file-cache-passphrase-for-symmetric-encryption t)

;;;; Org mode.
(setq org-startup-truncated nil) ; let lines wrap by default


;;;; Argh.
(defun elmord-new-line-after ()
  (interactive)
  (end-of-line)
  (newline nil t))

(defun elmord-new-line-before ()
  (interactive)
  (previous-line)
  (elmord-new-line-after))

(global-set-key "\C-o" 'elmord-new-line-after)
(global-set-key "\M-o" 'elmord-new-line-before)


;;;; Search.

;; Automatically wrapping I-search.
;; https://stackoverflow.com/questions/285660/automatically-wrapping-i-search
;; TODO: Still not perfect: does not distinguish overwrapped I-search anymore.
(defadvice isearch-search (after isearch-no-fail activate)
  (unless isearch-success
    ;; Avoid recursive loop
    (ad-disable-advice 'isearch-search 'after 'isearch-no-fail)
    (ad-activate 'isearch-search) ;; ad-activate to reflect the above change
    ;; Repeat the search (in the appropriate direction)
    (isearch-repeat (if isearch-forward 'forward))
    ;; Restore advice
    (ad-enable-advice 'isearch-search 'after 'isearch-no-fail)
    (ad-activate 'isearch-search)))

;; The above solution has a serious problem: it creates a spurious "input event"
;; when it implicitly repeats the search. This means you have to type DEL
;; *twice* to undo the last command. I should find a proper solution to this,
;; but the fact is that I don't generally want DEL to undo last event anyway, I
;; want it to remove the last characher from the search (what C-M-w does (yeah,
;; really). So instead I'll redefine DEL (which apparently has to be called
;; <backspace> for the graphical Emacs) to remove last character from search
;; query, and the undo key to undo the last search event. This leaves the undo
;; key with the aforementioned bug, but whatever.

(define-key isearch-mode-map (kbd "<backspace>") 'isearch-del-char)
(define-key isearch-mode-map (kbd "DEL") 'isearch-del-char)
(define-key isearch-mode-map (kbd "C-/") 'isearch-delete-char)
(define-key isearch-mode-map (kbd "C-_") 'isearch-delete-char)

;;;; Changing window focus.
(global-set-key (kbd "M-S-<up>") 'windmove-up)
(global-set-key (kbd "M-S-<down>") 'windmove-down)
(global-set-key (kbd "M-S-<left>") 'windmove-left)
(global-set-key (kbd "M-S-<right>") 'windmove-right)

;; Breaks arrows in terminals :(
;;(global-set-key (kbd "M-O") 'other-window)

;; C-x b should offer the last active buffer as the default option,
;; even if it is visible in another window.
(defadvice other-buffer (around allow-visible-buffers)
  (ad-set-arg 1 t)
  ad-do-it)

(ad-activate 'other-buffer)

;;;; w3m.
(require 'w3m)
(require 'w3m-search)
(add-to-list 'w3m-search-engine-alist
	     '("startpage" "https://startpage.com/do/search" utf-8 "query=%s&cat=web"))
(setq w3m-search-default-engine "startpage")


;;;; Shell/Term-mode alternation (http://emacswiki.org/emacs/ShellMode)

(require 'shell)
(require 'term)

(defun term-switch-to-shell-mode ()
  (interactive)
  (if (equal major-mode 'term-mode)
      (progn
        (shell-mode)
        (set-process-filter  (get-buffer-process (current-buffer)) 'comint-output-filter )
	;;(local-set-key (kbd "C-j") 'term-switch-to-shell-mode)
        (compilation-shell-minor-mode 1)
        (comint-send-input)
	)
    (progn
      (compilation-shell-minor-mode -1)
      (font-lock-mode -1)
      (set-process-filter  (get-buffer-process (current-buffer)) 'term-emulate-terminal)
      (term-mode)
      (term-char-mode)
      (term-send-raw-string (kbd "C-l"))
      )))


(global-set-key (kbd "M-g M-f") 'find-file-at-point)

(defun elmord-isearch-all-buffers ()
  (interactive)
  (multi-isearch-buffers (buffer-list)))

(global-set-key (kbd "M-s M-s") 'elmord-isearch-all-buffers)

;; I want to use Emacs instead of xterm as my default terminal.
;; This function is supposed to be used in something like:
;;    emacsclient -a "" -c -e "(elmord-new-shell)"
;; The window should close after the shell finishes, like a normal terminal.
;; The buffer, however, remains available for inspection.
;; To avoid creating buffers ad infinitum, buffers for shells that have
;; already finished are reused for new shells.

(defun elmord-new-shell ()
  (interactive)
  (let ((buffer (shell (elmord-find-vacant-shell-buffer)))
	(window (get-buffer-window)))
    (set-process-sentinel
     (get-buffer-process buffer)
     (lambda (proc event)
       (let ((window (get-buffer-window buffer)))
	 (when window
	   (with-selected-window window
	     (quit-window-maybe-frame))))))))
       ;;(with-selected-window window ; requires lexical scope!
       ;; (quit-window-maybe-frame))))))

(defun elmord-find-vacant-shell-buffer ()
  "Find an existing *shell* buffer with no attached process and is thus
suitable for starting a new shell. If none is find, generate a name for a
new *shell* buffer."
  (let ((vacant-buffers
	 (remove-if-not (lambda (buf)
			  (and (string-prefix-p "*shell*" (buffer-name buf))
			       (null (get-buffer-process buf))))
			(buffer-list))))
    (if vacant-buffers
	(first vacant-buffers)
      (generate-new-buffer-name "*shell*"))))

;;;; Shell mode behaviour.

(add-hook 'shell-mode-hook
  (defun elmord/shell-mode-hook ()
    ;; By default, Emacs ignores files with some extensions when completing filenames
    ;; (mainly object files such as .o). This should not apply to shell completion.
    (make-local-variable 'completion-ignored-extensions)
    (setq completion-ignored-extensions nil)
    ;; Fix process window size when it changes.
    (add-hook 'window-configuration-change-hook 'comint-fix-window-size)
    (compilation-shell-minor-mode)))

(setq shell-dynamic-complete-functions
      (remove 'pcomplete-completions-at-point shell-dynamic-complete-functions))


;; https://stackoverflow.com/questions/7987494/emacs-shell-mode-display-is-too-wide-after-splitting-window/11255996#11255996
;; Adapted because Emacs kept calling it after the process died.
(defun comint-fix-window-size ()
  "Change process window size."
  (when (derived-mode-p 'comint-mode)
    (let ((process (get-buffer-process (current-buffer))))
      (when process
	(set-process-window-size process
				 (window-height)
				 (window-width))))))

(eval-after-load 'comint
  (defun elmord/comint-eval-after-load ()
    (define-key comint-mode-map (kbd "C-r") 'comint-history-isearch-backward-regexp)
    (define-key comint-mode-map (kbd "C-p") 'comint-previous-input)
    (define-key comint-mode-map (kbd "C-n") 'comint-next-input)
    (add-hook 'comint-output-filter-functions 'force-mode-line-update)
    ))

(require 'shell)

;; Make prompt read-only.
;; (setq comint-prompt-read-only t)
;; (add-hook 'comint-mode-hook
;; 	  (lambda ()
;; 	    (define-key comint-mode-map [remap kill-region] 'comint-kill-region)
;; 	    (define-key comint-mode-map [remap kill-whole-line]
;; 	      'comint-kill-whole-line)))

;; Another solution:
(defun elmord/comint-backspace-no-delete-prompt (n)
  (interactive "p")
  (cond ((and (> (point) 1)
              (eq (get-char-property (1- (point)) 'font-lock-face (current-buffer))
                  'comint-highlight-prompt))
         (message "Not stepping over prompt"))
        (t (delete-backward-char n))))

(define-key shell-mode-map (kbd "DEL") 'elmord/comint-backspace-no-delete-prompt)


(setq frame-title-format '("%b (" default-directory ")"))

(setenv "GIT_MAN_VIEWER" "woman")
(setenv "GIT_PAGER" "cat")
(setenv "EDITOR" "emacsclient")

(setq comint-input-ring-size 10000)
(put 'upcase-region 'disabled nil)
(put 'downcase-region 'disabled nil)


;;;

;; https://stackoverflow.com/questions/2471557/how-to-undo-fill-paragraph-in-emacs

(defun unfill-paragraph ()
  (interactive)
  (let ((fill-column (point-max)))
    (fill-paragraph nil)))

(defun unfill-region ()
  (interactive)
  (let ((fill-column (point-max)))
    (fill-region (region-beginning) (region-end) nil)))


;;(load "~/.emacs.d/llvm-mode.el")

(global-set-key (kbd "C-z C-f") 'browse-url-emacs)


(defun git-commit-and-push (message)
  (interactive "sCommit message: ")
  (save-some-buffers (not compilation-ask-about-save)
                     compilation-save-buffers-predicate)
  (shell-command (concat "git commit -a -m "
                         (shell-quote-argument message)
                         " && git push")))

(global-set-key (kbd "C-z m") 'git-commit-and-push)


(add-hook 'w3m-mode-hook
          (defun elmord/w3m-mode-hook ()
            (local-set-key (kbd "C-<prior>") 'w3m-previous-buffer)
            (local-set-key (kbd "C-<next>") 'w3m-next-buffer)))



;; ;; Allow rectangles to go past the end of line.
;; (defvar elmord/rectangle-entered-artist-mode nil)

;; (add-hook 'rectangle-mark-mode-hook
;;           (lambda ()
;;             (when (not artist-mode)
;;               (artist-mode 1)
;;               (setq-local elmord/rectangle-entered-artist-mode t))))



(define-prefix-command 'ctl-2-map)
(global-set-key (kbd "C-2") 'ctl-2-map)
(global-set-key (kbd "C-2 C-2") 'split-window-below)
(global-set-key (kbd "C-2 C-f") 'find-file-other-window)
(global-set-key (kbd "C-2 f") 'find-file-other-window)
(global-set-key (kbd "C-2 C-b") 'switch-to-buffer-other-window)
(global-set-key (kbd "C-2 b") 'switch-to-buffer-other-window)


;;; C indentation.

(defvar elmord/c-style
  '("java"  ;; Inherit from this style
    (c-offsets-alist
     (case-label . 4)
     (statement-case-intro . 4)
     (statement-case-open . 4)
     (label . 2))))

(c-add-style "elmord" elmord/c-style)
(setq c-default-style "elmord")    


;;(add-hook 'c-mode-hook 'elmord/c-mode-hook)

;;;; Persistent scratch buffer (is this a good idea?)
;; Vaguely based on http://dorophone.blogspot.com.br/2011/11/how-to-make-emacs-scratch-buffer.html

(defvar elmord/scratch-file "~/.emacs.d/scratch")

(defun elmord/load-scratch-file ()
  (with-current-buffer (get-buffer "*scratch*")
    (when (file-exists-p elmord/scratch-file)
      (delete-region (point-min) (point-max))
      (insert-file elmord/scratch-file))
    ;; Associate buffer with file, but restore original buffer name.
    (set-visited-file-name elmord/scratch-file)
    (set-buffer-modified-p nil)
    (cd "~")
    (rename-buffer "*scratch*")))

(elmord/load-scratch-file) ;; load on init

(add-hook 'emacs-lisp-mode-hook
  (defun elmord/emacs-lisp-mode-hook ()
    (local-set-key (kbd "C-c C-l") 'eval-buffer)))


(defun elmord/start-security-hub ()
  (interactive)
  (let* ((frame (make-frame)))
    (with-selected-frame frame
      (split-window-below)
      (split-window-below)
      (balance-windows)
      (let ((windows (window-list)))
        (with-selected-window (first windows)
          (elmord/run-shell-command "shell-backend" "workon backend; cd ~/securityhub/backend; python manage.py runserver 0.0.0.0:8001"))
        (with-selected-window (second windows)
          (elmord/run-shell-command "shell-idp" "workon idp; cd ~/authserver; vagrant up; cd ~/securityhub/idp; python manage.py runserver 0.0.0.0:8002"))
        (with-selected-window (third windows)
          (elmord/run-shell-command "shell-frontend" "workon frontend_global_security; cd ~/securityhub/frontend_global_security; python manage.py runserver 0.0.0.0:8000"))))))

(defun elmord/run-shell-command (bufname cmdline)
  (let ((explicit-bash-args (append explicit-bash-args (list "-c" cmdline))))
    (shell bufname)))
      
                    
    
