diff options
Diffstat (limited to 'modes')
-rw-r--r-- | modes/Concepts.txt | 298 | ||||
-rw-r--r-- | modes/modes.el | 97 |
2 files changed, 395 insertions, 0 deletions
diff --git a/modes/Concepts.txt b/modes/Concepts.txt new file mode 100644 index 0000000..e9cfac4 --- /dev/null +++ b/modes/Concepts.txt @@ -0,0 +1,298 @@ +Title: Concepts and designs of modes +Author: JSDurand +Created: 2021-01-01 +------------------------------------- + +====================================================================== + Introduction +====================================================================== + +In this document, and in this document only, sometimes we refer to +Emacs as "the editor", though the situations referred to are +definitely different from those of other editors, and the editor Emacs +does not represent other editors in any way. This reference is solely +out of personal preferences. + +When we edit texts through a text editor, since there are a lot of +functionalities we would like to perform in the editor, we oft end up +with binding many keys with various functions in the editor. This +causes a problem: the keys are either too long to conveniently press +or too compactly packaged that it requires experience to remember and +use them at ease (what if we can execute the form “(require +'experience)” in Emacs ;P). + +The proposed way in this document to solve this dilemma is to make +keys behave according to the "contexts" of the editor. That is to say, +we are using the same key to perform such different tasks as to switch +buffers, to select different windows, and even to pop up a different +mail to read. + +To be honest, there is nothing new in this idea: it is the core idea +of the so-called "modal editing". And there are numerous packages that +implement similar ideas in Emacs already, such as evil, god-mode, or +even viper, just to name a few. + +The difference of my implementation with other packages is that I +would like to first push this idea to a more general level, before +specialising to some specific states. In other words, the modes should +reflect more dedicated states of the editor and the current task I am +performing. More precisely, when I am trying to switch buffers, I +shall be in a "buffer mode", that permits me to switch buffers just by +arrow keys, or whatever keys I have chosen for the concepts of "up" +and "down". Moreover, the concepts include not only four directions +but also "previous" and "next", whose functions are to switch to the +previously used and the next-to-be-used buffer respectively. + +Simply put, a mode is nothing but a correspondence from concepts to +functions. And the user should decide how to map these concepts to +keys. + +Then this document tries to record the modes that I accumulated +through the use of the editor. + + + +====================================================================== + Analogy with the languages +====================================================================== + +If we view pressing keys as the user speaking to the computer, then +these keypresses of a user form a "language", in a formal sense. The +letters are the keys on the keyboard (so each user has a potentially +different set of letters), and the words are valid key sequences, as +per the Emacs parlance. For example, in the built-in language of +Emacs, the word "C-x 4 d" means to open a directory in another window. + +There are some characteristics of this kind of languages. The most +obvious characteristics is that the verbs are all in one of two +"moods" (this term comes from Latin grammar if my memory serves me): +imperative and interrogative, since this language serves the only +purpose for the user to give the computer commands, or to query some +states. + +Another characteristics is that, there are a myriad of "tenses", +"conjugations" and "declensions". These refer to the fact that a key's +meaning depends upon the contexts. So we can view "C-x 4 d" as a +conjugation of the verb "C-x d", which is to open a directory in the +current window. + +However, as one of the reasons that the Sanskrit language is deemed +not easy to learn is that it has numerous declensions, conjugations, +liaisons, and irregularities, the same thing happens with the +key-binding language. If a key-binding language has too many tenses +and irregularities, then it is difficult to remember, to use, and to +"speak". And, from my point of view, the built-in key-binding language +of the editor is too difficult to use, however logical the design of +that language may be. + +The aim of this document is thus to solve this problem, for me and for +me only, at the same time laying some foundation upon which others may +build their own languages to solve their own problems, in the future. + + + +====================================================================== + The solution in terms of the analogy with the languages +====================================================================== + +So what does our solution mean in terms of the above analogy with the +languages? I think this is the difference between the agglutinative +languages and analytical languages. + +According to Wikipedia, "an agglutinative language is a type of +synthetic languages with morphology that primarily uses +agglutination." And agglutination is "a linguistic process pertaining +to derivational morphology in which complex words are formed by +stringing together morphemes without changing them in spelling or +phonetics." + +Basically, this is one of the characteristics of the default Emacs +language: concatenating affixes after affixes, or prefixes. + +Again according to Wikipedia, "an analytic language is a language that +primarily conveys relationships between words in sentences by way of +helper words (particles, prepositions, etc.) and word order, as +opposed to using inflections (changing the form of a word to convey +its role in the sentence)." + +In our contexts, this means we use helper words (key sequences) to +alter the meanings of keys. In addition, it is also a common oral +practice to use "persistent" contexts. For example, (I don't know +about English, so here I refer to the situation with my mother tongue +Chinese), when telling a story, after one declares this happened in +the past, we can safely use verbs without tenses and expect that the +listener understands this is a past action. Hence the effect I want to +achieve is that we can expect the editor to understand that after we +changed our contexts, the keys have a different meaning now. + + + +====================================================================== + Major modes and minor modes +====================================================================== + +One might wonder why I don't just use Emacs' built-in major modes and +minor modes, as those sound like a generalization of the ideas of +modes already. In this regard, I really think my idea of modes is just +like a minor mode, in that it is not directly relateed to the file or +the buffer being operated on. Rather it concerns the intention of the +user. And in effect we can implement the modes as depicted above using +nothing but major and minor modes. + +There is a problem though: the minor modes control the keymaps through +the variable "minor-mode-map-alist". It is a plain association list +that associates minor-mode symbols to keymaps. Wheneven the minor-mode +symbol has a non-nil value, the associated keymaps is in effect. +However, the order of priority for these maps is the same as the order +of appearance in this association list. This means we might sometimes +get unpredictable results when we try to activate certain functions in +our modes. Therefore, to avoid this problem, I decided to avail of a +higher-priority map, namely "emulation-mode-map-alists". And it +functions in the same way as the former. + + + +====================================================================== + General concepts +====================================================================== + +Some concepts are in a sense "universal" to all modes. These are the +concepts that don't concern with the specific functionalities of +modes, but rather with the abstract notion of modes. + +quit - Quit a mode, and nothing else. +quit & bury - Quit a mode and bury the current buffer. +quit & quit - Quit a mode and quit the selected window. +quit & kill - Quit a mode and kill the current buffer. + + + +====================================================================== + Buffer mode +====================================================================== + +The first mode that I desire is a mode that operates on the buffers. +As Protesilaos Stavrou rightly observed, the true power of Emacs lies +in its handling of the buffers, since this frees the limited screen +estate and leverages the power of the computer to manage the editing +resources. So naturally there are a lot of operations related to +buffers that are quite useful to us. Below is a list of the +correspondence between concepts and functions that I can think of. + +up - The previous buffer in the list of buffers. +down - The next buffer in the list of buffers. +previous - The previously used buffer. +next - The next-to-be-used buffer. + In practice this means the buffers that the user left by + going to the previous buffer. +jump/show - Either use the completion framework to select a buffer + or display an interactive list of buffers, i.e. ibuffer. + This can first call ibuffer, and the second call uses + the completion framework. +last - Whether this deserves an independent concept is still + debatable. This just switches to the last used buffer. If + called repeatedly, it switches between two buffers. +save - Save a buffer. + I am used to frequently save buffers, so it is important + to access this conveniently. +kill - Kill and save a buffer. +delete - Simply kill a buffer. +rename - Rename a buffer. +search - Search through a buffer. + + + +====================================================================== + Window mode +====================================================================== + + +To be honest I don't believe I really need to manage windows. Rather I +would like Emacs to automatically handle the windows the way I want. +But since it is too difficult to guess my mind, this mode is still +relevant. + +up - Go up a window. +down - Go down a window. +left - Go left a window. +right - Go right a window. +jump/show - Use the completion framework to choose a window to go to. + It might make sense to have a buffer list all windows that + I can jump to, just like an "iwindow". +save - Save a window configuration. +load - Load a window configuration. +delete - Kill a window. + + + +====================================================================== + Help mode +====================================================================== + +The idea is to make the help system behave more conveniently. For +example, currently if the user opens a file thruogh the help system, +then the user is put in the major mode corresponding to that file. I +think it might be more convenient if the user is still in the help +mode. When and only when the user chooses to exit the help mode, +should the user be put in the major mode corresponding to that file. + +up - Scroll "down". +down - Scroll "up". +previous - Go to the previous help buffer. +next - Go to the next help buffer. +search - Searching through the help buffer is also important. + + + +====================================================================== + Selection mode +====================================================================== + +I haven't decided yet whether this should be a separate mode or just a +concept for other different modes that turn the current "object" into +a selection. + + + +====================================================================== + View mode, or the screen mode +====================================================================== + +This mode of Emacs really surprised me when I first learned about its +existence. It hinted at endless possibilities, in some sense. But it +does not fully meet my expectations. + +I view document frequently. I believe that writing is a reflection of +thinking, and while I am thinking, I like to scroll through the +document to answer such questions as what have I written, does the +current writing follow the outline or the sketch in my mind, do the +writings hint at some directions that I didn't notice before I wrote +them down, and the rest. Consequently, it is am important part of my +editing activity to view the documents. + +Since my screen is limited, the occassion occurs frequently that I +need to scroll the document to view some parts of it. In addition, I +like to highlight things, so it is equally important to easily select +or highlight arbitrary things on the screen. Thus my primary focus in +viewing documents are scrolling, positioning the cursor, highlighting, +and (re)centering the buffer. + +To be more precise, I would like the directional concepts to be +directly (no pun intended) related to the scrolling of the documents; +this covers eight directional concepts (up, down, left, right, and +their "maximal" versions). + +In addition to the above, I would also like to have special "concepts" +to go to the top, the middle, and the bottom of the screen. + +Lastly, about the jump/show concept I shall talk a little. I used to +love the package "avy", which permits the user to jump to anywhere on +the screen by matching either a line, a character, or any character +sequence the user inputs. Upon further reflection, however, I do not +find anything this package offers that a plain old "interactive +search" does not offer. + +By the way, instead of the name "view mode" it would probably be +better to call it "screen mode", since it concerns the screens. And to +view is a verb, not a state of the editor; after all, we are always +viewing something on the editor, unless it crashes. diff --git a/modes/modes.el b/modes/modes.el new file mode 100644 index 0000000..538ab93 --- /dev/null +++ b/modes/modes.el @@ -0,0 +1,97 @@ +;;; A modal interface + +;;; Dictionary + +;; ;;;###autoload +;; (defvar modes-concepts +;; (list 'down 'up 'left 'right 'next 'previous +;; 'down-max 'up-max 'left-max 'right-max 'next-max 'previous-max +;; 'jump) +;; "The concepts that a mode can define.") + + +;;;###autoload +(defvar modes-concept-keymap + (list + (cons 'down '(?j down)) + (cons 'up '(?k up)) + (cons 'right '(?l right)) + (cons 'left '(?h left)) + (cons 'forward ?n) + (cons 'backward ?p) + (cons 'down-max '([?J] S-down)) + (cons 'up-max '([?K] S-up)) + (cons 'right-max '([?L] S-right)) + (cons 'left-max '([?H] S-left)) + (cons 'forwrad-max ?N) + (cons 'backward-max ?P) + (cons 'jump 'tab) + (cons 'other ?o) + (cons 'undo ?u) + (cons 'redo ?U)) + "An alist that maps concepts to keys.") + +;;;###autoload +(defvar modes-map-alist (cons (cons 'modes-mode '(keymap "Modes alist")) nil) + "The current mode map.") + +(push 'modes-map-alist emulation-mode-map-alists) + +;;;###autoload +(define-minor-mode modes-mode "A modal interface" nil "Modes" nil :global t) + +;;;###autoload +(defun modes-process-key (key) + "Process keys appropriately. +The return value is a list of keys acceptable by `define-key'." + (cond + ((stringp key) (list (kbd key))) + ((vectorp key) (list key)) + ((or (symbolp key) (integerp key)) (list (vector key))) + ((listp key) + (mapcar (lambda (k) + (cond + ((stringp k) (kbd k)) + ((vectorp k) k) + ((integerp k) (vector k)) + ((symbolp k) (vector k)) + (t (user-error "Unsupported key: %S" k)))) + key)) + (t (user-error "Unsupported key: %S" key)))) + +;;;###autoload +(defun modes-set-mode (alist) + "Set a mode. +A mode is just an ALIST between concepts and functions." + (let ((map (cons 'keymap "Modes"))) + (dolist (concept-to-function alist) + (let* ((key (or + (alist-get (car concept-to-function) + modes-concept-keymap) + (error "Unsupported concept: %S" (car concept-to-function)))) + (keys (modes-process-key key)) + (fun (cdr concept-to-function))) + (mapc (lambda (k) (define-key map k fun)) keys))) + (set 'modes-map-alist (list (cons 'modes-mode map))))) + +;;; Modes + +;;; buffer mode + +(defun switch-to-last-buffer () + "Switch to the last buffer." + (interactive) + (switch-to-buffer (other-buffer))) + +(set 'modes-buffer-mode + '((down . next-buffer) + (up . previous-buffer) + (other . switch-to-last-buffer) + (jump . ibuffer))) + + + + + + + |