Some hopefully helpful emacs tools for scryer-prolog v0.1.0 #2630
Replies: 3 comments
-
I figured out how to set the paths with an env var, and I wrote some routines that will make use of local modules as well. I also modified them so that it Falling asleep at the keyboard so I'll upload the changes tomorrow. This will be a whole lot easier when we start parsing these with Prolog!! |
Beta Was this translation helpful? Give feedback.
-
Very interesting, thank you a lot for sharing this! Regarding the https://github.com/emacs-helm/helm/blob/c0fd64b5491576853ecad9337996b38743effeee/helm-core.el#L3521 This buffer is filled with candidates to use: https://github.com/emacs-helm/helm/blob/c0fd64b5491576853ecad9337996b38743effeee/helm-core.el#L7112 It has its own keymap so that selecting an entry does meaningful things: https://github.com/emacs-helm/helm/blob/c0fd64b5491576853ecad9337996b38743effeee/helm-core.el#L395 |
Beta Was this translation helpful? Give feedback.
-
Ok updated version. It's slightly more portable, it now uses system env vars instead of hard coded paths. I'm not going to tell you it's the most elegant elisp in the world, certainly open to suggestions. @triska moving off of helm will prob have to wait till after meetup!! Time is very precious right now!! This clip demonstrates importing from menu with fuzzy search. Jumps back to your position in the file when complete. It will seek out an EXACT MATCH for You can import a library, a user module via module name, or user module via fully qualified path depending on the most inefficient number of prefix arguments you care to put it. Likewise you can jump to a scryer library or a custom module depending on the arguments you put in. In the future I'll probably consolidate it a little more, and I'd like to add in some parsing, and to parse for exports so you can import those, as well as cross-referencing, but that's all going to have to wait a bit. (defun scryer-handle-special (scryer-mod)
(cond
((string-equal scryer-mod "toplevel") "'$toplevel'")
((string-equal scryer-mod "machine/project_attributes") "'$project_atts'")
((string-equal scryer-mod "machine/attributed_variables") "'$atts'")
(t scryer-mod)))
(defun extract-module-name-tuple (scryer-src-dir scryer-file)
"Get directory, file path, and module name of scryer-file.
TODO: replaced by asking scryer for this information (read_term/3 etc)
"
(let* ((scryer-lib-path (string-remove-prefix scryer-src-dir scryer-file))
(scryer-lib-path (string-remove-prefix "/" scryer-lib-path))
(scryer-module (string-remove-suffix ".pl" scryer-lib-path))
(scryer-module (scryer-handle-special scryer-module)))
(list scryer-src-dir scryer-lib-path scryer-module)))
(comment
(extract-module-name-tuple "/home/jay/programs/scryer-prolog/src/lib" "/home/jay/programs/scryer-prolog/src/lib/http/http_open.pl")
;;=> ("/home/jay/programs/scryer-prolog/src/lib" "http/http_open.pl" "http/http_open")
)
(defun find-scryer-modules! (scryer-directory)
"Given a directory containing scryer files, recursively find files relative to this directory.
So <PATH>/something.pl -> ((<PATH>, something.pl, something))
<PATH>/foo/bar.pl -> ((<PATH>, foo/bar.pl, foo/bar))
"
(let* ((scryer-directory (expand-file-name scryer-directory))
(scryer-files (directory-files-recursively scryer-directory ".*"))
(scryer-files (seq-filter (lambda (path) (string-match-p ".*.pl$" path))
scryer-files)))
(seq-map (lambda (scryer-file)
(extract-module-name-tuple scryer-directory scryer-file))
scryer-files)))
(defun find-scryer-libraries! (scryer-install-dir library-paths)
;; to be replaced by asking scryer-prolog where its install directory and library directory is
;; see https://docs.python.org/3/library/sys.html#sys.exec_prefix for example
(let* ((scryer-directories (list (expand-file-name scryer-install-dir)))
(scryer-module-tuples (apply #'append (funcall #'seq-map #'find-scryer-modules! scryer-directories )))
(results
(cl-loop for (dir-name path-name mod-name) in scryer-module-tuples
for result =
(cl-loop for library-path in library-paths
if (= 1 (length (file-name-split path-name)))
return (list dir-name path-name mod-name)
if (string-match-p (concat "^" library-path ".*") path-name)
return (list dir-name path-name (string-remove-prefix (concat library-path "/") mod-name)))
if result
collect result)))
(cl-sort results (lambda (a b) (string-lessp (caddr a) (caddr b))))))
(defun find-scryer-reg-modules! ()
(let* ((scryer-path-string (getenv "SCRYER_PATH"))
(scryer-paths (split-string scryer-path-string "[:]" t nil)))
(cl-loop for scryer-path in scryer-paths
for files = (find-scryer-modules! scryer-path)
nconc files)))
(comment
(find-scryer-libraries! "~/programs/scryer-prolog/src" '("lib" "machine"))
;;=>
;; (("/home/jay/programs/scryer-prolog/src" "machine/attributed_variables.pl" "'$atts'")
;; ("/home/jay/programs/scryer-prolog/src" "machine/project_attributes.pl" "'$project_atts'")
;; ("/home/jay/programs/scryer-prolog/src" "toplevel.pl" "'$toplevel'")
;; ("/home/jay/programs/scryer-prolog/src" "lib/arithmetic.pl" "arithmetic")
;; ("/home/jay/programs/scryer-prolog/src" "lib/assoc.pl" "assoc")
;; ("/home/jay/programs/scryer-prolog/src" "lib/atts.pl" "atts")
;; ("/home/jay/programs/scryer-prolog/src" "lib/between.pl" "between")
;; ("/home/jay/programs/scryer-prolog/src" "lib/builtins.pl" "builtins")
;; ("/home/jay/programs/scryer-prolog/src" "lib/charsio.pl" "charsio")
;; ("/home/jay/programs/scryer-prolog/src" "lib/clpb.pl" "clpb")
;; ("/home/jay/programs/scryer-prolog/src" "lib/clpz.pl" "clpz")
;; ("/home/jay/programs/scryer-prolog/src" "lib/cont.pl" "cont")
)
(setenv "SCRYER_PATH" (expand-file-name "~/_project/tai/jlog/src/jlog/agent/bucketdomain/"))
(setenv "SCRYER_HOME" (expand-file-name "~/programs/scryer-prolog/src"))
(defun scryer-prolog-jump-to-source! (&optional arg)
"Choose from menu, jump to source.
C-u or no prefix because I can't figure out how to
dispatch off of one C-u only.
Jump to Scryer Library
C-u C-u or C-u C-u C-u -- Jump to source on SCRYER_PATH
"
(interactive "P")
(let ((module? (or (equal arg '(64))
(eq arg 2)
(equal arg '(16))))
(library? (or (not arg) (equal arg '(4)))))
(if library?
(helm :sources (helm-build-sync-source "Scryer Libraries"
:candidates (seq-map #'reverse (find-scryer-libraries! (expand-file-name (getenv "SCRYER_HOME")) '("lib" "machine")))
:action (helm-make-actions
"Jump to Source"
(lambda (candidate)
(let* ((candidate (reverse candidate))
(dir (car candidate))
(path (car (cdr candidate)))
(jump-path (file-name-concat dir path)))
(message jump-path)
(find-file jump-path))))))
(helm :sources
(helm-build-sync-source "Source Modules"
:candidates (seq-map #'reverse (find-scryer-reg-modules!))
:action (helm-make-actions
"Jump to Source"
(lambda (candidate)
(let* ((candidate (reverse candidate))
(dir (car candidate))
(path (car (cdr candidate)))
(jump-path (file-name-concat dir path)))
(message jump-path)
(find-file jump-path)))))))))
(defun scryer-prolog-insert-mod! (&optional arg)
"Choose from menu, insert to top of module.
NOTE: this does not correctly detect for linebreaks in the module export argument, didn't have
time to write a parser, sorry!
C-u or no prefix because I can't figure out how to
dispatch off of one C-u only
:- use_module(library(lib)).
C-u C-u -- insert fully qualified path, i.e.,
:- use_module('/path/to/module').
C-u C-u C-u -- insert module name (based on filename), i.e.,
:- use_module(module).
"
(interactive "P")
(with-current-buffer (current-buffer)
(let ((module? (or (equal arg '(64))
(eq arg 2)))
(file? (or (equal arg '(16))
(eq arg 2)))
(library? (or (not arg)
(equal arg '(4)))))
(message "%s" arg)
(save-excursion
(goto-char (point-min))
(search-forward ":- module(" nil t nil)
(forward-line)
(if (or module? file?)
(insert ":- use_module().")
(insert ":- use_module(library())."))
(newline)
(if library?
(forward-char -4)
(forward-char -3))
(helm :sources
(helm-build-sync-source "Scryer Libraries"
:candidates (if (or module? file?)
(cl-loop for (dir path module) in (find-scryer-reg-modules!)
collect (list module module (concat "'" dir path "'")))
(seq-map #'last (find-scryer-libraries! (expand-file-name (getenv "SCRYER_HOME")) '("lib" "machine"))))
:action (lambda (candidate)
(let ((inserted
(cond
(library? candidate)
(module? (car candidate))
(file? (cadr candidate)))))
;; (message "%s" candidate)
;; (message "%s" inserted)
(insert inserted))))))))) |
Beta Was this translation helpful? Give feedback.
-
Edit: latest code/features
These took depressingly long to write, hopefully some of you can make some good use of them. Also I could use some help making these a little more customizable, my elisp is ok but I don't know much about packaging or making easily customizable or distributable elisp code.
Also these assume you have helm installed, because that's the only way I know to make a menu in emacs. I'd be very open to someone helping to rewrite that to something more portable.
Anyway, inspired by some of @triska 's cool shortcuts, these build a little on top of that functionality.
The use library function allows you to "import" libraries from a menu. With a little work it could be tweaked to allow you to add custom modules too, I just haven't had a need yet but will probably add that soon.
The jump to source does exactly what it says it does. Only works for the files right now. I'd either need to get better at sockets or we'd need to get embedded going in order to make "clickable" jump to source and cross definition.
Note that currently you would need to hard code in your own local file path in
scryer-prolog-use-module-library
andscryer-prolog-jump-to-source-module
.May the odds be ever in your favor.
Beta Was this translation helpful? Give feedback.
All reactions