summaryrefslogtreecommitdiffstats
path: root/fcomp.el
blob: 5b9524b11572cc9c98a4c5f8c42333e4cdc9660b (plain) (blame)
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
;;; fcomp.el --- fcomp front-end                     -*- lexical-binding: t; -*-

;; Copyright (C) 2019  Anastasis Grammenos

;; Author: Anastasis Grammenos <anastasis.gramm2@gmail.com>
;; Keywords: lisp
;; Version: 0.0.1

;; 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 <http://www.gnu.org/licenses/>.

;;; Commentary:

;; Emacs front-end for fcomp. Get it from https://ubuntos.dynu.net/git/fcomp

;;; Install:

;; git clone https://ubuntos.dynu.net/git/fcomp && cd fcomp
;; make && make install # install copies the binary under /usr/local/bin

;; In the emacs init file:
;; (add-to-list 'load-path "/your/custom/path")
;; (require 'fcomp)
;; (setq fcomp-path "/your/custom/path/fcomp")
;; (global-set-key (kbd "C-<tab>") 'fcomp-autocomplete)

;; or with use-package
;; (use-package fcomp
;;   :load-path "/your/custom/path"
;;   :bind (("C-<tab>" . fcomp-autocomplete))
;;   :config
;;   (setq fcomp-path "/your/custom/path/fcomp"))


;;; Code:

(require 'ido)
(require 'subr-x)

(defvar fcomp-path "/usr/local/bin/fcomp")
(defvar fcomp-min-word-length 3)
(defvar fcomp-extra-valid-chars "")
(defvar fcomp-extra-invalid-chars "")

(defun empty-string-p (string)
  "Return true if the string is empty or nil. Expects string."
  (or (null string)
      (zerop (length (string-trim string)))))

(defun fcomp--start (query)
  "Initiaize fcomp variables"
  (setq fcomp--query query)
  (setq fcomp--output ""))

(defun fcomp--filter (proc str)
  "Fill the output buffer"
  (let* ((fcomp-pre
          (if (empty-string-p (process-get proc 'input-word))
              nil
            (format "%s" (process-get proc 'input-word)))))
    (setq fcomp--output (concat fcomp--output str))))

(defun fcomp--handle-output ()
  "Handle output buffer"
  (let ((initial-input fcomp--query)
        (out-string fcomp--output)
        (result nil))
    (if (empty-string-p fcomp--output)
        nil
      (setq result (ido-completing-read
                    "Complete:"
                    (remove "" (split-string fcomp--output "\n"))
                    nil nil
                    initial-input)))
    (if (empty-string-p result)
        (message "%s" "No completion candidates")
      (insert (string-remove-prefix
               (if (empty-string-p initial-input)
                   (format "%s" "")
                 (format "%s" initial-input))
               result)))
    (setq fcomp--query "")
    (setq fcomp--output "")))

(defun fcomp-autocomplete ()
  "Autocompete using fcomp.c

When `thing-at-point' is a word, autocomplete it based on buffer contents.

When invoked with no `thing-at-point' show a list of all possible candidates.

Requires fcomp. Get it at URL `https://ubuntos.dynu.net/git/fcomp'"
  ;; Thanks Dan for the syntax-table trick
  ;; https://emacs.stackexchange.com/questions/9583/how-to-treat-underscore-as-part-of-the-word
  (interactive)
  (let ((table (copy-syntax-table (syntax-table))))
    (modify-syntax-entry ?- "w" table)
    (with-syntax-table table
      (fcomp--autocomplete (thing-at-point 'word 'no-properties)))))

(defun fcomp--autocomplete (word)
  "Start fcomp process and orchistrate it's output."
  (let ((term (if (empty-string-p word)
                  (format "%s" "-a")
                (format "%s" word)))
        (input (buffer-substring-no-properties (point-min) (point-max))))
    (fcomp--start word)
    (set-process-filter
     (start-process
      "fcomp"
      nil
      (format "%s"fcomp-path)
      "-w" (format "%s" fcomp-min-word-length)
      "-F" (format "%s" input)
      "-v" (format "%s" fcomp-extra-valid-chars)
      "-i" (format "%s" fcomp-extra-invalid-chars)
      term)
     #'fcomp--filter)
    (process-put (get-process "fcomp") 'input-word word)
    (accept-process-output (get-process "fcomp"))
    (fcomp--handle-output)))

(provide 'fcomp)
;;; fcomp.el ends here