Share this page

Learn X in Y minutes

Where X=Emacs Lisp

;; 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.