From f5a395ce42c8212e44049d8942940ed5ac8aee70 Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Thu, 19 Jan 2023 11:15:35 +0200 Subject: [PATCH] ADDED: right-click context menus in sweeprolog-mode * sweeprolog.el (sweeprolog-context-menu-find-module) (sweeprolog-context-menu-find-module-other-window) (sweeprolog-context-menu-describe-module) (sweeprolog-context-menu-find-file) (sweeprolog-context-menu-find-file-other-window) (sweeprolog-context-menu-describe-predicate): new commands, add to context menus by... (sweeprolog-context-menu-for-predicate) (sweeprolog-context-menu-for-module) (sweeprolog-context-menu-for-file): new functions, used in... (sweeprolog-context-menu-functions): new abnormal hook, used by... (sweeprolog-context-menu-function): new function. (sweeprolog-mode): add it to context-menu-functions. * README.org (Context Menu): new section. --- README.org | 52 ++++++++++++++---- sweeprolog.el | 148 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 182 insertions(+), 18 deletions(-) diff --git a/README.org b/README.org index b232e76..2d5ebe7 100644 --- a/README.org +++ b/README.org @@ -1547,6 +1547,48 @@ match. Likewise, typing ~C-r~ after a successful term search invokes the command ~sweeprolog-term-search-repeat-backward~ which moves backward to the previous match. +** Context Menu +:PROPERTIES: +:CUSTOM_ID: context-menu +:DESCRIPTION: Right-click on Prolog code to open contextual menus +:ALT_TITLE: Context Menu +:END: + +#+CINDEX: context menu +#+CINDEX: right click menu +In addition to the keybindings that Sweep provides for invoking its +commands, it integrates with Emacs's standard Context Menu minor mode +to provide contextual menus that you operate with the mouse. + +- Command: context-menu-mode :: Toggle Context Menu mode. When + enabled, clicking the mouse button ~down-mouse-3~ (i.e. right-click) + activates a menu whose contents depends on its surrounding context. +- Variable: sweeprolog-context-menu-functions :: List of functions + that create Context Menu entries for Prolog tokens. Each function + should receive as its arguments the menu that is being created, the + Prolog token's description, its start position, its end position, + and the position of the mouse click. It should alter the menu + according to that context. + +To enable Context Menu, type ~M-x context-menu-mode~ or add a call to +~(context-menu-mode)~ in your Emacs initialization file to enable it in +all future sessions. You access the context menu by right-clicking +anywhere in Emacs. If you do it in a ~sweeprolog-mode~ buffer, you can +invoke several Prolog-specific commands based on where you click in +the buffer. + +If you right-click on a Prolog file specification or module name, +Sweep suggests visiting it either in the current window or in another. +If you right-click on a predicate, it lets you view its documentation +in dedicated buffer. + +You can further extend and customize the context menu that +~sweeprolog-mode~ provides by adding functions to the variable +~sweeprolog-context-menu-functions~. Each function on this list +receives the menu that is being created and a description of the +clicked Prolog token, and it can extend the menu with entries before +it's displayed. + * Prolog Help :PROPERTIES: :CUSTOM_ID: prolog-help @@ -2088,16 +2130,6 @@ there some further improvements that we want to pursue: has been loaded into the Prolog runtime and/or if its cross-reference data is up to date. -- Provide right-click (~mouse-3~) menus with ~context-menu-mode~ :: To - accommodate users who prefer a mouse-based workflow, ~sweeprolog-mode~ - should provide context-aware right-click menus by integrating with - ~context-menu-mode~. - -- Provide descriptions for tokens by setting their ~help-echo~ propety :: We - should annotate tokens in Prolog code with a short text in their - ~help-echo~ property that says what kind of token this is, to expose - the precise semantics of each token to the user. - - Make predicate completion aware of module-qualification :: predicate completion should detect when the prefix it's trying to complete starts with a module-qualification ~foo:ba<|>~ and restrict completion diff --git a/sweeprolog.el b/sweeprolog.el index 654b2d4..b4d99d7 100644 --- a/sweeprolog.el +++ b/sweeprolog.el @@ -1028,10 +1028,18 @@ module name, F is a functor name and N is its arity." 'sweeprolog-read-module-history))) ;;;###autoload -(defun sweeprolog-find-module (mod) - "Jump to the source file of the Prolog module MOD." +(defun sweeprolog-find-module (mod &optional other-window) + "Jump to the source file of the Prolog module MOD. + +If OTHER-WINDOW is non-nil, find it in another window. + +Interactively, OTHER-WINDOW is the prefix argument and this +command prompts for MOD." (interactive (list (sweeprolog-read-module-name))) - (find-file (sweeprolog-module-path mod))) + (let ((file (sweeprolog-module-path mod))) + (if other-window + (find-file-other-window file) + (find-file file)))) ;;;; Completion at point @@ -2929,13 +2937,18 @@ buffer to load." (setq fap file)))))) fap)) -(defun sweeprolog-find-file-at-point (point) +(defun sweeprolog-find-file-at-point (point &optional other-window) "Find file specified by the Prolog file spec at POINT. -Interactively, POINT is set to the current point." - (interactive "d" sweeprolog-mode) +If OTHER-WINDOW is non-nil, find it in another window. + +Interactively, POINT is set to the current point and OTHER-WINDOW +is the prefix argument." + (interactive "d\nP" sweeprolog-mode) (if-let ((file (sweeprolog-file-at-point point))) - (find-file file) + (if other-window + (find-file-other-window file) + (find-file file)) (user-error "No file specification found at point!"))) @@ -4232,7 +4245,10 @@ certain contexts to maintain conventional Prolog layout." (when sweeprolog-enable-cursor-sensor (add-hook 'sweeprolog-analyze-region-fragment-hook #'sweeprolog-analyze-fragment-variable nil t) - (cursor-sensor-mode 1))) + (cursor-sensor-mode 1)) + (when (boundp 'context-menu-functions) + (add-hook 'context-menu-functions + #'sweeprolog-context-menu-function))) ;;;; Skeletons and auto-insert @@ -5205,6 +5221,122 @@ GOAL." "\\[sweeprolog-term-search-repeat-backward] for previous match.")))))) +;;;; Right-Click Context Menu + +(defvar sweeprolog-context-menu-file-at-click nil + "Prolog file specification at mouse click.") + +(defvar sweeprolog-context-menu-module-at-click nil + "Prolog module name at mouse click.") + +(defvar sweeprolog-context-menu-predicate-at-click nil + "Prolog predicate indicator at mouse click.") + +(defun sweeprolog-context-menu-find-module () + "Find Prolog module at mouse click." + (interactive) + (sweeprolog-find-module sweeprolog-context-menu-module-at-click)) + +(defun sweeprolog-context-menu-find-module-other-window () + "Find Prolog module at mouse click in another window." + (interactive) + (sweeprolog-find-module sweeprolog-context-menu-module-at-click t)) + +(defun sweeprolog-context-menu-describe-module () + "Describe Prolog module at mouse click." + (interactive) + (sweeprolog-describe-module sweeprolog-context-menu-module-at-click)) + +(defun sweeprolog-context-menu-find-file () + "Find Prolog file at mouse click." + (interactive) + (find-file sweeprolog-context-menu-file-at-click)) + +(defun sweeprolog-context-menu-find-file-other-window () + "Find Prolog file at mouse click in another window." + (interactive) + (find-file-other-window sweeprolog-context-menu-file-at-click)) + +(defun sweeprolog-context-menu-describe-predicate () + "Describe Prolog predicate at mouse click." + (interactive) + (sweeprolog-describe-predicate sweeprolog-context-menu-predicate-at-click)) + +(defun sweeprolog-context-menu-for-predicate (menu tok _beg _end _point) + "Extend MENU with predicate-related commands if TOK describes one." + (pcase tok + ((or `("head" ,_ ,f ,a) + `("goal" ,_ ,f ,a)) + (let ((pred (sweeprolog--query-once "sweep" "sweep_functor_arity_pi" + (append (list f a) + (buffer-file-name))))) + (setq sweeprolog-context-menu-predicate-at-click pred) + (define-key menu [sweeprolog-describe-predicate] + `(menu-item "Describe This Predicate" + sweeprolog-context-menu-describe-predicate + :help ,(format "Describe predicate %s" pred) + :keys "\\[sweeprolog-describe-predicate]")))))) + +(defun sweeprolog-context-menu-for-module (menu tok _beg _end _point) + "Extend MENU with module-related commands if TOK describes one." + (pcase tok + (`("module" . ,module) + (setq sweeprolog-context-menu-module-at-click module) + (define-key menu [sweeprolog-predicate-module] + `(menu-item "Describe This Module" + sweeprolog-context-menu-describe-module + :help ,(format "Describe module %s" module) + :keys "\\[sweeprolog-describe-module]")) + (define-key menu [sweeprolog-find-module-other-window] + `(menu-item "Find in Other Window" + sweeprolog-context-menu-find-module-other-window + :help ,(format "Find module %s in other window" module) + :keys "\\[universal-argument] \\[sweeprolog-find-module]")) + (define-key menu [sweeprolog-find-module] + `(menu-item "Find This Module" + sweeprolog-context-menu-find-module + :help ,(format "Find module %s" module) + :keys "\\[sweeprolog-find-module]"))))) + +(defun sweeprolog-context-menu-for-file (menu tok _beg _end _point) + "Extend MENU with file-related commands if TOK specifies one." + (pcase tok + ((or `("file" . ,file) + `("file_no_depend" . ,file)) + (setq sweeprolog-context-menu-file-at-click file) + (define-key menu [sweeprolog-find-file-other-window] + `(menu-item "Find in Other Window" + sweeprolog-context-menu-find-file-other-window + :help ,(format "Find %s in other window" file) + :keys "\\[universal-argument] \\[sweeprolog-find-file-at-point]")) + (define-key menu [sweeprolog-find-file] + `(menu-item "Find This File" + sweeprolog-context-menu-find-file + :help ,(format "Find %s" file) + :keys "\\[sweeprolog-find-file-at-point]"))))) + +(defvar sweeprolog-context-menu-functions + '(sweeprolog-context-menu-for-file + sweeprolog-context-menu-for-module + sweeprolog-context-menu-for-predicate) + "Functions that create context menu entries for Prolog tokens. +Each function receives as its arguments the menu, the Prolog +token's description, its start position, its end position, and +the position for which the menu is created.") + +(defun sweeprolog-context-menu-function (menu click) + "Populate MENU with Prolog commands at CLICK." + (let ((point (posn-point (event-start click)))) + (save-mark-and-excursion + (goto-char point) + (sweeprolog-analyze-term-at-point + (lambda (beg end tok) + (when (<= beg point end) + (run-hook-with-args 'sweeprolog-context-menu-functions + menu tok beg point end)))))) + menu) + + ;;;; Footer (provide 'sweeprolog) -- 2.39.2