]>
Commit | Line | Data |
---|---|---|
1 | ;;; company-cmake.el --- company-mode completion backend for CMake | |
2 | ||
3 | ;; Copyright (C) 2013-2015, 2017-2018, 2020 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 <https://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-property-list" | |
44 | "--help-variable-list") | |
45 | "The arguments we pass to cmake, separately. | |
46 | They affect which types of symbols we get completion candidates for.") | |
47 | ||
48 | (defvar company-cmake--completion-pattern | |
49 | "^\\(%s[a-zA-Z0-9_<>]%s\\)$" | |
50 | "Regexp to match the candidates.") | |
51 | ||
52 | (defvar company-cmake-modes '(cmake-mode) | |
53 | "Major modes in which cmake may complete.") | |
54 | ||
55 | (defvar company-cmake--candidates-cache nil | |
56 | "Cache for the raw candidates.") | |
57 | ||
58 | (defvar company-cmake--meta-command-cache nil | |
59 | "Cache for command arguments to retrieve descriptions for the candidates.") | |
60 | ||
61 | (defun company-cmake--replace-tags (rlt) | |
62 | (setq rlt (replace-regexp-in-string | |
63 | "\\(.*?\\(IS_GNU\\)?\\)<LANG>\\(.*\\)" | |
64 | (lambda (_match) | |
65 | (mapconcat 'identity | |
66 | (if (match-beginning 2) | |
67 | '("\\1CXX\\3" "\\1C\\3" "\\1G77\\3") | |
68 | '("\\1CXX\\3" "\\1C\\3" "\\1Fortran\\3")) | |
69 | "\n")) | |
70 | rlt t)) | |
71 | (setq rlt (replace-regexp-in-string | |
72 | "\\(.*\\)<CONFIG>\\(.*\\)" | |
73 | (mapconcat 'identity '("\\1DEBUG\\2" "\\1RELEASE\\2" | |
74 | "\\1RELWITHDEBINFO\\2" "\\1MINSIZEREL\\2") | |
75 | "\n") | |
76 | rlt)) | |
77 | rlt) | |
78 | ||
79 | (defun company-cmake--fill-candidates-cache (arg) | |
80 | "Fill candidates cache if needed." | |
81 | (let (rlt) | |
82 | (unless company-cmake--candidates-cache | |
83 | (setq company-cmake--candidates-cache (make-hash-table :test 'equal))) | |
84 | ||
85 | ;; If hash is empty, fill it. | |
86 | (unless (gethash arg company-cmake--candidates-cache) | |
87 | (with-temp-buffer | |
88 | (let ((res (call-process company-cmake-executable nil t nil arg))) | |
89 | (unless (zerop res) | |
90 | (message "cmake executable exited with error=%d" res))) | |
91 | (setq rlt (buffer-string))) | |
92 | (setq rlt (company-cmake--replace-tags rlt)) | |
93 | (puthash arg rlt company-cmake--candidates-cache)) | |
94 | )) | |
95 | ||
96 | (defun company-cmake--parse (prefix content cmd) | |
97 | (let ((start 0) | |
98 | (pattern (format company-cmake--completion-pattern | |
99 | (regexp-quote prefix) | |
100 | (if (zerop (length prefix)) "+" "*"))) | |
101 | (lines (split-string content "\n")) | |
102 | match | |
103 | rlt) | |
104 | (dolist (line lines) | |
105 | (when (string-match pattern line) | |
106 | (let ((match (match-string 1 line))) | |
107 | (when match | |
108 | (puthash match cmd company-cmake--meta-command-cache) | |
109 | (push match rlt))))) | |
110 | rlt)) | |
111 | ||
112 | (defun company-cmake--candidates (prefix) | |
113 | (let (results | |
114 | cmd-opts | |
115 | str) | |
116 | ||
117 | (unless company-cmake--meta-command-cache | |
118 | (setq company-cmake--meta-command-cache (make-hash-table :test 'equal))) | |
119 | ||
120 | (dolist (arg company-cmake-executable-arguments) | |
121 | (company-cmake--fill-candidates-cache arg) | |
122 | (setq cmd-opts (replace-regexp-in-string "-list$" "" arg) ) | |
123 | ||
124 | (setq str (gethash arg company-cmake--candidates-cache)) | |
125 | (when str | |
126 | (setq results (nconc results | |
127 | (company-cmake--parse prefix str cmd-opts))))) | |
128 | results)) | |
129 | ||
130 | (defun company-cmake--unexpand-candidate (candidate) | |
131 | (cond | |
132 | ((string-match "^CMAKE_\\(C\\|CXX\\|Fortran\\)\\(_.*\\)$" candidate) | |
133 | (setq candidate (concat "CMAKE_<LANG>" (match-string 2 candidate)))) | |
134 | ||
135 | ;; C flags | |
136 | ((string-match "^\\(.*_\\)IS_GNU\\(C\\|CXX\\|G77\\)$" candidate) | |
137 | (setq candidate (concat (match-string 1 candidate) "IS_GNU<LANG>"))) | |
138 | ||
139 | ;; C flags | |
140 | ((string-match "^\\(.*_\\)OVERRIDE_\\(C\\|CXX\\|Fortran\\)$" candidate) | |
141 | (setq candidate (concat (match-string 1 candidate) "OVERRIDE_<LANG>"))) | |
142 | ||
143 | ((string-match "^\\(.*\\)\\(_DEBUG\\|_RELEASE\\|_RELWITHDEBINFO\\|_MINSIZEREL\\)\\(.*\\)$" candidate) | |
144 | (setq candidate (concat (match-string 1 candidate) | |
145 | "_<CONFIG>" | |
146 | (match-string 3 candidate))))) | |
147 | candidate) | |
148 | ||
149 | (defun company-cmake--meta (candidate) | |
150 | (let ((cmd-opts (gethash candidate company-cmake--meta-command-cache)) | |
151 | result) | |
152 | (setq candidate (company-cmake--unexpand-candidate candidate)) | |
153 | ||
154 | ;; Don't cache the documentation of every candidate (command) | |
155 | ;; Cache in this case will cost too much memory. | |
156 | (with-temp-buffer | |
157 | (call-process company-cmake-executable nil t nil cmd-opts candidate) | |
158 | ;; Go to the third line, trim it and return the result. | |
159 | ;; Tested with cmake 2.8.9. | |
160 | (goto-char (point-min)) | |
161 | (forward-line 2) | |
162 | (setq result (buffer-substring-no-properties (line-beginning-position) | |
163 | (line-end-position))) | |
164 | (setq result (replace-regexp-in-string "^[ \t\n\r]+" "" result)) | |
165 | result))) | |
166 | ||
167 | (defun company-cmake--doc-buffer (candidate) | |
168 | (let ((cmd-opts (gethash candidate company-cmake--meta-command-cache))) | |
169 | ||
170 | (setq candidate (company-cmake--unexpand-candidate candidate)) | |
171 | (with-temp-buffer | |
172 | (call-process company-cmake-executable nil t nil cmd-opts candidate) | |
173 | ;; Go to the third line, trim it and return the doc buffer. | |
174 | ;; Tested with cmake 2.8.9. | |
175 | (goto-char (point-min)) | |
176 | (forward-line 2) | |
177 | (company-doc-buffer | |
178 | (buffer-substring-no-properties (line-beginning-position) | |
179 | (point-max)))))) | |
180 | ||
181 | (defun company-cmake-prefix-dollar-brace-p () | |
182 | "Test if the current symbol follows ${." | |
183 | (save-excursion | |
184 | (skip-syntax-backward "w_") | |
185 | (and (eq (char-before (point)) ?\{) | |
186 | (eq (char-before (1- (point))) ?$)))) | |
187 | ||
188 | (defun company-cmake (command &optional arg &rest ignored) | |
189 | "`company-mode' completion backend for CMake. | |
190 | CMake is a cross-platform, open-source make system." | |
191 | (interactive (list 'interactive)) | |
192 | (cl-case command | |
193 | (interactive (company-begin-backend 'company-cmake)) | |
194 | (init (when (memq major-mode company-cmake-modes) | |
195 | (unless company-cmake-executable | |
196 | (error "Company found no cmake executable")))) | |
197 | (prefix (and (memq major-mode company-cmake-modes) | |
198 | (or (not (company-in-string-or-comment)) | |
199 | (company-cmake-prefix-dollar-brace-p)) | |
200 | (company-grab-symbol))) | |
201 | (candidates (company-cmake--candidates arg)) | |
202 | (meta (company-cmake--meta arg)) | |
203 | (doc-buffer (company-cmake--doc-buffer arg)) | |
204 | )) | |
205 | ||
206 | (provide 'company-cmake) | |
207 | ;;; company-cmake.el ends here |