repo fix first commit
This commit is contained in:
parent
4ea459b6e7
commit
bd32153557
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Normalize EOL for all files that Git considers text files.
|
||||||
|
* text=auto eol=lf
|
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# ---> Godot
|
||||||
|
# Godot 4+ specific ignores
|
||||||
|
.godot/
|
||||||
|
/android/
|
||||||
|
|
||||||
|
# Godot-specific ignores
|
||||||
|
.import/
|
||||||
|
export.cfg
|
||||||
|
export_presets.cfg
|
||||||
|
|
||||||
|
# Imported translations (automatically generated from CSV files)
|
||||||
|
*.translation
|
||||||
|
|
||||||
|
# Mono-specific ignores
|
||||||
|
.mono/
|
||||||
|
data_*/
|
||||||
|
mono_crash.*.json
|
||||||
|
|
||||||
|
|
1702
addons/godot-vim/godot-vim.gd
Normal file
1702
addons/godot-vim/godot-vim.gd
Normal file
File diff suppressed because it is too large
Load Diff
179
addons/godot-vim/icon.svg
Normal file
179
addons/godot-vim/icon.svg
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="409.99237"
|
||||||
|
height="551.82874"
|
||||||
|
version="1.1"
|
||||||
|
id="svg3763"
|
||||||
|
sodipodi:docname="iconsvg.svg"
|
||||||
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||||
|
<defs
|
||||||
|
id="defs3767" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="3840"
|
||||||
|
inkscape:window-height="2089"
|
||||||
|
id="namedview3765"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.88187112"
|
||||||
|
inkscape:cx="-391.82984"
|
||||||
|
inkscape:cy="385.67966"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg3763" />
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
id="g3845"
|
||||||
|
transform="matrix(4.3859694,0,0,4.3859694,-75.706218,-87.385926)">
|
||||||
|
<g
|
||||||
|
id="g3823"
|
||||||
|
transform="matrix(0.10073078,0,0,0.10073078,12.425923,2.256365)"
|
||||||
|
style="stroke-width:9.92745972">
|
||||||
|
<path
|
||||||
|
id="path3807"
|
||||||
|
d="m 0,0 c 0,0 -0.325,1.994 -0.515,1.976 l -36.182,-3.491 c -2.879,-0.278 -5.115,-2.574 -5.317,-5.459 l -0.994,-14.247 -27.992,-1.997 -1.904,12.912 c -0.424,2.872 -2.932,5.037 -5.835,5.037 h -38.188 c -2.902,0 -5.41,-2.165 -5.834,-5.037 l -1.905,-12.912 -27.992,1.997 -0.994,14.247 c -0.202,2.886 -2.438,5.182 -5.317,5.46 l -36.2,3.49 c -0.187,0.018 -0.324,-1.978 -0.511,-1.978 l -0.049,-7.83 30.658,-4.944 1.004,-14.374 c 0.203,-2.91 2.551,-5.263 5.463,-5.472 l 38.551,-2.75 c 0.146,-0.01 0.29,-0.016 0.434,-0.016 2.897,0 5.401,2.166 5.825,5.038 l 1.959,13.286 h 28.005 l 1.959,-13.286 c 0.423,-2.871 2.93,-5.037 5.831,-5.037 0.142,0 0.284,0.005 0.423,0.015 l 38.556,2.75 c 2.911,0.209 5.26,2.562 5.463,5.472 l 1.003,14.374 30.645,4.966 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#ffffff"
|
||||||
|
transform="matrix(4.162611,0,0,-4.162611,919.24059,771.67186)" />
|
||||||
|
<path
|
||||||
|
id="path3809"
|
||||||
|
transform="matrix(4.162611,0,0,-4.162611,104.69892,525.90697)"
|
||||||
|
d="m 0,0 v -47.514 -6.035 -5.492 c 0.108,-0.001 0.216,-0.005 0.323,-0.015 l 36.196,-3.49 c 1.896,-0.183 3.382,-1.709 3.514,-3.609 l 1.116,-15.978 31.574,-2.253 2.175,14.747 c 0.282,1.912 1.922,3.329 3.856,3.329 h 38.188 c 1.933,0 3.573,-1.417 3.855,-3.329 l 2.175,-14.747 31.575,2.253 1.115,15.978 c 0.133,1.9 1.618,3.425 3.514,3.609 l 36.182,3.49 c 0.107,0.01 0.214,0.014 0.322,0.015 v 4.711 l 0.015,0.005 V 0 c 5.09692,6.4164715 9.92323,13.494208 13.621,19.449 -5.651,9.62 -12.575,18.217 -19.976,26.182 -6.864,-3.455 -13.531,-7.369 -19.828,-11.534 -3.151,3.132 -6.7,5.694 -10.186,8.372 -3.425,2.751 -7.285,4.768 -10.946,7.118 1.09,8.117 1.629,16.108 1.846,24.448 -9.446,4.754 -19.519,7.906 -29.708,10.17 -4.068,-6.837 -7.788,-14.241 -11.028,-21.479 -3.842,0.642 -7.702,0.88 -11.567,0.926 v 0.006 c -0.027,0 -0.052,-0.006 -0.075,-0.006 -0.024,0 -0.049,0.006 -0.073,0.006 V 63.652 C 93.903,63.606 90.046,63.368 86.203,62.726 82.965,69.964 79.247,77.368 75.173,84.205 64.989,81.941 54.915,78.789 45.47,74.035 45.686,65.695 46.225,57.704 47.318,49.587 43.65,47.237 39.795,45.22 36.369,42.469 32.888,39.791 29.333,37.229 26.181,34.097 19.884,38.262 13.219,42.176 6.353,45.631 -1.048,37.666 -7.968,29.069 -13.621,19.449 -9.1783421,12.475308 -4.4130298,5.4661124 0,0 Z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#478cbf" />
|
||||||
|
<path
|
||||||
|
id="path3811"
|
||||||
|
d="m 0,0 -1.121,-16.063 c -0.135,-1.936 -1.675,-3.477 -3.611,-3.616 l -38.555,-2.751 c -0.094,-0.007 -0.188,-0.01 -0.281,-0.01 -1.916,0 -3.569,1.406 -3.852,3.33 l -2.211,14.994 H -81.09 l -2.211,-14.994 c -0.297,-2.018 -2.101,-3.469 -4.133,-3.32 l -38.555,2.751 c -1.936,0.139 -3.476,1.68 -3.611,3.616 L -130.721,0 -163.268,3.138 c 0.015,-3.498 0.06,-7.33 0.06,-8.093 0,-34.374 43.605,-50.896 97.781,-51.086 h 0.066 0.067 c 54.176,0.19 97.766,16.712 97.766,51.086 0,0.777 0.047,4.593 0.063,8.093 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#478cbf"
|
||||||
|
transform="matrix(4.162611,0,0,-4.162611,784.07144,817.24284)" />
|
||||||
|
<path
|
||||||
|
id="path3813"
|
||||||
|
transform="matrix(4.162611,0,0,-4.162611,389.21484,625.67104)"
|
||||||
|
d="m 0,0 c 0,-12.052 -9.765,-21.815 -21.813,-21.815 -12.042,0 -21.81,9.763 -21.81,21.815 0,12.044 9.768,21.802 21.81,21.802 C -9.765,21.802 0,12.044 0,0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
id="path3815"
|
||||||
|
transform="matrix(4.162611,0,0,-4.162611,367.36686,631.05679)"
|
||||||
|
d="m 0,0 c 0,-7.994 -6.479,-14.473 -14.479,-14.473 -7.996,0 -14.479,6.479 -14.479,14.473 0,7.994 6.483,14.479 14.479,14.479 C -6.479,14.479 0,7.994 0,0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#414042" />
|
||||||
|
<path
|
||||||
|
id="path3817"
|
||||||
|
transform="matrix(4.162611,0,0,-4.162611,511.99336,724.73954)"
|
||||||
|
d="m 0,0 c -3.878,0 -7.021,2.858 -7.021,6.381 v 20.081 c 0,3.52 3.143,6.381 7.021,6.381 3.878,0 7.028,-2.861 7.028,-6.381 V 6.381 C 7.028,2.858 3.878,0 0,0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
id="path3819"
|
||||||
|
transform="matrix(4.162611,0,0,-4.162611,634.78706,625.67104)"
|
||||||
|
d="m 0,0 c 0,-12.052 9.765,-21.815 21.815,-21.815 12.041,0 21.808,9.763 21.808,21.815 0,12.044 -9.767,21.802 -21.808,21.802 C 9.765,21.802 0,12.044 0,0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
id="path3821"
|
||||||
|
transform="matrix(4.162611,0,0,-4.162611,656.64056,631.05679)"
|
||||||
|
d="m 0,0 c 0,-7.994 6.477,-14.473 14.471,-14.473 8.002,0 14.479,6.479 14.479,14.473 0,7.994 -6.477,14.479 -14.479,14.479 C 6.477,14.479 0,7.994 0,0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#414042" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="layer1"
|
||||||
|
transform="matrix(0.7294074,0,0,0.7294074,-45.912295,80.565241)">
|
||||||
|
<g
|
||||||
|
id="g3699"
|
||||||
|
transform="matrix(1.532388,0,0,1.3939671,-54.912136,-41.792396)">
|
||||||
|
<path
|
||||||
|
id="path3650"
|
||||||
|
d="m 114.65715,353.09353 h 47.80701 l 2.91261,3.20613 v 9.83953 l -2.31001,3.09557 h -5.22261 v 48.86596 l 44.99482,-48.86596 h -7.43218 l -2.61131,-3.09557 v -10.39231 l 2.41044,-2.43224 h 48.40962 l 2.41043,2.65335 v 9.72897 L 136.55196,489.29907 h -12.45393 l -3.60484,-2.291 V 368.79254 h -6.03691 l -2.20956,-2.43224 v -10.39231 z"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:8.34521198;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
id="path3640"
|
||||||
|
d="m 162.97227,358.09475 2.6987,-1.5635 -2.76971,-3.04884 h -48.22135 l -2.45013,2.69704 v 10.20187 l 2.71645,2.9902 1.29608,-2.9902 -1.70443,-1.87621 v -7.19212 l 1.27832,-1.2508 h 46.01979 z"
|
||||||
|
style="fill:#478cbf;fill-opacity:1;stroke:#000000;stroke-width:0.41726059px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
d="m 197.06456,355.74729 -1.70266,1.87425 v 6.88137 l 1.49138,1.64168 h 7.87946 v 6.6488 l -52.12379,58.1565 v -64.72321 h 8.66244 l 1.77723,-1.95634 v -6.96346 l -1.64052,-1.39542 h -45.58657 l -1.49138,1.64168 v 7.11394 l 1.51624,1.66904 h 7.92918 v 119.18594 l 1.49138,1.64168 h 9.01043 L 243.50867,363.0938 v -5.47226 l -1.70266,-1.87425 z"
|
||||||
|
id="path3632"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.41726059px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
id="path3646"
|
||||||
|
d="m 123.69629,366.13919 v 119.40096 l 1.40609,1.7689 -1.10266,2.31328 -3.1156,-3.41884 V 369.23476 Z"
|
||||||
|
style="fill:#478cbf;fill-opacity:1;stroke:#000000;stroke-width:0.41726059px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
id="path3644"
|
||||||
|
d="m 115.90579,366.13919 -0.80348,2.87446 h 5.82523 l 3.21391,-2.87446 z"
|
||||||
|
style="fill:#478cbf;fill-opacity:1;stroke:#000000;stroke-width:0.41726059px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
id="path3638"
|
||||||
|
d="m 195.92471,369.36762 1.27833,-2.89248 -1.84647,-1.87621 v -6.41037 l 2.13055,-2.34526 h 44.45738 l 1.70443,2.50161 2.41462,-1.87621 -2.48563,-2.73613 h -47.79524 l -2.37911,2.61887 v 10.28004 l 2.46788,2.56024 m -38.62501,49.36179 -4.64282,12.40054 52.41142,-57.84966 v -6.87942 z"
|
||||||
|
style="fill:#478cbf;fill-opacity:1;stroke:#000000;stroke-width:0.41726059px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
id="path3642"
|
||||||
|
d="m 162.86589,357.7369 2.31001,-1.65835 v 10.06064 l -2.66153,2.92974 h -5.1724 v 49.58456 l -4.72044,12.27178 v -64.78608 h 8.6374 l 1.60696,-1.43724 z"
|
||||||
|
style="fill:#478cbf;fill-opacity:1;stroke:#000000;stroke-width:0.41726059px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
d="m 197.06456,355.74729 -1.70266,1.87425 v 6.88137 l 1.49138,1.64168 h 7.87946 v 6.6488 l -52.12379,58.1565 v -64.72321 h 8.66244 l 1.77723,-1.95634 v -6.96346 l -1.64052,-1.39542 h -45.58657 l -1.49138,1.64168 v 7.11394 l 1.51624,1.66904 h 7.92918 v 119.18594 l 1.49138,1.64168 h 9.01043 L 243.50867,363.0938 v -5.47226 l -1.70266,-1.87425 z"
|
||||||
|
id="path3622"
|
||||||
|
style="fill:#478cbf;fill-opacity:1;stroke:#000000;stroke-width:0.41726059px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
id="path3636"
|
||||||
|
d="m 243.64893,357.78205 2.426,-1.54181 v 9.67203 L 136.04072,489.68148 h -11.68216 l 1.11611,-2.44127 h 8.9483 L 243.5069,363.25432 Z"
|
||||||
|
style="fill:#478cbf;fill-opacity:1;stroke:#000000;stroke-width:0.41726059px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
id="path3652"
|
||||||
|
d="m 204.79746,366.30501 -2.46065,2.8192 h -6.42784 l 1.50652,-2.8192 c 0.0502,0 7.38197,0 7.38197,0 z"
|
||||||
|
style="fill:#478cbf;fill-opacity:1;stroke:#000000;stroke-width:0.41726059px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<g
|
||||||
|
transform="matrix(0.90138601,0,0,0.99222542,-92.530288,-192.23791)"
|
||||||
|
id="g3673">
|
||||||
|
<path
|
||||||
|
style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path3671"
|
||||||
|
d="m 399.78125,560 a 1.2330102,1.2330102 0 0 0 -0.5625,0.28125 l -5.3125,4.5625 A 1.2330102,1.2330102 0 0 0 393.5625,565.375 L 388.25,580.25 a 1.2330102,1.2330102 0 0 0 0.28125,1.28125 l 4.0625,4.0625 a 1.2330102,1.2330102 0 0 0 0.875,0.34375 H 409.875 a 1.2330102,1.2330102 0 0 0 0.875,-0.34375 l 4.28125,-4.3125 a 1.2330102,1.2330102 0 0 0 0.3125,-0.53125 l 4.5625,-15.65625 a 1.2330102,1.2330102 0 0 0 -0.3125,-1.21875 l -3.53125,-3.53125 A 1.2330102,1.2330102 0 0 0 415.1875,560 h -15.15625 a 1.2330102,1.2330102 0 0 0 -0.25,0 z m -30.0625,41.9375 a 1.2330102,1.2330102 0 0 0 -0.9375,0.90625 l -2.03125,8.0625 a 1.2330102,1.2330102 0 0 0 1.1875,1.53125 h 9.65625 l -23.9375,68.34375 a 1.2330102,1.2330102 0 0 0 1.15625,1.625 h 34.84375 a 1.2330102,1.2330102 0 0 0 1.1875,-0.84375 l 2.28125,-7.34375 a 1.2330102,1.2330102 0 0 0 -1.1875,-1.59375 h -7.875 L 407.75,603.5625 a 1.2330102,1.2330102 0 0 0 -1.15625,-1.625 h -36.625 a 1.2330102,1.2330102 0 0 0 -0.25,0 z m 110.875,0.25 a 1.2330102,1.2330102 0 0 0 -0.6875,0.40625 l -7.25,8.1875 H 461.125 l -7.6875,-7.96875 a 1.2330102,1.2330102 0 0 0 -0.875,-0.375 H 425.03125 A 1.2330102,1.2330102 0 0 0 423.875,603.25 l -2.53125,7.5625 a 1.2330102,1.2330102 0 0 0 1.15625,1.625 h 7.375 l -22.9375,67.59375 a 1.2330102,1.2330102 0 0 0 1.15625,1.625 h 29.3125 a 1.2330102,1.2330102 0 0 0 1.15625,-0.8125 l 2.25,-6.59375 a 1.2330102,1.2330102 0 0 0 -1.15625,-1.625 h -5.125 l 14.625,-46.03125 H 475.625 l -16.6875,53.46875 a 1.2330102,1.2330102 0 0 0 1.1875,1.59375 h 28.28125 a 1.2330102,1.2330102 0 0 0 1.125,-0.75 l 2.53125,-6.0625 a 1.2330102,1.2330102 0 0 0 -1.125,-1.6875 h -5.125 l 14.875,-46.8125 h 25.1875 l -16.9375,53.71875 a 1.2330102,1.2330102 0 0 0 1.1875,1.59375 h 31.0625 a 1.2330102,1.2330102 0 0 0 1.15625,-0.78125 l 2.53125,-6.59375 a 1.2330102,1.2330102 0 0 0 -1.15625,-1.65625 h -6.15625 l 18.71875,-60.78125 a 1.2330102,1.2330102 0 0 0 -0.1875,-1.125 l -5.8125,-7.8125 a 1.2330102,1.2330102 0 0 0 -1,-0.46875 H 527.0625 a 1.2330102,1.2330102 0 0 0 -0.90625,0.375 l -7,7.6875 h -12.25 l -7.25,-7.9375 a 1.2330102,1.2330102 0 0 0 -0.90625,-0.375 h -17.90625 a 1.2330102,1.2330102 0 0 0 -0.25,0 z"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
d="m 400.03125,561.21875 -5.3125,4.5625 -5.3125,14.875 4.0625,4.0625 H 409.875 l 4.28125,-4.3125 4.5625,-15.65625 -3.53125,-3.53125 z m -30.0625,41.9375 -2.03125,8.0625 h 11.375 l -24.5,69.96875 h 34.84375 l 2.28125,-7.34375 h -9.59375 l 24.25,-70.6875 z m 110.875,0.25 L 473.25,612 h -12.625 l -8.0625,-8.34375 h -27.53125 l -2.53125,7.5625 h 9.09375 l -23.5,69.21875 h 29.3125 l 2.25,-6.59375 h -6.8125 L 448.25,625.375 h 29.0625 l -17.1875,55.0625 h 28.28125 l 2.53125,-6.0625 h -6.8125 l 15.65625,-49.25 h 27.78125 l -17.4375,55.3125 h 31.0625 l 2.53125,-6.59375 H 535.875 l 19.21875,-62.375 -5.8125,-7.8125 H 527.0625 l -7.34375,8.0625 h -13.375 l -7.59375,-8.3125 z"
|
||||||
|
id="path3665"
|
||||||
|
style="fill:#478cbf;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 14 KiB |
37
addons/godot-vim/icon.svg.import
Normal file
37
addons/godot-vim/icon.svg.import
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://kinglqst816b"
|
||||||
|
path="res://.godot/imported/icon.svg-5744b51718b6a64145ec5798797f7631.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/godot-vim/icon.svg"
|
||||||
|
dest_files=["res://.godot/imported/icon.svg-5744b51718b6a64145ec5798797f7631.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
7
addons/godot-vim/plugin.cfg
Normal file
7
addons/godot-vim/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="godot-vim"
|
||||||
|
description="VIM bindings for godot4"
|
||||||
|
author="Original: Josh N; Forked by Wenqiang Wang"
|
||||||
|
version="4.3.0"
|
||||||
|
script="godot-vim.gd"
|
93
addons/godot_vim/command_line.gd
Normal file
93
addons/godot_vim/command_line.gd
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
extends LineEdit
|
||||||
|
|
||||||
|
const Cursor = preload("res://addons/godot_vim/cursor.gd")
|
||||||
|
const StatusBar = preload("res://addons/godot_vim/status_bar.gd")
|
||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const MODE = Constants.Mode
|
||||||
|
|
||||||
|
const Goto = preload("res://addons/godot_vim/commands/goto.gd")
|
||||||
|
const Find = preload("res://addons/godot_vim/commands/find.gd")
|
||||||
|
|
||||||
|
var code_edit: CodeEdit
|
||||||
|
var cursor: Cursor
|
||||||
|
var status_bar: StatusBar
|
||||||
|
var globals: Dictionary
|
||||||
|
|
||||||
|
var is_paused: bool = false
|
||||||
|
var search_pattern: String = ""
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
placeholder_text = "Enter command..."
|
||||||
|
show()
|
||||||
|
text_submitted.connect(_on_text_submitted)
|
||||||
|
text_changed.connect(_on_text_changed)
|
||||||
|
editable = true
|
||||||
|
|
||||||
|
|
||||||
|
func set_command(cmd: String):
|
||||||
|
text = cmd
|
||||||
|
caret_column = text.length()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_text_changed(cmd: String):
|
||||||
|
if !cmd.begins_with("/"):
|
||||||
|
return
|
||||||
|
# Update search
|
||||||
|
var pattern: String = cmd.substr(1)
|
||||||
|
var rmatch: RegExMatch = globals.vim_plugin.search_regex(
|
||||||
|
code_edit, pattern, cursor.get_caret_pos() + Vector2i.RIGHT
|
||||||
|
)
|
||||||
|
if rmatch == null:
|
||||||
|
code_edit.remove_secondary_carets()
|
||||||
|
return
|
||||||
|
|
||||||
|
var pos: Vector2i = globals.vim_plugin.idx_to_pos(code_edit, rmatch.get_start())
|
||||||
|
if code_edit.get_caret_count() < 2:
|
||||||
|
code_edit.add_caret(pos.y, pos.x)
|
||||||
|
code_edit.select(pos.y, pos.x, pos.y, pos.x + rmatch.get_string().length(), 1)
|
||||||
|
# code_edit.center_viewport_to_caret(1) # why no work, eh?
|
||||||
|
|
||||||
|
code_edit.scroll_vertical = (
|
||||||
|
code_edit.get_scroll_pos_for_line(pos.y) - code_edit.get_visible_line_count() / 2
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func handle_command(cmd: String):
|
||||||
|
if cmd.begins_with("/"):
|
||||||
|
var find = Find.new()
|
||||||
|
find.execute(globals, cmd)
|
||||||
|
return
|
||||||
|
|
||||||
|
if cmd.trim_prefix(":").is_valid_int():
|
||||||
|
var goto = Goto.new()
|
||||||
|
goto.execute(globals, cmd.trim_prefix(":"))
|
||||||
|
return
|
||||||
|
|
||||||
|
if globals.vim_plugin.dispatch(cmd) == OK:
|
||||||
|
set_paused(true)
|
||||||
|
return
|
||||||
|
|
||||||
|
status_bar.display_error('Unknown command: "%s"' % [cmd.trim_prefix(":")])
|
||||||
|
set_paused(true)
|
||||||
|
|
||||||
|
|
||||||
|
func close():
|
||||||
|
hide()
|
||||||
|
clear()
|
||||||
|
set_paused(false)
|
||||||
|
|
||||||
|
|
||||||
|
# Wait for user input
|
||||||
|
func set_paused(paused: bool):
|
||||||
|
is_paused = paused
|
||||||
|
text = "Press ENTER to continue" if is_paused else ""
|
||||||
|
editable = !is_paused
|
||||||
|
|
||||||
|
|
||||||
|
func _on_text_submitted(new_text: String):
|
||||||
|
if is_paused:
|
||||||
|
cursor.set_mode(MODE.NORMAL)
|
||||||
|
status_bar.main_label.text = ""
|
||||||
|
return
|
||||||
|
handle_command(new_text)
|
14
addons/godot_vim/commands/delmarks.gd
Normal file
14
addons/godot_vim/commands/delmarks.gd
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const StatusBar = preload("res://addons/godot_vim/status_bar.gd")
|
||||||
|
const MODE = Constants.Mode
|
||||||
|
|
||||||
|
const LINE_START_IDX: int = 8
|
||||||
|
const COL_START_IDX: int = 16
|
||||||
|
const FILE_START_IDX: int = 25
|
||||||
|
|
||||||
|
|
||||||
|
func execute(api: Dictionary, _args):
|
||||||
|
api.marks = {}
|
||||||
|
|
||||||
|
api.status_bar.display_text("Deleted all marks")
|
||||||
|
api.cursor.set_mode(MODE.NORMAL)
|
16
addons/godot_vim/commands/find.gd
Normal file
16
addons/godot_vim/commands/find.gd
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const MODE = Constants.Mode
|
||||||
|
|
||||||
|
|
||||||
|
func execute(api: Dictionary, args: String):
|
||||||
|
api.command_line.search_pattern = args.substr(1)
|
||||||
|
var rmatch: RegExMatch = api.vim_plugin.search_regex(
|
||||||
|
api.code_edit, api.command_line.search_pattern, api.cursor.get_caret_pos() + Vector2i.RIGHT
|
||||||
|
)
|
||||||
|
if rmatch != null:
|
||||||
|
var pos: Vector2i = api.vim_plugin.idx_to_pos(api.code_edit, rmatch.get_start())
|
||||||
|
api.cursor.set_caret_pos(pos.y, pos.x)
|
||||||
|
# api.code_edit.center_viewport_to_caret()
|
||||||
|
else:
|
||||||
|
api.status_bar.display_error('Pattern not found: "%s"' % [api.command_line.search_pattern])
|
||||||
|
api.cursor.set_mode(MODE.NORMAL)
|
7
addons/godot_vim/commands/goto.gd
Normal file
7
addons/godot_vim/commands/goto.gd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const MODE = Constants.Mode
|
||||||
|
|
||||||
|
|
||||||
|
func execute(api, args):
|
||||||
|
api.cursor.set_caret_pos(args.to_int() - 1, 0)
|
||||||
|
api.cursor.set_mode(MODE.NORMAL)
|
61
addons/godot_vim/commands/marks.gd
Normal file
61
addons/godot_vim/commands/marks.gd
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const StatusBar = preload("res://addons/godot_vim/status_bar.gd")
|
||||||
|
const MODE = Constants.Mode
|
||||||
|
|
||||||
|
const LINE_START_IDX: int = 8
|
||||||
|
const COL_START_IDX: int = 16
|
||||||
|
const FILE_START_IDX: int = 25
|
||||||
|
|
||||||
|
|
||||||
|
## Display format:
|
||||||
|
## (1) LINE_START_IDX
|
||||||
|
## (2) COL_START_IDX
|
||||||
|
## (3) FILE_START_IDX
|
||||||
|
##
|
||||||
|
## List of all marks:
|
||||||
|
## (1) (2) (3)
|
||||||
|
## | | |
|
||||||
|
## mark line col file
|
||||||
|
## a 123 456 res://some_file
|
||||||
|
func row_string(mark: String, line: String, col: String, file: String) -> String:
|
||||||
|
var text: String = mark
|
||||||
|
text += " ".repeat(LINE_START_IDX - mark.length()) + line
|
||||||
|
text += " ".repeat(COL_START_IDX - text.length()) + col
|
||||||
|
text += " ".repeat(FILE_START_IDX - text.length()) + file
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
func mark_string(key: String, m: Dictionary) -> String:
|
||||||
|
var pos: Vector2i = m.get("pos", Vector2i())
|
||||||
|
var file: String = m.get("file", "")
|
||||||
|
return row_string(key, str(pos.y), str(pos.x), file)
|
||||||
|
|
||||||
|
|
||||||
|
func execute(api, _args):
|
||||||
|
var marks: Dictionary = api.get("marks", {})
|
||||||
|
if marks.is_empty():
|
||||||
|
api.status_bar.display_error("No marks set")
|
||||||
|
api.cursor.set_mode(MODE.NORMAL)
|
||||||
|
return
|
||||||
|
|
||||||
|
var text: String = "[color=%s]List of all marks[/color]" % StatusBar.SPECIAL_COLOR
|
||||||
|
text += "\n" + row_string("mark", "line", "col", "file")
|
||||||
|
|
||||||
|
# Display user-defined marks first (alphabet)
|
||||||
|
for key in marks.keys():
|
||||||
|
if !is_key_alphabet(key):
|
||||||
|
continue
|
||||||
|
text += "\n" + mark_string(key, marks[key])
|
||||||
|
|
||||||
|
# Then display 'number' marks
|
||||||
|
for key in marks.keys():
|
||||||
|
if is_key_alphabet(key) or key == "-1":
|
||||||
|
continue
|
||||||
|
text += "\n" + mark_string(key, marks[key])
|
||||||
|
|
||||||
|
api.status_bar.display_text(text)
|
||||||
|
|
||||||
|
|
||||||
|
func is_key_alphabet(key: String) -> bool:
|
||||||
|
var unicode: int = key.unicode_at(0)
|
||||||
|
return (unicode >= 65 and unicode <= 90) or (unicode >= 97 and unicode <= 122)
|
2
addons/godot_vim/commands/movecolumn.gd
Normal file
2
addons/godot_vim/commands/movecolumn.gd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
func execute(api, args):
|
||||||
|
api.cursor.move_column(int(args))
|
2
addons/godot_vim/commands/moveline.gd
Normal file
2
addons/godot_vim/commands/moveline.gd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
func execute(api, args):
|
||||||
|
api.cursor.move_line(int(args))
|
4
addons/godot_vim/commands/reload.gd
Normal file
4
addons/godot_vim/commands/reload.gd
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
func execute(api: Dictionary, _args):
|
||||||
|
print("[Godot VIM] Reloading...")
|
||||||
|
api.status_bar.display_text("Reloading plugin...")
|
||||||
|
api.vim_plugin.initialize(true)
|
9
addons/godot_vim/commands/remap.gd
Normal file
9
addons/godot_vim/commands/remap.gd
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const StatusBar = preload("res://addons/godot_vim/status_bar.gd")
|
||||||
|
|
||||||
|
|
||||||
|
func execute(api: Dictionary, _args):
|
||||||
|
print("[Godot VIM] Please run :reload in the command line after changing your keybinds")
|
||||||
|
var script: Script = api.key_map.get_script()
|
||||||
|
# Line 45 is where KeyMap::map() is
|
||||||
|
EditorInterface.edit_script(script, 40, 0, false)
|
17
addons/godot_vim/commands/w.gd
Normal file
17
addons/godot_vim/commands/w.gd
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const MODE = Constants.Mode
|
||||||
|
|
||||||
|
|
||||||
|
func execute(api, _args: String):
|
||||||
|
#EditorInterface.save_scene()
|
||||||
|
press_save_shortcut()
|
||||||
|
api.cursor.set_mode(MODE.NORMAL)
|
||||||
|
|
||||||
|
|
||||||
|
func press_save_shortcut():
|
||||||
|
var a = InputEventKey.new()
|
||||||
|
a.keycode = KEY_S
|
||||||
|
a.ctrl_pressed = true
|
||||||
|
a.alt_pressed = true
|
||||||
|
a.pressed = true
|
||||||
|
Input.parse_input_event(a)
|
17
addons/godot_vim/commands/wa.gd
Normal file
17
addons/godot_vim/commands/wa.gd
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const MODE = Constants.Mode
|
||||||
|
|
||||||
|
|
||||||
|
func execute(api, _args: String):
|
||||||
|
#EditorInterface.save_scene()
|
||||||
|
press_save_shortcut()
|
||||||
|
api.cursor.set_mode(MODE.NORMAL)
|
||||||
|
|
||||||
|
|
||||||
|
func press_save_shortcut():
|
||||||
|
var a = InputEventKey.new()
|
||||||
|
a.keycode = KEY_S
|
||||||
|
a.shift_pressed = true
|
||||||
|
a.alt_pressed = true
|
||||||
|
a.pressed = true
|
||||||
|
Input.parse_input_event(a)
|
19
addons/godot_vim/constants.gd
Normal file
19
addons/godot_vim/constants.gd
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
enum Mode { NORMAL = 0, INSERT, VISUAL, VISUAL_LINE, COMMAND }
|
||||||
|
|
||||||
|
enum Language {
|
||||||
|
GDSCRIPT,
|
||||||
|
SHADER,
|
||||||
|
}
|
||||||
|
|
||||||
|
const KEYWORDS: String = ".,\"'-=+!@#$%^&*()[]{}?~/\\<>:;`"
|
||||||
|
const DIGITS: String = "0123456789"
|
||||||
|
const SPACES: String = " \t"
|
||||||
|
const PAIRS: Dictionary = {
|
||||||
|
'"': '"',
|
||||||
|
"'": "'",
|
||||||
|
"`": "`",
|
||||||
|
"(": ")",
|
||||||
|
"[": "]",
|
||||||
|
"{": "}",
|
||||||
|
}
|
||||||
|
const BRACES: String = "([{}])"
|
1135
addons/godot_vim/cursor.gd
Normal file
1135
addons/godot_vim/cursor.gd
Normal file
File diff suppressed because it is too large
Load Diff
39
addons/godot_vim/dispatcher.gd
Normal file
39
addons/godot_vim/dispatcher.gd
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
extends Object
|
||||||
|
|
||||||
|
var handlers: Dictionary = {
|
||||||
|
"goto": preload("res://addons/godot_vim/commands/goto.gd"),
|
||||||
|
"find": preload("res://addons/godot_vim/commands/find.gd"),
|
||||||
|
"marks": preload("res://addons/godot_vim/commands/marks.gd"),
|
||||||
|
"delmarks": preload("res://addons/godot_vim/commands/delmarks.gd"),
|
||||||
|
"moveline": preload("res://addons/godot_vim/commands/moveline.gd"),
|
||||||
|
"movecolumn": preload("res://addons/godot_vim/commands/movecolumn.gd"),
|
||||||
|
"w": preload("res://addons/godot_vim/commands/w.gd"),
|
||||||
|
"wa": preload("res://addons/godot_vim/commands/wa.gd"),
|
||||||
|
# GodotVIM speficic commands:
|
||||||
|
"reload": preload("res://addons/godot_vim/commands/reload.gd"),
|
||||||
|
"remap": preload("res://addons/godot_vim/commands/remap.gd"),
|
||||||
|
}
|
||||||
|
|
||||||
|
var aliases: Dictionary = {"delm": ":delmarks"}
|
||||||
|
|
||||||
|
var globals: Dictionary
|
||||||
|
|
||||||
|
|
||||||
|
## Returns [enum @GlobalScope.Error]
|
||||||
|
func dispatch(command: String, do_allow_aliases: bool = true) -> int:
|
||||||
|
var command_idx_end: int = command.find(" ", 1)
|
||||||
|
if command_idx_end == -1:
|
||||||
|
command_idx_end = command.length()
|
||||||
|
var handler_name: String = command.substr(1, command_idx_end - 1)
|
||||||
|
|
||||||
|
if do_allow_aliases and aliases.has(handler_name):
|
||||||
|
return dispatch(aliases[handler_name], false)
|
||||||
|
|
||||||
|
if not handlers.has(handler_name):
|
||||||
|
return ERR_DOES_NOT_EXIST
|
||||||
|
|
||||||
|
var handler = handlers.get(handler_name)
|
||||||
|
var handler_instance = handler.new()
|
||||||
|
var args: String = command.substr(command_idx_end, command.length())
|
||||||
|
handler_instance.execute(globals, args)
|
||||||
|
return OK
|
BIN
addons/godot_vim/hack_regular.ttf
Normal file
BIN
addons/godot_vim/hack_regular.ttf
Normal file
Binary file not shown.
34
addons/godot_vim/hack_regular.ttf.import
Normal file
34
addons/godot_vim/hack_regular.ttf.import
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="font_data_dynamic"
|
||||||
|
type="FontFile"
|
||||||
|
uid="uid://4q1f672e7ux2"
|
||||||
|
path="res://.godot/imported/hack_regular.ttf-0fe21890026c2274f233cdc75cf86ba7.fontdata"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/godot_vim/hack_regular.ttf"
|
||||||
|
dest_files=["res://.godot/imported/hack_regular.ttf-0fe21890026c2274f233cdc75cf86ba7.fontdata"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
Rendering=null
|
||||||
|
antialiasing=1
|
||||||
|
generate_mipmaps=false
|
||||||
|
disable_embedded_bitmaps=true
|
||||||
|
multichannel_signed_distance_field=false
|
||||||
|
msdf_pixel_range=8
|
||||||
|
msdf_size=48
|
||||||
|
allow_system_fallback=true
|
||||||
|
force_autohinter=false
|
||||||
|
hinting=1
|
||||||
|
subpixel_positioning=1
|
||||||
|
oversampling=0.0
|
||||||
|
Fallbacks=null
|
||||||
|
fallbacks=[]
|
||||||
|
Compress=null
|
||||||
|
compress=true
|
||||||
|
preload=[]
|
||||||
|
language_support={}
|
||||||
|
script_support={}
|
||||||
|
opentype_features={}
|
741
addons/godot_vim/key_map.gd
Normal file
741
addons/godot_vim/key_map.gd
Normal file
@ -0,0 +1,741 @@
|
|||||||
|
class_name KeyMap extends RefCounted
|
||||||
|
## Hanldes input stream and key mapping
|
||||||
|
##
|
||||||
|
## You may also set your keybindings in the [method map] function
|
||||||
|
|
||||||
|
|
||||||
|
## * SET YOUR KEYBINDINGS HERE *
|
||||||
|
## Also see the "COMMANDS" section at the bottom of cursor.gd
|
||||||
|
## E.g. the command for
|
||||||
|
## KeyRemap.new(...) .motion("foo", { "bar": 1 })
|
||||||
|
## is handled in Cursor::cmd_foo(args: Dictionary)
|
||||||
|
## where `args` is `{ "type": "foo", "bar": 1 }`
|
||||||
|
## Example:
|
||||||
|
## [codeblock]
|
||||||
|
## return [
|
||||||
|
## # Move 5 characters to the right with "L"
|
||||||
|
## KeyRemap.new([ "L" ])
|
||||||
|
## .motion("move_by_chars", { "move_by": 5 }),
|
||||||
|
##
|
||||||
|
## # Let's remove "d" (the delete operator) and replace it with "q"
|
||||||
|
## # You may additionally specify the type and context of the cmd to remove
|
||||||
|
## # using .operator() (or .motion() or .action() etc...) and .with_context()
|
||||||
|
## KeyRemap.new([ "d" ])
|
||||||
|
## .remove(),
|
||||||
|
## # "q" is now the new delete operator
|
||||||
|
## KeyRemap.new([ "q" ])
|
||||||
|
## .operator("delete"),
|
||||||
|
##
|
||||||
|
## # Delete this line along with the next two with "Z"
|
||||||
|
## # .operator() and .motion() automatically merge together
|
||||||
|
## KeyRemap.new([ "Z" ])
|
||||||
|
## .operator("delete")
|
||||||
|
## .motion("move_by_lines", { "move_by": 2, "line_wise": true }),
|
||||||
|
##
|
||||||
|
## # In Insert mode, return to Normal mode with "jk"
|
||||||
|
## KeyRemap.new([ "j", "k" ])
|
||||||
|
## .action("normal", { "backspaces": 1, "offset": 1 })
|
||||||
|
## .with_context(Mode.INSERT),
|
||||||
|
## ]
|
||||||
|
## [/codeblock]
|
||||||
|
static func map() -> Array[KeyRemap]:
|
||||||
|
# Example:
|
||||||
|
return [
|
||||||
|
# In Insert mode, return to Normal mode with "jk"
|
||||||
|
# KeyRemap.new([ "j", "k" ])
|
||||||
|
# .action("normal", { "backspaces": 1, "offset": 0 })
|
||||||
|
# .with_context(Mode.INSERT),
|
||||||
|
# Make "/" search in case insensitive mode
|
||||||
|
# KeyRemap.new([ "/" ])
|
||||||
|
# .action("command", { "command": "/(?i)" })
|
||||||
|
# .replace(),
|
||||||
|
# In Insert mode, return to Normal mode with "Ctrl-["
|
||||||
|
# KeyRemap.new([ "<C-[>" ])
|
||||||
|
# .action("normal")
|
||||||
|
# .with_context(Mode.INSERT),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const Mode = Constants.Mode
|
||||||
|
|
||||||
|
const INSERT_MODE_TIMEOUT_MS: int = 700
|
||||||
|
|
||||||
|
enum {
|
||||||
|
## Moves the cursor. Can be used in tandem with Operator
|
||||||
|
Motion,
|
||||||
|
## Operators (like delete, change, yank) work on selections
|
||||||
|
## In Normal mode, they need a Motion or another Operator bound to them (e.g. dj, yy)
|
||||||
|
Operator,
|
||||||
|
## Operator but with a motion already bound to it
|
||||||
|
## Can only be executed in Normal mode
|
||||||
|
OperatorMotion,
|
||||||
|
## A single action (e.g. i, o, v, J, u)
|
||||||
|
## Cannot be executed in Visual mode unless specified with "context": Mode.VISUAL
|
||||||
|
Action,
|
||||||
|
Incomplete, ## Incomplete command
|
||||||
|
NotFound, ## Command not found
|
||||||
|
}
|
||||||
|
|
||||||
|
#region key_map
|
||||||
|
|
||||||
|
# Also see the "COMMANDS" section at the bottom of cursor.gd
|
||||||
|
# Command for { "type": "foo", ... } is handled in Cursor::cmd_foo(args: Dictionary)
|
||||||
|
# where `args` is ^^^^^ this Dict ^^^^^^
|
||||||
|
var key_map: Array[Dictionary] = [
|
||||||
|
# MOTIONS
|
||||||
|
{"keys": ["h"], "type": Motion, "motion": {"type": "move_by_chars", "move_by": -1}},
|
||||||
|
{"keys": ["l"], "type": Motion, "motion": {"type": "move_by_chars", "move_by": 1}},
|
||||||
|
{
|
||||||
|
"keys": ["j"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_lines", "move_by": 1, "line_wise": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["k"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_lines", "move_by": -1, "line_wise": true}
|
||||||
|
},
|
||||||
|
# About motions: the argument `inclusive` is used with Operators (see execute_operator_motion())
|
||||||
|
{
|
||||||
|
"keys": ["w"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_word", "forward": true, "word_end": false}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["e"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_word", "forward": true, "word_end": true, "inclusive": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["b"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_word", "forward": false, "word_end": false}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["g", "e"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_word", "forward": false, "word_end": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["W"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_word", "forward": true, "word_end": false, "big_word": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["E"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion":
|
||||||
|
{
|
||||||
|
"type": "move_by_word",
|
||||||
|
"forward": true,
|
||||||
|
"word_end": true,
|
||||||
|
"big_word": true,
|
||||||
|
"inclusive": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["B"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_word", "forward": false, "word_end": false, "big_word": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["g", "E"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_word", "forward": false, "word_end": true, "big_word": true}
|
||||||
|
},
|
||||||
|
# Find & search
|
||||||
|
{
|
||||||
|
"keys": ["f", "{char}"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "find_in_line", "forward": true, "inclusive": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["t", "{char}"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "find_in_line", "forward": true, "stop_before": true, "inclusive": true}
|
||||||
|
},
|
||||||
|
{"keys": ["F", "{char}"], "type": Motion, "motion": {"type": "find_in_line", "forward": false}},
|
||||||
|
{
|
||||||
|
"keys": ["T", "{char}"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "find_in_line", "forward": false, "stop_before": true}
|
||||||
|
},
|
||||||
|
{"keys": [";"], "type": Motion, "motion": {"type": "find_in_line_again", "invert": false}},
|
||||||
|
{"keys": [","], "type": Motion, "motion": {"type": "find_in_line_again", "invert": true}},
|
||||||
|
{"keys": ["n"], "type": Motion, "motion": {"type": "find_again", "forward": true}},
|
||||||
|
{"keys": ["N"], "type": Motion, "motion": {"type": "find_again", "forward": false}},
|
||||||
|
{"keys": ["0"], "type": Motion, "motion": {"type": "move_to_bol"}},
|
||||||
|
{"keys": ["$"], "type": Motion, "motion": {"type": "move_to_eol"}},
|
||||||
|
{"keys": ["^"], "type": Motion, "motion": {"type": "move_to_first_non_whitespace_char"}},
|
||||||
|
{
|
||||||
|
"keys": ["{"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_paragraph", "forward": false, "line_wise": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["}"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_paragraph", "forward": true, "line_wise": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["[", "["],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_section", "forward": false, "line_wise": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["]", "]"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_section", "forward": true, "line_wise": true}
|
||||||
|
},
|
||||||
|
{"keys": ["g", "g"], "type": Motion, "motion": {"type": "move_to_bof", "line_wise": true}},
|
||||||
|
{"keys": ["G"], "type": Motion, "motion": {"type": "move_to_eof", "line_wise": true}},
|
||||||
|
{"keys": ["g", "m"], "type": Motion, "motion": {"type": "move_to_center_of_line"}},
|
||||||
|
{
|
||||||
|
"keys": ["<C-u>"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_screen", "percentage": -0.5, "line_wise": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["<C-d>"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "move_by_screen", "percentage": 0.5, "line_wise": true}
|
||||||
|
},
|
||||||
|
{"keys": ["%"], "type": Motion, "motion": {"type": "jump_to_next_brace_pair"}},
|
||||||
|
# TEXT OBJECTS
|
||||||
|
{
|
||||||
|
"keys": ["a", "w"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object_word", "around": true, "inclusive": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["a", "W"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object_word", "around": true, "inclusive": true}
|
||||||
|
},
|
||||||
|
{"keys": ["i", "w"], "type": Motion, "motion": {"type": "text_object_word", "inclusive": true}},
|
||||||
|
{
|
||||||
|
"keys": ["i", "W"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object_word", "big_word": true, "inclusive": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["i", "p"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object_paragraph", "line_wise": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["a", "p"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object_paragraph", "around": true, "line_wise": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["i", '"'],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object", "object": '"', "inclusive": true, "inline": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["a", '"'],
|
||||||
|
"type": Motion,
|
||||||
|
"motion":
|
||||||
|
{"type": "text_object", "object": '"', "around": true, "inclusive": true, "inline": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["i", "'"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object", "object": "'", "inclusive": true, "inline": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["a", "'"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion":
|
||||||
|
{"type": "text_object", "object": "'", "around": true, "inclusive": true, "inline": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["i", "`"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object", "object": "`", "inclusive": true, "inline": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["a", "`"],
|
||||||
|
"type": Motion,
|
||||||
|
"motion":
|
||||||
|
{"type": "text_object", "object": "`", "around": true, "inclusive": true, "inline": true}
|
||||||
|
},
|
||||||
|
# "i" + any of "(", ")", or "b"
|
||||||
|
{
|
||||||
|
"keys": ["i", ["(", ")", "b"]],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object", "object": "(", "inclusive": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["a", ["(", ")", "b"]],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object", "object": "(", "around": true, "inclusive": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["i", ["[", "]"]],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object", "object": "[", "inclusive": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["a", ["[", "]"]],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object", "object": "[", "around": true, "inclusive": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["i", ["{", "}", "B"]],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object", "object": "{", "inclusive": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["a", ["{", "}", "B"]],
|
||||||
|
"type": Motion,
|
||||||
|
"motion": {"type": "text_object", "object": "{", "around": true, "inclusive": true}
|
||||||
|
},
|
||||||
|
# OPERATORS
|
||||||
|
{"keys": ["d"], "type": Operator, "operator": {"type": "delete"}},
|
||||||
|
{
|
||||||
|
"keys": ["D"],
|
||||||
|
"type": OperatorMotion,
|
||||||
|
"operator": {"type": "delete"},
|
||||||
|
"motion": {"type": "move_to_eol"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["x"],
|
||||||
|
"type": OperatorMotion,
|
||||||
|
"operator": {"type": "delete"},
|
||||||
|
"motion": {"type": "move_by_chars", "move_by": 1}
|
||||||
|
},
|
||||||
|
{"keys": ["x"], "type": Operator, "context": Mode.VISUAL, "operator": {"type": "delete"}},
|
||||||
|
{"keys": ["y"], "type": Operator, "operator": {"type": "yank"}},
|
||||||
|
{
|
||||||
|
"keys": ["Y"],
|
||||||
|
"type": OperatorMotion,
|
||||||
|
"operator": {"type": "yank", "line_wise": true}, # No motion. Same as yy
|
||||||
|
},
|
||||||
|
{"keys": ["c"], "type": Operator, "operator": {"type": "change"}},
|
||||||
|
{
|
||||||
|
"keys": ["C"],
|
||||||
|
"type": OperatorMotion,
|
||||||
|
"operator": {"type": "change"},
|
||||||
|
"motion": {"type": "move_to_eol"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["s"],
|
||||||
|
"type": OperatorMotion,
|
||||||
|
"operator": {"type": "change"},
|
||||||
|
"motion": {"type": "move_by_chars", "move_by": 1}
|
||||||
|
},
|
||||||
|
{"keys": ["s"], "type": Operator, "context": Mode.VISUAL, "operator": {"type": "change"}},
|
||||||
|
{
|
||||||
|
"keys": ["p"],
|
||||||
|
"type": OperatorMotion,
|
||||||
|
"operator": {"type": "paste"},
|
||||||
|
"motion": {"type": "move_by_chars", "move_by": 1}
|
||||||
|
},
|
||||||
|
{"keys": ["p"], "type": Operator, "context": Mode.VISUAL, "operator": {"type": "paste"}},
|
||||||
|
{"keys": ["p"], "type": Operator, "context": Mode.VISUAL_LINE, "operator": {"type": "paste"}},
|
||||||
|
{"keys": [">"], "type": Operator, "operator": {"type": "indent", "forward": true}},
|
||||||
|
{"keys": ["<"], "type": Operator, "operator": {"type": "indent", "forward": false}},
|
||||||
|
{
|
||||||
|
"keys": ["g", "c", "c"],
|
||||||
|
"type": OperatorMotion,
|
||||||
|
"operator": {"type": "comment"},
|
||||||
|
"motion": {"type": "move_by_chars", "move_by": 1}
|
||||||
|
},
|
||||||
|
{"keys": ["g", "c"], "type": Operator, "operator": {"type": "comment"}},
|
||||||
|
{
|
||||||
|
"keys": ["~"],
|
||||||
|
"type": OperatorMotion,
|
||||||
|
"operator": {"type": "toggle_uppercase"},
|
||||||
|
"motion": {"type": "move_by_chars", "move_by": 1}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["~"],
|
||||||
|
"type": Operator,
|
||||||
|
"context": Mode.VISUAL,
|
||||||
|
"operator": {"type": "toggle_uppercase"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["u"],
|
||||||
|
"type": Operator,
|
||||||
|
"context": Mode.VISUAL,
|
||||||
|
"operator": {"type": "set_uppercase", "uppercase": false}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["U"],
|
||||||
|
"type": Operator,
|
||||||
|
"context": Mode.VISUAL,
|
||||||
|
"operator": {"type": "set_uppercase", "uppercase": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["V"],
|
||||||
|
"type": Operator,
|
||||||
|
"context": Mode.VISUAL,
|
||||||
|
"operator": {"type": "visual", "line_wise": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["v"],
|
||||||
|
"type": Operator,
|
||||||
|
"context": Mode.VISUAL_LINE,
|
||||||
|
"operator": {"type": "visual", "line_wise": false}
|
||||||
|
},
|
||||||
|
# ACTIONS
|
||||||
|
{"keys": ["i"], "type": Action, "action": {"type": "insert"}},
|
||||||
|
{"keys": ["a"], "type": Action, "action": {"type": "insert", "offset": "after"}},
|
||||||
|
{"keys": ["I"], "type": Action, "action": {"type": "insert", "offset": "bol"}},
|
||||||
|
{"keys": ["A"], "type": Action, "action": {"type": "insert", "offset": "eol"}},
|
||||||
|
{"keys": ["o"], "type": Action, "action": {"type": "insert", "offset": "new_line_below"}},
|
||||||
|
{"keys": ["O"], "type": Action, "action": {"type": "insert", "offset": "new_line_above"}},
|
||||||
|
{"keys": ["v"], "type": Action, "action": {"type": "visual"}},
|
||||||
|
{"keys": ["V"], "type": Action, "action": {"type": "visual", "line_wise": true}},
|
||||||
|
{"keys": ["u"], "type": Action, "action": {"type": "undo"}},
|
||||||
|
{"keys": ["<C-r>"], "type": Action, "action": {"type": "redo"}},
|
||||||
|
{"keys": ["r", "{char}"], "type": Action, "action": {"type": "replace"}},
|
||||||
|
{"keys": [":"], "type": Action, "action": {"type": "command"}},
|
||||||
|
{"keys": ["/"], "type": Action, "action": {"type": "command", "command": "/"}},
|
||||||
|
{"keys": ["J"], "type": Action, "action": {"type": "join"}},
|
||||||
|
{"keys": ["z", "z"], "type": Action, "action": {"type": "center_caret"}},
|
||||||
|
{"keys": ["m", "{char}"], "type": Action, "action": {"type": "mark"}},
|
||||||
|
{"keys": ["`", "{char}"], "type": Action, "action": {"type": "jump_to_mark"}},
|
||||||
|
# MISCELLANEOUS
|
||||||
|
{
|
||||||
|
"keys": ["o"],
|
||||||
|
"type": Operator,
|
||||||
|
"context": Mode.VISUAL,
|
||||||
|
"operator": {"type": "visual_jump_to_other_end"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keys": ["o"],
|
||||||
|
"type": Operator,
|
||||||
|
"context": Mode.VISUAL_LINE,
|
||||||
|
"operator": {"type": "visual_jump_to_other_end"}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
#endregion key_map
|
||||||
|
|
||||||
|
# Keys we won't handle
|
||||||
|
const BLACKLIST: Array[String] = [
|
||||||
|
"<C-s>", # Save
|
||||||
|
"<C-b>", # Bookmark
|
||||||
|
]
|
||||||
|
|
||||||
|
enum KeyMatch {
|
||||||
|
None = 0, # Keys don't match
|
||||||
|
Partial = 1, # Keys match partially
|
||||||
|
Full = 2, # Keys match totally
|
||||||
|
}
|
||||||
|
|
||||||
|
var input_stream: Array[String] = []
|
||||||
|
var cursor: Control
|
||||||
|
var last_insert_mode_input_ms: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
func _init(cursor_: Control):
|
||||||
|
cursor = cursor_
|
||||||
|
apply_remaps(KeyMap.map())
|
||||||
|
|
||||||
|
|
||||||
|
## Returns: Dictionary with the found command: { "type": Motion or Operator or OperatorMotion or Action or Incomplete or NotFound, ... }
|
||||||
|
## Warning: the returned Dict can be empty in if the event wasn't processed
|
||||||
|
func register_event(event: InputEventKey, with_context: Mode) -> Dictionary:
|
||||||
|
# Stringify event
|
||||||
|
var ch: String = event_to_string(event)
|
||||||
|
if ch.is_empty():
|
||||||
|
return {} # Invalid
|
||||||
|
if BLACKLIST.has(ch):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# Handle Insert mode timeout
|
||||||
|
if with_context == Mode.INSERT:
|
||||||
|
if handle_insert_mode_timeout():
|
||||||
|
clear()
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# Process input stream
|
||||||
|
# print("[KeyMap::register_event()] ch = ", ch) # DEBUG
|
||||||
|
input_stream.append(ch)
|
||||||
|
var cmd: Dictionary = parse_keys(input_stream, with_context)
|
||||||
|
if !is_cmd_valid(cmd):
|
||||||
|
return {"type": NotFound}
|
||||||
|
|
||||||
|
execute(cmd)
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
|
||||||
|
func parse_keys(keys: Array[String], with_context: Mode) -> Dictionary:
|
||||||
|
var blacklist: Array = get_blacklist_types_in_context(with_context)
|
||||||
|
var cmd: Dictionary = find_cmd(keys, with_context, blacklist)
|
||||||
|
if cmd.is_empty() or cmd.type == NotFound:
|
||||||
|
call_deferred(&"clear")
|
||||||
|
return cmd
|
||||||
|
if cmd.type == Incomplete:
|
||||||
|
# print(cmd)
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
# Execute the operation as-is if in VISUAL mode
|
||||||
|
# If in NORMAL mode, await further input
|
||||||
|
if cmd.type == Operator and with_context == Mode.NORMAL:
|
||||||
|
var op_args: Array[String] = keys.slice(cmd.keys.size()) # Get the rest of keys for motion
|
||||||
|
if op_args.is_empty(): # Incomplete; await further input
|
||||||
|
return {"type": Incomplete}
|
||||||
|
|
||||||
|
var next: Dictionary = find_cmd(op_args, with_context, [Action, OperatorMotion])
|
||||||
|
|
||||||
|
if next.is_empty() or next.type == NotFound: # Invalid sequence
|
||||||
|
call_deferred(&"clear")
|
||||||
|
return {"type": NotFound}
|
||||||
|
elif next.type == Incomplete:
|
||||||
|
return {"type": Incomplete}
|
||||||
|
|
||||||
|
cmd.modifier = next
|
||||||
|
|
||||||
|
call_deferred(&"clear")
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
|
||||||
|
## The returned cmd will always have a 'type' key
|
||||||
|
# TODO use bitmask instead of Array?
|
||||||
|
func find_cmd(keys: Array[String], with_context: Mode, blacklist: Array = []) -> Dictionary:
|
||||||
|
var partial: bool = false # In case none were found
|
||||||
|
var is_visual: bool = with_context == Mode.VISUAL or with_context == Mode.VISUAL_LINE
|
||||||
|
|
||||||
|
for cmd in key_map:
|
||||||
|
# FILTERS
|
||||||
|
# Don't allow anything in Insert mode unless specified
|
||||||
|
if with_context == Mode.INSERT and cmd.get("context", -1) != Mode.INSERT:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if blacklist.has(cmd.type):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Skip if contexts don't match
|
||||||
|
if cmd.has("context") and with_context != cmd.context:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# CHECK KEYS
|
||||||
|
var m: KeyMatch = match_keys(cmd.keys, keys)
|
||||||
|
partial = partial or m == KeyMatch.Partial # Set/keep partial = true if it was a partial match
|
||||||
|
|
||||||
|
if m != KeyMatch.Full:
|
||||||
|
continue
|
||||||
|
|
||||||
|
var cmd_mut: Dictionary = cmd.duplicate(true) # 'mut' ('mutable') because key_map is read-only
|
||||||
|
# Keep track of selected character, which will later be copied into the fucntion call for the command
|
||||||
|
# (See execute() where we check if cmd.has('selected_char'))
|
||||||
|
if cmd.keys[-1] is String and cmd.keys[-1] == "{char}":
|
||||||
|
cmd_mut.selected_char = keys.back()
|
||||||
|
return cmd_mut
|
||||||
|
|
||||||
|
return {"type": Incomplete if partial else NotFound}
|
||||||
|
|
||||||
|
|
||||||
|
# TODO use bitmask instead of Array?
|
||||||
|
func get_blacklist_types_in_context(context: Mode) -> Array:
|
||||||
|
match context:
|
||||||
|
Mode.VISUAL, Mode.VISUAL_LINE:
|
||||||
|
return [OperatorMotion, Action]
|
||||||
|
_:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
func execute_operator_motion(cmd: Dictionary):
|
||||||
|
if cmd.has("motion"):
|
||||||
|
if cmd.has("selected_char"):
|
||||||
|
cmd.motion.selected_char = cmd.selected_char
|
||||||
|
operator_motion(cmd.operator, cmd.motion)
|
||||||
|
else:
|
||||||
|
call_cmd(cmd.operator)
|
||||||
|
|
||||||
|
|
||||||
|
func execute_operator(cmd: Dictionary):
|
||||||
|
# print("[KeyMay::execute()] op: ", cmd) # DEBUG
|
||||||
|
if !cmd.has("modifier"): # Execute as-is
|
||||||
|
call_cmd(cmd.operator)
|
||||||
|
return
|
||||||
|
|
||||||
|
var mod: Dictionary = cmd.modifier
|
||||||
|
# Execute with motion
|
||||||
|
if mod.type == Motion:
|
||||||
|
if mod.has("selected_char"):
|
||||||
|
mod.motion.selected_char = mod.selected_char
|
||||||
|
operator_motion(cmd.operator, mod.motion)
|
||||||
|
|
||||||
|
# Execute with `line_wise = true` if repeating operations (e.g. dd, yy)
|
||||||
|
elif mod.type == Operator and mod.operator.type == cmd.operator.type:
|
||||||
|
var op_cmd: Dictionary = cmd.operator.duplicate()
|
||||||
|
op_cmd.line_wise = true
|
||||||
|
call_cmd(op_cmd)
|
||||||
|
|
||||||
|
|
||||||
|
func execute_action(cmd: Dictionary):
|
||||||
|
if cmd.has("selected_char"):
|
||||||
|
cmd.action.selected_char = cmd.selected_char
|
||||||
|
call_cmd(cmd.action)
|
||||||
|
|
||||||
|
|
||||||
|
func execute_motion(cmd: Dictionary):
|
||||||
|
if cmd.has("selected_char"):
|
||||||
|
cmd.motion.selected_char = cmd.selected_char
|
||||||
|
var pos = call_cmd(cmd.motion) # Vector2i for normal motion, or [Vector2i, Vector2i] for text object
|
||||||
|
|
||||||
|
if pos is Vector2i:
|
||||||
|
cursor.set_caret_pos(pos.y, pos.x)
|
||||||
|
elif pos is Array:
|
||||||
|
assert(pos.size() == 2)
|
||||||
|
# print("[execute_motion() -> text obj] pos = ", pos)
|
||||||
|
cursor.select(pos[0].y, pos[0].x, pos[1].y, pos[1].x)
|
||||||
|
|
||||||
|
|
||||||
|
func execute(cmd: Dictionary):
|
||||||
|
if !is_cmd_valid(cmd):
|
||||||
|
return
|
||||||
|
|
||||||
|
match cmd.type:
|
||||||
|
Motion:
|
||||||
|
execute_motion(cmd)
|
||||||
|
OperatorMotion:
|
||||||
|
execute_operator_motion(cmd)
|
||||||
|
Operator:
|
||||||
|
execute_operator(cmd)
|
||||||
|
Action:
|
||||||
|
execute_action(cmd)
|
||||||
|
_:
|
||||||
|
push_error("[KeyMap::execute()] Unknown command type: %s" % cmd.type)
|
||||||
|
|
||||||
|
|
||||||
|
func operator_motion(operator: Dictionary, motion: Dictionary):
|
||||||
|
# print("[KeyMay::execute_operator_motion()] op = ", operator, ", motion = ", motion) # DEBUG
|
||||||
|
|
||||||
|
# Execute motion before operation
|
||||||
|
var p = call_cmd(motion) # Vector2i for normal motion, or [Vector2i, Vector2i] for text object
|
||||||
|
if p is Vector2i:
|
||||||
|
var p0: Vector2i = cursor.get_caret_pos()
|
||||||
|
if motion.get("inclusive", false):
|
||||||
|
p.x += 1
|
||||||
|
cursor.code_edit.select(p0.y, p0.x, p.y, p.x)
|
||||||
|
elif p is Array:
|
||||||
|
assert(p.size() == 2)
|
||||||
|
if motion.get("inclusive", false):
|
||||||
|
p[1].x += 1
|
||||||
|
cursor.code_edit.select(p[0].y, p[0].x, p[1].y, p[1].x)
|
||||||
|
|
||||||
|
# Add line_wise flag if line wise motion
|
||||||
|
var op: Dictionary = operator.duplicate()
|
||||||
|
op.line_wise = motion.get("line_wise", false)
|
||||||
|
call_cmd(op)
|
||||||
|
|
||||||
|
|
||||||
|
## Unsafe: does not check if the function exists
|
||||||
|
func call_cmd(cmd: Dictionary) -> Variant:
|
||||||
|
var func_name: StringName = StringName("cmd_" + cmd.type)
|
||||||
|
return cursor.call(func_name, cmd)
|
||||||
|
|
||||||
|
|
||||||
|
static func is_cmd_valid(cmd: Dictionary):
|
||||||
|
return !cmd.is_empty() and cmd.type != Incomplete and cmd.type != NotFound
|
||||||
|
|
||||||
|
|
||||||
|
static func event_to_string(event: InputEventKey) -> String:
|
||||||
|
# Special chars
|
||||||
|
if event.keycode == KEY_ENTER:
|
||||||
|
return "<CR>"
|
||||||
|
if event.keycode == KEY_TAB:
|
||||||
|
return "<TAB>"
|
||||||
|
if event.keycode == KEY_ESCAPE:
|
||||||
|
return "<ESC>"
|
||||||
|
|
||||||
|
# Ctrl + key
|
||||||
|
if event.is_command_or_control_pressed():
|
||||||
|
if !OS.is_keycode_unicode(event.keycode):
|
||||||
|
return ""
|
||||||
|
var c: String = char(event.keycode)
|
||||||
|
return "<C-%s>" % [c if event.shift_pressed else c.to_lower()]
|
||||||
|
|
||||||
|
# You're not special.
|
||||||
|
return char(event.unicode)
|
||||||
|
|
||||||
|
|
||||||
|
## Matches single command keys
|
||||||
|
## expected_keys: Array[key: String] or Array[any_of_these_keys: Array[key: String]]
|
||||||
|
static func match_keys(expected_keys: Array, input_keys: Array) -> KeyMatch:
|
||||||
|
var in_size: int = input_keys.size()
|
||||||
|
var ex_size: int = expected_keys.size()
|
||||||
|
|
||||||
|
if expected_keys[-1] is String and expected_keys[-1] == "{char}":
|
||||||
|
# If everything + {char} matches
|
||||||
|
if _do_keys_match(input_keys.slice(0, -1), expected_keys.slice(0, -1)):
|
||||||
|
return KeyMatch.Full
|
||||||
|
|
||||||
|
# If everything up until {char} matches
|
||||||
|
elif _do_keys_match(input_keys, expected_keys.slice(0, in_size)):
|
||||||
|
return KeyMatch.Partial
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Check for full match
|
||||||
|
if _do_keys_match(input_keys, expected_keys):
|
||||||
|
return KeyMatch.Full
|
||||||
|
# Check for incomplete command (e.g. "ge", "gcc")
|
||||||
|
elif _do_keys_match(input_keys, expected_keys.slice(0, in_size)):
|
||||||
|
return KeyMatch.Partial
|
||||||
|
# Cases with operators like "dj", "ce"
|
||||||
|
elif _do_keys_match(input_keys.slice(0, ex_size), expected_keys) and in_size > ex_size:
|
||||||
|
return KeyMatch.Full
|
||||||
|
|
||||||
|
return KeyMatch.None
|
||||||
|
|
||||||
|
|
||||||
|
## input_keys: Array[key: String]
|
||||||
|
## expected_keys: Array[key: String] or Array[any_of_these_keys: Array[key: String]]
|
||||||
|
static func _do_keys_match(input_keys: Array, match_keys: Array) -> bool:
|
||||||
|
if match_keys.size() != input_keys.size():
|
||||||
|
return false
|
||||||
|
|
||||||
|
for i in input_keys.size():
|
||||||
|
var key: String = input_keys[i]
|
||||||
|
if match_keys[i] is String:
|
||||||
|
if !match_keys[i] == key:
|
||||||
|
return false
|
||||||
|
elif match_keys[i] is Array:
|
||||||
|
if !match_keys[i].has(key):
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
push_error("expected String or Array[String], found ", match_keys[i])
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
## Clears the input stream
|
||||||
|
func clear():
|
||||||
|
input_stream = []
|
||||||
|
|
||||||
|
|
||||||
|
func get_input_stream_as_string() -> String:
|
||||||
|
return "".join(PackedStringArray(input_stream))
|
||||||
|
|
||||||
|
|
||||||
|
## Returns whether the Insert mode input has timed out, in which case we
|
||||||
|
## don't want to process it
|
||||||
|
func handle_insert_mode_timeout() -> bool:
|
||||||
|
var current_tick_ms: int = Time.get_ticks_msec()
|
||||||
|
|
||||||
|
if input_stream.is_empty():
|
||||||
|
last_insert_mode_input_ms = current_tick_ms
|
||||||
|
return false
|
||||||
|
|
||||||
|
if current_tick_ms - last_insert_mode_input_ms > INSERT_MODE_TIMEOUT_MS:
|
||||||
|
last_insert_mode_input_ms = current_tick_ms
|
||||||
|
return true
|
||||||
|
last_insert_mode_input_ms = current_tick_ms
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
func apply_remaps(map: Array[KeyRemap]):
|
||||||
|
if map.is_empty():
|
||||||
|
return
|
||||||
|
print("[Godot VIM] Applying keybind remaps...")
|
||||||
|
for remap in map:
|
||||||
|
remap.apply(key_map)
|
7
addons/godot_vim/plugin.cfg
Normal file
7
addons/godot_vim/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="GodotVim"
|
||||||
|
description=""
|
||||||
|
author="Bernardo Bruning"
|
||||||
|
version="0.1"
|
||||||
|
script="plugin.gd"
|
354
addons/godot_vim/plugin.gd
Normal file
354
addons/godot_vim/plugin.gd
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
@tool
|
||||||
|
extends EditorPlugin
|
||||||
|
|
||||||
|
const StatusBar = preload("res://addons/godot_vim/status_bar.gd")
|
||||||
|
const CommandLine = preload("res://addons/godot_vim/command_line.gd")
|
||||||
|
const Cursor = preload("res://addons/godot_vim/cursor.gd")
|
||||||
|
const Dispatcher = preload("res://addons/godot_vim/dispatcher.gd")
|
||||||
|
|
||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const DIGITS = Constants.DIGITS
|
||||||
|
const LANGUAGE = Constants.Language
|
||||||
|
|
||||||
|
var cursor: Cursor
|
||||||
|
var key_map: KeyMap
|
||||||
|
var command_line: CommandLine
|
||||||
|
var status_bar: StatusBar
|
||||||
|
var globals: Dictionary = {}
|
||||||
|
var dispatcher: Dispatcher
|
||||||
|
|
||||||
|
|
||||||
|
func _enter_tree():
|
||||||
|
EditorInterface.get_script_editor().connect("editor_script_changed", _on_script_changed)
|
||||||
|
|
||||||
|
var shader_tabcontainer = get_shader_tabcontainer() as TabContainer
|
||||||
|
if shader_tabcontainer != null:
|
||||||
|
shader_tabcontainer.tab_changed.connect(_on_shader_tab_changed)
|
||||||
|
shader_tabcontainer.visibility_changed.connect(_on_shader_tab_visibility_changed)
|
||||||
|
else:
|
||||||
|
push_error(
|
||||||
|
"[Godot VIM] Failed to get shader editor's TabContainer. Vim will be disabled in the shader editor"
|
||||||
|
)
|
||||||
|
|
||||||
|
globals = {}
|
||||||
|
initialize(true)
|
||||||
|
|
||||||
|
|
||||||
|
func initialize(forced: bool = false):
|
||||||
|
_load(forced)
|
||||||
|
|
||||||
|
print("[Godot VIM] Initialized.")
|
||||||
|
print(" If you wish to set keybindings, please run :remap in the command line")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_script_changed(script: Script):
|
||||||
|
if !script:
|
||||||
|
return
|
||||||
|
|
||||||
|
mark_recent_file(script.resource_path)
|
||||||
|
|
||||||
|
_load()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_shader_tab_changed(_tab: int):
|
||||||
|
call_deferred(&"_load")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_shader_tab_visibility_changed():
|
||||||
|
call_deferred(&"_load")
|
||||||
|
|
||||||
|
|
||||||
|
func mark_recent_file(path: String):
|
||||||
|
if !globals.has("marks"):
|
||||||
|
globals.marks = {}
|
||||||
|
var marks: Dictionary = globals.marks
|
||||||
|
|
||||||
|
# Check if path is already in the recent files (stored in start_index)
|
||||||
|
# This is to avoid flooding the recent files list with the same files
|
||||||
|
var start_index: int = 0
|
||||||
|
while start_index <= 9:
|
||||||
|
var m: String = str(start_index)
|
||||||
|
if !marks.has(m) or marks[m].file == path: # Found
|
||||||
|
break
|
||||||
|
start_index += 1
|
||||||
|
|
||||||
|
# Shift all files from start_index down one
|
||||||
|
for i in range(start_index, -1, -1):
|
||||||
|
var m: String = str(i)
|
||||||
|
var prev_m: String = str(i - 1)
|
||||||
|
if !marks.has(prev_m):
|
||||||
|
continue
|
||||||
|
marks[m] = marks[prev_m]
|
||||||
|
|
||||||
|
# Mark "-1" won't be accessible to the user
|
||||||
|
# It's just the current file, and will be indexed next time the
|
||||||
|
# loop above ^^^ is called
|
||||||
|
marks["-1"] = {"file": path, "pos": Vector2i(-1, 0)}
|
||||||
|
|
||||||
|
|
||||||
|
func edit_script(path: String, pos: Vector2i):
|
||||||
|
var script = load(path)
|
||||||
|
if script == null:
|
||||||
|
status_bar.display_error('Could not open file "%s"' % path)
|
||||||
|
return ""
|
||||||
|
EditorInterface.edit_script(script, pos.y, pos.x)
|
||||||
|
|
||||||
|
|
||||||
|
#region LOAD
|
||||||
|
|
||||||
|
|
||||||
|
func _init_cursor(code_edit: CodeEdit, language: LANGUAGE):
|
||||||
|
if cursor != null:
|
||||||
|
cursor.queue_free()
|
||||||
|
|
||||||
|
cursor = Cursor.new()
|
||||||
|
code_edit.select(
|
||||||
|
code_edit.get_caret_line(),
|
||||||
|
code_edit.get_caret_column(),
|
||||||
|
code_edit.get_caret_line(),
|
||||||
|
code_edit.get_caret_column() + 1
|
||||||
|
)
|
||||||
|
cursor.code_edit = code_edit
|
||||||
|
cursor.language = language
|
||||||
|
cursor.globals = globals
|
||||||
|
|
||||||
|
|
||||||
|
func _init_command_line(code_edit: CodeEdit):
|
||||||
|
if command_line != null:
|
||||||
|
command_line.queue_free()
|
||||||
|
command_line = CommandLine.new()
|
||||||
|
|
||||||
|
command_line.code_edit = code_edit
|
||||||
|
cursor.command_line = command_line
|
||||||
|
command_line.cursor = cursor
|
||||||
|
command_line.globals = globals
|
||||||
|
command_line.hide()
|
||||||
|
|
||||||
|
|
||||||
|
func _init_status_bar():
|
||||||
|
if status_bar != null:
|
||||||
|
status_bar.queue_free()
|
||||||
|
status_bar = StatusBar.new()
|
||||||
|
cursor.status_bar = status_bar
|
||||||
|
command_line.status_bar = status_bar
|
||||||
|
|
||||||
|
|
||||||
|
func _load(forced: bool = false):
|
||||||
|
if globals == null:
|
||||||
|
globals = {}
|
||||||
|
|
||||||
|
var result: Dictionary = find_code_edit()
|
||||||
|
if result.is_empty():
|
||||||
|
return
|
||||||
|
var code_edit: CodeEdit = result.code_edit
|
||||||
|
var language: LANGUAGE = result.language
|
||||||
|
|
||||||
|
_init_cursor(code_edit, language)
|
||||||
|
_init_command_line(code_edit)
|
||||||
|
_init_status_bar()
|
||||||
|
|
||||||
|
# KeyMap
|
||||||
|
if key_map == null or forced:
|
||||||
|
key_map = KeyMap.new(cursor)
|
||||||
|
else:
|
||||||
|
key_map.cursor = cursor
|
||||||
|
cursor.key_map = key_map
|
||||||
|
|
||||||
|
var script_editor = EditorInterface.get_script_editor()
|
||||||
|
if script_editor == null:
|
||||||
|
return
|
||||||
|
var script_editor_base = script_editor.get_current_editor()
|
||||||
|
if script_editor_base == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
globals.command_line = command_line
|
||||||
|
globals.status_bar = status_bar
|
||||||
|
globals.code_edit = code_edit
|
||||||
|
globals.cursor = cursor
|
||||||
|
globals.script_editor = script_editor
|
||||||
|
globals.vim_plugin = self
|
||||||
|
globals.key_map = key_map
|
||||||
|
|
||||||
|
dispatcher = Dispatcher.new()
|
||||||
|
dispatcher.globals = globals
|
||||||
|
|
||||||
|
# Add nodes
|
||||||
|
if language != LANGUAGE.SHADER:
|
||||||
|
script_editor_base.add_child(cursor)
|
||||||
|
script_editor_base.add_child(status_bar)
|
||||||
|
script_editor_base.add_child(command_line)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get shader editor VBoxContainer
|
||||||
|
var shaders_container = code_edit
|
||||||
|
for i in 3:
|
||||||
|
shaders_container = shaders_container.get_parent()
|
||||||
|
if shaders_container == null:
|
||||||
|
# We do not print an error here because for this to fail,
|
||||||
|
# get_shader_code_edit() (through find_code_edit()) must have
|
||||||
|
# already failed
|
||||||
|
return
|
||||||
|
|
||||||
|
shaders_container.add_child(cursor)
|
||||||
|
shaders_container.add_child(status_bar)
|
||||||
|
shaders_container.add_child(command_line)
|
||||||
|
|
||||||
|
|
||||||
|
#endregion LOAD
|
||||||
|
|
||||||
|
|
||||||
|
func dispatch(command: String):
|
||||||
|
return dispatcher.dispatch(command)
|
||||||
|
|
||||||
|
|
||||||
|
## Finds whatever CodeEdit is open
|
||||||
|
func find_code_edit() -> Dictionary:
|
||||||
|
var code_edit: CodeEdit = get_shader_code_edit()
|
||||||
|
var language: LANGUAGE = LANGUAGE.SHADER
|
||||||
|
# Shader panel not open; normal gdscript code edit
|
||||||
|
if code_edit == null:
|
||||||
|
code_edit = get_regular_code_edit()
|
||||||
|
language = LANGUAGE.GDSCRIPT
|
||||||
|
if code_edit == null:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"code_edit": code_edit,
|
||||||
|
"language": language,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
## Gets the regular GDScript CodeEdit
|
||||||
|
func get_regular_code_edit():
|
||||||
|
var editor = EditorInterface.get_script_editor().get_current_editor()
|
||||||
|
return _select(editor, ["VSplitContainer", "CodeTextEditor", "CodeEdit"])
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME Handle cases where the shader editor is its own floating window
|
||||||
|
## Gets the shader editor's CodeEdit
|
||||||
|
## Returns Option<CodeEdit> (aka CodeEdit or null)
|
||||||
|
func get_shader_code_edit():
|
||||||
|
var container = get_shader_tabcontainer()
|
||||||
|
if container == null:
|
||||||
|
push_error(
|
||||||
|
"[Godot VIM] Failed to get shader editor's TabContainer. Vim will be disabled in the shader editor"
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
|
||||||
|
# Panel not open
|
||||||
|
if !container.is_visible_in_tree():
|
||||||
|
return null
|
||||||
|
|
||||||
|
var editors = container.get_children(false)
|
||||||
|
for tse in editors:
|
||||||
|
if !tse.visible: # Not open
|
||||||
|
continue
|
||||||
|
|
||||||
|
var code_edit = _select(
|
||||||
|
tse, ["VBoxContainer", "VSplitContainer", "ShaderTextEditor", "CodeEdit"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if code_edit == null:
|
||||||
|
push_error(
|
||||||
|
"[Godot Vim] Failed to get shader editor's CodeEdit. Vim will be disabled in the shader editor"
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
|
||||||
|
return code_edit
|
||||||
|
|
||||||
|
|
||||||
|
## Returns Option<TabContainer> (aka either TabContainer or null if it fails)
|
||||||
|
func get_shader_tabcontainer():
|
||||||
|
# Get the VSplitContainer containing the script editor and bottom panels
|
||||||
|
var container = EditorInterface.get_script_editor()
|
||||||
|
for i in 6:
|
||||||
|
container = container.get_parent()
|
||||||
|
if container == null:
|
||||||
|
# We don't print an error here, let us handle this exception elsewhere
|
||||||
|
return null
|
||||||
|
|
||||||
|
# Get code edit
|
||||||
|
container = _select(
|
||||||
|
container,
|
||||||
|
["PanelContainer", "VBoxContainer", "WindowWrapper", "HSplitContainer", "TabContainer"]
|
||||||
|
)
|
||||||
|
return container
|
||||||
|
|
||||||
|
|
||||||
|
func _select(obj: Node, types: Array[String]):
|
||||||
|
if not obj:
|
||||||
|
return null
|
||||||
|
for type in types:
|
||||||
|
for child in obj.get_children():
|
||||||
|
if child.is_class(type):
|
||||||
|
obj = child
|
||||||
|
continue
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
func _exit_tree():
|
||||||
|
if cursor != null:
|
||||||
|
cursor.queue_free()
|
||||||
|
if command_line != null:
|
||||||
|
command_line.queue_free()
|
||||||
|
if status_bar != null:
|
||||||
|
status_bar.queue_free()
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------------------------------------------
|
||||||
|
# ** UTIL **
|
||||||
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
func search_regex(text_edit: TextEdit, pattern: String, from_pos: Vector2i) -> RegExMatch:
|
||||||
|
var regex: RegEx = RegEx.new()
|
||||||
|
var err: int = regex.compile(pattern)
|
||||||
|
var idx: int = pos_to_idx(text_edit, from_pos)
|
||||||
|
var res: RegExMatch = regex.search(text_edit.text, idx)
|
||||||
|
if res == null:
|
||||||
|
return regex.search(text_edit.text, 0)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
func search_regex_backwards(text_edit: TextEdit, pattern: String, from_pos: Vector2i) -> RegExMatch:
|
||||||
|
var regex: RegEx = RegEx.new()
|
||||||
|
var err: int = regex.compile(pattern)
|
||||||
|
var idx: int = pos_to_idx(text_edit, from_pos)
|
||||||
|
# We use pop_back() so it doesn't print an error
|
||||||
|
var res: RegExMatch = regex.search_all(text_edit.text, 0, idx).pop_back()
|
||||||
|
if res == null:
|
||||||
|
return regex.search_all(text_edit.text).pop_back()
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
func pos_to_idx(text_edit: TextEdit, pos: Vector2i) -> int:
|
||||||
|
text_edit.select(0, 0, pos.y, pos.x)
|
||||||
|
var len: int = text_edit.get_selected_text().length()
|
||||||
|
text_edit.deselect()
|
||||||
|
return len
|
||||||
|
|
||||||
|
|
||||||
|
func idx_to_pos(text_edit: TextEdit, idx: int) -> Vector2i:
|
||||||
|
var line: int = text_edit.text.count("\n", 0, idx)
|
||||||
|
var col: int = idx - text_edit.text.rfind("\n", idx) - 1
|
||||||
|
return Vector2i(col, line)
|
||||||
|
|
||||||
|
|
||||||
|
func get_first_non_digit_idx(str: String) -> int:
|
||||||
|
if str.is_empty():
|
||||||
|
return -1
|
||||||
|
if str[0] == "0":
|
||||||
|
return 0 # '0...' is an exception
|
||||||
|
for i in str.length():
|
||||||
|
if !DIGITS.contains(str[i]):
|
||||||
|
return i
|
||||||
|
return -1 # All digits
|
||||||
|
|
||||||
|
|
||||||
|
## Repeat the function `f` and accumulate the result. A bit like Array::reduce()
|
||||||
|
## f: func(T) -> T where T is the previous output
|
||||||
|
func repeat_accum(count: int, inital_value: Variant, f: Callable) -> Variant:
|
||||||
|
var value: Variant = inital_value
|
||||||
|
for _index in count:
|
||||||
|
value = f.call(value)
|
||||||
|
return value
|
178
addons/godot_vim/remap.gd
Normal file
178
addons/godot_vim/remap.gd
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
class_name KeyRemap extends RefCounted
|
||||||
|
|
||||||
|
enum ApplyMode {
|
||||||
|
## Append this keybind to the end of the list
|
||||||
|
APPEND,
|
||||||
|
## Insert this keybind at the start of the list
|
||||||
|
PREPEND,
|
||||||
|
## Insert this keybind at the specified index
|
||||||
|
INSERT,
|
||||||
|
## Remove the specified keybind
|
||||||
|
REMOVE,
|
||||||
|
## Replace a keybind with this one
|
||||||
|
REPLACE,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const MODE = Constants.Mode
|
||||||
|
|
||||||
|
# Inner cmd
|
||||||
|
var inner: Dictionary = {}
|
||||||
|
var options: Dictionary = {"apply_mode": ApplyMode.APPEND}
|
||||||
|
|
||||||
|
|
||||||
|
func _init(keys: Array[String]):
|
||||||
|
assert(!keys.is_empty(), "cmd_keys cannot be empty")
|
||||||
|
inner = {"keys": keys}
|
||||||
|
|
||||||
|
|
||||||
|
## Returns self
|
||||||
|
func motion(motion_type: String, args: Dictionary = {}) -> KeyRemap:
|
||||||
|
var m: Dictionary = {"type": motion_type}
|
||||||
|
m.merge(args, true)
|
||||||
|
inner.motion = m
|
||||||
|
|
||||||
|
# Operator + Motion = OperatorMotion
|
||||||
|
if inner.get("type") == KeyMap.Operator:
|
||||||
|
inner.type = KeyMap.OperatorMotion
|
||||||
|
else:
|
||||||
|
inner.type = KeyMap.Motion
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
## Returns self
|
||||||
|
func operator(operator_type: String, args: Dictionary = {}) -> KeyRemap:
|
||||||
|
var o: Dictionary = {"type": operator_type}
|
||||||
|
o.merge(args, true)
|
||||||
|
inner.operator = o
|
||||||
|
|
||||||
|
# Motion + Operator = OperatorMotion
|
||||||
|
if inner.get("type") == KeyMap.Motion:
|
||||||
|
inner.type = KeyMap.OperatorMotion
|
||||||
|
else:
|
||||||
|
inner.type = KeyMap.Operator
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
## Returns self
|
||||||
|
func action(action_type: String, args: Dictionary = {}) -> KeyRemap:
|
||||||
|
var a: Dictionary = {"type": action_type}
|
||||||
|
a.merge(args, true)
|
||||||
|
inner.action = a
|
||||||
|
inner.type = KeyMap.Action
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
## Returns self
|
||||||
|
func with_context(mode: MODE) -> KeyRemap:
|
||||||
|
inner["context"] = mode
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
# `key_map` = KeyMap::key_map
|
||||||
|
func apply(key_map: Array[Dictionary]):
|
||||||
|
match options.get("apply_mode", ApplyMode.APPEND):
|
||||||
|
ApplyMode.APPEND:
|
||||||
|
key_map.append(inner)
|
||||||
|
|
||||||
|
ApplyMode.PREPEND:
|
||||||
|
var err: int = key_map.insert(0, inner)
|
||||||
|
if err != OK:
|
||||||
|
push_error("[Godot VIM] Failed to prepend keybind: %s" % error_string(err))
|
||||||
|
|
||||||
|
ApplyMode.INSERT:
|
||||||
|
var index: int = options.get("index", 0)
|
||||||
|
var err: int = key_map.insert(index, inner)
|
||||||
|
if err != OK:
|
||||||
|
push_error(
|
||||||
|
(
|
||||||
|
"[Godot VIM] Failed to insert keybind at index %s: %s"
|
||||||
|
% [index, error_string(err)]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
ApplyMode.REMOVE:
|
||||||
|
var index: int = _find(key_map, inner)
|
||||||
|
if index == -1:
|
||||||
|
return
|
||||||
|
key_map.remove_at(index)
|
||||||
|
|
||||||
|
ApplyMode.REPLACE:
|
||||||
|
var constraints: Dictionary = {"keys": inner.get("keys", [])}
|
||||||
|
var index: int = _find(key_map, constraints)
|
||||||
|
if index == -1:
|
||||||
|
return
|
||||||
|
|
||||||
|
# print('replacing at index ', index)
|
||||||
|
key_map[index] = inner
|
||||||
|
|
||||||
|
|
||||||
|
#region Apply options
|
||||||
|
|
||||||
|
|
||||||
|
## Append this keybind to the end of the list
|
||||||
|
## Returns self
|
||||||
|
func append() -> KeyRemap:
|
||||||
|
options = {"apply_mode": ApplyMode.APPEND}
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
## Insert this keybind at the start of the list
|
||||||
|
## Returns self
|
||||||
|
func prepend() -> KeyRemap:
|
||||||
|
options = {"apply_mode": ApplyMode.PREPEND}
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
## Insert this keybind at the specified index
|
||||||
|
func insert_at(index: int) -> KeyRemap:
|
||||||
|
options = {
|
||||||
|
"apply_mode": ApplyMode.INSERT,
|
||||||
|
"index": index,
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
## Removes the keybind from the list
|
||||||
|
## Returns self
|
||||||
|
func remove() -> KeyRemap:
|
||||||
|
options = {"apply_mode": ApplyMode.REMOVE}
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
## Replaces the keybind from the list with this new one
|
||||||
|
## Returns self
|
||||||
|
func replace():
|
||||||
|
options = {"apply_mode": ApplyMode.REPLACE}
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
func _find(key_map: Array[Dictionary], constraints: Dictionary) -> int:
|
||||||
|
var keys: Array[String] = constraints.get("keys")
|
||||||
|
if keys == null:
|
||||||
|
push_error("[Godot VIM::KeyRemap::_find()] Failed to find keybind: keys not specified")
|
||||||
|
return -1
|
||||||
|
if keys.is_empty():
|
||||||
|
push_error("[Godot VIM::KeyRemap::_find()] Failed to find keybind: keys cannot be empty")
|
||||||
|
return -1
|
||||||
|
|
||||||
|
for i in key_map.size():
|
||||||
|
var cmd: Dictionary = key_map[i]
|
||||||
|
# Check keys
|
||||||
|
var m: KeyMap.KeyMatch = KeyMap.match_keys(cmd.keys, keys)
|
||||||
|
if m != KeyMap.KeyMatch.Full:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If types DON'T match (if specified, ofc), skip
|
||||||
|
if constraints.has("type") and constraints.type != cmd.type:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If contexts DON'T match (if specified, ofc), skip
|
||||||
|
if constraints.get("context", -1) != cmd.get("context", -1):
|
||||||
|
continue
|
||||||
|
|
||||||
|
return i
|
||||||
|
return -1
|
88
addons/godot_vim/status_bar.gd
Normal file
88
addons/godot_vim/status_bar.gd
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
extends HBoxContainer
|
||||||
|
const ERROR_COLOR: String = "#ff8866"
|
||||||
|
const SPECIAL_COLOR: String = "#fcba03"
|
||||||
|
|
||||||
|
const Constants = preload("res://addons/godot_vim/constants.gd")
|
||||||
|
const MODE = Constants.Mode
|
||||||
|
|
||||||
|
var mode_label: Label
|
||||||
|
var main_label: RichTextLabel
|
||||||
|
var key_label: Label
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
var font = load("res://addons/godot_vim/hack_regular.ttf")
|
||||||
|
|
||||||
|
mode_label = Label.new()
|
||||||
|
mode_label.text = ""
|
||||||
|
mode_label.add_theme_color_override(&"font_color", Color.BLACK)
|
||||||
|
var stylebox: StyleBoxFlat = StyleBoxFlat.new()
|
||||||
|
stylebox.bg_color = Color.GOLD
|
||||||
|
stylebox.content_margin_left = 4.0
|
||||||
|
stylebox.content_margin_right = 4.0
|
||||||
|
stylebox.content_margin_top = 2.0
|
||||||
|
stylebox.content_margin_bottom = 2.0
|
||||||
|
mode_label.add_theme_stylebox_override(&"normal", stylebox)
|
||||||
|
mode_label.add_theme_font_override(&"font", font)
|
||||||
|
add_child(mode_label)
|
||||||
|
|
||||||
|
main_label = RichTextLabel.new()
|
||||||
|
main_label.bbcode_enabled = true
|
||||||
|
main_label.text = ""
|
||||||
|
main_label.fit_content = true
|
||||||
|
main_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
main_label.add_theme_font_override(&"normal_font", font)
|
||||||
|
add_child(main_label)
|
||||||
|
|
||||||
|
key_label = Label.new()
|
||||||
|
key_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||||
|
key_label.text = ""
|
||||||
|
key_label.add_theme_font_override(&"font", font)
|
||||||
|
key_label.custom_minimum_size.x = 120
|
||||||
|
add_child(key_label)
|
||||||
|
|
||||||
|
|
||||||
|
func display_text(text: String):
|
||||||
|
main_label.text = text
|
||||||
|
|
||||||
|
|
||||||
|
func display_error(text: String):
|
||||||
|
main_label.text = "[color=%s]%s" % [ERROR_COLOR, text]
|
||||||
|
|
||||||
|
|
||||||
|
func display_special(text: String):
|
||||||
|
main_label.text = "[color=%s]%s" % [SPECIAL_COLOR, text]
|
||||||
|
|
||||||
|
|
||||||
|
func set_mode_text(mode: MODE):
|
||||||
|
var stylebox: StyleBoxFlat = mode_label.get_theme_stylebox(&"normal")
|
||||||
|
match mode:
|
||||||
|
MODE.NORMAL:
|
||||||
|
mode_label.text = "NORMAL"
|
||||||
|
stylebox.bg_color = Color.LIGHT_SALMON
|
||||||
|
MODE.INSERT:
|
||||||
|
mode_label.text = "INSERT"
|
||||||
|
stylebox.bg_color = Color.POWDER_BLUE
|
||||||
|
MODE.VISUAL:
|
||||||
|
mode_label.text = "VISUAL"
|
||||||
|
stylebox.bg_color = Color.PLUM
|
||||||
|
MODE.VISUAL_LINE:
|
||||||
|
mode_label.text = "VISUAL LINE"
|
||||||
|
stylebox.bg_color = Color.PLUM
|
||||||
|
MODE.COMMAND:
|
||||||
|
mode_label.text = "COMMAND"
|
||||||
|
stylebox.bg_color = Color.TOMATO
|
||||||
|
_:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func set_keys_text(text: String):
|
||||||
|
key_label.text = text
|
||||||
|
|
||||||
|
|
||||||
|
func clear():
|
||||||
|
main_label.text = ""
|
||||||
|
|
||||||
|
|
||||||
|
func clear_keys():
|
||||||
|
key_label.text = ""
|
1
icon.svg
Normal file
1
icon.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
After Width: | Height: | Size: 994 B |
37
icon.svg.import
Normal file
37
icon.svg.import
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://b10c1776j6j60"
|
||||||
|
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://icon.svg"
|
||||||
|
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
28
project.godot
Normal file
28
project.godot
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
; Engine configuration file.
|
||||||
|
; It's best edited using the editor UI and not directly,
|
||||||
|
; since the parameters that go here are not all obvious.
|
||||||
|
;
|
||||||
|
; Format:
|
||||||
|
; [section] ; section goes between []
|
||||||
|
; param=value ; assign values to parameters
|
||||||
|
|
||||||
|
config_version=5
|
||||||
|
|
||||||
|
[application]
|
||||||
|
|
||||||
|
config/name="Semi-Idle ARPG"
|
||||||
|
run/main_scene="res://scenes/testScene.tscn"
|
||||||
|
config/features=PackedStringArray("4.3", "Forward Plus")
|
||||||
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
|
[display]
|
||||||
|
|
||||||
|
window/stretch/scale=3.0
|
||||||
|
|
||||||
|
[dotnet]
|
||||||
|
|
||||||
|
project/assembly_name="Semi-Idle ARPG"
|
||||||
|
|
||||||
|
[editor_plugins]
|
||||||
|
|
||||||
|
enabled=PackedStringArray("res://addons/godot-vim/plugin.cfg")
|
56
scenes/character.tscn
Normal file
56
scenes/character.tscn
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://ba27ufs8eak0b"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://scripts/character.gd" id="2_bft53"]
|
||||||
|
|
||||||
|
[node name="Character" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 0
|
||||||
|
script = ExtResource("2_bft53")
|
||||||
|
|
||||||
|
[node name="CharacterPanel" type="PanelContainer" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = 567.0
|
||||||
|
offset_top = 300.0
|
||||||
|
offset_right = 675.0
|
||||||
|
offset_bottom = 350.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="CharacterPanel"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
|
||||||
|
[node name="CharacterName" type="Label" parent="CharacterPanel/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
text = "Name"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="CharacterPanel/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
size_flags_vertical = 4
|
||||||
|
|
||||||
|
[node name="CharacterCurrHealth" type="Label" parent="CharacterPanel/VBoxContainer/HBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
text = "Current"
|
||||||
|
|
||||||
|
[node name="CharacterHealthSep" type="Label" parent="CharacterPanel/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "/"
|
||||||
|
|
||||||
|
[node name="CharacterMaxHealth" type="Label" parent="CharacterPanel/VBoxContainer/HBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
text = "Max
|
||||||
|
"
|
6
scenes/mapTile.tscn
Normal file
6
scenes/mapTile.tscn
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://bavjvxaourccu"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://scripts/map_tile.gd" id="1_jtqny"]
|
||||||
|
|
||||||
|
[node name="MapTile" type="Node2D"]
|
||||||
|
script = ExtResource("1_jtqny")
|
12
scenes/npc.tscn
Normal file
12
scenes/npc.tscn
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://cdu3sqa0k8dgs"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://scripts/npc.gd" id="2_a7mo0"]
|
||||||
|
|
||||||
|
[node name="NPC" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 0
|
||||||
|
offset_left = 669.0
|
||||||
|
offset_top = 330.0
|
||||||
|
offset_right = 669.0
|
||||||
|
offset_bottom = 330.0
|
||||||
|
script = ExtResource("2_a7mo0")
|
67
scenes/testScene.tscn
Normal file
67
scenes/testScene.tscn
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://dhvk3terpgsp3"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://scripts/test_scene.gd" id="1_mj8nf"]
|
||||||
|
|
||||||
|
[sub_resource type="LabelSettings" id="LabelSettings_ppp5s"]
|
||||||
|
|
||||||
|
[node name="TestScene" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
script = ExtResource("1_mj8nf")
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 8
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="OutputLabels" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 4
|
||||||
|
|
||||||
|
[node name="TestMaxHealth" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer/OutputLabels"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 8
|
||||||
|
text = "Max Health:"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="Output" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 4
|
||||||
|
|
||||||
|
[node name="TestMaxHealthVal" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer/Output"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
text = "Value"
|
||||||
|
label_settings = SubResource("LabelSettings_ppp5s")
|
||||||
|
|
||||||
|
[node name="Controls" type="VBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 8
|
||||||
|
|
||||||
|
[node name="TestButton" type="Button" parent="MarginContainer/VBoxContainer/Controls"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Test"
|
||||||
|
|
||||||
|
[node name="ExitButton" type="Button" parent="MarginContainer/VBoxContainer/Controls"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Exit"
|
||||||
|
|
||||||
|
[connection signal="pressed" from="MarginContainer/VBoxContainer/Controls/TestButton" to="." method="_on_test_button_pressed"]
|
||||||
|
[connection signal="pressed" from="MarginContainer/VBoxContainer/Controls/ExitButton" to="." method="_on_exit_button_pressed"]
|
12
scripts/character.gd
Normal file
12
scripts/character.gd
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
class_name Character
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
|
||||||
|
@export var charName := "Character"
|
||||||
|
@export var maxHealth := 10
|
||||||
|
@export var dmgTaken := 0
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
%CharacterName.text = self.charName
|
||||||
|
%CharacterMaxHealth.text = str(self.maxHealth)
|
||||||
|
%CharacterCurrHealth.text = str(self.maxHealth - self.dmgTaken)
|
17
scripts/equipmentGen.gd
Normal file
17
scripts/equipmentGen.gd
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# this script handles everything related to equipment generation
|
||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
var gearItem = load("gearRand.gd")
|
||||||
|
|
||||||
|
|
||||||
|
# func genPiece(tier: int, quality: int):
|
||||||
|
# # gernarate a single piece of equipment of tier and quality
|
||||||
|
# var gearPiece = gearItem
|
||||||
|
#
|
||||||
|
# return statValues
|
||||||
|
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
var gearPiece = gearItem.new(tier=2, quality=4)
|
||||||
|
print (gearPiece.statValues)
|
||||||
|
quit(
|
22
scripts/gear.gd
Normal file
22
scripts/gear.gd
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
class_name Gear
|
||||||
|
extends "item.gd"
|
||||||
|
|
||||||
|
|
||||||
|
# the basic stats that occur on gear
|
||||||
|
const STATLIST = [
|
||||||
|
"str", "dex", "int",
|
||||||
|
"con", "res", "spd",
|
||||||
|
"dot", "crd", "crc"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
var statValues = {}
|
||||||
|
var tier: int = 0
|
||||||
|
var slot: int = 0
|
||||||
|
var skill: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
for stat in STATLIST:
|
||||||
|
statValues[stat] = 0
|
||||||
|
|
39
scripts/gearRand.gd
Normal file
39
scripts/gearRand.gd
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
class_name GearRandom extends "gear.gd"
|
||||||
|
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
# generate random stats piece of gear with tier and quality
|
||||||
|
randomize()
|
||||||
|
var randStatValues = {}
|
||||||
|
# max tier of 10, max stats of 1000
|
||||||
|
var randStatMax = tier*100
|
||||||
|
match quality:
|
||||||
|
0:
|
||||||
|
# white, common
|
||||||
|
randStatValues["con"] = randi_range(0,tier*100)
|
||||||
|
1:
|
||||||
|
# green, uncommon
|
||||||
|
var statListQual1 = STATLIST.slice(0, 4)
|
||||||
|
for stat in statListQual1:
|
||||||
|
randStatValues[stat] = randi_range(0, randStatMax)
|
||||||
|
2:
|
||||||
|
# blue, rare
|
||||||
|
for stat in STATLIST:
|
||||||
|
randStatValues[stat] = randi_range(0, randStatMax)
|
||||||
|
3:
|
||||||
|
# purple, epic
|
||||||
|
for stat in STATLIST:
|
||||||
|
randStatValues[stat] = randi_range(0.3*randStatMax, randStatMax)
|
||||||
|
4:
|
||||||
|
# orange?, legendary
|
||||||
|
for stat in STATLIST:
|
||||||
|
randStatValues[stat] = randi_range(0.6*randStatMax, randStatMax)
|
||||||
|
5:
|
||||||
|
# yellow or red?, artifact
|
||||||
|
for stat in STATLIST:
|
||||||
|
randStatValues[stat] = randi_range(0.9*randStatMax, randStatMax)
|
||||||
|
_:
|
||||||
|
# should never occur, but just in case...
|
||||||
|
print("Unknown Equipment Quality")
|
||||||
|
|
||||||
|
self.statValues = randStatValues
|
10
scripts/item.gd
Normal file
10
scripts/item.gd
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
class_name Item
|
||||||
|
extends Object
|
||||||
|
|
||||||
|
|
||||||
|
var quality: int = 0
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
# simple random integer from 0 to 5 for the different item qualities
|
||||||
|
randomize()
|
||||||
|
self.quality = randi_range(0, 5)
|
12
scripts/map_tile.gd
Normal file
12
scripts/map_tile.gd
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
extends Node2D
|
||||||
|
|
||||||
|
|
||||||
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
func _ready() -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
pass
|
37
scripts/npc.gd
Normal file
37
scripts/npc.gd
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
class_name NPC
|
||||||
|
extends Character
|
||||||
|
|
||||||
|
|
||||||
|
enum npcDifficulties { MINION, NORMAL, MINIBOSS, BOSS, ELITEBOSS, BBEG }
|
||||||
|
|
||||||
|
@export var npcDifficulty: npcDifficulties
|
||||||
|
@export var tier: int
|
||||||
|
|
||||||
|
func _random_mod_health() -> void:
|
||||||
|
randomize()
|
||||||
|
# noise factor
|
||||||
|
var noise_factor = randf_range(7, 10)
|
||||||
|
self.maxHealth *= noise_factor
|
||||||
|
# difficulty factor
|
||||||
|
match self.npcDifficulty:
|
||||||
|
npcDifficulties.MINION:
|
||||||
|
self.maxHealth /= 2
|
||||||
|
npcDifficulties.MINIBOSS:
|
||||||
|
self.maxHealth *= 2
|
||||||
|
npcDifficulties.BOSS:
|
||||||
|
self.maxHealth *= 4
|
||||||
|
npcDifficulties.ELITEBOSS:
|
||||||
|
self.maxHealth *= 8
|
||||||
|
npcDifficulties.BBEG:
|
||||||
|
self.maxHealth *= 16
|
||||||
|
# tier factor
|
||||||
|
self.maxHealth *= exp(self.tier)
|
||||||
|
|
||||||
|
# fun factor (just additional factor to tweak)
|
||||||
|
self.maxHealth *= 10
|
||||||
|
|
||||||
|
|
||||||
|
func _init(npcDifficulty: npcDifficulties = npcDifficulties.NORMAL, tier: int = 0) -> void:
|
||||||
|
self.npcDifficulty = npcDifficulty
|
||||||
|
self.tier = tier
|
||||||
|
_random_mod_health()
|
33
scripts/test_scene.gd
Normal file
33
scripts/test_scene.gd
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
extends Control
|
||||||
|
|
||||||
|
|
||||||
|
func _on_exit_button_pressed() -> void:
|
||||||
|
get_tree().quit()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_test_button_pressed() -> void:
|
||||||
|
var rand_difficulty = NPC.npcDifficulties.values().pick_random()
|
||||||
|
var rand_tier = randi_range(0, 10)
|
||||||
|
var anNPC = NPC.new(rand_difficulty, rand_tier)
|
||||||
|
var TestMaxHealthVal = $MarginContainer/HBoxContainer/Output/TestMaxHealthVal
|
||||||
|
TestMaxHealthVal.text = str(anNPC.maxHealth)
|
||||||
|
#TestMaxHealthVal.label_settings = LabelSettings.new()
|
||||||
|
TestMaxHealthVal.label_settings.outline_size = 4
|
||||||
|
TestMaxHealthVal.label_settings.outline_color = Color("#1D2021")
|
||||||
|
match anNPC.npcDifficulty:
|
||||||
|
NPC.npcDifficulties.MINION:
|
||||||
|
TestMaxHealthVal.label_settings.font_color = Color.LIGHT_GRAY
|
||||||
|
NPC.npcDifficulties.NORMAL:
|
||||||
|
TestMaxHealthVal.label_settings.font_color = Color.SEA_GREEN
|
||||||
|
NPC.npcDifficulties.MINIBOSS:
|
||||||
|
TestMaxHealthVal.label_settings.font_color = Color.ROYAL_BLUE
|
||||||
|
NPC.npcDifficulties.BOSS:
|
||||||
|
TestMaxHealthVal.label_settings.font_color = Color.PURPLE
|
||||||
|
NPC.npcDifficulties.ELITEBOSS:
|
||||||
|
TestMaxHealthVal.label_settings.font_color = Color.ORANGE
|
||||||
|
NPC.npcDifficulties.BBEG:
|
||||||
|
TestMaxHealthVal.label_settings.font_color = Color.GOLD
|
||||||
|
TestMaxHealthVal.label_settings.outline_size = 8
|
||||||
|
TestMaxHealthVal.label_settings.outline_color = Color.DARK_RED
|
||||||
|
#anNPC.position = Vector2(64, 64)
|
||||||
|
#add_child(anNPC)
|
Loading…
Reference in New Issue
Block a user