-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcobra.el
143 lines (132 loc) · 6.38 KB
/
cobra.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
;;; cobra.el --- Complete Cobra command lines -*- lexical-binding: t; -*-
;; Copyright (C) 2024-2025 Free Software Foundation, Inc.
;; Author: Eshel Yaron <[email protected]>
;; Keywords: tools
;;; Commentary:
;; Cobra is a popular Golang framework for CLI programs. This library
;; defines function `cobra-read-command-line', which helps you read a
;; command line for a program that uses Cobra, with completion.
;; Prominent examples of Cobra programs are `kubectl' and `docker'.
;;; Code:
(defvar cobra--cache nil)
(defun cobra-completion-table (executable s p a)
"Completion table for command lines that invoke EXECUTABLE.
Perform completion action A on string S with predicate P."
(let ((start 0))
(while (string-match "[[:space:]=]" s start)
(setq start (match-end 0)))
(if (eq a 'metadata)
`(metadata
(category . cobra-command-line)
(affixation-function
. ,(lambda (cands)
(let ((max (seq-max
(cons 0 (mapcar #'string-width cands)))))
(mapcar
(lambda (cand)
(list
cand ""
(if-let
((desc (get-text-property
0 'cobra-argument-description
cand)))
(concat
(make-string (1+ (- max (string-width cand))) ?\s)
(propertize desc 'face 'completions-annotations))
"")))
cands)))))
(let* ((lines
(cdr
(if (string= s (car cobra--cache))
;; Cache hit.
cobra--cache
(setq
cobra--cache
(cons s
(apply #'process-lines-ignore-status
executable "__complete"
(let ((args (cdr (split-string-and-unquote s))))
(if (string-suffix-p " " s)
;; Restore omitted empty argument.
(nconc args '(""))
args))))))))
(code nil)
(comps (seq-take-while
(lambda (line)
(not (and (string-match "^:\\([0-9]+\\)$" line)
(setq code (string-to-number
(match-string 1 line))))))
lines)))
;; `code' encodes "completion directives", as follows:
;; #b000001: An error occurred, ignore completions.
;; #b000010: Don't add space after completion. (TODO.)
;; #b000100: Don't fall back to file completion.
;; #b001000: Completions are really file extension filters.
;; #b010000: Complete directory names.
;; #b100000: Preserve completions order.
(when (and code (zerop (logand 1 code)))
;; Error bit in unset, proceed.
(if (= #b100 (logand #b100 code))
;; No file name completion.
(if (eq (car-safe a) 'boundaries)
`(boundaries
,start . ,(and (string-match "[[:space:]=]" (cdr a))
(match-beginning 0)))
(let ((table
(mapcar
;; Annotate completion candidates.
(lambda (comp)
(pcase (split-string comp "\t" t)
(`(,c ,d . ,_)
(propertize
c 'cobra-argument-description
;; Only keep first sentence.
(car (split-string d "\\." t))))
(`(,c . ,_) c)))
comps)))
(if a (complete-with-action a table (substring s start) p)
;; `try-completion'.
(let ((comp (complete-with-action a table (substring s start) p)))
(if (stringp comp) (concat (substring s 0 start) comp) comp)))))
;; File name completion.
(setq p
(cond
((= #b1000 (logand #b1000 code))
;; `comps' are valid extensions.
(lambda (f)
(or (file-directory-p f)
(when (string-match "\\.[^.]*\\'" f)
(member (substring f (1+ (match-beginning 0)))
comps)))))
((= #b10000 (logand #b10000 code))
;; Directory name completion.
#'file-directory-p)))
(if (eq (car-safe a) 'boundaries)
;; Find nested boundaries.
(let* ((suf (cdr a))
(bounds (completion-boundaries
(substring s start) #'completion-file-name-table p
(substring suf 0 (string-match "[[:space:]=]" suf)))))
`(boundaries ,(+ (car bounds) start) . ,(cdr bounds)))
(if a (complete-with-action a #'completion-file-name-table
(substring s start) p)
(let ((comp (complete-with-action a #'completion-file-name-table
(substring s start) p)))
(if (stringp comp) (concat (substring s 0 start) comp) comp))))))))))
;;;###autoload
(defun cobra-read-command-line (prompt initial &optional hist)
"Prompt with PROMPT for a command line starting with INITIAL.
Optional argument HIST is the name of the history list variable to use,
if it is nil or omitted, it defaults to `shell-command-history'."
(let ((exec (car (split-string-and-unquote (car (ensure-list initial))))))
(minibuffer-with-setup-hook
(:append (lambda ()
(let ((map (make-sparse-keymap)))
;; Ensure SPC is self-inserting.
(keymap-unset map "SPC")
(use-local-map
(make-composed-keymap map (current-local-map))))))
(completing-read prompt (apply-partially #'cobra-completion-table exec)
nil nil initial (or hist 'shell-command-history)))))
(provide 'cobra)
;;; cobra.el ends here