Sunday, November 12, 2006

Lisp environment

Update: This environment evaluate to this - Pecan Emacs

Средата за разработка е била винаги важна и затова доста си играя с настройките. Все искам кода да е добре подреден, да имам лесен начин да достъпвам документацията, да има code completion и разни други глезотийки. Така и стана с Lisp средата за разработка. Просто задължителните неща са: SBCL, Linux и Emacs. Въпроса е как ще напаснем тези три неща и колко бързо ще свикнем с Emacs.
Аз използвам Debian и нещата, които се инсталират допълнително са:

sudo aptitude install sbcl
sudo aptitude install w3
sudo aptitude install w3-el-e21
sudo aptitude install hyperspec
sudo aptitude install emacs-goodies-el


Другото "важно" е да се смени клавишната комбинация за превключване от/към кирилица. В emacs "alt-shift" e много застъпена, така че "alt-alt"(Gnome default) или "ctrl-ctrl" e за предпочитане.
Аз не обичам много да пиша така,че искам да използвам и history-то на REPL-a. Както в моя shell. Като изплзвам стрелките да виждам какво е било. Затова инсталирам linedit:
> sbcl
* (require :asdf-install)
* (asdf-install:install :linedit)   ;first-time installation only

* (require :linedit)                ;if already installed
* (linedit:install-repl) 

Готови ли сте и за моя dot.emacs:
;;; Can use now Common Lisp functions                                                                                                                                                                
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                                                                                                                                                             

