av Lars Aronsson, december 1991
Detta LysKOM-inlägg från 1991 återges enbart av nostalgiska skäl. Det skrevs i en tidsålder då man gick över från 7-bitskoderna ASCII och ISO 646 till 8-bitskoden ISO 8859-1 och hade problem att få alla program att fungera med detta. Författaren har använt GNU Emacs dagligen sedan 1989, och gamla TECO Emacs dessförinnan.
153741 1991-12-05 04:04 /197 rader/ Lars Aronsson Lysator Mottagare: Emacs erfarenhetsutbyte <805> Markerad av dig och 2 andra. Ärende: Det här kunde nästan platsa i GARB... ------------------------------------------------------------ Jag har nu skrivit en nätt liten swedish-keys-mode, som liknar den swedish-iso-mode som Staflin med flera bidragit till. Skillnaden är att min är en minor mode och den andra är en major mode. Jag tänkte berätta lite hur den ser ut och hur man kan skriva egna minor modes. MAJOR OCH MINOR MODES Alla kanske inte vet skillnad på major och minor modes, så jag tar det helt kort. Båda slagen av moder kan binda tangenter till magiska funktioner (så att LINEFEED auto-indenterar C-program, eller C-c C-c skickar in ett inlägg i LysKOM-edit-mode). En major mode börjar dagen med att kasta ut allt gammalt badvatten och slår sedan fast sina egna tangentbindningar. Dessa gäller i bufferten ända tills man byter major mode igen. Typiska major modes är c-mode och fundamental-mode. En minor mode kan slås på och av flera gånger under det att man har en och samma major mode. När man slår av en minor mode, så måste man naturligtvis sopa igen alla spår efter sig. Detta betyder att man inte kan kasta ut gammalt badvatten när man slår på den, för man riskerar då att slänga ut eventuella barn också. En minor mode måste kunna samsas med andra moder, både major och minor. Typisk minor mode är auto-fill-mode, som normalt körs under lyskom-edit-mode. SWEDISH-ISO-MODE ÄR EN MAJOR MODE Swedish-iso-mode är en major mode som sätter amerikanska ][\ till att skapa svenska ÅÄÖ, vilket är behändigt om man har amerikanskt tangentbord. Om man skriver C-kod och vill skriva lite svensk text i kommentarerna, så kan man slå över till swedish-iso-mode och sedan gå tillbaks till c-mode när man är klar. Men om man kör outline-mode, en major mode för att strukturredigera stora rapporter, så är det väldigt obekvämt att byta till en annan major mode. Det var därför jag valde att skriva en minor mode, som kunde samsas med t ex outline-mode. Swedish-iso-mode är egentligen två moder. Den andra heter indented-swedish-iso-mode. Båda dessa inkluderar funktionalitet från vanliga text-mode, som också är en major mode, nämligen center-line (ESC s), center-paragraph (ESC S) och indentering (TAB). Detta behövs inte när min swedish-keys-mode är en minor mode. Den samsas nämligen utmärkt med vanliga text-mode. HUR DU KAN SKRIVA MINOR MODES Det är några saker du bör tänka på när du skriver en minor mode. Jag visste inte om det här innan och allt står inte i dokumentationen till GNU Emacs Lisp. Det enda exemplet jag har hittills på en minor-mode är auto-fill-mode. Auto-fill-mode vänder till ny rad när man trycker mellanslag bortom fyll-positionen (normalt 70). Det är alltså bara _en_ tangent som påverkas, mellanslag. Dessutom binds den inte om (bara en gång). Det är _en_ funktion som tar hand om mellanslag oavsett om auto-fill-mode är på eller inte. Den funktionen kollar sedan om den buffert-lokala flaggan auto-fill-hook (illa valt namn) är på eller inte. Bindningen av mellanslagstangenten kan alltså vara global, det räcker om flaggan är buffert-lokal. När jag gjorde swedish-keys-mode, så ville jag inte göra som auto-fill-mode. Enkelt, tänker ni, bara att lokalt binda om dom sex tangenterna [\]{|}. Funktionen "local-set-key: Give KEY a local definition of COMMAND" borde duga. Trodde jag också, tills jag läste fortsättningen: "The definition goes in the current buffer's local map, which is shared with other buffers in the same major mode". Vad sägs om det? Ni kommer alltså att samtidigt slå på eller av moden i samtliga buffertar som kör samma major mode! Det duger inte. EN BUFFERT-LOKAL TANGENTKARTA Vi måste veta att den här bufferten har en egen lokal tangentkarta. Det gör vi genom att skaffa en buffert-lokal variabel och en funktion (dom har samma namn, vilket går bra i Emacs Lisp): (defvar aronsson-buffer-local-map nil "Pointer to buffer-local keymap.") (make-variable-buffer-local 'aronsson-buffer-local-map) (defun aronsson-buffer-local-map () "Return a buffer-local keymap. So-called local keymaps tend to be major-mode-local, not buffer-local." (cond ((not aronsson-buffer-local-map) (setq aronsson-buffer-local-map (current-local-map)) (if (not aronsson-buffer-local-map) (setq aronsson-buffer-local-map (make-keymap)) (setq aronsson-buffer-local-map (copy-keymap aronsson-buffer-local-map))) (use-local-map aronsson-buffer-local-map))) aronsson-buffer-local-map) Om det är första gången vi efterfrågar en buffert-lokal tangentkarta i den här bufferten, så exekveras första cond-grenen och en helt ny karta skapas. Vid upprepade anrop returneras den befintliga variabeln. Funktions- och variabelnamnet har tills vidare förledet "aronsson-" eftersom jag använder den i flera moder. SPARA UNDAN GAMLA BINDNINGAR Antag nu att vi redigerar C-kod med c-mode och slår på swedish-keys-mode för att skriva nåra kommentarer. När vi sedan slår av swedish-keys-mode, så ska ju c-modes bindningar av { och } (auto-indenterande) komma tillbaka. Innan vi gör define-key måste vi alltså spara undan de befintliga bindningarna i en buffert-lokal variabel. (defvar swedish-keys-old-bindings nil "Buffer-local store for hidden key bindings.") SLÅ PÅ OCH AV EN MINOR MODE En minor mode består, förutom av tangentbindningarna, av en funktion som slår på och av moden. Det finns en hel vetenskap för hur detta ska fungera. Det finns emellertid ännu ingen minor mode som helt följer alla reglerna (så vitt jag vet). Jag gör likadant som auto-fill-mode och det betyder att utan argument till funktionen swedish-keys-mode, så slås den omväxlande på eller av (togglas). Om ett argument finns, och är ett positivt heltal, så slås moden på (oavsett om den redan var på) och för andra argument slås den av. Detta implementeras som så här. Vi har en buffert-lokal flagga som vet om moden var på- eller avslagen: (defvar swedish-keys-mode nil "If this buffer-local variable is true, Swedish keys are used.") (defun swedish-keys-mode (&optional arg) "Toggle Swedish keys. With arg, turn Swedish keys on iff arg is positive. This minor mode binds keys [\\]{|} to insert Swedish letters. Unfortunately, this applies only to the local buffer, and not to the mini buffer used by search commands." (interactive "P") (make-local-variable 'swedish-keys-mode) (make-local-variable 'swedish-keys-old-bindings) (cond ((equal swedish-keys-mode (if arg (> (prefix-numeric-value arg) 0) (not swedish-keys-mode))) ) ((not swedish-keys-mode) ; Turn on (swedish-keys-on)) (swedish-keys-mode ; Turn off (swedish-keys-off)))) Det finns en global associationslista som heter minor-mode-alist. Den håller reda på vad som ska skrivas i Emacs mode-rad när vi kör en speciell mode: (setq minor-mode-alist (append minor-mode-alist '((swedish-keys-mode " ÅÄÖ" )))) BIND OM TANGENTERNA Så slutligen till själva koden som binder om tangenterna. Den är rätt så enkel och rakt på: (defconst swedish-keys-list '(("[" . "\C-q\304") ("\\". "\C-q\326") ("]" . "\C-q\305") ("{" . "\C-q\344") ("|" . "\C-q\366") ("}" . "\C-q\345")) "A list of key bindings used for Swedish keys. In each element of the list, CAR is the key and CDR is the binding.") (defun swedish-keys-on () "Turn on Swedish keys. Internal function." (setq swedish-keys-mode t) (let ((r swedish-keys-list) (m (8859-buffer-local-map)) (old nil)) (while r (setq old (cons (key-binding (car (car r))) old)) (define-key m (car (car r)) (cdr (car r))) (setq r (cdr r))) (setq swedish-keys-old-bindings (reverse old)))) (defun swedish-keys-off () "Turn on Swedish keys. Internal function." (setq swedish-keys-mode nil) (let ((r swedish-keys-list) (m (8859-buffer-local-map)) (old swedish-keys-old-bindings)) (while r (define-key m (car (car r)) (car old)) (setq r (cdr r)) (setq old (cdr old))))) (153741) ----------------------------------- Kommentar i text 153749 av Marwin