~khiker/+junk/icomp

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
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
;;; icomp.el --- inline completion functions for emacs

;; Copyright (C) 2007-2008  khiker

;; Author: khiker <khiker+elisp@gmail.com>
;;         plus   <MLB33828@nifty.com>

;; Keywords: dabbrev lisp info completion inline

;; This file 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 2, or (at your option)
;; any later version.

;; This file 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 GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary:

;; inline completion for multiple selection.
;; This package tested on Emacs 22 and Emacs 23.0.50.2.

;;; Installation:

;; Put this file into your load-path, and,
;; add following line to your .emacs.
;;
;;   (require 'icomp)
;;   (global-set-key "\M-/" 'icomp-dabbrev-expand)
;;   (global-set-key (kbd "C-M-/") 'icomp-dabbrev-expanding)
;;
;;   (add-hook
;;    'emacs-lisp-mode-hook
;;    '(lambda()
;;       (define-key emacs-lisp-mode-map "\C-\M-i" 'icomp-elisp-complete-symbol)
;;   ;; continues version
;;   ;;    (define-key emacs-lisp-mode-map "\C-\M-i" 'icomp-elisp-completing-symbol)
;;    ))
;;
;;   (global-set-key "\C-\M-i" 'icomp-info-complete-symbol)
;;   ;; continues version
;;   ;; (global-set-key "\C-\M-i" 'icomp-info-completing-symbol)
;;
;; Please match key setting to your favor.

;;; Note:

;; following functions was remodeled by based on functions of SKK.
;;
;; icomp-in-minibuffer-p
;; icomp-show-tooltip
;; icomp-mouse-position
;; icomp-inline-show
;; icomp-inline-hide
;; icomp-inline-show-vertical
;;
;; skk.el --- Daredevil SKK (Simple Kana to Kanji conversion program)
;;
;; Copyright (C) 1988-1997 Masahiko Sato <masahiko@kuis.kyoto-u.ac.jp>
;; Copyright (C) 1999-2007 SKK Development Team <skk@ring.gr.jp>
;;
;; -----
;;
;; and, multibyte treatment of icomp-dabbrev-expand was remodeled by
;; based on dabbrev-ja.el.
;;
;; dabbrev-ja.el
;;
;; Copyright (C) 2002 TSUCHIYA Masatoshi <tsuchiya@namazu.org>

;;; Code:

(require 'dabbrev)
(require 'info-look)

;; Variables:

(defconst icomp-version "0.0.10"
  "icomp's version")

(defgroup icomp nil
  "inline completion functions for emacs"
  :tag "inline completion functions for emacs"
  :group 'icomp)

(defcustom icomp-select-keys
  '("a" "s" "d" "f" "j" "k" "l")
  "*Key config for selecting options."
  :type  '(repeat string)
  :group 'icomp)

(defcustom icomp-multi-selection-keys '("\M-/")
  "*Key config for moving to multiple option displaying menu."
  :type  '(repeat string)
  :group 'icomp)

(defcustom icomp-next-keys '("\M-/" " ")
  "*Key config for moving to next complementarity list."
  :type  '(repeat string)
  :group 'icomp)

(defcustom icomp-previous-keys '("x" "\177")
  "*Key config for moving to previous complementarity list."
  :type  '(repeat string)
  :group 'icomp)

(defcustom icomp-vinline-select-next-key '("\C-n")
  "*In vertical inline, the key that selects next one."
  :type  '(repeat string)
  :group 'icomp)

(defcustom icomp-vinline-select-previous-key '("\C-p")
  "*In vertical inline, the key that selects previous one."
  :type  '(repeat string)
  :group 'icomp)

(defcustom icomp-vinline-select-return-key '("\C-m" [(retrun)])
  "*In vertical inline, the key that insert selected one."
  :type  '(repeat string)
  :group 'icomp)

(defcustom icomp-tooltip-timeout 2000
  "*Seconds for displaying tooltip."
  :type  'number
  :group 'icomp)

(defcustom icomp-tooltip-params
  '((foreground-color . "white")
    (background-color . "NavyBlue")
    (border-color . "blue"))
  "*Face config for tooltip."
  :type  'boolean
  :group 'icomp)

(defface icomp-highlight-inline
  '((((class color) (background  dark))
     (:foreground "white" :background "NavyBlue" :bold t))
    (((class color) (background light))
     (:foreground "white" :background "NavyBlue" :bold t))
    (t (:bold t)))
  "face of icomp-highlight-face and icomp-inline-show-face."
  :group 'icomp)

(defface icomp-highlight-inline-selected
  '((((class color) (background  dark))
     (:foreground "white" :background "purple" :bold t))
    (((class color) (background light))
     (:foreground "white" :background "purple" :bold t))
    (t (:bold t)))
  "face of icomp-vinline-select-face."
  :group 'icomp)

(defcustom icomp-highlight-face 'icomp-highlight-inline
  "*Face to highlight frist time expanded string."
  :type  'face
  :group 'icomp)

(defcustom icomp-inline-show-face 'icomp-highlight-inline
  "*A Variable to appoint a face when display a option in inline.
By skk-inline-show-face of SKK."
  :type  '(radio (face  :tag "Specify face")
                 (const :tag "Use default face attribute of candidate string."))
  :group 'icomp)

(defcustom icomp-vinline-select-face 'icomp-highlight-inline-selected
  "*In vertical inline, The face that highlights a selected line."
  :type  '(radio (face  :tag "Specify face")
                 (const :tag "Use default face attribute of candidate string."))
  :group 'icomp)

(defcustom icomp-display-style 'vertical
  "*displaying style for multiple selection menu.
---------------------------------
|  vertical|vertical inline show|
|    inline|  normal inline show|
|minibuffer|     minibuffer show|
|   tooltip|        tooltip show|
|         t|  normal inline show|
|       nil|  normal inline show|
---------------------------------
Defualt is vertical inline."
  :type '(choice (const :tag "minibuffer" minibuffer)
                 (const :tag "inline" inline)
                 (const :tag "vertical inline" vertical)
                 (const :tag "tooltip" tooltip))
  :group 'icomp)

(defcustom icomp-display-no-candidate-message t
  "*Non-nil means displaying message when `icomp-read' could not
found completion candidate."
  :type  'boolean
  :group 'icomp)

(defcustom icomp-tags-exts '("c" "h" "C" "H" "cpp" "CPP" "hpp" "HPP"
                             "html" "htm" "tex" "el" "l" "lua" "lisp"
                             "pl" "php" "ps" "py" "scm" "asm")
  "*"
  :type '(repeat string)
  :group 'icomp)

(defvar icomp-inline-overlays nil)
(defvar icomp-last-key nil)


;; Macros:

(defmacro icomp-dabbrev-ja (&rest body)
  "dabbrev for multibyte words.
From http://namazu.org/~tsuchiya/elisp/dabbrev-ja.e"
  `(let* ((dabbrev--abbrev-char-regexp
           (let ((c (char-category-set (char-before))))
             (cond
              ((aref c ?a) "[-_A-Za-z0-9]") ; ASCII
              ((aref c ?j)                  ; Japanese
               (cond
                ((aref c ?K) "\\cK") ; katakana
                ((aref c ?A) "\\cA") ; 2byte alphanumeric
                ((aref c ?H) "\\cH") ; hiragana
                ((aref c ?C) "\\cC") ; kanji
                (t "\\cj")))
              ((aref c ?k) "\\ck") ; hankaku-kana
              ((aref c ?r) "\\cr") ; Japanese roman ?
              (t dabbrev-abbrev-char-regexp)))))
     ,@body))


;; Functions:

(defun icomp-dabbrev-expand ()
  "The function that do dynamic abbrev expansion for multiple selection.

When you executed the function, the function behaves as well as normal
dabbrev-expand. It complement only one candidate.
If that candidate is not something that you want, It displays multiple
selection by pushing `M-/' or key that launch icomp-dabbrev-expand.

The abbrev displayed at a time is three in default.
This number is controled by `icomp-select-keys'.

style format is prepared three types.
- minibuffer
- inline
- tooltip
if variable `icomp-display-style' is non-nil,
display by vertical inline.
Nil means displaying inline.
If variable is nil, And point is minibuffer, display by minibuffer."
  (interactive)
  (dabbrev--reset-global-variables)
  (icomp-dabbrev-ja
   (let* ((ex-target (dabbrev--abbrev-at-point))
          (expands (list (dabbrev--find-expansion ex-target
                                                  0
                                                  dabbrev-case-fold-search))))
     (icomp-read1 ex-target
                  '(lambda (ex-target num alist &optional max)
                     (when (= num 0)
                       (setq expands
                             (nreverse
                              (cons (car expands)
                                    (nreverse
                                     (dabbrev--find-all-expansions ex-target
                                                                   nil))))))
                     (icomp-get-candidates num
                                           alist
                                           (icomp-get-completions ex-target
                                                                  expands)))))))

(defun icomp-dabbrev-expanding ()
  "execute dabbrev-expnad in succession.
see also `icomp-completing'."
  (interactive)
  (icomp-completing '(lambda ()
                       (dabbrev--reset-global-variables)
                       (icomp-dabbrev-ja (dabbrev--abbrev-at-point)))
                    '(lambda (target num alist &optional max)
                       (icomp-dabbrev-ja
                        (funcall 'icomp-dabbrev-get-candidates
                                 target
                                 num
                                 alist
                                 max)))))

(defun icomp-elisp-complete-symbol ()
  "The function that do lisp complete symbol for multiple selection.
also see `icomp-dabbrev-expand'."
  (interactive)
  (let* ((el-target (icomp-elisp-get-target))
         (el-symbols (icomp-elisp-get-completions el-target)))
    (icomp-read1 el-target
                 '(lambda (el-target num alist &optional max)
                    (icomp-get-candidates num
                                          alist
                                          (icomp-get-completions el-target
                                                                 el-symbols))))))

(defun icomp-elisp-completing-symbol ()
  "execute lisp-complete-symbol in succession."
  (interactive)
  (let* ((el-target (icomp-elisp-get-target))
         (el-symbols (icomp-elisp-get-completions el-target)))
    (icomp-completing 'icomp-elisp-get-target
                      '(lambda (el-target num alist &optional max)
                         (when (null alist)
                           ;; Since predicate had been already executed,
                           ;; so it is not necessary here.
                           (setq el-symbols
                                 (icomp-get-completions el-target
                                                        el-symbols)))
                         (icomp-get-candidates num
                                               alist
                                               el-symbols
                                               max)))))

(defun icomp-info-complete-symbol ()
  "The function that do info complete symbol for multiple selection.
also see `icomp-dabbrev-expand'."
  (interactive)
  (let ((start (point))
        (mode (info-lookup-select-mode))
        info-target info-symbols)
    (when mode
      (setq info-target (info-lookup-guess-default* 'symbol mode))
      (setq info-symbols (icomp-get-completions info-target
                                                (info-lookup->completions
                                                 'symbol
                                                 (info-lookup-select-mode))))
      ;; adjust point
      (end-of-line)
      (while (and (search-backward info-target nil t)
                  (< start (point))))
      (goto-char (+ (point) (length info-target)))
      (icomp-read1 info-target '(lambda (info-target num alist &optional max)
                                  (icomp-get-candidates num
                                                        alist
                                                        info-symbols))))))

(defun icomp-info-completing-symbol ()
  "execute info-complete-symbol in succession."
  (interactive)
  (let ((start (point))
        (mode (info-lookup-select-mode))
        info-target info-symbols)
    (when mode
      (setq info-target (info-lookup-guess-default* 'symbol mode))
      ;; adjust point
      (end-of-line)
      (while (and (search-backward info-target nil t)
                  (< start (point))))
      (goto-char (+ (point) (length info-target)))
      (icomp-completing '(lambda ()
                           (let ((mode (info-lookup-select-mode)))
                             (setq info-target
                                   (info-lookup-guess-default* 'symbol mode))))
                        '(lambda (info-target num alist &optional max)
                           (when (null alist)
                             (setq info-symbols
                                   (icomp-get-completions info-target
                                                          (info-lookup->completions
                                                           'symbol
                                                           (info-lookup-select-mode)))))
                           (icomp-get-candidates num
                                                 alist
                                                 info-symbols))))))

(defun icomp-tags-complete-symbol ()
  "The function that do complete-tag for multiple selection.
also see `icomp-dabbrev-expand'."
  (interactive)
  (let* ((tag-target (funcall (or find-tag-default-function
                                  (get major-mode 'find-tag-default-function)
                                  'find-tag-default)))
         (tag-symbols (icomp-get-completions tag-target (tags-completion-table))))
    ;; move point to end point of target.
    (search-backward tag-target)
    (forward-char (length tag-target))
    ;; load tags automatically.
    (icomp-tags-load)
    (icomp-read1 tag-target '(lambda (tag-target num alist &optional max)
                               (icomp-get-candidates num
                                                     alist
                                                     tag-symbols)))))

(defun icomp-tags-completing-symbol ()
  "execute icomp-tags-complete-symbol in succession."
  (interactive)
  (let* ((tag-target (funcall (or find-tag-default-function
                                  (get major-mode 'find-tag-default-function)
                                  'find-tag-default)))
         (tag-symbols (icomp-get-completions tag-target (tags-completion-table))))
    ;; move point to end point of target.
    (search-backward tag-target)
    (forward-char (length tag-target))
    ;; load tags automatically.
    (icomp-tags-load)
    (icomp-completing '(lambda ()
                         (funcall (or find-tag-default-function
                                      (get major-mode
                                           'find-tag-default-function)
                                      'find-tag-default)))
                      '(lambda (tag-target num alist &optional max)
                         (when (null alist)
                           (setq tag-symbols
                                 (icomp-get-completions tag-target
                                                        tag-symbols)))
                         (icomp-get-candidates num
                                               alist
                                               tag-symbols)))))

(defun icomp-dabbrev-get-candidates (dtarget num alist &optional max)
  "get new abbrevs. return list of abbrev."
  (let ((len (if max max (length icomp-select-keys)))
        (dtarget (if (and dtarget (stringp dtarget))
                     dtarget
                   (dabbrev--abbrev-at-point)))
        (i 0)
        abbrev abbrev-list)
    (if (null (nth num alist))
        (while (and (> len i)
                    (setq abbrev
                          (dabbrev--find-expansion
                           dtarget 0 dabbrev-case-fold-search)))
          (add-to-list 'abbrev-list abbrev t)
          (setq i (1+ i)))
      (setq abbrev-list (nth num alist)))
    abbrev-list))

(defun icomp-elisp-get-target ()
  "get target for emacs lisp completion."
  (let ((end (point))
        (beg (save-excursion
               (with-syntax-table emacs-lisp-mode-syntax-table
                 (backward-sexp 1)
                 (while (= (char-syntax (following-char))
                           ?\')
                   (forward-char 1))
                 (point)))))
    (buffer-substring beg end)))

(defun icomp-elisp-get-completions (etarget)
  "return list of elisp symbol."
  (let* ((predicate (if (eq (char-after (1- (- (point)
                                               (string-width etarget))))
                            ?\()
                        'fboundp
                      (function (lambda (sym)
                                  (or (boundp sym) (fboundp sym)
                                      (symbol-plist sym))))))
         ;; get candidates from obarray using function mapatoms.
         ;; lambda expression divides symbols to function and variables.
         ;; this mapatoms expression returns list of completion candidates.
         (completions (let ((tlen (length etarget))
                            tstr tlist)
                        (mapatoms '(lambda (x)
                                     (setq tstr (format "%s" x))
                                     (when (and (funcall predicate x)
                                                (>= (length tstr) tlen)
                                                (string= etarget
                                                         (substring tstr
                                                                    0
                                                                    tlen)))
                                       (setq tlist (cons x tlist)))))
                        tlist)))
    ;; [issue] Had better sort a list?
    (nreverse completions)))

(defun icomp-tags-load ()
  "load TAGS file."
  (let ((tag-file (concat default-directory "TAGS"))
        (dir-files (directory-files default-directory))
        (regex '())
        tag-target)
    (dolist (i dir-files)
      (when (and (member (file-name-extension i) icomp-tags-exts)
                 (not (member (file-name-extension i) regex)))
        (setq regex (cons (file-name-extension i) regex))))
    (setq tag-target (mapconcat '(lambda (x) (concat " *." x)) regex ""))
    ;; Should i consider update?
    (when (not (file-exists-p tag-file))
      (shell-command (concat "etags" tag-target " -o TAGS 2>/dev/null")))
    (visit-tags-table tag-file)))

(defun icomp-get-leaves (alist)
  "count leaves of alist."
  (eval (cons '+ (mapcar '(lambda (x) (length x)) alist))))

(defun icomp-get-completions (comp-target completions &optional predicate)
  "return list of symbol."
  (let ((tlen (length comp-target))
        tlist)
    (mapc '(lambda (x)
             (when (consp x)
               (setq x (car x)))
             (when (not (stringp x))
               (setq x (format "%s" x)))
             (when (and (>= (length x) tlen)
                        (string= comp-target (substring x 0 tlen))
                        (if predicate (funcall predicate x) t))
               (setq tlist (cons x tlist))))
          completions)
    tlist))

(defun icomp-get-candidates (num alist completions &optional max)
  "get new candidates from completions. return length of icomp-select-keys
or num of max."
  (let ((len (if max max (length icomp-select-keys)))
        (candidate-list (nth num alist))
        candidates)
    (when (null candidate-list)
      (let* ((leaves (icomp-get-leaves alist))
             (completions (nthcdr leaves completions)))
        (setq candidate-list nil)
        (when completions
          (dotimes (i len)
            (when (car completions)
              (setq candidate-list (cons (car completions) candidate-list))
              (setq completions (cdr completions))))))
      (setq candidate-list (reverse candidate-list)))
    candidate-list))

(defun icomp-read1 (target get-candidates)
  "This function put up one candidate first, and next executes `icomp-read'.

Arguments:
`target' is completion target.
`get-candidates' is function that returns next candidates.

func's definition is following.
arguments for func.
- num: target number of alist.
- alist: associative arrangement.
- max(optional): max number of completion candidates.
For example:
 (func 1 '((a) (b c d) (e f g))) returns (b c d)
 (func 3 '((a) (b c d) (e f g))) returns next list of candidates."
  (setq icomp-last-key nil)    ; init last inputted last-key
  (let* ((candidate (car (funcall get-candidates target 0 nil 1)))
         (prompt "")
         action overlay)
    (cond
     ;; Expansion candidate found.
     (candidate
      ;; insert candidate
      (insert (substring candidate (length target)))
      ;; hightlight candidate
      (setq overlay (make-overlay (- (point) (length candidate)) (point)))
      (overlay-put overlay 'face icomp-highlight-face)
      ;; wait key input. and, record pushed key.
      (setq action (read-key-sequence-vector prompt))
      ;; reset hightlight
      (delete-overlay overlay)
      ;; record last-command-char
      (setq icomp-last-key last-command-char)
      (cond
       ((icomp-selection-keys-p action icomp-multi-selection-keys t)
        ;; reset candidate
        (delete-char (- (length candidate)))
        (insert target)
        ;; start multiple selection
        (icomp-read target get-candidates 1 (list (list candidate))))
       ;; execute command that bound to pushed key.
       (t
        (icomp-do-last-command action))))
     ;;  Expansion candidate *Not* found.
     (t
      (message "No expansion candidate for `%s' found" target)))))

(defun icomp-read (target get-candidates &optional num alist)
  "The function that completes expansion candidates for
multiple selection.

argumets:
Explanation of `target' and `func' exist `icomp-read1'.
`num' and `alist' are only used by called by `icomp-read1'.

The candidate displayed at a time is seven in default.
This number is controled by `icomp-select-keys'.

show next candidates: M-/, space, or launched key.
show previous candidates: x or Backspace
close: C-g

style format is prepared three types.
- minibuffer
- inline
- tooltip
- vertical inline.
if variable `icomp-display-style' is non-nil, display by vertical inline.
Nil means displaying inline.
If variable is nil, And point is minibuffer, display by minibuffer."
  ;; when last-key is not registered, init it.
  (when (null icomp-last-key)
    (setq icomp-last-key last-command-char))
  ;; setting local variable
  (let* ((num (if num num 0))
         (vert (and (not (icomp-in-minibuffer-p))
                    (or (eq icomp-display-style 'vertical)
                        (eq icomp-display-style 'tooltip)
                        (eq icomp-display-style 't))))
         candidate-list sel action prompt vtarget)
    (while (> num -1)
      ;; initialize variables
      (setq candidate-list (funcall get-candidates target num alist) ; get new candidates
            sel (null candidate-list)
            prompt ""
            action nil)
      ;; case: do not display "couldn't get new candidate" message.
      ;;       and, could not get new candidates.
      (when (and (null candidate-list)
                 (null icomp-display-no-candidate-message)
                 alist)
        (setq num (1- num)
              candidate-list (funcall get-candidates target num alist)
              sel (null candidate-list)))
      ;; set prompt
      (let ((len (length candidate-list))
            (tlist '())
            str)
        (dotimes (i len)
          (setq tlist (cons (concat (nth (- len i 1) icomp-select-keys)
                                    ": "
                                    (setq str
                                          (nth (- len i 1) candidate-list)))
                            tlist)))
        (setq prompt (mapconcat 'identity tlist (if vert "\n" " "))))
      ;; couldn't get new candidate
      (when (and (null candidate-list) icomp-display-no-candidate-message)
        (setq prompt " No expansion candidate. "))
      ;; create tooltip, overlay or minibuffer message
      (cond
       ;; case: icomp-display-no-candidate-message => nil
       ;;       search candidates on first time    => nil
       ((string= prompt ""))
       ;; point in minibuffer
       ((or (icomp-in-minibuffer-p)
            (eq icomp-display-style 'minibuffer))
        (setq action (read-key-sequence-vector prompt)))
       ;; use tooltip
       ((eq icomp-display-style 'tooltip)
        (let* ((P (icomp-mouse-position))
               (frame (car P))
               (x (cadr P))
               (y (cddr P))
               (oP (mouse-position))
               (oframe (car oP))
               ;; unless mouse curosr is on the frame,
               ;; not be able to get original mouse position.
               ;; this line is the countermeasure.
               (ox (or (cadr oP) 0))
               (oy (or (cddr oP) 15)))
          ;; move mouse position.
          (set-mouse-position frame x y)
          (icomp-show-tooltip prompt)
          (setq action (read-key-sequence-vector ""))
          (tooltip-hide)
          (set-mouse-position oframe ox oy)))
       ;; use vertical inline show
       ((eq icomp-display-style 'vertical)
        (icomp-inline-show-vertical prompt icomp-inline-show-face vtarget)
        (setq action (read-key-sequence-vector ""))
        (icomp-inline-hide))
       ;; use inline show
       (t
        (icomp-inline-show prompt icomp-inline-show-face)
        (setq action (read-key-sequence-vector ""))
        (icomp-inline-hide)))
      ;; if icomp-display-no-candidate-message is non-nil, prompt is not "".
      ;; if icomp-display-no-candidate-message is nil,
      ;; there is possibility of "" in the prompt.
      (when (not (string= prompt ""))
        ;; clear minibuffer
        (message "")
        (setq sel (length (member (string (aref action 0))
                                  (reverse icomp-select-keys))))
        ;; if this time did not use cache, update alist.
        (when (null (nth num alist))
          (add-to-list 'alist candidate-list t)))
      (cond
       ;; * features only vertical inline START
       ;; vinline highlight selection: next
       ((and (eq icomp-display-style 'vertical)
             (icomp-selection-keys-p action icomp-vinline-select-next-key))
        (cond
         ;; init
         ((null vtarget) (setq vtarget 0))
         ;; do nothing
         ((null candidate-list))
         ;; when vtarget is last one, display next candidates.
         ((and (= vtarget (1- (length (nth num alist))))
               candidate-list)
          (setq num (1+ num) vtarget 0))
         (t
          (setq vtarget (1+ vtarget)))))
       ;; vinline highlight selection: previous
       ((and (eq icomp-display-style 'vertical)
             (icomp-selection-keys-p action icomp-vinline-select-previous-key))
        (cond
         ;; init
         ((null vtarget) (setq vtarget (length icomp-select-keys)))
         ;; when vtarget is first one, display previsou candidates.
         ((= vtarget 0)
          (when (> num 0)
            (setq num (1- num))))
         (t
          (setq vtarget (1- vtarget)))))
       ;; vinline highlight selection: enter
       ((and (eq icomp-display-style 'vertical)
             (icomp-selection-keys-p action icomp-vinline-select-return-key))
        (when (and vtarget candidate-list)
          (setq sel (1+ vtarget)))
        (setq num -1))
       ;; * features only vertical inline END
       ;; next selection
       ((icomp-selection-keys-p action icomp-next-keys t)
        (when candidate-list
          (setq num (1+ num))))
       ;; previous selection
       ((icomp-selection-keys-p action icomp-previous-keys)
        (when (> num 0)
          (setq num (1- num))))
       ;; exit while loop
       (t (setq num -1))))
    ;; why variables name is " *not* completed"
    ;; this reason is to specification of icomp-reading.
    ;; icomp-reading finished at time of the case of the
    ;; "can not get new candidates" or
    ;; the case of the "cat get new candidates and completed".
    ;; The reason is because I wanted to unify the cdr of these.
    ;; --
    ;; can not get new candidates => nil
    ;; cat get new candidates and completed => (alist nil)
    ;; cat get new candidates and not completed => (alist t)
    (let ((not-completed t))
      (cond
       ;; get no candidates.
       ((null action)
        (message ""))
       ;; pushed a key except icomp-select-keys
       ((or (= sel 0) (> sel (length candidate-list)))
        (icomp-do-last-command action))
       (t
        (setq not-completed nil)
        ;; insert selected string
        (insert
         (substring (nth (1- sel) candidate-list) (length target)))))
      (if (and (= (length alist) 0) (null candidate-list))
          nil
        ;; make values. cons alist and not-complete or not
        (cons (if (listp alist) alist (list alist)) not-completed)))))

(defun icomp-completing (get-target get-candidates)
  "The function that complete in succession.

If applied to either of the following, quit execution of a function.

1. execute completion.
2. buffer-file-name is changed.
3. original point > now point.
4. do not get new target of completion.
4. next completion target length < first completion target length.
5. target of completion changed.
6. the string between an original point and the current point has space or tab.
7. move another line.
8. can not get new candidates.

argumets:
`get-target' is function that returns next completion target.
`get-candidates': is function that returns next candidates.

execute of completion is upcase.
for example:
not a, s, d, f ... but A, S, D, F ...

if completion candidates is nothing, do not display message.

variable `icomp-next-keys' `icomp-previous-keys' is nil.

As known problem, if user uses vertical inline and completes C-n...C-m,
program continues displaying vertical inline."
  (setq icomp-last-key nil)
  (let* ((icomp-select-keys (mapcar 'upcase icomp-select-keys))
         (icomp-display-no-candidate-message nil)
         (icomp-next-keys nil)
         (icomp-previous-keys nil)
         (line (count-lines (point-min) (point)))
         (buf (buffer-file-name))
         (def-target (funcall get-target))
         (word def-target)
         (len (length def-target))
         (op (point)))
    (while (and (not (member (cond
                              ((and last-input-char
                                    (integerp last-input-char))
                               (char-to-string last-input-char))
                               (t
                                nil))
                             icomp-select-keys))
                (string= buf (buffer-file-name))
                (>= (point) op)
                (setq word (funcall get-target)) ; get new target
                (>= (length word) len)
                (string= (substring word 0 len) def-target)
                (not (string-match " \\|	" ; should be a variable?
                                   (buffer-substring-no-properties op (point))))
                (= line (+ (count-lines (point-min) (point)) (if (bolp) 1 0)))
                (cdr (icomp-read word get-candidates)))
      (setq icomp-last-key nil))))

(defun icomp-selection-keys-p (action keys &optional last)
  "Check whether these keys were pushed.
1. A key same as the key which you used to lauch icomp-read1 or icomp-read.
2. one of variables keys."
  (when action
    (not (not (memq (aref action 0)
                    (apply 'append
                           (when last
                             (list icomp-last-key))
                           (mapcar
                            (lambda (x)
                              (cond
                               ((stringp x)
                                (listify-key-sequence x))
                               ((numberp x)
                                (list x))
                               ((listp x)
                                (list (event-convert-list x)))
                               ((vectorp x)
                                (list (event-convert-list (aref x 0))))))
                            keys)))))))

(defun icomp-do-last-command (action)
  "Execute command assigned to the key which you input."
  (let ((last-command-char (aref action 0))
        (command (key-binding action)))
    (when command
      (call-interactively command))
    (message "")))

(defun icomp-in-minibuffer-p ()
  "Check whether point is current-buffer or minibuffer.
by skk-in-minibuffer-p of SKK."
  (eq (current-buffer) (window-buffer (minibuffer-window))))

(defun icomp-show-tooltip (text)
  "Display tooltip.
by skk-tooltip-show-1 of SKK."
  (condition-case error
      (let ((params (copy-sequence tooltip-frame-parameters))
            fg bg)
        (if icomp-tooltip-params
            ;; tooltip display config for user
            (dolist (cell icomp-tooltip-params)
              (setq params (tooltip-set-param params
                                              (car cell)
                                              (cdr cell))))
          ;; set the default of tooltip
          (setq fg (face-attribute 'tooltip :foreground))
          (setq bg (face-attribute 'tooltip :background))
          (when (stringp fg)
            (setq params (tooltip-set-param params 'foreground-color fg))
            (setq params (tooltip-set-param params 'border-color fg)))
          (when (stringp bg)
            (setq params (tooltip-set-param params 'background-color bg))))
        (x-show-tip (propertize text 'face 'tooltip)
                    (selected-frame)
                    params
                    icomp-tooltip-timeout
                    tooltip-x-offset
                    tooltip-y-offset))
    (error
     (message "Error while displaying tooltip: %s" error)
     (sit-for 1)
     (message "%s" text))))

(defun icomp-mouse-position ()
  "Return the position of point as (FRAME X . Y).
Analogous to mouse-position.

by skk-e21-mouse-position of SKK."
  (let* ((w (selected-window))
         (edges (window-edges w))
         (list
          (compute-motion
           (max (window-start w) (point-min))
           '(0 . 0)
           (point)
           (cons (window-width w) (window-height w))
           (1- (window-width w))
           (cons (window-hscroll w) 0)
           w)))
    (cons (selected-frame)
          (cons (+ (car edges)       (car (cdr list)))
                (+ (car (cdr edges)) (car (cdr (cdr list))))))))

(defun icomp-inline-show (string face)
  "Display string on inline by using overlay.
by skk-inline-show of SKK."
  (icomp-inline-hide)
  (unless (icomp-in-minibuffer-p)
    (let (base-ol)
      (setq base-ol (make-overlay (point) (point)))
      (overlay-put base-ol
                   'after-string
                   (apply #'propertize string
                          (if face `(face ,face) nil)))
      (setq icomp-inline-overlays (cons base-ol icomp-inline-overlays)))))

(defun icomp-inline-show-vertical (string face &optional target)
  "Display string on vertical inline by using overlay.
by skk-inline-show-vertical of SKK."
  (icomp-inline-hide)
  (unless (icomp-in-minibuffer-p)
    (let* ((margin 2)
           (beg-col (current-column))
           (candidates (split-string string "\n"))
           (max-width (apply 'max (mapcar 'string-width candidates)))
           (i 0)
           bottom ol invisible)
      ;; first setting: ol
      (setq ol (make-overlay (point) (1+ (point))))
      (overlay-put ol 'after-string "")
      (setq icomp-inline-overlays (cons ol icomp-inline-overlays))
      (dolist (str candidates)
        ;; adjust string length
        (setq str
              (concat str (make-string (- max-width (string-width str)) ? )))
        ;; add face to candidate.
        (when face
          (setq str (propertize str 'face face)))
        (when (and target (= i target))
          (setq str (propertize str 'face icomp-vinline-select-face)))
        (save-excursion
          (setq bottom (not (and (= 0 (forward-line (1+ i))) (bolp))))
          (end-of-line)
          (cond
           (bottom
            (setq ol
                  (prog1 (car icomp-inline-overlays)
                    (setq icomp-inline-overlays (cdr icomp-inline-overlays))))
            (setq str (concat (overlay-get ol 'after-string)
                              "\n" (make-string beg-col ? ) str)))
           ((> beg-col (current-column))
            ;; To adjust column number, add space.
            (setq str (concat (make-string (- beg-col (current-column)) ? )
                              str)))
           ((= beg-col (current-column))) ; do nothing.
           (t
            ;; move point to start point of overlay.
            (while (and (not (bolp))
                        (< beg-col (current-column)))
              (backward-char))
            ;; when far left of overlay overlap with multibyte char, do adjust.
            (unless (= beg-col (current-column))
              (setq str (concat (make-string (- beg-col (current-column)) ? )
                                str)))))
          ;; In this time, point is start point of overlay.
          (unless bottom
            (let ((ol-beg (point))
                  (insert-width (string-width str))
                  ol-width base-ol)
              ;; decide end point of overlay.
              (unless (eolp)
                (forward-char))
              (while (and (not (eolp))
                          (< (setq ol-width (string-width
                                             (buffer-substring
                                              ol-beg (point))))
                             insert-width))
                (forward-char))
              ;; when far right of overlay overlap with multibyte char, do adjust.
              (when (and ol-width
                         (> ol-width insert-width))
                (setq str (concat str
                                  (make-string (- ol-width insert-width) ? ))))
              (setq ol (make-overlay ol-beg (point)))
              ;; So as not succeed face of former text, make the overlay behind original.
              (setq base-ol (make-overlay (point) (1+ (point))))
              (overlay-put base-ol 'face 'default)
              (setq icomp-inline-overlays (cons base-ol icomp-inline-overlays))
              ;; check visibility of candidate
              (unless (pos-visible-in-window-p (point))
                (setq invisible t)))))
        (overlay-put ol 'invisible t)
        (overlay-put ol 'after-string str)
        (setq icomp-inline-overlays (cons ol icomp-inline-overlays))
        (setq i (1+ i)))
      ;; adjusting display
      (when (or invisible
                (and bottom
                     (> (1+ (length icomp-select-keys))
                        (- (if (fboundp 'window-body-height)
                               (window-body-height)
                             (- (window-height)
                                (if mode-line-format 1 0)
                                (if header-line-format 1 0)))
                           (count-lines (window-start) (point))))))
        (recenter (- (1+ (length icomp-select-keys)))))
      (scroll-left (max 0
                        (- (+ beg-col max-width 1)
                           (window-width) (window-hscroll)))))))

(defun icomp-inline-hide ()
  "Delete overlay of icomp-inline-show.
by skk-inline-hide of SKK."
  (when icomp-inline-overlays
    (dolist (ol icomp-inline-overlays)
      (delete-overlay ol))
    (setq icomp-inline-overlays nil)))

(provide 'icomp)

;; Local Variables:
;; Coding: iso-2022-7bit
;; End:

;; icomp.el ends here