-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy patheacl.el
663 lines (580 loc) · 24.2 KB
/
eacl.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
;;; eacl.el --- Auto-complete lines by grepping project
;; Copyright (C) 2017-2021 Chen Bin
;;
;; Version: 2.2.0
;; Author: Chen Bin <chenbin DOT sh AT gmail DOT com>
;; URL: http://github.com/redguardtoo/eacl
;; Package-Requires: ((emacs "25.1"))
;; Keywords: abbrev, convenience, matching
;; This file is not part of GNU Emacs.
;; 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
;;; Commentary:
;; Multiple commands are provided to grep files in the project to get
;; auto complete candidates.
;;
;; The keyword to grep is text from the line beginning to current cursor.
;;
;; Project is *automatically* detected if Git/Mercurial/Subversion is used.
;; You can override the project root by setting `eacl-project-root',
;;
;; List of commands,
;;
;; `eacl-complete-line' completes single line by grepping the project root.
;; Line candidates are extracted from the files in the project root.
;; "C-u M-x eacl-complete-line" completes single line from deleted code
;; if current project is tracked by Git.
;;
;; `eacl-complete-multiline' completes multiline code or html tag.
;;
;; `eacl-complete-line-from-buffer' completes single line by searching text
;; in the buffers. Set `eacl-ignore-buffers' and `eacl-include-buffers' to specify
;; ignored&included buffers.
;;
;; `eacl-complete-line-from-buffer-or-project' completes single line by grepping
;; the project root when editing a physical file. Or else, it searches all buffers
;; for line completion.
;;
;; Modify `grep-find-ignored-directories' and `grep-find-ignored-files'
;; to setup directories and files grep should ignore:
;; (with-eval-after-load 'grep
;; (dolist (v '("node_modules"
;; "bower_components"
;; ".sass_cache"
;; ".cache"
;; ".npm"))
;; (add-to-list 'grep-find-ignored-directories v))
;; (dolist (v '("*.min.js"
;; "*.bundle.js"
;; "*.min.css"
;; "*.json"
;; "*.log"))
;; (add-to-list 'grep-find-ignored-files v)))
;;
;; Or you can setup above ignore options in ".dir-locals.el".
;; The content of ".dir-locals.el":
;; ((nil . ((eval . (progn
;; (dolist (v '("node_modules"
;; "bower_components"
;; ".sass_cache"
;; ".cache"
;; ".npm"))
;; (add-to-list 'grep-find-ignored-directories v))
;; (dolist (v '("*.min.js"
;; "*.bundle.js"
;; "*.min.css"
;; "*.json"
;; "*.log"))
;; (add-to-list 'grep-find-ignored-files v)))))))
;;
;; "git grep" is automatically used for grepping in git repository.
;; Please note "git grep" does NOT use `grep-find-ignored-directories' OR
;; `grep-find-ignored-files'.
;;
;; The command line program of grep and git need be added into environment variable
;; "PATH". Or else you need set `eacl-grep-program' and `eacl-git-program' to
;; specify their path.
;;
;; Set `eacl-git-grep-untracked' if untracked files should be git grepped too.
;;
;;; Code:
(require 'grep)
(require 'cl-lib)
(require 'subr-x)
(require 'comint)
(defgroup eacl nil
"Emacs auto-complete line(s) by grepping project."
:group 'tools)
(defcustom eacl-grep-program "grep"
"The path of GNU Grep command line program."
:type 'string
:group 'eacl)
(defcustom eacl-git-program "git"
"The path of Git command line program."
:type 'string
:group 'eacl)
(defcustom eacl-git-grep-untracked t
"Search text in untracked files in Git repository."
:type 'boolean
:group 'eacl)
(defcustom eacl-project-root nil
"The project root. If it's nil project root is detected automatically."
:type 'string
:group 'eacl)
(defcustom eacl-project-file '(".svn" ".hg" ".git")
"The file/directory used to locate project root."
:type '(repeat sexp)
:group 'eacl)
(defcustom eacl-project-root-callback 'eacl-get-project-root
"The callback to get project root directory.
The callback is expected to return the path of project root."
:type 'function
:group 'eacl)
(defcustom eacl-ignore-buffers
'("^ *\\*.*\\*$"
dired-mode)
"A list specifying which buffers not to search (if not current).
Can contain both regexps matching buffer names (as strings) and major modes
\(as atoms).
Please note `eacl-include-buffers' has higher priority than this variable."
:type '(repeat (choice regexp (symbol :tag "Major Mode")))
:group 'eacl)
(defcustom eacl-include-buffers
'("^\\*shell\\*$")
"A list specifying which buffers to search (if not current).
Can contain regexps matching buffer names (as strings).
Please note `eacl-ignore-buffers' has lower priority than this variable."
:type '(repeat regexp)
:group 'eacl)
(defcustom eacl-search-buffer-max-size
(* 16 1024 1024)
"Only search buffer whose size is not greater than this number."
:type 'number
:group 'eacl)
(defcustom eacl-use-git-grep-p nil
"Use git grep even current file is not tracked by Git."
:type 'boolean
:group 'eacl)
(defvar eacl-keyword-start nil
"The start position of multiline keyword. Internal variable.")
(defvar eacl-debug nil
"Enable debug mode. Internal variable.")
(defvar projectile-project-root) ;; avoid compiling error
(defalias 'eacl-complete-statement 'eacl-complete-multiline)
(defalias 'eacl-complete-snippet 'eacl-complete-multiline)
(defalias 'eacl-complete-tag 'eacl-complete-multiline)
;; {{ make linter happy
(defvar evil-state)
(declare-function evil-insert-state "evil")
(declare-function evil-exit-visual-state "evil")
;; }}
(defun eacl-relative-path ()
"Relative path of current file."
(let* ((p (if buffer-file-truename buffer-file-truename "")))
(file-relative-name p (eacl-get-project-root))))
;;;###autoload
(defun eacl-get-project-root ()
"Get project root."
(or eacl-project-root
;; use projectile to find project root
(and (fboundp 'projectile-project-root)
(unless (featurep 'projectile) (require 'projectile))
(funcall 'projectile-project-root))
;; use find-file-in-project to find project root
(and (fboundp 'ffip-project-root) (ffip-project-root))
;; find project root manually
(cl-some (apply-partially 'locate-dominating-file
default-directory)
eacl-project-file)))
;;;###autoload
(defun eacl-current-line-info ()
"Current line."
(let* ((b (line-beginning-position))
(e (line-end-position)))
(cons (buffer-substring-no-properties b (point))
(buffer-substring-no-properties b e))))
(defun eacl-current-line-text ()
"Current line text."
(buffer-substring-no-properties (line-beginning-position) (line-end-position)))
(defmacro eacl-process-space-in-regex (regex)
"Space character in REGEX is converted to pattern to match any string."
`(replace-regexp-in-string "[\t ]+" ".*" ,regex))
(defun eacl-encode(str)
"Encode STR."
(setq str (regexp-quote str))
;; 1, Be generic about quotes. Most script languages could use either double quotes
;; or single quote to wrap string.
;; In this case, we don't care, we just want to get mores candidates for
;; code completion.
;; For example, in javascript, `import { Button } from "react-bootstrap"` and
;; `import { Button } from 'react-bootstrap';` are same.
;; 2, white spaces match any string (see `eacl-shell-quote-argument')
(replace-regexp-in-string "'\\|\"" "." str))
(defun eacl-shell-quote-argument (argument)
"Try `shell-quote-argument' ARGUMENT and process special characters."
(cond
((eq system-type 'ms-dos)
(shell-quote-argument argument))
((equal argument "")
"''")
(t
;; We only use GNU Grep from Cygwin/MSYS2 even on Windows.
;; So we can safely assume the Linux Shell is available.
;; Below code is copied from `shell-quote-argument'. []
(replace-regexp-in-string
"\\\\\]" "]"
(replace-regexp-in-string
"[^-0-9a-zA-Z<>{}\[:_./\n()*]" "\\\\\\&"
(eacl-process-space-in-regex argument))))))
(defun eacl-grep-exclude-opts ()
"Create grep exclude options."
(concat (mapconcat (lambda (e) (format "--exclude-dir='%s'" e))
grep-find-ignored-directories " ")
" "
(mapconcat (lambda (e) (format "--exclude='%s'" e))
grep-find-ignored-files " ")))
;;;###autoload
(defun eacl-get-keyword (line &optional space-p)
"Get trimmed keyword from LINE.
If SPACE-P is t, space characters are converted to pattern matching any string."
(let ((keyword (eacl-encode (string-trim line))))
(if space-p (eacl-process-space-in-regex keyword)
keyword)))
(defun eacl-replace-text (content end)
"Delete current line and insert CONTENT.
Original text from END is preserved."
(delete-region eacl-keyword-start end)
(insert content))
(defun eacl-clean-summary (s)
"Clean candidate summary S."
(string-trim-left (replace-regexp-in-string "[ \t]*[\n\r]+[ \t]*" "\\\\n" s)))
(defun eacl-multiline-candidate-summary (s)
"If S is too wide to fit into the screen, return pair summary and S."
(let* ((w (frame-width))
(key (eacl-clean-summary s))
(len (length key))
(tw (- w 4)))
;; fit to the minibuffer width by remove trailing characters
(cond
((<= len w)) ; do nothing
((< len (+ w (/ w 8)))
;; strip candidate end
(setq key (concat (substring key 0 tw) "...")))
(eacl-keyword-start
;; strip candidate beginning (text we already inserted)
(let* ((from (- (point) eacl-keyword-start))
(to (min (+ from tw)
(- len from))))
;; a corner case to cover
(when (>= from to)
(setq from (- len tw))
(setq to len))
(if eacl-debug (message "from=%s w=%s len=%s tw=%s" from w len tw))
(setq key (concat "..." (substring key from to))))))
(cons key s)))
(defun eacl-get-candidates (cmd sep keyword &optional deleted-p)
"Create candidates by running CMD. Use SEP to split output into lines.
Candidates same as KEYWORD in current file is excluded.
If DELETED-P is t and git grep is used, grep only from deleted code."
(when eacl-debug
(message "eacl-get-candidates called. cmd=%s deleted-p=%s" cmd deleted-p))
(let* ((cands (split-string (shell-command-to-string cmd) sep t "[ \t\r\n]+"))
(str (format "%s:1:%s" (eacl-relative-path) keyword))
rlt)
;; the candidate match pattern "^-" if delete-p is t
(when deleted-p
(setq cands (mapcar (lambda (cand) (replace-regexp-in-string "^-" "" cand)) cands)))
(setq rlt (cl-remove-if `(lambda (e) (string= ,str e)) cands))
(when eacl-debug (message "cands=%s" cands))
cands))
(defun eacl-clean-candidates (cands)
"Remove duplicated lines from CANDS."
(delq nil (delete-dups cands)))
(defun eacl-git-p ()
"Return non-nil if current file is in a git repository."
(let ((path (buffer-file-name)))
(or eacl-use-git-grep-p
(and path
(zerop (call-process eacl-git-program nil nil nil "ls-files" "--error-unmatch" path))))))
(defun eacl-search-command (search-regex multiline-p &optional deleted-p)
"Return a shell command searching for SEARCH-REGEX.
If MULTILINE-P is t, command is for multiline matching.
If DELETED-P is t and git grep is used, grep only from deleted code."
(let* ((git-p (eacl-git-p))
(git-grep-opts (concat "-I --no-color"
(if eacl-git-grep-untracked " --untracked"))))
;; (setq git-p nil) ; debug
(cond
;; auto-complete multiple lines
(multiline-p
(cond
;; use git grep
(git-p
(format "%s --no-pager grep -n %s \"%s\""
eacl-git-program
git-grep-opts
search-regex))
;; use grep
(t
(format "%s -rsnI %s -- \"%s\" ."
eacl-grep-program
(eacl-grep-exclude-opts)
search-regex))))
;; auto-complete single line
(t
(cond
;; use git grep
(git-p
(if deleted-p (format "%s --no-pager log -p --all -G \"%s\" | %s \"^-.*%s\""
eacl-git-program
search-regex
eacl-grep-program search-regex)
(format "%s --no-pager grep -h %s \"%s\""
eacl-git-program
git-grep-opts
search-regex)))
;; use grep
(t
(format "%s -rshI %s -- \"%s\" ."
eacl-grep-program
(eacl-grep-exclude-opts)
search-regex)))))))
(defun eacl-root-directory ()
"Get directory to grep text with N."
(or (funcall eacl-project-root-callback) default-directory))
(defun eacl-hint (time)
"Hint for candidates since TIME."
(format "candidates (%.01f seconds): "
(float-time (time-since time))))
(defun eacl-insert-text-at-point (lines &optional no-confirm-p)
"Use one of the LINES to insert text at point.
If NO-CONFIRM-P is t and, there's only one candidate, input it immediately."
(let* (selected
(line-end (line-end-position)))
(cond
((or (not lines) (= 0 (length lines)))
(message "Matched line does not exist."))
((and no-confirm-p (= 1 (length lines)))
;; one candidate, just complete it now
(eacl-replace-text (car lines) line-end))
(t
(when (setq selected (completing-read (eacl-hint (current-time)) lines))
(eacl-replace-text selected line-end))))))
(defun eacl-complete-line-internal (keyword extra &optional deleted-p)
"Complete line(s) by grepping with KEYWORD, EXTRA information.
If DELETED-P is t and git grep is used, grep only from deleted code."
(let* ((default-directory (eacl-root-directory))
(cmd (eacl-search-command (eacl-shell-quote-argument keyword) nil deleted-p))
(orig-collection (eacl-get-candidates cmd "[\r\n]+" keyword deleted-p))
(line (string-trim (cdr extra)))
(collection (delq nil (mapcar `(lambda (s) (unless (string= s ,line) s))
(eacl-clean-candidates orig-collection)))))
(when eacl-debug
(message "eacl-complete-line-internal called. cmd=%s" cmd))
(eacl-insert-text-at-point collection extra)))
(defun eacl-line-beginning-position ()
"Get line beginning position."
(save-excursion (back-to-indentation) (point)))
(defun eacl-ensure-no-region-selected ()
"If region is selected, delete text out of selected region."
(when (region-active-p)
(let* ((b (region-beginning))
(e (region-end)))
;; delete text outside of selected region
(cond
((or (< b (line-beginning-position))
(< (line-end-position) e))
(error "Please select region inside current line!"))
(t
(delete-region e (line-end-position))
(delete-region (line-beginning-position) b)))
;; de-select region and move focus to region end
(when (and (boundp 'evil-mode) evil-mode (eq evil-state 'visual))
(evil-exit-visual-state)
(evil-insert-state))
(goto-char (line-end-position)))))
;;;###autoload
(defun eacl-complete-line (&optional deleted-p)
"Complete line by grepping in root.
The selected region replaces current line first.
The text from line beginning to current point is used as grep keyword.
Whitespace in the keyword matches any characters.
If DELETED-P is t and Git is used, complete from deleted code."
(interactive "P")
(eacl-ensure-no-region-selected)
(let* ((cur-line-info (eacl-current-line-info))
(cur-line (car cur-line-info))
(eacl-keyword-start (eacl-line-beginning-position))
(keyword (eacl-get-keyword cur-line)))
(eacl-complete-line-internal keyword cur-line-info deleted-p)
(setq eacl-keyword-start nil)))
(defmacro eacl-find-multiline-end (indentation)
"Find next line with same INDENTATION."
`(let* ((rlt (re-search-forward ,indentation (point-max) t)))
(if rlt (line-end-position))))
(defun eacl-html-p ()
"Is html related mode."
(or (memq major-mode '(web-mode rjsx-mode xml-mode js2-jsx-mode))
(derived-mode-p 'sgml-mode)))
(defun eacl-extract-matched-multiline (line linenum file &optional html-p)
"Extract matched lines start from LINE at LINENUM in FILE.
If HTML-P is not t, current `major-mode' support html tags.
Return (cons multiline-text end-line-text) or nil."
(when eacl-debug
(message "eacl-extract-matched-multiline called => %s %s %s %s" line linenum file html-p))
(let* ((beg (line-beginning-position))
end
rlt)
(when (string-match "^\\([ \t]*\\)\\(.*\\)*" line)
(let* ((case-fold-search nil)
(leading-whitespaces (match-string 1 line))
(pattern (concat "^" leading-whitespaces "[^ \t\r\n]"))
end-line
(continue t)
line)
(save-excursion
(while continue
;; skip current line which is already processed
(forward-line)
(goto-char (line-beginning-position))
(cond
((not (setq end (eacl-find-multiline-end pattern)))
;; no multiline candidate, quit
(setq continue nil))
(t
(goto-char end)
(setq line (eacl-current-line-text))
(when (and (not (string-match "^[ \t]*[\\[{(][ \t]*$" line))
(not (and html-p
;; eng html tag can't be ">"
(string-match "^[ \t]*>[ \t]*$" line))))
;; candidate found!
(setq rlt (buffer-substring-no-properties beg end))
(setq continue nil))))))))
(when eacl-debug
(message "eacl-extract-matched-multiline rlt=%s" rlt))
rlt))
;;;###autoload
(defun eacl-complete-multiline ()
"Complete multi-line code or html tag.
The selected region will replace current line first.
The text from line beginning to current point is used as grep keyword.
Whitespace in keyword could match any characters."
(interactive)
(eacl-ensure-no-region-selected)
(let* ((orig-linenum (count-lines 1 (point)))
(orig-file (and buffer-file-name (file-truename buffer-file-name)))
(eacl-keyword-start (eacl-line-beginning-position))
(keyword (eacl-get-keyword (car (eacl-current-line-info))))
(default-directory (eacl-root-directory))
(cmd (eacl-search-command (eacl-shell-quote-argument keyword) t))
(time (current-time))
(orig-collection (eacl-get-candidates cmd "[\r\n]+" keyword))
(line-end (line-end-position))
(html-p (eacl-html-p))
rlt
cached-file-name
cached-file-content)
(when orig-collection
(dolist (item orig-collection)
(when (string-match "\\`\\([^:]+\\):\\([0-9]+\\):\\([^:]+\\)\\'" item)
(let* ((strs (split-string item ":"))
(file (car strs))
(linenum (string-to-number (nth 1 strs)))
(line (nth 2 strs))
cand)
(when (or (not (string= file orig-file))
(not (= linenum orig-linenum)))
;; item's format is like '~/proj1/ab.el:39: (defun hello() )'
(with-temp-buffer
(cond
((or (not (string= cached-file-name file))
(not cached-file-content))
(insert-file-contents file)
(setq cached-file-name file)
(setq cached-file-content (buffer-string)))
(t
(insert cached-file-content)))
(goto-char (point-min))
(forward-line (1- linenum))
(goto-char (line-beginning-position))
(when (setq cand (eacl-extract-matched-multiline line
linenum
file
html-p))
(when eacl-debug (message "cand=%s" cand))
(add-to-list 'rlt cand)))))))
(cond
((or (not rlt) (= (length rlt) 0))
(message "No multiline match was found!"))
((= (length rlt) 1)
(eacl-replace-text (car rlt) line-end))
(t
(let* ((cands (mapcar 'eacl-multiline-candidate-summary rlt))
(selected (completing-read (eacl-hint time) cands)))
(when selected
(eacl-replace-text (cdr (assoc selected cands)) line-end))))))))
(defun eacl-extract-matched-lines (keyword buffer info)
"Extract lines matching KEYWORD from the BUFFER with INFO of current input."
(set-buffer buffer)
(let* ((strip-prompt (and (get-buffer-process (current-buffer))
comint-prompt-regexp))
(current-input (car info))
(lines (split-string (buffer-string) "[\r\n]+")))
(when strip-prompt
(setq strip-prompt (concat strip-prompt "\\(.*\\)$"))
;; strip the prompt in shell buffer
(setq lines (mapcar (lambda (l)
(when (string-match strip-prompt l)
(setq l (match-string 1 l)))
l)
lines)))
(setq lines (cl-remove-if (lambda (l)
(or (not (string-match keyword l))
(equal l current-input)))
lines))
(setq lines (cl-remove-duplicates lines))
lines))
(defun eacl-buffer-ignore-p (buffer)
"Ignore the BUFFER for line candidates extraction."
(let ((buf-name (buffer-name buffer)))
(set-buffer buffer)
(cl-some (lambda (e)
(or (and (stringp e)
(string-match e buf-name)
(not (cl-some (lambda (p) (string-match p buf-name))
eacl-include-buffers)))
(eq e major-mode)
(> (buffer-size buffer) eacl-search-buffer-max-size)))
eacl-ignore-buffers)))
(defun eacl-complete-line-from-buffer ()
"Complete one line from buffer(s).
Set `eacl-ignore-buffers' and `eacl-include-buffers' to specify ignored&included
buffers."
(interactive)
(let* ((original-buf (current-buffer))
strip-prompt
(original-buf-name (buffer-name original-buf))
(eacl-keyword-start (eacl-line-beginning-position))
(info (eacl-current-line-info))
(keyword (eacl-get-keyword (car info) t))
(all-bufs (cl-delete-if 'eacl-buffer-ignore-p (buffer-list)))
lines
cands)
;; Use current buffer to complete
(when (eacl-buffer-ignore-p original-buf)
(push original-buf all-bufs))
;; Use other buffer plus current buffer
(dolist (buf all-bufs)
(when (setq lines (eacl-extract-matched-lines keyword buf info))
;; append lines to cands
(setq cands (nconc cands lines))))
;; go back to original buffer
(set-buffer original-buf)
;; complete line now
(eacl-insert-text-at-point cands)
(setq eacl-keyword-start nil)))
(defun eacl-complete-line-from-buffer-or-project (&optional deleted-p)
"When editing a physical file, complete one line by grepping project root.
Or else, complete one line by search all buffers.
If DELETED-P is t and git grep is used, grep only from deleted code.
You can set `eacl-ignore-buffers' and `eacl-include-buffers' to specify
ignored&included buffers. Please note `eacl-include-buffers' has higher
priority than `eacl-ignore-buffers'."
(interactive "P")
(cond
(buffer-file-name
(eacl-complete-line deleted-p))
(t
(eacl-complete-line-from-buffer))))
(provide 'eacl)
;;; eacl.el ends here