Get the code: learn-emacs-lisp.el
;; Ini adalah pengenalan kepada Emacs Lisp dalam masa 15 minit (v0.2d)
;;
;; Mula-mula pastikan anda sudah membaca artikel daripada Peter Norvig ini:
;; http://norvig.com/21-days.html
;;
;; Kemudian install GNU Emacs 24.3:
;;
;; Debian: apt-get install emacs (atau lihat arahan untuk distro anda)
;; OSX: http://emacsformacosx.com/emacs-builds/Emacs-24.3-universal-10.6.8.dmg
;; Windows: http://ftp.gnu.org/gnu/windows/emacs/emacs-24.3-bin-i386.zip
;;
;; Maklumat lanjut boleh didapati di:
;; http://www.gnu.org/software/emacs/#Obtaining
;; Amaran penting:
;;
;; Tutorial ini tidak akan merosakkan komputer anda melainkan jika anda berasa
;; terlalu marah sehingga anda menghempap komputer anda ke lantai. Kalau begitu,
;; saya dengan ini tidak akan bertanggungjawab terhadap apa-apa. Berseronoklah ya!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Buka Emacs.
;;
;; Tekan `q' untuk tutup mesej selamat datang.
;;
;; Sekarang lihat garis kelabu di bahagian bawah window:
;;
;; "*scratch*" ialah nama ruangan untuk anda edit.
;; Ruangan ini disebut sebagai "buffer".
;;
;; Buffer scratch ialah buffer yang default setiap kali Emacs dibuka.
;; Anda bukannya edit file: anda edit buffer yang kemudiannya
;; boleh save ke file.
;;
;; "Lisp interaction (interaksi)" merujuk kepada command yang wujud di sini.
;;
;; Emacs mempunyai beberapa command yang sedia ada dalam setiap buffer,
;; dan sesetengah command yang lain boleh didapati jika sesetengah mode
;; diaktifkan. Di sini kita menggunakan `lisp-interaction-mode', yang
;; mempunyai command untuk menjalankan dan mengendalikan code Elisp.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Semicolon akan menjadikan comment sepanjang baris tersebut.
;;
;; Program Elisp mengandungi symbolic expressions ("sexps"):
(+ 2 2)
;; Symbolic expression di atas dibaca begini "Tambah 2 pada 2".
;; Sexps dilitupi oleh parentheses, dan boleh dalam bentuk nested (parentheses
;; dalam parentheses):
(+ 2 (+ 1 1))
;; Symbolic expression mengandungi atom atau symbolic expression
;; yang lain. Untuk contoh di atas, 1 dan 2 ialah atom,
;; (+ 2 (+ 1 1)) dan (+ 1 1) ialah symbolic expression.
;; Dengan menggunakan `lisp-interaction-mode', anda boleh evaluate
;; (mendapatkan hasil pengiraan) sexps. Letak cursor selepas parenthesis penutup
;; kemudian tekan control dan j ("C-j").
(+ 3 (+ 1 2))
;; ^ cursor di sini
;; `C-j' => 6
;; `C-j' memasukkan jawapan pengiraan ke dalam buffer.
;; `C-xC-e' memaparkan jawapan yang sama di bahagian bawah Emacs,
;; yang dipanggil "echo area". Secara umumnya kita akan menggunakan `C-xC-e',
;; sebab kita tidak mahu memenuhi buffer dengan teks yang tidak penting.
;; `setq' menyimpan value ke dalam variable:
(setq my-name "Bastien")
;; `C-xC-e' => "Bastien" (terpapar di echo area)
;; `insert' akan memasukkan "Hello!" di tempat di mana cursor berada:
(insert "Hello!")
;; `C-xC-e' => "Hello!"
;; Di atas, kita menggunakan `insert' dengan satu argument "Hello!", tetapi
;; kita boleh meletakkan beberapa argument -- di sini kita letak dua:
(insert "Hello" " world!")
;; `C-xC-e' => "Hello world!"
;; Anda boleh menggunakan variable selain string:
(insert "Hello, I am " my-name)
;; `C-xC-e' => "Hello, I am Bastien"
;; Anda boleh menggabungkan sexps untuk membuat function:
(defun hello () (insert "Hello, I am " my-name))
;; `C-xC-e' => hello
;; Anda boleh evaluate function:
(hello)
;; `C-xC-e' => Hello, I am Bastien
;; Parentheses kosong di dalam function bermaksud function tersebut tidak
;; terima argument. Sekarang kita tukar function untuk menerima satu argument.
;; Di sini, argument tersebut dinamakan "name":
(defun hello (name) (insert "Hello " name))
;; `C-xC-e' => hello
;; Sekarang panggil function tersebut dengan string "you" sebagai value
;; untuk argument:
(hello "you")
;; `C-xC-e' => "Hello you"
;; Yay!
;; Tarik nafas.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Sekarang tukar ke buffer baru dengan nama "*test*" di window yang lain:
(switch-to-buffer-other-window "*test*")
;; `C-xC-e'
;; => [The screen has two windows and cursor is in the *test* buffer]
;; Gerakkan mouse ke window atas dan klik kiri untuk pergi balik ke buffer scratch.
;; Cara lain adalah dengan menggunakan `C-xo' (i.e. tekan control-x kemudian
;; tekan o) untuk pergi ke window yang lain.
;; Anda boleh menggabungkan beberapa sexps menggunakan `progn':
(progn
(switch-to-buffer-other-window "*test*")
(hello "you"))
;; `C-xC-e'
;; => [The screen has two windows and cursor is in the *test* buffer]
;; Mulai dari sekarang saya tidak akan beritahu anda untuk tekan `C-xC-e' lagi:
;; buat untuk setiap sexp yang akan datang.
;; Pergi balik ke buffer *scratch* menggunakan mouse atau `C-xo'.
;; Seelok-eloknya padam buffer tersebut:
(progn
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello "there"))
;; Atau pergi balik ke window lain:
(progn
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello "you")
(other-window 1))
;; Anda boleh menetapkan value dengan local variable menggunakan `let':
(let ((local-name "you"))
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello local-name)
(other-window 1))
;; Tidak perlu menggunakan `progn', sebab `let' juga menggabungkan
;; beberapa sexps.
;; Jom format string:
(format "Hello %s!\n" "visitor")
;; %s ialah tempat untuk meletakkan string, digantikan dengan "visitor".
;; \n ialah character untuk membuat baris baru.
;; Jom tukar function kita menggunakan format:
(defun hello (name)
(insert (format "Hello %s!\n" name)))
(hello "you")
;; Jom buat function lain menggunakan `let':
(defun greeting (name)
(let ((your-name "Bastien"))
(insert (format "Hello %s!\n\nI am %s."
name ; argument untuk function
your-name ; variable "Bastien" daripada let
))))
;; Kemudian evaluate:
(greeting "you")
;; Sesetengah function adalah interaktif:
(read-from-minibuffer "Enter your name: ")
;; Function tersebut akan memulangkan kembali apa yang anda masukkan ke prompt.
;; Jom jadikan function `greeting' untuk prompt nama anda:
(defun greeting (from-name)
(let ((your-name (read-from-minibuffer "Enter your name: ")))
(insert (format "Hello!\n\nI am %s and you are %s."
from-name ; argument untuk function
your-name ; variable daripada let, yang dimasukkan dari prompt
))))
(greeting "Bastien")
;; Jom siapkan function dengan memaparkan result di window yang lain:
(defun greeting (from-name)
(let ((your-name (read-from-minibuffer "Enter your name: ")))
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(insert (format "Hello %s!\n\nI am %s." your-name from-name))
(other-window 1)))
;; Test function tersebut:
(greeting "Bastien")
;; Tarik nafas.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Jom simpan senarai nama:
;; Jika anda ingin membuat list(senarai) data, guna ' untuk elak
;; daripada list tersebut evaluate.
(setq list-of-names '("Sarah" "Chloe" "Mathilde"))
;; Dapatkan elemen pertama daripada list menggunakan `car':
(car list-of-names)
;; Dapatkan semua elemen kecuali yang pertama menggunakan `cdr':
(cdr list-of-names)
;; Tambah elemen di awal list menggunakan `push':
(push "Stephanie" list-of-names)
;; NOTA: `car' dan `cdr' tidak ubah suai list, tetapi `push' ya.
;; Perbezaan ini penting: sesetengah function tiada side-effects (kesan sampingan)
;; (seperti `car') dan yang lain ada side-effect (seperti `push').
;; Jom panggil `hello' untuk setiap elemen dalam `list-of-names':
(mapcar 'hello list-of-names)
;; Tukar `greeting' supaya ucapkan hello kepada semua orang dalam `list-of-names':
(defun greeting ()
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(mapcar 'hello list-of-names)
(other-window 1))
(greeting)
;; Ingat lagi function `hello' di atas? Function tersebut mengambil satu
;; argument, iaitu nama. `mapcar' memanggil `hello', kemudian menggunakan setiap
;; nama dalam `list-of-names' sebagai argument untuk function `hello'.
;; Sekarang kita susun sedikit untuk apa yang terpapar di buffer:
(defun replace-hello-by-bonjour ()
(switch-to-buffer-other-window "*test*")
(goto-char (point-min))
(while (search-forward "Hello")
(replace-match "Bonjour"))
(other-window 1))
;; (goto-char (point-min)) akan pergi ke permulaan buffer.
;; (search-forward "Hello") akan mencari string "Hello".
;; (while x y) evaluate sexp(s) y selagi x masih pulangkan sesuatu.
;; Jika x pulangkan `nil', kita akan keluar daripada while loop.
(replace-hello-by-bonjour)
;; Anda akan dapat melihat semua "Hello" dalam buffer *test*
;; ditukarkan dengan "Bonjour".
;; Anda juga akan dapat error: "Search failed: Hello".
;;
;; Bagi mengelakkan error tersebut, anda perlu beritahu `search-forward' sama ada
;; perlu berhenti mencari pada suatu ketika, dan sama ada perlu diam jika
;; tidak jumpa apa yang dicari:
;; (search-forward "Hello" nil 't) selesai masalah:
;; Argument `nil' cakap: carian tidak mengikut kedudukan.
;; Argument `'t' cakap: diam saja jika tidak jumpa apa yang dicari.
;; Kita guna sexp ini di function berikut, barulah tidak keluar error:
(defun hello-to-bonjour ()
(switch-to-buffer-other-window "*test*")
(erase-buffer)
;; Ucap hello pada nama-nama dalam `list-of-names'
(mapcar 'hello list-of-names)
(goto-char (point-min))
;; Ganti "Hello" dengan "Bonjour"
(while (search-forward "Hello" nil 't)
(replace-match "Bonjour"))
(other-window 1))
(hello-to-bonjour)
;; Jom jadikan nama-nama tersebut bold:
(defun boldify-names ()
(switch-to-buffer-other-window "*test*")
(goto-char (point-min))
(while (re-search-forward "Bonjour \\(.+\\)!" nil 't)
(add-text-properties (match-beginning 1)
(match-end 1)
(list 'face 'bold)))
(other-window 1))
;; Function ini memperkenalkan `re-search-forward': anda mencari menggunakan
;; pattern iaitu "regular expression", bukannya mencari string "Bonjour".
;; Regular expression tersebut ialah "Bonjour \\(.+\\)!" dan dibaca begini:
;; string "Bonjour ", dan
;; kumpulan | ini ialah \\( ... \\)
;; mana-mana character | ini ialah .
;; yang boleh berulang | ini ialah +
;; dan string "!".
;; Dah sedia? Test function tersebut!
(boldify-names)
;; `add-text-properties' tambah... ciri-ciri teks, seperti face.
;; OK, kita sudah selesai. Selamat ber-hacking!
;; Jika anda ingin tahu lebih mengenai variable atau function:
;;
;; C-h v a-variable RET
;; C-h f a-function RET
;;
;; Jika anda ingin membaca manual Emacs Lisp menggunakan Emacs:
;;
;; C-h i m elisp RET
;;
;; Jika ingin membaca pengenalan kepada Emacs Lisp secara online:
;; https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html
Got a suggestion? A correction, perhaps? Open an Issue on the GitHub Repo, or make a pull request yourself!
Originally contributed by Bastien Guerry, and updated by 2 contributors.