چگونه ایمکسمان را تربیت کنیم (قسمت اول)
احتمالا هم دیگر معلوم است که چند وقتی است دارم عادت میکنم که با ایمکس کار کنم. شاید برای باقی ملت این هم جزء همان جو گرفتنهای اولیه باشد. ولی این بار اینطور نیست. موضوع ناموسی شده! بیشتر از تمام نرمافزارهایی که در طول عمرم با آنها سر و کار داشتهام، دوست دارم با ایمکس کار کنم. ولی این یکی تمام دانستههایم را، عادتهایم را، علایقم را، بود و نبودم را به چالش میکشد! چطور؟ در اینجا با نرمافزاری طرف هستم که بسیاری چیز دارد که من نمیخواهم، و بسیاری چیز دیگر ندارد که من میخواهم، (که دقیقا عکس همین عبارت هم صادق است). حالا نکتهاش اینجاست که این نرمافزار این قابلیت را به من میدهد که تبدیلش کنم به همان چیزی که میخواهم! چطور؟ elisp!
در این مدت دو سه ماهه که با ایمکس سر و کله زدهام، منابعی یافتهام (که در پست بعدی سعی میکنم معرفیشان کنم) که کمکم کرده تا اندکی این زبان و تنظیمات (زیادزیادزیاد) ایمکس را درک کنم. همین درک اندک کلی هیجان هم برایم به همراه آورده. این است که میخواهم تا تمام شدن دوره کردن این منابعم، هر چیزی که به نظرم جالب و کاربردی میآید را تحت یک سری مقاله منتشر کنم. باشد که برای آیندگان مفید واقع شود ;-)
پیشنیاز
من اینطور فرض میکنم که خوانندهٔ عادی این متن تا الان حداقل کلیدهای میانبر ایمکس را حفظ کرده، بلد است Frame کدام است، یا چطور بین پنجرهها جابجا شود یا پکیجی نصب کند! در اینجا قرار است ما یاد بگیریم که چطور پکیجهای خودمان را بسازیم ;-)
یک نکته
قرار نیست که در مورد هر دستوری که استفاده میکنم کامل توضیح دهم. گاهی میشود که تنها به یک توضیح کوتاه و نشان دادن عملکرد دستور بسنده میکنم. اینطوری سعی دارم که بیشتر تمرکز را بگذارم روی هدف اصلی مطلب.
ساختار دستورات elisp
اولین نکته در مورد لیسپ (یا در این مورد elisp) این است که تمامی دستوراتش باید داخل پرانتز نوشته شود. یعنی فرمی به صورت زیر:
(command)
نکتهٔ بعد نوع نوشتن دستورات است. در زبانهایی مانند C یا Python یا دیگر زبانهایی که تا الان دیده بودم (جز اسمبلی) دستورات تقریبا به صورت عامیانه نوشته میشد. مثلا ۲ + ۳ را دقیقا همینطور مینوشتیم. ولی در لیسپ باید این ترتیب رعایت شود: ۱- عملگر ۲- آرگومانها. یعنی برای دستور ۲ + ۳ داریم:
(+ 2 3)
خوب مزیتش میشود این که دیگر برای ۲ + ۳ + ۴ + ۵ + ۶ فقط مینویسیم:
(+ 2 3 4 5 6 )
و دیگر خبری هم از دردسر اولویت عملگرها نمیشود. مثلا:
(+ 3 (* 4 5)) که جوابش میشود ۲۳
(* (+ 3 4) 5) که جوابش میشود ۳۵
یا مثلا اگر قرار بود قبلا تابعی را با آرگومانهایش به صورت زیر فرا بخوانیم:
foo (3 + 4, 5 + 6)
الان کارمان به این صورت انجام میگیرد:
(foo (+ 3 4) (+ 5 6))
ساده بود؟ خوب نکتهٔ بعدی لیستها هستند. که به صورت زیر تعریف میشوند:
(hello there) که لیست شامل دو نشانه است.
(1 2 xyz ") که لیستی شامل دو عدد و یک رشته است.
(a (b c)) که لیستی شامل یک نشانه و یک زیر لیست است.
() که یک لیست خالی است.
خوب تنها نکتهای که باقی میماند و از باقی نکتهها مهمتر است کامنت است! در لیسپ کامنتها بعد از «;» نوشته میشوند. هر وقت این علامت ظاهر شد تا انتهای خط به عنوان کامنت توسط مفسر لیسپ کامنت طلقی شده و نادیده گرفته میشود.
نکات پایهای در مورد نحو لیسپ را تا اینجای کار دیدیم. نکات ضریفتر و کاربردیتری هم وجود دارد که در ادامهٔ مقالات و وقتی به آنها نیاز شد مطالعهشان میکنیم.
کلیدها در elisp
برای این که به ایمکس بفمانیم با فشرده شدن فلان کلید فلان کار را انجام بده، باید اول بدانیم که چطور یک کلید را تعریف کنیم. کلیدها در حالت عادی بین دو کتیشن "" و به صورت یک رشته تعریف میشوند. مثلا در مثلا زیر ما سه کلید xyz را تعریف میکنیم:
"xyz"
اما نکته اینجاست که چطور کلیدهای Ctrl و Alt را به ایمکس حالی کنیم! این کلیدها عملا با حرف C بزرگ برای Ctrl و M برای کلید Meta یا همان Alt شناخته میشوند. اما اگر ما "M" یا "C" استفاده کنیم، حروف M بزرگ و C بزرگ را معرفی کردهایم و نه Ctrl و Alt را. اینجاست که از علامت شکنندهٔ «\» استفاده میکنیم. (اگر قبلا برنامه نویسی کرده باشید احتمالا خوب از مزایایش با خبرید). و داریم:
"\M"
خوب دیگر کارمان راحت شد. فقط یک نکتهٔ کوچک دیگر باقی میماند. این که چطور فرق بین پایین نگه داشتن کلید Ctrl و زدن x و بعد رها کردن هر دویشان و زدن مثلا کلید l را تعیین کنیم. این عبارت در ایمکس به صورت C-x l نوشته میشود. خوب احتمالا حدس زدهاید:
"\C-x l"
نکته: برای نمایش کلید Esc به صورت "\e" و برای کلید Del از عبارت "\C-?" استفاده میکنیم.
امتحان تغییرات ایجاد شده روی ایمکس
خوب ما تغییرات و توابع ایجاد شدهیمان را در فایل .emacs موجود در پوشهٔ خانگیمان وارد میکنیم. اما چطور تغییرات را اعمال کنیم؟ سادهترین راه و اولین چیزی که احتمالا به ذهنتان میرسد بستن ایمکس و باز کردن دوبارهاش است. روش خوبی است، ولی آسانتر از این هم پیدا میشود. مثلا زدن دستور زیر:
M-x eval-buffer RET
این دستور تقریبا کار راه انداز است. ولی موقعی که فایل کانفیگتان زیادی سنگین شد، و مثلا رسید به هزار خط (میرسد، غم مخورید ;-)) ممکن است زمان زیادی تا لود شدن طول بکشد. پس منطقی نیست که همهٔ فایل را لود کنیم. چطور است که فقط تغییرات ایجاد شده را دوباره لود کنیم؟ (همین انتخابهای گسترده است که دارد عاشق دل باختهام میکند ;-)) پس راه بعدیمان میشود این که برویم در انتهای خطی که تغییر جدیدمان را اعمال کردهایم بایستیم و کلیدهای زیر را وارد کنیم:
C-x C-e
کار این کلیدها این است که خط قبل از علامت نشانگر را پردازش کنند و در کانفیگ حاضر ایمکس مداخله دهند.
خوب ما میدانیم که این کلیدها حتما یک دستوری را پشت پردهٔ ایمکس اجرا میکنند. میخواهید بدانید که آن دستور چیست؟ اول دستور زیر را وارد کنید:
M-x describe-key
و سپس کلیدهای ترکیبیتان را وارد کنید. یک توضیح کامل از دستور مورد استفاده در پنجرهای جدید روبرویتان ظاهر میشود.
خوب دیگر بحث تئوری کافیست. بهتر است برویم سراغ کمی کار عملی.
حل مشکل پرش بین پنجرههای ایمکس
مساله خیلی ساده است. تا به حال حتما پیش آمده که چند تا پنجرهٔ باز دارید و میخواهید بینشان سوئیچ کرده و به پنجرهٔ سمت راستتان بروید. خوب برای این کار از کلیدهای C-x o استفاده میکنید. حالا اگر بخواهید به سمت چپ بروید چطور؟ شاید وقتی دو پنجرهٔ باز دارید کار سختی نباشد و با تکرار همین دستور به سمت چپی هدایت شوید. ولی در پنج پنجرهٔ باز چطور؟ چهار بار باید این کار را انجام دهید. یا شاید اصلا نظرتان به نظر من نزدیک باشد که C-x o یک کمی را دست نباشد. (این نظر شخصیست) طی ادامهٔ این شماره از مقاله و شمارهٔ بعد قصد حل این مساله را داریم. یعنی اول این که کلیدهای C-x o که وظیفهشان پرش به پنجرهٔ بعدی است را با یک کلید دلخواه جایگزین کنیم. و سپس یک کلید که بتواند عکس همین عمل را یعنی پرش به پنجرهٔ سمت چپ را انجام دهد هم بسازیم.
خوب تا اینجای کار میدانیم چه کارهایی میتوان انجام داد و میدانیم که چه مسالهای پیش رو داریم. پس حل تقریبا آسان است. پس شروع میکنیم. اولین کاری که باید بکنیم این است که بفهمیم با چه دستوری میتوان یک کلید میانبر تعریف کرد. دستور مورد نظر ما global-set-key نام دارد. که به صورت زیر میتواند مورد استفاده قرار گیرد:
(global-set-key KEY COMMAND)
خوب حالا دو چیز بعدی که نیاز داریم هم سر و کلهشان پیدا شد. اولی KEY که منظور کلیدهای میانبر مورد نظرمان است، و دیگری COMMAND که دستوری است که میخواهیم با زدن کلیدهای KEY اجرا شود. خوب چه کلیدهایی را انتخاب کنیم؟ این کتابی که من به عنوان راهنما استفاده میکنم کلیدهای C-x C-n را برای پرش به پنجرهٔ سمت راست و C-x C-p را برای پرش به پنجرهٔ سمت چپ انتخاب کرده. انتخاب جالبی به نظر میرسد و من هم همانها را انتخاب میکنم. اما نکته اینجاست که اول ببینیم نکند یادمان نباشد و بزنیم یکی از کلیدهای میانبر حاضرمان را ناکار کنیم. پس میآییم و با describe-key ته و توی قضیه را میریزیم روی دایره. هاها بعله، این کلیدها به ترتیب برای فراخوانی set-goal-column و mark-page استفاده میشوند. که خوب حتی روحم هم از وجود چنین چیزهایی خبردار نبود. پس فکر نمیکنم که این تغییر اشکالی داشته باشد. ضمن این که هر وقت که بخواهم میتوانم دستورام را بعد از M-x وارد و اجرا کنم D:. پس کلیدها هم تعیین شد و تا اینجای کار دستور مربوط به پرشمان به پنجرهٔ سمت راست چیزی شبیه به این شده:
(global-set-key "\C-x\C-n" COMMAND)
و حالا بخش آخر داستان. حتما دیگر یاد گرفتهاید که چطور دستور مربوط به C-x o را که میخواهیم به C-x C-n پیوند بدهیم را پیدا کنید. ما پیدایش کردیم و فهمیدیم که دستوری به اسم other-window را اجرا میکند. پس احتمالا میگویید که دستورمان بعد از تکامل میشود این:
(global-set-key "\C-x\C-n" other-window)
خوب تا حدودی راست گفتهاید. ولی مشکل اینجاست که لیسپ کمی خنگ است و آن دستوری که سمت راست نوشتهایم other-window را هم اجرا میکند و این اصلا خوب نیست. پس باید یک جوری حالیش کنیم که بیخیال این دستور شود و فقط سمت چپی را اجرا کند. این است که یک «'» به تنهایی به کمکمان میآید و دستور میشود:
(global-set-key "\C-x\C-n" 'other-window)
همین کتیشن کوچک در ابتدای دستور از اجرای دستور دوم جلوگیری میکند و دستورمان کامل میشود. حالا میماند که چطور به ایمکس بگوییم که از آن استفاده کند. خوب قبلا در موردش توضیح دادم. مثلا همین الان بروی به آخر خط همین دستور (C-e) و بزنید C-x C-e و تمام. حالا دستورتان را امتحان کنید.
چیزی که باقی میماند این است که کلید C-x C-p را تعریف کنیم تا پس از زدنشان به پنجرهٔ قبلی یا سمت چپیمان برود و چون این کار یک کمی پیچیدهتر است بهتر است آن را برای قسمت بعد بگذاریم. قسمتی که شما تا اینجای داستان را به درستی درک کردهاید ;-)