editorconfig-core.el (7645B)
1 ;;; editorconfig-core.el --- EditorConfig Core library in Emacs Lisp -*- lexical-binding: t -*- 2 3 ;; Copyright (C) 2011-2021 EditorConfig Team 4 5 ;; Author: EditorConfig Team <editorconfig@googlegroups.com> 6 7 ;; See 8 ;; https://github.com/editorconfig/editorconfig-emacs/graphs/contributors 9 ;; or the CONTRIBUTORS file for the list of contributors. 10 11 ;; This file is part of EditorConfig Emacs Plugin. 12 13 ;; EditorConfig Emacs Plugin is free software: you can redistribute it and/or 14 ;; modify it under the terms of the GNU General Public License as published by 15 ;; the Free Software Foundation, either version 3 of the License, or (at your 16 ;; option) any later version. 17 18 ;; EditorConfig Emacs Plugin is distributed in the hope that it will be useful, 19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 21 ;; Public License for more details. 22 23 ;; You should have received a copy of the GNU General Public License along with 24 ;; EditorConfig Emacs Plugin. If not, see <https://www.gnu.org/licenses/>. 25 26 ;;; Commentary: 27 28 ;; This library is one implementation of EditorConfig Core, which parses 29 ;; .editorconfig files and returns properties for given files. 30 ;; This can be used in place of, for example, editorconfig-core-c. 31 32 33 ;; Use from EditorConfig Emacs Plugin 34 35 ;; Emacs plugin (v0.5 or later) can utilize this implementation. 36 ;; By default, the plugin first search for any EditorConfig executable, 37 ;; and fallback to this library if not found. 38 ;; If you always want to use this library, add following lines to your init.el: 39 40 ;; (setq editorconfig-get-properties-function 41 ;; 'editorconfig-core-get-properties-hash) 42 43 44 ;; Functions 45 46 ;; editorconfig-core-get-properties (&optional file confname confversion) 47 48 ;; Get EditorConfig properties for FILE. 49 50 ;; If FILE is not given, use currently visiting file. 51 ;; Give CONFNAME for basename of config file other than .editorconfig. 52 ;; If need to specify config format version, give CONFVERSION. 53 54 ;; This functions returns alist of properties. Each element will look like 55 ;; (KEY . VALUE) . 56 57 58 ;; editorconfig-core-get-properties-hash (&optional file confname confversion) 59 60 ;; Get EditorConfig properties for FILE. 61 62 ;; This function is almost same as `editorconfig-core-get-properties', but 63 ;; returns hash object instead. 64 65 ;;; Code: 66 67 (require 'cl-lib) 68 69 (require 'editorconfig-core-handle) 70 71 72 (defun editorconfig-core--get-handles (dir confname &optional result) 73 "Get list of EditorConfig handlers for DIR from CONFNAME. 74 75 In the resulting list, the handle for root config file comes first, and the 76 nearest comes last. 77 The list may contains nil when no file was found for directories. 78 RESULT is used internally and normally should not be used." 79 (setq dir (expand-file-name dir)) 80 (let ((handle (editorconfig-core-handle (concat (file-name-as-directory dir) 81 confname))) 82 (parent (file-name-directory (directory-file-name dir)))) 83 (if (or (string= parent 84 dir) 85 (and handle 86 (editorconfig-core-handle-root-p handle))) 87 (cl-remove-if-not 'identity 88 (cons handle result)) 89 (editorconfig-core--get-handles parent 90 confname 91 (cons handle 92 result))))) 93 94 ;;;###autoload 95 (defun editorconfig-core-get-nearest-editorconfig (directory) 96 "Return path to .editorconfig file that is closest to DIRECTORY." 97 (let ((handle (car (last (editorconfig-core--get-handles directory 98 ".editorconfig"))))) 99 (when handle 100 (editorconfig-core-handle-path handle)))) 101 102 ;;;###autoload 103 (defun editorconfig-core-get-properties (&optional file confname confversion) 104 "Get EditorConfig properties for FILE. 105 If FILE is not given, use currently visiting file. 106 Give CONFNAME for basename of config file other than .editorconfig. 107 If need to specify config format version, give CONFVERSION. 108 109 This functions returns alist of properties. Each element will look like 110 '(KEY . VALUE) ." 111 (let ((hash (editorconfig-core-get-properties-hash file confname confversion)) 112 (result nil)) 113 (maphash (lambda (key value) 114 (add-to-list 'result 115 (cons (symbol-name key) 116 value))) 117 hash) 118 result)) 119 120 (defun editorconfig-core--hash-merge (into update) 121 "Merge two hashes INTO and UPDATE. 122 123 This is a destructive function, hash INTO will be modified. 124 When the same key exists in both two hashes, values of UPDATE takes precedence." 125 (maphash (lambda (key value) 126 (puthash key 127 value 128 into)) 129 update) 130 into) 131 132 ;;;###autoload 133 (defun editorconfig-core-get-properties-hash (&optional file confname confversion) 134 "Get EditorConfig properties for FILE. 135 If FILE is not given, use currently visiting file. 136 Give CONFNAME for basename of config file other than .editorconfig. 137 If need to specify config format version, give CONFVERSION. 138 139 This function is almost same as `editorconfig-core-get-properties', but returns 140 hash object instead." 141 (setq file 142 (expand-file-name (or file 143 buffer-file-name 144 (error "FILE is not given and `buffer-file-name' is nil")))) 145 (setq confname (or confname 146 ".editorconfig")) 147 (setq confversion (or confversion 148 "0.12.0")) 149 (let ((result (make-hash-table))) 150 (dolist (handle (editorconfig-core--get-handles (file-name-directory file) 151 confname)) 152 (editorconfig-core--hash-merge result 153 (editorconfig-core-handle-get-properties-hash handle 154 file))) 155 156 ;; Downcase known boolean values 157 (dolist (key '( 158 end_of_line indent_style indent_size insert_final_newline 159 trim_trailing_whitespace charset 160 )) 161 (let ((val (gethash key 162 result))) 163 (when val 164 (puthash key 165 (downcase val) 166 result)))) 167 168 ;; Add indent_size property 169 (let ((v-indent-size (gethash 'indent_size result)) 170 (v-indent-style (gethash 'indent_style result))) 171 (when (and (not v-indent-size) 172 (string= v-indent-style "tab") 173 ;; If VERSION < 0.9.0, indent_size should have no default value 174 (version<= "0.9.0" 175 confversion)) 176 (puthash 'indent_size 177 "tab" 178 result))) 179 ;; Add tab_width property 180 (let ((v-indent-size (gethash 'indent_size result)) 181 (v-tab-width (gethash 'tab_width result))) 182 (when (and v-indent-size 183 (not v-tab-width) 184 (not (string= v-indent-size "tab"))) 185 (puthash 'tab_width 186 v-indent-size 187 result))) 188 ;; Update indent-size property 189 (let ((v-indent-size (gethash 'indent_size result)) 190 (v-tab-width (gethash 'tab_width result))) 191 (when (and v-indent-size 192 v-tab-width 193 (string= v-indent-size "tab")) 194 (puthash 'indent_size 195 v-tab-width 196 result))) 197 198 result)) 199 200 (provide 'editorconfig-core) 201 202 ;;; editorconfig-core.el ends here