פורסם ב זה רק קוד, כלים לחיים קלים

zsh, you complete me

קוד: .repo_switch
זמן קריאה: 9 דקות

אז התחלתי עבודה חדשה!
אני עכשיו פנדה גאה בחברת Big Panda! מה שאומר שבזמן הקרוב אתן יכולות לצפות להרבה סיפורי עבודה חדשה, שפות תכנות חדשות, וכמו בפוסט הספציפי הזה – סביבת עבודה חדשה.

ביום הראשון קיבלתי את המחשב החדש שלי – צח, נקי וחף מפשע, והתחלתי להרים עליו סביבה.

מעבר לכל התוכנות, האתרים והקונפיגורציות הדרושות שרשומות במסמך ה-setup בכל חברת הייטק, עוד שלב חשוב מאוד הוא כמובן להפוך את המחשב לשלי – אני הרי לא יכולה לתכנת כמו שצריך אם אין לי את כל קיצורי הדרך שאני רגילה אליהם.

אחד הדברים הראשונים שעשיתי היה כמובן להוריד את zsh, תוכנת ה-shell האהובה עליי, וליצור קובץ פקודות alias.
** זו אמנם לא חובה, אבל אם אני כבר בנושא, לחווית zsh מיטבית אני ממליצה להתקין גם את Oh my zsh **

בעבודה הקודמת שלי עבדתי עם שני פרויקטים גדולים (לצורך פוסט זה, נקרא להם luke ו- leia), לכל אחד מהם היה מאגר קוד יחיד, וסביבת עבודה וירטואלית.
כדי לקצר תהליכים השתמשתי בשתי פקודות שמכינות shell חדש לעבודה בכל אחת מהסביבות – 

alias luk = ”source ~/virtual_envs/luke/bin/activate; cd ~/git_repositories/luke”
alias lei = ”source ~/virtual_envs/leia/bin/activate; cd ~/git_repositories/leia”

ככה בכל פעם שרציתי לעבור לעבוד על סביבה מסוימת יכולתי לעשות זאת בפקודה יחידה. 
שימושי מאוד!

אבל, בעבודה הנוכחית שלי אני עומדת לעבוד בארכיטקטורת מיקרו-סרוויסים, מה שאומר הרבה מאוד מאגרי קוד וסביבות עבודה שצריך לדלג ביניהם בנוחות. 
מן הסתם, זה לא פרקטי להגדיר alias נפרד לעשרות סביבות, ולכן הייתי צריכה לחשוב על שיטה אחרת. 

הפתרון האינטואיטיבי שחשבתי עליו היה לכתוב פונקציית bash, כיוון שזו דרך ממש טבעית ונוחה להוציא לפועל פקודות shell. 

זו הפונקציה הראשונית שכתבתי – 

function repo {
        source ~/virtual_envs/$1/bin/activate; 
        cd ~/git_repositories/$1
}

הפונקציה מקבלת כמשתנה שם של פרויקט, מריצה את הסביבה שלו ונכנסת לתיקיה המתאימה.
לדוגמא, הפקודה – 

repo luke

תריץ את הפקודות – 

source ~/virtual_envs/luke/bin/activate; 
cd ~/git_repositories/luke

בדיוק כמו שה-alias הישן שלי עשה.

עד פה הכל טוב ויפה, אבל כשניסיתי להשתמש בפונקציה כדי לעבור בין הפרויקטים השונים גיליתי בעיה גדולה – הפונקציה אכן פעלה כמו שצריך, אבל חסר לה אחד הדברים הכי שימושיים והכי מובנים מאליהם בפקודות shell – ההשלמה האוטומטית של הפקודה.

אני רגילה במהלך העבודה שלי להקליד רק התחלה של פקודות, ואז ללחוץ על tab עד שאני מוצאת את ההשלמה שרציתי, אבל לפונקציה שלי לא הייתה השלמה אוטומטית, מה שאומר שכדי להשתמש בה הייתי צריכה לזכור בעל פה את השמות והאיותים של הפרויקטים השונים (!that way madness lies).

גיגלתי קצת את הנושא, וניסיתי להגדיר השלמה אוטומטית, אבל לא מצאתי אף מדריך ברור, אז החלטתי להתפשר, ובמקום לייצר השלמה אוטומטית אמיתית לפונקציה שלי, מצאתי את פלאגין המגניב הזה – zsh-autosuggestions. זה פלאגין ממש מוצלח שמשתמש בהיסטוריה של ה-shell כדי להציע השלמות לפקודות שהשתמשתי בהן בעבר.

