diff --git a/home.nix b/home.nix index ec96c12..8a1be4d 100644 --- a/home.nix +++ b/home.nix @@ -71,4 +71,8 @@ dashboard: ); defaultEditor = true; }; + xdg.configFile."emacs" = { + source = ./xdg/emacs; + recursive = true; + }; } diff --git a/xdg/emacs/bindings.el b/xdg/emacs/bindings.el new file mode 100644 index 0000000..afb6b31 --- /dev/null +++ b/xdg/emacs/bindings.el @@ -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))) diff --git a/xdg/emacs/custom.el b/xdg/emacs/custom.el new file mode 100644 index 0000000..3187c08 --- /dev/null +++ b/xdg/emacs/custom.el @@ -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))))) diff --git a/xdg/emacs/init.el b/xdg/emacs/init.el new file mode 100644 index 0000000..4ae8ef0 --- /dev/null +++ b/xdg/emacs/init.el @@ -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-") 'copilot-accept-completion) +;; (define-key copilot-mode-map (kbd "M-") 'copilot-next-completion) +;; (define-key copilot-mode-map (kbd "M-") 'copilot-previous-completion) +;; (define-key copilot-mode-map (kbd "M-") '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"))))) + )) diff --git a/xdg/emacs/lisp/functions.el b/xdg/emacs/lisp/functions.el new file mode 100644 index 0000000..79690bf --- /dev/null +++ b/xdg/emacs/lisp/functions.el @@ -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.")))) diff --git a/xdg/emacs/lisp/go-mode-load.el b/xdg/emacs/lisp/go-mode-load.el new file mode 100644 index 0000000..3fc35c1 --- /dev/null +++ b/xdg/emacs/lisp/go-mode-load.el @@ -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 diff --git a/xdg/emacs/lisp/go-mode.el b/xdg/emacs/lisp/go-mode.el new file mode 100644 index 0000000..79f7a26 --- /dev/null +++ b/xdg/emacs/lisp/go-mode.el @@ -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) diff --git a/xdg/emacs/lisp/multi-term.el b/xdg/emacs/lisp/multi-term.el new file mode 100644 index 0000000..780e382 --- /dev/null +++ b/xdg/emacs/lisp/multi-term.el @@ -0,0 +1,794 @@ +;;; multi-term.el --- Managing multiple terminal buffers in Emacs. + +;; Author: Andy Stewart +;; Maintainer: ahei +;; 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 +;; For create multi-shell.el +;; Aaron S. Hawley +;; 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" "") + "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 diff --git a/xdg/emacs/mode-line.el b/xdg/emacs/mode-line.el new file mode 100644 index 0000000..e8a5a52 --- /dev/null +++ b/xdg/emacs/mode-line.el @@ -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") diff --git a/xdg/emacs/modes/c-mode.el b/xdg/emacs/modes/c-mode.el new file mode 100644 index 0000000..08564dc --- /dev/null +++ b/xdg/emacs/modes/c-mode.el @@ -0,0 +1,27 @@ +;; New Keybindings +(define-key c-mode-base-map (kbd "") '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) + ) diff --git a/xdg/emacs/modes/go-mode-custom.el b/xdg/emacs/modes/go-mode-custom.el new file mode 100644 index 0000000..b02551e --- /dev/null +++ b/xdg/emacs/modes/go-mode-custom.el @@ -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) diff --git a/xdg/emacs/modes/mode-hooks.el b/xdg/emacs/modes/mode-hooks.el new file mode 100644 index 0000000..932e1e3 --- /dev/null +++ b/xdg/emacs/modes/mode-hooks.el @@ -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) diff --git a/xdg/emacs/packages.el b/xdg/emacs/packages.el new file mode 100644 index 0000000..f496633 --- /dev/null +++ b/xdg/emacs/packages.el @@ -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))