rc

Create fix for Company-fuzzy bug

Apparently NOBODY on the interwebz seemed to have a bug like this, and so, nobody knew how to fix it. Related bugs in other projects and their general solutions also failed. So eventually I just tried a dirty hack and it works flawlessly again. I added the fixed file to the repo tracker and added a warning for future me so I never have to deal with this annoying shitstain ever again.

Author
Maarten Vangeneugden
Date
Sept. 15, 2024, 5:32 p.m.
Hash
226ba1eacdfb635e5235eb8141724d5cb1ce2045
Parent
d8fd1df30e145c60dbe0ad775b5aca18398c064a
Modified files
company-fuzzy.el
init.el

company-fuzzy.el

876 additions and 0 deletions.

View changes Hide changes
+
1
+
2
;; Copyright (C) 2019-2024  Shen, Jen-Chieh
+
3
;; Created date 2019-08-01 16:54:34
+
4
;; FIXED by Maarten Vangeneugden at 2024-09-15
+
5
+
6
;; Author: Shen, Jen-Chieh <jcs090218@gmail.com>
+
7
;; URL: https://github.com/jcs-elpa/company-fuzzy
+
8
;; Version: 1.4.0
+
9
;; Package-Requires: ((emacs "26.1") (company "0.8.12") (s "1.12.0") (ht "2.0"))
+
10
;; Keywords: matching auto-complete complete fuzzy
+
11
+
12
;; This file is NOT part of GNU Emacs.
+
13
+
14
;; This program is free software; you can redistribute it and/or modify
+
15
;; it under the terms of the GNU General Public License as published by
+
16
;; the Free Software Foundation, either version 3 of the License, or
+
17
;; (at your option) any later version.
+
18
+
19
;; This program is distributed in the hope that it will be useful,
+
20
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+
21
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
22
;; GNU General Public License for more details.
+
23
+
24
;; You should have received a copy of the GNU General Public License
+
25
;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
26
+
27
;;; Commentary:
+
28
;;
+
29
;; Fuzzy matching for `company-mode'.
+
30
;;
+
31
+
32
;;; Code:
+
33
+
34
(require 'cl-lib)
+
35
(require 'ffap)
+
36
(require 'subr-x)
+
37
+
38
(require 'company)
+
39
(require 'ht)
+
40
(require 's)
+
41
+
42
(defgroup company-fuzzy nil
+
43
  "Fuzzy matching for `company-mode'."
+
44
  :prefix "company-fuzzy-"
+
45
  :group 'company
+
46
  :link '(url-link :tag "Repository" "https://github.com/jcs-elpa/company-fuzzy"))
+
47
+
48
(defcustom company-fuzzy-sorting-backend 'alphabetic
+
49
  "Type for sorting/scoring backend."
+
50
  :type '(choice (const :tag "none" none)
+
51
                 (const :tag "alphabetic" alphabetic)
+
52
                 (const :tag "flex" flex)
+
53
                 (const :tag "flx" flx)
+
54
                 (const :tag "flx-rs" flx-rs)
+
55
                 (const :tag "flxy" flxy)
+
56
                 (const :tag "fuz-skim" fuz-skim)
+
57
                 (const :tag "fuz-clangd" fuz-clangd)
+
58
                 (const :tag "fuz-bin-skim" fuz-bin-skim)
+
59
                 (const :tag "fuz-bin-clangd" fuz-bin-clangd)
+
60
                 (const :tag "liquidmetal" liquidmetal)
+
61
                 (const :tag "sublime-fuzzy" sublime-fuzzy))
+
62
  :group 'company-fuzzy)
