diff --git a/editorconfig-core-handle.el b/editorconfig-core-handle.el index cb2cbb9b..e14eaf21 100644 --- a/editorconfig-core-handle.el +++ b/editorconfig-core-handle.el @@ -31,6 +31,8 @@ ;;; Code: +(require 'cl-lib) + (require 'editorconfig-fnmatch) ;; For cl-defstruct @@ -45,6 +47,7 @@ ;; e.g. (("root" . "true")) (top-prop nil) + ;; TODO: Define struct for section ;; Alist of properties ;; Key: Section name ;; Value: Alist of properties for each section name @@ -105,6 +108,25 @@ If HANDLE is nil return nil." (car prop) (file-name-directory (editorconfig-core-handle-path handle)))) (editorconfig-core-handle-prop handle))))) +(make-obsolete 'editorconfig-core-handle-get-properties + 'editorconfig-core-handle-get-properties-hash + "0.8.0") + + +(defun editorconfig-core-handle-get-properties-hash (handle file) + "Return hash of properties from HANDLE for FILE. + +If HANDLE is nil return nil." + (when handle + (let ((hash (make-hash-table))) + (dolist (prop (editorconfig-core-handle-prop handle)) + (when (editorconfig-core-handle--fnmatch-p file + (car prop) + (file-name-directory (editorconfig-core-handle-path + handle))) + (cl-loop for (key . value) in (cdr prop) + do (puthash (intern key) value hash)))) + hash))) (defun editorconfig-core-handle--fnmatch-p (name pattern dir) "Return non-nil if NAME match PATTERN. diff --git a/editorconfig-core.el b/editorconfig-core.el index a9a52edc..3988cfe9 100644 --- a/editorconfig-core.el +++ b/editorconfig-core.el @@ -74,28 +74,6 @@ "0.7.14" "EditorConfig core version.") -(defun editorconfig-core--remove-duplicate (alist) - "Remove duplicated keys in ALIST. - -If same keys are found in ALIST multiple times, the latter ones take precedence. -For example, when ALIST is - - '((a 1) (b 2) (c 3) (b 4)) - -then the result will be - - '((a 1) (b 4) (c 3)) ." - (let ((result ())) - (dolist (e alist) - (let ((pair (assoc (car e) - result))) - (if pair - (setcdr pair - (cdr e)) - (setq result - `(,@result ,e))))) - result)) - (defun editorconfig-core--get-handles (dir confname &optional result) "Get list of EditorConfig handlers for DIR from CONFNAME. @@ -135,76 +113,95 @@ If need to specify config format version, give CONFVERSION. This functions returns alist of properties. Each element will look like '(KEY . VALUE) ." - (setq file (expand-file-name (or file - buffer-file-name - (error "FILE is not given and `buffer-file-name' is nil")))) + (let ((hash (editorconfig-core-get-properties-hash file confname confversion)) + (result nil)) + (maphash (lambda (key value) + (add-to-list 'result + (cons (symbol-name key) + value))) + hash) + result)) + +(defun editorconfig-core--hash-merge (into update) + "Merge to hashes INTO and UPDATE. + +This is a destructive function, hash INTO will be modified. +When the same key exists in both two hashes, values of UPDATE takes precedence." + (maphash (lambda (key value) + (puthash key + value + into)) + update) + into) + +;;;###autoload +(defun editorconfig-core-get-properties-hash (&optional file confname confversion) + "Get EditorConfig properties for FILE. +If FILE is not given, use currently visiting file. +Give CONFNAME for basename of config file other than .editorconfig. +If need to specify config format version, give CONFVERSION. + +This function is almost same as `editorconfig-core-get-properties', but returns +hash object instead." + (setq file + (expand-file-name (or file + buffer-file-name + (error "FILE is not given and `buffer-file-name' is nil")))) (setq confname (or confname ".editorconfig")) (setq confversion (or confversion "0.12.0")) - (let ((result (editorconfig-core--remove-duplicate - (apply 'append - (mapcar (lambda (handle) - (apply 'append - (editorconfig-core-handle-get-properties handle - file))) - (editorconfig-core--get-handles (file-name-directory file) - confname)))))) - (dolist (key '("end_of_line" "indent_style" "indent_size" - "insert_final_newline" "trim_trailing_whitespace" "charset")) - (let ((pair (assoc key - result))) - (when pair - (setcdr pair - (downcase (cdr pair)))))) + (let ((result (make-hash-table))) + (dolist (handle (editorconfig-core--get-handles (file-name-directory file) + confname)) + (editorconfig-core--hash-merge result + (editorconfig-core-handle-get-properties-hash handle + file))) + + ;; Downcase known boolean values + (dolist (key '( + end_of_line indent_style indent_size insert_final_newline + trim_trailing_whitespace charset + )) + (let ((val (gethash key + result))) + (when val + (puthash key + (downcase val) + result)))) ;; Add indent_size property - (let ((p-indent-size (assoc "indent_size" result)) - (p-indent-style (assoc "indent_style" result))) - (when (and (not p-indent-size) - (string= (cdr p-indent-style) "tab") + (let ((v-indent-size (gethash 'indent_size result)) + (v-indent-style (gethash 'indent_style result))) + (when (and (not v-indent-size) + (string= v-indent-style "tab") ;; If VERSION < 0.9.0, indent_size should have no default value (version<= "0.9.0" confversion)) - (setq result - `(,@result ("indent_size" . "tab"))))) + (puthash 'indent_size + "tab" + result))) ;; Add tab_width property - (let ((p-indent-size (assoc "indent_size" result)) - (p-tab-width (assoc "tab_width" result))) - (when (and p-indent-size - (not p-tab-width) - (not (string= (cdr p-indent-size) "tab"))) - (setq result - `(,@result ("tab_width" . ,(cdr p-indent-size)))))) + (let ((v-indent-size (gethash 'indent_size result)) + (v-tab-width (gethash 'tab_width result))) + (when (and v-indent-size + (not v-tab-width) + (not (string= v-indent-size "tab"))) + (puthash 'tab_width + v-indent-size + result))) ;; Update indent-size property - (let ((p-indent-size (assoc "indent_size" result)) - (p-tab-width (assoc "tab_width" result))) - (when (and p-indent-size - p-tab-width - (string= (cdr p-indent-size) "tab")) - (setcdr p-indent-size (cdr p-tab-width)))) + (let ((v-indent-size (gethash 'indent_size result)) + (v-tab-width (gethash 'tab_width result))) + (when (and v-indent-size + v-tab-width + (string= v-indent-size "tab")) + (puthash 'indent_size + v-tab-width + result))) result)) -;;;###autoload -(defun editorconfig-core-get-properties-hash (&optional file confname confversion) - "Get EditorConfig properties for FILE. -If FILE is not given, use currently visiting file. -Give CONFNAME for basename of config file other than .editorconfig. -If need to specify config format version, give CONFVERSION. - -This function is almost same as `editorconfig-core-get-properties', but returns -hash object instead." - (let ((result (editorconfig-core-get-properties file - confname - confversion)) - (hash (make-hash-table :test 'equal))) - (dolist (prop result) - (puthash (intern (car prop)) - (cdr prop) - hash)) - hash)) - (provide 'editorconfig-core) ;;; editorconfig-core.el ends here diff --git a/ert-tests/editorconfig-core.el b/ert-tests/editorconfig-core.el index f85e2ba4..a09d38b2 100644 --- a/ert-tests/editorconfig-core.el +++ b/ert-tests/editorconfig-core.el @@ -1,14 +1,5 @@ (require 'editorconfig-core) -(ert-deftest test-editorconfig-core--remove-duplicate () - (should (equal (editorconfig-core--remove-duplicate '(("a" . 1) ("b" . 2) ("c" . 3) ("b" . 4))) - '(("a" . 1) ("b" . 4) ("c" . 3)))) - (should (equal (editorconfig-core--remove-duplicate '(("a" . 1) ("b" . 2) ("c" . 3))) - '(("a" . 1) ("b" . 2) ("c" . 3)))) - (should (equal (editorconfig-core--remove-duplicate nil) - nil)) - ) - (ert-deftest test-editorconfig-core--get-handles () (let* ((fixtures (concat default-directory