From 9e7a471ad97025a60e33932d309cbfd5ee0bf4a9 Mon Sep 17 00:00:00 2001 From: Matheus Consoli Date: Mon, 30 Jun 2025 20:04:54 -0300 Subject: [PATCH] enable configuration of the posframe appearance and position --- README.org | 72 ++++++++++++++++++------ treesitter-context.el | 125 +++++++++++++++++++++++++++++++++++------- 2 files changed, 159 insertions(+), 38 deletions(-) diff --git a/README.org b/README.org index 8d4b754..64c800a 100644 --- a/README.org +++ b/README.org @@ -52,25 +52,61 @@ Be sure your Emacs version is 29.1 and above (built with tree-sitter support), and install the tree-sitter parsers you need. +* Positioning and Appearance + The context frame can be positioned at different locations within the current window and customized with offsets for fine-tuning. + +** Position Options + - ='top-right= (default): Top-right corner of the window + - ='top-left=: Top-left corner of the window + - ='bottom-right=: Bottom-right corner of the window + - ='bottom-left=: Bottom-left corner of the window + - Custom function: You can provide your own positioning function + +** Examples + #+BEGIN_SRC elisp + ;; Position at top-left with 20px padding from edges + (setq treesitter-context-frame-position 'top-left + treesitter-context-frame-offset-x 20 + treesitter-context-frame-offset-y 20) + + ;; Customize appearance + (setq treesitter-context-background-color "#2d3748" + treesitter-context-border-color "#4a5568" + treesitter-context-border-width 2 + treesitter-context-frame-padding 3) + + ;; Custom positioning function + (setq treesitter-context-frame-position + (lambda (info) + ;; Position 100px from left, 50px from top of window + (cons 100 50))) + #+END_SRC + * Customization -| Variable | Default | Description | -|-------------------------------------------------+-----------+---------------------------------------------------------------------------------------------| -| treesitter-context-idle-time | 2.0 | How many seconds to wait before refreshing context information | -| treesitter-context-show-context-always | t | Show context all the time if t, if nil only show context when outmost parent is invisible | -| treesitter-context-show-line-number | t | Show line number in the child frame | -| treesitter-context-frame-autohide-timeout | 15 | Child frame will hide itself after this many seconds | -| treesitter-context-frame-indent-offset | 4 | Indent offset in the child frame | -| treesitter-context-frame-min-width | 60 | Minimal width of the child frame | -| treesitter-context-frame-min-height | 5 | Minimal height of the child frame | -| treesitter-context-frame-font | nil | Font of the child frame | -| treesitter-context-java-show-modifiers | nil | If t, show modifiers of the classes/methods | -| treesitter-context-background-color | "#000000" | Background color of the context frame | -| treesitter-context-border-color | "#FFFFFF" | Context frame border color | -| treesitter-context-border-width | 1 | Context frame border width | -| treesitter-context-fold-ellipsis-content | "..." | Text to show in place of a folded block. | -| treesitter-context-fold-show-fringe-marks | t | Whether to show fold markers in the fringe or not. | -| treesitter-context-fold-unfold-when-fold-region | nil | When folding a region, whether unfold old foldings in this region or not. | -| treesitter-context-frame-font-fraction | nil | Fraction of font height in the child frame. Prefer this to `treesitter-context-frame-font'. | +| Variable | Default | Description | +|-------------------------------------------------+------------+---------------------------------------------------------------------------------------------| +| treesitter-context-idle-time | 2.0 | How many seconds to wait before refreshing context information | +| treesitter-context-show-context-always | t | Show context all the time if t, if nil only show context when outmost parent is invisible | +| treesitter-context-show-line-number | t | Show line number in the child frame | +| treesitter-context-frame-autohide-timeout | 15 | Child frame will hide itself after this many seconds | +| treesitter-context-frame-indent-offset | 4 | Indent offset in the child frame | +| treesitter-context-frame-min-width | 60 | Minimal width of the child frame | +| treesitter-context-frame-min-height | 5 | Minimal height of the child frame | +| treesitter-context-frame-font | nil | Font of the child frame | +| treesitter-context-java-show-modifiers | nil | If t, show modifiers of the classes/methods | +| treesitter-context-background-color | "#000000" | Background color of the context frame | +| treesitter-context-border-color | "#FFFFFF" | Context frame border color | +| treesitter-context-border-width | 1 | Context frame border width | +| treesitter-context-fold-ellipsis-content | "..." | Text to show in place of a folded block. | +| treesitter-context-fold-show-fringe-marks | t | Whether to show fold markers in the fringe or not. | +| treesitter-context-fold-unfold-when-fold-region | nil | When folding a region, whether unfold old foldings in this region or not. | +| treesitter-context-frame-font-fraction | nil | Fraction of font height in the child frame. Prefer this to `treesitter-context-frame-font'. | +| treesitter-context-frame-position | 'top-right | Position of the context frame (top-right, top-left, bottom-right, bottom-left) | +| treesitter-context-frame-max-width | nil | Maximum width of the context frame. If nil, use window width | +| treesitter-context-frame-max-height | nil | Maximum height of the context frame. If nil, no limit | +| treesitter-context-frame-padding | 2 | Padding between line numbers and content | +| treesitter-context-frame-offset-x | 0 | Horizontal offset from the position anchor point in pixels | +| treesitter-context-frame-offset-y | 0 | Vertical offset from the position anchor point in pixels | * Commands ** treesitter-context-fold-hide diff --git a/treesitter-context.el b/treesitter-context.el index cfad092..4a69a93 100644 --- a/treesitter-context.el +++ b/treesitter-context.el @@ -97,20 +97,74 @@ If nil, show context only when the outmost parent is invisible." :safe 'floatp :group 'treesitter-context) +(defcustom treesitter-context-frame-position 'top-right + "Position of the context frame. +Can be window-relative positions or a custom poshandler function." + :version "29.1" + :type '(choice (const :tag "Window Top Right" top-right) + (const :tag "Window Top Left" top-left) + (const :tag "Window Bottom Right" bottom-right) + (const :tag "Window Bottom Left" bottom-left) + (function :tag "Custom poshandler function")) + :group 'treesitter-context) + +(defcustom treesitter-context-frame-max-width nil + "Maximum width of the context frame. If nil, use window width." + :version "29.1" + :type '(choice (const :tag "No limit" nil) + (integer :tag "Max width")) + :group 'treesitter-context) + +(defcustom treesitter-context-frame-max-height nil + "Maximum height of the context frame. If nil, no limit." + :version "29.1" + :type '(choice (const :tag "No limit" nil) + (integer :tag "Max height")) + :group 'treesitter-context) + +(defcustom treesitter-context-frame-padding 2 + "Padding between line numbers and content." + :version "29.1" + :type 'integer + :safe 'integerp + :group 'treesitter-context) + +(defcustom treesitter-context-frame-offset-x 0 + "Horizontal offset from the position anchor point in pixels." + :version "29.1" + :type 'integer + :group 'treesitter-context) + +(defcustom treesitter-context-frame-offset-y 0 + "Vertical offset from the position anchor point in pixels." + :version "29.1" + :type 'integer + :group 'treesitter-context) + (defvar treesitter-context--buffer-name "*treesitter-context*" "Name of buffer used to show context.") -(defvar treesitter-context--indent-level 0 +(defcustom treesitter-context--indent-level 0 "Indent level used to generate context information.") -(defvar treesitter-context-background-color "#000000" - "Background color for treesitter-context posframe") +(defcustom treesitter-context-background-color "#000000" + "Background color for treesitter-context posframe." + :version "29.1" + :type 'color + :group 'treesitter-context) -(defvar treesitter-context-border-color "#FFFFFF" - "Border color for treesitter-context posframe") +(defcustom treesitter-context-border-color "#FFFFFF" + "Border color for treesitter-context posframe." + :version "29.1" + :type 'color + :group 'treesitter-context) -(defvar treesitter-context-border-width 1 - "Border width for treesitter-context posframe") +(defcustom treesitter-context-border-width 1 + "Border width for treesitter-context posframe." + :version "29.1" + :type 'integer + :safe 'integerp + :group 'treesitter-context) (defvar-local treesitter-context--refresh-timer nil "Idle timer for refreshing context.") @@ -139,6 +193,33 @@ See `posframe-show' for more infor about hidehandler and INFO ." (let ((extra (max 0 (- len (length s))))) (concat s (make-string extra ?\s)))) +(defun treesitter-context--poshandler-with-offset (base-handler) + "Wrap a base poshandler to add offset support." + (lambda (info) + (let* ((base-pos (funcall base-handler info)) + (offset-x treesitter-context-frame-offset-x) + (offset-y treesitter-context-frame-offset-y)) + (cons (+ (car base-pos) offset-x) + (+ (cdr base-pos) offset-y))))) + +(defun treesitter-context--get-poshandler () + "Get the appropriate poshandler based on customization." + (cond + ;; Window-relative positions with offset support + ((eq treesitter-context-frame-position 'top-right) + (treesitter-context--poshandler-with-offset #'posframe-poshandler-window-top-right-corner)) + ((eq treesitter-context-frame-position 'top-left) + (treesitter-context--poshandler-with-offset #'posframe-poshandler-window-top-left-corner)) + ((eq treesitter-context-frame-position 'bottom-right) + (treesitter-context--poshandler-with-offset #'posframe-poshandler-window-bottom-right-corner)) + ((eq treesitter-context-frame-position 'bottom-left) + (treesitter-context--poshandler-with-offset #'posframe-poshandler-window-bottom-left-corner)) + ;; Custom function + ((functionp treesitter-context-frame-position) + treesitter-context-frame-position) + ;; Default fallback + (t (treesitter-context--poshandler-with-offset #'posframe-poshandler-window-top-right-corner)))) + (defun treesitter-context--show-context () "Show context in a child frame." (let* ((buffer (get-buffer-create treesitter-context--buffer-name)) @@ -148,7 +229,7 @@ See `posframe-show' for more infor about hidehandler and INFO ." (prefix-len 0) (line-no-prefix "") (blank-prefix "") - (padding " ") + (padding (make-string treesitter-context-frame-padding ?\s)) (font-height (face-attribute 'default :height)) first-line-p) (when treesitter-context-show-line-number @@ -178,18 +259,22 @@ See `posframe-show' for more infor about hidehandler and INFO ." (> treesitter-context-frame-font-fraction 0.0)) (setq treesitter-context-frame-font nil) (setq font-height (round (* font-height treesitter-context-frame-font-fraction)))) - (setq treesitter-context--child-frame (posframe-show buffer - :poshandler #'posframe-poshandler-window-top-right-corner - :font treesitter-context-frame-font - :border-width treesitter-context-border-width - :background-color treesitter-context-background-color - :internal-border-color treesitter-context-border-color - :internal-border-width treesitter-context-border-width - :min-width (min (max treesitter-context-frame-min-width (/ (window-width) 2)) (window-width)) - :min-height treesitter-context-frame-min-height - :accept-focus nil - :hidehandler #'treesitter-context--posframe-hidehandler-when-buffer-change - :timeout treesitter-context-frame-autohide-timeout)) + (setq treesitter-context--child-frame + (posframe-show buffer + :poshandler (treesitter-context--get-poshandler) + :font treesitter-context-frame-font + :border-width treesitter-context-border-width + :background-color treesitter-context-background-color + :internal-border-color treesitter-context-border-color + :internal-border-width treesitter-context-border-width + :min-width (min (max treesitter-context-frame-min-width (/ (window-width) 2)) + (or treesitter-context-frame-max-width (window-width))) + :max-width treesitter-context-frame-max-width + :min-height treesitter-context-frame-min-height + :max-height treesitter-context-frame-max-height + :accept-focus nil + :hidehandler #'treesitter-context--posframe-hidehandler-when-buffer-change + :timeout treesitter-context-frame-autohide-timeout)) (when font-height (set-face-attribute 'default treesitter-context--child-frame :height font-height))) nil)