-
Notifications
You must be signed in to change notification settings - Fork 4
/
ivy-clojuredocs.el
146 lines (120 loc) · 5.31 KB
/
ivy-clojuredocs.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
;;; ivy-clojuredocs.el --- Search for help in clojuredocs.org -*- lexical-binding: t; -*-
;; Author: Wanderson Ferreira <[email protected]>
;; URL: https://github.com/wandersoncferreira/ivy-clojuredocs
;; Package-Requires: ((edn "1.1.2") (ivy "0.12.0") (emacs "24.4"))
;; Version: 0.1
;; Keywords: matching
;; Copyright (C) 2019 Wanderson Ferreira <[email protected]>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 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, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This work is heavily inspired by `helm-clojuredocs'. Despite the completion
;; engine difference, there are minor implementation details and bug fixes in
;; this first version.
;;; Code:
(require 'ivy)
(require 'browse-url)
(require 'parseedn)
(require 'cl-lib)
(require 'subr-x)
(defgroup ivy-clojuredocs nil
"Ivy applications"
:group 'ivy)
(defcustom ivy-clojuredocs-url
"https://clojuredocs.org/"
"Url used for searching in ClojureDocs website."
:type 'string
:group 'ivy-clojuredocs)
(defcustom ivy-clojuredocs-min-chars-number
2
"Value for minimum input character before start searching on ClojureDocs website."
:type 'integer
:group 'ivy-clojuredocs)
(defvar ivy-clojuredocs-cache (make-hash-table :test 'equal))
(defun ivy-clojuredocs--parse-entry (entry)
"Parse each ENTRY returned by ClojureDocs API."
(let ((cd-namespace (or (gethash ':ns entry) ""))
(cd-type (or (gethash ':type entry) ""))
(cd-name (gethash ':name entry)))
(format "%s %s %s" cd-namespace cd-name cd-type)))
(defun ivy-clojuredocs--parse-response-buffer (buffer)
"Get the BUFFER with the response content and parse each returned entry."
(cl-loop for i in (parseedn-read-str buffer)
collect (ivy-clojuredocs--parse-entry i) into result
finally return result))
(defun ivy-clojuredocs-fetch (candidate)
"Call the ClojureDocs API for a given CANDIDATE.
Place the parsed results in cache.T he cache data structure is a
hash-table whose keys are the searched candidates."
(let ((url (concat ivy-clojuredocs-url "ac-search?query=" candidate)))
(with-current-buffer (url-retrieve-synchronously url)
(goto-char (point-min))
(when (re-search-forward "\\(({.+})\\)" nil t)
(puthash candidate
(ivy-clojuredocs--parse-response-buffer (match-string 0))
ivy-clojuredocs-cache)))))
(defun ivy-clojuredocs-candidates (str &rest _u)
"Orchestrate call to get suggestions to candidate (STR).
There are two behaviors here, at the first search we cache the
returned data for this candidate (STR) in a hash-table data
structure. Second search in the same candidate, will capture the
data from cache."
(if (< (length str) ivy-clojuredocs-min-chars-number)
(ivy-more-chars)
(let ((candidates (or (gethash str ivy-clojuredocs-cache)
(ivy-clojuredocs-fetch str))))
(if (member str candidates)
candidates
(append
candidates
(list (format "Search for '%s' on clojuredocs.org" str)))))))
(defun ivy-clojuredocs-fmt-web-entry (entry)
"Parse an given ENTRY called by ivy-action.
The idea is to return a string that is useful to the `browse-url'
function."
(if (string-match "on clojuredocs.org$" entry)
(format "search?q=%s" (cadr (split-string entry "'")))
(let* ((lentry (cl-remove-if #'string-empty-p
(split-string entry " "))))
(replace-regexp-in-string "?" "_q"
(string-join (nbutlast lentry) "/")))))
(defun ivy-clojuredocs--clean-cache ()
"Clear the cache data structure for `ivy-clojuredocs' previous search."
(clrhash ivy-clojuredocs-cache))
(defun ivy-clojuredocs-thing-at-point (thing)
"Preprocess THING to be given as parameter as a candidate to search."
(when thing
(car (last (split-string thing "/")))))
(defun ivy-clojuredocs-invoke (&optional initial-input)
"Ivy function to read and display candidates to the user.
We can pass an INITIAL-INPUT value to be the first candidate searched."
(ivy-read "ClojureDocs: " #'ivy-clojuredocs-candidates
:initial-input initial-input
:dynamic-collection t
:action (lambda (entry)
(browse-url (concat ivy-clojuredocs-url (ivy-clojuredocs-fmt-web-entry entry))))
:unwind #'ivy-clojuredocs--clean-cache
:caller #'ivy-clojuredocs))
;;;###autoload
(defun ivy-clojuredocs ()
"Search for help at ClojureDocs API."
(interactive)
(ivy-clojuredocs-invoke))
;;;###autoload
(defun ivy-clojuredocs-at-point ()
"Search for help using word at point at ClojureDocs API."
(interactive)
(ivy-clojuredocs-invoke (ivy-clojuredocs-thing-at-point (thing-at-point 'symbol))))
(provide 'ivy-clojuredocs)
;; Local Variables:
;; coding: utf-8
;; End:
;;; ivy-clojuredocs.el ends here