From 5f6e9e79451c2c257678a099a796951a64f493a4 Mon Sep 17 00:00:00 2001 From: Michael Heerdegen Date: Thu, 26 May 2016 00:19:09 +0200 Subject: [PATCH] Rewrite the `change' and `changed' patterns so that they don't rely on `diff-hl-mode's overlays. Instead use the output of `diff-hl-changes' directly (and cache it). Make both patterns accept an optional REVISION argument. --- packages/el-search/el-search.el | 89 +++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 20 deletions(-) diff --git a/packages/el-search/el-search.el b/packages/el-search/el-search.el index 9b2fe52ab..0c5543b2d 100644 --- a/packages/el-search/el-search.el +++ b/packages/el-search/el-search.el @@ -7,7 +7,7 @@ ;; Created: 29 Jul 2015 ;; Keywords: lisp ;; Compatibility: GNU Emacs 25 -;; Version: 0.2 +;; Version: 0.2.1 ;; Package-Requires: ((emacs "25")) @@ -908,25 +908,74 @@ the search pattern." (point) ',property nil ,limit) ,limit)))))) -(el-search-defpattern change () - "Matches the object if it is part of a change. -This is equivalent to (char-prop diff-hl-hunk). - -You need `diff-hl-mode' turned on, provided by the library -\"diff-hl\" available in Gnu Elpa." - (or (bound-and-true-p diff-hl-mode) - (error "diff-hl-mode not enabled")) - '(char-prop diff-hl-hunk)) - -(el-search-defpattern changed () - "Matches the object if it contains a change. -This is equivalent to (includes-prop diff-hl-hunk). - -You need `diff-hl-mode' turned on, provided by the library -\"diff-hl\" available in Gnu Elpa." - (or (bound-and-true-p diff-hl-mode) - (error "diff-hl-mode not enabled")) - '(includes-prop diff-hl-hunk)) +(defvar diff-hl-reference-revision) +(declare-function diff-hl-changes "diff-hl") +(defvar-local el-search--cached-changes nil) + +(defun el-search--changes-from-diff-hl (revision) + "Return a list of changed regions (as conses of positions) since REVISION. +Use variable `el-search--cached-changes' for caching." + (if (and (consp el-search--cached-changes) + (equal (car el-search--cached-changes) + revision)) + (cdr el-search--cached-changes) + (require 'diff-hl) + ;; `diff-hl-changes' returns line numbers. We must convert them into positions. + (save-restriction + (widen) + (save-excursion + (let ((diff-hl-reference-revision revision) + (current-line-nbr 1) change-beg) + (goto-char 1) + (cdr (setq el-search--cached-changes + (cons revision + (delq nil (mapcar (pcase-lambda (`(,start-line ,nbr-lines ,kind)) + (if (eq kind 'delete) nil + (forward-line (- start-line current-line-nbr)) + (setq change-beg (point)) + (forward-line (1- nbr-lines)) + (setq current-line-nbr (+ start-line nbr-lines -1)) + (cons change-beg (line-end-position)))) + (diff-hl-changes))))))))))) + +(defun el-search--change-p (posn &optional revision) + ;; Non-nil when sexp after POSN is part of a change + (when (buffer-modified-p) + (error "Buffer is modified - please save")) + (save-restriction + (widen) + (let ((changes (el-search--changes-from-diff-hl revision)) + (sexp-end (scan-sexps posn 1))) + (while (and changes (< (cdar changes) sexp-end)) + (pop changes)) + (and changes + (<= (caar changes) posn))))) + +(defun el-search--changed-p (posn &optional revision) + ;; Non-nil when sexp after POSN contains a change + (when (buffer-modified-p) + (error "Buffer is modified - please save")) + (save-restriction + (widen) + (let ((changes (el-search--changes-from-diff-hl revision))) + (while (and changes (<= (cdar changes) posn)) + (pop changes)) + (and changes + (< (caar changes) (scan-sexps posn 1)))))) + +(el-search-defpattern change (&optional revision) + "Matches the object if its text is part of a file change. + +Requires library \"diff-hl\". REVISION defaults to the file's +repository's HEAD commit." + `(guard (el-search--change-p (point) ,revision))) + +(el-search-defpattern changed (&optional revision) + "Matches the object if its text contains a file change. + +Requires library \"diff-hl\". REVISION defaults to the file's +repository's HEAD commit." + `(guard (el-search--changed-p (point) ,revision))) ;;;; Highlighting -- 2.39.2