Difference between revisions of "Emacs Lisp Cookbook"

From WikEmacs
Jump to navigation Jump to search
m (Reverted edits by 174.137.184.37 (talk) to last revision by 88.55.116.18)
 
(18 intermediate revisions by 4 users not shown)
Line 12: Line 12:
  
 
== Strings ==
 
== Strings ==
 +
 +
Note: the [[Melpa]] package [https://github.com/magnars/s.el s.el] provides a modern string manipulation library.
  
 
The empty string (zero-length string, null string, ...):
 
The empty string (zero-length string, null string, ...):
Line 50: Line 52:
 
of both.
 
of both.
  
Just found in my email's sent item. Nicolas  is the head of IT of UK office:=================================Re : Qubee Service in BangladeshDear Nicolas Ruelle:Greeting!I would be glad if you take some of your time and go thugroh my email. This is regards to the internet service you are providing in Bangladesh. Since I failed to get the contact email address of head of IT or some upper management of Qubee Bangladesh I had no choice but take this opportunity to write you few words of mine.In order for me to work to my remote office in Canada I needed a true reliable consumer/business high speed internet which I did not find until I learned about Qubee's WiMax as I believe wireless is the reliable link among others when it comes to Bangladesh.Incident 1:Since I started the Qubee service on 2nd week of January with 512kbs it was satisfactory with the speed accept for the VPN issue that started to occur from the day one. I was able to dial in the VPN and connect but was not able to communicate with any of the servers within the LAN, I had done some checks with other ISPs and found no issues which satisfied me that it was either the WiMax Router/Modem or the ISP's router. I called the customer care number wanted to speak to someone from the technical support. Customer care asked me provide her with the problem and so did I. Unfortunately the individual had no clear picture about VPN but asked me to check download speed, radio's parameter, and all the basic things that not related to the issue I had specified. At the end I had to introduce myself as an advance user and a technical person and asked her if I could speak to someone from the technical support. She created ticket for it and informed me that someone will get back to me soon. 36 hours past no one contacted me and I had to call the customer care number to follow up with my ticket. Few hours later I had a phone call from some individual and I had to describe the issue and I was asked to check the speed and other non-related VPN checks. It was frustrating. I beged to speak to someone who is familiar with the VPN and its usage. Next day I had a phone call from the customer care and told me that there was no issues from Qubee end but the VPN provider or my PC so I had to fight against the customer care's word. Few days later I received a call from Qubee and asking for my VPN credential for them to test from their office which surprised me big time. I refused and offered myself with my laptop at Qubee's office. I went there to prove that it is the Qubee not the VPN provider by connecting via other ISP's Edge Modem. They started tracing finally and at a point I had to ask them to test my laptop with a public IP assigned and it went successful. The issue was resolved by assigning a public IP address as all the Qubee modems are assigned with private 10.11. network.Incident 2:A week ago around 7 PM local time my internet link went down all of sudden. After investing I found it was about 70-90% packet loss to some hosts including Google and Qubee's local DNS server. I called the customer care number after my further investigation to make sure my end was good. The customer care individual wanted to me access my PC to troubleshoot and take some reports on the modem. Unfortunately I was unable to explain to her that it was not possible for her get into PC due to network issue. I was surprised that she claimed to be technical without knowing about ping utility and packet loss terms. She also did not have idea about pinging between their DNS name and my end or how it works. I requested to talk to someone from technical team,  we have to follow our procedure and some will visit your premises tomorrow  , she replied. I knew that the issue may recover next day or later by itself but I needed support at that particular time as I was in the middle of my regular remote work. At the end I had to forget about hoping and use my backup internet (real slow) to report the office about my unavailability for that evening (imagine if I had no backup internet).Next morning everything was resolved by itself, I suspect there were some issues within the base station during the incident period. I had a visitor from Qubee's third party support to check my modem and some of the numbers of it after 3 days. He found nothing or could not do anything but took some snapshots with him. Note: issue resolved by it self. Imagine you were down for three days and no followups were made. Next day I had another phone call from Qubee's third party contractor to schedule a time to visit my place regarding the ticket  Download/upload Issue . Note the ticket description as I specifically asked her to put packet loss somewhere in the ticket. I had to tell him everything was fine, no issues.Summary:I would like Qubee to put extra attention to be a modern and standard internet service provider in Bangladesh. As a subscriber I would like to see the following in Qubee.    * Valuing customers by listening and understanding their specific problems and the needs (should be one them on the top)    * A 24/7 true technical support team with basic troubleshooting skills set.    * An escalation procedure should be implemented as well so customers are in touch in time (no 3 days waiting periods).    * Understanding the terms Customer Care and Technical Support and implementation of it.    * Response time and proper follow up manner.    * Downtime notifications in advance    * Responding emails with their inquiries. (I never received one)    * Plug-n-play account activation system, where customer will be able to activate their unit on their own after purchasing the box from the outlet so installation engineer is required to visit the customer premises.Thank you!================Reply received after 30 minutes:================Dear Sayeed,Thank you very much for this valuable input. I am forwarding it immediately to the appropriate managers at Qubee Bangladesh: the head of customer care and the head of IT.Best regards,Nicolas Ruelle============I believe it never helped.
+
=== Processing characters ===
  
=== Processing characters ===
+
For string manipulation, you must use [https://github.com/magnars/s.el#s-trim-s s.el].
  
 
Reversing a string:
 
Reversing a string:
  
 
<syntaxhighlight lang="lisp">
 
<syntaxhighlight lang="lisp">
 +
;; with s.el
 +
(s-reverse "ab xyz") ;; => "zyx ba"
 +
;; otherwise
 
(string-to-list "foo")
 
(string-to-list "foo")
 
==> (102 111 111)
 
==> (102 111 111)
Line 65: Line 70:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
See CharacterProcessing and StringModification. See [[tr]] for an example of you sometimes need to mix strings and characters.
+
See CharacterProcessing and StringModification. See [[tr]] for an example if you sometimes need to mix strings and characters.
  
 
Looking at characters in buffers:
 
Looking at characters in buffers:
Line 83: Line 88:
 
Trim whitespace from the end of a string:
 
Trim whitespace from the end of a string:
  
 +
With s.el, see [https://github.com/magnars/s.el#tweak-whitespace tweak whitespace].
 
<syntaxhighlight lang="lisp">
 
<syntaxhighlight lang="lisp">
 +
;; with s.el
 +
(s-trim " this") ;; => "this"
 +
;; or
 
(setq test-str "abcdefg  ")
 
(setq test-str "abcdefg  ")
 
(when (string-match "[ \t]*$" test-str)
 
(when (string-match "[ \t]*$" test-str)
Line 101: Line 110:
  
 
=== Splitting strings ===
 
=== Splitting strings ===
 +
 +
With [https://github.com/magnars/s.el s.el], see '''s-split''', '''s-truncate''' and many more.
  
 
The 'split-string' function is defined in 'subr.el' as
 
The 'split-string' function is defined in 'subr.el' as
Line 124: Line 135:
 
=== Joining strings ===
 
=== Joining strings ===
  
Use `mapconcat' to join a list into a string using a separator ("glue") between elements in the string.
+
With s.el, use '''s-join'''.
 +
 
 +
Or use '''mapconcat''' to join a list into a string using a separator ("glue") between elements in the string.
  
 
Example:
 
Example:
  
 
<syntaxhighlight lang="lisp">
 
<syntaxhighlight lang="lisp">
 +
(s-join "+" '("abc" "def" "ghi")) ;; => "abc+def+ghi"
 +
;; or
 
(mapconcat 'identity '("" "home" "alex " "elisp" "erc") "/")
 
(mapconcat 'identity '("" "home" "alex " "elisp" "erc") "/")
 
==> "/home/alex /elisp/erc"
 
==> "/home/alex /elisp/erc"
Line 327: Line 342:
 
                   (point)))
 
                   (point)))
 
</syntaxhighlight>
 
</syntaxhighlight>
                 
+
 
Забавно!недавно был очень похожий случай.есть функция:string addsign(char sign, const srnitg & s){1) return sign+s; // так нельзя2) return srnitg(s1)+s; //тоже нельзя3) return srnitg(s1, 1)+s;// только так можно... но блин не сразу допетриваешь }Решил это сам проверить, на компиляторе gcc 3.4.2.вот такая программулинка:#include <string>#include <stdio.h>using namespace std;string addsign(char s1, const srnitg & s){//1)return s1+s; // так нельзя//2)return srnitg(s1)+s; //тоже нельзя//3)return srnitg(s1, 1)+s;// только так можно... но блин не сразу допетриваешь//4)return srnitg(&s1, 1)+s;}int main(int n, char **args){    srnitg text = "1.9876";    srnitg res = addsign('-', text);    puts(res.c_str());    return 0;}выдала мне срвсем другое:первый вариант (как "нельзя") выдал правильный результат: -1.9876, второй вариант - ошибку компиляции, а третий(который "только так и можно") дал нечто невразумительное - кучу смеющихся рожиц и в конце-число 1.9876. Мне даже непонятно, как у автора сообщения получился нормальный результат.Последний (4-ый, "мой" вариант) дал тоже верный результат: -1.9876
+
== Search and Replace ==
 +
Searching and replacing text is a fundamental editing need. Emacs has separate
 +
facilities for both interactive and scripted search and replace.
 +
 
 +
=== Interactive Use ===
 +
The '''replace-regexp''' function provides a way to replace text interactively.
 +
This function supports embedded emacs lisp statements in the second arguement
 +
(the replacement expression). By default '''replace-regexp''' replaces
 +
every match after the cursor location until it reaches the end of the buffer.
 +
A more useful method is to mark a region for replacement.
 +
 
 +
For example: calling
 +
 
 +
{{Command|replace-regexp RET \([A-Z]\) RET \,(downcase \1)}}
 +
 
 +
on the marked region '''AABBCC''' will convert it to '''aabbcc'''. The first
 +
arguement is a regular expression matching any capital letter and saving it
 +
as the first match, while the '''\,''' indicates embedded emacs lisp code
 +
(which calls the `downcase` function on the matched pattern).
 +
 
 +
=== Scripted Use ===
 +
A cleaner solution while scripting is to combine '''search-forward-regexp'''
 +
with '''replace-match'''. Every time the search is successful, the results
 +
are implicitly saved in to '''match-string'''. This short function replaces
 +
every pattern in a marked region with a new string drawn from its components:
 +
<syntaxhighlight lang="lisp">
 +
(defun camelCase-to_underscores (start end)
 +
  "Convert any string matching something like aBc to a_bc"
 +
  (interactive "r")
 +
  (save-restriction
 +
    (narrow-to-region start end)
 +
    (goto-char 1)
 +
    (let ((case-fold-search nil))
 +
      (while (search-forward-regexp "\\([a-z]\\)\\([A-Z]\\)\\([a-z]\\)" nil t)
 +
        (replace-match (concat (match-string 1)
 +
                              "_"
 +
                              (downcase (match-string 2))
 +
                              (match-string 3))
 +
                      t nil)))))
 +
</syntaxhighlight>
 +
 
 +
Note: to toggle between underscore, CamelCase and uppercase styles, you can use the [http://melpa.milkbox.net/#/string-inflection string-inflection] package.
 +
 
 +
'''Note''': to search for text on buffers, have a look to the [https://github.com/phillord/m-buffer-el m-buffer] library. For example, to get all strings matching a regexp in the current buffer, do
 +
 
 +
<source lang="lisp">
 +
(m-buffer-match-string-no-properties
 +
    (m-buffer-match (current-buffer) "[a-z]*"))
 +
</source>
  
 
== Dates and times ==
 
== Dates and times ==
Line 343: Line 406:
 
(eshell/date)
 
(eshell/date)
 
</syntaxhighlight>
 
</syntaxhighlight>
 
I discovered Emacs in '97 when I was doing C++ depnmovleet for BellSouth (the Sun depnmovleet tools had great integration with either vi, Emacs or XEmacs editors). I'd been a hardcore vi user since 1990 (still am), but I could adapt Emacs (actually, for some reason I opted for XEmacs at the time  can't remember why  maybe it had better fonts) better for the job at hand. Syntax highlighting  check (vi never had this  it only arrived with vim), split buffers (top for the .h, bottom for the .cpp)  check, tab completion for finding files  check, nice macro record/playback features  check, nice search/replace  check,control behavior of tab key  check, auto replace tabs with 4 spaces check. So, after a while you can imagine I crafted a pretty customized/personal .emacs file. I even continued to use emacs for a while after switching to Java in 2000 (it wasn't until Eclipse 2.0 before I switched to an IDE and left Emacs behind).Then I moved on, and for some reason the .emacs file got left behind. Shame really  I haven't really used Emacs since  it's not the same without my .emacs file  and I don't have the time or inclination to re-create it.
 
  
 
=== Conversions ===
 
=== Conversions ===
Line 422: Line 483:
  
 
TODO
 
TODO
 
Its funny that your example uses std::string beaucse it has the solution you're looking for.What you're trying to do is initialize a class member before initializing the base class, and you found that you can't. The typical way to get around this is to abandon hope of extending Foo and implement the wrapper in terms of Foo. (has-a vs. is-a) This solution has the annoyance of you needing to redeclare each and every method you want to use and then forwarding those calls to Foo, and you won't be able to use a FooWrapper in place of a Foo as well, meaning you may need to change a ton of references in your code. However, all you have to do is have FooWrapper expose its underlying Foo and you're good. (kinda like what string::c_str() does)Check out your copy of  Effective C++.  From the TOC I think item 40 discusses this.
 
  
 
== Sequences ==
 
== Sequences ==
Line 615: Line 674:
  
 
In the latter, a more efficient algorithm would use a loop (a non-local exit).
 
In the latter, a more efficient algorithm would use a loop (a non-local exit).
 
I don't think so. Normally you will open it up with latest veorisn, then save it in an earlier veorisn, that wat you can retain the 2008 veorisn with all the new options, and then convert it to an earlier. The improvements which are not handled by an earlier veorisn will be prompted when you try to save. You can lose layers or part of layers.
 
  
 
=== Vectors ===
 
=== Vectors ===
Line 728: Line 785:
 
                     (point-max)
 
                     (point-max)
 
                     file))))
 
                     file))))
</syntaxhighlight>                    
+
</syntaxhighlight>
 
 
Maybe you are interested in word-count.el. It's cnirately to much foryour use-case but it is a great tool if you have some target lengthfor a document (e.g. diary entries).What I'm really missing in Emacs is some usage statistics. I canalready get an overview over my keybind habits with keyfreq but Iwould like that to be more comprehensive. Average buffer-length, timespent in different modes, time spent in files that are part of acertain directory, lines added etc. Maybe I'm just addicted to lifestatistics and this is not really useful. Who knows.
 
  
 
=== Searching ===
 
=== Searching ===
Line 795: Line 850:
  
 
     (walk-path "~/" 'walk-path-visitor)
 
     (walk-path "~/" 'walk-path-visitor)
</syntaxhighlight>  
+
</syntaxhighlight>
 +
 
 +
'''Note:''' see also ''f-entries'' of [https://github.com/rejeep/f.el#f-entries-path-optional-fn-recursive f.el] to find all files and directories in a path.
  
 
=== Path splitting ===
 
=== Path splitting ===
Line 876: Line 933:
 
[[Category:Customization]]
 
[[Category:Customization]]
 
[[Category:Intermediate]]
 
[[Category:Intermediate]]
 +
[[Category:Emacs Lisp]]
 +
[[Category:Lisp]]
 +
[[Category:Programming]]
 +
[[Category:Tutorial]]

Latest revision as of 11:43, 18 September 2016

This page contains snippets of code that demonstrate basic Emacs Lisp programming operations in the spirit of O'Reilly's Cookbook series of books. For every task addressed, a worked-out solution is presented as a short, focused, directly usable piece of code.

All this stuff can be found elsewhere, but it is scattered about in libraries, manuals, etc. It would be helpful to have it here in one spot.

These recipes should be pastable into the *scratch* buffer so that users can hit C-j and evaluate them step by step.

Strings

Note: the Melpa package s.el provides a modern string manipulation library.

The empty string (zero-length string, null string, ...):

(zerop (string-match "" "")) ;; O(n)
==> t

(string-equal "" "") ;; O(n)?
==> t

(equal "" "") ;; O(n)?
==> t

(zerop (length "")) ;; O(1)
==> t

(eq "" "") ;; O(1)
==> t

As a space and performance optimization, Emacs keeps an intern-ed copy of the empty string as a single object

(eq "" (purecopy ""))
==> t

(eq "" (propertize "" 'face 'italic))
==> t

Strings vs buffer content

While it is quite common in other programming languages to work on strings contained in variables in Emacs it is even more idiomatic to work on strings in buffers. That's why the following contains examples of both.

Processing characters

For string manipulation, you must use s.el.

Reversing a string:

;; with s.el
(s-reverse "ab xyz") ;; => "zyx ba"
;; otherwise
(string-to-list "foo")
==> (102 111 111)
(reverse (string-to-list "foo"))
==> (111 111 102)
(apply 'string (reverse (string-to-list "foo")))
==> "oof"

See CharacterProcessing and StringModification. See tr for an example if you sometimes need to mix strings and characters.

Looking at characters in buffers:

(with-temp-buffer
  (insert "abcdefg")
  (goto-char (point-min))
  (while (not (= (char-after) ?b))
    (forward-char))
  (point))
==> 2

Trim whitespace

Trim whitespace from the end of a string:

With s.el, see tweak whitespace.

;; with s.el
(s-trim " this") ;; => "this"
;; or
(setq test-str "abcdefg  ")
(when (string-match "[ \t]*$" test-str)
  (message (concat "[" (replace-match "" nil nil test-str) "]")))

Trim whitespace from a string with a Perl-like chomp function:

(defun chomp (str)
  "Chomp leading and tailing whitespace from STR."
  (while (string-match "\\`\n+\\|^\\s-+\\|\\s-+$\\|\n+\\'"
                       str)
    (setq str (replace-match "" t t str)))
  str)

Splitting strings

With s.el, see s-split, s-truncate and many more.

The 'split-string' function is defined in 'subr.el' as

(defun split-string (string &optional separators omit-nulls)
 ...)

where 'separators' is a regular expression describing where to split the string. 'separators' defaults to white-space characters (spaces, form feeds, tabs, newlines, carriage returns, and vertical tabs). If 'omit-nulls' is set as 't' then zero-length strings are deleted from output.

(split-string "1 thing 2 say 3 words 4 you" "[1-9]")
==> ("" " thing " " say " " words " " you")

Omitting nulls:

(split-string "1 thing 2 say 3 words 4 you" "[1-9]" t)
(" thing " " say " " words " " you")

Joining strings

With s.el, use s-join.

Or use mapconcat to join a list into a string using a separator ("glue") between elements in the string.

Example:

(s-join "+" '("abc" "def" "ghi")) ;; => "abc+def+ghi"
;; or
(mapconcat 'identity '("" "home" "alex " "elisp" "erc") "/")
==> "/home/alex /elisp/erc"

Serialization

The basic idea is to convert forms to strings with `prin1-to-string' and convert it back from a string with `read'.

(length (read (prin1-to-string (make-list 1000000 '(x)))))
==> 1000000

(read (prin1-to-string "Hello World!"))
==> "Hello World!"

This only works in the simplest cases. Unfortunately, this doesn't work for all Emacs data types for programming or the editor.

(read (prin1-to-string (make-hash-table))) ;; Error before Emacs 23.
==> #s(hash-table size 65 test eql rehash-size 1.5 [...] data ())

(read (prin1-to-string (current-buffer)))
==> Lisp error: (invalid-read-syntax "#")

Formatting

Killing text

As the Emacs Lisp Manual says, "Most of the kill commands are primarily for interactive use [...] When you need to delete text for internal purposes within a Lisp function, you should normally use deletion functions, so as not to disturb the kill ring contents."

The following mimic the `kill-' commands but without disturbing the kill ring.

Delete region

The Lisp equivalent of `kill-region' (`C-w') but without kill ring side effects::

(delete-region (region-beginning) (region-end))

According to the ElispManual, "Few programs need to use the `region-beginning' and `region-end' functions." This is because Lisp code should not rely on nor "alter the mark unless altering the mark is part of the user-level functionality of the command. (And, in that case, this effect should be documented.) To remember a location for internal use in the Lisp program, store it in a Lisp variable. For example: [...]"

(let ((beg (point)))
  (forward-line 1)
  (delete-region beg (point)))

Delete line

The equivalent of `kill-line' (`C-k') but without kill ring side effects:

(let ((beg (point)))
  (forward-line 1)
  (forward-char -1)
  (delete-region beg (point)))

Alternatively, replacing the `let' with `save-excursion'.

(delete-region (point)
               (save-excursion
                 (forward-line 1)
                 (forward-char -1)
                 (point)))

Or simplest of all,

(delete-region (point) (line-end-position))

The examples with `forward-line' are shown because the paradigm is used later, see below.

Delete line backwards

The equivalent of killing the line backwards (`C-0 C-k') but without kill ring side effects:

(let ((beg (point)))
  (forward-line 0)
  (delete-region (point) beg))

Alternatively, replacing the `let' with `save-excursion'.

(delete-region (save-excursion
                 (forward-line 0)
                 (point))
               (point))

Or simplest of all,

(delete-region (line-beginning-position) (point))


Delete line to next line

The equivalent of killing the line and the newline (`C-1 C-k') but without kill ring side effects:

(let ((beg (point)))
  (forward-line 1)
  (delete-region beg (point)))

Alternatively, replacing the `let' with `save-excursion'.

(delete-region (point)
               (save-excursion
                 (forward-line 1)
                 (point)))

Delete whole line

The equivalent of `kill-whole-line' (`C-S-DEL') but without kill ring side effects:

(let ((beg (progn (forward-line 0)
                  (point))))
  (forward-line 1)
  (delete-region beg (point)))

Alternatively, replacing the `let' with `save-excursion'.

(delete-region (save-excursion
                 (forward-line 0)
                 (point))
               (save-excursion
                 (forward-line 1)
                 (point)))

Or simplest of all,

(delete-region (line-beginning-position) (line-end-position))

Delete word

The equivalent of `kill-word' (`M-d') but without kill ring side effects:

(let ((beg (point)))
  (forward-word 1)
  (delete-region beg (point)))

Alternatively, replacing the `let' with `save-excursion'.

(delete-region (point)
               (save-excursion
                 (forward-word 1)
                 (point)))

Delete sentence

The equivalent of `kill-sentence' (`M-k') but without kill ring side effects:

(let ((beg (point)))
  (forward-sentence 1)
  (delete-region beg (point)))

Alternatively, replacing the `let' with `save-excursion'.

  (delete-region (point)
                 (save-excursion
                   (forward-sentence 1)
                   (point)))

Search and Replace

Searching and replacing text is a fundamental editing need. Emacs has separate facilities for both interactive and scripted search and replace.

Interactive Use

The replace-regexp function provides a way to replace text interactively. This function supports embedded emacs lisp statements in the second arguement (the replacement expression). By default replace-regexp replaces every match after the cursor location until it reaches the end of the buffer. A more useful method is to mark a region for replacement.

For example: calling

M-x replace-regexp RET \([A-Z]\) RET \,(downcase \1)

on the marked region AABBCC will convert it to aabbcc. The first arguement is a regular expression matching any capital letter and saving it as the first match, while the \, indicates embedded emacs lisp code (which calls the `downcase` function on the matched pattern).

Scripted Use

A cleaner solution while scripting is to combine search-forward-regexp with replace-match. Every time the search is successful, the results are implicitly saved in to match-string. This short function replaces every pattern in a marked region with a new string drawn from its components:

(defun camelCase-to_underscores (start end)
  "Convert any string matching something like aBc to a_bc"
  (interactive "r")
  (save-restriction
    (narrow-to-region start end)
    (goto-char 1)
    (let ((case-fold-search nil))
      (while (search-forward-regexp "\\([a-z]\\)\\([A-Z]\\)\\([a-z]\\)" nil t)
        (replace-match (concat (match-string 1)
                               "_"
                               (downcase (match-string 2))
                               (match-string 3))
                       t nil)))))

Note: to toggle between underscore, CamelCase and uppercase styles, you can use the string-inflection package.

Note: to search for text on buffers, have a look to the m-buffer library. For example, to get all strings matching a regexp in the current buffer, do

(m-buffer-match-string-no-properties
    (m-buffer-match (current-buffer) "[a-z]*"))

Dates and times

Get today's date

(format-time-string "%d %B %Y")

or

(eshell/date)

Conversions

Read a date from a string.

  (let ((time (date-to-time "Tue, 27-Sep-83 12:35:59 EST")))
    (set-time-zone-rule t) ;; Use Universal time.
    (prog1 (format-time-string "%Y-%m-%d %T UTC" time)
      (set-time-zone-rule nil))) ;; Reset to default time zone.
  ==> "1983-09-27 17:35:59 UTC"

Decode a time object.

  (decode-time (date-to-time "Tue, 27-Sep-83 12:35:59 EST"))
  ==> (59 35 13 27 9 1983 2 t -14400)

Get the seconds from the unix epoch.

  (let ((time (date-to-time "13 Feb 2009 23:31:30 UTC")))
    (float-time time))
  ==> 1234585890.0

Find the date for seconds from the unix epoch.

  (format-time-string "%Y-%m-%d %T UTC" (seconds-to-time 1234585890))
  ==> "2009-02-13 23:31:30 UTC"

Find the date 30 seconds in the future.

  (format-time-string "%Y-%m-%d %T UTC" (time-add (current-time)
                                                  (seconds-to-time 30)))
  ==> "2012-02-13 10:07:11 UTC"

Formatting elapsed time in years, days, hours, minutes and seconds.

  (format-seconds "%Y %D %h:%m:%s" (1- (* 367 24 3600)))
  ==> "1 year 1 day 23:59:59"

Find the days between two dates.

  (let ((days1 (time-to-days (date-to-time "Tue, 27-Sep-83 12:35:59 EST")))
        (days2 (time-to-days (date-to-time "2009-02-13 23:31:30 UTC"))))
    (- days2 days1))
  ==> 9271

Getting the day in the year.

  (time-to-day-in-year (current-time))
  ==> 44

Build a date based on the day of the year.

  (format-time-string "%j"
                      (encode-time 0 0 0 44 1 2012))
  ==> "044"

Timers

TODO

Sequences

Datatypes used to represent sequences of things:

    _____________________________________________
   |                                             |
   |          Sequence                           |
   |  ______   ________________________________  |
   | |      | |                                | |
   | | List | |             Array              | |
   | |      | |    ________       ________     | |
   | |______| |   |        |     |        |    | |
   |          |   | Vector |     | String |    | |
   |          |   |________|     |________|    | |
   |          |  ____________   _____________  | |
   |          | |            | |             | | |
   |          | | Char-table | | Bool-vector | | |
   |          | |____________| |_____________| | |
   |          |________________________________| |
   |_____________________________________________|


Lists

List basics are explained on ListStructure. Lists can shrink and grow, but access to elements towards the end of the list is slow if the list is long.

Use `cons' to append a new element to the front of a list. Use `nth' to access an element of the list.

    (let ((words '("fight" "foo" "for" "food!")))
      (when (string= "foo" (nth 1 words))
        (setq words (cons "bar" words)))
      words)
    ==> ("bar" "fight" "foo" "for" "food!")

See ListModification for more ways of changing a list.

Iteration:

    (let ((result))
      (dolist (word '("fight" "foo" "for" "food!"))
        (when (string-match "o" word)
          (setq result (cons word result))))
      (nreverse result))
    ==> ("foo" "for" "food!")

Note how `cons' adds an element to the front of the list, so that usually the list has to be reversed after the loop. `nreverse' is particularly efficient because it does this destructively by swiveling pointers around. See DestructiveOperations for more about this.

Copying:

Use `copy-sequence' to make a copy of a list that won't change the elements of the original.

    (let* ((orig '((1 2) (3 4)))
           (copy (copy-sequence orig)))
      (setcdr copy '((5 6)))
      (list orig copy))
    ==> (((1 2) (3 4)) ((1 2) (5 6)))

However, the elements in the copy are still from the original.

    (let* ((orig '((1 2) (3 4)))
           (copy (copy-sequence orig)))
      (setcdr (cadr copy) '(0))
      (list orig copy))
    ==> (((1 2) (3 0)) ((1 2) (3 0)))

The function `copy-tree' is the recursive version of `copy-sequence'.

    (let* ((orig '((1 2) (3 4)))
           (copy (copy-tree orig)))
      (setcdr (cadr copy) '(0))
      (list orig copy))
    ==> (((1 2) (3 4)) ((1 2) (3 0)))

Filtering:

Emacs Lisp doesn't come with a `filter' function to keep elements that satisfy a conditional and excise the elements that do not satisfy it. One can use `mapcar' to iterate over a list with a conditional, and then use `delq' to remove the `nil' values.

  (defun my-filter (condp lst)
    (delq nil
          (mapcar (lambda (x) (and (funcall condp x) x)) lst)))

Therefore,

  (my-filter 'identity my-list)

is equivalent to

  (delq nil my-list)

For example:

  (let ((num-list '(1 'a 2 "nil" 3 nil 4)))
    (my-filter 'numberp num-list))
  ==> (1 2 3 4)

Actually the package cl-seq contains the functions `remove-if' and `remove-if-not'. The latter can be used instead of `my-filter'.

  (let ((num-list '(1 'a 2 "nil" 3 nil 4)))
    (remove-if-not 'numberp num-list))
  ==> (1 2 3 4)

  (let ((num-list '(1 'a 2 "nil" 3 nil 4)))
    (remove-if 'numberp num-list))
  ==> ((quote a) "nil" nil)

As an example here is the quick sort algorithm:

  (defun quicksort (lst)
    "Implement the quicksort algorithm."
    (if (null lst) nil
      (let* ((spl (car lst))
             (rst (cdr lst))
             (smalp (lambda (x)
                   (< x spl))))
        (append (quicksort (remove-if-not smalp rst))
                (list spl)
                (quicksort (remove-if smalp rst))))))

  (quicksort '(5 7 1 3 -9 8 7 -4 0))
  ==> (-9 -4 0 1 3 5 7 7 8)

Tranposing:

Convert multiple lists into a list

 ((lambda (&rest args)
    (mapcar (lambda (n)
              (delq nil (mapcar (lambda (arg) (nth n arg)) args)))
            (number-sequence 0 (1- (apply 'max (mapcar 'length args))))))
  '(1 2 3) '(a b c) '(A B C))
  ==> ((1 a A) (2 b B) (3 c C))

A more concise version is possible with the the higher-arity version of mapcar available with the `cl' library.

  ((lambda (&rest args)
     (apply (function mapcar*) (function list) args))
   '(1 2 3) '(a b c) '(A B C))
  ==> ((1 a A) (2 b B) (3 c C))

Searching:

Simply checking for existence of a value in a list can be done with `member' or `memq'.

  (let ((words '("fight" "foo" "for" "food!")))
    (car (member "for" words)))
  ==> "for"

  (let ((re "\\wo\\b")
        (words '("fight" "foo" "for" "food!")))
    (consp (memq t
             (mapcar (lambda (s) (numberp (string-match re s))) words))))
  ==> t

In the latter, a more efficient algorithm would use a loop (a non-local exit).

Vectors

Vectors are fixed in size but elements can be accessed in constant time.

    (let ((words ["fight" "foo" "for" "food!"]))
      (when (string= "foo" (aref words 1))
        (aset words 1 "bar"))
      words)
    ==> ["fight" "bar" "for" "food!"]

Hashes

Hashes map keys to values. In a way they are similar to alists, except they are more efficient for a large number of keys.

More info is available on the HashMap page.

Storing and retrieving keys and values

By default, hash tables use `eql' to compare keys. This is not appropriate for strings: ##(eql "alex" "alex")## ==> nil. Thus, use `equal' in these cases:

    (let ((nick-table (make-hash-table :test 'equal)))
      (puthash "kensanata" "Alex Schroeder" nick-table)
      (gethash "kensanata" nick-table))
    ==> "Alex Schroeder"

Iterate:

    (let ((nick-table (make-hash-table :test 'equal))
          nicks)
      (puthash "kensanata" "Alex Schroeder" nick-table)
      (puthash "e1f" "Luis Fernandes" nick-table)
      (puthash "pjb" "Pascal J. Bourguignon" nick-table)
      (maphash (lambda (nick real-name)
                 (setq nicks (cons nick nicks)))
               nick-table)
      nicks)
      ==> ("pjb" "e1f" "kensanata")

Sorting keys

Use `maphash' to build up a list of keys, sort it, and then loop through the list:

    (let ((nick-table (make-hash-table :test 'equal))
          nicks)
      (puthash "kensanata" "Alex Schroeder" nick-table)
      (puthash "e1f" "Luis Fernandes" nick-table)
      (puthash "pjb" "Pascal J. Bourguignon" nick-table)
      (maphash (lambda (nick real-name)
                 (setq nicks (cons nick nicks)))
               nick-table)
      (mapcar (lambda (nick)
                (concat nick " => " (gethash nick nick-table)))
              (sort nicks 'string<)))
      ==> ("e1f => Luis Fernandes"
           "kensanata => Alex Schroeder"
           "pjb => Pascal J. Bourguignon")

Files

Read

Processing a file is usually done with a temporary buffer:

 (defun process-file (file)
   "Read the contents of a file into a temp buffer and then do
 something there."
   (when (file-readable-p file)
     (with-temp-buffer
       (insert-file-contents file)
       (goto-char (point-min))
       (while (not (eobp))
       ;; do something here with buffer content
         (forward-line)))))

On the chance that a buffer may already be actively visiting the file, consider using `find-file-noselect'

  (defun file-string (file)
    "Read the contents of a file and return as a string."
    (with-current-buffer (find-file-noselect file)
      (buffer-string)))

Write

To write something to a file you can create a temporary buffer, insert the things to write there and write the buffer contents to a file. The following example read a string and a filename (with completion, but doesn't need to exist, see InteractiveCodeChar F) and write the string to that file.

 (defun write-string-to-file (string file)
   (interactive "sEnter the string: \nFFile to save to: ")
   (with-temp-buffer
     (insert string)
     (when (file-writable-p file)
       (write-region (point-min)
                     (point-max)
                     file))))

Searching

If you don't have grep, then you may need to write some Lisp which can find a match in a file.

  ;; Visit file unless its already open.
  (with-current-buffer (find-file-noselect "~/.emacs")
    (save-excursion ;; Don't change location of point.
      (goto-char (point-min)) ;; From the beginning...
      (if (re-search-forward ".*load-path.*" nil t 1)
          (match-string-no-properties 0)
        (error "Search failed"))))
  ==> "(add-to-list 'load-path \"/usr/share/emacs/site-lisp/\")"

Filter

Locking

Stat

An interface to the kernel's stat(2) is provided by the function file-attributes. The way times are represented may be a bit unexpected, though.

Deleting

  (if (file-exists-p filename)
      (delete-file filename))

Copy, move and rename

Directories

Traversing

    (defun walk-path (dir action)
       "walk DIR executing ACTION with (dir file)"
       (cond ((file-directory-p dir)
              (or (char-equal ?/ (aref dir(1- (length dir))))
                  (setq dir (file-name-as-directory dir)))
              (let ((lst (directory-files dir nil nil t))
                     fullname file)
                (while lst
                  (setq file (car lst))
                  (setq lst (cdr lst))
                  (cond ((member file '("." "..")))
                        (t
                         (and (funcall action dir file)
                              (setq fullname (concat dir file))
                              (file-directory-p fullname)
                              (walk-path fullname action)))))))
             (t
              (funcall action
                       (file-name-directory dir)
                       (file-name-nondirectory dir)))))

    (defun walk-path-visitor (dir file)
       "Called by walk-path for each file found"
       (message (concat  dir file)))

    (walk-path "~/" 'walk-path-visitor)

Note: see also f-entries of f.el to find all files and directories in a path.

Path splitting

Splitting the path can be done with `split-string' and with the slash. Previously, Emacs would determine the character separating directory names with `directory-sep-char'. However, the variable is obselete with Emacs 21.1.

(split-string default-directory "/")
==> ("" "usr" "share" "emacs" "22.2" "lisp" "")

For splitting a path variable, Emacs already has the `parse-colon-path' function.

(parse-colon-path (getenv "PATH"))
==> ("/usr/lib/qt-3.3/bin/" "/usr/kerberos/bin/" "/usr/local/bin/"
"/usr/bin/" "/bin/" "/usr/local/sbin/" "/usr/sbin/" "/sbin/")

Processes

Running a program

Run a command without caring about its output.

  (async-shell-command "emacs")

Run a command and put its output in the current buffer.

  (shell-command "seq 8 12 | sort" t)
  10
  11
  12
  8
  9

Run a command and put its output in a new buffer.

  (shell-command "seq 8 12 | sort"
                 (get-buffer-create "*Standard output*"))

Run a command return its output as a string.

  (shell-command-to-string "seq 8 12 | sort")

XEmacs also comes with `exec-to-string'.

Handling signals

Sockets

Tcp client

Tcp server

Perhaps EmacsEchoServer and EmacsDaytimeServer can be useful here.

Keyboard events

  • Call function bound to key
(funcall (key-binding (kbd "M-TAB")))

or

(call-interactively (key-binding (kbd "M-TAB")))