parent
c57f07ffc2
commit
25ec8d2585
|
|
@ -0,0 +1,4 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
project.godot
|
||||
*.import
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Joshua Najera
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
VIM bindings for Godot 4
|
||||
|
||||
recently improved thanks to wenqiangwang
|
||||
If you would like ctrl+F to be move-forward by page then uncomment the following line
|
||||
|
||||
#"Ctrl+F": 1, ## Uncomment if you want Ctrl+F for move forward by page
|
||||
|
||||
### Supported Mode
|
||||
|
||||
- Normal mode
|
||||
- Insert mode
|
||||
- Visual mode
|
||||
- Visual line mode
|
||||
|
||||
### Supported motions
|
||||
|
||||
h, l, j, k, +, -
|
||||
^, 0, $, |
|
||||
H, L, M,
|
||||
c-f, c-b, c-d, c-u,
|
||||
G, gg
|
||||
w, W, e, E, b, ge
|
||||
%, f, F, t, T, ;
|
||||
*, #, /, n, N
|
||||
aw, a(, a{, a[, a", a'
|
||||
iw, i(, i{, i[, i", i'
|
||||
|
||||
### Supported operator
|
||||
|
||||
c, C,
|
||||
d, D, x, X,
|
||||
y, Y,
|
||||
u, U, ~
|
||||
|
||||
### Supported actions
|
||||
|
||||
p,
|
||||
u, c-r,
|
||||
c-o, c-i,
|
||||
za, zM, zR,
|
||||
q, @, .,
|
||||
>, <
|
||||
m, '
|
||||
|
||||
### Override Default Godot Shortcuts with `godot-vim`'s ones
|
||||
|
||||
Note that all non-ascii character mappings that are already mapped in the default Godot editor have to be unmapped from the Editor settings (Editor >> Editor Settings >> Shorcuts) before being usable with `godot-vim`.
|
||||
|
||||
This currently goes for:
|
||||
|
||||
- `Ctrl+R`
|
||||
- `Ctrl+U`
|
||||
- `Ctrl+D`
|
||||
|
||||
See the full list of non-ascii shortucts that may already be mapped by Godot and thus wouldn't work in `godot-vim` before releasing them in Godot settings: https://github.com/joshnajera/godot-vim/blob/main/addons/godot-vim/godot-vim.gd#L135
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Ant navigation is wonky -> check if next_dir behaves as expected.
|
||||
ant sprite does not budge from centre of screen no matter what i try -> fix that
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1 @@
|
|||
uid://t4imnbu2n5vw
|
||||
|
|
@ -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 |
|
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="godot-vim"
|
||||
description="VIM bindings for godot4"
|
||||
author="Josh N"
|
||||
version="0.3"
|
||||
script="godot-vim.gd"
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
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 Dispatcher = preload("res://addons/godot_vim/dispatcher.gd")
|
||||
const Mode = Constants.Mode
|
||||
|
||||
const Marks = preload("res://addons/godot_vim/commands/marks.gd")
|
||||
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 dispatcher: Dispatcher
|
||||
|
||||
var is_paused: bool = false
|
||||
var search_pattern: String = ''
|
||||
|
||||
func _ready():
|
||||
dispatcher = Dispatcher.new()
|
||||
dispatcher.globals = globals
|
||||
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
|
||||
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.scroll_vertical = code_edit.get_scroll_pos_for_line(pos.y)
|
||||
|
||||
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 dispatcher.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)
|
||||
|
||||
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)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://lk5et75ba1pr
|
||||
|
|
@ -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)
|
||||
else:
|
||||
api.status_bar.display_error('Pattern not found: "%s"' % [api.command_line.search_pattern])
|
||||
api.cursor.set_mode(Mode.NORMAL)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dp7ck72w5b66n
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
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(), 0)
|
||||
api.cursor.set_mode(Mode.NORMAL)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cu4md7kr2qrp
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
const Contants = preload("res://addons/godot_vim/constants.gd")
|
||||
const StatusBar = preload("res://addons/godot_vim/status_bar.gd")
|
||||
const Mode = Contants.Mode
|
||||
|
||||
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 display_mark = func(key: String, m: Dictionary) -> String:
|
||||
var pos: Vector2i = m.get('pos', Vector2i())
|
||||
var file: String = m.get('file', '')
|
||||
return "\n%s\t\t%s \t%s \t\t %s" % [key, pos.y, pos.x, file]
|
||||
|
||||
var text: String = "[color=%s]List of all marks[/color]\nmark\tline\tcol \t file" % StatusBar.SPECIAL_COLOR
|
||||
for key in marks.keys():
|
||||
var unicode: int = key.unicode_at(0)
|
||||
if (unicode < 65 or unicode > 90) and (unicode < 97 or unicode > 122):
|
||||
continue
|
||||
text += display_mark.call(key, marks[key])
|
||||
for key in marks.keys():
|
||||
var unicode: int = key.unicode_at(0)
|
||||
if (unicode >= 65 and unicode <= 90) or (unicode >= 97 and unicode <= 122) or key == "-1":
|
||||
continue
|
||||
text += display_mark.call(key, marks[key])
|
||||
|
||||
api.status_bar.display_text(text, Control.TEXT_DIRECTION_LTR)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cf1an2q01i3ix
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
enum Mode { NORMAL, INSERT, VISUAL, VISUAL_LINE, COMMAND }
|
||||
|
||||
# Used for commands like "w" "b" and "e" respectively
|
||||
enum WordEdgeMode { WORD, BEGINNING, END }
|
||||
|
||||
const SPACES: String = " \t"
|
||||
const KEYWORDS: String = ".,\"'-=+!@#$%^&*()[]{}?~/\\<>:;"
|
||||
const DIGITS: String = "0123456789"
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://d2ro7q1ec5gk0
|
||||
|
|
@ -0,0 +1,722 @@
|
|||
extends Control
|
||||
|
||||
const CommandLine = preload("res://addons/godot_vim/command_line.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 WordEdgeMode = Constants.WordEdgeMode
|
||||
const KEYWORDS = Constants.KEYWORDS
|
||||
const SPACES = Constants.SPACES
|
||||
|
||||
var code_edit: CodeEdit
|
||||
var command_line: CommandLine
|
||||
var status_bar: StatusBar
|
||||
|
||||
var mode: Mode = Mode.NORMAL
|
||||
var caret: Vector2
|
||||
var input_stream: String = ""
|
||||
var selection_from: Vector2i = Vector2i() # For visual modes
|
||||
var selection_to: Vector2i = Vector2i() # For visual modes
|
||||
var globals: Dictionary = {}
|
||||
|
||||
func _init():
|
||||
set_focus_mode(FOCUS_ALL)
|
||||
|
||||
func _ready():
|
||||
code_edit.connect("focus_entered", focus_entered)
|
||||
code_edit.connect("caret_changed", cursor_changed)
|
||||
call_deferred('set_mode', Mode.NORMAL)
|
||||
|
||||
func cursor_changed():
|
||||
draw_cursor()
|
||||
|
||||
func focus_entered():
|
||||
if mode == Mode.NORMAL:
|
||||
code_edit.release_focus()
|
||||
self.grab_focus()
|
||||
|
||||
|
||||
func reset_normal():
|
||||
code_edit.cancel_code_completion()
|
||||
input_stream = ''
|
||||
set_mode(Mode.NORMAL)
|
||||
selection_from = Vector2i.ZERO
|
||||
selection_to = Vector2i.ZERO
|
||||
set_column(code_edit.get_caret_column())
|
||||
return
|
||||
|
||||
|
||||
func back_to_normal_mode(event, m):
|
||||
var old_caret_pos = code_edit.get_caret_column()
|
||||
if Input.is_key_pressed(KEY_ESCAPE):
|
||||
if m == Mode.INSERT:
|
||||
handle_input_stream('l')
|
||||
reset_normal()
|
||||
return 1
|
||||
if m == Mode.INSERT:
|
||||
var old_time = Time.get_ticks_msec()
|
||||
if Input.is_key_label_pressed(KEY_J):
|
||||
old_caret_pos = code_edit.get_caret_column()
|
||||
if Time.get_ticks_msec() - old_time < 700 and Input.is_key_label_pressed(KEY_K):
|
||||
code_edit.backspace()
|
||||
code_edit.cancel_code_completion()
|
||||
reset_normal()
|
||||
handle_input_stream('l')
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
func _input(event):
|
||||
if back_to_normal_mode(event, mode): return
|
||||
draw_cursor()
|
||||
code_edit.cancel_code_completion()
|
||||
if !has_focus(): return
|
||||
if !event is InputEventKey: return
|
||||
if !event.pressed: return
|
||||
if mode == Mode.INSERT or mode == Mode.COMMAND: return
|
||||
|
||||
if event.keycode == KEY_ESCAPE:
|
||||
input_stream = ''
|
||||
return
|
||||
|
||||
|
||||
var ch: String
|
||||
if !event is InputEventMouseMotion and !event is InputEventMouseButton:
|
||||
ch = char(event.unicode)
|
||||
|
||||
if Input.is_key_pressed(KEY_ENTER):
|
||||
ch = '<CR>'
|
||||
if Input.is_key_pressed(KEY_TAB):
|
||||
ch = '<TAB>'
|
||||
if Input.is_key_pressed(KEY_CTRL):
|
||||
if OS.is_keycode_unicode(event.keycode):
|
||||
var c: String = char(event.keycode)
|
||||
if !Input.is_key_pressed(KEY_SHIFT):
|
||||
c = c.to_lower()
|
||||
ch = '<C-%s>' % c
|
||||
|
||||
input_stream += ch
|
||||
status_bar.display_text(input_stream)
|
||||
|
||||
var s: int = globals.vim_plugin.get_first_non_digit_idx(input_stream)
|
||||
if s == -1: return # All digits
|
||||
|
||||
var cmd: String = input_stream.substr(s)
|
||||
var count: int = maxi( input_stream.left(s).to_int(), 1 )
|
||||
for i in count:
|
||||
input_stream = handle_input_stream(cmd)
|
||||
|
||||
|
||||
func handle_input_stream(stream: String) -> String:
|
||||
# BEHOLD, THE IF STATEMENT HELL!!! MUAHAHAHAHa
|
||||
if stream == 'h':
|
||||
move_column(-1)
|
||||
return ''
|
||||
if stream == 'j':
|
||||
move_line(+1)
|
||||
return ''
|
||||
if stream == 'k':
|
||||
move_line(-1)
|
||||
return ''
|
||||
if stream == 'l':
|
||||
move_column(+1)
|
||||
return ''
|
||||
if stream.to_lower().begins_with('w'):
|
||||
var p: Vector2i = get_word_edge_pos(get_line(), get_column(), '' if stream[0] == 'W' else KEYWORDS, WordEdgeMode.WORD)
|
||||
set_caret_pos(p.y, p.x)
|
||||
return ''
|
||||
if stream.to_lower().begins_with('e'):
|
||||
var p: Vector2i = get_word_edge_pos(get_line(), get_column(), '' if stream[0] == 'E' else KEYWORDS, WordEdgeMode.END)
|
||||
set_caret_pos(p.y, p.x)
|
||||
return ''
|
||||
if stream.to_lower().begins_with('b'):
|
||||
var p: Vector2i = get_word_edge_pos(get_line(), get_column(), '' if stream[0] == 'B' else KEYWORDS, WordEdgeMode.BEGINNING)
|
||||
set_caret_pos(p.y, p.x)
|
||||
return ''
|
||||
|
||||
if stream.to_lower() .begins_with('f') or stream.to_lower() .begins_with('t'):
|
||||
if stream.length() == 1: return stream
|
||||
|
||||
var char: String = stream[1] # TODO check for <TAB>, <CR> and <Ctrl-somethign>
|
||||
globals.last_search = stream.left(2) # First 2 in case it's longer
|
||||
var col: int = find_char_motion(get_line(), get_column(), stream[0], char)
|
||||
if col >= 0:
|
||||
set_column(col)
|
||||
return ''
|
||||
if stream.begins_with(';') and globals.has('last_search'):
|
||||
var cmd: String = globals.last_search[0]
|
||||
var col: int = find_char_motion(get_line(), get_column(), cmd, globals.last_search[1])
|
||||
if col >= 0:
|
||||
set_column(col)
|
||||
return ''
|
||||
if stream.begins_with(',') and globals.has('last_search'):
|
||||
var cmd: String = globals.last_search[0]
|
||||
cmd = cmd.to_upper() if is_lowercase(cmd) else cmd.to_lower()
|
||||
var col: int = find_char_motion(get_line(), get_column(), cmd, globals.last_search[1])
|
||||
if col >= 0:
|
||||
set_column(col)
|
||||
return ''
|
||||
|
||||
if mode == Mode.VISUAL: # TODO make it work for visual line too
|
||||
var range: Array = calc_double_motion_region(selection_to, stream)
|
||||
if range.size() == 1: return stream
|
||||
if range.size() == 2:
|
||||
selection_from = range[0]
|
||||
selection_to = range[1]
|
||||
update_visual_selection()
|
||||
|
||||
if stream.begins_with('J') and mode == Mode.NORMAL:
|
||||
code_edit.begin_complex_operation()
|
||||
code_edit.select( get_line(), get_line_length(), get_line()+1, code_edit.get_first_non_whitespace_column(get_line()+1) )
|
||||
code_edit.delete_selection()
|
||||
code_edit.deselect()
|
||||
code_edit.insert_text_at_caret(' ')
|
||||
code_edit.end_complex_operation()
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
if stream.begins_with('d'):
|
||||
if is_mode_visual(mode):
|
||||
DisplayServer.clipboard_set( '\r' + code_edit.get_selected_text() )
|
||||
code_edit.delete_selection()
|
||||
move_line(+1)
|
||||
set_mode(Mode.NORMAL)
|
||||
return ''
|
||||
|
||||
if stream.begins_with('dd') and mode == Mode.NORMAL:
|
||||
code_edit.select( get_line()-1, get_line_length(get_line()-1), get_line(), get_line_length() )
|
||||
DisplayServer.clipboard_set( '\r' + code_edit.get_selected_text() )
|
||||
code_edit.delete_selection()
|
||||
move_line(+1)
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
|
||||
var range: Array = calc_double_motion_region(get_caret_pos(), stream, 1)
|
||||
if range.size() == 0: return ''
|
||||
if range.size() == 1: return stream
|
||||
if range.size() == 2:
|
||||
code_edit.select(range[0].y, range[0].x, range[1].y, range[1].x + 1)
|
||||
code_edit.cut()
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
|
||||
if mode == Mode.NORMAL and stream.begins_with('D'):
|
||||
code_edit.select( get_line(), code_edit.get_caret_column(), get_line(), get_line_length() )
|
||||
code_edit.cut()
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
if stream.begins_with('p'):
|
||||
code_edit.begin_complex_operation()
|
||||
if is_mode_visual(mode):
|
||||
code_edit.delete_selection()
|
||||
if DisplayServer.clipboard_get().begins_with('\r\n'):
|
||||
set_column(get_line_length())
|
||||
else:
|
||||
move_column(+1)
|
||||
code_edit.deselect()
|
||||
code_edit.paste()
|
||||
move_column(-1)
|
||||
code_edit.end_complex_operation()
|
||||
set_mode(Mode.NORMAL)
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
if stream.begins_with('P'):
|
||||
status_bar.display_error("Unimplemented command: P")
|
||||
return ''
|
||||
if stream.begins_with('$'):
|
||||
set_column(get_line_length())
|
||||
return ''
|
||||
if stream.begins_with('^'):
|
||||
set_column( code_edit.get_first_non_whitespace_column(get_line()) )
|
||||
return ''
|
||||
if stream == 'G':
|
||||
set_line(code_edit.get_line_count())
|
||||
return ''
|
||||
if stream.begins_with('g'):
|
||||
if stream.begins_with('gg'):
|
||||
set_line(0)
|
||||
return ''
|
||||
|
||||
if stream.begins_with('gc') and is_mode_visual(mode):
|
||||
code_edit.begin_complex_operation()
|
||||
for line in range( min(selection_from.y, selection_to.y), max(selection_from.y, selection_to.y)+1 ):
|
||||
toggle_comment(line)
|
||||
code_edit.end_complex_operation()
|
||||
set_mode(Mode.NORMAL)
|
||||
return ''
|
||||
if stream.begins_with('gcc') and mode == Mode.NORMAL:
|
||||
toggle_comment(get_line())
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
return stream
|
||||
|
||||
if stream == '0':
|
||||
set_column(0)
|
||||
return ''
|
||||
if stream == 'i' and mode == Mode.NORMAL:
|
||||
set_mode(Mode.INSERT)
|
||||
return ''
|
||||
if stream == 'a' and mode == Mode.NORMAL:
|
||||
set_mode(Mode.INSERT)
|
||||
move_column(+1)
|
||||
return ''
|
||||
if stream == 'I' and mode == Mode.NORMAL:
|
||||
set_column(code_edit.get_first_non_whitespace_column(get_line()))
|
||||
set_mode(Mode.INSERT)
|
||||
return ''
|
||||
if stream.begins_with('A') and mode == Mode.NORMAL:
|
||||
set_mode(Mode.INSERT)
|
||||
set_column(get_line_length())
|
||||
return ''
|
||||
if stream == 'v':
|
||||
set_mode(Mode.VISUAL)
|
||||
return ''
|
||||
if stream == 'V':
|
||||
set_mode(Mode.VISUAL_LINE)
|
||||
return ''
|
||||
if stream.begins_with('o'):
|
||||
if is_mode_visual(mode):
|
||||
var tmp: Vector2i = selection_from
|
||||
selection_from = selection_to
|
||||
selection_to = tmp
|
||||
return ''
|
||||
|
||||
var ind: int = code_edit.get_first_non_whitespace_column(get_line())
|
||||
if code_edit.get_line(get_line()).ends_with(':'):
|
||||
ind += 1
|
||||
var line: int = code_edit.get_caret_line()
|
||||
code_edit.insert_line_at(line + int(line < code_edit.get_line_count() - 1), "\t".repeat(ind))
|
||||
move_line(+1)
|
||||
set_column(ind)
|
||||
set_mode(Mode.INSERT)
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
if stream.begins_with('O') and mode == Mode.NORMAL:
|
||||
var ind: int = code_edit.get_first_non_whitespace_column(get_line())
|
||||
code_edit.insert_line_at(get_line(), "\t".repeat(ind))
|
||||
move_line(-1)
|
||||
set_column(ind)
|
||||
set_mode(Mode.INSERT)
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
|
||||
if stream == 'x':
|
||||
code_edit.copy()
|
||||
code_edit.delete_selection()
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
if stream.begins_with('s'):
|
||||
code_edit.cut()
|
||||
set_mode(Mode.INSERT)
|
||||
return ''
|
||||
if stream == 'u':
|
||||
code_edit.undo()
|
||||
set_mode(Mode.NORMAL)
|
||||
return ''
|
||||
if stream.begins_with('<C-r>'):
|
||||
code_edit.redo()
|
||||
return ''
|
||||
if stream.begins_with('r') and mode == Mode.NORMAL:
|
||||
if stream.length() < 2: return stream
|
||||
code_edit.begin_complex_operation()
|
||||
code_edit.delete_selection()
|
||||
var ch: String = stream[1]
|
||||
if stream.substr(1).begins_with('<CR>'):
|
||||
ch = '\n'
|
||||
elif stream.substr(1).begins_with('<TAB>'):
|
||||
ch = '\t'
|
||||
code_edit.insert_text_at_caret(ch)
|
||||
move_column(-1)
|
||||
code_edit.end_complex_operation()
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
if stream.begins_with('y'):
|
||||
if is_mode_visual(mode):
|
||||
code_edit.copy()
|
||||
set_mode(Mode.NORMAL)
|
||||
return ''
|
||||
|
||||
if stream.length() == 1: return stream
|
||||
if stream.begins_with('yy') and mode == Mode.NORMAL:
|
||||
code_edit.select(code_edit.get_caret_line(), 0, code_edit.get_caret_line(), get_line_length())
|
||||
DisplayServer.clipboard_set( '\r\n' + code_edit.get_selected_text() )
|
||||
move_column(0)
|
||||
code_edit.deselect()
|
||||
|
||||
var range: Array = calc_double_motion_region(get_caret_pos(), stream, 1)
|
||||
if range.size() == 0: return ''
|
||||
if range.size() == 1: return stream
|
||||
if range.size() == 2:
|
||||
code_edit.select(range[0].y, range[0].x, range[1].y, range[1].x + 1)
|
||||
code_edit.copy()
|
||||
code_edit.deselect()
|
||||
return ''
|
||||
|
||||
if stream == '.':
|
||||
if globals.has('last_command'):
|
||||
handle_input_stream(globals.last_command)
|
||||
call_deferred(&'set_mode', Mode.NORMAL)
|
||||
return ''
|
||||
|
||||
if stream.begins_with(':') and mode == Mode.NORMAL: # Could make this work with visual too ig
|
||||
set_mode(Mode.COMMAND)
|
||||
command_line.set_command(':')
|
||||
return ''
|
||||
if stream.begins_with('/') and mode == Mode.NORMAL:
|
||||
set_mode(Mode.COMMAND)
|
||||
command_line.set_command('/')
|
||||
return ''
|
||||
if stream.begins_with('n'):
|
||||
var rmatch: RegExMatch = globals.vim_plugin.search_regex(
|
||||
code_edit,
|
||||
command_line.search_pattern,
|
||||
get_caret_pos() + Vector2i.RIGHT
|
||||
)
|
||||
if rmatch != null:
|
||||
var pos: Vector2i = globals.vim_plugin.idx_to_pos(code_edit,rmatch.get_start())
|
||||
set_caret_pos(pos.y, pos.x)
|
||||
return ''
|
||||
if stream.begins_with('N'):
|
||||
var rmatch: RegExMatch = globals.vim_plugin.search_regex_backwards(
|
||||
code_edit,
|
||||
command_line.search_pattern,
|
||||
get_caret_pos() + Vector2i.LEFT
|
||||
)
|
||||
if rmatch != null:
|
||||
var pos: Vector2i = globals.vim_plugin.idx_to_pos(code_edit,rmatch.get_start())
|
||||
set_caret_pos(pos.y, pos.x)
|
||||
return ''
|
||||
|
||||
if stream.begins_with('c'):
|
||||
if mode == Mode.VISUAL:
|
||||
code_edit.cut()
|
||||
set_mode(Mode.INSERT)
|
||||
return ''
|
||||
|
||||
if stream.begins_with('cc') and mode == Mode.NORMAL:
|
||||
code_edit.begin_complex_operation()
|
||||
var l: int = get_line()
|
||||
var ind: int = code_edit.get_first_non_whitespace_column(l)
|
||||
code_edit.select( l-1, get_line_length(l-1), l, get_line_length(l) )
|
||||
code_edit.cut()
|
||||
code_edit.insert_line_at(get_line()+1, "\t".repeat(ind))
|
||||
code_edit.end_complex_operation()
|
||||
move_line(+1)
|
||||
set_mode(Mode.INSERT)
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
|
||||
var range: Array = calc_double_motion_region(get_caret_pos(), stream, 1)
|
||||
if range.size() == 0: return ''
|
||||
if range.size() == 1: return stream
|
||||
if range.size() == 2:
|
||||
code_edit.select(range[0].y, range[0].x, range[1].y, range[1].x + 1)
|
||||
code_edit.cut()
|
||||
set_mode(Mode.INSERT)
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
if mode == Mode.NORMAL and stream.begins_with('C'):
|
||||
code_edit.select( get_line(), code_edit.get_caret_column(), get_line(), get_line_length() )
|
||||
code_edit.cut()
|
||||
set_mode(Mode.INSERT)
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
if stream.begins_with('z'):
|
||||
if stream.begins_with('zz') and mode == Mode.NORMAL:
|
||||
code_edit.center_viewport_to_caret()
|
||||
return ''
|
||||
return stream
|
||||
|
||||
if stream.begins_with('>'):
|
||||
if is_mode_visual(mode) and stream.length() == 1:
|
||||
code_edit.indent_lines()
|
||||
return ''
|
||||
if stream.length() == 1: return stream
|
||||
if stream.begins_with('>>') and mode == Mode.NORMAL:
|
||||
code_edit.indent_lines()
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
if stream.begins_with('<'):
|
||||
if is_mode_visual(mode) and stream.length() == 1:
|
||||
code_edit.unindent_lines()
|
||||
return ''
|
||||
if stream.length() == 1: return stream
|
||||
if stream.begins_with('<<') and mode == Mode.NORMAL:
|
||||
code_edit.unindent_lines()
|
||||
globals.last_command = stream
|
||||
return ''
|
||||
|
||||
if stream.begins_with('}'):
|
||||
var para_edge: Vector2i = get_paragraph_edge_pos( get_line(), 1 )
|
||||
set_caret_pos(para_edge.y, para_edge.x)
|
||||
return ''
|
||||
|
||||
if stream.begins_with('{'):
|
||||
var para_edge: Vector2i = get_paragraph_edge_pos( get_line(), -1 )
|
||||
set_caret_pos(para_edge.y, para_edge.x)
|
||||
return ''
|
||||
|
||||
if stream.begins_with('m') and mode == Mode.NORMAL:
|
||||
if stream.length() < 2: return stream
|
||||
if !globals.has('marks'): globals.marks = {}
|
||||
var m: String = stream[1]
|
||||
var unicode: int = m.unicode_at(0)
|
||||
if (unicode < 65 or unicode > 90) and (unicode < 97 or unicode > 122):
|
||||
status_bar.display_error('Marks must be between a-z or A-Z')
|
||||
return ''
|
||||
globals.marks[m] = {
|
||||
'file' : globals.script_editor.get_current_script().resource_path,
|
||||
'pos' : Vector2i(code_edit.get_caret_column(), code_edit.get_caret_line())
|
||||
}
|
||||
status_bar.display_text('Mark "%s" set' % m, TEXT_DIRECTION_LTR)
|
||||
return ''
|
||||
if stream.begins_with('`'):
|
||||
if stream.length() < 2: return stream
|
||||
if !globals.has('marks'): globals.marks = {}
|
||||
if !globals.marks.has(stream[1]):
|
||||
status_bar.display_error('Mark "%s" not set' % [ stream[1] ])
|
||||
return ''
|
||||
var mark: Dictionary = globals.marks[stream[1]]
|
||||
globals.vim_plugin.edit_script(mark.file, mark.pos)
|
||||
return ''
|
||||
return ''
|
||||
|
||||
|
||||
# Mostly used for commands like "w", "b", and "e"
|
||||
# delims is the keywords / characters used as delimiters. Usually, it's the constant KEYWORDS
|
||||
func get_word_edge_pos(from_line: int, from_col: int, delims: String, mode: WordEdgeMode) -> Vector2i:
|
||||
var search_dir: int = -1 if mode == WordEdgeMode.BEGINNING else 1
|
||||
var char_offset: int = 1 if mode == WordEdgeMode.END else -1
|
||||
var line: int = from_line
|
||||
var col: int = from_col + search_dir
|
||||
var text: String = get_line_text(line)
|
||||
|
||||
while line >= 0 and line < code_edit.get_line_count():
|
||||
while col >= 0 and col < text.length():
|
||||
var char: String = text[col]
|
||||
if SPACES.contains(char):
|
||||
col += search_dir
|
||||
continue
|
||||
# Please don't question this lmao. It just works, alight?
|
||||
var other_char: String = ' ' if col == (text.length()-1) * int(char_offset > 0) else text[col + char_offset]
|
||||
|
||||
if SPACES.contains(other_char):
|
||||
return Vector2i(col, line)
|
||||
if delims.contains(char) != delims.contains(other_char):
|
||||
return Vector2i(col, line)
|
||||
col += search_dir
|
||||
line += search_dir
|
||||
text = get_line_text(line)
|
||||
col = (text.length() - 1) * int(search_dir < 0 and char_offset < 0)
|
||||
return Vector2i(from_col, from_line)
|
||||
|
||||
func get_paragraph_edge_pos(from_line: int, search_dir: int):
|
||||
var line: int = from_line
|
||||
var prev_empty: bool = code_edit.get_line(line) .strip_edges().is_empty()
|
||||
line += search_dir
|
||||
while line >= 0 and line < code_edit.get_line_count():
|
||||
var text: String = code_edit.get_line(line) .strip_edges()
|
||||
if text.is_empty() and !prev_empty:
|
||||
return Vector2i(text.length(), line)
|
||||
prev_empty = text.is_empty()
|
||||
line += search_dir
|
||||
return Vector2i(0, line)
|
||||
|
||||
# motion: command like "f", "t", "F", or "T"
|
||||
func find_char_motion(in_line: int, from_col: int, motion: String, char: String) -> int:
|
||||
var search_dir: int = 1 if is_lowercase(motion) else -1
|
||||
var offset: int = int(motion == 'T') - int(motion == 't') # 1 if T, -1 if t, 0 otherwise
|
||||
var text: String = get_line_text(in_line)
|
||||
|
||||
var col: int = -1
|
||||
if motion == 'f' or motion == 't':
|
||||
col = text.find(char, from_col + search_dir)
|
||||
elif motion == 'F' or motion == 'T':
|
||||
col = text.rfind(char, from_col + search_dir)
|
||||
if col == -1:
|
||||
return -1
|
||||
return col + offset
|
||||
|
||||
# returns: [ Vector2i from_pos, Vector2i to_pos ]
|
||||
func calc_double_motion_region(from_pos: Vector2i, stream: String, from_char: int = 0) -> Array[Vector2i]:
|
||||
var primary: String = get_stream_char(stream, from_char)
|
||||
var secondary: String = get_stream_char(stream, from_char + 1)
|
||||
if primary == '':
|
||||
return [from_pos] # Incomplete
|
||||
|
||||
if primary.to_lower() == 'w':
|
||||
var p1: Vector2i = get_word_edge_pos(from_pos.y, from_pos.x, '' if primary == 'W' else KEYWORDS, WordEdgeMode.WORD)
|
||||
return [from_pos, p1 + Vector2i.LEFT]
|
||||
if primary.to_lower() == 'b':
|
||||
var p0: Vector2i = get_word_edge_pos(from_pos.y, from_pos.x, '' if primary == 'B' else KEYWORDS, WordEdgeMode.BEGINNING)
|
||||
return [p0, from_pos + Vector2i.LEFT]
|
||||
if primary.to_lower() == 'e':
|
||||
var p1: Vector2i = get_word_edge_pos(from_pos.y, from_pos.x, '' if primary == 'E' else KEYWORDS, WordEdgeMode.END)
|
||||
return [from_pos, p1]
|
||||
|
||||
if primary == '$':
|
||||
var p1: Vector2i = Vector2i(get_line_length(from_pos.y), from_pos.y)
|
||||
return [from_pos, p1]
|
||||
if primary == '^':
|
||||
var p0: Vector2i = Vector2i(code_edit.get_first_non_whitespace_column(from_pos.y), from_pos.y)
|
||||
return [p0, from_pos + Vector2i.LEFT]
|
||||
|
||||
if primary != 'i' and primary != 'a':
|
||||
return [] # Invalid
|
||||
if secondary == '':
|
||||
return [from_pos] # Incomplete
|
||||
|
||||
if primary == 'i' and secondary.to_lower() == 'w':
|
||||
var p0: Vector2i = get_word_edge_pos(from_pos.y, from_pos.x + 1, '' if secondary == 'W' else KEYWORDS, WordEdgeMode.BEGINNING)
|
||||
var p1: Vector2i = get_word_edge_pos(from_pos.y, from_pos.x - 1, '' if secondary == 'W' else KEYWORDS, WordEdgeMode.END)
|
||||
return [ p0, p1 ]
|
||||
|
||||
if primary == 'i' and secondary == 'p':
|
||||
var p0: Vector2i = get_paragraph_edge_pos(from_pos.y + 1, -1) + Vector2i.DOWN
|
||||
var p1: Vector2i = get_paragraph_edge_pos(from_pos.y - 1, 1)
|
||||
return [ p0, p1 ]
|
||||
|
||||
return [] # Unknown combination
|
||||
|
||||
func toggle_comment(line: int):
|
||||
var ind: int = code_edit.get_first_non_whitespace_column(line)
|
||||
var text: String = get_line_text(line)
|
||||
# Comment line
|
||||
if text[ind] != '#':
|
||||
code_edit.set_line(line, text.insert(ind, '# '))
|
||||
return
|
||||
# Uncomment line
|
||||
var start_col: int = get_word_edge_pos(line, ind, KEYWORDS, WordEdgeMode.WORD).x
|
||||
code_edit.select(line, ind, line, start_col)
|
||||
code_edit.delete_selection()
|
||||
|
||||
func set_mode(m: int):
|
||||
var old_mode: int = mode
|
||||
mode = m
|
||||
command_line.close()
|
||||
match mode:
|
||||
Mode.NORMAL:
|
||||
code_edit.remove_secondary_carets()
|
||||
code_edit.deselect()
|
||||
code_edit.release_focus()
|
||||
code_edit.deselect()
|
||||
self.grab_focus()
|
||||
status_bar.set_mode_text(Mode.NORMAL)
|
||||
if old_mode == Mode.INSERT:
|
||||
move_column(-1)
|
||||
Mode.VISUAL:
|
||||
if old_mode != Mode.VISUAL_LINE:
|
||||
selection_from = Vector2i(code_edit.get_caret_column(), code_edit.get_caret_line())
|
||||
selection_to = Vector2i(code_edit.get_caret_column(), code_edit.get_caret_line())
|
||||
set_caret_pos(selection_to.y, selection_to.x)
|
||||
status_bar.set_mode_text(Mode.VISUAL)
|
||||
Mode.VISUAL_LINE:
|
||||
if old_mode != Mode.VISUAL:
|
||||
selection_from = Vector2i(code_edit.get_caret_column(), code_edit.get_caret_line())
|
||||
selection_to = Vector2i(code_edit.get_caret_column(), code_edit.get_caret_line())
|
||||
set_caret_pos(selection_to.y, selection_to.x)
|
||||
status_bar.set_mode_text(Mode.VISUAL_LINE)
|
||||
Mode.COMMAND:
|
||||
command_line.show()
|
||||
command_line.call_deferred("grab_focus")
|
||||
status_bar.set_mode_text(Mode.COMMAND)
|
||||
Mode.INSERT:
|
||||
code_edit.call_deferred("grab_focus")
|
||||
status_bar.set_mode_text(Mode.INSERT)
|
||||
_:
|
||||
pass
|
||||
|
||||
func move_line(offset:int):
|
||||
set_line(get_line() + offset)
|
||||
|
||||
func get_line() -> int:
|
||||
if is_mode_visual(mode):
|
||||
return selection_to.y
|
||||
return code_edit.get_caret_line()
|
||||
|
||||
func get_line_text(line: int = -1) -> String:
|
||||
if line == -1:
|
||||
return code_edit.get_line(get_line())
|
||||
return code_edit.get_line(line)
|
||||
|
||||
func get_line_length(line: int = -1) -> int:
|
||||
return get_line_text(line).length()
|
||||
|
||||
func set_caret_pos(line: int, column: int):
|
||||
set_line(line) # line has to be set before column
|
||||
set_column(column)
|
||||
|
||||
func get_caret_pos() -> Vector2i:
|
||||
return Vector2i(code_edit.get_caret_column(), code_edit.get_caret_line())
|
||||
|
||||
func set_line(position:int):
|
||||
if !is_mode_visual(mode):
|
||||
code_edit.set_caret_line(min(position, code_edit.get_line_count()-1))
|
||||
return
|
||||
|
||||
selection_to = Vector2i( clampi(selection_to.x, 0, get_line_length(position)), clampi(position, 0, code_edit.get_line_count()) )
|
||||
update_visual_selection()
|
||||
|
||||
|
||||
func move_column(offset: int):
|
||||
set_column(get_column()+offset)
|
||||
|
||||
func get_column():
|
||||
if is_mode_visual(mode):
|
||||
return selection_to.x
|
||||
return code_edit.get_caret_column()
|
||||
|
||||
func set_column(position: int):
|
||||
if !is_mode_visual(mode):
|
||||
var line: String = code_edit.get_line(code_edit.get_caret_line())
|
||||
code_edit.set_caret_column(min(line.length(), position))
|
||||
return
|
||||
|
||||
selection_to = Vector2i( clampi(position, 0, get_line_length(selection_to.y)), clampi(selection_to.y, 0, code_edit.get_line_count()) )
|
||||
update_visual_selection()
|
||||
|
||||
func update_visual_selection():
|
||||
if mode == Mode.VISUAL:
|
||||
var to_right: bool = selection_to.x >= selection_from.x or selection_to.y > selection_from.y
|
||||
code_edit.select( selection_from.y, selection_from.x + int(!to_right), selection_to.y, selection_to.x + int(to_right) )
|
||||
elif mode == Mode.VISUAL_LINE:
|
||||
var f: int = mini(selection_from.y, selection_to.y) - 1
|
||||
var t: int = maxi(selection_from.y, selection_to.y)
|
||||
code_edit.select(f, get_line_length(f), t, get_line_length(t))
|
||||
|
||||
func is_mode_visual(m: int) -> bool:
|
||||
return m == Mode.VISUAL or m == Mode.VISUAL_LINE
|
||||
|
||||
func is_lowercase(text: String) -> bool:
|
||||
return text == text.to_lower()
|
||||
|
||||
func is_uppercase(text: String) -> bool:
|
||||
return text == text.to_upper()
|
||||
|
||||
func get_stream_char(stream: String, idx: int) -> String:
|
||||
return stream[idx] if stream.length() > idx else ''
|
||||
|
||||
func draw_cursor():
|
||||
if code_edit.is_dragging_cursor():
|
||||
selection_from = Vector2i(code_edit.get_selection_from_column(), code_edit.get_selection_from_line())
|
||||
selection_to = Vector2i(code_edit.get_selection_to_column(), code_edit.get_selection_to_line())
|
||||
|
||||
if code_edit.get_selected_text(0).length() > 1 and !is_mode_visual(mode):
|
||||
code_edit.release_focus()
|
||||
self.grab_focus()
|
||||
set_mode(Mode.VISUAL)
|
||||
|
||||
if mode == Mode.INSERT:
|
||||
if code_edit.has_selection(0):
|
||||
code_edit.deselect(0)
|
||||
return
|
||||
|
||||
if mode != Mode.NORMAL:
|
||||
return
|
||||
|
||||
var line: int = code_edit.get_caret_line()
|
||||
var column: int = code_edit.get_caret_column()
|
||||
if column >= code_edit.get_line(line).length():
|
||||
column -= 1
|
||||
code_edit.set_caret_column(column)
|
||||
|
||||
code_edit.select(line, column, line, column+1)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bmkia3drpjivh
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
extends Object
|
||||
|
||||
var handlers = {
|
||||
"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")
|
||||
}
|
||||
|
||||
var globals
|
||||
|
||||
func dispatch(command : String):
|
||||
var command_idx_end = command.find(' ', 1)
|
||||
if command_idx_end == -1: command_idx_end = command.length()
|
||||
var handler_name = command.substr(1, command_idx_end-1)
|
||||
if not handlers.has(handler_name):
|
||||
return ERR_DOES_NOT_EXIST
|
||||
|
||||
var handler = handlers.get(handler_name)
|
||||
var handler_instance = handler.new()
|
||||
var args = command.substr(command_idx_end, command.length())
|
||||
handler_instance.execute(globals, args)
|
||||
return OK
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dvfyfj3wtqfds
|
||||
Binary file not shown.
|
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="GodotVim"
|
||||
description=""
|
||||
author="Bernardo Bruning"
|
||||
version="0.1"
|
||||
script="plugin.gd"
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
enum Mode { NORMAL, INSERT, VISUAL, VISUAL_LINE, COMMAND }
|
||||
|
||||
# Used for commands like "w" "b" and "e" respectively
|
||||
enum WordEdgeMode { WORD, BEGINNING, END }
|
||||
|
||||
const SPACES: String = " \t"
|
||||
const KEYWORDS: String = ".,\"'-=+!@#$%^&*()[]{}?~/\\<>:;"
|
||||
const DIGITS: String = "0123456789"
|
||||
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")
|
||||
|
||||
var cursor: Cursor
|
||||
var command_line: CommandLine
|
||||
var status_bar: StatusBar
|
||||
var globals: Dictionary = {}
|
||||
|
||||
func _enter_tree():
|
||||
globals = {}
|
||||
|
||||
if get_code_edit() != null:
|
||||
_load()
|
||||
get_editor_interface().get_script_editor().connect("editor_script_changed", _script_changed)
|
||||
|
||||
func _script_changed(script: Script):
|
||||
# Add to recent files
|
||||
var path: String = script.resource_path
|
||||
var marks: Dictionary = globals.get('marks', {})
|
||||
for i in range(9, -1, -1):
|
||||
var m: String = str(i)
|
||||
var pm: String = str(i - 1)
|
||||
if !marks.has(pm):
|
||||
continue
|
||||
marks[m] = marks[pm]
|
||||
marks['-1'] = { 'file' : path, 'pos' : Vector2i(-1, 0) }
|
||||
|
||||
_load()
|
||||
|
||||
|
||||
func edit_script(path: String, pos: Vector2i):
|
||||
var script = load(path)
|
||||
var editor_interface: EditorInterface = globals.editor_interface
|
||||
if script == null:
|
||||
status_bar.display_error('Could not open file "%s"' % path)
|
||||
return ''
|
||||
editor_interface.edit_script(script)
|
||||
cursor.call_deferred('set_caret_pos', pos.y, pos.x)
|
||||
|
||||
|
||||
func _load():
|
||||
if globals == null:
|
||||
globals = {}
|
||||
|
||||
# Cursor
|
||||
if cursor != null:
|
||||
cursor.queue_free()
|
||||
cursor = Cursor.new()
|
||||
var code_edit = get_code_edit()
|
||||
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.globals = globals
|
||||
|
||||
# Command line
|
||||
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()
|
||||
|
||||
# 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
|
||||
|
||||
var editor_interface = get_editor_interface()
|
||||
if editor_interface == null: return
|
||||
var script_editor = editor_interface.get_script_editor()
|
||||
if script_editor == null: return
|
||||
var script_editor_base = script_editor.get_current_editor()
|
||||
if script_editor_base == null: return
|
||||
|
||||
globals.editor_interface = editor_interface
|
||||
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
|
||||
script_editor_base.add_child(cursor)
|
||||
script_editor_base.add_child(status_bar)
|
||||
script_editor_base.add_child(command_line)
|
||||
|
||||
|
||||
func get_code_edit():
|
||||
var editor = get_editor_interface().get_script_editor().get_current_editor()
|
||||
return _select(editor, ['VSplitContainer', 'CodeTextEditor', 'CodeEdit'])
|
||||
|
||||
func _select(obj: Node, types: Array[String]): # ???
|
||||
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
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://duaf0qcswso06
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
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
|
||||
|
||||
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.text_direction = Control.TEXT_DIRECTION_RTL
|
||||
main_label.add_theme_font_override(&"normal_font", font)
|
||||
add_child(main_label)
|
||||
|
||||
func display_text(text: String, text_direction: Control.TextDirection = TEXT_DIRECTION_RTL):
|
||||
main_label.text = text
|
||||
main_label.text_direction = text_direction
|
||||
|
||||
func display_error(text: String):
|
||||
main_label.text = '[color=%s]%s' % [ERROR_COLOR, text]
|
||||
main_label.text_direction = Control.TEXT_DIRECTION_LTR
|
||||
|
||||
func display_special(text: String):
|
||||
main_label.text = '[color=%s]%s' % [SPECIAL_COLOR, text]
|
||||
main_label.text_direction = Control.TEXT_DIRECTION_LTR
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bsixy362mx4r7
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
extends Sprite2D
|
||||
|
||||
var pos: Vector2i = Vector2i(0,0)
|
||||
var dir: TileSet.CellNeighbor = TileSet.CELL_NEIGHBOR_TOP_RIGHT_SIDE
|
||||
@onready var tiles: TileMapLayer = get_node("TileMapLayer")
|
||||
|
||||
var next_dir: Dictionary = {
|
||||
0: TileSet.CELL_NEIGHBOR_RIGHT_SIDE,
|
||||
1: TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE,
|
||||
2: TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_SIDE,
|
||||
3: TileSet.CELL_NEIGHBOR_TOP_LEFT_SIDE,
|
||||
4: TileSet.CELL_NEIGHBOR_TOP_RIGHT_SIDE,
|
||||
}
|
||||
|
||||
var next_int: Dictionary = {
|
||||
TileSet.CELL_NEIGHBOR_RIGHT_SIDE: 1,
|
||||
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: 2,
|
||||
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: 3,
|
||||
TileSet.CELL_NEIGHBOR_TOP_LEFT_SIDE: 4,
|
||||
TileSet.CELL_NEIGHBOR_TOP_RIGHT_SIDE: 0,
|
||||
}
|
||||
|
||||
func _ready() -> void:
|
||||
set_frame(1)
|
||||
|
||||
func update() -> void:
|
||||
var tile: int = tiles.get_tile_colour(pos)
|
||||
print("tile %s" % tile)
|
||||
var newFrame: int = next_int[dir]
|
||||
|
||||
if tile == 0:
|
||||
newFrame = (newFrame + 1) % 5
|
||||
dir = next_dir[newFrame]
|
||||
else:
|
||||
newFrame = (newFrame) % 5
|
||||
dir = next_dir[newFrame]
|
||||
|
||||
#var tmpPos: Vector2i
|
||||
#for i in range(0, 16):
|
||||
#tmpPos = tiles.get_neighbor_cell(pos, i)
|
||||
#print("%s: (%d %d)" % [i, tmpPos.x, tmpPos.y])
|
||||
|
||||
print("(%d %d)" % [pos.x, pos.y])
|
||||
pos = tiles.get_neighbor_cell(pos, dir)
|
||||
tile = tiles.get_tile_colour(pos)
|
||||
print("(%d %d)" % [pos.x, pos.y])
|
||||
tiles.set_tile(pos, (tile + 1) % 2) # TODO: should probably use another dictionary
|
||||
set_frame(newFrame)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
var vel: Vector2 = Vector2(0,0)
|
||||
match dir:
|
||||
TileSet.CELL_NEIGHBOR_TOP_RIGHT_SIDE:
|
||||
vel = Vector2(1,1)
|
||||
TileSet.CELL_NEIGHBOR_RIGHT_SIDE:
|
||||
vel = Vector2(1,0)
|
||||
_:
|
||||
vel = Vector2(0,-1)
|
||||
vel = vel.normalized() * 16
|
||||
global_position += vel * delta
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://b6ll30b7xtwal
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 305 B |
Binary file not shown.
|
After Width: | Height: | Size: 305 B |
Binary file not shown.
|
After Width: | Height: | Size: 404 B |
|
|
@ -0,0 +1,18 @@
|
|||
extends Node2D
|
||||
|
||||
var t: float = 0
|
||||
@export var speed: float = 58
|
||||
@export var ant: Sprite2D
|
||||
@export var tiles: TileMapLayer
|
||||
|
||||
# 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:
|
||||
t += delta
|
||||
if (t > 60-speed):
|
||||
t = 0
|
||||
ant.update()
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://by3d3t5ymxl3m
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
[gd_scene load_steps=8 format=3 uid="uid://b6xi32r3co6md"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://by3d3t5ymxl3m" path="res://grid.gd" id="1_bghhw"]
|
||||
[ext_resource type="Script" uid="uid://b6ll30b7xtwal" path="res://ant.gd" id="1_ebq2e"]
|
||||
[ext_resource type="Texture2D" uid="uid://bnfy5vx72ux33" path="res://assets/ant.png" id="3_sle3t"]
|
||||
[ext_resource type="Texture2D" uid="uid://d0v1qlkltasln" path="res://assets/tiles.png" id="4_fqc2p"]
|
||||
[ext_resource type="Script" uid="uid://cqxfmo3hixad1" path="res://tile_map_layer.gd" id="5_fqc2p"]
|
||||
|
||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_g2qvd"]
|
||||
texture = ExtResource("4_fqc2p")
|
||||
texture_region_size = Vector2i(32, 32)
|
||||
0:0/0 = 0
|
||||
1:0/0 = 0
|
||||
0:1/0 = 0
|
||||
1:1/0 = 0
|
||||
0:2/0 = 0
|
||||
1:2/0 = 0
|
||||
0:3/0 = 0
|
||||
1:3/0 = 0
|
||||
|
||||
[sub_resource type="TileSet" id="TileSet_05i0m"]
|
||||
tile_shape = 3
|
||||
tile_size = Vector2i(32, 32)
|
||||
sources/0 = SubResource("TileSetAtlasSource_g2qvd")
|
||||
|
||||
[node name="Grid" type="Node2D" node_paths=PackedStringArray("ant", "tiles")]
|
||||
script = ExtResource("1_bghhw")
|
||||
speed = 60.0
|
||||
ant = NodePath("ant")
|
||||
tiles = NodePath("ant/TileMapLayer")
|
||||
|
||||
[node name="ant" type="Sprite2D" parent="."]
|
||||
texture = ExtResource("3_sle3t")
|
||||
vframes = 6
|
||||
script = ExtResource("1_ebq2e")
|
||||
|
||||
[node name="Camera2D" type="Camera2D" parent="ant"]
|
||||
|
||||
[node name="TileMapLayer" type="TileMapLayer" parent="ant"]
|
||||
position = Vector2(-74, -8)
|
||||
tile_set = SubResource("TileSet_05i0m")
|
||||
collision_enabled = false
|
||||
script = ExtResource("5_fqc2p")
|
||||
|
|
@ -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: 995 B |
|
|
@ -0,0 +1,61 @@
|
|||
extends TileMapLayer
|
||||
|
||||
var tiles: Dictionary = {}
|
||||
|
||||
func get_tile_colour(pos: Vector2i) -> int:
|
||||
return tiles.get_or_add(pos, 0)
|
||||
|
||||
# TODO: state sh1ould be an enum probably
|
||||
func set_tile(pos: Vector2i, state: int) -> void:
|
||||
print("setting cell (%s) at %d %d\n" % [state, pos.x, pos.y])
|
||||
set_cell(pos, 0, Vector2i(0, state))
|
||||
tiles.erase(pos)
|
||||
tiles.get_or_add(pos, state)
|
||||
|
||||
#var direction = 0
|
||||
#var antPos = Vector2i(0,0)
|
||||
#var grid = {}
|
||||
#var tileSize = 16
|
||||
#
|
||||
## Called when the node enters the scene tree for the first time.
|
||||
#func _ready() -> void:
|
||||
#set_process(true)
|
||||
#grid[antPos] = 0
|
||||
#updateTile(antPos, 0)
|
||||
#
|
||||
#func updateTile(pos: Vector2i, col: int):
|
||||
#var tileCol = Color.WHITE if col == 0 else Color.BLACK
|
||||
#var tileRect = ColorRect.new()
|
||||
#tileRect.color = tileCol
|
||||
#tileRect.size = Vector2(tileSize, tileSize)
|
||||
#tileRect.position = Vector2(pos.x * tileSize, pos.y * tileSize)
|
||||
#add_child(tileRect)
|
||||
#
|
||||
#
|
||||
## Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
#func _process(delta: float) -> void:
|
||||
#moveAnt()
|
||||
#
|
||||
#func moveAnt():
|
||||
#var curCol = grid.get(antPos, 0)
|
||||
#
|
||||
#if curCol == 0:
|
||||
#direction = (direction + 1) % 4
|
||||
#else:
|
||||
#direction = (direction + 3) % 4
|
||||
#
|
||||
#grid[antPos] = 1 - curCol
|
||||
#updateTile(antPos, grid[antPos])
|
||||
#
|
||||
#match direction:
|
||||
#0:
|
||||
#antPos.y -= 1
|
||||
#1:
|
||||
#antPos.x += 1
|
||||
#2:
|
||||
#antPos.y += 1
|
||||
#3:
|
||||
#antPos.x -= 1
|
||||
#
|
||||
#if not grid.has(antPos):
|
||||
#grid[antPos] = 0
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cqxfmo3hixad1
|
||||
Loading…
Reference in New Issue