beginend-2.0.0/ 0000755 0001750 0001750 00000000000 13132150262 013111 5 ustar dogsleg dogsleg beginend-2.0.0/CONTRIBUTING.md 0000644 0001750 0001750 00000002252 13132150262 015343 0 ustar dogsleg dogsleg # Contributing
Contributions are welcome. If you discover bugs or issues, or have ideas for
improvements or new features, please file a report on the issue tracker for this
repository. Follow the guidelines below to make sure everything goes smoothly.
## Issue reporting
- Check that the issue has not already been reported
- Check that the issue has not already been fixed in the latest code
- Open an issue with a clear title
- Write as grammatically correct as you can in the description.
## Pull requests
- Perform all changes on a topic branch for easier merging
- Follow the coding conventions already in use
- Verify Emacs Lisp code with `checkdoc`
- Add unit tests whenever possible
- Open a [pull request](https://help.github.com/articles/using-pull-requests)
relating to a single issue.
## Coding Conventions
### Naming
- Use a `beginend-` prefix for all public names.
- Use a `beginend--` prefix for all internal names.
### Docstrings
Write meaningful docstrings for all functions and vars.
- Document all functions and variables as directed by `checkdoc`.
- Consider using [Flycheck](https://github.com/flycheck/flycheck) to automate
`checkdoc` while you're editing.
beginend-2.0.0/README.org 0000644 0001750 0001750 00000010107 13132150262 014556 0 ustar dogsleg dogsleg * Beginend
#+BEGIN_HTML
#+END_HTML
** Summary
Redefine ~M-<~ and ~M->~ (or any key bound to ~beginning-of-buffer~ or
~end-of-buffer~) for some modes so that point moves to meaningful
locations. The real beginning and end of buffers (i.e., ~point-min~
and ~point-max~) are still accessible by pressing the same key again.
In particular, these modes are supported:
| *Mode* | ~M-<~ | ~M->~ |
|-----------------------+-------------------------+----------------------------|
| *dired-mode* | first file | last file |
| *magit-status-mode* | first section | last section |
| *message-mode* | first body line | last line before signature |
| *prog-mode* | after initial comments | before final comments |
| *occur-mode* | first match | last match |
| *ibuffer-mode* | first buffer | last buffer |
| *bs-mode* | first buffer | last buffer |
| *vc-dir-mode* | first interesting file | last interesting file |
| *recentf-dialog-mode* | first most recent file | last most recent file |
| *org-agenda-mode* | first agenda item | last agenda item |
| *compilation-mode* | first compilation error | last compilation error |
| *notmuch-search-mode* | first thread | last thread |
| *elfeed-mode* | first feed | last feed |
| *prodigy-mode* | first service | last service |
Finally, beginend does what you expect when your buffer is narrowed.
** Installing
Use [[http://melpa.org/][melpa]].
You can activate beginend for all modes it supports by customizing the
variable ~beginend-global-mode~ (~M-x customize-variable RET
beginend-global-mode RET~) or by adding this line to your
configuration file:
#+BEGIN_SRC emacs-lisp
(beginend-global-mode)
#+END_SRC
You can also decide to only activate beginend for some of its
supported major modes (e.g., through ~beginend-dired-mode~).
** Using
At this point, newly opened supported buffers will get improved
versions of ~M-<~ and ~M->~ (or any key bound to ~beginning-of-buffer~
or ~end-of-buffer~).
The following shows some screencasts. In each screencast, the cursor
is moved to the meaningful beginning and end and to the real beginning
and end.
*** Dired mode
[[file:media/beginend-dired-mode.gif]]
*** Magit status mode
[[file:media/beginend-magit-mode.gif]]
*** Message mode
[[file:media/beginend-message-mode.gif]]
*** Programming mode
[[file:media/beginend-prog-mode.gif]]
*** Occur mode
[[file:media/beginend-occur-mode.gif]]
** Contributing
Yes, please do! See [[file:CONTRIBUTING.md][CONTRIBUTING]] for guidelines.
Adding new modes is a matter of a few lines of code. For example,
these five lines (already included) define the behavior of beginend in
~org-agenda-mode~:
#+BEGIN_SRC emacs-lisp
(beginend-define-mode org-agenda-mode
(progn
(org-agenda-next-item 1))
(progn
(org-agenda-previous-item 1)))
#+END_SRC
The first ~progn~ is responsible for moving point to the meaningful
beginning of buffer. Before being executed, point is at the real
beginning of the buffer (i.e., ~point-min~). The expression
~(org-agenda-next-item 1)~ thus moves to the first agenda item.
Similarly, the second ~progn~ is responsible for moving point to the
meaningful end of buffer starting from real end (i.e., ~point-max~).
** License
See [[file:COPYING][COPYING]]. Copyright (c) 2017 [[mailto:damien@cassou.me][Damien Cassou]] and [[mailto:matus.goljer@gmail.com][Matus Goljer]].
beginend-2.0.0/Makefile 0000644 0001750 0001750 00000002745 13132150262 014561 0 ustar dogsleg dogsleg CASK ?= cask
EMACS ?= emacs
DIST ?= dist
EMACSFLAGS = --batch -Q
EMACSBATCH = $(EMACS) $(EMACSFLAGS)
VERSION := $(shell EMACS=$(EMACS) $(CASK) version)
PKG_DIR := $(shell EMACS=$(EMACS) $(CASK) package-directory)
PROJ_ROOT := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
EMACS_D = ~/.emacs.d
USER_ELPA_D = $(EMACS_D)/elpa
SRCS = $(filter-out %-pkg.el, $(wildcard *.el))
TESTS = $(wildcard test/*.el)
TAR = $(DIST)/beginend-$(VERSION).tar
.PHONY: all deps check test lint install uninstall reinstall clean-all clean clean-elc
all : deps $(TAR)
deps :
$(CASK) install
install : $(TAR)
$(EMACSBATCH) -l package -f package-initialize \
--eval '(package-install-file "$(PROJ_ROOT)/$(TAR)")'
uninstall :
rm -rf $(USER_ELPA_D)/beginend-*
reinstall : clean uninstall install
clean-all : clean
rm -rf $(PKG_DIR)
clean-elc :
rm -f *.elc test/*.elc
clean : clean-elc
rm -rf $(DIST)
rm -f *-pkg.el
$(TAR) : $(DIST) $(SRCS)
$(CASK) package $(DIST)
$(DIST) :
mkdir $(DIST)
check : test lint
test: unit
unit: $(PKG_DIR) clean-elc
${CASK} exec buttercup -L .
lint : $(SRCS) clean-elc
# Byte compile all and stop on any warning or error
${CASK} emacs $(EMACSFLAGS) \
--eval "(setq byte-compile-error-on-warn t)" \
-L . -f batch-byte-compile ${SRCS} ${EXAMPLES} ${TESTS}
# Run package-lint to check for packaging mistakes
${CASK} emacs $(EMACSFLAGS) \
-l package-lint.el \
-f package-lint-batch-and-exit ${SRCS}
beginend-2.0.0/beginend.el 0000644 0001750 0001750 00000025554 13132150262 015221 0 ustar dogsleg dogsleg ;;; beginend.el --- Redefine M-< and M-> for some modes -*- lexical-binding: t; -*-
;; Copyright (C) 2015-2017 Damien Cassou
;; Authors: Damien Cassou
;; Matus Goljer
;; Version: 2.0.0
;; GIT: https://github.com/DamienCassou/beginend
;; Package-Requires: ((emacs "24.4"))
;; Created: 01 Jun 2015
;; This file is not part of GNU Emacs.
;; 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 .
;;; Commentary:
;; Redefine M-< and M-> for some modes. For example,
;;
;; - in dired mode, M-< (respectively M->) goes to first (respectively last)
;; file line
;;
;; - in message mode,
;; - M-< goes to first line of message body (after headings)
;; - M-> goes to last line before message signature
;;
;; - in prog-mode,
;; - M-< goes to the first line after comments
;; - M-> goes to the last line before comments
;;
;; Many more modes are supported.
;;
;;; Code:
;;; Helper code
(defun beginend--defkey (map command-begin command-end)
"Bind \[beginning-of-buffer] and \[end-of-buffer] in MAP to COMMAND-BEGIN and COMMAND-END."
(define-key map (vector 'remap 'beginning-of-buffer) command-begin)
(define-key map (vector 'remap 'end-of-buffer) command-end))
(defun beginend--goto-nonwhitespace ()
"Move point backward after the first non-whitespace character."
(re-search-backward "[^[:space:]]" nil t)
(forward-char))
(defun beginend--out-of-bounds-p (point)
"Return non-nil if POINT is outside [`point-min', `point-max'].
This is possible if buffer was narrowed after POINT was stored."
(not (<= (point-min) point (point-max))))
(defmacro beginend--double-tap (extremum &rest body)
"Go to point EXTREMUM if executing BODY did not change point."
(declare (debug (form body))
(indent 1))
(let ((oldpos-var (make-symbol "old-position"))
(newpos-var (make-symbol "new-position"))
(extremum-var (make-symbol "extremum")))
`(let ((,oldpos-var (point))
(,newpos-var nil)
(,extremum-var ,extremum))
(goto-char ,extremum-var)
(save-restriction
(widen)
,@body
(setq ,newpos-var (point)))
(if (or (beginend--out-of-bounds-p ,newpos-var)
(= ,oldpos-var ,newpos-var))
(goto-char ,extremum-var)
(when (/= ,oldpos-var ,extremum-var)
(push-mark ,oldpos-var))))))
(defmacro beginend--double-tap-begin (&rest body)
"Evaluate BODY and go-to `point-min' if point did not move."
(declare (debug (body)))
`(beginend--double-tap (point-min)
,@body))
(defmacro beginend--double-tap-end (&rest body)
"Evaluate BODY and go-to `point-max' if point did not move."
(declare (debug (body)))
`(beginend--double-tap (point-max)
,@body))
(defvar beginend-modes
'(
(mu4e-view-mode-hook . beginend-message-mode)
(mu4e-compose-mode-hook . beginend-message-mode)
)
"List all beginend modes.
Each element has the form (STD-MODE-HOOK . BEGINEND-MODE). STD-MODE-HOOK
is the standard mode hook (e.g., `dired-mode-hook') to which
BEGINEND-MODE (e.g., function `beginend-dired-mode') should be added.")
(defmacro beginend-define-mode (mode begin-body end-body)
"Define a new beginend mode.
MODE is a name of an existing mode that should be adapted.
BEGIN-BODY and END-BODY are two `progn' expressions passed to respectively
`beginend--double-tap-begin' and `beginend--double-tap-end'."
(declare (indent 1)
(debug (symbolp ("progn" body) ("progn" body))))
(let* ((mode-name (symbol-name mode))
(hook (intern (format "%s-hook" mode-name)))
(beginfunc-name (intern (format "beginend-%s-goto-beginning" mode-name)))
(endfunc-name (intern (format "beginend-%s-goto-end" mode-name)))
(map-name (intern (format "beginend-%s-map" mode-name)))
(beginend-mode-name (intern (format "beginend-%s" mode-name))))
`(progn
(defun ,beginfunc-name ()
,(format "Go to beginning of buffer in `%s'." mode-name)
(interactive)
(beginend--double-tap-begin
,@(cdr begin-body)))
(defun ,endfunc-name ()
,(format "Go to beginning of buffer in `%s'." mode-name)
(interactive)
(beginend--double-tap-end
,@(cdr end-body)))
(defvar ,map-name
(let ((map (make-sparse-keymap)))
(beginend--defkey map #',beginfunc-name #',endfunc-name)
map)
,(format "Keymap for beginend in `%s'." mode-name))
(define-minor-mode ,beginend-mode-name
,(format "Mode for beginend in `%s'.\n\\{%s}" mode-name map-name)
:lighter " be"
:keymap ,map-name)
(add-to-list 'beginend-modes
(cons ',hook
#',beginend-mode-name)))))
;;; Modes
(declare-function message-goto-body "message")
(beginend-define-mode message-mode
(progn
(message-goto-body))
(progn
(when (re-search-backward "^-- $" nil t)
(beginend--goto-nonwhitespace))))
(declare-function dired-next-line "dired")
(declare-function dired-move-to-filename "dired")
(beginend-define-mode dired-mode
(progn
(let ((move 4))
(when (and (boundp 'dired-omit-mode) dired-omit-mode)
;; dired-omit-mode hides `.' and `..'.
(setf move (- move 2)))
(when (and (boundp 'dired-hide-details-hide-information-lines)
dired-hide-details-hide-information-lines
(boundp 'dired-hide-details-mode)
dired-hide-details-mode)
;; 1 line containing directory size
(setf move (- move 1)))
(dired-next-line move)))
(progn
(beginend--goto-nonwhitespace)
(dired-move-to-filename)))
(beginend-define-mode occur-mode
(progn
(occur-next 1))
(progn
(occur-prev 1)))
(declare-function ibuffer-forward-line "ibuffer")
(declare-function ibuffer-backward-line "ibuffer")
(beginend-define-mode ibuffer-mode
(progn
(ibuffer-forward-line 1))
(progn
(ibuffer-backward-line 1)))
(declare-function vc-dir-next-line "vc-dir")
(declare-function vc-dir-previous-line "vc-dir")
(beginend-define-mode vc-dir-mode
(progn
(vc-dir-next-line 1))
(progn
(vc-dir-previous-line 1)))
(declare-function bs-up "bs")
(declare-function bs-down "bs")
(beginend-define-mode bs-mode
(progn
(bs-down 2))
(progn
(bs-up 1)
(bs-down 1)))
(beginend-define-mode recentf-dialog-mode
(progn
(when (re-search-forward "^ \\[" nil t)
(goto-char (match-beginning 0))
(back-to-indentation)))
(progn
(re-search-backward "^ \\[" nil t)
(back-to-indentation)))
(declare-function org-agenda-next-item "org-agenda")
(declare-function org-agenda-previous-item "org-agenda")
(beginend-define-mode org-agenda-mode
(progn
(org-agenda-next-item 1)
(back-to-indentation))
(progn
(org-agenda-previous-item 1)
(back-to-indentation)))
(declare-function compilation-next-error "compile")
(declare-function compilation-previous-error "compile")
(beginend-define-mode compilation-mode
(progn
(compilation-next-error 1))
(progn
(compilation-previous-error 1)))
(declare-function notmuch-search-first-thread "notmuch")
(declare-function notmuch-search-last-thread "notmuch")
(beginend-define-mode notmuch-search-mode
(progn
(notmuch-search-first-thread)
(back-to-indentation))
(progn
(notmuch-search-last-thread)
(back-to-indentation)))
(beginend-define-mode elfeed-search-mode
(progn)
(progn
(forward-line -2)))
(declare-function prodigy-first "prodigy")
(declare-function prodigy-last "prodigy")
(beginend-define-mode prodigy-mode
(progn
(prodigy-first)
(goto-char (line-beginning-position))
(back-to-indentation))
(progn
(prodigy-last)
(goto-char (line-beginning-position))
(back-to-indentation)))
(declare-function magit-section-backward "magit-section")
(beginend-define-mode magit-status-mode
(progn
(re-search-forward "^$")
(forward-line))
(progn
(magit-section-backward)
(magit-section-backward)))
(defun beginend--point-is-in-comment-p (&optional p)
"Return non-nil if point is in comment.
If optional argument P is present test at that point instead of `point'."
(setq p (or p (point)))
(ignore-errors
(save-excursion
(or (nth 4 (syntax-ppss p))
(eq (char-syntax (char-after p)) ?<)
(let ((s (car (syntax-after p))))
(when s
(or (and (/= 0 (logand (lsh 1 16) s))
(nth 4 (syntax-ppss (+ p 2))))
(and (/= 0 (logand (lsh 1 17) s))
(nth 4 (syntax-ppss (+ p 1)))))))))))
(defun beginend--prog-mode-code-position-p ()
"Return non-nil if point, at beginning of line, is inside code."
(not
(or (beginend--point-is-in-comment-p)
(= (point) (line-end-position))
(looking-at (char-to-string ?\f))))) ;; form-feed (^L)
(beginend-define-mode prog-mode
(progn
(while (not (or (eobp)
(beginend--prog-mode-code-position-p)))
(forward-line)))
(progn
(while (not (or (bobp)
(beginend--prog-mode-code-position-p)))
(forward-line -1))
(goto-char (line-end-position))))
;;;###autoload
(defun beginend-setup-all ()
"Use beginend on all compatible modes.
For example, this activates function `beginend-dired-mode' in `dired' and
function `beginend-message-mode' in `message-mode'. All affected minor
modes are described in `beginend-modes'."
(mapc (lambda (pair)
(add-hook (car pair) (cdr pair)))
beginend-modes))
;;;###autoload
(defun beginend-unsetup-all ()
"Remove beginend from all compatible modes in `beginend-modes'."
(mapc (lambda (pair)
(remove-hook (car pair) (cdr pair)))
beginend-modes))
(define-minor-mode beginend-global-mode
"Toggle beginend mode.
Interactively with no argument, this command toggles the mode. A positive
prefix argument enables the mode, any other prefix argument disables it.
From Lisp, argument omitted or nil enables the mode, `toggle' toggles the
state.
When beginend mode is enabled, modes such as `dired-mode', `message-mode'
and `compilation-mode' will have their \\[beginning-of-buffer] and
\\[end-of-buffer] keys adapted to go to meaningful places."
:lighter " be"
:global t
(if beginend-global-mode
(beginend-setup-all)
(beginend-unsetup-all)))
(provide 'beginend)
;;; beginend.el ends here
;; LocalWords: beginend
beginend-2.0.0/Cask 0000644 0001750 0001750 00000000347 13132150262 013721 0 ustar dogsleg dogsleg (source gnu)
(source melpa)
(package-file "beginend.el")
(development
(depends-on "ert")
(depends-on "ert-runner")
(depends-on "undercover")
(depends-on "package-lint")
(depends-on "assess" "0.4")
(depends-on "buttercup"))
beginend-2.0.0/.dir-locals.el 0000644 0001750 0001750 00000000355 13132150262 015545 0 ustar dogsleg dogsleg ((emacs-lisp-mode
(eval . (flycheck-mode))
(eval . (flycheck-cask-setup))
(eval . (checkdoc-minor-mode))
(indent-tabs-mode . nil)
(fill-column . 80)
(sentence-end-double-space . t)
(emacs-lisp-docstring-fill-column . 75)))
beginend-2.0.0/media/ 0000755 0001750 0001750 00000000000 13132150262 014170 5 ustar dogsleg dogsleg beginend-2.0.0/media/beginend-magit-mode.gif 0000644 0001750 0001750 00000627513 13132150262 020471 0 ustar dogsleg dogsleg GIF89a (,4"%,,13""")+&[Ai$(/e)-/xNS\6:B()4&*1dde9=C>BK*)5EJS&*2'1=&)2:?H**6/7-19CGPRW`>AE]blmr|ioytz{iq3:4[\]*()afqx~UYc.1:UZcejt>BJKOXRSVac+8G9<>VY[7A6}X8B7.19>I959>24526=FIMIKNqvBEM`Y^hiN^ER=qwinys-1(7/B15=b[rSACFy~^ac;F9Z^dlS>a15