הלכתי הביתה שמחה וטובת לב, אבל עניין ההשלמה האוטומטית המשיך להציק לי, ומצאתי את עצמי חוקרת עד השעות הקטנות של הלילה בחיפוש אחר תשובה, עד שלבסוף הצלחתי לשלב את הידע ממספר מדריכים, כדי להגיע לפונקציית השלמה עובדת!


השלב הראשון היה להבין איך אני מגדריה פונקצית השלמה ב-zsh.
פונקצית השלמה היא פונקציה שמגדירה כל מיני סוגים של השלמות אפשריות, וכדי ש-zsh ידע להשתמש בה לפונקציה הנכונה צריך להגדיר את הקשר ביניהן כך  – 

compdef _repo repo

רוב המדריכים ציינו שצריך ליצור קובץ נפרד לפונקציית ההשלמה, ולשים אותו בספריית המערכת המתאימה. אבל למטרה שלי זה היה לחלוטין overkill, ולכן הוספתי את ההשלמה באותו קובץ של הפונקציה הראשית שלי.

בניסיונות הראשונים רק רציתי לראות שההשלמה בכלל עובדת, ולכן הלכתי על השלמה סטטית –

function _repo {
        _arguments "1: :(a1, a2, a3)"
}

יש הרבה סוגים של השלמות ודי הלכתי לאיבוד בחיפושים, עד שמצאתי את המדריך הזה שעזר לכוון אותי להשלמה הנוחה ביותר – arguments.

השימוש ב-arguments מחזיר השלמות אופציונליות לכל ארגומנט, ואפשר להשתמש בגרסה הסטטית שלו, כמו שעשיתי למעלה, כדי להציע השלמות לסקריפטים שמקבלים ארגומנטים ספציפיים.

עכשיו כשהקלדתי לטרמינל שלי את הפקודה repo קיבלתי את ההשלמות a1, a2 ו-a3.

עד לפה, עוד הכל היה די פשוט והגיוני. הבעיה היא שאני לא רציתי השלמה סטטית – נכון, הייתי יכולה לנהל רשימה של שמות פרויקטים וזה לא היה סוף העולם, אבל זו לא הייתה אוטומציה מושלמת, ולשם כיוונתי.

מה שרציתי היה שההשלמה תציע לי את כל שמות הקבצים בתיקיית git_repositories שלי, כדי שאני אוכל לבחור מהם. זה אולי נשמע אינטואיטיבי, אבל שוב, הנושא הזה ממש לא מתועד טוב, ולכן התקשתי להבין מה הדרך הנכונה להשיג את המטרה שלי. 

כאן התחיל תהליך די ארוך של ניסוי וטעייה עם שלל קונפיגורציות, פרמטרים וכל טכניקה הזויה שהייתה ל-stackoverflow להציע. 

אני לא אלאה אתכן בכל הניסיונות שלי, אבל בסוף הגעתי לתוצאה סופית שעבדה, והחזירה לי השלמה אוטומטית של כל ספריות הפרויקטים שלי – 

function repo {
        source ~/virtual_envs/$1/bin/activate; 
        cd ~/git_repositories/$1
}
function _repo {
	_arguments "1: :($(ls ~/git_repositories))"
}
compdef _repo repo

השימוש ב-$ מחשב ומחזיר את ערך הפקודה בסוגריים – ls – שמחזירה את רשימת כל הקבצים בתיקייה git_repositories, והרשימה הזו חוזרת בתור השלמה אופציונאלית לפונקציה repo, בול כמו שרציתי!

סה"כ הפיצוח הזה לקח לי די הרבה זמן, אבל אני ממש מרוצה ממנו, כי אני יודעת שעכשיו העבודה עם כל פונקציית bash עתידית שאכתוב תהיה הרבה יותר חלקה. פונקציות shell זה כיף, ואני ממש ממליצה גם לכן לכתוב פונקציות מגניבות משלכן.
קוד לדוגמא בגיט – https://github.com/mgershovitz/maya_solves/blob/master/dot_files/.repo_switch


עדכון נחמד – הפוסט תורגם לאנגלית, ואפשר לקרוא אותו במדיום, אז אתן יכולות לשלוח את כל חברותיכן המתכנתות שלא דוברות עברית לכאן – https://medium.com/bigpanda-engineering/zsh-you-complete-me-4e7eefd1b23

תגובה אחת בנושא “zsh, you complete me

השאר תגובה