mirror of https://github.com/Chizi123/.emacs.d.git

Chizi123
2018-11-18 c655eea759be1db69c5e6b45c228139d8390122a
commit | author | age
5cb5f7 1 ;;; company-cmake.el --- company-mode completion backend for CMake
C 2
3 ;; Copyright (C) 2013-2014, 2017-2018  Free Software Foundation, Inc.
4
5 ;; Author: Chen Bin <chenbin DOT sh AT gmail>
6 ;; Version: 0.2
7
8 ;; This program is free software: you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation, either version 3 of the License, or
11 ;; (at your option) any later version.
12
13 ;; This program is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 ;; GNU General Public License for more details.
17
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 ;;; Commentary:
22 ;;
23 ;; company-cmake offers completions for module names, variable names and
24 ;; commands used by CMake.  And their descriptions.
25
26 ;;; Code:
27
28 (require 'company)
29 (require 'cl-lib)
30
31 (defgroup company-cmake nil
32   "Completion backend for CMake."
33   :group 'company)
34
35 (defcustom company-cmake-executable
36   (executable-find "cmake")
37   "Location of cmake executable."
38   :type 'file)
39
40 (defvar company-cmake-executable-arguments
41   '("--help-command-list"
42     "--help-module-list"
43     "--help-variable-list")
44   "The arguments we pass to cmake, separately.
45 They affect which types of symbols we get completion candidates for.")
46
47 (defvar company-cmake--completion-pattern
48   "^\\(%s[a-zA-Z0-9_<>]%s\\)$"
49   "Regexp to match the candidates.")
50
51 (defvar company-cmake-modes '(cmake-mode)
52   "Major modes in which cmake may complete.")
53
54 (defvar company-cmake--candidates-cache nil
55   "Cache for the raw candidates.")
56
57 (defvar company-cmake--meta-command-cache nil
58   "Cache for command arguments to retrieve descriptions for the candidates.")
59
60 (defun company-cmake--replace-tags (rlt)
61   (setq rlt (replace-regexp-in-string
62              "\\(.*?\\(IS_GNU\\)?\\)<LANG>\\(.*\\)"
63              (lambda (_match)
64                (mapconcat 'identity
65                           (if (match-beginning 2)
66                               '("\\1CXX\\3" "\\1C\\3" "\\1G77\\3")
67                             '("\\1CXX\\3" "\\1C\\3" "\\1Fortran\\3"))
68                           "\n"))
69              rlt t))
70   (setq rlt (replace-regexp-in-string
71              "\\(.*\\)<CONFIG>\\(.*\\)"
72              (mapconcat 'identity '("\\1DEBUG\\2" "\\1RELEASE\\2"
73                                     "\\1RELWITHDEBINFO\\2" "\\1MINSIZEREL\\2")
74                         "\n")
75              rlt))
76   rlt)
77
78 (defun company-cmake--fill-candidates-cache (arg)
79   "Fill candidates cache if needed."
80   (let (rlt)
81     (unless company-cmake--candidates-cache
82       (setq company-cmake--candidates-cache (make-hash-table :test 'equal)))
83
84     ;; If hash is empty, fill it.
85     (unless (gethash arg company-cmake--candidates-cache)
86       (with-temp-buffer
87         (let ((res (call-process company-cmake-executable nil t nil arg)))
88           (unless (zerop res)
89             (message "cmake executable exited with error=%d" res)))
90         (setq rlt (buffer-string)))
91       (setq rlt (company-cmake--replace-tags rlt))
92       (puthash arg rlt company-cmake--candidates-cache))
93     ))
94
95 (defun company-cmake--parse (prefix content cmd)
96   (let ((start 0)
97         (pattern (format company-cmake--completion-pattern
98                          (regexp-quote prefix)
99                          (if (zerop (length prefix)) "+" "*")))
100         (lines (split-string content "\n"))
101         match
102         rlt)
103     (dolist (line lines)
104       (when (string-match pattern line)
105         (let ((match (match-string 1 line)))
106           (when match
107             (puthash match cmd company-cmake--meta-command-cache)
108             (push match rlt)))))
109     rlt))
110
111 (defun company-cmake--candidates (prefix)
112   (let (results
113         cmd-opts
114         str)
115
116     (unless company-cmake--meta-command-cache
117       (setq company-cmake--meta-command-cache (make-hash-table :test 'equal)))
118
119     (dolist (arg company-cmake-executable-arguments)
120       (company-cmake--fill-candidates-cache arg)
121       (setq cmd-opts (replace-regexp-in-string "-list$" "" arg) )
122
123       (setq str (gethash arg company-cmake--candidates-cache))
124       (when str
125         (setq results (nconc results
126                              (company-cmake--parse prefix str cmd-opts)))))
127     results))
128
129 (defun company-cmake--unexpand-candidate (candidate)
130   (cond
131    ((string-match "^CMAKE_\\(C\\|CXX\\|Fortran\\)\\(_.*\\)$" candidate)
132     (setq candidate (concat "CMAKE_<LANG>" (match-string 2 candidate))))
133
134    ;; C flags
135    ((string-match "^\\(.*_\\)IS_GNU\\(C\\|CXX\\|G77\\)$" candidate)
136     (setq candidate (concat (match-string 1 candidate) "IS_GNU<LANG>")))
137
138    ;; C flags
139    ((string-match "^\\(.*_\\)OVERRIDE_\\(C\\|CXX\\|Fortran\\)$" candidate)
140     (setq candidate (concat (match-string 1 candidate) "OVERRIDE_<LANG>")))
141
142    ((string-match "^\\(.*\\)\\(_DEBUG\\|_RELEASE\\|_RELWITHDEBINFO\\|_MINSIZEREL\\)\\(.*\\)$" candidate)
143     (setq candidate (concat (match-string 1 candidate)
144                             "_<CONFIG>"
145                             (match-string 3 candidate)))))
146   candidate)
147
148 (defun company-cmake--meta (candidate)
149   (let ((cmd-opts (gethash candidate company-cmake--meta-command-cache))
150         result)
151     (setq candidate (company-cmake--unexpand-candidate candidate))
152
153     ;; Don't cache the documentation of every candidate (command)
154     ;; Cache in this case will cost too much memory.
155     (with-temp-buffer
156       (call-process company-cmake-executable nil t nil cmd-opts candidate)
157       ;; Go to the third line, trim it and return the result.
158       ;; Tested with cmake 2.8.9.
159       (goto-char (point-min))
160       (forward-line 2)
161       (setq result (buffer-substring-no-properties (line-beginning-position)
162                                                    (line-end-position)))
163       (setq result (replace-regexp-in-string "^[ \t\n\r]+" "" result))
164       result)))
165
166 (defun company-cmake--doc-buffer (candidate)
167   (let ((cmd-opts (gethash candidate company-cmake--meta-command-cache)))
168
169     (setq candidate (company-cmake--unexpand-candidate candidate))
170     (with-temp-buffer
171       (call-process company-cmake-executable nil t nil cmd-opts candidate)
172       ;; Go to the third line, trim it and return the doc buffer.
173       ;; Tested with cmake 2.8.9.
174       (goto-char (point-min))
175       (forward-line 2)
176       (company-doc-buffer
177        (buffer-substring-no-properties (line-beginning-position)
178                                        (point-max))))))
179
180 (defun company-cmake-prefix-dollar-brace-p ()
181   "Test if the current symbol follows ${."
182   (save-excursion
183     (skip-syntax-backward "w_")
184     (and (eq (char-before (point)) ?\{)
185          (eq (char-before (1- (point))) ?$))))
186
187 (defun company-cmake (command &optional arg &rest ignored)
188   "`company-mode' completion backend for CMake.
189 CMake is a cross-platform, open-source make system."
190   (interactive (list 'interactive))
191   (cl-case command
192     (interactive (company-begin-backend 'company-cmake))
193     (init (when (memq major-mode company-cmake-modes)
194             (unless company-cmake-executable
195               (error "Company found no cmake executable"))))
196     (prefix (and (memq major-mode company-cmake-modes)
197                  (or (not (company-in-string-or-comment))
198                      (company-cmake-prefix-dollar-brace-p))
199                  (company-grab-symbol)))
200     (candidates (company-cmake--candidates arg))
201     (meta (company-cmake--meta arg))
202     (doc-buffer (company-cmake--doc-buffer arg))
203     ))
204
205 (provide 'company-cmake)
206 ;;; company-cmake.el ends here