+
63
+
64
(defcustom company-fuzzy-prefix-on-top t
+
65
  "Have the matching prefix on top."
+
66
  :type 'boolean
+
67
  :group 'company-fuzzy)
+
68
+
69
(defcustom company-fuzzy-sorting-function nil
+
70
  "Function that gives all candidates and let you do your own sorting."
+
71
  :type '(choice (const :tag "None" nil)
+
72
                 function)
+
73
  :group 'company-fuzzy)
+
74
+
75
(defcustom company-fuzzy-sorting-score-function nil
+
76
  "Function that gives candidates with same score and let you do your own sorting."
+
77
  :type '(choice (const :tag "None" nil)
+
78
                 function)
+
79
  :group 'company-fuzzy)
+
80
+
81
(defcustom company-fuzzy-show-annotation t
+
82
  "Show annotation from source."
+
83
  :type 'boolean
+
84
  :group 'company-fuzzy)
+
85
+
86
(defcustom company-fuzzy-annotation-format " <%s>"
+
87
  "Annotation string format."
+
88
  :type 'string
+
89
  :group 'company-fuzzy)
+
90
+
91
(defcustom company-fuzzy-passthrough-backends nil
+
92
  "List of backends that already are fuzzy, so no filtering of candidates is done."
+
93
  :type 'list
+
94
  :group 'company-fuzzy)
+
95
+
96
(defcustom company-fuzzy-trigger-symbols '("." "->")
+
97
  "List of symbols that allow trigger company when there is no prefix."
+
98
  :type 'list
+
99
  :group 'company-fuzzy)
+
100
+
101
(defcustom company-fuzzy-completion-separator "[ \t\r\n]\\|\\_<\\|\\_>"
+
102
  "Use to identify the completion unit."
+
103
  :type 'string
+
104
  :group 'company-fuzzy)
+
105
+
106
(defcustom company-fuzzy-reset-selection nil
+
107
  "If non-nil, reset the selection to default."
+
108
  :type 'boolean
+
109
  :group 'company-fuzzy)
+
110
+
111
(defface company-fuzzy-annotation-face
+
112
  '((t (:inherit company-tooltip-annotation)))
+
113
  "Face for annotation."
+
114
  :group 'company-fuzzy)
+
115
+
116
(defvar-local company-fuzzy--prefix ""
+
117
  "Generic prefix.")
+
118
+
119
(defvar-local company-fuzzy--prefix-first ""
+
120
  "Store generic prefix's first character for caching.")
+
121
+
122
(defvar-local company-fuzzy--backends nil
+
123
  "Company fuzzy backends we are going to use.")
+
124
+
125
(defvar-local company-fuzzy--recorded-backends nil
+
126
  "Record down company local backends in current buffer.")
+
127
+
128
(defvar-local company-fuzzy--is-trigger-prefix-p nil
+
129
  "Flag to see if currently completion having a valid prefix.")
+
130
+
131
(defvar-local company-fuzzy--prefixes (make-hash-table :test 'equal)
+
132
  "Map for each backend's prefix.")
+
133
+
134
(defvar-local company-fuzzy--candidates (make-hash-table :test 'equal)
+
135
  "Map for each bakend's candidates.")
+
136
+
137
;;
+
138
;; (@* "External" )
+
139
;;
+
140
+
141
(declare-function flex-score "ext:flex.el")
+
142
(declare-function flx-score "ext:flx.el")
+
143
(declare-function flx-rs-score "ext:flx-rs.el")
+
144
(declare-function flx-rs-load-dyn "ext:flx-rs.el")
+
145
+
146
(declare-function flxy-score "ext:flxy.el")
+
147
(declare-function flxy-load-dyn "ext:flxy.el")
+
148
+
149
(declare-function fuz-calc-score-skim "ext:fuz.el")
+
150
(declare-function fuz-calc-score-clangd "ext:fuz.el")
+
151
(declare-function fuz-build-and-load-dymod "ext:fuz.el")
+
152
+
153
(declare-function fuz-bin-score-skim "ext:fuz-bin.el")
+
154
(declare-function fuz-bin-score-clangd "ext:fuz-bin.el")
+
155
(declare-function fuz-bin-load-dyn "ext:fuz-bin.el")
+
156
+
157
(declare-function liquidmetal-score "ext:liquidmetal.el")
+
158
+
159
(declare-function sublime-fuzzy-score "ext:sublime-fuzzy.el")
+
160
(declare-function sublime-fuzzy-load-dyn "ext:sublime-fuzzy.el")
+
161
+
162
(declare-function company-emmet--prefix "ext:company-emmet.el")
+
163
+
164
;;
+
165
;; (@* "Mode" )
+
166
;;
+
167
+
168
(defun company-fuzzy--init ()
+
169
  "Initialize all sorting backends."
+
170
  (cl-case company-fuzzy-sorting-backend
+
171
    (`flex (require 'flex))
+
172
    (`flx (require 'flx))
+
173
    (`flx-rs (require 'flx-rs) (flx-rs-load-dyn))
+
174
    (`flxy (require 'flxy) (flxy-load-dyn))
+
175
    ((or fuz-skim fuz-clangd)
+
176
     (require 'fuz)
+
177
     (unless (require 'fuz-core nil t) (fuz-build-and-load-dymod)))
+
178
    ((or fuz-bin-skim fuz-bin-clangd)
+
179
     (require 'fuz-bin) (fuz-bin-load-dyn))
+
180
    (`liquidmetal (require 'liquidmetal))
+
181
    (`sublime-fuzzy (require 'sublime-fuzzy) (sublime-fuzzy-load-dyn))))
+
182
+
183
(defun company-fuzzy--enable ()
+
184
  "Record down all other backend to `company-fuzzy--backends'."
+
185
  (company-fuzzy--init)
+
186
  ;; XXX Don't know why, but you need to clear it first to make local
+
187
  ;; variables work!
+
188
  (ht-clear company-fuzzy--prefixes)
+
189
  (ht-clear company-fuzzy--candidates)
+
190
  (unless company-fuzzy--recorded-backends
+
191
    (setq company-fuzzy--recorded-backends company-backends
+
192
          company-fuzzy--backends (company-fuzzy--normalize-backend-list company-fuzzy--recorded-backends))
+
193
    (setq-local company-backends '(company-fuzzy-all-other-backends))
+
194
    (setq-local company-transformers (append company-transformers '(company-fuzzy--sort-candidates)))
+
195
    (advice-add 'company--insert-candidate :before #'company-fuzzy--insert-candidate)
+
196
    (advice-add 'company-yasnippet--completions-for-prefix :around #'company-fuzzy-yasnippet--completions-for-prefix))
+
197
  (add-hook 'lsp-completion-mode-hook #'company-fuzzy--lsp-after-enabled nil t)
+
198
  (add-hook 'eglot-managed-mode-hook #'company-fuzzy--lsp-after-enabled nil t))
+
199
+
200
(defun company-fuzzy--disable ()
+
201
  "Revert all other backend back to `company-backends'."
+
202
  (when company-fuzzy--recorded-backends
+
203
    (setq-local company-backends company-fuzzy--recorded-backends)
+
204
    (setq-local company-transformers (delq 'company-fuzzy--sort-candidates company-transformers))
+
205
    (setq company-fuzzy--recorded-backends nil
+
206
          company-fuzzy--backends nil)
+
207
    (advice-remove 'company--insert-candidate #'company-fuzzy--insert-candidate)
+
208
    (advice-remove 'company-yasnippet--completions-for-prefix #'company-fuzzy-yasnippet--completions-for-prefix))
+
209
  (remove-hook 'lsp-completion-mode-hook #'company-fuzzy--lsp-after-enabled t)
+
210
  (remove-hook 'eglot-managed-mode-hook #'company-fuzzy--lsp-after-enabled t))
+
211
+
212
;;;###autoload
+
213
(define-minor-mode company-fuzzy-mode
+
214
  "Minor mode `company-fuzzy-mode'."
+
215
  :lighter " ComFuz"
+
216
  :group company-fuzzy
+
217
  (if company-fuzzy-mode (company-fuzzy--enable) (company-fuzzy--disable)))
+
218
+
219
(defun company-fuzzy-turn-on-company-fuzzy-mode ()
+
220
  "Turn on the `company-fuzzy-mode'."
+
221
  (company-fuzzy-mode 1))
+
222
+
223
;;;###autoload
+
224
(define-globalized-minor-mode global-company-fuzzy-mode
+
225
  company-fuzzy-mode company-fuzzy-turn-on-company-fuzzy-mode
+
226
  :group 'company-fuzzy
+
227
  :require 'company-fuzzy)
+
228
+
229
;;
+
230
;; (@* "Utilies" )
+
231
;;
+
232
+
233
(defun company-fuzzy--valid-candidates-p (candidates)
+
234
  "Return non-nil if CANDIDATES is list of valid candidates."
+
235
  (ignore-errors (stringp (nth 0 candidates))))
+
236
+
237
(defun company-fuzzy--async-candidates-p (candidates)
+
238
  "Return non-nil if CANDIDATES is in async format."
+
239
  (when (consp candidates)
+
240
    (and (eq (car candidates) :async) (functionp (cdr candidates)))))
+
241
+
242
(defun company-fuzzy--symbol-start ()
+
243
  "Return symbol start point from current cursor position."
+
244
  (ignore-errors
+
245
    (save-excursion
+
246
      (forward-char -1)
+
247
      (re-search-backward company-fuzzy-completion-separator)
+
248
      (point))))
+
249
+
250
(defun company-fuzzy--furthest-prefix ()
+
251
  "Return the possible furthest (greatest length) prefix."
+
252
  (ht-clear company-fuzzy--prefixes)
+
253
  (let ((final-len 0) final-prefix)
+
254
    (dolist (backend company-fuzzy--backends)
+
255
      (when-let ((prefix (ignore-errors (funcall backend 'prefix))))
+
256
        (ht-set company-fuzzy--prefixes backend prefix)
+
257
        (when-let* ((len (length prefix))
+
258
                    ((< final-len len)))
+
259
          (setq final-prefix prefix
+
260
                final-len len))))
+
261
    final-prefix))
+
262
+
263
(defun company-fuzzy--generic-prefix ()
+
264
  "Return the most generic prefix."
+
265
  (let ((start (company-fuzzy--symbol-start)))
+
266
    (ignore-errors
+
267
      (string-trim (buffer-substring-no-properties (or start (point-min)) (point))))))
+
268
+
269
(defun company-fuzzy--trigger-prefix-p ()
+
270
  "Check if current prefix a trigger prefix."
+
271
  (member company-fuzzy--prefix company-fuzzy-trigger-symbols))
+
272
+
273
(defun company-fuzzy--string-match (regexp string &optional start)
+
274
  "Safe way to execute function `string-match'.
+
275
See function `string-match' for arguments REGEXP, STRING and START."
+
276
  (or (ignore-errors (string-match regexp string start))
+
277
      (ignore-errors (string-match (regexp-quote regexp) string start))))
+
278
+
279
(defun company-fuzzy--string-match-p (regexp string &optional start)
+
280
  "Safe way to execute function `string-match-p'.
+
281
See function `string-match-p' for arguments REGEXP, STRING and START."
+
282
  (or (ignore-errors (string-match-p regexp string start))
+
283
      (ignore-errors (string-match-p (regexp-quote regexp) string start))))
+
284
+
285
(defun company-fuzzy--string-prefix-p (prefix string &optional ignore-case)
+
286
  "Safe way to execute function `string-prefix-p'.
+
287
See function `string-prefix-p' for arguments PREFIX, STRING and IGNORE-CASE."
+
288
  (ignore-errors (string-prefix-p prefix string ignore-case)))
+
289
+
290
(defun company-fuzzy--normalize-backend-list (backends)
+
291
  "Normalize all BACKENDS as list."
+
292
  (let (result-lst)
+
293
    (dolist (backend backends)
+
294
      (if (listp backend)
+
295
          (let ((index 0))
+
296
            (dolist (back backend)
+
297
              (when (company-fuzzy--string-prefix-p "company-" (symbol-name back))
+
298
                (push (nth index backend) result-lst))
+
299
              (setq index (1+ index))))
+
300
        (push backend result-lst)))
+
301
    (setq result-lst (reverse result-lst))
+
302
    (cl-remove-duplicates result-lst)))
+
303
+
304
(defun company-fuzzy--get-backend-by-candidate (candidate)
+
305
  "Return the backend symbol by using CANDIDATE as search index."
+
306
  (let ((match (ht-find (lambda (_backend cands)
+
307
                          (member candidate cands))
+
308
                        company-fuzzy--candidates)))
+
309
    (car match)))
+
310
+
311
(defun company-fuzzy--call-backend (backend command key)
+
312
  "Safely call BACKEND by COMMAND and KEY."
+
313
  (ignore-errors (funcall backend command key)))
+
314
+
315
(defun company-fuzzy--backend-command (candidate command)
+
316
  "Find the backend from the CANDIDATE then call the COMMAND."
+
317
  (unless (string-empty-p candidate)
+
318
    (when-let ((backend (company-fuzzy--get-backend-by-candidate candidate)))
+
319
      (company-fuzzy--call-backend backend command candidate))))
+
320
+
321
;;
+
322
;; (@* "Annotation" )
+
323
;;
+
324
+
325
(defun company-fuzzy--get-backend-string (backend)
+
326
  "Get BACKEND's as a string."
+
327
  (if backend
+
328
      (let ((name (symbol-name backend)))
+
329
        (setq name (s-replace "company-" "" name)
+
330
              name (s-replace "-company" "" name))
+
331
        name)
+
332
    ""))
+
333
+
334
(defun company-fuzzy--backend-string (candidate backend)
+
335
  "Form the BACKEND string by CANDIDATE."
+
336
  (if (and company-fuzzy-show-annotation candidate)
+
337
      (let ((backend-str (company-fuzzy--get-backend-string backend)))
+
338
        (when (string-empty-p backend-str) (setq backend-str "unknown"))
+
339
        (propertize
+
340
         (format company-fuzzy-annotation-format backend-str)
+
341
         'face 'company-fuzzy-annotation-face))
+
342
    ""))
+
343
+
344
(defun company-fuzzy--source-anno-string (candidate backend)
+
345
  "Return the source annotation string by CANDIDATE and BACKEND."
+
346
  (if (and candidate backend)
+
347
      (company-fuzzy--call-backend backend 'annotation candidate)
+
348
    ""))
+
349
+
350
(defun company-fuzzy--extract-annotation (candidate)
+
351
  "Extract annotation from CANDIDATE."
+
352
  (let* ((backend (company-fuzzy--get-backend-by-candidate candidate))
+
353
         (backend-str (company-fuzzy--backend-string candidate backend))
+
354
         (orig-anno (company-fuzzy--source-anno-string candidate backend)))
+
355
    (concat orig-anno backend-str)))
+
356
+
357
;;
+
358
;; (@* "Highlighting" )
+
359
;;
+
360
+
361
(defun company-fuzzy--pre-render (str &optional annotation-p)
+
362
  "Prerender color with STR and flag ANNOTATION-P."
+
363
  (unless annotation-p
+
364
    (let* ((str-len (length str))
+
365
           (prefix (company-fuzzy--backend-prefix-candidate str 'match))
+
366
           (prefix (company-fuzzy--validate-prefix prefix))
+
367
           (selection (or company-selection 0))
+
368
           (cur-selection (nth selection company-candidates))
+
369
           (splitted-section (remove "" (split-string str " ")))
+
370
           (process-selection (nth 0 splitted-section))
+
371
           (selected (string= cur-selection process-selection))
+
372
           (selected-face (if selected
+
373
                              'company-tooltip-common-selection
+
374
                            'company-tooltip-common))
+
375
           (selected-common-face (if selected
+
376
                                     'company-tooltip-selection
+
377
                                   'company-tooltip))
+
378
           (splitted-c (remove "" (split-string prefix ""))))
+
379
      (set-text-properties 0 str-len nil str)
+
380
      (font-lock-prepend-text-property 0 str-len 'face selected-common-face str)
+
381
      (dolist (c splitted-c)
+
382
        (let ((pos (company-fuzzy--string-match-p (regexp-quote c) str)))
+
383
          (while (and (numberp pos) (< pos str-len))
+
384
            (font-lock-prepend-text-property pos (1+ pos) 'face selected-face str)
+
385
            (setq pos (company-fuzzy--string-match-p (regexp-quote c) str (1+ pos))))))))
+
386
  str)
+
387
+
388
;;
+
389
;; (@* "Sorting / Scoring" )
+
390
;;
+
391
+
392
(defun company-fuzzy--sort-prefix-on-top (candidates)
+
393
  "Sort CANDIDATES that match prefix on top of all other selection."
+
394
  (let (prefix-matches prefix)
+
395
    (dolist (cand candidates)
+
396
      (setq prefix (company-fuzzy--backend-prefix-candidate cand 'match)
+
397
            prefix (company-fuzzy--validate-prefix prefix))
+
398
      (when (company-fuzzy--string-prefix-p prefix cand)
+
399
        (push cand prefix-matches)
+
400
        (setq candidates (remove cand candidates))))
+
401
    (setq prefix-matches (sort prefix-matches #'string-lessp)
+
402
          candidates (append prefix-matches candidates)))
+
403
  candidates)
+
404
+
405
(defun company-fuzzy--sort-candidates-by-function (candidates fnc &optional flip)
+
406
  "Sort CANDIDATES with function call FNC.
+
407
+
408
If optional argument FLIP is non-nil, reverse query and pattern order."
+
409
  (let ((scoring-table (make-hash-table :test 'equal)) scoring-keys)
+
410
    (dolist (cand candidates)
+
411
      (when-let* ((prefix (company-fuzzy--backend-prefix-candidate cand 'match))
+
412
                  (scoring (or (equal prefix 'anything)
+
413
                               (ignore-errors
+
414
                                 (if flip (funcall fnc prefix cand)
+
415
                                   (funcall fnc cand prefix)))))
+
416
                  (score (cond ((listp scoring) (nth 0 scoring))
+
417
                               ((vectorp scoring) (aref scoring 0))
+
418
                               ((numberp scoring) scoring)
+
419
                               (t 0))))
+
420
        (ht-set scoring-table score (push cand (ht-get scoring-table score)))))
+
421
    ;; Get all keys, and turn into a list.
+
422
    (ht-map (lambda (score-key _cands) (push score-key scoring-keys)) scoring-table)
+
423
    (setq scoring-keys (sort scoring-keys #'>)  ; Sort keys in order.
+
424
          candidates nil)  ; Clean up, and ready for final output.
+
425
    (dolist (key scoring-keys)
+
426
      (let ((cands (ht-get scoring-table key)))
+
427
        (setq cands (reverse cands))  ; Respect to backend order.
+
428
        (when (functionp company-fuzzy-sorting-score-function)
+
429
          (setq cands (funcall company-fuzzy-sorting-score-function cands)))
+
430
        (setq candidates (append candidates cands)))))
+
431
  candidates)
+
432
+
433
(defun company-fuzzy--sort-candidates (candidates)
+
434
  "Sort all CANDIDATES base on type of sorting backend."
+
435
  ;; IMPORTANT: Since the command `candidates' will change by `company-mode',
+
436
  ;; we manually set the candidates here so we get can consistent result.
+
437
  (setq candidates (company-fuzzy--ht-all-candidates))
+
438
  (when company-fuzzy-reset-selection
+
439
    (setq company-selection company-selection-default))
+
440
  ;; Don't score when it start fresh, e.g. completing a function name in Java
+
441
  ;; with the . (dot) symbol
+
442
  (unless company-fuzzy--is-trigger-prefix-p
+
443
    (setq candidates
+
444
          (cl-case company-fuzzy-sorting-backend
+
445
            (`none candidates)
+
446
            (`alphabetic (sort candidates #'string-lessp))
+
447
            (`flex
+
448
             (company-fuzzy--sort-candidates-by-function candidates #'flex-score))
+
449
            (`flx
+
450
             (company-fuzzy--sort-candidates-by-function candidates #'flx-score))
+
451
            (`flx-rs
+
452
             (company-fuzzy--sort-candidates-by-function candidates #'flx-rs-score))
+
453
            (`flxy
+
454
             (company-fuzzy--sort-candidates-by-function candidates #'flxy-score))
+
455
            ((or fuz-skim fuz-clangd)
+
456
             (company-fuzzy--sort-candidates-by-function
+
457
              candidates (if (eq company-fuzzy-sorting-backend 'fuz-skim)
+
458
                             #'fuz-calc-score-skim
+
459
                           #'fuz-calc-score-clangd)
+
460
              t))
+
461
            ((or fuz-bin-skim fuz-bin-clangd)
+
462
             (company-fuzzy--sort-candidates-by-function
+
463
              candidates (if (eq company-fuzzy-sorting-backend 'fuz-bin-skim)
+
464
                             'fuz-bin-score-skim
+
465
                           'fuz-bin-score-clangd)
+
466
              t))
+
467
            (`liquidmetal
+
468
             (company-fuzzy--sort-candidates-by-function candidates #'liquidmetal-score))
+
469
            (`sublime-fuzzy
+
470
             (company-fuzzy--sort-candidates-by-function candidates #'sublime-fuzzy-score t))))
+
471
    (when company-fuzzy-prefix-on-top
+
472
      (setq candidates (company-fuzzy--sort-prefix-on-top candidates)))
+
473
    (when (functionp company-fuzzy-sorting-function)
+
474
      (setq candidates (funcall company-fuzzy-sorting-function candidates))))
+
475
  candidates)
+
476
+
477
;;
+
478
;; (@* "Completion" )
+
479
;;
+
480
+
481
;; ZEG WEET GE WAAROM IK DIE tfuuiyf HEB TOEGEVOEGD??
+
482
;; OMDAT DEZE KAKFUNCTIE DAN INEENS GEFIKST IS WAUW
+
483
(defun company-fuzzy--insert-candidate (candidate tfuuiyf)
+
484
  "Insertion for CANDIDATE."
+
485
  (when company-fuzzy-mode
+
486
    ;; NOTE: Here we force to change `company-prefix' so the completion
+
487
    ;; will do what we expected.
+
488
    (let ((backend (company-fuzzy--get-backend-by-candidate candidate)))
+
489
      (setq company-prefix (company-fuzzy--backend-prefix backend 'complete)))))
+
490
+
491
;;
+
492
;; (@* "Prefix" )
+
493
;;
+
494
+
495
(defun company-fuzzy--valid-prefix (backend)
+
496
  "Guess the current BACKEND prefix."
+
497
  (let ((prefix (ht-get company-fuzzy--prefixes backend)))
+
498
    (if (stringp prefix) prefix
+
499
      (thing-at-point 'symbol))))  ; Fallback
+
500
+
501
(defun company-fuzzy--validate-prefix (prefix)
+
502
  "Validate the PREFIX to proper string."
+
503
  (if (stringp prefix)  ; this will handle 'anything symbol type
+
504
      prefix ""))
+
505
+
506
(defun company-fuzzy--backend-prefix-complete (backend)
+
507
  "Return prefix for each BACKEND while doing completion.
+
508
+
509
This function is use when function `company-fuzzy--insert-candidate' is
+
510
called.  It returns the current selection prefix to prevent completion
+
511
completes in an odd way."
+
512
  (cl-case backend
+
513
    (`company-paths (company-fuzzy--valid-prefix backend))
+
514
    (t (company-fuzzy--backend-prefix backend 'filter))))
+
515
+
516
(defun company-fuzzy--backend-prefix-filter (backend)
+
517
  "Return prefix for each BACKEND while doing the first basic filerting.
+
518
+
519
This is some what the opposite to function `company-fuzzy--backend-prefix-get'
+
520
since it's try get as much candidates as possible, but this function returns
+
521
a prefix that can filter out some obvious impossible candidates."
+
522
  (cl-case backend
+
523
    (`company-capf (let* ((prefix (company-fuzzy--backend-prefix backend 'match))
+
524
                          (prefix (company-fuzzy--validate-prefix prefix)))
+
525
                     prefix))
+
526
    (`company-files (company-fuzzy--valid-prefix backend))
+
527
    (`company-paths (company-fuzzy--backend-prefix 'company-files 'match))
+
528
    (t (company-fuzzy--backend-prefix backend 'match))))
+
529
+
530
(defun company-fuzzy--backend-prefix-match (backend)
+
531
  "Return prefix for each BACKEND while matching candidates.
+
532
+
533
This function is use for scoring and matching algorithm.  It returns a prefix
+
534
that best describe the current possible candidate.
+
535
+
536
For instance, if there is a candidate function `buffer-file-name' and with
+
537
current prefix `bfn'.  It will just return `bfn' because the current prefix
+
538
does best describe the for this candidate."
+
539
  (cl-case backend
+
540
    ((company-capf) (or (company-fuzzy--valid-prefix backend)
+
541
                        'anything))
+
542
    (`company-c-headers
+
543
     (when-let ((prefix (ht-get company-fuzzy--prefixes backend)))
+
544
       ;; Skip the first < or " symbol
+
545
       (substring prefix 1 (length prefix))))
+
546
    (`company-files
+
547
     ;; NOTE: For `company-files', we will return the last section of the path
+
548
     ;; for the best match.
+
549
     ;;
+
550
     ;; Example, if I have path `/path/to/dir'; then it shall return `dir'.
+
551
     (when-let* ((prefix (ht-get company-fuzzy--prefixes backend))
+
552
                 (splitted (split-string prefix "/" t))
+
553
                 (len-splitted (length splitted))
+
554
                 (last (nth (1- len-splitted) splitted)))
+
555
       last))
+
556
    (`company-paths
+
557
     (when-let ((prefix (ht-get company-fuzzy--prefixes backend)))
+
558
       (if (string-suffix-p "/" prefix) 'anything
+
559
         (nth 0 (last (split-string prefix "/" t))))))
+
560
    (t company-fuzzy--prefix)))
+
561
+
562
(defun company-fuzzy--backend-prefix-get (backend)
+
563
  "Return prefix for each BACKEND while getting candidates.
+
564
+
565
This function is use for simplify prefix, in order to get as much candidates
+
566
as possible for fuzzy work.
+
567
+
568
For instance, if I have prefix `bfn'; then most BACKEND will not return
+
569
function `buffer-file-name' as candidate.  But with this function will use a
+
570
letter `b' instead of full prefix `bfn'.  So the BACKEND will return something
+
571
that may be relavent to the first character `b'.
+
572
+
573
P.S.  Not all backend work this way."
+
574
  (cl-case backend
+
575
    (`company-c-headers
+
576
     ;; Skip the < or " symbol for the first character
+
577
     (ignore-errors (substring (ht-get company-fuzzy--prefixes backend) 1 2)))
+
578
    (`company-files
+
579
     (when-let ((prefix (ht-get company-fuzzy--prefixes backend)))
+
580
       (let* ((splitted (split-string prefix "/" t))
+
581
              (len-splitted (length splitted))
+
582
              (last (nth (1- len-splitted) splitted))
+
583
              (new-prefix prefix))
+
584
         (when (< 1 len-splitted)
+
585
           (setq new-prefix
+
586
                 (substring prefix 0 (- (length prefix) (length last)))))
+
587
         new-prefix)))
+
588
    (`company-paths
+
589
     (when-let ((prefix (ht-get company-fuzzy--prefixes backend)))
+
590
       (if (string-suffix-p "/" prefix) prefix
+
591
         (file-name-directory prefix))))
+
592
    (`company-emmet (company-emmet--prefix))
+
593
    (t
+
594
     ;; Return an empty string or first character is likely going to return a
+
595
     ;; full list of candaidates. And this is what we want.
+
596
     (when (ht-get company-fuzzy--prefixes backend)
+
597
       company-fuzzy--prefix-first))))
+
598
+
599
(defun company-fuzzy--backend-prefix-candidate (cand type)
+
600
  "Get the backend prefix by CAND and TYPE."
+
601
  (let ((backend (company-fuzzy--get-backend-by-candidate cand)))
+
602
    (company-fuzzy--backend-prefix backend type)))
+
603
+
604
(defun company-fuzzy--backend-prefix (backend type)
+
605
  "Get the BACKEND prefix by TYPE."
+
606
  (cl-case type
+
607
    (`complete (company-fuzzy--backend-prefix-complete backend))
+
608
    (`filter   (company-fuzzy--backend-prefix-filter backend))
+
609
    (`match    (company-fuzzy--backend-prefix-match backend))
+
610
    (`get      (company-fuzzy--backend-prefix-get backend))))
+
611
+
612
;;
+
613
;; (@* "Fuzzy Matching" )
+
614
;;
+
615
+
616
(defun company-fuzzy--trim-trailing-re (regex)
+
617
  "Trim incomplete REGEX.
+
618
If REGEX ends with \\|, trim it, since then it matches an empty string."
+
619
  (if (company-fuzzy--string-match "\\`\\(.*\\)[\\]|\\'" regex) (match-string 1 regex) regex))
+
620
+
621
(defun company-fuzzy--regex-fuzzy (str)
+
622
  "Build a regex sequence from STR.
+
623
Insert .* between each char."
+
624
  (setq str (company-fuzzy--trim-trailing-re str))
+
625
  (if (company-fuzzy--string-match "\\`\\(\\^?\\)\\(.*?\\)\\(\\$?\\)\\'" str)
+
626
      (concat (match-string 1 str)
+
627
              (let ((lst (string-to-list (match-string 2 str))))
+
628
                (apply #'concat
+
629
                       (cl-mapcar
+
630
                        #'concat
+
631
                        (cons "" (cdr (mapcar (lambda (c) (format "[^%c\n]*" c))
+
632
                                              lst)))
+
633
                        (mapcar (lambda (x) (format "\\(%s\\)" (regexp-quote (char-to-string x))))
+
634
                                lst))))
+
635
              (match-string 3 str))
+
636
    str))
+
637
+
638
(defun company-fuzzy--match-string (prefix candidates)
+
639
  "Return new CANDIDATES that match PREFIX."
+
640
  (when (stringp prefix)
+
641
    (let ((fuz-str (company-fuzzy--regex-fuzzy prefix)) new-cands)
+
642
      (dolist (cand candidates)
+
643
        (when (company-fuzzy--string-match-p fuz-str cand)
+
644
          (push cand new-cands)))
+
645
      new-cands)))
+
646
+
647
;;
+
648
;; (@* "Core" )
+
649
;;
+
650
+
651
(defun company-fuzzy--ht-all-candidates ()
+
652
  "Return all candidates from the data."
+
653
  (let (all-candidates)
+
654
    (ht-map (lambda (_backend cands)
+
655
              (setq all-candidates (append all-candidates cands)))
+
656
            company-fuzzy--candidates)
+
657
    (delete-dups all-candidates)))
+
658
+
659
(defun company-fuzzy-all-candidates ()
+
660
  "Return the list of all candidates."
+
661
  (ht-clear company-fuzzy--candidates)  ; Clean up
+
662
  (setq company-fuzzy--is-trigger-prefix-p (company-fuzzy--trigger-prefix-p))
+
663
  (dolist (backend company-fuzzy--backends)
+
664
    (if (or (company-fuzzy--lsp-passthrough backend)
+
665
            (memq backend company-fuzzy-passthrough-backends))
+
666
        (company-fuzzy--candidates-from-passthrough-backend backend)
+
667
      (company-fuzzy--candidates-from-backend backend)))
+
668
  ;; Since we insert the candidates before sorting event, see function
+
669
  ;; `company-fuzzy--sort-candidates', we return to simply avoid the process
+
670
  ;; from `company-mode'.
+
671
  ;;
+
672
  ;; This should help us save some performance!
+
673
  (when (eq this-command 'company-diag)
+
674
    ;; We did return candidates here, yet this does not mean `company-diag'
+
675
    ;; will respect this result.
+
676
    (company-fuzzy--ht-all-candidates)))
+
677
+
678
(defun company-fuzzy--candidates-from-passthrough-backend (backend)
+
679
  "Use candidates of already fuzzy BACKEND as is."
+
680
  (let ((prefix-get (company-fuzzy--backend-prefix backend 'get))
+
681
        temp-candidates)
+
682
    (when prefix-get
+
683
      (setq temp-candidates (company-fuzzy--call-backend backend 'candidates prefix-get)))
+
684
    (company-fuzzy--collect-candidates backend temp-candidates)))
+
685
+
686
(defun company-fuzzy--candidates-from-backend (backend)
+
687
  "Do fuzzy matching for current BACKEND."
+
688
  (let ((prefix-get (company-fuzzy--backend-prefix backend 'get))
+
689
        (prefix-fil (company-fuzzy--backend-prefix backend 'filter))
+
690
        temp-candidates)
+
691
    (when prefix-get
+
692
      (setq temp-candidates (company-fuzzy--call-backend backend 'candidates prefix-get)))
+
693
    ;; NOTE: Do the very basic filtering for speed up.
+
694
    ;;
+
695
    ;; The function `company-fuzzy--match-string' does the very first basic
+
696
    ;; filtering in order to lower the performance before sending to function
+
697
    ;; scoring engine.
+
698
    (when (and (not company-fuzzy--is-trigger-prefix-p)
+
699
               (company-fuzzy--valid-candidates-p temp-candidates)
+
700
               prefix-fil)
+
701
      (setq temp-candidates (company-fuzzy--match-string prefix-fil temp-candidates)))
+
702
    ;; NOTE: Made the final completion.
+
703
    (company-fuzzy--collect-candidates backend temp-candidates)))
+
704
+
705
(defun company-fuzzy--register-candidates (backend candidates)
+
706
  "Register CANDIDATES with BACKEND id."
+
707
  (delete-dups candidates)
+
708
  (ht-set company-fuzzy--candidates backend (copy-sequence candidates)))
+
709
+
710
(defun company-fuzzy--collect-candidates (backend candidates)
+
711
  "Collect BACKEND's CANDIDATES by it's type."
+
712
  (cond
+
713
   ;; NOTE: Asynchronous
+
714
   ((company-fuzzy--async-candidates-p candidates)
+
715
    (ignore-errors
+
716
      (funcall (cdr candidates)
+
717
               (lambda (async-candidates)
+
718
                 (company-fuzzy--register-candidates backend async-candidates)))))
+
719
   ;; NOTE: Synchronous
+
720
   ;;
+
721
   ;; This is the final ensure step before processing it to scoring phase.
+
722
   ;; We confirm candidates by adding it to `company-fuzzy--candidates'.
+
723
   ;; The function `company-fuzzy--valid-candidates-p' is use to ensure the
+
724
   ;; candidates returns a list of strings, which this is the current only valid
+
725
   ;; type to this package.
+
726
   ((company-fuzzy--valid-candidates-p candidates)
+
727
    (company-fuzzy--register-candidates backend candidates))))
+
728
+
729
(defun company-fuzzy--get-prefix ()
+
730
  "Set the prefix just right before completion."
+
731
  (setq company-fuzzy--is-trigger-prefix-p nil
+
732
        company-fuzzy--prefix (or (ignore-errors (company-fuzzy--furthest-prefix))
+
733
                                  (ignore-errors (company-fuzzy--generic-prefix))
+
734
                                  (ffap-guesser))
+
735
        company-fuzzy--prefix-first (ignore-errors (substring company-fuzzy--prefix 0 1)))
+
736
  company-fuzzy--prefix)  ; make sure return it
+
737
+
738
(defun company-fuzzy-all-other-backends (command &optional arg &rest ignored)
+
739
  "Backend source for all other backend except this backend, COMMAND, ARG, IGNORED."
+
740
  (interactive (list 'interactive))
+
741
  (cl-case command
+
742
    (`interactive (company-begin-backend 'company-fuzzy-all-other-backends))
+
743
    (`prefix (company-fuzzy--get-prefix))
+
744
    (`annotation (company-fuzzy--extract-annotation arg))
+
745
    (`candidates (company-fuzzy-all-candidates))
+
746
    (`pre-render (company-fuzzy--pre-render arg (nth 0 ignored)))
+
747
    (t (company-fuzzy--backend-command arg command))))
+
748
+
749
;;
+
750
;; (@* "Users" )
+
751
;;
+
752
+
753
(defun company-fuzzy--ensure-local ()
+
754
  "Ensure modified variable effect locally."
+
755
  (make-local-variable 'company-fuzzy--backends)
+
756
  (make-local-variable 'company-fuzzy--recorded-backends)
+
757
  (make-local-variable 'company-backends))
+
758
+
759
(defun company-fuzzy--backend-organize ()
+
760
  "Organize backend after modified the backend list."
+
761
  (if company-fuzzy-mode
+
762
      (setq company-fuzzy--backends (delete-dups company-fuzzy--backends)
+
763
            company-fuzzy--recorded-backends (delete-dups company-fuzzy--recorded-backends))
+
764
    (setq company-backends (delete-dups company-backends))))
+
765
+
766
;;;###autoload
+
767
(defun company-fuzzy-backend-add (backend)
+
768
  "Safe way to add BACKEND."
+
769
  (company-fuzzy--ensure-local)
+
770
  (if company-fuzzy-mode
+
771
      (progn
+
772
        (add-to-list 'company-fuzzy--backends backend t)
+
773
        (add-to-list 'company-fuzzy--recorded-backends backend t))
+
774
    (add-to-list 'company-backends backend t))
+
775
  (company-fuzzy--backend-organize))
+
776
+
777
;;;###autoload
+
778
(defun company-fuzzy-backend-remove (backend)
+
779
  "Safe way to remove BACKEND."
+
780
  (company-fuzzy--ensure-local)
+
781
  (if company-fuzzy-mode
+
782
      (progn
+
783
        (setq company-fuzzy--backends (cl-remove backend company-fuzzy--backends)
+
784
              company-fuzzy--recorded-backends (cl-remove backend company-fuzzy--recorded-backends)))
+
785
    (setq company-backends (cl-remove backend company-backends)))
+
786
  (company-fuzzy--backend-organize))
+
787
+
788
(defun company-fuzzy--insert-to (list elm n)
+
789
  "Insert into list LIST an element ELM at index N.
+
790
+
791
If N is 0, ELM is inserted before the first element.
+
792
+
793
The resulting list is returned.  As the list contents is mutated
+
794
in-place, the old list reference does not remain valid."
+
795
  (let* ((padded-list (cons nil (copy-sequence list)))
+
796
         (c (nthcdr n padded-list)))
+
797
    (setcdr c (cons elm (cdr c)))
+
798
    (cdr padded-list)))
+
799
+
800
(defun company-fuzzy--insert-before (list elm new-elm)
+
801
  "Add a NEW-ELM to the LIST before ELM."
+
802
  (let ((position (or (cl-position elm list :test 'equal) 0)))
+
803
    (company-fuzzy--insert-to list new-elm position)))
+
804
+
805
(defun company-fuzzy--insert-after (list elm new-elm)
+
806
  "Add a NEW-ELM to the LIST after ELM."
+
807
  (let ((position (or (cl-position elm list :test 'equal) 0)))
+
808
    (company-fuzzy--insert-to list new-elm (1+ position))))
+
809
+
810
;;;###autoload
+
811
(defun company-fuzzy-backend-add-before (backend target)
+
812
  "Add the BACKEND before the TARGET backend."
+
813
  (company-fuzzy--ensure-local)
+
814
  (if company-fuzzy-mode
+
815
      (setq company-fuzzy--backends
+
816
            (company-fuzzy--insert-before company-fuzzy--backends
+
817
                                          target backend)
+
818
            company-fuzzy--recorded-backends
+
819
            (company-fuzzy--insert-before company-fuzzy--recorded-backends
+
820
                                          target backend))
+
821
    (setq company-backends
+
822
          (company-fuzzy--insert-before company-backends
+
823
                                        target backend)))
+
824
  (company-fuzzy--backend-organize))
+
825
+
826
;;;###autoload
+
827
(defun company-fuzzy-backend-add-after (backend target)
+
828
  "Add the BACKEND after the TARGET backend."
+
829
  (company-fuzzy--ensure-local)
+
830
  (if company-fuzzy-mode
+
831
      (setq company-fuzzy--backends
+
832
            (company-fuzzy--insert-after company-fuzzy--backends
+
833
                                         target backend)
+
834
            company-fuzzy--recorded-backends
+
835
            (company-fuzzy--insert-after company-fuzzy--recorded-backends
+
836
                                         target backend))
+
837
    (setq company-backends
+
838
          (company-fuzzy--insert-after company-backends
+
839
                                       target backend)))
+
840
  (company-fuzzy--backend-organize))
+
841
+
842
;;
+
843
;; (@* "Plugins" )
+
844
;;
+
845
+
846
(defun company-fuzzy--lsp-connected-p ()
+
847
  "Return non-nil if lsp is connected."
+
848
  (or (bound-and-true-p lsp-managed-mode)
+
849
      (bound-and-true-p eglot--managed-mode)))
+
850
+
851
(defun company-fuzzy--lsp-after-enabled (&rest _)
+
852
  "Hook run after LSP is enabled."
+
853
  (when (company-fuzzy--lsp-connected-p)
+
854
    ;; No need to check for `company-fuzzy-mode' is on or not since this
+
855
    ;; is hook only added when `company-fuzzy-mode' is on.
+
856
    (setq-local company-backends '(company-fuzzy-all-other-backends))))
+
857
+
858
(defun company-fuzzy--lsp-passthrough (backend)
+
859
  "Respect `capf' BACKEND when LSP is available."
+
860
  (when (memq backend '(company-capf))
+
861
    (company-fuzzy--lsp-connected-p)))
+
862
+
863
(defun company-fuzzy-yasnippet--completions-for-prefix (fnc &rest args)
+
864
  "Wrap around `company-yasnippet--completions-for-prefix' function in order to
+
865
get all possible candidates.
+
866
+
867
Arguments FNC and ARGS are used to apply original operations."
+
868
  (when company-fuzzy-mode
+
869
    ;; `prefix' came from `company-fuzzy--backend-prefix-get', so we simply
+
870
    ;; replace set `key-prefix' to `prefix'.
+
871
    (setf (nth 1 args) (nth 0 args)))
+
872
  (apply fnc args))
+
873
+
874
(provide 'company-fuzzy)
+
875
;;; company-fuzzy.el ends here
+
876

init.el

12 additions and 39 deletions.

View changes Hide changes
1
1
;; Copyright © 2018-2022 Maarten Vangeneugden
2
2
;; Author: Maarten Vangeneugden <contact_me@maartenv.be>
3
3
;; URL: https://maartenv.be
4
4
;;
5
5
;; Welcome to Ghent Emacs.
6
6
;;
7
7
;; This is the configuration file for Ghent Emacs, a GNU Emacs derivative
8
8
;; developed in the city of Ghent, Belgium, by a student of Engineering
9
9
;; Informatics at Ghent University.
10
10
;;
11
11
;; License: GPLv3+
12
12
13
13
;; USAGE:
14
14
;; This configuration file uses the use-package declaration, that means the
15
15
;; settings are a bit different from what you'd normally do in a GNU Emacs
16
16
;; init.el file. Consult https://github.com/jwiegley/use-package for info on how
17
17
;; to correctly write package configuration.
18
18
19
19
(eval-when-compile
+
20
;; Do you get shit like "wrong number of arguments" that completely freezes up
+
21
;; the autocompleter? Fix it by removing the company-fuzzy.elc file from the
+
22
;; .emacs elpa layers directory, and replacing company-fuzzy.el with the
+
23
;; company-fuzzy.el file in the rc repository. Just adding this fixes a bug that
+
24
;; apparently nobody else on the internet has or has managed to figure out a fix for.
+
25
+
26
+
27
(eval-when-compile
20
28
  (require 'use-package))
21
29
;(require 'diminish)                ;; if you use :diminish
22
30
(require 'bind-key)                ;; if you use any :bind variant
23
31
24
32
(custom-set-variables
25
33
 ;; custom-set-variables was added by Custom.
26
34
 ;; If you edit it by hand, you could mess it up, so be careful.
27
35
 ;; Your init file should contain only one such instance.
28
36
 ;; If there is more than one, they won't work right.
29
37
 '(custom-safe-themes
30
38
   '("763bf89898a06b03f7b65fbc29857a1c292e4350246093702fdbd6c4e46e2cf0" "78e6be576f4a526d212d5f9a8798e5706990216e9be10174e3f3b015b8662e27" "d9646b131c4aa37f01f909fbdd5a9099389518eb68f25277ed19ba99adeb7279" "8b58ef2d23b6d164988a607ee153fd2fa35ee33efc394281b1028c2797ddeebb" "f9aede508e587fe21bcfc0a85e1ec7d27312d9587e686a6f5afdbb0d220eab50" "83ae405e25a0a81f2840bfe5daf481f74df0ddb687f317b5e005aa61261126e9" "c433c87bd4b64b8ba9890e8ed64597ea0f8eb0396f4c9a9e01bd20a04d15d358" "a24c5b3c12d147da6cef80938dca1223b7c7f70f2f382b26308eba014dc4833a" "732b807b0543855541743429c9979ebfb363e27ec91e82f463c91e68c772f6e3" "a2cde79e4cc8dc9a03e7d9a42fabf8928720d420034b66aecc5b665bbf05d4e9" "8aebf25556399b58091e533e455dd50a6a9cba958cc4ebb0aab175863c25b9a4" "bd7b7c5df1174796deefce5debc2d976b264585d51852c962362be83932873d9" default))
31
39
 '(inhibit-startup-screen t)
32
40
 '(org-agenda-files nil nil nil "Customized with use-package org")
33
-
 '(package-selected-packages
34
41
   '(company-fuzzy web-mode use-package srefactor spacemacs-theme spaceline solarized-theme rainbow-delimiters pretty-mode page-break-lines monokai-theme helm general evil dashboard dante company-ycmd company-flx company-anaconda clang-format auto-package-update auctex a))
35
-
 '(spaceline-inflation 1 t nil "Customized with use-package spaceline")
+
42
 '(spaceline-inflation 1 t nil "Customized with use-package spaceline")
36
43
 '(spaceline-toggle-buffer-size-off nil t nil "Customized with use-package spaceline")
37
44
 '(warning-suppress-types '((comp))))
38
45
;(custom-set-faces
39
46
 ;; custom-set-faces was added by Custom.
40
47
 ;; If you edit it by hand, you could mess it up, so be careful.
41
48
 ;; Your init file should contain only one such instance.
42
49
 ;; If there is more than one, they won't work right.
43
50
 ;'(default ((((class color) (min-colors 89)) (:foreground "#ffffff" :background "#263238")))))
44
51
45
52
46
53
(add-to-list 'default-frame-alist
47
54
             '(font . "Hack-11"))
48
55
49
56
;; To get rid of those FUCKING ANNOYING BACKUP FILES FUCKING HELL DUDE
50
57
(setq backup-directory-alist '(("." . "~/.emacs.d/backup"))
51
58
  backup-by-copying t    ; Don't delink hardlinks
52
59
  version-control t      ; Use version numbers on backups
53
60
  delete-old-versions t  ; Automatically delete excess backups
54
61
  kept-new-versions 20   ; how many of the newest versions to keep
55
62
  kept-old-versions 5    ; and how many of the old
56
63
  )
57
64
;; And yes I copied it from SO, suck it: https://stackoverflow.com/questions/2680389/how-to-remove-all-files-ending-with-made-by-emacs
58
65
59
66
;; Disables the use of tabs, replaces them with spaces
60
67
(setq-default indent-tabs-mode nil)
61
68
62
69
;; Activate UTF-8 throughout Ghent Emacs
63
70
(prefer-coding-system 'utf-8)
64
71
(set-default-coding-systems 'utf-8)
65
72
(set-terminal-coding-system 'utf-8)
66
73
(set-keyboard-coding-system 'utf-8)
67
74
(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))
68
75
69
76
;; Line numbering config
70
77
(global-display-line-numbers-mode)
71
78
(setq display-line-numbers-mode t)
72
79
(setq display-line-numbers-widen t)
73
80
(setq display-line-numbers-type 'relative)
74
81
(setq display-line-numbers-current-absolute t)
75
82
76
83
(setq mouse-wheel-scroll-amount '(1 ((shift) . 1)))
77
84
(setq scroll-step 1)
78
85
(setq scroll-margin 10) ;; Keeps 10 lines above and down always visible if still left
79
86
80
87
;; Enable line breaks in all modes at column 80
81
88
(setq-default auto-fill-function 'do-auto-fill)
82
89
(setq-default fill-column 80)
83
90
84
91
(setq ring-bell-function 'ignore) ;; Disables the FUCKING ANNOYING BEEPS HOLY SHIT
85
92
86
93
;; Setup packages
87
94
;(package-initialize)
88
95
(require 'package)
89
96
(setq package-enable-at-startup nil)
90
97
(add-to-list 'package-archives '("org" . "https://orgmode.org/elpa/"))
91
98
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
92
99
(unless (package-installed-p 'use-package)
93
100
  (package-refresh-contents)
94
101
(package-install 'use-package))
95
102
96
103
97
104
98
105
99
106
;; Next two lines allow foregoing :ensure t on all packages
100
107
;; :ensure t simply makes sure that, if a package isn't installed on this
101
108
;; machine, it's automatically fetched and installed.
102
109
;; This doesn't keep packages up-to-date, cfr. auto-package-update for that
103
110
(require 'use-package-ensure)
104
111
(setq use-package-always-ensure t)
105
112
106
113
;; Package for automatic updating of all the other packages
107
114
(use-package auto-package-update
108
115
  :config
109
116
  (setq auto-package-update-delete-old-versions t)
110
117
  (setq auto-package-update-hide-results t)
111
118
  (auto-package-update-maybe))
112
119
113
120
;; Easier keybindings so we can do more lazy loading
114
121
(use-package general
115
122
  :config
116
123
  (general-create-definer my-leader-def
117
124
	;; :prefix my-leader
118
125
	:prefix "SPC")
119
126
  (general-create-definer local-leader-def
120
127
	;; :prefix local-leader
121
128
	:prefix ","))
122
129
123
130
(use-package dashboard
124
131
  :init
125
132
  (setq dashboard-banner-logo-title "") ; It's nicer to just not have a title text.
126
133
  (setq dashboard-startup-banner "~/ghent-emacs.png")
127
134
  :config
128
135
  (dashboard-setup-startup-hook))
129
136
130
137
;; AUCTeX configuration
131
138
;; Partly inspired by https://piotr.is/2010/emacs-as-the-ultimate-latex-editor/
132
139
;;;;(use-package auctex
133
140
  ;;:init
134
141
  ;;(setq TeX-auto-save t)
135
142
  ;;(setq TeX-parse-self t)
136
143
  ;;(setq Tex-save-query nil)
137
144
  ;;)
138
145
139
146
;; Updates all packages automatically
140
147
(use-package auto-package-update
141
148
  :config
142
149
  (setq auto-package-update-delete-old-versions t)
143
150
  (setq auto-package-update-hide-results t)
144
151
  (auto-package-update-maybe))
145
152
146
153
;; UI settings
147
154
(tool-bar-mode -1)
148
155
(menu-bar-mode -1)
149
156
(toggle-scroll-bar -1)
150
157
151
158
;; Tabs and width
152
159
(setq-default tab-width 4)
153
160
(setq-default c-basic-offset 4)
154
161
155
162
(use-package spaceline
156
163
    :init
157
164
    (setq frame-char-height 35)
158
165
    (setq powerline-height 35)
159
166
    :custom
160
167
     (spaceline-toggle-buffer-size-off)
161
168
     ;(setq powerline-height 21)
162
169
  (spaceline-inflation 1)
163
170
  (powerline-default-separator 'slant)
164
171
  :config
165
172
  (spaceline-spacemacs-theme)
166
173
  (setq spaceline-highlight-face-func 'spaceline-highlight-face-evil-state)) 
167
174
168
175
169
176
(use-package monokai-theme
170
177
    :config
171
178
    (load-theme 'monokai t)
172
179
    (setq monokai-variable-pitch-mode t))
173
180
;; When using solarized as theme, put in :config
174
181
;; (setq x-underline-at-descent-line t)
175
182
;; Which will block an ugly line being drawn trhough the modeline.
176
183
177
184
;(add-hook 'org-mode-hook '(lambda () (setq fill-column 80)))
178
185
;(add-hook 'org-mode-hook 'auto-fill-mode)
179
186
;; (Programming) languages
180
187
181
188
182
189
183
190
(use-package dante
184
191
  :after haskell-mode
185
192
  :commands 'dante-mode
186
193
  :init
187
194
  (add-hook 'haskell-mode-hook 'dante-mode)
188
195
  (add-hook 'haskell-mode-hook 'flycheck-mode)
189
196
  (add-hook 'haskell-mode-hook '
190
197
            (lambda () (modify-syntax-entry ?_ "w")))
191
198
  )
192
199
193
200
    
194
201
;; HTML/CSS/Django
195
202
(use-package web-mode
196
203
  :mode "\\.html?\\.djhtml\\'"
197
204
  :init
198
205
  (add-to-list 'auto-mode-alist '("\\.djhtml\\'" . web-mode))
199
206
  (setq web-mode-markup-indent-offset 4)
200
207
  (setq web-mode-css-indent-offset 4)
201
208
  (setq web-mode-code-indent-offset 4)
202
209
  ;(setq web-mode-engines-alist
203
210
;	'(("django"    . "\\.html\\"))) ;; FIXME this doesn't work yet. IDK why.
204
211
  :custom ;; Next lines disable all the automatic completion stuff that I didn't explicitely ask for
205
212
  (web-mode-enable-auto-pairing nil)
206
213
  (web-mode-enable-auto-closing nil)
207
214
  (web-mode-enable-auto-expanding nil)
208
215
  (web-mode-enable-auto-opening nil)
209
216
  (web-mode-enable-auto-quoting nil))
210
217
211
218
212
219
;;(use-package ycmd-mode
213
-
  ;;:ensure ycmd
214
-
  ;;;:hook (after-init . global-ycmd-mode)
215
-
  ;;:hook (haskell-mode elisp-mode python-mode c-mode c++-mode) ; Add the modes of the languages where YCM
216
-
                                      ;;; needs to kick in
217
-
  ;;:init
218
-
  ;;; Old paths
219
-
  ;;;(set-variable 'ycmd-server-command `("/usr/bin/python" ,(file-truename "/usr/share/vim/vimfiles/third_party/ycmd/ycmd/")))
220
-
  ;;;(set-variable 'ycmd-global-config "/home/simba/.emacs.d/lang/ycm_conf.py")
221
-
  ;;(set-variable 'ycmd-server-command `("python" ,(file-truename "~/ycmd/ycmd")))
222
-
  ;;(set-variable 'ycmd-global-config (file-truename "~/Repositories/rc/.ycm_extra_conf.py"))
223
-
  ;;(set-variable 'ycmd-global-modes 'all) ; Activates YCMD in 'all' major modes,
224
-
                                        ;;; not only those in ycmd-file-type-map
225
-
  ;;;(set-variable 'ycmd-extra-conf-whitelist '("/home/jorrit/Dev/*"))
226
-
  ;;:config
227
-
  ;;(setq url-show-status nil)  ; Makes those FUCKING annoying messages SHUT THE HELL UP
228
-
  ;;(setq request-message-level -1)
229
-
  
230
-
  ;;(use-package flycheck-ycmd
231
-
	;;:ensure t
232
-
	;;:commands (flycheck-ycmd-setup)
233
-
	;;:hook (ycmd-mode . flycheck-ycmd-setup)
234
-
	;;:init
235
-
	;;(setq flycheck-clang-language-standard "c++11")
236
-
	;;(when (not (display-graphic-p))
237
-
      ;;(setq flycheck-indication-mode nil))))
238
-
239
-
(use-package company
240
220
 :hook (after-init . global-company-mode)
241
221
 :init
242
222
 (company-tng-configure-default)  ; Use <TAB> to scroll in completion options
243
223
 (setq company-minimum-prefix-length 2)
244
224
 (setq company-idle-delay 0.0))
245
225
 ;:config
246
-
 ;;(use-package company-ycmd-mode
247
-
	;;:ensure company-ycmd
248
-
	;;:commands (company-ycmd-setup)
249
-
	;;:hook (ycmd-mode . company-ycmd-setup))
250
-
 ;;(use-package company-flx
251
-
              ;;:ensure t
252
-
              ;;:config
253
-
              ;;(company-flx-mode +1)))
254
-
;;
255
-
(use-package company-fuzzy
+
226
+
227
(use-package company-fuzzy
256
228
  :hook (company-mode . company-fuzzy-mode)
257
229
  :init
258
230
  (setq company-fuzzy-sorting-backend 'flx
259
231
        company-fuzzy-prefix-on-top nil
+
232
        company-fuzzy-prefix-on-top nil
260
233
        company-fuzzy-history-backends '(company-yasnippet)
261
234
        company-fuzzy-trigger-symbols '("." "->" "<" "\"" "'" "@")))
262
235
263
236
(global-company-fuzzy-mode 1)
264
237
265
238
(use-package clang-format
266
239
  :general
267
240
  (local-leader-def
268
241
   :states 'normal
269
242
   :modes '(c-mode-map c++-mode-map)
270
243
   "b" '(:ignore t :which-key "clang")
271
244
   "bf" 'clang-format-buffer)
272
245
  :init
273
246
  (setq-default clang-format-style "{BasedOnStyle: llvm, IndentWidth: 4}"))
274
247
275
248
;; eViL-mode
276
249
(use-package evil
277
250
  :hook (after-init . evil-mode)
278
251
  :bind (:map evil-normal-state-map ;; Scrolls over lines correctly
279
252
              ("j" . evil-next-visual-line)
280
253
              ("k" . evil-previous-visual-line))
281
254
  :general
282
255
  (my-leader-def
283
256
	:states 'normal
284
257
    "b" '(:ignore t :which-key "buffer")
285
258
    "g" '(:ignore t :which-key "magit")
286
259
    "f" '(:ignore t :which-key "files")
287
260
	"bk" 'kill-this-buffer
288
261
	"bd" 'kill-other-buffers
289
262
	"bh" 'switch-to-home-buffer
290
263
	"br" 'revert-buffer
291
264
	"h" 'split-window-vertically
292
265
	"v" 'split-window-horizontally)
293
266
  (local-leader-def
294
267
	:states 'normal
295
268
    ;; General lang options
296
269
	"c" 'compile)
297
270
  :init
298
271
  (setq evil-find-skip-newlines t) ;; Allows f/F/t/T to search beyond CR/LF
299
272
  (setq evil-undo-system 'undo-redo)  ;; Just uses standard undo-redo behaviour
300
273
  ;; a normal human expects
301
274
  (setq evil-want-C-u-scroll t)) ;; Activates c-u scroll to above.
302
275
303
276
;; About smooth-scrolling: I'd normally just use some variables for scrolling
304
277
;; but they all fail with org-mode. This package fixes everything that's wrong for me.
305
278
;(use-package smooth-scrolling
306
279
   ;:config
307
280
   ;(smooth-scrolling-mode 1))
308
281
309
282
(use-package org
310
283
  :pin "org"
311
284
  ; FIXME This causes some errors with bind-keys or what have you. Commented
312
285
  ; until it is fixed.
313
286
  ;:bind (:bind-keymap
314
287
  ;        ("C-c x" . org-todo-state-map)
315
288
  ;       :map org-todo-state-map
316
289
  ;        ("x" . #'(lambda nil (interactive) (org-todo "CANCELLED")))
317
290
  ;        ("d" . #'(lambda nil (interactive) (org-todo "DONE")))
318
291
  ;        ("f" . #'(lambda nil (interactive) (org-todo "DEFERRED")))
319
292
  ;        ("l" . #'(lambda nil (interactive) (org-todo "DELEGATED")))
320
293
  ;        ("s" . #'(lambda nil (interactive) (org-todo "STARTED")))
321
294
  ;        ("w" . #'(lambda nil (interactive) (org-todo "WAITING"))))
322
295
;         :map org-mode-map
323
296
;          ("C-c x" . org-todo-state-map))
324
297
   :init
325
298
  (define-key mode-specific-map [?a] 'org-agenda)
326
299
  :custom
327
300
  ;; Because I use XeLaTeX (it's objectively better), some variables need to be
328
301
  ;; set so org-mode knows what to call.
329
302
330
303
  ;; Okay, so you cán export asynchronous so compiling your PDF doesn't lock up
331
304
  ;; the editor completely, but for one reason or another, you MUST set the
332
305
  ;; async-debug to nil. Otherwise it will constantly "exit abnormally" without
333
306
  ;; reason. This fixes it, who knows why.
334
307
  (org-export-async-debug nil)
335
308
  
336
309
  (org-latex-compiler 'xelatex)  ; Makes sure the XeLaTeX compiler is used
337
310
  
338
311
  ; Tells Org to use dvisvgm because dvipng does not support XeLaTeX's output format
339
312
  (org-preview-latex-default-process 'dvisvgm)
340
313
  
341
314
  ; This edits the preview process so it works correctly with XeLaTeX.
342
315
  ; Changes: 
343
316
  ; - "latex" to "xelatex" 
344
317
  ; - Added the "-no-pdf" flag so it prints an XDV file instead of PDF.
345
318
  ; - change input type from dvi to xdv
346
319
  ; - Only leave the dvisvgm option because I don't need the others
347
320
  (org-preview-latex-process-alist
348
321
   (quote
349
322
    ((dvisvgm :programs
350
323
              ("xelatex" "dvisvgm")
351
324
              :description "dvi > svg" :message "you need to install the programs: xelatex and dvisvgm." :use-xcolor t :image-input-type "xdv" :image-output-type "svg" :image-size-adjust
352
325
              (1.7 . 1.5)
353
326
              :latex-compiler
354
327
              ("xelatex -interaction nonstopmode -no-pdf -output-directory %o %f")
355
328
              :image-converter
356
329
              ("dvisvgm %f -n -b min -c %S -o %O")))))
357
330
358
331
  ;; Sets the formatting options. I only changed :scale to 1.6, but I don't know
359
332
  ;; how to change only thát, so for now I'm configuring the entire variable
360
333
  (org-format-latex-options (quote
361
334
   (:foreground default :background default :scale 1.6 :html-foreground "Black" :html-background "Transparent" :html-scale 1.0 :matchers
362
335
             ("begin" "$1" "$" "$$" "\\(" "\\["))))
363
336
    
364
337
  ;; Sets the directory in which the preview fragments are stored to the /tmp
365
338
  ;; folder. Otherwise it clutters up the folder in which the source resides
366
339
  ;; with what are essentially cache files.
367
340
  (org-preview-latex-image-directory "/tmp/ltximg/")
368
341
  
369
342
  ;; Following customizations are in regard to org-agenda configuration.
370
343
  (org-agenda-files (quote ("~/shared/planning/planning.org")))
371
344
  (org-default-notes-file "~/shared/planning/notes.org")
372
345
  (org-agenda-span 62)
373
346
  (org-deadline-warning-days 14)
374
347
  (org-agenda-show-all-dates t)
375
348
  (org-agenda-skip-deadline-if-done t)
376
349
  (org-agenda-skip-scheduled-if-done t)
377
350
  (org-agenda-start-on-weekday nil)
378
351
  (org-reverse-note-order t)
379
352
  (org-fast-tag-selection-single-key (quote expert))
380
353
  (org-agenda-custom-commands
381
354
    (quote (("d" todo "DELEGATED" nil)
382
355
      ("c" todo "DONE|DEFERRED|CANCELED" nil)
383
356
      ("w" todo "WAITING" nil)
384
357
      ("W" agenda "" ((org-agenda-ndays 21)))
385
358
      ("A" agenda ""
386
359
        ((org-agenda-skip-function
387
360
          (lambda nil
388
361
      (org-agenda-skip-entry-if (quote notregexp) "\\=.*\\[#A\\]")))
389
362
        (org-agenda-ndays 1)
390
363
        (org-agenda-overriding-header "Today's Priority #A tasks: ")))
391
364
      ("u" alltodo ""
392
365
        ((org-agenda-skip-function
393
366
          (lambda nil
394
367
      (org-agenda-skip-entry-if (quote scheduled) (quote deadline)
395
368
              (quote regexp) "\n]+>")))
396
369
        (org-agenda-overriding-header "Unscheduled TODO entries: "))))))
397
370
  (org-capture-store-without-prompt t)
398
371
  (org-capture-templates
399
372
    (quote ((116 "* TODO %?\n  %u" "~/Repositories/private/org/planning.org" "Tasks")
400
373
      (110 "* %u %?" "~/Repositories/private/org/notes.org" "Notes"))))
401
374
  (capture-annotation-functions (quote (org-capture-annotation)))
402
375
  (capture-handler-functions (quote (org-capture-handler))))
403
376
404
377
  ;:general
405
378
  ;(local-leader-def
406
379
  ;  :states 'normal
407
380
  ;  :keymaps 'org-mode-map
408
381
;	"e" 'org-export-dispatch
409
382
;	"x" 'org-table-export
410
383
;	"." 'org-time-stamp
411
384
;	"t" 'org-twbs-export-to-html
412
385
;	"s" 'org-schedule
413
386
;	"d" 'org-deadline
414
387
;	"'" 'org-edit-special)
415
388
;  :config
416
389
;  ;; log todo items with timestamp
417
390
;  (setq org-log-done 'time))
418
391
419
392
(use-package helm
420
393
  :hook (after-init . helm-mode)
421
394
  :commands (helm-autoresize-mode)
422
395
  :init
423
396
  ;; (defvar helm-google-suggest-use-curl-p)
424
397
  (defvar helm-ff-search-library-in-sexp)
425
398
  (defvar helm-echo-input-in-header-line)
426
399
  (defvar helm-ff-file-name-history-use-recentf)
427
400
  :general
428
401
  (general-define-key
429
402
   "M-x" 'helm-M-x
430
403
   "M-:" 'helm-eval-expression
431
404
   "C-c h" 'helm-command-prefix)
432
405
  (general-define-key
433
406
   :keymaps 'helm-map
434
407
   "<tab>" 'helm-execute-persistent-action
435
408
   "C-i" 'helm-execute-persistent-action
436
409
   "C-z" 'helm-select-action)
437
410
  (my-leader-def
438
411
	:states 'normal
439
412
	"bb" 'helm-buffers-list
440
413
	"ff" 'helm-find-files
441
414
	"p" 'helm-show-kill-ring)
442
415
  :config
443
416
  (setq helm-split-window-inside-p           t ; open helm buffer inside current window, not occupy whole other window
444
417
	helm-move-to-line-cycle-in-source     t ; move to end or beginning of source when reaching top or bottom of source.
445
418
	helm-ff-search-library-in-sexp        t ; search for library in `require' and `declare-function' sexp.
446
419
	helm-scroll-amount                    8 ; scroll 8 lines other window using m-<next>/m-<prior>
447
420
	helm-ff-file-name-history-use-recentf t
448
421
	helm-echo-input-in-header-line t)
449
422
450
423
  ;; hide some autogenerated files from helm
451
424
  (setq helm-ff-skip-boring-files t)
452
425
  ;; (add-to-list 'helm-boring-file-regexp-list "\\~$")
453
426
  
454
427
  (setq helm-autoresize-max-height 0)
455
428
  (setq helm-autoresize-min-height 20)
456
429
  (helm-autoresize-mode 1))
457
430
458
431
459
432
(use-package rainbow-delimiters
460
433
  :hook (prog-mode . rainbow-delimiters-mode))
461
434
462
435
;; Settings for having an LaTeX beamer class export tool
463
436
(unless (boundp 'org-export-latex-classes)
464
437
(setq org-export-latex-classes nil))
465
438
(add-to-list 'org-export-latex-classes
466
439
;; beamer class, for presentations
467
440
'("beamer"
468
441
    "\\documentclass[11pt]{beamer}\n
469
442
    \\mode<{{{beamermode}}}>\n
470
443
    \\usetheme{{{{beamertheme}}}}\n
471
444
    \\usecolortheme{{{{beamercolortheme}}}}\n
472
445
    \\beamertemplateballitem\n
473
446
    \\setbeameroption{show notes}
474
447
    \\usepackage[utf8]{inputenc}\n
475
448
    \\usepackage[T1]{fontenc}\n
476
449
    \\usepackage{hyperref}\n
477
450
    \\usepackage{color}
478
451
    \\usepackage{listings}
479
452
    \\lstset{numbers=none,language=[ISO]C++,tabsize=4,
480
453
frame=single,
481
454
basicstyle=\\small,
482
455
showspaces=false,showstringspaces=false,
483
456
showtabs=false,
484
457
keywordstyle=\\color{blue}\\bfseries,
485
458
commentstyle=\\color{red},
486
459
}\n
487
460
    \\usepackage{verbatim}\n
488
461
    \\institute{{{{beamerinstitute}}}}\n          
489
462
    \\subject{{{{beamersubject}}}}\n"
490
463
491
464
    ("\\section{%s}" . "\\section*{%s}")
492
465
493
466
    ("\\begin{frame}[fragile]\\frametitle{%s}"
494
467
    "\\end{frame}"
495
468
    "\\begin{frame}[fragile]\\frametitle{%s}"
496
469
    "\\end{frame}")))
497
470
498
471
;; Adds a letter class, for formal letters
499
472
500
473
(add-to-list 'org-export-latex-classes
501
474
502
475
'("letter"
503
476
    "\\documentclass[11pt]{letter}\n
504
477
    \\usepackage[utf8]{inputenc}\n
505
478
    \\usepackage[T1]{fontenc}\n
506
479
    \\usepackage{color}"
507
480
508
481
    ("\\section{%s}" . "\\section*{%s}")
509
482
    ("\\subsection{%s}" . "\\subsection*{%s}")
510
483
    ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
511
484
    ("\\paragraph{%s}" . "\\paragraph*{%s}")
512
485
    ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
513
486
514
487
515
488
(custom-set-faces
516
489
 ;; custom-set-faces was added by Custom.
517
490
 ;; If you edit it by hand, you could mess it up, so be careful.
518
491
 ;; Your init file should contain only one such instance.
519
492
 ;; If there is more than one, they won't work right.
520
493
 '(default ((t (:background nil)))))
521
494
522
495
523
496
;; This is to fix the retarded scrolling behaviour in Emacs as much as possible:
524
497
(setq redisplay-dont-pause t
525
498
  scroll-margin 1
526
499
  scroll-step 1
527
500
  scroll-conservatively 10000
528
501
  scroll-preserve-screen-position 1)
529
502
;; Stops Emacs from accelerating my scroll:
530
503
(setq mouse-wheel-progressive-speed nil)
531
504
;; Makes scrolling do 5 lines at a time, otherwise it's pretty slow
532
505
(setq mouse-wheel-scroll-amount '(2))
533
506