(wip) advanced langtons ant

(wip) advanced langtons ant
This commit is contained in:
djaidjai committed 2025-11-12 00:59:27 +01:00
1 parent c57f07ffc2
commit 25ec8d2585
42 files changed
+3283

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
root = true
[*]
charset = utf-8
+2
View File
@@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf
+4
View File
@@ -0,0 +1,4 @@
# Godot 4+ specific ignores
.godot/
project.godot
*.import
+21
View File
@@ -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.
+55
View File
@@ -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
+2
View File
@@ -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
+179
View 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

+7
View File
@@ -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
+722
View File
@@ -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.
+7
View File
@@ -0,0 +1,7 @@
[plugin]
name="GodotVim"
description=""
author="Bernardo Bruning"
version="0.1"
script="plugin.gd"
+164
View File
@@ -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
+60
View File
@@ -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
+1
View File
@@ -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

+18
View File
@@ -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()
+1
View File
@@ -0,0 +1 @@
uid://by3d3t5ymxl3m
+43
View File
@@ -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")
+1
View 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: 995 B

+61
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
uid://cqxfmo3hixad1
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.