(require 'cl)                                                                                                                                                                                        

;;; Art                                                                                                                                                                                              
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                                                                                                                                                             

(set-background-color "black")                                                                                                                                                                       
(set-face-background 'default "black")                                                                                                                                                               
(set-face-background 'region "black")                                                                                                                                                                
(set-face-foreground 'default "white")                                                                                                                                                               
(set-face-foreground 'region "gray60")                                                                                                                                                               
(set-foreground-color "white")                                                                                                                                                                       
(set-cursor-color "red")                                                                                                                                                                             

;;; System settinngs                                                                                                                                                                                 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                                                                                                                                                             

;; Misc customizations                                                                                                                                                                               
(setq inhibit-startup-message t)        ;no splash screen                                                                                                                                            

(auto-compression-mode t)               ;turn on auto file uncompression                                                                                                                             

(menu-bar-mode 0)                       ;turn off unused UI                                                                                                                                          

(setq tab-width 4)                      ;tab size                                                                                                                                                    

(setq display-time-24hr-format t)       ;display the current time                                                                                                                                    
(display-time)                                                                                                                                                                                       

(global-font-lock-mode t)               ;colorize all buffers                                                                                                                                        
(setq font-lock-maximum-decoration t)                                                                                                                                                                

(show-paren-mode t)                     ;highlight parens                                                                                                                                            
(defconst query-replace-highlight t)    ;highlight during query                                                                                                                                      
(defconst search-highlight t)           ;highlight incremental search                                                                                                                                
(transient-mark-mode t)                 ;higlight the marked region (C-SPC)                                                                                                                          

;; Some useful key bindings                                                                                                                                                                          
(global-set-key [home] 'beginning-of-line)                                                                                                                                                           
(global-set-key [end] 'end-of-line)                                                                                                                                                                  

;; Column & line numbers in mode bar                                                                                                                                                                 
(column-number-mode t)                                                                                                                                                                               
(line-number-mode t)                                                                                                                                                                                 

(setq frame-title-format "%b")          ;set title to buffer name                                                                                                                                    

;; Ediff customizations                                                                                                                                                                              
(defconst ediff-ignore-similar-regions t)                                                                                                                                                            
(defconst ediff-use-last-dir t)                                                                                                                                                                      
(defconst ediff-diff-options " -b ")                                                                                                                                                                 

(setq dired-listing-switches "-l")      ;better list display                                                                                                                                         
(setq ls-lisp-dirs-first t)             ;display dirs first in dired                                                                                                                                 

;; Specify where backup files are stored                                                                                                                                                             
(setq backup-directory-alist (quote ((".*" . "~/.backups"))))                                                                                                                                        

(setq custom-file "~/.emacs-custom.el") ;what to load if error                                                                                                                                       
(load custom-file 'noerror)                                                                                                                                                                          

;; Type brackets in pairs                                                                                                                                                                            
(setq skeleton-pair t)                                                                                                                                                                               
(global-set-key (kbd "[") 'skeleton-pair-insert-maybe)                                                                                                                                               
(global-set-key (kbd "{") 'skeleton-pair-insert-maybe)                                                                                                                                               
(global-set-key (kbd "\"") 'skeleton-pair-insert-maybe)

;;; Common Lisp                                                                                                                                                                                      
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                                                                                                                                                             

;; Specify modes for Lisp file extensions                                                                                                                                                            
(setq auto-mode-alist                                                                                                                                                                                
(append '(                                                                                                                                                                                     
("\\.lisp$" . lisp-mode)                                                                                                                                                             
("\\.lsp$" . lisp-mode)                                                                                                                                                              
("\\.cl$" . lisp-mode)                                                                                                                                                               
("\\.asd$" . lisp-mode)                                                                                                                                                              
("\\.system$" . lisp-mode)                                                                                                                                                           
)auto-mode-alist))                                                                                                                                                                   

;; SLIME and generic Common Lisp.                                                                                                                                                                    
(require 'slime)                                                                                                                                                                                     

(setq slime-edit-definition-fallback-function 'find-tag)                                                                                                                                             

(setq inferior-lisp-program "sbcl"                                                                                                                                                                   
lisp-indent-function 'common-lisp-indent-function                                                                                                                                               
slime-complete-symbol-function 'slime-fuzzy-complete-symbol                                                                                                                                     
common-lisp-hyperspec-root "file:/usr/share/doc/hyperspec/" ;; Debian                                                                                                                           
slime-startup-animation t)                                                                                                                                                                      

(add-hook 'lisp-mode-hook (lambda () (slime-mode t)))                                                                                                                                                
(add-hook 'slime-repl-mode-hook (lambda () (slime-mode t)))                                                                                                                                          
(add-hook 'inferior-lisp-mode-hook (lambda () (inferior-slime-mode t)))                                                                                                                              

(defun customised-lisp-keyboard()                                                                                                                                                                    
(local-set-key [C-tab] 'slime-fuzzy-complete-symbol)                                                                                                                                               
(local-set-key [return] 'newline-and-indent))                                                                                                                                                      

(add-hook 'lisp-mode-hook 'customised-lisp-keyboard)                                                                                                                                                 

(global-set-key "\C-cs" 'slime-selector)                                                                                                                                                             

;; Search in lispdoc.org                                                                                                                                                                             
(defun lispdoc ()                                                                                                                                                                                    
"Searches lispdoc.com for SYMBOL, which is by default the symbol                                                                                                                                   
currently under the curser. Use M-x lispdoc"                                                                                                                                                         
(interactive)                                                                                                                                                                                      
(let* ((word-at-point (word-at-point))                                                                                                                                                             
(symbol-at-point (symbol-at-point))                                                                                                                                                         
(default (symbol-name symbol-at-point))                                                                                                                                                     
(inp (read-from-minibuffer                                                                                                                                                                  
(if (or word-at-point symbol-at-point)                                                                                                                                                
(concat "Symbol (default " default "): ")                                                                                                                                         
"Symbol (no default): "))))                                                                                                                                                      
(if (and (string= inp "") (not word-at-point) (not                                                                                                                                               
symbol-at-point))                                                                                                                              
(message "you didn't enter a symbol!")                                                                                                                                                       
(let ((search-type (read-from-minibuffer                                                                                                                                                       
"full-text (f) or basic (b) search (default b)? ")))                                                                                                                     
(browse-url (concat "http://lispdoc.com?q="                                                                                                                                                  
(if (string= inp "")                                                                                                                                                 
default                                                                                                                                                          
inp)                                                                                                                                                       
"&search="                                                                                                                                                       
(if (string-equal search-type "f")                                                                                                                           
"full+text+search"                                                                                                                                       
"basic+search")))))))
;; Fontify *SLIME Description* buffer                                                                                                                                                                
(defun slime-description-fontify ()                                                                                                                                                                  
"Fontify sections of SLIME Description."                                                                                                                                                           
(with-current-buffer "*SLIME Description*"                                                                                                                                                         
(highlight-regexp                                                                                                                                                                                
(concat "^Function:\\|"                                                                                                                                                                         
"^Macro-function:\\|"                                                                                                                                                                   
"^Its associated name.+?) is\\|"                                                                                                                                                        
"^The .+'s arguments are:\\|"                                                                                                                                                           
"^Function documentation:$\\|"                                                                                                                                                          
"^Its.+\\(is\\|are\\):\\|"                                                                                                                                                              
"^On.+it was compiled from:$")                                                                                                                                                          
'hi-green-b)))                                                                                                                                                                                  

(defadvice slime-show-description (after slime-description-fontify activate)                                                                                                                         
"Fontify sections of SLIME Description."                                                                                                                                                           
(slime-description-fontify))                                                                                                                                                                       

;; Use W3M                                                                                                                                                                                           
(require 'w3m)                                                                                                                                                                                       

(defun w3m-browse-url-other-window (url &optional newwin)                                                                                                                                            
(interactive                                                                                                                                                                                       
(browse-url-interactive-arg "w3m URL: "))                                                                                                                                                         
(let ((pop-up-frames nil))                                                                                                                                                                         
(switch-to-buffer-other-window                                                                                                                                                                   
(w3m-get-buffer-create "*w3m*"))                                                                                                                                                                
(w3m-browse-url url)))                                                                                                                                                                           

(setq browse-url-browser-function                                                                                                                                                                    
(list (cons "^ftp:/.*"  (lambda (url &optional nf)                                                                                                                                             
(call-interactively #'find-file-at-point url)))                                                                                                                      
(cons "."  #'w3m-browse-url-other-window)))                                                                                                                                          

;;; JavaScript                                                                                                                                                                                       
;;; http://web.comhem.se/~u34308910/emacs.html#javascript                                                                                                                                            
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                                                                                                                                                             

(autoload 'javascript-mode "javascript" nil t)                                                                                                                                                       
(add-to-list `auto-mode-alist `("\\.js\\'" . javascript-mode)) 

;;; Helper functions                                                                                                                                                                                 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 


Сега нещата стоят по друг начин, писането на Lisp е песен. Ето и командите, които най-често използвам:
LISP RELATED

; Parants
M-( Prints ()

; Compilation
C-c M-k slime-compile-file
C-c C-c slime-compile-defun

; Evaluation
C-M-x slime-eval-defun

; Documentation
C-c C-d a slime-apropos
C-c C-d h slime-hyperspec-lookup
C-c C-m slime-macroexpand-1

; Cross-reference
C-c C-w r slime-who-references

; Manage Slime
M-x slime start slime
C-c C-c slime compile defun
C-c C-k slime compile and load file
C-c M-g slime quit

C-c C-t slime clear repl
M-p slime prev command
M-n slime next command

M-r slime search prev commands
M-s slime search next commands

C-c C-z go to lisp buffer
C-c C-q close all parens


Ето един много добър линк за Emacs командите: Beyond Emacs Tutorial

Oстана да се добави малко творчество и нещата добиват завършен вид. Горе долу подхода ми е следния:

   1. Всичко(functions, classes, special variables, and constants) го слагам в defun, за да мога лесно да го проверя дали има грешки с С-с С-с. Ако нещо много ме усъмни(да проверя нещо как работи) се прехвърлям на REPL с C-c C-z и го изпълнявам.


   2. Програмата трябва да е lоаdable, сиреч да не се прави нищо допълнително, за да може да се стартира самостоятелно. Ако програмата да кажем, че се казва proba.lisp не ползва допълнителни библиотеки, тогава няма нищо за правене. По-сложнен е другия случай, да иска. Да кажем ползва cl-ppcre. Тогава в кода има:
(defpackage #:proba
(:use #:cl #:cl-ppcre))

Това само по себе си не е достатъчно. Ако направим:
(load (compile-file "proba"))

Получаваме следната грешка: The name "CL-PPCRE" does not designate any package. Ха сега де.
По някакъв начин трябва да съм сигурен, че cl-ppcre ще се зареди и компилира преди моя файл proba.lisp. Най-лесния вариант е да изплзвам ASDF. Създавам един файл proba.asd и в него слагам:
(asdf:defsystem #:proba
:depends-on (#:cl-ppcre)
:components ((:file "proba")))

Ако пък работя в REPL-a ще използвам: ,load-system или (asdf:oos 'asdf:load-op 'proba)


   3. Ами ако проета от един файл нарасне и стане проект? Тогава ще се наложи да разделяме на малки файлове и да ги "вържем" по някакъв начин. Да речем proba.lisp нарасне много и аз реша да го рефакторирам и изкарам някои операции в друг файл - operations.lisp. За целта ще се наложи да работя с пакети. Създавам файла package.lisp и в началото на proba.lisp и operations.lisp указвам, на кой пакет пренадлежат.
(in-package #:proba)

Послендия файл ще изглежда малко променен:
(asdf:defsystem #:proba
:depends-on (#:cl-ppcre)
:components ((:file "package")
(:file "operations"
:depends-on ("package"))
(:file "proba"
:depends-on ("package"
"operations"))))


   4. Приготвям се за финиширане като се попитам: А какви са нещата от проекта, които могат да се използеват дирекно(кои са интерфейсите)? Определям ги и променям пакетната дефиниция в package.cl:
(defpackage #:proba
(:use #:cl #:cl-ppcre)
(:export #:blah
#:blah2
#:useful-thing3
#:*logfile-directory*))


   5. И накрая малко хитрости. Винаги стартирам проета си от неговата директория. Ако съм направил нещо наистина полезно правя нещата още по-глобални като линквам .asd в ~/.sbcl/systems/.
cd ~/.sbcl/system
ln -s ../../Lisp/proba/proba.asd .

Така много лесно мога да си ползвам проета отвсякъде като пиша просто:(require 'proba). Или ако от друг проект ми притрябват моите "полезни" неща от proba. Просто:
(asdf:defsystem #:useful_thing3
:depends-on (#:proba #:drakma)
:components (...))


В общи линии така върви моя Лисп път.

2 comments:

Anonymous said...

Download asdf.
http://www.cliki.net/asdf

You should eventually get an asdf.lisp file.

Put in your ~/.clisprc (or ~/sbcl/system):

(load "/path/to/your/asdf.lisp")

and launch clisp.

Then, you should indicate asdf where to find the .asd file:

(pushnew "/path/to/the/directory/where/the/system/definition/file/is/"
asdf:*central-registry*)

and finally you can load the system:

(asdf:operate 'asdf:load-op :opengl)

Anonymous said...

.emacs-a е доста голям, защо не опиташ нещо такова. Всяка отделна конфигурация да е в отделен файл и тогава в .emacs-a ще имаш:


(setq files-to-load
'(tweaks
appearance
clojure
slime
;arc
paredit
w3m
ecb
js2
psgml
matlab
magit
markdown))

(defun load-config (f)
(load (concat "~/.emacs.d/"
(symbol-name f)
".el")))

(mapcar 'load-config files-to-load)

algorithms (1) cpp (3) cv (1) daily (4) emacs (2) freebsd (4) java (3) javascript (1) JSON (1) linux (2) Lisp (7) misc (8) programming (16) Python (4) SICP (1) source control (4) sql (1) думи (8)