diff options
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/circe-20180525.1231/lui.el')
-rw-r--r-- | configs/shared/emacs/.emacs.d/elpa/circe-20180525.1231/lui.el | 1513 |
1 files changed, 0 insertions, 1513 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/circe-20180525.1231/lui.el b/configs/shared/emacs/.emacs.d/elpa/circe-20180525.1231/lui.el deleted file mode 100644 index 07cc758f29c0..000000000000 --- a/configs/shared/emacs/.emacs.d/elpa/circe-20180525.1231/lui.el +++ /dev/null @@ -1,1513 +0,0 @@ -;;; lui.el --- Linewise User Interface -*- lexical-binding: t -*- - -;; Copyright (C) 2005 - 2016 Jorgen Schaefer - -;; Author: Jorgen Schaefer <forcer@forcix.cx> -;; URL: https://github.com/jorgenschaefer/circe/wiki/Lui - -;; 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: - -;; Lui is a library for other Emacs Lisp programs and not useful by -;; itself. - -;; This major mode provides a user interface for applications. The -;; user interface is quite simple, consisting of an input line, a -;; prompt, and some output area, but Lui includes a lot of common -;; options, such as time stamps, filling, colorization, etc. - -;; Application programs should create modes derived from lui-mode. - -;; The application API consists of: - -;; lui-mode -;; lui-set-prompt -;; lui-insert -;; lui-input-function -;; lui-completion-function -;; lui-time-stamp-time -;; lui-time-stamp-zone -;; and the 'lui-fool and 'lui-do-not-track text properties - -;;; Code: - -(require 'button) -(require 'flyspell) -(require 'help-mode) -(require 'ispell) -(require 'paren) -(require 'ring) -(require 'thingatpt) -(require 'rx) - -(require 'tracking) - - -;;;;;;;;;;;;;;;;;;;;; -;;; Customization ;;; -;;;;;;;;;;;;;;;;;;;;; - -(defgroup lui nil - "The Linewise User Interface." - :prefix "lui-" - :group 'applications) - -(defcustom lui-scroll-behavior t - "Set the behavior lui should exhibit for scrolling. - -The following values are possible. If in doubt, use post-output. - -nil - Use default Emacs scrolling. - -post-command - Keep the input line at the end of the window if point is - after the input mark. - -post-output - Keep the input line at the end of the window only after output. - -t - Combine both post-command and post-output. - -post-scroll - Keep the input line at the end of the window on every scroll - event. Careful, this might interact badly with other functions - on `window-scroll-functions'. - - -It would be entirely sensible for Emacs to provide a setting to -do this kind of scrolling by default in a buffer. It seems rather -intuitive and sensible. But as noted on emacs-devel: - - [T]hose who know the code know that it's going to be a pain to - implement, especially if you want acceptable performance. IOW, - patches welcome - -The full discussion can be found here: - -https://lists.gnu.org/archive/html/emacs-devel/2012-10/msg00652.html - -These settings are all hacks that try to give the user the choice -between most correct behavior (post-scroll) and most compliant -behavior (post-output)." - :type '(choice (const :tag "Post Command" t) - (const :tag "Post Output" post-output) - (const :tag "Post Scroll" post-scroll) - (const :tag "Use default scrolling" nil)) - :group 'lui) -(defvaralias 'lui-scroll-to-bottom-p 'lui-scroll-behavior) - -(defcustom lui-flyspell-p nil - "Non-nil if Lui should spell-check your input. -See the function `flyspell-mode' for more information." - :type 'boolean - :group 'lui) - -(defcustom lui-flyspell-alist nil - "Alist of buffer dictionaries. - -This is a list of mappings from buffers to dictionaries to use -for the function `flyspell-mode'. The appropriate dictionary is -automatically used when Lui is activated in a buffer with a -matching buffer name. - -The entries are of the form (REGEXP DICTIONARY), where REGEXP -must match a buffer name, and DICTIONARY specifies an existing -dictionary for the function `flyspell-mode'. See -`ispell-local-dictionary-alist' and `ispell-dictionary-alist' for -a valid list of dictionaries." - :type 'string - :group 'lui) - -(defcustom lui-highlight-keywords nil - "A list of keywords to highlight. - -This specifies a list of keywords that Lui should highlight. Each -entry is of one of the following forms (similar to -`font-lock-keywords'): - - REGEXP - Highlight every match in `lui-highlight-face' - (REGEXP SUBMATCH) - Highlight the SUBMATCH (a number) in REGEXP in - `lui-highlight-face' - (REGEXP FACE) - Highlight everything matching REGEXP in FACE (a symbol) - (REGEXP SUBMATCH FACE) - Highlight the SUBMATCH in REGEXP in FACE - -In all of these cases, the FACE can also be a property list which -is then associated with the match. - -All matches are run, which means later matches can override -changes by earlier ones." - :type '(repeat (choice - (string :tag "Regular Expression") - (list :tag "Submatch" - (string :tag "Regular Expression") - (integer :tag "Submatch")) - (list :tag "Regular Expression in Specific Face" - (string :tag "Regular Expression") - (face :tag "Face")) - (list :tag "Submatch in Specific Face" - (string :tag "Regular Expression") - (integer :tag "Submatch") - (face :tag "Face")))) - :group 'lui) - -(defface lui-strong-face - '((t (:inherit bold))) - "Face used for strong markup." - :group 'lui-irc-colors) - -(defface lui-emphasis-face - '((t (:inherit italic))) - "Face for emphasis markup." - :group 'lui-irc-colors) - -(defface lui-deleted-face - '((t (:strike-through t))) - "Face for deleted messages" - :group 'lui-irc-colors) - -(defcustom lui-formatting-list nil - "List of enabled formatting types. -Each list item is a list consisting of a regular expression -matching the highlighted text, an integer for the submatch and a -face for highlighting the match." - :type `(set - (const :tag "*Strong* text" - (,(rx (or bol whitespace) - (group "*" (+? (not (any whitespace "*"))) "*") - (or eol whitespace)) - 1 lui-strong-face)) - (const :tag "_Emphasized_ text" - (,(rx (or bol whitespace) - (group "_" (+? (not (any whitespace "_"))) "_") - (or eol whitespace)) - 1 lui-emphasis-face))) - :group 'lui) - -(defcustom lui-buttons-list - `(("`\\([A-Za-z0-9+=*/-]+\\)'" 1 - lui-button-elisp-symbol 1) - ("\\<debbugs[#:]\\([0-9]+\\)" 0 - "https://debbugs.gnu.org/cgi/bugreport.cgi?bug=%s" 1) - ("\\<RFC ?\\([0-9]+\\)" 0 - "http://www.ietf.org/rfc/rfc%s.txt" 1) - ("\\<CVE[- ]\\([0-9]+-[0-9]+\\)" 0 - "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-%s" 1) - ("\\<SRFI[- ]?\\([0-9]+\\)" 0 - "http://srfi.schemers.org/srfi-%s/srfi-%s.html" 1 1) - ("\\<PEP[- ]?\\([0-9]+\\)" 0 lui-button-pep 1) - ("\\<xkcd[ #]*\\([0-9]+\\)" 0 - "https://xkcd.com/%s" 1) - ("\\([0-9a-zA-Z_.-]+/[0-9a-zA-Z_.-]+\\)#\\([0-9]+\\)" 0 - "https://github.com/%s/issues/%s" 1 2)) - "The list of buttons to buttonize. -This consists of lists of four elements each: - - (REGEXP SUBMATCH FUNCTION ARG-MATCH...) - -Whenever REGEXP is found, SUBMATCH is marked as a button. When -that button is activated, FUNCTION is called with the matches -specified in ARG-MATCHES as its arguments. - -If FUNCTION is a string, it is formatted with %s replaced by -the matches in ARG-MATCHES." - :type '(repeat (list (regexp :tag "Regular expression") - (integer :tag "Submatch to buttonize") - (function :tag "Function to call for this button") - (integer :tag "Submatch to pass as an argument"))) - :group 'lui) - -(defcustom lui-button-issue-tracker nil - "A tracker URL for the current channel. - -This will cause simple #123 links to highlight as issue links to -the given repository. Use %s to insert the actual number." - :type 'string - :group 'lui) - -(defcustom lui-fill-type " " - "How Lui should fill its output. -This can be one of the following values: - - A string - This is used as the fill prefix - 'variable - The first sequence of non-whitespace characters in the - output is used as an alignment, and the rest is filled with - spaces. - A number - The first sequence of non-whitespace characters is - right-aligned at this column, and the rest is filled to - this column. - nil - Turn filling off." - :type '(choice (string :tag "Fill Prefix") - (const :tag "Variable Fill Prefix" variable) - (integer :tag "Fill Column") - (const :tag "No filling" nil)) - :group 'lui) - -(defcustom lui-fill-column 70 - "The column at which Lui should break output. -See `fill-column'." - :type 'integer - :group 'lui) - -(defcustom lui-fill-remove-face-from-newline t - "Non-nil when filling should remove faces from newlines. -Faces on a newline extend to the end of the displayed line, which -is often not was is wanted." - :type 'boolean - :group 'lui) - -(defcustom lui-time-stamp-format "[%H:%M]" - "The format of time stamps. -See `format-time-string' for a full description of available -formatting directives." - :type 'string - :group 'lui) - -(defcustom lui-time-stamp-position 'right - "Where Lui should put time-stamps. -This can be one of the following values: - - A number - At this column of the first line of output - 'right - At a column just right to `lui-fill-column' - 'left - At the left side of the output. The output is thereby moved - to the right. - 'right-margin - In the right margin. You will need to set `right-margin-width' - in all circe buffers. - 'left-margin - In the left margin. You will need to set `left-margin-width' - in all circe buffers. - nil - Do not add any time stamp." - :type '(choice (const :tag "Right" right) - (integer :tag "Column") - (const :tag "Left" left) - (const :tag "Right Margin" right-margin) - (const :tag "Left Margin" left-margin) - (const :tag "None" nil)) - :group 'lui) - -(defcustom lui-time-stamp-only-when-changed-p t - "Non-nil if Lui should only add a time stamp when the time changes. -If `lui-time-stamp-position' is 'left, this will still add the -necessary whitespace." - :type 'boolean - :group 'lui) - -(defcustom lui-read-only-output-p t - "Non-nil if Lui should make the output read-only. -Switching this off makes copying (by killing) easier for some." - :type 'boolean - :group 'lui) - -(defcustom lui-max-buffer-size 102400 - "Non-nil if Lui should truncate the buffer if it grows too much. -If the buffer size (in characters) exceeds this number, it is -truncated at the top." - :type '(choice (const :tag "Never Truncate" nil) - (integer :tag "Maximum Buffer Size")) - :group 'lui) - -(defcustom lui-input-ring-size 32 - "The size of the input history of Lui. -This is the size of the input history used by -\\[lui-previous-input] and \\[lui-next-input]." - :type 'integer - :group 'lui) - -(defcustom lui-mode-hook nil - "The hook run when Lui is started." - :type 'hook - :group 'lui) - -(defcustom lui-pre-input-hook nil - "A hook run before Lui interprets the user input. -It is called with the buffer narrowed to the input line. -Functions can modify the input if they really want to, but the -user won't see the modifications, so that's a bad idea." - :type 'hook - :group 'lui) - -(defcustom lui-pre-output-hook nil - "The hook run before output is formatted." - :type 'hook - :group 'lui) - -(defcustom lui-post-output-hook nil - "The hook run after output has been formatted." - :type 'hook - :group 'lui) - -(defface lui-time-stamp-face - '((t (:foreground "SlateBlue" :weight bold))) - "Lui mode face used for time stamps." - :group 'lui) - -(defface lui-highlight-face - ;; Taken from `font-lock-keyword-face' - '((((class grayscale) (background light)) (:foreground "LightGray" :weight bold)) - (((class grayscale) (background dark)) (:foreground "DimGray" :weight bold)) - (((class color) (background light)) (:foreground "Purple")) - (((class color) (background dark)) (:foreground "Cyan1")) - (t (:weight bold))) - "Lui mode face used for highlighting." - :group 'lui) - -(defface lui-button-face - '((((class color) (background light)) (:foreground "Purple" :underline t)) - (((class color) (background dark)) (:foreground "Cyan" :underline t)) - (t (:underline t))) - "Default face used for LUI buttons." - :group 'lui) - - -;;;;;;;;;;;;;;;;;;;;;;;; -;;; Client interface ;;; -;;;;;;;;;;;;;;;;;;;;;;;; - -(defvar lui-input-function nil - "The function to be called for Lui input. -This function is called with a single argument, the input -string.") -(make-variable-buffer-local 'lui-input-function) - -(defvar lui-completion-function 'completion-at-point - "A function called to actually do completion.") - - -;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Private variables ;;; -;;;;;;;;;;;;;;;;;;;;;;;;; - -(defvar lui-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "RET") 'lui-send-input) - (define-key map (kbd "TAB") 'lui-next-button-or-complete) - (define-key map (kbd "<backtab>") 'lui-previous-button) - (define-key map (kbd "<S-tab>") 'lui-previous-button) - (define-key map (kbd "M-p") 'lui-previous-input) - (define-key map (kbd "M-n") 'lui-next-input) - (define-key map (kbd "C-c C-u") 'lui-kill-to-beginning-of-line) - (define-key map (kbd "C-c C-i") 'lui-fool-toggle-display) - map) - "The key map used in Lui modes.") - -(defvar lui-input-marker nil - "The marker where input should be inserted.") -(make-variable-buffer-local 'lui-input-marker) - -(defvar lui-output-marker nil - "The marker where output should be inserted. -Use `lui-insert' instead of accessing this marker directly.") -(make-variable-buffer-local 'lui-output-marker) - -(defvar lui-input-ring nil - "The input history ring.") -(make-variable-buffer-local 'lui-input-ring) - -(defvar lui-input-ring-index nil - "The index to the current item in the input ring.") -(make-variable-buffer-local 'lui-input-ring-index) - - -;;;;;;;;;;;;;; -;;; Macros ;;; -;;;;;;;;;;;;;; - -(defmacro lui-save-undo-list (&rest body) - "Run BODY without modifying the undo list." - (let ((old-marker-sym (make-symbol "old-marker"))) - `(let ((,old-marker-sym (marker-position lui-input-marker)) - (val nil)) - ;; Don't modify the undo list. The undo list is for the user's - ;; input only. - (let ((buffer-undo-list t)) - (setq val (progn ,@body))) - (when (consp buffer-undo-list) - ;; Not t :-) - (lui-adjust-undo-list ,old-marker-sym (- lui-input-marker - ,old-marker-sym))) - val))) - - -;;;;;;;;;;;;;;;;;; -;;; Major Mode ;;; -;;;;;;;;;;;;;;;;;; - -(define-derived-mode lui-mode nil "LUI" - "The Linewise User Interface mode. -This can be used as a user interface for various applications. -Those should define derived modes of this, so this function -should never be called directly. - -It can be customized for an application by specifying a -`lui-input-function'." - (setq lui-input-marker (make-marker) - lui-output-marker (make-marker) - lui-input-ring (make-ring lui-input-ring-size) - lui-input-ring-index nil - flyspell-generic-check-word-p 'lui-flyspell-check-word-p) - (set-marker lui-input-marker (point-max)) - (set-marker lui-output-marker (point-max)) - (add-hook 'window-scroll-functions 'lui-scroll-window nil t) - (add-hook 'post-command-hook 'lui-scroll-post-command) - (add-hook 'change-major-mode-hook 'lui-change-major-mode nil t) - (lui-paren-highlighting) - (lui-time-stamp-enable-filtering) - (tracking-mode 1) - (auto-fill-mode 0) - (when (fboundp 'cursor-intangible-mode) - (cursor-intangible-mode 1)) - (when lui-flyspell-p - (require 'flyspell) - (lui-flyspell-change-dictionary))) - -(defun lui-change-major-mode () - "Assure that the user really wants to change the major mode. -This is a good value for a buffer-local `change-major-mode-hook'." - (when (not (y-or-n-p "Really change major mode in a Lui buffer? ")) - (error "User disallowed mode change"))) - -(defun lui-scroll-window (window _display-start) - "Scroll the input line to the bottom of the WINDOW. - -DISPLAY-START is passed by the hook `window-scroll-functions' and -is ignored. - -See `lui-scroll-behavior' for how to customize this." - (when (and (eq lui-scroll-behavior 'post-scroll) - window - (window-live-p window)) - (with-selected-window window - (when (or (>= (point) lui-input-marker) - (equal (point-max) - (window-end nil t))) - (let ((resize-mini-windows nil)) - (save-excursion - (goto-char (point-max)) - (recenter -1))))))) - -(defun lui-scroll-post-command () - "Scroll the input line to the bottom of the window. - -This is called from `post-command-hook'. - -See `lui-scroll-behavior' for how to customize this." - (condition-case err - (dolist (w (window-list)) - (with-current-buffer (window-buffer w) - (when (and lui-input-marker - (memq lui-scroll-behavior '(t post-command))) - ;; Code from ERC's erc-goodies.el. I think this was originally - ;; mine anyhow, not sure though. - (save-restriction - (widen) - (when (>= (point) lui-input-marker) - (save-excursion - (goto-char (point-max)) - (with-selected-window w - (recenter -1)))))))) - (error - (message "Error in lui-scroll-post-command: %S" err) - ))) - -(defun lui-scroll-post-output () - "Scroll the input line to the bottom of the window. - -This is called when lui output happens. - -See `lui-scroll-behavior' for how to customize this." - (when (memq lui-scroll-behavior '(t post-output)) - (let ((resize-mini-windows nil)) - (dolist (window (get-buffer-window-list (current-buffer) nil t)) - (when (or (>= (point) lui-input-marker) - (equal (point-max) - (window-end window))) - (with-selected-window window - (save-excursion - (goto-char (point-max)) - (recenter -1)))))))) - - -;;;;;;;;;;;;; -;;; Input ;;; -;;;;;;;;;;;;; - -(defun lui-send-input () - "Send the current input to the Lui application. -If point is not in the input area, insert a newline." - (interactive) - (if (< (point) lui-input-marker) - (newline) - (save-restriction - (narrow-to-region lui-input-marker (point-max)) - (run-hooks 'lui-pre-input-hook)) - (let ((input (buffer-substring lui-input-marker (point-max)))) - (delete-region lui-input-marker (point-max)) - (lui-add-input input) - (if lui-input-function - (funcall lui-input-function input) - (error "No input function specified"))))) - -(defun lui-add-input (input) - "Add INPUT as if entered by the user." - (ring-insert lui-input-ring input) - (setq lui-input-ring-index nil)) - - -;;;;;;;;;;;;;;; -;;; Buttons ;;; -;;;;;;;;;;;;;;; - -(define-button-type 'lui-button - 'supertype 'button - 'follow-link t - 'face 'lui-button-face) - -(defun lui-buttonize () - "Buttonize the current message." - (lui-buttonize-urls) - (lui-buttonize-custom) - (lui-buttonize-issues)) - -(defun lui-buttonize-custom () - "Add custom buttons to the current message. - -This uses `lui-buttons-list'." - (dolist (entry lui-buttons-list) - (let ((regex (nth 0 entry)) - (submatch (nth 1 entry)) - (function-or-url (nth 2 entry)) - (arg-matches (nthcdr 3 entry))) - (goto-char (point-min)) - (while (re-search-forward regex nil t) - ;; Ensure we're not inserting a button inside a URL button - (when (not (button-at (match-beginning 0))) - (let* ((function (if (functionp function-or-url) - function-or-url - 'browse-url)) - (matches (mapcar (lambda (n) - (match-string-no-properties n)) - arg-matches)) - (arguments (if (functionp function-or-url) - matches - (list (apply #'format function-or-url - matches))))) - (make-button (match-beginning submatch) - (match-end submatch) - 'type 'lui-button - 'action 'lui-button-activate - 'lui-button-function function - 'lui-button-arguments arguments))))))) - -(defun lui-buttonize-issues () - "Buttonize issue references in the current message, if configured." - (when lui-button-issue-tracker - (goto-char (point-min)) - (while (re-search-forward "\\(?:^\\|\\W\\)\\(#\\([0-9]+\\)\\)" nil t) - ;; Ensure we're not inserting a button inside a URL button - (when (not (button-at (point))) - (make-button (match-beginning 1) - (match-end 1) - 'type 'lui-button - 'action 'lui-button-activate - 'lui-button-function 'browse-url - 'lui-button-arguments - (list (format lui-button-issue-tracker - (match-string 2)))))))) - -(defun lui-buttonize-urls () - "Buttonize URLs in the current message." - (let ((regex (regexp-opt thing-at-point-uri-schemes))) - (goto-char (point-min)) - (while (re-search-forward regex nil t) - (let ((bounds (bounds-of-thing-at-point 'url))) - (when bounds - (make-button (car bounds) - (cdr bounds) - 'type 'lui-button - 'action 'lui-button-activate - 'lui-button-function 'browse-url - 'lui-button-arguments - (list (buffer-substring-no-properties - (car bounds) - (cdr bounds))))))))) - -(defun lui-button-activate (button) - "Activate BUTTON. -This calls the function stored in the `lui-button-function' -property with the argument stored in `lui-button-arguments'." - (apply (button-get button 'lui-button-function) - (button-get button 'lui-button-arguments))) - -(defun lui-next-button-or-complete () - "Go to the next button, or complete at point. -When point is in the input line, call `lui-completion-function'. -Otherwise, we move to the next button." - (interactive) - (if (>= (point) - lui-input-marker) - (funcall lui-completion-function) - (forward-button 1))) - -(defun lui-previous-button () - "Go to the previous button." - (interactive) - (backward-button 1)) - -(defun lui-button-elisp-symbol (name) - "Show the documentation for the symbol named NAME." - (let ((sym (intern-soft name))) - (cond - ((not sym) - (message "No such symbol %s" name) - (ding)) - (t - (help-xref-interned sym))))) - -(defun lui-button-pep (number) - "Browse the PEP NUMBER." - (browse-url (format "https://www.python.org/dev/peps/pep-%04i" - (string-to-number number)))) - -(defun lui-button-issue (issue) - "Browse the issue tracker number ISSUE, if configured." - (if lui-button-issue-tracker - (browse-url (format lui-button-issue-tracker issue)) - (error "No issue tracker configured, see `lui-button-issue-tracker'"))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Input Line Killing ;;; -;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun lui-kill-to-beginning-of-line () - "Kill the input from point to the beginning of the input." - (interactive) - (let* ((beg (point-at-bol)) - (end (point)) - (str (buffer-substring beg end))) - (delete-region beg end) - (kill-new str))) - - -;;;;;;;;;;;;;;;;;;;;; -;;; Input History ;;; -;;;;;;;;;;;;;;;;;;;;; - -;; FIXME! -;; These need some better algorithm. They clobber input when it is not -;; in the ring! -(defun lui-previous-input () - "Cycle through the input history to the last input." - (interactive) - (when (> (ring-length lui-input-ring) 0) - (if (and lui-input-ring-index - (= (1- (ring-length lui-input-ring)) - lui-input-ring-index)) - ;; last item - insert a single empty line - (progn - (lui-replace-input "") - (setq lui-input-ring-index nil)) - ;; If any input is left, store it in the input ring - (when (and (null lui-input-ring-index) - (> (point-max) lui-input-marker)) - (ring-insert lui-input-ring - (buffer-substring lui-input-marker (point-max))) - (setq lui-input-ring-index 0)) - ;; Increment the index - (setq lui-input-ring-index - (if lui-input-ring-index - (ring-plus1 lui-input-ring-index (ring-length lui-input-ring)) - 0)) - ;; And insert the last input - (lui-replace-input (ring-ref lui-input-ring lui-input-ring-index)) - (goto-char (point-max))))) - -(defun lui-next-input () - "Cycle through the input history to the next input." - (interactive) - (when (> (ring-length lui-input-ring) 0) - (if (and lui-input-ring-index - (= 0 lui-input-ring-index)) - ;; first item - insert a single empty line - (progn - (lui-replace-input "") - (setq lui-input-ring-index nil)) - ;; If any input is left, store it in the input ring - (when (and (null lui-input-ring-index) - (> (point-max) lui-input-marker)) - (ring-insert lui-input-ring - (buffer-substring lui-input-marker (point-max))) - (setq lui-input-ring-index 0)) - ;; Decrement the index - (setq lui-input-ring-index (ring-minus1 (or lui-input-ring-index 0) - (ring-length lui-input-ring))) - ;; And insert the next input - (lui-replace-input (ring-ref lui-input-ring lui-input-ring-index)) - (goto-char (point-max))))) - -(defun lui-replace-input (str) - "Replace input with STR." - (save-excursion - (goto-char lui-input-marker) - (delete-region lui-input-marker (point-max)) - (insert str))) - - -;;;;;;;;;;;;; -;;; Fools ;;; -;;;;;;;;;;;;; - -(defun lui-fools () - "Propertize the current narrowing according to foolhardiness. -That is, if any part of it has the text property 'lui-fool set, -make the whole thing invisible." - (when (text-property-any (point-min) - (point-max) - 'lui-fool t) - (add-text-properties (point-min) - (point-max) - '(invisible lui-fool)))) - -(defun lui-fools-hidden-p () - "Return whether fools are currently hidden." - (if (or (eq t buffer-invisibility-spec) - (memq 'lui-fool buffer-invisibility-spec)) - t - nil)) - -(defun lui-fool-toggle-display () - "Display what fools have said." - (interactive) - (when (eq buffer-invisibility-spec t) - (add-to-invisibility-spec 'lui-fool)) - (cond - ((lui-fools-hidden-p) - (message "Now showing the gibberish of fools") - (remove-from-invisibility-spec 'lui-fool)) - (t - (message "Now hiding fools again *phew*") - (add-to-invisibility-spec 'lui-fool))) - ;; For some reason, after this, the display does not always update - ;; (issue #31). Force an update just in case. - (force-mode-line-update t)) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Blink Paren and Show Paren Mode ;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun lui-paren-highlighting () - "Enable sane parenthesis highlighting in this buffer." - (set (make-local-variable 'blink-paren-function) - 'lui-blink-paren-function) - (when (boundp 'show-paren-data-function) - (set (make-local-variable 'show-paren-data-function) - 'lui-show-paren-data-function))) - -(defun lui-blink-paren-function () - "Do not blink opening parens outside of the lui input area. - -When point is within the lui input area, inserting a closing -parenthesis should only blink parens within the input area, not -outside of it. - -This is a suitable value for `blink-paren-function', which see." - (if (> (point) lui-input-marker) - (let ((blink-matching-paren-distance (- (point) - lui-input-marker))) - (blink-matching-open)) - (blink-matching-open))) - -(defun lui-show-paren-data-function () - "Show parens only within the input area. - -When `show-paren-mode' is enabled, and point is in the input -area, parenthesis highlighting should only happen within the -input area, not include the rest of the buffer. - -This is a suitable value for `show-paren-data-function', which see." - (when (fboundp 'show-paren--default) - (let ((range (show-paren--default))) - (if (or (< (point) lui-input-marker) - (not (elt range 2)) - (>= (elt range 2) lui-input-marker)) - range - nil)))) - - -;;;;;;;;;;;;;;;; -;;; Flyspell ;;; -;;;;;;;;;;;;;;;; - -(defun lui-flyspell-change-dictionary (&optional dictionary) - "*Change flyspell to DICTIONARY. -If DICTIONARY is nil, set a default dictionary according to -`lui-flyspell-alist'. -If it is \"\", disable flyspell." - (interactive (list (completing-read - "Use new dictionary (RET for none, SPC to complete): " - (and (fboundp 'ispell-valid-dictionary-list) - (mapcar 'list (ispell-valid-dictionary-list))) - nil t))) - (let ((dictionary (or dictionary - (lui-find-dictionary (buffer-name))))) - (when flyspell-mode - (flyspell-mode 0)) - (when (and dictionary - (not (equal dictionary ""))) - (ispell-change-dictionary dictionary)) - (flyspell-mode 1))) - - -(defun lui-find-dictionary (buffer-name) - "Return a dictionary appropriate for BUFFER-NAME." - (let ((lis lui-flyspell-alist) - (result nil)) - (while lis - (if (string-match (caar lis) buffer-name) - (setq result (cadr (car lis)) - lis nil) - (setq lis (cdr lis)))) - result)) - -(defun lui-flyspell-check-word-p () - "Return non-nil when flyspell should verify at this position. -This is the value of Lui for `flyspell-generic-check-word-p'." - (>= (point) - lui-input-marker)) - - -;;;;;;;;;;;;;; -;;; Output ;;; -;;;;;;;;;;;;;; - -(defvar lui-message-id 0 - "Unique id for each message. -Used to allow navigation between messages and editing and -deleting.") -(make-variable-buffer-local 'lui-message-id) - -(defvar lui-internal-text-properties '(lui-formatted-time-stamp - lui-time-stamp-last - lui-raw-text - lui-message-id - lui-saved-text-properties) - "Text properties used internally by lui. - -These are always kept when replacing messages.") - -(defun lui-insert (str &optional not-tracked-p) - "Insert STR into the current Lui buffer. - -If NOT-TRACKED-P is given, this insertion won't trigger tracking -of the buffer." - (if not-tracked-p - (lui-insert-with-text-properties str 'not-tracked-p t) - (lui-insert-with-text-properties str))) - -(defun lui-plist-keys (plist) - "Get the keys from PLIST. - -PLIST should be a flat list with keys and values alternating, -like used for setting and getting text properties." - (let ((key t) result) - (dolist (item plist (reverse result)) - (when key - (push item result)) - (setq key (not key))))) - -(defun lui-insert-with-text-properties (str &rest text-properties) - "Insert STR into the current Lui buffer. - -TEXT-PROPERTIES is a property list containing text properties to -add to the inserted message." - (let ((not-tracked-p (plist-get text-properties 'not-tracked-p)) - (saved-text-properties (append (lui-plist-keys text-properties) - lui-internal-text-properties))) - (lui-save-undo-list - (save-excursion - (save-restriction - (let ((inhibit-read-only t) - (inhibit-point-motion-hooks t)) - (widen) - (goto-char lui-output-marker) - (let ((beg (point)) - (end nil)) - (insert str "\n") - (setq end (point)) - (set-marker lui-output-marker (point)) - (narrow-to-region beg end)) - (goto-char (point-min)) - (add-text-properties (point-min) - (point-max) - `(lui-raw-text ,str)) - (run-hooks 'lui-pre-output-hook) - (lui-apply-formatting) - (lui-highlight-keywords) - (lui-buttonize) - (lui-fill) - (lui-time-stamp - (plist-get text-properties 'lui-formatted-time-stamp)) - (goto-char (point-min)) - (add-text-properties - (point-min) (point-max) - (plist-put text-properties 'lui-message-id lui-message-id)) - (setq lui-message-id (1+ lui-message-id)) - (run-hooks 'lui-post-output-hook) - (lui-fools) - (goto-char (point-min)) - (add-text-properties - (point-min) (point-max) - `(lui-saved-text-properties ,saved-text-properties)) - (let ((faces (lui-faces-in-region (point-min) - (point-max))) - (foolish (text-property-any (point-min) - (point-max) - 'lui-fool t)) - (not-tracked-p - (or not-tracked-p - (text-property-any (point-min) - (point-max) - 'lui-do-not-track t)))) - (widen) - (lui-truncate) - (lui-read-only) - (when (and (not not-tracked-p) - (not foolish)) - (tracking-add-buffer (current-buffer) - faces))) - (lui-scroll-post-output))))))) - -(defun lui--adjust-p (pos old) - (and (numberp pos) (>= (abs pos) old))) - -(defun lui--new-pos (pos shift) - (* (if (< pos 0) -1 1) (+ (abs pos) shift))) - -(defun lui-adjust-undo-list (old-begin shift) - ;; Translate buffer positions in buffer-undo-list by SHIFT. - (unless (or (zerop shift) (atom buffer-undo-list)) - (let ((list buffer-undo-list) elt) - (while list - (setq elt (car list)) - (cond ((integerp elt) ; POSITION - (if (lui--adjust-p elt old-begin) - (setf (car list) (lui--new-pos elt shift)))) - ((or (atom elt) ; nil, EXTENT - (markerp (car elt))) ; (MARKER . DISTANCE) - nil) - ((integerp (car elt)) ; (BEGIN . END) - (if (lui--adjust-p (car elt) old-begin) - (setf (car elt) (lui--new-pos (car elt) shift))) - (if (lui--adjust-p (cdr elt) old-begin) - (setf (cdr elt) (lui--new-pos (cdr elt) shift)))) - ((stringp (car elt)) ; (TEXT . POSITION) - (if (lui--adjust-p (cdr elt) old-begin) - (setf (cdr elt) (lui--new-pos (cdr elt) shift)))) - ((null (car elt)) ; (nil PROPERTY VALUE BEG . END) - (let ((cons (nthcdr 3 elt))) - (if (lui--adjust-p (car cons) old-begin) - (setf (car cons) (lui--new-pos (car cons) shift))) - (if (lui--adjust-p (cdr cons) old-begin) - (setf (cdr cons) (lui--new-pos (cdr cons) shift))))) - ((and (featurep 'xemacs) - (extentp (car elt))) ; (EXTENT START END) - (if (lui--adjust-p (nth 1 elt) old-begin) - (setf (nth 1 elt) (lui--new-pos (nth 1 elt) shift))) - (if (lui--adjust-p (nth 2 elt) old-begin) - (setf (nth 2 elt) (lui--new-pos (nth 2 elt) shift))))) - (setq list (cdr list)))))) - -(defvar lui-prompt-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "<end>") 'lui-prompt-end-of-line) - (define-key map (kbd "C-e") 'lui-prompt-end-of-line) - map) - "Keymap for Lui prompts. -Since \\[end-of-line] can't move out of fields, this DTRT for an -unexpecting user.") - -(defun lui-set-prompt (prompt) - "Set PROMPT as the current Lui prompt." - (let ((inhibit-read-only t)) - (lui-save-undo-list - (save-excursion - (goto-char lui-output-marker) - (insert prompt) - (if (> lui-input-marker (point)) - (delete-region (point) lui-input-marker) - (set-marker lui-input-marker (point))) - (add-text-properties lui-output-marker lui-input-marker - `(read-only t - rear-nonsticky t - field lui-prompt - keymap ,lui-prompt-map - front-sticky t - )))))) - -(defun lui-prompt-end-of-line (&optional _N) - "Move past the prompt, and then to the end of the line. -This uses `end-of-line'. - -The argument N is ignored." - (interactive "p") - (goto-char lui-input-marker) - (call-interactively 'end-of-line)) - -(defun lui-faces-in-region (beg end) - "Return a face that describes the region between BEG and END." - (goto-char beg) - (let ((faces nil)) - (while (not (= (point) end)) - (let ((face (get-text-property (point) 'face))) - (dolist (face (if (consp face) - face - (list face))) - (when (and face - (facep face) - (face-differs-from-default-p face)) - (push face faces))) - (goto-char (next-single-property-change (point) 'face - nil end)))) - faces)) - - - -;;;;;;;;;;;;;;;;;;;; -;;; Highlighting ;;; -;;;;;;;;;;;;;;;;;;;; - -(defun lui-highlight-keywords () - "Highlight the entries in the variable `lui-highlight-keywords'. - -This is called automatically when new text is inserted." - (let ((regex (lambda (entry) - (if (stringp entry) - entry - (car entry)))) - (submatch (lambda (entry) - (if (and (consp entry) - (numberp (cadr entry))) - (cadr entry) - 0))) - (properties (lambda (entry) - (let ((face (cond - ;; REGEXP - ((stringp entry) - 'lui-highlight-face) - ;; (REGEXP SUBMATCH) - ((and (numberp (cadr entry)) - (null (cddr entry))) - 'lui-highlight-face) - ;; (REGEXP FACE) - ((null (cddr entry)) - (cadr entry)) - ;; (REGEXP SUBMATCH FACE) - (t - (nth 2 entry))))) - (if (facep face) - `(face ,face) - face))))) - (dolist (entry lui-highlight-keywords) - (goto-char (point-min)) - (while (re-search-forward (funcall regex entry) nil t) - (let* ((exp (funcall submatch entry)) - (beg (match-beginning exp)) - (end (match-end exp))) - (when (not (text-property-any beg end 'lui-highlight-fontified-p t)) - (add-text-properties beg end - (append (funcall properties entry) - '(lui-highlight-fontified-p t))))))))) - -(defun lui-apply-formatting () - "Highlight the entries in `lui-formatting-list'." - (dolist (entry lui-formatting-list) - (goto-char (point-min)) - (let ((re (car entry)) - (subgroup (cadr entry)) - (face (nth 2 entry))) - (while (re-search-forward re nil t) - (when face - (add-face-text-property (match-beginning subgroup) (match-end subgroup) - face nil (current-buffer))))))) - - -;;;;;;;;;;;;;;; -;;; Filling ;;; -;;;;;;;;;;;;;;; - -(defun lui-fill () - "Fill the text in the buffer. -This is called automatically when new text is inserted. See -`lui-fill-type' and `lui-fill-column' on how to customize this -function." - (cond - ((stringp lui-fill-type) - (let ((fill-prefix lui-fill-type) - (fill-column (or lui-fill-column - fill-column))) - (fill-region (point-min) (point-max) - nil t))) - ((eq lui-fill-type 'variable) - (let ((fill-prefix (save-excursion - (goto-char (point-min)) - (let ((beg (point))) - (re-search-forward "\\s-" nil t) - (make-string (- (point) beg) ? )))) - (fill-column (or lui-fill-column - fill-column))) - (fill-region (point-min) (point-max) - nil t))) - ((numberp lui-fill-type) - (let ((right-end (save-excursion - (goto-char (point-min)) - (re-search-forward "\\s-" nil t) - (- (point) - (point-at-bol))))) - (goto-char (point-min)) - (when (< right-end lui-fill-type) - (insert (make-string (- lui-fill-type - right-end) - ? ))) - (let ((fill-prefix (make-string lui-fill-type ? )) - (fill-column (or lui-fill-column - fill-column))) - (fill-region (point-min) (point-max) - nil t))))) - (when lui-fill-remove-face-from-newline - (goto-char (point-min)) - (while (re-search-forward "\n" nil t) - (put-text-property (match-beginning 0) - (match-end 0) - 'face - nil)))) - - -;;;;;;;;;;;;;;;;;;; -;;; Time Stamps ;;; -;;;;;;;;;;;;;;;;;;; - -(defvar lui-time-stamp-last nil - "The last time stamp.") -(make-variable-buffer-local 'lui-time-stamp-last) - -(defvar lui-time-stamp-time nil - "A custom time to use as the time stamp for `lui-insert'. - -This variable should be let-bound when you wish to provide a -custom time to be printed by `lui-time-stamp'. If this variable -is nil the current time is used. See the TIME argument to -`format-time-string' for more information.") - -(defvar lui-time-stamp-zone nil - "A custom timezone to use for the time stamp for `lui-insert'. - -This variable should be let-bound when you wish to provide a -custom time zone when printing the time stamp with -`lui-time-stamp'. If this variable is nil local time is used. -See the ZONE argument to `format-time-string' for more -information.") - -(defun lui-time-stamp (&optional text) - "Add a time stamp to the current buffer. - -If TEXT is specified, use that instead of formatting a new time stamp." - (let ((ts (or text - (format-time-string lui-time-stamp-format - lui-time-stamp-time - lui-time-stamp-zone)))) - (cond - ;; Time stamps right - ((or (numberp lui-time-stamp-position) - (eq lui-time-stamp-position 'right)) - (when (or (not lui-time-stamp-only-when-changed-p) - (not lui-time-stamp-last) - (not (string= ts lui-time-stamp-last))) - (goto-char (point-min)) - (goto-char (point-at-eol)) - (let* ((curcol (current-column)) - (col (if (numberp lui-time-stamp-position) - lui-time-stamp-position - (+ 2 (or lui-fill-column - fill-column - (point))))) - (indent (if (> col curcol) - (- col curcol) - 1)) - (ts-string (propertize - (concat (make-string indent ?\s) - (propertize - ts - 'face 'lui-time-stamp-face)) - 'lui-time-stamp t)) - (start (point))) - (insert ts-string) - (add-text-properties start (1+ (point)) '(intangible t)) - (add-text-properties (1+ start) (point) '(cursor-intangible t))))) - ;; Time stamps left - ((eq lui-time-stamp-position 'left) - (let ((indent-string (propertize (make-string (length ts) ?\s) - 'lui-time-stamp t))) - (goto-char (point-min)) - (cond - ;; Time stamp - ((or (not lui-time-stamp-only-when-changed-p) - (not lui-time-stamp-last) - (not (string= ts lui-time-stamp-last))) - (insert (propertize ts - 'face 'lui-time-stamp-face - 'lui-time-stamp t))) - ;; Just indentation - (t - (insert indent-string))) - (forward-line 1) - (while (< (point) (point-max)) - (insert indent-string) - (forward-line 1)))) - ;; Time stamps in margin - ((or (eq lui-time-stamp-position 'right-margin) - (eq lui-time-stamp-position 'left-margin)) - (when (or (not lui-time-stamp-only-when-changed-p) - (not lui-time-stamp-last) - (not (string= ts lui-time-stamp-last))) - (goto-char (point-min)) - (when lui-fill-type - (goto-char (point-at-eol))) - (let* ((ts (propertize ts 'face 'lui-time-stamp-face)) - (ts-margin (propertize - " " - 'display `((margin ,lui-time-stamp-position) - ,ts) - 'lui-time-stamp t))) - (insert ts-margin))))) - (add-text-properties (point-min) (point-max) - `(lui-formatted-time-stamp ,ts - lui-time-stamp-last ,lui-time-stamp-last)) - (setq lui-time-stamp-last ts))) - -(defun lui-time-stamp-enable-filtering () - "Enable filtering of timestamps from copied text." - (set (make-local-variable 'filter-buffer-substring-functions) - '(lui-filter-buffer-time-stamps))) - -(defun lui-filter-buffer-time-stamps (fun beg end delete) - "Filter text from copied strings. - -This is meant for the variable `filter-buffer-substring-functions', -which see for an explanation of the argument FUN, BEG, END and -DELETE." - (let ((string (funcall fun beg end delete)) - (inhibit-point-motion-hooks t) - (inhibit-read-only t) - ;; Emacs 24.4, 24.5 - deactivate-mark) - (with-temp-buffer - (insert string) - (let ((start (text-property-any (point-min) - (point-max) - 'lui-time-stamp t))) - (while start - (let ((end (next-single-property-change start 'lui-time-stamp - nil (point-max)))) - (delete-region start end) - (setq start (text-property-any (point-min) (point-max) - 'lui-time-stamp t)))) - (buffer-string))))) - -(defun lui-time-stamp-buffer-substring (buffer-string) - "Filter text from copied strings. - -This is meant for the variable `buffer-substring-filters', -which see for an explanation of the argument BUFFER-STRING." - (lui-filter-buffer-time-stamps (lambda (_beg _end _delete) - buffer-string) - nil nil nil)) - - -;;;;;;;;;;;;;;;;;; -;;; Truncating ;;; -;;;;;;;;;;;;;;;;;; - -(defun lui-truncate () - "Truncate the current buffer if it exceeds `lui-max-buffer-size'." - (when (and lui-max-buffer-size - (> (point-max) - lui-max-buffer-size)) - (goto-char (- (point-max) - lui-max-buffer-size)) - (forward-line 0) - (let ((inhibit-read-only t)) - (delete-region (point-min) (point))))) - - -;;;;;;;;;;;;;;;;; -;;; Read-Only ;;; -;;;;;;;;;;;;;;;;; - -(defun lui-read-only () - "Make the current output read-only if `lui-read-only-output-p' is non-nil." - (when lui-read-only-output-p - (add-text-properties (point-min) lui-output-marker - '(read-only t - front-sticky t)))) - - -;;;;;;;;;;;;;;;;;; -;;; Navigation ;;; -;;;;;;;;;;;;;;;;;; - -(defun lui-at-message-p () - "Check if point is on a message." - (get-text-property (point) 'lui-message-id)) - -(defun lui-beginning-of-message-p () - "Check if point is at the beginning of a message." - (or (= (point) (point-min)) - (not (equal (get-text-property (point) 'lui-message-id) - (get-text-property (1- (point)) 'lui-message-id))))) - -(defun lui-beginning-of-message () - "Move point to the beginning of the message at point." - (goto-char (previous-single-property-change (point) 'lui-message-id))) - -(defun lui-forward-message () - "Move point to the next message in the buffer and return point. -If there is no next message, move to the end of the buffer instead." - (let ((current-id (get-text-property (point) 'lui-message-id)) - (next-point - (next-single-property-change (point) 'lui-message-id))) - (if (not next-point) - (goto-char (point-max)) - (let ((message-id - (get-text-property next-point 'lui-message-id))) - (goto-char next-point) - (when (or (not (or current-id message-id)) - (and current-id (not message-id)) - (and current-id message-id - (= current-id message-id))) - (lui-forward-message)))) - (point))) - -(defun lui-backward-message () - "Move point to the previous message in the buffer and return point. -If there is no previous message, move to the beginning of the -buffer instead." - (let ((current-id (get-text-property (point) 'lui-message-id)) - (prev-point - (previous-single-property-change (point) 'lui-message-id))) - (if (not prev-point) - (goto-char (point-min)) - (let ((message-id - (get-text-property prev-point 'lui-message-id))) - (goto-char prev-point) - (when (or (not (or current-id message-id)) - (and current-id (not message-id)) - (and current-id message-id - (= current-id message-id))) - (lui-backward-message)))) - (point))) - - -;;;;;;;;;;;;;;; -;;; Editing ;;; -;;;;;;;;;;;;;;; - -(defun lui-recover-output-marker () - "Reset the output marker to just before the lui prompt." - (let ((input-position (marker-position lui-input-marker))) - (set-marker lui-output-marker - (field-beginning (1- input-position))))) - -(defun lui-build-plist (keys) - "Build a plist with KEYS taken from current text properties." - (let (result) - (dolist (key keys result) - (let ((value (get-text-property (point) key))) - (when value - (setq result (plist-put result key value))))))) - -(defun lui-replace-message (new-message) - "Replace the message at point with NEW-MESSAGE." - (unless (lui-at-message-p) - (error "Point is not on a message")) - (unless (lui-beginning-of-message-p) - (lui-beginning-of-message)) - (let* ((saved-text-properties - (get-text-property (point) 'lui-saved-text-properties)) - (text-properties (lui-build-plist saved-text-properties)) - (inhibit-read-only t) - (lui-time-stamp-last - (get-text-property (point) 'lui-time-stamp-last)) - (lui-message-id - (get-text-property (point) 'lui-message-id))) - (unwind-protect - (progn - (setq lui-output-marker (point-marker)) - (delete-region (point) - (next-single-property-change (point) 'lui-message-id)) - (apply #'lui-insert-with-text-properties new-message - (plist-put text-properties 'not-tracked-p t))) - (lui-recover-output-marker)))) - -(defun lui-replace (new-message predicate) - "Replace a message with NEW-MESSAGE. - -PREDICATE should be a function that returns a non-nil value for -the message that should be replaced." - (save-excursion - (goto-char (point-max)) - (while (> (lui-backward-message) (point-min)) - (when (funcall predicate) - (lui-replace-message new-message))))) - -(defun lui-delete-message () - "Delete the message at point." - (unless (lui-at-message-p) - (error "Point is not on a message")) - (unless (lui-beginning-of-message-p) - (lui-beginning-of-message)) - (let ((inhibit-read-only t)) - (add-text-properties (point) - (next-single-property-change (point) 'lui-message-id) - '(face lui-deleted-face)))) - -(defun lui-delete (predicate) - "Delete a message. - -PREDICATE should be a function that returns a non-nil value for -the message that should be replaced." - (save-excursion - (goto-char (point-max)) - (while (> (lui-backward-message) (point-min)) - (when (funcall predicate) - (lui-delete-message))))) - - -(provide 'lui) -;;; lui.el ends here |