Simplify and add emacs config
This commit is contained in:
76
xdg/emacs/bindings.el
Normal file
76
xdg/emacs/bindings.el
Normal file
@ -0,0 +1,76 @@
|
||||
;; bindings.el
|
||||
|
||||
;; Let C-o have the same function as C-x
|
||||
(use-local-map (make-sparse-keymap))
|
||||
(global-set-key (kbd "C-o") ctl-x-map)
|
||||
(global-set-key "\C-o\C-o" 'exchange-point-and-mark)
|
||||
|
||||
;; assign another commnad to M-x, for running commands not bound to a key.
|
||||
(global-set-key "\C-ot" 'execute-extended-command)
|
||||
|
||||
;; attempt to use C-w instead of backspace
|
||||
(global-set-key "\C-w" 'backward-kill-word)
|
||||
(global-set-key "\M-W" 'kill-region)
|
||||
|
||||
;; Make C-a go to the beginning of typed lines
|
||||
(global-set-key "\C-a" 'back-to-indentation)
|
||||
(global-set-key "\M-m" 'move-beginning-of-line)
|
||||
|
||||
(global-set-key (kbd "M-#") 'comment-or-uncomment-region)
|
||||
|
||||
(global-set-key "\C-z" 'eshell)
|
||||
|
||||
;; Use expand-region.
|
||||
(global-set-key (kbd "M-SPC") 'er/expand-region)
|
||||
|
||||
;; Basic binding for hide-show mode
|
||||
|
||||
;; Bind
|
||||
(global-set-key (kbd "C-'") 'other-window)
|
||||
(global-set-key (kbd "C-,") (lambda () (interactive) (switch-to-buffer (other-buffer))))
|
||||
|
||||
;; Magit settings
|
||||
(global-set-key (kbd "M-g s") 'magit-status)
|
||||
(global-set-key (kbd "M-g M-s") 'magit-status)
|
||||
(define-key ctl-x-map (kbd "g") 'magit-status)
|
||||
(define-key ctl-x-map (kbd "C-g") 'magit-status)
|
||||
|
||||
;; Dired mode bindings
|
||||
(define-key ctl-x-map (kbd "d") (lambda () (interactive) (dired (file-name-directory (or load-file-name buffer-file-name)))))
|
||||
(define-key ctl-x-map (kbd "C-d") (lambda () (interactive) (dired (file-name-directory (or load-file-name buffer-file-name)))))
|
||||
|
||||
;; Fix minor modes overwriting my keybindings
|
||||
(add-hook 'org-mode-hook
|
||||
'(lambda ()
|
||||
(define-key org-mode-map (kbd "C-'") nil)))
|
||||
|
||||
(add-hook 'flyspell-mode-hook
|
||||
'(lambda ()
|
||||
(define-key flyspell-mode-map (kbd "C-,") nil)))
|
||||
|
||||
(add-hook 'dired-mode-hook
|
||||
'(lambda ()
|
||||
(define-key dired-mode-map (kbd "C-o") nil)))
|
||||
|
||||
(add-hook 'read-only-mode-hook
|
||||
'(lambda ()
|
||||
(local-set-key (kbd "j")
|
||||
'(lambda () (interactive) (scroll-up 3)))
|
||||
(local-set-key (kbd "k")
|
||||
'(lambda () (interactive) (scroll-down 3)))))
|
||||
|
||||
(global-set-key (kbd "C-c e") 'eval-and-replace)
|
||||
|
||||
;; projectile mode stuff
|
||||
|
||||
(add-hook 'projectile-mode-hook
|
||||
'(lambda ()
|
||||
(define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)
|
||||
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)))
|
||||
|
||||
(global-set-key [f8] 'neotree-project-dir)
|
||||
|
||||
|
||||
(add-hook 'lsp-mode-hook
|
||||
'(lambda ()
|
||||
(define-key lsp-mode-map (kbd "C-c C-l") 'lsp-command-map)))
|
||||
26
xdg/emacs/custom.el
Normal file
26
xdg/emacs/custom.el
Normal file
@ -0,0 +1,26 @@
|
||||
(custom-set-variables
|
||||
;; custom-set-variables was added by Custom.
|
||||
;; If you edit it by hand, you could mess it up, so be careful.
|
||||
;; Your init file should contain only one such instance.
|
||||
;; If there is more than one, they won't work right.
|
||||
'(custom-safe-themes
|
||||
'("70936e3b54ca6d668354fdc87eea5f0a5129380c0c459598be943efba6ae1563" default))
|
||||
'(emdroid-activity-creator "ActivityCreator.py")
|
||||
'(emdroid-tools-dir "/opt/android-sdk")
|
||||
'(inhibit-startup-screen t)
|
||||
'(mpc-host "pi.triazo.net")
|
||||
'(package-selected-packages
|
||||
'(nginx-mode lsp-treemacs magit-gh-pulls lsp-mode tree-sitter-langs tree-sitter neotree projectile-ripgrep discover markdown-mode rust-mode dart-mode shader-mode csharp-mode glsl-mode sclang-extensions ag elixir-mode yaml-mode emacs-xkcd terraform-mode magit pushbullet php-mode expand-region auto-highlight-symbol auto-complete dart-mode shader-mode glsl-mode rust-mode elixir-mode))
|
||||
'(split-height-threshold 160)
|
||||
'(tramp-syntax 'default nil (tramp))
|
||||
'(vc-follow-symlinks t))
|
||||
;(custom-set-faces
|
||||
;; custom-set-faces was added by Custom.
|
||||
;; If you edit it by hand, you could mess it up, so be careful.
|
||||
;; Your init file should contain only one such instance.
|
||||
;; If there is more than one, they won't work right.
|
||||
;;'(default ((t (:inherit nil :stipple nil :inverse-video nil :box nil :strike-through nil :overline nil :underline nil :slant normal :weight normal :height 130 :width normal :foundry "unknown" :family "Roboto Mono"))))
|
||||
;;'(fringe ((t (:background "#000000" :foreground "#0074FF"))))
|
||||
;;'(mode-line ((t (:box nil :overline "#FF213B"))))
|
||||
;;'(mode-line-inactive ((t (:overline "#0074FF"))))
|
||||
;;'(scroll-bar ((t (:background "#000000" :foreground "#0074FF" :height 1.0 :width condensed)))))
|
||||
153
xdg/emacs/init.el
Normal file
153
xdg/emacs/init.el
Normal file
@ -0,0 +1,153 @@
|
||||
;; pager emacs config, currently updated April 4, 2023
|
||||
|
||||
;; Packages now handled in `.emacs.d/packages.el`
|
||||
(setq package-enable-at-startup nil)
|
||||
(load "~/.config/emacs/packages.el")
|
||||
|
||||
|
||||
;; Misc Requires and imports
|
||||
(add-to-list 'load-path "~/.config/emacs/lisp/")
|
||||
(load "~/.config/emacs/lisp/functions.el")
|
||||
(add-to-list 'load-path "~/.config/emacs/modes")
|
||||
|
||||
(require 'expand-region)
|
||||
(require 'ido)
|
||||
(require 'go-mode-load)
|
||||
(require 'popup)
|
||||
(require 'auto-complete)
|
||||
(require 'auto-highlight-symbol)
|
||||
|
||||
|
||||
;; Custom styles and general bindings
|
||||
(setq custom-file "~/.config/emacs/custom.el")
|
||||
(load custom-file)
|
||||
(load "~/.config/emacs/bindings.el")
|
||||
(load "~/.config/emacs/modes/mode-hooks.el")
|
||||
|
||||
;; Change settings for backup files.
|
||||
;; Set the directory to store them in to ~/.emacs.d/autosaves.
|
||||
(if (eq (user-uid) 0)
|
||||
;; Root Backups do not go in the shared emacs config file.
|
||||
(setq backup-directory-alist `(("." . "~/.emacs-autosaves")))
|
||||
;; Else
|
||||
(setq backup-directory-alist `(("." . "~/.config/emacs/autosaves"))))
|
||||
|
||||
;; Backup things by copying them.
|
||||
(setq backup-by-copying t)
|
||||
;; Set some other backup related options.
|
||||
(setq delete-old-versions t
|
||||
kept-new-versions 6
|
||||
kept-old-versions 2
|
||||
version-control t)
|
||||
|
||||
;; Set syntax hilighting to maximum. Does this do anything?
|
||||
(setq font-lock-maximum-decoration t)
|
||||
|
||||
;; Start ido mode
|
||||
(ido-mode t)
|
||||
|
||||
;; Set tetris score file
|
||||
;; (setq tetris-score-file "~/.emacs.d/tetris-scores")
|
||||
|
||||
;; Automatically added when running functions
|
||||
(put 'upcase-region 'disabled nil)
|
||||
(put 'scroll-left 'disabled nil)
|
||||
|
||||
(setq auto-fill-mode 1)
|
||||
|
||||
;; Fix the scratch buffer, make it more useful
|
||||
(setq initial-major-mode 'text-mode)
|
||||
(setq initial-scratch-message (concat "-- " (format-time-string "%A %B %d %H:%M:%S") " --\n\n"))
|
||||
|
||||
(add-hook 'c++-mode-hook 'my:ac-c-headers-init)
|
||||
(add-hook 'c-mode-hook 'my:ac-c-headers-init)
|
||||
;;(add-hook 'org-mode (lambda () (load "~/.emacs.d/org-mode.el") t)
|
||||
|
||||
;; Indentation settings
|
||||
(setq indent-tabs-mode nil)
|
||||
(global-visual-line-mode t)
|
||||
|
||||
;; For Scheme programming
|
||||
(setq scheme-program-name "tinyscheme")
|
||||
|
||||
;; Slime mode setup
|
||||
(add-hook 'slime-mode-hook (lambda () (interactive)
|
||||
(local-set-key (kbd "C-o C-e") 'slime-eval-last-expression)))
|
||||
|
||||
;; basic programming settings
|
||||
(add-hook 'prog-mode-hook
|
||||
(lambda()
|
||||
(global-display-line-numbers-mode)
|
||||
;; (copilot-mode)
|
||||
(hs-minor-mode)))
|
||||
|
||||
(add-hook 'hs-minor-mode-hook
|
||||
(lambda()
|
||||
(local-set-key (kbd "C-c SPC") 'hs-toggle-hiding)))
|
||||
|
||||
;; copilot mode bindings
|
||||
;; (add-hook 'copilot-mode-hook
|
||||
;; (lambda()
|
||||
;; (define-key copilot-mode-map (kbd "C-<tab>") 'copilot-accept-completion)
|
||||
;; (define-key copilot-mode-map (kbd "M-<right>") 'copilot-next-completion)
|
||||
;; (define-key copilot-mode-map (kbd "M-<left>") 'copilot-previous-completion)
|
||||
;; (define-key copilot-mode-map (kbd "M-<down>") 'copilot-accept-completion-by-line)))
|
||||
|
||||
(put 'set-goal-column 'disabled nil)
|
||||
|
||||
;; Auto-hilight symbol mode
|
||||
(global-auto-highlight-symbol-mode t)
|
||||
|
||||
;; For js-beutify.el
|
||||
(eval-after-load 'js2-mode
|
||||
'(define-key js2-mode-map (kbd "C-c b") 'web-beautify-js))
|
||||
(eval-after-load 'json-mode
|
||||
'(define-key json-mode-map (kbd "C-c b") 'web-beautify-js))
|
||||
(eval-after-load 'sgml-mode
|
||||
'(define-key html-mode-map (kbd "C-c b") 'web-beautify-html))
|
||||
(eval-after-load 'css-mode
|
||||
'(define-key css-mode-map (kbd "C-c b") 'web-beautify-css))
|
||||
|
||||
;; Delete trailing whitespace on save
|
||||
(add-hook 'before-save-hook 'delete-trailing-whitespace)
|
||||
|
||||
;; For magit setup
|
||||
(setq magit-last-seen-setup-instructions "1.4.0")
|
||||
|
||||
;; Trunkating lines
|
||||
(setq truncate-partial-width-windows nil)
|
||||
(set-default 'truncate-lines t)
|
||||
(put 'narrow-to-region 'disabled nil)
|
||||
|
||||
;; Create a buffer-local hook to run elixir-format on save, only when we enable elixir-mode.
|
||||
(add-hook 'elixir-mode-hook
|
||||
(lambda ()
|
||||
(add-hook 'before-save-hook 'elixir-format nil t)))
|
||||
|
||||
;; Finally some python stuff
|
||||
(add-hook 'python-mode-hook
|
||||
(lambda ()
|
||||
(python-black-on-save-mode)))
|
||||
;; (load-theme 'FairyFloss t)
|
||||
|
||||
|
||||
;; (load "~/.emacs.d/fairyfloss-theme.el")
|
||||
|
||||
;; Ported from previous styles.el
|
||||
(setq inhibit-startup-screen t)
|
||||
|
||||
(menu-bar-mode 0)
|
||||
(if (display-graphic-p)
|
||||
(progn
|
||||
(load-theme 'dracula t)
|
||||
(tool-bar-mode 0)
|
||||
(setq default-frame-alist '((right-fringe . 0)
|
||||
(left-fringe . 0)
|
||||
;; scroll-bar-width only applies
|
||||
;; when not using toolkit scrollbars
|
||||
(scroll-bar-width . 10))
|
||||
)
|
||||
(custom-set-faces
|
||||
'(scroll-bar ((t (:background "#4d405b" :foreground "#cf87de" :height 1.0 :width condensed))))
|
||||
'(default ((t (:inherit nil :stipple nil :inverse-video nil :box nil :strike-through nil :overline nil :underline nil :slant normal :weight normal :height 130 :width normal :foundry "unknown" :family "Roboto Mono")))))
|
||||
))
|
||||
69
xdg/emacs/lisp/functions.el
Normal file
69
xdg/emacs/lisp/functions.el
Normal file
@ -0,0 +1,69 @@
|
||||
(defun fullpath-relative-to-current-file (file-relative-path)
|
||||
"Returns the full path of FILE-RELATIVE-PATH, relative to file location where this function is called."
|
||||
(concat (file-name-directory (or load-file-name buffer-file-name)) file-relative-path))
|
||||
|
||||
(defun bury-compile-buffer-if-successful (buffer string)
|
||||
"Bury a compilation buffer if succeeded without warnings "
|
||||
(if (and
|
||||
(string-match "compilation" (buffer-name buffer))
|
||||
(string-match "finished" string)
|
||||
(not
|
||||
(with-current-buffer buffer
|
||||
(search-forward "warning" nil t))))
|
||||
(run-with-timer 1 nil
|
||||
(lambda (buf)
|
||||
(bury-buffer buf)
|
||||
(switch-to-prev-buffer (get-buffer-window buf) 'kill))
|
||||
buffer)))
|
||||
|
||||
(defun iwb ()
|
||||
"indent whole buffer"
|
||||
(interactive)
|
||||
(delete-trailing-whitespace)
|
||||
(indent-region (point-min) (point-max) nil)
|
||||
(untabify (point-min) (point-max)))
|
||||
|
||||
|
||||
(defun what-face (pos)
|
||||
(interactive "d")
|
||||
(let ((face (or (get-char-property (point) 'read-face-name)
|
||||
(get-char-property (point) 'face))))
|
||||
(if face (message "Face: %s" face) (message "No face at %d" pos))))
|
||||
|
||||
|
||||
(defun big-font () (interactive)
|
||||
(set-face-attribute 'default (selected-frame) :family "inconsolata" :height 300) )
|
||||
|
||||
(defun normal-font () (interactive)
|
||||
(set-face-attribute 'default (selected-frame) :family "lime" :height 75))
|
||||
|
||||
|
||||
(define-derived-mode chat-mode text-mode "chat"
|
||||
"Major mode for screen chatting with people"
|
||||
(interactive)
|
||||
(buffer-face-set :family "inconsolata" :height 300)
|
||||
(set 'fill-column 70))
|
||||
|
||||
(defun eval-and-replace ()
|
||||
"Replace the preceding sexp with its value."
|
||||
(interactive)
|
||||
(backward-kill-sexp)
|
||||
(condition-case nil
|
||||
(prin1 (eval (read (current-kill 0)))
|
||||
(current-buffer))
|
||||
(error (message "Invalid expression")
|
||||
(insert (current-kill 0)))))
|
||||
|
||||
;; projectile mode tree thing?
|
||||
(defun neotree-project-dir ()
|
||||
"Open NeoTree using the git root."
|
||||
(interactive)
|
||||
(let ((project-dir (projectile-project-root))
|
||||
(file-name (buffer-file-name)))
|
||||
(neotree-toggle)
|
||||
(if project-dir
|
||||
(if (neo-global--window-exists-p)
|
||||
(progn
|
||||
(neotree-dir project-dir)
|
||||
(neotree-find file-name)))
|
||||
(message "Could not find git project root."))))
|
||||
96
xdg/emacs/lisp/go-mode-load.el
Normal file
96
xdg/emacs/lisp/go-mode-load.el
Normal file
@ -0,0 +1,96 @@
|
||||
;;; go-mode-load.el --- automatically extracted autoloads
|
||||
;;; Commentary:
|
||||
|
||||
;; To install go-mode, add the following lines to your .emacs file:
|
||||
;; (add-to-list 'load-path "PATH CONTAINING go-mode-load.el" t)
|
||||
;; (require 'go-mode-load)
|
||||
;;
|
||||
;; After this, go-mode will be used for files ending in '.go'.
|
||||
;;
|
||||
;; To compile go-mode from the command line, run the following
|
||||
;; emacs -batch -f batch-byte-compile go-mode.el
|
||||
;;
|
||||
;; See go-mode.el for documentation.
|
||||
;;
|
||||
;; To update this file, evaluate the following form
|
||||
;; (let ((generated-autoload-file buffer-file-name)) (update-file-autoloads "go-mode.el"))
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
||||
;;;### (autoloads (go-download-play godoc gofmt-before-save go-mode)
|
||||
;;;;;; "go-mode" "go-mode.el" (20767 50749))
|
||||
;;; Generated autoloads from go-mode.el
|
||||
|
||||
(autoload 'go-mode "go-mode" "\
|
||||
Major mode for editing Go source text.
|
||||
|
||||
This mode provides (not just) basic editing capabilities for
|
||||
working with Go code. It offers almost complete syntax
|
||||
highlighting, indentation that is almost identical to gofmt,
|
||||
proper parsing of the buffer content to allow features such as
|
||||
navigation by function, manipulation of comments or detection of
|
||||
strings.
|
||||
|
||||
Additionally to these core features, it offers various features to
|
||||
help with writing Go code. You can directly run buffer content
|
||||
through gofmt, read godoc documentation from within Emacs, modify
|
||||
and clean up the list of package imports or interact with the
|
||||
Playground (uploading and downloading pastes).
|
||||
|
||||
The following extra functions are defined:
|
||||
|
||||
- `gofmt'
|
||||
- `godoc'
|
||||
- `go-import-add'
|
||||
- `go-remove-unused-imports'
|
||||
- `go-goto-imports'
|
||||
- `go-play-buffer' and `go-play-region'
|
||||
- `go-download-play'
|
||||
|
||||
If you want to automatically run `gofmt' before saving a file,
|
||||
add the following hook to your emacs configuration:
|
||||
|
||||
\(add-hook 'before-save-hook 'gofmt-before-save)
|
||||
|
||||
If you're looking for even more integration with Go, namely
|
||||
on-the-fly syntax checking, auto-completion and snippets, it is
|
||||
recommended to look at goflymake
|
||||
\(https://github.com/dougm/goflymake), gocode
|
||||
\(https://github.com/nsf/gocode) and yasnippet-go
|
||||
\(https://github.com/dominikh/yasnippet-go)
|
||||
|
||||
\(fn)" t nil)
|
||||
|
||||
(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode))
|
||||
|
||||
(autoload 'gofmt-before-save "go-mode" "\
|
||||
Add this to .emacs to run gofmt on the current buffer when saving:
|
||||
(add-hook 'before-save-hook 'gofmt-before-save).
|
||||
|
||||
Note that this will cause go-mode to get loaded the first time
|
||||
you save any file, kind of defeating the point of autoloading.
|
||||
|
||||
\(fn)" t nil)
|
||||
|
||||
(autoload 'godoc "go-mode" "\
|
||||
Show go documentation for a query, much like M-x man.
|
||||
|
||||
\(fn QUERY)" t nil)
|
||||
|
||||
(autoload 'go-download-play "go-mode" "\
|
||||
Downloads a paste from the playground and inserts it in a Go
|
||||
buffer. Tries to look for a URL at point.
|
||||
|
||||
\(fn URL)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
(provide 'go-mode-load)
|
||||
;; Local Variables:
|
||||
;; version-control: never
|
||||
;; no-byte-compile: t
|
||||
;; no-update-autoloads: t
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; go-mode-load.el ends here
|
||||
911
xdg/emacs/lisp/go-mode.el
Normal file
911
xdg/emacs/lisp/go-mode.el
Normal file
@ -0,0 +1,911 @@
|
||||
;;; go-mode.el --- Major mode for the Go programming language
|
||||
|
||||
;; Copyright 2013 The Go Authors. All rights reserved.
|
||||
;; Use of this source code is governed by a BSD-style
|
||||
;; license that can be found in the LICENSE file.
|
||||
|
||||
(require 'cl)
|
||||
(require 'ffap)
|
||||
(require 'url)
|
||||
|
||||
;; XEmacs compatibility guidelines
|
||||
;; - Minimum required version of XEmacs: 21.5.32
|
||||
;; - Feature that cannot be backported: POSIX character classes in
|
||||
;; regular expressions
|
||||
;; - Functions that could be backported but won't because 21.5.32
|
||||
;; covers them: plenty.
|
||||
;; - Features that are still partly broken:
|
||||
;; - godef will not work correctly if multibyte characters are
|
||||
;; being used
|
||||
;; - Fontification will not handle unicode correctly
|
||||
;;
|
||||
;; - Do not use \_< and \_> regexp delimiters directly; use
|
||||
;; go--regexp-enclose-in-symbol
|
||||
;;
|
||||
;; - The character `_` must not be a symbol constituent but a
|
||||
;; character constituent
|
||||
;;
|
||||
;; - Do not use process-lines
|
||||
;;
|
||||
;; - Use go--old-completion-list-style when using a plain list as the
|
||||
;; collection for completing-read
|
||||
;;
|
||||
;; - Use go--kill-whole-line instead of kill-whole-line (called
|
||||
;; kill-entire-line in XEmacs)
|
||||
;;
|
||||
;; - Use go--position-bytes instead of position-bytes
|
||||
(defmacro go--xemacs-p ()
|
||||
`(featurep 'xemacs))
|
||||
|
||||
(defalias 'go--kill-whole-line
|
||||
(if (fboundp 'kill-whole-line)
|
||||
'kill-whole-line
|
||||
'kill-entire-line))
|
||||
|
||||
;; XEmacs unfortunately does not offer position-bytes. We can fall
|
||||
;; back to just using (point), but it will be incorrect as soon as
|
||||
;; multibyte characters are being used.
|
||||
(if (fboundp 'position-bytes)
|
||||
(defalias 'go--position-bytes 'position-bytes)
|
||||
(defun go--position-bytes (point) point))
|
||||
|
||||
(defun go--old-completion-list-style (list)
|
||||
(mapcar (lambda (x) (cons x nil)) list))
|
||||
|
||||
;; GNU Emacs 24 has prog-mode, older GNU Emacs and XEmacs do not.
|
||||
;; Ideally we'd use defalias instead, but that breaks in XEmacs.
|
||||
;;
|
||||
;; TODO: If XEmacs decides to add prog-mode, change this to use
|
||||
;; defalias to alias prog-mode or fundamental-mode to go--prog-mode
|
||||
;; and use that in define-derived-mode.
|
||||
(if (not (fboundp 'prog-mode))
|
||||
(define-derived-mode prog-mode fundamental-mode "" ""))
|
||||
|
||||
(defun go--regexp-enclose-in-symbol (s)
|
||||
;; XEmacs does not support \_<, GNU Emacs does. In GNU Emacs we make
|
||||
;; extensive use of \_< to support unicode in identifiers. Until we
|
||||
;; come up with a better solution for XEmacs, this solution will
|
||||
;; break fontification in XEmacs for identifiers such as "typeµ".
|
||||
;; XEmacs will consider "type" a keyword, GNU Emacs won't.
|
||||
|
||||
(if (go--xemacs-p)
|
||||
(concat "\\<" s "\\>")
|
||||
(concat "\\_<" s "\\_>")))
|
||||
|
||||
(defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]")
|
||||
(defconst go-identifier-regexp "[[:word:][:multibyte:]]+")
|
||||
(defconst go-label-regexp go-identifier-regexp)
|
||||
(defconst go-type-regexp "[[:word:][:multibyte:]*]+")
|
||||
(defconst go-func-regexp (concat (go--regexp-enclose-in-symbol "func") "\\s *\\(" go-identifier-regexp "\\)"))
|
||||
(defconst go-func-meth-regexp (concat (go--regexp-enclose-in-symbol "func") "\\s *\\(?:(\\s *" go-identifier-regexp "\\s +" go-type-regexp "\\s *)\\s *\\)?\\(" go-identifier-regexp "\\)("))
|
||||
(defconst go-builtins
|
||||
'("append" "cap" "close" "complex" "copy"
|
||||
"delete" "imag" "len" "make" "new"
|
||||
"panic" "print" "println" "real" "recover")
|
||||
"All built-in functions in the Go language. Used for font locking.")
|
||||
|
||||
(defconst go-mode-keywords
|
||||
'("break" "default" "func" "interface" "select"
|
||||
"case" "defer" "go" "map" "struct"
|
||||
"chan" "else" "goto" "package" "switch"
|
||||
"const" "fallthrough" "if" "range" "type"
|
||||
"continue" "for" "import" "return" "var")
|
||||
"All keywords in the Go language. Used for font locking.")
|
||||
|
||||
(defconst go-constants '("nil" "true" "false" "iota"))
|
||||
(defconst go-type-name-regexp (concat "\\(?:[*(]\\)*\\(?:" go-identifier-regexp "\\.\\)?\\(" go-identifier-regexp "\\)"))
|
||||
|
||||
(defvar go-dangling-cache)
|
||||
(defvar go-godoc-history nil)
|
||||
|
||||
(defgroup go nil
|
||||
"Major mode for editing Go code"
|
||||
:group 'languages)
|
||||
|
||||
(defcustom go-fontify-function-calls t
|
||||
"Fontify function and method calls if this is non-nil."
|
||||
:type 'boolean
|
||||
:group 'go)
|
||||
|
||||
(defvar go-mode-syntax-table
|
||||
(let ((st (make-syntax-table)))
|
||||
(modify-syntax-entry ?+ "." st)
|
||||
(modify-syntax-entry ?- "." st)
|
||||
(modify-syntax-entry ?% "." st)
|
||||
(modify-syntax-entry ?& "." st)
|
||||
(modify-syntax-entry ?| "." st)
|
||||
(modify-syntax-entry ?^ "." st)
|
||||
(modify-syntax-entry ?! "." st)
|
||||
(modify-syntax-entry ?= "." st)
|
||||
(modify-syntax-entry ?< "." st)
|
||||
(modify-syntax-entry ?> "." st)
|
||||
(modify-syntax-entry ?/ (if (go--xemacs-p) ". 1456" ". 124b") st)
|
||||
(modify-syntax-entry ?* ". 23" st)
|
||||
(modify-syntax-entry ?\n "> b" st)
|
||||
(modify-syntax-entry ?\" "\"" st)
|
||||
(modify-syntax-entry ?\' "\"" st)
|
||||
(modify-syntax-entry ?` "\"" st)
|
||||
(modify-syntax-entry ?\\ "\\" st)
|
||||
;; It would be nicer to have _ as a symbol constituent, but that
|
||||
;; would trip up XEmacs, which does not support the \_< anchor
|
||||
(modify-syntax-entry ?_ "w" st)
|
||||
|
||||
st)
|
||||
"Syntax table for Go mode.")
|
||||
|
||||
(defun go--build-font-lock-keywords ()
|
||||
;; we cannot use 'symbols in regexp-opt because emacs <24 doesn't
|
||||
;; understand that
|
||||
(append
|
||||
`((,(go--regexp-enclose-in-symbol (regexp-opt go-mode-keywords t)) . font-lock-keyword-face)
|
||||
(,(go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) . font-lock-builtin-face)
|
||||
(,(go--regexp-enclose-in-symbol (regexp-opt go-constants t)) . font-lock-constant-face)
|
||||
(,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name
|
||||
|
||||
(if go-fontify-function-calls
|
||||
`((,(concat "\\(" go-identifier-regexp "\\)[[:space:]]*(") 1 font-lock-function-name-face) ;; function call/method name
|
||||
(,(concat "(\\(" go-identifier-regexp "\\))[[:space:]]*(") 1 font-lock-function-name-face)) ;; bracketed function call
|
||||
`((,go-func-meth-regexp 1 font-lock-function-name-face))) ;; method name
|
||||
|
||||
`(
|
||||
(,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]*\\([^[:space:]]+\\)") 1 font-lock-type-face) ;; types
|
||||
(,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]*" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types
|
||||
(,(concat "[^[:word:][:multibyte:]]\\[\\([[:digit:]]+\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 2 font-lock-type-face) ;; Arrays/slices
|
||||
(,(concat "\\(" go-identifier-regexp "\\)" "{") 1 font-lock-type-face)
|
||||
(,(concat (go--regexp-enclose-in-symbol "map") "\\[[^]]+\\]" go-type-name-regexp) 1 font-lock-type-face) ;; map value type
|
||||
(,(concat (go--regexp-enclose-in-symbol "map") "\\[" go-type-name-regexp) 1 font-lock-type-face) ;; map key type
|
||||
(,(concat (go--regexp-enclose-in-symbol "chan") "[[:space:]]*\\(?:<-\\)?" go-type-name-regexp) 1 font-lock-type-face) ;; channel type
|
||||
(,(concat (go--regexp-enclose-in-symbol "\\(?:new\\|make\\)") "\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) ;; new/make type
|
||||
;; TODO do we actually need this one or isn't it just a function call?
|
||||
(,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face) ;; Type conversion
|
||||
(,(concat (go--regexp-enclose-in-symbol "func") "[[:space:]]+(" go-identifier-regexp "[[:space:]]+" go-type-name-regexp ")") 1 font-lock-type-face) ;; Method receiver
|
||||
;; Like the original go-mode this also marks compound literal
|
||||
;; fields. There, it was marked as to fix, but I grew quite
|
||||
;; accustomed to it, so it'll stay for now.
|
||||
(,(concat "^[[:space:]]*\\(" go-label-regexp "\\)[[:space:]]*:\\(\\S.\\|$\\)") 1 font-lock-constant-face) ;; Labels and compound literal fields
|
||||
(,(concat (go--regexp-enclose-in-symbol "\\(goto\\|break\\|continue\\)") "[[:space:]]*\\(" go-label-regexp "\\)") 2 font-lock-constant-face)))) ;; labels in goto/break/continue
|
||||
|
||||
(defvar go-mode-map
|
||||
(let ((m (make-sparse-keymap)))
|
||||
(define-key m "}" 'go-mode-insert-and-indent)
|
||||
(define-key m ")" 'go-mode-insert-and-indent)
|
||||
(define-key m "," 'go-mode-insert-and-indent)
|
||||
(define-key m ":" 'go-mode-insert-and-indent)
|
||||
(define-key m "=" 'go-mode-insert-and-indent)
|
||||
(define-key m (kbd "C-c C-a") 'go-import-add)
|
||||
(define-key m (kbd "C-c C-j") 'godef-jump)
|
||||
(define-key m (kbd "C-c C-d") 'godef-describe)
|
||||
m)
|
||||
"Keymap used by Go mode to implement electric keys.")
|
||||
|
||||
(defun go-mode-insert-and-indent (key)
|
||||
"Invoke the global binding of KEY, then reindent the line."
|
||||
|
||||
(interactive (list (this-command-keys)))
|
||||
(call-interactively (lookup-key (current-global-map) key))
|
||||
(indent-according-to-mode))
|
||||
|
||||
(defmacro go-paren-level ()
|
||||
`(car (syntax-ppss)))
|
||||
|
||||
(defmacro go-in-string-or-comment-p ()
|
||||
`(nth 8 (syntax-ppss)))
|
||||
|
||||
(defmacro go-in-string-p ()
|
||||
`(nth 3 (syntax-ppss)))
|
||||
|
||||
(defmacro go-in-comment-p ()
|
||||
`(nth 4 (syntax-ppss)))
|
||||
|
||||
(defmacro go-goto-beginning-of-string-or-comment ()
|
||||
`(goto-char (nth 8 (syntax-ppss))))
|
||||
|
||||
(defun go--backward-irrelevant (&optional stop-at-string)
|
||||
"Skips backwards over any characters that are irrelevant for
|
||||
indentation and related tasks.
|
||||
|
||||
It skips over whitespace, comments, cases and labels and, if
|
||||
STOP-AT-STRING is not true, over strings."
|
||||
|
||||
(let (pos (start-pos (point)))
|
||||
(skip-chars-backward "\n\s\t")
|
||||
(if (and (save-excursion (beginning-of-line) (go-in-string-p)) (looking-back "`") (not stop-at-string))
|
||||
(backward-char))
|
||||
(if (and (go-in-string-p) (not stop-at-string))
|
||||
(go-goto-beginning-of-string-or-comment))
|
||||
(if (looking-back "\\*/")
|
||||
(backward-char))
|
||||
(if (go-in-comment-p)
|
||||
(go-goto-beginning-of-string-or-comment))
|
||||
(setq pos (point))
|
||||
(beginning-of-line)
|
||||
(if (or (looking-at (concat "^" go-label-regexp ":")) (looking-at "^[[:space:]]*\\(case .+\\|default\\):"))
|
||||
(end-of-line 0)
|
||||
(goto-char pos))
|
||||
(if (/= start-pos (point))
|
||||
(go--backward-irrelevant stop-at-string))
|
||||
(/= start-pos (point))))
|
||||
|
||||
(defun go--buffer-narrowed-p ()
|
||||
"Return non-nil if the current buffer is narrowed."
|
||||
(/= (buffer-size)
|
||||
(- (point-max)
|
||||
(point-min))))
|
||||
|
||||
(defun go-previous-line-has-dangling-op-p ()
|
||||
"Returns non-nil if the current line is a continuation line."
|
||||
(let* ((cur-line (line-number-at-pos))
|
||||
(val (gethash cur-line go-dangling-cache 'nope)))
|
||||
(if (or (go--buffer-narrowed-p) (equal val 'nope))
|
||||
(save-excursion
|
||||
(beginning-of-line)
|
||||
(go--backward-irrelevant t)
|
||||
(setq val (looking-back go-dangling-operators-regexp))
|
||||
(if (not (go--buffer-narrowed-p))
|
||||
(puthash cur-line val go-dangling-cache))))
|
||||
val))
|
||||
|
||||
(defun go--at-function-definition ()
|
||||
"Return non-nil if point is on the opening curly brace of a
|
||||
function definition.
|
||||
|
||||
We do this by first calling (beginning-of-defun), which will take
|
||||
us to the start of *some* function. We then look for the opening
|
||||
curly brace of that function and compare its position against the
|
||||
curly brace we are checking. If they match, we return non-nil."
|
||||
(if (= (char-after) ?\{)
|
||||
(save-excursion
|
||||
(let ((old-point (point))
|
||||
start-nesting)
|
||||
(beginning-of-defun)
|
||||
(when (looking-at "func ")
|
||||
(setq start-nesting (go-paren-level))
|
||||
(skip-chars-forward "^{")
|
||||
(while (> (go-paren-level) start-nesting)
|
||||
(forward-char)
|
||||
(skip-chars-forward "^{") 0)
|
||||
(if (and (= (go-paren-level) start-nesting) (= old-point (point)))
|
||||
t))))))
|
||||
|
||||
(defun go-goto-opening-parenthesis (&optional char)
|
||||
(let ((start-nesting (go-paren-level)))
|
||||
(while (and (not (bobp))
|
||||
(>= (go-paren-level) start-nesting))
|
||||
(if (zerop (skip-chars-backward
|
||||
(if char
|
||||
(case char (?\] "^[") (?\} "^{") (?\) "^("))
|
||||
"^[{(")))
|
||||
(if (go-in-string-or-comment-p)
|
||||
(go-goto-beginning-of-string-or-comment)
|
||||
(backward-char))))))
|
||||
|
||||
(defun go--indentation-for-opening-parenthesis ()
|
||||
"Return the semantic indentation for the current opening parenthesis.
|
||||
|
||||
If point is on an opening curly brace and said curly brace
|
||||
belongs to a function declaration, the indentation of the func
|
||||
keyword will be returned. Otherwise the indentation of the
|
||||
current line will be returned."
|
||||
(save-excursion
|
||||
(if (go--at-function-definition)
|
||||
(progn
|
||||
(beginning-of-defun)
|
||||
(current-indentation))
|
||||
(current-indentation))))
|
||||
|
||||
(defun go-indentation-at-point ()
|
||||
(save-excursion
|
||||
(let (start-nesting (outindent 0))
|
||||
(back-to-indentation)
|
||||
(setq start-nesting (go-paren-level))
|
||||
|
||||
(cond
|
||||
((go-in-string-p)
|
||||
(current-indentation))
|
||||
((looking-at "[])}]")
|
||||
(go-goto-opening-parenthesis (char-after))
|
||||
(if (go-previous-line-has-dangling-op-p)
|
||||
(- (current-indentation) tab-width)
|
||||
(go--indentation-for-opening-parenthesis)))
|
||||
((progn (go--backward-irrelevant t) (looking-back go-dangling-operators-regexp))
|
||||
;; only one nesting for all dangling operators in one operation
|
||||
(if (go-previous-line-has-dangling-op-p)
|
||||
(current-indentation)
|
||||
(+ (current-indentation) tab-width)))
|
||||
((zerop (go-paren-level))
|
||||
0)
|
||||
((progn (go-goto-opening-parenthesis) (< (go-paren-level) start-nesting))
|
||||
(if (go-previous-line-has-dangling-op-p)
|
||||
(current-indentation)
|
||||
(+ (go--indentation-for-opening-parenthesis) tab-width)))
|
||||
(t
|
||||
(current-indentation))))))
|
||||
|
||||
(defun go-mode-indent-line ()
|
||||
(interactive)
|
||||
(let (indent
|
||||
shift-amt
|
||||
end
|
||||
(pos (- (point-max) (point)))
|
||||
(point (point))
|
||||
(beg (line-beginning-position)))
|
||||
(back-to-indentation)
|
||||
(if (go-in-string-or-comment-p)
|
||||
(goto-char point)
|
||||
(setq indent (go-indentation-at-point))
|
||||
(if (looking-at (concat go-label-regexp ":\\([[:space:]]*/.+\\)?$\\|case .+:\\|default:"))
|
||||
(decf indent tab-width))
|
||||
(setq shift-amt (- indent (current-column)))
|
||||
(if (zerop shift-amt)
|
||||
nil
|
||||
(delete-region beg (point))
|
||||
(indent-to indent))
|
||||
;; If initial point was within line's indentation,
|
||||
;; position after the indentation. Else stay at same point in text.
|
||||
(if (> (- (point-max) pos) (point))
|
||||
(goto-char (- (point-max) pos))))))
|
||||
|
||||
(defun go-beginning-of-defun (&optional count)
|
||||
(unless count (setq count 1))
|
||||
(let ((first t) failure)
|
||||
(dotimes (i (abs count))
|
||||
(while (and (not failure)
|
||||
(or first (go-in-string-or-comment-p)))
|
||||
(if (>= count 0)
|
||||
(progn
|
||||
(go--backward-irrelevant)
|
||||
(if (not (re-search-backward go-func-meth-regexp nil t))
|
||||
(setq failure t)))
|
||||
(if (looking-at go-func-meth-regexp)
|
||||
(forward-char))
|
||||
(if (not (re-search-forward go-func-meth-regexp nil t))
|
||||
(setq failure t)))
|
||||
(setq first nil)))
|
||||
(if (< count 0)
|
||||
(beginning-of-line))
|
||||
(not failure)))
|
||||
|
||||
(defun go-end-of-defun ()
|
||||
(let (orig-level)
|
||||
;; It can happen that we're not placed before a function by emacs
|
||||
(if (not (looking-at "func"))
|
||||
(go-beginning-of-defun -1))
|
||||
(skip-chars-forward "^{")
|
||||
(forward-char)
|
||||
(setq orig-level (go-paren-level))
|
||||
(while (>= (go-paren-level) orig-level)
|
||||
(skip-chars-forward "^}")
|
||||
(forward-char))))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode go-mode prog-mode "Go"
|
||||
"Major mode for editing Go source text.
|
||||
|
||||
This mode provides (not just) basic editing capabilities for
|
||||
working with Go code. It offers almost complete syntax
|
||||
highlighting, indentation that is almost identical to gofmt and
|
||||
proper parsing of the buffer content to allow features such as
|
||||
navigation by function, manipulation of comments or detection of
|
||||
strings.
|
||||
|
||||
In addition to these core features, it offers various features to
|
||||
help with writing Go code. You can directly run buffer content
|
||||
through gofmt, read godoc documentation from within Emacs, modify
|
||||
and clean up the list of package imports or interact with the
|
||||
Playground (uploading and downloading pastes).
|
||||
|
||||
The following extra functions are defined:
|
||||
|
||||
- `gofmt'
|
||||
- `godoc'
|
||||
- `go-import-add'
|
||||
- `go-remove-unused-imports'
|
||||
- `go-goto-imports'
|
||||
- `go-play-buffer' and `go-play-region'
|
||||
- `go-download-play'
|
||||
- `godef-describe' and `godef-jump'
|
||||
|
||||
If you want to automatically run `gofmt' before saving a file,
|
||||
add the following hook to your emacs configuration:
|
||||
|
||||
\(add-hook 'before-save-hook 'gofmt-before-save)
|
||||
|
||||
If you want to use `godef-jump' instead of etags (or similar),
|
||||
consider binding godef-jump to `M-.', which is the default key
|
||||
for `find-tag':
|
||||
|
||||
\(add-hook 'go-mode-hook (lambda ()
|
||||
(local-set-key (kbd \"M-.\") 'godef-jump)))
|
||||
|
||||
Please note that godef is an external dependency. You can install
|
||||
it with
|
||||
|
||||
go get code.google.com/p/rog-go/exp/cmd/godef
|
||||
|
||||
|
||||
If you're looking for even more integration with Go, namely
|
||||
on-the-fly syntax checking, auto-completion and snippets, it is
|
||||
recommended that you look at goflymake
|
||||
\(https://github.com/dougm/goflymake), gocode
|
||||
\(https://github.com/nsf/gocode) and yasnippet-go
|
||||
\(https://github.com/dominikh/yasnippet-go)"
|
||||
|
||||
;; Font lock
|
||||
(set (make-local-variable 'font-lock-defaults)
|
||||
'(go--build-font-lock-keywords))
|
||||
|
||||
;; Indentation
|
||||
(set (make-local-variable 'indent-line-function) 'go-mode-indent-line)
|
||||
|
||||
;; Comments
|
||||
(set (make-local-variable 'comment-start) "// ")
|
||||
(set (make-local-variable 'comment-end) "")
|
||||
(set (make-local-variable 'comment-use-syntax) t)
|
||||
(set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *")
|
||||
|
||||
(set (make-local-variable 'beginning-of-defun-function) 'go-beginning-of-defun)
|
||||
(set (make-local-variable 'end-of-defun-function) 'go-end-of-defun)
|
||||
|
||||
(set (make-local-variable 'parse-sexp-lookup-properties) t)
|
||||
(if (boundp 'syntax-propertize-function)
|
||||
(set (make-local-variable 'syntax-propertize-function) 'go-propertize-syntax))
|
||||
|
||||
(set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql))
|
||||
(add-hook 'before-change-functions (lambda (x y) (setq go-dangling-cache (make-hash-table :test 'eql))) t t)
|
||||
|
||||
|
||||
(setq imenu-generic-expression
|
||||
'(("type" "^type *\\([^ \t\n\r\f]*\\)" 1)
|
||||
("func" "^func *\\(.*\\) {" 1)))
|
||||
(imenu-add-to-menubar "Index")
|
||||
|
||||
;; Go style
|
||||
(setq indent-tabs-mode t)
|
||||
|
||||
;; Handle unit test failure output in compilation-mode
|
||||
;;
|
||||
;; Note the final t argument to add-to-list for append, ie put these at the
|
||||
;; *ends* of compilation-error-regexp-alist[-alist]. We want go-test to be
|
||||
;; handled first, otherwise other elements will match that don't work, and
|
||||
;; those alists are traversed in *reverse* order:
|
||||
;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-12/msg00674.html
|
||||
(when (and (boundp 'compilation-error-regexp-alist)
|
||||
(boundp 'compilation-error-regexp-alist-alist))
|
||||
(add-to-list 'compilation-error-regexp-alist 'go-test t)
|
||||
(add-to-list 'compilation-error-regexp-alist-alist
|
||||
'(go-test . ("^\t+\\([^()\t\n]+\\):\\([0-9]+\\):? .*$" 1 2)) t)))
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode))
|
||||
|
||||
(defun go--apply-rcs-patch (patch-buffer)
|
||||
"Apply an RCS-formatted diff from PATCH-BUFFER to the current
|
||||
buffer."
|
||||
(let ((target-buffer (current-buffer))
|
||||
;; Relative offset between buffer line numbers and line numbers
|
||||
;; in patch.
|
||||
;;
|
||||
;; Line numbers in the patch are based on the source file, so
|
||||
;; we have to keep an offset when making changes to the
|
||||
;; buffer.
|
||||
;;
|
||||
;; Appending lines decrements the offset (possibly making it
|
||||
;; negative), deleting lines increments it. This order
|
||||
;; simplifies the forward-line invocations.
|
||||
(line-offset 0))
|
||||
(save-excursion
|
||||
(with-current-buffer patch-buffer
|
||||
(goto-char (point-min))
|
||||
(while (not (eobp))
|
||||
(unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
|
||||
(error "invalid rcs patch or internal error in go--apply-rcs-patch"))
|
||||
(forward-line)
|
||||
(let ((action (match-string 1))
|
||||
(from (string-to-number (match-string 2)))
|
||||
(len (string-to-number (match-string 3))))
|
||||
(cond
|
||||
((equal action "a")
|
||||
(let ((start (point)))
|
||||
(forward-line len)
|
||||
(let ((text (buffer-substring start (point))))
|
||||
(with-current-buffer target-buffer
|
||||
(decf line-offset len)
|
||||
(goto-char (point-min))
|
||||
(forward-line (- from len line-offset))
|
||||
(insert text)))))
|
||||
((equal action "d")
|
||||
(with-current-buffer target-buffer
|
||||
(goto-char (point-min))
|
||||
(forward-line (- from line-offset 1))
|
||||
(incf line-offset len)
|
||||
(go--kill-whole-line len)))
|
||||
(t
|
||||
(error "invalid rcs patch or internal error in go--apply-rcs-patch")))))))))
|
||||
|
||||
(defun gofmt ()
|
||||
"Formats the current buffer according to the gofmt tool."
|
||||
|
||||
(interactive)
|
||||
(let ((tmpfile (make-temp-file "gofmt" nil ".go"))
|
||||
(patchbuf (get-buffer-create "*Gofmt patch*"))
|
||||
(errbuf (get-buffer-create "*Gofmt Errors*"))
|
||||
(coding-system-for-read 'utf-8)
|
||||
(coding-system-for-write 'utf-8))
|
||||
|
||||
(with-current-buffer errbuf
|
||||
(setq buffer-read-only nil)
|
||||
(erase-buffer))
|
||||
(with-current-buffer patchbuf
|
||||
(erase-buffer))
|
||||
|
||||
(write-region nil nil tmpfile)
|
||||
|
||||
;; We're using errbuf for the mixed stdout and stderr output. This
|
||||
;; is not an issue because gofmt -w does not produce any stdout
|
||||
;; output in case of success.
|
||||
(if (zerop (call-process "gofmt" nil errbuf nil "-w" tmpfile))
|
||||
(if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile))
|
||||
(progn
|
||||
(kill-buffer errbuf)
|
||||
(message "Buffer is already gofmted"))
|
||||
(go--apply-rcs-patch patchbuf)
|
||||
(kill-buffer errbuf)
|
||||
(message "Applied gofmt"))
|
||||
(message "Could not apply gofmt. Check errors for details")
|
||||
(gofmt--process-errors (buffer-file-name) tmpfile errbuf))
|
||||
|
||||
(kill-buffer patchbuf)
|
||||
(delete-file tmpfile)))
|
||||
|
||||
|
||||
(defun gofmt--process-errors (filename tmpfile errbuf)
|
||||
;; Convert the gofmt stderr to something understood by the compilation mode.
|
||||
(with-current-buffer errbuf
|
||||
(goto-char (point-min))
|
||||
(insert "gofmt errors:\n")
|
||||
(while (search-forward-regexp (concat "^\\(" (regexp-quote tmpfile) "\\):") nil t)
|
||||
(replace-match (file-name-nondirectory filename) t t nil 1))
|
||||
(compilation-mode)
|
||||
(display-buffer errbuf)))
|
||||
|
||||
;;;###autoload
|
||||
(defun gofmt-before-save ()
|
||||
"Add this to .emacs to run gofmt on the current buffer when saving:
|
||||
(add-hook 'before-save-hook 'gofmt-before-save).
|
||||
|
||||
Note that this will cause go-mode to get loaded the first time
|
||||
you save any file, kind of defeating the point of autoloading."
|
||||
|
||||
(interactive)
|
||||
(when (eq major-mode 'go-mode) (gofmt)))
|
||||
|
||||
(defun godoc--read-query ()
|
||||
"Read a godoc query from the minibuffer."
|
||||
;; Compute the default query as the symbol under the cursor.
|
||||
;; TODO: This does the wrong thing for e.g. multipart.NewReader (it only grabs
|
||||
;; half) but I see no way to disambiguate that from e.g. foobar.SomeMethod.
|
||||
(let* ((bounds (bounds-of-thing-at-point 'symbol))
|
||||
(symbol (if bounds
|
||||
(buffer-substring-no-properties (car bounds)
|
||||
(cdr bounds)))))
|
||||
(completing-read (if symbol
|
||||
(format "godoc (default %s): " symbol)
|
||||
"godoc: ")
|
||||
(go--old-completion-list-style (go-packages)) nil nil nil 'go-godoc-history symbol)))
|
||||
|
||||
(defun godoc--get-buffer (query)
|
||||
"Get an empty buffer for a godoc query."
|
||||
(let* ((buffer-name (concat "*godoc " query "*"))
|
||||
(buffer (get-buffer buffer-name)))
|
||||
;; Kill the existing buffer if it already exists.
|
||||
(when buffer (kill-buffer buffer))
|
||||
(get-buffer-create buffer-name)))
|
||||
|
||||
(defun godoc--buffer-sentinel (proc event)
|
||||
"Sentinel function run when godoc command completes."
|
||||
(with-current-buffer (process-buffer proc)
|
||||
(cond ((string= event "finished\n") ;; Successful exit.
|
||||
(goto-char (point-min))
|
||||
(view-mode 1)
|
||||
(display-buffer (current-buffer) t))
|
||||
((/= (process-exit-status proc) 0) ;; Error exit.
|
||||
(let ((output (buffer-string)))
|
||||
(kill-buffer (current-buffer))
|
||||
(message (concat "godoc: " output)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun godoc (query)
|
||||
"Show go documentation for a query, much like M-x man."
|
||||
(interactive (list (godoc--read-query)))
|
||||
(unless (string= query "")
|
||||
(set-process-sentinel
|
||||
(start-process-shell-command "godoc" (godoc--get-buffer query)
|
||||
(concat "godoc " query))
|
||||
'godoc--buffer-sentinel)
|
||||
nil))
|
||||
|
||||
(defun go-goto-imports ()
|
||||
"Move point to the block of imports.
|
||||
|
||||
If using
|
||||
|
||||
import (
|
||||
\"foo\"
|
||||
\"bar\"
|
||||
)
|
||||
|
||||
it will move point directly behind the last import.
|
||||
|
||||
If using
|
||||
|
||||
import \"foo\"
|
||||
import \"bar\"
|
||||
|
||||
it will move point to the next line after the last import.
|
||||
|
||||
If no imports can be found, point will be moved after the package
|
||||
declaration."
|
||||
(interactive)
|
||||
;; FIXME if there's a block-commented import before the real
|
||||
;; imports, we'll jump to that one.
|
||||
|
||||
;; Generally, this function isn't very forgiving. it'll bark on
|
||||
;; extra whitespace. It works well for clean code.
|
||||
(let ((old-point (point)))
|
||||
(goto-char (point-min))
|
||||
(cond
|
||||
((re-search-forward "^import ([^)]+)" nil t)
|
||||
(backward-char 2)
|
||||
'block)
|
||||
((re-search-forward "\\(^import \\([^\"]+ \\)?\"[^\"]+\"\n?\\)+" nil t)
|
||||
'single)
|
||||
((re-search-forward "^[[:space:]\n]*package .+?\n" nil t)
|
||||
(message "No imports found, moving point after package declaration")
|
||||
'none)
|
||||
(t
|
||||
(goto-char old-point)
|
||||
(message "No imports or package declaration found. Is this really a Go file?")
|
||||
'fail))))
|
||||
|
||||
(defun go-play-buffer ()
|
||||
"Like `go-play-region', but acts on the entire buffer."
|
||||
(interactive)
|
||||
(go-play-region (point-min) (point-max)))
|
||||
|
||||
(defun go-play-region (start end)
|
||||
"Send the region to the Playground and stores the resulting
|
||||
link in the kill ring."
|
||||
(interactive "r")
|
||||
(let* ((url-request-method "POST")
|
||||
(url-request-extra-headers
|
||||
'(("Content-Type" . "application/x-www-form-urlencoded")))
|
||||
(url-request-data (buffer-substring-no-properties start end))
|
||||
(content-buf (url-retrieve
|
||||
"http://play.golang.org/share"
|
||||
(lambda (arg)
|
||||
(cond
|
||||
((equal :error (car arg))
|
||||
(signal 'go-play-error (cdr arg)))
|
||||
(t
|
||||
(re-search-forward "\n\n")
|
||||
(kill-new (format "http://play.golang.org/p/%s" (buffer-substring (point) (point-max))))
|
||||
(message "http://play.golang.org/p/%s" (buffer-substring (point) (point-max)))))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun go-download-play (url)
|
||||
"Downloads a paste from the playground and inserts it in a Go
|
||||
buffer. Tries to look for a URL at point."
|
||||
(interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p (ffap-string-at-point 'url)))))
|
||||
(with-current-buffer
|
||||
(let ((url-request-method "GET") url-request-data url-request-extra-headers)
|
||||
(url-retrieve-synchronously (concat url ".go")))
|
||||
(let ((buffer (generate-new-buffer (concat (car (last (split-string url "/"))) ".go"))))
|
||||
(goto-char (point-min))
|
||||
(re-search-forward "\n\n")
|
||||
(copy-to-buffer buffer (point) (point-max))
|
||||
(kill-buffer)
|
||||
(with-current-buffer buffer
|
||||
(go-mode)
|
||||
(switch-to-buffer buffer)))))
|
||||
|
||||
(defun go-propertize-syntax (start end)
|
||||
(save-excursion
|
||||
(goto-char start)
|
||||
(while (search-forward "\\" end t)
|
||||
(put-text-property (1- (point)) (point) 'syntax-table (if (= (char-after) ?`) '(1) '(9))))))
|
||||
|
||||
;; ;; Commented until we actually make use of this function
|
||||
;; (defun go--common-prefix (sequences)
|
||||
;; ;; mismatch and reduce are cl
|
||||
;; (assert sequences)
|
||||
;; (flet ((common-prefix (s1 s2)
|
||||
;; (let ((diff-pos (mismatch s1 s2)))
|
||||
;; (if diff-pos (subseq s1 0 diff-pos) s1))))
|
||||
;; (reduce #'common-prefix sequences)))
|
||||
|
||||
(defun go-import-add (arg import)
|
||||
"Add a new import to the list of imports.
|
||||
|
||||
When called with a prefix argument asks for an alternative name
|
||||
to import the package as.
|
||||
|
||||
If no list exists yet, one will be created if possible.
|
||||
|
||||
If an identical import has been commented, it will be
|
||||
uncommented, otherwise a new import will be added."
|
||||
|
||||
;; - If there's a matching `// import "foo"`, uncomment it
|
||||
;; - If we're in an import() block and there's a matching `"foo"`, uncomment it
|
||||
;; - Otherwise add a new import, with the appropriate syntax
|
||||
(interactive
|
||||
(list
|
||||
current-prefix-arg
|
||||
(replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go--old-completion-list-style (go-packages))))))
|
||||
(save-excursion
|
||||
(let (as line import-start)
|
||||
(if arg
|
||||
(setq as (read-from-minibuffer "Import as: ")))
|
||||
(if as
|
||||
(setq line (format "%s \"%s\"" as import))
|
||||
(setq line (format "\"%s\"" import)))
|
||||
|
||||
(goto-char (point-min))
|
||||
(if (re-search-forward (concat "^[[:space:]]*//[[:space:]]*import " line "$") nil t)
|
||||
(uncomment-region (line-beginning-position) (line-end-position))
|
||||
(case (go-goto-imports)
|
||||
('fail (message "Could not find a place to add import."))
|
||||
('block
|
||||
(save-excursion
|
||||
(re-search-backward "^import (")
|
||||
(setq import-start (point)))
|
||||
(if (re-search-backward (concat "^[[:space:]]*//[[:space:]]*" line "$") import-start t)
|
||||
(uncomment-region (line-beginning-position) (line-end-position))
|
||||
(insert "\n\t" line)))
|
||||
('single (insert "import " line "\n"))
|
||||
('none (insert "\nimport (\n\t" line "\n)\n")))))))
|
||||
|
||||
(defun go-root-and-paths ()
|
||||
(let* ((output (split-string (shell-command-to-string "go env GOROOT GOPATH") "\n"))
|
||||
(root (car output))
|
||||
(paths (split-string (cadr output) ":")))
|
||||
(append (list root) paths)))
|
||||
|
||||
(defun go--string-prefix-p (s1 s2 &optional ignore-case)
|
||||
"Return non-nil if S1 is a prefix of S2.
|
||||
If IGNORE-CASE is non-nil, the comparison is case-insensitive."
|
||||
(eq t (compare-strings s1 nil nil
|
||||
s2 0 (length s1) ignore-case)))
|
||||
|
||||
(defun go--directory-dirs (dir)
|
||||
"Recursively return all subdirectories in DIR."
|
||||
(if (file-directory-p dir)
|
||||
(let ((dir (directory-file-name dir))
|
||||
(dirs '())
|
||||
(files (directory-files dir nil nil t)))
|
||||
(dolist (file files)
|
||||
(unless (member file '("." ".."))
|
||||
(let ((file (concat dir "/" file)))
|
||||
(if (file-directory-p file)
|
||||
(setq dirs (append (cons file
|
||||
(go--directory-dirs file))
|
||||
dirs))))))
|
||||
dirs)
|
||||
'()))
|
||||
|
||||
|
||||
(defun go-packages ()
|
||||
(sort
|
||||
(delete-dups
|
||||
(mapcan
|
||||
(lambda (topdir)
|
||||
(let ((pkgdir (concat topdir "/pkg/")))
|
||||
(mapcan (lambda (dir)
|
||||
(mapcar (lambda (file)
|
||||
(let ((sub (substring file (length pkgdir) -2)))
|
||||
(unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub))
|
||||
(mapconcat 'identity (cdr (split-string sub "/")) "/"))))
|
||||
(if (file-directory-p dir)
|
||||
(directory-files dir t "\\.a$"))))
|
||||
(if (file-directory-p pkgdir)
|
||||
(go--directory-dirs pkgdir)))))
|
||||
(go-root-and-paths)))
|
||||
'string<))
|
||||
|
||||
(defun go-unused-imports-lines ()
|
||||
;; FIXME Technically, -o /dev/null fails in quite some cases (on
|
||||
;; Windows, when compiling from within GOPATH). Practically,
|
||||
;; however, it has the same end result: There won't be a
|
||||
;; compiled binary/archive, and we'll get our import errors when
|
||||
;; there are any.
|
||||
(reverse (remove nil
|
||||
(mapcar
|
||||
(lambda (line)
|
||||
(if (string-match "^\\(.+\\):\\([[:digit:]]+\\): imported and not used: \".+\"$" line)
|
||||
(if (string= (file-truename (match-string 1 line)) (file-truename buffer-file-name))
|
||||
(string-to-number (match-string 2 line)))))
|
||||
(split-string (shell-command-to-string
|
||||
(if (string-match "_test\.go$" buffer-file-truename)
|
||||
"go test -c"
|
||||
"go build -o /dev/null")) "\n")))))
|
||||
|
||||
(defun go-remove-unused-imports (arg)
|
||||
"Removes all unused imports. If ARG is non-nil, unused imports
|
||||
will be commented, otherwise they will be removed completely."
|
||||
(interactive "P")
|
||||
(save-excursion
|
||||
(let ((cur-buffer (current-buffer)) flymake-state lines)
|
||||
(when (boundp 'flymake-mode)
|
||||
(setq flymake-state flymake-mode)
|
||||
(flymake-mode-off))
|
||||
(save-some-buffers nil (lambda () (equal cur-buffer (current-buffer))))
|
||||
(if (buffer-modified-p)
|
||||
(message "Cannot operate on unsaved buffer")
|
||||
(setq lines (go-unused-imports-lines))
|
||||
(dolist (import lines)
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- import))
|
||||
(beginning-of-line)
|
||||
(if arg
|
||||
(comment-region (line-beginning-position) (line-end-position))
|
||||
(go--kill-whole-line)))
|
||||
(message "Removed %d imports" (length lines)))
|
||||
(if flymake-state (flymake-mode-on)))))
|
||||
|
||||
(defun godef--find-file-line-column (specifier)
|
||||
"Given a file name in the format of `filename:line:column',
|
||||
visit FILENAME and go to line LINE and column COLUMN."
|
||||
(let* ((components (split-string specifier ":"))
|
||||
(line (string-to-number (nth 1 components)))
|
||||
(column (string-to-number (nth 2 components))))
|
||||
(with-current-buffer (find-file (car components))
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- line))
|
||||
(beginning-of-line)
|
||||
(forward-char (1- column))
|
||||
(if (buffer-modified-p)
|
||||
(message "Buffer is modified, file position might not have been correct")))))
|
||||
|
||||
(defun godef--call (point)
|
||||
"Call godef, acquiring definition position and expression
|
||||
description at POINT."
|
||||
(if (go--xemacs-p)
|
||||
(message "godef does not reliably work in XEmacs, expect bad results"))
|
||||
(if (not buffer-file-name)
|
||||
(message "Cannot use godef on a buffer without a file name")
|
||||
(let ((outbuf (get-buffer-create "*godef*")))
|
||||
(with-current-buffer outbuf
|
||||
(erase-buffer))
|
||||
(call-process-region (point-min) (point-max) "godef" nil outbuf nil "-i" "-t" "-f" (file-truename buffer-file-name) "-o" (number-to-string (go--position-bytes (point))))
|
||||
(with-current-buffer outbuf
|
||||
(split-string (buffer-substring-no-properties (point-min) (point-max)) "\n")))))
|
||||
|
||||
(defun godef-describe (point)
|
||||
"Describe the expression at POINT."
|
||||
(interactive "d")
|
||||
(condition-case nil
|
||||
(let ((description (nth 1 (godef--call point))))
|
||||
(if (string= "" description)
|
||||
(message "No description found for expression at point")
|
||||
(message "%s" description)))
|
||||
(file-error (message "Could not run godef binary"))))
|
||||
|
||||
(defun godef-jump (point)
|
||||
"Jump to the definition of the expression at POINT."
|
||||
(interactive "d")
|
||||
(condition-case nil
|
||||
(let ((file (car (godef--call point))))
|
||||
(cond
|
||||
((string= "-" file)
|
||||
(message "godef: expression is not defined anywhere"))
|
||||
((string= "godef: no identifier found" file)
|
||||
(message "%s" file))
|
||||
((go--string-prefix-p "godef: no declaration found for " file)
|
||||
(message "%s" file))
|
||||
(t
|
||||
(push-mark)
|
||||
(godef--find-file-line-column file))))
|
||||
(file-error (message "Could not run godef binary"))))
|
||||
|
||||
(load "~/.emacs.d/modes/go-mode-custom.el")
|
||||
|
||||
(provide 'go-mode)
|
||||
794
xdg/emacs/lisp/multi-term.el
Normal file
794
xdg/emacs/lisp/multi-term.el
Normal file
@ -0,0 +1,794 @@
|
||||
;;; multi-term.el --- Managing multiple terminal buffers in Emacs.
|
||||
|
||||
;; Author: Andy Stewart <lazycat.manatee@gmail.com>
|
||||
;; Maintainer: ahei <ahei0802@gmail.com>
|
||||
;; Copyright (C) 2008, 2009, Andy Stewart, all rights reserved.
|
||||
;; Copyright (C) 2010, ahei, all rights reserved.
|
||||
;; Created: <2008-09-19 23:02:42>
|
||||
;; Version: 0.8.9
|
||||
;; Last-Updated: <2013-01-08 10:11:43 Tuesday by jpkotta>
|
||||
;; URL: http://www.emacswiki.org/emacs/download/multi-term.el
|
||||
;; Keywords: term, terminal, multiple buffer
|
||||
;; Compatibility: GNU Emacs 23.2.1
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, 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, Boston, MA 02110-1301, USA.
|
||||
|
||||
;; Features that might be required by this library:
|
||||
;;
|
||||
;; `term' `cl' `advice'
|
||||
;;
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; This package is for creating and managing multiple terminal buffers in Emacs.
|
||||
;;
|
||||
;; By default, term.el provides a great terminal emulator in Emacs.
|
||||
;; But I have some troubles with term-mode:
|
||||
;;
|
||||
;; 1. term.el just provides commands `term' or `ansi-term'
|
||||
;; for creating a terminal buffer.
|
||||
;; And there is no special command to create or switch
|
||||
;; between multiple terminal buffers quickly.
|
||||
;;
|
||||
;; 2. By default, the keystrokes of term.el conflict with global-mode keystrokes,
|
||||
;; which makes it difficult for the user to integrate term.el with Emacs.
|
||||
;;
|
||||
;; 3. By default, executing *NIX command “exit” from term-mode,
|
||||
;; it will leave an unused buffer.
|
||||
;;
|
||||
;; 4. term.el won’t quit running sub-process when you kill terminal buffer forcibly.
|
||||
;;
|
||||
;; 5. Haven’t a dedicated window for debug program.
|
||||
;;
|
||||
;; And multi-term.el is enhanced with those features.
|
||||
;;
|
||||
|
||||
;;; Installation:
|
||||
;;
|
||||
;; Copy multi-term.el to your load-path and add to your ~/.emacs
|
||||
;;
|
||||
;; (require 'multi-term)
|
||||
;;
|
||||
;; And setup program that `multi-term' will need:
|
||||
;;
|
||||
;; (setq multi-term-program "/bin/bash")
|
||||
;;
|
||||
;; or setup like me "/bin/zsh" ;)
|
||||
;;
|
||||
;; Below are the commands you can use:
|
||||
;;
|
||||
;; `multi-term' Create a new term buffer.
|
||||
;; `multi-term-next' Switch to next term buffer.
|
||||
;; `multi-term-prev' Switch to previous term buffer.
|
||||
;; `multi-term-dedicated-open' Open dedicated term window.
|
||||
;; `multi-term-dedicated-close' Close dedicated term window.
|
||||
;; `multi-term-dedicated-toggle' Toggle dedicated term window.
|
||||
;; `multi-term-dedicated-select' Select dedicated term window.
|
||||
;;
|
||||
;; Tips:
|
||||
;;
|
||||
;; You can type `C-u' before command `multi-term' or `multi-term-dedicated-open'
|
||||
;; then will prompt you shell name for creating terminal buffer.
|
||||
;;
|
||||
|
||||
;;; Customize:
|
||||
;;
|
||||
;; `multi-term-program' default is nil, so when creating new term buffer,
|
||||
;; send environment variable of `SHELL' (`ESHELL', `/bin/sh') to `make-term'.
|
||||
;;
|
||||
;; And you can set it to your liking, like me: ;-)
|
||||
;;
|
||||
;; (setq multi-term-program "/bin/zsh")
|
||||
;;
|
||||
;; `multi-term-default-dir' default is `~/', only use when current buffer
|
||||
;; is not in a real directory.
|
||||
;;
|
||||
;; `multi-term-buffer-name' is the name of term buffer.
|
||||
;;
|
||||
;; `multi-term-scroll-show-maximum-output' controls how interpreter
|
||||
;; output causes window to scroll.
|
||||
;;
|
||||
;; `multi-term-scroll-to-bottom-on-output' controls whether interpreter
|
||||
;; output causes window to scroll.
|
||||
;;
|
||||
;; `multi-term-switch-after-close' try to switch other `multi-term' buffer
|
||||
;; after close current one.
|
||||
;; If you don't like this feature just set it with nil.
|
||||
;;
|
||||
;; `term-unbind-key-list' is a key list to unbind some keystroke.
|
||||
;;
|
||||
;; `term-bind-key-alist' is a key alist that binds some keystroke.
|
||||
;; If you don't like default, modify it.
|
||||
;;
|
||||
;; `multi-term-dedicated-window-height' the height of a dedicated term window.
|
||||
;;
|
||||
;; `multi-term-dedicated-max-window-height' the max height limit that dedicated
|
||||
;; window is allowed.
|
||||
;;
|
||||
;; `multi-term-dedicated-skip-other-window-p' whether skip dedicated term
|
||||
;; window when use command `other-window' to cycle windows order.
|
||||
;;
|
||||
;; All of the above can be customize by:
|
||||
;; M-x customize-group RET multi-term RET
|
||||
;;
|
||||
|
||||
;;; Change log:
|
||||
;; 2013/01/08
|
||||
;; * Fix customize group of `multi-term-try-create'.
|
||||
;; * Add autoloads.
|
||||
;; * Add new commands `term-send-quote' and `term-send-M-x'.
|
||||
;;
|
||||
;; 2009/07/04
|
||||
;; * Add new option `multi-term-dedicated-select-after-open-p'.
|
||||
;;
|
||||
;; 2009/06/29
|
||||
;; * Fix regexp bug.
|
||||
;;
|
||||
;; 2009/04/21
|
||||
;; * Fix a bug that bring at `2009/03/28':
|
||||
;; It will kill sub-process in other multi-term buffer
|
||||
;; when we kill current multi-term buffer.
|
||||
;;
|
||||
;; 2009/03/29
|
||||
;; * Add new command `term-send-reverse-search-history'.
|
||||
;;
|
||||
;; 2009/03/28
|
||||
;; * Add new option `multi-term-switch-after-close'.
|
||||
;;
|
||||
;; 2009/02/18
|
||||
;; * Fix bug between ECB and `multi-term-dedicated-close'.
|
||||
;;
|
||||
;; 2009/02/05
|
||||
;; * Prompt user shell name when type `C-u' before command
|
||||
;; `multi-term' or `multi-term-dedicated-open'.
|
||||
;; * Fix doc.
|
||||
;;
|
||||
;; 2009/01/29
|
||||
;; * Use `term-quit-subjob' instead `term-interrupt-subjob'.
|
||||
;; * Fix doc.
|
||||
;;
|
||||
;; 2009/01/13
|
||||
;; * Rewrite advice for `pop-to-buffer' to avoid `pop-to-buffer' not effect
|
||||
;; when have many dedicated window in current frame.
|
||||
;; * Rewrite advice for `delete-other-windows' to avoid use common variable
|
||||
;; `delete-protected-window-list' and use `window-dedicated-p' instead.
|
||||
;; Remove variable `delete-protected-window-list' and function
|
||||
;; `multi-term-dedicated-match-protected-window-p'.
|
||||
;;
|
||||
;; 2009/01/06
|
||||
;; * Improve document.
|
||||
;;
|
||||
;; 2008/12/29
|
||||
;; * Remove option `multi-term-current-window-height' and
|
||||
;; function `multi-term-current-directory'.
|
||||
;; * Add some functions to make get dedicated term buffer,
|
||||
;; those functions is beginning with `multi-term-dedicated-'.
|
||||
;; * Modified advice `delete-window', make command `delete-window'
|
||||
;; and delete dedicated window, but will remember window height
|
||||
;; before deleted.
|
||||
;; * Don't remember dedicated window height if larger than max value.
|
||||
;; * Fix some bug with `delete-other-windows' and window configuration.
|
||||
;; And this bug exists with another extension `sr-speedbar'.
|
||||
;; * Add new variable `delete-protected-window-list' for protected
|
||||
;; special window that won't be deleted.
|
||||
;; This variable is common for any extension that use dedicated
|
||||
;; window.
|
||||
;; * Fix doc.
|
||||
;;
|
||||
;; 2008/12/21
|
||||
;; * Default bind `C-m' with `term-send-input'.
|
||||
;;
|
||||
;; 2008/12/10
|
||||
;; * Improve customize interface.
|
||||
;; * Setup customize automatically, don't need to user setup it up.
|
||||
;; * Add option `multi-term-try-create'.
|
||||
;; * Make function `multi-term-switch' accept offset argument.
|
||||
;; * Fix doc.
|
||||
;;
|
||||
;; 2008/10/22
|
||||
;; * Add variable `multi-term-current-window-height'.
|
||||
;; * Add variable `multi-term-buffer-name'.
|
||||
;; * Add variable `term-unbind-key-list'.
|
||||
;; * Add variable `term-rebind-key-alist'.
|
||||
;; * Move key setup and some extension from `term-extension.el'.
|
||||
;; * Create new function `multi-term-keystroke-setup'.
|
||||
;; * Fix doc.
|
||||
;;
|
||||
;; 2008/09/19
|
||||
;; * First released.
|
||||
;;
|
||||
|
||||
;;; Acknowledgments:
|
||||
;;
|
||||
;; Mark Triggs <mst@dishevelled.net>
|
||||
;; For create multi-shell.el
|
||||
;; Aaron S. Hawley <aaron.s.hawley@gmail.com>
|
||||
;; For improve document.
|
||||
;;
|
||||
|
||||
;;; Bug
|
||||
;;
|
||||
;;
|
||||
|
||||
;;; TODO
|
||||
;;
|
||||
;;
|
||||
;;
|
||||
|
||||
;;; Require:
|
||||
(require 'term)
|
||||
(require 'cl)
|
||||
(require 'advice)
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; Customize
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Customize ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(defgroup multi-term nil
|
||||
"Multi term manager."
|
||||
:group 'term)
|
||||
|
||||
(defcustom multi-term-program nil
|
||||
"The program of term.
|
||||
If this is nil, setup to environment variable of `SHELL'."
|
||||
:type 'string
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom multi-term-program-switches nil
|
||||
"The command-line switches to pass to the term program."
|
||||
:type 'string
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom multi-term-try-create t
|
||||
"Try to create a new term buffer when switch.
|
||||
|
||||
When use `multi-term-next' or `multi-term-prev', switch term buffer,
|
||||
and try to create a new term buffer if no term buffers exist."
|
||||
:type 'boolean
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom multi-term-default-dir "~/"
|
||||
"The default directory for terms if current directory doesn't exist."
|
||||
:type 'string
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom multi-term-buffer-name "terminal"
|
||||
"The buffer name of term buffer."
|
||||
:type 'string
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom multi-term-scroll-show-maximum-output nil
|
||||
"*Controls how interpreter output causes window to scroll.
|
||||
If non-nil, then show the maximum output when the window is scrolled.
|
||||
|
||||
See variable `multi-term-scroll-to-bottom-on-output'."
|
||||
:type 'boolean
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom multi-term-scroll-to-bottom-on-output nil
|
||||
"*Controls whether interpreter output causes window to scroll.
|
||||
If nil, then do not scroll. If t or `all', scroll all windows showing buffer.
|
||||
If `this', scroll only the selected window.
|
||||
If `others', scroll only those that are not the selected window.
|
||||
|
||||
The default is nil.
|
||||
|
||||
See variable `multi-term-scroll-show-maximum-output'."
|
||||
:type 'boolean
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom multi-term-switch-after-close 'NEXT
|
||||
"Try to switch other `multi-term' buffer after close current one.
|
||||
If this option is 'NEXT, switch to next `multi-term' buffer;
|
||||
If this option is 'PREVIOUS, switch to previous `multi-term' buffer.
|
||||
If this option is nil, don't switch other `multi-term' buffer."
|
||||
:type 'symbol
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom term-unbind-key-list
|
||||
'("C-z" "C-x" "C-c" "C-h" "C-y" "<ESC>")
|
||||
"The key list that will need to be unbind."
|
||||
:type 'list
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom term-bind-key-alist
|
||||
'(
|
||||
("C-c C-c" . term-interrupt-subjob)
|
||||
("C-p" . previous-line)
|
||||
("C-n" . next-line)
|
||||
("C-s" . isearch-forward)
|
||||
("C-r" . isearch-backward)
|
||||
("C-m" . term-send-raw)
|
||||
("M-f" . term-send-forward-word)
|
||||
("M-b" . term-send-backward-word)
|
||||
("M-o" . term-send-backspace)
|
||||
("M-p" . term-send-up)
|
||||
("M-n" . term-send-down)
|
||||
("M-M" . term-send-forward-kill-word)
|
||||
("M-N" . term-send-backward-kill-word)
|
||||
("M-r" . term-send-reverse-search-history)
|
||||
("M-," . term-send-input)
|
||||
("M-." . comint-dynamic-complete))
|
||||
"The key alist that will need to be bind.
|
||||
If you do not like default setup, modify it, with (KEY . COMMAND) format."
|
||||
:type 'alist
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom multi-term-dedicated-window-height 14
|
||||
"The height of `multi-term' dedicated window."
|
||||
:type 'integer
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom multi-term-dedicated-max-window-height 30
|
||||
"The max height limit of `multi-term' dedicated window.
|
||||
Default, when hide `multi-term' dedicated window, will remember
|
||||
window height before hide, except height is larger than this.`"
|
||||
:type 'integer
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom multi-term-dedicated-skip-other-window-p nil
|
||||
"Default, can have `other-window' select window in cyclic ordering of windows.
|
||||
In cases you don't want to select `multi-term' dedicated window, use `other-window'
|
||||
and make `multi-term' dedicated window as a viewable sidebar.
|
||||
|
||||
So please turn on this option if you want to skip `multi-term' dedicated window with `other-window'.
|
||||
|
||||
Default is nil."
|
||||
:type 'boolean
|
||||
:set (lambda (symbol value)
|
||||
(set symbol value)
|
||||
(when (ad-advised-definition-p 'other-window)
|
||||
(multi-term-dedicated-handle-other-window-advice value)))
|
||||
:group 'multi-term)
|
||||
|
||||
(defcustom multi-term-dedicated-select-after-open-p nil
|
||||
"Default, multi-term won't focus terminal window after you open dedicated window.
|
||||
Please make this option with t if you want focus terminal window.
|
||||
|
||||
Default is nil."
|
||||
:type 'boolean
|
||||
:group 'multi-term)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Constant ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(defconst multi-term-dedicated-buffer-name "MULTI-TERM-DEDICATED"
|
||||
"The buffer name of dedicated `multi-term'.")
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Variable ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(defvar multi-term-dedicated-window nil
|
||||
"The dedicated `multi-term' window.")
|
||||
|
||||
(defvar multi-term-dedicated-buffer nil
|
||||
"The dedicated `multi-term' buffer.")
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interactive Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;###autoload
|
||||
(defun multi-term ()
|
||||
"Create new term buffer.
|
||||
Will prompt you shell name when you type `C-u' before this command."
|
||||
(interactive)
|
||||
(let (term-buffer)
|
||||
;; Set buffer.
|
||||
(setq term-buffer (multi-term-get-buffer current-prefix-arg))
|
||||
(set-buffer term-buffer)
|
||||
;; Internal handle for `multi-term' buffer.
|
||||
(multi-term-internal)
|
||||
;; Switch buffer
|
||||
(switch-to-buffer term-buffer)))
|
||||
|
||||
;;;###autoload
|
||||
(defun multi-term-next (&optional offset)
|
||||
"Go to the next term buffer.
|
||||
If OFFSET is `non-nil', will goto next term buffer with OFFSET."
|
||||
(interactive "P")
|
||||
(multi-term-switch 'NEXT (or offset 1)))
|
||||
|
||||
;;;###autoload
|
||||
(defun multi-term-prev (&optional offset)
|
||||
"Go to the previous term buffer.
|
||||
If OFFSET is `non-nil', will goto previous term buffer with OFFSET."
|
||||
(interactive "P")
|
||||
(multi-term-switch 'PREVIOUS (or offset 1)))
|
||||
|
||||
;;;###autoload
|
||||
(defun multi-term-dedicated-open ()
|
||||
"Open dedicated `multi-term' window.
|
||||
Will prompt you shell name when you type `C-u' before this command."
|
||||
(interactive)
|
||||
(if (not (multi-term-dedicated-exist-p))
|
||||
(let ((current-window (selected-window)))
|
||||
(if (multi-term-buffer-exist-p multi-term-dedicated-buffer)
|
||||
(unless (multi-term-window-exist-p multi-term-dedicated-window)
|
||||
(multi-term-dedicated-get-window))
|
||||
;; Set buffer.
|
||||
(setq multi-term-dedicated-buffer (multi-term-get-buffer current-prefix-arg t))
|
||||
(set-buffer (multi-term-dedicated-get-buffer-name))
|
||||
;; Get dedicate window.
|
||||
(multi-term-dedicated-get-window)
|
||||
;; Whether skip `other-window'.
|
||||
(multi-term-dedicated-handle-other-window-advice multi-term-dedicated-skip-other-window-p)
|
||||
;; Internal handle for `multi-term' buffer.
|
||||
(multi-term-internal))
|
||||
(set-window-buffer multi-term-dedicated-window (get-buffer (multi-term-dedicated-get-buffer-name)))
|
||||
(set-window-dedicated-p multi-term-dedicated-window t)
|
||||
;; Select window.
|
||||
(select-window
|
||||
(if multi-term-dedicated-select-after-open-p
|
||||
;; Focus dedicated terminal window if option `multi-term-dedicated-select-after-open-p' is enable.
|
||||
multi-term-dedicated-window
|
||||
;; Otherwise focus current window.
|
||||
current-window)))
|
||||
(message "`multi-term' dedicated window has exist.")))
|
||||
|
||||
(defun multi-term-dedicated-close ()
|
||||
"Close dedicated `multi-term' window."
|
||||
(interactive)
|
||||
(if (multi-term-dedicated-exist-p)
|
||||
(let ((current-window (selected-window)))
|
||||
;; Remember height.
|
||||
(multi-term-dedicated-select)
|
||||
(multi-term-dedicated-remember-window-height)
|
||||
;; Close window.
|
||||
(if (and (require 'ecb nil t)
|
||||
ecb-activated-window-configuration)
|
||||
;; Toggle ECB window when ECB window activated.
|
||||
(progn
|
||||
(ecb-deactivate)
|
||||
(ecb-activate))
|
||||
;; Otherwise delete dedicated window.
|
||||
(delete-window multi-term-dedicated-window)
|
||||
(if (multi-term-window-exist-p current-window)
|
||||
(select-window current-window))))
|
||||
(message "`multi-term' window is not exist.")))
|
||||
|
||||
(defun multi-term-dedicated-remember-window-height ()
|
||||
"Remember window height."
|
||||
(let ((win-height (multi-term-current-window-take-height)))
|
||||
(if (and (multi-term-dedicated-window-p) ;in `multi-term' window
|
||||
(> win-height 1)
|
||||
(<= win-height multi-term-dedicated-max-window-height))
|
||||
(setq multi-term-dedicated-window-height win-height))))
|
||||
|
||||
;;;###autoload
|
||||
(defun multi-term-dedicated-toggle ()
|
||||
"Toggle dedicated `multi-term' window."
|
||||
(interactive)
|
||||
(if (multi-term-dedicated-exist-p)
|
||||
(multi-term-dedicated-close)
|
||||
(multi-term-dedicated-open)))
|
||||
|
||||
;;;###autoload
|
||||
(defun multi-term-dedicated-select ()
|
||||
"Select the `multi-term' dedicated window."
|
||||
(interactive)
|
||||
(if (multi-term-dedicated-exist-p)
|
||||
(select-window multi-term-dedicated-window)
|
||||
(message "`multi-term' window is not exist.")))
|
||||
|
||||
(defun term-send-backward-kill-word ()
|
||||
"Backward kill word in term mode."
|
||||
(interactive)
|
||||
(term-send-raw-string "\C-w"))
|
||||
|
||||
(defun term-send-forward-kill-word ()
|
||||
"Kill word in term mode."
|
||||
(interactive)
|
||||
(term-send-raw-string "\ed"))
|
||||
|
||||
(defun term-send-backward-word ()
|
||||
"Move backward word in term mode."
|
||||
(interactive)
|
||||
(term-send-raw-string "\eb"))
|
||||
|
||||
(defun term-send-forward-word ()
|
||||
"Move forward word in term mode."
|
||||
(interactive)
|
||||
(term-send-raw-string "\ef"))
|
||||
|
||||
(defun term-send-reverse-search-history ()
|
||||
"Search history reverse."
|
||||
(interactive)
|
||||
(term-send-raw-string "\C-r"))
|
||||
|
||||
(defun term-send-quote ()
|
||||
"Quote the next character in term-mode.
|
||||
Similar to how `quoted-insert' works in a regular buffer."
|
||||
(interactive)
|
||||
(term-send-raw-string "\C-v"))
|
||||
|
||||
(defun term-send-M-x ()
|
||||
"Type M-x in term-mode."
|
||||
(interactive)
|
||||
(term-send-raw-string "\ex"))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Utilise Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(defun multi-term-internal ()
|
||||
"Internal handle for `multi-term' buffer."
|
||||
;; Add customize keystroke with `term-mode-hook'
|
||||
(remove-hook 'term-mode-hook 'multi-term-keystroke-setup)
|
||||
(add-hook 'term-mode-hook 'multi-term-keystroke-setup)
|
||||
;; Load term mode
|
||||
(term-mode)
|
||||
(term-char-mode)
|
||||
;; Handle term buffer close
|
||||
(multi-term-handle-close)
|
||||
;; Handle `output' variable.
|
||||
(setq term-scroll-show-maximum-output multi-term-scroll-show-maximum-output
|
||||
term-scroll-to-bottom-on-output multi-term-scroll-to-bottom-on-output)
|
||||
;; Add hook to be sure `term' quit subjob before buffer killed.
|
||||
(add-hook 'kill-buffer-hook 'multi-term-kill-buffer-hook))
|
||||
|
||||
(defun multi-term-get-buffer (&optional special-shell dedicated-window)
|
||||
"Get term buffer.
|
||||
If option SPECIAL-SHELL is `non-nil', will use shell from user input.
|
||||
If option DEDICATED-WINDOW is `non-nil' will create dedicated `multi-term' window ."
|
||||
(with-temp-buffer
|
||||
(let ((shell-name (or multi-term-program ;shell name
|
||||
(getenv "SHELL")
|
||||
(getenv "ESHELL")
|
||||
"/bin/sh"))
|
||||
term-list-length ;get length of term list
|
||||
index ;setup new term index
|
||||
term-name) ;term name
|
||||
(if dedicated-window
|
||||
(setq term-name multi-term-dedicated-buffer-name)
|
||||
;; Compute index.
|
||||
(setq term-list-length (length (multi-term-list)))
|
||||
(setq index (if term-list-length (1+ term-list-length) 1))
|
||||
;; switch to current local directory,
|
||||
;; if in-existence, switch to `multi-term-default-dir'.
|
||||
(cd (or default-directory (expand-file-name multi-term-default-dir)))
|
||||
;; adjust value N when max index of term buffer is less than length of term list
|
||||
(while (buffer-live-p (get-buffer (format "*%s<%s>*" multi-term-buffer-name index)))
|
||||
(setq index (1+ index)))
|
||||
(setq term-name (format "%s<%s>" multi-term-buffer-name index)))
|
||||
;; Try get other shell name if `special-shell' is non-nil.
|
||||
(if special-shell
|
||||
(setq shell-name (read-from-minibuffer "Run program: " shell-name)))
|
||||
;; Make term, details to see function `make-term' in `term.el'.
|
||||
(if multi-term-program-switches
|
||||
(make-term term-name shell-name nil multi-term-program-switches)
|
||||
(make-term term-name shell-name)))))
|
||||
|
||||
|
||||
(defun multi-term-handle-close ()
|
||||
"Close current term buffer when `exit' from term buffer."
|
||||
(when (ignore-errors (get-buffer-process (current-buffer)))
|
||||
(set-process-sentinel (get-buffer-process (current-buffer))
|
||||
(lambda (proc change)
|
||||
(when (string-match "\\(finished\\|exited\\)" change)
|
||||
(kill-buffer (process-buffer proc)))))))
|
||||
|
||||
(defun multi-term-kill-buffer-hook ()
|
||||
"Function that hook `kill-buffer-hook'."
|
||||
(when (eq major-mode 'term-mode)
|
||||
;; Quit the current subjob
|
||||
;; when have alive process with current term buffer.
|
||||
;; Must do this job BEFORE `multi-term-switch-after-close' action.
|
||||
(when (term-check-proc (current-buffer))
|
||||
;; Quit sub-process.
|
||||
(term-quit-subjob))
|
||||
;; Remember dedicated window height.
|
||||
(multi-term-dedicated-remember-window-height)
|
||||
;; Try to switch other multi-term buffer
|
||||
;; when option `multi-term-switch-after-close' is non-nil.
|
||||
(when multi-term-switch-after-close
|
||||
(multi-term-switch-internal multi-term-switch-after-close 1))))
|
||||
|
||||
(defun multi-term-list ()
|
||||
"List term buffers presently active."
|
||||
;; Autload command `remove-if-not'.
|
||||
(autoload 'remove-if-not "cl-seq")
|
||||
(sort
|
||||
(remove-if-not (lambda (b)
|
||||
(setq case-fold-search t)
|
||||
(string-match
|
||||
(format "^\\\*%s<[0-9]+>\\\*$" multi-term-buffer-name)
|
||||
(buffer-name b)))
|
||||
(buffer-list))
|
||||
(lambda (a b)
|
||||
(< (string-to-number
|
||||
(cadr (split-string (buffer-name a) "[<>]")))
|
||||
(string-to-number
|
||||
(cadr (split-string (buffer-name b) "[<>]")))))))
|
||||
|
||||
(defun multi-term-switch (direction offset)
|
||||
"Switch `multi-term' buffers.
|
||||
If DIRECTION is `NEXT', switch to the next term.
|
||||
If DIRECTION `PREVIOUS', switch to the previous term.
|
||||
Option OFFSET for skip OFFSET number term buffer."
|
||||
(unless (multi-term-switch-internal direction offset)
|
||||
(if multi-term-try-create
|
||||
(progn
|
||||
(multi-term)
|
||||
(message "Create a new `multi-term' buffer."))
|
||||
(message "Haven't any `multi-term' buffer exist."))))
|
||||
|
||||
(defun multi-term-switch-internal (direction offset)
|
||||
"Internal `multi-term' buffers switch function.
|
||||
If DIRECTION is `NEXT', switch to the next term.
|
||||
If DIRECTION `PREVIOUS', switch to the previous term.
|
||||
Option OFFSET for skip OFFSET number term buffer."
|
||||
(let (terms this-buffer)
|
||||
(setq terms (multi-term-list))
|
||||
(if (consp terms)
|
||||
(progn
|
||||
(setf (cdr (last terms)) terms)
|
||||
(setq this-buffer (position (current-buffer) (multi-term-list)))
|
||||
(if this-buffer
|
||||
(if (eql direction 'NEXT)
|
||||
(switch-to-buffer (nth (+ this-buffer offset) terms))
|
||||
(switch-to-buffer (nth (+ (- (length (multi-term-list)) offset)
|
||||
this-buffer) terms)))
|
||||
(switch-to-buffer (car terms)))
|
||||
t)
|
||||
nil)))
|
||||
|
||||
(defun multi-term-keystroke-setup ()
|
||||
"Keystroke setup of `term-char-mode'.
|
||||
|
||||
By default, the key bindings of `term-char-mode' conflict with user's keystroke.
|
||||
So this function unbinds some keys with `term-raw-map',
|
||||
and binds some keystroke with `term-raw-map'."
|
||||
(let (bind-key bind-command)
|
||||
;; Unbind base key that conflict with user's keys-tokes.
|
||||
(dolist (unbind-key term-unbind-key-list)
|
||||
(cond
|
||||
((stringp unbind-key) (setq unbind-key (read-kbd-macro unbind-key)))
|
||||
((vectorp unbind-key) nil)
|
||||
(t (signal 'wrong-type-argument (list 'array unbind-key))))
|
||||
(define-key term-raw-map unbind-key nil))
|
||||
;; Add some i use keys.
|
||||
;; If you don't like my keystroke,
|
||||
;; just modified `term-bind-key-alist'
|
||||
(dolist (element term-bind-key-alist)
|
||||
(setq bind-key (car element))
|
||||
(setq bind-command (cdr element))
|
||||
(cond
|
||||
((stringp bind-key) (setq bind-key (read-kbd-macro bind-key)))
|
||||
((vectorp bind-key) nil)
|
||||
(t (signal 'wrong-type-argument (list 'array bind-key))))
|
||||
(define-key term-raw-map bind-key bind-command))))
|
||||
|
||||
(defun multi-term-dedicated-handle-other-window-advice (activate)
|
||||
"Handle advice for function `other-window'.
|
||||
If ACTIVATE is `non-nil', will enable advice
|
||||
`multi-term-dedicated-other-window-advice'.
|
||||
Otherwise, disable it."
|
||||
(if activate
|
||||
(ad-enable-advice 'other-window 'after 'multi-term-dedicated-other-window-advice)
|
||||
(ad-disable-advice 'other-window 'after 'multi-term-dedicated-other-window-advice))
|
||||
(ad-activate 'other-window))
|
||||
|
||||
(defun multi-term-current-window-take-height (&optional window)
|
||||
"Return the height the `window' takes up.
|
||||
Not the value of `window-height', it returns usable rows available for WINDOW.
|
||||
If `window' is nil, get current window."
|
||||
(let ((edges (window-edges window)))
|
||||
(- (nth 3 edges) (nth 1 edges))))
|
||||
|
||||
(defun multi-term-dedicated-get-window ()
|
||||
"Get `multi-term' dedicated window."
|
||||
(setq multi-term-dedicated-window
|
||||
(split-window
|
||||
(selected-window)
|
||||
(- (multi-term-current-window-take-height) multi-term-dedicated-window-height))))
|
||||
|
||||
(defun multi-term-dedicated-get-buffer-name ()
|
||||
"Get the buffer name of `multi-term' dedicated window."
|
||||
(format "*%s*" multi-term-dedicated-buffer-name))
|
||||
|
||||
(defun multi-term-dedicated-exist-p ()
|
||||
"Return `non-nil' if `multi-term' dedicated window exist."
|
||||
(and (multi-term-buffer-exist-p multi-term-dedicated-buffer)
|
||||
(multi-term-window-exist-p multi-term-dedicated-window)))
|
||||
|
||||
(defun multi-term-window-exist-p (window)
|
||||
"Return `non-nil' if WINDOW exist.
|
||||
Otherwise return nil."
|
||||
(and window (window-live-p window)))
|
||||
|
||||
(defun multi-term-buffer-exist-p (buffer)
|
||||
"Return `non-nil' if `BUFFER' exist.
|
||||
Otherwise return nil."
|
||||
(and buffer (buffer-live-p buffer)))
|
||||
|
||||
(defun multi-term-dedicated-window-p ()
|
||||
"Return `non-nil' if current window is `multi-term' dedicated window.
|
||||
Otherwise return nil."
|
||||
(equal (multi-term-dedicated-get-buffer-name) (buffer-name (window-buffer))))
|
||||
|
||||
(defun multi-term-window-dedicated-only-one-p ()
|
||||
"Only have one non-dedicated window."
|
||||
(interactive)
|
||||
(let ((window-number 0)
|
||||
(dedicated-window-number 0))
|
||||
(walk-windows
|
||||
(lambda (w)
|
||||
(with-selected-window w
|
||||
(incf window-number)
|
||||
(if (window-dedicated-p w)
|
||||
(incf dedicated-window-number)))))
|
||||
(if (and (> dedicated-window-number 0)
|
||||
(= (- window-number dedicated-window-number) 1))
|
||||
t nil)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Advice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(defadvice delete-other-windows (around multi-term-delete-other-window-advice activate)
|
||||
"This is advice to make `multi-term' avoid dedicated window deleted.
|
||||
Dedicated window can't deleted by command `delete-other-windows'."
|
||||
(let ((multi-term-dedicated-active-p (multi-term-window-exist-p multi-term-dedicated-window)))
|
||||
(if multi-term-dedicated-active-p
|
||||
(let ((current-window (selected-window)))
|
||||
(dolist (win (window-list))
|
||||
(when (and (window-live-p win)
|
||||
(not (eq current-window win))
|
||||
(not (window-dedicated-p win)))
|
||||
(delete-window win))))
|
||||
ad-do-it)))
|
||||
|
||||
(defadvice delete-window (before multi-term-delete-window-advice activate)
|
||||
"Use `delete-window' delete `multi-term' dedicated window.
|
||||
Have same effect as command `multi-term-dedicated-close'.
|
||||
This advice to remember `multi-term' dedicated window height before deleting."
|
||||
;; Remember window height before deleted.
|
||||
(multi-term-dedicated-remember-window-height))
|
||||
|
||||
(defadvice pop-to-buffer (before multi-term-pop-to-buffer-advice activate)
|
||||
"This advice fix the problem between `pop-to-buffer' and dedicated window.
|
||||
By default, function `display-buffer' can't display buffer in selected window
|
||||
if current window is `dedicated'.
|
||||
|
||||
So function `display-buffer' conflicts with `sr-speedbar' window, because
|
||||
`sr-speedbar' window is a `dedicated' window.
|
||||
|
||||
That is to say, when current frame just have one `non-dedicated' window,
|
||||
any functions that uses `display-buffer' can't split windows
|
||||
to display buffer, even when the option `pop-up-windows' is enabled.
|
||||
|
||||
And the example function that can induce the problem is `pop-to-buffer'.
|
||||
|
||||
This advice will fix this problem when current frame just have one `non-dedicated' window."
|
||||
(when (and pop-up-windows ;`pop-up-windows' is enable
|
||||
(multi-term-window-dedicated-only-one-p) ;just have one `non-dedicated' window.
|
||||
(multi-term-window-exist-p multi-term-dedicated-window)
|
||||
(not (multi-term-dedicated-window-p))) ;not in `sr-speedbar' window
|
||||
(split-window-vertically)
|
||||
(windmove-down)))
|
||||
|
||||
(defadvice other-window (after multi-term-dedicated-other-window-advice)
|
||||
"Default, can use `other-window' select window in cyclic ordering of windows.
|
||||
But sometimes we don't want to select `sr-speedbar' window,
|
||||
but use `other-window' and just make `multi-term' dedicated
|
||||
window as a viewable sidebar.
|
||||
|
||||
This advice can make `other-window' skip `multi-term' dedicated window."
|
||||
(let ((count (or (ad-get-arg 0) 1)))
|
||||
(when (and (multi-term-window-exist-p multi-term-dedicated-window)
|
||||
(eq multi-term-dedicated-window (selected-window)))
|
||||
(other-window count))))
|
||||
|
||||
(provide 'multi-term)
|
||||
|
||||
;; Local Variables:
|
||||
;; time-stamp-line-limit: 10
|
||||
;; time-stamp-start: "Last-Updated: <"
|
||||
;; time-stamp-end: ">"
|
||||
;; End:
|
||||
|
||||
;;; multi-term.el ends here
|
||||
|
||||
;;; LocalWords: multi el dir sr Hawley eb ef cd
|
||||
109
xdg/emacs/mode-line.el
Normal file
109
xdg/emacs/mode-line.el
Normal file
@ -0,0 +1,109 @@
|
||||
;; Taken from http://amitp.blogspot.com/2011/08/emacs-custom-mode-line.html
|
||||
|
||||
;; Mode line setup
|
||||
(setq-default
|
||||
mode-line-format
|
||||
'(; Position, including warning for 80 columns
|
||||
(:propertize "%3l:" face mode-line-position-face)
|
||||
(:eval (propertize "%3c" 'face
|
||||
(if (>= (current-column) 80)
|
||||
(if (>= (current-column) 100)
|
||||
'mode-line-100col-face
|
||||
'mode-line-80col-face)
|
||||
'mode-line-position-face)))
|
||||
; emacsclient [default -- keep?]
|
||||
mode-line-client
|
||||
" "
|
||||
; read-only or modified status
|
||||
(:eval
|
||||
(cond (buffer-read-only
|
||||
(propertize " RO " 'face 'mode-line-read-only-face))
|
||||
((buffer-modified-p)
|
||||
(propertize " ** " 'face 'mode-line-modified-face))
|
||||
(t " ")))
|
||||
" "
|
||||
; directory and buffer/file name
|
||||
(:propertize (:eval (shorten-directory default-directory 30))
|
||||
face mode-line-folder-face)
|
||||
(:propertize "%b"
|
||||
face mode-line-filename-face)
|
||||
; narrow [default -- keep?]
|
||||
" %n "
|
||||
; mode indicators: vc, recursive edit, major mode, minor modes, process, global
|
||||
(vc-mode vc-mode)
|
||||
" %["
|
||||
(:propertize mode-name
|
||||
face mode-line-mode-face)
|
||||
"%] "
|
||||
(:eval (propertize (format-mode-line minor-mode-alist)
|
||||
'face 'mode-line-face))
|
||||
(:propertize mode-line-process
|
||||
face mode-line-process-face)
|
||||
(global-mode-string global-mode-string)
|
||||
" "
|
||||
))
|
||||
|
||||
;; Helper function
|
||||
(defun shorten-directory (dir max-length)
|
||||
"Show up to `max-length' characters of a directory name `dir'."
|
||||
(let ((path (reverse (split-string (abbreviate-file-name dir) "/")))
|
||||
(output ""))
|
||||
(when (and path (equal "" (car path)))
|
||||
(setq path (cdr path)))
|
||||
(while (and path (< (length output) (- max-length 4)))
|
||||
(setq output (concat (car path) "/" output))
|
||||
(setq path (cdr path)))
|
||||
(when path
|
||||
(setq output (concat ".../" output)))
|
||||
output))
|
||||
|
||||
;; Extra mode line faces
|
||||
(make-face 'mode-line-read-only-face)
|
||||
(make-face 'mode-line-modified-face)
|
||||
(make-face 'mode-line-folder-face)
|
||||
(make-face 'mode-line-filename-face)
|
||||
(make-face 'mode-line-position-face)
|
||||
(make-face 'mode-line-mode-face)
|
||||
(make-face 'mode-line-process-face)
|
||||
(make-face 'mode-line-80col-face)
|
||||
(make-face 'mode-line-100col-face)
|
||||
|
||||
(set-face-attribute 'mode-line-read-only-face nil
|
||||
:inherit 'mode-line-face
|
||||
:foreground "#4271ae")
|
||||
|
||||
(set-face-attribute 'mode-line-modified-face nil
|
||||
:inherit 'mode-line-face
|
||||
:foreground "#c82829")
|
||||
|
||||
(set-face-attribute 'mode-line-folder-face nil
|
||||
:inherit 'mode-line-face
|
||||
:foreground "gray90")
|
||||
|
||||
(set-face-attribute 'mode-line-filename-face nil
|
||||
:inherit 'mode-line-face
|
||||
:foreground "grey70"
|
||||
:weight 'bold)
|
||||
|
||||
(set-face-attribute 'mode-line-position-face nil
|
||||
:inherit 'mode-line-face
|
||||
:family "Roboto mono"
|
||||
:foreground "grey70"
|
||||
:weight 'bold
|
||||
:height 100)
|
||||
|
||||
(set-face-attribute 'mode-line-mode-face nil
|
||||
:inherit 'mode-line-face
|
||||
:foreground "gray100")
|
||||
|
||||
(set-face-attribute 'mode-line-process-face nil
|
||||
:inherit 'mode-line-face
|
||||
:foreground "#718c00")
|
||||
|
||||
(set-face-attribute 'mode-line-80col-face nil
|
||||
:inherit 'mode-line-position-face
|
||||
:foreground "white" :background "#eab700")
|
||||
|
||||
(set-face-attribute 'mode-line-100col-face nil
|
||||
:inherit 'mode-line-position-face
|
||||
:foreground "white" :background "red")
|
||||
27
xdg/emacs/modes/c-mode.el
Normal file
27
xdg/emacs/modes/c-mode.el
Normal file
@ -0,0 +1,27 @@
|
||||
;; New Keybindings
|
||||
(define-key c-mode-base-map (kbd "<f11>") 'compile)
|
||||
(define-key c-mode-base-map "\C-c\C-c" 'compile)
|
||||
(define-key c-mode-base-map (kbd "RET") 'newline-and-indent)
|
||||
(define-key c-mode-base-map (kbd "C-c SPC") 'hs-toggle-hiding)
|
||||
|
||||
;; Syntax hilighting
|
||||
(setq c-default-style "stroustrup")
|
||||
|
||||
;; Bury the compile buffer when finished compiling. Function in lisp/functions.el
|
||||
(add-hook 'compilation-finish-functions 'bury-compile-buffer-if-successful)
|
||||
|
||||
;; Define Tab width
|
||||
(setq truncate-lines 1)
|
||||
(setq tab-width 4)
|
||||
(setq indent-tabs-mode nil)
|
||||
|
||||
;; Make backspace and C-d delete whitespace
|
||||
(c-toggle-hungry-state t)
|
||||
|
||||
;; When using a new enough version of emacs, use many gdb windows.
|
||||
(when (or (> emacs-major-version 23)
|
||||
(and (= emacs-major-version 23)
|
||||
(>= emacs-minor-version 2)))
|
||||
;; Use the GDB visual debugging moden
|
||||
(setq gdb-many-windows t)
|
||||
)
|
||||
7
xdg/emacs/modes/go-mode-custom.el
Normal file
7
xdg/emacs/modes/go-mode-custom.el
Normal file
@ -0,0 +1,7 @@
|
||||
;; Bury the compile buffer when finished compiling. Function in lisp/functions.el
|
||||
(add-hook 'compilation-finish-functions 'bury-compile-buffer-if-successful)
|
||||
|
||||
(add-hook 'before-save-hook #'gofmt-before-save)
|
||||
|
||||
;; Define Tab width
|
||||
(setq truncate-lines t)
|
||||
13
xdg/emacs/modes/mode-hooks.el
Normal file
13
xdg/emacs/modes/mode-hooks.el
Normal file
@ -0,0 +1,13 @@
|
||||
; Hooks to enable custom modes
|
||||
(add-to-list 'auto-mode-alist '("\\.cu\\'" . c++-mode))
|
||||
|
||||
; Settings for Text mode
|
||||
(add-hook 'text-mode-hook 'auto-fill-mode)
|
||||
(add-hook 'text-mode-hook 'flyspell-mode)
|
||||
|
||||
; Settings for C++ mode (and I guess C as well)
|
||||
(add-hook 'c-mode-common-hook (lambda () (load "~/.emacs.d/modes/c-mode.el")))
|
||||
(add-hook 'c-mode-common-hook 'hs-minor-mode)
|
||||
|
||||
;; Terraform mode hooks
|
||||
(add-hook 'terraform-mode-hook 'terraform-format-on-save-mode)
|
||||
49
xdg/emacs/packages.el
Normal file
49
xdg/emacs/packages.el
Normal file
@ -0,0 +1,49 @@
|
||||
;; This file made during the switch from package.el to straight.el
|
||||
|
||||
(defvar bootstrap-version)
|
||||
(let ((bootstrap-file
|
||||
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
|
||||
(bootstrap-version 5))
|
||||
(unless (file-exists-p bootstrap-file)
|
||||
(with-current-buffer
|
||||
(url-retrieve-synchronously
|
||||
"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
|
||||
'silent 'inhibit-cookies)
|
||||
(goto-char (point-max))
|
||||
(eval-print-last-sexp)))
|
||||
(load bootstrap-file nil 'nomessage))
|
||||
|
||||
|
||||
;; Attempting to make emacs not take forever to start up
|
||||
|
||||
(setq straight-check-for-modifications '(watch-files))
|
||||
|
||||
(straight-use-package 'use-package)
|
||||
(straight-use-package 'expand-region)
|
||||
(straight-use-package 'popup)
|
||||
(straight-use-package 'json-mode)
|
||||
(straight-use-package 'auto-complete)
|
||||
(straight-use-package 'auto-highlight-symbol)
|
||||
;; (straight-use-package 'pushbullet)
|
||||
(straight-use-package 'php-mode)
|
||||
(straight-use-package 'magit)
|
||||
(straight-use-package 'tree-sitter)
|
||||
(straight-use-package 'tree-sitter-langs)
|
||||
(straight-use-package 'lsp-mode)
|
||||
(straight-use-package 'elixir-mode)
|
||||
(straight-use-package 'nginx-mode)
|
||||
(straight-use-package 'markdown-mode)
|
||||
(straight-use-package 'terraform-mode)
|
||||
(straight-use-package 'nix-mode)
|
||||
(straight-use-package 'shader-mode)
|
||||
(straight-use-package 'dracula-theme)
|
||||
(straight-use-package 'nix-mode)
|
||||
(straight-use-package 'python-black)
|
||||
(straight-use-package 'rust-mode)
|
||||
(straight-use-package 'palette)
|
||||
(straight-use-package
|
||||
'(unity :type git :host github :repo "elizagamedev/unity.el"))
|
||||
(add-hook 'after-init-hook #'unity-mode)
|
||||
(straight-use-package 'gdscript-mode)
|
||||
;; (straight-use-package
|
||||
;; '(copilot :type git :host github :repo "zerolfx/copilot.el" :files ("dist" "*.el") :ensure t))
|
||||
Reference in New Issue
Block a user