diff options
Diffstat (limited to 'elpa/tablist-20200427.2205/tablist-filter.el')
-rw-r--r-- | elpa/tablist-20200427.2205/tablist-filter.el | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/elpa/tablist-20200427.2205/tablist-filter.el b/elpa/tablist-20200427.2205/tablist-filter.el new file mode 100644 index 0000000..c5d56b8 --- /dev/null +++ b/elpa/tablist-20200427.2205/tablist-filter.el @@ -0,0 +1,464 @@ +;;; tablist-filter.el --- Filter expressions for tablists. -*- lexical-binding:t -*- + +;; Copyright (C) 2013, 2014 Andreas Politz + +;; Author: Andreas Politz <politza@fh-trier.de> +;; Keywords: extensions, lisp + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; + +(defvar python-mode-hook) +(let (python-mode-hook) ;FIXME: Why? +(require 'semantic/wisent/comp) +(require 'semantic/wisent/wisent)) + +;;; Code: + +(defvar wisent-eoi-term) +(declare-function wisent-parse "semantic/wisent/wisent.el") + +;; +;; *Variables +;; + +(defvar tablist-filter-binary-operator + '((== . tablist-filter-op-equal) + (=~ . tablist-filter-op-regexp) + (< . tablist-filter-op-<) + (> . tablist-filter-op->) + (<= . tablist-filter-op-<=) + (>= . tablist-filter-op->=) + (= . tablist-filter-op-=))) + +(defvar tablist-filter-unary-operator nil) + +(defvar tablist-filter-wisent-parser nil) + +(defvar tablist-filter-lexer-regexps nil) + +(defvar tablist-filter-wisent-grammar + '( + ;; terminals + ;; Use lowercase for better looking error messages. + (operand unary-operator binary-operator or and not) + + ;; terminal associativity & precedence + ((left binary-operator) + (left unary-operator) + (left or) + (left and) + (left not)) + + ;; rules + (filter-or-empty + ((nil)) + ((?\( ?\)) nil) + ((filter) $1)) + (filter + ((operand) $1) ;;Named filter + ((operand binary-operator operand) `(,(intern $2) ,$1 ,$3)) + ((unary-operator operand) `(,(intern $1) ,$2)) + ((not filter) `(not ,$2)) + ((filter and filter) `(and ,$1 ,$3)) + ((filter or filter) `(or ,$1 ,$3)) + ((?\( filter ?\)) $2)))) + +;; +;; *Filter Parsing +;; + +(defun tablist-filter-parser-init (&optional reinitialize interactive) + (interactive (list t t)) + (unless (and tablist-filter-lexer-regexps + (not reinitialize)) + (let ((re (mapcar + (lambda (l) + (let ((re (regexp-opt + (mapcar 'symbol-name + (mapcar 'car l)) t))) + (if (= (length re) 0) + ".\\`" ;;matches nothing + re))) + (list tablist-filter-binary-operator + tablist-filter-unary-operator)))) + (setq tablist-filter-lexer-regexps + (nreverse + (cons (concat "\\(?:" (car re) "\\|" (cadr re) + "\\|[ \t\f\r\n\"!()]\\|&&\\|||\\)") + re))))) + (unless (and tablist-filter-wisent-parser + (not reinitialize)) + (let ((wisent-compile-grammar* + (symbol-function + 'wisent-compile-grammar))) + (setq tablist-filter-wisent-parser + ;; Trick the byte-compile into not using the byte-compile + ;; handler in semantic/wisent/comp.el, since it does not + ;; always work (wisent-context-compile-grammar n/a). + (funcall wisent-compile-grammar* + tablist-filter-wisent-grammar)))) + (when interactive + (message "Parser reinitialized.")) + nil) + +(defun tablist-filter-wisent-lexer () + (cl-destructuring-bind (unary-op binary-op keywords) + tablist-filter-lexer-regexps + (skip-chars-forward " \t\f\r\n") + (cond + ((eobp) (list wisent-eoi-term)) + ((= ?\" (char-after)) + `(operand , (condition-case err + (read (current-buffer)) + (error (signal (car err) (cons + "invalid lisp string" + (cdr err))))))) + ((looking-at unary-op) + (goto-char (match-end 0)) + `(unary-operator ,(match-string-no-properties 0))) + ((looking-at binary-op) + (goto-char (match-end 0)) + `(binary-operator ,(match-string-no-properties 0))) + ((looking-at "&&") + (forward-char 2) + `(and "&&")) + ((looking-at "||") + (forward-char 2) + `(or "||")) + ((= ?! (char-after)) + (forward-char) + `(not "!")) + ((= ?\( (char-after)) + (forward-char) + `(?\( "(")) + ((= ?\) (char-after)) + (forward-char) + `(?\) ")")) + (t + (let ((beg (point))) + (when (re-search-forward keywords nil 'move) + (goto-char (match-beginning 0))) + `(operand ,(buffer-substring-no-properties + beg + (point)))))))) + +(defun tablist-filter-parse (filter) + (interactive "sFilter: ") + (tablist-filter-parser-init) + (with-temp-buffer + (save-excursion (insert filter)) + (condition-case error + (wisent-parse tablist-filter-wisent-parser + 'tablist-filter-wisent-lexer + (lambda (msg) + (signal 'error + (replace-regexp-in-string + "\\$EOI" "end of input" + msg t t)))) + (error + (signal 'error + (append (if (consp (cdr error)) + (cdr error) + (list (cdr error))) + (list (point)))))))) + +(defun tablist-filter-unparse (filter &optional noerror) + (cl-labels + ((unparse (filter &optional noerror) + (cond + ((stringp filter) + (if (or (string-match (nth 2 tablist-filter-lexer-regexps) + filter) + (= 0 (length filter))) + (format "%S" filter) + filter)) + ((and (eq (car-safe filter) 'not) + (= (length filter) 2)) + (let ((paren (memq (car-safe (nth 1 filter)) '(or and)))) + (format "!%s%s%s" + (if paren "(" "") + (unparse (cadr filter) noerror) + (if paren ")" "")))) + ((and (memq (car-safe filter) '(and or)) + (= (length filter) 3)) + (let ((lparen (and (eq (car filter) 'and) + (eq 'or (car-safe (car-safe (cdr filter)))))) + (rparen (and (eq (car filter) 'and) + (eq 'or (car-safe (car-safe (cddr filter))))))) + (format "%s%s%s %s %s%s%s" + (if lparen "(" "") + (unparse (cadr filter) noerror) + (if lparen ")" "") + (cl-case (car filter) + (and "&&") (or "||")) + (if rparen "(" "") + (unparse (car (cddr filter)) noerror) + (if rparen ")" "")))) + ((and (assq (car-safe filter) tablist-filter-binary-operator) + (= (length filter) 3)) + (format "%s %s %s" + (unparse (cadr filter) noerror) + (car filter) + (unparse (car (cddr filter)) noerror))) + ((and (assq (car-safe filter) tablist-filter-unary-operator) + (= (length filter) 2)) + (format "%s %s" + (car filter) + (unparse (cadr filter) noerror))) + ((not filter) "") + (t (funcall (if noerror 'format 'error) + "Invalid filter: %s" filter))))) + (tablist-filter-parser-init) + (unparse filter noerror))) + +(defun tablist-filter-eval (filter id entry &optional named-alist) + (cl-labels + ((feval (filter) + (pcase filter + (`(not . ,(and operand (guard (not (cdr operand))))) + (not (feval (car operand)))) + (`(and . ,(and operands (guard (= 2 (length operands))))) + (and + (feval (nth 0 operands)) + (feval (nth 1 operands)))) + (`(or . ,(and operands (guard (= 2 (length operands))))) + (or + (feval (nth 0 operands)) + (feval (nth 1 operands)))) + (`(,op . ,(and operands (guard (= (length operands) 1)))) + (let ((fn (assq op tablist-filter-unary-operator))) + (unless fn + (error "Undefined unary operator: %s" op)) + (funcall fn id entry (car operands)))) + (`(,op . ,(and operands (guard (= (length operands) 2)))) + (let ((fn (cdr (assq op tablist-filter-binary-operator)))) + (unless fn + (error "Undefined binary operator: %s" op)) + (funcall fn id entry (car operands) + (cadr operands)))) + ((guard (stringp filter)) + (let ((fn (cdr (assoc filter named-alist)))) + (unless fn + (error "Undefined named filter: %s" filter)) + (if (functionp fn) + (funcall fn id entry)) + (feval + (if (stringp fn) (tablist-filter-unparse fn) fn)))) + (`nil t) + (_ (error "Invalid filter: %s" filter))))) + (feval filter))) + +;; +;; *Filter Operators +;; + +(defun tablist-filter-get-item-by-name (entry col-name) + (let* ((col (cl-position col-name tabulated-list-format + :key 'car + :test + (lambda (s1 s2) + (eq t (compare-strings + s1 nil nil s2 nil nil t))))) + (item (and col (elt entry col)))) + (unless col + (error "No such column: %s" col-name)) + (if (consp item) ;(LABEL . PROPS) + (car item) + item))) + +(defun tablist-filter-op-equal (_id entry op1 op2) + "COLUMN == STRING : Matches if COLUMN's entry is equal to STRING." + (let ((item (tablist-filter-get-item-by-name entry op1))) + (string= item op2))) + +(defun tablist-filter-op-regexp (_id entry op1 op2) + "COLUMN =~ REGEXP : Matches if COLUMN's entry matches REGEXP." + (let ((item (tablist-filter-get-item-by-name entry op1))) + (string-match op2 item))) + +(defun tablist-filter-op-< (id entry op1 op2) + "COLUMN < NUMBER : Matches if COLUMN's entry is less than NUMBER." + (tablist-filter-op-numeric '< id entry op1 op2)) + +(defun tablist-filter-op-> (id entry op1 op2) + "COLUMN > NUMBER : Matches if COLUMN's entry is greater than NUMBER." + (tablist-filter-op-numeric '> id entry op1 op2)) + +(defun tablist-filter-op-<= (id entry op1 op2) + "COLUMN <= NUMBER : Matches if COLUMN's entry is less than or equal to NUMBER." + (tablist-filter-op-numeric '<= id entry op1 op2)) + +(defun tablist-filter-op->= (id entry op1 op2) + "COLUMN >= NUMBER : Matches if COLUMN's entry is greater than or equal to NUMBER." + (tablist-filter-op-numeric '>= id entry op1 op2)) + +(defun tablist-filter-op-= (id entry op1 op2) + "COLUMN = NUMBER : Matches if COLUMN's entry as a number is equal to NUMBER." + (tablist-filter-op-numeric '= id entry op1 op2)) + +(defun tablist-filter-op-numeric (op _id entry op1 op2) + (let ((item (tablist-filter-get-item-by-name entry op1))) + (funcall op (string-to-number item) + (string-to-number op2)))) + +(defun tablist-filter-help (&optional temporary) + (interactive) + (cl-labels + ((princ-op (op) + (princ (car op)) + (princ (concat (make-string (max 0 (- 4 (length (symbol-name (car op))))) + ?\s) + "- " + (car (split-string + (or (documentation (cdr op)) + (format "FIXME: Not documented: %s" + (cdr op))) + "\n" t)) + "\n")))) + (with-temp-buffer-window + "*Help*" + (if temporary + '((lambda (buf alist) + (let ((win + (or (display-buffer-reuse-window buf alist) + (display-buffer-in-side-window buf alist)))) + (fit-window-to-buffer win) + win)) + (side . bottom))) + nil + (princ "Filter entries with the following operators.\n\n") + (princ "&& - FILTER1 && FILTER2 : Locical and.\n") + (princ "|| - FILTER1 || FILTER2 : Locical or.\n") + (dolist (op tablist-filter-binary-operator) + (princ-op op)) + (princ "! - ! FILTER : Locical not.\n\n") + (dolist (op tablist-filter-unary-operator) + (princ-op op)) + (princ "\"...\" may be used to quote names and values if necessary, +and \(...\) to group expressions.") + (with-current-buffer standard-output + (help-mode))))) + +;; +;; *Filter Functions +;; + +;; filter ::= nil | named | fn | (OP OP1 [OP2]) + +(defun tablist-filter-negate (filter) + "Return a filter not matching filter." + (cond + ((eq (car-safe filter) 'not) + (cadr filter)) + (filter + (list 'not filter)))) + +(defun tablist-filter-push (filter new-filter &optional or-p) + "Return a filter combining FILTER and NEW-FILTER. + +By default the filters are and'ed, unless OR-P is non-nil." + (if (or (null filter) + (null new-filter)) + (or filter + new-filter) + (list (if or-p 'or 'and) + filter new-filter))) + +(defun tablist-filter-pop (filter) + "Remove the first operator or operand from filter. + +If filter starts with a negation, return filter unnegated, +if filter starts with a dis- or conjunction, remove the first operand, +if filter is nil, raise an error, +else return nil." + (pcase filter + (`(,(or `and `or) . ,tail) + (car (cdr tail))) + (`(not . ,op1) + (car op1)) + (_ (unless filter + (error "Filter is empty"))))) + +(defun tablist-filter-map (fn filter) + (pcase filter + (`(,(or `and `or `not) . ,tail) + (cons (car filter) + (mapcar (lambda (f) + (tablist-filter-map fn f)) + tail))) + (_ (funcall fn filter)))) + +;; +;; *Reading Filter +;; + +(defvar tablist-filter-edit-history nil) +(defvar tablist-filter-edit-display-help t) + +(defun tablist-filter-edit-filter (prompt &optional + initial-filter history + validate-fn) + (let* ((str (tablist-filter-unparse initial-filter)) + (filter initial-filter) + (validate-fn (or validate-fn 'identity)) + error done) + (save-window-excursion + (when tablist-filter-edit-display-help + (tablist-filter-help t)) + (while (not done) + (minibuffer-with-setup-hook + (lambda () + (when error + (when (car error) + (goto-char (+ (field-beginning) + (car error))) + (skip-chars-backward " \t\n")) + (minibuffer-message "%s" (cdr error)) + (setq error nil))) + (setq str (propertize + (read-string prompt str + (or history 'tablist-filter-edit-history))) + done t)) + (condition-case err + (progn + (setq filter (tablist-filter-parse str)) + (funcall validate-fn filter)) + (error + (setq done nil) + (setq error (cons (car-safe (cddr err)) nil)) + (when (car error) + (setq str (with-temp-buffer + (insert str) + (goto-char (car error)) + (set-text-properties + (progn + (skip-chars-backward " \t\n") + (backward-char) + (point)) + (min (car error) (point-max)) + '(face error rear-nonsticky t)) + (buffer-string)))) + (setcdr error (error-message-string err))))) + filter))) + +(provide 'tablist-filter) +;; Local Variables: +;; outline-regexp: ";;\\(\\(?:[;*]+ \\| \\*+\\)[^\s\t\n]\\|###autoload\\)\\|(" +;; indent-tabs-mode: nil +;; End: +;;; tablist-filter.el ends here |