+++ /dev/null
-;;; 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)))
-
-
-\f
-;;; 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-"))
-\f
-;;; 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
+++ /dev/null
-;; -*- 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" . "syohexNOSPAMgmail.com"))
- :maintainers '(("Syohei YOSHIDA" . "syohexNOSPAMgmail.com")))
+++ /dev/null
-;;; terraform-mode.el --- Major mode for terraform configuration file -*- lexical-binding: t -*-
-
-;; Copyright (C) 2017 by Syohei YOSHIDA
-
-;; Author: Syohei YOSHIDA <syohexNOSPAMgmail.com>
-;; 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 <http://www.gnu.org/licenses/>.
-
-;;; 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
--- /dev/null
+;;; 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)))
+
+
+\f
+;;; 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-"))
+\f
+;;; 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
--- /dev/null
+;; -*- 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" . "syohexNOSPAMgmail.com"))
+ :maintainers '(("Syohei YOSHIDA" . "syohexNOSPAMgmail.com")))
--- /dev/null
+;;; terraform-mode.el --- Major mode for terraform configuration file -*- lexical-binding: t -*-
+
+;; Copyright (C) 2017 by Syohei YOSHIDA
+
+;; Author: Syohei YOSHIDA <syohexNOSPAMgmail.com>
+;; 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 <http://www.gnu.org/licenses/>.
+
+;;; 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