dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

company-cmake.el (7695B)


      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