From 0fa8631eca53ab5dfee5e37af4d65ab4a2567b65 Mon Sep 17 00:00:00 2001 From: Manas Jayanth <3097018+ManasJayanth@users.noreply.github.com> Date: Fri, 10 Feb 2023 11:32:20 +0530 Subject: [PATCH 1/7] Temporarily comment old transient menu macros --- esy-mode.el | 75 +++++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/esy-mode.el b/esy-mode.el index fc28f1e..30bd34c 100644 --- a/esy-mode.el +++ b/esy-mode.el @@ -20,6 +20,7 @@ ;;; Code: (require 'json) +(require 'transient) ;; Customization (defgroup esy nil @@ -424,31 +425,31 @@ package.json or not" (interactive) (run-esy (list "npm-release") (lambda () (message "[esy] NPM release done")))) -(define-transient-command esy-install () - "Open esy install transient menu pop up." - ["Arguments" - ("-p" "Package name providing the ocaml compiler" "--ocaml-pkg-name=") - ("-v" "OCaml compiler version" "--ocaml-version=") - ("-rl" "Local path to opam repository" "--opam-repository-local=") - ("-rr" "HTTP url to remote opam repository" "--opam-repository-remote=") - ("-ol" "Local path to opam override repository. For more info, see (TODO document this at esy.sh)" "--opam-override-repository-local=") - ("-or" "HTTP url to remote opam override repository. For more info, see (TODO document this at esy.sh)" "--opam-override-repository-remote=") - ] - [["Command" - ("i" "Install" esy/cmd-install)]] - (interactive) - (transient-setup 'esy/cmd-install)) - -(define-transient-command esy-build () - "Open esy build transient menu pop up." - ["Arguments" - ("-p" "Package name providing the ocaml compiler" "--ocaml-pkg-name") - ("-v" " OCaml compiler version" "--ocaml-version") - ] - [["Command" - ("b" "Build" esy-build)]] - (interactive) - (transient-setup 'esy/cmd-build)) +;; (define-transient-command esy-install () +;; "Open esy install transient menu pop up." +;; ["Arguments" +;; ("-p" "Package name providing the ocaml compiler" "--ocaml-pkg-name=") +;; ("-v" "OCaml compiler version" "--ocaml-version=") +;; ("-rl" "Local path to opam repository" "--opam-repository-local=") +;; ("-rr" "HTTP url to remote opam repository" "--opam-repository-remote=") +;; ("-ol" "Local path to opam override repository. For more info, see (TODO document this at esy.sh)" "--opam-override-repository-local=") +;; ("-or" "HTTP url to remote opam override repository. For more info, see (TODO document this at esy.sh)" "--opam-override-repository-remote=") +;; ] +;; [["Command" +;; ("i" "Install" esy/cmd-install)]] +;; (interactive) +;; (transient-setup 'esy/cmd-install)) + +;; (define-transient-command esy-build () +;; "Open esy build transient menu pop up." +;; ["Arguments" +;; ("-p" "Package name providing the ocaml compiler" "--ocaml-pkg-name") +;; ("-v" " OCaml compiler version" "--ocaml-version") +;; ] +;; [["Command" +;; ("b" "Build" esy-build)]] +;; (interactive) +;; (transient-setup 'esy/cmd-build)) (defun esy-test () @@ -462,18 +463,18 @@ package.json or not" (run-esy (list command) (lambda () (message "[esy] done")))) ;; Entrypoint menu -(define-transient-command esy-menu () - "Open esy transient menu pop up." - [["Command" - ("e" "Build and install" esy-build-and-install) - ("b" "Build" esy-build) - ("i" "Install" esy-install) - ("r" "Run Script" esy-run-script) - ("n" "Run npm-release" esy-npm-release) - ("t" "Test" esy-test) - ]] - (interactive) - (transient-setup 'esy-menu)) +;; (define-transient-command esy-menu () +;; "Open esy transient menu pop up." +;; [["Command" +;; ("e" "Build and install" esy-build-and-install) +;; ("b" "Build" esy-build) +;; ("i" "Install" esy-install) +;; ("r" "Run Script" esy-run-script) +;; ("n" "Run npm-release" esy-npm-release) +;; ("t" "Test" esy-test) +;; ]] +;; (interactive) +;; (transient-setup 'esy-menu)) (defun esy () "Entrypoint function to the esy-mode interactive functions From e77e8e14e19fc82d722974d62fe21f6ea332c2a9 Mon Sep 17 00:00:00 2001 From: Manas Jayanth <3097018+ManasJayanth@users.noreply.github.com> Date: Fri, 17 Mar 2023 09:52:56 +0530 Subject: [PATCH 2/7] Major internal refactoring --- esy-mode.el | 220 ++++++++++++++++++++++++++++++++++++--------------- tests/esy.el | 39 +++++---- 2 files changed, 175 insertions(+), 84 deletions(-) diff --git a/esy-mode.el b/esy-mode.el index 30bd34c..27d802d 100644 --- a/esy-mode.el +++ b/esy-mode.el @@ -16,6 +16,12 @@ ;; "esy exec-command refmt" only if it is present (in an OCaml only ;; project it need not be available). + +;; TIP: To test individual defuns, consider the following example; +;; (esy/process-env-to-exec-path +;; (esy/opam--process-environment-of-project +;; (esy/project--of-path "/Users/manas/development/ligolang/ligo"))) + ;;; Change Log: TODO ;;; Code: @@ -48,41 +54,99 @@ Common use case is to enable ask lsp client to connect to the server "Write to file" (with-temp-file fname (insert data))) -(defun esy/internal--project--get-manifest-file-path - (esy-status-json) +(defun esy/internal--persist-obj (obj file-path) + "Persists object to file" + (esy/f--write file-path (prin1-to-string obj))) + +(defun esy/internal--read-obj (file-path) + "Reads object from file" + (car (read-from-string (esy/f--read file-path)))) + +(defun esy/project--persist (project) + "Persist project indexed by path" + (let* ((project-db-name "esy-projects.db") + (project-db-path (concat "~/.emacs.d/" project-db-name)) + (db (condition-case + err + (esy/internal--read-obj project-db-path) + (error (make-hash-table)))) + (project-path (esy/project--get-path project))) + (puthash project-path project db) + (esy/internal--persist-obj db project-db-path))) + +(defun esy/project--read-db (project-path) + "Load a project" + (let* ((project-db-name "esy-projects.db") + (project-db-path (concat "~/.emacs.d/" project-db-name)) + (db (condition-case + err + (esy/internal--read-obj project-db-path) + (message "not nilllllll") + (error (princ (format "The error was: %s" err)) (make-hash-table))))) + (gethash project-path db))) + +(defun esy/internal-status--get-manifest-file-path (esy-status) "Given the json object of 'esy status' output, it returns the manifest file" - (gethash "rootPackageConfigPath" esy-status-json)) - -(defun esy/project--of-path (project-path) - "Returns an abstract structure that can later -be used to obtain more info about the project" - (let* ((default-directory project-path) + (gethash "rootPackageConfigPath" esy-status)) + +(defun esy/internal--cwd-of-buffer (buffer) + "Given buffer, finds tries to find the cwd of the file attached to the buffer. +Returns nil, if it fails" + (let* ((file-name (buffer-file-name buffer))) + (if file-name (file-name-directory file-name) nil))) + +(defun esy/internal--cwd-of-buffer-or-default (buffer) + "Same as esy/internal--cwd-of-buffer, but returns default-directory if cwd of attached +buffer could not be found" + (let ((cwd (esy/internal--cwd-of-buffer buffer))) + (if cwd cwd default-directory))) + +(defun esy/internal--esy-status (cwd) + "Given a working directory path (default or a buffer's file directory), returns project root" + (let* ((default-directory cwd) (json-str (shell-command-to-string (concat esy-command " status"))) (json-array-type 'list) (json-key-type 'string) (json-false 'nil) - (json-object-type 'hash-table) - (esy-status-json - (condition-case nil - (json-read-from-string json-str) - (error (progn - (message (format "Error while json parsing \ + (json-object-type 'hash-table)) + (condition-case nil + (json-read-from-string json-str) + (error (progn + (message (format "Error while json parsing \ 'esy status' -> %s" json-str)) - (make-hash-table)))))) + (make-hash-table)))))) + +(defun esy/internal--esy-status-of-buffer (buffer) + "Returns 'esy status' output for a project associated with the given buffer" + (let* ((cwd (esy/internal--cwd-of-buffer-or-default buffer))) + (esy/internal--esy-status cwd))) + +(defun esy/project--of-path (project-path) + "Returns an abstract structure that can later +be used to obtain more info about the project" + (let* ((esy-status-json (esy/internal--esy-status project-path)) + (manifest-path + (esy/internal-status--get-manifest-file-path esy-status-json)) + (project-path (if manifest-path (file-name-directory manifest-path) + (read-file-name "Couldn't detect project root. Enter project root (where opam or esy manifests are present): " (file-name-as-directory default-directory))))) (list 'json esy-status-json - 'path (let* ((manifest-path (esy/internal--project--get-manifest-file-path esy-status-json))) - (if manifest-path - (file-name-directory manifest-path) - (read-file-name "Couldn't detect project root. Enter project root (where opam or esy manifests are present): " (file-name-as-directory default-directory))))))) + 'usable 'not-solved ;; | 'solved-not-fetched | 'fetched-not-built | 'built-and-ready + 'path project-path + 'type (esy/internal-package-manager--of-project manifest-path)))) +;; Getters and setters for type project (defun esy/project--get-path (project) "Returns the root of the project" (plist-get project 'path)) +(defun esy/project--get-type (project) + "Returns type (npm|opam|esy) of project" + (plist-get project 'type)) + (defun esy/project--get-manifest-file-path (project) "Returns the path to manifest file" - (esy/internal--project--get-manifest-file-path + (esy/internal-status--get-manifest-file-path (plist-get project 'json))) (defun esy/project--of-file-path (file-path) @@ -94,12 +158,15 @@ later be used to obtain more info about the esy project" (file-directory-p parent-path)) (make-directory parent-path t) (message (format "esy-mode just created %s for you. If this is annoying, please raise a ticket." parent-path))) - (esy/project--of-path parent-path)))) + (esy/project--of-cwd parent-path)))) (defun esy/project--of-buffer (buffer) "Returns an abstract structure that can later be used to obtain more info about the esy project" - (let* ((file-name (buffer-file-name buffer))) (if file-name (esy/project--of-file-path file-name) (esy/project--of-path default-directory)))) + (let* ((file-name (buffer-file-name buffer))) + (if file-name + (esy/project--of-file-path file-name) + (esy/project--of-path default-directory)))) (defun esy/project--fetched-p (project) "Returns if a given project's sources have been solved and fetched. This @@ -139,6 +206,28 @@ command-env" (json-read-from-string json-str))) (list 'command-env esy-command-env-json))) +(defun esy/opam--process-environment-of-project (project) + "Given a project, it returns an abstract structure +representing opam env" + (let* + ((project-path (esy/project--get-path project)) + (default-directory project-path) + ;; We use opam exec -- env and not opam env, + ;; because, opam env returns values that are meant + ;; to be executed by a shell like bash + ;; Ex: OPAM_SWITCH_PREFIX='/Users//.opam/default'; export OPAM_SWITCH_PREFIX; + ;; We just need key, value pairs. + (env-str + (condition-case + err + (shell-command-to-string + "opam exec -- env") + (error (progn + (debug err) + (message "Error while running 'opam exec -- env' %s" (error-message-string err)) + "{}"))))) + (split-string env-str "\n"))) + (defun esy/command-env--to-process-environment (command-env) "Given a command-env, it turns it into a list that can be assigned to 'process-environment" @@ -151,22 +240,23 @@ that can be assigned to 'process-environment" command-env-json) penv))) +(defun esy/process-env-to-exec-path (penv) + "Given a list of environment variables (ex: '(\"PATH=/foo/bar\" \"LDFLAGS=some_values\")'), +gets just exec-path" + (let* ((path-env-str-list + (seq-filter (lambda (s) (string-match "^path=" s)) penv)) + (path-env-str-key-value (car path-env-str-list)) + (path-env-str (nth 1 (split-string path-env-str-key-value "=")))) + (split-string path-env-str + (if (string= system-type "windows-nt") ";" ":")))) + (defun esy/command-env--get-exec-path (command-env) "Given a command-env, it turns it into a list that can be assigned to 'exec-path" (let* ((penv (esy/command-env--to-process-environment - command-env)) - (exec-path-list '()) - (path-env-str-list (seq-filter - (lambda (s) (string-match "^path$" s)) - penv))) - (setq exec-path-list - (split-string - (gethash "PATH" (nth 1 command-env)) - (if (string= - system-type - "windows-nt") ";" ":"))))) + command-env))) + (setq exec-path-list (esy/process-env-to-exec-path penv)))) (defun esy/setup--esy-get-available-tools (project) @@ -214,9 +304,11 @@ for development" nil)) (defun esy/setup--opam (project callback) - "setup--opam(_): currently doesn't do anything. opam-user-setup works well enough, IMO!" (message "Detected an opam project. Experimental support.") - (esy/setup--esy project callback)) + (setq process-environment + (esy/opam--process-environment-of-project project)) + (setq exec-path (esy/process-env-to-exec-path process-environment))) + (defun esy/setup--npm(project callback) @@ -279,32 +371,33 @@ package.json or not" "Checks if a manifest structure contains esy field" (if manifest (gethash "esy" manifest) nil)) -(defun esy/package-manager--of-project (project) +(defun esy/internal-package-manager--of-project (manifest-file-path) "Detect the package manager of the project. Returns either -'esy|'opam|'npm" - (let* ((manifest-file-path - (esy/project--get-manifest-file-path project))) - (if (esy/manifest--json-p manifest-file-path) - ;; The manifest file is a json. - (if (esy/manifest--package-json-p - manifest-file-path) - ;; Could be npm or esy - (if (esy/project--ready-p project) - ;; esy says this project with package.json - ;; is ready for development i.e. all it's - ;; dependencies were fetched and installed - ;; by esy. Definitely an esy project - 'esy - (progn - ;; Checking if there is an esy field in - ;; the package.json. If there is one, - ;; it's an esy project - (if (esy/manifest--contains-esy-field-p - (esy/manifest--of-path manifest-file-path)) - 'esy - 'npm))) - 'esy) - 'opam))) +'esy|'opam|'npm. Note, manifest-file-path is expected to be either an opam file or json. +This assumes that this value comes from `esy status`'s output" + (if (esy/manifest--json-p manifest-file-path) + ;; The manifest file is a json. + (if (esy/manifest--package-json-p + manifest-file-path) + ;; Could be npm or esy + ;; Checking if there is an esy field in + ;; the package.json. If there is one, + ;; it's an esy project + (if (esy/manifest--contains-esy-field-p + (esy/manifest--of-path manifest-file-path)) + 'esy + 'npm) + ;; Previously, we believed the following, + ;; > esy says this project with package.json + ;; > is ready for development i.e. all it's + ;; > dependencies were fetched and installed + ;; > by esy. Definitely an esy project + ;; Should we reconsider this? + (if (esy/manifest--contains-esy-field-p + (esy/manifest--of-path manifest-file-path)) + 'esy + nil)) + 'opam)) (defun esy-mode-init () @@ -323,7 +416,7 @@ package.json or not" (let* ((project (if file-path (esy/project--of-file-path file-path) (esy/project--of-buffer (current-buffer))))) - (esy/package-manager--of-project project))) + (esy/project--get-type project))) (defun run-cmd-legacy (buffer-name cmd-and-args &optional callback) "Run the cmd" @@ -528,7 +621,8 @@ it returns if the project is ready for development" (if esy-mode (progn (if (esy-mode-init) - (let* ((project (esy/project--of-buffer (current-buffer)))) + (let* ((project (esy/project--cached-of-buffer (current-buffer)))) + (esy/project--persist project) (if (esy/project--p project) (progn @@ -541,7 +635,7 @@ it returns if the project is ready for development" ;; place? `esy ocamlmerlin-lsp` needs projects to ;; install/solve deps - (let* ((project-type (esy/package-manager--of-project project))) + (let* ((project-type (esy/project--get-type project))) (cond ((eq project-type 'opam) (esy/setup--opam project diff --git a/tests/esy.el b/tests/esy.el index 8d0bfc3..0a5f0da 100644 --- a/tests/esy.el +++ b/tests/esy.el @@ -43,7 +43,7 @@ (make-directory test-esy-project-dir) (esy-test-utils/f--write test-esy-project-manifest - "{ \"dependencies\": {} }") + "{ \"esy\": {}, \"dependencies\": {} }") test-esy-project-dir))) (defun esy-test-utils/fixture--create-npm (tmp-dir) @@ -77,9 +77,11 @@ (make-directory test-esy-project-dir) (esy-test-utils/f--write test-esy-project-manifest - "name: \"foo\"") + "name: \"foo\"\nopam-version: \"2.0\"") test-esy-project-dir))) +(defun add-two (n) (+ n 2)) + (ert-deftest test-add-two () @@ -95,38 +97,35 @@ (should (not (esy/manifest--json-p "/foo/foo.opam")))) (ert-deftest - test-esy/package-manager--of-project-when-esy + test-esy/internal-package-manager--of-project-when-esy () - "package-manager--of-project must return correct project type" + "internal-package-manager--of-project must return correct project type" (ert/test-suite :setup (lambda (tmp-dir) (esy-test-utils/fixture--create tmp-dir)) :body (lambda (test-project-path) - (let ((test-project (esy/project--of-path test-project-path))) (should (eq - (esy/package-manager--of-project test-project) - 'esy)))) + (esy/internal-package-manager--of-project (concat test-project-path "/esy.json")) + 'esy))) :teardown (lambda (x) (delete-directory x t)))) (ert-deftest - test-esy/package-manager--of-project-when-opam + test-esy/internal-package-manager--of-project-when-opam () - "package-manager--of-project must properly detect an opam project + "internal-package-manager--of-project must properly detect an opam project with an opam file" (ert/test-suite :setup (lambda (tmp-dir) (esy-test-utils/fixture--create-opam tmp-dir)) :body (lambda (test-project-path) - (let ((test-project - (esy/project--of-path test-project-path))) (should (eq - (esy/package-manager--of-project test-project) - 'opam)))) + (esy/internal-package-manager--of-project (concat test-project-path "/foo.opam")) + 'opam))) :teardown (lambda (x) (delete-directory x t)))) (ert-deftest - test-esy/package-manager--of-project-when-npm + test-esy/internal-package-manager--of-project-when-npm () - "package-manager--of-project must properly detect an npm + "internal-package-manager--of-project must properly detect an npm project with a package.json (but no esy field in it)" (ert/test-suite :setup (lambda @@ -134,12 +133,10 @@ project with a package.json (but no esy field in it)" (esy-test-utils/fixture--create-npm tmp-dir)) :body (lambda (test-project-path) - (let ((test-project - (esy/project--of-path test-project-path))) - (should (eq - (esy/package-manager--of-project - test-project) - 'npm)))) + (should (eq + (esy/internal-package-manager--of-project + (concat test-project-path "/package.json")) + 'npm))) :teardown (lambda (x) (delete-directory x t)))) From e6c92c81f5f92d1d769fdca610dd639c78ea1bc1 Mon Sep 17 00:00:00 2001 From: Manas Jayanth <3097018+ManasJayanth@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:03:11 +0530 Subject: [PATCH 3/7] missing project--of-cwd --- esy-mode.el | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/esy-mode.el b/esy-mode.el index 27d802d..008edc4 100644 --- a/esy-mode.el +++ b/esy-mode.el @@ -135,6 +135,11 @@ be used to obtain more info about the project" 'path project-path 'type (esy/internal-package-manager--of-project manifest-path)))) +(defun esy/project--of-cwd (project-path) + "Alias for esy/project--of-path" + (esy/project--of-path project-path)) + + ;; Getters and setters for type project (defun esy/project--get-path (project) "Returns the root of the project" @@ -621,7 +626,7 @@ it returns if the project is ready for development" (if esy-mode (progn (if (esy-mode-init) - (let* ((project (esy/project--cached-of-buffer (current-buffer)))) + (let* ((project (esy/project--of-buffer (current-buffer)))) (esy/project--persist project) (if (esy/project--p project) (progn From a62264e71bcf5d611d86107bfa158f8746716d64 Mon Sep 17 00:00:00 2001 From: Manas Jayanth <3097018+ManasJayanth@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:40:39 +0530 Subject: [PATCH 4/7] Fixes hash-table read writes --- esy-mode.el | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/esy-mode.el b/esy-mode.el index 008edc4..0f4cd32 100644 --- a/esy-mode.el +++ b/esy-mode.el @@ -44,6 +44,9 @@ Common use case is to enable ask lsp client to connect to the server (since this can only be done after the esy project is ready)") +(defun esy--make-hash-table () + (make-hash-table :test 'equal)) + (defun esy/f--read (file-path) "Return file content." (with-temp-buffer @@ -60,7 +63,7 @@ Common use case is to enable ask lsp client to connect to the server (defun esy/internal--read-obj (file-path) "Reads object from file" - (car (read-from-string (esy/f--read file-path)))) + (eval (car (read-from-string (esy/f--read file-path))))) (defun esy/project--persist (project) "Persist project indexed by path" @@ -69,7 +72,7 @@ Common use case is to enable ask lsp client to connect to the server (db (condition-case err (esy/internal--read-obj project-db-path) - (error (make-hash-table)))) + (error (esy--make-hash-table)))) (project-path (esy/project--get-path project))) (puthash project-path project db) (esy/internal--persist-obj db project-db-path))) @@ -82,7 +85,7 @@ Common use case is to enable ask lsp client to connect to the server err (esy/internal--read-obj project-db-path) (message "not nilllllll") - (error (princ (format "The error was: %s" err)) (make-hash-table))))) + (error (princ (format "The error was: %s" err)) (esy--make-hash-table))))) (gethash project-path db))) (defun esy/internal-status--get-manifest-file-path (esy-status) @@ -115,7 +118,7 @@ buffer could not be found" (error (progn (message (format "Error while json parsing \ 'esy status' -> %s" json-str)) - (make-hash-table)))))) + (esy--make-hash-table)))))) (defun esy/internal--esy-status-of-buffer (buffer) "Returns 'esy status' output for a project associated with the given buffer" From 6fc0ee79a0213072959e2cd998f1da25f489d4d2 Mon Sep 17 00:00:00 2001 From: Manas Jayanth <3097018+ManasJayanth@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:54:16 +0530 Subject: [PATCH 5/7] Adds persistence to project detection --- esy-mode.el | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/esy-mode.el b/esy-mode.el index 0f4cd32..b394923 100644 --- a/esy-mode.el +++ b/esy-mode.el @@ -84,7 +84,6 @@ Common use case is to enable ask lsp client to connect to the server (db (condition-case err (esy/internal--read-obj project-db-path) - (message "not nilllllll") (error (princ (format "The error was: %s" err)) (esy--make-hash-table))))) (gethash project-path db))) @@ -93,12 +92,22 @@ Common use case is to enable ask lsp client to connect to the server it returns the manifest file" (gethash "rootPackageConfigPath" esy-status)) +(defun esy/internal-status--get-project-root (esy-status) + "Given the json object of 'esy status' output, +it returns the manifest file" + (file-name-directory (esy/internal-status--get-manifest-file-path esy-status))) + (defun esy/internal--cwd-of-buffer (buffer) "Given buffer, finds tries to find the cwd of the file attached to the buffer. Returns nil, if it fails" (let* ((file-name (buffer-file-name buffer))) (if file-name (file-name-directory file-name) nil))) +(defun esy/internal--root-of-cwd (cwd) + "Given current working directory, get's project root using 'esy status' command" + (let* ((esy-status (esy/internal--esy-status cwd))) + (esy/internal-status--get-project-root esy-status))) + (defun esy/internal--cwd-of-buffer-or-default (buffer) "Same as esy/internal--cwd-of-buffer, but returns default-directory if cwd of attached buffer could not be found" @@ -176,6 +185,12 @@ later be used to obtain more info about the esy project" (esy/project--of-file-path file-name) (esy/project--of-path default-directory)))) +(defun esy/cached-project--of-buffer (buffer) + "Looks up the project db first, then call esy/project--of-buffer if necessary" + (let* ((project-root (esy/internal--root-of-cwd (esy/internal--cwd-of-buffer buffer))) + (cached-project (esy/project--read-db project-root))) + (if cached-project cached-project (esy/project--of-buffer buffer)))) + (defun esy/project--fetched-p (project) "Returns if a given project's sources have been solved and fetched. This is necessary for commands like 'esy command-env', 'esy build-plan' etc to work." @@ -629,7 +644,7 @@ it returns if the project is ready for development" (if esy-mode (progn (if (esy-mode-init) - (let* ((project (esy/project--of-buffer (current-buffer)))) + (let* ((project (esy/cached-project--of-buffer (current-buffer)))) (esy/project--persist project) (if (esy/project--p project) (progn From ce0e95806342cf76131d6e97aadb420e233a0aa4 Mon Sep 17 00:00:00 2001 From: Manas Jayanth <3097018+ManasJayanth@users.noreply.github.com> Date: Wed, 5 Apr 2023 11:00:14 +0530 Subject: [PATCH 6/7] Fixes order of args for append --- esy-mode.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esy-mode.el b/esy-mode.el index b394923..f7bed0f 100644 --- a/esy-mode.el +++ b/esy-mode.el @@ -602,7 +602,7 @@ First checks if file backing the current buffer is a part of an esy project, the (defun run-esy (args callback) "Runs esy command in *esy* buffer" - (let ((command (if args (append args esy-command) (list esy-command)))) + (let ((command (if args (push esy-command args) (list esy-command)))) (run-cmd "*esy*" command From 115445bb44433eff87610173da25f9a45fd2c00b Mon Sep 17 00:00:00 2001 From: Manas Jayanth <3097018+ManasJayanth@users.noreply.github.com> Date: Fri, 14 Apr 2023 07:41:45 +0530 Subject: [PATCH 7/7] WIP: extending comint for esy-build-shell --- esy-mode.el | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/esy-mode.el b/esy-mode.el index f7bed0f..748880b 100644 --- a/esy-mode.el +++ b/esy-mode.el @@ -637,6 +637,79 @@ it returns if the project is ready for development" (interactive) (run-esy (list "build-dependencies") (lambda () (message "[esy] Finished")))) +(defvar esy-build-shell-file-path "esy" + "Path to the program used by `esy-build-shell-run'") + +(defvar esy-build-shell-arguments '("build-shell") + "Commandline arguments to pass to `esy-build-shell'.") + +(defvar esy-build-shell-mode-map + (let ((map (nconc (make-sparse-keymap) comint-mode-map))) + ;; example definition + (define-key map "\t" 'completion-at-point) + map) + "Basic mode map for `esy-build-shell'.") + +(defvar esy-build-shell-prompt-regexp "\\[build [^\]]+\\] %" ;; "^\\(?:\\[build [\\]]+\\]\\)" + "Prompt for `esy-build-shell'.") + +(defvar esy-build-shell-buffer-name "*Esy Build Shell*" + "Name of the buffer to use for the `esy-build-shell' comint instance.") + +(defun esy-build-shell-run () + "Run an inferior instance of `esy-build-shell' inside Emacs." + (interactive) + (let* ((esy-build-shell-program esy-build-shell-file-path) + (buffer (get-buffer-create esy-build-shell-buffer-name)) + (proc-alive (comint-check-proc buffer)) + (process (get-buffer-process buffer)) + (package-name (thing-at-point 'symbol t))) + (setq esy-build-shell-arguments (if package-name (list "build-shell" "-p" package-name) esy-build-shell-arguments)) + ;; if the process is dead then re-create the process and reset the + ;; mode. + (unless proc-alive + (with-current-buffer buffer + (make-local-variable 'default-directory) + (setq default-directory (esy/internal--cwd-of-buffer-or-default buffer)) + (apply 'make-comint-in-buffer "Esy Build Shell" buffer + esy-build-shell-program nil esy-build-shell-arguments) + (esy-build-shell-mode))) + ;; Regardless, provided we have a valid buffer, we pop to it. + (when buffer + (pop-to-buffer buffer)))) + +(defun esy-build-shell--initialize () + "Helper function to initialize esy-build-shell." + (setq comint-process-echoes t) + (setq comint-use-prompt-regexp t)) + + +(define-derived-mode esy-build-shell-mode comint-mode "Esy Build Shell" + "Major mode for `esy-build-shell'. + +\\" + ;; this sets up the prompt so it matches things like: [foo@bar] + (setq comint-prompt-regexp esy-build-shell-prompt-regexp) + ;; this makes it read only; a contentious subject as some prefer the + ;; buffer to be overwritable. + (setq comint-prompt-read-only t) + ;; this makes it so commands like M-{ and M-} work. + (set (make-local-variable 'paragraph-separate) "\\'") + (set (make-local-variable 'font-lock-defaults) '(esy-build-shell-font-lock-keywords t)) + (set (make-local-variable 'paragraph-start) esy-build-shell-prompt-regexp)) + +(add-hook 'esy-build-shell-mode-hook 'esy-build-shell--initialize) + +(defconst esy-build-shell-keywords + '() + "List of keywords to highlight in `esy-build-shell-font-lock-keywords'.") + +(defvar esy-build-shell-font-lock-keywords + (list + ;; highlight all the reserved commands. + `(,(concat "\\_<" (regexp-opt esy-build-shell-keywords) "\\_>") . font-lock-keyword-face)) + "Additional expressions to highlight in `esy-build-shell-mode'.") + ;;;###autoload (define-minor-mode esy-mode "Minor mode for esy - the package manager for Reason/OCaml"