branch: externals/bicep-ts-mode
commit 5a1029df3ea66633d2dc1dca3087792fcde8b3b4
Author: Jostein Kjønigsen <jost...@kjonigsen.net>
Commit: Jostein Kjønigsen <jost...@kjonigsen.net>

    Don't depend on VSCode to install language-server.
    
    We can install it ourselves :)
---
 README.md        |  9 ++++++---
 bicep-ts-mode.el | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 61 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index 7ea95eb7c4..57d1aa5cbf 100644
--- a/README.md
+++ b/README.md
@@ -11,9 +11,9 @@ Emacs](https://www.gnu.org/software/emacs/), powered by the
 - Syntax highlighting
 - Indentation
 - Imenu-support, when LSP not configured
-- Automatic eglot-configuration, if Bicep Langserver found with
-  configurable default search-location (by default it looks for
-  location installed by VSCode Bicep-extension)
+- Automatic LSP langserver installation and eglot-configuration (`M-x
+  bicep-install-langserver`) with configurable search-location when
+  not using default install.
 
 ## Prerequisites
 
@@ -45,6 +45,9 @@ package-manager, so you will have to install both manually.
         :rev :newest))
 ```
 
+4. Use `M-x bicep-install-langserver` to install or update to latest
+   langserver version.
+
 If you have any issues or corrections, feel free to provide a PR to
 help others :)
 
diff --git a/bicep-ts-mode.el b/bicep-ts-mode.el
index 906a9b336f..f5b8beadb1 100644
--- a/bicep-ts-mode.el
+++ b/bicep-ts-mode.el
@@ -30,6 +30,8 @@
 ;;; Code:
 
 (require 'treesit)
+(require 'json)
+(require 'url)
 
 (declare-function treesit-parser-create "treesit.c")
 (declare-function treesit-induce-sparse-tree "treesit.c")
@@ -47,7 +49,8 @@
   :type 'natnum
   :safe #'natnump)
 
-(defcustom bicep-ts-mode-default-langserver-path 
"$HOME/.vscode/extensions/ms-azuretools.vscode-bicep-*/bicepLanguageServer/Bicep.LangServer.dll"
+(defcustom bicep-ts-mode-default-langserver-path
+  (expand-file-name ".cache/bicep/Bicep.LangServer.dll" user-emacs-directory)
   ;; FIXME: Document the ability to use $ENV vars and glob patterns?
   "Default expression used to locate Bicep Languageserver.
 If found, added to eglot."
@@ -177,6 +180,56 @@ If found, added to eglot."
    '((ERROR) @font-lock-warning-face))
   "Font-lock settings for BICEP.")
 
+(defun bicep--fetch-json-array (url)
+  "Fetch JSON from URL, extract first array element, and return PROPERTY."
+  (with-current-buffer (url-retrieve-synchronously url t)
+    (goto-char (point-min))
+    (re-search-forward "\n\n")  ;; Skip headers
+    (let* ((json-array (json-parse-buffer :array-type 'list)))
+      json-array)))
+
+(defun bicep--unzip-file (zip-file destination)
+  "Unzip ZIP-FILE into DESTINATION directory using the 'unzip' shell command."
+  (unless (file-directory-p destination)
+    (make-directory destination :parents))  ;; Ensure the destination 
directory exists
+  (let ((exit-code (call-process "unzip" nil nil nil "-o" zip-file "-d" 
destination)))
+    (if (zerop exit-code)
+        (message "Successfully unzipped %s to %s" zip-file destination)
+      (error "Failed to unzip %s (exit code %d)" zip-file exit-code))))
+
+(defun bicep--get-latest-release-version ()
+  (let* ((release-json (bicep--fetch-json-array 
"https://api.github.com/repos/Azure/bicep/releases";))
+         (first        (car release-json)) ;; assume first = latest
+         (version      (gethash "tag_name" first)))
+    version))
+
+(defun bicep--download-langserver ()
+  (let* ((bicep-dir (expand-file-name ".cache/bicep" user-emacs-directory))
+         (download-dir  (expand-file-name "dl" bicep-dir))
+         (download-file (expand-file-name "bicep-langserver.zip" 
download-dir)))
+    (make-directory bicep-dir :parents)
+    (make-directory download-dir :parents)
+    (let* ((version     (bicep--get-latest-release-version))
+           (url         (format 
"https://github.com/Azure/bicep/releases/download/%s/bicep-langserver.zip"; 
version)))
+      (url-copy-file url download-file 't)
+      (bicep--unzip-file download-file bicep-dir)
+      (delete-directory download-dir t)
+      ;; make our function respond with something more interesting than nil :)
+      (message (format "Bicep LangServer version %s downloaded and unpacked to 
\'%s\'" version bicep-dir)))))
+
+(defun bicep-install-langserver ()
+  "Downloads the lang-server and unpacks it in the default location."
+  (interactive)
+  (bicep--download-langserver)
+  (bicep--register-langserver))
+
+(defun bicep--register-langserver ()
+  (defvar eglot-server-programs)
+  (and (file-exists-p (bicep-langserver-path))
+       (add-to-list 'eglot-server-programs
+                    `(bicep-ts-mode . ("dotnet" ,(bicep-langserver-path))))))
+
+
 (defun bicep-langserver-path ()
   ;; Note: In GNU land, we call this a file name, not a path.
   (car (file-expand-wildcards
@@ -262,10 +315,7 @@ Return the first matching node, or nil if none is found."
 
 ;;;###autoload
 (with-eval-after-load 'eglot
-  (defvar eglot-server-programs)
-  (and (file-exists-p (bicep-langserver-path))
-       (add-to-list 'eglot-server-programs
-                    `(bicep-ts-mode . ("dotnet" ,(bicep-langserver-path))))))
+  (bicep--register-langserver))
 
 (provide 'bicep-ts-mode)
 

Reply via email to