diff --git a/.gitignore b/.gitignore index 4f86348..36a7586 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +elpa +eshell backup/ *.elc diff --git a/.gitmodules b/.gitmodules index 8ac246e..71004b4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,3 @@ [submodule "stuff/go-mode.el"] path = stuff/go.el url = https://github.com/dominikh/go-mode.el.git -[submodule "stuff/dockerfile-mode"] - path = stuff/dockerfile-mode - url = https://github.com/spotify/dockerfile-mode -[submodule "stuff/sass-mode"] - path = stuff/sass-mode - url = https://github.com/nex3/sass-mode diff --git a/configs/hooks.el b/configs/hooks.el index d07aa65..ff59073 100644 --- a/configs/hooks.el +++ b/configs/hooks.el @@ -1,20 +1,20 @@ ; Delete trailing whitespaces on save -(add-hook 'write-file-hooks 'delete-trailing-whitespace) +(add-hook 'before-save-hook 'delete-trailing-whitespace) ;; Mode to collapse code block (add-hook 'c-mode-common-hook (lambda () (hs-minor-mode 1))) -(add-hook 'lisp-mode-hooks (lambda () (hs-minor-mode 1))) -(add-hook 'java-mode-hooks (lambda () (hs-minor-mode 1))) -(add-hook 'python-mode-hooks (lambda () (hs-minor-mode 1))) +(add-hook 'lisp-mode-hook (lambda () (hs-minor-mode 1))) +(add-hook 'java-mode-hook (lambda () (hs-minor-mode 1))) +(add-hook 'python-mode-hook (lambda () (hs-minor-mode 1))) ; Auto insert C/C++ header guard -(add-hook 'find-file-hooks +(add-hook 'find-file-hook (lambda () (when (and (memq major-mode '(c-mode c++-mode)) (equal (point-min) (point-max)) (string-match ".*\\.hh?" (buffer-file-name))) (insert-header-guard) (goto-line 3) (insert "\n")))) -(add-hook 'find-file-hooks +(add-hook 'find-file-hook (lambda () (when (and (memq major-mode '(c-mode c++-mode)) (equal (point-min) (point-max)) (string-match ".*\\.cc?" (buffer-file-name))) (insert-header-inclusion)))) @@ -28,18 +28,7 @@ (insert-shebang-if-empty "/usr/bin/ruby"))) ; Golang +; Modern Go configuration now in packages.el using eglot and go-mode (add-hook 'go-mode-hook (lambda () (set (make-local-variable 'compile-command) "go build"))) - -(add-hook 'go-mode-hook - (lambda () - (when (buffer-empty-p) - (save-excursion - (goto-char (point-min)) - (insert "package \n\nimport (\n\t\n)\n"))))) - -(add-hook 'before-save-hook 'gofmt-before-save) - -(eval-after-load "go-mode" - '(require 'flymake-go)) diff --git a/configs/key-binding.el b/configs/key-binding.el index 4133af7..54e2f9d 100644 --- a/configs/key-binding.el +++ b/configs/key-binding.el @@ -41,10 +41,6 @@ (global-set-key (kbd "C-c k") 'kill-this-buffer) (put 'narrow-to-region 'disabled nil) -;; Magit -(global-set-key (kbd "C-x g") 'magit-status) -(global-set-key (kbd "C-x M-g") 'magit-dispatch-popup) - ;; Don't shift-selection (setq shift-select-mode nil) diff --git a/configs/modes.el b/configs/modes.el index c092bc6..eb2adad 100644 --- a/configs/modes.el +++ b/configs/modes.el @@ -18,18 +18,76 @@ (add-to-list 'auto-mode-alist '("\\.bbclass$" . conf-mode)) (add-to-list 'auto-mode-alist '("\\.bbappend$" . conf-mode)) -;; Go mode -(require 'go-mode-autoloads) - ;; Changelog mode (add-to-list 'auto-mode-alist '("COMMIT_EDITMSG" . change-log-mode)) -;; Edje-mode -(require 'edje-mode) -(add-to-list 'auto-mode-alist '("\\.edc$" . edje-mode)) +;; web-mode +;; Now configured in packages.el with LSP support and prettier auto-formatting + ;(require 'web-mode) + ;(add-to-list 'auto-mode-alist '("\\.svelte$" . web-mode)) + ;(setq web-mode-code-indent-offset 4) + ;(setq web-mode-css-indent-offset 4) + ;(setq web-mode-markup-indent-offset 4) + ;(setq web-mode-script-padding 4) + ;(setq web-mode-style-padding 4) + ;(setq web-mode-void-elements + ; '("area" "base" "br" "command" "embed" "hr" "img" "input" "keygen" + ; "link" "meta" "param" "source" "track" "wbr" "tmpl_var")) + +;; Vue.js-mode +(require 'vue-mode) +(add-to-list 'auto-mode-alist '("\\.vue$" . vue-mode)) + ;(setq mmm-js-mode-enter-hook (lambda () (setq syntax-ppss-table nil))) + ;(setq mmm-typescript-mode-enter-hook (lambda () (setq syntax-ppss-table nil))) +(defun fix-mmm-syntax () + (save-restriction + (setq-local syntax-ppss-table typescript-mode-syntax-table) + )) +(add-hook 'mmm-typescript-mode-enter-hook 'fix-mmm-syntax) +;; js-indent-level now configured in packages.el (set to 4 spaces) +;(setq js-indent-level 2) +(setq vue-html-extra-indent 2) +(setq-default indent-tabs-mode nil) + +(add-hook 'mmm-mode-hook + (lambda () + (set-face-background 'mmm-default-submode-face nil) + (add-to-list 'sgml-tag-alist '("b-col" \n ("cols" "6") ("sm" "6") ("md" "6") ("lg" "6") ("xl" "6") ("offset" "6") ("offset-sm" "6") ("offset-md" "6") ("offset-lg" "6") ("offset-xl" "6") ("order" "1") ("order-sm" "1") ("order-md" "1") ("order-lg" "1") ("order-xl" "1") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-container" \n ("fluid") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-row" \n ("no-gutters") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-form-row" \n ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-alert" \n ("variant" "info") ("dismissible") ("dismiss-label" "Close") ("show") ("fade") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-badge" \n ("href" "#") ("rel" "") ("target" "_self") ("active") ("disabled") ("to" "") ("append") ("replace") ("event" "click") ("active-class" "") ("exact") ("exact-active-class" "") ("router-tag" "a") ("no-prefetch") ("tag" "span") ("variant" "info") ("pill") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-breadcrumb" \n ("items" "") (":items" "{}") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-button" \n ("href" "#") ("rel" "") ("target" "_self") ("active") ("disabled") ("to" "") ("append") ("replace") ("event" "click") ("active-class" "") ("exact") ("exact-active-class" "") ("router-tag" "a") ("no-prefetch") ("block") ("size" "sm") ("variant" "info") ("type" "button") ("tag" "button") ("pill") ("squared") ("pressed") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-button-group" \n ("vertical") ("size" "sm") ("tag" "div") ("aria-role" "group") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-button-toolbar" \n ("justify") ("key-nav") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-calendar" \n ("id" "") ("value" "") ("value-as-date") ("initial-date" "") ("disabled") ("readonly") ("min" "") ("max" "") ("date-disabled-fn" "") ("start-weekday" "0") ("locale" "") ("direction" "") ("selected-variant" "primary") ("today-variant" "") ("no-highlight-today") ("date-info-fn" "") ("width" "270px") ("block") ("hide-header") ("show-decade-nav") ("hidden") ("aria-controls" "") ("role-description" "") ("label-prev-decade" "Previous decade") ("label-prev-year" "Previous year") ("label-prev-month" "Previous month") ("label-current-month" "Current month") ("label-next-month" "Next month") ("label-next-year" "Next year") ("label-next-decade" "Next decade") ("label-today" "Today") ("label-selected" "Selected date") ("label-no-date-selected" "No date selected") ("label-calendar" "Calendar") ("label-nav" "Calendar navigation") ("label-help" "Use cursor keys to navigate calendar dates") ("date-format-options" "{}") ("weekday-header-format" "short") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-form" \n ("id" "") ("inline") ("novalidate") ("validated") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-form-text" \n ("id" "") ("tag" "small") ("inline") ("text-variant" "muted") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-form-invalid-feedback" \n ("id" "") ("tag" "small") ("tooltip") ("force-show") ("state") ("aria-live" "") ("role" "") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-form-valid-feedback" \n ("id" "") ("tag" "small") ("tooltip") ("force-show") ("state") ("aria-live" "") ("role" "") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-form-datalist" \n (":options" "{}") ("value-field" "value") ("text-field" "text") ("html-field" "html") ("disabled-field" "disabled") ("id" "") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-form-input" \n ("id" "") ("name" "") ("disabled") ("required") ("form" "") ("autofocus") ("size" "sm") ("state") ("value" "") ("aria-invalid") ("readonly") ("plaintext") ("autocomplete" "") ("placeholder" "") ("formatter" "") ("lazy-formatter") ("trim") ("number") ("lazy") ("debounce" "0") ("type" "text") ("no-wheel") ("min" "") ("max" "") ("step" "") ("list" "{}") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-input-group" \n ("id" "") ("size" "sm") ("prepend" "") ("prepend-html" "") ("append" "") ("append-html" "") ("tag" "div") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-input-group-prepend" \n ("id" "") ("tag" "div") ("is-text") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-input-group-append" \n ("id" "") ("tag" "div") ("is-text") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-input-group-text" \n ("tag" "div") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-input-group-addon" \n ("id" "") ("tag" "div") ("is-text") ("append") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-nav" \n ("tag" "ul") ("fill") ("justified") ("align" "") ("tabs") ("pills") ("vertical") ("small") ("card-header") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-nav-item" \n ("href" "") ("rel" "") ("target" "") ("active") ("disabled") ("to" "") ("append") ("replace") ("event" "") ("active-class" "") ("exact") ("exact-active-class" "") ("router-tag" "a") ("no-prefetch") ("link-attrs" "{}") ("link-classes" "") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-nav-text" \n ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-nav-form" \n ("id" "") ("novalidate") ("validated") ("form-class" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-nav-item-dropdown" \n ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-navbar" \n ("tag" "nav") ("type" "light") ("variant" "") ("toggleable") ("fixed" "") ("sticky") ("print") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-navbar-nav" \n ("tag" "ul") ("fill") ("justified") ("align" "") ("small") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-navbar-brand" \n ("href" "#") ("ref" "") ("target" "_self") ("active") ("disabled") ("to" "") ("replace") ("event" "click") ("active-class" "") ("exact") ("exact-active-class" "") ("router-tag" "a") ("no-prefetch") ("tag" "div") ("v-model" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-navbar-toggle" \n ("label" "") ("target" "") ("v-if" "") ("v-for" ""))) + (add-to-list 'sgml-tag-alist '("b-spinner" \n ("type" "border") ("label" "") ("variant" "primary") ("small") ("role" "status") ("tag" "span") ("v-if" "") ("v-for" ""))) + ) + ) ;; Org-mode -(require 'org-install) (add-to-list 'auto-mode-alist '("\\.org$" . org-mode)) (define-key global-map "\C-cl" 'org-store-link) (define-key global-map "\C-ca" 'org-agenda) @@ -43,22 +101,3 @@ ;; Markdown-mode (add-to-list 'auto-mode-alist '("\\.md$" . markdown-mode)) - -;; Python-mode -;(when (load "flymake" t) -; (defun flymake-pyflakes-init () -; (let* ((temp-file (flymake-init-create-temp-buffer-copy -; 'flymake-create-temp-inplace)) -; (local-file (file-relative-name -; temp-file -; (file-name-directory buffer-file-name)))) -; (list "pyflakes" (list local-file)))) -; -; (add-to-list 'flymake-allowed-file-name-masks -; '("\\.py\\'" flymake-pyflakes-init))) -;(add-hook 'find-file-hook 'flymake-find-file-hook) - -;; mmm-mode -(add-hook 'mmm-mode-hook - (lambda () - (set-face-background 'mmm-default-submode-face nil))) diff --git a/configs/project.el b/configs/project.el deleted file mode 100644 index 9df1e67..0000000 --- a/configs/project.el +++ /dev/null @@ -1,17 +0,0 @@ -(setq project-roots - `(("Django project" - :root-contains-files ("manage.py") - :filename-regex ,(regexify-ext-list '(py html css js sh)) - :exclude-paths '("contrib")))) - -(global-set-key (kbd "C-c p f") 'project-root-find-file) -(global-set-key (kbd "C-c p g") 'project-root-grep) -(global-set-key (kbd "C-c p a") 'project-root-ack) -(global-set-key (kbd "C-c p d") 'project-root-goto-root) -(global-set-key (kbd "C-c p l") 'project-root-browse-seen-projects) - -(global-set-key (kbd "C-c p s") - (lambda () (interactive) - (with-project-root - (ansi-term (getenv "SHELL") - (concat (car project-details) "-shell"))) diff --git a/init.el b/init.el index 40ba2b3..8d2f090 100644 --- a/init.el +++ b/init.el @@ -3,6 +3,45 @@ ;; ;; Made by Némunaire +(require 'site-gentoo) + +(require 'package) +(add-to-list 'package-archives + '("MELPA Stable" . "https://stable.melpa.org/packages/") t) + +;; Added by Package.el. This must come before configurations of +;; installed packages. Don't delete this line. If you don't want it, +;; just comment it out by adding a semicolon to the start of the line. +;; You may delete these explanatory comments. + +;; Initialize package system but don't activate packages yet +(setq package-enable-at-startup nil) +(package-initialize) + +;; Register system-installed vterm in package-alist +;; (site-gentoo only adds to load-path, not package-alist) +(unless (assq 'vterm package-alist) + (let ((desc (package-desc-create + :name 'vterm + :version '(0 0 2) + :summary "Emulation of a terminal" + :reqs '((emacs (25 1))) + :kind 'dir + :dir "/usr/share/emacs/site-lisp/vterm"))) + (push (cons 'vterm (list desc)) package-alist))) + +;; Now activate all packages (including claude-code which will find vterm) +(package-activate-all) + +;; Bootstrap use-package +(unless (package-installed-p 'use-package) + (package-refresh-contents) + (package-install 'use-package)) +(require 'use-package) +(setq use-package-always-ensure t) + +(setq flymake-allowed-file-name-masks nil) + (defun may-load (path) "Load a file if it exists." (when (file-readable-p path) @@ -42,11 +81,10 @@ (require 'my-layout) (require 'my-lisp-mode) (require 'my-python-mode) -(require 'vue-mode) ;; load my configuration files -(toc:load-config-file '("key-binding" -;; "project" +(toc:load-config-file '("packages" ; Modern package management (load first) + "key-binding" "editing" "coding-style" "tags" @@ -56,3 +94,8 @@ "custom" "perso" )) +(custom-set-variables + '(package-selected-packages nil) + '(python-indent 4) + '(query-user-mail-address nil) + '(user-mail-address "nemunaire@nemunai.re")) diff --git a/stuff/edje-mode.el b/stuff/edje-mode.el deleted file mode 100644 index 33c6fe6..0000000 --- a/stuff/edje-mode.el +++ /dev/null @@ -1,512 +0,0 @@ -;;; edje-mode-el -- Major mode for editing Edje files - -;; Author: Gustavo Sverzut Barbieri -;; Created: 2007-07-23 -;; Keywords: Edje major-mode -;; Url: http://barbieri-playground.googlecode.com/svn/dot-files/edje-mode.el -;; (if you find this file have problems, check that Url and request update) - -;; Copyright (C) 2007 Gustavo Sverzut Barbieri - -;; 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 2 of -;; the License, 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; if not, write to the Free -;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, -;; MA 02111-1307 USA - -;;; Commentary: -;; -;; This mode is based on tutorial from Scott Andrew Borton: -;; http://two-wugs.net/emacs/mode-tutorial.html - - -(defvar edje-mode-hook nil) - -(defun number-or-nil-to-string (v &optional default) - (cond ((numberp v) (number-to-string v)) - ((stringp v) (if (string= v "") (number-to-string default) v)) - (t (number-to-string default)))) - -(defun non-empty-string (s) - (and (not (eq 'nil s)) - (not (string= "" s)))) - -(defun edje-new-program-action-signal-emit (source emission) - "Insert new program SIGNAL_EMIT" - (interactive "ssource: \nsemission: ") - (insert - (concat - " action: SIGNAL_EMIT \"" source "\" \"" emission "\";\n" - ))) - -(defun edje-new-program-action-state-set (state value target) - "Insert new program STATE_SET" - (interactive "sstate: \nsvalue (0.0): \nstarget: ") - (insert - (concat - " action: STATE_SET \"" state "\" " - (number-or-nil-to-string value 0.0) ";\n" - " target: \"" target "\";\n" - ))) - -(defun edje-new-program-action (action) - "Insert new program action" - (interactive "saction: ") - (setq action (upcase action)) - (cond ((string= action "STATE_SET") - (edje-new-program-action-state-set "" 0.0 "")) - ((string= action "SIGNAL_EMIT") - (edje-new-program-action-signal-emit "" "")) - )) - -(defun edje-new-program (name signal source action) - "Insert new program block" - (interactive "sname: \nssignal: \nssource: \nsaction: ") - (insert - (concat - "\n" - " program {\n" - " name: \"" name "\";\n" - - (if (non-empty-string signal) - (concat " signal: \"" signal "\";\n")) - - (if (non-empty-string source) - (concat " source: \"" source "\";\n")) - )) - - (edje-new-program-action action) - - (insert - (concat - " }\n" - "\n" - ))) - -(defun edje-new-desc-relative (x y &optional defx defy) - "Insert new part description 'relative' line" - (interactive "sx: \nsy: ") - (insert - (concat - " relative: " - (number-or-nil-to-string x defx) " " - (number-or-nil-to-string y defy) ";\n" - ))) - -(defun edje-new-desc-offset (x y &optional defx defy) - "Insert new part description 'offset' line" - (interactive "sx: \nsy: ") - (insert - (concat - " offset: " - (number-or-nil-to-string x defx) " " - (number-or-nil-to-string y defy) ";\n" - ))) - -(defun edje-new-desc-inherit (name val) - "Insert new part description 'inherit' line" - (interactive "sname: \nsvalue: ") - (insert - (concat - " inherit: \"" name "\" " - (number-or-nil-to-string val 0.0) ";\n" - ))) - -(defun edje-new-desc-text (font size text) - "Insert new part description 'text' block" - (interactive "sfont: \nssize: \nstext: ") - (insert - (concat - " text {\n" - " font: \"" font "\";\n" - " size: " (number-or-nil-to-string size) ";\n" - " text: \"" text "\";\n" - " }\n" - ))) - -(defun edje-new-desc-image (name) - "Insert new part description 'image' block" - (interactive "sname: ") - (insert - (concat - " image {\n" - " normal: \"" name "\";\n" - " }\n" - ))) - -(defun edje-new-desc-color (r g b a &optional defr defg defb defa) - "Insert new part description 'color' line" - (interactive "sred: \nsgreen: \nsblue: \nsalpha: ") - (insert - (concat - " color: " - (number-or-nil-to-string r defr) " " - (number-or-nil-to-string g defg) " " - (number-or-nil-to-string b defb) " " - (number-or-nil-to-string a defa) ";\n" - ))) - -(defun edje-new-desc (name val &optional - r1_rx r1_ry - r2_rx r2_ry - r1_ox r1_oy - r2_ox r2_oy - part_type) - "Insert new part description block" - (interactive "sName: \nsValue: ") - (insert - (concat - " description {\n" - " state: \"" name "\" " (number-or-nil-to-string val 0.0) ";\n")) - (if (string= part_type "RECT") (edje-new-desc-color 255 255 255 255)) - (insert " rel1 {\n") - (edje-new-desc-relative r1_rx r1_ry 0.0 0.0) - (edje-new-desc-offset r1_ox r1_oy 0 0) - (insert - (concat - " }\n" - " rel2 {\n" - )) - (edje-new-desc-relative r2_rx r2_ry 1.0 1.0) - (edje-new-desc-offset r2_ox r2_oy -1 -1) - (insert " }\n") - (cond ((string= part_type "IMAGE") (edje-new-desc-image "")) - ((string= part_type "TEXT") (edje-new-desc-text "" 10 "contents")) - ) - (insert " }\n") - ) - -(defun edje-new-part (name type &optional - r1_rx r1_ry - r2_rx r2_ry - r1_ox r1_oy - r2_ox r2_oy) - "Insert new part" - (interactive "sName: \nsType: ") - (setq type (upcase type)) - (insert - (concat - "\n" - " part {\n" - " name: \"" name "\";\n" - " type: " type ";\n" - " mouse_events: 0;\n" - )) - (edje-new-desc "default" 0.0 r1_rx r1_ry r2_rx r2_ry r1_ox r1_oy r2_ox r2_oy type) - (insert - (concat - " }\n" - ))) - -(defun edje-setup-compile () - (set (make-local-variable 'compile-command) - (concat "edje_cc " (buffer-file-name)) - )) - -(defun edje-cc () - "Runs edje_cc with current buffer." - (interactive) - (compile (edje-setup-compile))) - -(defvar edje-mode-map - (let ((edje-mode-map (make-sparse-keymap))) - (define-key edje-mode-map "\C-j" 'newline-and-indent) - (define-key edje-mode-map "\C-cp" 'edje-new-part) - (define-key edje-mode-map "\C-cd" 'edje-new-desc) - (define-key edje-mode-map "\C-cr" 'edje-new-desc-relative) - (define-key edje-mode-map "\C-co" 'edje-new-desc-offset) - (define-key edje-mode-map "\C-ch" 'edje-new-desc-inherit) - (define-key edje-mode-map "\C-cc" 'edje-new-desc-color) - (define-key edje-mode-map "\C-ci" 'edje-new-desc-image) - (define-key edje-mode-map "\C-ct" 'edje-new-desc-text) - (define-key edje-mode-map "\C-cg" 'edje-new-program) - (define-key edje-mode-map "\C-ca" 'edje-new-program-action) - (define-key edje-mode-map "\C-cs" 'edje-new-program-action-state-set) - (define-key edje-mode-map "\C-ce" 'edje-new-program-action-signal-emit) - edje-mode-map) - "Keymap for Edje major mode") - -(add-hook 'c-mode-hook 'edje-setup-compile) -(add-to-list 'auto-mode-alist '("\\.edc$" . edje-mode)) - -(defconst edje-font-lock-keywords-1 - (eval-when-compile - (list - (list (concat "[ \t]*\\<" - (regexp-opt - '( - "collections" - "data" - "description" - "dragable" - "fill" - "fonts" - "group" - "image" - "images" - "origin" - "part" - "parts" - "program" - "programs" - "rel1" - "rel2" - "script" - "spectra" - "style" - "styles" - "text" - ) t) "\\>\\([ \t]*{\\|\\.\\)") - '(1 font-lock-function-name-face)) - - )) - "Major keywords") - -(defconst edje-font-lock-keywords-2 - (eval-when-compile - (append edje-font-lock-keywords-1 - (list - (list - (concat "^\\([ \t]*\\|[ \t]*[a-z]+\\.\\|\\)\\<" - (regexp-opt - '("action" - "after" - "alias" - "align" - "angle" - "aspect" - "aspect_preference" - "base" - "border" - "clip_to" - "collections" - "color" - "color2" - "color3" - "color_class" - "color_classes" - "confine" - "data" - "description" - "dragable" - "effect" - "elipsis" - "events" - "fill" - "fit" - "fixed" - "font" - "fonts" - "gradient" - "group" - "ignore_flags" - "image" - "images" - "in" - "inherit" - "item" - "max" - "middle" - "min" - "mouse_events" - "name" - "normal" - "offset" - "origin" - "part" - "parts" - "pointer_mode" - "precise_is_inside" - "program" - "programs" - "rel1" - "rel2" - "relative" - "repeat_events" - "signal" - "size" - "smooth" - "source" - "spectra" - "spectrum" - "spread" - "state" - "step" - "style" - "styles" - "tag" - "target" - "text" - "text_class" - "text_source" - "to" - "to_x" - "to_y" - "transition" - "tween" - "type" - "use_alternate_font_metrics" - "visible" - "x" - "y" - ) t) "\\>[ \t]*[:,]") - '(2 font-lock-keyword-face)) - ))) - "Minor keywords") - -(defconst edje-font-lock-keywords-3 - (eval-when-compile - (append edje-font-lock-keywords-2 - (list - (list - (concat "\\<" - (regexp-opt - '(; image options (st_images_image) - "RAW" - "COMP" - "LOSSY" - "USER" - ; part types (st_collections_group_parts_part_type) - "NONE" - "RECT" - "TEXT" - "IMAGE" - "SWALLOW" - "TEXTBLOCK" - "GRADIENT" - "GROUP" - ; ignore flags (st_collections_group_parts_part_ignore_flags) - ;"NONE" - "ON_HOLD" - ; pointer mode (st_collections_group_parts_part_pointer_mode) - "AUTOGRAB" - "NOGRAB" - ; aspect (st_collections_group_parts_part_description_aspect_preference) - "NONE" - "VERTICAL" - "HORIZONTAL" - "BOTH" - ; text effect (st_collections_group_parts_part_effect) - "NONE" - "PLAIN" - "OUTLINE" - "SOFT_OUTLINE" - "SHADOW" - "SOFT_SHADOW" - "OUTLINE_SHADOW" - "OUTLINE_SOFT_SHADOW" - "FAR_SHADOW" - "FAR_SOFT_SHADOW" - "GLOW" - ; image fill (st_collections_group_parts_part_description_fill_type) - "SCALE" - "TILE" - ; program action (st_collections_group_programs_program_action) - "STATE_SET" - "ACTION_STOP" - "SIGNAL_EMIT" - "DRAG_VAL_SET" - "DRAG_VAL_STEP" - "DRAG_VAL_PAGE" - "SCRIPT" - ; program transition (st_collections_group_programs_program_transition) - "LINEAR" - "SINUSOIDAL" - "ACCELERATE" - "DECELERATE" - ) t) "\\>") - '(1 font-lock-builtin-face)) - ))) - "Enumerate values") - -(defconst edje-font-lock-keywords-4 - (eval-when-compile - (append edje-font-lock-keywords-3 - (list - (list - (concat "[ \t]*#" - (regexp-opt - '("if" - "ifdef" - "ifndef" - "define" - "else" - "endif" - "include" - "undef") t) "[ \t]*") - '(1 font-lock-builtin-face)) - ))) - "CPP directives") - -(defconst edje-font-lock-keywords-5 - (eval-when-compile - (append edje-font-lock-keywords-4 - (list - (list "[ \t]*#undef[ \t]+\\([a-zA-Z_][a-zA-Z0-9_]*\\)" - '(1 font-lock-variable-name-face)) - (list "[ \t]*#define[ \t]+\\([a-zA-Z_][a-zA-Z0-9_]*\\)(" - '(1 font-lock-function-name-face)) - (list "[ \t]*#define[ \t]+\\([a-zA-Z_][a-zA-Z0-9_]*\\)" - '(1 font-lock-variable-name-face)) - ))) - "CPP directives that define constants") - - -(defvar edje-font-lock-keywords edje-font-lock-keywords-5) - -(defvar edje-mode-syntax-table - (let ((edje-mode-syntax-table (make-syntax-table))) - ; This is added so entity names with underscores can be more easily parsed - (modify-syntax-entry ?_ "w" edje-mode-syntax-table) - (modify-syntax-entry ?/ ". 124b" edje-mode-syntax-table) - (modify-syntax-entry ?* ". 23" edje-mode-syntax-table) - (modify-syntax-entry ?\n "> b" edje-mode-syntax-table) - - edje-mode-syntax-table) - "Syntax table for edje-mode") - -(c-add-style - "edje" - '("gnu" - (indent-tabs-mode . nil) - (tab-width . 8) - (c-basic-offset . 3) - (c-backslash-column . 72) - (c-hanging-braces-alist . - ((block-open after) - (brace-list-open after) - (substatement-open after)) - ) - (c-offsets-alist . - ((statement-block-intro . +) - (defun-open . 0) - (substatement-open . 0) - (defun-block-intro . +) - (block-open . 0) - (label . +) - )))) - - -(define-derived-mode edje-mode c-mode "Edje" - "Major mode for editing Edje files" - (interactive) - (use-local-map edje-mode-map) - (set-syntax-table edje-mode-syntax-table) - (set (make-local-variable 'font-lock-defaults) '(edje-font-lock-keywords)) - (set (make-local-variable 'require-final-newline) t) - (c-set-style "edje") - (run-hooks 'edje-mode-hook) - ) - -(provide 'edje-mode) - -;;; edje-mode.el ends here diff --git a/stuff/find-cmd.el b/stuff/find-cmd.el deleted file mode 100644 index 877b96c..0000000 --- a/stuff/find-cmd.el +++ /dev/null @@ -1,242 +0,0 @@ -;;; find-cmd.el --- Build a valid find(1) command with sexps - -;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. - -;; Author: Philip Jackson -;; Version: 0.6 - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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 GNU Emacs. If not, see . - -;;; Commentary: - -;; With this module you can build up a (hopefully) valid find(1) -;; string ready for the command line. For example: - -;; (find-cmd '(prune (name ".svn" ".git" ".CVS")) -;; '(and (or (name "*.pl" "*.pm" "*.t") -;; (mtime "+1")) -;; (fstype "nfs" "ufs")))) - -;; will become (un-wrapped): - -;; "find '/home/phil/' \\( \\( -name '.svn' -or -name '.git' -or -;; -name '.CVS' \\) -prune -or -true \\) \\( \\( \\( -name '*.pl' -;; -or -name '*.pm' -or -name '*.t' \\) -or -mtime '+1' \\) -and \\( -;; -fstype 'nfs' -or -fstype 'ufs' \\) \\)" - -;;; Code: - -(defconst find-constituents - '((and . find-and) - (not . find-not) - (or . find-or) - - (a . find-and) - (n . find-not) - (o . find-or) - - (prune . find-prune) - - ;; switches - (L . (0)) - (P . (0)) - (H . (0)) - - ;; generic tests - (amin . (1)) - (anewer . (1)) - (atime . (1)) - (cmin . (1)) - (cnewer . (1)) - (ctime . (1)) - (empty . (0)) - (false . (0)) - (fstype . (1)) - (gid . (1)) - (group . (1)) - (ilname . (1)) - (iname . (1)) - (inum . (1)) - (iwholename . (1)) - (iregex . (1)) - (links . (1)) - (lname . (1)) - (mmin . (1)) - (mtime . (1)) - (name . (1)) - (newer . (1)) - (nouser . (0)) - (nogroup . (0)) - (path . (1)) - (perm . (0)) - (regex . (1)) - (wholename . (1)) - (size . (1)) - (true . (0)) - (type . (1)) - (uid . (1)) - (used . (1)) - (user . (1)) - (xtype . (nil)) - - ;; normal options (always true) - (depth . (0)) - (maxdepth . (1)) - (mindepth . (1)) - (mount . (0)) - (noleaf . (0)) - (xdev . (0)) - (ignore_readdir_race . (0)) - (noignore_readdir_race . (0)) - - ;; actions - (delete . (0)) - (print0 . (0)) - (printf . (1)) - (fprintf . (2)) - (print . (0)) - (fprint0 . (1)) - (fprint . (1)) - (ls . (0)) - (fls . (1)) - (prune . (0)) - (quit . (0)) - - ;; these need to be terminated with a ; - (exec . (1 find-command t)) - (ok . (1 find-command t)) - (execdir . (1 find-command t)) - (okdir . (1 find-command t))) - "Holds details of each of the find options. The car of each -alist is the name. The cdr is minimum args, the function used -to join many occurences of the argument together, and whether or -not to leave quotes off the string (non-nil means the string will -be quoted).") - -;;;###autoload -(defun find-cmd (&rest subfinds) - "Initiate the building of a find command. For exmple: - -\(find-cmd '\(prune \(name \".svn\" \".git\" \".CVS\"\)\) - '\(and \(or \(name \"*.pl\" \"*.pm\" \"*.t\"\) - \(mtime \"+1\"\)\) - \(fstype \"nfs\" \"ufs\"\)\)\)\) - -`default-directory' is used as the initial search path. The -result is a string that should be ready for the command line." - (concat - "find " (shell-quote-argument (expand-file-name default-directory)) " " - (cond - ((cdr subfinds) - (mapconcat 'find-to-string subfinds "")) - (t - (find-to-string (car subfinds)))))) - -(defun find-and (form) - "And FORMs together, so: - \(and \(mtime \"+1\"\) \(name \"something\"\)\) -will produce: - find . \\\( -mtime '+1' -and -name 'something' \\\)" - (if (< (length form) 2) - (find-to-string (car form)) - (concat "\\( " - (mapconcat 'find-to-string form "-and ") - "\\) "))) - -(defun find-or (form) - "Or FORMs together, so: - \(or \(mtime \"+1\"\) \(name \"something\"\)\) -will produce: - find . \\\( -mtime '+1' -or -name 'something' \\\)" - (if (< (length form) 2) - (find-to-string (car form)) - (concat "\\( " - (mapconcat 'find-to-string form "-or ") - "\\) "))) - -(defun find-not (form) - "Or FORMs together and prefix with a -not, so: - \(not \(mtime \"+1\"\) \(name \"something\"\)\) -will produce: - -not \\\( -mtime '+1' -or -name 'something' \\\) -If you wanted the FORMs -and(ed) together instead then this would -suffice: - \(not \(and \(mtime \"+1\"\) \(name \"something\"\)\)\)" - (concat "-not " (find-or (mapcar 'find-to-string form)))) - -(defun find-prune (form) - "-or together FORM(s) postfix '-prune' and then -or that with a --true, so: - \(prune \(name \".svn\" \".git\"\)\) \(name \"*.pm\"\) -will produce (unwrapped): - \\\( \\\( \\\( -name '.svn' -or -name '.git' \\\) / - -prune -or -true \\\) -and -name '*.pm' \\\)" - (find-or - (list - (concat (find-or (mapcar 'find-to-string form)) (find-generic "prune")) - (find-generic "true")))) - -(defun find-generic (option &optional oper argcount args dont-quote) - "This function allows an arbitrary string to be used as a -form. OPTION is the name of the form, OPER is the function used -to either OR or AND multiple results together. ARGCOUNT is the -minimum of args that OPTION can receive and ARGS are the -arguments for OPTION." - (when (and (numberp argcount) (< (length args) argcount)) - (error "'%s' needs at least %d arguments" option argcount)) - (let ((oper (or oper 'find-or))) - (if (and args (length args)) - (funcall oper (mapcar (lambda (x) - (concat "-" option - (if dont-quote - (concat " " x " ") - (concat " " - (shell-quote-argument x) - " ")))) - args)) - (concat "-" option " ")))) - -(defun find-command (form) - "For each item in FORM add a terminating semi-colon and turn -them into valid switches. The result is -and(ed) together." - (find-and (mapcar (lambda (x) - (concat (find-to-string x) "\\; ")) - form))) - -(defun find-to-string (form) - "Parse FORM to produce a set of valid find arguments." - (cond - ((stringp form) - form) - ((consp form) - (let ((option (cdr (assoc (car form) find-constituents)))) - (cond - ((and (symbolp option) (fboundp option)) - (funcall option (cdr form))) - ((consp option) - (let ((option (symbol-name (car form))) - (argcnt (car option)) - (oper (cadr option)) - (dont-quote (car (cddr option)))) - (find-to-string - (find-generic option oper argcnt (cdr form) dont-quote)))) - (t - (error "Sorry I don't know how to handle '%s'" (car form)))))))) - -(provide 'find-cmd) - -;; arch-tag: 9687fd9e-4e90-4022-864a-f904526e2046 -;;; find-cmd.el ends here diff --git a/stuff/flymake-cursor.el b/stuff/flymake-cursor.el deleted file mode 100644 index 0f0b8c5..0000000 --- a/stuff/flymake-cursor.el +++ /dev/null @@ -1,167 +0,0 @@ -;;; flymake-cursor.el --- displays flymake error msg in minibuffer after delay -;; -;; Author : ?? -;; origin : http://paste.lisp.org/display/60617,1/raw -;; Maintainer : Dino Chiesa -;; : Donald Curtis -;; Created : May 2011 -;; Modified : December 2012 -;; Version : 0.1.5 -;; Keywords : languages mode flymake -;; X-URL : http://www.emacswiki.org/emacs/flymake-cursor.el -;; Last-saved : <2012-Dec-20 09:49:28> -;; -;; ------------------------------------------------------- -;; -;; License: None. This code is in the Public Domain. -;; -;; -;; Additional functionality that makes flymake error messages appear -;; in the minibuffer when point is on a line containing a flymake -;; error. This saves having to mouse over the error, which is a -;; keyboard user's annoyance. -;; ------------------------------------------------------- -;; -;; This flymake-cursor module displays the flymake error in the -;; minibuffer, after a short delay. It is based on code I found roaming -;; around on the net, unsigned and unattributed. I suppose it's public -;; domain, because, while there is a "License" listed in it, there -;; is no license holder, no one to own the license. -;; -;; This version is modified slightly from that code. The post-command fn -;; defined in this code does not display the message directly. Instead -;; it sets a timer, and when the timer fires, the timer event function -;; displays the message. -;; -;; The reason to do this: the error message is displayed only if the -;; user doesn't do anything, for about one second. This way, if the user -;; scrolls through a buffer and there are myriad errors, the minibuffer -;; is not constantly being updated. -;; -;; If the user moves away from the line with the flymake error message -;; before the timer expires, then no error is displayed in the minibuffer. -;; -;; I've also updated the names of the defuns. They all start with flyc now. -;; -;; To use this, include this line in your .emacs: -;; -;; ;; enhancements for displaying flymake errors -;; (require 'flymake-cursor) -;; -;; You can, of course, put that in an eval-after-load clause. -;; -;; ------------------------------------------------------- -;; -;; Update 2012-03-06 by Donald Curtis -;; -- -;; Added some autoload statements and the closing comment to make -;; compatible with package.el parser. -;; -;; Update 2012-12-20 by Jeremy Moore -;; -- -;; Alter post-command-hook's local value via add-hook so that it plays -;; nicely with other packages. -;; - - -(require 'cl) - -(defvar flyc--e-at-point nil - "Error at point, after last command") - -(defvar flyc--e-display-timer nil - "A timer; when it fires, it displays the stored error message.") - -(defun flyc/maybe-fixup-message (errore) - "pyflake is flakey if it has compile problems, this adjusts the -message to display, so there is one ;)" - (cond ((not (or (eq major-mode 'Python) (eq major-mode 'python-mode) t))) - ((null (flymake-ler-file errore)) - ;; normal message do your thing - (flymake-ler-text errore)) - (t ;; could not compile error - (format "compile error, problem on line %s" (flymake-ler-line errore))))) - -(defun flyc/show-stored-error-now () - "Displays the stored error in the minibuffer." - (interactive) - (let ((editing-p (= (minibuffer-depth) 0))) - (if (and flyc--e-at-point editing-p) - (progn - (message "%s" (flyc/maybe-fixup-message flyc--e-at-point)) - (setq flyc--e-display-timer nil))))) - - -(defun flyc/-get-error-at-point () - "Gets the first flymake error on the line at point." - (let ((line-no (line-number-at-pos)) - flyc-e) - (dolist (elem flymake-err-info) - (if (eq (car elem) line-no) - (setq flyc-e (car (second elem))))) - flyc-e)) - - -;;;###autoload -(defun flyc/show-fly-error-at-point-now () - "If the cursor is sitting on a flymake error, display -the error message in the minibuffer." - (interactive) - (if flyc--e-display-timer - (progn - (cancel-timer flyc--e-display-timer) - (setq flyc--e-display-timer nil))) - (let ((error-at-point (flyc/-get-error-at-point))) - (if error-at-point - (progn - (setq flyc--e-at-point error-at-point) - (flyc/show-stored-error-now))))) - - -;;;###autoload -(defun flyc/show-fly-error-at-point-pretty-soon () - "If the cursor is sitting on a flymake error, grab the error, -and set a timer for \"pretty soon\". When the timer fires, the error -message will be displayed in the minibuffer. - -This allows a post-command-hook to NOT cause the minibuffer to be -updated 10,000 times as a user scrolls through a buffer -quickly. Only when the user pauses on a line for more than a -second, does the flymake error message (if any) get displayed. - -" - (if flyc--e-display-timer - (cancel-timer flyc--e-display-timer)) - - (let ((error-at-point (flyc/-get-error-at-point))) - (if error-at-point - (setq flyc--e-at-point error-at-point - flyc--e-display-timer - (run-at-time "0.9 sec" nil 'flyc/show-stored-error-now)) - (setq flyc--e-at-point nil - flyc--e-display-timer nil)))) - - -;;;###autoload -(eval-after-load "flymake" - '(progn - - (defadvice flymake-goto-next-error (after flyc/display-message-1 activate compile) - "Display the error in the mini-buffer rather than having to mouse over it" - (flyc/show-fly-error-at-point-now)) - - (defadvice flymake-goto-prev-error (after flyc/display-message-2 activate compile) - "Display the error in the mini-buffer rather than having to mouse over it" - (flyc/show-fly-error-at-point-now)) - - (defadvice flymake-mode (before flyc/post-command-fn activate compile) - "Add functionality to the post command hook so that if the -cursor is sitting on a flymake error the error information is -displayed in the minibuffer (rather than having to mouse over -it)" - (add-hook 'post-command-hook 'flyc/show-fly-error-at-point-pretty-soon t t)))) - - -(provide 'flymake-cursor) - -;;; flymake-cursor.el ends here diff --git a/stuff/go-mode-autoloads.el b/stuff/go-mode-autoloads.el deleted file mode 100644 index 96b27d8..0000000 --- a/stuff/go-mode-autoloads.el +++ /dev/null @@ -1,98 +0,0 @@ -;;; go-mode-autoloads.el --- automatically extracted autoloads -;; -;;; Code: - - -;;;### (autoloads (go-download-play godoc gofmt-before-save go-mode) -;;;;;; "go-mode" "go-mode.el" (21514 38760 682820 85000)) -;;; 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 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' -- `go-coverage' - -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 github.com/rogpeppe/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 flycheck -\(see URL `https://github.com/flycheck/flycheck') or flymake in combination -with goflymake (see URL `https://github.com/dougm/goflymake'), gocode -\(see URL `https://github.com/nsf/gocode'), go-eldoc -\(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go -\(see URL `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-autoloads) -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; coding: utf-8 -;; End: -;;; go-mode-autoloads.el ends here diff --git a/stuff/go-mode.el b/stuff/go-mode.el deleted file mode 100644 index e569aa0..0000000 --- a/stuff/go-mode.el +++ /dev/null @@ -1,2009 +0,0 @@ -;;; go-mode.el --- Major mode for the Go programming language - -;;; Commentary: - -;; Copyright 2013 The go-mode Authors. All rights reserved. -;; Use of this source code is governed by a BSD-style -;; license that can be found in the LICENSE file. - -;; Author: The go-mode Authors -;; Version: 1.5.0 -;; Keywords: languages go -;; URL: https://github.com/dominikh/go-mode.el -;; -;; This file is not part of GNU Emacs. - -;;; Code: - -(require 'cl-lib) -(require 'compile) -(require 'etags) -(require 'ffap) -(require 'find-file) -(require 'ring) -(require 'url) -(require 'xref nil :noerror) ; xref is new in Emacs 25.1 - - -(eval-when-compile - (defmacro go--forward-word (&optional arg) - (if (fboundp 'forward-word-strictly) - `(forward-word-strictly ,arg) - `(forward-word ,arg)))) - -(defun go--delete-whole-line (&optional arg) - "Delete the current line without putting it in the `kill-ring'. -Derived from function `kill-whole-line'. ARG is defined as for that -function." - (setq arg (or arg 1)) - (if (and (> arg 0) - (eobp) - (save-excursion (forward-visible-line 0) (eobp))) - (signal 'end-of-buffer nil)) - (if (and (< arg 0) - (bobp) - (save-excursion (end-of-visible-line) (bobp))) - (signal 'beginning-of-buffer nil)) - (cond ((zerop arg) - (delete-region (progn (forward-visible-line 0) (point)) - (progn (end-of-visible-line) (point)))) - ((< arg 0) - (delete-region (progn (end-of-visible-line) (point)) - (progn (forward-visible-line (1+ arg)) - (unless (bobp) - (backward-char)) - (point)))) - (t - (delete-region (progn (forward-visible-line 0) (point)) - (progn (forward-visible-line arg) (point)))))) - -(defun go-goto-opening-parenthesis (&optional _legacy-unused) - "Move up one level of parentheses." - ;; The old implementation of go-goto-opening-parenthesis had an - ;; optional argument to speed up the function. It didn't change the - ;; function's outcome. - - ;; Silently fail if there's no matching opening parenthesis. - (condition-case nil - (backward-up-list) - (scan-error nil))) - - -(defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]") -(defconst go--max-dangling-operator-length 2 - "The maximum length of dangling operators. -This must be at least the length of the longest string matched by -‘go-dangling-operators-regexp.’, and must be updated whenever -that constant is changed.") - -(defconst go-identifier-regexp "[[:word:][:multibyte:]]+") -(defconst go-type-name-no-prefix-regexp "\\(?:[[:word:][:multibyte:]]+\\.\\)?[[:word:][:multibyte:]]+") -(defconst go-qualified-identifier-regexp (concat go-identifier-regexp "\\." go-identifier-regexp)) -(defconst go-label-regexp go-identifier-regexp) -(defconst go-type-regexp "[[:word:][:multibyte:]*]+") -(defconst go-func-regexp (concat "\\_\\s *\\(" go-identifier-regexp "\\)")) -(defconst go-func-meth-regexp (concat - "\\_\\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 "\\)")) - -;; Maximum number of identifiers that can be highlighted as type names -;; in one function type/declaration. -(defconst go--font-lock-func-param-num-groups 16) - -(defvar go-dangling-cache) -(defvar go-godoc-history nil) -(defvar go--coverage-current-file-name) - -(defgroup go nil - "Major mode for editing Go code." - :link '(url-link "https://github.com/dominikh/go-mode.el") - :group 'languages) - -(defgroup go-cover nil - "Options specific to `cover`." - :group 'go) - -(defgroup godoc nil - "Options specific to `godoc'." - :group 'go) - -(defcustom go-fontify-function-calls t - "Fontify function and method calls if this is non-nil." - :type 'boolean - :group 'go) - -(defcustom go-mode-hook nil - "Hook called by `go-mode'." - :type 'hook - :group 'go) - -(defcustom go-command "go" - "The 'go' command. -Some users have multiple Go development trees and invoke the 'go' -tool via a wrapper that sets GOROOT and GOPATH based on the -current directory. Such users should customize this variable to -point to the wrapper script." - :type 'string - :group 'go) - -(defcustom gofmt-command "gofmt" - "The 'gofmt' command. -Some users may replace this with 'goimports' -from https://golang.org/x/tools/cmd/goimports." - :type 'string - :group 'go) - -(defcustom gofmt-args nil - "Additional arguments to pass to gofmt." - :type '(repeat string) - :group 'go) - -(defcustom gofmt-show-errors 'buffer - "Where to display gofmt error output. -It can either be displayed in its own buffer, in the echo area, or not at all. - -Please note that Emacs outputs to the echo area when writing -files and will overwrite gofmt's echo output if used from inside -a `before-save-hook'." - :type '(choice - (const :tag "Own buffer" buffer) - (const :tag "Echo area" echo) - (const :tag "None" nil)) - :group 'go) - -(defcustom godef-command "godef" - "The 'godef' command." - :type 'string - :group 'go) - -(defcustom go-other-file-alist - '(("_test\\.go\\'" (".go")) - ("\\.go\\'" ("_test.go"))) - "See the documentation of `ff-other-file-alist' for details." - :type '(repeat (list regexp (choice (repeat string) function))) - :group 'go) - -(defcustom go-packages-function 'go-packages-native - "Function called by `go-packages' to determine the list of available packages. -This is used in e.g. tab completion in `go-import-add'. - -This package provides two functions: `go-packages-native' uses -elisp to find all .a files in all /pkg/ directories. -`go-packages-go-list' uses 'go list all' to determine all Go -packages. `go-packages-go-list' generally produces more accurate -results, but can be slower than `go-packages-native'." - :type 'function - :package-version '(go-mode . 1.4.0) - :group 'go) - -(defcustom go-guess-gopath-functions (list #'go-godep-gopath - #'go-wgo-gopath - #'go-gb-gopath - #'go-plain-gopath) - "Functions to call in sequence to detect a project's GOPATH. - -The functions in this list will be called one after another, -until a function returns non-nil. The order of the functions in -this list is important, as some project layouts may superficially -look like others. For example, a subset of wgo projects look like -gb projects. That's why we need to detect wgo first, to avoid -mis-identifying them as gb projects." - :type '(repeat function) - :group 'go) - -(defcustom godoc-command "go doc" - "Which executable to use for `godoc'. -This can either be 'godoc' or 'go doc', both as an absolute path -or an executable in PATH." - :type 'string - :group 'go) - -(defcustom godoc-and-godef-command "godoc" - "Which executable to use for `godoc' in `godoc-and-godef-command'. -Must be 'godoc' and not 'go doc' and can be an absolute path or -an executable in PATH." - :type 'string - :group 'go) - -(defcustom godoc-use-completing-read nil - "Provide auto-completion for godoc. -Only really desirable when using `godoc' instead of `go doc'." - :type 'boolean - :group 'godoc) - -(defcustom godoc-at-point-function #'godoc-and-godef - "Function to call to display the documentation for an -identifier at a given position. - -This package provides two functions: `godoc-and-godef' uses a -combination of godef and godoc to find the documentation. This -approach has several caveats. See its documentation for more -information. The second function, `godoc-gogetdoc' uses an -additional tool that correctly determines the documentation for -any identifier. It provides better results than -`godoc-and-godef'." - :type 'function - :group 'godoc) - -(defun godoc-and-godef (point) - "Use a combination of godef and godoc to guess the documentation at POINT. - -Due to a limitation in godoc, it is not possible to differentiate -between functions and methods, which may cause `godoc-at-point' -to display more documentation than desired. Furthermore, it -doesn't work on package names or variables. - -Consider using ‘godoc-gogetdoc’ instead for more accurate results." - (condition-case nil - (let* ((output (godef--call point)) - (file (car output)) - (name-parts (split-string (cadr output) " ")) - (first (car name-parts))) - (if (not (godef--successful-p file)) - (message "%s" (godef--error file)) - (go--godoc (format "%s %s" - (file-name-directory file) - (if (or (string= first "type") (string= first "const")) - (cadr name-parts) - (car name-parts))) - godoc-and-godef-command))) - (file-error (message "Could not run godef binary")))) - -(defun godoc-gogetdoc (point) - "Use the gogetdoc tool to find the documentation for an identifier at POINT. - -You can install gogetdoc with 'go get -u github.com/zmb3/gogetdoc'." - (if (not (buffer-file-name (go--coverage-origin-buffer))) - ;; TODO: gogetdoc supports unsaved files, but not introducing - ;; new artifical files, so this limitation will stay for now. - (error "Cannot use gogetdoc on a buffer without a file name")) - (let ((posn (format "%s:#%d" (shell-quote-argument (file-truename buffer-file-name)) (1- (position-bytes point)))) - (out (godoc--get-buffer ""))) - (with-current-buffer (get-buffer-create "*go-gogetdoc-input*") - (setq buffer-read-only nil) - (erase-buffer) - (go--insert-modified-files) - (call-process-region (point-min) (point-max) "gogetdoc" nil out nil - "-modified" - (format "-pos=%s" posn))) - (with-current-buffer out - (goto-char (point-min)) - (godoc-mode) - (display-buffer (current-buffer) t)))) - -(defun go--kill-new-message (url) - "Make URL the latest kill and print a message." - (kill-new url) - (message "%s" url)) - -(defcustom go-play-browse-function 'go--kill-new-message - "Function to call with the Playground URL. -See `go-play-region' for more details." - :type '(choice - (const :tag "Nothing" nil) - (const :tag "Kill + Message" go--kill-new-message) - (const :tag "Browse URL" browse-url) - (function :tag "Call function")) - :group 'go) - -(defcustom go-coverage-display-buffer-func 'display-buffer-reuse-window - "How `go-coverage' should display the coverage buffer. -See `display-buffer' for a list of possible functions." - :type 'function - :group 'go-cover) - -(defface go-coverage-untracked - '((t (:foreground "#505050"))) - "Coverage color of untracked code." - :group 'go-cover) - -(defface go-coverage-0 - '((t (:foreground "#c00000"))) - "Coverage color for uncovered code." - :group 'go-cover) -(defface go-coverage-1 - '((t (:foreground "#808080"))) - "Coverage color for covered code with weight 1." - :group 'go-cover) -(defface go-coverage-2 - '((t (:foreground "#748c83"))) - "Coverage color for covered code with weight 2." - :group 'go-cover) -(defface go-coverage-3 - '((t (:foreground "#689886"))) - "Coverage color for covered code with weight 3." - :group 'go-cover) -(defface go-coverage-4 - '((t (:foreground "#5ca489"))) - "Coverage color for covered code with weight 4." - :group 'go-cover) -(defface go-coverage-5 - '((t (:foreground "#50b08c"))) - "Coverage color for covered code with weight 5." - :group 'go-cover) -(defface go-coverage-6 - '((t (:foreground "#44bc8f"))) - "Coverage color for covered code with weight 6." - :group 'go-cover) -(defface go-coverage-7 - '((t (:foreground "#38c892"))) - "Coverage color for covered code with weight 7." - :group 'go-cover) -(defface go-coverage-8 - '((t (:foreground "#2cd495"))) - "Coverage color for covered code with weight 8. -For mode=set, all covered lines will have this weight." - :group 'go-cover) -(defface go-coverage-9 - '((t (:foreground "#20e098"))) - "Coverage color for covered code with weight 9." - :group 'go-cover) -(defface go-coverage-10 - '((t (:foreground "#14ec9b"))) - "Coverage color for covered code with weight 10." - :group 'go-cover) -(defface go-coverage-covered - '((t (:foreground "#2cd495"))) - "Coverage color of covered code." - :group 'go-cover) - -(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 ?/ ". 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) - ;; TODO make _ a symbol constituent now that xemacs is gone - (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 GNU Emacs <24 - ;; doesn't understand that - (append - `((go--match-func - ,@(mapcar (lambda (x) `(,x font-lock-type-face)) - (number-sequence 1 go--font-lock-func-param-num-groups))) - (,(concat "\\_<" (regexp-opt go-mode-keywords t) "\\_>") . font-lock-keyword-face) - (,(concat "\\(\\_<" (regexp-opt go-builtins t) "\\_>\\)[[:space:]]*(") 1 font-lock-builtin-face) - (,(concat "\\_<" (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 "[^[:word:][:multibyte:]](\\(" go-identifier-regexp "\\))[[:space:]]*(") 1 font-lock-function-name-face)) ;; bracketed function call - `((,go-func-meth-regexp 2 font-lock-function-name-face))) ;; method name - - `( - ("\\(`[^`]*`\\)" 1 font-lock-multiline) ;; raw string literal, needed for font-lock-syntactic-keywords - (,(concat "\\_[[:space:]]+\\([^[:space:](]+\\)") 1 font-lock-type-face) ;; types - (,(concat "\\_[[: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-type-name-regexp) 1 font-lock-type-face) ;; map value type - (,(concat "\\_\\[" go-type-name-regexp) 1 font-lock-type-face) ;; map key type - (,(concat "\\_[[:space:]]*\\(?:<-[[:space:]]*\\)?" go-type-name-regexp) 1 font-lock-type-face) ;; channel type - (,(concat "\\_<\\(?: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 - ;; 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 "\\_<\\(goto\\|break\\|continue\\)\\_>[[:space:]]*\\(" go-label-regexp "\\)") 2 font-lock-constant-face)))) ;; labels in goto/break/continue - -(let ((m (define-prefix-command 'go-goto-map))) - (define-key m "a" #'go-goto-arguments) - (define-key m "d" #'go-goto-docstring) - (define-key m "f" #'go-goto-function) - (define-key m "i" #'go-goto-imports) - (define-key m "m" #'go-goto-method-receiver) - (define-key m "n" #'go-goto-function-name) - (define-key m "r" #'go-goto-return-values)) - -(defvar go-mode-map - (let ((m (make-sparse-keymap))) - (unless (boundp 'electric-indent-chars) - (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-x 4 C-c C-j") #'godef-jump-other-window) - (define-key m (kbd "C-c C-d") #'godef-describe) - (define-key m (kbd "C-c C-f") 'go-goto-map) - m) - "Keymap used by ‘go-mode’.") - -(easy-menu-define go-mode-menu go-mode-map - "Menu for Go mode." - '("Go" - ["Describe Expression" godef-describe t] - ["Jump to Definition" godef-jump t] - "---" - ["Add Import" go-import-add t] - ["Remove Unused Imports" go-remove-unused-imports t] - ["Go to Imports" go-goto-imports t] - "---" - ("Playground" - ["Send Buffer" go-play-buffer t] - ["Send Region" go-play-region t] - ["Download" go-download-play t]) - "---" - ["Coverage" go-coverage t] - ["Gofmt" gofmt t] - ["Godoc" godoc t] - "---" - ["Customize Mode" (customize-group 'go) t])) - -(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) - "Skip 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)) - (= (char-before) ?`) - (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 "\\*/" (line-beginning-position)) - (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 () - "Return 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 - (- (point) go--max-dangling-operator-length))) - (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--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) - (back-to-indentation) - (setq start-nesting (go-paren-level)) - - (cond - ((go-in-string-p) - (current-indentation)) - ((looking-at "[])}]") - (go-goto-opening-parenthesis) - (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 - (- (point) go--max-dangling-operator-length))) - ;; 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 - (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:")) - (cl-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 (bolp) - (end-of-line)) - (setq count (or count 1)) - (let (first failure) - (dotimes (i (abs count)) - (setq first t) - (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)) - ;; Find the { that starts the function, i.e., the next { that isn't - ;; preceded by struct or interface, or a comment or struct tag. BUG: - ;; breaks if there's a comment between the struct/interface keyword and - ;; bracket, like this: - ;; - ;; struct /* why? */ { - (while (progn - (skip-chars-forward "^{") - (forward-char) - (or (go-in-string-or-comment-p) - (looking-back "\\(struct\\|interface\\)\\s-*{" - (line-beginning-position))))) - (setq orig-level (go-paren-level)) - (while (>= (go-paren-level) orig-level) - (skip-chars-forward "^}") - (forward-char)))) - -(defun go--find-enclosing-parentheses (position) - "Return points of outermost '(' and ')' surrounding POSITION if -such parentheses exist. - -If outermost '(' exists but ')' does not, it returns the next blank -line or end-of-buffer position instead of the position of the closing -parenthesis. - -If the starting parenthesis is not found, it returns (POSITION -POSITION)." - (save-excursion - (let (beg end) - (goto-char position) - (while (> (go-paren-level) 0) - (re-search-backward "[(\\[{]" nil t) - (when (looking-at "(") - (setq beg (point)))) - (if (null beg) - (list position position) - (goto-char position) - (while (and (> (go-paren-level) 0) - (search-forward ")" nil t))) - (when (> (go-paren-level) 0) - (unless (re-search-forward "^[[:space:]]*$" nil t) - (goto-char (point-max)))) - (list beg (point)))))) - -(defun go--search-next-comma (end) - "Search forward from point for a comma whose nesting level is -the same as point. If it reaches the end of line or a closing -parenthesis before a comma, it stops at it." - (let ((orig-level (go-paren-level))) - (while (and (< (point) end) - (or (looking-at "[^,)\n]") - (> (go-paren-level) orig-level))) - (forward-char)) - (when (and (looking-at ",") - (< (point) (1- end))) - (forward-char)))) - -(defun go--looking-at-keyword () - (and (looking-at (concat "\\(" go-identifier-regexp "\\)")) - (member (match-string 1) go-mode-keywords))) - -(defun go--match-func (end) - "Search for identifiers used as type names from a function -parameter list, and set the identifier positions as the results -of last search. Return t if search succeeded." - (when (re-search-forward "\\_" end t) - (let ((regions (go--match-func-type-names end))) - (if (null regions) - ;; Nothing to highlight. This can happen if the current func - ;; is "func()". Try next one. - (go--match-func end) - ;; There are something to highlight. Set those positions as - ;; last search results. - (setq regions (go--filter-match-data regions end)) - (when regions - (set-match-data (go--make-match-data regions)) - t))))) - -(defun go--match-func-type-names (end) - (cond - ;; Function declaration (e.g. "func foo(") - ((looking-at (concat "[[:space:]\n]*" go-identifier-regexp "[[:space:]\n]*(")) - (goto-char (match-end 0)) - (nconc (go--match-parameter-list end) - (go--match-function-result end))) - ;; Method declaration, function literal, or function type - ((looking-at "[[:space:]]*(") - (goto-char (match-end 0)) - (let ((regions (go--match-parameter-list end))) - ;; Method declaration (e.g. "func (x y) foo(") - (when (looking-at (concat "[[:space:]]*" go-identifier-regexp "[[:space:]\n]*(")) - (goto-char (match-end 0)) - (setq regions (nconc regions (go--match-parameter-list end)))) - (nconc regions (go--match-function-result end)))))) - -(defun go--parameter-list-type (end) - "Return `present' if the parameter list has names, or `absent' if not. -Assumes point is at the beginning of a parameter list, just -after '('." - (save-excursion - (skip-chars-forward "[:space:]\n" end) - (cond ((> (point) end) - nil) - ((looking-at (concat go-identifier-regexp "[[:space:]\n]*,")) - (goto-char (match-end 0)) - (go--parameter-list-type end)) - ((or (looking-at go-qualified-identifier-regexp) - (looking-at (concat go-type-name-no-prefix-regexp "[[:space:]\n]*\\(?:)\\|\\'\\)")) - (go--looking-at-keyword) - (looking-at "[*\\[]\\|\\.\\.\\.\\|\\'")) - 'absent) - (t 'present)))) - -(defconst go--opt-dotdotdot-regexp "\\(?:\\.\\.\\.\\)?") -(defconst go--parameter-type-regexp - (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(" go-type-name-no-prefix-regexp "\\)[[:space:]\n]*\\([,)]\\|\\'\\)")) -(defconst go--func-type-in-parameter-list-regexp - (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(\\_" "\\)")) - -(defun go--match-parameters-common (identifier-regexp end) - (let ((acc ()) - (start -1)) - (while (progn (skip-chars-forward "[:space:]\n" end) - (and (not (looking-at "\\(?:)\\|\\'\\)")) - (< start (point)) - (<= (point) end))) - (setq start (point)) - (cond - ((looking-at (concat identifier-regexp go--parameter-type-regexp)) - (setq acc (nconc acc (list (match-beginning 1) (match-end 1)))) - (goto-char (match-beginning 2))) - ((looking-at (concat identifier-regexp go--func-type-in-parameter-list-regexp)) - (goto-char (match-beginning 1)) - (setq acc (nconc acc (go--match-func-type-names end))) - (go--search-next-comma end)) - (t - (go--search-next-comma end)))) - (when (and (looking-at ")") - (< (point) end)) - (forward-char)) - acc)) - -(defun go--match-parameters-with-identifier-list (end) - (go--match-parameters-common - (concat go-identifier-regexp "[[:space:]\n]+") - end)) - -(defun go--match-parameters-without-identifier-list (end) - (go--match-parameters-common "" end)) - -(defun go--filter-match-data (regions end) - "Remove points from REGIONS if they are beyond END. -REGIONS are a list whose size is multiple of 2. Element 2n is beginning of a -region and 2n+1 is end of it. - -This function is used to make sure we don't override end point -that `font-lock-mode' gave to us." - (when regions - (let* ((vec (vconcat regions)) - (i 0) - (len (length vec))) - (while (and (< i len) - (<= (nth i regions) end) - (<= (nth (1+ i) regions) end)) - (setq i (+ i 2))) - (cond ((= i len) - regions) - ((zerop i) - nil) - (t - (butlast regions (- (length regions) i))))))) - -(defun go--make-match-data (regions) - (let ((deficit (- (* 2 go--font-lock-func-param-num-groups) - (length regions)))) - (when (> deficit 0) - (let ((last (car (last regions)))) - (setq regions (nconc regions (make-list deficit last)))))) - `(,(car regions) ,@(last regions) ,@regions)) - -(defun go--match-parameter-list (end) - "Return a list of identifier positions that are used as type -names in a function parameter list, assuming point is at the -beginning of a parameter list. Return nil if the text after -point does not look like a parameter list. - -Set point to end of closing parenthesis on success. - -In Go, the names must either all be present or all be absent -within a list of parameters. - -Parsing a parameter list is a little bit complicated because we -have to scan through the parameter list to determine whether or -not the list has names. Until a type name is found or reaching -end of a parameter list, we are not sure which form the parameter -list is. - -For example, X and Y are type names in a parameter list \"(X, -Y)\" but are parameter names in \"(X, Y int)\". We cannot say if -X is a type name until we see int after Y. - -Note that even \"(int, float T)\" is a valid parameter -list. Builtin type names are not reserved words. In this example, -int and float are parameter names and only T is a type name. - -In this function, we first scan the parameter list to see if the -list has names, and then handle it accordingly." - (let ((name (go--parameter-list-type end))) - (cond ((eq name 'present) - (go--match-parameters-with-identifier-list end)) - ((eq name 'absent) - (go--match-parameters-without-identifier-list end)) - (t nil)))) - -(defun go--match-function-result (end) - "Return a list of identifier positions that are used as type -names in a function result, assuming point is at the beginning of -a result. - -Function result is a unparenthesized type or a parameter list." - (cond ((and (looking-at (concat "[[:space:]*]*\\(" go-type-name-no-prefix-regexp "\\)")) - (not (member (match-string 1) go-mode-keywords))) - (list (match-beginning 1) (match-end 1))) - ((looking-at "[[:space:]]*(") - (goto-char (match-end 0)) - (go--match-parameter-list end)) - (t nil))) - -(defun go--reset-dangling-cache-before-change (&optional _beg _end) - "Reset `go-dangling-cache'. - -This is intended to be called from `before-change-functions'." - (setq go-dangling-cache (make-hash-table :test 'eql))) - -;;;###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' and `godoc-at-point' -- `go-import-add' -- `go-remove-unused-imports' -- `go-goto-arguments' -- `go-goto-docstring' -- `go-goto-function' -- `go-goto-function-name' -- `go-goto-imports' -- `go-goto-return-values' -- `go-goto-method-receiver' -- `go-play-buffer' and `go-play-region' -- `go-download-play' -- `godef-describe' and `godef-jump' -- `go-coverage' -- `go-set-project' -- `go-reset-gopath' - -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 github.com/rogpeppe/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 flycheck -\(see URL `https://github.com/flycheck/flycheck') or flymake in combination -with goflymake \(see URL `https://github.com/dougm/goflymake'), gocode -\(see URL `https://github.com/nsf/gocode'), go-eldoc -\(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go -\(see URL `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) - (set (make-local-variable 'syntax-propertize-function) #'go-propertize-syntax) - - (if (boundp 'electric-indent-chars) - (set (make-local-variable 'electric-indent-chars) '(?\n ?} ?\)))) - - (set (make-local-variable 'compilation-error-screen-columns) nil) - - (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql)) - (add-hook 'before-change-functions #'go--reset-dangling-cache-before-change t t) - - ;; ff-find-other-file - (setq ff-other-file-alist 'go-other-file-alist) - - (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 that we add our entry to the beginning of - ;; compilation-error-regexp-alist. In older versions of Emacs, the - ;; list was processed from the end, and we would've wanted to add - ;; ours last. But at some point this changed, and now the list is - ;; processed from the beginning. It's important that our entry comes - ;; before gnu, because gnu matches go test output, but includes the - ;; leading whitespace in the file name. - ;; - ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-12/msg00674.html - ;; documents the old, reverseed order. - (when (and (boundp 'compilation-error-regexp-alist) - (boundp 'compilation-error-regexp-alist-alist)) - (add-to-list 'compilation-error-regexp-alist 'go-test) - (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 - (cl-decf line-offset len) - (goto-char (point-min)) - (forward-line (- from len line-offset)) - (insert text))))) - ((equal action "d") - (with-current-buffer target-buffer - (go--goto-line (- from line-offset)) - (cl-incf line-offset len) - (go--delete-whole-line len))) - (t - (error "Invalid rcs patch or internal error in go--apply-rcs-patch"))))))))) - -(defun gofmt--is-goimports-p () - (string-equal (file-name-base gofmt-command) "goimports")) - -(defun gofmt () - "Format the current buffer according to the gofmt tool." - (interactive) - (let ((tmpfile (make-temp-file "gofmt" nil ".go")) - (patchbuf (get-buffer-create "*Gofmt patch*")) - (errbuf (if gofmt-show-errors (get-buffer-create "*Gofmt Errors*"))) - (coding-system-for-read 'utf-8) - (coding-system-for-write 'utf-8) - our-gofmt-args) - - (unwind-protect - (save-restriction - (widen) - (if errbuf - (with-current-buffer errbuf - (setq buffer-read-only nil) - (erase-buffer))) - (with-current-buffer patchbuf - (erase-buffer)) - - (write-region nil nil tmpfile) - - (when (and (gofmt--is-goimports-p) buffer-file-name) - (setq our-gofmt-args - (append our-gofmt-args - ;; srcdir, despite its name, supports - ;; accepting a full path, and some features - ;; of goimports rely on knowing the full - ;; name. - (list "-srcdir" (file-truename buffer-file-name))))) - (setq our-gofmt-args (append our-gofmt-args - gofmt-args - (list "-w" tmpfile))) - (message "Calling gofmt: %s %s" gofmt-command our-gofmt-args) - ;; 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 (apply #'call-process gofmt-command nil errbuf nil our-gofmt-args)) - (progn - (if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile)) - (message "Buffer is already gofmted") - (go--apply-rcs-patch patchbuf) - (message "Applied gofmt")) - (if errbuf (gofmt--kill-error-buffer errbuf))) - (message "Could not apply gofmt") - (if errbuf (gofmt--process-errors (buffer-file-name) tmpfile errbuf)))) - - (kill-buffer patchbuf) - (delete-file tmpfile)))) - - -(defun gofmt--process-errors (filename tmpfile errbuf) - (with-current-buffer errbuf - (if (eq gofmt-show-errors 'echo) - (progn - (message "%s" (buffer-string)) - (gofmt--kill-error-buffer errbuf)) - ;; Convert the gofmt stderr to something understood by the compilation mode. - (goto-char (point-min)) - (if (save-excursion - (save-match-data - (search-forward "flag provided but not defined: -srcdir" nil t))) - (insert "Your version of goimports is too old and doesn't support vendoring. Please update goimports!\n\n")) - (insert "gofmt errors:\n") - (let ((truefile - (if (gofmt--is-goimports-p) - (concat (file-name-directory filename) (file-name-nondirectory tmpfile)) - tmpfile))) - (while (search-forward-regexp (concat "^\\(" (regexp-quote truefile) "\\):") nil t) - (replace-match (file-name-nondirectory filename) t t nil 1))) - (compilation-mode) - (display-buffer errbuf)))) - -(defun gofmt--kill-error-buffer (errbuf) - (let ((win (get-buffer-window errbuf))) - (if win - (quit-window t win) - (kill-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." - (if godoc-use-completing-read - (completing-read "godoc; " - (go-packages) nil nil nil 'go-godoc-history) - (read-from-minibuffer "godoc: " nil nil nil 'go-godoc-history))) - -(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)) - (godoc-mode) - (display-buffer (current-buffer) t)) - ((/= (process-exit-status proc) 0) ;; Error exit. - (let ((output (buffer-string))) - (kill-buffer (current-buffer)) - (message (concat "godoc: " output))))))) - -(define-derived-mode godoc-mode special-mode "Godoc" - "Major mode for showing Go documentation." - (view-mode-enter)) - -;;;###autoload -(defun godoc (query) - "Show Go documentation for QUERY, much like \\\\[man]." - (interactive (list (godoc--read-query))) - (go--godoc query godoc-command)) - -(defun go--godoc (query command) - (unless (string= query "") - (set-process-sentinel - (start-process-shell-command "godoc" (godoc--get-buffer query) - (concat command " " query)) - 'godoc--buffer-sentinel) - nil)) - -(defun godoc-at-point (point) - "Show Go documentation for the identifier at POINT. - -It uses `godoc-at-point-function' to look up the documentation." - (interactive "d") - (funcall godoc-at-point-function point)) - -(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 1) - 'block-empty) - ((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 between START and END to the Playground. -If non-nil `go-play-browse-function' is called with the -Playground URL." - (interactive "r") - (let* ((url-request-method "POST") - (url-request-extra-headers - '(("Content-Type" . "application/x-www-form-urlencoded"))) - (url-request-data - (encode-coding-string - (buffer-substring-no-properties start end) - 'utf-8)) - (content-buf (url-retrieve - "https://play.golang.org/share" - (lambda (arg) - (cond - ((equal :error (car arg)) - (signal 'go-play-error (cdr arg))) - (t - (re-search-forward "\n\n") - (let ((url (format "https://play.golang.org/p/%s" - (buffer-substring (point) (point-max))))) - (when go-play-browse-function - (funcall go-play-browse-function url))))))))))) - -;;;###autoload -(defun go-download-play (url) - "Download a paste from the playground and insert 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)))))) - -(defun go-import-add (arg import) - "Add a new IMPORT to the list of imports. - -When called with a prefix ARG 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-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)) - (cl-case (go-goto-imports) - ('fail (message "Could not find a place to add import.")) - ('block-empty - (insert "\n\t" line "\n")) - ('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 (process-lines go-command "env" "GOROOT" "GOPATH")) - (root (car output)) - (paths (split-string (cadr output) path-separator))) - (cons 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 () - (funcall go-packages-function)) - -(defun go-packages-native () - "Return a list of all installed Go packages. -It looks for archive files in /pkg/." - (sort - (delete-dups - (cl-mapcan - (lambda (topdir) - (let ((pkgdir (concat topdir "/pkg/"))) - (cl-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-packages-go-list () - "Return a list of all Go packages, using `go list'." - (process-lines go-command "list" "-e" "all")) - -(defun go-unused-imports-lines () - (reverse (remove nil - (mapcar - (lambda (line) - (when (string-match "^\\(.+\\):\\([[:digit:]]+\\): imported and not used: \".+\".*$" line) - (let ((error-file-name (match-string 1 line)) - (error-line-num (match-string 2 line))) - (if (string= (file-truename error-file-name) (file-truename buffer-file-name)) - (string-to-number error-line-num))))) - (split-string (shell-command-to-string - (concat go-command - (if (string-match "_test\\.go$" buffer-file-truename) - " test -c" - (concat " build -o " null-device)) - " -gcflags=-e" - " " - (shell-quote-argument (file-truename buffer-file-name)))) "\n"))))) - -(defun go-remove-unused-imports (arg) - "Remove 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) - (go--goto-line import) - (beginning-of-line) - (if arg - (comment-region (line-beginning-position) (line-end-position)) - (go--delete-whole-line))) - (message "Removed %d imports" (length lines))) - (if flymake-state (flymake-mode-on))))) - -(defun godef--find-file-line-column (specifier other-window) - "Given a file name in the format of `filename:line:column', -visit FILENAME and go to line LINE and column COLUMN." - (if (not (string-match "\\(.+\\):\\([0-9]+\\):\\([0-9]+\\)" specifier)) - ;; We've only been given a directory name - (funcall (if other-window #'find-file-other-window #'find-file) specifier) - (let ((filename (match-string 1 specifier)) - (line (string-to-number (match-string 2 specifier))) - (column (string-to-number (match-string 3 specifier)))) - (funcall (if other-window #'find-file-other-window #'find-file) filename) - (go--goto-line 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 (not (buffer-file-name (go--coverage-origin-buffer))) - (error "Cannot use godef on a buffer without a file name") - (let ((outbuf (generate-new-buffer "*godef*")) - (coding-system-for-read 'utf-8) - (coding-system-for-write 'utf-8)) - (prog2 - (call-process-region (point-min) - (point-max) - godef-command - nil - outbuf - nil - "-i" - "-t" - "-f" - (file-truename (buffer-file-name (go--coverage-origin-buffer))) - "-o" - (number-to-string (position-bytes point))) - (with-current-buffer outbuf - (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n")) - (kill-buffer outbuf))))) - -(defun godef--successful-p (output) - (not (or (string= "-" output) - (string= "godef: no identifier found" output) - (go--string-prefix-p "godef: no declaration found for " output) - (go--string-prefix-p "error finding import path for " output)))) - -(defun godef--error (output) - (cond - ((godef--successful-p output) - nil) - ((string= "-" output) - "godef: expression is not defined anywhere") - (t - output))) - -(defun godef-describe (point) - "Describe the expression at POINT." - (interactive "d") - (condition-case nil - (let ((description (cdr (butlast (godef--call point) 1)))) - (if (not description) - (message "No description found for expression at point") - (message "%s" (mapconcat #'identity description "\n")))) - (file-error (message "Could not run godef binary")))) - -(defun godef-jump (point &optional other-window) - "Jump to the definition of the expression at POINT." - (interactive "d") - (condition-case nil - (let ((file (car (godef--call point)))) - (if (not (godef--successful-p file)) - (message "%s" (godef--error file)) - (push-mark) - (if (eval-when-compile (fboundp 'xref-push-marker-stack)) - ;; TODO: Integrate this facility with XRef. - (xref-push-marker-stack) - (ring-insert find-tag-marker-ring (point-marker))) - (godef--find-file-line-column file other-window))) - (file-error (message "Could not run godef binary")))) - -(defun godef-jump-other-window (point) - (interactive "d") - (godef-jump point t)) - -(defun go--goto-line (line) - (goto-char (point-min)) - (forward-line (1- line))) - -(defun go--line-column-to-point (line column) - (save-excursion - (go--goto-line line) - (forward-char (1- column)) - (point))) - -(cl-defstruct go--covered - start-line start-column end-line end-column covered count) - -(defun go--coverage-file () - "Return the coverage file to use, either by reading it from the -current coverage buffer or by prompting for it." - (if (boundp 'go--coverage-current-file-name) - go--coverage-current-file-name - (read-file-name "Coverage file: " nil nil t))) - -(defun go--coverage-origin-buffer () - "Return the buffer to base the coverage on." - (or (buffer-base-buffer) (current-buffer))) - -(defun go--coverage-face (count divisor) - "Return the intensity face for COUNT when using DIVISOR -to scale it to a range [0,10]. - -DIVISOR scales the absolute cover count to values from 0 to 10. -For DIVISOR = 0 the count will always translate to 8." - (let* ((norm (cond - ((= count 0) - -0.1) ;; Uncovered code, set to -0.1 so n becomes 0. - ((= divisor 0) - 0.8) ;; covermode=set, set to 0.8 so n becomes 8. - (t - (/ (log count) divisor)))) - (n (1+ (floor (* norm 9))))) ;; Convert normalized count [0,1] to intensity [0,10] - (concat "go-coverage-" (number-to-string n)))) - -(defun go--coverage-make-overlay (range divisor) - "Create a coverage overlay for a RANGE of covered/uncovered code. -Use DIVISOR to scale absolute counts to a [0,10] scale." - (let* ((count (go--covered-count range)) - (face (go--coverage-face count divisor)) - (ov (make-overlay (go--line-column-to-point (go--covered-start-line range) - (go--covered-start-column range)) - (go--line-column-to-point (go--covered-end-line range) - (go--covered-end-column range))))) - - (overlay-put ov 'face face) - (overlay-put ov 'help-echo (format "Count: %d" count)))) - -(defun go--coverage-clear-overlays () - "Remove existing overlays and put a single untracked overlay -over the entire buffer." - (remove-overlays) - (overlay-put (make-overlay (point-min) (point-max)) - 'face - 'go-coverage-untracked)) - -(defun go--coverage-parse-file (coverage-file file-name) - "Parse COVERAGE-FILE and extract coverage information and -divisor for FILE-NAME." - (let (ranges - (max-count 0)) - (with-temp-buffer - (insert-file-contents coverage-file) - (go--goto-line 2) ;; Skip over mode - (while (not (eobp)) - (let* ((parts (split-string (buffer-substring (point-at-bol) (point-at-eol)) ":")) - (file (car parts)) - (rest (split-string (nth 1 parts) "[., ]"))) - - (cl-destructuring-bind - (start-line start-column end-line end-column num count) - (mapcar #'string-to-number rest) - - (when (string= (file-name-nondirectory file) file-name) - (if (> count max-count) - (setq max-count count)) - (push (make-go--covered :start-line start-line - :start-column start-column - :end-line end-line - :end-column end-column - :covered (/= count 0) - :count count) - ranges))) - - (forward-line))) - - (list ranges (if (> max-count 0) (log max-count) 0))))) - -(defun go-coverage (&optional coverage-file) - "Open a clone of the current buffer and overlay it with -coverage information gathered via go test -coverprofile=COVERAGE-FILE. - -If COVERAGE-FILE is nil, it will either be inferred from the -current buffer if it's already a coverage buffer, or be prompted -for." - (interactive) - (let* ((cur-buffer (current-buffer)) - (origin-buffer (go--coverage-origin-buffer)) - (gocov-buffer-name (concat (buffer-name origin-buffer) "")) - (coverage-file (or coverage-file (go--coverage-file))) - (ranges-and-divisor (go--coverage-parse-file - coverage-file - (file-name-nondirectory (buffer-file-name origin-buffer)))) - (cov-mtime (nth 5 (file-attributes coverage-file))) - (cur-mtime (nth 5 (file-attributes (buffer-file-name origin-buffer))))) - - (if (< (float-time cov-mtime) (float-time cur-mtime)) - (message "Coverage file is older than the source file.")) - - (with-current-buffer (or (get-buffer gocov-buffer-name) - (make-indirect-buffer origin-buffer gocov-buffer-name t)) - (set (make-local-variable 'go--coverage-current-file-name) coverage-file) - - (save-excursion - (go--coverage-clear-overlays) - (dolist (range (car ranges-and-divisor)) - (go--coverage-make-overlay range (cadr ranges-and-divisor)))) - - (if (not (eq cur-buffer (current-buffer))) - (display-buffer (current-buffer) `(,go-coverage-display-buffer-func)))))) - -(defun go-goto-function (&optional arg) - "Go to the function defintion (named or anonymous) surrounding point. - -If we are on a docstring, follow the docstring down. -If no function is found, assume that we are at the top of a file -and search forward instead. - -If point is looking at the func keyword of an anonymous function, -go to the surrounding function. - -If ARG is non-nil, anonymous functions are ignored." - (interactive "P") - (let ((p (point))) - (cond - ((save-excursion - (beginning-of-line) - (looking-at "^//")) - ;; In case we are looking at the docstring, move on forward until we are - ;; not anymore - (beginning-of-line) - (while (looking-at "^//") - (forward-line 1)) - ;; If we are still not looking at a function, retry by calling self again. - (when (not (looking-at "\\")) - (go-goto-function arg))) - - ;; If we're already looking at an anonymous func, look for the - ;; surrounding function. - ((and (looking-at "\\") - (not (looking-at "^func\\>"))) - (re-search-backward "\\" nil t)) - - ((not (looking-at "\\")) - ;; If point is on the "func" keyword, step back a word and retry - (if (string= (symbol-name (symbol-at-point)) "func") - (backward-word) - ;; If we are not looking at the beginning of a function line, do a regexp - ;; search backwards - (re-search-backward "\\" nil t)) - - ;; If nothing is found, assume that we are at the top of the file and - ;; should search forward instead. - (when (not (looking-at "\\")) - (re-search-forward "\\" nil t) - (go--forward-word -1)) - - ;; If we have landed at an anonymous function, it is possible that we - ;; were not inside it but below it. If we were not inside it, we should - ;; go to the containing function. - (while (and (not (go--in-function-p p)) - (not (looking-at "^func\\>"))) - (go-goto-function arg))))) - - (cond - ((go-in-comment-p) - ;; If we are still in a comment, redo the call so that we get out of it. - (go-goto-function arg)) - - ((and (looking-at "\\")) - (go-goto-function)) - (let ((start (point))) - (go--goto-opening-curly-brace) - - (unless (looking-at "{") - (error "Expected to be looking at opening curly brace")) - (forward-list 1) - (and (>= compare-point start) - (<= compare-point (point)))))) - -(defun go-goto-function-name (&optional arg) - "Go to the name of the current function. - -If the function is a test, place point after 'Test'. -If the function is anonymous, place point on the 'func' keyword. - -If ARG is non-nil, anonymous functions are skipped." - (interactive "P") - (when (not (looking-at "\\")) - (go-goto-function arg)) - ;; If we are looking at func( we are on an anonymous function and - ;; nothing else should be done. - (when (not (looking-at "\\