├── .gitignore
├── .travis.yml
├── Cask
├── Changes
├── Makefile
├── README.md
├── go-add-tags.el
├── image
└── go-add-tags.gif
└── test
└── test.el
/.gitignore:
--------------------------------------------------------------------------------
1 | /.cask/
2 | *.elc
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: generic
2 | sudo: false
3 | before_install:
4 | - curl -fsSkL https://gist.github.com/rejeep/ebcd57c3af83b049833b/raw > x.sh && source ./x.sh
5 | - evm install $EVM_EMACS --use --skip
6 | - cask
7 | env:
8 | - EVM_EMACS=emacs-24.3-travis
9 | - EVM_EMACS=emacs-24.5-travis
10 | - EVM_EMACS=emacs-25.1-travis
11 | script:
12 | - emacs --version
13 | - make test
14 |
--------------------------------------------------------------------------------
/Cask:
--------------------------------------------------------------------------------
1 | (source gnu)
2 | (source melpa)
3 |
4 | (package-file "go-add-tags.el")
5 |
6 | (development
7 | (depends-on "go-mode"))
8 |
--------------------------------------------------------------------------------
/Changes:
--------------------------------------------------------------------------------
1 | Revision history for go-add-tags.el
2 |
3 | Version 0.04 2016/11/23 syohex
4 | - Drop Emacs 24.1 and 24.2 support
5 |
6 | Version 0.03 2016/10/06 syohex
7 | - Support nested struct(#5)
8 | - Implement replacing tag feature(#3)
9 |
10 | Version 0.02 2016/09/09 syohex
11 | - Fix dependency packages
12 |
13 | Version 0.01 2016/09/09 syohex
14 | - init version
15 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY : test test-function
2 |
3 | EMACS ?= emacs
4 | CASK ?= cask
5 |
6 | LOADPATH = -L .
7 |
8 | ELPA_DIR = $(shell EMACS=$(EMACS) $(CASK) package-directory)
9 |
10 | test: elpa
11 | $(CASK) exec $(EMACS) -Q -batch $(LOADPATH) \
12 | -l test/test.el \
13 | -f ert-run-tests-batch-and-exit
14 |
15 | elpa: $(ELPA_DIR)
16 | $(ELPA_DIR): Cask
17 | $(CASK) install
18 | touch $@
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-add-tags.el [![travis badge][travis-badge]][travis-link] [![melpa badge][melpa-badge]][melpa-link] [![melpa stable badge][melpa-stable-badge]][melpa-stable-link]
2 |
3 | Add field tags for struct fields. This package is inspired by `GoAddTags` of [vim-go](https://github.com/fatih/vim-go).
4 |
5 | ## Screencast
6 |
7 | 
8 |
9 | ## Installation
10 |
11 | `go-add-tags` is available on [MELPA](https://melpa.org/) and [MELPA stable](https://stable.melpa.org/)
12 |
13 | You can install `go-add-tags` with the following command.
14 |
15 | M-x package-install [RET] go-add-tags [RET]
16 |
17 | ## Interfaces
18 |
19 | ##### `go-add-tags`
20 |
21 | Insert tag at current line. You can input multiple tags at once by comma, like `json,yaml`.
22 | If region is enabled, then tags are inserted in lines in region. And `current-prefix-key` is specified,
23 | then you can choose field style function.
24 |
25 | ## Customization
26 |
27 | ##### `go-add-tags-style`(Default: 'snake-case)
28 |
29 | How to convert field in tag from field name.
30 |
31 | - `snake-case`
32 | - `lower-camel-case`
33 | - `upper-camel-case`
34 | - `original`
35 |
36 | ## Sample Configuration
37 |
38 | ``` emacs-lisp
39 | (custom-set-variables
40 | '(go-add-tags-style 'lower-camel-case))
41 |
42 | (with-eval-after-load 'go-mode
43 | (define-key go-mode-map (kbd "C-c t") #'go-add-tags))
44 | ```
45 |
46 | [travis-badge]: https://travis-ci.org/syohex/emacs-go-add-tags.svg
47 | [travis-link]: https://travis-ci.org/syohex/emacs-go-add-tags
48 | [melpa-link]: https://melpa.org/#/go-add-tags
49 | [melpa-stable-link]: https://stable.melpa.org/#/go-add-tags
50 | [melpa-badge]: https://melpa.org/packages/go-add-tags-badge.svg
51 | [melpa-stable-badge]: https://stable.melpa.org/packages/go-add-tags-badge.svg
52 |
--------------------------------------------------------------------------------
/go-add-tags.el:
--------------------------------------------------------------------------------
1 | ;;; go-add-tags.el --- Add field tags for struct fields -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2016 by Syohei YOSHIDA
4 |
5 | ;; Author: Syohei YOSHIDA
6 | ;; URL: https://github.com/syohex/emacs-go-add-tags
7 | ;; Version: 0.04
8 | ;; Package-Requires: ((emacs "24.3") (s "1.11.0"))
9 |
10 | ;; This program is free software; you can redistribute it and/or modify
11 | ;; it under the terms of the GNU General Public License as published by
12 | ;; the Free Software Foundation, either version 3 of the License, or
13 | ;; (at your option) any later version.
14 |
15 | ;; This program is distributed in the hope that it will be useful,
16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | ;; GNU General Public License for more details.
19 |
20 | ;; You should have received a copy of the GNU General Public License
21 | ;; along with this program. If not, see .
22 |
23 | ;;; Commentary:
24 |
25 | ;; Add field tags (such as json, yaml, toml, datastore and
26 | ;; mapstructure) for struct fields. This package is inspired
27 | ;; by vim-go's GoAddTags command
28 |
29 | ;;; Code:
30 |
31 | (require 's)
32 | (require 'cl-lib)
33 |
34 | (defgroup go-add-tags nil
35 | "Add field tag for struct fields."
36 | :group 'go)
37 |
38 | (defcustom go-add-tags-style 'snake-case
39 | "How to convert field in tag from field name."
40 | :type '(choice (const :tag "snake_case" snake-case)
41 | (const :tag "camelCase" lower-camel-case)
42 | (const :tag "UpperCamelCase" upper-camel-case)
43 | (const :tag "Use original field name" original)))
44 |
45 | (defcustom go-add-tags-fields-tags
46 | '("json" "yaml" "toml" "datastore" "mapstructure")
47 | "List of field tags offered as completion candidates."
48 | :type '(repeat (symbol :tag "Field")))
49 |
50 | (defvar go-add-tags--style-functions
51 | '((snake-case . s-snake-case)
52 | (camel-case . s-lower-camel-case)
53 | (upper-camel-case . s-upper-camel-case)))
54 |
55 | (defun go-add-tags--inside-struct-p (begin)
56 | (save-excursion
57 | (goto-char begin)
58 | (ignore-errors
59 | (backward-up-list))
60 | (looking-back "struct\\s-*" (line-beginning-position))))
61 |
62 | (defun go-add-tags--tag-string (tags field)
63 | (mapconcat (lambda (tag)
64 | (format "%s:\"%s\"" tag field))
65 | tags
66 | " "))
67 |
68 | (defun go-add-tags--tag-exist-p ()
69 | (let ((line (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
70 | (string-match-p "`[^`]+`" line)))
71 |
72 | (defun go-add-tags--insert-tag (tags field)
73 | (dolist (tag tags)
74 | (save-excursion
75 | (let ((re (concat tag ":\"[^\"]+\""))
76 | (tag-field (concat tag ":" "\"" field "\"")))
77 | (if (re-search-forward re (line-end-position) t)
78 | (replace-match tag-field)
79 | (search-forward "`" (line-end-position) t 2)
80 | (backward-char 1)
81 | (insert " " tag-field))))))
82 |
83 | (defun go-add-tags--overwrite-or-insert-tag (tags field conv-fn)
84 | (let* ((prop (funcall conv-fn field))
85 | (exist-p (go-add-tags--tag-exist-p)))
86 | (if (not exist-p)
87 | (insert (format " `%s`" (go-add-tags--tag-string tags prop)))
88 | (go-add-tags--insert-tag tags prop))))
89 |
90 | (defun go-add-tags--struct-name ()
91 | (save-excursion
92 | (when (ignore-errors (backward-sexp 1) t)
93 | (back-to-indentation)
94 | (when (looking-at "\\(\\S-+\\)\\s-+\\(?:\\[\\]\\)?struct\\s-*")
95 | (match-string-no-properties 1)))))
96 |
97 | (defun go-add-tags--insert-tags (tags begin end conv-fn)
98 | (save-excursion
99 | (let ((end-marker (make-marker)))
100 | (set-marker end-marker end)
101 | (goto-char begin)
102 | (goto-char (line-beginning-position))
103 | (while (and (<= (point) end-marker) (not (eobp)))
104 | (let ((bound (min end-marker (line-end-position))))
105 | (cond ((re-search-forward "^\\s-*\\(\\S-+\\)\\s-+\\(\\S-+\\)" bound t)
106 | (let ((field (match-string-no-properties 1))
107 | (type-end (match-end 2))
108 | (line (buffer-substring-no-properties
109 | (line-beginning-position) (line-end-position))))
110 | (unless (string-match-p "struct\\s-*+{" line)
111 | (goto-char (min bound type-end))
112 | (go-add-tags--overwrite-or-insert-tag tags field conv-fn))))
113 | ((re-search-forward "^\\s-*}" bound t)
114 | (unless (zerop (current-indentation))
115 | (let ((field (go-add-tags--struct-name)))
116 | (when field
117 | (go-add-tags--overwrite-or-insert-tag tags field conv-fn)))))))
118 | (forward-line 1)))))
119 |
120 | (defun go-add-tags--style-candidates (field)
121 | (cl-loop for (_ . func) in go-add-tags--style-functions
122 | collect (cons (funcall func field) func) into candidates
123 | finally return (append candidates (list (cons "Original" #'identity)))))
124 |
125 | (defun go-add-tags--prompt (candidates)
126 | (cl-loop for cand in candidates
127 | for i = 1 then (1+ i)
128 | collect (format "[%d] %s" i cand) into parts
129 | finally
130 | return (mapconcat #'identity parts " ")))
131 |
132 | (defun go-add-tags--read-style-function ()
133 | (let* ((candidates (go-add-tags--style-candidates "FieldName"))
134 | (len (length candidates))
135 | (prompt (go-add-tags--prompt (mapcar #'car candidates)))
136 | ret finish)
137 | (while (not finish)
138 | (let* ((input (read-char prompt))
139 | (num (- input ?0)))
140 | (when (and (<= 1 num) (<= num len))
141 | (setq finish t)
142 | (setq ret (cdr (nth (1- num) candidates))))))
143 | ret))
144 |
145 | ;;;###autoload
146 | (defun go-add-tags (tags begin end conv-fn)
147 | "Add field tags for struct fields."
148 | (interactive
149 | (list
150 | (completing-read-multiple "Tags: " go-add-tags-fields-tags)
151 | (or (and (use-region-p) (region-beginning)) (line-beginning-position))
152 | (or (and (use-region-p) (region-end)) (line-end-position))
153 | (if current-prefix-arg
154 | (go-add-tags--read-style-function)
155 | (assoc-default go-add-tags-style go-add-tags--style-functions))))
156 | (deactivate-mark)
157 | (let ((inside-struct-p (go-add-tags--inside-struct-p begin)))
158 | (unless inside-struct-p
159 | (user-error "Here is not struct"))
160 | (save-excursion
161 | (go-add-tags--insert-tags tags begin end (or conv-fn #'identity)))))
162 |
163 | (provide 'go-add-tags)
164 |
165 | ;;; go-add-tags.el ends here
166 |
--------------------------------------------------------------------------------
/image/go-add-tags.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/emacsorphanage/go-add-tags/93ecde9f82bc960493eaf6921d46a5adc3699ffc/image/go-add-tags.gif
--------------------------------------------------------------------------------
/test/test.el:
--------------------------------------------------------------------------------
1 | ;;; test.el --- test of go-add-tags
2 |
3 | ;; Copyright (C) 2016 by Syohei YOSHIDA
4 |
5 | ;; Author: Syohei YOSHIDA
6 |
7 | ;; This program is free software; you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 |
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 |
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 |
20 | ;;; Code:
21 |
22 | (require 'ert)
23 | (require 'go-add-tags)
24 |
25 | (defmacro with-go-temp-buffer (code &rest body)
26 | "Insert `code'. cursor is beginning of buffer"
27 | (declare (indent 0) (debug t))
28 | `(with-temp-buffer
29 | (insert ,code)
30 | (goto-char (point-min))
31 | ,@body))
32 |
33 | (ert-deftest tag-string ()
34 | "internal function go-add-tags--tags-string"
35 | (let ((got (go-add-tags--tag-string '("datastore") "foo_bar"))
36 | (expected "datastore:\"foo_bar\""))
37 | (should (string= got expected)))
38 |
39 | (let ((got (go-add-tags--tag-string '("json" "yaml") "foo_bar"))
40 | (expected "json:\"foo_bar\" yaml:\"foo_bar\""))
41 | (should (string= got expected))))
42 |
43 | (ert-deftest tag-exist-p ()
44 | "internal function go-add-tags--tag-exist-p"
45 | (with-go-temp-buffer
46 | "
47 | type Foo struct {
48 | BarBaz string `json:\"bar_baz\"`
49 | Apple Fruit
50 | }
51 | "
52 | (search-forward "BarBaz")
53 | (should (go-add-tags--tag-exist-p))
54 |
55 | (forward-line +1)
56 | (should-not (go-add-tags--tag-exist-p))))
57 |
58 | (ert-deftest insert-tags ()
59 | "internal function go-add-tags--insert-tags"
60 | (with-go-temp-buffer
61 | "
62 | type Foo struct {
63 | BarBaz string
64 | Apple Fruit `yaml:\"apple\"`
65 | }
66 | "
67 | (search-forward "BarBaz")
68 | (go-add-tags--insert-tags
69 | '("json") (line-beginning-position) (line-end-position) #'s-lower-camel-case)
70 | (let ((line (buffer-substring (line-beginning-position) (line-end-position))))
71 | (should (s-contains? "`json:\"barBaz\"`" line)))
72 |
73 | (forward-line +1)
74 | (go-add-tags--insert-tags
75 | '("json") (line-beginning-position) (line-end-position) #'s-snake-case)
76 | (let ((line (buffer-substring (line-beginning-position) (line-end-position))))
77 | (should (s-contains? "`yaml:\"apple\" json:\"apple\"`" line)))))
78 |
79 | (ert-deftest replace-tags ()
80 | "internal function go-add-tags--insert-tags in replacing case"
81 | (with-go-temp-buffer
82 | "
83 | type Foo struct {
84 | BarBaz string `json:\"barbaz\"`
85 | AppleOrange Fruit `yaml:\"apple_orange\" json:\"apple_orange\"`
86 | }
87 | "
88 | (search-forward "BarBaz")
89 | (go-add-tags--insert-tags
90 | '("json") (line-beginning-position) (line-end-position) #'s-lower-camel-case)
91 | (let ((line (buffer-substring (line-beginning-position) (line-end-position))))
92 | (should (s-contains? "`json:\"barBaz\"`" line)))
93 |
94 | (forward-line +1)
95 | (go-add-tags--insert-tags
96 | '("json" "yaml") (line-beginning-position) (line-end-position) #'s-snake-case)
97 | (let ((line (buffer-substring (line-beginning-position) (line-end-position))))
98 | (should (s-contains? "`yaml:\"apple_orange\" json:\"apple_orange\"`" line)))))
99 |
100 | (ert-deftest nested-tag ()
101 | "Insert tag for nested struct
102 | https://github.com/syohex/emacs-go-add-tags/issues/4"
103 | (with-go-temp-buffer
104 | "
105 | type APIErr struct {
106 | Message string
107 | Errors []struct {
108 | Resource string
109 | Code string
110 | }
111 | }
112 | "
113 | (let ((expected "
114 | type APIErr struct {
115 | Message string `json:\"message\"`
116 | Errors []struct {
117 | Resource string `json:\"resource\"`
118 | Code string `json:\"code\"`
119 | } `json:\"errors\"`
120 | }
121 | "))
122 |
123 | (forward-line 1)
124 | (go-add-tags--insert-tags
125 | '("json") (point) (point-max) #'s-lower-camel-case)
126 | (should (string= (buffer-string) expected)))))
127 |
128 | ;;; test.el ends here
129 |
--------------------------------------------------------------------------------