2018-04-07 07:03:47 +00:00
|
|
|
---
|
|
|
|
contributors:
|
|
|
|
- ["Bastien Guerry", "https://bzg.fr"]
|
|
|
|
- ["Saurabh Sandav", "http://github.com/SaurabhSandav"]
|
|
|
|
translators:
|
|
|
|
- ["Burhanuddin Baharuddin", "https://github.com/burhanloey"]
|
|
|
|
filename: learn-emacs-lisp-ms.el
|
|
|
|
---
|
|
|
|
|
|
|
|
```scheme
|
|
|
|
;; 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,
|
2024-05-13 10:29:03 +00:00
|
|
|
;; yang dipanggil "echo area". Secara umumnya kita akan menggunakan `C-xC-e',
|
2018-04-07 07:03:47 +00:00
|
|
|
;; sebab kita tidak mahu memenuhi buffer dengan teks yang tidak penting.
|
|
|
|
|
|
|
|
;; `setq' menyimpan value ke dalam variable:
|
|
|
|
(setq my-name "Bastien")
|
2024-05-13 10:29:03 +00:00
|
|
|
;; `C-xC-e' => "Bastien" (terpapar di echo area)
|
2018-04-07 07:03:47 +00:00
|
|
|
|
|
|
|
;; `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
|
|
|
|
```
|