From 2d5a14aa4e0814dcadf72c7b77f7ab4bbf3f06f1 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Fri, 25 May 2018 17:51:35 -0400 Subject: [PATCH 01/35] Destroyed everything. Reorganized entire project. --- docs/{META.txt => VERSION.txt} | 0 {share => shells}/bash/bashrc.example | 0 {share => shells}/bash/controller.sh | 0 {share/bash => shells/bash/plugins}/bashmarks-aliases.sh | 0 {share => shells}/fish/config.fish.example | 0 {share => shells}/fish/controller.fish | 0 {share/fish => shells/fish/plugins}/bashmarks-aliases.fish | 0 {share => shells}/zsh/controller.sh | 0 {share/zsh => shells/zsh/plugins}/bashmarks-aliases.sh | 0 {share => shells}/zsh/zshrc.example | 0 source/core/__init__.py | 2 ++ {bin => source/core}/sc-handler.py | 0 {bin => source/core}/sc-init.py | 0 source/main.py | 0 source/tester.py | 0 tests/database-tester.py => source/tests/db.py | 0 test.sh | 4 ---- 17 files changed, 2 insertions(+), 4 deletions(-) rename docs/{META.txt => VERSION.txt} (100%) rename {share => shells}/bash/bashrc.example (100%) rename {share => shells}/bash/controller.sh (100%) rename {share/bash => shells/bash/plugins}/bashmarks-aliases.sh (100%) rename {share => shells}/fish/config.fish.example (100%) rename {share => shells}/fish/controller.fish (100%) rename {share/fish => shells/fish/plugins}/bashmarks-aliases.fish (100%) rename {share => shells}/zsh/controller.sh (100%) rename {share/zsh => shells/zsh/plugins}/bashmarks-aliases.sh (100%) rename {share => shells}/zsh/zshrc.example (100%) create mode 100644 source/core/__init__.py rename {bin => source/core}/sc-handler.py (100%) rename {bin => source/core}/sc-init.py (100%) create mode 100644 source/main.py create mode 100644 source/tester.py rename tests/database-tester.py => source/tests/db.py (100%) delete mode 100755 test.sh diff --git a/docs/META.txt b/docs/VERSION.txt similarity index 100% rename from docs/META.txt rename to docs/VERSION.txt diff --git a/share/bash/bashrc.example b/shells/bash/bashrc.example similarity index 100% rename from share/bash/bashrc.example rename to shells/bash/bashrc.example diff --git a/share/bash/controller.sh b/shells/bash/controller.sh similarity index 100% rename from share/bash/controller.sh rename to shells/bash/controller.sh diff --git a/share/bash/bashmarks-aliases.sh b/shells/bash/plugins/bashmarks-aliases.sh similarity index 100% rename from share/bash/bashmarks-aliases.sh rename to shells/bash/plugins/bashmarks-aliases.sh diff --git a/share/fish/config.fish.example b/shells/fish/config.fish.example similarity index 100% rename from share/fish/config.fish.example rename to shells/fish/config.fish.example diff --git a/share/fish/controller.fish b/shells/fish/controller.fish similarity index 100% rename from share/fish/controller.fish rename to shells/fish/controller.fish diff --git a/share/fish/bashmarks-aliases.fish b/shells/fish/plugins/bashmarks-aliases.fish similarity index 100% rename from share/fish/bashmarks-aliases.fish rename to shells/fish/plugins/bashmarks-aliases.fish diff --git a/share/zsh/controller.sh b/shells/zsh/controller.sh similarity index 100% rename from share/zsh/controller.sh rename to shells/zsh/controller.sh diff --git a/share/zsh/bashmarks-aliases.sh b/shells/zsh/plugins/bashmarks-aliases.sh similarity index 100% rename from share/zsh/bashmarks-aliases.sh rename to shells/zsh/plugins/bashmarks-aliases.sh diff --git a/share/zsh/zshrc.example b/shells/zsh/zshrc.example similarity index 100% rename from share/zsh/zshrc.example rename to shells/zsh/zshrc.example diff --git a/source/core/__init__.py b/source/core/__init__.py new file mode 100644 index 0000000..4005bfb --- /dev/null +++ b/source/core/__init__.py @@ -0,0 +1,2 @@ +from database import * +from parser import * diff --git a/bin/sc-handler.py b/source/core/sc-handler.py similarity index 100% rename from bin/sc-handler.py rename to source/core/sc-handler.py diff --git a/bin/sc-init.py b/source/core/sc-init.py similarity index 100% rename from bin/sc-init.py rename to source/core/sc-init.py diff --git a/source/main.py b/source/main.py new file mode 100644 index 0000000..e69de29 diff --git a/source/tester.py b/source/tester.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/database-tester.py b/source/tests/db.py similarity index 100% rename from tests/database-tester.py rename to source/tests/db.py diff --git a/test.sh b/test.sh deleted file mode 100755 index f7a7cb4..0000000 --- a/test.sh +++ /dev/null @@ -1,4 +0,0 @@ -# Unfinished -cp tests/database-tester.py bin/ -python3 bin/database-tester.py -rm bin/database-tester.py -r bin/__pycache__/ From 0a25590e4e67ac8832d2b1d7d2b391f974a2c589 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sat, 26 May 2018 19:12:54 -0400 Subject: [PATCH 02/35] adding tests for commands --- source/tests/__init__.py | 0 .../tests/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 145 bytes source/tests/__pycache__/db.cpython-36.pyc | Bin 0 -> 5467 bytes .../__pycache__/test_commands.cpython-36.pyc | Bin 0 -> 1486 bytes .../__pycache__/test_database.cpython-36.pyc | Bin 0 -> 5478 bytes source/tests/db.py | 11 +- source/tests/test_commands.py | 42 +++++ source/tests/test_database.py | 143 ++++++++++++++++++ 8 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 source/tests/__init__.py create mode 100644 source/tests/__pycache__/__init__.cpython-36.pyc create mode 100644 source/tests/__pycache__/db.cpython-36.pyc create mode 100644 source/tests/__pycache__/test_commands.cpython-36.pyc create mode 100644 source/tests/__pycache__/test_database.cpython-36.pyc create mode 100644 source/tests/test_commands.py create mode 100644 source/tests/test_database.py diff --git a/source/tests/__init__.py b/source/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/tests/__pycache__/__init__.cpython-36.pyc b/source/tests/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f61f473fee8673cfb6a2f5c4cbcfdf0691242c87 GIT binary patch literal 145 zcmXr!<>lg-$Pvu|1dl-k3@`#24nSPY0whuxf*CX!{Z=v*frJsnFFXB={M=OilJw%l zTZlX-=vg$lPKeW&i;C?ISz@ literal 0 HcmV?d00001 diff --git a/source/tests/__pycache__/db.cpython-36.pyc b/source/tests/__pycache__/db.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1eadf1b0c59c3094ad97e283076fc75176d0527b GIT binary patch literal 5467 zcmbVQ+j85+8ODhuNKuE#ie<-6i#lm!IyNa=uIo-bnIw{(aqB9stxQwq0)eaD&pMm?&a0S0c!IZATRHnIVSCzHa)lloMJ~q0BN^OQ)8CSbiRryk36;^$z zuqxL^wIjW2vKljAD&0EM9xJVSxAC>tA9}-Jz}b(OTiI=?vGLFe2dGzfoY2{K0>15g z9`A)i-|JDI?5C$U!C!I16-c9yBwhWL(uaIZe`$6NX0Qsr2Ps$$b(NW{j=IJgYyq{& znrsnuoh`9t)D5=6-a);>N9fdYQe)E}&j{r5H;09jxVI zyp+ki$AgfI=#$T_p6djG6%L%x3h|O|xy90DS^cReSLL|F@VVvpEx_Qqy=fSV5>P$5ecu~~L(CX)`AIG;$75E&LyPgg zGj&7D^?MG1Y4v@P3)7C)I^lTIA=$HawAub{%ZMw%bize!+BTR9ZChv%BQ_?)k{%lY zcl)v7h=U-mJ~^Zs+|_McuHCl(q5SuDXW)-{Cp-w8-XP$e9pO**{b!wRpYhJ)Y|Wj( zpNby35l|hr-<~`dReY&VD<=(9v?VQC&xA`rZf8Q}fDv$XZQ}~gp?IN;0N9Zt*5sXb zgl49KF)jLFhr2v1G9vu3H4*&j(4T@VNYdVI>7s^d;@U8<`$Lz<)v4zWy(cYIn0POm zd9g^{DuDXyiKHtLx}ipEb3)55xx@tLDlRGUcen{jX3+Zj!Jk0wNDKAkd1M?ZMwn16@f3}8h^&2_dKoz-@_RAR(Itq z=$uqj$Gs~A`WSs@=(aQodaSYidEn0xU7MSm$;x*5VJ6cWz7!Bkiq=%uR83vMKibHe zQ$>2y=oy+Cm_*XFhntKhvciq&K^E1z!ewyN>^#Gg!E z5Krwx?;yiKawOS`mlLAy2fKc_3r&cd2RyVh&lES0n?0VgAp%qklkHDp~`aNItG)vXE$8v`-mcPRT7ymr?Ti5{OVo6qmFktw2Ib$_ab8c;6%S zOwsg0AC{59-;NEEDQZXuNpf{|Ta|)tFAwG593*i8q6l)cW3%lGlk41td@ zG9cGS3fokp%UJ`7kwa55upF4rFi{T7)j43)k)F5_=&3<&CDYrJ5EY!I)h9<;fJpol zADKm{dW?{;(2AHTz3nkfqy}#b)7B$C_Mh^5_-=;ARSXZv1t&1U&z1vKGPE412bd}Y zs$kS@37JlDV;-dZIW_JRpj~~K8e9P^X};2&`|dOt@402v$aIEZr-E$Gq|=eRl(>?a)KXZjWe1xJFXn7JD4rQ>hDtc&k3>j zdqhwcB3%k<@C5;xS@8{){Ozqzk_CyTn)KahJjiP);$`X*ZZ+ng^Ra zLMLyK7Nz~OfK`Hjce*7SW~dx(5UdW%3Rc!2#zVK*fmYi#t~z>)4X zs?+T!7C1<9q^%N%yGH7riD9f2Q=+WtG>X;})xQmS?@4h}dcGxkRwO-?2+6;; zs;;WhN2Nw^VuBfRax?gMIXVB5UPWf31+P+}t#V3Gg$yFU$Cf!Ii^f#R`=mV+lc<6y zHSM3hsp3sCN*!UV&+4o(XJ8Be*PL}41qG<33Q&UrG^0BN`PvR9vTmO!_Uf z;#XM2j8+UVPVV)y^!QjWRy7zf^kY?jgNB@#Y(_(J^X=jjCFXnQSVJfe%uO@d!H{Xr zrqjfK7Tk+Yu@c>&i~^|*PCn?=04EYB4AlWiurUkCHf=PL^k-L+Ad!cAoXvp}kO|t; zP`xN^?fPbV5Hf>oKyIs$1W8l-xXjevFut`)=OCUl=C&O-Y0JJ!{T#yT(m|rvdWTOI%Fb7u>DIIju!HL)b&nca>s9ObR_kKK3mvqX-Pt7VTrhx zd0oktQ)XNpjRrwS0kJuSLXt?`8l7Re!+m)o(OvApZM0ds=}xk4 plhk+{XOI%PL?3ew5;o+ik9jB2gik4zk^$FHP5fcG+Uj2v^?wRtDeV9N literal 0 HcmV?d00001 diff --git a/source/tests/__pycache__/test_commands.cpython-36.pyc b/source/tests/__pycache__/test_commands.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b0d4e6fe8dd38e8d73f1e50c2ca39413bcbea0d GIT binary patch literal 1486 zcmZ`(O^@6(7`B~c;z?&{fp%NkOOFJknMGU?LQD%<2~82ZTA^^T(%8F`*?bJP)7_5t zmX&%c|AU`|uUz0SaN>QFjJ6BgNPb?&pU?aFvA>*7qd)!{|MIy{$e-k$5B|wFP|Pua zB$5^+=_5@j{Ii0UUgA+~^9ui{k3kpxVpN7nNQvSyXlXM3p2(35uZRp4T}Q2#+>!K< z#Qbkqn=E7M_@UK#b$aju>hv>!V3XQxwS7Fd3aDa;*(?yd?Pnq z)Mc4g@<yyPu@v7O{P27m znb&4!tgIUw(_~VqbCIP*k-jWc)~yN4VnpXGpqPCC3o^7s--F0nLW%VdFUi`s!FnV? zqIKAkmbE*X*fJR}>*M~MW9}xyk&BbOP%g|(Ul}!3kymH#?vUcn87>0Rl(t8@QI2Wl zMndkr~_P{;UI9dDZtcMvX5m6l2f7YR|;vMI1W5#rY-E&86$ z>>7>Or--)H4-p;#xan}YGI&;mxTYUdeFq1j5i~w25fp>K*(Bg}Z_H`e9#1+s65hZq z#%vwSYmK`k(e?F7rd@q-s7a=~(X+D7&N_OD|6^)b9ZARWpcW~(7q`A<^^Q6DSAD); RZFnB*raT^t~>E$l2CTWt*f}UGEJEa1mZ45(jdSr zK+8tm!;C!RtJZIj`#wc)`V_cquks4L>h~`W0x4MIDLL2$um}A6egA29dugfh!$0fa z{&7Q5{-YFr2JSz@75o|nQ@RRMndYipRn}TpL#?~|*ytK6wHa<@T((D?{U=@51Qm`88Dl=Ifb&WOH0&0^j zvL)1Yw#-&gH`pqB2lWD5V`oq=vUT<@>LqrTZJ=Ie=h%7FE9^aX0rl!D#ZbEMU@aHp zQ6nnD#sm$&n>@i0S4FYO~atoQ`5!K zBI#Pe679&)OZI@%H0v&F#RS ziXOTVP_nc$q*{~bqKc2!2?SC`MO)UQjZDr2?^Y&XjvZl0R|i*c4#f*)1niC!u`chl zBQ!G=XL zuf9-6l~I+nKvjOPc$T4rwUPOxAs(t&ol4_jY}EP(#nZk)AEy6Kd8B-U8Y?%Vc1Lgr zD|al9ALjaC9S*qQmhS-)p9kJ%Xz^!5fUp@|e0=x8-OiqM#k%+CtKV9%QJ34Pas|c1sebgC2v(Ap+2nJcEw(xgy*3ryYlTXBUJbv>zWbmh~{-DWl3nD~>a z3*xDL=pAGjNRA|1@k&C}{b1J*ccBUK;sFos%t^(I$ITv3*$`)e#ylMt5H3)A)^KK7sTd-!gK##Iat$OR`b!OxZhRWh_3s0WxT z1FB%uEeV-Uabq5&{5du56QEsvm>OIGENQ;docr!H806_eOHjBH=TX2`#e4KXkteas zitH@j3bXY@wQk;{`7^k-F;&6nOmc!BWQ{YLH9M{xt2>x2!|L_rM5*RfNgGv;6sDcW zMK*W>n`@l`?>zx9#SpX^0m><+I&BP7O!Huq zN9g1Y(xS9~7LY1RV!~zFNj#AQO3$}M&#I({5+V85 z*3>mM`l!?hPE0UEPHqPOE+^+-(yPd9wBS`Lv};ZYs*pkC_t-tBWYL%^d7rdrViHvl zrKbI}H&whzMyVt0`dOVd<_v7%|C+N-qo4q_Q~_#GfW_$3FH00)pJR`Utqnql(?`BE z?*{`b^;WcD5F4XgF_HHK)~U|2}5;25^T&uvP~O}B>mZyBuM1p9%pl)1Z0Bt zG*mB2Tf4rMo{7vL8<5*7Btg>DJ}xu0H;iwt(UFMfjJa*c4ci|3Y>JJr9WUB8B^GH* z&9<4}!>I{n@q!Fo;CM|DKcV7hR9vFsLnnMw~41oP#G z4bVn1cR^?fMzH-$7>*Y5ebn`KVsgiCZnP!!g+ANZY-veCXJLuB zmw8>umQ!lWD}o_OCmPb9%C|U*xQzxuM*&@P3WX$*x-~k^a) Date: Sat, 26 May 2018 19:13:26 -0400 Subject: [PATCH 03/35] clean up dir --- .../tests/__pycache__/__init__.cpython-36.pyc | Bin 145 -> 0 bytes source/tests/__pycache__/db.cpython-36.pyc | Bin 5467 -> 0 bytes .../__pycache__/test_commands.cpython-36.pyc | Bin 1486 -> 0 bytes .../__pycache__/test_database.cpython-36.pyc | Bin 5478 -> 0 bytes source/tests/db.py | 140 ------------------ 5 files changed, 140 deletions(-) delete mode 100644 source/tests/__pycache__/__init__.cpython-36.pyc delete mode 100644 source/tests/__pycache__/db.cpython-36.pyc delete mode 100644 source/tests/__pycache__/test_commands.cpython-36.pyc delete mode 100644 source/tests/__pycache__/test_database.cpython-36.pyc delete mode 100644 source/tests/db.py diff --git a/source/tests/__pycache__/__init__.cpython-36.pyc b/source/tests/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index f61f473fee8673cfb6a2f5c4cbcfdf0691242c87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmXr!<>lg-$Pvu|1dl-k3@`#24nSPY0whuxf*CX!{Z=v*frJsnFFXB={M=OilJw%l zTZlX-=vg$lPKeW&i;C?ISz@ diff --git a/source/tests/__pycache__/db.cpython-36.pyc b/source/tests/__pycache__/db.cpython-36.pyc deleted file mode 100644 index 1eadf1b0c59c3094ad97e283076fc75176d0527b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5467 zcmbVQ+j85+8ODhuNKuE#ie<-6i#lm!IyNa=uIo-bnIw{(aqB9stxQwq0)eaD&pMm?&a0S0c!IZATRHnIVSCzHa)lloMJ~q0BN^OQ)8CSbiRryk36;^$z zuqxL^wIjW2vKljAD&0EM9xJVSxAC>tA9}-Jz}b(OTiI=?vGLFe2dGzfoY2{K0>15g z9`A)i-|JDI?5C$U!C!I16-c9yBwhWL(uaIZe`$6NX0Qsr2Ps$$b(NW{j=IJgYyq{& znrsnuoh`9t)D5=6-a);>N9fdYQe)E}&j{r5H;09jxVI zyp+ki$AgfI=#$T_p6djG6%L%x3h|O|xy90DS^cReSLL|F@VVvpEx_Qqy=fSV5>P$5ecu~~L(CX)`AIG;$75E&LyPgg zGj&7D^?MG1Y4v@P3)7C)I^lTIA=$HawAub{%ZMw%bize!+BTR9ZChv%BQ_?)k{%lY zcl)v7h=U-mJ~^Zs+|_McuHCl(q5SuDXW)-{Cp-w8-XP$e9pO**{b!wRpYhJ)Y|Wj( zpNby35l|hr-<~`dReY&VD<=(9v?VQC&xA`rZf8Q}fDv$XZQ}~gp?IN;0N9Zt*5sXb zgl49KF)jLFhr2v1G9vu3H4*&j(4T@VNYdVI>7s^d;@U8<`$Lz<)v4zWy(cYIn0POm zd9g^{DuDXyiKHtLx}ipEb3)55xx@tLDlRGUcen{jX3+Zj!Jk0wNDKAkd1M?ZMwn16@f3}8h^&2_dKoz-@_RAR(Itq z=$uqj$Gs~A`WSs@=(aQodaSYidEn0xU7MSm$;x*5VJ6cWz7!Bkiq=%uR83vMKibHe zQ$>2y=oy+Cm_*XFhntKhvciq&K^E1z!ewyN>^#Gg!E z5Krwx?;yiKawOS`mlLAy2fKc_3r&cd2RyVh&lES0n?0VgAp%qklkHDp~`aNItG)vXE$8v`-mcPRT7ymr?Ti5{OVo6qmFktw2Ib$_ab8c;6%S zOwsg0AC{59-;NEEDQZXuNpf{|Ta|)tFAwG593*i8q6l)cW3%lGlk41td@ zG9cGS3fokp%UJ`7kwa55upF4rFi{T7)j43)k)F5_=&3<&CDYrJ5EY!I)h9<;fJpol zADKm{dW?{;(2AHTz3nkfqy}#b)7B$C_Mh^5_-=;ARSXZv1t&1U&z1vKGPE412bd}Y zs$kS@37JlDV;-dZIW_JRpj~~K8e9P^X};2&`|dOt@402v$aIEZr-E$Gq|=eRl(>?a)KXZjWe1xJFXn7JD4rQ>hDtc&k3>j zdqhwcB3%k<@C5;xS@8{){Ozqzk_CyTn)KahJjiP);$`X*ZZ+ng^Ra zLMLyK7Nz~OfK`Hjce*7SW~dx(5UdW%3Rc!2#zVK*fmYi#t~z>)4X zs?+T!7C1<9q^%N%yGH7riD9f2Q=+WtG>X;})xQmS?@4h}dcGxkRwO-?2+6;; zs;;WhN2Nw^VuBfRax?gMIXVB5UPWf31+P+}t#V3Gg$yFU$Cf!Ii^f#R`=mV+lc<6y zHSM3hsp3sCN*!UV&+4o(XJ8Be*PL}41qG<33Q&UrG^0BN`PvR9vTmO!_Uf z;#XM2j8+UVPVV)y^!QjWRy7zf^kY?jgNB@#Y(_(J^X=jjCFXnQSVJfe%uO@d!H{Xr zrqjfK7Tk+Yu@c>&i~^|*PCn?=04EYB4AlWiurUkCHf=PL^k-L+Ad!cAoXvp}kO|t; zP`xN^?fPbV5Hf>oKyIs$1W8l-xXjevFut`)=OCUl=C&O-Y0JJ!{T#yT(m|rvdWTOI%Fb7u>DIIju!HL)b&nca>s9ObR_kKK3mvqX-Pt7VTrhx zd0oktQ)XNpjRrwS0kJuSLXt?`8l7Re!+m)o(OvApZM0ds=}xk4 plhk+{XOI%PL?3ew5;o+ik9jB2gik4zk^$FHP5fcG+Uj2v^?wRtDeV9N diff --git a/source/tests/__pycache__/test_commands.cpython-36.pyc b/source/tests/__pycache__/test_commands.cpython-36.pyc deleted file mode 100644 index 5b0d4e6fe8dd38e8d73f1e50c2ca39413bcbea0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1486 zcmZ`(O^@6(7`B~c;z?&{fp%NkOOFJknMGU?LQD%<2~82ZTA^^T(%8F`*?bJP)7_5t zmX&%c|AU`|uUz0SaN>QFjJ6BgNPb?&pU?aFvA>*7qd)!{|MIy{$e-k$5B|wFP|Pua zB$5^+=_5@j{Ii0UUgA+~^9ui{k3kpxVpN7nNQvSyXlXM3p2(35uZRp4T}Q2#+>!K< z#Qbkqn=E7M_@UK#b$aju>hv>!V3XQxwS7Fd3aDa;*(?yd?Pnq z)Mc4g@<yyPu@v7O{P27m znb&4!tgIUw(_~VqbCIP*k-jWc)~yN4VnpXGpqPCC3o^7s--F0nLW%VdFUi`s!FnV? zqIKAkmbE*X*fJR}>*M~MW9}xyk&BbOP%g|(Ul}!3kymH#?vUcn87>0Rl(t8@QI2Wl zMndkr~_P{;UI9dDZtcMvX5m6l2f7YR|;vMI1W5#rY-E&86$ z>>7>Or--)H4-p;#xan}YGI&;mxTYUdeFq1j5i~w25fp>K*(Bg}Z_H`e9#1+s65hZq z#%vwSYmK`k(e?F7rd@q-s7a=~(X+D7&N_OD|6^)b9ZARWpcW~(7q`A<^^Q6DSAD); RZFnB*raT^t~>E$l2CTWt*f}UGEJEa1mZ45(jdSr zK+8tm!;C!RtJZIj`#wc)`V_cquks4L>h~`W0x4MIDLL2$um}A6egA29dugfh!$0fa z{&7Q5{-YFr2JSz@75o|nQ@RRMndYipRn}TpL#?~|*ytK6wHa<@T((D?{U=@51Qm`88Dl=Ifb&WOH0&0^j zvL)1Yw#-&gH`pqB2lWD5V`oq=vUT<@>LqrTZJ=Ie=h%7FE9^aX0rl!D#ZbEMU@aHp zQ6nnD#sm$&n>@i0S4FYO~atoQ`5!K zBI#Pe679&)OZI@%H0v&F#RS ziXOTVP_nc$q*{~bqKc2!2?SC`MO)UQjZDr2?^Y&XjvZl0R|i*c4#f*)1niC!u`chl zBQ!G=XL zuf9-6l~I+nKvjOPc$T4rwUPOxAs(t&ol4_jY}EP(#nZk)AEy6Kd8B-U8Y?%Vc1Lgr zD|al9ALjaC9S*qQmhS-)p9kJ%Xz^!5fUp@|e0=x8-OiqM#k%+CtKV9%QJ34Pas|c1sebgC2v(Ap+2nJcEw(xgy*3ryYlTXBUJbv>zWbmh~{-DWl3nD~>a z3*xDL=pAGjNRA|1@k&C}{b1J*ccBUK;sFos%t^(I$ITv3*$`)e#ylMt5H3)A)^KK7sTd-!gK##Iat$OR`b!OxZhRWh_3s0WxT z1FB%uEeV-Uabq5&{5du56QEsvm>OIGENQ;docr!H806_eOHjBH=TX2`#e4KXkteas zitH@j3bXY@wQk;{`7^k-F;&6nOmc!BWQ{YLH9M{xt2>x2!|L_rM5*RfNgGv;6sDcW zMK*W>n`@l`?>zx9#SpX^0m><+I&BP7O!Huq zN9g1Y(xS9~7LY1RV!~zFNj#AQO3$}M&#I({5+V85 z*3>mM`l!?hPE0UEPHqPOE+^+-(yPd9wBS`Lv};ZYs*pkC_t-tBWYL%^d7rdrViHvl zrKbI}H&whzMyVt0`dOVd<_v7%|C+N-qo4q_Q~_#GfW_$3FH00)pJR`Utqnql(?`BE z?*{`b^;WcD5F4XgF_HHK)~U|2}5;25^T&uvP~O}B>mZyBuM1p9%pl)1Z0Bt zG*mB2Tf4rMo{7vL8<5*7Btg>DJ}xu0H;iwt(UFMfjJa*c4ci|3Y>JJr9WUB8B^GH* z&9<4}!>I{n@q!Fo;CM|DKcV7hR9vFsLnnMw~41oP#G z4bVn1cR^?fMzH-$7>*Y5ebn`KVsgiCZnP!!g+ANZY-veCXJLuB zmw8>umQ!lWD}o_OCmPb9%C|U*xQzxuM*&@P3WX$*x-~k^a) Date: Sat, 26 May 2018 19:14:00 -0400 Subject: [PATCH 04/35] finish reorganizing and splitting logical files --- source/core/__init__.py | 2 - source/core/commands.py | 155 +++++++++++ source/core/constants.py | 11 + source/core/database.py | 125 +++++++++ source/core/{sc-init.py => init.py} | 0 source/core/parser.py | 44 +++ source/core/sc-handler.py | 397 ---------------------------- source/core/utils.py | 20 ++ 8 files changed, 355 insertions(+), 399 deletions(-) create mode 100644 source/core/commands.py create mode 100644 source/core/constants.py create mode 100644 source/core/database.py rename source/core/{sc-init.py => init.py} (100%) create mode 100644 source/core/parser.py delete mode 100644 source/core/sc-handler.py create mode 100644 source/core/utils.py diff --git a/source/core/__init__.py b/source/core/__init__.py index 4005bfb..e69de29 100644 --- a/source/core/__init__.py +++ b/source/core/__init__.py @@ -1,2 +0,0 @@ -from database import * -from parser import * diff --git a/source/core/commands.py b/source/core/commands.py new file mode 100644 index 0000000..e392e27 --- /dev/null +++ b/source/core/commands.py @@ -0,0 +1,155 @@ +"""""" +import os +import shutil +from pathlib import Path +from .utils import error_message, load_version_info +from .constants import SHELLCUTS_FILE, SHELL_CONFIGS +from .database import DatabaseConnection + +def command_bashmarks(enabled): + """Enable or disable Bashmarks syntax. + + Determines which shells Shellcuts is configured to use, then either + installs the bashmarks-alias files into the appropriate config folder, or + removes them. + """ + command = 'printf "{0} Bashmarks syntax.\n"' + + for install in [item for item in SHELLCUTS_FILE.parent.iterdir() if item.is_dir()]: + if enabled is True: + for f in SHELL_CONFIGS.joinpath(install.name).iterdir(): + if f.stem == 'bashmarks-aliases': + shutil.copyfile(f, install.joinpath(f.name)) + break + else: + error_message("BadInstall") + + elif not enabled: + for f in install.iterdir(): + if f.stem == 'bashmarks-aliases': + os.remove(f) + break + + print(command.format("Enabled" if enabled is True else "Disabled")) + +def command_delete(shellcut): + """Delete shellcut from database.""" + command = 'printf "Deleted shellcut \'{0}\'\n"' + + with DatabaseConnection(SHELLCUTS_FILE) as db: + db.delete_shellcut(shellcut) + + print(command.format(shellcut)) + +def command_go(shellcut): + """Access shellcut and return 'cd' command to shellcut dir.""" + command = 'cd "{0}"' + + with DatabaseConnection(SHELLCUTS_FILE) as db: + path = db.get_shellcut_path(shellcut) + + if path is None: + error_message("DoesNotExist") + elif Path(path).exists(): + print(command.format(path)) + else: + db.delete_shellcut(shellcut) + error_message("BadPath") + +def command_help(*_): + """Print a small help menu to the screen.""" + HELP_SCRIPT = ( + 'Shellcuts usage: \$ sc [--flag] ', + '----------------------------------------------------------------', + 'Create a new shellcut for the current directory (named example):', + ' \$ sc -n example', + '', + 'Jump to that location from anywhere else on the system:', + ' \$ sc example', + '', + 'Remove that shellcut:', + ' \$ sc -d example', + '', + 'List all available shellcuts:', + ' \$ sc -l', + '', + 'See the manpage for lots more information and examples:', + ' \$ man shellcuts') + command = 'printf "' + + for line in HELP_SCRIPT: + command += line + '\n' + command += '"' + + print(command) + +def command_init(*_): + """Run initialization script.""" + command = 'python3 /usr/bin/sc-init' + + print(command) + +def command_list(*_): + """List all shellcuts.""" + command = 'printf "' + + with DatabaseConnection(SHELLCUTS_FILE) as db: + shellcuts = db.get_all_shellcuts() + + if shellcuts is not None and len(shellcuts) > 0: + command += 'SHELLCUTS\n' + + for shellcut in shellcuts: + command += '{0} : {1} : {2}\n'.format(*shellcut) + else: + command += '(No shellcuts yet. Create some with the -n flag!)\n' + + command += '"' + print(command) + +def command_move(shellcut): + """Reinsert existing shellcut at new location.""" + command = 'printf "Moved shellcut \'{0}\'\n"' + + with DatabaseConnection(SHELLCUTS_FILE) as db: + # The insert_shellcut function will delete old versions of the shellcut + # and use the most recent version. + db.insert_shellcut(shellcut, os.getcwd()) + + print(command.format(shellcut)) + +def command_new(shellcut): + """Add shellcut to database and print confirmation.""" + command = 'printf "Added new shellcut \'{0}\'\n"' + + with DatabaseConnection(SHELLCUTS_FILE) as db: + db.insert_shellcut(shellcut, os.getcwd()) + + print(command.format(shellcut)) + +def command_print(shellcut): + """Print specific shellcut.""" + command = 'printf "{0} : {1} : {2}\n"' + + with DatabaseConnection(SHELLCUTS_FILE) as db: + shellcut_tuple = db.get_shellcut(shellcut) + + if shellcut_tuple is not None: + print(command.format(*shellcut_tuple)) + else: + error_message("DoesNotExist") + +def command_version(*_): + """Echo version information found in F_VERSION.""" + lines = load_version_info() + + if lines is None: + error_message("NoVersion") + else: + command = 'printf "' + + for line in lines: + command += line + command += '"' + + print(command) diff --git a/source/core/constants.py b/source/core/constants.py new file mode 100644 index 0000000..650de14 --- /dev/null +++ b/source/core/constants.py @@ -0,0 +1,11 @@ +from pathlib import Path + +ERRORS = { + "DoesNotExist" : "That shellcut does not exist.", + "Unimplemented" : "This feature is unimplemented.", + "NoVersion" : "Version information not found.", + "BadInstall" : "Installed files are not in the expected place.", + "BadPath" : "The path associated with this shellcut is invalid."} +VERSION_FILE = Path('/usr/share/doc/shellcuts/META.txt') +SHELLCUTS_FILE = Path('~/.config/shellcuts/shellcuts.db').expanduser() +SHELL_CONFIGS = Path('/usr/share/shellcuts/') diff --git a/source/core/database.py b/source/core/database.py new file mode 100644 index 0000000..3773e6c --- /dev/null +++ b/source/core/database.py @@ -0,0 +1,125 @@ +import sqlite3 +from pathlib import Path +"""""" +class DatabaseConnection: + """An SQLite database connection containing shellcuts.""" + def __init__(self, path): + """Save path of database as self.path.""" + self.path = path + + def __enter__(self): + """Initialize connection and cursor.""" + if self.path.is_file(): + self.connection = sqlite3.connect(str(self.path)) + self.cursor = self.connection.cursor() + else: + self.connection = sqlite3.connect(str(self.path)) + self.cursor = self.connection.cursor() + self.create() + + return self + + def __exit__(self, type, value, traceback): + """Save changes to database and close connection.""" + self.connection.commit() + self.connection.close() + + def flush(self): + """Delete all data from database.""" + self.cursor.execute('DELETE FROM table_shellcuts') + self.cursor.execute('DELETE FROM table_defaults') + + def create(self): + """Create neccessary tables in database.""" + self.cursor.execute("""CREATE TABLE table_shellcuts (name TEXT, + path TEXT, + command TEXT)""") + self.cursor.execute("""CREATE TABLE table_defaults (enabled BOOLEAN, + command TEXT)""") + self.set_default_command(False) + + def get_default_command(self): + """Get the default command from table_defaults.""" + self.cursor.execute('SELECT command FROM table_defaults') + + command = self.cursor.fetchone() + + return None if command is None else command[0] + + def set_default_command(self, enabled, command=None): + """Toggle default flag in table_defaults and set command if true.""" + self.cursor.execute('SELECT * FROM table_defaults') + + # If the table_defaults is empty, add a new entry. + if self.cursor.fetchone() is None: + self.cursor.execute('INSERT INTO table_defaults VALUES (?,?)', + (enabled, command)) + # Else if flag is being set to true, set both flag and command. + elif enabled is True: + self.cursor.execute('UPDATE table_defaults SET enabled=?, command=?', + (enabled, command)) + # Else flag is being set to false. Ignores command. + else: + self.cursor.execute('UPDATE table_defaults SET enabled=?', + (enabled,)) + + def insert_shellcut(self, name, path): + """Insert shellcut into database.""" + + # If the shellcut already exists in the table, delete it first. + if self.get_shellcut(name) is not None: + self.delete_shellcut(name) + + self.cursor.execute('INSERT INTO table_shellcuts VALUES (?,?,?)', + (name, path, None)) + + def delete_shellcut(self, name): + """Delete a shellcut from the database.""" + self.cursor.execute('DELETE FROM table_shellcuts WHERE name=?', (name,)) + + def get_shellcut(self, name): + """Get path, name, and command of named shellcut.""" + self.cursor.execute('SELECT * FROM table_shellcuts WHERE name=?', + (name,)) + + return self.cursor.fetchone() + + def get_all_shellcuts(self): + """Get all shellcuts in database.""" + self.cursor.execute('SELECT * FROM table_shellcuts') + + return self.cursor.fetchall() + + def get_shellcut_path(self, name): + """Get path of named shellcut.""" + shellcut = self.get_shellcut(name) + + return None if shellcut is None else shellcut[1] + + def get_shellcut_command(self, name): + """Get follow command for shellcut.""" + shellcut = self.get_shellcut(name) + + # If the fetch fails, returns None. + if shellcut is None: + return None + # If the command is None and default commands are enabled, returns + # the default command. + elif shellcut[2] is None and self.check_default_command_enabled(): + return self.get_default_command() + # Else returns the custom command. + else: + return shellcut[2] + + def set_shellcut_command(self, name, command): + """Set follow command for shellcut.""" + self.cursor.execute('UPDATE table_shellcuts SET command=? WHERE name=?', + (command, name)) + + def check_default_command_enabled(self): + """Check if default commands are enabled.""" + self.cursor.execute('SELECT enabled FROM table_defaults') + + enabled = self.cursor.fetchone() + + return None if enabled is None else enabled[0] diff --git a/source/core/sc-init.py b/source/core/init.py similarity index 100% rename from source/core/sc-init.py rename to source/core/init.py diff --git a/source/core/parser.py b/source/core/parser.py new file mode 100644 index 0000000..c008865 --- /dev/null +++ b/source/core/parser.py @@ -0,0 +1,44 @@ +import shutil +import argparse +from pathlib import Path +from .commands import command_help +"""""" +class Parser(argparse.ArgumentParser): + """Subclass of ArgumentParser. + + Necessary to override error method in ArgumentParser. Sometimes the + ArgumentParser throws errors (if argument syntax is bad, for example) and + in all of these cases I want the help menu to appear, instead of the + provided error messages. This subclass also provides the ability to create + a base argument to attempt before parsing the rest, improving speed when + jumping. + """ + def __init__(self, *args, **kwargs): + """Initialize by initializing super and adding base argument.""" + super().__init__(*args, add_help=False, **kwargs) + + self.add_argument('shellcut', nargs='?', default=None) + + def add_additional_arguments(self): + """Add flags to parser.""" + self.add_argument('-d', '--delete') + self.add_argument('-h', '--help', action='store_true', default=None) + self.add_argument('-l', '--list', action='store_true', default=None) + self.add_argument('-n', '--new') + self.add_argument('-m', '--move') + self.add_argument('-p', '--print') + self.add_argument('-v', '--version', action='store_true', default=None) + self.add_argument('--init', action='store_true', default=None) + self.add_argument('--enable-bashmarks-syntax', + action='store_true', + dest='bashmarks', + default=None) + self.add_argument('--disable-bashmarks-syntax', + action='store_false', + dest='bashmarks', + default=None) + + def error(self, message): + """Call help command in case of error.""" + command_help() + exit(0) diff --git a/source/core/sc-handler.py b/source/core/sc-handler.py deleted file mode 100644 index f8eea03..0000000 --- a/source/core/sc-handler.py +++ /dev/null @@ -1,397 +0,0 @@ -"""Handles arguments from main shell function. - -This is the core engine of the program. It handles all relevant options and -commands. After the arguments are parsed and handled, a command is returned to -the calling shell function. - -Part of Shellcuts by Tgsachse. -""" -import os -import shutil -import sqlite3 -import argparse -from pathlib import Path - -### CONSTANTS ### -# Can be changed to save the shellcuts in a different location. -D_SHELL_CONFIGS = Path('/usr/share/shellcuts/') -F_VERSION = Path('/usr/share/doc/shellcuts/META.txt') -F_SHELLCUTS = Path('~/.config/shellcuts/shellcuts.db').expanduser() - -### SUBCLASSES ### -class DatabaseConnection: - """An SQLite database connection containing shellcuts.""" - def __init__(self, path): - """Save path of database as self.path.""" - self.path = path - - def __enter__(self): - """Initialize connection and cursor.""" - if self.path.is_file(): - self.connection = sqlite3.connect(str(self.path)) - self.cursor = self.connection.cursor() - else: - self.connection = sqlite3.connect(str(self.path)) - self.cursor = self.connection.cursor() - self.create() - - return self - - def __exit__(self, type, value, traceback): - """Save changes to database and close connection.""" - self.connection.commit() - self.connection.close() - - def flush(self): - """Delete all data from database.""" - self.cursor.execute('DELETE FROM table_shellcuts') - self.cursor.execute('DELETE FROM table_defaults') - - def create(self): - """Create neccessary tables in database.""" - self.cursor.execute("""CREATE TABLE table_shellcuts (name TEXT, - path TEXT, - command TEXT)""") - self.cursor.execute("""CREATE TABLE table_defaults (enabled BOOLEAN, - command TEXT)""") - self.set_default_command(False) - - def get_default_command(self): - """Get the default command from table_defaults.""" - self.cursor.execute('SELECT command FROM table_defaults') - - command = self.cursor.fetchone() - - return None if command is None else command[0] - - def set_default_command(self, enabled, command=None): - """Toggle default flag in table_defaults and set command if true.""" - self.cursor.execute('SELECT * FROM table_defaults') - - # If the table_defaults is empty, add a new entry. - if self.cursor.fetchone() is None: - self.cursor.execute('INSERT INTO table_defaults VALUES (?,?)', - (enabled, command)) - # Else if flag is being set to true, set both flag and command. - elif enabled is True: - self.cursor.execute('UPDATE table_defaults SET enabled=?, command=?', - (enabled, command)) - # Else flag is being set to false. Ignores command. - else: - self.cursor.execute('UPDATE table_defaults SET enabled=?', - (enabled,)) - - def insert_shellcut(self, name, path): - """Insert shellcut into database.""" - - # If the shellcut already exists in the table, delete it first. - if self.get_shellcut(name) is not None: - self.delete_shellcut(name) - - self.cursor.execute('INSERT INTO table_shellcuts VALUES (?,?,?)', - (name, path, None)) - - def delete_shellcut(self, name): - """Delete a shellcut from the database.""" - self.cursor.execute('DELETE FROM table_shellcuts WHERE name=?', (name,)) - - def get_shellcut(self, name): - """Get path, name, and command of named shellcut.""" - self.cursor.execute('SELECT * FROM table_shellcuts WHERE name=?', - (name,)) - - return self.cursor.fetchone() - - def get_all_shellcuts(self): - """Get all shellcuts in database.""" - self.cursor.execute('SELECT * FROM table_shellcuts') - - return self.cursor.fetchall() - - def get_shellcut_path(self, name): - """Get path of named shellcut.""" - shellcut = self.get_shellcut(name) - - return None if shellcut is None else shellcut[1] - - def get_shellcut_command(self, name): - """Get follow command for shellcut.""" - shellcut = self.get_shellcut(name) - - # If the fetch fails, returns None. - if shellcut is None: - return None - # If the command is None and default commands are enabled, returns - # the default command. - elif shellcut[2] is None and self.check_default_command_enabled(): - return self.get_default_command() - # Else returns the custom command. - else: - return shellcut[2] - - def set_shellcut_command(self, name, command): - """Set follow command for shellcut.""" - self.cursor.execute('UPDATE table_shellcuts SET command=? WHERE name=?', - (command, name)) - - def check_default_command_enabled(self): - """Check if default commands are enabled.""" - self.cursor.execute('SELECT enabled FROM table_defaults') - - enabled = self.cursor.fetchone() - - return None if enabled is None else enabled[0] - -class Parser(argparse.ArgumentParser): - """Subclass of ArgumentParser. - - Necessary to override error method in ArgumentParser. Sometimes the - ArgumentParser throws errors (if argument syntax is bad, for example) and - in all of these cases I want the help menu to appear, instead of the - provided error messages. This subclass also provides the ability to create - a base argument to attempt before parsing the rest, improving speed when - jumping. - """ - def __init__(self, *args, **kwargs): #### add no help menu here so it doesnt have to be passed - """Initialize by initializing super and adding base argument.""" - super().__init__(*args, add_help=False, **kwargs) - - self.add_argument('shellcut', nargs='?', default=None) - - def add_additional_arguments(self): - """Add flags to parser.""" - self.add_argument('-d', '--delete') - self.add_argument('-h', '--help', action='store_true', default=None) - self.add_argument('-l', '--list', action='store_true', default=None) - self.add_argument('-n', '--new') - self.add_argument('-m', '--move') - self.add_argument('-p', '--print') - self.add_argument('-v', '--version', action='store_true', default=None) - self.add_argument('--init', action='store_true', default=None) - self.add_argument('--enable-bashmarks-syntax', - action='store_true', - dest='bashmarks', - default=None) - self.add_argument('--disable-bashmarks-syntax', - action='store_false', - dest='bashmarks', - default=None) - - def error(self, message): - """Call help command in case of error.""" - command_help() - exit(0) - - -### COMMANDS ### -def command_bashmarks(enabled): - """Enable or disable Bashmarks syntax. - - Determines which shells Shellcuts is configured to use, then either - installs the bashmarks-alias files into the appropriate config folder, or - removes them. - """ - command = 'printf "{0} Bashmarks syntax.\n"' - - for install in [item for item in F_SHELLCUTS.parent.iterdir() if item.is_dir()]: - if enabled is True: - for f in D_SHELL_CONFIGS.joinpath(install.name).iterdir(): - if f.stem == 'bashmarks-aliases': - shutil.copyfile(f, install.joinpath(f.name)) - break - else: - error_message("BadInstall") - - elif not enabled: - for f in install.iterdir(): - if f.stem == 'bashmarks-aliases': - os.remove(f) - break - - print(command.format("Enabled" if enabled is True else "Disabled")) - -def command_delete(shellcut): - """Delete shellcut from database.""" - command = 'printf "Deleted shellcut \'{0}\'\n"' - - with DatabaseConnection(F_SHELLCUTS) as db: - db.delete_shellcut(shellcut) - - print(command.format(shellcut)) - -def command_go(shellcut): - """Access shellcut and return 'cd' command to shellcut dir.""" - command = 'cd "{0}"' - - with DatabaseConnection(F_SHELLCUTS) as db: - path = db.get_shellcut_path(shellcut) - - if path is None: - error_message("DoesNotExist") - elif Path(path).exists(): - print(command.format(path)) - else: - db.delete_shellcut(shellcut) - error_message("BadPath") - -def command_help(*_): - """Print a small help menu to the screen.""" - HELP_SCRIPT = ( - 'Shellcuts usage: \$ sc [--flag] ', - '----------------------------------------------------------------', - 'Create a new shellcut for the current directory (named example):', - ' \$ sc -n example', - '', - 'Jump to that location from anywhere else on the system:', - ' \$ sc example', - '', - 'Remove that shellcut:', - ' \$ sc -d example', - '', - 'List all available shellcuts:', - ' \$ sc -l', - '', - 'See the manpage for lots more information and examples:', - ' \$ man shellcuts') - command = 'printf "' - - for line in HELP_SCRIPT: - command += line + '\n' - command += '"' - - print(command) - -def command_init(*_): - """Run initialization script.""" - command = 'python3 /usr/bin/sc-init' - - print(command) - -def command_list(*_): - """List all shellcuts.""" - command = 'printf "' - - with DatabaseConnection(F_SHELLCUTS) as db: - shellcuts = db.get_all_shellcuts() - - if shellcuts is not None and len(shellcuts) > 0: - command += 'SHELLCUTS\n' - - for shellcut in shellcuts: - command += '{0} : {1} : {2}\n'.format(*shellcut) - else: - command += '(No shellcuts yet. Create some with the -n flag!)\n' - - command += '"' - print(command) - -def command_move(shellcut): - """Reinsert existing shellcut at new location.""" - command = 'printf "Moved shellcut \'{0}\'\n"' - - with DatabaseConnection(F_SHELLCUTS) as db: - # The insert_shellcut function will delete old versions of the shellcut - # and use the most recent version. - db.insert_shellcut(shellcut, os.getcwd()) - - print(command.format(shellcut)) - -def command_new(shellcut): - """Add shellcut to database and print confirmation.""" - command = 'printf "Added new shellcut \'{0}\'\n"' - - with DatabaseConnection(F_SHELLCUTS) as db: - db.insert_shellcut(shellcut, os.getcwd()) - - print(command.format(shellcut)) - -def command_print(shellcut): - """Print specific shellcut.""" - command = 'printf "{0} : {1} : {2}\n"' - - with DatabaseConnection(F_SHELLCUTS) as db: - shellcut_tuple = db.get_shellcut(shellcut) - - if shellcut_tuple is not None: - print(command.format(*shellcut_tuple)) - else: - error_message("DoesNotExist") - -def command_version(*_): - """Echo version information found in F_VERSION.""" - command = 'printf "' - - for line in load_version_info(): - command += line - command += '"' - - print(command) - - -### HELPER FUNCTIONS ### -def error_message(error): - """Echo an error message. - - Includes a master dictionary of all supported errors. These are accessible - by key. - """ - errors = { - "DoesNotExist" : "That shellcut does not exist.", - "Unimplemented" : "This feature is unimplemented.", - "NoVersion" : "Version information not found.", - "BadInstall" : "Installed files are not in the expected place.", - "BadPath" : "The path associated with this shellcut is invalid."} - command = 'printf "ERROR {0}: {1}\n"'.format(error, errors[error]) - - print(command) - exit(0) - -def load_version_info(): - """Load version information found at F_VERSION.""" - try: - with open(str(F_VERSION), 'r') as f: - return f.readlines() - except FileNotFoundError: - error_message("NoVersion") - - -### START MAIN PROGRAM ### -if __name__ == '__main__': - parser = Parser() - arguments, unknown = parser.parse_known_args() - - # Attempts to short-circuit the program and jump if only one argument given. - if len(unknown) < 1: - command_go(arguments.shellcut) - exit(0) - - # Adds other flags and re-parses arguments. - parser.add_additional_arguments() - arguments, unknown = parser.parse_known_args() - - # If anything unknown is passed, show help and exit. - if len(unknown) > 0: - command_help() - exit(0) - - # This tuple associates arguments from the parser with their functions. - command_pairs = ( # there's gotta be abetter way - (arguments.help, command_help), - (arguments.list, command_list), - (arguments.version, command_version), - (arguments.init, command_init), - (arguments.bashmarks, command_bashmarks), - (arguments.delete, command_delete), - (arguments.new, command_new), - (arguments.move, command_move), - (arguments.print, command_print)) - - # For each in tuple, if value is not 'None', execute associated function. - for pair in command_pairs: - if pair[0] != None: - # Passes value to corresponding function. Functions are designed to - # handle this value even if they don't need it. - pair[1](pair[0]) - break - else: - command_help() diff --git a/source/core/utils.py b/source/core/utils.py new file mode 100644 index 0000000..862a271 --- /dev/null +++ b/source/core/utils.py @@ -0,0 +1,20 @@ +from .constants import ERRORS, VERSION_FILE +def error_message(error): + """Echo an error message. + + Includes a master dictionary of all supported errors. These are accessible + by key. + """ + command = 'printf "ERROR {0}: {1}\n"'.format(error, ERRORS[error]) + + print(command) + #exit(0) + +def load_version_info(): + """Load version information found at VERSION_FILE.""" + try: + with open(str(VERSION_FILE), 'r') as f: + return f.readlines() + except FileNotFoundError: + return None + #error_message("NoVersion") From 24a78a7427d6f57f81e27d6e143d47807fc4d13c Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sat, 26 May 2018 19:14:14 -0400 Subject: [PATCH 05/35] fixed imports --- source/shellcuts.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 source/shellcuts.py diff --git a/source/shellcuts.py b/source/shellcuts.py new file mode 100644 index 0000000..9536a28 --- /dev/null +++ b/source/shellcuts.py @@ -0,0 +1,42 @@ +from core.database import DatabaseConnection +from core.parser import Parser +from core.commands import * + +parser = Parser() +arguments, unknown = parser.parse_known_args() + +# Attempts to short-circuit the program and jump if only one argument given. +if len(unknown) < 1: + command_go(arguments.shellcut) + exit(0) + +# Adds other flags and re-parses arguments. +parser.add_additional_arguments() +arguments, unknown = parser.parse_known_args() + +# If anything unknown is passed, show help and exit. +if len(unknown) > 0: + command_help() + exit(0) + +# This tuple associates arguments from the parser with their functions. +command_pairs = ( # there's gotta be abetter way + (arguments.help, command_help), + (arguments.list, command_list), + (arguments.version, command_version), + (arguments.init, command_init), + (arguments.bashmarks, command_bashmarks), + (arguments.delete, command_delete), + (arguments.new, command_new), + (arguments.move, command_move), + (arguments.print, command_print)) + +# For each in tuple, if value is not 'None', execute associated function. +for pair in command_pairs: + if pair[0] != None: + # Passes value to corresponding function. Functions are designed to + # handle this value even if they don't need it. + pair[1](pair[0]) + break +else: + command_help() From 398457692528700230420d8ae421847571a06bbb Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sat, 26 May 2018 19:15:02 -0400 Subject: [PATCH 06/35] removed some stuff --- source/main.py | 0 source/tester.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 source/main.py delete mode 100644 source/tester.py diff --git a/source/main.py b/source/main.py deleted file mode 100644 index e69de29..0000000 diff --git a/source/tester.py b/source/tester.py deleted file mode 100644 index e69de29..0000000 From d97e56f1a598124873effc54543d04b84a12000b Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sat, 26 May 2018 19:42:35 -0400 Subject: [PATCH 07/35] add init and new checks --- .../tests/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 145 bytes .../__pycache__/test_commands.cpython-36.pyc | Bin 0 -> 2734 bytes .../__pycache__/test_database.cpython-36.pyc | Bin 0 -> 5478 bytes source/tests/test_commands.py | 70 +++++++++++++----- 4 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 source/tests/__pycache__/__init__.cpython-36.pyc create mode 100644 source/tests/__pycache__/test_commands.cpython-36.pyc create mode 100644 source/tests/__pycache__/test_database.cpython-36.pyc diff --git a/source/tests/__pycache__/__init__.cpython-36.pyc b/source/tests/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f61f473fee8673cfb6a2f5c4cbcfdf0691242c87 GIT binary patch literal 145 zcmXr!<>lg-$Pvu|1dl-k3@`#24nSPY0whuxf*CX!{Z=v*frJsnFFXB={M=OilJw%l zTZlX-=vg$lPKeW&i;C?ISz@ literal 0 HcmV?d00001 diff --git a/source/tests/__pycache__/test_commands.cpython-36.pyc b/source/tests/__pycache__/test_commands.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bfcb766ba698741115222bd3415c0cff32a9ab05 GIT binary patch literal 2734 zcmbtWPmkL~6rZu3#Bq|{c4@bCsgNZOB~?kf?EwiP)RHbxsY;={6l5eUuV;4S)PKUv zxZ7Hbgp^(@B)$b_zD7TRubg`0%!&8xICa_-Rba_)#xrl;yg$G9o^SMe*7LvGKm67q z5fiuO%IKOa%>c%5jLZi)1ri%tTCrf8^$J# z)|Z5}nE8}2lhbKut~*^u_esC~0`4H=KF!U2C4%_)&OMmZD?l1EPx+HLl z`l*ucBQ9i+!~^wMy|%|Z1)W>9)k1ezGiz&YW+5loXs`~q3MpOIV;j%v@;2F-QXX)2*jcc7EANex zDDq-X}rpT1ei8uqJHYb!Q9qB2V8mcjEGI~mjt8?qD`92JLahwMPwQ6MEil-M zsogTj-hI>7xNqg%dTjDmAl;)NY`)@&-14O4LLG>d=b8XU$vxpua`Tw0 zM_!n6r=gP6pE%Zu2o!e}f2{giZb%*;0kaa^i++bdLSX+M4JS#&hw4~*{zUTOZIR3l zlgGnR!uW8%N{^vTQsDy|`m&(zLMb_zJ;~q4b@$q}Ypy@x{=+I{6;BTlgtrmXq^8!? zwl$MlFlJX*5IgvHG3$|OgIGltNTf=e>ID*w3W-buZ0XtV3bqhJl~=*WnLUFd0Vwh8 z;w$2DI>^o}&9_tO9rI7d{f5)69}^Z_E}+m6(M5uW_BFAA^GzgZbV<&_EProd;O@dt zcvSG!zv3@k_^+EZvQE(=Fl4(!XdKmn7m#k8C3mRoz&`; zPnm#S`s?6l9qMTNRG(VN&Gd`VALm+e3RgSDs(~xt5!U#I+$Y}?);!hRt^&Ll6}Oc1 z`CCb9R>kOtOAQ{UDtx=k+`P0ciLwneiK*A|2|!p8k>Zn|#lMcj3B z%XOoKr6JC{u8TG=c3Q5>65n+NB3QJLV8<6*NZvtm4#@>1s9VjRqN*Y2MFR+yzoNRE z)SxEaK7X!tt_Ag-7Q6jkQ6C*Mh1^9K(tB5PfO!JLnJM9G-(&K1PVRK)cyfobl{x; literal 0 HcmV?d00001 diff --git a/source/tests/__pycache__/test_database.cpython-36.pyc b/source/tests/__pycache__/test_database.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b9f0f49cee4d41c9c8f2e7398e52d40d264d792f GIT binary patch literal 5478 zcmbVQ+j85+8ODhuNKuE#N@T}Qi#nMobZpYL9LJq_GD#>q3RwpV$DUiJGI2Z0o<@su3w0@wro{l5RSyS=>J`1?QW z-~VA#QU0S8eg^Kpz!m%o1ygzoQ<>(fJyq6PPeZM{`o!oNDzzDIWm4@`Rpo1iRao_f z!m3;w*N*g_$!g4eq4erZd!)4Ly~elRVC0R0A!k2fZe_Qn#>N9D9HL&|aYAR`33%7{ zJl+pSzSpNd*-uYzg1_K~E09JZNqYKAWdQk@{=)1T%wQFK4^pri>MAo?9d(U0*b-`! zHQ6%iI$L3@s2gmJy@h&-t+O+zn{0!Q(kGJCAzprD7<(x3HEA z@k%D|9uGn;qR+mx`mPfMRycG*E5u8>H2q;jVNRqoS>7(Pk!Rf_FQUFUO8Bq^pZ7IE&)BG6r@>irA2M z+7X(W3i7n*{T=S|ut<*ZC)QN(CnJ9b-XK(ax2203rip8#z#fcT9#?0cJMtd4RAJ)1 zXy(N-b*ljNZ>N&EMCgVZZ7c{ayW|oRvg^2{#6RFBB$>hRtNVWh%VRCnljpH2RURtep~lLMsM8hP z!O9)Wz{N^_nY}Dm;s$4;I zCR?E8`Jwd$n#cfLLy!_D=rv#yT!edK#-%EW6*R@w{!9eEh->^QhY9ew7W@di;M?)q zH&8#Rv5tF32=p-q&d6Xd;753A-r?(?O3xRzzxhr7^(xOu=sJ9AQT^SIgLDH{TzY1YHg+44JgTEacBvaIozLMnX?6xWe-CiBZ!8u5RT(+P^o(9mlWQEt0FWa;$D+whXIRA0o^Iee2Z%ry(+I>piao3>4hahUgq=>XN$e=F ziZdwUrNndH`ElmBu{q#je+c(>nuVq=?30}&huRqh^2_%;1Q%L=(MHSNSLx7lKzA@< z4(L??iog$hgxbL($%Ro&ks-a%qNl*lN08Q@e{kR;hNrn-5y<)cPXf00QvzGTrHL&< z?7cn_ltp7UBb1jl5N5Re7HV@T?7btfc{)wllS8vpaOWgA;JK z@9yqJO%5%ywkX~8w~!*&QV?T(ddP}FM36U2U_V0vH{~t^{WM%tHT^UZBgM6w7|*W> z6-u?>BejOQrmv`L20}`?x|U8chjg9*{RvZMNEdwN4~ehL<1U34pqyf=)5b8xG!Hg; zgihWdElT@m0jYvFaukrSrpQ+%@>`Fv!x{L%_7M4y^cI0s@Bmwn!p2HE*4XH~fg{~( zRHxg|EO3zINZTb2cZ1YB%X=~&5{5R4DN)vR8bzCl>feIAccr)~J>L^OYmy#Hgydga zSJ&0(!%`zSF~J-;xf%SsoSc72uOhS2f>)`~t~n*BLI#oFWA~hrMPsVuebSzZNmN0U zn)c7$RPiPmrH--dXLZ(CFtDZnYtA~2f&$c11*ky*n$hQ9mngtK#~v438-xyLfP87u z4+d81!P?UOB~kY}AY0T8K_L@HTyX>I%9q&hwW-r36fJWhJWIV0A5;4cDn6ltO!^J9 z;+I&&oK_4mPHy|N^!QjW)-@O~^kZFrjfR|$0kO|t; zP`w~+?b_A!Ok@t(fZSFg36iGvaha*TW_)X%jzm0X!fiWl*!IL{Gi-$IxM|yzSfnjA z+h%?rrzVue3o>v)M-k%ZR9vLu11c!d7cDBTP;re4+Qx`askntAUQW+c`iLNyuSpG5 zKpV;21)VtvI%uG5M=Fh}8fvp@R?Is7RkJ3`y18a9oA1b;AZ;;!S*jl1Vo5Sue7B#9K)=s3$A?aLF1-f|yqqs`Jycan9R pq{dwwM@r-peatyX*pR0_=IuliKBrVl23$im@rUJV>wi|%{{g41F@XR8 literal 0 HcmV?d00001 diff --git a/source/tests/test_commands.py b/source/tests/test_commands.py index 5419164..24824cf 100644 --- a/source/tests/test_commands.py +++ b/source/tests/test_commands.py @@ -10,28 +10,21 @@ class CommandTester(unittest.TestCase): """""" - def __init__(self, *args, **kwargs): - """""" - super().__init__(*args, **kwargs) - - @patch("core.utils.VERSION_FILE", VERSION_FILE) - @patch("sys.stdout", new_callable=StringIO) - def test_000_get_version_error(self, stream): - """""" + @patch('core.utils.VERSION_FILE', VERSION_FILE) + @patch('sys.stdout', new_callable=StringIO) + def test_000_check_version_command(self, stream): + """Confirm that command_version can open the version information. + + Also tests that if the information is missing, version throws the + NoVersion error. + """ if VERSION_FILE.is_file(): VERSION_FILE.unlink() command_version() - self.assertTrue(re.search("NoVersion", stream.getvalue())) + self.assertTrue(re.search('NoVersion', stream.getvalue())) - @patch("core.utils.VERSION_FILE", VERSION_FILE) - @patch("sys.stdout", new_callable=StringIO) - def test_001_get_version(self, stream): - """""" - if VERSION_FILE.is_file(): - VERSION_FILE.unlink() - VERSION_FILE.touch() VERSION_FILE.write_text('Test version information.') @@ -39,4 +32,47 @@ def test_001_get_version(self, stream): self.assertTrue(re.search('Test version information', stream.getvalue())) - def test_002_ + @patch('sys.stdout', new_callable=StringIO) + def test_001_check_help_command(self, stream): + """Confirm that the command_help prints a help menu.""" + command_help() + + self.assertTrue(re.search('Shellcuts usage:', stream.getvalue())) + + @patch('sys.stdout', new_callable=StringIO) + def test_002_check_init_command(self, stream): + """Confirm that command_init returns the command to run the init file.""" + command_init() + + self.assertTrue(re.search('python3 /usr/bin/sc-init', stream.getvalue())) + + @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) + @patch('os.getcwd', return_value='/tmp/test1/') + @patch('sys.stdout', new_callable=StringIO) + def test_003_check_new_command(self, stream, mock_getcwd): + """Confirm that command_new adds a shellcut to a database.""" + if SHELLCUTS_FILE.is_file(): + SHELLCUTS_FILE.unlink() + + command_new('test1') + + # Check that a courtesy message is printed to the screen. + self.assertTrue(re.search('Added new shellcut \'test1\'', stream.getvalue())) + + # Check that the new shellcut is there. + with DatabaseConnection(SHELLCUTS_FILE) as db: + self.assertEqual(db.get_shellcut('test1')[0], 'test1') + self.assertEqual(db.get_shellcut_path('test1'), '/tmp/test1/') + + # Change the 'current working directory,' meaning a second call to the + # new command will overwrite the previous entry. + mock_getcwd.return_value = '/tmp/test2/' + + command_new('test1') + + # Confirm that the previous entry was overwritten, and that there is + # only one item in the database. + with DatabaseConnection(SHELLCUTS_FILE) as db: + self.assertEqual(db.get_shellcut('test1')[0], 'test1') + self.assertEqual(db.get_shellcut_path('test1'), '/tmp/test2/') + self.assertEqual(len(db.get_all_shellcuts()), 1) From c581775e38a5ab62dd641de695aae24fda3446c8 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sat, 26 May 2018 19:43:11 -0400 Subject: [PATCH 08/35] making a mess --- source/core/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 144 bytes source/core/__pycache__/commands.cpython-36.pyc | Bin 0 -> 4568 bytes source/core/__pycache__/constants.cpython-36.pyc | Bin 0 -> 656 bytes source/core/__pycache__/database.cpython-36.pyc | Bin 0 -> 4851 bytes source/core/__pycache__/utils.cpython-36.pyc | Bin 0 -> 782 bytes source/tests/__pycache__/__init__.cpython-36.pyc | Bin 145 -> 0 bytes .../__pycache__/test_commands.cpython-36.pyc | Bin 2734 -> 0 bytes .../__pycache__/test_database.cpython-36.pyc | Bin 5478 -> 0 bytes 8 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 source/core/__pycache__/__init__.cpython-36.pyc create mode 100644 source/core/__pycache__/commands.cpython-36.pyc create mode 100644 source/core/__pycache__/constants.cpython-36.pyc create mode 100644 source/core/__pycache__/database.cpython-36.pyc create mode 100644 source/core/__pycache__/utils.cpython-36.pyc delete mode 100644 source/tests/__pycache__/__init__.cpython-36.pyc delete mode 100644 source/tests/__pycache__/test_commands.cpython-36.pyc delete mode 100644 source/tests/__pycache__/test_database.cpython-36.pyc diff --git a/source/core/__pycache__/__init__.cpython-36.pyc b/source/core/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f1d958601010bb3bbe5db44af3e4f1f72371f9d GIT binary patch literal 144 zcmXr!<>flQku#bB2p)q77+?f49Dul(1xTbY1T$zd`mJOr0tq9CU$*)g`MIh3CF#Y9 z$r;6|`YuKJ1xfi8`p)?&srtbgsW~~xr6tAs#rdU0$*KCu`9-Pv@$s2?nI-Y@dIgoY TIBatBQ%ZAE?LfvB12F>tNvtE0 literal 0 HcmV?d00001 diff --git a/source/core/__pycache__/commands.cpython-36.pyc b/source/core/__pycache__/commands.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d405f260ef19df9b4bc704ab4b633129dfc4da3 GIT binary patch literal 4568 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 H(60O&ynkE; literal 0 HcmV?d00001 diff --git a/source/core/__pycache__/constants.cpython-36.pyc b/source/core/__pycache__/constants.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf1ec631a9d6088d4a6cd3c7a10494488804e00c GIT binary patch literal 656 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 literal 0 HcmV?d00001 diff --git a/source/core/__pycache__/utils.cpython-36.pyc b/source/core/__pycache__/utils.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb2aea8d88d90569ebda6df351409b445b51dcdf GIT binary patch literal 782 zcmYjO&59F25bmCt{Ax5S9uzz@K@YiP6v4w1L|79DELluMmv{)B?#^a*XS#>(9xUFEteDSOCr5_;l8yy*((K~SS90ErirzpcS zoCUK0BOdVZJDP<&;uTmUUY(+D?GJ|3ZctVSlgY(oTHgOOm`q0(P!&R=bYMiqC}ZEB>Fe8dF(DCI5+Z4c`Br#w(SH z*j^dRRz}3{waORjTYRRth^H%&rmV0gHmcA}#7t=s7dA;vFJG4}4>`YU-O(ARAgZB84eW zhDj>M$_`!qz)iG^OT6?N9YWnzoTNZJzaySI;uc(+CIl1&xQ!p-2Uxe^1bnjwQ_@%} it?{$)q+S@GafIzAp*=yppDA9X;;oa+tJ%i&d;b6*|HC%` literal 0 HcmV?d00001 diff --git a/source/tests/__pycache__/__init__.cpython-36.pyc b/source/tests/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index f61f473fee8673cfb6a2f5c4cbcfdf0691242c87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmXr!<>lg-$Pvu|1dl-k3@`#24nSPY0whuxf*CX!{Z=v*frJsnFFXB={M=OilJw%l zTZlX-=vg$lPKeW&i;C?ISz@ diff --git a/source/tests/__pycache__/test_commands.cpython-36.pyc b/source/tests/__pycache__/test_commands.cpython-36.pyc deleted file mode 100644 index bfcb766ba698741115222bd3415c0cff32a9ab05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2734 zcmbtWPmkL~6rZu3#Bq|{c4@bCsgNZOB~?kf?EwiP)RHbxsY;={6l5eUuV;4S)PKUv zxZ7Hbgp^(@B)$b_zD7TRubg`0%!&8xICa_-Rba_)#xrl;yg$G9o^SMe*7LvGKm67q z5fiuO%IKOa%>c%5jLZi)1ri%tTCrf8^$J# z)|Z5}nE8}2lhbKut~*^u_esC~0`4H=KF!U2C4%_)&OMmZD?l1EPx+HLl z`l*ucBQ9i+!~^wMy|%|Z1)W>9)k1ezGiz&YW+5loXs`~q3MpOIV;j%v@;2F-QXX)2*jcc7EANex zDDq-X}rpT1ei8uqJHYb!Q9qB2V8mcjEGI~mjt8?qD`92JLahwMPwQ6MEil-M zsogTj-hI>7xNqg%dTjDmAl;)NY`)@&-14O4LLG>d=b8XU$vxpua`Tw0 zM_!n6r=gP6pE%Zu2o!e}f2{giZb%*;0kaa^i++bdLSX+M4JS#&hw4~*{zUTOZIR3l zlgGnR!uW8%N{^vTQsDy|`m&(zLMb_zJ;~q4b@$q}Ypy@x{=+I{6;BTlgtrmXq^8!? zwl$MlFlJX*5IgvHG3$|OgIGltNTf=e>ID*w3W-buZ0XtV3bqhJl~=*WnLUFd0Vwh8 z;w$2DI>^o}&9_tO9rI7d{f5)69}^Z_E}+m6(M5uW_BFAA^GzgZbV<&_EProd;O@dt zcvSG!zv3@k_^+EZvQE(=Fl4(!XdKmn7m#k8C3mRoz&`; zPnm#S`s?6l9qMTNRG(VN&Gd`VALm+e3RgSDs(~xt5!U#I+$Y}?);!hRt^&Ll6}Oc1 z`CCb9R>kOtOAQ{UDtx=k+`P0ciLwneiK*A|2|!p8k>Zn|#lMcj3B z%XOoKr6JC{u8TG=c3Q5>65n+NB3QJLV8<6*NZvtm4#@>1s9VjRqN*Y2MFR+yzoNRE z)SxEaK7X!tt_Ag-7Q6jkQ6C*Mh1^9K(tB5PfO!JLnJM9G-(&K1PVRK)cyfobl{x; diff --git a/source/tests/__pycache__/test_database.cpython-36.pyc b/source/tests/__pycache__/test_database.cpython-36.pyc deleted file mode 100644 index b9f0f49cee4d41c9c8f2e7398e52d40d264d792f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5478 zcmbVQ+j85+8ODhuNKuE#N@T}Qi#nMobZpYL9LJq_GD#>q3RwpV$DUiJGI2Z0o<@su3w0@wro{l5RSyS=>J`1?QW z-~VA#QU0S8eg^Kpz!m%o1ygzoQ<>(fJyq6PPeZM{`o!oNDzzDIWm4@`Rpo1iRao_f z!m3;w*N*g_$!g4eq4erZd!)4Ly~elRVC0R0A!k2fZe_Qn#>N9D9HL&|aYAR`33%7{ zJl+pSzSpNd*-uYzg1_K~E09JZNqYKAWdQk@{=)1T%wQFK4^pri>MAo?9d(U0*b-`! zHQ6%iI$L3@s2gmJy@h&-t+O+zn{0!Q(kGJCAzprD7<(x3HEA z@k%D|9uGn;qR+mx`mPfMRycG*E5u8>H2q;jVNRqoS>7(Pk!Rf_FQUFUO8Bq^pZ7IE&)BG6r@>irA2M z+7X(W3i7n*{T=S|ut<*ZC)QN(CnJ9b-XK(ax2203rip8#z#fcT9#?0cJMtd4RAJ)1 zXy(N-b*ljNZ>N&EMCgVZZ7c{ayW|oRvg^2{#6RFBB$>hRtNVWh%VRCnljpH2RURtep~lLMsM8hP z!O9)Wz{N^_nY}Dm;s$4;I zCR?E8`Jwd$n#cfLLy!_D=rv#yT!edK#-%EW6*R@w{!9eEh->^QhY9ew7W@di;M?)q zH&8#Rv5tF32=p-q&d6Xd;753A-r?(?O3xRzzxhr7^(xOu=sJ9AQT^SIgLDH{TzY1YHg+44JgTEacBvaIozLMnX?6xWe-CiBZ!8u5RT(+P^o(9mlWQEt0FWa;$D+whXIRA0o^Iee2Z%ry(+I>piao3>4hahUgq=>XN$e=F ziZdwUrNndH`ElmBu{q#je+c(>nuVq=?30}&huRqh^2_%;1Q%L=(MHSNSLx7lKzA@< z4(L??iog$hgxbL($%Ro&ks-a%qNl*lN08Q@e{kR;hNrn-5y<)cPXf00QvzGTrHL&< z?7cn_ltp7UBb1jl5N5Re7HV@T?7btfc{)wllS8vpaOWgA;JK z@9yqJO%5%ywkX~8w~!*&QV?T(ddP}FM36U2U_V0vH{~t^{WM%tHT^UZBgM6w7|*W> z6-u?>BejOQrmv`L20}`?x|U8chjg9*{RvZMNEdwN4~ehL<1U34pqyf=)5b8xG!Hg; zgihWdElT@m0jYvFaukrSrpQ+%@>`Fv!x{L%_7M4y^cI0s@Bmwn!p2HE*4XH~fg{~( zRHxg|EO3zINZTb2cZ1YB%X=~&5{5R4DN)vR8bzCl>feIAccr)~J>L^OYmy#Hgydga zSJ&0(!%`zSF~J-;xf%SsoSc72uOhS2f>)`~t~n*BLI#oFWA~hrMPsVuebSzZNmN0U zn)c7$RPiPmrH--dXLZ(CFtDZnYtA~2f&$c11*ky*n$hQ9mngtK#~v438-xyLfP87u z4+d81!P?UOB~kY}AY0T8K_L@HTyX>I%9q&hwW-r36fJWhJWIV0A5;4cDn6ltO!^J9 z;+I&&oK_4mPHy|N^!QjW)-@O~^kZFrjfR|$0kO|t; zP`w~+?b_A!Ok@t(fZSFg36iGvaha*TW_)X%jzm0X!fiWl*!IL{Gi-$IxM|yzSfnjA z+h%?rrzVue3o>v)M-k%ZR9vLu11c!d7cDBTP;re4+Qx`askntAUQW+c`iLNyuSpG5 zKpV;21)VtvI%uG5M=Fh}8fvp@R?Is7RkJ3`y18a9oA1b;AZ;;!S*jl1Vo5Sue7B#9K)=s3$A?aLF1-f|yqqs`Jycan9R pq{dwwM@r-peatyX*pR0_=IuliKBrVl23$im@rUJV>wi|%{{g41F@XR8 From 0e7b022a85e283daed0ac2b9250a46bfd238ae52 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sat, 26 May 2018 19:43:47 -0400 Subject: [PATCH 09/35] pycache everywhere --- source/core/__pycache__/__init__.cpython-36.pyc | Bin 144 -> 0 bytes source/core/__pycache__/commands.cpython-36.pyc | Bin 4568 -> 0 bytes source/core/__pycache__/constants.cpython-36.pyc | Bin 656 -> 0 bytes source/core/__pycache__/database.cpython-36.pyc | Bin 4851 -> 0 bytes source/core/__pycache__/utils.cpython-36.pyc | Bin 782 -> 0 bytes 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 source/core/__pycache__/__init__.cpython-36.pyc delete mode 100644 source/core/__pycache__/commands.cpython-36.pyc delete mode 100644 source/core/__pycache__/constants.cpython-36.pyc delete mode 100644 source/core/__pycache__/database.cpython-36.pyc delete mode 100644 source/core/__pycache__/utils.cpython-36.pyc diff --git a/source/core/__pycache__/__init__.cpython-36.pyc b/source/core/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 0f1d958601010bb3bbe5db44af3e4f1f72371f9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 144 zcmXr!<>flQku#bB2p)q77+?f49Dul(1xTbY1T$zd`mJOr0tq9CU$*)g`MIh3CF#Y9 z$r;6|`YuKJ1xfi8`p)?&srtbgsW~~xr6tAs#rdU0$*KCu`9-Pv@$s2?nI-Y@dIgoY TIBatBQ%ZAE?LfvB12F>tNvtE0 diff --git a/source/core/__pycache__/commands.cpython-36.pyc b/source/core/__pycache__/commands.cpython-36.pyc deleted file mode 100644 index 0d405f260ef19df9b4bc704ab4b633129dfc4da3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4568 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 H(60O&ynkE; diff --git a/source/core/__pycache__/constants.cpython-36.pyc b/source/core/__pycache__/constants.cpython-36.pyc deleted file mode 100644 index bf1ec631a9d6088d4a6cd3c7a10494488804e00c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 656 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 diff --git a/source/core/__pycache__/utils.cpython-36.pyc b/source/core/__pycache__/utils.cpython-36.pyc deleted file mode 100644 index fb2aea8d88d90569ebda6df351409b445b51dcdf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 782 zcmYjO&59F25bmCt{Ax5S9uzz@K@YiP6v4w1L|79DELluMmv{)B?#^a*XS#>(9xUFEteDSOCr5_;l8yy*((K~SS90ErirzpcS zoCUK0BOdVZJDP<&;uTmUUY(+D?GJ|3ZctVSlgY(oTHgOOm`q0(P!&R=bYMiqC}ZEB>Fe8dF(DCI5+Z4c`Br#w(SH z*j^dRRz}3{waORjTYRRth^H%&rmV0gHmcA}#7t=s7dA;vFJG4}4>`YU-O(ARAgZB84eW zhDj>M$_`!qz)iG^OT6?N9YWnzoTNZJzaySI;uc(+CIl1&xQ!p-2Uxe^1bnjwQ_@%} it?{$)q+S@GafIzAp*=yppDA9X;;oa+tJ%i&d;b6*|HC%` From 7ed672342c7eb0781931ffc6254dd2e0b2b1d54e Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sun, 27 May 2018 12:24:22 -0400 Subject: [PATCH 10/35] move and delete checks --- .../tests/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 145 bytes .../__pycache__/test_commands.cpython-36.pyc | Bin 0 -> 4112 bytes .../__pycache__/test_database.cpython-36.pyc | Bin 0 -> 5478 bytes source/tests/test_commands.py | 70 +++++++++++++++++- 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 source/tests/__pycache__/__init__.cpython-36.pyc create mode 100644 source/tests/__pycache__/test_commands.cpython-36.pyc create mode 100644 source/tests/__pycache__/test_database.cpython-36.pyc diff --git a/source/tests/__pycache__/__init__.cpython-36.pyc b/source/tests/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a31822fbed724adb2a769b257dcdf9777e0a8fe GIT binary patch literal 145 zcmXr!<>g}g$QjK51dl-k3@`#24nSPY0whuxf*CX!{Z=v*frJsnFFXB={M=OilJw%l zTZlX-=vg$lPKeW&i*#?IW82 literal 0 HcmV?d00001 diff --git a/source/tests/__pycache__/test_commands.cpython-36.pyc b/source/tests/__pycache__/test_commands.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae60768ae5bdb5db8deb5d81c95ef2b8ebb1afe9 GIT binary patch literal 4112 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 literal 0 HcmV?d00001 diff --git a/source/tests/test_commands.py b/source/tests/test_commands.py index 24824cf..bea8a20 100644 --- a/source/tests/test_commands.py +++ b/source/tests/test_commands.py @@ -10,6 +10,11 @@ class CommandTester(unittest.TestCase): """""" + def setUp(self): + """""" + if SHELLCUTS_FILE.is_file(): + SHELLCUTS_FILE.unlink() + @patch('core.utils.VERSION_FILE', VERSION_FILE) @patch('sys.stdout', new_callable=StringIO) def test_000_check_version_command(self, stream): @@ -51,9 +56,6 @@ def test_002_check_init_command(self, stream): @patch('sys.stdout', new_callable=StringIO) def test_003_check_new_command(self, stream, mock_getcwd): """Confirm that command_new adds a shellcut to a database.""" - if SHELLCUTS_FILE.is_file(): - SHELLCUTS_FILE.unlink() - command_new('test1') # Check that a courtesy message is printed to the screen. @@ -76,3 +78,65 @@ def test_003_check_new_command(self, stream, mock_getcwd): self.assertEqual(db.get_shellcut('test1')[0], 'test1') self.assertEqual(db.get_shellcut_path('test1'), '/tmp/test2/') self.assertEqual(len(db.get_all_shellcuts()), 1) + + @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) + @patch('os.getcwd', return_value='/tmp/test1/') + @patch('sys.stdout', new_callable=StringIO) + def test_004_check_move_command(self, stream, mock_getcwd): + command_move('test1') + + # Move will function as the new command if the shellcut doesn't exist. + self.assertTrue(re.search('Moved shellcut \'test1\'', stream.getvalue())) + + # Check that the new shellcut is there. + with DatabaseConnection(SHELLCUTS_FILE) as db: + self.assertEqual(db.get_shellcut('test1')[0], 'test1') + self.assertEqual(db.get_shellcut_path('test1'), '/tmp/test1/') + + # Change the working directory. + mock_getcwd.return_value = '/tmp/test2/' + + command_move('test1') + + # Now there should only be one shellcut pointing to the latest directory. + with DatabaseConnection(SHELLCUTS_FILE) as db: + self.assertEqual(db.get_shellcut('test1')[0], 'test1') + self.assertEqual(db.get_shellcut_path('test1'), '/tmp/test2/') + self.assertEqual(len(db.get_all_shellcuts()), 1) + + + @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) + @patch('sys.stdout', new_callable=StringIO) + def test_005_check_delete_command(self, stream): + """Confirm that the delete command works properly.""" + # Create a database with 3 entries. + with DatabaseConnection(SHELLCUTS_FILE) as db: + db.insert_shellcut('test1', '/tmp/test1') + db.insert_shellcut('test2', '/tmp/test2') + db.insert_shellcut('test3', '/tmp/test3') + self.assertEqual(len(db.get_all_shellcuts()), 3) + + command_delete('test2') + + # Confirm that the deletion message is printed. + self.assertTrue(re.search('Deleted shellcut \'test2\'', stream.getvalue())) + + # Confirm that only test2 was removed from the database. + with DatabaseConnection(SHELLCUTS_FILE) as db: + self.assertEqual(db.get_shellcut('test1')[0], 'test1') + self.assertIsNone(db.get_shellcut('test2')) + self.assertEqual(db.get_shellcut('test3')[0], 'test3') + + # Attempt to remove something from the database that isn't there. + command_delete('test4') + + # Confirm that the deletion message shows anyway. + self.assertTrue(re.search('Deleted shellcut \'test4\'', stream.getvalue())) + + # Check that nothing was deleted. + with DatabaseConnection(SHELLCUTS_FILE) as db: + self.assertEqual(len(db.get_all_shellcuts()), 2) + + + + From bd46889bb8318476e42862fa5843a733c9578744 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sun, 27 May 2018 13:38:35 -0400 Subject: [PATCH 11/35] clean up comments and add more tests --- source/tests/test_commands.py | 129 +++++++++++++++++++++++++--------- 1 file changed, 97 insertions(+), 32 deletions(-) diff --git a/source/tests/test_commands.py b/source/tests/test_commands.py index bea8a20..dc6dc89 100644 --- a/source/tests/test_commands.py +++ b/source/tests/test_commands.py @@ -1,3 +1,5 @@ +""" +""" import re import unittest from io import StringIO @@ -9,67 +11,94 @@ SHELLCUTS_FILE = Path('/tmp/shellcuts_database.db') class CommandTester(unittest.TestCase): - """""" + """Testing class for all commands. + + These commands generally wrap the database operations that form the + backbone of the whole program. + """ def setUp(self): - """""" + """Method called before every test.""" + self.delete_test_database() + + def delete_test_database(self): + """Delete test database if it exists.""" if SHELLCUTS_FILE.is_file(): SHELLCUTS_FILE.unlink() + def create_test_database(self): + """Create a test database with some sample entries.""" + with DatabaseConnection(SHELLCUTS_FILE) as db: + db.insert_shellcut('test1', '/tmp/test1') + db.insert_shellcut('test2', '/tmp/test2') + db.insert_shellcut('test3', '/tmp/test3') + db.insert_shellcut('test4', '/tmp/test4') + @patch('core.utils.VERSION_FILE', VERSION_FILE) @patch('sys.stdout', new_callable=StringIO) def test_000_check_version_command(self, stream): - """Confirm that command_version can open the version information. + """Confirm that version command can open the version information. - Also tests that if the information is missing, version throws the - NoVersion error. + Also tests that if the information is missing, version command throws + the NoVersion error. The first patch decorator replaces lookups of the + constant VERSION_FILE with a custom VERSION_FILE defined in this test + script. The second patch decorator forces called functions to print to + the StringIO object 'stream' instead of stdout. This keeps the screen + clean and saves the output for regex checks. """ + # Delete version file if it exists. if VERSION_FILE.is_file(): VERSION_FILE.unlink() command_version() - + # Should throw the NoVersion error here. self.assertTrue(re.search('NoVersion', stream.getvalue())) - VERSION_FILE.touch() + # Create a version file with some junk in it. + VERSION_FILE.touch() VERSION_FILE.write_text('Test version information.') command_version() - - self.assertTrue(re.search('Test version information', stream.getvalue())) + # Should now print that junk to the screen. + self.assertTrue(re.search('Test version information', + stream.getvalue())) @patch('sys.stdout', new_callable=StringIO) def test_001_check_help_command(self, stream): - """Confirm that the command_help prints a help menu.""" + """Confirm that the help command prints a help menu.""" command_help() - self.assertTrue(re.search('Shellcuts usage:', stream.getvalue())) @patch('sys.stdout', new_callable=StringIO) def test_002_check_init_command(self, stream): - """Confirm that command_init returns the command to run the init file.""" + """Confirm that the init command returns the command for the init file.""" command_init() - - self.assertTrue(re.search('python3 /usr/bin/sc-init', stream.getvalue())) + self.assertTrue(re.search('python3 /usr/bin/sc-init', + stream.getvalue())) @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) @patch('os.getcwd', return_value='/tmp/test1/') @patch('sys.stdout', new_callable=StringIO) def test_003_check_new_command(self, stream, mock_getcwd): - """Confirm that command_new adds a shellcut to a database.""" + """Confirm that the new command adds a shellcut to a database. + + The second patch mocks the getcwd command in the os module. When this + command is called it will automatically return the return_value + defined in the decorator. This return value can be manipulated later + in the function. + """ command_new('test1') # Check that a courtesy message is printed to the screen. self.assertTrue(re.search('Added new shellcut \'test1\'', stream.getvalue())) - # Check that the new shellcut is there. + # Check that the new shellcut is in the database. with DatabaseConnection(SHELLCUTS_FILE) as db: self.assertEqual(db.get_shellcut('test1')[0], 'test1') self.assertEqual(db.get_shellcut_path('test1'), '/tmp/test1/') - # Change the 'current working directory,' meaning a second call to the - # new command will overwrite the previous entry. + # Change the mock current working directory so a second call to + # command_new will overwrite the old version. mock_getcwd.return_value = '/tmp/test2/' - command_new('test1') # Confirm that the previous entry was overwritten, and that there is @@ -83,19 +112,22 @@ def test_003_check_new_command(self, stream, mock_getcwd): @patch('os.getcwd', return_value='/tmp/test1/') @patch('sys.stdout', new_callable=StringIO) def test_004_check_move_command(self, stream, mock_getcwd): + """Confirm that the move command moves a shellcut to a new location. + + This command is essentially identical to the new command. + """ command_move('test1') # Move will function as the new command if the shellcut doesn't exist. self.assertTrue(re.search('Moved shellcut \'test1\'', stream.getvalue())) - # Check that the new shellcut is there. + # Check that the new shellcut is in the database. with DatabaseConnection(SHELLCUTS_FILE) as db: self.assertEqual(db.get_shellcut('test1')[0], 'test1') self.assertEqual(db.get_shellcut_path('test1'), '/tmp/test1/') # Change the working directory. mock_getcwd.return_value = '/tmp/test2/' - command_move('test1') # Now there should only be one shellcut pointing to the latest directory. @@ -109,13 +141,7 @@ def test_004_check_move_command(self, stream, mock_getcwd): @patch('sys.stdout', new_callable=StringIO) def test_005_check_delete_command(self, stream): """Confirm that the delete command works properly.""" - # Create a database with 3 entries. - with DatabaseConnection(SHELLCUTS_FILE) as db: - db.insert_shellcut('test1', '/tmp/test1') - db.insert_shellcut('test2', '/tmp/test2') - db.insert_shellcut('test3', '/tmp/test3') - self.assertEqual(len(db.get_all_shellcuts()), 3) - + self.create_test_database() command_delete('test2') # Confirm that the deletion message is printed. @@ -128,15 +154,54 @@ def test_005_check_delete_command(self, stream): self.assertEqual(db.get_shellcut('test3')[0], 'test3') # Attempt to remove something from the database that isn't there. - command_delete('test4') + command_delete('test5') # Confirm that the deletion message shows anyway. - self.assertTrue(re.search('Deleted shellcut \'test4\'', stream.getvalue())) + self.assertTrue(re.search('Deleted shellcut \'test5\'', stream.getvalue())) - # Check that nothing was deleted. + # Confirm that nothing was deleted. with DatabaseConnection(SHELLCUTS_FILE) as db: - self.assertEqual(len(db.get_all_shellcuts()), 2) + self.assertEqual(len(db.get_all_shellcuts()), 3) + @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) + @patch('sys.stdout', new_callable=StringIO) + def test_006_check_print_command(self, stream): + """Confirm that the print command prints correctly.""" + self.create_test_database() + + command_print('test1') + + # Confirm that the output stream contains information from test1. + self.assertTrue(re.search('test1.*/tmp/test1.*None', stream.getvalue())) + + command_print('test5') + + # Confirm that attempting to print something that is not in the + # database will throw the DoesNotExist error. + self.assertTrue(re.search('DoesNotExist', stream.getvalue())) + + @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) + @patch('sys.stdout', new_callable=StringIO) + def test_007_check_list_command(self, stream): + """Confirm that the list command prints properly.""" + self.create_test_database() + command_list() + # Check that each of the four entries is printed into the output stream. + for num in range(1, 5): + self.assertTrue(re.search('test{0}.*/tmp/test{0}.*None'.format(num), + stream.getvalue())) + self.delete_test_database() + command_list() + # Check that if the database is empty, a helpful message appears. + self.assertTrue(re.search('No shellcuts yet.', stream.getvalue())) + + def test_008_check_go_command(self): + """""" + pass + + def test_009_check_bashmarks_command(self): + """""" + pass From f63968b44c3adb1fa94f4f97d75c6cb0d9d7a8b1 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sun, 27 May 2018 13:55:05 -0400 Subject: [PATCH 12/35] Improved comments --- source/tests/test_commands.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/tests/test_commands.py b/source/tests/test_commands.py index dc6dc89..6cae957 100644 --- a/source/tests/test_commands.py +++ b/source/tests/test_commands.py @@ -1,4 +1,6 @@ -""" +"""Define a testing class for all commands in core package. + +Part of Shellcuts by Tgsachse. """ import re import unittest From 11fb5c909db1df21b8f962d5e7fe9cc157bfed7e Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sun, 27 May 2018 14:25:24 -0400 Subject: [PATCH 13/35] add go command test --- source/tests/test_commands.py | 47 ++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/source/tests/test_commands.py b/source/tests/test_commands.py index 6cae957..20ccd2c 100644 --- a/source/tests/test_commands.py +++ b/source/tests/test_commands.py @@ -30,10 +30,10 @@ def delete_test_database(self): def create_test_database(self): """Create a test database with some sample entries.""" with DatabaseConnection(SHELLCUTS_FILE) as db: - db.insert_shellcut('test1', '/tmp/test1') - db.insert_shellcut('test2', '/tmp/test2') - db.insert_shellcut('test3', '/tmp/test3') - db.insert_shellcut('test4', '/tmp/test4') + db.insert_shellcut('test1', '/tmp/test1/') + db.insert_shellcut('test2', '/tmp/test2/') + db.insert_shellcut('test3', '/tmp/test3/') + db.insert_shellcut('test4', '/tmp/test4/') @patch('core.utils.VERSION_FILE', VERSION_FILE) @patch('sys.stdout', new_callable=StringIO) @@ -174,7 +174,7 @@ def test_006_check_print_command(self, stream): command_print('test1') # Confirm that the output stream contains information from test1. - self.assertTrue(re.search('test1.*/tmp/test1.*None', stream.getvalue())) + self.assertTrue(re.search('test1.*/tmp/test1/.*None', stream.getvalue())) command_print('test5') @@ -191,7 +191,7 @@ def test_007_check_list_command(self, stream): # Check that each of the four entries is printed into the output stream. for num in range(1, 5): - self.assertTrue(re.search('test{0}.*/tmp/test{0}.*None'.format(num), + self.assertTrue(re.search('test{0}.*/tmp/test{0}/.*None'.format(num), stream.getvalue())) self.delete_test_database() @@ -200,9 +200,38 @@ def test_007_check_list_command(self, stream): # Check that if the database is empty, a helpful message appears. self.assertTrue(re.search('No shellcuts yet.', stream.getvalue())) - def test_008_check_go_command(self): - """""" - pass + @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) + @patch('sys.stdout', new_callable=StringIO) + def test_008_check_go_command(self, stream): + """Test the multiple behaviors of the go command.""" + self.create_test_database() + destination = Path('/tmp/test4/') + if destination.is_dir(): + destination.rmdir() + + # Attempting to go to a nonexistant shellcut results in a + # DoesNotExist error. + command_go('test5') + self.assertTrue(re.search('DoesNotExist', stream.getvalue())) + + # Attempting to go to a shellcut whose path does not exist throws a + # BadPath error and deletes the shellcut from the database. + command_go('test4') + self.assertTrue(re.search('BadPath', stream.getvalue())) + + # Confirm that the shellcut has been deleted from the database. + command_go('test4') + self.assertTrue(re.search('DoesNotExist', stream.getvalue())) + + # Make the test4 path valid. + destination.mkdir() + # Re-add test4 to the database. + with DatabaseConnection(SHELLCUTS_FILE) as db: + db.insert_shellcut('test4', '/tmp/test4/') + + # Call command_go for a valid shellcut and retrieve the cd command. + command_go('test4') + self.assertTrue(re.search('cd "/tmp/test4/"', stream.getvalue())) def test_009_check_bashmarks_command(self): """""" From df41aad679938e795addb21d949eccd7a8cf5fea Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sun, 27 May 2018 15:13:02 -0400 Subject: [PATCH 14/35] partially implemented test for bashmarks --- .../tests/__pycache__/__init__.cpython-36.pyc | Bin 145 -> 0 bytes .../__pycache__/test_commands.cpython-36.pyc | Bin 4112 -> 0 bytes .../__pycache__/test_database.cpython-36.pyc | Bin 5478 -> 0 bytes source/tests/test_commands.py | 28 ++++++++++++++++-- 4 files changed, 25 insertions(+), 3 deletions(-) delete mode 100644 source/tests/__pycache__/__init__.cpython-36.pyc delete mode 100644 source/tests/__pycache__/test_commands.cpython-36.pyc delete mode 100644 source/tests/__pycache__/test_database.cpython-36.pyc diff --git a/source/tests/__pycache__/__init__.cpython-36.pyc b/source/tests/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 5a31822fbed724adb2a769b257dcdf9777e0a8fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmXr!<>g}g$QjK51dl-k3@`#24nSPY0whuxf*CX!{Z=v*frJsnFFXB={M=OilJw%l zTZlX-=vg$lPKeW&i*#?IW82 diff --git a/source/tests/__pycache__/test_commands.cpython-36.pyc b/source/tests/__pycache__/test_commands.cpython-36.pyc deleted file mode 100644 index ae60768ae5bdb5db8deb5d81c95ef2b8ebb1afe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4112 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 diff --git a/source/tests/test_commands.py b/source/tests/test_commands.py index 20ccd2c..735cf6d 100644 --- a/source/tests/test_commands.py +++ b/source/tests/test_commands.py @@ -3,6 +3,7 @@ Part of Shellcuts by Tgsachse. """ import re +import shutil import unittest from io import StringIO from core.utils import * @@ -233,6 +234,27 @@ def test_008_check_go_command(self, stream): command_go('test4') self.assertTrue(re.search('cd "/tmp/test4/"', stream.getvalue())) - def test_009_check_bashmarks_command(self): - """""" - pass + @patch('core.commands.SHELL_CONFIGS', Path('/tmp/src/')) + @patch('core.commands.SHELLCUTS_FILE', Path('/tmp/dest/shellcuts.db')) + @patch('sys.stdout', new_callable=StringIO) + def test_009_check_bashmarks_command(self, stream): + """Unfinished.""" + dest_directories = [Path('/tmp/dest/bash/'), + Path('/tmp/dest/fish/'), + Path('/tmp/dest/zsh/')] + src_directories = [Path('/tmp/src/bash/'), + Path('/tmp/src/fish/'), + Path('/tmp/src/zsh/')] + + shutil.rmtree(str('/tmp/dest/')) + for directory in dest_directories: + directory.mkdir(parents=True) + + shutil.rmtree(str('/tmp/src/')) + for directory in src_directories: + directory.mkdir(parents=True) + directory.joinpath('bashmarks-aliases.txt').touch() + directory.joinpath('other1.txt').touch() + directory.joinpath('other2.txt').touch() + + command_bashmarks(True) From e6715d139d5694bfdc206df1e64b67e4644a84b4 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Wed, 29 Aug 2018 18:57:20 -0400 Subject: [PATCH 15/35] im going to nuke the repo again --- shells/bash/controller.sh | 24 +++++++------------ .../plugins/bashmarks_aliases.sh} | 4 ++-- shells/bash/plugins/full_name_aliases.sh | 7 ++++++ shells/zsh/controller.sh | 24 +++++++------------ .../plugins/bashmarks_aliases.sh} | 0 shells/zsh/plugins/full_name_aliases.sh | 7 ++++++ source/{core => }/init.py | 0 7 files changed, 34 insertions(+), 32 deletions(-) rename shells/{zsh/plugins/bashmarks-aliases.sh => bash/plugins/bashmarks_aliases.sh} (65%) create mode 100644 shells/bash/plugins/full_name_aliases.sh rename shells/{bash/plugins/bashmarks-aliases.sh => zsh/plugins/bashmarks_aliases.sh} (100%) create mode 100644 shells/zsh/plugins/full_name_aliases.sh rename source/{core => }/init.py (100%) diff --git a/shells/bash/controller.sh b/shells/bash/controller.sh index de54a6c..882b3fd 100644 --- a/shells/bash/controller.sh +++ b/shells/bash/controller.sh @@ -1,21 +1,15 @@ # Part of Shellcuts by Tgsachse. -# File path constants. -F_BASHMARKS=~/.config/shellcuts/bash/bashmarks-aliases.sh +HANDLER_PATH=~/.shellcuts/binary/sc_handler +PLUGINS_PATH=~/.shellcuts/shells/bash/plugins -# Core function of program. Sends first two arguments to sc-handler. -# sc-handler returns a function, which is then executed. +# Get a command from the handler, based on the given +# inputs, then execute that command. function sc { - eval "$(python3 /usr/bin/sc-handler $1 $2)" + eval "$(python3 $HANDLER_PATH $1 $2)" } -# Full-name aliases. -alias shellcut="sc" -alias shellcuts="sc" -alias shellc="sc" -alias scut="sc" - -# If Bashmarks syntax is enabled (e.g. the file exists), load it. -if [ -f "$F_BASHMARKS" ]; then - . $F_BASHMARKS -fi +# Load all shell plugins. +for FILE in ${PLUGINS_PATH}/*; do + . $FILE +done diff --git a/shells/zsh/plugins/bashmarks-aliases.sh b/shells/bash/plugins/bashmarks_aliases.sh similarity index 65% rename from shells/zsh/plugins/bashmarks-aliases.sh rename to shells/bash/plugins/bashmarks_aliases.sh index b1d4346..2fc0fd9 100644 --- a/shells/zsh/plugins/bashmarks-aliases.sh +++ b/shells/bash/plugins/bashmarks_aliases.sh @@ -1,8 +1,8 @@ # Part of Shellcuts by Tgsachse. -# Aliases to emulate the original huyng/bashmarks syntax. -alias s="sc -n" +# Aliases that emulate the original Bashmarks syntax. alias g="sc" +alias s="sc -n" alias p="sc -p" alias d="sc -d" alias l="sc -l" diff --git a/shells/bash/plugins/full_name_aliases.sh b/shells/bash/plugins/full_name_aliases.sh new file mode 100644 index 0000000..2f54598 --- /dev/null +++ b/shells/bash/plugins/full_name_aliases.sh @@ -0,0 +1,7 @@ +# Part of Shellcuts by Tiger Sachse. + +# Full-name aliases. +alias scut="sc" +alias shellc="sc" +alias shellcut="sc" +alias shellcuts="sc" diff --git a/shells/zsh/controller.sh b/shells/zsh/controller.sh index 6cf3e63..882b3fd 100644 --- a/shells/zsh/controller.sh +++ b/shells/zsh/controller.sh @@ -1,21 +1,15 @@ # Part of Shellcuts by Tgsachse. -# File path constants. -F_BASHMARKS=~/.config/shellcuts/zsh/bashmarks-aliases.sh +HANDLER_PATH=~/.shellcuts/binary/sc_handler +PLUGINS_PATH=~/.shellcuts/shells/bash/plugins -# Core function of program. Sends first two arguments to sc-handler. -# sc-handler returns a function, which is then executed. +# Get a command from the handler, based on the given +# inputs, then execute that command. function sc { - eval "$(python3 /usr/bin/sc-handler $1 $2)" + eval "$(python3 $HANDLER_PATH $1 $2)" } -# Full-name aliases. -alias shellcut="sc" -alias shellcuts="sc" -alias shellc="sc" -alias scut="sc" - -# If Bashmarks syntax is enabled (e.g. the file exists), load it. -if [ -f "$F_BASHMARKS" ]; then - . $F_BASHMARKS -fi +# Load all shell plugins. +for FILE in ${PLUGINS_PATH}/*; do + . $FILE +done diff --git a/shells/bash/plugins/bashmarks-aliases.sh b/shells/zsh/plugins/bashmarks_aliases.sh similarity index 100% rename from shells/bash/plugins/bashmarks-aliases.sh rename to shells/zsh/plugins/bashmarks_aliases.sh diff --git a/shells/zsh/plugins/full_name_aliases.sh b/shells/zsh/plugins/full_name_aliases.sh new file mode 100644 index 0000000..52e0bc3 --- /dev/null +++ b/shells/zsh/plugins/full_name_aliases.sh @@ -0,0 +1,7 @@ +# Part of Shellcuts by Tiger Sachse. + +# Full-name aliases. +alias shellcut="sc" +alias shellcuts="sc" +alias shellc="sc" +alias scut="sc" diff --git a/source/core/init.py b/source/init.py similarity index 100% rename from source/core/init.py rename to source/init.py From 5b94880313a8118d235cfc6057de6fcca6065202 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Wed, 29 Aug 2018 19:13:40 -0400 Subject: [PATCH 16/35] shells reworked --- bin/sc | 1 - build.py | 118 ------------------ dist/shellcuts-1.2.1-1.fc27.noarch.rpm | Bin 32184 -> 0 bytes dist/shellcuts.deb | Bin 22960 -> 0 bytes install.sh | 0 pack/deb/control | 10 -- pack/rpm/shellcuts.spec | 93 -------------- shells/bash/bashrc.example | 5 +- shells/bash/controller.sh | 12 +- ...name_aliases.sh => descriptive_aliases.sh} | 3 +- shells/fish/config.fish.example | 5 +- shells/fish/controller.fish | 22 ++-- ...ks-aliases.fish => bashmarks_aliases.fish} | 4 +- shells/fish/plugins/descriptive_aliases.fish | 8 ++ shells/zsh/controller.sh | 12 +- shells/zsh/plugins/bashmarks_aliases.sh | 4 +- ...name_aliases.sh => descriptive_aliases.sh} | 7 +- shells/zsh/zshrc.example | 5 +- 18 files changed, 47 insertions(+), 262 deletions(-) delete mode 100755 bin/sc delete mode 100755 build.py delete mode 100644 dist/shellcuts-1.2.1-1.fc27.noarch.rpm delete mode 100644 dist/shellcuts.deb create mode 100644 install.sh delete mode 100644 pack/deb/control delete mode 100644 pack/rpm/shellcuts.spec rename shells/bash/plugins/{full_name_aliases.sh => descriptive_aliases.sh} (63%) rename shells/fish/plugins/{bashmarks-aliases.fish => bashmarks_aliases.fish} (65%) create mode 100644 shells/fish/plugins/descriptive_aliases.fish rename shells/zsh/plugins/{full_name_aliases.sh => descriptive_aliases.sh} (63%) diff --git a/bin/sc b/bin/sc deleted file mode 100755 index 61f9041..0000000 --- a/bin/sc +++ /dev/null @@ -1 +0,0 @@ -python3 /usr/bin/sc-init diff --git a/build.py b/build.py deleted file mode 100755 index e4b326f..0000000 --- a/build.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python3 -"""Builds Shellcuts into a variety of packages. - -Feel free to submit additions to this script to include more types of packages. -One of my primary goals for Shellcuts is universal support. - -Part of Shellcuts by Tgsachse. -""" -import os -import shutil -import subprocess -import compileall -from pathlib import Path -from sys import argv as args - -def chop_trees(trees): - """Remove each directory tree in trees if it exists.""" - for tree in trees: - try: - shutil.rmtree(tree) - except FileNotFoundError: - pass - -def build_deb(): - """Build deb package from source.""" - DEB = 'shellcuts.deb' - BUILD = Path.cwd().joinpath('shellcuts') - DEB_TREE = ( - ('pack/deb/', 'shellcuts/DEBIAN/', ()), - ('bin/', 'shellcuts/usr/bin/', ('*.py',)), - ('share/', 'shellcuts/usr/share/shellcuts/', ()), - ('docs/', 'shellcuts/usr/share/man/man1/', ('*.txt', '*.rst')), - ('docs/', 'shellcuts/usr/share/doc/shellcuts/', ('shellcuts.1',))) - COMPILED_FILES = Path('bin/__pycache__') - - # Cuts down leftover trees. - chop_trees((BUILD,)) - - # Creates shellcuts tree based on DEB_TREE tuple. - for branch in DEB_TREE: - shutil.copytree(branch[0], - branch[1], - ignore=shutil.ignore_patterns(*branch[2])) - - # Compiles core files into bytecode and moves into the shellcuts tree. - compileall.compile_dir(DEB_TREE[1][0]) - for f in COMPILED_FILES.iterdir(): - # Drops all suffixes. - dest_name = f.stem.split('.')[0] - shutil.move(f, BUILD.joinpath('usr/bin').joinpath(dest_name)) - - # Builds deb package. - subprocess.run(('dpkg', '--build', 'shellcuts')) - - # Makes the DIST directory if it doesn't exist. - DIST.mkdir(exist_ok=True) - - # Moves the package to the DIST directory. - shutil.copy(DEB, DIST) - - # Chops down all new trees and delete copied deb archive. - chop_trees((BUILD, COMPILED_FILES)) - os.remove(DEB) - -def build_rpm(): - """Build RPM package from source.""" - ARCHIVE_NAME = 'v1.2.2' - SOURCE_SPEC = 'pack/rpm/shellcuts.spec' - ARCHIVE = Path.cwd().joinpath(ARCHIVE_NAME) - RPM_BUILD = Path('~/rpmbuild').expanduser() - TARBALL_CONTENTS = ('docs', 'share', 'bin') - DESTINATION_SPEC = RPM_BUILD.joinpath('SPECS/shellcuts.spec') - TARBALL = RPM_BUILD.joinpath(Path('SOURCES/').joinpath(ARCHIVE_NAME)) - - # Cuts down leftover trees. - chop_trees((ARCHIVE, RPM_BUILD)) - - # Copies source into tarball folder. - for directory in TARBALL_CONTENTS: - shutil.copytree(directory, ARCHIVE.joinpath(directory)) - - # Generates the RPM build folder. - RPM_BUILD.joinpath('BUILD').mkdir(parents=True) - RPM_BUILD.joinpath('RPMS/noarch').mkdir(parents=True) - RPM_BUILD.joinpath('SOURCES').mkdir(parents=True) - RPM_BUILD.joinpath('SPECS').mkdir(parents=True) - RPM_BUILD.joinpath('SRPMS').mkdir(parents=True) - - # Compresses a tarball from source and copy files into RPM build folder. - shutil.make_archive(TARBALL, 'gztar', os.getcwd(), ARCHIVE_NAME) - shutil.copy(SOURCE_SPEC, DESTINATION_SPEC) - - # Builds the RPM using the SPEC file. - subprocess.run(('rpmbuild', '-bb', DESTINATION_SPEC)) - - # Makes the DIST directory if it doesn't exist. - DIST.mkdir(exist_ok=True) - - # Moves the RPM back to the project directory. - for package in RPM_BUILD.joinpath('RPMS/noarch/').iterdir(): - shutil.copy(package, DIST) - - # Chops down all generated trees. - chop_trees((RPM_BUILD, ARCHIVE)) - - -### MAIN PROGRAM ### -DIST = Path.cwd().joinpath('dist/') - -if len(args) > 1: - if args[1] == 'deb': - build_deb() - quit() - elif args[1] == 'rpm': - build_rpm() - quit() - -print("Pass either 'deb' or 'rpm' as the first argument.") diff --git a/dist/shellcuts-1.2.1-1.fc27.noarch.rpm b/dist/shellcuts-1.2.1-1.fc27.noarch.rpm deleted file mode 100644 index be38481be7b2d88c1084bb2fe76003e9cc54d42c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32184 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 diff --git a/dist/shellcuts.deb b/dist/shellcuts.deb deleted file mode 100644 index 5ef228953947552439800a8189596a931f5e23c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22960 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 b00FwL0f5j2Oo+%IvBYQl0ssI200dcD%l=mm diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..e69de29 diff --git a/pack/deb/control b/pack/deb/control deleted file mode 100644 index ab7c4a0..0000000 --- a/pack/deb/control +++ /dev/null @@ -1,10 +0,0 @@ -Package: shellcuts -Version: 1.2.2 -Section: utils -Depends: python3 -Architecture: all -Priority: optional -Maintainer: Tiger Sachse -Homepage: https://www.github.com/tgsachse/shellcuts -Description: directory shortcuts for your shell - See the manpage for more information. diff --git a/pack/rpm/shellcuts.spec b/pack/rpm/shellcuts.spec deleted file mode 100644 index c80ef87..0000000 --- a/pack/rpm/shellcuts.spec +++ /dev/null @@ -1,93 +0,0 @@ -# Part of Shellcuts by Tgsachse - -### METADATA ### -Name: shellcuts - -Version: 1.2.2 -Release: 1%{?dist} -Summary: directory shortcuts for your shell - -License: GPLv3 -BuildArch: noarch -BuildRequires: python3 -Requires: python3 -URL: https://www.github.com/tgsachse/%{name} -Source0: https://www.github.com/tgsachse/archive/v%{version}.tar.gz - - -### DEFINES ### -%define share %{buildroot}/usr/share - -# Honestly unsure why this line is needed. -%global debug_package %{nil} - -%description -Shellcuts are directory shortcuts for your shell. This program allows you to save locations -as 'shellcuts' and then cut back to those locations with a single, short command. - - -### BUILD PROCESS ### -%prep -%autosetup -n v%{version} - -%build -# Compiles core python files. -python3 -m compileall bin/sc-handler.py bin/sc-init.py - -%install -rm -rf $RPM_BUILD_ROOT - -# Makes the directory structure for the package. -mkdir -p %{share}/man/man1 -mkdir -p %{share}/doc/%{name} -mkdir -p %{buildroot}/usr/bin -mkdir -p %{share}/%{name}/zsh -mkdir -p %{share}/%{name}/bash -mkdir -p %{share}/%{name}/fish - -# Moves files from their unzipped locations into the directory structure. -install -m 0555 bin/sc %{buildroot}/usr/bin/ -install -m 0555 bin/__pycache__/sc-init.cpython-36.pyc %{buildroot}/usr/bin/sc-init -install -m 0555 bin/__pycache__/sc-handler.cpython-36.pyc %{buildroot}/usr/bin/sc-handler - -install -m 0444 docs/*.1 %{share}/man/man1/ -install -m 0444 docs/*.txt %{share}/doc/%{name}/ -install -m 0444 docs/*.rst %{share}/doc/%{name}/ - -install -m 0444 share/zsh/* %{share}/%{name}/zsh/ -install -m 0444 share/bash/* %{share}/%{name}/bash/ -install -m 0444 share/fish/* %{share}/%{name}/fish/ - - -### STRUCTURE ### -%files -/usr/bin/sc -/usr/bin/sc-init -/usr/bin/sc-handler - -/usr/share/%{name}/* - -%doc /usr/share/man/man1/* -%doc /usr/share/doc/%{name}/* - -%dir /usr/share/%{name}/ -%dir /usr/share/doc/%{name} -%dir /usr/share/%{name}/zsh/ -%dir /usr/share/%{name}/bash/ -%dir /usr/share/%{name}/fish/ - -%readme /usr/share/doc/%{name}/README.rst -%license /usr/share/doc/%{name}/LICENSE.txt - - -### CHANGES ### -%changelog -* Wed Feb 28 2018 Tiger Sachse -- reorganizing file for clarity -- including support for install source code - -* Sat Feb 10 2018 Tiger Sachse -- preparing for initial release - -* Thu Feb 8 2018 Tiger Sachse -- initial packaging diff --git a/shells/bash/bashrc.example b/shells/bash/bashrc.example index 57ac558..c874cc8 100644 --- a/shells/bash/bashrc.example +++ b/shells/bash/bashrc.example @@ -1,4 +1,5 @@ # Load Shellcuts controller file if it exists. -if [ -f ~/.config/shellcuts/bash/controller.sh ]; then - . ~/.config/shellcuts/bash/controller.sh +CONTROLLER="~/.shellcuts/config/bash/controller.sh" +if [ -f $CONTROLLER ]; then + . $CONTROLLER fi diff --git a/shells/bash/controller.sh b/shells/bash/controller.sh index 882b3fd..8279967 100644 --- a/shells/bash/controller.sh +++ b/shells/bash/controller.sh @@ -1,15 +1,15 @@ # Part of Shellcuts by Tgsachse. -HANDLER_PATH=~/.shellcuts/binary/sc_handler -PLUGINS_PATH=~/.shellcuts/shells/bash/plugins +HANDLER="~/.shellcuts/binaries/shellcuts.pyc" +PLUGINS="~/.shellcuts/shells/bash/plugins/*" -# Get a command from the handler, based on the given -# inputs, then execute that command. +# Get a command from the Python handler, based on +# the given inputs, then execute that command. function sc { - eval "$(python3 $HANDLER_PATH $1 $2)" + eval "$(python3 $HANDLER $1 $2)" } # Load all shell plugins. -for FILE in ${PLUGINS_PATH}/*; do +for FILE in $PLUGINS; do . $FILE done diff --git a/shells/bash/plugins/full_name_aliases.sh b/shells/bash/plugins/descriptive_aliases.sh similarity index 63% rename from shells/bash/plugins/full_name_aliases.sh rename to shells/bash/plugins/descriptive_aliases.sh index 2f54598..a52b985 100644 --- a/shells/bash/plugins/full_name_aliases.sh +++ b/shells/bash/plugins/descriptive_aliases.sh @@ -1,6 +1,7 @@ # Part of Shellcuts by Tiger Sachse. -# Full-name aliases. +# Provide longer, more descriptive versions +# of the sc command. alias scut="sc" alias shellc="sc" alias shellcut="sc" diff --git a/shells/fish/config.fish.example b/shells/fish/config.fish.example index 205b656..ae408f9 100644 --- a/shells/fish/config.fish.example +++ b/shells/fish/config.fish.example @@ -1,4 +1,5 @@ # Load Shellcuts controller file if it exists. -if test -e ~/.config/shellcuts/fish/controller.fish - . ~/.config/shellcuts/fish/controller.fish +set CONTROLLER "~/.shellcuts/config/fish/controller.fish" +if test -e $CONTROLLER + . $CONTROLLER end diff --git a/shells/fish/controller.fish b/shells/fish/controller.fish index b7d8b5e..69cdd26 100644 --- a/shells/fish/controller.fish +++ b/shells/fish/controller.fish @@ -1,23 +1,17 @@ # Part of Shellcuts by Tgsachse. -# File path constants. -set F_BASHMARKS ~/.config/shellcuts/fish/bashmarks-aliases.fish +set HANDLER "~/.shellcuts/binaries/shellcuts.pyc" +set PLUGINS "~/.shellcuts/shells/fish/plugins/*" -# Core function of program. Sends first two arguments to sc-handler. -# sc-handler returns a function, which is then executed. +# Get a command from the Python handler, based on +# the given inputs, then execute that command. function sc set -l IFS - eval (python3 /usr/bin/sc-handler $argv) + eval (python3 $HANDLER $argv) set -e IFS end -# Full-name aliases. -alias shellcut="sc" -alias shellcuts="sc" -alias shellc="sc" -alias scut="sc" - -# If Bashmarks syntax is enabled (e.g. the file exists), load it. -if test -e $F_BASHMARKS - . $F_BASHMARKS +# Load all shell plugins. +for FILE in $PLUGINS + . $FILE end diff --git a/shells/fish/plugins/bashmarks-aliases.fish b/shells/fish/plugins/bashmarks_aliases.fish similarity index 65% rename from shells/fish/plugins/bashmarks-aliases.fish rename to shells/fish/plugins/bashmarks_aliases.fish index b1d4346..2fc0fd9 100644 --- a/shells/fish/plugins/bashmarks-aliases.fish +++ b/shells/fish/plugins/bashmarks_aliases.fish @@ -1,8 +1,8 @@ # Part of Shellcuts by Tgsachse. -# Aliases to emulate the original huyng/bashmarks syntax. -alias s="sc -n" +# Aliases that emulate the original Bashmarks syntax. alias g="sc" +alias s="sc -n" alias p="sc -p" alias d="sc -d" alias l="sc -l" diff --git a/shells/fish/plugins/descriptive_aliases.fish b/shells/fish/plugins/descriptive_aliases.fish new file mode 100644 index 0000000..e88f42a --- /dev/null +++ b/shells/fish/plugins/descriptive_aliases.fish @@ -0,0 +1,8 @@ +# Part of Shellcuts by Tgsachse. + +# Provide longer, more descriptive versions +# of the sc command. +alias scut="sc" +alias shellc="sc" +alias shellcut="sc" +alias shellcuts="sc" diff --git a/shells/zsh/controller.sh b/shells/zsh/controller.sh index 882b3fd..bbb9201 100644 --- a/shells/zsh/controller.sh +++ b/shells/zsh/controller.sh @@ -1,15 +1,15 @@ # Part of Shellcuts by Tgsachse. -HANDLER_PATH=~/.shellcuts/binary/sc_handler -PLUGINS_PATH=~/.shellcuts/shells/bash/plugins +HANDLER="~/.shellcuts/binary/shellcuts.pyc" +PLUGINS="~/.shellcuts/config/zsh/plugins/*" -# Get a command from the handler, based on the given -# inputs, then execute that command. +# Get a command from the Python handler, based on +# the given inputs, then execute that command. function sc { - eval "$(python3 $HANDLER_PATH $1 $2)" + eval "$(python3 $HANDLER $1 $2)" } # Load all shell plugins. -for FILE in ${PLUGINS_PATH}/*; do +for FILE in $PLUGINS; do . $FILE done diff --git a/shells/zsh/plugins/bashmarks_aliases.sh b/shells/zsh/plugins/bashmarks_aliases.sh index b1d4346..2fc0fd9 100644 --- a/shells/zsh/plugins/bashmarks_aliases.sh +++ b/shells/zsh/plugins/bashmarks_aliases.sh @@ -1,8 +1,8 @@ # Part of Shellcuts by Tgsachse. -# Aliases to emulate the original huyng/bashmarks syntax. -alias s="sc -n" +# Aliases that emulate the original Bashmarks syntax. alias g="sc" +alias s="sc -n" alias p="sc -p" alias d="sc -d" alias l="sc -l" diff --git a/shells/zsh/plugins/full_name_aliases.sh b/shells/zsh/plugins/descriptive_aliases.sh similarity index 63% rename from shells/zsh/plugins/full_name_aliases.sh rename to shells/zsh/plugins/descriptive_aliases.sh index 52e0bc3..a52b985 100644 --- a/shells/zsh/plugins/full_name_aliases.sh +++ b/shells/zsh/plugins/descriptive_aliases.sh @@ -1,7 +1,8 @@ # Part of Shellcuts by Tiger Sachse. -# Full-name aliases. +# Provide longer, more descriptive versions +# of the sc command. +alias scut="sc" +alias shellc="sc" alias shellcut="sc" alias shellcuts="sc" -alias shellc="sc" -alias scut="sc" diff --git a/shells/zsh/zshrc.example b/shells/zsh/zshrc.example index bdef82a..c874cc8 100644 --- a/shells/zsh/zshrc.example +++ b/shells/zsh/zshrc.example @@ -1,4 +1,5 @@ # Load Shellcuts controller file if it exists. -if [ -f ~/.config/shellcuts/zsh/controller.sh ]; then - . ~/.config/shellcuts/zsh/controller.sh +CONTROLLER="~/.shellcuts/config/bash/controller.sh" +if [ -f $CONTROLLER ]; then + . $CONTROLLER fi From 902aef7ecfa1ac066017e46b4ee8be66688ce05f Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Thu, 30 Aug 2018 03:10:42 -0400 Subject: [PATCH 17/35] It compiles! --- docs/VERSION.txt | 2 +- install.sh | 14 ++ source/core/commands.py | 155 ------------ source/core/constants.py | 11 - source/core/database.py | 125 ---------- source/core/parser.py | 44 ---- source/core/utils.py | 20 -- source/init.py | 232 ------------------ source/shellcuts.py | 49 ++-- source/tests/__init__.py | 0 source/utilities/__init__.py | 5 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 263 bytes .../__pycache__/commander.cpython-36.pyc | Bin 0 -> 2825 bytes .../__pycache__/constants.cpython-36.pyc | Bin 0 -> 660 bytes .../__pycache__/extras.cpython-36.pyc | Bin 0 -> 1296 bytes .../__pycache__/parser.cpython-36.pyc | Bin 0 -> 1618 bytes .../__pycache__/reader.cpython-36.pyc | Bin 0 -> 1207 bytes source/utilities/commander.py | 90 +++++++ source/utilities/constants.py | 12 + source/utilities/extras.py | 38 +++ source/utilities/parser.py | 34 +++ source/utilities/reader.py | 25 ++ source/core/__init__.py => test.sh | 0 {source/tests => tests}/test_commands.py | 0 {source/tests => tests}/test_database.py | 0 25 files changed, 233 insertions(+), 623 deletions(-) mode change 100644 => 100755 install.sh delete mode 100644 source/core/commands.py delete mode 100644 source/core/constants.py delete mode 100644 source/core/database.py delete mode 100644 source/core/parser.py delete mode 100644 source/core/utils.py delete mode 100644 source/init.py delete mode 100644 source/tests/__init__.py create mode 100644 source/utilities/__init__.py create mode 100644 source/utilities/__pycache__/__init__.cpython-36.pyc create mode 100644 source/utilities/__pycache__/commander.cpython-36.pyc create mode 100644 source/utilities/__pycache__/constants.cpython-36.pyc create mode 100644 source/utilities/__pycache__/extras.cpython-36.pyc create mode 100644 source/utilities/__pycache__/parser.cpython-36.pyc create mode 100644 source/utilities/__pycache__/reader.cpython-36.pyc create mode 100644 source/utilities/commander.py create mode 100644 source/utilities/constants.py create mode 100644 source/utilities/extras.py create mode 100644 source/utilities/parser.py create mode 100644 source/utilities/reader.py rename source/core/__init__.py => test.sh (100%) mode change 100644 => 100755 rename {source/tests => tests}/test_commands.py (100%) rename {source/tests => tests}/test_database.py (100%) diff --git a/docs/VERSION.txt b/docs/VERSION.txt index aa1bfa7..b3d70ef 100644 --- a/docs/VERSION.txt +++ b/docs/VERSION.txt @@ -1,5 +1,5 @@ AUTHOR: Tiger Sachse LICENSE: GPLv3 -VERSION: 1.2.2 +VERSION: 1.2.3 INITIAL RELEASE: 12/31/2017 CURRENT RELEASE: 05/25/2018 diff --git a/install.sh b/install.sh old mode 100644 new mode 100755 index e69de29..5e34116 --- a/install.sh +++ b/install.sh @@ -0,0 +1,14 @@ + +function append_controller { + if [ -f $1 ] + then + echo "file" + if [ "$(grep -c -f $2 $1)" -eq 0 ] + then + echo "here" + cat $2 >> $1 + fi + fi +} + +append_controller gooble.txt gerber.txt diff --git a/source/core/commands.py b/source/core/commands.py deleted file mode 100644 index e392e27..0000000 --- a/source/core/commands.py +++ /dev/null @@ -1,155 +0,0 @@ -"""""" -import os -import shutil -from pathlib import Path -from .utils import error_message, load_version_info -from .constants import SHELLCUTS_FILE, SHELL_CONFIGS -from .database import DatabaseConnection - -def command_bashmarks(enabled): - """Enable or disable Bashmarks syntax. - - Determines which shells Shellcuts is configured to use, then either - installs the bashmarks-alias files into the appropriate config folder, or - removes them. - """ - command = 'printf "{0} Bashmarks syntax.\n"' - - for install in [item for item in SHELLCUTS_FILE.parent.iterdir() if item.is_dir()]: - if enabled is True: - for f in SHELL_CONFIGS.joinpath(install.name).iterdir(): - if f.stem == 'bashmarks-aliases': - shutil.copyfile(f, install.joinpath(f.name)) - break - else: - error_message("BadInstall") - - elif not enabled: - for f in install.iterdir(): - if f.stem == 'bashmarks-aliases': - os.remove(f) - break - - print(command.format("Enabled" if enabled is True else "Disabled")) - -def command_delete(shellcut): - """Delete shellcut from database.""" - command = 'printf "Deleted shellcut \'{0}\'\n"' - - with DatabaseConnection(SHELLCUTS_FILE) as db: - db.delete_shellcut(shellcut) - - print(command.format(shellcut)) - -def command_go(shellcut): - """Access shellcut and return 'cd' command to shellcut dir.""" - command = 'cd "{0}"' - - with DatabaseConnection(SHELLCUTS_FILE) as db: - path = db.get_shellcut_path(shellcut) - - if path is None: - error_message("DoesNotExist") - elif Path(path).exists(): - print(command.format(path)) - else: - db.delete_shellcut(shellcut) - error_message("BadPath") - -def command_help(*_): - """Print a small help menu to the screen.""" - HELP_SCRIPT = ( - 'Shellcuts usage: \$ sc [--flag] ', - '----------------------------------------------------------------', - 'Create a new shellcut for the current directory (named example):', - ' \$ sc -n example', - '', - 'Jump to that location from anywhere else on the system:', - ' \$ sc example', - '', - 'Remove that shellcut:', - ' \$ sc -d example', - '', - 'List all available shellcuts:', - ' \$ sc -l', - '', - 'See the manpage for lots more information and examples:', - ' \$ man shellcuts') - command = 'printf "' - - for line in HELP_SCRIPT: - command += line + '\n' - command += '"' - - print(command) - -def command_init(*_): - """Run initialization script.""" - command = 'python3 /usr/bin/sc-init' - - print(command) - -def command_list(*_): - """List all shellcuts.""" - command = 'printf "' - - with DatabaseConnection(SHELLCUTS_FILE) as db: - shellcuts = db.get_all_shellcuts() - - if shellcuts is not None and len(shellcuts) > 0: - command += 'SHELLCUTS\n' - - for shellcut in shellcuts: - command += '{0} : {1} : {2}\n'.format(*shellcut) - else: - command += '(No shellcuts yet. Create some with the -n flag!)\n' - - command += '"' - print(command) - -def command_move(shellcut): - """Reinsert existing shellcut at new location.""" - command = 'printf "Moved shellcut \'{0}\'\n"' - - with DatabaseConnection(SHELLCUTS_FILE) as db: - # The insert_shellcut function will delete old versions of the shellcut - # and use the most recent version. - db.insert_shellcut(shellcut, os.getcwd()) - - print(command.format(shellcut)) - -def command_new(shellcut): - """Add shellcut to database and print confirmation.""" - command = 'printf "Added new shellcut \'{0}\'\n"' - - with DatabaseConnection(SHELLCUTS_FILE) as db: - db.insert_shellcut(shellcut, os.getcwd()) - - print(command.format(shellcut)) - -def command_print(shellcut): - """Print specific shellcut.""" - command = 'printf "{0} : {1} : {2}\n"' - - with DatabaseConnection(SHELLCUTS_FILE) as db: - shellcut_tuple = db.get_shellcut(shellcut) - - if shellcut_tuple is not None: - print(command.format(*shellcut_tuple)) - else: - error_message("DoesNotExist") - -def command_version(*_): - """Echo version information found in F_VERSION.""" - lines = load_version_info() - - if lines is None: - error_message("NoVersion") - else: - command = 'printf "' - - for line in lines: - command += line - command += '"' - - print(command) diff --git a/source/core/constants.py b/source/core/constants.py deleted file mode 100644 index 650de14..0000000 --- a/source/core/constants.py +++ /dev/null @@ -1,11 +0,0 @@ -from pathlib import Path - -ERRORS = { - "DoesNotExist" : "That shellcut does not exist.", - "Unimplemented" : "This feature is unimplemented.", - "NoVersion" : "Version information not found.", - "BadInstall" : "Installed files are not in the expected place.", - "BadPath" : "The path associated with this shellcut is invalid."} -VERSION_FILE = Path('/usr/share/doc/shellcuts/META.txt') -SHELLCUTS_FILE = Path('~/.config/shellcuts/shellcuts.db').expanduser() -SHELL_CONFIGS = Path('/usr/share/shellcuts/') diff --git a/source/core/database.py b/source/core/database.py deleted file mode 100644 index 3773e6c..0000000 --- a/source/core/database.py +++ /dev/null @@ -1,125 +0,0 @@ -import sqlite3 -from pathlib import Path -"""""" -class DatabaseConnection: - """An SQLite database connection containing shellcuts.""" - def __init__(self, path): - """Save path of database as self.path.""" - self.path = path - - def __enter__(self): - """Initialize connection and cursor.""" - if self.path.is_file(): - self.connection = sqlite3.connect(str(self.path)) - self.cursor = self.connection.cursor() - else: - self.connection = sqlite3.connect(str(self.path)) - self.cursor = self.connection.cursor() - self.create() - - return self - - def __exit__(self, type, value, traceback): - """Save changes to database and close connection.""" - self.connection.commit() - self.connection.close() - - def flush(self): - """Delete all data from database.""" - self.cursor.execute('DELETE FROM table_shellcuts') - self.cursor.execute('DELETE FROM table_defaults') - - def create(self): - """Create neccessary tables in database.""" - self.cursor.execute("""CREATE TABLE table_shellcuts (name TEXT, - path TEXT, - command TEXT)""") - self.cursor.execute("""CREATE TABLE table_defaults (enabled BOOLEAN, - command TEXT)""") - self.set_default_command(False) - - def get_default_command(self): - """Get the default command from table_defaults.""" - self.cursor.execute('SELECT command FROM table_defaults') - - command = self.cursor.fetchone() - - return None if command is None else command[0] - - def set_default_command(self, enabled, command=None): - """Toggle default flag in table_defaults and set command if true.""" - self.cursor.execute('SELECT * FROM table_defaults') - - # If the table_defaults is empty, add a new entry. - if self.cursor.fetchone() is None: - self.cursor.execute('INSERT INTO table_defaults VALUES (?,?)', - (enabled, command)) - # Else if flag is being set to true, set both flag and command. - elif enabled is True: - self.cursor.execute('UPDATE table_defaults SET enabled=?, command=?', - (enabled, command)) - # Else flag is being set to false. Ignores command. - else: - self.cursor.execute('UPDATE table_defaults SET enabled=?', - (enabled,)) - - def insert_shellcut(self, name, path): - """Insert shellcut into database.""" - - # If the shellcut already exists in the table, delete it first. - if self.get_shellcut(name) is not None: - self.delete_shellcut(name) - - self.cursor.execute('INSERT INTO table_shellcuts VALUES (?,?,?)', - (name, path, None)) - - def delete_shellcut(self, name): - """Delete a shellcut from the database.""" - self.cursor.execute('DELETE FROM table_shellcuts WHERE name=?', (name,)) - - def get_shellcut(self, name): - """Get path, name, and command of named shellcut.""" - self.cursor.execute('SELECT * FROM table_shellcuts WHERE name=?', - (name,)) - - return self.cursor.fetchone() - - def get_all_shellcuts(self): - """Get all shellcuts in database.""" - self.cursor.execute('SELECT * FROM table_shellcuts') - - return self.cursor.fetchall() - - def get_shellcut_path(self, name): - """Get path of named shellcut.""" - shellcut = self.get_shellcut(name) - - return None if shellcut is None else shellcut[1] - - def get_shellcut_command(self, name): - """Get follow command for shellcut.""" - shellcut = self.get_shellcut(name) - - # If the fetch fails, returns None. - if shellcut is None: - return None - # If the command is None and default commands are enabled, returns - # the default command. - elif shellcut[2] is None and self.check_default_command_enabled(): - return self.get_default_command() - # Else returns the custom command. - else: - return shellcut[2] - - def set_shellcut_command(self, name, command): - """Set follow command for shellcut.""" - self.cursor.execute('UPDATE table_shellcuts SET command=? WHERE name=?', - (command, name)) - - def check_default_command_enabled(self): - """Check if default commands are enabled.""" - self.cursor.execute('SELECT enabled FROM table_defaults') - - enabled = self.cursor.fetchone() - - return None if enabled is None else enabled[0] diff --git a/source/core/parser.py b/source/core/parser.py deleted file mode 100644 index c008865..0000000 --- a/source/core/parser.py +++ /dev/null @@ -1,44 +0,0 @@ -import shutil -import argparse -from pathlib import Path -from .commands import command_help -"""""" -class Parser(argparse.ArgumentParser): - """Subclass of ArgumentParser. - - Necessary to override error method in ArgumentParser. Sometimes the - ArgumentParser throws errors (if argument syntax is bad, for example) and - in all of these cases I want the help menu to appear, instead of the - provided error messages. This subclass also provides the ability to create - a base argument to attempt before parsing the rest, improving speed when - jumping. - """ - def __init__(self, *args, **kwargs): - """Initialize by initializing super and adding base argument.""" - super().__init__(*args, add_help=False, **kwargs) - - self.add_argument('shellcut', nargs='?', default=None) - - def add_additional_arguments(self): - """Add flags to parser.""" - self.add_argument('-d', '--delete') - self.add_argument('-h', '--help', action='store_true', default=None) - self.add_argument('-l', '--list', action='store_true', default=None) - self.add_argument('-n', '--new') - self.add_argument('-m', '--move') - self.add_argument('-p', '--print') - self.add_argument('-v', '--version', action='store_true', default=None) - self.add_argument('--init', action='store_true', default=None) - self.add_argument('--enable-bashmarks-syntax', - action='store_true', - dest='bashmarks', - default=None) - self.add_argument('--disable-bashmarks-syntax', - action='store_false', - dest='bashmarks', - default=None) - - def error(self, message): - """Call help command in case of error.""" - command_help() - exit(0) diff --git a/source/core/utils.py b/source/core/utils.py deleted file mode 100644 index 862a271..0000000 --- a/source/core/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -from .constants import ERRORS, VERSION_FILE -def error_message(error): - """Echo an error message. - - Includes a master dictionary of all supported errors. These are accessible - by key. - """ - command = 'printf "ERROR {0}: {1}\n"'.format(error, ERRORS[error]) - - print(command) - #exit(0) - -def load_version_info(): - """Load version information found at VERSION_FILE.""" - try: - with open(str(VERSION_FILE), 'r') as f: - return f.readlines() - except FileNotFoundError: - return None - #error_message("NoVersion") diff --git a/source/init.py b/source/init.py deleted file mode 100644 index 9913c99..0000000 --- a/source/init.py +++ /dev/null @@ -1,232 +0,0 @@ -"""Finishes installation of Shellcuts. - -This script can automatically configure Shellcuts to be used with various -supported shells. It can also print help to allow users to do the manual -configuration. - -Part of Shellcuts by Tgsachse. -""" -import re -import shutil -import subprocess -from pathlib import Path - -### CONSTANTS ### -D_SHELLCUTS = Path('~/.config/shellcuts').expanduser() - -INIT_SCRIPT = ( - 'Thank you for installing Shellcuts.', - '', - 'This is the initialization script to help you finish installation. If you', - 'are still seeing this prompt after automatic configuration, try restarting', - 'your terminal/reloading your shell.', - '', - 'Listed below are your initialization options:', - '', - '(0) Quit this script and do nothing. This prompt will appear again next time.', - '(1) Automatically configure Shellcuts for your shells. (recommended)', - '(2) Get help for manual configuration.', - '', - 'Enter the number next to the command you wish to perform: ') - -MANUAL_SCRIPT = ( - 'To install for {0}:', - '', - '(1) Copy the contents of {0}', - ' to {0}', - ' If the destination file does not exist then create it.', - '', - '(2) Copy the controller file located at {0}', - ' to {0}', - '', - 'That\'s it! Restart your shell session to begin using Shellcuts.') - -SHELLS = { - 'bash' : {'config' : Path('~/.bashrc').expanduser(), - 'example' : Path('/usr/share/shellcuts/bash/bashrc.example'), - 'controller' : Path('/usr/share/shellcuts/bash/controller.sh')}, - 'zsh' : {'config' : Path('~/.zshrc').expanduser(), - 'example' : Path('/usr/share/shellcuts/zsh/zshrc.example'), - 'controller' : Path('/usr/share/shellcuts/zsh/controller.sh')}, - 'fish' : {'config' : Path('~/.config/fish/config.fish').expanduser(), - 'example' : Path('/usr/share/shellcuts/fish/config.fish.example'), - 'controller' : Path('/usr/share/shellcuts/fish/controller.fish')}} - - -### FUNCTIONS ### -def automatic_configuration(selected_shells=[]): - """Automatically configure Shellcuts for user. - - Can install for all shells or only selected shells. - """ - clear_screen() - - if len(selected_shells) < 1: - shells = detect_shells() - print_installed_shells(shells) - - print("Enter the number(s) next to the shell(s) you'd like to install Shellcuts for.") - command = input("Separate numbers by a space: ") - - # Extracts numbers from user command string. - for num in range(len(shells)): - if re.search(str(num), command): - selected_shells.append(shells[num]) - - if len(selected_shells) == 0: - print("No shells selected. Exiting...") - return - - # Runs installation functions for all shells in selected_shells. - for shell in selected_shells: - print("Installing Shellcuts for the " + shell + " shell...") - create_directory(D_SHELLCUTS) - create_config(shell) - edit_config(shell) - install_controller(shell) - - print("\nThat's it! Restart your shell session to begin using Shellcuts.") - -def automatic_configuration_prompt(): - """Shortcut for user to skip to full automatic configuration.""" - prompt = "Automatically configure for all shells? (yes/no): " - yes_list = ['yes', 'y', 'Y', 'Yes'] - no_list = ['no', 'n', 'N', 'No'] - - command = check_input(prompt, yes_list + no_list) - - return True if command in yes_list else False - -def check_input(prompt, acceptable_list): - """Sanitize user input based on list of acceptable values.""" - command = input(prompt) - - tries = 0 - while command not in acceptable_list: - tries += 1 - if tries > 5: - print("Tries exceeded. Exiting...") - exit(0) - else: - command = input("Invalid response, try again: ") - - return command - -def clear_screen(): - """Clear the screen.""" - subprocess.run(['clear']) - -def create_config(shell): - """Create config file if it does not exist.""" - create_directory(SHELLS[shell]['config'].parent) - SHELLS[shell]['config'].touch(exist_ok=True) - -def create_directory(directory): - """Create directory structure if it does not exist.""" - directory.mkdir(parents=True, exist_ok=True) - -def detect_shells(): - """Return shells detected by 'which' command.""" - detected_shells = [] - - for shell in SHELLS.keys(): - if shutil.which(shell): - detected_shells.append(shell) - - return detected_shells - -def edit_config(shell): - """Add needed text to config file for specified shell.""" - create_config(shell) - - # Concatenates the example text to the config text. - new_config = (SHELLS[shell]['config'].read_text() + '\n' + - SHELLS[shell]['example'].read_text()) - - # Writes newly concatenated text to file. - SHELLS[shell]['config'].write_text(new_config) - -def exit_script(): - """Exit the script.""" - print("Exiting script...") - exit(0) - -def format_manual_script(shell): - """Format the manual script using provided shell key.""" - formatted_script = MANUAL_SCRIPT - formatting = ((0, shell), - (2, str(SHELLS[shell]['example'])), - (3, str(SHELLS[shell]['config'])), - (6, str(SHELLS[shell]['controller'])), - (7, str(D_SHELLCUTS) + '/' + shell + '/')) - - # Formats lines in the MANUAL_SCRIPT based on shell. - for line, string in formatting: - formatted_script[line] = MANUAL_SCRIPT[line].format(string) - - return formatted_script - -def install_controller(shell): - """Copy controller file into configuration directory.""" - destination_dir = D_SHELLCUTS.joinpath(shell) - - create_directory(destination_dir) - - shutil.copyfile(SHELLS[shell]['controller'], - destination_dir.joinpath(SHELLS[shell]['controller'].name)) - -def manual_configuration(): - """Show the manual configuration menus.""" - shells = detect_shells() - - clear_screen() - - print_installed_shells(shells) - - prompt = "Enter the number next to the shell you'd like to install: " - acceptable_list = [str(num) for num in range(len(shells))] - command = int(check_input(prompt, acceptable_list)) - - clear_screen() - - # Formats the MANUAL_SCRIPT, then prints it line-by-line. - formatted_manual_script = format_manual_script(shells[command]) - [print(line) for line in formatted_manual_script] - -def print_installed_shells(shells): - """Enumerate installed shells to the screen.""" - print("Currently installed shells:") - - shell_list = enumerate(shells) - [print("{0} {1}".format(shell[0], shell[1])) for shell in shell_list] - -def welcome(): - """Welcome the user and present a list of options.""" - clear_screen() - - if automatic_configuration_prompt(): - automatic_configuration(detect_shells()) - return - else: - clear_screen() - - [print(line) for line in INIT_SCRIPT[:-1:]] - - command = check_input(INIT_SCRIPT[-1], ['0', '1', '2']) - - if command == '0': - exit_script() - elif command == '1': - automatic_configuration() - elif command == '2': - manual_configuration() - - -### MAIN PROGRAM ### -# Try except wrapper gets rid of ugly stacktrace when using control-c to -# close the script. -try: - welcome() -except KeyboardInterrupt: - print() - exit(0) diff --git a/source/shellcuts.py b/source/shellcuts.py index 9536a28..e96832c 100644 --- a/source/shellcuts.py +++ b/source/shellcuts.py @@ -1,42 +1,21 @@ -from core.database import DatabaseConnection -from core.parser import Parser -from core.commands import * +from pathlib import Path +from utilities.commander import Commander +from utilities.parser import Parser +from utilities import constants, extras -parser = Parser() -arguments, unknown = parser.parse_known_args() +commander = Commander(constants.VERSION_FILE, constants.SHELLCUTS_FILE) -# Attempts to short-circuit the program and jump if only one argument given. -if len(unknown) < 1: - command_go(arguments.shellcut) - exit(0) - -# Adds other flags and re-parses arguments. -parser.add_additional_arguments() -arguments, unknown = parser.parse_known_args() +parser = Parser(commander) +parser.parse_arguments() -# If anything unknown is passed, show help and exit. -if len(unknown) > 0: - command_help() +if len(parser.unknown) <= 0: + commander.go(parser.arguments.name) exit(0) -# This tuple associates arguments from the parser with their functions. -command_pairs = ( # there's gotta be abetter way - (arguments.help, command_help), - (arguments.list, command_list), - (arguments.version, command_version), - (arguments.init, command_init), - (arguments.bashmarks, command_bashmarks), - (arguments.delete, command_delete), - (arguments.new, command_new), - (arguments.move, command_move), - (arguments.print, command_print)) +parser.add_arguments() +parser.parse_arguments() -# For each in tuple, if value is not 'None', execute associated function. -for pair in command_pairs: - if pair[0] != None: - # Passes value to corresponding function. Functions are designed to - # handle this value even if they don't need it. - pair[1](pair[0]) - break +if len(parser.unknown) > 0: + extras.throw_help() else: - command_help() + commander.execute_command(parser.arguments) diff --git a/source/tests/__init__.py b/source/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/source/utilities/__init__.py b/source/utilities/__init__.py new file mode 100644 index 0000000..86fbe45 --- /dev/null +++ b/source/utilities/__init__.py @@ -0,0 +1,5 @@ +from utilities import (parser, + constants, + extras, + commander, + reader) diff --git a/source/utilities/__pycache__/__init__.cpython-36.pyc b/source/utilities/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b095512878d2d2094a9c1f610a176617afac3a1c GIT binary patch literal 263 zcmX|)Jx&BM427NfiFT3p4z$ydf)*hJ)HK_LViYD86DgUD;*7MMfFn>-av2VgmWnG- z;WTUMeSWg7=j+4a;``(M`4a$sSm$ck-LdGBVZjOyL1+;g-Go-Rq0?RHbuYj+M;O{) zzJj4Iay5xCmHiYK%4W$_B%X3IR7WA0#qw4rG~AyCS@v6Y-erfB>dVwtbQOJtWPCx| zRbTOUujUv~8pr!9Om-N2>q; literal 0 HcmV?d00001 diff --git a/source/utilities/__pycache__/commander.cpython-36.pyc b/source/utilities/__pycache__/commander.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a11e847144b80b425ce9451d0a5eb558e0235b88 GIT binary patch literal 2825 zcmb7GTW{Mo7$qrLk`*U$(sXNg=~S;k>j8_q3$S4*if&1}p=gDz+1Ad(pt&X!+mWS| zq?^=ro?Kv$dm6C6u)oCDJ?$^_Y2QcLa+2j`GDOl8Z|CrQhx}xDIb8qqx1ay067n}W z@iVXC>5#P<#j=w+L$~ePvYS?hUfZMOYr-qsdquc6Wo@6+9n$pw zfiXzjq{jWWqkE8h;z-Mm!qOwr;X-z?#r$DVxC5_x0*N4P%1N7X+IBeO&MVS(xyvh% z!#v!F+~WbSLhkdBFF_vg8efLI%2)U*rJE}H6AJ{)#3nBI^s z0>%e(T)-B*Y6Fr9osfiRI$={R!9t)jlOBAEn~teH7gF`}EZOa+Lb_0EJZUk(M^}k- zSNc%dUMebWv%iQ8q3-ootCtT&OYf;px2HsFTjmE(^P|>Q&P8jdC(^V#(yFEMk?e}r zNcYpe?hDoG&ey$p@WKR1($D%jNdg#B;Q|Qr=y<&Zw>fXnMO^q`PT{o}u?HuBiN_cJ>AoI6Y<) zG9Uvwp;JnUW)n7Wu#Erg+E_e}4rD*myHR6Xq(TcGsTo1h^_Mq~uZNAME0F@T%58&W z{@qk;Pvh<8a@f&Lr@-fCouM%P48vKBUNOiz45)Cc33X|Wj_V6}MS~I*(paMU75Az{Bj9Mrn~~uaRvpk z48K0R`Iuw@eDUxf16Vo6$+r zazm7HQjuAT3vMsNWQ}{4{eol(}eS48VVbN_)ogo$v~3WFbDgU;4ZfyD;rgS~MEEQaO` zpbG#ir3IY;*q8(Sl5+rwn{I2!k(}XrFh2L7MrMsq5^n*ap#MvFJzQoIsdFFnJ#n^q)cun^;;{lr7=yLE;ADiE39Fz_A*5feUXfs0Tsd2cc~N yI>W#*J|0SG|7meVRm(e&)n2<^{@*OFSM~yC2RLtT6Wl7`-9~%2hJnWt{Mf%;E?{>6 literal 0 HcmV?d00001 diff --git a/source/utilities/__pycache__/constants.cpython-36.pyc b/source/utilities/__pycache__/constants.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e757d4ad82928aa9702f5b559ba764f3e5740b1b GIT binary patch literal 660 zcmY+BKX2497{;CS{x#%KCFUYC-jYbLB80dL;-u3RCD#sNsNDEH;>xk3*nuNMmk#{| zY50ZXjG#y1df6aIQIo v$^TyJy2>D|t&qYBFkz;`$!F0Y@!zb) zI*4qCtREa7zdI(kFNZC1`)XKO*DkT6dND0^ln$xYF2R#hzO9`W^WRcm^a5M=(qbNZ zI>l5N(un^+x(vIs-DX#XqG>M-WnCkh`&z~4s@L38Tr}HVAtg(!X&RMiCYp&2WMBhf znxbblHJfoiov~f5u0Q4U1)ww~090DUWvnaUF*kMQKGtCD!7wcVzBojDc8Ggx1GJCM zYC~^;2OghWMO$cyzs!9=77Z|;UqyLIFt6n$S%8u(r6}8&ghvurju!d&j2K2v8;y>n z7iVPK{E2@j+im=(WxIP?Pz#kJk+>#fW&$@)Y*!GLXe}ZOW!HjP1yz3$(a_bpN5fc( zdMjIYY8i#t040sqv#rAfQ$TeLBRp?mF5 zqVtv}(`DzaOs0>Vw+c}AwCi_WbbjHaWS99v}N&1=nyD>nE`LsP3oJlRn8?GH-+Y>;oTkri#y7tt+kW**4+KnAF5=ah*!{N-w`S@Zo8U6Lg{VzYejQztNd^l&v z*v!}10Rx`1m`0KFLd1fz?-@AY-Z5|$yKruu*ad#Zrry73&rYYj^VQp0Cnlx&_H%l{ z9HXFE%z?!Mc1qT7nU4VXE@a|abfe!;5eF$KP;{ZnR1jpc>xhx*yS>w+CG0{fp z)*FTRrM!1f$9UNGvDpM_cFQjK4F^0V<2T|@@jK`2hF##1bR78a9nQc*r-SyJAIhxG zl00iwc>XTTwzI5UhNf979VR7&2>>b16QjaJFPlP@^(+ohEmtaEzv->vuXpfCdB- zT{ja)yxrd*I@`vz!<4WkTu-o>L+lVeMDFNUz zXIQfsq6ic@KJ0&?y|96pg{0Z zIo=|dtlSqO4BorF2J4WW;g+d&kqbykwiRXdELE2RzkK$wT<FU}t8dX%V-Jnp$Bun*a<$88&h$frL5bd~+(=JR$M0R=;knAHViwc^Yj literal 0 HcmV?d00001 diff --git a/source/utilities/__pycache__/reader.cpython-36.pyc b/source/utilities/__pycache__/reader.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ada503816e0e3fb7b598fe452c6471bf5e3058a9 GIT binary patch literal 1207 zcmbtTy>8nu5GEyAR{Rq;Xi;SB>LnW5u_z3rKsOJ~<_?6aV`2ev` z(pTczx9HUSC`(Y>bSQel-I3xQKi+rb%jtCV{oB_MwG`r~=t;=q1*?6*=A9rx@}BiB zSYZ}W$g_}VlA;SSALJa>Nx-88t9`~s3yu-1fFuR?Vii)z@sUd|*19b7U7Z670*}md zR{Mm_t-KXV+y-oU2hNhqdElb!Y@=PY&Fb|$bdlBNr9qlO{tsaW?0zlMb+yrHePy$J zZFTzARNIf$=k&ZHowmp}wbjn#I^ESpS=5ELsnHo}bGH5B5~YgkqE-rf+lY;jvAj8H zlh4}qC5llBr&7v|l-g9ZD>0rZ^=X%t&59ZF4*qaG6XPEu5S?paz>AxaE_JPy-CvYR5AFFL?I7g;K_^$b?htVa z+xnjF5&P>o7=*Mfe4I_`**qZ7onWV|EITy+T3Wc%9^8YV2hZj29Sj>xwAnw5L-a{T z)FGd8R14hJ*k($UPYCePi{G|Pj U_mju6UGlnlQEyPKKjv}#8x;QG?*IS* literal 0 HcmV?d00001 diff --git a/source/utilities/commander.py b/source/utilities/commander.py new file mode 100644 index 0000000..15ab2a2 --- /dev/null +++ b/source/utilities/commander.py @@ -0,0 +1,90 @@ +import os +from pathlib import Path +from utilities import extras +from utilities.reader import Reader + +class Commander: + """""" + def __init__(self, version_file, reader_file): + """""" + self.version_file = version_file + self.reader = Reader(reader_file) + + + def execute_command(self, arguments): + pass + + + def delete(self, name): + command = 'printf "Deleted shellcut \'{0}\'\n"' + self.reader.delete_shellcut(name) + print(command.format(name)) + + + def go(self, name): + command = 'cd "{0}"' + shellcut = self.reader.get_shellcut(name) + if shellcut is None: + extras.throw_error("DoesNotExist") + elif not Path(shellcut.path).exists(): + self.reader.delete_shellcut(shellcut.name) + extras.throw_error("BadPath") + else: + print(command.format(shellcut.path)) + + + def list(self): + """""" + command = 'printf "' + + shellcuts = self.reader.get_all_shellcuts() + if shellcuts is not None and len(shellcuts) > 0: + command += 'SHELLCUTS\n' + for shellcut in shellcuts: + command += '{0} : {1}\n'.format(shellcut.name, shellcut.path) + else: + command += '(No shellcuts yet. Create some with the -n flag!)\n' + + command += '"' + print(command) + + + def move(self, name): + """""" + command = 'printf "Moved shellcut \'{0}\'\n"' + self.reader.delete_shellcut(name) + self.reader.add_shellcut(name, os.getcwd()) + print(command.format(name)) + + + def new(self, name): + """""" + command = 'printf "Added new shellcut \'{0}\'\n"' + self.reader.add_shellcut(name, os.getcwd()) + print(command.format(name)) + + + def print(self, name): + """""" + command = 'printf "{0} : {1}\n"' + shellcut = self.reader.get_shellcut(name) + if shellcut is None: + extras.throw_error("DoesNotExist") + else: + print(command.format(shellcut.name, shellcut.path)) + + + def version(self): + """""" + command = 'printf "' + + try: + with open(str(self.version_file), 'r') as f: + lines = f.readlines() + except FileNotFoundError: + extras.throw_error("NoVersion") + + for line in lines: + command += line + command += '"' + print(command) diff --git a/source/utilities/constants.py b/source/utilities/constants.py new file mode 100644 index 0000000..7c35965 --- /dev/null +++ b/source/utilities/constants.py @@ -0,0 +1,12 @@ +from pathlib import Path + +SHELL_DIRS = Path('~/.shellcuts/shells').expanduser() +VERSION_FILE = Path('~/.shellcuts/docs/VERSION.txt').expanduser() +SHELLCUTS_FILE = Path('~/.shellcuts/data/shellcuts.json').expanduser() +ERRORS = { + 'DoesNotExist' : 'That shellcut does not exist.', + 'Unimplemented' : 'This feature is unimplemented.', + 'NoVersion' : 'Version information not found.', + 'BadInstall' : 'Installed files are not in the expected place.', + 'BadPath' : 'The path associated with this shellcut is invalid.', +} diff --git a/source/utilities/extras.py b/source/utilities/extras.py new file mode 100644 index 0000000..ee1473d --- /dev/null +++ b/source/utilities/extras.py @@ -0,0 +1,38 @@ +from utilities import constants + +def throw_error(error): + """Echo an error message. + + Includes a master dictionary of all supported errors. These are accessible + by key. + """ + command = 'printf "ERROR {0}: {1}\n"'.format(error, constants.ERRORS[error]) + print(command) + exit(0) + + +def throw_help(): + """""" + script = ( + 'Shellcuts usage: \$ sc [--flag] ', + '----------------------------------------------------------------', + 'Create a new shellcut for the current directory (named example):', + ' \$ sc -n example', + '', + 'Jump to that location from anywhere else on the system:', + ' \$ sc example', + '', + 'Remove that shellcut:', + ' \$ sc -d example', + '', + 'List all available shellcuts:', + ' \$ sc -l', + '', + 'See the manpage for lots more information and examples:', + ' \$ man shellcuts') + command = 'printf "' + + for line in script: + command += line + '\n' + print(command + '"') + exit(0) diff --git a/source/utilities/parser.py b/source/utilities/parser.py new file mode 100644 index 0000000..2fb8a95 --- /dev/null +++ b/source/utilities/parser.py @@ -0,0 +1,34 @@ +import argparse +from utilities import extras + +class Parser(argparse.ArgumentParser): + """ + """ + def __init__(self, *args, **kwargs): + """Initialize by initializing super and adding base argument.""" + super().__init__(*args, add_help=False, **kwargs) + self.add_argument('name', nargs='?', default=None) + + + def parse_arguments(self): + self.arguments, self.unknown = self.parse_known_args() + + + def has_unknown_arguments(self): + return True if len(self.unknown) > 0 else False + + + def add_arguments(self): + """Add flags to parser.""" + self.add_argument('-d', '--delete') + self.add_argument('-h', '--help', action='store_true', default=None) + self.add_argument('-l', '--list', action='store_true', default=None) + self.add_argument('-n', '--new') + self.add_argument('-m', '--move') + self.add_argument('-p', '--print') + self.add_argument('-v', '--version', action='store_true', default=None) + + + def error(self, message): + """Call help command in case of error.""" + extras.throw_help() diff --git a/source/utilities/reader.py b/source/utilities/reader.py new file mode 100644 index 0000000..4b7bd3a --- /dev/null +++ b/source/utilities/reader.py @@ -0,0 +1,25 @@ +class Shellcut: + def __init__(self, name, path): + self.name = name + self.path = path + + +class Reader: + def __init__(self, reader_file): + pass + + + def delete_shellcut(self, name): + pass + + + def get_shellcut(self, name): + pass + + + def get_all_shellcuts(self): + pass + + + def add_shellcut(self, name, path): + pass diff --git a/source/core/__init__.py b/test.sh old mode 100644 new mode 100755 similarity index 100% rename from source/core/__init__.py rename to test.sh diff --git a/source/tests/test_commands.py b/tests/test_commands.py similarity index 100% rename from source/tests/test_commands.py rename to tests/test_commands.py diff --git a/source/tests/test_database.py b/tests/test_database.py similarity index 100% rename from source/tests/test_database.py rename to tests/test_database.py From 8fff05bf1b66334b8032d04eb06a65779c07c4b6 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Thu, 30 Aug 2018 03:11:03 -0400 Subject: [PATCH 18/35] no cache --- .../__pycache__/__init__.cpython-36.pyc | Bin 263 -> 0 bytes .../__pycache__/commander.cpython-36.pyc | Bin 2825 -> 0 bytes .../__pycache__/constants.cpython-36.pyc | Bin 660 -> 0 bytes .../utilities/__pycache__/extras.cpython-36.pyc | Bin 1296 -> 0 bytes .../utilities/__pycache__/parser.cpython-36.pyc | Bin 1618 -> 0 bytes .../utilities/__pycache__/reader.cpython-36.pyc | Bin 1207 -> 0 bytes 6 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 source/utilities/__pycache__/__init__.cpython-36.pyc delete mode 100644 source/utilities/__pycache__/commander.cpython-36.pyc delete mode 100644 source/utilities/__pycache__/constants.cpython-36.pyc delete mode 100644 source/utilities/__pycache__/extras.cpython-36.pyc delete mode 100644 source/utilities/__pycache__/parser.cpython-36.pyc delete mode 100644 source/utilities/__pycache__/reader.cpython-36.pyc diff --git a/source/utilities/__pycache__/__init__.cpython-36.pyc b/source/utilities/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index b095512878d2d2094a9c1f610a176617afac3a1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 263 zcmX|)Jx&BM427NfiFT3p4z$ydf)*hJ)HK_LViYD86DgUD;*7MMfFn>-av2VgmWnG- z;WTUMeSWg7=j+4a;``(M`4a$sSm$ck-LdGBVZjOyL1+;g-Go-Rq0?RHbuYj+M;O{) zzJj4Iay5xCmHiYK%4W$_B%X3IR7WA0#qw4rG~AyCS@v6Y-erfB>dVwtbQOJtWPCx| zRbTOUujUv~8pr!9Om-N2>q; diff --git a/source/utilities/__pycache__/commander.cpython-36.pyc b/source/utilities/__pycache__/commander.cpython-36.pyc deleted file mode 100644 index a11e847144b80b425ce9451d0a5eb558e0235b88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2825 zcmb7GTW{Mo7$qrLk`*U$(sXNg=~S;k>j8_q3$S4*if&1}p=gDz+1Ad(pt&X!+mWS| zq?^=ro?Kv$dm6C6u)oCDJ?$^_Y2QcLa+2j`GDOl8Z|CrQhx}xDIb8qqx1ay067n}W z@iVXC>5#P<#j=w+L$~ePvYS?hUfZMOYr-qsdquc6Wo@6+9n$pw zfiXzjq{jWWqkE8h;z-Mm!qOwr;X-z?#r$DVxC5_x0*N4P%1N7X+IBeO&MVS(xyvh% z!#v!F+~WbSLhkdBFF_vg8efLI%2)U*rJE}H6AJ{)#3nBI^s z0>%e(T)-B*Y6Fr9osfiRI$={R!9t)jlOBAEn~teH7gF`}EZOa+Lb_0EJZUk(M^}k- zSNc%dUMebWv%iQ8q3-ootCtT&OYf;px2HsFTjmE(^P|>Q&P8jdC(^V#(yFEMk?e}r zNcYpe?hDoG&ey$p@WKR1($D%jNdg#B;Q|Qr=y<&Zw>fXnMO^q`PT{o}u?HuBiN_cJ>AoI6Y<) zG9Uvwp;JnUW)n7Wu#Erg+E_e}4rD*myHR6Xq(TcGsTo1h^_Mq~uZNAME0F@T%58&W z{@qk;Pvh<8a@f&Lr@-fCouM%P48vKBUNOiz45)Cc33X|Wj_V6}MS~I*(paMU75Az{Bj9Mrn~~uaRvpk z48K0R`Iuw@eDUxf16Vo6$+r zazm7HQjuAT3vMsNWQ}{4{eol(}eS48VVbN_)ogo$v~3WFbDgU;4ZfyD;rgS~MEEQaO` zpbG#ir3IY;*q8(Sl5+rwn{I2!k(}XrFh2L7MrMsq5^n*ap#MvFJzQoIsdFFnJ#n^q)cun^;;{lr7=yLE;ADiE39Fz_A*5feUXfs0Tsd2cc~N yI>W#*J|0SG|7meVRm(e&)n2<^{@*OFSM~yC2RLtT6Wl7`-9~%2hJnWt{Mf%;E?{>6 diff --git a/source/utilities/__pycache__/constants.cpython-36.pyc b/source/utilities/__pycache__/constants.cpython-36.pyc deleted file mode 100644 index e757d4ad82928aa9702f5b559ba764f3e5740b1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 660 zcmY+BKX2497{;CS{x#%KCFUYC-jYbLB80dL;-u3RCD#sNsNDEH;>xk3*nuNMmk#{| zY50ZXjG#y1df6aIQIo v$^TyJy2>D|t&qYBFkz;`$!F0Y@!zb) zI*4qCtREa7zdI(kFNZC1`)XKO*DkT6dND0^ln$xYF2R#hzO9`W^WRcm^a5M=(qbNZ zI>l5N(un^+x(vIs-DX#XqG>M-WnCkh`&z~4s@L38Tr}HVAtg(!X&RMiCYp&2WMBhf znxbblHJfoiov~f5u0Q4U1)ww~090DUWvnaUF*kMQKGtCD!7wcVzBojDc8Ggx1GJCM zYC~^;2OghWMO$cyzs!9=77Z|;UqyLIFt6n$S%8u(r6}8&ghvurju!d&j2K2v8;y>n z7iVPK{E2@j+im=(WxIP?Pz#kJk+>#fW&$@)Y*!GLXe}ZOW!HjP1yz3$(a_bpN5fc( zdMjIYY8i#t040sqv#rAfQ$TeLBRp?mF5 zqVtv}(`DzaOs0>Vw+c}AwCi_WbbjHaWS99v}N&1=nyD>nE`LsP3oJlRn8?GH-+Y>;oTkri#y7tt+kW**4+KnAF5=ah*!{N-w`S@Zo8U6Lg{VzYejQztNd^l&v z*v!}10Rx`1m`0KFLd1fz?-@AY-Z5|$yKruu*ad#Zrry73&rYYj^VQp0Cnlx&_H%l{ z9HXFE%z?!Mc1qT7nU4VXE@a|abfe!;5eF$KP;{ZnR1jpc>xhx*yS>w+CG0{fp z)*FTRrM!1f$9UNGvDpM_cFQjK4F^0V<2T|@@jK`2hF##1bR78a9nQc*r-SyJAIhxG zl00iwc>XTTwzI5UhNf979VR7&2>>b16QjaJFPlP@^(+ohEmtaEzv->vuXpfCdB- zT{ja)yxrd*I@`vz!<4WkTu-o>L+lVeMDFNUz zXIQfsq6ic@KJ0&?y|96pg{0Z zIo=|dtlSqO4BorF2J4WW;g+d&kqbykwiRXdELE2RzkK$wT<FU}t8dX%V-Jnp$Bun*a<$88&h$frL5bd~+(=JR$M0R=;knAHViwc^Yj diff --git a/source/utilities/__pycache__/reader.cpython-36.pyc b/source/utilities/__pycache__/reader.cpython-36.pyc deleted file mode 100644 index ada503816e0e3fb7b598fe452c6471bf5e3058a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1207 zcmbtTy>8nu5GEyAR{Rq;Xi;SB>LnW5u_z3rKsOJ~<_?6aV`2ev` z(pTczx9HUSC`(Y>bSQel-I3xQKi+rb%jtCV{oB_MwG`r~=t;=q1*?6*=A9rx@}BiB zSYZ}W$g_}VlA;SSALJa>Nx-88t9`~s3yu-1fFuR?Vii)z@sUd|*19b7U7Z670*}md zR{Mm_t-KXV+y-oU2hNhqdElb!Y@=PY&Fb|$bdlBNr9qlO{tsaW?0zlMb+yrHePy$J zZFTzARNIf$=k&ZHowmp}wbjn#I^ESpS=5ELsnHo}bGH5B5~YgkqE-rf+lY;jvAj8H zlh4}qC5llBr&7v|l-g9ZD>0rZ^=X%t&59ZF4*qaG6XPEu5S?paz>AxaE_JPy-CvYR5AFFL?I7g;K_^$b?htVa z+xnjF5&P>o7=*Mfe4I_`**qZ7onWV|EITy+T3Wc%9^8YV2hZj29Sj>xwAnw5L-a{T z)FGd8R14hJ*k($UPYCePi{G|Pj U_mju6UGlnlQEyPKKjv}#8x;QG?*IS* From e3b6db7d530938623eaadebf3e2772c90b941ed9 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Thu, 30 Aug 2018 18:56:02 -0400 Subject: [PATCH 19/35] new json dictionary --- source/shellcuts.py | 2 +- source/utilities/__init__.py | 4 +- source/utilities/json_dict.py | 55 +++++++++++++ source/utilities/reader.py | 25 ------ tests/test_database.py | 143 ---------------------------------- 5 files changed, 58 insertions(+), 171 deletions(-) create mode 100644 source/utilities/json_dict.py delete mode 100644 source/utilities/reader.py delete mode 100644 tests/test_database.py diff --git a/source/shellcuts.py b/source/shellcuts.py index e96832c..b40dfad 100644 --- a/source/shellcuts.py +++ b/source/shellcuts.py @@ -1,7 +1,7 @@ from pathlib import Path -from utilities.commander import Commander from utilities.parser import Parser from utilities import constants, extras +from utilities.commander import Commander commander = Commander(constants.VERSION_FILE, constants.SHELLCUTS_FILE) diff --git a/source/utilities/__init__.py b/source/utilities/__init__.py index 86fbe45..bc2b139 100644 --- a/source/utilities/__init__.py +++ b/source/utilities/__init__.py @@ -1,5 +1,5 @@ from utilities import (parser, - constants, extras, + constants, commander, - reader) + json_dict) diff --git a/source/utilities/json_dict.py b/source/utilities/json_dict.py new file mode 100644 index 0000000..03c860a --- /dev/null +++ b/source/utilities/json_dict.py @@ -0,0 +1,55 @@ +"""An iterable JSON dictionary with extra safety. + +Part of Shellcuts by Tiger Sachse. +""" +import json +from pathlib import Path + +class JSONDictionary: + """Implements a dictionary that is iterable and handles JSON I/O.""" + + def __init__(self, json_path): + """Attempt to load the given JSON path.""" + self.__json_path = json_path + + if not Path(self.__json_path).exists(): + self.__contents = {} + else: + with open(self.__json_path, 'r') as f: + self.__contents = json.load(f) + + + def __delitem__(self, key): + """Delete an item from the dictionary, if it exists.""" + if key in self.__contents: + del self.__contents[key] + + + def __getitem__(self, key): + """Return a value from the dictionary, if it exists.""" + if key in self.__contents: + return self.__contents[key] + else: + return None + + + def __setitem__(self, key, value): + """Set an item in the dictionary.""" + self.__contents[key] = value + + + def __iter__(self): + """Return an iterator for the dictionary.""" + return iter(self.__generate_items()) + + + def __generate_items(self): + """Generate all items in the list.""" + for key in self.__contents.keys(): + yield key, self.__contents[key] + + + def write(self): + """Write the dictionary to a file.""" + with open(self.__json_path, 'w') as f: + json.dump(self.__contents, f) diff --git a/source/utilities/reader.py b/source/utilities/reader.py deleted file mode 100644 index 4b7bd3a..0000000 --- a/source/utilities/reader.py +++ /dev/null @@ -1,25 +0,0 @@ -class Shellcut: - def __init__(self, name, path): - self.name = name - self.path = path - - -class Reader: - def __init__(self, reader_file): - pass - - - def delete_shellcut(self, name): - pass - - - def get_shellcut(self, name): - pass - - - def get_all_shellcuts(self): - pass - - - def add_shellcut(self, name, path): - pass diff --git a/tests/test_database.py b/tests/test_database.py deleted file mode 100644 index 1c5790b..0000000 --- a/tests/test_database.py +++ /dev/null @@ -1,143 +0,0 @@ -"""Unfinished""" -import sqlite3 -import unittest -from pathlib import Path -from core.database import DatabaseConnection - -class DatabaseTester(unittest.TestCase): - """A class that tests the database functionality of shellcuts.""" - def __init__(self, *args, **kwargs): - """Initialize tester and set default location for database.""" - super().__init__(*args, **kwargs) - - self.path = Path("/tmp/TestDatabase.db") - - def setUp(self): - """Delete database from previous test.""" - if self.path.is_file(): - self.path.unlink() - - def test000CreateNewDatabase(self): - """Create a new database where one doesn't exist.'""" - with DatabaseConnection(self.path) as db: - self.assertTrue(Path(self.path).is_file()) - try: - db.cursor.execute("SELECT * FROM table_defaults") - except sqlite3.OperationalError: - self.fail("Database not valid.") - - def test001LoadExistingDatabase(self): - """Create a database and then reopen and confirm data is still there.""" - populateTestingDatabase(self.path) - - with DatabaseConnection(self.path) as db: - self.assertIsNotNone(db.get_shellcut("Test1")) - self.assertIsNotNone(db.get_shellcut("Test2")) - self.assertIsNone(db.get_shellcut("Test4")) - - def test002InsertIntoDatabase(self): - """Insert data into a database.""" - with DatabaseConnection(self.path) as db: - db.insert_shellcut("Test", "/tmp/test/") - self.assertIsNotNone(db.get_shellcut("Test")) - - def test003RemoveFromDatabase(self): - """Delete data from a database.""" - with DatabaseConnection(self.path) as db: - db.insert_shellcut("Test", "/tmp/test/") - db.delete_shellcut("Test") - self.assertIsNone(db.get_shellcut("Test")) - - def test004FlushDatabase(self): - """Populate a database and then flush the data.""" - populateTestingDatabase(self.path) - - with DatabaseConnection(self.path) as db: - db.flush() - self.assertIsNone(db.get_shellcut("Test1")) - self.assertIsNone(db.get_shellcut("Test2")) - - def test005DefaultDisabledInNew(self): - """Check a new database's default command flag.'""" - with DatabaseConnection(self.path) as db: - db.cursor.execute("SELECT enabled FROM table_defaults") - self.assertEqual(db.cursor.fetchone()[0], 0) - - def test006ToggleDefaults(self): - """Toggle default command flag for database.""" - with DatabaseConnection(self.path) as db: - self.assertEqual(db.check_default_command_enabled(), 0) - - db.set_default_command(True) - - self.assertEqual(db.check_default_command_enabled(), 1) - - def test007ChangeDefaultCommand(self): - """Change default command.""" - with DatabaseConnection(self.path) as db: - # Check that the default command is nothing at first. - db.set_default_command(False) - self.assertEqual(db.get_default_command(), None) - - # Check that the default command is updated when toggled on. - db.set_default_command(True, command="echo 'hello'") - self.assertEqual(db.get_default_command(), "echo 'hello'") - - # Check that the default command persists even when toggled off. - # Ignores command passed if toggling to false. - db.set_default_command(False, "pwd") - self.assertEqual(db.get_default_command(), "echo 'hello'") - - def test008SetFollowCommand(self): - """Set follow commands for some shellcuts.""" - populateTestingDatabase(self.path) - - with DatabaseConnection(self.path) as db: - db.set_shellcut_command("Test1", "clsa") - db.set_shellcut_command("Test2", "clsa; pwd") - db.set_shellcut_command("Test3", None) - - self.assertEqual(db.get_shellcut_command("Test1"), "clsa") - self.assertEqual(db.get_shellcut_command("Test2"), "clsa; pwd") - self.assertEqual(db.get_shellcut_command("Test3"), None) - - def test009GetFollowCommand(self): - """Get follow commands before and after default commands enabled.""" - populateTestingDatabase(self.path) - - with DatabaseConnection(self.path) as db: - self.assertEqual(db.check_default_command_enabled(), 0) - db.set_shellcut_command("Test1", "clsa") - db.set_shellcut_command("Test2", "clsa; pwd") - db.set_shellcut_command("Test3", None) - - self.assertEqual(db.get_shellcut_command("Test1"), "clsa") - self.assertEqual(db.get_shellcut_command("Test2"), "clsa; pwd") - self.assertEqual(db.get_shellcut_command("Test3"), None) - - db.set_default_command(True, command="ls -A") - self.assertEqual(db.check_default_command_enabled(), 1) - self.assertEqual(db.get_shellcut_command("Test1"), "clsa") - self.assertEqual(db.get_shellcut_command("Test2"), "clsa; pwd") - # The return command for Test3 changes because default overrides - # any None commands. - self.assertEqual(db.get_shellcut_command("Test3"), "ls -A") - - def test010InitialFollowCommands(self): - """Test that initial follow commands are set to None.""" - populateTestingDatabase(self.path) - - with DatabaseConnection(self.path) as db: - self.assertEqual(db.get_shellcut_command("Test1"), None) - self.assertEqual(db.get_shellcut_command("Test2"), None) - self.assertEqual(db.get_shellcut_command("Test3"), None) - -def populateTestingDatabase(path): - """Populate a database for testing.""" - with DatabaseConnection(path) as db: - db.insert_shellcut("Test1", "/tmp/test1/") - db.insert_shellcut("Test2", "/tmp/test2/") - db.insert_shellcut("Test3", "/tmp/test3/") - -##if __name__ == '__main__': -## unittest.main() From 1eab5a72d9e9d345cb9b64bc8c35150e8df86578 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Thu, 30 Aug 2018 19:05:27 -0400 Subject: [PATCH 20/35] adjusting commands for json dict --- source/utilities/__init__.py | 2 +- source/utilities/commander.py | 38 +++++++++---------- .../{json_dict.py => json_dictionary.py} | 3 ++ 3 files changed, 23 insertions(+), 20 deletions(-) rename source/utilities/{json_dict.py => json_dictionary.py} (96%) diff --git a/source/utilities/__init__.py b/source/utilities/__init__.py index bc2b139..1beb5ce 100644 --- a/source/utilities/__init__.py +++ b/source/utilities/__init__.py @@ -2,4 +2,4 @@ extras, constants, commander, - json_dict) + json_dictionary) diff --git a/source/utilities/commander.py b/source/utilities/commander.py index 15ab2a2..e69a888 100644 --- a/source/utilities/commander.py +++ b/source/utilities/commander.py @@ -1,14 +1,14 @@ import os from pathlib import Path from utilities import extras -from utilities.reader import Reader +from utilities.json_dictionary import JSONDictionary class Commander: """""" - def __init__(self, version_file, reader_file): + def __init__(self, version_file, shellcuts_file): """""" self.version_file = version_file - self.reader = Reader(reader_file) + self.shellcuts = JSONDictionary(shellcuts_file) def execute_command(self, arguments): @@ -17,31 +17,30 @@ def execute_command(self, arguments): def delete(self, name): command = 'printf "Deleted shellcut \'{0}\'\n"' - self.reader.delete_shellcut(name) + del self.shellcuts[name] + self.shellcuts.write() print(command.format(name)) def go(self, name): command = 'cd "{0}"' - shellcut = self.reader.get_shellcut(name) - if shellcut is None: + if self.shellcuts[name] is None: extras.throw_error("DoesNotExist") - elif not Path(shellcut.path).exists(): - self.reader.delete_shellcut(shellcut.name) + elif not Path(self.shellcuts[name]).exists(): + del self.shellcuts[name] extras.throw_error("BadPath") else: - print(command.format(shellcut.path)) + print(command.format(self.shellcuts[name])) def list(self): """""" command = 'printf "' - shellcuts = self.reader.get_all_shellcuts() - if shellcuts is not None and len(shellcuts) > 0: + if len(self.shellcuts) > 0: command += 'SHELLCUTS\n' - for shellcut in shellcuts: - command += '{0} : {1}\n'.format(shellcut.name, shellcut.path) + for shellcut in self.shellcuts: + command += '{0} : {1}\n'.format(*shellcut) else: command += '(No shellcuts yet. Create some with the -n flag!)\n' @@ -52,26 +51,27 @@ def list(self): def move(self, name): """""" command = 'printf "Moved shellcut \'{0}\'\n"' - self.reader.delete_shellcut(name) - self.reader.add_shellcut(name, os.getcwd()) + del self.shellcuts[name] + self.shellcuts[name] = os.getcwd() + self.shellcuts.write() print(command.format(name)) def new(self, name): """""" command = 'printf "Added new shellcut \'{0}\'\n"' - self.reader.add_shellcut(name, os.getcwd()) + self.shellcuts[name] = os.getcwd() + self.shellcuts.write() print(command.format(name)) def print(self, name): """""" command = 'printf "{0} : {1}\n"' - shellcut = self.reader.get_shellcut(name) - if shellcut is None: + if self.shellcuts[name] is None: extras.throw_error("DoesNotExist") else: - print(command.format(shellcut.name, shellcut.path)) + print(command.format(*self.shellcuts[name])) def version(self): diff --git a/source/utilities/json_dict.py b/source/utilities/json_dictionary.py similarity index 96% rename from source/utilities/json_dict.py rename to source/utilities/json_dictionary.py index 03c860a..b4017fc 100644 --- a/source/utilities/json_dict.py +++ b/source/utilities/json_dictionary.py @@ -43,6 +43,9 @@ def __iter__(self): return iter(self.__generate_items()) + def __len__(self): + return len(self.__contents) + def __generate_items(self): """Generate all items in the list.""" for key in self.__contents.keys(): From cb792696b3ffc4ca72a1a9cd10aafc9b3c3b2895 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Thu, 30 Aug 2018 20:01:01 -0400 Subject: [PATCH 21/35] final build before comments --- docs/{shellcuts.1 => SHELLCUTS.man} | 0 source/core/__init__.py | 6 + .../core/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 255 bytes .../core/__pycache__/commander.cpython-36.pyc | Bin 0 -> 3142 bytes .../core/__pycache__/jsonary.cpython-36.pyc | Bin 0 -> 2335 bytes source/core/__pycache__/parser.cpython-36.pyc | Bin 0 -> 1650 bytes .../core/__pycache__/utilities.cpython-36.pyc | Bin 0 -> 1608 bytes source/{utilities => core}/commander.py | 67 +++-- .../json_dictionary.py => core/jsonary.py} | 23 +- source/{utilities => core}/parser.py | 21 +- source/core/utilities.py | 47 ++++ source/shellcuts.py | 21 +- source/utilities/__init__.py | 5 - source/utilities/constants.py | 12 - source/utilities/extras.py | 38 --- tests/test_commands.py | 260 ------------------ 16 files changed, 142 insertions(+), 358 deletions(-) rename docs/{shellcuts.1 => SHELLCUTS.man} (100%) create mode 100644 source/core/__init__.py create mode 100644 source/core/__pycache__/__init__.cpython-36.pyc create mode 100644 source/core/__pycache__/commander.cpython-36.pyc create mode 100644 source/core/__pycache__/jsonary.cpython-36.pyc create mode 100644 source/core/__pycache__/parser.cpython-36.pyc create mode 100644 source/core/__pycache__/utilities.cpython-36.pyc rename source/{utilities => core}/commander.py (57%) rename source/{utilities/json_dictionary.py => core/jsonary.py} (66%) rename source/{utilities => core}/parser.py (79%) create mode 100644 source/core/utilities.py delete mode 100644 source/utilities/__init__.py delete mode 100644 source/utilities/constants.py delete mode 100644 source/utilities/extras.py delete mode 100644 tests/test_commands.py diff --git a/docs/shellcuts.1 b/docs/SHELLCUTS.man similarity index 100% rename from docs/shellcuts.1 rename to docs/SHELLCUTS.man diff --git a/source/core/__init__.py b/source/core/__init__.py new file mode 100644 index 0000000..820b0ad --- /dev/null +++ b/source/core/__init__.py @@ -0,0 +1,6 @@ +""" +""" +from core import (parser, + jsonary, + commander, + utilities) diff --git a/source/core/__pycache__/__init__.cpython-36.pyc b/source/core/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b8dacf009bb6858910c78f47b4b7bc90fe1a50e GIT binary patch literal 255 zcmX|+u};J=42F|7?Ojj0cVNAR46GbNf{B6UwlG8`v1m|IS57LpnT1#3m9jGN3QQym zmi|wlEZdT=_WRw#$LG@RC~saP&Lf@kU@(Mnwxu_$$$6I?#K%LQH(FaXT4!O9fZDQ8qN1zl3%(c) z)hm9Fa>4*Iy~NQ>5#&AL@{I5D4lT&j1m2r9MHz7=1G1i!TD#dLtuL2z-d+c^tB0HV ITC5QK4=BMyod5s; literal 0 HcmV?d00001 diff --git a/source/core/__pycache__/commander.cpython-36.pyc b/source/core/__pycache__/commander.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8a53d6c01ede9a1289a80963963991435ad017b GIT binary patch literal 3142 zcmb7GU2hXd6rI^!@2)qF2_XbQ+i2^yKq`^6fLav+q9jm+s8veKhYQ*o&m^%~?}ph8 z3C5ACnuk77ssEzCp#NoF`_$*Y@zir?{gI?pDr?Qmo$>6)IrrXk=iKyk@W;=;{P6b_ zV}G(emks+0T6PtkU=1d?uwvfe812~V+6|l2niD(SQln(-QtWoUhR4}gOt`{($%Hqs z8osdZuti_<;4eIi)fc&TzG>zg7|UCEJ8tJ~nPKAH%+jQ(p0{Y%=(1p3LCbzbCmCMP z8kXPr#s6lsgTQeM$cQ$(k<1J%dJ#VHw3h_`CR)^)K1!Y z6a{!%=AvVk%U#}^9phdaZD|o?x@a83{|%Eq?=UsjXB}e=IA=)@Yg~PpTOE7AFz;aQ zsPow4n0W*9lDdI;i7a!^by%)?h&7iivvwAir#7+Xk!98vVEO7Ntoi0yRm{uk73KlW z_t8G3T_IzcOYMM-O+~KLLB65V?T8k%6SuQmJKgk|)OI4bb!k(zlU#d42Q_TCO}mBf z;7#NOT-8+TTV0vtnQ=qWP18S~+>vmhJd8;eq9Z=5U^EyuvPX^9dWR=W5APilO*u!+ z#Mx;Wg6~>@37Ri48atc-Q^a-bJ|Ex*_>^6?iMn2Bu&jl(Rl|V@N1+s+dhyBbsi20q zROEVH+E(pcs-tAPYpLosb3DX=b&_URYHt`f2LP)>xZ@+VjKY#x4)+1IH!}fM(7z88 zIl*9h2xEfjIKKUfuNHFNK{6XxU&xVJg!ecbAb8N~KB>|Js$bJ2UjCcd#<#M!gGL zy+<9TuHPA23+)>F)w#90WUl!FT<r&8e9Py(>V!N> z+O548|m^E-?y~fk~J?zgQ%WQKj5L*g${&oj404dcKA(yeVzmB5kZR)9$*= zTiZfWA^86hqK;tu4YZ8%uSlFKrOqCNBuZ~axP)5>VQwS<;ebFe$cB3R-^gksWM2va zWKg`ZecpmE)K7{+y|pdI$aQzuNG zqoSgS%p;Pmjze{Js0n6W*obSVi0dOGc#|NmsRcZ=zF3;1xC*iI7QP}`e1JB_6Hp=_ zn&2^S9p^Hkq3AV;p1}Yj{`_=~KqOaDJ`b#4tH@sCu3h^c6ZUQPExXTL~e#&d@ZQ{5cw;vb9&7otVseFwJ inMrcOz3Hg&O zT@KtoftEf5BT0uyDp^Q76nZCQk<)P~c}AowOCN|VObb8CPkOxFS7F#4nUr7L@E?LprFq-$_EXiY z{sjc&tU)vP+&BF${+nG4Zk|9(UxQHu=tzf2+HoY4&IbZamakKiWm$p$W$DQc=qs`+ zH=*}rP1d2`kay%3^i{bn??S&RcjPYgHMs{qeUw#Ba~Hic}1s{@gLs_YDuy zh1-5Cc^^!u(mVvuTQ8bjq{Rzky8-POT6zLzLMKEJ19m{Bbik%0JqQRqbK*ne;!{bd z?6;lw%#;S@37g`4=M|Ccd-9UJh8`%L@!?ZrR5Uc)Bs@%f$xUDJ%ix`gbBG~?ubEMO z)~IB=qHswFJRX5#8UeGix(-s4W;=@+-6S^Xa^@yO6=yEKk-4}L-zEnak83W$Q#I!;9p)b(~1mnxA+pO-8z_THQpe7A^x?X%25PPlzB6 zj1Zd`rz#l2%IpsHppCari;1&I^XQ@z*lct^h8v9TfV?T|9qcFcJBTL4i;bHnDpW>s z-wHP3J)J~0W(CC8Jm|rhf@CouO^~3jz~txOeJ; zA*@Qry9?m?jP=Y1@Wz~ZFK6B-ZA(2Pf2qt!#~{IXemGM9lU#4Z4r|CSmC4DGAccxx z$*XLceLkm)D#i@^*;?q*j;+@u(#$kzp}Ef?8*B|+J>S)wS*yI75LjURAWnsN zw1l>|KwC_hM+;9Cv#X15d_-s7+Zt5NJoE2AD~^x* zVTffrg5sTnhmh-_N@nAjx!_e=B>Xz~&bAP%QuqdO@~q#c<9iDiR;R7Ffzh?;^AK+G zL$?JVoG@(QfLrbaJ^+;br{o{J(m=ng11$bN?**ZPQh#;UC}|HC)g?6e(aBsniiXRD zOk-NymTu_^7k&&Qg)>ld>oaHU*jb~FB|7#aB}7&gB1+^a#CtfFNH`e06; zK+`kmI7tbo+>$hXFEuHI+0eS^bDFlfewfRLVvNY38T>927x%Mhnu(4iE+U$tNNtnPF`cTeRGNK`c-PeEu9Cf&fD zUpnd-QA#7XUx>B!U9y7;OZmnK7pC6 z8VLw2helrD!XKHPsi!`Jb$G$l(qu&M;EQ2GP7j|BFWeTrIq=TQtqKml-p23^GOp2^ JLjxLF{{bt2j}-s_ literal 0 HcmV?d00001 diff --git a/source/core/__pycache__/utilities.cpython-36.pyc b/source/core/__pycache__/utilities.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa91974032ae250e5935bd4a44667c9d721c1a27 GIT binary patch literal 1608 zcmbVM%WfMt6eVXy4@xVy52>pfb z6btZIQ00$cD6$d7)bMd&*ak+-pymu;nKNWtfUN~i0ZuJ&8gP1nGc-f9bc5!8Gwm$R z)50&v-k_Vb2yKpTbx^q!;oRSl2$gYUy>V23yz_IdD*Md$-Jz1T1j+H?y`Pl3iyl3u z`t0Aj-!`kO7d`gc;0suS*9hFGf6_O2pf4)RmWevV?ug7 zBwpC#B5;((y1&PVA+3&|pZ6IV!0L%3C3ihXF-mSc@KxWF>$rfu@Y?Y`S}mJ);S|!e zxN7Jm_C|{@ve=og9jdu-v1SH}0Ts>(oj{e3!B9NK3Qh18o*-pR&_gscW*8%7(xjy( zaBGeC-v}>M#0d!#f(sHbDVoqT#W%{>x2RjX`8sboZy zp6@qGCj&c?1&pY&5v7uVQO4y#;Dq#dByun@KCbn7z-sDJI&NRG+Nt1!3qGoyaLQ_( z^{Miqa9Pdeg4Kq~^F8G;SsmO)dDR#ErkeyW>CL9WAj87DSQG%c+SSE`ss|*D)B(_) zDWb(G9vw{3Df(EN8WTJ*=+-_uLR0)x@jcRf(b7Ijc*1mBxAXzDfy=1Zx-(T<#8;IjzDN8b&D0Z|<0ftr2p&V||&JOB;c-t?JZg!z&Y zkgtv{Z>3^EJ>FgiTttia+AQGLY#wU4&HJh8GIe~|1O*ev_la}uc)oMtL+s^g*WRe2O2#DQ#OQt7DYy5(@slQcmpLiz>O^53t6K6 zxykFFxw($DoOC$(1Po{v|JP?>%gEspE@1IFjBb_lQKs94x31f@Gx~Ayy$hjrALZC+ z0Mra66LzliZKK)jo}RTkQU3c^t+%h5-EYpCji|7A0q0&Z8Fupayp#As->c+CCAsb_ kUteR9>>>Ly;B@G-WBnvZ&6I@;W&zd*pJ8AFXU%NpA7%)$tN;K2 literal 0 HcmV?d00001 diff --git a/source/utilities/commander.py b/source/core/commander.py similarity index 57% rename from source/utilities/commander.py rename to source/core/commander.py index e69a888..b653eb4 100644 --- a/source/utilities/commander.py +++ b/source/core/commander.py @@ -1,21 +1,41 @@ +""" +""" import os from pathlib import Path -from utilities import extras -from utilities.json_dictionary import JSONDictionary +from core import utilities +from core.jsonary import Jsonary class Commander: """""" - def __init__(self, version_file, shellcuts_file): + def __init__(self, version_file, shellcuts_file, manual_file): """""" + self.manual_file = manual_file self.version_file = version_file - self.shellcuts = JSONDictionary(shellcuts_file) + self.shellcuts = Jsonary(shellcuts_file) - def execute_command(self, arguments): - pass - + def execute(self, arguments): + """""" + if arguments.delete: + self.delete(arguments.delete) + elif arguments.help: + utilities.throw_help() + elif arguments.list: + self.list() + elif arguments.move: + self.move(arguments.move) + elif arguments.new: + self.new(arguments.new) + elif arguments.print: + self.print(arguments.print) + elif arguments.version: + self.version() + elif arguments.man: + self.manual() + def delete(self, name): + """""" command = 'printf "Deleted shellcut \'{0}\'\n"' del self.shellcuts[name] self.shellcuts.write() @@ -23,15 +43,16 @@ def delete(self, name): def go(self, name): + """""" command = 'cd "{0}"' if self.shellcuts[name] is None: - extras.throw_error("DoesNotExist") + utilities.throw_error("DoesNotExist") elif not Path(self.shellcuts[name]).exists(): del self.shellcuts[name] - extras.throw_error("BadPath") + utilities.throw_error("BadPath") else: print(command.format(self.shellcuts[name])) - + def list(self): """""" @@ -47,7 +68,7 @@ def list(self): command += '"' print(command) - + def move(self, name): """""" command = 'printf "Moved shellcut \'{0}\'\n"' @@ -56,7 +77,7 @@ def move(self, name): self.shellcuts.write() print(command.format(name)) - + def new(self, name): """""" command = 'printf "Added new shellcut \'{0}\'\n"' @@ -68,23 +89,31 @@ def new(self, name): def print(self, name): """""" command = 'printf "{0} : {1}\n"' - if self.shellcuts[name] is None: - extras.throw_error("DoesNotExist") + if name not in self.shellcuts: + utilities.throw_error("DoesNotExist") else: - print(command.format(*self.shellcuts[name])) + print(command.format(name, self.shellcuts[name])) def version(self): """""" command = 'printf "' - + try: with open(str(self.version_file), 'r') as f: - lines = f.readlines() + lines = f.readlines() except FileNotFoundError: - extras.throw_error("NoVersion") - + utilities.throw_error("NoVersion") + for line in lines: command += line command += '"' print(command) + + + def manual(self): + """""" + command = 'man ".' + command += str(self.manual_file) + command += '"' + print(command) diff --git a/source/utilities/json_dictionary.py b/source/core/jsonary.py similarity index 66% rename from source/utilities/json_dictionary.py rename to source/core/jsonary.py index b4017fc..c096ff6 100644 --- a/source/utilities/json_dictionary.py +++ b/source/core/jsonary.py @@ -1,12 +1,12 @@ -"""An iterable JSON dictionary with extra safety. +"""An iterable JSON jsonary with extra safety. Part of Shellcuts by Tiger Sachse. """ import json from pathlib import Path -class JSONDictionary: - """Implements a dictionary that is iterable and handles JSON I/O.""" +class Jsonary: + """Implements a jsonary that is iterable and handles JSON I/O.""" def __init__(self, json_path): """Attempt to load the given JSON path.""" @@ -20,13 +20,13 @@ def __init__(self, json_path): def __delitem__(self, key): - """Delete an item from the dictionary, if it exists.""" + """Delete an item from the jsonary, if it exists.""" if key in self.__contents: del self.__contents[key] def __getitem__(self, key): - """Return a value from the dictionary, if it exists.""" + """Return a value from the jsonary, if it exists.""" if key in self.__contents: return self.__contents[key] else: @@ -34,18 +34,25 @@ def __getitem__(self, key): def __setitem__(self, key, value): - """Set an item in the dictionary.""" + """Set an item in the jsonary.""" self.__contents[key] = value def __iter__(self): - """Return an iterator for the dictionary.""" + """Return an iterator for the jsonary.""" return iter(self.__generate_items()) def __len__(self): + """Return the size of the jsonary.""" return len(self.__contents) + + def __contains__(self, key): + """Return whether a key is in the jsonary.""" + return key in self.__contents + + def __generate_items(self): """Generate all items in the list.""" for key in self.__contents.keys(): @@ -53,6 +60,6 @@ def __generate_items(self): def write(self): - """Write the dictionary to a file.""" + """Write the jsonary to a file.""" with open(self.__json_path, 'w') as f: json.dump(self.__contents, f) diff --git a/source/utilities/parser.py b/source/core/parser.py similarity index 79% rename from source/utilities/parser.py rename to source/core/parser.py index 2fb8a95..f12292d 100644 --- a/source/utilities/parser.py +++ b/source/core/parser.py @@ -1,5 +1,7 @@ +""" +""" import argparse -from utilities import extras +from core import utilities class Parser(argparse.ArgumentParser): """ @@ -8,27 +10,30 @@ def __init__(self, *args, **kwargs): """Initialize by initializing super and adding base argument.""" super().__init__(*args, add_help=False, **kwargs) self.add_argument('name', nargs='?', default=None) - + def parse_arguments(self): + """""" self.arguments, self.unknown = self.parse_known_args() def has_unknown_arguments(self): + """""" return True if len(self.unknown) > 0 else False def add_arguments(self): """Add flags to parser.""" - self.add_argument('-d', '--delete') - self.add_argument('-h', '--help', action='store_true', default=None) - self.add_argument('-l', '--list', action='store_true', default=None) self.add_argument('-n', '--new') self.add_argument('-m', '--move') self.add_argument('-p', '--print') - self.add_argument('-v', '--version', action='store_true', default=None) - + self.add_argument('-d', '--delete') + self.add_argument('--man', action='store_true', default=None) + self.add_argument('--version', action='store_true', default=None) + self.add_argument('-h', '--help', action='store_true', default=None) + self.add_argument('-l', '--list', action='store_true', default=None) + def error(self, message): """Call help command in case of error.""" - extras.throw_help() + utilities.throw_help() diff --git a/source/core/utilities.py b/source/core/utilities.py new file mode 100644 index 0000000..f1d8b19 --- /dev/null +++ b/source/core/utilities.py @@ -0,0 +1,47 @@ +""" +""" +from pathlib import Path + +# Constants for the whole program. +SHELL_DIRS = Path('~/.shellcuts/shells').expanduser() +VERSION_FILE = Path('~/.shellcuts/docs/VERSION.txt').expanduser() +MANUAL_FILE = Path('~/.shellcuts/docs/SHELLCUTS.man').expanduser() +SHELLCUTS_FILE = Path('~/.shellcuts/data/shellcuts.json').expanduser() +ERRORS = { + 'DoesNotExist' : 'That shellcut does not exist.', + 'NoVersion' : 'Version information not found.', + 'BadPath' : 'The path associated with this shellcut is invalid.', +} + +def throw_error(error): + """Print an error message.""" + command = 'printf "ERROR {0}: {1}\n"' + print(command.format(error, ERRORS[error])) + exit(0) + + +def throw_help(): + """Print a help message.""" + script = ( + 'Shellcuts usage: \$ sc [-f] ', + '--------------------------------------------------------', + 'Create a new shellcut for the current directory:', + ' \$ sc -n example', + '', + 'Jump to that location from anywhere else on the system:', + ' \$ sc example', + '', + 'Remove that shellcut:', + ' \$ sc -d example', + '', + 'List all available shellcuts:', + ' \$ sc -l', + '', + 'See the manpage for lots more information and examples:', + ' \$ sc --man') + command = 'printf "' + + for line in script: + command += line + '\n' + print(command + '"') + exit(0) diff --git a/source/shellcuts.py b/source/shellcuts.py index b40dfad..247d972 100644 --- a/source/shellcuts.py +++ b/source/shellcuts.py @@ -1,14 +1,19 @@ -from pathlib import Path -from utilities.parser import Parser -from utilities import constants, extras -from utilities.commander import Commander +""" +""" +from core import utilities +from core.parser import Parser +from core.commander import Commander -commander = Commander(constants.VERSION_FILE, constants.SHELLCUTS_FILE) +commander = Commander(utilities.VERSION_FILE, + utilities.SHELLCUTS_FILE, + utilities.MANUAL_FILE) parser = Parser(commander) parser.parse_arguments() -if len(parser.unknown) <= 0: +if parser.arguments.name is None: + utilities.throw_help() +elif len(parser.unknown) <= 0: commander.go(parser.arguments.name) exit(0) @@ -16,6 +21,6 @@ parser.parse_arguments() if len(parser.unknown) > 0: - extras.throw_help() + utilities.throw_help() else: - commander.execute_command(parser.arguments) + commander.execute(parser.arguments) diff --git a/source/utilities/__init__.py b/source/utilities/__init__.py deleted file mode 100644 index 1beb5ce..0000000 --- a/source/utilities/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from utilities import (parser, - extras, - constants, - commander, - json_dictionary) diff --git a/source/utilities/constants.py b/source/utilities/constants.py deleted file mode 100644 index 7c35965..0000000 --- a/source/utilities/constants.py +++ /dev/null @@ -1,12 +0,0 @@ -from pathlib import Path - -SHELL_DIRS = Path('~/.shellcuts/shells').expanduser() -VERSION_FILE = Path('~/.shellcuts/docs/VERSION.txt').expanduser() -SHELLCUTS_FILE = Path('~/.shellcuts/data/shellcuts.json').expanduser() -ERRORS = { - 'DoesNotExist' : 'That shellcut does not exist.', - 'Unimplemented' : 'This feature is unimplemented.', - 'NoVersion' : 'Version information not found.', - 'BadInstall' : 'Installed files are not in the expected place.', - 'BadPath' : 'The path associated with this shellcut is invalid.', -} diff --git a/source/utilities/extras.py b/source/utilities/extras.py deleted file mode 100644 index ee1473d..0000000 --- a/source/utilities/extras.py +++ /dev/null @@ -1,38 +0,0 @@ -from utilities import constants - -def throw_error(error): - """Echo an error message. - - Includes a master dictionary of all supported errors. These are accessible - by key. - """ - command = 'printf "ERROR {0}: {1}\n"'.format(error, constants.ERRORS[error]) - print(command) - exit(0) - - -def throw_help(): - """""" - script = ( - 'Shellcuts usage: \$ sc [--flag] ', - '----------------------------------------------------------------', - 'Create a new shellcut for the current directory (named example):', - ' \$ sc -n example', - '', - 'Jump to that location from anywhere else on the system:', - ' \$ sc example', - '', - 'Remove that shellcut:', - ' \$ sc -d example', - '', - 'List all available shellcuts:', - ' \$ sc -l', - '', - 'See the manpage for lots more information and examples:', - ' \$ man shellcuts') - command = 'printf "' - - for line in script: - command += line + '\n' - print(command + '"') - exit(0) diff --git a/tests/test_commands.py b/tests/test_commands.py deleted file mode 100644 index 735cf6d..0000000 --- a/tests/test_commands.py +++ /dev/null @@ -1,260 +0,0 @@ -"""Define a testing class for all commands in core package. - -Part of Shellcuts by Tgsachse. -""" -import re -import shutil -import unittest -from io import StringIO -from core.utils import * -from core.commands import * -from unittest.mock import patch - -VERSION_FILE = Path('/tmp/shellcuts_version.txt') -SHELLCUTS_FILE = Path('/tmp/shellcuts_database.db') - -class CommandTester(unittest.TestCase): - """Testing class for all commands. - - These commands generally wrap the database operations that form the - backbone of the whole program. - """ - def setUp(self): - """Method called before every test.""" - self.delete_test_database() - - def delete_test_database(self): - """Delete test database if it exists.""" - if SHELLCUTS_FILE.is_file(): - SHELLCUTS_FILE.unlink() - - def create_test_database(self): - """Create a test database with some sample entries.""" - with DatabaseConnection(SHELLCUTS_FILE) as db: - db.insert_shellcut('test1', '/tmp/test1/') - db.insert_shellcut('test2', '/tmp/test2/') - db.insert_shellcut('test3', '/tmp/test3/') - db.insert_shellcut('test4', '/tmp/test4/') - - @patch('core.utils.VERSION_FILE', VERSION_FILE) - @patch('sys.stdout', new_callable=StringIO) - def test_000_check_version_command(self, stream): - """Confirm that version command can open the version information. - - Also tests that if the information is missing, version command throws - the NoVersion error. The first patch decorator replaces lookups of the - constant VERSION_FILE with a custom VERSION_FILE defined in this test - script. The second patch decorator forces called functions to print to - the StringIO object 'stream' instead of stdout. This keeps the screen - clean and saves the output for regex checks. - """ - # Delete version file if it exists. - if VERSION_FILE.is_file(): - VERSION_FILE.unlink() - - command_version() - # Should throw the NoVersion error here. - self.assertTrue(re.search('NoVersion', stream.getvalue())) - - # Create a version file with some junk in it. - VERSION_FILE.touch() - VERSION_FILE.write_text('Test version information.') - - command_version() - # Should now print that junk to the screen. - self.assertTrue(re.search('Test version information', - stream.getvalue())) - - @patch('sys.stdout', new_callable=StringIO) - def test_001_check_help_command(self, stream): - """Confirm that the help command prints a help menu.""" - command_help() - self.assertTrue(re.search('Shellcuts usage:', stream.getvalue())) - - @patch('sys.stdout', new_callable=StringIO) - def test_002_check_init_command(self, stream): - """Confirm that the init command returns the command for the init file.""" - command_init() - self.assertTrue(re.search('python3 /usr/bin/sc-init', - stream.getvalue())) - - @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) - @patch('os.getcwd', return_value='/tmp/test1/') - @patch('sys.stdout', new_callable=StringIO) - def test_003_check_new_command(self, stream, mock_getcwd): - """Confirm that the new command adds a shellcut to a database. - - The second patch mocks the getcwd command in the os module. When this - command is called it will automatically return the return_value - defined in the decorator. This return value can be manipulated later - in the function. - """ - command_new('test1') - - # Check that a courtesy message is printed to the screen. - self.assertTrue(re.search('Added new shellcut \'test1\'', stream.getvalue())) - - # Check that the new shellcut is in the database. - with DatabaseConnection(SHELLCUTS_FILE) as db: - self.assertEqual(db.get_shellcut('test1')[0], 'test1') - self.assertEqual(db.get_shellcut_path('test1'), '/tmp/test1/') - - # Change the mock current working directory so a second call to - # command_new will overwrite the old version. - mock_getcwd.return_value = '/tmp/test2/' - command_new('test1') - - # Confirm that the previous entry was overwritten, and that there is - # only one item in the database. - with DatabaseConnection(SHELLCUTS_FILE) as db: - self.assertEqual(db.get_shellcut('test1')[0], 'test1') - self.assertEqual(db.get_shellcut_path('test1'), '/tmp/test2/') - self.assertEqual(len(db.get_all_shellcuts()), 1) - - @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) - @patch('os.getcwd', return_value='/tmp/test1/') - @patch('sys.stdout', new_callable=StringIO) - def test_004_check_move_command(self, stream, mock_getcwd): - """Confirm that the move command moves a shellcut to a new location. - - This command is essentially identical to the new command. - """ - command_move('test1') - - # Move will function as the new command if the shellcut doesn't exist. - self.assertTrue(re.search('Moved shellcut \'test1\'', stream.getvalue())) - - # Check that the new shellcut is in the database. - with DatabaseConnection(SHELLCUTS_FILE) as db: - self.assertEqual(db.get_shellcut('test1')[0], 'test1') - self.assertEqual(db.get_shellcut_path('test1'), '/tmp/test1/') - - # Change the working directory. - mock_getcwd.return_value = '/tmp/test2/' - command_move('test1') - - # Now there should only be one shellcut pointing to the latest directory. - with DatabaseConnection(SHELLCUTS_FILE) as db: - self.assertEqual(db.get_shellcut('test1')[0], 'test1') - self.assertEqual(db.get_shellcut_path('test1'), '/tmp/test2/') - self.assertEqual(len(db.get_all_shellcuts()), 1) - - - @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) - @patch('sys.stdout', new_callable=StringIO) - def test_005_check_delete_command(self, stream): - """Confirm that the delete command works properly.""" - self.create_test_database() - command_delete('test2') - - # Confirm that the deletion message is printed. - self.assertTrue(re.search('Deleted shellcut \'test2\'', stream.getvalue())) - - # Confirm that only test2 was removed from the database. - with DatabaseConnection(SHELLCUTS_FILE) as db: - self.assertEqual(db.get_shellcut('test1')[0], 'test1') - self.assertIsNone(db.get_shellcut('test2')) - self.assertEqual(db.get_shellcut('test3')[0], 'test3') - - # Attempt to remove something from the database that isn't there. - command_delete('test5') - - # Confirm that the deletion message shows anyway. - self.assertTrue(re.search('Deleted shellcut \'test5\'', stream.getvalue())) - - # Confirm that nothing was deleted. - with DatabaseConnection(SHELLCUTS_FILE) as db: - self.assertEqual(len(db.get_all_shellcuts()), 3) - - @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) - @patch('sys.stdout', new_callable=StringIO) - def test_006_check_print_command(self, stream): - """Confirm that the print command prints correctly.""" - self.create_test_database() - - command_print('test1') - - # Confirm that the output stream contains information from test1. - self.assertTrue(re.search('test1.*/tmp/test1/.*None', stream.getvalue())) - - command_print('test5') - - # Confirm that attempting to print something that is not in the - # database will throw the DoesNotExist error. - self.assertTrue(re.search('DoesNotExist', stream.getvalue())) - - @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) - @patch('sys.stdout', new_callable=StringIO) - def test_007_check_list_command(self, stream): - """Confirm that the list command prints properly.""" - self.create_test_database() - command_list() - - # Check that each of the four entries is printed into the output stream. - for num in range(1, 5): - self.assertTrue(re.search('test{0}.*/tmp/test{0}/.*None'.format(num), - stream.getvalue())) - - self.delete_test_database() - command_list() - - # Check that if the database is empty, a helpful message appears. - self.assertTrue(re.search('No shellcuts yet.', stream.getvalue())) - - @patch('core.commands.SHELLCUTS_FILE', SHELLCUTS_FILE) - @patch('sys.stdout', new_callable=StringIO) - def test_008_check_go_command(self, stream): - """Test the multiple behaviors of the go command.""" - self.create_test_database() - destination = Path('/tmp/test4/') - if destination.is_dir(): - destination.rmdir() - - # Attempting to go to a nonexistant shellcut results in a - # DoesNotExist error. - command_go('test5') - self.assertTrue(re.search('DoesNotExist', stream.getvalue())) - - # Attempting to go to a shellcut whose path does not exist throws a - # BadPath error and deletes the shellcut from the database. - command_go('test4') - self.assertTrue(re.search('BadPath', stream.getvalue())) - - # Confirm that the shellcut has been deleted from the database. - command_go('test4') - self.assertTrue(re.search('DoesNotExist', stream.getvalue())) - - # Make the test4 path valid. - destination.mkdir() - # Re-add test4 to the database. - with DatabaseConnection(SHELLCUTS_FILE) as db: - db.insert_shellcut('test4', '/tmp/test4/') - - # Call command_go for a valid shellcut and retrieve the cd command. - command_go('test4') - self.assertTrue(re.search('cd "/tmp/test4/"', stream.getvalue())) - - @patch('core.commands.SHELL_CONFIGS', Path('/tmp/src/')) - @patch('core.commands.SHELLCUTS_FILE', Path('/tmp/dest/shellcuts.db')) - @patch('sys.stdout', new_callable=StringIO) - def test_009_check_bashmarks_command(self, stream): - """Unfinished.""" - dest_directories = [Path('/tmp/dest/bash/'), - Path('/tmp/dest/fish/'), - Path('/tmp/dest/zsh/')] - src_directories = [Path('/tmp/src/bash/'), - Path('/tmp/src/fish/'), - Path('/tmp/src/zsh/')] - - shutil.rmtree(str('/tmp/dest/')) - for directory in dest_directories: - directory.mkdir(parents=True) - - shutil.rmtree(str('/tmp/src/')) - for directory in src_directories: - directory.mkdir(parents=True) - directory.joinpath('bashmarks-aliases.txt').touch() - directory.joinpath('other1.txt').touch() - directory.joinpath('other2.txt').touch() - - command_bashmarks(True) From bf2fccea8970a41633f48a6eea2275d1520213cb Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Thu, 30 Aug 2018 20:23:27 -0400 Subject: [PATCH 22/35] documentation updates --- docs/CHANGES.txt | 10 +++ docs/SHELLCUTS.man | 62 ++++++++---------- .../core/__pycache__/__init__.cpython-36.pyc | Bin 255 -> 0 bytes .../core/__pycache__/commander.cpython-36.pyc | Bin 3142 -> 0 bytes .../core/__pycache__/jsonary.cpython-36.pyc | Bin 2335 -> 0 bytes source/core/__pycache__/parser.cpython-36.pyc | Bin 1650 -> 0 bytes .../core/__pycache__/utilities.cpython-36.pyc | Bin 1608 -> 0 bytes 7 files changed, 38 insertions(+), 34 deletions(-) delete mode 100644 source/core/__pycache__/__init__.cpython-36.pyc delete mode 100644 source/core/__pycache__/commander.cpython-36.pyc delete mode 100644 source/core/__pycache__/jsonary.cpython-36.pyc delete mode 100644 source/core/__pycache__/parser.cpython-36.pyc delete mode 100644 source/core/__pycache__/utilities.cpython-36.pyc diff --git a/docs/CHANGES.txt b/docs/CHANGES.txt index 60f03ab..f9b74e4 100644 --- a/docs/CHANGES.txt +++ b/docs/CHANGES.txt @@ -1,5 +1,15 @@ Changelog for Shellcuts +v1.2.3 - 08/XX/2018 + - Changed storage technology back to SQLite + - Removed bashmarks syntax toggle + - Completely rewrote file organization + - Removed deb and rpm support + - Removed initialization script + - Added installation script + - Added manual command + - Added local installation support for universality + v1.2.2 - 05/25/2018 - Changed data storage technology from JSON to SQLite - Added --move command diff --git a/docs/SHELLCUTS.man b/docs/SHELLCUTS.man index 1a3e8af..c173d61 100644 --- a/docs/SHELLCUTS.man +++ b/docs/SHELLCUTS.man @@ -1,13 +1,14 @@ -.TH shellcuts 1 "25 May 2018" "1.2.2" +.TH shellcuts 1 "25 May 2018" "1.2.3" .SH NAME shellcuts - directory shortcuts for your shell. .SH SYNOPSIS -sc [shellcut] [-flag] [flag option] +sc [-f/--flag] +.I shellcut .SH DESCRIPTION -Shellcuts are directory shortcuts for your shell. This program allows you to save locations as 'shellcuts' and then cut back to those locations with a single, short command. This program is inspired by Bashmarks and hopes to improve on Bashmarks by supporting more systems and shells. Shellcuts includes the following features: +Shellcuts are directory shortcuts for your shell. This program allows you to save locations in your filesystem and then cut back to those locations with a single, short command. This program is inspired by Bashmarks and hopes to improve on Bashmarks by supporting more systems and shells. Shellcuts includes the following features: .PP .RS 2 - create named shellcuts to any location in the filesystem @@ -18,7 +19,7 @@ Shellcuts are directory shortcuts for your shell. This program allows you to sav .PP - save shellcuts on a per-user basis .PP -- Bashmarks syntax can be enabled for user comfort and familiarity +- Bashmarks syntax is supported for user comfort and familiarity .PP - supports Bash, Fish, and Zsh .RE @@ -29,8 +30,6 @@ Planned features include: - tab completion .PP - Csh, Korn shell support -.PP -- local, non-administrator installs .RE .SH OPTIONS @@ -68,44 +67,33 @@ move an existing shellcut to a new directory print the specified shellcut to the screen .RE .PP -.B -v, --version +.B --version .RS 4 display version information .RE .PP -.B --init +.B --man .RS 4 -launch the initialization script -.RE -.PP -.B --enable-bashmarks-syntax, --disable-bashmarks-syntax -.RS 4 -enable or disable Bashmarks syntax (default: disabled) +display this manual page .RE .SH USAGE -The configuration utility that runs during installation can automatically configure your system, or if you'd prefer it can show you how to do the configuration manually. It's highly recommended that you use the automatic configuration, as it's safe from human error and is really easy! If you want to re-run the configuration utility, use this command: -.PP -.RS 2 -.I sc --init -.RE -.PP The core command for Shellcuts is .I sc -and by default this program includes the following aliases: +and this program includes the following aliases: .PP .RS 2 - -.I shellcut +.I scut .PP - -.I shellcuts +.I shellc .PP - -.I shellc +.I shellcut .PP - -.I scut +.I shellcuts .PP .RE Feel free to use any of the above or the main @@ -139,12 +127,6 @@ to save, equivalent to .I sc -n .RE -This syntax is disabled by default, but it can be enabled easily using this command: -.PP -.RS 4 -.I sc --enable-bashmarks-syntax -.RE - .SH EXAMPLES Save a shellcut named .I downloads @@ -205,9 +187,21 @@ alias: .RE .SH FILES -.B ~/.config/shellcuts/shellcuts.json +.B ~/.shellcuts/ +.RS 4 +contains all program files +.RE +.B ~/.shellcuts/docs/ +.RS 4 +location of all documentation +.RE +.B ~/.shellcuts/source/ +.RS 4 +contains all Python source code for Shellcuts +.RE +.B ~/.shellcuts/data/shellcuts.json .RS 4 -storage location for all shellcuts +contains all Shellcuts in the JSON format .RE .SH LICENSE @@ -216,7 +210,7 @@ GPLv3 .SH SOURCE Visit .I https://www.github.com/tgsachse/shellcuts -to view the source code for this program and give the project a star if you really liked it! +to view the complete project and give the repository a star if you really liked it! .SH AUTHOR Tiger Sachse (tgsachse) diff --git a/source/core/__pycache__/__init__.cpython-36.pyc b/source/core/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 1b8dacf009bb6858910c78f47b4b7bc90fe1a50e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 255 zcmX|+u};J=42F|7?Ojj0cVNAR46GbNf{B6UwlG8`v1m|IS57LpnT1#3m9jGN3QQym zmi|wlEZdT=_WRw#$LG@RC~saP&Lf@kU@(Mnwxu_$$$6I?#K%LQH(FaXT4!O9fZDQ8qN1zl3%(c) z)hm9Fa>4*Iy~NQ>5#&AL@{I5D4lT&j1m2r9MHz7=1G1i!TD#dLtuL2z-d+c^tB0HV ITC5QK4=BMyod5s; diff --git a/source/core/__pycache__/commander.cpython-36.pyc b/source/core/__pycache__/commander.cpython-36.pyc deleted file mode 100644 index f8a53d6c01ede9a1289a80963963991435ad017b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3142 zcmb7GU2hXd6rI^!@2)qF2_XbQ+i2^yKq`^6fLav+q9jm+s8veKhYQ*o&m^%~?}ph8 z3C5ACnuk77ssEzCp#NoF`_$*Y@zir?{gI?pDr?Qmo$>6)IrrXk=iKyk@W;=;{P6b_ zV}G(emks+0T6PtkU=1d?uwvfe812~V+6|l2niD(SQln(-QtWoUhR4}gOt`{($%Hqs z8osdZuti_<;4eIi)fc&TzG>zg7|UCEJ8tJ~nPKAH%+jQ(p0{Y%=(1p3LCbzbCmCMP z8kXPr#s6lsgTQeM$cQ$(k<1J%dJ#VHw3h_`CR)^)K1!Y z6a{!%=AvVk%U#}^9phdaZD|o?x@a83{|%Eq?=UsjXB}e=IA=)@Yg~PpTOE7AFz;aQ zsPow4n0W*9lDdI;i7a!^by%)?h&7iivvwAir#7+Xk!98vVEO7Ntoi0yRm{uk73KlW z_t8G3T_IzcOYMM-O+~KLLB65V?T8k%6SuQmJKgk|)OI4bb!k(zlU#d42Q_TCO}mBf z;7#NOT-8+TTV0vtnQ=qWP18S~+>vmhJd8;eq9Z=5U^EyuvPX^9dWR=W5APilO*u!+ z#Mx;Wg6~>@37Ri48atc-Q^a-bJ|Ex*_>^6?iMn2Bu&jl(Rl|V@N1+s+dhyBbsi20q zROEVH+E(pcs-tAPYpLosb3DX=b&_URYHt`f2LP)>xZ@+VjKY#x4)+1IH!}fM(7z88 zIl*9h2xEfjIKKUfuNHFNK{6XxU&xVJg!ecbAb8N~KB>|Js$bJ2UjCcd#<#M!gGL zy+<9TuHPA23+)>F)w#90WUl!FT<r&8e9Py(>V!N> z+O548|m^E-?y~fk~J?zgQ%WQKj5L*g${&oj404dcKA(yeVzmB5kZR)9$*= zTiZfWA^86hqK;tu4YZ8%uSlFKrOqCNBuZ~axP)5>VQwS<;ebFe$cB3R-^gksWM2va zWKg`ZecpmE)K7{+y|pdI$aQzuNG zqoSgS%p;Pmjze{Js0n6W*obSVi0dOGc#|NmsRcZ=zF3;1xC*iI7QP}`e1JB_6Hp=_ zn&2^S9p^Hkq3AV;p1}Yj{`_=~KqOaDJ`b#4tH@sCu3h^c6ZUQPExXTL~e#&d@ZQ{5cw;vb9&7otVseFwJ inMrcOz3Hg&O zT@KtoftEf5BT0uyDp^Q76nZCQk<)P~c}AowOCN|VObb8CPkOxFS7F#4nUr7L@E?LprFq-$_EXiY z{sjc&tU)vP+&BF${+nG4Zk|9(UxQHu=tzf2+HoY4&IbZamakKiWm$p$W$DQc=qs`+ zH=*}rP1d2`kay%3^i{bn??S&RcjPYgHMs{qeUw#Ba~Hic}1s{@gLs_YDuy zh1-5Cc^^!u(mVvuTQ8bjq{Rzky8-POT6zLzLMKEJ19m{Bbik%0JqQRqbK*ne;!{bd z?6;lw%#;S@37g`4=M|Ccd-9UJh8`%L@!?ZrR5Uc)Bs@%f$xUDJ%ix`gbBG~?ubEMO z)~IB=qHswFJRX5#8UeGix(-s4W;=@+-6S^Xa^@yO6=yEKk-4}L-zEnak83W$Q#I!;9p)b(~1mnxA+pO-8z_THQpe7A^x?X%25PPlzB6 zj1Zd`rz#l2%IpsHppCari;1&I^XQ@z*lct^h8v9TfV?T|9qcFcJBTL4i;bHnDpW>s z-wHP3J)J~0W(CC8Jm|rhf@CouO^~3jz~txOeJ; zA*@Qry9?m?jP=Y1@Wz~ZFK6B-ZA(2Pf2qt!#~{IXemGM9lU#4Z4r|CSmC4DGAccxx z$*XLceLkm)D#i@^*;?q*j;+@u(#$kzp}Ef?8*B|+J>S)wS*yI75LjURAWnsN zw1l>|KwC_hM+;9Cv#X15d_-s7+Zt5NJoE2AD~^x* zVTffrg5sTnhmh-_N@nAjx!_e=B>Xz~&bAP%QuqdO@~q#c<9iDiR;R7Ffzh?;^AK+G zL$?JVoG@(QfLrbaJ^+;br{o{J(m=ng11$bN?**ZPQh#;UC}|HC)g?6e(aBsniiXRD zOk-NymTu_^7k&&Qg)>ld>oaHU*jb~FB|7#aB}7&gB1+^a#CtfFNH`e06; zK+`kmI7tbo+>$hXFEuHI+0eS^bDFlfewfRLVvNY38T>927x%Mhnu(4iE+U$tNNtnPF`cTeRGNK`c-PeEu9Cf&fD zUpnd-QA#7XUx>B!U9y7;OZmnK7pC6 z8VLw2helrD!XKHPsi!`Jb$G$l(qu&M;EQ2GP7j|BFWeTrIq=TQtqKml-p23^GOp2^ JLjxLF{{bt2j}-s_ diff --git a/source/core/__pycache__/utilities.cpython-36.pyc b/source/core/__pycache__/utilities.cpython-36.pyc deleted file mode 100644 index aa91974032ae250e5935bd4a44667c9d721c1a27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1608 zcmbVM%WfMt6eVXy4@xVy52>pfb z6btZIQ00$cD6$d7)bMd&*ak+-pymu;nKNWtfUN~i0ZuJ&8gP1nGc-f9bc5!8Gwm$R z)50&v-k_Vb2yKpTbx^q!;oRSl2$gYUy>V23yz_IdD*Md$-Jz1T1j+H?y`Pl3iyl3u z`t0Aj-!`kO7d`gc;0suS*9hFGf6_O2pf4)RmWevV?ug7 zBwpC#B5;((y1&PVA+3&|pZ6IV!0L%3C3ihXF-mSc@KxWF>$rfu@Y?Y`S}mJ);S|!e zxN7Jm_C|{@ve=og9jdu-v1SH}0Ts>(oj{e3!B9NK3Qh18o*-pR&_gscW*8%7(xjy( zaBGeC-v}>M#0d!#f(sHbDVoqT#W%{>x2RjX`8sboZy zp6@qGCj&c?1&pY&5v7uVQO4y#;Dq#dByun@KCbn7z-sDJI&NRG+Nt1!3qGoyaLQ_( z^{Miqa9Pdeg4Kq~^F8G;SsmO)dDR#ErkeyW>CL9WAj87DSQG%c+SSE`ss|*D)B(_) zDWb(G9vw{3Df(EN8WTJ*=+-_uLR0)x@jcRf(b7Ijc*1mBxAXzDfy=1Zx-(T<#8;IjzDN8b&D0Z|<0ftr2p&V||&JOB;c-t?JZg!z&Y zkgtv{Z>3^EJ>FgiTttia+AQGLY#wU4&HJh8GIe~|1O*ev_la}uc)oMtL+s^g*WRe2O2#DQ#OQt7DYy5(@slQcmpLiz>O^53t6K6 zxykFFxw($DoOC$(1Po{v|JP?>%gEspE@1IFjBb_lQKs94x31f@Gx~Ayy$hjrALZC+ z0Mra66LzliZKK)jo}RTkQU3c^t+%h5-EYpCji|7A0q0&Z8Fupayp#As->c+CCAsb_ kUteR9>>>Ly;B@G-WBnvZ&6I@;W&zd*pJ8AFXU%NpA7%)$tN;K2 From 0afeeb63b56466a653fc5e94ef971ab50996ad63 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Thu, 30 Aug 2018 22:43:10 -0400 Subject: [PATCH 23/35] work on installation script --- install.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ install.sh | 14 -------------- test.sh | 0 3 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 install.py delete mode 100755 install.sh delete mode 100755 test.sh diff --git a/install.py b/install.py new file mode 100644 index 0000000..09e83fa --- /dev/null +++ b/install.py @@ -0,0 +1,48 @@ +""" +""" +import re +import shutil +import compileall +from pathlib import Path + +CORE = 'core' +CACHE = '__pycache__' +SHELLS = ('zsh', 'fish', 'bash') +SOURCE_DIRS = ('docs', 'source', 'shells') +COMPILE_PATTERN = r'^(?P[_a-z]+)\.[\-a-z0-9]+\.pyc$' + +DESTINATION_DIRS = { + 'shellcuts' : Path('~/.shellcuts').expanduser(), + 'docs' : Path('~/.shellcuts/docs').expanduser(), + 'data' : Path('~/.shellcuts/data').expanduser(), + 'shells' : Path('~/.shellcuts/shells').expanduser(), + 'source' : Path('~/.shellcuts/source').expanduser(), + 'binaries' : Path('~/.shellcuts/binaries').expanduser(), + 'core' : Path('~/.shellcuts/binaries/core').expanduser(), +} + +shutil.rmtree(str(DESTINATION_DIRS['shellcuts'])) + +DESTINATION_DIRS['shellcuts'].mkdir(parents=True) +for directory in SOURCE_DIRS: + shutil.copytree(Path(directory), DESTINATION_DIRS[directory]) + +for directory in DESTINATION_DIRS.keys(): + DESTINATION_DIRS[directory].mkdir(exist_ok=True) + +compileall.compile_dir(str(DESTINATION_DIRS['source'])) + +top_cache = DESTINATION_DIRS['source'] / CACHE +for binary in top_cache.iterdir(): + binary_match = re.match(COMPILE_PATTERN, binary.name) + target = Path(binary_match.group('name') + '.pyc') + binary.replace(DESTINATION_DIRS['binaries'] / target) +top_cache.rmdir() + +core_cache = DESTINATION_DIRS['source'] / CORE / CACHE +for binary in core_cache.iterdir(): + binary_match = re.match(COMPILE_PATTERN, binary.name) + target = Path(binary_match.group('name') + '.pyc') + binary.replace(DESTINATION_DIRS['binaries'] / CORE / target) +core_cache.rmdir() + diff --git a/install.sh b/install.sh deleted file mode 100755 index 5e34116..0000000 --- a/install.sh +++ /dev/null @@ -1,14 +0,0 @@ - -function append_controller { - if [ -f $1 ] - then - echo "file" - if [ "$(grep -c -f $2 $1)" -eq 0 ] - then - echo "here" - cat $2 >> $1 - fi - fi -} - -append_controller gooble.txt gerber.txt diff --git a/test.sh b/test.sh deleted file mode 100755 index e69de29..0000000 From 4ec3d521a614286f637b5aea8627ab654be74460 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Thu, 30 Aug 2018 22:46:04 -0400 Subject: [PATCH 24/35] bug showing list --- source/core/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 255 bytes source/core/__pycache__/commander.cpython-36.pyc | Bin 0 -> 3179 bytes source/core/__pycache__/jsonary.cpython-36.pyc | Bin 0 -> 2335 bytes source/core/__pycache__/parser.cpython-36.pyc | Bin 0 -> 1650 bytes source/core/__pycache__/utilities.cpython-36.pyc | Bin 0 -> 1606 bytes source/core/commander.py | 4 +++- source/shellcuts.py | 4 +--- 7 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 source/core/__pycache__/__init__.cpython-36.pyc create mode 100644 source/core/__pycache__/commander.cpython-36.pyc create mode 100644 source/core/__pycache__/jsonary.cpython-36.pyc create mode 100644 source/core/__pycache__/parser.cpython-36.pyc create mode 100644 source/core/__pycache__/utilities.cpython-36.pyc diff --git a/source/core/__pycache__/__init__.cpython-36.pyc b/source/core/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b8dacf009bb6858910c78f47b4b7bc90fe1a50e GIT binary patch literal 255 zcmX|+u};J=42F|7?Ojj0cVNAR46GbNf{B6UwlG8`v1m|IS57LpnT1#3m9jGN3QQym zmi|wlEZdT=_WRw#$LG@RC~saP&Lf@kU@(Mnwxu_$$$6I?#K%LQH(FaXT4!O9fZDQ8qN1zl3%(c) z)hm9Fa>4*Iy~NQ>5#&AL@{I5D4lT&j1m2r9MHz7=1G1i!TD#dLtuL2z-d+c^tB0HV ITC5QK4=BMyod5s; literal 0 HcmV?d00001 diff --git a/source/core/__pycache__/commander.cpython-36.pyc b/source/core/__pycache__/commander.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..535ff7dca6f99d0d9107258446d27e5caa2c5346 GIT binary patch literal 3179 zcmb7GU2hvj6rI^!@2)qF^U*X_ONopTZH*u)Edr@Zt3uL(O0h~)+G3~{*E31%u6NVf zO_R9JQ~J;+c;kV8fOz8X%qvg)g+6iatUr<{LdDk1+?k#IIOm>w?_8an4F3A_^^bQ- zjQ!0H{cPAb(b6x`3D#hO3oGIcj?s>+uHCRXtvQj?Ei?+oE<|qEYj~V}#e^%oS4?;Z zR>K$88msx52mjzvtX|{Vx!24#F&4M8cGS+=GR4HZlO}Of?X_sv@MpoeiI)D1PBOfn zH7vm!wy=c#iZvYJhyun!{=$YUd{M;Q6M>k(=!=q=#8?z%F@-S@(_#kWgqRhlFqXue zIE`^q%;O`^=;BJ!?KWc}m1Zpm@1!4%^dT!~={aKtJFdeTS>_eb(V*(MsHdWvP!cyW<=% z%sZGD)H3!I%)EiQtL|a$l4b6>4a-vkYaUrlbfu%+5@rd=T;nMv({oGnG3)IqkXlAVwiv=g<{Ogr7=h17N|ceFQfRKte* zw43`7UY(#ji%qq$-IZ~c8h7N~Bwg|3u7oS)3?^w69kYD!T1E>7jqG8gwcgB_^TFLC z$|?G&8O2?KA^5(PlX0EV2)Y58wAZo88AOnYGu!B|S25_`Lto6m$%-n#LU$&{7g8vmEXNYHxZBDrf!( zCgR^hrTegZ0Z;w}oxoe=T!{*A5`61C~C_=hq=f>~hSm zxy%J8-gxJTnL^Ht-x8H7IH=ZjX*H2)J;`qEk_fyno5JKJ-^fJG;uCXnL1B5y-OX%>mh+y8_6P8QhMr;tTHe0S*{E0^lYNKa{HPiBji6(yUQ ztfa_Jm7R9BS;;nK<>R=r9yK@Ks|9a32~OLQj18rRGsE7HIO43~4^?`MJ#`9Km(fxZ z9q6;lL>r&8e1uuAJSLCA`H13(LZi6PU>uI342kN%#8RIx=P_lQm~x<>!bvf;Fr?>O zWQ&s`%FX3sC8>5dWY*ddimJuit3=IV`!{GQg=Q|3GDX!Pgp{gIMo9W1gn2CG5nzxZ zNBlRk>JZr%LI4@^;K;w@eBr7O@B&34pZoBCVmpl+AE1r$g+Vqs_8ne(1Q=CGBu@LI3^~YmmdSU>iX#pNx}fS_sV*AGbtIw>@dQQHEYD1N(LX7ht25ZN zjW+T<<)g#3FF!ZbYh^leQ5g+G9fV;w5!(^jr7(Ps!{OkH7ltBfg`q0b%g<6rQc-k} zFuBhp21OB+3zS$hA909IQ*h`oqUc;lFjCs)b5v%}KkfT|*`M-@7>oY2*;6a3tF)iu yT6}cv>qeSLnCqJBrcOz3Hg&O zT@KtoftEf5BT0uyDp^Q76nZCQk<)P~c}AowOCN|VObb8CPkOxFS7F#4nUr7L@E?LprFq-$_EXiY z{sjc&tU)vP+&BF${+nG4Zk|9(UxQHu=tzf2+HoY4&IbZamakKiWm$p$W$DQc=qs`+ zH=*}rP1d2`kay%3^i{bn??S&RcjPYgHMs{qeUw#Ba~Hic}1s{@gLs_YDuy zh1-5Cc^^!u(mVvuTQ8bjq{Rzky8-POT6zLzLMKEJ19m{Bbik%0JqQRqbK*ne;!{bd z?6;lw%#;S@37g`4=M|Ccd-9UJh8`%L@!?ZrR5Uc)Bs@%f$xUDJ%ix`gbBG~?ubEMO z)~IB=qHswFJRX5#8UeGix(-s4W;=@+-6S^Xa^@yO6=yEKk-4}L-zEnak83W$Q#I!;9p)b(~1mnxA+pO-8z_THQpe7A^x?X%25PPlzB6 zj1Zd`rz#l2%IpsHppCari;1&I^XQ@z*lct^h8v9TfV?T|9qcFcJBTL4i;bHnDpW>s z-wHP3J)J~0W(CC8Jm|rhf@CouO^~3jz~txOeJ; zA*@Qry9?m?jP=Y1@Wz~ZFK6B-ZA(2Pf2qt!#~{IXemGM9lU#4Z4r|CSmC4DGAccxx z$*XLceLkm)D#i@^*;?q*j;+@u(#$kzp}Ef?8*B|+J>S)wS*yI75LjURAWnsN zw1l>|KwC_hM+;9Cv#X15d_-s7+Zt5NJoE2AD~^x* zVTffrg5sTnhmh-_N@nAjx!_e=B>Xz~&bAP%QuqdO@~q#c<9iDiR;R7Ffzh?;^AK+G zL$?JVoG@(QfLrbaJ^+;br{o{J(m=ng11$bN?**ZPQh#;UC}|HC)g?6e(aBsniiXRD zOk-NymTu_^7k&&Qg)>ld>oaHU*jb~FB|7#aB}7&gB1+^a#CtfFNH`e06; zK+`kmI7tbo+>$hXFEuHI+0eS^bDFlfewfRLVvNY38T>927x%Mhnu(4iE+U$tNNtnPF`cTeRGNK`c-PeEu9Cf&fD zUpnd-QA#7XUx>B!U9y7;OZmnK7pC6 z8VLw2helrD!XKHPsi!`Jb$G$l(qu&M;EQ2GP7j|BFWeTrIq=TQtqKml-p23^GOp2^ JLjxLF{{bt2j}-s_ literal 0 HcmV?d00001 diff --git a/source/core/__pycache__/utilities.cpython-36.pyc b/source/core/__pycache__/utilities.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6146555bec32246419e0e790e74ea35bb7e8f11 GIT binary patch literal 1606 zcmbVM-EP}96eeZ)C$i%>O|qoGvJpiwzyoY|+eHSW2-YPYmceriJM92(uJ#JL+M)cHU_dWMfk)pFsdLDW-{BXVn^yI=-(UZgMd)vI zr?X8~syI0rblzm2n<$-~Lv`Or~hnV3+{Bni19 zZ0t!@pM3O+3F+~Wc;SGHz)>3O@c|!&v_AR#e8@-y>nDzs-1QvAD7p5)S3^&(;{x`= zE64X}y_&I0r;w(@RZAzaH#&Tg#m;~2P|b#mH8;=<=x|c#1iE|-hT<7kXo@fK6e(kh z9->?07GtC`G=0+yuxn2CUkEQ$#0d!#f(sHbDV?{hJ~@c=z<@kzb-T~Ipyno9{Qyr4xIx35=+!5ognYao*(tFiihM!h#=@xOipDP~;mo;22SYxC--%}ow_2?!ps-fW5{WN$*Z#D-887AJvq6Em* zu5KCVdP2%b69CY_Pnd4&H+=v+ z?N0U4(!4dvkB^CT$?MwS4SBj$%W?8$?SHo9$%$Yf7UGbQU9WWl zq=Rx8aYsThI6&$N<|;03n#nGKHeW^!j&o1*?`%{8p0q{GQi!GK=z`yLaUhJ`D*gvEz2yHhR3`Fx;$r zE`-*6Y_V|!)Qlt(wv}FU{nN8YD#n6 mS-vh|k?x`JG~jgPvtxZ7NX?XqOPLa^4<5t71} Date: Thu, 30 Aug 2018 23:40:57 -0400 Subject: [PATCH 25/35] commenting frenzy --- source/core/__init__.py | 9 ++-- .../core/__pycache__/__init__.cpython-36.pyc | Bin 255 -> 0 bytes .../core/__pycache__/commander.cpython-36.pyc | Bin 3179 -> 0 bytes .../core/__pycache__/jsonary.cpython-36.pyc | Bin 2335 -> 0 bytes source/core/__pycache__/parser.cpython-36.pyc | Bin 1650 -> 0 bytes .../core/__pycache__/utilities.cpython-36.pyc | Bin 1606 -> 0 bytes source/core/commander.py | 48 ++++++++++-------- source/core/jsonary.py | 8 +-- source/core/parser.py | 17 ++++--- source/core/utilities.py | 4 +- source/shellcuts.py | 16 +++++- 11 files changed, 63 insertions(+), 39 deletions(-) delete mode 100644 source/core/__pycache__/__init__.cpython-36.pyc delete mode 100644 source/core/__pycache__/commander.cpython-36.pyc delete mode 100644 source/core/__pycache__/jsonary.cpython-36.pyc delete mode 100644 source/core/__pycache__/parser.cpython-36.pyc delete mode 100644 source/core/__pycache__/utilities.cpython-36.pyc diff --git a/source/core/__init__.py b/source/core/__init__.py index 820b0ad..1273384 100644 --- a/source/core/__init__.py +++ b/source/core/__init__.py @@ -1,6 +1,5 @@ +"""Package initialization for core utilities. + +Part of Shellcuts by Tiger Sachse. """ -""" -from core import (parser, - jsonary, - commander, - utilities) +from core import parser, jsonary, commander, utilities diff --git a/source/core/__pycache__/__init__.cpython-36.pyc b/source/core/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 1b8dacf009bb6858910c78f47b4b7bc90fe1a50e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 255 zcmX|+u};J=42F|7?Ojj0cVNAR46GbNf{B6UwlG8`v1m|IS57LpnT1#3m9jGN3QQym zmi|wlEZdT=_WRw#$LG@RC~saP&Lf@kU@(Mnwxu_$$$6I?#K%LQH(FaXT4!O9fZDQ8qN1zl3%(c) z)hm9Fa>4*Iy~NQ>5#&AL@{I5D4lT&j1m2r9MHz7=1G1i!TD#dLtuL2z-d+c^tB0HV ITC5QK4=BMyod5s; diff --git a/source/core/__pycache__/commander.cpython-36.pyc b/source/core/__pycache__/commander.cpython-36.pyc deleted file mode 100644 index 535ff7dca6f99d0d9107258446d27e5caa2c5346..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3179 zcmb7GU2hvj6rI^!@2)qF^U*X_ONopTZH*u)Edr@Zt3uL(O0h~)+G3~{*E31%u6NVf zO_R9JQ~J;+c;kV8fOz8X%qvg)g+6iatUr<{LdDk1+?k#IIOm>w?_8an4F3A_^^bQ- zjQ!0H{cPAb(b6x`3D#hO3oGIcj?s>+uHCRXtvQj?Ei?+oE<|qEYj~V}#e^%oS4?;Z zR>K$88msx52mjzvtX|{Vx!24#F&4M8cGS+=GR4HZlO}Of?X_sv@MpoeiI)D1PBOfn zH7vm!wy=c#iZvYJhyun!{=$YUd{M;Q6M>k(=!=q=#8?z%F@-S@(_#kWgqRhlFqXue zIE`^q%;O`^=;BJ!?KWc}m1Zpm@1!4%^dT!~={aKtJFdeTS>_eb(V*(MsHdWvP!cyW<=% z%sZGD)H3!I%)EiQtL|a$l4b6>4a-vkYaUrlbfu%+5@rd=T;nMv({oGnG3)IqkXlAVwiv=g<{Ogr7=h17N|ceFQfRKte* zw43`7UY(#ji%qq$-IZ~c8h7N~Bwg|3u7oS)3?^w69kYD!T1E>7jqG8gwcgB_^TFLC z$|?G&8O2?KA^5(PlX0EV2)Y58wAZo88AOnYGu!B|S25_`Lto6m$%-n#LU$&{7g8vmEXNYHxZBDrf!( zCgR^hrTegZ0Z;w}oxoe=T!{*A5`61C~C_=hq=f>~hSm zxy%J8-gxJTnL^Ht-x8H7IH=ZjX*H2)J;`qEk_fyno5JKJ-^fJG;uCXnL1B5y-OX%>mh+y8_6P8QhMr;tTHe0S*{E0^lYNKa{HPiBji6(yUQ ztfa_Jm7R9BS;;nK<>R=r9yK@Ks|9a32~OLQj18rRGsE7HIO43~4^?`MJ#`9Km(fxZ z9q6;lL>r&8e1uuAJSLCA`H13(LZi6PU>uI342kN%#8RIx=P_lQm~x<>!bvf;Fr?>O zWQ&s`%FX3sC8>5dWY*ddimJuit3=IV`!{GQg=Q|3GDX!Pgp{gIMo9W1gn2CG5nzxZ zNBlRk>JZr%LI4@^;K;w@eBr7O@B&34pZoBCVmpl+AE1r$g+Vqs_8ne(1Q=CGBu@LI3^~YmmdSU>iX#pNx}fS_sV*AGbtIw>@dQQHEYD1N(LX7ht25ZN zjW+T<<)g#3FF!ZbYh^leQ5g+G9fV;w5!(^jr7(Ps!{OkH7ltBfg`q0b%g<6rQc-k} zFuBhp21OB+3zS$hA909IQ*h`oqUc;lFjCs)b5v%}KkfT|*`M-@7>oY2*;6a3tF)iu yT6}cv>qeSLnCqJBrcOz3Hg&O zT@KtoftEf5BT0uyDp^Q76nZCQk<)P~c}AowOCN|VObb8CPkOxFS7F#4nUr7L@E?LprFq-$_EXiY z{sjc&tU)vP+&BF${+nG4Zk|9(UxQHu=tzf2+HoY4&IbZamakKiWm$p$W$DQc=qs`+ zH=*}rP1d2`kay%3^i{bn??S&RcjPYgHMs{qeUw#Ba~Hic}1s{@gLs_YDuy zh1-5Cc^^!u(mVvuTQ8bjq{Rzky8-POT6zLzLMKEJ19m{Bbik%0JqQRqbK*ne;!{bd z?6;lw%#;S@37g`4=M|Ccd-9UJh8`%L@!?ZrR5Uc)Bs@%f$xUDJ%ix`gbBG~?ubEMO z)~IB=qHswFJRX5#8UeGix(-s4W;=@+-6S^Xa^@yO6=yEKk-4}L-zEnak83W$Q#I!;9p)b(~1mnxA+pO-8z_THQpe7A^x?X%25PPlzB6 zj1Zd`rz#l2%IpsHppCari;1&I^XQ@z*lct^h8v9TfV?T|9qcFcJBTL4i;bHnDpW>s z-wHP3J)J~0W(CC8Jm|rhf@CouO^~3jz~txOeJ; zA*@Qry9?m?jP=Y1@Wz~ZFK6B-ZA(2Pf2qt!#~{IXemGM9lU#4Z4r|CSmC4DGAccxx z$*XLceLkm)D#i@^*;?q*j;+@u(#$kzp}Ef?8*B|+J>S)wS*yI75LjURAWnsN zw1l>|KwC_hM+;9Cv#X15d_-s7+Zt5NJoE2AD~^x* zVTffrg5sTnhmh-_N@nAjx!_e=B>Xz~&bAP%QuqdO@~q#c<9iDiR;R7Ffzh?;^AK+G zL$?JVoG@(QfLrbaJ^+;br{o{J(m=ng11$bN?**ZPQh#;UC}|HC)g?6e(aBsniiXRD zOk-NymTu_^7k&&Qg)>ld>oaHU*jb~FB|7#aB}7&gB1+^a#CtfFNH`e06; zK+`kmI7tbo+>$hXFEuHI+0eS^bDFlfewfRLVvNY38T>927x%Mhnu(4iE+U$tNNtnPF`cTeRGNK`c-PeEu9Cf&fD zUpnd-QA#7XUx>B!U9y7;OZmnK7pC6 z8VLw2helrD!XKHPsi!`Jb$G$l(qu&M;EQ2GP7j|BFWeTrIq=TQtqKml-p23^GOp2^ JLjxLF{{bt2j}-s_ diff --git a/source/core/__pycache__/utilities.cpython-36.pyc b/source/core/__pycache__/utilities.cpython-36.pyc deleted file mode 100644 index a6146555bec32246419e0e790e74ea35bb7e8f11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1606 zcmbVM-EP}96eeZ)C$i%>O|qoGvJpiwzyoY|+eHSW2-YPYmceriJM92(uJ#JL+M)cHU_dWMfk)pFsdLDW-{BXVn^yI=-(UZgMd)vI zr?X8~syI0rblzm2n<$-~Lv`Or~hnV3+{Bni19 zZ0t!@pM3O+3F+~Wc;SGHz)>3O@c|!&v_AR#e8@-y>nDzs-1QvAD7p5)S3^&(;{x`= zE64X}y_&I0r;w(@RZAzaH#&Tg#m;~2P|b#mH8;=<=x|c#1iE|-hT<7kXo@fK6e(kh z9->?07GtC`G=0+yuxn2CUkEQ$#0d!#f(sHbDV?{hJ~@c=z<@kzb-T~Ipyno9{Qyr4xIx35=+!5ognYao*(tFiihM!h#=@xOipDP~;mo;22SYxC--%}ow_2?!ps-fW5{WN$*Z#D-887AJvq6Em* zu5KCVdP2%b69CY_Pnd4&H+=v+ z?N0U4(!4dvkB^CT$?MwS4SBj$%W?8$?SHo9$%$Yf7UGbQU9WWl zq=Rx8aYsThI6&$N<|;03n#nGKHeW^!j&o1*?`%{8p0q{GQi!GK=z`yLaUhJ`D*gvEz2yHhR3`Fx;$r zE`-*6Y_V|!)Qlt(wv}FU{nN8YD#n6 mS-vh|k?x`JG~jgPvtxZ7NX?XqOPLa^4<5t71} 0: @@ -72,16 +81,15 @@ def list(self): def move(self, name): - """""" + """Move an existing shellcut to the current working directory.""" command = 'printf "Moved shellcut \'{0}\'\n"' - del self.shellcuts[name] self.shellcuts[name] = os.getcwd() self.shellcuts.write() print(command.format(name)) def new(self, name): - """""" + """Create a new shellcut.""" command = 'printf "Added new shellcut \'{0}\'\n"' self.shellcuts[name] = os.getcwd() self.shellcuts.write() @@ -89,7 +97,7 @@ def new(self, name): def print(self, name): - """""" + """Print a specific shellcut.""" command = 'printf "{0} : {1}\n"' if name not in self.shellcuts: utilities.throw_error("DoesNotExist") @@ -98,7 +106,7 @@ def print(self, name): def version(self): - """""" + """Print the version information for Shellcuts.""" command = 'printf "' try: @@ -114,7 +122,7 @@ def version(self): def manual(self): - """""" + """Open the manual page for Shellcuts.""" command = 'man ".' command += str(self.manual_file) command += '"' diff --git a/source/core/jsonary.py b/source/core/jsonary.py index c096ff6..e2708eb 100644 --- a/source/core/jsonary.py +++ b/source/core/jsonary.py @@ -1,4 +1,7 @@ -"""An iterable JSON jsonary with extra safety. +"""An iterable JSON dictionary with extra safety. + +In the future, this class will be updated to extend dict directly, thus +eliminating some redundancy and inefficiencies. Part of Shellcuts by Tiger Sachse. """ @@ -6,8 +9,7 @@ from pathlib import Path class Jsonary: - """Implements a jsonary that is iterable and handles JSON I/O.""" - + """A JSON dictionary that is iterable and handles JSON I/O.""" def __init__(self, json_path): """Attempt to load the given JSON path.""" self.__json_path = json_path diff --git a/source/core/parser.py b/source/core/parser.py index f12292d..15f55c7 100644 --- a/source/core/parser.py +++ b/source/core/parser.py @@ -1,29 +1,30 @@ -""" +"""Parser that handles arguments for Shellcuts. + +Part of Shellcuts by Tiger Sachse. """ import argparse from core import utilities class Parser(argparse.ArgumentParser): - """ - """ + """Class to handle command line arguments.""" def __init__(self, *args, **kwargs): - """Initialize by initializing super and adding base argument.""" + """Initialize super and add a base argument for short-circuiting.""" super().__init__(*args, add_help=False, **kwargs) self.add_argument('name', nargs='?', default=None) def parse_arguments(self): - """""" + """Parse known arguments and save them.""" self.arguments, self.unknown = self.parse_known_args() def has_unknown_arguments(self): - """""" + """Determine if the most recent parse found unknown arguments.""" return True if len(self.unknown) > 0 else False def add_arguments(self): - """Add flags to parser.""" + """Add flags to the parser.""" self.add_argument('-n', '--new') self.add_argument('-m', '--move') self.add_argument('-p', '--print') @@ -35,5 +36,5 @@ def add_arguments(self): def error(self, message): - """Call help command in case of error.""" + """In case of an error, show the user the help menu.""" utilities.throw_help() diff --git a/source/core/utilities.py b/source/core/utilities.py index f1d8b19..b21d378 100644 --- a/source/core/utilities.py +++ b/source/core/utilities.py @@ -1,4 +1,6 @@ -""" +"""A collection of utilities and constants necessary for Shellcuts. + +Part of Shellcuts by Tiger Sachse. """ from pathlib import Path diff --git a/source/shellcuts.py b/source/shellcuts.py index a70b9ec..0b9a3ad 100644 --- a/source/shellcuts.py +++ b/source/shellcuts.py @@ -1,4 +1,10 @@ -""" +"""Main driver and entry point for Shellcuts. + +This script parses command line arguments and ultimately prints a shell command +to stdout. It is only ever called by a shell script that captures the printed +shell command and evaluates it. + +Part of Shellcuts by Tiger Sachse. """ from core import utilities from core.parser import Parser @@ -8,16 +14,22 @@ utilities.SHELLCUTS_FILE, utilities.MANUAL_FILE) -parser = Parser(commander) +# The initial parser only accepts 'go' commands (in an attempt to +# short-circuit the program). This allows the most common command to execute +# as quickly as possible. +parser = Parser() parser.parse_arguments() +# If there are no unknowns after the initial parse then it must be a 'go' command. if len(parser.unknown) <= 0: commander.go(parser.arguments.name) exit(0) +# Add in all the rest of the arguments and re-parse. parser.add_arguments() parser.parse_arguments() +# Unknowns trigger the help menu, else tell the commander to execute. if len(parser.unknown) > 0: utilities.throw_help() else: From 1aa22256cad6e8514d5a510b6671ba84f15b1f1f Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Thu, 30 Aug 2018 23:54:39 -0400 Subject: [PATCH 26/35] Update README.rst --- docs/README.rst | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/docs/README.rst b/docs/README.rst index f264bde..c8fdf08 100644 --- a/docs/README.rst +++ b/docs/README.rst @@ -1,21 +1,22 @@ shellcuts - directory shortcuts for your shell ---------------------------------------------- -Shellcuts are directory shortcuts for your shell. This program allows you to save locations as 'shellcuts' and then cut back to those locations with a single, short command. This program is inspired by Bashmarks and hopes to improve on Bashmarks_ by supporting more systems and shells. Shellcuts includes the following features: +Shellcuts are directory shortcuts for your shell. This program allows you to save locations in +your filesystem and then cut back to those locations with a single, short command. This program +is inspired by Bashmarks and hopes to improve on Bashmarks by supporting more systems and +shells. Shellcuts includes the following features: - creates named shellcuts to any location in the filesystem - lists all saved shellcuts - deletes shellcuts by name - saves shellcuts on a per-user basis -- Bashmarks syntax can be enabled for user comfort and familiarity +- Bashmarks syntax is supported for user comfort and familiarity - supports Bash, Fish, and Zsh Planned features include: - tab completion - Csh, Korn shell support -- installable via APT, DNF, Homebrew -- local, non-administrator installs installation ------------ @@ -36,16 +37,12 @@ RedHat-based machines (Fedora, CentOS, etc) should install using this method! DN usage ----- -The configuration utility that runs during installation can automatically configure your system, or if you'd prefer it can show you how to do the configuration manually. It's highly recommended that you use the automatic configuration, as it's safe from human error and is really easy! If you want to re-run the configuration utility, use this command: -:: - $ sc --init - -The core command for Shellcuts is ``sc`` and by default this program includes the aliases: +The core command for Shellcuts is ``sc`` and this program includes the following aliases: +- ``scut`` +- ``shellc`` - ``shellcut`` - ``shellcuts`` -- ``shellc`` -- ``scut`` Feel free to use any of the above or the main ``sc`` command to operate Shellcuts. This program also includes aliases to replicate Bashmarks_ syntax. They are as follows: @@ -55,32 +52,26 @@ Feel free to use any of the above or the main ``sc`` command to operate Shellcut - ``d`` to delete, equivalent to ``sc -d`` - ``l`` to list, equivalent to ``sc -l`` -This syntax is disabled by default, but it can be enabled easily using this command: -:: - $ sc --enable-bashmarks-syntax - flags ----- Here is a list of all available options/flags: -``-d, --delete [shellcut]`` - Delete the specified shellcut if it exists. -``-h, --help`` - Display a help menu for quick reference. -``-l, --list`` - List all available shellcuts. ``-n, --new [shellcut]`` Create a new shellcut for the current working directory. ``-m, --move [shellcut]`` Move an existing shellcut to a new directory. +``-d, --delete [shellcut]`` + Delete the specified shellcut if it exists. ``-p, --print [shellcut]`` Print the specified shellcut to the screen. -``-v, --version`` +``-l, --list`` + List all available shellcuts. +``-h, --help`` + Display a help menu for quick reference. +``--version`` Display version information. -``--init`` - Launch the initialization script. -``--enable-bashmarks-syntax, --disable-bashmarks-syntax`` - Enable or disable Bashmarks syntax. (default: disabled) +``--man`` + Display a Linux manual page. examples -------- From a651b9231c775974ae2c93b7465e83af3416e0fe Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Fri, 31 Aug 2018 00:01:53 -0400 Subject: [PATCH 27/35] manual tweaks --- docs/SHELLCUTS.man | 51 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/docs/SHELLCUTS.man b/docs/SHELLCUTS.man index c173d61..32c4ffa 100644 --- a/docs/SHELLCUTS.man +++ b/docs/SHELLCUTS.man @@ -33,38 +33,39 @@ Planned features include: .RE .SH OPTIONS -.B -d, --delete +.PP +.B -n, --new .I shellcut .RS 4 -delete the specified shellcut if it exists +create a new shellcut for the current working directory .RE .PP -.B -h, --help +.B -m, --move +.I shellcut .RS 4 -display a help menu for quick reference +move an existing shellcut to a new directory .RE .PP -.B -l, --list +.B -d, --delete +.I shellcut .RS 4 -list all available shellcuts +delete the specified shellcut if it exists .RE .PP -.B -n, --new +.B -p, --print .I shellcut .RS 4 -create a new shellcut for the current working directory +print the specified shellcut to the screen .RE .PP -.B -m, --move -.I shellcut +.B -l, --list .RS 4 -move an existing shellcut to a new directory +list all available shellcuts .RE .PP -.B -p, --print -.I shellcut +.B -h, --help .RS 4 -print the specified shellcut to the screen +display a help menu for quick reference .RE .PP .B --version @@ -99,12 +100,7 @@ and this program includes the following aliases: Feel free to use any of the above or the main .I sc command to operate Shellcuts. This program also includes aliases to replicate Bashmarks syntax. They are as follows: -.PP .RS 2 -- -.I d -to delete, equivalent to -.I sc -d .PP - .I g @@ -112,9 +108,14 @@ to go, equivalent to .I sc .PP - -.I l -to list, equivalent to -.I sc -l +.I s +to save, equivalent to +.I sc -n +.PP +- +.I d +to delete, equivalent to +.I sc -d .PP - .I p @@ -122,9 +123,9 @@ to print, equivalent to .I sc -p .PP - -.I s -to save, equivalent to -.I sc -n +.I l +to list, equivalent to +.I sc -l .RE .SH EXAMPLES From abcd0e5fa07547120bc7c0e0d3fd43b3238242f9 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Fri, 31 Aug 2018 00:23:55 -0400 Subject: [PATCH 28/35] initial new installation instructions --- docs/README.rst | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/README.rst b/docs/README.rst index c8fdf08..6da785f 100644 --- a/docs/README.rst +++ b/docs/README.rst @@ -21,19 +21,28 @@ Planned features include: installation ------------ -Shellcuts is meant to be easy to install--use any of the following methods. You need the latest version of `Python 3`_ installed for it to work! +Shellcuts is easy to install and doesn't require any special privileges! To run, you need the latest version of `Python 3`_ installed, as well as a supported shell. Linux users almost certainly have both of these but macOS users may need to install Python3. Windows is not supported by Shellcuts. -**METHOD 1:** Install with wget and dpkg +**METHOD 1:** Install with ``wget`` and ``tar`` -If you use a Debian-based machine (Ubuntu, Linux Mint, Debian, etc) then use this method! APT support is hopefully coming soon. Run the following command: +The following commands will work for nearly everyone. If it does not work for you, try the next method. :: - $ wget https://github.com/tgsachse/shellcuts/releases/download/v1.2.2/shellcuts.deb && sudo dpkg -i shellcuts.deb + wget https://github.com/tgsachse/shellcuts/archive/v1.2.2.tar.gz -P /tmp + tar xzf /tmp/v1.2.2.tar.gz -C /tmp/shellcuts + python3 /tmp/shellcuts/install.py -**METHOD 2:** Install with wget and dnf +**METHOD 2:** Install with ``git`` -RedHat-based machines (Fedora, CentOS, etc) should install using this method! DNF support is on the way. Run the following command: +This method requires that you have ``git`` installed on your machine. Run these commands: :: - $ wget https://github.com/tgsachse/shellcuts/releases/download/v1.2.2/shellcuts-1.2.2-1.fc27.noarch.rpm && sudo dnf install shellcuts-1.2.2-1.fc27.noarch.rpm + git clone https://www.github.com/tgsachse/shellcuts.git /tmp/shellcuts + python3 /tmp/shellcuts/install.py + +**METHOD 3:** Install manually + +Download Shellcuts from `this link`_ and unzip it using ``tar`` or any other decompression software. Next, navigate into the decompressed files and run the ``install.py`` script using this command: +:: + python3 install.py usage ----- @@ -46,10 +55,10 @@ The core command for Shellcuts is ``sc`` and this program includes the following Feel free to use any of the above or the main ``sc`` command to operate Shellcuts. This program also includes aliases to replicate Bashmarks_ syntax. They are as follows: -- ``s`` to save, equivalent to ``sc -n`` - ``g`` to go, equivalent to ``sc`` -- ``p`` to print, equivalent to ``sc -p`` +- ``s`` to save, equivalent to ``sc -n`` - ``d`` to delete, equivalent to ``sc -d`` +- ``p`` to print, equivalent to ``sc -p`` - ``l`` to list, equivalent to ``sc -l`` flags @@ -75,7 +84,7 @@ Here is a list of all available options/flags: examples -------- -Here are some examples of Shellcuts in action. See this program's man page for more. +Here are some examples of Shellcuts in action. See this program's manual page for more. :: $ pwd # Show current directory /home/tgsachse/Downloads @@ -105,3 +114,4 @@ Here are some examples of Shellcuts in action. See this program's man page for m .. _Bashmarks: https://www.github.com/huyng/bashmarks .. _`Python 3`: https://www.python.org +.. _`this link`: https://github.com/tgsachse/shellcuts/archive/v1.2.2.tar.gz From 20c18c333c96e104f30177e2720372d318275850 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Fri, 31 Aug 2018 12:04:42 -0400 Subject: [PATCH 29/35] prevent naming conflicts --- shells/bash/bashrc.example | 6 +++--- shells/bash/controller.sh | 8 ++++---- shells/fish/config.fish.example | 6 +++--- shells/fish/controller.fish | 8 ++++---- shells/zsh/controller.sh | 8 ++++---- shells/zsh/zshrc.example | 6 +++--- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/shells/bash/bashrc.example b/shells/bash/bashrc.example index c874cc8..09aa74e 100644 --- a/shells/bash/bashrc.example +++ b/shells/bash/bashrc.example @@ -1,5 +1,5 @@ # Load Shellcuts controller file if it exists. -CONTROLLER="~/.shellcuts/config/bash/controller.sh" -if [ -f $CONTROLLER ]; then - . $CONTROLLER +SHELLCUTS_CONTROLLER="~/.shellcuts/config/bash/controller.sh" +if [ -f $SHELLCUTS_CONTROLLER ]; then + . $SHELLCUTS_CONTROLLER fi diff --git a/shells/bash/controller.sh b/shells/bash/controller.sh index 8279967..d80cf58 100644 --- a/shells/bash/controller.sh +++ b/shells/bash/controller.sh @@ -1,15 +1,15 @@ # Part of Shellcuts by Tgsachse. -HANDLER="~/.shellcuts/binaries/shellcuts.pyc" -PLUGINS="~/.shellcuts/shells/bash/plugins/*" +SHELLCUTS_HANDLER="~/.shellcuts/binaries/shellcuts.pyc" +SHELLCUTS_PLUGINS="~/.shellcuts/shells/bash/plugins/*" # Get a command from the Python handler, based on # the given inputs, then execute that command. function sc { - eval "$(python3 $HANDLER $1 $2)" + eval "$(python3 $SHELLCUTS_HANDLER $1 $2)" } # Load all shell plugins. -for FILE in $PLUGINS; do +for FILE in $SHELLCUTS_PLUGINS; do . $FILE done diff --git a/shells/fish/config.fish.example b/shells/fish/config.fish.example index ae408f9..7f0dd3f 100644 --- a/shells/fish/config.fish.example +++ b/shells/fish/config.fish.example @@ -1,5 +1,5 @@ # Load Shellcuts controller file if it exists. -set CONTROLLER "~/.shellcuts/config/fish/controller.fish" -if test -e $CONTROLLER - . $CONTROLLER +set SHELLCUTS_CONTROLLER "~/.shellcuts/config/fish/controller.fish" +if test -e $SHELLCUTS_CONTROLLER + . $SHELLCUTS_CONTROLLER end diff --git a/shells/fish/controller.fish b/shells/fish/controller.fish index 69cdd26..4b1bd6d 100644 --- a/shells/fish/controller.fish +++ b/shells/fish/controller.fish @@ -1,17 +1,17 @@ # Part of Shellcuts by Tgsachse. -set HANDLER "~/.shellcuts/binaries/shellcuts.pyc" -set PLUGINS "~/.shellcuts/shells/fish/plugins/*" +set SHELLCUTS_HANDLER "~/.shellcuts/binaries/shellcuts.pyc" +set SHELLCUTS_PLUGINS "~/.shellcuts/shells/fish/plugins/*" # Get a command from the Python handler, based on # the given inputs, then execute that command. function sc set -l IFS - eval (python3 $HANDLER $argv) + eval (python3 $SHELLCUTS_HANDLER $argv) set -e IFS end # Load all shell plugins. -for FILE in $PLUGINS +for FILE in $SHELLCUTS_PLUGINS . $FILE end diff --git a/shells/zsh/controller.sh b/shells/zsh/controller.sh index bbb9201..a3a210a 100644 --- a/shells/zsh/controller.sh +++ b/shells/zsh/controller.sh @@ -1,15 +1,15 @@ # Part of Shellcuts by Tgsachse. -HANDLER="~/.shellcuts/binary/shellcuts.pyc" -PLUGINS="~/.shellcuts/config/zsh/plugins/*" +SHELLCUTS_HANDLER="~/.shellcuts/binary/shellcuts.pyc" +SHELLCUTS_PLUGINS="~/.shellcuts/config/zsh/plugins/*" # Get a command from the Python handler, based on # the given inputs, then execute that command. function sc { - eval "$(python3 $HANDLER $1 $2)" + eval "$(python3 $SHELLCUTS_HANDLER $1 $2)" } # Load all shell plugins. -for FILE in $PLUGINS; do +for FILE in $SHELLCUTS_PLUGINS; do . $FILE done diff --git a/shells/zsh/zshrc.example b/shells/zsh/zshrc.example index c874cc8..09aa74e 100644 --- a/shells/zsh/zshrc.example +++ b/shells/zsh/zshrc.example @@ -1,5 +1,5 @@ # Load Shellcuts controller file if it exists. -CONTROLLER="~/.shellcuts/config/bash/controller.sh" -if [ -f $CONTROLLER ]; then - . $CONTROLLER +SHELLCUTS_CONTROLLER="~/.shellcuts/config/bash/controller.sh" +if [ -f $SHELLCUTS_CONTROLLER ]; then + . $SHELLCUTS_CONTROLLER fi From eea2a518d99531b1817d4f36b664b1f5e87cb295 Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Fri, 31 Aug 2018 12:35:08 -0400 Subject: [PATCH 30/35] space for cleaner configs --- shells/bash/bashrc.example | 1 + shells/fish/config.fish.example | 1 + shells/zsh/zshrc.example | 1 + 3 files changed, 3 insertions(+) diff --git a/shells/bash/bashrc.example b/shells/bash/bashrc.example index 09aa74e..4719cf3 100644 --- a/shells/bash/bashrc.example +++ b/shells/bash/bashrc.example @@ -1,3 +1,4 @@ + # Load Shellcuts controller file if it exists. SHELLCUTS_CONTROLLER="~/.shellcuts/config/bash/controller.sh" if [ -f $SHELLCUTS_CONTROLLER ]; then diff --git a/shells/fish/config.fish.example b/shells/fish/config.fish.example index 7f0dd3f..746559c 100644 --- a/shells/fish/config.fish.example +++ b/shells/fish/config.fish.example @@ -1,3 +1,4 @@ + # Load Shellcuts controller file if it exists. set SHELLCUTS_CONTROLLER "~/.shellcuts/config/fish/controller.fish" if test -e $SHELLCUTS_CONTROLLER diff --git a/shells/zsh/zshrc.example b/shells/zsh/zshrc.example index 09aa74e..4719cf3 100644 --- a/shells/zsh/zshrc.example +++ b/shells/zsh/zshrc.example @@ -1,3 +1,4 @@ + # Load Shellcuts controller file if it exists. SHELLCUTS_CONTROLLER="~/.shellcuts/config/bash/controller.sh" if [ -f $SHELLCUTS_CONTROLLER ]; then From d25663c4579602da09444fb2edeb4adecdf0db6f Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Fri, 31 Aug 2018 16:57:10 -0400 Subject: [PATCH 31/35] shell variables are weird --- install.py | 39 ++++++++++++++++++++++++++++++++- shells/bash/bashrc.example | 5 ++--- shells/bash/controller.sh | 7 ++---- shells/fish/config.fish.example | 5 ++--- shells/fish/controller.fish | 7 ++---- shells/zsh/controller.sh | 7 ++---- shells/zsh/zshrc.example | 5 ++--- 7 files changed, 50 insertions(+), 25 deletions(-) diff --git a/install.py b/install.py index 09e83fa..2708905 100644 --- a/install.py +++ b/install.py @@ -11,6 +11,16 @@ SOURCE_DIRS = ('docs', 'source', 'shells') COMPILE_PATTERN = r'^(?P[_a-z]+)\.[\-a-z0-9]+\.pyc$' +SHELL_CONFIGS = { + 'zsh' : Path('~/.zshrc').expanduser(), + 'bash' : Path('~/.bashrc').expanduser(), + 'fish' : Path('~/.config/fish/config.fish').expanduser(), +} +SHELL_EXAMPLES = { + 'zsh' : Path('~/.shellcuts/shells/zsh/zshrc.example').expanduser(), + 'bash' : Path('~/.shellcuts/shells/bash/bashrc.example').expanduser(), + 'fish' : Path('~/.shellcuts/shells/fish/config.fish.example').expanduser(), +} DESTINATION_DIRS = { 'shellcuts' : Path('~/.shellcuts').expanduser(), 'docs' : Path('~/.shellcuts/docs').expanduser(), @@ -21,17 +31,22 @@ 'core' : Path('~/.shellcuts/binaries/core').expanduser(), } +# remove old installations shutil.rmtree(str(DESTINATION_DIRS['shellcuts'])) +# copy directories DESTINATION_DIRS['shellcuts'].mkdir(parents=True) for directory in SOURCE_DIRS: shutil.copytree(Path(directory), DESTINATION_DIRS[directory]) +# make any directories we missed in copying for directory in DESTINATION_DIRS.keys(): DESTINATION_DIRS[directory].mkdir(exist_ok=True) +# compile everything compileall.compile_dir(str(DESTINATION_DIRS['source'])) +# move bytecode to binaries top_cache = DESTINATION_DIRS['source'] / CACHE for binary in top_cache.iterdir(): binary_match = re.match(COMPILE_PATTERN, binary.name) @@ -45,4 +60,26 @@ target = Path(binary_match.group('name') + '.pyc') binary.replace(DESTINATION_DIRS['binaries'] / CORE / target) core_cache.rmdir() - + +# add configs as needed. +for shell in SHELLS: + if SHELL_CONFIGS[shell].exists(): + with open(SHELL_EXAMPLES[shell], 'r') as f: + new_lines = f.readlines() + + with open(SHELL_CONFIGS[shell], 'r') as f: + shell_lines = f.readlines() + + for new_line in new_lines: + if new_line not in shell_lines: + missing_new_lines = True + break + else: + missing_new_lines = False + + print(shell, missing_new_lines) + if missing_new_lines: + with open(SHELL_CONFIGS[shell], 'a') as f: + for new_line in new_lines: + f.write(new_line) + diff --git a/shells/bash/bashrc.example b/shells/bash/bashrc.example index 4719cf3..51a8a34 100644 --- a/shells/bash/bashrc.example +++ b/shells/bash/bashrc.example @@ -1,6 +1,5 @@ # Load Shellcuts controller file if it exists. -SHELLCUTS_CONTROLLER="~/.shellcuts/config/bash/controller.sh" -if [ -f $SHELLCUTS_CONTROLLER ]; then - . $SHELLCUTS_CONTROLLER +if [ -f $HOME/.shellcuts/shells/bash/controller.sh ]; then + . $HOME/.shellcuts/shells/bash/controller.sh fi diff --git a/shells/bash/controller.sh b/shells/bash/controller.sh index d80cf58..fcfadd8 100644 --- a/shells/bash/controller.sh +++ b/shells/bash/controller.sh @@ -1,15 +1,12 @@ # Part of Shellcuts by Tgsachse. -SHELLCUTS_HANDLER="~/.shellcuts/binaries/shellcuts.pyc" -SHELLCUTS_PLUGINS="~/.shellcuts/shells/bash/plugins/*" - # Get a command from the Python handler, based on # the given inputs, then execute that command. function sc { - eval "$(python3 $SHELLCUTS_HANDLER $1 $2)" + eval "$(python3 $HOME/.shellcuts/binaries/shellcuts.pyc $1 $2)" } # Load all shell plugins. -for FILE in $SHELLCUTS_PLUGINS; do +for FILE in $HOME/.shellcuts/shells/bash/plugins/*; do . $FILE done diff --git a/shells/fish/config.fish.example b/shells/fish/config.fish.example index 746559c..7457e29 100644 --- a/shells/fish/config.fish.example +++ b/shells/fish/config.fish.example @@ -1,6 +1,5 @@ # Load Shellcuts controller file if it exists. -set SHELLCUTS_CONTROLLER "~/.shellcuts/config/fish/controller.fish" -if test -e $SHELLCUTS_CONTROLLER - . $SHELLCUTS_CONTROLLER +if test -e $HOME/.shellcuts/shells/fish/controller.fish + . $HOME/.shellcuts/shells/fish/controller.fish end diff --git a/shells/fish/controller.fish b/shells/fish/controller.fish index 4b1bd6d..d962a40 100644 --- a/shells/fish/controller.fish +++ b/shells/fish/controller.fish @@ -1,17 +1,14 @@ # Part of Shellcuts by Tgsachse. -set SHELLCUTS_HANDLER "~/.shellcuts/binaries/shellcuts.pyc" -set SHELLCUTS_PLUGINS "~/.shellcuts/shells/fish/plugins/*" - # Get a command from the Python handler, based on # the given inputs, then execute that command. function sc set -l IFS - eval (python3 $SHELLCUTS_HANDLER $argv) + eval (python3 $HOME/.shellcuts/binaries/shellcuts.pyc $argv) set -e IFS end # Load all shell plugins. -for FILE in $SHELLCUTS_PLUGINS +for FILE in $HOME/.shellcuts/shells/fish/plugins/* . $FILE end diff --git a/shells/zsh/controller.sh b/shells/zsh/controller.sh index a3a210a..cf8b2eb 100644 --- a/shells/zsh/controller.sh +++ b/shells/zsh/controller.sh @@ -1,15 +1,12 @@ # Part of Shellcuts by Tgsachse. -SHELLCUTS_HANDLER="~/.shellcuts/binary/shellcuts.pyc" -SHELLCUTS_PLUGINS="~/.shellcuts/config/zsh/plugins/*" - # Get a command from the Python handler, based on # the given inputs, then execute that command. function sc { - eval "$(python3 $SHELLCUTS_HANDLER $1 $2)" + eval "$(python3 $HOME/.shellcuts/binaries/shellcuts.pyc $1 $2)" } # Load all shell plugins. -for FILE in $SHELLCUTS_PLUGINS; do +for FILE in $HOME/.shellcuts/shells/zsh/plugins/*; do . $FILE done diff --git a/shells/zsh/zshrc.example b/shells/zsh/zshrc.example index 4719cf3..f768095 100644 --- a/shells/zsh/zshrc.example +++ b/shells/zsh/zshrc.example @@ -1,6 +1,5 @@ # Load Shellcuts controller file if it exists. -SHELLCUTS_CONTROLLER="~/.shellcuts/config/bash/controller.sh" -if [ -f $SHELLCUTS_CONTROLLER ]; then - . $SHELLCUTS_CONTROLLER +if [ -f $HOME/.shellcuts/shells/zsh/controller.sh ]; then + . $HOME/.shellcuts/shells/zsh/controller.sh fi From 75f626abf5aeea19eb0d1234be1c356d3d4bf4bd Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Fri, 31 Aug 2018 17:29:47 -0400 Subject: [PATCH 32/35] save old shellcuts --- install.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/install.py b/install.py index 2708905..9fc5621 100644 --- a/install.py +++ b/install.py @@ -9,7 +9,9 @@ CACHE = '__pycache__' SHELLS = ('zsh', 'fish', 'bash') SOURCE_DIRS = ('docs', 'source', 'shells') +TEMPORARY_JSON = Path('/tmp/shellcuts.json.temp') COMPILE_PATTERN = r'^(?P[_a-z]+)\.[\-a-z0-9]+\.pyc$' +SHELLCUTS_JSON = Path('~/.shellcuts/data/shellcuts.json').expanduser() SHELL_CONFIGS = { 'zsh' : Path('~/.zshrc').expanduser(), @@ -31,6 +33,10 @@ 'core' : Path('~/.shellcuts/binaries/core').expanduser(), } +# Save any existing json file +if SHELLCUTS_JSON.exists(): + SHELLCUTS_JSON.replace(TEMPORARY_JSON) + # remove old installations shutil.rmtree(str(DESTINATION_DIRS['shellcuts'])) @@ -83,3 +89,6 @@ for new_line in new_lines: f.write(new_line) +# if a temporary file was saved, move +if TEMPORARY_JSON.exists(): + TEMPORARY_JSON.replace(SHELLCUTS_JSON) From c7748fb66d73b7fdd4249fc76c69db44fcfdbb3d Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sat, 1 Sep 2018 14:15:07 -0400 Subject: [PATCH 33/35] final comments on existing code --- docs/CHANGES.txt | 6 +++--- docs/SHELLCUTS.man | 2 +- docs/VERSION.txt | 2 +- install.py | 36 ++++++++++++++++++++++++--------- shells/bash/bashrc.example | 2 +- shells/fish/config.fish.example | 2 +- shells/zsh/zshrc.example | 2 +- source/core/commander.py | 21 ++++++++++--------- source/core/jsonary.py | 4 ++-- source/core/utilities.py | 8 ++++---- source/shellcuts.py | 3 ++- 11 files changed, 54 insertions(+), 34 deletions(-) diff --git a/docs/CHANGES.txt b/docs/CHANGES.txt index f9b74e4..2f1616b 100644 --- a/docs/CHANGES.txt +++ b/docs/CHANGES.txt @@ -1,9 +1,9 @@ Changelog for Shellcuts -v1.2.3 - 08/XX/2018 - - Changed storage technology back to SQLite +v1.2.3 - 09/01/2018 + - Changed storage technology back to JSON - Removed bashmarks syntax toggle - - Completely rewrote file organization + - Completely reorganized file structure - Removed deb and rpm support - Removed initialization script - Added installation script diff --git a/docs/SHELLCUTS.man b/docs/SHELLCUTS.man index 32c4ffa..be130a4 100644 --- a/docs/SHELLCUTS.man +++ b/docs/SHELLCUTS.man @@ -1,4 +1,4 @@ -.TH shellcuts 1 "25 May 2018" "1.2.3" +.TH shellcuts 1 "1 September 2018" "1.2.3" .SH NAME shellcuts - directory shortcuts for your shell. diff --git a/docs/VERSION.txt b/docs/VERSION.txt index b3d70ef..64cd83d 100644 --- a/docs/VERSION.txt +++ b/docs/VERSION.txt @@ -2,4 +2,4 @@ AUTHOR: Tiger Sachse LICENSE: GPLv3 VERSION: 1.2.3 INITIAL RELEASE: 12/31/2017 -CURRENT RELEASE: 05/25/2018 +CURRENT RELEASE: 09/01/2018 diff --git a/install.py b/install.py index 9fc5621..6eeb780 100644 --- a/install.py +++ b/install.py @@ -1,4 +1,9 @@ -""" +"""Local installation script for Shellcuts. + +This script should work to install Shellcuts on any system with a Unix-like +file organization scheme (e.g. Linux and macOS). + +Part of Shellcuts by Tiger Sachse. """ import re import shutil @@ -33,26 +38,29 @@ 'core' : Path('~/.shellcuts/binaries/core').expanduser(), } -# Save any existing json file +# add a thing to confirm that all the files exist + +# If a shellcuts JSON file already exists, temporarily move it to save it. if SHELLCUTS_JSON.exists(): SHELLCUTS_JSON.replace(TEMPORARY_JSON) -# remove old installations +# Remove any old installation files. shutil.rmtree(str(DESTINATION_DIRS['shellcuts'])) -# copy directories +# Copy the source directories to the installation location. DESTINATION_DIRS['shellcuts'].mkdir(parents=True) for directory in SOURCE_DIRS: shutil.copytree(Path(directory), DESTINATION_DIRS[directory]) -# make any directories we missed in copying +# Create other destination-only directories that will be used later. for directory in DESTINATION_DIRS.keys(): DESTINATION_DIRS[directory].mkdir(exist_ok=True) -# compile everything +# Compile all of the installed source code. compileall.compile_dir(str(DESTINATION_DIRS['source'])) -# move bytecode to binaries +# Move all compiled top-level files into the binaries folder, then delete the +# compilation cache. top_cache = DESTINATION_DIRS['source'] / CACHE for binary in top_cache.iterdir(): binary_match = re.match(COMPILE_PATTERN, binary.name) @@ -60,6 +68,8 @@ binary.replace(DESTINATION_DIRS['binaries'] / target) top_cache.rmdir() +# Move all compiled core package files into the binaries/core folder, then +# delete the compilation cache. core_cache = DESTINATION_DIRS['source'] / CORE / CACHE for binary in core_cache.iterdir(): binary_match = re.match(COMPILE_PATTERN, binary.name) @@ -67,8 +77,12 @@ binary.replace(DESTINATION_DIRS['binaries'] / CORE / target) core_cache.rmdir() -# add configs as needed. +# Append example shell configuration instructions to existing shell +# configuration files. These configuration instructions allow Shellcuts to +# hook into the shell correctly. for shell in SHELLS: + + # Only append to configuration files that exist. if SHELL_CONFIGS[shell].exists(): with open(SHELL_EXAMPLES[shell], 'r') as f: new_lines = f.readlines() @@ -76,6 +90,8 @@ with open(SHELL_CONFIGS[shell], 'r') as f: shell_lines = f.readlines() + # If the current configuration file already has the necessary + # instructions (from a previous install) then don't add them again. for new_line in new_lines: if new_line not in shell_lines: missing_new_lines = True @@ -83,12 +99,12 @@ else: missing_new_lines = False - print(shell, missing_new_lines) + # if the instructions are not in the configuration file, add them. if missing_new_lines: with open(SHELL_CONFIGS[shell], 'a') as f: for new_line in new_lines: f.write(new_line) -# if a temporary file was saved, move +# Move the old shellcuts JSON back into place. if TEMPORARY_JSON.exists(): TEMPORARY_JSON.replace(SHELLCUTS_JSON) diff --git a/shells/bash/bashrc.example b/shells/bash/bashrc.example index 51a8a34..5c35c5a 100644 --- a/shells/bash/bashrc.example +++ b/shells/bash/bashrc.example @@ -1,5 +1,5 @@ -# Load Shellcuts controller file if it exists. +# Load the Shellcuts controller file if it exists. if [ -f $HOME/.shellcuts/shells/bash/controller.sh ]; then . $HOME/.shellcuts/shells/bash/controller.sh fi diff --git a/shells/fish/config.fish.example b/shells/fish/config.fish.example index 7457e29..6026c63 100644 --- a/shells/fish/config.fish.example +++ b/shells/fish/config.fish.example @@ -1,5 +1,5 @@ -# Load Shellcuts controller file if it exists. +# Load the Shellcuts controller file if it exists. if test -e $HOME/.shellcuts/shells/fish/controller.fish . $HOME/.shellcuts/shells/fish/controller.fish end diff --git a/shells/zsh/zshrc.example b/shells/zsh/zshrc.example index f768095..c67bf12 100644 --- a/shells/zsh/zshrc.example +++ b/shells/zsh/zshrc.example @@ -1,5 +1,5 @@ -# Load Shellcuts controller file if it exists. +# Load the Shellcuts controller file if it exists. if [ -f $HOME/.shellcuts/shells/zsh/controller.sh ]; then . $HOME/.shellcuts/shells/zsh/controller.sh fi diff --git a/source/core/commander.py b/source/core/commander.py index db0b562..f670f86 100644 --- a/source/core/commander.py +++ b/source/core/commander.py @@ -1,11 +1,10 @@ -"""Print commands associated with Shellcuts' options. +"""Print shell commands based on given arguments. -All printed shell commands are captured by the shell script that calls -shellcuts.py, where they are then executed by the shell script. This is -necessary because the 'cd' command moves the user only if it is called by the -user (or a shell script that the user has directly invoked). Any calls to 'cd', -or even system calls to 'chdir', will change the working directory of the -script but not the user. +All printed commands are captured and evaluated by the shell script that calls +this file. This method is necessary because the 'cd' command moves the user +only if it is called by the user (or a shell script that the user has directly +invoked). Any calls in Python programs to 'cd', or even system calls to 'chdir', +will change the working directory of the program but not the user. Part of Shellcuts by Tiger Sachse. """ @@ -17,14 +16,18 @@ class Commander: """A class that holds all possible commands for Shellcuts.""" def __init__(self, version_file, shellcuts_file, manual_file): - """Initialize with external, saved information.""" + """Initialize with external, saved information. + + The jsonary loads shellcuts from an external JSON file into a + dictionary structure. + """ self.manual_file = manual_file self.version_file = version_file self.shellcuts = Jsonary(shellcuts_file) def execute(self, arguments): - """Call the appropriate function based on the given arguments.""" + """Call the appropriate function based on given arguments.""" if arguments.new: self.new(arguments.new) elif arguments.delete: diff --git a/source/core/jsonary.py b/source/core/jsonary.py index e2708eb..dc1c1a8 100644 --- a/source/core/jsonary.py +++ b/source/core/jsonary.py @@ -1,6 +1,6 @@ """An iterable JSON dictionary with extra safety. -In the future, this class will be updated to extend dict directly, thus +In the future, this class may be updated to extend dict directly, thus eliminating some redundancy and inefficiencies. Part of Shellcuts by Tiger Sachse. @@ -56,7 +56,7 @@ def __contains__(self, key): def __generate_items(self): - """Generate all items in the list.""" + """Generate all items in the jsonary.""" for key in self.__contents.keys(): yield key, self.__contents[key] diff --git a/source/core/utilities.py b/source/core/utilities.py index b21d378..d367905 100644 --- a/source/core/utilities.py +++ b/source/core/utilities.py @@ -26,20 +26,20 @@ def throw_help(): """Print a help message.""" script = ( 'Shellcuts usage: \$ sc [-f] ', - '--------------------------------------------------------', + '---------------------------------------------------', 'Create a new shellcut for the current directory:', ' \$ sc -n example', '', - 'Jump to that location from anywhere else on the system:', + 'Jump to the example location:', ' \$ sc example', '', - 'Remove that shellcut:', + 'Remove the example shellcut:', ' \$ sc -d example', '', 'List all available shellcuts:', ' \$ sc -l', '', - 'See the manpage for lots more information and examples:', + 'See the manpage for more information and examples:', ' \$ sc --man') command = 'printf "' diff --git a/source/shellcuts.py b/source/shellcuts.py index 0b9a3ad..ee95688 100644 --- a/source/shellcuts.py +++ b/source/shellcuts.py @@ -20,7 +20,8 @@ parser = Parser() parser.parse_arguments() -# If there are no unknowns after the initial parse then it must be a 'go' command. +# If there are no unknowns after the initial parse then it must +# be a 'go' command. if len(parser.unknown) <= 0: commander.go(parser.arguments.name) exit(0) From b11fcd49fdb25aa80f5c851f33245e5af3c0d43c Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sat, 1 Sep 2018 15:08:34 -0400 Subject: [PATCH 34/35] final build for 1.2.3 --- install.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/install.py b/install.py index 6eeb780..dbf517e 100644 --- a/install.py +++ b/install.py @@ -38,16 +38,34 @@ 'core' : Path('~/.shellcuts/binaries/core').expanduser(), } -# add a thing to confirm that all the files exist +print('Thank you for choosing Shellcuts.') +print('=================================\n') +print('Beginning installation.') + +missing_dirs = False +for directory in SOURCE_DIRS: + if not Path(directory).exists(): + print('Missing directory \'{0}\'...'.format(str(directory))) + missing_dirs = True + +if missing_dirs: + print('You appear to be missing some source directories, so the installation') + print('cannot continue. Please follow the installation instructions located') + print('at www.github.com/tgsachse/shellcuts') + print('Aborting script.') + exit(0) # If a shellcuts JSON file already exists, temporarily move it to save it. if SHELLCUTS_JSON.exists(): + print('Saving existing shellcuts...') SHELLCUTS_JSON.replace(TEMPORARY_JSON) # Remove any old installation files. +print('Removing old installations...') shutil.rmtree(str(DESTINATION_DIRS['shellcuts'])) # Copy the source directories to the installation location. +print('Making directory structure and copying source...') DESTINATION_DIRS['shellcuts'].mkdir(parents=True) for directory in SOURCE_DIRS: shutil.copytree(Path(directory), DESTINATION_DIRS[directory]) @@ -57,6 +75,7 @@ DESTINATION_DIRS[directory].mkdir(exist_ok=True) # Compile all of the installed source code. +print('\nCompiling from source...') compileall.compile_dir(str(DESTINATION_DIRS['source'])) # Move all compiled top-level files into the binaries folder, then delete the @@ -77,6 +96,9 @@ binary.replace(DESTINATION_DIRS['binaries'] / CORE / target) core_cache.rmdir() +print() +need_extra_space = False + # Append example shell configuration instructions to existing shell # configuration files. These configuration instructions allow Shellcuts to # hook into the shell correctly. @@ -101,6 +123,8 @@ # if the instructions are not in the configuration file, add them. if missing_new_lines: + need_extra_space = True + print('Appending hook for {0} shell...'.format(shell)) with open(SHELL_CONFIGS[shell], 'a') as f: for new_line in new_lines: f.write(new_line) @@ -108,3 +132,8 @@ # Move the old shellcuts JSON back into place. if TEMPORARY_JSON.exists(): TEMPORARY_JSON.replace(SHELLCUTS_JSON) + +if need_extra_space: + print() +print('Installation complete!') +print('Restart your terminal to apply changes.') From b87089b1e6054a2856605e8e2ae925f89331aeca Mon Sep 17 00:00:00 2001 From: Tiger Sachse Date: Sat, 1 Sep 2018 16:30:52 -0400 Subject: [PATCH 35/35] final working links for v1.2.3 --- docs/README.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/README.rst b/docs/README.rst index 6da785f..f2ca606 100644 --- a/docs/README.rst +++ b/docs/README.rst @@ -21,22 +21,22 @@ Planned features include: installation ------------ -Shellcuts is easy to install and doesn't require any special privileges! To run, you need the latest version of `Python 3`_ installed, as well as a supported shell. Linux users almost certainly have both of these but macOS users may need to install Python3. Windows is not supported by Shellcuts. +Shellcuts is easy to install and doesn't require any special privileges! To run, you need the latest version of `Python 3`_ installed, as well as a supported shell. Linux users almost certainly have both of these but macOS users may need to install Python3. Shellcuts is not available for Windows shell or PowerShell. **METHOD 1:** Install with ``wget`` and ``tar`` The following commands will work for nearly everyone. If it does not work for you, try the next method. :: - wget https://github.com/tgsachse/shellcuts/archive/v1.2.2.tar.gz -P /tmp - tar xzf /tmp/v1.2.2.tar.gz -C /tmp/shellcuts - python3 /tmp/shellcuts/install.py + cd /tmp && wget https://github.com/tgsachse/shellcuts/archive/v1.2.3.tar.gz + tar -xzf v1.2.3.tar.gz && cd shellcuts-1.2.3 + python3 install.py **METHOD 2:** Install with ``git`` This method requires that you have ``git`` installed on your machine. Run these commands: :: - git clone https://www.github.com/tgsachse/shellcuts.git /tmp/shellcuts - python3 /tmp/shellcuts/install.py + cd /tmp && git clone https://www.github.com/tgsachse/shellcuts.git + cd shellcuts && python3 install.py **METHOD 3:** Install manually @@ -114,4 +114,4 @@ Here are some examples of Shellcuts in action. See this program's manual page fo .. _Bashmarks: https://www.github.com/huyng/bashmarks .. _`Python 3`: https://www.python.org -.. _`this link`: https://github.com/tgsachse/shellcuts/archive/v1.2.2.tar.gz +.. _`this link`: https://github.com/tgsachse/shellcuts/archive/v1.2.3.tar.gz