go-tag.el (9150B)
1 ;;; go-tag.el --- Edit Golang struct field tag -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2017 Brantou 4 5 ;; Author: Brantou <brantou89@gmail.com> 6 ;; URL: https://github.com/brantou/emacs-go-tag 7 ;; Keywords: tools 8 ;; Version: 1.1.0 9 ;; Package-Requires: ((emacs "24.0")(go-mode "1.5.0")) 10 11 ;; This program is free software: you can redistribute it and/or modify 12 ;; it under the terms of the GNU General Public License as published by 13 ;; the Free Software Foundation, either version 3 of the License, or 14 ;; (at your option) any later version. 15 16 ;; This program is distributed in the hope that it will be useful, 17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 ;; GNU General Public License for more details. 20 21 ;; You should have received a copy of the GNU General Public License 22 ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 23 24 ;; This file is not part of GNU Emacs. 25 26 ;;; Commentary: 27 ;; 28 ;; Edit field tags for golang struct fields, based on gomodifytags. 29 ;; This package is inspired by GoAddTags of vim-go and go-add-tags. 30 ;; 31 32 ;;; Requirements: 33 ;; 34 ;; - gomodifytags :: https://github.com/fatih/gomodifytags 35 ;; 36 37 ;;; TODO 38 ;; 39 ;; - Provide better error feedback. 40 ;; - Provide more configuration. 41 ;; 42 43 ;;; Code: 44 45 (require 'go-mode) 46 47 (defgroup go-tag nil 48 "Modify field tag for struct fields." 49 :prefix "go-tag-" 50 :link '(url-link :tag "MELPA" "https://melpa.org/#/go-tag") 51 :link '(url-link :tag "MELPA Stable" "https://stable.melpa.org/#/go-tag") 52 :link '(url-link :tag "GitHub" "https://github.com/brantou/emacs-go-tag") 53 :group 'go) 54 55 (defcustom go-tag-command "gomodifytags" 56 "The 'gomodifytags' command. 57 from https://github.com/fatih/gomodifytags." 58 :type 'string 59 :group 'go-tag) 60 61 (defcustom go-tag-args nil 62 "Additional arguments to pass to go-tag." 63 :type '(repeat string) 64 :group 'go-tag) 65 66 (defcustom go-tag-show-errors 'buffer 67 "Where to display go-tag error output. 68 It can either be displayed in its own buffer, in the echo area, or not at all." 69 :type '(choice 70 (const :tag "Own buffer" buffer) 71 (const :tag "Echo area" echo) 72 (const :tag "None" nil)) 73 :group 'go-tag) 74 75 (defun go-tag--parse-tag (tags) 76 (mapconcat 77 'car 78 (mapcar 79 (lambda(str) 80 (split-string str ",")) 81 (split-string tags)) 82 ",")) 83 84 (defun go-tag--parse-option (tags) 85 (mapconcat 'identity 86 (delete 87 "" 88 (split-string 89 (mapconcat 90 (lambda(str-lst) 91 (when (< 1 (length str-lst)) 92 (concat (car str-lst) "=" (cadr str-lst)))) 93 (mapcar 94 (lambda(str) 95 (split-string str ",")) 96 (split-string tags)) 97 ",") 98 ",")) 99 ",")) 100 101 ;;;###autoload 102 (defun go-tag-open-github() 103 "go-tag open Github page." 104 (interactive) 105 (browse-url "https://github.com/brantou/emacs-go-tag")) 106 107 ;;;###autoload 108 (defun go-tag-refresh (tags) 109 "Refresh field TAGS for struct fields." 110 (interactive "sTags: ") 111 (let ((stags (go-tag--parse-tag tags)) 112 (options (go-tag--parse-option tags))) 113 (when (string-equal stags "") (setq stags "json")) 114 (if (use-region-p) 115 (progn 116 (go-tag--region-remove (region-beginning) (region-end) stags "") 117 (go-tag--region (region-beginning) (region-end) stags options)) 118 (progn 119 (go-tag--point-remove (position-bytes (point)) stags "") 120 (go-tag--point (position-bytes (point)) stags options))))) 121 122 ;;;###autoload 123 (defun go-tag-add (tags) 124 "Add field TAGS for struct fields." 125 (interactive "sTags: ") 126 (let ((stags (go-tag--parse-tag tags)) 127 (options (go-tag--parse-option tags))) 128 (if (use-region-p) 129 (go-tag--region (region-beginning) (region-end) stags options) 130 (go-tag--point (position-bytes (point)) stags options)))) 131 132 (defun go-tag--region (start end tags &optional options) 133 "Add field TAGS for the region between START and END." 134 (let ((cmd-args (append 135 go-tag-args 136 (list "-line" (format "%S,%S" (line-number-at-pos start) (line-number-at-pos end)))))) 137 (go-tag--add cmd-args tags options))) 138 139 (defun go-tag--point (point tags &optional options) 140 "Add field TAGS for the struct under the POINT." 141 (let ((cmd-args (append go-tag-args 142 (list "-offset" (format "%S" point))))) 143 (go-tag--add cmd-args tags options))) 144 145 (defun go-tag--add (cmd-args tags &optional options) 146 "Init CMD-ARGS, add TAGS and OPTIONS to CMD-ARGS." 147 (let ((tags (if (string-equal tags "") "json" tags))) 148 (setq cmd-args 149 (append cmd-args 150 (list "-add-tags" tags))) 151 (unless (string-equal options "") 152 (setq cmd-args 153 (append cmd-args 154 (list "-add-options" options)))) 155 (go-tag--proc cmd-args))) 156 157 ;;;###autoload 158 (defun go-tag-remove (tags) 159 "Remove field TAGS for struct fields." 160 (interactive "sTags: ") 161 (let ((stags (go-tag--parse-tag tags)) 162 (options (go-tag--parse-option tags))) 163 (if (use-region-p) 164 (go-tag--region-remove (region-beginning) (region-end) stags options) 165 (go-tag--point-remove (position-bytes (point)) stags options)))) 166 167 (defun go-tag--region-remove (start end tags &optional options) 168 "Remove field TAGS for the region between START and END." 169 (let ((cmd-args (append 170 go-tag-args 171 (list "-line" (format "%S,%S" (line-number-at-pos start) (line-number-at-pos end)))))) 172 (go-tag--remove cmd-args tags options))) 173 174 (defun go-tag--point-remove (point tags &optional options) 175 "Add field TAGS for the struct under the POINT." 176 (let ((cmd-args (append 177 go-tag-args 178 (list "-offset" (format "%S" point))))) 179 (go-tag--remove cmd-args tags options))) 180 181 (defun go-tag--remove(cmd-args tags &optional options) 182 "Init CMD-ARGS, add TAGS and OPTIONS to CMD-ARGS." 183 (progn 184 (if (string-equal tags "") 185 (setq cmd-args 186 (append cmd-args 187 (list "-clear-tags"))) 188 (if (string-equal options "") 189 (setq cmd-args 190 (append cmd-args 191 (list "-remove-tags" tags))) 192 (let ((tags (go-tag--filter-tags tags options))) 193 (unless (string-equal tags "") 194 (setq cmd-args 195 (append cmd-args 196 (list "-remove-tags" tags)))) 197 (setq cmd-args 198 (append cmd-args 199 (list "-remove-options" options)))))) 200 (go-tag--proc cmd-args))) 201 202 (defun go-tag--filter-tags (tags options) 203 (let ((tag-lst (split-string tags ",")) 204 (option-alst (mapcar 205 (lambda (opt) 206 (let ((opt-pair (split-string opt "="))) 207 (cons (car opt-pair) (cadr opt-pair)))) 208 (split-string options ",")))) 209 (mapconcat 210 'identity 211 (delete "" (split-string 212 (mapconcat 213 (lambda (tag) 214 (unless (assoc tag option-alst) tag)) 215 tag-lst 216 ",") ",")) 217 ","))) 218 219 (defun go-tag--proc (cmd-args) 220 "Modify field tags based on CMD-ARGS. 221 222 The tool used can be set via ‘go-tag-command` (default: go-tag) 223 and additional arguments can be set as a list via ‘go-tag-args`." 224 (let ((tmpfile (make-temp-file "go-tag" nil ".go")) 225 (patchbuf (get-buffer-create "*Go-Tag patch*")) 226 (errbuf (if go-tag-show-errors 227 (get-buffer-create "*Go-Tag Errors*"))) 228 (coding-system-for-read 'utf-8) 229 (coding-system-for-write 'utf-8)) 230 231 (unwind-protect 232 (save-restriction 233 (widen) 234 (if errbuf 235 (with-current-buffer errbuf 236 (setq buffer-read-only nil) 237 (erase-buffer))) 238 (with-current-buffer patchbuf 239 (erase-buffer)) 240 241 (write-region nil nil tmpfile) 242 243 (setq cmd-args (append cmd-args (list "-file" tmpfile "-w"))) 244 245 (message "Calling go-tag: %s %s" go-tag-command cmd-args) 246 (if (zerop (apply #'call-process go-tag-command nil errbuf nil cmd-args)) 247 (progn 248 (if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile)) 249 (message "Buffer is already go-tag") 250 (go--apply-rcs-patch patchbuf) 251 (message "Applied go-tag")) 252 (if errbuf (go-tag--kill-error-buffer errbuf))) 253 (message "Could not apply go-tag") 254 (if errbuf 255 (progn 256 (message (with-current-buffer errbuf (buffer-string))) 257 (go-tag--kill-error-buffer errbuf))))) 258 259 (kill-buffer patchbuf) 260 (delete-file tmpfile)))) 261 262 (defun go-tag--kill-error-buffer (errbuf) 263 "Kill ERRBUF." 264 (let ((win (get-buffer-window errbuf))) 265 (if win 266 (quit-window t win) 267 (kill-buffer errbuf)))) 268 269 (provide 'go-tag) 270 271 ;;; go-tag.el ends here