From: Andreas Gerler Date: Wed, 24 Dec 2025 14:25:42 +0000 (+0100) Subject: update terraform-mode X-Git-Url: http://git.bundesbrandschatzamt.de/?a=commitdiff_plain;h=92ec4784d0a756c3d8476deb45803a7c564fb3f5;p=emacsconfig.git update terraform-mode --- diff --git a/elpa/terraform-mode-20241217.1628/terraform-mode-autoloads.el b/elpa/terraform-mode-20241217.1628/terraform-mode-autoloads.el deleted file mode 100644 index f7c32175..00000000 --- a/elpa/terraform-mode-20241217.1628/terraform-mode-autoloads.el +++ /dev/null @@ -1,33 +0,0 @@ -;;; terraform-mode-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from terraform-mode.el - -(autoload 'terraform-mode "terraform-mode" "\ -Major mode for editing terraform configuration file - -(fn)" t) -(add-to-list 'auto-mode-alist '("\\.t\\(f\\(vars\\)?\\|ofu\\)\\'" . terraform-mode)) -(register-definition-prefixes "terraform-mode" '("terraform-")) - -;;; End of scraped data - -(provide 'terraform-mode-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; terraform-mode-autoloads.el ends here diff --git a/elpa/terraform-mode-20241217.1628/terraform-mode-pkg.el b/elpa/terraform-mode-20241217.1628/terraform-mode-pkg.el deleted file mode 100644 index 2d4ff5e3..00000000 --- a/elpa/terraform-mode-20241217.1628/terraform-mode-pkg.el +++ /dev/null @@ -1,11 +0,0 @@ -;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "terraform-mode" "20241217.1628" - "Major mode for terraform configuration file." - '((emacs "24.3") - (hcl-mode "0.03") - (dash "2.17.0")) - :url "https://github.com/hcl-emacs/terraform-mode" - :commit "5bdd734a87f67f6574664f63eb4d02a0bc74c0d0" - :revdesc "5bdd734a87f6" - :authors '(("Syohei YOSHIDA" . "syohex@gmail.com")) - :maintainers '(("Syohei YOSHIDA" . "syohex@gmail.com"))) diff --git a/elpa/terraform-mode-20241217.1628/terraform-mode.el b/elpa/terraform-mode-20241217.1628/terraform-mode.el deleted file mode 100644 index 794f596b..00000000 --- a/elpa/terraform-mode-20241217.1628/terraform-mode.el +++ /dev/null @@ -1,417 +0,0 @@ -;;; terraform-mode.el --- Major mode for terraform configuration file -*- lexical-binding: t -*- - -;; Copyright (C) 2017 by Syohei YOSHIDA - -;; Author: Syohei YOSHIDA -;; URL: https://github.com/syohex/emacs-terraform-mode -;; Package-Version: 20241217.1628 -;; Package-Revision: 5bdd734a87f6 -;; Package-Requires: ((emacs "24.3") (hcl-mode "0.03") (dash "2.17.0")) - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; Major mode of terraform configuration file. terraform-mode provides -;; syntax highlighting, indentation function and formatting. - -;; Format the current buffer with terraform-format-buffer. To always -;; format terraform buffers when saving, use: -;; (setq terraform-format-on-save t) - -;;; Code: - -(require 'cl-lib) -(require 'rx) -(require 'hcl-mode) -(require 'dash) -(require 'thingatpt) -(require 'outline) - -(defgroup terraform nil - "Major mode of Terraform configuration file." - :group 'languages) - -(defcustom terraform-indent-level 2 - "The tab width to use when indenting." - :type 'integer) - -(defcustom terraform-format-on-save nil - "Format buffer on save" - :type 'boolean - :group 'terraform-mode) - -(defcustom terraform-command "terraform" - "Command used to invoke terraform" - :type 'string - :group 'terraform-mode) - -(defface terraform-resource-type-face - '((t :inherit font-lock-type-face)) - "Face for resource names." - :group 'terraform-mode) - -(define-obsolete-face-alias 'terraform--resource-type-face 'terraform-resource-type-face "1.0.0") - -(defface terraform-resource-name-face - '((t :inherit font-lock-function-name-face)) - "Face for resource names." - :group 'terraform-mode) - -(define-obsolete-face-alias 'terraform--resource-name-face 'terraform-resource-name-face "1.0.0") - -(defface terraform-builtin-face - '((t :inherit font-lock-builtin-face)) - "Face for builtins." - :group 'terraform-mode) - -(define-obsolete-face-alias 'terraform--builtin-face 'terraform-builtin-face "1.0.0") - -(defface terraform-variable-name-face - '((t :inherit font-lock-variable-name-face)) - "Face for varriables." - :group 'terraform-mode) - -(defconst terraform--constants-regexp - (concat "\\(?:^\\|[^.]\\)" (regexp-opt '("null") 'words))) - -(defconst terraform--block-builtins-without-name-or-type-regexp - (rx line-start - (zero-or-more space) - (group-n 1 (or "terraform" "locals" "required_providers" "atlas" "connection")) - (or (one-or-more space) "{"))) - -(defconst terraform--block-builtins-with-type-only - (rx (or "backend" "provider" "provisioner"))) - -(defconst terraform--block-builtins-with-type-only--builtin-highlight-regexp - (eval `(rx line-start - (zero-or-more space) - (group-n 1 (regexp ,(eval terraform--block-builtins-with-type-only))) - (one-or-more space)))) - -(defconst terraform--block-builtins-with-type-only--resource-type-highlight-regexp - (eval `(rx (regexp ,(eval terraform--block-builtins-with-type-only--builtin-highlight-regexp)) - (group-n 2 (and (not (any "=")) (+? (not space)))) - (or (one-or-more space) "{")))) - -(defconst terraform--block-builtins-with-name-only - (rx (or "variable" "module" "output"))) - -(defconst terraform--block-builtins-with-name-only--builtin-highlight-regexp - (eval `(rx line-start - (zero-or-more space) - (group-n 1 (regexp ,(eval terraform--block-builtins-with-name-only))) - (one-or-more space)))) - -(defconst terraform--block-builtins-with-name-only--name-highlight-regexp - (eval `(rx (regexp ,(eval terraform--block-builtins-with-name-only--builtin-highlight-regexp)) - (group-n 2 (+? (not space))) - (or (one-or-more space) "{")))) - -(defconst terraform--block-builtins-with-type-and-name - (rx (or "data" "resource"))) - -(defconst terraform--block-builtins-with-type-and-name--builtin-highlight-regexp - (eval `(rx line-start - (zero-or-more space) - (group-n 1 (regexp ,(eval terraform--block-builtins-with-type-and-name))) - (one-or-more space)))) - -(defconst terraform--block-builtins-with-type-and-name--type-highlight-regexp - (eval `(rx (regexp ,(eval terraform--block-builtins-with-type-and-name--builtin-highlight-regexp)) - (group-n 2 "\"" (+? (not space)) "\"") - (one-or-more space)))) - -(defconst terraform--block-builtins-with-type-and-name--name-highlight-regexp - (eval `(rx (regexp ,(eval terraform--block-builtins-with-type-and-name--type-highlight-regexp)) - (group-n 3 (+? (not space))) - (or (one-or-more space) "{")))) - -(defconst terraform--assignment-statement - (rx line-start - (zero-or-more space) - (group-n 1 (minimal-match (one-or-more any))) - (zero-or-more space) - "=")) - -(defvar terraform-font-lock-keywords - `((,terraform--assignment-statement 1 'terraform-variable-name-face) - (,terraform--block-builtins-without-name-or-type-regexp 1 'terraform-builtin-face) - (,terraform--block-builtins-with-type-only--builtin-highlight-regexp 1 'terraform-builtin-face) - (,terraform--block-builtins-with-type-only--resource-type-highlight-regexp - (1 'terraform-builtin-face) - (2 'terraform-resource-type-face t)) - (,terraform--block-builtins-with-name-only--builtin-highlight-regexp 1 'terraform-builtin-face) - (,terraform--block-builtins-with-name-only--name-highlight-regexp - (1 'terraform-builtin-face) - (2 'terraform-resource-name-face t)) - (,terraform--block-builtins-with-type-and-name--builtin-highlight-regexp 1 'terraform-builtin-face) - (,terraform--block-builtins-with-type-and-name--type-highlight-regexp 2 'terraform-resource-type-face t) - (,terraform--block-builtins-with-type-and-name--name-highlight-regexp - (1 'terraform-builtin-face) - (2 'terraform-resource-type-face t) - (3 'terraform-resource-name-face t)) - (,terraform--constants-regexp 1 'font-lock-constant-face) - ,@hcl-font-lock-keywords)) - -(defun terraform-format-buffer () - "Rewrite current buffer in a canonical format using terraform fmt." - (interactive) - (let ((buf (get-buffer-create "*terraform-fmt*"))) - (if (zerop (call-process-region (point-min) (point-max) - terraform-command nil buf nil "fmt" "-no-color" "-")) - (let ((point (point)) - (window-start (window-start))) - (erase-buffer) - (insert-buffer-substring buf) - (when (/= terraform-indent-level 2) - (indent-region (point-min) (point-max))) - (goto-char point) - (set-window-start nil window-start)) - (message "terraform fmt: %s" (with-current-buffer buf (buffer-string)))) - (kill-buffer buf))) - -(defun terraform-format-region () - "Rewrite current region in a canonical format using terraform fmt." - (interactive) - (let ((buf (get-buffer-create "*terraform-fmt*"))) - (when (use-region-p) - (if (zerop (call-process-region (region-beginning) (region-end) - terraform-command nil buf nil "fmt" "-")) - (let ((point (region-end)) - (window-start (region-beginning))) - (delete-region window-start point) - (insert-buffer-substring buf) - (goto-char point) - (set-window-start nil window-start)) - (message "terraform fmt: %s" (with-current-buffer buf (buffer-string)))) - (kill-buffer buf)))) - -(define-minor-mode terraform-format-on-save-mode - "Run terraform-format-buffer before saving current buffer." - :lighter "" - (if terraform-format-on-save-mode - (add-hook 'before-save-hook #'terraform-format-buffer nil t) - (remove-hook 'before-save-hook #'terraform-format-buffer t))) - -(defun terraform--generate-imenu () - (let ((search-results (make-hash-table :test #'equal)) - (menu-list '())) - (save-match-data - (goto-char (point-min)) - (while (re-search-forward terraform--block-builtins-with-type-only--resource-type-highlight-regexp nil t) - (let ((key (match-string 1)) - (location (match-beginning 2)) - (resource-type (replace-regexp-in-string "\"" "" (match-string 2)))) - (-if-let (matches (gethash key search-results)) - (puthash key (push `(,resource-type . ,location) matches) search-results) - (puthash key `((,resource-type . ,location)) search-results)))) - - - (goto-char (point-min)) - (while (re-search-forward terraform--block-builtins-with-name-only--name-highlight-regexp nil t) - (let ((key (match-string 1)) - (location (match-beginning 2)) - (resource-name (replace-regexp-in-string "\"" "" (match-string 2)))) - (-if-let (matches (gethash key search-results)) - (puthash key (push `(,resource-name . ,location) matches) search-results) - (puthash key `((,resource-name . ,location)) search-results)))) - - (goto-char (point-min)) - (while (re-search-forward terraform--block-builtins-with-type-and-name--name-highlight-regexp nil t) - (let* ((key (match-string 1)) - (location (match-beginning 2)) - (type (match-string 2)) - (name (match-string 3)) - (resource-name (concat (replace-regexp-in-string "\"" "" type) - "/" - (replace-regexp-in-string "\"" "" name)))) - (-if-let (matches (gethash key search-results)) - (puthash key (push `(,resource-name . ,location) matches) search-results) - (puthash key `((,resource-name . ,location)) search-results)))) - - (maphash (lambda (k v) (push `(,k ,@v) menu-list)) search-results) - menu-list))) - -(defun terraform--extract-provider (resource-name) - "Return the provider associated with a RESOURCE-NAME." - (car (split-string resource-name "_"))) - -(defun terraform--extract-resource (resource-name) - "Return the resource associated with a RESOURCE-NAME." - (mapconcat #'identity (cdr (split-string resource-name "_")) "_")) - -(defun terraform--get-resource-provider-namespace (provider) - "Return provider namespace for PROVIDER." - (let ((provider-info (shell-command-to-string (concat terraform-command " providers")))) - (with-temp-buffer - (insert provider-info) - (goto-char (point-min)) - (when (re-search-forward (concat "/\\(.*?\\)/" provider "\\]") nil t) - (match-string 1))))) - -(defun terraform--get-resource-provider-source (provider &optional dir) - "Return Terraform provider source for PROVIDER located in DIR. -Terraform provider source is searched in `required_provider' declaration -in current buffer or in other Terraform files located in the same directory -of the file of current buffer. If still not found, the provider source is -searched by running command `terraform providers'. -The DIR parameter is optional and used only for tests." - (goto-char (point-min)) - ;; find current directory if it's not specified in arguments - (if (and (not dir) buffer-file-name) (setq dir (file-name-directory buffer-file-name))) - (let (tf-files - ;; try to find provider source in current buffer - (provider-source (terraform--get-resource-provider-source-in-buffer provider))) - ;; check if terraform provider-source was found - (when (and (= (length provider-source) 0) dir) - ;; find all terraform files of this project. One of them - ;; should contain required_provider declaration - (setq tf-files (directory-files dir nil "^[[:alnum:][:blank:]_.-]+\\.tf$"))) - ;; iterate on terraform files until a provider source is found - (while (and (= (length provider-source) 0) tf-files) - (with-temp-buffer - (let* ((file (pop tf-files)) - (file-path (if dir (concat dir "/" file) file))) - (insert-file-contents file-path) - ;; look for provider source in a terraform file - (setq provider-source (terraform--get-resource-provider-source-in-buffer provider))))) - provider-source)) - -(defun terraform--get-resource-provider-source-in-buffer (provider) - "Search and return provider namespace for PROVIDER in current buffer. -Return nil if not found." - (goto-char (point-min)) - (if (and (re-search-forward "^terraform[[:blank:]]*{" nil t) - (re-search-forward "^[[:blank:]]*required_providers[[:blank:]]*{" nil t) - (re-search-forward (concat "^[[:blank:]]*" provider "[[:blank:]]*=[[:blank:]]*{") nil t) - (re-search-forward "^[[:blank:]]*source[[:blank:]]*=[[:blank:]]*\"\\([a-z/]+\\)\"" nil t)) - (match-string 1))) - -(defun terraform--resource-url (resource doc-dir) - "Return the url containing the documentation for RESOURCE using DOC-DIR." - (let* ((provider (terraform--extract-provider resource)) - ;; search provider source in terraform files - (provider-source (terraform--get-resource-provider-source provider)) - (resource-name (terraform--extract-resource resource))) - (when (= (length provider-source) 0) - ;; fallback to old method with terraform providers command - (setq provider-source (concat - (terraform--get-resource-provider-namespace provider) - "/" provider))) - (if (> (length provider-source) 0) - (format "https://registry.terraform.io/providers/%s/latest/docs/%s/%s" - provider-source doc-dir resource-name) - (user-error "Can not determine the provider source for %s" provider)))) - -(defun terraform--resource-url-at-point () - (save-excursion - (goto-char (line-beginning-position)) - (unless (looking-at-p "^resource\\|^data") - (re-search-backward "^resource\\|^data" nil t)) - (let ((doc-dir (if (equal (word-at-point) "data") "data-sources" "resources"))) - (forward-symbol 2) - (terraform--resource-url (thing-at-point 'symbol) doc-dir)))) - -(defun terraform-open-doc () - "Open a browser at the URL documenting the resource at point." - (interactive) - (browse-url (terraform--resource-url-at-point))) - -(defun terraform-kill-doc-url () - "Kill the URL documenting the resource at point (i.e. copy it to the clipboard)." - (interactive) - (let* ((url (substring-no-properties (terraform--resource-url-at-point)))) - (kill-new url) - (message "Copied URL: %s" url))) - -(defun terraform-insert-doc-in-comment () - "Insert a comment containing an URL documenting the resource at point." - (interactive) - (let ((doc-url (terraform--resource-url-at-point))) - (save-excursion - (unless (looking-at-p "^resource\\|^data") - (re-search-backward "^resource\\|^data" nil t)) - (insert (format "# %s\n" doc-url))))) - -(defun terraform--outline-level () - "Return the depth to which a statement is nested in the outline. - -See also `outline-level'." - (or (cdr (assoc (match-string 1) outline-heading-alist)) - (- (match-end 1) (match-beginning 1)))) - -(defun terraform--setup-outline-mode () - (set (make-local-variable 'outline-level) #'terraform--outline-level) - - (let ((terraform-keywords - (list "terraform" "locals" "required_providers" "atlas" "connection" - "backend" "provider" "provisioner" - "variable" "module" "output" - "data" "resource"))) - (set (make-local-variable 'outline-regexp) - (concat - "^" - (regexp-opt terraform-keywords 'symbols) - "[[:blank:]].*{[[:blank:]]*$")) - (set (make-local-variable 'outline-heading-alist) - (mapcar - (lambda (item) (cons item 2)) - terraform-keywords)))) - -(defun terraform-toggle-or-indent (&optional arg) - "Toggle visibility of block under point or indent. - -If the point is not at the heading, call -`indent-for-tab-command'." - (interactive) - (if (and outline-minor-mode (outline-on-heading-p)) - (outline-toggle-children) - (indent-for-tab-command arg))) - -(defvar terraform-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-d C-w") #'terraform-open-doc) - (define-key map (kbd "C-c C-d C-c") #'terraform-kill-doc-url) - (define-key map (kbd "C-c C-d C-r") #'terraform-insert-doc-in-comment) - (define-key map (kbd "C-c C-f") #'outline-toggle-children) - map)) - -;;;###autoload -(define-derived-mode terraform-mode hcl-mode "Terraform" - "Major mode for editing terraform configuration file" - - (setq font-lock-defaults '((terraform-font-lock-keywords))) - (when terraform-format-on-save - (terraform-format-on-save-mode 1)) - - ;; indentation - (make-local-variable 'terraform-indent-level) - (setq hcl-indent-level terraform-indent-level) - - ;; outline - (terraform--setup-outline-mode) - - ;; imenu - (setq imenu-sort-function 'imenu--sort-by-name) - (setq imenu-create-index-function 'terraform--generate-imenu) - (imenu-add-to-menubar "Terraform")) - -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.t\\(f\\(vars\\)?\\|ofu\\)\\'" . terraform-mode)) - -(provide 'terraform-mode) - -;;; terraform-mode.el ends here diff --git a/elpa/terraform-mode-20251115.2210/terraform-mode-autoloads.el b/elpa/terraform-mode-20251115.2210/terraform-mode-autoloads.el new file mode 100644 index 00000000..f7c32175 --- /dev/null +++ b/elpa/terraform-mode-20251115.2210/terraform-mode-autoloads.el @@ -0,0 +1,33 @@ +;;; terraform-mode-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) + + + +;;; Generated autoloads from terraform-mode.el + +(autoload 'terraform-mode "terraform-mode" "\ +Major mode for editing terraform configuration file + +(fn)" t) +(add-to-list 'auto-mode-alist '("\\.t\\(f\\(vars\\)?\\|ofu\\)\\'" . terraform-mode)) +(register-definition-prefixes "terraform-mode" '("terraform-")) + +;;; End of scraped data + +(provide 'terraform-mode-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; terraform-mode-autoloads.el ends here diff --git a/elpa/terraform-mode-20251115.2210/terraform-mode-pkg.el b/elpa/terraform-mode-20251115.2210/terraform-mode-pkg.el new file mode 100644 index 00000000..025c3bcf --- /dev/null +++ b/elpa/terraform-mode-20251115.2210/terraform-mode-pkg.el @@ -0,0 +1,11 @@ +;; -*- no-byte-compile: t; lexical-binding: nil -*- +(define-package "terraform-mode" "20251115.2210" + "Major mode for terraform configuration file." + '((emacs "24.3") + (hcl-mode "0.03") + (dash "2.17.0")) + :url "https://github.com/syohex/emacs-terraform-mode" + :commit "01635df3625c0cec2bb4613a6f920b8569d41009" + :revdesc "01635df3625c" + :authors '(("Syohei YOSHIDA" . "syohex@gmail.com")) + :maintainers '(("Syohei YOSHIDA" . "syohex@gmail.com"))) diff --git a/elpa/terraform-mode-20251115.2210/terraform-mode.el b/elpa/terraform-mode-20251115.2210/terraform-mode.el new file mode 100644 index 00000000..e12133ce --- /dev/null +++ b/elpa/terraform-mode-20251115.2210/terraform-mode.el @@ -0,0 +1,417 @@ +;;; terraform-mode.el --- Major mode for terraform configuration file -*- lexical-binding: t -*- + +;; Copyright (C) 2017 by Syohei YOSHIDA + +;; Author: Syohei YOSHIDA +;; URL: https://github.com/syohex/emacs-terraform-mode +;; Package-Version: 20251115.2210 +;; Package-Revision: 01635df3625c +;; Package-Requires: ((emacs "24.3") (hcl-mode "0.03") (dash "2.17.0")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Major mode of terraform configuration file. terraform-mode provides +;; syntax highlighting, indentation function and formatting. + +;; Format the current buffer with terraform-format-buffer. To always +;; format terraform buffers when saving, use: +;; (setq terraform-format-on-save t) + +;;; Code: + +(require 'cl-lib) +(require 'rx) +(require 'hcl-mode) +(require 'dash) +(require 'thingatpt) +(require 'outline) + +(defgroup terraform nil + "Major mode of Terraform configuration file." + :group 'languages) + +(defcustom terraform-indent-level 2 + "The tab width to use when indenting." + :type 'integer) + +(defcustom terraform-format-on-save nil + "Format buffer on save" + :type 'boolean + :group 'terraform-mode) + +(defcustom terraform-command "terraform" + "Command used to invoke terraform" + :type 'string + :group 'terraform-mode) + +(defface terraform-resource-type-face + '((t :inherit font-lock-type-face)) + "Face for resource names." + :group 'terraform-mode) + +(define-obsolete-face-alias 'terraform--resource-type-face 'terraform-resource-type-face "1.0.0") + +(defface terraform-resource-name-face + '((t :inherit font-lock-function-name-face)) + "Face for resource names." + :group 'terraform-mode) + +(define-obsolete-face-alias 'terraform--resource-name-face 'terraform-resource-name-face "1.0.0") + +(defface terraform-builtin-face + '((t :inherit font-lock-builtin-face)) + "Face for builtins." + :group 'terraform-mode) + +(define-obsolete-face-alias 'terraform--builtin-face 'terraform-builtin-face "1.0.0") + +(defface terraform-variable-name-face + '((t :inherit font-lock-variable-name-face)) + "Face for varriables." + :group 'terraform-mode) + +(defconst terraform--constants-regexp + (concat "\\(?:^\\|[^.]\\)" (regexp-opt '("null") 'words))) + +(defconst terraform--block-builtins-without-name-or-type-regexp + (rx line-start + (zero-or-more space) + (group-n 1 (or "terraform" "locals" "required_providers" "atlas" "connection")) + (or (one-or-more space) "{"))) + +(defconst terraform--block-builtins-with-type-only + (rx (or "backend" "provider" "provisioner"))) + +(defconst terraform--block-builtins-with-type-only--builtin-highlight-regexp + (eval `(rx line-start + (zero-or-more space) + (group-n 1 (regexp ,(eval terraform--block-builtins-with-type-only))) + (one-or-more space)))) + +(defconst terraform--block-builtins-with-type-only--resource-type-highlight-regexp + (eval `(rx (regexp ,(eval terraform--block-builtins-with-type-only--builtin-highlight-regexp)) + (group-n 2 (and (not (any "=")) (+? (not space)))) + (or (one-or-more space) "{")))) + +(defconst terraform--block-builtins-with-name-only + (rx (or "variable" "module" "output"))) + +(defconst terraform--block-builtins-with-name-only--builtin-highlight-regexp + (eval `(rx line-start + (zero-or-more space) + (group-n 1 (regexp ,(eval terraform--block-builtins-with-name-only))) + (one-or-more space)))) + +(defconst terraform--block-builtins-with-name-only--name-highlight-regexp + (eval `(rx (regexp ,(eval terraform--block-builtins-with-name-only--builtin-highlight-regexp)) + (group-n 2 (+? (not space))) + (or (one-or-more space) "{")))) + +(defconst terraform--block-builtins-with-type-and-name + (rx (or "data" "resource" "ephemeral"))) + +(defconst terraform--block-builtins-with-type-and-name--builtin-highlight-regexp + (eval `(rx line-start + (zero-or-more space) + (group-n 1 (regexp ,(eval terraform--block-builtins-with-type-and-name))) + (one-or-more space)))) + +(defconst terraform--block-builtins-with-type-and-name--type-highlight-regexp + (eval `(rx (regexp ,(eval terraform--block-builtins-with-type-and-name--builtin-highlight-regexp)) + (group-n 2 "\"" (+? (not space)) "\"") + (one-or-more space)))) + +(defconst terraform--block-builtins-with-type-and-name--name-highlight-regexp + (eval `(rx (regexp ,(eval terraform--block-builtins-with-type-and-name--type-highlight-regexp)) + (group-n 3 (+? (not space))) + (or (one-or-more space) "{")))) + +(defconst terraform--assignment-statement + (rx line-start + (zero-or-more space) + (group-n 1 (minimal-match (one-or-more any))) + (zero-or-more space) + "=")) + +(defvar terraform-font-lock-keywords + `((,terraform--assignment-statement 1 'terraform-variable-name-face) + (,terraform--block-builtins-without-name-or-type-regexp 1 'terraform-builtin-face) + (,terraform--block-builtins-with-type-only--builtin-highlight-regexp 1 'terraform-builtin-face) + (,terraform--block-builtins-with-type-only--resource-type-highlight-regexp + (1 'terraform-builtin-face) + (2 'terraform-resource-type-face t)) + (,terraform--block-builtins-with-name-only--builtin-highlight-regexp 1 'terraform-builtin-face) + (,terraform--block-builtins-with-name-only--name-highlight-regexp + (1 'terraform-builtin-face) + (2 'terraform-resource-name-face t)) + (,terraform--block-builtins-with-type-and-name--builtin-highlight-regexp 1 'terraform-builtin-face) + (,terraform--block-builtins-with-type-and-name--type-highlight-regexp 2 'terraform-resource-type-face t) + (,terraform--block-builtins-with-type-and-name--name-highlight-regexp + (1 'terraform-builtin-face) + (2 'terraform-resource-type-face t) + (3 'terraform-resource-name-face t)) + (,terraform--constants-regexp 1 'font-lock-constant-face) + ,@hcl-font-lock-keywords)) + +(defun terraform-format-buffer () + "Rewrite current buffer in a canonical format using terraform fmt." + (interactive) + (let ((buf (get-buffer-create "*terraform-fmt*"))) + (if (zerop (call-process-region (point-min) (point-max) + terraform-command nil buf nil "fmt" "-no-color" "-")) + (let ((point (point)) + (window-start (window-start))) + (erase-buffer) + (insert-buffer-substring buf) + (when (/= terraform-indent-level 2) + (indent-region (point-min) (point-max))) + (goto-char point) + (set-window-start nil window-start)) + (message "terraform fmt: %s" (with-current-buffer buf (buffer-string)))) + (kill-buffer buf))) + +(defun terraform-format-region () + "Rewrite current region in a canonical format using terraform fmt." + (interactive) + (let ((buf (get-buffer-create "*terraform-fmt*"))) + (when (use-region-p) + (if (zerop (call-process-region (region-beginning) (region-end) + terraform-command nil buf nil "fmt" "-")) + (let ((point (region-end)) + (window-start (region-beginning))) + (delete-region window-start point) + (insert-buffer-substring buf) + (goto-char point) + (set-window-start nil window-start)) + (message "terraform fmt: %s" (with-current-buffer buf (buffer-string)))) + (kill-buffer buf)))) + +(define-minor-mode terraform-format-on-save-mode + "Run terraform-format-buffer before saving current buffer." + :lighter "" + (if terraform-format-on-save-mode + (add-hook 'before-save-hook #'terraform-format-buffer nil t) + (remove-hook 'before-save-hook #'terraform-format-buffer t))) + +(defun terraform--generate-imenu () + (let ((search-results (make-hash-table :test #'equal)) + (menu-list '())) + (save-match-data + (goto-char (point-min)) + (while (re-search-forward terraform--block-builtins-with-type-only--resource-type-highlight-regexp nil t) + (let ((key (match-string 1)) + (location (match-beginning 2)) + (resource-type (replace-regexp-in-string "\"" "" (match-string 2)))) + (-if-let (matches (gethash key search-results)) + (puthash key (push `(,resource-type . ,location) matches) search-results) + (puthash key `((,resource-type . ,location)) search-results)))) + + + (goto-char (point-min)) + (while (re-search-forward terraform--block-builtins-with-name-only--name-highlight-regexp nil t) + (let ((key (match-string 1)) + (location (match-beginning 2)) + (resource-name (replace-regexp-in-string "\"" "" (match-string 2)))) + (-if-let (matches (gethash key search-results)) + (puthash key (push `(,resource-name . ,location) matches) search-results) + (puthash key `((,resource-name . ,location)) search-results)))) + + (goto-char (point-min)) + (while (re-search-forward terraform--block-builtins-with-type-and-name--name-highlight-regexp nil t) + (let* ((key (match-string 1)) + (location (match-beginning 2)) + (type (match-string 2)) + (name (match-string 3)) + (resource-name (concat (replace-regexp-in-string "\"" "" type) + "/" + (replace-regexp-in-string "\"" "" name)))) + (-if-let (matches (gethash key search-results)) + (puthash key (push `(,resource-name . ,location) matches) search-results) + (puthash key `((,resource-name . ,location)) search-results)))) + + (maphash (lambda (k v) (push `(,k ,@v) menu-list)) search-results) + menu-list))) + +(defun terraform--extract-provider (resource-name) + "Return the provider associated with a RESOURCE-NAME." + (car (split-string resource-name "_"))) + +(defun terraform--extract-resource (resource-name) + "Return the resource associated with a RESOURCE-NAME." + (mapconcat #'identity (cdr (split-string resource-name "_")) "_")) + +(defun terraform--get-resource-provider-namespace (provider) + "Return provider namespace for PROVIDER." + (let ((provider-info (shell-command-to-string (concat terraform-command " providers")))) + (with-temp-buffer + (insert provider-info) + (goto-char (point-min)) + (when (re-search-forward (concat "/\\(.*?\\)/" provider "\\]") nil t) + (match-string 1))))) + +(defun terraform--get-resource-provider-source (provider &optional dir) + "Return Terraform provider source for PROVIDER located in DIR. +Terraform provider source is searched in `required_provider' declaration +in current buffer or in other Terraform files located in the same directory +of the file of current buffer. If still not found, the provider source is +searched by running command `terraform providers'. +The DIR parameter is optional and used only for tests." + (goto-char (point-min)) + ;; find current directory if it's not specified in arguments + (if (and (not dir) buffer-file-name) (setq dir (file-name-directory buffer-file-name))) + (let (tf-files + ;; try to find provider source in current buffer + (provider-source (terraform--get-resource-provider-source-in-buffer provider))) + ;; check if terraform provider-source was found + (when (and (= (length provider-source) 0) dir) + ;; find all terraform files of this project. One of them + ;; should contain required_provider declaration + (setq tf-files (directory-files dir nil "^[[:alnum:][:blank:]_.-]+\\.tf$"))) + ;; iterate on terraform files until a provider source is found + (while (and (= (length provider-source) 0) tf-files) + (with-temp-buffer + (let* ((file (pop tf-files)) + (file-path (if dir (concat dir "/" file) file))) + (insert-file-contents file-path) + ;; look for provider source in a terraform file + (setq provider-source (terraform--get-resource-provider-source-in-buffer provider))))) + provider-source)) + +(defun terraform--get-resource-provider-source-in-buffer (provider) + "Search and return provider namespace for PROVIDER in current buffer. +Return nil if not found." + (goto-char (point-min)) + (if (and (re-search-forward "^terraform[[:blank:]]*{" nil t) + (re-search-forward "^[[:blank:]]*required_providers[[:blank:]]*{" nil t) + (re-search-forward (concat "^[[:blank:]]*" provider "[[:blank:]]*=[[:blank:]]*{") nil t) + (re-search-forward "^[[:blank:]]*source[[:blank:]]*=[[:blank:]]*\"\\([a-z/]+\\)\"" nil t)) + (match-string 1))) + +(defun terraform--resource-url (resource doc-dir) + "Return the url containing the documentation for RESOURCE using DOC-DIR." + (let* ((provider (terraform--extract-provider resource)) + ;; search provider source in terraform files + (provider-source (terraform--get-resource-provider-source provider)) + (resource-name (terraform--extract-resource resource))) + (when (= (length provider-source) 0) + ;; fallback to old method with terraform providers command + (setq provider-source (concat + (terraform--get-resource-provider-namespace provider) + "/" provider))) + (if (> (length provider-source) 0) + (format "https://registry.terraform.io/providers/%s/latest/docs/%s/%s" + provider-source doc-dir resource-name) + (user-error "Can not determine the provider source for %s" provider)))) + +(defun terraform--resource-url-at-point () + (save-excursion + (goto-char (line-beginning-position)) + (unless (looking-at-p "^resource\\|^data") + (re-search-backward "^resource\\|^data" nil t)) + (let ((doc-dir (if (equal (word-at-point) "data") "data-sources" "resources"))) + (forward-symbol 2) + (terraform--resource-url (thing-at-point 'symbol) doc-dir)))) + +(defun terraform-open-doc () + "Open a browser at the URL documenting the resource at point." + (interactive) + (browse-url (terraform--resource-url-at-point))) + +(defun terraform-kill-doc-url () + "Kill the URL documenting the resource at point (i.e. copy it to the clipboard)." + (interactive) + (let* ((url (substring-no-properties (terraform--resource-url-at-point)))) + (kill-new url) + (message "Copied URL: %s" url))) + +(defun terraform-insert-doc-in-comment () + "Insert a comment containing an URL documenting the resource at point." + (interactive) + (let ((doc-url (terraform--resource-url-at-point))) + (save-excursion + (unless (looking-at-p "^resource\\|^data") + (re-search-backward "^resource\\|^data" nil t)) + (insert (format "# %s\n" doc-url))))) + +(defun terraform--outline-level () + "Return the depth to which a statement is nested in the outline. + +See also `outline-level'." + (or (cdr (assoc (match-string 1) outline-heading-alist)) + (- (match-end 1) (match-beginning 1)))) + +(defun terraform--setup-outline-mode () + (set (make-local-variable 'outline-level) #'terraform--outline-level) + + (let ((terraform-keywords + (list "terraform" "locals" "required_providers" "atlas" "connection" + "backend" "provider" "provisioner" + "variable" "module" "output" + "data" "resource"))) + (set (make-local-variable 'outline-regexp) + (concat + "^" + (regexp-opt terraform-keywords 'symbols) + "[[:blank:]].*{[[:blank:]]*$")) + (set (make-local-variable 'outline-heading-alist) + (mapcar + (lambda (item) (cons item 2)) + terraform-keywords)))) + +(defun terraform-toggle-or-indent (&optional arg) + "Toggle visibility of block under point or indent. + +If the point is not at the heading, call +`indent-for-tab-command'." + (interactive) + (if (and outline-minor-mode (outline-on-heading-p)) + (outline-toggle-children) + (indent-for-tab-command arg))) + +(defvar terraform-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c C-d C-w") #'terraform-open-doc) + (define-key map (kbd "C-c C-d C-c") #'terraform-kill-doc-url) + (define-key map (kbd "C-c C-d C-r") #'terraform-insert-doc-in-comment) + (define-key map (kbd "C-c C-f") #'outline-toggle-children) + map)) + +;;;###autoload +(define-derived-mode terraform-mode hcl-mode "Terraform" + "Major mode for editing terraform configuration file" + + (setq font-lock-defaults '((terraform-font-lock-keywords))) + (when terraform-format-on-save + (terraform-format-on-save-mode 1)) + + ;; indentation + (make-local-variable 'terraform-indent-level) + (setq hcl-indent-level terraform-indent-level) + + ;; outline + (terraform--setup-outline-mode) + + ;; imenu + (set (make-local-variable 'imenu-sort-function) 'imenu--sort-by-name) + (setq imenu-create-index-function 'terraform--generate-imenu) + (imenu-add-to-menubar "Terraform")) + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.t\\(f\\(vars\\)?\\|ofu\\)\\'" . terraform-mode)) + +(provide 'terraform-mode) + +;;; terraform-mode.el ends here