Compare commits
250 commits
Author | SHA1 | Date | |
---|---|---|---|
|
9eb8782ff2 | ||
|
46e55c8e91 | ||
|
dd913df9e9 | ||
|
e9bd1b35f4 | ||
|
4911a56181 | ||
|
343812bd2e | ||
|
35630aa736 | ||
|
78fcf2fc31 | ||
|
5dbdf8c0e8 | ||
|
889a841575 | ||
|
917650826a | ||
|
b70f0eb113 | ||
|
5a159ce444 | ||
|
bca35a5939 | ||
|
1f51d0b9e2 | ||
|
0a1447b688 | ||
|
2ecdac8405 | ||
|
385437d400 | ||
|
1c35f3dc39 | ||
|
07cda68795 | ||
|
3919cf399f | ||
|
b05df07df2 | ||
|
8af890a0a3 | ||
|
ff5b147639 | ||
|
3f810c24d2 | ||
|
26fa15c147 | ||
|
147943837d | ||
|
24406a5ae8 | ||
|
8632b82cbe | ||
|
fade304667 | ||
|
5b3737fb2a | ||
|
36bf3f6619 | ||
|
8c7f63ac15 | ||
|
18f3e1bf89 | ||
|
e48575f349 | ||
|
eec068a4fc | ||
|
5510317942 | ||
|
169a9a65fa | ||
|
c3052b491f | ||
|
b929c61228 | ||
|
08c516c730 | ||
|
1bddc8d03e | ||
|
f9cad2e448 | ||
|
3aed20fde9 | ||
|
a436dae587 | ||
|
5610d01e08 | ||
|
0806addbd7 | ||
|
6cd39efddc | ||
|
089160a7e4 | ||
|
ed993a4021 | ||
|
4cafa601b5 | ||
|
87ee41ab27 | ||
|
8d8bc58f91 | ||
|
6ffabd626f | ||
|
2c53d1fcab | ||
|
f265179def | ||
|
390794213e | ||
|
430da61314 | ||
|
f386b29e16 | ||
|
4283881591 | ||
|
186817d0c4 | ||
|
2a1790d15a | ||
|
c6dc5a4b1f | ||
|
426aa9bb8b | ||
|
acb0d763df | ||
|
d1d38d1ed7 | ||
|
467c71dbb8 | ||
|
a3ca054371 | ||
|
b6dcbfa846 | ||
|
6e71e37568 | ||
|
dd7134a762 | ||
|
2830c4878e | ||
|
53d56d032c | ||
|
c493e14eb4 | ||
|
69dc54b407 | ||
|
c5d32f625b | ||
|
baca0e5cb2 | ||
|
d67ce731ed | ||
|
828871acdf | ||
|
dc833d3552 | ||
|
08028cf415 | ||
|
93dd8ca729 | ||
|
3d7024e059 | ||
|
b291f27c3f | ||
|
3903859970 | ||
|
839e86849e | ||
|
20bf7096b8 | ||
|
d96f060b4c | ||
|
08892b125f | ||
|
838f371486 | ||
|
fc7efbdbe9 | ||
|
2ab1b3132e | ||
|
d64c9443f5 | ||
|
ee6519f5cb | ||
|
984c32b513 | ||
|
1595c5ddda | ||
|
1021f61a81 | ||
|
053949eac6 | ||
|
9ee82a6cb3 | ||
|
66a3839589 | ||
|
b2a428f1cd | ||
|
5492d30953 | ||
|
6c3b5ad17c | ||
|
39e410aa46 | ||
|
3f4942cedb | ||
|
2b8d925925 | ||
|
0c923aa156 | ||
|
13483602d5 | ||
|
c2c2b2addf | ||
|
8b4e9d2c5e | ||
|
a57d29ada9 | ||
|
bb1f4dad77 | ||
|
426e6c600f | ||
|
9ab9f8bc1c | ||
|
f15db6aa30 | ||
|
4895a29be2 | ||
|
b518bda50c | ||
|
c64add289b | ||
|
5ae2799b70 | ||
|
16e38b988c | ||
|
8a3d83f7c7 | ||
|
55b251ffee | ||
|
8724709cf9 | ||
|
4a53419c62 | ||
|
8af304cc21 | ||
|
399134fe5b | ||
|
db26b5fee5 | ||
|
cb903f414c | ||
|
0a69cc68dc | ||
|
1d1b363fa7 | ||
|
4ffc2206ee | ||
|
9089e9ec83 | ||
|
c24604d1ab | ||
|
606bcecf03 | ||
|
94af5f13bd | ||
|
80db98dc81 | ||
|
e424537ff8 | ||
|
5bfda7b5f6 | ||
|
3dba23a348 | ||
|
00174bb376 | ||
|
c4c5b184c2 | ||
|
7b718cb87c | ||
|
6dc3df646b | ||
|
13d1407f60 | ||
|
53efce72fa | ||
|
f108c90643 | ||
|
b824e767d6 | ||
|
c52ccad14b | ||
|
104caf08dd | ||
|
64370b70d6 | ||
|
8368af3cc8 | ||
|
ca3a9d0794 | ||
|
bd306d67b4 | ||
|
a01ae92541 | ||
|
dcdd3e749a | ||
|
628d9bb37b | ||
|
bfc4b1d195 | ||
|
d2ee6107a3 | ||
|
69eaa9191a | ||
|
14dca7d349 | ||
|
fad4e449fb | ||
|
f0bc6281d4 | ||
|
fe4ade78df | ||
|
88b4498ce0 | ||
|
3fce03dfd0 | ||
|
15b36ce0d6 | ||
|
321322af31 | ||
|
0de16334d3 | ||
|
c15abea64c | ||
|
9fdea82542 | ||
|
eedebd80d4 | ||
|
e5026ef3fa | ||
|
af2ec9d540 | ||
|
59dda01cb7 | ||
|
fce8db80de | ||
|
e5a9b906f3 | ||
|
422305af99 | ||
|
4e383dd110 | ||
|
2d82362a66 | ||
|
359b58a89b | ||
|
a373d22939 | ||
|
12398916c7 | ||
|
c791cef9c6 | ||
|
3c16df87ee | ||
|
2d0d0416e7 | ||
|
2aa386f455 | ||
|
93151f8109 | ||
|
433879046e | ||
|
d8e9d61a95 | ||
|
6fa12743d6 | ||
|
dcc7205699 | ||
|
68d88b571d | ||
|
041ee002dd | ||
|
f7244d09c6 | ||
|
9da6af91c8 | ||
|
2a9a5afbb2 | ||
|
c55fb3329f | ||
|
c27593c163 | ||
|
fe89df1d4e | ||
|
9b81589fba | ||
|
dc6a275e04 | ||
|
db5fcf11a9 | ||
|
db6d4f5461 | ||
|
1231d24279 | ||
|
d8abfd3999 | ||
|
9fabffc880 | ||
|
630b3229ee | ||
|
fbce241753 | ||
|
ffc7118af0 | ||
|
1ab390e1a2 | ||
|
909c1a2dda | ||
|
c2cebaa3d1 | ||
|
75b9a6cefe | ||
|
1cbe1441aa | ||
|
d0f0b95e45 | ||
|
a78c2c3509 | ||
|
ceaa143c62 | ||
|
cb260bf6bf | ||
|
0c28fbf7a5 | ||
|
b02afb1116 | ||
|
e5bbeff8ac | ||
|
7e64a43af6 | ||
|
51022e6162 | ||
|
5cb9d5eaaf | ||
|
ffa7f987b6 | ||
|
9593c2a720 | ||
|
d7c8daad0d | ||
|
0859f4aa36 | ||
|
c46467b5b9 | ||
|
1b4f6ecb12 | ||
|
fa468cac5f | ||
|
ff1107bc5b | ||
|
651a30105b | ||
|
9cef21ecd6 | ||
|
61c90b27ab | ||
|
27ed176a22 | ||
|
04d30ebced | ||
|
d5f6b626d2 | ||
|
5739daffc8 | ||
|
9c2ce486a5 | ||
|
7bef54856c | ||
|
dda79ca70e | ||
|
b16af564a7 | ||
|
127b340a08 | ||
|
7dd88c2f23 | ||
|
c492466583 | ||
|
2d95064ff6 | ||
|
e5093892fd | ||
|
124fa9e2e7 | ||
|
34ac83b594 |
188 changed files with 3918 additions and 1608 deletions
15
README.md
15
README.md
|
@ -53,7 +53,7 @@ You can also check out the website for Micro at https://micro-editor.github.io.
|
||||||
- Extremely good mouse support.
|
- Extremely good mouse support.
|
||||||
- This means mouse dragging to create a selection, double click to select by word, and triple click to select by line.
|
- This means mouse dragging to create a selection, double click to select by word, and triple click to select by line.
|
||||||
- Cross-platform (it should work on all the platforms Go runs on).
|
- Cross-platform (it should work on all the platforms Go runs on).
|
||||||
- Note that while Windows is supported Mingw/Cygwin is not (see below).
|
- Note that while Windows is supported, Mingw/Cygwin is not (see below).
|
||||||
- Plugin system (plugins are written in Lua).
|
- Plugin system (plugins are written in Lua).
|
||||||
- micro has a built-in plugin manager to automatically install, remove, and update plugins.
|
- micro has a built-in plugin manager to automatically install, remove, and update plugins.
|
||||||
- Built-in diff gutter.
|
- Built-in diff gutter.
|
||||||
|
@ -68,6 +68,7 @@ You can also check out the website for Micro at https://micro-editor.github.io.
|
||||||
- Small and simple.
|
- Small and simple.
|
||||||
- Easily configurable.
|
- Easily configurable.
|
||||||
- Macros.
|
- Macros.
|
||||||
|
- Smart highlighting of trailing whitespace and tab vs space errors.
|
||||||
- Common editor features such as undo/redo, line numbers, Unicode support, soft wrapping, …
|
- Common editor features such as undo/redo, line numbers, Unicode support, soft wrapping, …
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
@ -83,7 +84,7 @@ A desktop entry file and man page can be found in the [assets/packaging](https:/
|
||||||
|
|
||||||
### Pre-built binaries
|
### Pre-built binaries
|
||||||
|
|
||||||
Pre-built binaries are distributed with [releases](https://github.com/zyedidia/micro/releases).
|
Pre-built binaries are distributed in [releases](https://github.com/zyedidia/micro/releases).
|
||||||
|
|
||||||
To uninstall micro, simply remove the binary, and the configuration directory at `~/.config/micro`.
|
To uninstall micro, simply remove the binary, and the configuration directory at `~/.config/micro`.
|
||||||
|
|
||||||
|
@ -132,7 +133,7 @@ On Linux, you can install micro through [snap](https://snapcraft.io/docs/core/in
|
||||||
snap install micro --classic
|
snap install micro --classic
|
||||||
```
|
```
|
||||||
|
|
||||||
Micro is also available through other package managers on Linux such dnf, AUR, Nix, and package managers
|
Micro is also available through other package managers on Linux such as dnf, AUR, Nix, and package managers
|
||||||
for other operating systems. These packages are not guaranteed to be up-to-date.
|
for other operating systems. These packages are not guaranteed to be up-to-date.
|
||||||
|
|
||||||
<!-- * `apt install micro` (Ubuntu 20.04 `focal`, and Debian `unstable | testing | buster-backports`). At the moment, this package (2.0.1-1) is outdated and has a known bug where debug mode is enabled. -->
|
<!-- * `apt install micro` (Ubuntu 20.04 `focal`, and Debian `unstable | testing | buster-backports`). At the moment, this package (2.0.1-1) is outdated and has a known bug where debug mode is enabled. -->
|
||||||
|
@ -159,8 +160,8 @@ for other operating systems. These packages are not guaranteed to be up-to-date.
|
||||||
**Note for Linux desktop environments:**
|
**Note for Linux desktop environments:**
|
||||||
|
|
||||||
For interfacing with the local system clipboard, the following tools need to be installed:
|
For interfacing with the local system clipboard, the following tools need to be installed:
|
||||||
* For X11 `xclip` or `xsel`
|
* For X11, `xclip` or `xsel`
|
||||||
* For [Wayland](https://wayland.freedesktop.org/) `wl-clipboard`
|
* For [Wayland](https://wayland.freedesktop.org/), `wl-clipboard`
|
||||||
|
|
||||||
Without these tools installed, micro will use an internal clipboard for copy and paste, but it won't be accessible to external applications.
|
Without these tools installed, micro will use an internal clipboard for copy and paste, but it won't be accessible to external applications.
|
||||||
|
|
||||||
|
@ -221,7 +222,7 @@ If you open micro and it doesn't seem like syntax highlighting is working, this
|
||||||
you are using a terminal which does not support 256 color mode. Try changing the color scheme to `simple`
|
you are using a terminal which does not support 256 color mode. Try changing the color scheme to `simple`
|
||||||
by pressing <kbd>Ctrl-e</kbd> in micro and typing `set colorscheme simple`.
|
by pressing <kbd>Ctrl-e</kbd> in micro and typing `set colorscheme simple`.
|
||||||
|
|
||||||
If you are using the default Ubuntu terminal, to enable 256 make sure your `TERM` variable is set
|
If you are using the default Ubuntu terminal, to enable 256 color mode make sure your `TERM` variable is set
|
||||||
to `xterm-256color`.
|
to `xterm-256color`.
|
||||||
|
|
||||||
Many of the Windows terminals don't support more than 16 colors, which means
|
Many of the Windows terminals don't support more than 16 colors, which means
|
||||||
|
@ -240,7 +241,7 @@ winpty micro.exe ...
|
||||||
|
|
||||||
Micro uses the amazing [tcell library](https://github.com/gdamore/tcell), but this
|
Micro uses the amazing [tcell library](https://github.com/gdamore/tcell), but this
|
||||||
means that micro is restricted to the platforms tcell supports. As a result, micro does not support
|
means that micro is restricted to the platforms tcell supports. As a result, micro does not support
|
||||||
Plan9, and Cygwin (although this may change in the future). Micro also doesn't support NaCl (which is deprecated anyway).
|
Plan9 or Cygwin (although this may change in the future). Micro also doesn't support NaCl (which is deprecated anyway).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
viewBox="0 0 304.70001 103.2"
|
viewBox="0 0 304.70001 103.2"
|
||||||
enable-background="new 0 0 960 560"
|
enable-background="new 0 0 960 560"
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
sodipodi:docname="micro-logo-drop.svg"
|
sodipodi:docname="micro-logo-drop.svg"
|
||||||
width="304.70001"
|
width="304.70001"
|
||||||
height="103.2"
|
height="103.2"
|
||||||
|
@ -43,21 +43,23 @@
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1043"
|
inkscape:window-height="1080"
|
||||||
id="namedview17"
|
id="namedview17"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
fit-margin-top="0"
|
fit-margin-top="0"
|
||||||
fit-margin-left="0"
|
fit-margin-left="0"
|
||||||
fit-margin-right="0"
|
fit-margin-right="0"
|
||||||
fit-margin-bottom="0"
|
fit-margin-bottom="0"
|
||||||
inkscape:zoom="2.5161345"
|
inkscape:zoom="13.204388"
|
||||||
inkscape:cx="158.97401"
|
inkscape:cx="71.832181"
|
||||||
inkscape:cy="109.69207"
|
inkscape:cy="63.956011"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="Layer_1"
|
inkscape:current-layer="Layer_1"
|
||||||
inkscape:pagecheckerboard="0" /><g
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:deskcolor="#d1d1d1" /><g
|
||||||
id="g838"
|
id="g838"
|
||||||
transform="translate(-178,-172.8)"
|
transform="translate(-178,-172.8)"
|
||||||
style="fill:#ffffff;fill-opacity:1;filter:url(#filter1040)"><path
|
style="fill:#ffffff;fill-opacity:1;filter:url(#filter1040)"><path
|
||||||
|
@ -101,4 +103,7 @@
|
||||||
d="M 51.6,0 C 23.1,0 0,23.1 0,51.6 c 0,28.5 23.1,51.6 51.6,51.6 28.5,0 51.6,-23.1 51.6,-51.6 C 103.2,23.1 80.1,0 51.6,0 Z m 24.5,58.6 c -0.5,2 -1.3,3.6 -2.4,4.9 -1,1.3 -2,2.1 -3.1,2.5 -1.1,0.4 -2.2,0.6 -3.4,0.6 -1.2,0 -2.2,-0.2 -3,-0.7 C 63.4,65.5 62.8,64.8 62.3,64 61.8,63.2 61.5,62.2 61.3,61.1 61.1,60 61,58.8 61,57.5 c 0,-0.5 0,-1 0.1,-1.7 0.1,-0.7 0.2,-1.6 0.3,-1.6 h -0.2 c -1.6,4 -3.8,6.9 -6.6,9.2 -2.8,2.3 -5.9,3.4 -9.3,3.4 -2.3,0 -4.2,-0.9 -5.5,-2.6 -1.4,-1.7 -2.1,-4.3 -2.1,-7.7 0,-0.5 0,-1 0.1,-1.6 0.1,-0.5 0.1,-0.7 0.2,-1.7 h -0.7 c -0.9,2 -1.7,4.8 -2.3,7.3 -0.6,2.5 -1.1,4.8 -1.4,6.9 -0.4,2.1 -0.6,4 -0.8,5.6 -0.2,1.6 -0.3,2.7 -0.4,3.3 0.1,0.5 0.2,1 0.3,1.6 0.2,0.6 0.3,1.2 0.5,1.7 0.2,0.5 0.3,1.1 0.4,1.6 0.1,0.5 0.2,0.9 0.2,1.2 0,1.4 -0.3,2.5 -0.9,3.2 -0.6,0.7 -1.3,1.1 -2,1.1 -0.9,0 -1.7,-0.3 -2.3,-0.8 -0.7,-0.6 -1,-1.5 -1,-2.7 0,-1.7 0.3,-3.9 0.9,-6.5 0.6,-2.6 1.5,-5.9 2.6,-9.8 0.6,-1.8 1.1,-3.6 1.7,-5.4 0.6,-1.8 1.1,-3.5 1.6,-5 0.5,-1.5 0.9,-2.9 1.3,-4.1 0.4,-1.2 0.6,-2.1 0.7,-2.8 0.1,-0.3 0.2,-1 0.3,-2 0.1,-1 0.2,-2.1 0.4,-3.4 0.2,-1.3 0.3,-2.7 0.5,-4.1 0.2,-1.5 0.4,-2.8 0.5,-4 0.2,-0.9 0.3,-1.9 0.5,-3 0.2,-1.1 0.5,-2.2 0.9,-3.1 0.4,-1 1,-1.8 1.7,-2.5 0.7,-0.7 1.6,-1 2.7,-1 1.2,0 2,0.4 2.4,1.1 0.4,0.7 0.6,1.6 0.5,2.6 -0.1,1 -0.2,2.1 -0.5,3.2 -0.3,1.1 -0.6,2.1 -0.9,2.9 -0.8,2.5 -1.6,4.8 -2.5,6.7 -0.9,1.9 -1.7,4 -2.4,6.2 -0.6,1.5 -0.8,2.9 -0.8,4.1 0,2.2 0.7,3.8 2,5 1.4,1.2 3,1.7 4.9,1.7 1.5,0 3,-0.5 4.4,-1.6 1.4,-1.1 2.7,-2.4 3.9,-3.9 1.2,-1.5 2.2,-3.1 3,-4.9 0.8,-1.7 1.4,-3.3 1.8,-4.6 0.1,-0.2 0.2,-0.6 0.3,-1.4 0.2,-0.8 0.3,-1.7 0.5,-2.7 0.2,-1 0.4,-2 0.6,-3.1 0.2,-1.1 0.4,-2 0.5,-2.7 0.2,-0.8 0.3,-1.6 0.5,-2.6 0.2,-1 0.5,-1.9 0.9,-2.8 0.4,-0.9 1,-1.6 1.6,-2.2 0.7,-0.6 1.5,-0.9 2.6,-0.9 1.3,0 2.1,0.4 2.6,1.1 0.4,0.7 0.6,1.6 0.6,2.6 -0.1,1 -0.2,2 -0.5,3 -0.3,1 -0.5,1.8 -0.7,2.4 -0.8,2.5 -1.6,4.7 -2.4,6.7 -0.8,2 -1.5,3.8 -2.2,5.2 -0.6,1.5 -1.1,2.6 -1.5,3.5 -0.4,0.9 -0.6,1.5 -0.6,1.8 0,2.6 0.6,4.5 1.7,5.6 1.1,1.1 2.3,1.7 3.6,1.7 2.2,0 3.9,-0.7 5.2,-2 1.3,-1.4 2.3,-3.9 2.9,-6.9 h 0.8 c 0.2,2.9 -0.1,5.3 -0.6,7.3 z"
|
d="M 51.6,0 C 23.1,0 0,23.1 0,51.6 c 0,28.5 23.1,51.6 51.6,51.6 28.5,0 51.6,-23.1 51.6,-51.6 C 103.2,23.1 80.1,0 51.6,0 Z m 24.5,58.6 c -0.5,2 -1.3,3.6 -2.4,4.9 -1,1.3 -2,2.1 -3.1,2.5 -1.1,0.4 -2.2,0.6 -3.4,0.6 -1.2,0 -2.2,-0.2 -3,-0.7 C 63.4,65.5 62.8,64.8 62.3,64 61.8,63.2 61.5,62.2 61.3,61.1 61.1,60 61,58.8 61,57.5 c 0,-0.5 0,-1 0.1,-1.7 0.1,-0.7 0.2,-1.6 0.3,-1.6 h -0.2 c -1.6,4 -3.8,6.9 -6.6,9.2 -2.8,2.3 -5.9,3.4 -9.3,3.4 -2.3,0 -4.2,-0.9 -5.5,-2.6 -1.4,-1.7 -2.1,-4.3 -2.1,-7.7 0,-0.5 0,-1 0.1,-1.6 0.1,-0.5 0.1,-0.7 0.2,-1.7 h -0.7 c -0.9,2 -1.7,4.8 -2.3,7.3 -0.6,2.5 -1.1,4.8 -1.4,6.9 -0.4,2.1 -0.6,4 -0.8,5.6 -0.2,1.6 -0.3,2.7 -0.4,3.3 0.1,0.5 0.2,1 0.3,1.6 0.2,0.6 0.3,1.2 0.5,1.7 0.2,0.5 0.3,1.1 0.4,1.6 0.1,0.5 0.2,0.9 0.2,1.2 0,1.4 -0.3,2.5 -0.9,3.2 -0.6,0.7 -1.3,1.1 -2,1.1 -0.9,0 -1.7,-0.3 -2.3,-0.8 -0.7,-0.6 -1,-1.5 -1,-2.7 0,-1.7 0.3,-3.9 0.9,-6.5 0.6,-2.6 1.5,-5.9 2.6,-9.8 0.6,-1.8 1.1,-3.6 1.7,-5.4 0.6,-1.8 1.1,-3.5 1.6,-5 0.5,-1.5 0.9,-2.9 1.3,-4.1 0.4,-1.2 0.6,-2.1 0.7,-2.8 0.1,-0.3 0.2,-1 0.3,-2 0.1,-1 0.2,-2.1 0.4,-3.4 0.2,-1.3 0.3,-2.7 0.5,-4.1 0.2,-1.5 0.4,-2.8 0.5,-4 0.2,-0.9 0.3,-1.9 0.5,-3 0.2,-1.1 0.5,-2.2 0.9,-3.1 0.4,-1 1,-1.8 1.7,-2.5 0.7,-0.7 1.6,-1 2.7,-1 1.2,0 2,0.4 2.4,1.1 0.4,0.7 0.6,1.6 0.5,2.6 -0.1,1 -0.2,2.1 -0.5,3.2 -0.3,1.1 -0.6,2.1 -0.9,2.9 -0.8,2.5 -1.6,4.8 -2.5,6.7 -0.9,1.9 -1.7,4 -2.4,6.2 -0.6,1.5 -0.8,2.9 -0.8,4.1 0,2.2 0.7,3.8 2,5 1.4,1.2 3,1.7 4.9,1.7 1.5,0 3,-0.5 4.4,-1.6 1.4,-1.1 2.7,-2.4 3.9,-3.9 1.2,-1.5 2.2,-3.1 3,-4.9 0.8,-1.7 1.4,-3.3 1.8,-4.6 0.1,-0.2 0.2,-0.6 0.3,-1.4 0.2,-0.8 0.3,-1.7 0.5,-2.7 0.2,-1 0.4,-2 0.6,-3.1 0.2,-1.1 0.4,-2 0.5,-2.7 0.2,-0.8 0.3,-1.6 0.5,-2.6 0.2,-1 0.5,-1.9 0.9,-2.8 0.4,-0.9 1,-1.6 1.6,-2.2 0.7,-0.6 1.5,-0.9 2.6,-0.9 1.3,0 2.1,0.4 2.6,1.1 0.4,0.7 0.6,1.6 0.6,2.6 -0.1,1 -0.2,2 -0.5,3 -0.3,1 -0.5,1.8 -0.7,2.4 -0.8,2.5 -1.6,4.7 -2.4,6.7 -0.8,2 -1.5,3.8 -2.2,5.2 -0.6,1.5 -1.1,2.6 -1.5,3.5 -0.4,0.9 -0.6,1.5 -0.6,1.8 0,2.6 0.6,4.5 1.7,5.6 1.1,1.1 2.3,1.7 3.6,1.7 2.2,0 3.9,-0.7 5.2,-2 1.3,-1.4 2.3,-3.9 2.9,-6.9 h 0.8 c 0.2,2.9 -0.1,5.3 -0.6,7.3 z"
|
||||||
id="path15"
|
id="path15"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#2e3192" /></svg>
|
style="fill:#2e3192" /><path
|
||||||
|
style="fill:#ffffff;stroke-width:0.0757324"
|
||||||
|
d="m 30.026506,86.559353 c -1.017302,-0.241662 -1.787869,-0.887419 -2.143612,-1.796406 -0.545654,-1.394246 -0.158934,-4.812615 1.126179,-9.954732 1.255925,-5.025324 2.459082,-9.096362 5.109736,-17.289458 0.344312,-1.064257 1.654133,-5.2136 1.888607,-5.982859 0.296596,-0.97307 0.598551,-2.708021 0.79743,-4.581811 0.108312,-1.020494 0.246431,-2.186451 0.306932,-2.591018 0.0605,-0.404565 0.178758,-1.341754 0.262796,-2.082641 0.224837,-1.982189 0.649291,-5.218012 0.916787,-6.98913 0.444542,-2.943359 0.753682,-4.198397 1.354756,-5.499991 0.686842,-1.487323 1.771061,-2.655188 2.805126,-3.021538 0.542395,-0.19216 1.381388,-0.270583 1.982594,-0.185316 1.252526,0.17764 1.883508,0.754167 2.211742,2.020866 0.313761,1.21084 -0.05565,3.930951 -0.877141,6.458782 -1.290698,3.971623 -2.036395,5.990995 -2.986916,8.088674 -1.185138,2.61545 -2.712212,6.873258 -2.939609,8.196258 -0.49042,2.853282 0.04972,5.146283 1.578225,6.6999 0.913915,0.928929 2.023939,1.521458 3.413442,1.82209 0.903748,0.195534 2.608483,0.179674 3.407958,-0.03171 1.383427,-0.365777 2.763884,-1.250325 4.377299,-2.804821 3.163126,-3.047616 5.113532,-6.222841 6.797438,-11.066108 0.353971,-1.018094 0.493359,-1.574562 0.749316,-2.991429 0.271014,-1.500218 1.040858,-5.574621 1.51657,-8.026458 0.08082,-0.416528 0.218253,-1.149239 0.305416,-1.628246 0.472088,-2.594388 1.148516,-4.178722 2.330295,-5.458032 0.763841,-0.826879 1.674493,-1.206419 2.894632,-1.206419 1.24359,0 2.138991,0.401576 2.574266,1.154526 0.974305,1.685378 0.683954,4.053139 -1.163626,9.489195 -0.954432,2.808181 -2.572717,6.998752 -3.493593,9.046702 -0.971745,2.161077 -2.201912,5.041664 -2.441809,5.717796 l -0.268706,0.757324 0.09021,1.120423 c 0.212423,2.638199 0.889316,4.086035 2.469149,5.281365 0.932959,0.705895 1.786459,0.982601 3.026274,0.981126 2.426542,-0.0029 4.480731,-1.028876 5.685658,-2.839769 0.811784,-1.220036 1.58443,-3.158397 2.044887,-5.130071 l 0.207813,-0.889855 h 0.356374 0.356373 l 0.04799,0.892492 c 0.0554,1.030319 -0.04881,3.015268 -0.219241,4.175846 -0.345822,2.354993 -1.040859,4.427262 -1.983165,5.91286 -0.701565,1.106055 -1.958204,2.491062 -2.717404,2.994989 -1.555814,1.032691 -4.187858,1.499135 -6.161832,1.091984 -0.603718,-0.124523 -1.72865,-0.689523 -2.178956,-1.094387 -1.477985,-1.328835 -2.187139,-3.341642 -2.360358,-6.699454 -0.08196,-1.588814 0.0522,-3.504923 0.298559,-4.263967 0.05681,-0.175039 0.04587,-0.208265 -0.06857,-0.208265 -0.09667,0 -0.197671,0.148268 -0.348229,0.511194 -0.711765,1.715746 -1.965261,3.867832 -3.142896,5.395934 -0.680786,0.883388 -2.612844,2.822501 -3.483678,3.496397 -2.517073,1.947843 -5.073167,2.951502 -8.060525,3.164993 -1.592379,0.1138 -2.868371,-0.07567 -4.016971,-0.596469 -1.69649,-0.769225 -3.109446,-2.469115 -3.819014,-4.594555 -0.614034,-1.839276 -0.863382,-4.754214 -0.580679,-6.788275 0.05951,-0.428202 0.126068,-0.957467 0.147897,-1.176145 l 0.03969,-0.397595 H 37.651633 37.254872 L 36.96284,53.90253 c -0.705326,1.783387 -1.458627,4.293583 -2.085205,6.948448 -1.027173,4.352223 -1.56307,7.486558 -2.197428,12.852248 -0.310323,2.624858 -0.310577,2.629265 -0.189513,3.294359 0.13956,0.766706 0.417018,1.85334 0.68249,2.672894 0.306093,0.944956 0.565598,2.296449 0.565598,2.945615 0,1.819491 -0.751236,3.258298 -2.006909,3.84374 -0.402074,0.187462 -1.15114,0.231172 -1.705369,0.09951 z"
|
||||||
|
id="path218" /></svg>
|
||||||
|
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 16 KiB |
|
@ -2,13 +2,6 @@
|
||||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
|
||||||
<svg
|
<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"
|
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="Layer_1"
|
id="Layer_1"
|
||||||
x="0px"
|
x="0px"
|
||||||
|
@ -16,13 +9,20 @@
|
||||||
viewBox="0 0 103.2 103.2"
|
viewBox="0 0 103.2 103.2"
|
||||||
enable-background="new 0 0 960 560"
|
enable-background="new 0 0 960 560"
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
inkscape:version="0.91 r13725"
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
sodipodi:docname="micro-logo-notext.svg"
|
sodipodi:docname="micro-logo-mark.svg"
|
||||||
width="103.2"
|
width="103.2"
|
||||||
height="103.2"><metadata
|
height="103.2"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata
|
||||||
id="metadata9"><rdf:RDF><cc:Work
|
id="metadata9"><rdf:RDF><cc:Work
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
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><defs
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||||
id="defs7" /><sodipodi:namedview
|
id="defs7" /><sodipodi:namedview
|
||||||
pagecolor="#ffffff"
|
pagecolor="#ffffff"
|
||||||
bordercolor="#666666"
|
bordercolor="#666666"
|
||||||
|
@ -32,22 +32,28 @@
|
||||||
guidetolerance="10"
|
guidetolerance="10"
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:window-width="733"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="480"
|
inkscape:window-height="1080"
|
||||||
id="namedview5"
|
id="namedview5"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
fit-margin-top="0"
|
fit-margin-top="0"
|
||||||
fit-margin-left="0"
|
fit-margin-left="0"
|
||||||
fit-margin-right="0"
|
fit-margin-right="0"
|
||||||
fit-margin-bottom="0"
|
fit-margin-bottom="0"
|
||||||
inkscape:zoom="0.28541667"
|
inkscape:zoom="5.405335"
|
||||||
inkscape:cx="302"
|
inkscape:cx="75.573484"
|
||||||
inkscape:cy="-4"
|
inkscape:cy="51.153166"
|
||||||
inkscape:window-x="1699"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="277"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="0"
|
inkscape:window-maximized="0"
|
||||||
inkscape:current-layer="Layer_1" /><path
|
inkscape:current-layer="Layer_1"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1" /><path
|
||||||
d="M 51.6,0 C 23.1,0 0,23.1 0,51.6 c 0,28.5 23.1,51.6 51.6,51.6 28.5,0 51.6,-23.1 51.6,-51.6 C 103.2,23.1 80.1,0 51.6,0 Z m 24.5,58.6 c -0.5,2 -1.3,3.6 -2.4,4.9 -1,1.3 -2,2.1 -3.1,2.5 -1.1,0.4 -2.2,0.6 -3.4,0.6 -1.2,0 -2.2,-0.2 -3,-0.7 C 63.4,65.5 62.8,64.8 62.3,64 61.8,63.2 61.5,62.2 61.3,61.1 61.1,60 61,58.8 61,57.5 c 0,-0.5 0,-1 0.1,-1.7 0.1,-0.7 0.2,-1.6 0.3,-1.6 l -0.2,0 c -1.6,4 -3.8,6.9 -6.6,9.2 -2.8,2.3 -5.9,3.4 -9.3,3.4 -2.3,0 -4.2,-0.9 -5.5,-2.6 -1.4,-1.7 -2.1,-4.3 -2.1,-7.7 0,-0.5 0,-1 0.1,-1.6 0.1,-0.5 0.1,-0.7 0.2,-1.7 l -0.7,0 c -0.9,2 -1.7,4.8 -2.3,7.3 -0.6,2.5 -1.1,4.8 -1.4,6.9 -0.4,2.1 -0.6,4 -0.8,5.6 -0.2,1.6 -0.3,2.7 -0.4,3.3 0.1,0.5 0.2,1 0.3,1.6 0.2,0.6 0.3,1.2 0.5,1.7 0.2,0.5 0.3,1.1 0.4,1.6 0.1,0.5 0.2,0.9 0.2,1.2 0,1.4 -0.3,2.5 -0.9,3.2 -0.6,0.7 -1.3,1.1 -2,1.1 -0.9,0 -1.7,-0.3 -2.3,-0.8 -0.7,-0.6 -1,-1.5 -1,-2.7 0,-1.7 0.3,-3.9 0.9,-6.5 0.6,-2.6 1.5,-5.9 2.6,-9.8 0.6,-1.8 1.1,-3.6 1.7,-5.4 0.6,-1.8 1.1,-3.5 1.6,-5 0.5,-1.5 0.9,-2.9 1.3,-4.1 0.4,-1.2 0.6,-2.1 0.7,-2.8 0.1,-0.3 0.2,-1 0.3,-2 0.1,-1 0.2,-2.1 0.4,-3.4 0.2,-1.3 0.3,-2.7 0.5,-4.1 0.2,-1.5 0.4,-2.8 0.5,-4 0.2,-0.9 0.3,-1.9 0.5,-3 0.2,-1.1 0.5,-2.2 0.9,-3.1 0.4,-1 1,-1.8 1.7,-2.5 0.7,-0.7 1.6,-1 2.7,-1 1.2,0 2,0.4 2.4,1.1 0.4,0.7 0.6,1.6 0.5,2.6 -0.1,1 -0.2,2.1 -0.5,3.2 -0.3,1.1 -0.6,2.1 -0.9,2.9 -0.8,2.5 -1.6,4.8 -2.5,6.7 -0.9,1.9 -1.7,4 -2.4,6.2 -0.6,1.5 -0.8,2.9 -0.8,4.1 0,2.2 0.7,3.8 2,5 1.4,1.2 3,1.7 4.9,1.7 1.5,0 3,-0.5 4.4,-1.6 1.4,-1.1 2.7,-2.4 3.9,-3.9 1.2,-1.5 2.2,-3.1 3,-4.9 0.8,-1.7 1.4,-3.3 1.8,-4.6 0.1,-0.2 0.2,-0.6 0.3,-1.4 0.2,-0.8 0.3,-1.7 0.5,-2.7 0.2,-1 0.4,-2 0.6,-3.1 0.2,-1.1 0.4,-2 0.5,-2.7 0.2,-0.8 0.3,-1.6 0.5,-2.6 0.2,-1 0.5,-1.9 0.9,-2.8 0.4,-0.9 1,-1.6 1.6,-2.2 0.7,-0.6 1.5,-0.9 2.6,-0.9 1.3,0 2.1,0.4 2.6,1.1 0.4,0.7 0.6,1.6 0.6,2.6 -0.1,1 -0.2,2 -0.5,3 -0.3,1 -0.5,1.8 -0.7,2.4 -0.8,2.5 -1.6,4.7 -2.4,6.7 -0.8,2 -1.5,3.8 -2.2,5.2 -0.6,1.5 -1.1,2.6 -1.5,3.5 -0.4,0.9 -0.6,1.5 -0.6,1.8 0,2.6 0.6,4.5 1.7,5.6 1.1,1.1 2.3,1.7 3.6,1.7 2.2,0 3.9,-0.7 5.2,-2 1.3,-1.4 2.3,-3.9 2.9,-6.9 l 0.8,0 c 0.2,2.9 -0.1,5.3 -0.6,7.3 z"
|
d="M 51.6,0 C 23.1,0 0,23.1 0,51.6 c 0,28.5 23.1,51.6 51.6,51.6 28.5,0 51.6,-23.1 51.6,-51.6 C 103.2,23.1 80.1,0 51.6,0 Z m 24.5,58.6 c -0.5,2 -1.3,3.6 -2.4,4.9 -1,1.3 -2,2.1 -3.1,2.5 -1.1,0.4 -2.2,0.6 -3.4,0.6 -1.2,0 -2.2,-0.2 -3,-0.7 C 63.4,65.5 62.8,64.8 62.3,64 61.8,63.2 61.5,62.2 61.3,61.1 61.1,60 61,58.8 61,57.5 c 0,-0.5 0,-1 0.1,-1.7 0.1,-0.7 0.2,-1.6 0.3,-1.6 l -0.2,0 c -1.6,4 -3.8,6.9 -6.6,9.2 -2.8,2.3 -5.9,3.4 -9.3,3.4 -2.3,0 -4.2,-0.9 -5.5,-2.6 -1.4,-1.7 -2.1,-4.3 -2.1,-7.7 0,-0.5 0,-1 0.1,-1.6 0.1,-0.5 0.1,-0.7 0.2,-1.7 l -0.7,0 c -0.9,2 -1.7,4.8 -2.3,7.3 -0.6,2.5 -1.1,4.8 -1.4,6.9 -0.4,2.1 -0.6,4 -0.8,5.6 -0.2,1.6 -0.3,2.7 -0.4,3.3 0.1,0.5 0.2,1 0.3,1.6 0.2,0.6 0.3,1.2 0.5,1.7 0.2,0.5 0.3,1.1 0.4,1.6 0.1,0.5 0.2,0.9 0.2,1.2 0,1.4 -0.3,2.5 -0.9,3.2 -0.6,0.7 -1.3,1.1 -2,1.1 -0.9,0 -1.7,-0.3 -2.3,-0.8 -0.7,-0.6 -1,-1.5 -1,-2.7 0,-1.7 0.3,-3.9 0.9,-6.5 0.6,-2.6 1.5,-5.9 2.6,-9.8 0.6,-1.8 1.1,-3.6 1.7,-5.4 0.6,-1.8 1.1,-3.5 1.6,-5 0.5,-1.5 0.9,-2.9 1.3,-4.1 0.4,-1.2 0.6,-2.1 0.7,-2.8 0.1,-0.3 0.2,-1 0.3,-2 0.1,-1 0.2,-2.1 0.4,-3.4 0.2,-1.3 0.3,-2.7 0.5,-4.1 0.2,-1.5 0.4,-2.8 0.5,-4 0.2,-0.9 0.3,-1.9 0.5,-3 0.2,-1.1 0.5,-2.2 0.9,-3.1 0.4,-1 1,-1.8 1.7,-2.5 0.7,-0.7 1.6,-1 2.7,-1 1.2,0 2,0.4 2.4,1.1 0.4,0.7 0.6,1.6 0.5,2.6 -0.1,1 -0.2,2.1 -0.5,3.2 -0.3,1.1 -0.6,2.1 -0.9,2.9 -0.8,2.5 -1.6,4.8 -2.5,6.7 -0.9,1.9 -1.7,4 -2.4,6.2 -0.6,1.5 -0.8,2.9 -0.8,4.1 0,2.2 0.7,3.8 2,5 1.4,1.2 3,1.7 4.9,1.7 1.5,0 3,-0.5 4.4,-1.6 1.4,-1.1 2.7,-2.4 3.9,-3.9 1.2,-1.5 2.2,-3.1 3,-4.9 0.8,-1.7 1.4,-3.3 1.8,-4.6 0.1,-0.2 0.2,-0.6 0.3,-1.4 0.2,-0.8 0.3,-1.7 0.5,-2.7 0.2,-1 0.4,-2 0.6,-3.1 0.2,-1.1 0.4,-2 0.5,-2.7 0.2,-0.8 0.3,-1.6 0.5,-2.6 0.2,-1 0.5,-1.9 0.9,-2.8 0.4,-0.9 1,-1.6 1.6,-2.2 0.7,-0.6 1.5,-0.9 2.6,-0.9 1.3,0 2.1,0.4 2.6,1.1 0.4,0.7 0.6,1.6 0.6,2.6 -0.1,1 -0.2,2 -0.5,3 -0.3,1 -0.5,1.8 -0.7,2.4 -0.8,2.5 -1.6,4.7 -2.4,6.7 -0.8,2 -1.5,3.8 -2.2,5.2 -0.6,1.5 -1.1,2.6 -1.5,3.5 -0.4,0.9 -0.6,1.5 -0.6,1.8 0,2.6 0.6,4.5 1.7,5.6 1.1,1.1 2.3,1.7 3.6,1.7 2.2,0 3.9,-0.7 5.2,-2 1.3,-1.4 2.3,-3.9 2.9,-6.9 l 0.8,0 c 0.2,2.9 -0.1,5.3 -0.6,7.3 z"
|
||||||
id="path3"
|
id="path3"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#2e3192" /></svg>
|
style="fill:#2e3192" /><path
|
||||||
|
style="fill:#ffffff;stroke-width:0.185002"
|
||||||
|
d="m 29.320064,86.164872 c -1.277771,-0.647664 -1.573829,-1.327981 -1.549788,-3.561297 0.04016,-3.730697 1.622887,-10.030031 5.903272,-23.495306 2.770635,-8.715885 2.799071,-8.822813 3.148729,-11.840154 0.585284,-5.050637 1.565844,-12.45598 1.8369,-13.872547 0.43516,-2.274196 0.976755,-3.690519 1.880879,-4.918684 0.974445,-1.323691 1.896478,-1.826405 3.360953,-1.832474 3.009215,-0.01247 3.55713,2.574946 1.786201,8.434969 -0.742771,2.45784 -2.2493,6.487571 -3.407575,9.114735 -0.420971,0.954834 -1.151241,2.827983 -1.622823,4.162554 -0.839682,2.376289 -0.857669,2.47434 -0.869358,4.739023 -0.01095,2.122185 0.02796,2.3976 0.472736,3.346042 0.91751,1.956495 2.602228,3.131322 5.078862,3.541714 2.587757,0.428804 4.551892,-0.347899 7.187533,-2.842264 2.232774,-2.113092 3.746907,-4.117682 4.998184,-6.617188 1.816108,-3.627792 2.213624,-4.978174 3.527565,-11.983266 0.66466,-3.543546 1.376157,-6.951356 1.581104,-7.57291 0.970636,-2.943689 2.922262,-4.567831 5.096985,-4.241711 1.740397,0.260989 2.500104,1.361773 2.494406,3.614287 -0.0068,2.696563 -2.48184,9.966491 -6.424307,18.870246 l -1.269708,2.867537 0.02005,1.757523 c 0.01504,1.318294 0.119434,2.015481 0.417735,2.789716 1.028756,2.67011 3.517063,4.054736 6.342356,3.529224 3.19144,-0.593617 4.98902,-2.612828 6.217715,-6.984325 0.403553,-1.435775 0.552101,-1.739647 0.850428,-1.739647 0.34646,0 0.356492,0.101757 0.241656,2.451282 -0.238951,4.888854 -1.330826,7.853563 -3.80789,10.339358 -1.255532,1.259957 -1.547319,1.456015 -2.694109,1.81022 -1.395674,0.431082 -3.784736,0.537505 -4.865716,0.216749 -1.759682,-0.522141 -3.031085,-2.027386 -3.686869,-4.364972 -0.336042,-1.197843 -0.516218,-5.455318 -0.283812,-6.706338 0.266094,-1.432359 -0.105859,-1.235144 -0.879069,0.466093 -1.724383,3.794037 -4.750586,7.236231 -8.063683,9.172148 -2.368072,1.383716 -5.903865,2.143782 -8.230062,1.769159 -2.672688,-0.430424 -4.588062,-2.213422 -5.66376,-5.272324 -0.491128,-1.396592 -0.514658,-1.618704 -0.512739,-4.840059 0.0018,-3.093063 -0.02515,-3.376294 -0.321772,-3.376294 -0.414677,0 -0.706335,0.582138 -1.434591,2.863386 -1.443227,4.52088 -2.73082,10.895957 -3.516703,17.411762 l -0.381426,3.162426 0.469219,1.740138 c 0.927877,3.441104 1.066474,4.326417 0.841521,5.375336 -0.537458,2.506081 -2.272098,3.528416 -4.269226,2.516133 z"
|
||||||
|
id="path210" /></svg>
|
||||||
|
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 6.2 KiB |
|
@ -2,13 +2,6 @@
|
||||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
|
||||||
<svg
|
<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"
|
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="Layer_1"
|
id="Layer_1"
|
||||||
x="0px"
|
x="0px"
|
||||||
|
@ -16,13 +9,20 @@
|
||||||
viewBox="0 0 299.89999 103.2"
|
viewBox="0 0 299.89999 103.2"
|
||||||
enable-background="new 0 0 960 560"
|
enable-background="new 0 0 960 560"
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
inkscape:version="0.91 r13725"
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
sodipodi:docname="micro-logo.svg"
|
sodipodi:docname="micro-logo.svg"
|
||||||
width="299.89999"
|
width="299.89999"
|
||||||
height="103.2"><metadata
|
height="103.2"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata
|
||||||
id="metadata21"><rdf:RDF><cc:Work
|
id="metadata21"><rdf:RDF><cc:Work
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
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><defs
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||||
id="defs19" /><sodipodi:namedview
|
id="defs19" /><sodipodi:namedview
|
||||||
pagecolor="#ffffff"
|
pagecolor="#ffffff"
|
||||||
bordercolor="#666666"
|
bordercolor="#666666"
|
||||||
|
@ -32,21 +32,24 @@
|
||||||
guidetolerance="10"
|
guidetolerance="10"
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:window-width="1237"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="867"
|
inkscape:window-height="1080"
|
||||||
id="namedview17"
|
id="namedview17"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
fit-margin-top="0"
|
fit-margin-top="0"
|
||||||
fit-margin-left="0"
|
fit-margin-left="0"
|
||||||
fit-margin-right="0"
|
fit-margin-right="0"
|
||||||
fit-margin-bottom="0"
|
fit-margin-bottom="0"
|
||||||
inkscape:zoom="1.1416667"
|
inkscape:zoom="16.645603"
|
||||||
inkscape:cx="75.655934"
|
inkscape:cx="65.092264"
|
||||||
inkscape:cy="-4"
|
inkscape:cy="49.051992"
|
||||||
inkscape:window-x="1097"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="185"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="0"
|
inkscape:window-maximized="0"
|
||||||
inkscape:current-layer="Layer_1" /><g
|
inkscape:current-layer="Layer_1"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1" /><g
|
||||||
id="g3"
|
id="g3"
|
||||||
transform="translate(-178,-172.8)"><path
|
transform="translate(-178,-172.8)"><path
|
||||||
d="m 306.8,213.8 0,-2.6 c 1.6,-0.1 2.9,-0.4 4.1,-0.8 1.2,-0.4 2.5,-1 4,-1.8 l 2.3,0 0,5.2 c 2.4,-1.9 4.2,-3.1 5.5,-3.8 2,-1 4,-1.5 5.8,-1.5 1.3,0 2.5,0.2 3.7,0.7 1.2,0.5 2.2,1 2.9,1.7 0.7,0.7 1.4,1.6 1.9,2.8 2.2,-1.9 4.2,-3.3 6,-4 1.9,-0.8 3.7,-1.2 5.6,-1.2 1.8,0 3.4,0.4 4.8,1.1 1.4,0.8 2.4,1.7 3,2.8 0.6,1.1 0.9,2.8 0.9,5 l 0,14.4 c 0,1.5 0,2.4 0.1,2.6 0.1,0.4 0.3,0.8 0.7,1.1 0.3,0.4 0.7,0.6 1.2,0.7 0.4,0.1 1.2,0.2 2.4,0.2 l 1,0 0,2.6 -15.5,0 0,-2.6 c 1.8,0 2.9,-0.1 3.5,-0.4 0.5,-0.2 0.9,-0.6 1.2,-1.2 0.3,-0.6 0.4,-1.6 0.4,-3.2 l 0,-13.7 c 0,-1.7 -0.2,-2.9 -0.5,-3.6 -0.3,-0.7 -0.9,-1.2 -1.7,-1.7 -0.8,-0.4 -1.8,-0.7 -3,-0.7 -1.5,0 -3,0.4 -4.6,1.2 -2.2,1.1 -3.9,2.3 -5.1,3.6 l 0,14.8 c 0,1.4 0.1,2.4 0.2,2.8 0.1,0.4 0.4,0.8 0.7,1.1 0.3,0.3 0.7,0.5 1.1,0.6 0.4,0.1 1.5,0.2 3.1,0.2 l 0,2.6 -15.3,0 0,-2.6 0.9,0 c 1.2,0 2.1,-0.1 2.6,-0.4 0.5,-0.3 0.9,-0.7 1.2,-1.3 0.2,-0.5 0.3,-1.5 0.3,-2.9 l 0,-13.2 c 0,-1.9 -0.2,-3.3 -0.5,-3.9 -0.3,-0.7 -0.9,-1.3 -1.7,-1.7 -0.8,-0.5 -1.8,-0.7 -3,-0.7 -1.3,0 -2.7,0.3 -4.1,1 -2,1 -3.9,2.2 -5.6,3.8 l 0,15.9 c 0,1 0.1,1.6 0.4,2.1 0.3,0.4 0.7,0.8 1.2,1.1 0.6,0.3 1.3,0.4 2.3,0.4 l 1.1,0 0,2.6 -15.6,0 0,-2.6 0.8,0 c 1.4,0 2.4,-0.1 2.8,-0.3 0.7,-0.3 1.1,-0.8 1.4,-1.5 0.2,-0.4 0.2,-1.3 0.2,-2.9 l 0,-18.1 -5.1,0 z"
|
d="m 306.8,213.8 0,-2.6 c 1.6,-0.1 2.9,-0.4 4.1,-0.8 1.2,-0.4 2.5,-1 4,-1.8 l 2.3,0 0,5.2 c 2.4,-1.9 4.2,-3.1 5.5,-3.8 2,-1 4,-1.5 5.8,-1.5 1.3,0 2.5,0.2 3.7,0.7 1.2,0.5 2.2,1 2.9,1.7 0.7,0.7 1.4,1.6 1.9,2.8 2.2,-1.9 4.2,-3.3 6,-4 1.9,-0.8 3.7,-1.2 5.6,-1.2 1.8,0 3.4,0.4 4.8,1.1 1.4,0.8 2.4,1.7 3,2.8 0.6,1.1 0.9,2.8 0.9,5 l 0,14.4 c 0,1.5 0,2.4 0.1,2.6 0.1,0.4 0.3,0.8 0.7,1.1 0.3,0.4 0.7,0.6 1.2,0.7 0.4,0.1 1.2,0.2 2.4,0.2 l 1,0 0,2.6 -15.5,0 0,-2.6 c 1.8,0 2.9,-0.1 3.5,-0.4 0.5,-0.2 0.9,-0.6 1.2,-1.2 0.3,-0.6 0.4,-1.6 0.4,-3.2 l 0,-13.7 c 0,-1.7 -0.2,-2.9 -0.5,-3.6 -0.3,-0.7 -0.9,-1.2 -1.7,-1.7 -0.8,-0.4 -1.8,-0.7 -3,-0.7 -1.5,0 -3,0.4 -4.6,1.2 -2.2,1.1 -3.9,2.3 -5.1,3.6 l 0,14.8 c 0,1.4 0.1,2.4 0.2,2.8 0.1,0.4 0.4,0.8 0.7,1.1 0.3,0.3 0.7,0.5 1.1,0.6 0.4,0.1 1.5,0.2 3.1,0.2 l 0,2.6 -15.3,0 0,-2.6 0.9,0 c 1.2,0 2.1,-0.1 2.6,-0.4 0.5,-0.3 0.9,-0.7 1.2,-1.3 0.2,-0.5 0.3,-1.5 0.3,-2.9 l 0,-13.2 c 0,-1.9 -0.2,-3.3 -0.5,-3.9 -0.3,-0.7 -0.9,-1.3 -1.7,-1.7 -0.8,-0.5 -1.8,-0.7 -3,-0.7 -1.3,0 -2.7,0.3 -4.1,1 -2,1 -3.9,2.2 -5.6,3.8 l 0,15.9 c 0,1 0.1,1.6 0.4,2.1 0.3,0.4 0.7,0.8 1.2,1.1 0.6,0.3 1.3,0.4 2.3,0.4 l 1.1,0 0,2.6 -15.6,0 0,-2.6 0.8,0 c 1.4,0 2.4,-0.1 2.8,-0.3 0.7,-0.3 1.1,-0.8 1.4,-1.5 0.2,-0.4 0.2,-1.3 0.2,-2.9 l 0,-18.1 -5.1,0 z"
|
||||||
|
@ -67,4 +70,7 @@
|
||||||
d="M 51.6,0 C 23.1,0 0,23.1 0,51.6 c 0,28.5 23.1,51.6 51.6,51.6 28.5,0 51.6,-23.1 51.6,-51.6 C 103.2,23.1 80.1,0 51.6,0 Z m 24.5,58.6 c -0.5,2 -1.3,3.6 -2.4,4.9 -1,1.3 -2,2.1 -3.1,2.5 -1.1,0.4 -2.2,0.6 -3.4,0.6 -1.2,0 -2.2,-0.2 -3,-0.7 C 63.4,65.5 62.8,64.8 62.3,64 61.8,63.2 61.5,62.2 61.3,61.1 61.1,60 61,58.8 61,57.5 c 0,-0.5 0,-1 0.1,-1.7 0.1,-0.7 0.2,-1.6 0.3,-1.6 l -0.2,0 c -1.6,4 -3.8,6.9 -6.6,9.2 -2.8,2.3 -5.9,3.4 -9.3,3.4 -2.3,0 -4.2,-0.9 -5.5,-2.6 -1.4,-1.7 -2.1,-4.3 -2.1,-7.7 0,-0.5 0,-1 0.1,-1.6 0.1,-0.5 0.1,-0.7 0.2,-1.7 l -0.7,0 c -0.9,2 -1.7,4.8 -2.3,7.3 -0.6,2.5 -1.1,4.8 -1.4,6.9 -0.4,2.1 -0.6,4 -0.8,5.6 -0.2,1.6 -0.3,2.7 -0.4,3.3 0.1,0.5 0.2,1 0.3,1.6 0.2,0.6 0.3,1.2 0.5,1.7 0.2,0.5 0.3,1.1 0.4,1.6 0.1,0.5 0.2,0.9 0.2,1.2 0,1.4 -0.3,2.5 -0.9,3.2 -0.6,0.7 -1.3,1.1 -2,1.1 -0.9,0 -1.7,-0.3 -2.3,-0.8 -0.7,-0.6 -1,-1.5 -1,-2.7 0,-1.7 0.3,-3.9 0.9,-6.5 0.6,-2.6 1.5,-5.9 2.6,-9.8 0.6,-1.8 1.1,-3.6 1.7,-5.4 0.6,-1.8 1.1,-3.5 1.6,-5 0.5,-1.5 0.9,-2.9 1.3,-4.1 0.4,-1.2 0.6,-2.1 0.7,-2.8 0.1,-0.3 0.2,-1 0.3,-2 0.1,-1 0.2,-2.1 0.4,-3.4 0.2,-1.3 0.3,-2.7 0.5,-4.1 0.2,-1.5 0.4,-2.8 0.5,-4 0.2,-0.9 0.3,-1.9 0.5,-3 0.2,-1.1 0.5,-2.2 0.9,-3.1 0.4,-1 1,-1.8 1.7,-2.5 0.7,-0.7 1.6,-1 2.7,-1 1.2,0 2,0.4 2.4,1.1 0.4,0.7 0.6,1.6 0.5,2.6 -0.1,1 -0.2,2.1 -0.5,3.2 -0.3,1.1 -0.6,2.1 -0.9,2.9 -0.8,2.5 -1.6,4.8 -2.5,6.7 -0.9,1.9 -1.7,4 -2.4,6.2 -0.6,1.5 -0.8,2.9 -0.8,4.1 0,2.2 0.7,3.8 2,5 1.4,1.2 3,1.7 4.9,1.7 1.5,0 3,-0.5 4.4,-1.6 1.4,-1.1 2.7,-2.4 3.9,-3.9 1.2,-1.5 2.2,-3.1 3,-4.9 0.8,-1.7 1.4,-3.3 1.8,-4.6 0.1,-0.2 0.2,-0.6 0.3,-1.4 0.2,-0.8 0.3,-1.7 0.5,-2.7 0.2,-1 0.4,-2 0.6,-3.1 0.2,-1.1 0.4,-2 0.5,-2.7 0.2,-0.8 0.3,-1.6 0.5,-2.6 0.2,-1 0.5,-1.9 0.9,-2.8 0.4,-0.9 1,-1.6 1.6,-2.2 0.7,-0.6 1.5,-0.9 2.6,-0.9 1.3,0 2.1,0.4 2.6,1.1 0.4,0.7 0.6,1.6 0.6,2.6 -0.1,1 -0.2,2 -0.5,3 -0.3,1 -0.5,1.8 -0.7,2.4 -0.8,2.5 -1.6,4.7 -2.4,6.7 -0.8,2 -1.5,3.8 -2.2,5.2 -0.6,1.5 -1.1,2.6 -1.5,3.5 -0.4,0.9 -0.6,1.5 -0.6,1.8 0,2.6 0.6,4.5 1.7,5.6 1.1,1.1 2.3,1.7 3.6,1.7 2.2,0 3.9,-0.7 5.2,-2 1.3,-1.4 2.3,-3.9 2.9,-6.9 l 0.8,0 c 0.2,2.9 -0.1,5.3 -0.6,7.3 z"
|
d="M 51.6,0 C 23.1,0 0,23.1 0,51.6 c 0,28.5 23.1,51.6 51.6,51.6 28.5,0 51.6,-23.1 51.6,-51.6 C 103.2,23.1 80.1,0 51.6,0 Z m 24.5,58.6 c -0.5,2 -1.3,3.6 -2.4,4.9 -1,1.3 -2,2.1 -3.1,2.5 -1.1,0.4 -2.2,0.6 -3.4,0.6 -1.2,0 -2.2,-0.2 -3,-0.7 C 63.4,65.5 62.8,64.8 62.3,64 61.8,63.2 61.5,62.2 61.3,61.1 61.1,60 61,58.8 61,57.5 c 0,-0.5 0,-1 0.1,-1.7 0.1,-0.7 0.2,-1.6 0.3,-1.6 l -0.2,0 c -1.6,4 -3.8,6.9 -6.6,9.2 -2.8,2.3 -5.9,3.4 -9.3,3.4 -2.3,0 -4.2,-0.9 -5.5,-2.6 -1.4,-1.7 -2.1,-4.3 -2.1,-7.7 0,-0.5 0,-1 0.1,-1.6 0.1,-0.5 0.1,-0.7 0.2,-1.7 l -0.7,0 c -0.9,2 -1.7,4.8 -2.3,7.3 -0.6,2.5 -1.1,4.8 -1.4,6.9 -0.4,2.1 -0.6,4 -0.8,5.6 -0.2,1.6 -0.3,2.7 -0.4,3.3 0.1,0.5 0.2,1 0.3,1.6 0.2,0.6 0.3,1.2 0.5,1.7 0.2,0.5 0.3,1.1 0.4,1.6 0.1,0.5 0.2,0.9 0.2,1.2 0,1.4 -0.3,2.5 -0.9,3.2 -0.6,0.7 -1.3,1.1 -2,1.1 -0.9,0 -1.7,-0.3 -2.3,-0.8 -0.7,-0.6 -1,-1.5 -1,-2.7 0,-1.7 0.3,-3.9 0.9,-6.5 0.6,-2.6 1.5,-5.9 2.6,-9.8 0.6,-1.8 1.1,-3.6 1.7,-5.4 0.6,-1.8 1.1,-3.5 1.6,-5 0.5,-1.5 0.9,-2.9 1.3,-4.1 0.4,-1.2 0.6,-2.1 0.7,-2.8 0.1,-0.3 0.2,-1 0.3,-2 0.1,-1 0.2,-2.1 0.4,-3.4 0.2,-1.3 0.3,-2.7 0.5,-4.1 0.2,-1.5 0.4,-2.8 0.5,-4 0.2,-0.9 0.3,-1.9 0.5,-3 0.2,-1.1 0.5,-2.2 0.9,-3.1 0.4,-1 1,-1.8 1.7,-2.5 0.7,-0.7 1.6,-1 2.7,-1 1.2,0 2,0.4 2.4,1.1 0.4,0.7 0.6,1.6 0.5,2.6 -0.1,1 -0.2,2.1 -0.5,3.2 -0.3,1.1 -0.6,2.1 -0.9,2.9 -0.8,2.5 -1.6,4.8 -2.5,6.7 -0.9,1.9 -1.7,4 -2.4,6.2 -0.6,1.5 -0.8,2.9 -0.8,4.1 0,2.2 0.7,3.8 2,5 1.4,1.2 3,1.7 4.9,1.7 1.5,0 3,-0.5 4.4,-1.6 1.4,-1.1 2.7,-2.4 3.9,-3.9 1.2,-1.5 2.2,-3.1 3,-4.9 0.8,-1.7 1.4,-3.3 1.8,-4.6 0.1,-0.2 0.2,-0.6 0.3,-1.4 0.2,-0.8 0.3,-1.7 0.5,-2.7 0.2,-1 0.4,-2 0.6,-3.1 0.2,-1.1 0.4,-2 0.5,-2.7 0.2,-0.8 0.3,-1.6 0.5,-2.6 0.2,-1 0.5,-1.9 0.9,-2.8 0.4,-0.9 1,-1.6 1.6,-2.2 0.7,-0.6 1.5,-0.9 2.6,-0.9 1.3,0 2.1,0.4 2.6,1.1 0.4,0.7 0.6,1.6 0.6,2.6 -0.1,1 -0.2,2 -0.5,3 -0.3,1 -0.5,1.8 -0.7,2.4 -0.8,2.5 -1.6,4.7 -2.4,6.7 -0.8,2 -1.5,3.8 -2.2,5.2 -0.6,1.5 -1.1,2.6 -1.5,3.5 -0.4,0.9 -0.6,1.5 -0.6,1.8 0,2.6 0.6,4.5 1.7,5.6 1.1,1.1 2.3,1.7 3.6,1.7 2.2,0 3.9,-0.7 5.2,-2 1.3,-1.4 2.3,-3.9 2.9,-6.9 l 0.8,0 c 0.2,2.9 -0.1,5.3 -0.6,7.3 z"
|
||||||
id="path15"
|
id="path15"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#2e3192" /></svg>
|
style="fill:#2e3192" /><path
|
||||||
|
style="fill:#ffffff;stroke-width:0.0600759"
|
||||||
|
d="m 30.192709,86.597991 c -0.530828,-0.09608 -1.19875,-0.411872 -1.578921,-0.746511 -0.792953,-0.697985 -1.054327,-1.680313 -0.947823,-3.562219 0.16271,-2.875042 0.852662,-6.034057 2.963728,-13.569713 0.66017,-2.356543 0.955814,-3.307037 3.762987,-12.097989 1.219825,-3.820007 1.435496,-4.505244 1.616654,-5.136492 0.306236,-1.067081 0.590331,-2.663175 0.753866,-4.235353 0.08592,-0.826044 0.236455,-2.096649 0.334514,-2.823568 0.09806,-0.726919 0.246246,-1.916422 0.329306,-2.643341 0.08306,-0.726918 0.231698,-1.902905 0.330307,-2.613302 0.09861,-0.710398 0.231242,-1.724179 0.294741,-2.252848 0.19473,-1.621264 0.604712,-4.037809 0.845956,-4.986301 0.495326,-1.947452 1.158621,-3.216325 2.26111,-4.325467 0.731983,-0.736399 1.547763,-1.051329 2.723316,-1.051329 1.344787,0 2.103359,0.409522 2.539237,1.370828 0.373167,0.823003 0.432731,1.702332 0.227502,3.358553 -0.206897,1.669687 -0.429401,2.498899 -1.62432,6.053417 -0.891865,2.653022 -1.418886,4.025585 -2.237847,5.828196 -0.890733,1.960586 -1.401439,3.281416 -2.291175,5.925621 -0.696894,2.071095 -0.858755,3.003396 -0.79649,4.587665 0.05016,1.276299 0.270881,2.168068 0.761945,3.078469 1.114561,2.066325 3.341124,3.259541 6.082361,3.259541 0.831865,0 1.52957,-0.113832 2.245267,-0.366322 1.037155,-0.365895 1.69838,-0.767468 2.829986,-1.718697 2.058613,-1.730473 4.031033,-4.098263 5.356083,-6.429706 1.132231,-1.992175 2.742129,-5.986041 2.978686,-7.389579 0.126006,-0.747618 0.37151,-2.073261 0.753923,-4.070941 0.459374,-2.399719 0.965049,-5.073707 1.26106,-6.668427 0.439666,-2.368642 0.948255,-3.731056 1.831386,-4.905927 1.000947,-1.33161 1.919678,-1.818989 3.424905,-1.816884 1.371199,0.0019 2.259901,0.453797 2.692584,1.369104 0.199937,0.42295 0.37898,1.160518 0.431897,1.779189 0.0423,0.494585 -0.08313,1.707742 -0.270194,2.613303 -0.520247,2.51845 -2.995194,9.527499 -4.836622,13.697311 -0.189691,0.429543 -0.709117,1.619046 -1.154281,2.64334 -0.445164,1.024295 -0.903857,2.078627 -1.019317,2.342962 -0.593057,1.357747 -0.644155,1.607255 -0.563046,2.7493 0.142046,2.000035 0.604952,3.420811 1.436759,4.409774 0.719848,0.85585 1.902762,1.62255 2.859809,1.853569 0.533147,0.128695 1.669602,0.128252 2.472607,-9.67e-4 1.437635,-0.231339 2.769133,-0.900566 3.72751,-1.873493 1.098243,-1.114915 2.227996,-3.662559 2.785802,-6.282105 l 0.13752,-0.645816 h 0.37414 0.37414 l 0.04419,0.94284 c 0.124949,2.666054 -0.382363,6.016009 -1.237138,8.16926 -0.848692,2.137927 -2.617365,4.354096 -4.156972,5.208738 -1.58257,0.878493 -4.420415,1.19721 -6.111929,0.68643 -0.649563,-0.196146 -1.47209,-0.685817 -1.961392,-1.167665 -1.354216,-1.333585 -1.999054,-3.254244 -2.18916,-6.52045 -0.03525,-0.60571 -0.04689,-1.38515 -0.02584,-1.732089 0.04435,-0.731258 0.257009,-2.357205 0.335205,-2.562875 0.04613,-0.121335 0.03516,-0.140427 -0.08025,-0.139702 -0.11259,7.09e-4 -0.171074,0.09313 -0.370649,0.58574 -0.571777,1.411317 -1.625409,3.288777 -2.58713,4.609988 -2.555402,3.510606 -5.935984,6.014779 -9.311242,6.897323 -1.386313,0.362485 -1.927076,0.42829 -3.514441,0.427668 -1.398071,-5.41e-4 -1.500695,-0.0084 -2.047014,-0.157216 -1.248806,-0.340101 -2.244463,-0.904197 -3.05944,-1.733346 -1.343156,-1.366511 -2.129105,-3.116872 -2.494126,-5.554581 -0.150028,-1.001927 -0.191427,-3.616227 -0.06949,-4.388291 0.05195,-0.328906 0.113311,-0.84947 0.136367,-1.156809 l 0.04192,-0.558799 -0.380315,0.01812 -0.380315,0.01812 -0.231805,0.570721 c -1.478913,3.641182 -3.072314,10.383891 -3.918324,16.580955 -0.190557,1.395837 -0.701916,5.676121 -0.706953,5.917479 -0.0093,0.446744 0.454257,2.427922 0.818884,3.499628 0.121802,0.358001 0.382754,1.549663 0.538684,2.459961 0.04595,0.268246 -0.06655,1.468043 -0.178759,1.906478 -0.165253,0.645686 -0.477741,1.20884 -0.915337,1.649588 -0.463951,0.467293 -0.819805,0.689321 -1.309045,0.816755 -0.410787,0.106995 -0.564727,0.106887 -1.159735,-7.81e-4 z"
|
||||||
|
id="path240" /></svg>
|
||||||
|
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 12 KiB |
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
lua "github.com/yuin/gopher-lua"
|
lua "github.com/yuin/gopher-lua"
|
||||||
luar "layeh.com/gopher-luar"
|
luar "layeh.com/gopher-luar"
|
||||||
|
@ -47,14 +48,18 @@ func luaImportMicro() *lua.LTable {
|
||||||
ulua.L.SetField(pkg, "InfoBar", luar.New(ulua.L, action.GetInfoBar))
|
ulua.L.SetField(pkg, "InfoBar", luar.New(ulua.L, action.GetInfoBar))
|
||||||
ulua.L.SetField(pkg, "Log", luar.New(ulua.L, log.Println))
|
ulua.L.SetField(pkg, "Log", luar.New(ulua.L, log.Println))
|
||||||
ulua.L.SetField(pkg, "SetStatusInfoFn", luar.New(ulua.L, display.SetStatusInfoFnLua))
|
ulua.L.SetField(pkg, "SetStatusInfoFn", luar.New(ulua.L, display.SetStatusInfoFnLua))
|
||||||
ulua.L.SetField(pkg, "CurPane", luar.New(ulua.L, func() action.Pane {
|
ulua.L.SetField(pkg, "CurPane", luar.New(ulua.L, func() *action.BufPane {
|
||||||
return action.MainTab().CurPane()
|
return action.MainTab().CurPane()
|
||||||
}))
|
}))
|
||||||
ulua.L.SetField(pkg, "CurTab", luar.New(ulua.L, action.MainTab))
|
ulua.L.SetField(pkg, "CurTab", luar.New(ulua.L, action.MainTab))
|
||||||
ulua.L.SetField(pkg, "Tabs", luar.New(ulua.L, func() *action.TabList {
|
ulua.L.SetField(pkg, "Tabs", luar.New(ulua.L, func() *action.TabList {
|
||||||
return action.Tabs
|
return action.Tabs
|
||||||
}))
|
}))
|
||||||
ulua.L.SetField(pkg, "Lock", luar.New(ulua.L, ulua.Lock))
|
ulua.L.SetField(pkg, "After", luar.New(ulua.L, func(t time.Duration, f func()) {
|
||||||
|
time.AfterFunc(t, func() {
|
||||||
|
timerChan <- f
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
return pkg
|
return pkg
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -22,7 +23,6 @@ import (
|
||||||
"github.com/zyedidia/micro/v2/internal/buffer"
|
"github.com/zyedidia/micro/v2/internal/buffer"
|
||||||
"github.com/zyedidia/micro/v2/internal/clipboard"
|
"github.com/zyedidia/micro/v2/internal/clipboard"
|
||||||
"github.com/zyedidia/micro/v2/internal/config"
|
"github.com/zyedidia/micro/v2/internal/config"
|
||||||
ulua "github.com/zyedidia/micro/v2/internal/lua"
|
|
||||||
"github.com/zyedidia/micro/v2/internal/screen"
|
"github.com/zyedidia/micro/v2/internal/screen"
|
||||||
"github.com/zyedidia/micro/v2/internal/shell"
|
"github.com/zyedidia/micro/v2/internal/shell"
|
||||||
"github.com/zyedidia/micro/v2/internal/util"
|
"github.com/zyedidia/micro/v2/internal/util"
|
||||||
|
@ -30,9 +30,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Event channel
|
|
||||||
autosave chan bool
|
|
||||||
|
|
||||||
// Command line flags
|
// Command line flags
|
||||||
flagVersion = flag.Bool("version", false, "Show the version number and information")
|
flagVersion = flag.Bool("version", false, "Show the version number and information")
|
||||||
flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
|
flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
|
||||||
|
@ -45,6 +42,8 @@ var (
|
||||||
|
|
||||||
sigterm chan os.Signal
|
sigterm chan os.Signal
|
||||||
sighup chan os.Signal
|
sighup chan os.Signal
|
||||||
|
|
||||||
|
timerChan chan func()
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitFlags() {
|
func InitFlags() {
|
||||||
|
@ -67,7 +66,7 @@ func InitFlags() {
|
||||||
fmt.Println("-version")
|
fmt.Println("-version")
|
||||||
fmt.Println(" \tShow the version number and information")
|
fmt.Println(" \tShow the version number and information")
|
||||||
|
|
||||||
fmt.Print("\nMicro's plugin's can be managed at the command line with the following commands.\n")
|
fmt.Print("\nMicro's plugins can be managed at the command line with the following commands.\n")
|
||||||
fmt.Println("-plugin install [PLUGIN]...")
|
fmt.Println("-plugin install [PLUGIN]...")
|
||||||
fmt.Println(" \tInstall plugin(s)")
|
fmt.Println(" \tInstall plugin(s)")
|
||||||
fmt.Println("-plugin remove [PLUGIN]...")
|
fmt.Println("-plugin remove [PLUGIN]...")
|
||||||
|
@ -255,7 +254,9 @@ func main() {
|
||||||
screen.TermMessage(err)
|
screen.TermMessage(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config.InitRuntimeFiles()
|
config.InitRuntimeFiles(true)
|
||||||
|
config.InitPlugins()
|
||||||
|
|
||||||
err = config.ReadSettings()
|
err = config.ReadSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage(err)
|
screen.TermMessage(err)
|
||||||
|
@ -274,6 +275,7 @@ func main() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
config.GlobalSettings[k] = nativeValue
|
config.GlobalSettings[k] = nativeValue
|
||||||
|
config.VolatileSettings[k] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,9 +362,11 @@ func main() {
|
||||||
|
|
||||||
sigterm = make(chan os.Signal, 1)
|
sigterm = make(chan os.Signal, 1)
|
||||||
sighup = make(chan os.Signal, 1)
|
sighup = make(chan os.Signal, 1)
|
||||||
signal.Notify(sigterm, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
|
signal.Notify(sigterm, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGABRT)
|
||||||
signal.Notify(sighup, syscall.SIGHUP)
|
signal.Notify(sighup, syscall.SIGHUP)
|
||||||
|
|
||||||
|
timerChan = make(chan func())
|
||||||
|
|
||||||
// Here is the event loop which runs in a separate thread
|
// Here is the event loop which runs in a separate thread
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
@ -413,21 +417,19 @@ func DoEvent() {
|
||||||
select {
|
select {
|
||||||
case f := <-shell.Jobs:
|
case f := <-shell.Jobs:
|
||||||
// If a new job has finished while running in the background we should execute the callback
|
// If a new job has finished while running in the background we should execute the callback
|
||||||
ulua.Lock.Lock()
|
|
||||||
f.Function(f.Output, f.Args)
|
f.Function(f.Output, f.Args)
|
||||||
ulua.Lock.Unlock()
|
|
||||||
case <-config.Autosave:
|
case <-config.Autosave:
|
||||||
ulua.Lock.Lock()
|
|
||||||
for _, b := range buffer.OpenBuffers {
|
for _, b := range buffer.OpenBuffers {
|
||||||
b.Save()
|
b.AutoSave()
|
||||||
}
|
}
|
||||||
ulua.Lock.Unlock()
|
|
||||||
case <-shell.CloseTerms:
|
case <-shell.CloseTerms:
|
||||||
case event = <-screen.Events:
|
case event = <-screen.Events:
|
||||||
case <-screen.DrawChan():
|
case <-screen.DrawChan():
|
||||||
for len(screen.DrawChan()) > 0 {
|
for len(screen.DrawChan()) > 0 {
|
||||||
<-screen.DrawChan()
|
<-screen.DrawChan()
|
||||||
}
|
}
|
||||||
|
case f := <-timerChan:
|
||||||
|
f()
|
||||||
case <-sighup:
|
case <-sighup:
|
||||||
for _, b := range buffer.OpenBuffers {
|
for _, b := range buffer.OpenBuffers {
|
||||||
if !b.Modified() {
|
if !b.Modified() {
|
||||||
|
@ -448,13 +450,39 @@ func DoEvent() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
ulua.Lock.Lock()
|
if e, ok := event.(*tcell.EventError); ok {
|
||||||
// if event != nil {
|
log.Println("tcell event error: ", e.Error())
|
||||||
if action.InfoBar.HasPrompt {
|
|
||||||
|
if e.Err() == io.EOF {
|
||||||
|
// shutdown due to terminal closing/becoming inaccessible
|
||||||
|
for _, b := range buffer.OpenBuffers {
|
||||||
|
if !b.Modified() {
|
||||||
|
b.Fini()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if screen.Screen != nil {
|
||||||
|
screen.Screen.Fini()
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if event != nil {
|
||||||
|
_, resize := event.(*tcell.EventResize)
|
||||||
|
if resize {
|
||||||
|
action.InfoBar.HandleEvent(event)
|
||||||
|
action.Tabs.HandleEvent(event)
|
||||||
|
} else if action.InfoBar.HasPrompt {
|
||||||
action.InfoBar.HandleEvent(event)
|
action.InfoBar.HandleEvent(event)
|
||||||
} else {
|
} else {
|
||||||
action.Tabs.HandleEvent(event)
|
action.Tabs.HandleEvent(event)
|
||||||
}
|
}
|
||||||
// }
|
}
|
||||||
ulua.Lock.Unlock()
|
|
||||||
|
err := config.RunPluginFn("onAnyEvent")
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,9 @@ func startup(args []string) (tcell.SimulationScreen, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
config.InitRuntimeFiles()
|
config.InitRuntimeFiles(true)
|
||||||
|
config.InitPlugins()
|
||||||
|
|
||||||
err = config.ReadSettings()
|
err = config.ReadSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -107,7 +109,10 @@ func handleEvent() {
|
||||||
if e != nil {
|
if e != nil {
|
||||||
screen.Events <- e
|
screen.Events <- e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for len(screen.DrawChan()) > 0 || len(screen.Events) > 0 {
|
||||||
DoEvent()
|
DoEvent()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func injectKey(key tcell.Key, r rune, mod tcell.ModMask) {
|
func injectKey(key tcell.Key, r rune, mod tcell.ModMask) {
|
||||||
|
@ -149,6 +154,16 @@ func openFile(file string) {
|
||||||
injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone)
|
injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findBuffer(file string) *buffer.Buffer {
|
||||||
|
var buf *buffer.Buffer
|
||||||
|
for _, b := range buffer.OpenBuffers {
|
||||||
|
if b.Path == file {
|
||||||
|
buf = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
func createTestFile(name string, content string) (string, error) {
|
func createTestFile(name string, content string) (string, error) {
|
||||||
testf, err := ioutil.TempFile("", name)
|
testf, err := ioutil.TempFile("", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -188,14 +203,7 @@ func TestSimpleEdit(t *testing.T) {
|
||||||
|
|
||||||
openFile(file)
|
openFile(file)
|
||||||
|
|
||||||
var buf *buffer.Buffer
|
if findBuffer(file) == nil {
|
||||||
for _, b := range buffer.OpenBuffers {
|
|
||||||
if b.Path == file {
|
|
||||||
buf = b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if buf == nil {
|
|
||||||
t.Errorf("Could not find buffer %s", file)
|
t.Errorf("Could not find buffer %s", file)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -234,6 +242,11 @@ func TestMouse(t *testing.T) {
|
||||||
|
|
||||||
openFile(file)
|
openFile(file)
|
||||||
|
|
||||||
|
if findBuffer(file) == nil {
|
||||||
|
t.Errorf("Could not find buffer %s", file)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// buffer:
|
// buffer:
|
||||||
// base content
|
// base content
|
||||||
// the selections need to happen at different locations to avoid a double click
|
// the selections need to happen at different locations to avoid a double click
|
||||||
|
@ -297,6 +310,11 @@ func TestSearchAndReplace(t *testing.T) {
|
||||||
|
|
||||||
openFile(file)
|
openFile(file)
|
||||||
|
|
||||||
|
if findBuffer(file) == nil {
|
||||||
|
t.Errorf("Could not find buffer %s", file)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
injectKey(tcell.KeyCtrlE, rune(tcell.KeyCtrlE), tcell.ModCtrl)
|
injectKey(tcell.KeyCtrlE, rune(tcell.KeyCtrlE), tcell.ModCtrl)
|
||||||
injectString(fmt.Sprintf("replaceall %s %s", "foo", "test_string"))
|
injectString(fmt.Sprintf("replaceall %s %s", "foo", "test_string"))
|
||||||
injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone)
|
injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone)
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<component>
|
|
||||||
<id>com.github.zyedidia.micro</id>
|
|
||||||
<launchable type="desktop-id">micro.desktop</launchable>
|
|
||||||
<name>Micro Text Editor</name>
|
|
||||||
<summary>A modern and intuitive terminal-based text editor</summary>
|
|
||||||
<metadata_license>MIT</metadata_license>
|
|
||||||
<categories>
|
|
||||||
<category>Development</category>
|
|
||||||
<category>TextEditor</category>
|
|
||||||
</categories>
|
|
||||||
<provides>
|
|
||||||
<binary>micro</binary>
|
|
||||||
</provides>
|
|
||||||
<developer_name>Zachary Yedidia</developer_name>
|
|
||||||
<screenshots>
|
|
||||||
<screenshot type="default">
|
|
||||||
<caption>Micro Text Editor editing its source code.</caption>
|
|
||||||
<image type="source">https://raw.githubusercontent.com/zyedidia/micro/master/assets/micro-solarized.png</image>
|
|
||||||
</screenshot>
|
|
||||||
</screenshots>
|
|
||||||
<url type="homepage">https://micro-editor.github.io</url>
|
|
||||||
<url type="bugtracker">https://github.com/zyedidia/micro/issues</url>
|
|
||||||
</component>
|
|
51
data/io.github.zyedidia.micro.metainfo.xml
Normal file
51
data/io.github.zyedidia.micro.metainfo.xml
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component type="desktop-application">
|
||||||
|
<id>io.github.zyedidia.micro</id>
|
||||||
|
<launchable type="desktop-id">micro.desktop</launchable>
|
||||||
|
<name>Micro Text Editor</name>
|
||||||
|
<summary>A modern and intuitive terminal-based text editor</summary>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
micro is a terminal-based text editor that aims to be easy to use and
|
||||||
|
intuitive, while also taking advantage of the capabilities of modern terminals.
|
||||||
|
It comes as a single, batteries-included, static binary with no dependencies;
|
||||||
|
you can download and use it right now!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
As its name indicates, micro aims to be somewhat of a successor to the nano
|
||||||
|
editor by being easy to install and use. It strives to be enjoyable as a full-time
|
||||||
|
editor for people who prefer to work in a terminal, or those who regularly
|
||||||
|
edit files over SSH.
|
||||||
|
</p>
|
||||||
|
</description>
|
||||||
|
<metadata_license>MIT</metadata_license>
|
||||||
|
<project_license>MIT</project_license>
|
||||||
|
<categories>
|
||||||
|
<category>Development</category>
|
||||||
|
<category>TextEditor</category>
|
||||||
|
</categories>
|
||||||
|
<releases>
|
||||||
|
<release version="2.0.13" date="2023-10-22"/>
|
||||||
|
<release version="2.0.12" date="2023-09-06"/>
|
||||||
|
<release version="2.0.11" date="2022-08-01"/>
|
||||||
|
</releases>
|
||||||
|
<provides>
|
||||||
|
<binary>micro</binary>
|
||||||
|
<id>com.github.zyedidia.micro</id>
|
||||||
|
</provides>
|
||||||
|
<developer_name>Zachary Yedidia</developer_name>
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<caption>Micro Text Editor editing its source code</caption>
|
||||||
|
<image type="source">https://raw.githubusercontent.com/zyedidia/micro/master/assets/micro-solarized.png</image>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
<content_rating type="oars-1.1" />
|
||||||
|
<url type="homepage">https://micro-editor.github.io</url>
|
||||||
|
<url type="bugtracker">https://github.com/zyedidia/micro/issues</url>
|
||||||
|
<url type="faq">https://micro-editor.github.io/about.html</url>
|
||||||
|
<url type="help">https://micro-editor.github.io/about.html</url>
|
||||||
|
<url type="contact">https://github.com/zyedidia</url>
|
||||||
|
<url type="vcs-browser">https://github.com/zyedidia/micro</url>
|
||||||
|
<url type="contribute">https://github.com/zyedidia/micro#contributing</url>
|
||||||
|
</component>
|
367
data/micro.json
Normal file
367
data/micro.json
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
{
|
||||||
|
"$comment": "https://github.com/zyedidia/micro",
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "options",
|
||||||
|
"description": "A micro editor config schema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"autoindent": {
|
||||||
|
"description": "Whether to use the same indentation as a previous line\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"autosave": {
|
||||||
|
"description": "A delay between automatic saves\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"autosu": {
|
||||||
|
"description": "Whether attempt to use super user privileges\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"backup": {
|
||||||
|
"description": "Whether to backup all open buffers\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"backupdir": {
|
||||||
|
"description": "A directory to store backups\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"basename": {
|
||||||
|
"description": "Whether to show a basename instead of a full path\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"clipboard": {
|
||||||
|
"description": "A way to access the system clipboard\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"external",
|
||||||
|
"terminal",
|
||||||
|
"internal"
|
||||||
|
],
|
||||||
|
"default": "external"
|
||||||
|
},
|
||||||
|
"colorcolumn": {
|
||||||
|
"description": "A position to display a column\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"colorscheme": {
|
||||||
|
"description": "A color scheme\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"atom-dark",
|
||||||
|
"bubblegum",
|
||||||
|
"cmc-16",
|
||||||
|
"cmc-tc",
|
||||||
|
"darcula",
|
||||||
|
"default",
|
||||||
|
"dracula-tc",
|
||||||
|
"dukedark-tc",
|
||||||
|
"dukelight-tc",
|
||||||
|
"dukeubuntu-tc",
|
||||||
|
"geany",
|
||||||
|
"gotham",
|
||||||
|
"gruvbox",
|
||||||
|
"gruvbox-tc",
|
||||||
|
"material-tc",
|
||||||
|
"monokai-dark",
|
||||||
|
"monokai",
|
||||||
|
"one-dark",
|
||||||
|
"railscast",
|
||||||
|
"simple",
|
||||||
|
"solarized",
|
||||||
|
"solarized-tc",
|
||||||
|
"sunny-day",
|
||||||
|
"twilight",
|
||||||
|
"zenburn"
|
||||||
|
],
|
||||||
|
"default": "default"
|
||||||
|
},
|
||||||
|
"cursorline": {
|
||||||
|
"description": "Whether to highlight a line with a cursor with a different color\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"diffgutter": {
|
||||||
|
"description": "Whether to display diff inticators before lines\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"divchars": {
|
||||||
|
"description": "Divider chars for vertical and horizontal splits\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"default": "|-"
|
||||||
|
},
|
||||||
|
"divreverse": {
|
||||||
|
"description": "Whether to use inversed color scheme colors for splits\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"encoding": {
|
||||||
|
"description": "An encoding used to open and save files\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"default": "utf-8"
|
||||||
|
},
|
||||||
|
"eofnewline": {
|
||||||
|
"description": "Whether to add a missing trailing new line\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"fastdirty": {
|
||||||
|
"description": "Whether to use a fast algorithm to determine whether a file is changed\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"fileformat": {
|
||||||
|
"description": "A line ending format\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"unix",
|
||||||
|
"dos"
|
||||||
|
],
|
||||||
|
"default": "unix"
|
||||||
|
},
|
||||||
|
"filetype": {
|
||||||
|
"description": "A filetype for the current buffer\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"default": "unknown"
|
||||||
|
},
|
||||||
|
"hlsearch": {
|
||||||
|
"description": "Whether to highlight all instances of a searched text after a successful search\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"incsearch": {
|
||||||
|
"description": "Whether to enable an incremental search in `Find` prompt\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"ignorecase": {
|
||||||
|
"description": "Whether to perform case-insensitive searches\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"indentchar": {
|
||||||
|
"description": "An indentation character\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 1,
|
||||||
|
"default": " "
|
||||||
|
},
|
||||||
|
"infobar": {
|
||||||
|
"description": "Whether to enable a line at the bottom where messages are printed\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"keepautoindent": {
|
||||||
|
"description": "Whether add a whitespace while using autoindent\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"keymenu": {
|
||||||
|
"description": "Whether to display nano-style key menu at the bottom\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"matchbrace": {
|
||||||
|
"description": "Whether to show matching braces\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"matchbracestyle": {
|
||||||
|
"description": "Whether to underline or highlight matching braces\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"underline",
|
||||||
|
"highlight"
|
||||||
|
],
|
||||||
|
"default": "underline"
|
||||||
|
},
|
||||||
|
"mkparents": {
|
||||||
|
"description": "Whether to create missing directories\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"mouse": {
|
||||||
|
"description": "Whether to enable mouse support\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"paste": {
|
||||||
|
"description": "Whether to treat characters sent from the terminal in a single chunk as a paste event\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"parsecursor": {
|
||||||
|
"description": "Whether to extract a line number and a column to open files with from file names\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"permbackup": {
|
||||||
|
"description": "Whether to permanently save backups\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"pluginchannels": {
|
||||||
|
"description": "A file with list of plugin channels\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"default": "https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"
|
||||||
|
},
|
||||||
|
"pluginrepos": {
|
||||||
|
"description": "Plugin repositories\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "array",
|
||||||
|
"uniqueItems": true,
|
||||||
|
"items": {
|
||||||
|
"description": "A pluging repository\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"readonly": {
|
||||||
|
"description": "Whether to forbid buffer editing\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"rmtrailingws": {
|
||||||
|
"description": "Whether to remove trailing whitespaces\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"ruler": {
|
||||||
|
"description": "Whether to display line numbers\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"relativeruler": {
|
||||||
|
"description": "Whether to display relative line numbers\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"savecursor": {
|
||||||
|
"description": "Whether to save cursor position in files\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"savehistory": {
|
||||||
|
"description": "Whether to save command history between closing and re-opening editor\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"saveundo": {
|
||||||
|
"description": "Whether to save undo after closing file\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"scrollbar": {
|
||||||
|
"description": "Whether to save undo after closing file\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"scrollmargin": {
|
||||||
|
"description": "A margin at which a view starts scrolling when a cursor approaches an edge of a view\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "integer",
|
||||||
|
"default": 3
|
||||||
|
},
|
||||||
|
"scrollspeed": {
|
||||||
|
"description": "Line count to scroll for one scroll event\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "integer",
|
||||||
|
"default": 2
|
||||||
|
},
|
||||||
|
"smartpaste": {
|
||||||
|
"description": "Whether to add a leading whitespace while pasting multiple lines\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"softwrap": {
|
||||||
|
"description": "Whether to wrap long lines\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"splitbottom": {
|
||||||
|
"description": "Whether to create a new horizontal split below the current one\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"splitright": {
|
||||||
|
"description": "Whether to create a new vertical split right of the current one\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"statusformatl": {
|
||||||
|
"description": "Format string of left-justified part of the statusline\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"default": "$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)"
|
||||||
|
},
|
||||||
|
"statusformatr": {
|
||||||
|
"description": "Format string of right-justified part of the statusline\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"default": "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help"
|
||||||
|
},
|
||||||
|
"statusline": {
|
||||||
|
"description": "Whether to display a status line\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"default": "sudo"
|
||||||
|
},
|
||||||
|
"sucmd": {
|
||||||
|
"description": "A super user command\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "string",
|
||||||
|
"default": "sudo",
|
||||||
|
"examples": [
|
||||||
|
"sudo",
|
||||||
|
"doas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"syntax": {
|
||||||
|
"description": "Whether to enable a syntax highlighting\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"tabmovement": {
|
||||||
|
"description": "Whether to navigate spaces at the beginning of lines as if they are tabs\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"tabhighlight": {
|
||||||
|
"description": "Whether to invert tab character colors\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"tabreverse": {
|
||||||
|
"description": "Whether to reverse tab bar colors\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"tabsize": {
|
||||||
|
"description": "A tab size\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "integer",
|
||||||
|
"default": 4
|
||||||
|
},
|
||||||
|
"tabstospaces": {
|
||||||
|
"description": "Whether to use spaces instead of tabs\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"useprimary": {
|
||||||
|
"description": "Whether to use primary clipboard to copy selections in the background\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"wordwrap": {
|
||||||
|
"description": "Whether to wrap long lines by words\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"xterm": {
|
||||||
|
"description": "Whether to assume that the current terminal is `xterm`\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
7
go.mod
7
go.mod
|
@ -14,10 +14,9 @@ require (
|
||||||
github.com/zyedidia/clipper v0.1.1
|
github.com/zyedidia/clipper v0.1.1
|
||||||
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3
|
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3
|
||||||
github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d
|
github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d
|
||||||
github.com/zyedidia/pty v1.1.20 // indirect
|
github.com/zyedidia/tcell/v2 v2.0.10 // indirect
|
||||||
github.com/zyedidia/tcell/v2 v2.0.10-0.20221007181625-f562052bccb8 // indirect
|
github.com/zyedidia/terminal v0.0.0-20230315200948-4b3bcf6dddef
|
||||||
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415
|
golang.org/x/text v0.3.8
|
||||||
golang.org/x/text v0.3.2
|
|
||||||
gopkg.in/yaml.v2 v2.2.8
|
gopkg.in/yaml.v2 v2.2.8
|
||||||
layeh.com/gopher-luar v1.0.7
|
layeh.com/gopher-luar v1.0.7
|
||||||
)
|
)
|
||||||
|
|
49
go.sum
49
go.sum
|
@ -3,6 +3,8 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||||
|
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -36,13 +38,10 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/xo/terminfo v0.0.0-20200218205459-454e5b68f9e8 h1:woqigIZtZUZxws1zZA99nAvuz2mQrxtWsuZSR9c8I/A=
|
github.com/xo/terminfo v0.0.0-20200218205459-454e5b68f9e8 h1:woqigIZtZUZxws1zZA99nAvuz2mQrxtWsuZSR9c8I/A=
|
||||||
github.com/xo/terminfo v0.0.0-20200218205459-454e5b68f9e8/go.mod h1:6Yhx5ZJl5942QrNRWLwITArVT9okUXc5c3brgWJMoDc=
|
github.com/xo/terminfo v0.0.0-20200218205459-454e5b68f9e8/go.mod h1:6Yhx5ZJl5942QrNRWLwITArVT9okUXc5c3brgWJMoDc=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
||||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
|
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
|
||||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
||||||
github.com/zyedidia/clipper v0.0.0-20220613212750-517cd4a6c524 h1:sWYUMHs1EAlsPERKLkaLCxLM0misLylZMEc9Ip5Csjw=
|
|
||||||
github.com/zyedidia/clipper v0.0.0-20220613212750-517cd4a6c524/go.mod h1:7YApPNiiTZTXdKKZG92G50qj6mnWEX975Sdu65J7YpQ=
|
|
||||||
github.com/zyedidia/clipper v0.1.0 h1:e16nhM1RgL3HYcugcHRUpMya1K830TS5uo6LlPJHySg=
|
|
||||||
github.com/zyedidia/clipper v0.1.0/go.mod h1:7YApPNiiTZTXdKKZG92G50qj6mnWEX975Sdu65J7YpQ=
|
|
||||||
github.com/zyedidia/clipper v0.1.1 h1:HBgguFNDq/QmSQKBnhy4sMKzILINr139VEgAhftOUTw=
|
github.com/zyedidia/clipper v0.1.1 h1:HBgguFNDq/QmSQKBnhy4sMKzILINr139VEgAhftOUTw=
|
||||||
github.com/zyedidia/clipper v0.1.1/go.mod h1:7YApPNiiTZTXdKKZG92G50qj6mnWEX975Sdu65J7YpQ=
|
github.com/zyedidia/clipper v0.1.1/go.mod h1:7YApPNiiTZTXdKKZG92G50qj6mnWEX975Sdu65J7YpQ=
|
||||||
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 h1:oMHjjTLfGXVuyOQBYj5/td9WC0mw4g1xDBPovIqmHew=
|
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 h1:oMHjjTLfGXVuyOQBYj5/td9WC0mw4g1xDBPovIqmHew=
|
||||||
|
@ -55,26 +54,46 @@ github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d h1:zmDMkh22zXOB7gz8
|
||||||
github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d/go.mod h1:NDJSTTYWivnza6zkRapeX2/LwhKPEMQ7bJxqgDVT78I=
|
github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d/go.mod h1:NDJSTTYWivnza6zkRapeX2/LwhKPEMQ7bJxqgDVT78I=
|
||||||
github.com/zyedidia/poller v1.0.1 h1:Tt9S3AxAjXwWGNiC2TUdRJkQDZSzCBNVQ4xXiQ7440s=
|
github.com/zyedidia/poller v1.0.1 h1:Tt9S3AxAjXwWGNiC2TUdRJkQDZSzCBNVQ4xXiQ7440s=
|
||||||
github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE=
|
github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE=
|
||||||
github.com/zyedidia/pty v1.1.15 h1:XlxMFph7HDvTn4sDG8Klgmb/g4ATGiSj4655vAETp1U=
|
|
||||||
github.com/zyedidia/pty v1.1.15/go.mod h1:HWbpfrLoVM9FmU+/9NV+PzVQV8jSxgnQLk8fvx0q/i8=
|
|
||||||
github.com/zyedidia/pty v1.1.19 h1:GouvvD/u+uml5EPFUAt5N3rFQKPBmZuuUXHvzAJhVA0=
|
|
||||||
github.com/zyedidia/pty v1.1.19/go.mod h1:HWbpfrLoVM9FmU+/9NV+PzVQV8jSxgnQLk8fvx0q/i8=
|
|
||||||
github.com/zyedidia/pty v1.1.20 h1:mkZ5/UiEjZVMFzoXp8oyJAlbn3b380m5lvFrbx/NL/g=
|
|
||||||
github.com/zyedidia/pty v1.1.20/go.mod h1:HWbpfrLoVM9FmU+/9NV+PzVQV8jSxgnQLk8fvx0q/i8=
|
|
||||||
github.com/zyedidia/tcell/v2 v2.0.9 h1:FxXRkE62N0GPHES7EMLtp2rteYqC9r1kVid8vJN1kOE=
|
github.com/zyedidia/tcell/v2 v2.0.9 h1:FxXRkE62N0GPHES7EMLtp2rteYqC9r1kVid8vJN1kOE=
|
||||||
github.com/zyedidia/tcell/v2 v2.0.9/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws=
|
github.com/zyedidia/tcell/v2 v2.0.9/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws=
|
||||||
github.com/zyedidia/tcell/v2 v2.0.10-0.20221007181625-f562052bccb8 h1:53ULv4mmLyQDnqbjVxanckP57WSreWHwTmlLJrJEutY=
|
github.com/zyedidia/tcell/v2 v2.0.10-0.20221007181625-f562052bccb8 h1:53ULv4mmLyQDnqbjVxanckP57WSreWHwTmlLJrJEutY=
|
||||||
github.com/zyedidia/tcell/v2 v2.0.10-0.20221007181625-f562052bccb8/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws=
|
github.com/zyedidia/tcell/v2 v2.0.10-0.20221007181625-f562052bccb8/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws=
|
||||||
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415 h1:752dTQ5OatJ9M5ULK2+9lor+nzyZz+LYDo3WGngg3Rc=
|
github.com/zyedidia/tcell/v2 v2.0.10-0.20230320201625-54f6acdada4a h1:W4TWa++Wk6uRGxZoxr2nPX1TpIEl+Wxv0mTtocG4TYc=
|
||||||
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415/go.mod h1:8leT8G0Cm8NoJHdrrKHyR9MirWoF4YW7pZh06B6H+1E=
|
github.com/zyedidia/tcell/v2 v2.0.10-0.20230320201625-54f6acdada4a/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws=
|
||||||
|
github.com/zyedidia/tcell/v2 v2.0.10-0.20230831153116-061c5b2c7260 h1:SCAmAacT5BxZsmOFdFy5zwwi6nj1MjA60gydjKdTgXo=
|
||||||
|
github.com/zyedidia/tcell/v2 v2.0.10-0.20230831153116-061c5b2c7260/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws=
|
||||||
|
github.com/zyedidia/tcell/v2 v2.0.10 h1:6fbbYAx/DYc9A//4jU1OeBrxtc9qJxYCZXCtGQbtTWU=
|
||||||
|
github.com/zyedidia/tcell/v2 v2.0.10/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws=
|
||||||
|
github.com/zyedidia/terminal v0.0.0-20230315200948-4b3bcf6dddef h1:LeB4Qs0Tss4r/Qh8pfsTTqagDYHysfKJLYzAH3MVfu0=
|
||||||
|
github.com/zyedidia/terminal v0.0.0-20230315200948-4b3bcf6dddef/go.mod h1:zeb8MJdcCObFKVvur3n2B4BANIPuo2Q8r4iiNs9Enx0=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
@ -51,9 +51,13 @@ func (h *BufPane) ScrollAdjust() {
|
||||||
func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
|
func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
|
||||||
b := h.Buf
|
b := h.Buf
|
||||||
mx, my := e.Position()
|
mx, my := e.Position()
|
||||||
|
// ignore click on the status line
|
||||||
|
if my >= h.BufView().Y+h.BufView().Height {
|
||||||
|
return false
|
||||||
|
}
|
||||||
mouseLoc := h.LocFromVisual(buffer.Loc{mx, my})
|
mouseLoc := h.LocFromVisual(buffer.Loc{mx, my})
|
||||||
h.Cursor.Loc = mouseLoc
|
h.Cursor.Loc = mouseLoc
|
||||||
if h.mouseReleased {
|
|
||||||
if b.NumCursors() > 1 {
|
if b.NumCursors() > 1 {
|
||||||
b.ClearCursors()
|
b.ClearCursors()
|
||||||
h.Relocate()
|
h.Relocate()
|
||||||
|
@ -89,23 +93,52 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
|
||||||
h.Cursor.CurSelection[0] = h.Cursor.Loc
|
h.Cursor.CurSelection[0] = h.Cursor.Loc
|
||||||
h.Cursor.CurSelection[1] = h.Cursor.Loc
|
h.Cursor.CurSelection[1] = h.Cursor.Loc
|
||||||
}
|
}
|
||||||
h.mouseReleased = false
|
|
||||||
} else if !h.mouseReleased {
|
h.Cursor.StoreVisualX()
|
||||||
|
h.lastLoc = mouseLoc
|
||||||
|
h.Relocate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *BufPane) MouseDrag(e *tcell.EventMouse) bool {
|
||||||
|
mx, my := e.Position()
|
||||||
|
// ignore drag on the status line
|
||||||
|
if my >= h.BufView().Y+h.BufView().Height {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
h.Cursor.Loc = h.LocFromVisual(buffer.Loc{mx, my})
|
||||||
|
|
||||||
if h.tripleClick {
|
if h.tripleClick {
|
||||||
h.Cursor.AddLineToSelection()
|
h.Cursor.AddLineToSelection()
|
||||||
} else if h.doubleClick {
|
} else if h.doubleClick {
|
||||||
h.Cursor.AddWordToSelection()
|
h.Cursor.AddWordToSelection()
|
||||||
} else {
|
} else {
|
||||||
h.Cursor.SetSelectionEnd(h.Cursor.Loc)
|
h.Cursor.SelectTo(h.Cursor.Loc)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Cursor.StoreVisualX()
|
h.Cursor.StoreVisualX()
|
||||||
h.lastLoc = mouseLoc
|
|
||||||
h.Relocate()
|
h.Relocate()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *BufPane) MouseRelease(e *tcell.EventMouse) bool {
|
||||||
|
// We could finish the selection based on the release location as in the
|
||||||
|
// commented out code below, to allow text selections even in a terminal
|
||||||
|
// that doesn't support mouse motion events. But when the mouse click is
|
||||||
|
// within the scroll margin, that would cause a scroll and selection
|
||||||
|
// even for a simple mouse click, which is not good.
|
||||||
|
// if !h.doubleClick && !h.tripleClick {
|
||||||
|
// mx, my := e.Position()
|
||||||
|
// h.Cursor.Loc = h.LocFromVisual(buffer.Loc{mx, my})
|
||||||
|
// h.Cursor.SetSelectionEnd(h.Cursor.Loc)
|
||||||
|
// }
|
||||||
|
|
||||||
|
if h.Cursor.HasSelection() {
|
||||||
|
h.Cursor.CopySelection(clipboard.PrimaryReg)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ScrollUpAction scrolls the view up
|
// ScrollUpAction scrolls the view up
|
||||||
func (h *BufPane) ScrollUpAction() bool {
|
func (h *BufPane) ScrollUpAction() bool {
|
||||||
h.ScrollUp(util.IntOpt(h.Buf.Settings["scrollspeed"]))
|
h.ScrollUp(util.IntOpt(h.Buf.Settings["scrollspeed"]))
|
||||||
|
@ -176,7 +209,7 @@ func (h *BufPane) CursorUp() bool {
|
||||||
|
|
||||||
// CursorDown moves the cursor down
|
// CursorDown moves the cursor down
|
||||||
func (h *BufPane) CursorDown() bool {
|
func (h *BufPane) CursorDown() bool {
|
||||||
h.Cursor.Deselect(true)
|
h.Cursor.Deselect(false)
|
||||||
h.MoveCursorDown(1)
|
h.MoveCursorDown(1)
|
||||||
h.Relocate()
|
h.Relocate()
|
||||||
return true
|
return true
|
||||||
|
@ -211,7 +244,7 @@ func (h *BufPane) CursorLeft() bool {
|
||||||
func (h *BufPane) CursorRight() bool {
|
func (h *BufPane) CursorRight() bool {
|
||||||
if h.Cursor.HasSelection() {
|
if h.Cursor.HasSelection() {
|
||||||
h.Cursor.Deselect(false)
|
h.Cursor.Deselect(false)
|
||||||
h.Cursor.Loc = h.Cursor.Loc.Move(1, h.Buf)
|
h.Cursor.Right()
|
||||||
} else {
|
} else {
|
||||||
tabstospaces := h.Buf.Settings["tabstospaces"].(bool)
|
tabstospaces := h.Buf.Settings["tabstospaces"].(bool)
|
||||||
tabmovement := h.Buf.Settings["tabmovement"].(bool)
|
tabmovement := h.Buf.Settings["tabmovement"].(bool)
|
||||||
|
@ -250,6 +283,22 @@ func (h *BufPane) WordLeft() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubWordRight moves the cursor one sub-word to the right
|
||||||
|
func (h *BufPane) SubWordRight() bool {
|
||||||
|
h.Cursor.Deselect(false)
|
||||||
|
h.Cursor.SubWordRight()
|
||||||
|
h.Relocate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubWordLeft moves the cursor one sub-word to the left
|
||||||
|
func (h *BufPane) SubWordLeft() bool {
|
||||||
|
h.Cursor.Deselect(true)
|
||||||
|
h.Cursor.SubWordLeft()
|
||||||
|
h.Relocate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// SelectUp selects up one line
|
// SelectUp selects up one line
|
||||||
func (h *BufPane) SelectUp() bool {
|
func (h *BufPane) SelectUp() bool {
|
||||||
if !h.Cursor.HasSelection() {
|
if !h.Cursor.HasSelection() {
|
||||||
|
@ -326,6 +375,28 @@ func (h *BufPane) SelectWordLeft() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectSubWordRight selects the sub-word to the right of the cursor
|
||||||
|
func (h *BufPane) SelectSubWordRight() bool {
|
||||||
|
if !h.Cursor.HasSelection() {
|
||||||
|
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||||
|
}
|
||||||
|
h.Cursor.SubWordRight()
|
||||||
|
h.Cursor.SelectTo(h.Cursor.Loc)
|
||||||
|
h.Relocate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectSubWordLeft selects the sub-word to the left of the cursor
|
||||||
|
func (h *BufPane) SelectSubWordLeft() bool {
|
||||||
|
if !h.Cursor.HasSelection() {
|
||||||
|
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||||
|
}
|
||||||
|
h.Cursor.SubWordLeft()
|
||||||
|
h.Cursor.SelectTo(h.Cursor.Loc)
|
||||||
|
h.Relocate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// StartOfText moves the cursor to the start of the text of the line
|
// StartOfText moves the cursor to the start of the text of the line
|
||||||
func (h *BufPane) StartOfText() bool {
|
func (h *BufPane) StartOfText() bool {
|
||||||
h.Cursor.Deselect(true)
|
h.Cursor.Deselect(true)
|
||||||
|
@ -589,6 +660,28 @@ func (h *BufPane) DeleteWordLeft() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteSubWordRight deletes the sub-word to the right of the cursor
|
||||||
|
func (h *BufPane) DeleteSubWordRight() bool {
|
||||||
|
h.SelectSubWordRight()
|
||||||
|
if h.Cursor.HasSelection() {
|
||||||
|
h.Cursor.DeleteSelection()
|
||||||
|
h.Cursor.ResetSelection()
|
||||||
|
}
|
||||||
|
h.Relocate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSubWordLeft deletes the sub-word to the left of the cursor
|
||||||
|
func (h *BufPane) DeleteSubWordLeft() bool {
|
||||||
|
h.SelectSubWordLeft()
|
||||||
|
if h.Cursor.HasSelection() {
|
||||||
|
h.Cursor.DeleteSelection()
|
||||||
|
h.Cursor.ResetSelection()
|
||||||
|
}
|
||||||
|
h.Relocate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Delete deletes the next character
|
// Delete deletes the next character
|
||||||
func (h *BufPane) Delete() bool {
|
func (h *BufPane) Delete() bool {
|
||||||
if h.Cursor.HasSelection() {
|
if h.Cursor.HasSelection() {
|
||||||
|
@ -712,8 +805,8 @@ func (h *BufPane) Autocomplete() bool {
|
||||||
}
|
}
|
||||||
r := h.Cursor.RuneUnder(h.Cursor.X)
|
r := h.Cursor.RuneUnder(h.Cursor.X)
|
||||||
prev := h.Cursor.RuneUnder(h.Cursor.X - 1)
|
prev := h.Cursor.RuneUnder(h.Cursor.X - 1)
|
||||||
if !util.IsAutocomplete(prev) || !util.IsNonAlphaNumeric(r) {
|
if !util.IsAutocomplete(prev) || util.IsWordChar(r) {
|
||||||
// don't autocomplete if cursor is on alpha numeric character (middle of a word)
|
// don't autocomplete if cursor is within a word
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -793,16 +886,16 @@ func (h *BufPane) SaveAsCB(action string, callback func()) bool {
|
||||||
filename := strings.Join(args, " ")
|
filename := strings.Join(args, " ")
|
||||||
fileinfo, err := os.Stat(filename)
|
fileinfo, err := os.Stat(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission) {
|
||||||
noPrompt := h.saveBufToFile(filename, action, callback)
|
noPrompt := h.saveBufToFile(filename, action, callback)
|
||||||
if noPrompt {
|
if noPrompt {
|
||||||
h.completeAction(action)
|
h.completeAction(action)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
InfoBar.YNPrompt(
|
InfoBar.YNPrompt(
|
||||||
fmt.Sprintf("the file %s already exists in the directory, would you like to overwrite? Y/n", fileinfo.Name()),
|
fmt.Sprintf("The file %s already exists in the directory, would you like to overwrite? Y/n", fileinfo.Name()),
|
||||||
func(yes, canceled bool) {
|
func(yes, canceled bool) {
|
||||||
if yes && !canceled {
|
if yes && !canceled {
|
||||||
noPrompt := h.saveBufToFile(filename, action, callback)
|
noPrompt := h.saveBufToFile(filename, action, callback)
|
||||||
|
@ -813,6 +906,7 @@ func (h *BufPane) SaveAsCB(action string, callback func()) bool {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -953,6 +1047,9 @@ func (h *BufPane) find(useRegex bool) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pattern := string(h.Cursor.GetSelection())
|
pattern := string(h.Cursor.GetSelection())
|
||||||
|
if useRegex && pattern != "" {
|
||||||
|
pattern = regexp.QuoteMeta(pattern)
|
||||||
|
}
|
||||||
if eventCallback != nil && pattern != "" {
|
if eventCallback != nil && pattern != "" {
|
||||||
eventCallback(pattern)
|
eventCallback(pattern)
|
||||||
}
|
}
|
||||||
|
@ -1027,6 +1124,28 @@ func (h *BufPane) FindPrevious() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DiffNext searches forward until the beginning of the next block of diffs
|
||||||
|
func (h *BufPane) DiffNext() bool {
|
||||||
|
cur := h.Cursor.Loc.Y
|
||||||
|
dl, err := h.Buf.FindNextDiffLine(cur, true)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
h.GotoLoc(buffer.Loc{0, dl})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiffPrevious searches forward until the end of the previous block of diffs
|
||||||
|
func (h *BufPane) DiffPrevious() bool {
|
||||||
|
cur := h.Cursor.Loc.Y
|
||||||
|
dl, err := h.Buf.FindNextDiffLine(cur, false)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
h.GotoLoc(buffer.Loc{0, dl})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Undo undoes the last action
|
// Undo undoes the last action
|
||||||
func (h *BufPane) Undo() bool {
|
func (h *BufPane) Undo() bool {
|
||||||
h.Buf.Undo()
|
h.Buf.Undo()
|
||||||
|
@ -1251,9 +1370,13 @@ func (h *BufPane) PastePrimary() bool {
|
||||||
|
|
||||||
func (h *BufPane) paste(clip string) {
|
func (h *BufPane) paste(clip string) {
|
||||||
if h.Buf.Settings["smartpaste"].(bool) {
|
if h.Buf.Settings["smartpaste"].(bool) {
|
||||||
if h.Cursor.X > 0 && len(util.GetLeadingWhitespace([]byte(strings.TrimLeft(clip, "\r\n")))) == 0 {
|
if h.Cursor.X > 0 {
|
||||||
leadingWS := util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y))
|
leadingPasteWS := string(util.GetLeadingWhitespace([]byte(clip)))
|
||||||
clip = strings.ReplaceAll(clip, "\n", "\n"+string(leadingWS))
|
if leadingPasteWS != " " && strings.Contains(clip, "\n"+leadingPasteWS) {
|
||||||
|
leadingWS := string(util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y)))
|
||||||
|
clip = strings.TrimPrefix(clip, leadingPasteWS)
|
||||||
|
clip = strings.ReplaceAll(clip, "\n"+leadingPasteWS, "\n"+leadingWS)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1271,11 +1394,7 @@ func (h *BufPane) paste(clip string) {
|
||||||
// JumpToMatchingBrace moves the cursor to the matching brace if it is
|
// JumpToMatchingBrace moves the cursor to the matching brace if it is
|
||||||
// currently on a brace
|
// currently on a brace
|
||||||
func (h *BufPane) JumpToMatchingBrace() bool {
|
func (h *BufPane) JumpToMatchingBrace() bool {
|
||||||
for _, bp := range buffer.BracePairs {
|
matchingBrace, left, found := h.Buf.FindMatchingBrace(h.Cursor.Loc)
|
||||||
r := h.Cursor.RuneUnder(h.Cursor.X)
|
|
||||||
rl := h.Cursor.RuneUnder(h.Cursor.X - 1)
|
|
||||||
if r == bp[0] || r == bp[1] || rl == bp[0] || rl == bp[1] {
|
|
||||||
matchingBrace, left, found := h.Buf.FindMatchingBrace(bp, h.Cursor.Loc)
|
|
||||||
if found {
|
if found {
|
||||||
if left {
|
if left {
|
||||||
h.Cursor.GotoLoc(matchingBrace)
|
h.Cursor.GotoLoc(matchingBrace)
|
||||||
|
@ -1285,8 +1404,6 @@ func (h *BufPane) JumpToMatchingBrace() bool {
|
||||||
h.Relocate()
|
h.Relocate()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1311,7 +1428,7 @@ func (h *BufPane) OpenFile() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenFile opens a new file in the buffer
|
// JumpLine asks the user to enter a line number to jump to
|
||||||
func (h *BufPane) JumpLine() bool {
|
func (h *BufPane) JumpLine() bool {
|
||||||
InfoBar.Prompt("> ", "goto ", "Command", nil, func(resp string, canceled bool) {
|
InfoBar.Prompt("> ", "goto ", "Command", nil, func(resp string, canceled bool) {
|
||||||
if !canceled {
|
if !canceled {
|
||||||
|
@ -1417,9 +1534,7 @@ func (h *BufPane) HalfPageDown() bool {
|
||||||
func (h *BufPane) ToggleDiffGutter() bool {
|
func (h *BufPane) ToggleDiffGutter() bool {
|
||||||
if !h.Buf.Settings["diffgutter"].(bool) {
|
if !h.Buf.Settings["diffgutter"].(bool) {
|
||||||
h.Buf.Settings["diffgutter"] = true
|
h.Buf.Settings["diffgutter"] = true
|
||||||
h.Buf.UpdateDiff(func(synchronous bool) {
|
h.Buf.UpdateDiff()
|
||||||
screen.Redraw()
|
|
||||||
})
|
|
||||||
InfoBar.Message("Enabled diff gutter")
|
InfoBar.Message("Enabled diff gutter")
|
||||||
} else {
|
} else {
|
||||||
h.Buf.Settings["diffgutter"] = false
|
h.Buf.Settings["diffgutter"] = false
|
||||||
|
@ -1560,9 +1675,7 @@ func (h *BufPane) QuitAll() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
quit := func() {
|
quit := func() {
|
||||||
for _, b := range buffer.OpenBuffers {
|
buffer.CloseOpenBuffers()
|
||||||
b.Close()
|
|
||||||
}
|
|
||||||
screen.Screen.Fini()
|
screen.Screen.Fini()
|
||||||
InfoBar.Close()
|
InfoBar.Close()
|
||||||
runtime.Goexit()
|
runtime.Goexit()
|
||||||
|
@ -1691,7 +1804,7 @@ func (h *BufPane) PlayMacro() bool {
|
||||||
switch t := action.(type) {
|
switch t := action.(type) {
|
||||||
case rune:
|
case rune:
|
||||||
h.DoRuneInsert(t)
|
h.DoRuneInsert(t)
|
||||||
case func(*BufPane) bool:
|
case BufKeyAction:
|
||||||
t(h)
|
t(h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1740,15 +1853,39 @@ func (h *BufPane) SpawnMultiCursor() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpawnMultiCursorUp creates additional cursor, at the same X (if possible), one Y less.
|
// SpawnMultiCursorUpN is not an action
|
||||||
func (h *BufPane) SpawnMultiCursorUp() bool {
|
func (h *BufPane) SpawnMultiCursorUpN(n int) bool {
|
||||||
if h.Cursor.Y == 0 {
|
lastC := h.Buf.GetCursor(h.Buf.NumCursors() - 1)
|
||||||
|
var c *buffer.Cursor
|
||||||
|
if !h.Buf.Settings["softwrap"].(bool) {
|
||||||
|
if n > 0 && lastC.Y == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n < 0 && lastC.Y+1 == h.Buf.LinesNum() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
h.Cursor.GotoLoc(buffer.Loc{h.Cursor.X, h.Cursor.Y - 1})
|
|
||||||
h.Cursor.Relocate()
|
|
||||||
|
|
||||||
c := buffer.NewCursor(h.Buf, buffer.Loc{h.Cursor.X, h.Cursor.Y + 1})
|
h.Buf.DeselectCursors()
|
||||||
|
|
||||||
|
c = buffer.NewCursor(h.Buf, buffer.Loc{lastC.X, lastC.Y - n})
|
||||||
|
c.LastVisualX = lastC.LastVisualX
|
||||||
|
c.X = c.GetCharPosInLine(h.Buf.LineBytes(c.Y), c.LastVisualX)
|
||||||
|
c.Relocate()
|
||||||
|
} else {
|
||||||
|
vloc := h.VLocFromLoc(lastC.Loc)
|
||||||
|
sloc := h.Scroll(vloc.SLoc, -n)
|
||||||
|
if sloc == vloc.SLoc {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Buf.DeselectCursors()
|
||||||
|
|
||||||
|
vloc.SLoc = sloc
|
||||||
|
vloc.VisualX = lastC.LastVisualX
|
||||||
|
c = buffer.NewCursor(h.Buf, h.LocFromVLoc(vloc))
|
||||||
|
c.LastVisualX = lastC.LastVisualX
|
||||||
|
}
|
||||||
|
|
||||||
h.Buf.AddCursor(c)
|
h.Buf.AddCursor(c)
|
||||||
h.Buf.SetCurCursor(h.Buf.NumCursors() - 1)
|
h.Buf.SetCurCursor(h.Buf.NumCursors() - 1)
|
||||||
h.Buf.MergeCursors()
|
h.Buf.MergeCursors()
|
||||||
|
@ -1757,20 +1894,14 @@ func (h *BufPane) SpawnMultiCursorUp() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpawnMultiCursorUp creates additional cursor, at the same X (if possible), one Y less.
|
||||||
|
func (h *BufPane) SpawnMultiCursorUp() bool {
|
||||||
|
return h.SpawnMultiCursorUpN(1)
|
||||||
|
}
|
||||||
|
|
||||||
// SpawnMultiCursorDown creates additional cursor, at the same X (if possible), one Y more.
|
// SpawnMultiCursorDown creates additional cursor, at the same X (if possible), one Y more.
|
||||||
func (h *BufPane) SpawnMultiCursorDown() bool {
|
func (h *BufPane) SpawnMultiCursorDown() bool {
|
||||||
if h.Cursor.Y+1 == h.Buf.LinesNum() {
|
return h.SpawnMultiCursorUpN(-1)
|
||||||
return false
|
|
||||||
}
|
|
||||||
h.Cursor.GotoLoc(buffer.Loc{h.Cursor.X, h.Cursor.Y + 1})
|
|
||||||
h.Cursor.Relocate()
|
|
||||||
|
|
||||||
c := buffer.NewCursor(h.Buf, buffer.Loc{h.Cursor.X, h.Cursor.Y - 1})
|
|
||||||
h.Buf.AddCursor(c)
|
|
||||||
h.Buf.SetCurCursor(h.Buf.NumCursors() - 1)
|
|
||||||
h.Buf.MergeCursors()
|
|
||||||
h.Relocate()
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpawnMultiCursorSelect adds a cursor at the beginning of each line of a selection
|
// SpawnMultiCursorSelect adds a cursor at the beginning of each line of a selection
|
||||||
|
@ -1807,11 +1938,27 @@ func (h *BufPane) SpawnMultiCursorSelect() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MouseMultiCursor is a mouse action which puts a new cursor at the mouse position
|
// MouseMultiCursor is a mouse action which puts a new cursor at the mouse position,
|
||||||
|
// or removes a cursor if it is already there
|
||||||
func (h *BufPane) MouseMultiCursor(e *tcell.EventMouse) bool {
|
func (h *BufPane) MouseMultiCursor(e *tcell.EventMouse) bool {
|
||||||
b := h.Buf
|
b := h.Buf
|
||||||
mx, my := e.Position()
|
mx, my := e.Position()
|
||||||
|
// ignore click on the status line
|
||||||
|
if my >= h.BufView().Y+h.BufView().Height {
|
||||||
|
return false
|
||||||
|
}
|
||||||
mouseLoc := h.LocFromVisual(buffer.Loc{X: mx, Y: my})
|
mouseLoc := h.LocFromVisual(buffer.Loc{X: mx, Y: my})
|
||||||
|
|
||||||
|
if h.Buf.NumCursors() > 1 {
|
||||||
|
cursors := h.Buf.GetCursors()
|
||||||
|
for _, c := range cursors {
|
||||||
|
if c.Loc == mouseLoc {
|
||||||
|
h.Buf.RemoveCursor(c.Num)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
c := buffer.NewCursor(b, mouseLoc)
|
c := buffer.NewCursor(b, mouseLoc)
|
||||||
b.AddCursor(c)
|
b.AddCursor(c)
|
||||||
b.MergeCursors()
|
b.MergeCursors()
|
||||||
|
|
|
@ -88,6 +88,10 @@ func BindKey(k, v string, bind func(e Event, a string)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(k, "\x1b") {
|
||||||
|
screen.Screen.RegisterRawSeq(k)
|
||||||
|
}
|
||||||
|
|
||||||
bind(event, v)
|
bind(event, v)
|
||||||
|
|
||||||
// switch e := event.(type) {
|
// switch e := event.(type) {
|
||||||
|
@ -153,7 +157,6 @@ modSearch:
|
||||||
k = k[5:]
|
k = k[5:]
|
||||||
modifiers |= tcell.ModShift
|
modifiers |= tcell.ModShift
|
||||||
case strings.HasPrefix(k, "\x1b"):
|
case strings.HasPrefix(k, "\x1b"):
|
||||||
screen.Screen.RegisterRawSeq(k)
|
|
||||||
return RawEvent{
|
return RawEvent{
|
||||||
esc: k,
|
esc: k,
|
||||||
}, true
|
}, true
|
||||||
|
@ -201,11 +204,20 @@ modSearch:
|
||||||
}, true
|
}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mstate MouseState = MousePress
|
||||||
|
if strings.HasSuffix(k, "Drag") {
|
||||||
|
k = k[:len(k)-4]
|
||||||
|
mstate = MouseDrag
|
||||||
|
} else if strings.HasSuffix(k, "Release") {
|
||||||
|
k = k[:len(k)-7]
|
||||||
|
mstate = MouseRelease
|
||||||
|
}
|
||||||
// See if we can find the key in bindingMouse
|
// See if we can find the key in bindingMouse
|
||||||
if code, ok := mouseEvents[k]; ok {
|
if code, ok := mouseEvents[k]; ok {
|
||||||
return MouseEvent{
|
return MouseEvent{
|
||||||
btn: code,
|
btn: code,
|
||||||
mod: modifiers,
|
mod: modifiers,
|
||||||
|
state: mstate,
|
||||||
}, true
|
}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +251,24 @@ func findEvent(k string) (Event, error) {
|
||||||
return event, nil
|
return event, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func eventsEqual(e1 Event, e2 Event) bool {
|
||||||
|
seq1, ok1 := e1.(KeySequenceEvent)
|
||||||
|
seq2, ok2 := e2.(KeySequenceEvent)
|
||||||
|
if ok1 && ok2 {
|
||||||
|
if len(seq1.keys) != len(seq2.keys) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < len(seq1.keys); i++ {
|
||||||
|
if seq1.keys[i] != seq2.keys[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return e1 == e2
|
||||||
|
}
|
||||||
|
|
||||||
// TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
|
// TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
|
||||||
// Returns true if the keybinding already existed and a possible error
|
// Returns true if the keybinding already existed and a possible error
|
||||||
func TryBindKey(k, v string, overwrite bool) (bool, error) {
|
func TryBindKey(k, v string, overwrite bool) (bool, error) {
|
||||||
|
@ -264,21 +294,23 @@ func TryBindKey(k, v string, overwrite bool) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for ev := range parsed {
|
var ev string
|
||||||
|
for ev = range parsed {
|
||||||
if e, err := findEvent(ev); err == nil {
|
if e, err := findEvent(ev); err == nil {
|
||||||
if e == key {
|
if eventsEqual(e, key) {
|
||||||
if overwrite {
|
|
||||||
parsed[ev] = v
|
|
||||||
}
|
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if found && !overwrite {
|
if found {
|
||||||
|
if overwrite {
|
||||||
|
parsed[ev] = v
|
||||||
|
} else {
|
||||||
return true, nil
|
return true, nil
|
||||||
} else if !found {
|
}
|
||||||
|
} else {
|
||||||
parsed[k] = v
|
parsed[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,13 +347,17 @@ func UnbindKey(k string) error {
|
||||||
|
|
||||||
for ev := range parsed {
|
for ev := range parsed {
|
||||||
if e, err := findEvent(ev); err == nil {
|
if e, err := findEvent(ev); err == nil {
|
||||||
if e == key {
|
if eventsEqual(e, key) {
|
||||||
delete(parsed, ev)
|
delete(parsed, ev)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(k, "\x1b") {
|
||||||
|
screen.Screen.UnregisterRawSeq(k)
|
||||||
|
}
|
||||||
|
|
||||||
defaults := DefaultBindings("buffer")
|
defaults := DefaultBindings("buffer")
|
||||||
if a, ok := defaults[k]; ok {
|
if a, ok := defaults[k]; ok {
|
||||||
BindKey(k, a, Binder["buffer"])
|
BindKey(k, a, Binder["buffer"])
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
lua "github.com/yuin/gopher-lua"
|
lua "github.com/yuin/gopher-lua"
|
||||||
"github.com/zyedidia/micro/v2/internal/buffer"
|
"github.com/zyedidia/micro/v2/internal/buffer"
|
||||||
"github.com/zyedidia/micro/v2/internal/clipboard"
|
|
||||||
"github.com/zyedidia/micro/v2/internal/config"
|
"github.com/zyedidia/micro/v2/internal/config"
|
||||||
"github.com/zyedidia/micro/v2/internal/display"
|
"github.com/zyedidia/micro/v2/internal/display"
|
||||||
ulua "github.com/zyedidia/micro/v2/internal/lua"
|
ulua "github.com/zyedidia/micro/v2/internal/lua"
|
||||||
|
@ -17,6 +16,8 @@ import (
|
||||||
"github.com/zyedidia/tcell/v2"
|
"github.com/zyedidia/tcell/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type BufAction interface{}
|
||||||
|
|
||||||
// BufKeyAction represents an action bound to a key.
|
// BufKeyAction represents an action bound to a key.
|
||||||
type BufKeyAction func(*BufPane) bool
|
type BufKeyAction func(*BufPane) bool
|
||||||
|
|
||||||
|
@ -44,8 +45,9 @@ func init() {
|
||||||
BufBindings = NewKeyTree()
|
BufBindings = NewKeyTree()
|
||||||
}
|
}
|
||||||
|
|
||||||
// LuaAction makes a BufKeyAction from a lua function.
|
// LuaAction makes an action from a lua function. It returns either a BufKeyAction
|
||||||
func LuaAction(fn string) func(*BufPane) bool {
|
// or a BufMouseAction depending on the event type.
|
||||||
|
func LuaAction(fn string, k Event) BufAction {
|
||||||
luaFn := strings.Split(fn, ".")
|
luaFn := strings.Split(fn, ".")
|
||||||
if len(luaFn) <= 1 {
|
if len(luaFn) <= 1 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -55,7 +57,11 @@ func LuaAction(fn string) func(*BufPane) bool {
|
||||||
if pl == nil {
|
if pl == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return func(h *BufPane) bool {
|
|
||||||
|
var action BufAction
|
||||||
|
switch k.(type) {
|
||||||
|
case KeyEvent, KeySequenceEvent, RawEvent:
|
||||||
|
action = BufKeyAction(func(h *BufPane) bool {
|
||||||
val, err := pl.Call(plFn, luar.New(ulua.L, h))
|
val, err := pl.Call(plFn, luar.New(ulua.L, h))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage(err)
|
screen.TermMessage(err)
|
||||||
|
@ -65,23 +71,28 @@ func LuaAction(fn string) func(*BufPane) bool {
|
||||||
} else {
|
} else {
|
||||||
return bool(v)
|
return bool(v)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
case MouseEvent:
|
||||||
|
action = BufMouseAction(func(h *BufPane, te *tcell.EventMouse) bool {
|
||||||
|
val, err := pl.Call(plFn, luar.New(ulua.L, h), luar.New(ulua.L, te))
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage(err)
|
||||||
}
|
}
|
||||||
|
if v, ok := val.(lua.LBool); !ok {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return bool(v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return action
|
||||||
}
|
}
|
||||||
|
|
||||||
// BufMapKey maps an event to an action
|
// BufMapEvent maps an event to an action
|
||||||
func BufMapEvent(k Event, action string) {
|
func BufMapEvent(k Event, action string) {
|
||||||
config.Bindings["buffer"][k.Name()] = action
|
config.Bindings["buffer"][k.Name()] = action
|
||||||
|
|
||||||
switch e := k.(type) {
|
var actionfns []BufAction
|
||||||
case KeyEvent, KeySequenceEvent, RawEvent:
|
|
||||||
bufMapKey(e, action)
|
|
||||||
case MouseEvent:
|
|
||||||
bufMapMouse(e, action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func bufMapKey(k Event, action string) {
|
|
||||||
var actionfns []func(*BufPane) bool
|
|
||||||
var names []string
|
var names []string
|
||||||
var types []byte
|
var types []byte
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
|
@ -102,7 +113,7 @@ func bufMapKey(k Event, action string) {
|
||||||
action = ""
|
action = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var afn func(*BufPane) bool
|
var afn BufAction
|
||||||
if strings.HasPrefix(a, "command:") {
|
if strings.HasPrefix(a, "command:") {
|
||||||
a = strings.SplitN(a, ":", 2)[1]
|
a = strings.SplitN(a, ":", 2)[1]
|
||||||
afn = CommandAction(a)
|
afn = CommandAction(a)
|
||||||
|
@ -113,7 +124,7 @@ func bufMapKey(k Event, action string) {
|
||||||
names = append(names, "")
|
names = append(names, "")
|
||||||
} else if strings.HasPrefix(a, "lua:") {
|
} else if strings.HasPrefix(a, "lua:") {
|
||||||
a = strings.SplitN(a, ":", 2)[1]
|
a = strings.SplitN(a, ":", 2)[1]
|
||||||
afn = LuaAction(a)
|
afn = LuaAction(a, k)
|
||||||
if afn == nil {
|
if afn == nil {
|
||||||
screen.TermMessage("Lua Error:", a, "does not exist")
|
screen.TermMessage("Lua Error:", a, "does not exist")
|
||||||
continue
|
continue
|
||||||
|
@ -129,13 +140,16 @@ func bufMapKey(k Event, action string) {
|
||||||
} else if f, ok := BufKeyActions[a]; ok {
|
} else if f, ok := BufKeyActions[a]; ok {
|
||||||
afn = f
|
afn = f
|
||||||
names = append(names, a)
|
names = append(names, a)
|
||||||
|
} else if f, ok := BufMouseActions[a]; ok {
|
||||||
|
afn = f
|
||||||
|
names = append(names, a)
|
||||||
} else {
|
} else {
|
||||||
screen.TermMessage("Error in bindings: action", a, "does not exist")
|
screen.TermMessage("Error in bindings: action", a, "does not exist")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
actionfns = append(actionfns, afn)
|
actionfns = append(actionfns, afn)
|
||||||
}
|
}
|
||||||
bufAction := func(h *BufPane) bool {
|
bufAction := func(h *BufPane, te *tcell.EventMouse) bool {
|
||||||
cursors := h.Buf.GetCursors()
|
cursors := h.Buf.GetCursors()
|
||||||
success := true
|
success := true
|
||||||
for i, a := range actionfns {
|
for i, a := range actionfns {
|
||||||
|
@ -147,7 +161,7 @@ func bufMapKey(k Event, action string) {
|
||||||
h.Buf.SetCurCursor(c.Num)
|
h.Buf.SetCurCursor(c.Num)
|
||||||
h.Cursor = c
|
h.Cursor = c
|
||||||
if i == 0 || (success && types[i-1] == '&') || (!success && types[i-1] == '|') || (types[i-1] == ',') {
|
if i == 0 || (success && types[i-1] == '&') || (!success && types[i-1] == '|') || (types[i-1] == ',') {
|
||||||
innerSuccess = innerSuccess && h.execAction(a, names[i], j)
|
innerSuccess = innerSuccess && h.execAction(a, names[i], j, te)
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -155,21 +169,21 @@ func bufMapKey(k Event, action string) {
|
||||||
// if the action changed the current pane, update the reference
|
// if the action changed the current pane, update the reference
|
||||||
h = MainTab().CurPane()
|
h = MainTab().CurPane()
|
||||||
success = innerSuccess
|
success = innerSuccess
|
||||||
|
if h == nil {
|
||||||
|
// stop, in case the current pane is not a BufPane
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
BufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(bufAction))
|
switch e := k.(type) {
|
||||||
}
|
case KeyEvent, KeySequenceEvent, RawEvent:
|
||||||
|
BufBindings.RegisterKeyBinding(e, BufKeyActionGeneral(func(h *BufPane) bool {
|
||||||
// BufMapMouse maps a mouse event to an action
|
return bufAction(h, nil)
|
||||||
func bufMapMouse(k MouseEvent, action string) {
|
}))
|
||||||
if f, ok := BufMouseActions[action]; ok {
|
case MouseEvent:
|
||||||
BufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f))
|
BufBindings.RegisterMouseBinding(e, BufMouseActionGeneral(bufAction))
|
||||||
} else {
|
|
||||||
// TODO
|
|
||||||
// delete(BufMouseBindings, k)
|
|
||||||
bufMapKey(k, action)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,11 +214,15 @@ type BufPane struct {
|
||||||
// Cursor is the currently active buffer cursor
|
// Cursor is the currently active buffer cursor
|
||||||
Cursor *buffer.Cursor
|
Cursor *buffer.Cursor
|
||||||
|
|
||||||
// Since tcell doesn't differentiate between a mouse release event
|
// Since tcell doesn't differentiate between a mouse press event
|
||||||
// and a mouse move event with no keys pressed, we need to keep
|
// and a mouse move event with button pressed (nor between a mouse
|
||||||
// track of whether or not the mouse was pressed (or not released) last event to determine
|
// release event and a mouse move event with no buttons pressed),
|
||||||
// mouse release events
|
// we need to keep track of whether or not the mouse was previously
|
||||||
mouseReleased bool
|
// pressed, to determine mouse release and mouse drag events.
|
||||||
|
// Moreover, since in case of a release event tcell doesn't tell us
|
||||||
|
// which button was released, we need to keep track of which
|
||||||
|
// (possibly multiple) buttons were pressed previously.
|
||||||
|
mousePressed map[MouseEvent]bool
|
||||||
|
|
||||||
// We need to keep track of insert key press toggle
|
// We need to keep track of insert key press toggle
|
||||||
isOverwriteMode bool
|
isOverwriteMode bool
|
||||||
|
@ -250,7 +268,7 @@ func newBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane {
|
||||||
h.tab = tab
|
h.tab = tab
|
||||||
|
|
||||||
h.Cursor = h.Buf.GetActiveCursor()
|
h.Cursor = h.Buf.GetActiveCursor()
|
||||||
h.mouseReleased = true
|
h.mousePressed = make(map[MouseEvent]bool)
|
||||||
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
@ -276,7 +294,11 @@ func NewBufPaneFromBuf(buf *buffer.Buffer, tab *Tab) *BufPane {
|
||||||
func (h *BufPane) finishInitialize() {
|
func (h *BufPane) finishInitialize() {
|
||||||
h.initialRelocate()
|
h.initialRelocate()
|
||||||
h.initialized = true
|
h.initialized = true
|
||||||
config.RunPluginFn("onBufPaneOpen", luar.New(ulua.L, h))
|
|
||||||
|
err := config.RunPluginFn("onBufPaneOpen", luar.New(ulua.L, h))
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize resizes the pane
|
// Resize resizes the pane
|
||||||
|
@ -304,9 +326,9 @@ func (h *BufPane) ResizePane(size int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PluginCB calls all plugin callbacks with a certain name and displays an
|
// PluginCB calls all plugin callbacks with a certain name and displays an
|
||||||
// error if there is one and returns the aggregrate boolean response
|
// error if there is one and returns the aggregate boolean response
|
||||||
func (h *BufPane) PluginCB(cb string) bool {
|
func (h *BufPane) PluginCB(cb string) bool {
|
||||||
b, err := config.RunPluginFnBool(cb, luar.New(ulua.L, h))
|
b, err := config.RunPluginFnBool(h.Buf.Settings, cb, luar.New(ulua.L, h))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage(err)
|
screen.TermMessage(err)
|
||||||
}
|
}
|
||||||
|
@ -315,13 +337,19 @@ func (h *BufPane) PluginCB(cb string) bool {
|
||||||
|
|
||||||
// PluginCBRune is the same as PluginCB but also passes a rune to the plugins
|
// PluginCBRune is the same as PluginCB but also passes a rune to the plugins
|
||||||
func (h *BufPane) PluginCBRune(cb string, r rune) bool {
|
func (h *BufPane) PluginCBRune(cb string, r rune) bool {
|
||||||
b, err := config.RunPluginFnBool(cb, luar.New(ulua.L, h), luar.New(ulua.L, string(r)))
|
b, err := config.RunPluginFnBool(h.Buf.Settings, cb, luar.New(ulua.L, h), luar.New(ulua.L, string(r)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage(err)
|
screen.TermMessage(err)
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *BufPane) resetMouse() {
|
||||||
|
for me := range h.mousePressed {
|
||||||
|
delete(h.mousePressed, me)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// OpenBuffer opens the given buffer in this pane.
|
// OpenBuffer opens the given buffer in this pane.
|
||||||
func (h *BufPane) OpenBuffer(b *buffer.Buffer) {
|
func (h *BufPane) OpenBuffer(b *buffer.Buffer) {
|
||||||
h.Buf.Close()
|
h.Buf.Close()
|
||||||
|
@ -332,7 +360,7 @@ func (h *BufPane) OpenBuffer(b *buffer.Buffer) {
|
||||||
h.initialRelocate()
|
h.initialRelocate()
|
||||||
// Set mouseReleased to true because we assume the mouse is not being
|
// Set mouseReleased to true because we assume the mouse is not being
|
||||||
// pressed when the editor is opened
|
// pressed when the editor is opened
|
||||||
h.mouseReleased = true
|
h.resetMouse()
|
||||||
// Set isOverwriteMode to false, because we assume we are in the default
|
// Set isOverwriteMode to false, because we assume we are in the default
|
||||||
// mode when editor is opened
|
// mode when editor is opened
|
||||||
h.isOverwriteMode = false
|
h.isOverwriteMode = false
|
||||||
|
@ -395,9 +423,23 @@ func (h *BufPane) Name() string {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReOpen reloads the file opened in the bufpane from disk
|
||||||
|
func (h *BufPane) ReOpen() {
|
||||||
|
h.Buf.ReOpen()
|
||||||
|
h.Relocate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *BufPane) getReloadSetting() string {
|
||||||
|
reloadSetting := h.Buf.Settings["reload"]
|
||||||
|
return reloadSetting.(string)
|
||||||
|
}
|
||||||
|
|
||||||
// HandleEvent executes the tcell event properly
|
// HandleEvent executes the tcell event properly
|
||||||
func (h *BufPane) HandleEvent(event tcell.Event) {
|
func (h *BufPane) HandleEvent(event tcell.Event) {
|
||||||
if h.Buf.ExternallyModified() && !h.Buf.ReloadDisabled {
|
if h.Buf.ExternallyModified() && !h.Buf.ReloadDisabled {
|
||||||
|
reload := h.getReloadSetting()
|
||||||
|
|
||||||
|
if reload == "prompt" {
|
||||||
InfoBar.YNPrompt("The file on disk has changed. Reload file? (y,n,esc)", func(yes, canceled bool) {
|
InfoBar.YNPrompt("The file on disk has changed. Reload file? (y,n,esc)", func(yes, canceled bool) {
|
||||||
if canceled {
|
if canceled {
|
||||||
h.Buf.DisableReload()
|
h.Buf.DisableReload()
|
||||||
|
@ -405,10 +447,16 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
|
||||||
if !yes || canceled {
|
if !yes || canceled {
|
||||||
h.Buf.UpdateModTime()
|
h.Buf.UpdateModTime()
|
||||||
} else {
|
} else {
|
||||||
h.Buf.ReOpen()
|
h.ReOpen()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else if reload == "auto" {
|
||||||
|
h.ReOpen()
|
||||||
|
} else if reload == "disabled" {
|
||||||
|
h.Buf.DisableReload()
|
||||||
|
} else {
|
||||||
|
InfoBar.Message("Invalid reload setting")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
|
@ -432,50 +480,37 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
|
||||||
h.DoRuneInsert(e.Rune())
|
h.DoRuneInsert(e.Rune())
|
||||||
}
|
}
|
||||||
case *tcell.EventMouse:
|
case *tcell.EventMouse:
|
||||||
cancel := false
|
if e.Buttons() != tcell.ButtonNone {
|
||||||
switch e.Buttons() {
|
|
||||||
case tcell.Button1:
|
|
||||||
_, my := e.Position()
|
|
||||||
if h.Buf.Type.Kind != buffer.BTInfo.Kind && h.Buf.Settings["statusline"].(bool) && my >= h.GetView().Y+h.GetView().Height-1 {
|
|
||||||
cancel = true
|
|
||||||
}
|
|
||||||
case tcell.ButtonNone:
|
|
||||||
// Mouse event with no click
|
|
||||||
if !h.mouseReleased {
|
|
||||||
// Mouse was just released
|
|
||||||
|
|
||||||
// mx, my := e.Position()
|
|
||||||
// mouseLoc := h.LocFromVisual(buffer.Loc{X: mx, Y: my})
|
|
||||||
|
|
||||||
// we could finish the selection based on the release location as described
|
|
||||||
// below but when the mouse click is within the scroll margin this will
|
|
||||||
// cause a scroll and selection even for a simple mouse click which is
|
|
||||||
// not good
|
|
||||||
// for terminals that don't support mouse motion events, selection via
|
|
||||||
// the mouse won't work but this is ok
|
|
||||||
|
|
||||||
// Relocating here isn't really necessary because the cursor will
|
|
||||||
// be in the right place from the last mouse event
|
|
||||||
// However, if we are running in a terminal that doesn't support mouse motion
|
|
||||||
// events, this still allows the user to make selections, except only after they
|
|
||||||
// release the mouse
|
|
||||||
|
|
||||||
// if !h.doubleClick && !h.tripleClick {
|
|
||||||
// h.Cursor.SetSelectionEnd(h.Cursor.Loc)
|
|
||||||
// }
|
|
||||||
if h.Cursor.HasSelection() {
|
|
||||||
h.Cursor.CopySelection(clipboard.PrimaryReg)
|
|
||||||
}
|
|
||||||
h.mouseReleased = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cancel {
|
|
||||||
me := MouseEvent{
|
me := MouseEvent{
|
||||||
btn: e.Buttons(),
|
btn: e.Buttons(),
|
||||||
mod: metaToAlt(e.Modifiers()),
|
mod: metaToAlt(e.Modifiers()),
|
||||||
|
state: MousePress,
|
||||||
|
}
|
||||||
|
isDrag := len(h.mousePressed) > 0
|
||||||
|
|
||||||
|
if e.Buttons() & ^(tcell.WheelUp|tcell.WheelDown|tcell.WheelLeft|tcell.WheelRight) != tcell.ButtonNone {
|
||||||
|
h.mousePressed[me] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDrag {
|
||||||
|
me.state = MouseDrag
|
||||||
}
|
}
|
||||||
h.DoMouseEvent(me, e)
|
h.DoMouseEvent(me, e)
|
||||||
|
} else {
|
||||||
|
// Mouse event with no click - mouse was just released.
|
||||||
|
// If there were multiple mouse buttons pressed, we don't know which one
|
||||||
|
// was actually released, so we assume they all were released.
|
||||||
|
pressed := len(h.mousePressed) > 0
|
||||||
|
for me := range h.mousePressed {
|
||||||
|
delete(h.mousePressed, me)
|
||||||
|
|
||||||
|
me.state = MouseRelease
|
||||||
|
h.DoMouseEvent(me, e)
|
||||||
|
}
|
||||||
|
if !pressed {
|
||||||
|
// Propagate the mouse release in case the press wasn't for this BufPane
|
||||||
|
Tabs.ResetMouse()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.Buf.MergeCursors()
|
h.Buf.MergeCursors()
|
||||||
|
@ -495,6 +530,14 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
|
||||||
InfoBar.ClearGutter()
|
InfoBar.ClearGutter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursors := h.Buf.GetCursors()
|
||||||
|
for _, c := range cursors {
|
||||||
|
if c.NewTrailingWsY != c.Y && (!c.HasSelection() ||
|
||||||
|
(c.NewTrailingWsY != c.CurSelection[0].Y && c.NewTrailingWsY != c.CurSelection[1].Y)) {
|
||||||
|
c.NewTrailingWsY = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bindings returns the current bindings tree for this buffer.
|
// Bindings returns the current bindings tree for this buffer.
|
||||||
|
@ -506,7 +549,10 @@ func (h *BufPane) Bindings() *KeyTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoKeyEvent executes a key event by finding the action it is bound
|
// DoKeyEvent executes a key event by finding the action it is bound
|
||||||
// to and executing it (possibly multiple times for multiple cursors)
|
// to and executing it (possibly multiple times for multiple cursors).
|
||||||
|
// Returns true if the action was executed OR if there are more keys
|
||||||
|
// remaining to process before executing an action (if this is a key
|
||||||
|
// sequence event). Returns false if no action found.
|
||||||
func (h *BufPane) DoKeyEvent(e Event) bool {
|
func (h *BufPane) DoKeyEvent(e Event) bool {
|
||||||
binds := h.Bindings()
|
binds := h.Bindings()
|
||||||
action, more := binds.NextEvent(e, nil)
|
action, more := binds.NextEvent(e, nil)
|
||||||
|
@ -520,7 +566,7 @@ func (h *BufPane) DoKeyEvent(e Event) bool {
|
||||||
return more
|
return more
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BufPane) execAction(action func(*BufPane) bool, name string, cursor int) bool {
|
func (h *BufPane) execAction(action BufAction, name string, cursor int, te *tcell.EventMouse) bool {
|
||||||
if name != "Autocomplete" && name != "CycleAutocompleteBack" {
|
if name != "Autocomplete" && name != "CycleAutocompleteBack" {
|
||||||
h.Buf.HasSuggestions = false
|
h.Buf.HasSuggestions = false
|
||||||
}
|
}
|
||||||
|
@ -528,7 +574,13 @@ func (h *BufPane) execAction(action func(*BufPane) bool, name string, cursor int
|
||||||
_, isMulti := MultiActions[name]
|
_, isMulti := MultiActions[name]
|
||||||
if (!isMulti && cursor == 0) || isMulti {
|
if (!isMulti && cursor == 0) || isMulti {
|
||||||
if h.PluginCB("pre" + name) {
|
if h.PluginCB("pre" + name) {
|
||||||
success := action(h)
|
var success bool
|
||||||
|
switch a := action.(type) {
|
||||||
|
case BufKeyAction:
|
||||||
|
success = a(h)
|
||||||
|
case BufMouseAction:
|
||||||
|
success = a(h, te)
|
||||||
|
}
|
||||||
success = success && h.PluginCB("on"+name)
|
success = success && h.PluginCB("on"+name)
|
||||||
|
|
||||||
if isMulti {
|
if isMulti {
|
||||||
|
@ -649,6 +701,10 @@ func (h *BufPane) Close() {
|
||||||
|
|
||||||
// SetActive marks this pane as active.
|
// SetActive marks this pane as active.
|
||||||
func (h *BufPane) SetActive(b bool) {
|
func (h *BufPane) SetActive(b bool) {
|
||||||
|
if h.IsActive() == b {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
h.BWindow.SetActive(b)
|
h.BWindow.SetActive(b)
|
||||||
if b {
|
if b {
|
||||||
// Display any gutter messages for this line
|
// Display any gutter messages for this line
|
||||||
|
@ -664,8 +720,12 @@ func (h *BufPane) SetActive(b bool) {
|
||||||
if none && InfoBar.HasGutter {
|
if none && InfoBar.HasGutter {
|
||||||
InfoBar.ClearGutter()
|
InfoBar.ClearGutter()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
err := config.RunPluginFn("onSetActive", luar.New(ulua.L, h))
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BufKeyActions contains the list of all possible key actions the bufhandler could execute
|
// BufKeyActions contains the list of all possible key actions the bufhandler could execute
|
||||||
|
@ -686,10 +746,16 @@ var BufKeyActions = map[string]BufKeyAction{
|
||||||
"SelectRight": (*BufPane).SelectRight,
|
"SelectRight": (*BufPane).SelectRight,
|
||||||
"WordRight": (*BufPane).WordRight,
|
"WordRight": (*BufPane).WordRight,
|
||||||
"WordLeft": (*BufPane).WordLeft,
|
"WordLeft": (*BufPane).WordLeft,
|
||||||
|
"SubWordRight": (*BufPane).SubWordRight,
|
||||||
|
"SubWordLeft": (*BufPane).SubWordLeft,
|
||||||
"SelectWordRight": (*BufPane).SelectWordRight,
|
"SelectWordRight": (*BufPane).SelectWordRight,
|
||||||
"SelectWordLeft": (*BufPane).SelectWordLeft,
|
"SelectWordLeft": (*BufPane).SelectWordLeft,
|
||||||
|
"SelectSubWordRight": (*BufPane).SelectSubWordRight,
|
||||||
|
"SelectSubWordLeft": (*BufPane).SelectSubWordLeft,
|
||||||
"DeleteWordRight": (*BufPane).DeleteWordRight,
|
"DeleteWordRight": (*BufPane).DeleteWordRight,
|
||||||
"DeleteWordLeft": (*BufPane).DeleteWordLeft,
|
"DeleteWordLeft": (*BufPane).DeleteWordLeft,
|
||||||
|
"DeleteSubWordRight": (*BufPane).DeleteSubWordRight,
|
||||||
|
"DeleteSubWordLeft": (*BufPane).DeleteSubWordLeft,
|
||||||
"SelectLine": (*BufPane).SelectLine,
|
"SelectLine": (*BufPane).SelectLine,
|
||||||
"SelectToStartOfLine": (*BufPane).SelectToStartOfLine,
|
"SelectToStartOfLine": (*BufPane).SelectToStartOfLine,
|
||||||
"SelectToStartOfText": (*BufPane).SelectToStartOfText,
|
"SelectToStartOfText": (*BufPane).SelectToStartOfText,
|
||||||
|
@ -708,6 +774,8 @@ var BufKeyActions = map[string]BufKeyAction{
|
||||||
"FindLiteral": (*BufPane).FindLiteral,
|
"FindLiteral": (*BufPane).FindLiteral,
|
||||||
"FindNext": (*BufPane).FindNext,
|
"FindNext": (*BufPane).FindNext,
|
||||||
"FindPrevious": (*BufPane).FindPrevious,
|
"FindPrevious": (*BufPane).FindPrevious,
|
||||||
|
"DiffNext": (*BufPane).DiffNext,
|
||||||
|
"DiffPrevious": (*BufPane).DiffPrevious,
|
||||||
"Center": (*BufPane).Center,
|
"Center": (*BufPane).Center,
|
||||||
"Undo": (*BufPane).Undo,
|
"Undo": (*BufPane).Undo,
|
||||||
"Redo": (*BufPane).Redo,
|
"Redo": (*BufPane).Redo,
|
||||||
|
@ -788,6 +856,8 @@ var BufKeyActions = map[string]BufKeyAction{
|
||||||
// BufMouseActions contains the list of all possible mouse actions the bufhandler could execute
|
// BufMouseActions contains the list of all possible mouse actions the bufhandler could execute
|
||||||
var BufMouseActions = map[string]BufMouseAction{
|
var BufMouseActions = map[string]BufMouseAction{
|
||||||
"MousePress": (*BufPane).MousePress,
|
"MousePress": (*BufPane).MousePress,
|
||||||
|
"MouseDrag": (*BufPane).MouseDrag,
|
||||||
|
"MouseRelease": (*BufPane).MouseRelease,
|
||||||
"MouseMultiCursor": (*BufPane).MouseMultiCursor,
|
"MouseMultiCursor": (*BufPane).MouseMultiCursor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -812,10 +882,16 @@ var MultiActions = map[string]bool{
|
||||||
"SelectRight": true,
|
"SelectRight": true,
|
||||||
"WordRight": true,
|
"WordRight": true,
|
||||||
"WordLeft": true,
|
"WordLeft": true,
|
||||||
|
"SubWordRight": true,
|
||||||
|
"SubWordLeft": true,
|
||||||
"SelectWordRight": true,
|
"SelectWordRight": true,
|
||||||
"SelectWordLeft": true,
|
"SelectWordLeft": true,
|
||||||
|
"SelectSubWordRight": true,
|
||||||
|
"SelectSubWordLeft": true,
|
||||||
"DeleteWordRight": true,
|
"DeleteWordRight": true,
|
||||||
"DeleteWordLeft": true,
|
"DeleteWordLeft": true,
|
||||||
|
"DeleteSubWordRight": true,
|
||||||
|
"DeleteSubWordLeft": true,
|
||||||
"SelectLine": true,
|
"SelectLine": true,
|
||||||
"SelectToStartOfLine": true,
|
"SelectToStartOfLine": true,
|
||||||
"SelectToStartOfText": true,
|
"SelectToStartOfText": true,
|
||||||
|
|
|
@ -41,6 +41,7 @@ func InitCommands() {
|
||||||
"unbind": {(*BufPane).UnbindCmd, nil},
|
"unbind": {(*BufPane).UnbindCmd, nil},
|
||||||
"quit": {(*BufPane).QuitCmd, nil},
|
"quit": {(*BufPane).QuitCmd, nil},
|
||||||
"goto": {(*BufPane).GotoCmd, nil},
|
"goto": {(*BufPane).GotoCmd, nil},
|
||||||
|
"jump": {(*BufPane).JumpCmd, nil},
|
||||||
"save": {(*BufPane).SaveCmd, nil},
|
"save": {(*BufPane).SaveCmd, nil},
|
||||||
"replace": {(*BufPane).ReplaceCmd, nil},
|
"replace": {(*BufPane).ReplaceCmd, nil},
|
||||||
"replaceall": {(*BufPane).ReplaceAllCmd, nil},
|
"replaceall": {(*BufPane).ReplaceAllCmd, nil},
|
||||||
|
@ -329,13 +330,30 @@ func (h *BufPane) ToggleLogCmd(args []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReloadCmd reloads all files (syntax files, colorschemes...)
|
// ReloadCmd reloads all files (syntax files, colorschemes, plugins...)
|
||||||
func (h *BufPane) ReloadCmd(args []string) {
|
func (h *BufPane) ReloadCmd(args []string) {
|
||||||
ReloadConfig()
|
reloadRuntime(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReloadConfig reloads only the configuration
|
||||||
func ReloadConfig() {
|
func ReloadConfig() {
|
||||||
config.InitRuntimeFiles()
|
reloadRuntime(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reloadRuntime(reloadPlugins bool) {
|
||||||
|
if reloadPlugins {
|
||||||
|
err := config.RunPluginFn("deinit")
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.InitRuntimeFiles(true)
|
||||||
|
|
||||||
|
if reloadPlugins {
|
||||||
|
config.InitPlugins()
|
||||||
|
}
|
||||||
|
|
||||||
err := config.ReadSettings()
|
err := config.ReadSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage(err)
|
screen.TermMessage(err)
|
||||||
|
@ -344,14 +362,36 @@ func ReloadConfig() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage(err)
|
screen.TermMessage(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if reloadPlugins {
|
||||||
|
err = config.LoadAllPlugins()
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
InitBindings()
|
InitBindings()
|
||||||
InitCommands()
|
InitCommands()
|
||||||
|
|
||||||
|
if reloadPlugins {
|
||||||
|
err = config.RunPluginFn("preinit")
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage(err)
|
||||||
|
}
|
||||||
|
err = config.RunPluginFn("init")
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage(err)
|
||||||
|
}
|
||||||
|
err = config.RunPluginFn("postinit")
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = config.InitColorscheme()
|
err = config.InitColorscheme()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage(err)
|
screen.TermMessage(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, b := range buffer.OpenBuffers {
|
for _, b := range buffer.OpenBuffers {
|
||||||
b.UpdateRules()
|
b.UpdateRules()
|
||||||
}
|
}
|
||||||
|
@ -363,22 +403,24 @@ func (h *BufPane) ReopenCmd(args []string) {
|
||||||
InfoBar.YNPrompt("Save file before reopen?", func(yes, canceled bool) {
|
InfoBar.YNPrompt("Save file before reopen?", func(yes, canceled bool) {
|
||||||
if !canceled && yes {
|
if !canceled && yes {
|
||||||
h.Save()
|
h.Save()
|
||||||
h.Buf.ReOpen()
|
h.ReOpen()
|
||||||
} else if !canceled {
|
} else if !canceled {
|
||||||
h.Buf.ReOpen()
|
h.ReOpen()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
h.Buf.ReOpen()
|
h.ReOpen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BufPane) openHelp(page string) error {
|
func (h *BufPane) openHelp(page string) error {
|
||||||
if data, err := config.FindRuntimeFile(config.RTHelp, page).Data(); err != nil {
|
if data, err := config.FindRuntimeFile(config.RTHelp, page).Data(); err != nil {
|
||||||
return errors.New(fmt.Sprint("Unable to load help text", page, "\n", err))
|
return errors.New(fmt.Sprintf("Unable to load help text for %s: %v", page, err))
|
||||||
} else {
|
} else {
|
||||||
helpBuffer := buffer.NewBufferFromString(string(data), page+".md", buffer.BTHelp)
|
helpBuffer := buffer.NewBufferFromString(string(data), page+".md", buffer.BTHelp)
|
||||||
helpBuffer.SetName("Help " + page)
|
helpBuffer.SetName("Help " + page)
|
||||||
|
helpBuffer.SetOptionNative("hltaberrors", false)
|
||||||
|
helpBuffer.SetOptionNative("hltrailingws", false)
|
||||||
|
|
||||||
if h.Buf.Type == buffer.BTHelp {
|
if h.Buf.Type == buffer.BTHelp {
|
||||||
h.OpenBuffer(helpBuffer)
|
h.OpenBuffer(helpBuffer)
|
||||||
|
@ -471,17 +513,18 @@ func (h *BufPane) NewTabCmd(args []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetGlobalOptionNative(option string, nativeValue interface{}) error {
|
func SetGlobalOptionNative(option string, nativeValue interface{}) error {
|
||||||
local := false
|
// check for local option first...
|
||||||
for _, s := range config.LocalSettings {
|
for _, s := range config.LocalSettings {
|
||||||
if s == option {
|
if s == option {
|
||||||
local = true
|
MainTab().CurPane().Buf.SetOptionNative(option, nativeValue)
|
||||||
break
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !local {
|
// ...if it's not local continue with the globals
|
||||||
config.GlobalSettings[option] = nativeValue
|
config.GlobalSettings[option] = nativeValue
|
||||||
config.ModifiedSettings[option] = true
|
config.ModifiedSettings[option] = true
|
||||||
|
delete(config.VolatileSettings, option)
|
||||||
|
|
||||||
if option == "colorscheme" {
|
if option == "colorscheme" {
|
||||||
// LoadSyntaxFiles()
|
// LoadSyntaxFiles()
|
||||||
|
@ -530,7 +573,6 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for _, b := range buffer.OpenBuffers {
|
for _, b := range buffer.OpenBuffers {
|
||||||
b.SetOptionNative(option, nativeValue)
|
b.SetOptionNative(option, nativeValue)
|
||||||
|
@ -634,6 +676,11 @@ func (h *BufPane) ShowCmd(args []string) {
|
||||||
InfoBar.Message(option)
|
InfoBar.Message(option)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseKeyArg(arg string) string {
|
||||||
|
// If this is a raw escape sequence, convert it to its raw byte form
|
||||||
|
return strings.ReplaceAll(arg, "\\x1b", "\x1b")
|
||||||
|
}
|
||||||
|
|
||||||
// ShowKeyCmd displays the action that a key is bound to
|
// ShowKeyCmd displays the action that a key is bound to
|
||||||
func (h *BufPane) ShowKeyCmd(args []string) {
|
func (h *BufPane) ShowKeyCmd(args []string) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
|
@ -641,7 +688,7 @@ func (h *BufPane) ShowKeyCmd(args []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
event, err := findEvent(args[0])
|
event, err := findEvent(parseKeyArg(args[0]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InfoBar.Error(err)
|
InfoBar.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -660,7 +707,7 @@ func (h *BufPane) BindCmd(args []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := TryBindKey(args[0], args[1], true)
|
_, err := TryBindKey(parseKeyArg(args[0]), args[1], true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InfoBar.Error(err)
|
InfoBar.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -673,7 +720,7 @@ func (h *BufPane) UnbindCmd(args []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := UnbindKey(args[0])
|
err := UnbindKey(parseKeyArg(args[0]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InfoBar.Error(err)
|
InfoBar.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -701,41 +748,65 @@ func (h *BufPane) QuitCmd(args []string) {
|
||||||
// position in the buffer
|
// position in the buffer
|
||||||
// For example: `goto line`, or `goto line:col`
|
// For example: `goto line`, or `goto line:col`
|
||||||
func (h *BufPane) GotoCmd(args []string) {
|
func (h *BufPane) GotoCmd(args []string) {
|
||||||
if len(args) <= 0 {
|
line, col, err := h.parseLineCol(args)
|
||||||
InfoBar.Error("Not enough arguments")
|
|
||||||
} else {
|
|
||||||
h.RemoveAllMultiCursors()
|
|
||||||
if strings.Contains(args[0], ":") {
|
|
||||||
parts := strings.SplitN(args[0], ":", 2)
|
|
||||||
line, err := strconv.Atoi(parts[0])
|
|
||||||
if err != nil {
|
|
||||||
InfoBar.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
col, err := strconv.Atoi(parts[1])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InfoBar.Error(err)
|
InfoBar.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if line < 0 {
|
if line < 0 {
|
||||||
line = h.Buf.LinesNum() + 1 + line
|
line = h.Buf.LinesNum() + 1 + line
|
||||||
}
|
}
|
||||||
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
|
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
|
||||||
col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line)))
|
col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line)))
|
||||||
|
|
||||||
|
h.RemoveAllMultiCursors()
|
||||||
h.GotoLoc(buffer.Loc{col, line})
|
h.GotoLoc(buffer.Loc{col, line})
|
||||||
} else {
|
}
|
||||||
line, err := strconv.Atoi(args[0])
|
|
||||||
|
// JumpCmd is a command that will send the cursor to a certain relative
|
||||||
|
// position in the buffer
|
||||||
|
// For example: `jump line`, `jump -line`, or `jump -line:col`
|
||||||
|
func (h *BufPane) JumpCmd(args []string) {
|
||||||
|
line, col, err := h.parseLineCol(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InfoBar.Error(err)
|
InfoBar.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if line < 0 {
|
|
||||||
line = h.Buf.LinesNum() + 1 + line
|
line = h.Buf.GetActiveCursor().Y + 1 + line
|
||||||
}
|
|
||||||
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
|
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
|
||||||
h.GotoLoc(buffer.Loc{0, line})
|
col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line)))
|
||||||
|
|
||||||
|
h.RemoveAllMultiCursors()
|
||||||
|
h.GotoLoc(buffer.Loc{col, line})
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseLineCol is a helper to parse the input of GotoCmd and JumpCmd
|
||||||
|
func (h *BufPane) parseLineCol(args []string) (line int, col int, err error) {
|
||||||
|
if len(args) <= 0 {
|
||||||
|
return 0, 0, errors.New("Not enough arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
line, col = 0, 0
|
||||||
|
if strings.Contains(args[0], ":") {
|
||||||
|
parts := strings.SplitN(args[0], ":", 2)
|
||||||
|
line, err = strconv.Atoi(parts[0])
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
col, err = strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
line, err = strconv.Atoi(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return line, col, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveCmd saves the buffer optionally with an argument file name
|
// SaveCmd saves the buffer optionally with an argument file name
|
||||||
|
@ -810,7 +881,7 @@ func (h *BufPane) ReplaceCmd(args []string) {
|
||||||
end = h.Cursor.CurSelection[1]
|
end = h.Cursor.CurSelection[1]
|
||||||
}
|
}
|
||||||
if all {
|
if all {
|
||||||
nreplaced, _ = h.Buf.ReplaceRegex(start, end, regex, replace)
|
nreplaced, _ = h.Buf.ReplaceRegex(start, end, regex, replace, !noRegex)
|
||||||
} else {
|
} else {
|
||||||
inRange := func(l buffer.Loc) bool {
|
inRange := func(l buffer.Loc) bool {
|
||||||
return l.GreaterEqual(start) && l.LessEqual(end)
|
return l.GreaterEqual(start) && l.LessEqual(end)
|
||||||
|
@ -840,7 +911,7 @@ func (h *BufPane) ReplaceCmd(args []string) {
|
||||||
|
|
||||||
InfoBar.YNPrompt("Perform replacement (y,n,esc)", func(yes, canceled bool) {
|
InfoBar.YNPrompt("Perform replacement (y,n,esc)", func(yes, canceled bool) {
|
||||||
if !canceled && yes {
|
if !canceled && yes {
|
||||||
_, nrunes := h.Buf.ReplaceRegex(locs[0], locs[1], regex, replace)
|
_, nrunes := h.Buf.ReplaceRegex(locs[0], locs[1], regex, replace, !noRegex)
|
||||||
|
|
||||||
searchLoc = locs[0]
|
searchLoc = locs[0]
|
||||||
searchLoc.X += nrunes + locs[0].Diff(locs[1], h.Buf)
|
searchLoc.X += nrunes + locs[0].Diff(locs[1], h.Buf)
|
||||||
|
|
|
@ -41,6 +41,8 @@ var bufdefaults = map[string]string{
|
||||||
"Alt-F": "FindLiteral",
|
"Alt-F": "FindLiteral",
|
||||||
"Ctrl-n": "FindNext",
|
"Ctrl-n": "FindNext",
|
||||||
"Ctrl-p": "FindPrevious",
|
"Ctrl-p": "FindPrevious",
|
||||||
|
"Alt-[": "DiffPrevious|CursorStart",
|
||||||
|
"Alt-]": "DiffNext|CursorEnd",
|
||||||
"Ctrl-z": "Undo",
|
"Ctrl-z": "Undo",
|
||||||
"Ctrl-y": "Redo",
|
"Ctrl-y": "Redo",
|
||||||
"Ctrl-c": "CopyLine|Copy",
|
"Ctrl-c": "CopyLine|Copy",
|
||||||
|
@ -93,6 +95,8 @@ var bufdefaults = map[string]string{
|
||||||
"MouseWheelUp": "ScrollUp",
|
"MouseWheelUp": "ScrollUp",
|
||||||
"MouseWheelDown": "ScrollDown",
|
"MouseWheelDown": "ScrollDown",
|
||||||
"MouseLeft": "MousePress",
|
"MouseLeft": "MousePress",
|
||||||
|
"MouseLeftDrag": "MouseDrag",
|
||||||
|
"MouseLeftRelease": "MouseRelease",
|
||||||
"MouseMiddle": "PastePrimary",
|
"MouseMiddle": "PastePrimary",
|
||||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||||
|
|
||||||
|
@ -176,5 +180,7 @@ var infodefaults = map[string]string{
|
||||||
"MouseWheelUp": "HistoryUp",
|
"MouseWheelUp": "HistoryUp",
|
||||||
"MouseWheelDown": "HistoryDown",
|
"MouseWheelDown": "HistoryDown",
|
||||||
"MouseLeft": "MousePress",
|
"MouseLeft": "MousePress",
|
||||||
|
"MouseLeftDrag": "MouseDrag",
|
||||||
|
"MouseLeftRelease": "MouseRelease",
|
||||||
"MouseMiddle": "PastePrimary",
|
"MouseMiddle": "PastePrimary",
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !darwin
|
||||||
// +build !darwin
|
// +build !darwin
|
||||||
|
|
||||||
package action
|
package action
|
||||||
|
@ -43,6 +44,8 @@ var bufdefaults = map[string]string{
|
||||||
"Alt-F": "FindLiteral",
|
"Alt-F": "FindLiteral",
|
||||||
"Ctrl-n": "FindNext",
|
"Ctrl-n": "FindNext",
|
||||||
"Ctrl-p": "FindPrevious",
|
"Ctrl-p": "FindPrevious",
|
||||||
|
"Alt-[": "DiffPrevious|CursorStart",
|
||||||
|
"Alt-]": "DiffNext|CursorEnd",
|
||||||
"Ctrl-z": "Undo",
|
"Ctrl-z": "Undo",
|
||||||
"Ctrl-y": "Redo",
|
"Ctrl-y": "Redo",
|
||||||
"Ctrl-c": "CopyLine|Copy",
|
"Ctrl-c": "CopyLine|Copy",
|
||||||
|
@ -95,6 +98,8 @@ var bufdefaults = map[string]string{
|
||||||
"MouseWheelUp": "ScrollUp",
|
"MouseWheelUp": "ScrollUp",
|
||||||
"MouseWheelDown": "ScrollDown",
|
"MouseWheelDown": "ScrollDown",
|
||||||
"MouseLeft": "MousePress",
|
"MouseLeft": "MousePress",
|
||||||
|
"MouseLeftDrag": "MouseDrag",
|
||||||
|
"MouseLeftRelease": "MouseRelease",
|
||||||
"MouseMiddle": "PastePrimary",
|
"MouseMiddle": "PastePrimary",
|
||||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||||
|
|
||||||
|
@ -178,5 +183,7 @@ var infodefaults = map[string]string{
|
||||||
"MouseWheelUp": "HistoryUp",
|
"MouseWheelUp": "HistoryUp",
|
||||||
"MouseWheelDown": "HistoryDown",
|
"MouseWheelDown": "HistoryDown",
|
||||||
"MouseLeft": "MousePress",
|
"MouseLeft": "MousePress",
|
||||||
|
"MouseLeftDrag": "MouseDrag",
|
||||||
|
"MouseLeftRelease": "MouseRelease",
|
||||||
"MouseMiddle": "PastePrimary",
|
"MouseMiddle": "PastePrimary",
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,11 +100,20 @@ func (k KeySequenceEvent) Name() string {
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MouseState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
MousePress = iota
|
||||||
|
MouseDrag
|
||||||
|
MouseRelease
|
||||||
|
)
|
||||||
|
|
||||||
// MouseEvent is a mouse event with a mouse button and
|
// MouseEvent is a mouse event with a mouse button and
|
||||||
// any possible key modifiers
|
// any possible key modifiers
|
||||||
type MouseEvent struct {
|
type MouseEvent struct {
|
||||||
btn tcell.ButtonMask
|
btn tcell.ButtonMask
|
||||||
mod tcell.ModMask
|
mod tcell.ModMask
|
||||||
|
state MouseState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MouseEvent) Name() string {
|
func (m MouseEvent) Name() string {
|
||||||
|
@ -122,9 +131,17 @@ func (m MouseEvent) Name() string {
|
||||||
mod = "Ctrl-"
|
mod = "Ctrl-"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state := ""
|
||||||
|
switch m.state {
|
||||||
|
case MouseDrag:
|
||||||
|
state = "Drag"
|
||||||
|
case MouseRelease:
|
||||||
|
state = "Release"
|
||||||
|
}
|
||||||
|
|
||||||
for k, v := range mouseEvents {
|
for k, v := range mouseEvents {
|
||||||
if v == m.btn {
|
if v == m.btn {
|
||||||
return fmt.Sprintf("%s%s", mod, k)
|
return fmt.Sprintf("%s%s%s", mod, k, state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/zyedidia/micro/v2/internal/buffer"
|
"github.com/zyedidia/micro/v2/internal/buffer"
|
||||||
"github.com/zyedidia/micro/v2/internal/config"
|
"github.com/zyedidia/micro/v2/internal/config"
|
||||||
"github.com/zyedidia/micro/v2/internal/util"
|
"github.com/zyedidia/micro/v2/internal/util"
|
||||||
|
"github.com/zyedidia/micro/v2/pkg/highlight"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This file is meant (for now) for autocompletion in command mode, not
|
// This file is meant (for now) for autocompletion in command mode, not
|
||||||
|
@ -17,7 +18,7 @@ import (
|
||||||
// CommandComplete autocompletes commands
|
// CommandComplete autocompletes commands
|
||||||
func CommandComplete(b *buffer.Buffer) ([]string, []string) {
|
func CommandComplete(b *buffer.Buffer) ([]string, []string) {
|
||||||
c := b.GetActiveCursor()
|
c := b.GetActiveCursor()
|
||||||
input, argstart := buffer.GetArg(b)
|
input, argstart := b.GetArg()
|
||||||
|
|
||||||
var suggestions []string
|
var suggestions []string
|
||||||
for cmd := range commands {
|
for cmd := range commands {
|
||||||
|
@ -38,7 +39,7 @@ func CommandComplete(b *buffer.Buffer) ([]string, []string) {
|
||||||
// HelpComplete autocompletes help topics
|
// HelpComplete autocompletes help topics
|
||||||
func HelpComplete(b *buffer.Buffer) ([]string, []string) {
|
func HelpComplete(b *buffer.Buffer) ([]string, []string) {
|
||||||
c := b.GetActiveCursor()
|
c := b.GetActiveCursor()
|
||||||
input, argstart := buffer.GetArg(b)
|
input, argstart := b.GetArg()
|
||||||
|
|
||||||
var suggestions []string
|
var suggestions []string
|
||||||
|
|
||||||
|
@ -77,6 +78,63 @@ func colorschemeComplete(input string) (string, []string) {
|
||||||
return chosen, suggestions
|
return chosen, suggestions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filetypeComplete autocompletes filetype
|
||||||
|
func filetypeComplete(input string) (string, []string) {
|
||||||
|
var suggestions []string
|
||||||
|
|
||||||
|
// We cannot match filetypes just by names of syntax files,
|
||||||
|
// since those names may be different from the actual filetype values
|
||||||
|
// specified inside syntax files (e.g. "c++" filetype in cpp.yaml).
|
||||||
|
// So we need to parse filetype values out of those files.
|
||||||
|
for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
|
||||||
|
data, err := f.Data()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
header, err := highlight.MakeHeaderYaml(data)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Prevent duplicated defaults
|
||||||
|
if header.FileType == "off" || header.FileType == "unknown" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(header.FileType, input) {
|
||||||
|
suggestions = append(suggestions, header.FileType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headerLoop:
|
||||||
|
for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) {
|
||||||
|
data, err := f.Data()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
header, err := highlight.MakeHeader(data)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, v := range suggestions {
|
||||||
|
if v == header.FileType {
|
||||||
|
continue headerLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(header.FileType, input) {
|
||||||
|
suggestions = append(suggestions, header.FileType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix("off", input) {
|
||||||
|
suggestions = append(suggestions, "off")
|
||||||
|
}
|
||||||
|
|
||||||
|
var chosen string
|
||||||
|
if len(suggestions) == 1 {
|
||||||
|
chosen = suggestions[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return chosen, suggestions
|
||||||
|
}
|
||||||
|
|
||||||
func contains(s []string, e string) bool {
|
func contains(s []string, e string) bool {
|
||||||
for _, a := range s {
|
for _, a := range s {
|
||||||
if a == e {
|
if a == e {
|
||||||
|
@ -89,7 +147,7 @@ func contains(s []string, e string) bool {
|
||||||
// OptionComplete autocompletes options
|
// OptionComplete autocompletes options
|
||||||
func OptionComplete(b *buffer.Buffer) ([]string, []string) {
|
func OptionComplete(b *buffer.Buffer) ([]string, []string) {
|
||||||
c := b.GetActiveCursor()
|
c := b.GetActiveCursor()
|
||||||
input, argstart := buffer.GetArg(b)
|
input, argstart := b.GetArg()
|
||||||
|
|
||||||
var suggestions []string
|
var suggestions []string
|
||||||
for option := range config.GlobalSettings {
|
for option := range config.GlobalSettings {
|
||||||
|
@ -116,7 +174,7 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
|
||||||
c := b.GetActiveCursor()
|
c := b.GetActiveCursor()
|
||||||
l := b.LineBytes(c.Y)
|
l := b.LineBytes(c.Y)
|
||||||
l = util.SliceStart(l, c.X)
|
l = util.SliceStart(l, c.X)
|
||||||
input, argstart := buffer.GetArg(b)
|
input, argstart := b.GetArg()
|
||||||
|
|
||||||
completeValue := false
|
completeValue := false
|
||||||
args := bytes.Split(l, []byte{' '})
|
args := bytes.Split(l, []byte{' '})
|
||||||
|
@ -172,13 +230,8 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
|
||||||
switch inputOpt {
|
switch inputOpt {
|
||||||
case "colorscheme":
|
case "colorscheme":
|
||||||
_, suggestions = colorschemeComplete(input)
|
_, suggestions = colorschemeComplete(input)
|
||||||
case "fileformat":
|
case "filetype":
|
||||||
if strings.HasPrefix("unix", input) {
|
_, suggestions = filetypeComplete(input)
|
||||||
suggestions = append(suggestions, "unix")
|
|
||||||
}
|
|
||||||
if strings.HasPrefix("dos", input) {
|
|
||||||
suggestions = append(suggestions, "dos")
|
|
||||||
}
|
|
||||||
case "sucmd":
|
case "sucmd":
|
||||||
if strings.HasPrefix("sudo", input) {
|
if strings.HasPrefix("sudo", input) {
|
||||||
suggestions = append(suggestions, "sudo")
|
suggestions = append(suggestions, "sudo")
|
||||||
|
@ -186,15 +239,13 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
|
||||||
if strings.HasPrefix("doas", input) {
|
if strings.HasPrefix("doas", input) {
|
||||||
suggestions = append(suggestions, "doas")
|
suggestions = append(suggestions, "doas")
|
||||||
}
|
}
|
||||||
case "clipboard":
|
default:
|
||||||
if strings.HasPrefix("external", input) {
|
if choices, ok := config.OptionChoices[inputOpt]; ok {
|
||||||
suggestions = append(suggestions, "external")
|
for _, choice := range choices {
|
||||||
|
if strings.HasPrefix(choice, input) {
|
||||||
|
suggestions = append(suggestions, choice)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix("internal", input) {
|
|
||||||
suggestions = append(suggestions, "internal")
|
|
||||||
}
|
}
|
||||||
if strings.HasPrefix("terminal", input) {
|
|
||||||
suggestions = append(suggestions, "terminal")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,7 +261,7 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
|
||||||
// PluginCmdComplete autocompletes the plugin command
|
// PluginCmdComplete autocompletes the plugin command
|
||||||
func PluginCmdComplete(b *buffer.Buffer) ([]string, []string) {
|
func PluginCmdComplete(b *buffer.Buffer) ([]string, []string) {
|
||||||
c := b.GetActiveCursor()
|
c := b.GetActiveCursor()
|
||||||
input, argstart := buffer.GetArg(b)
|
input, argstart := b.GetArg()
|
||||||
|
|
||||||
var suggestions []string
|
var suggestions []string
|
||||||
for _, cmd := range PluginCmds {
|
for _, cmd := range PluginCmds {
|
||||||
|
@ -232,7 +283,7 @@ func PluginComplete(b *buffer.Buffer) ([]string, []string) {
|
||||||
c := b.GetActiveCursor()
|
c := b.GetActiveCursor()
|
||||||
l := b.LineBytes(c.Y)
|
l := b.LineBytes(c.Y)
|
||||||
l = util.SliceStart(l, c.X)
|
l = util.SliceStart(l, c.X)
|
||||||
input, argstart := buffer.GetArg(b)
|
input, argstart := b.GetArg()
|
||||||
|
|
||||||
completeValue := false
|
completeValue := false
|
||||||
args := bytes.Split(l, []byte{' '})
|
args := bytes.Split(l, []byte{' '})
|
||||||
|
|
|
@ -83,6 +83,8 @@ func (h *InfoPane) Close() {
|
||||||
|
|
||||||
func (h *InfoPane) HandleEvent(event tcell.Event) {
|
func (h *InfoPane) HandleEvent(event tcell.Event) {
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
|
case *tcell.EventResize:
|
||||||
|
// TODO
|
||||||
case *tcell.EventKey:
|
case *tcell.EventKey:
|
||||||
ke := KeyEvent{
|
ke := KeyEvent{
|
||||||
code: e.Key(),
|
code: e.Key(),
|
||||||
|
@ -93,12 +95,14 @@ func (h *InfoPane) HandleEvent(event tcell.Event) {
|
||||||
done := h.DoKeyEvent(ke)
|
done := h.DoKeyEvent(ke)
|
||||||
hasYN := h.HasYN
|
hasYN := h.HasYN
|
||||||
if e.Key() == tcell.KeyRune && hasYN {
|
if e.Key() == tcell.KeyRune && hasYN {
|
||||||
if (e.Rune() == 'y' || e.Rune() == 'Y') && hasYN {
|
y := e.Rune() == 'y' || e.Rune() == 'Y'
|
||||||
h.YNResp = true
|
n := e.Rune() == 'n' || e.Rune() == 'N'
|
||||||
h.DonePrompt(false)
|
if y || n {
|
||||||
} else if (e.Rune() == 'n' || e.Rune() == 'N') && hasYN {
|
h.YNResp = y
|
||||||
h.YNResp = false
|
|
||||||
h.DonePrompt(false)
|
h.DonePrompt(false)
|
||||||
|
|
||||||
|
InfoBindings.ResetEvents()
|
||||||
|
InfoBufBindings.ResetEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if e.Key() == tcell.KeyRune && !done && !hasYN {
|
if e.Key() == tcell.KeyRune && !done && !hasYN {
|
||||||
|
@ -108,7 +112,11 @@ func (h *InfoPane) HandleEvent(event tcell.Event) {
|
||||||
if done && h.HasPrompt && !hasYN {
|
if done && h.HasPrompt && !hasYN {
|
||||||
resp := string(h.LineBytes(0))
|
resp := string(h.LineBytes(0))
|
||||||
hist := h.History[h.PromptType]
|
hist := h.History[h.PromptType]
|
||||||
|
if resp != hist[h.HistoryNum] {
|
||||||
|
h.HistoryNum = len(hist) - 1
|
||||||
hist[h.HistoryNum] = resp
|
hist[h.HistoryNum] = resp
|
||||||
|
h.HistorySearch = false
|
||||||
|
}
|
||||||
if h.EventCallback != nil {
|
if h.EventCallback != nil {
|
||||||
h.EventCallback(resp)
|
h.EventCallback(resp)
|
||||||
}
|
}
|
||||||
|
@ -118,7 +126,10 @@ func (h *InfoPane) HandleEvent(event tcell.Event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoKeyEvent executes a key event for the command bar, doing any overridden actions
|
// DoKeyEvent executes a key event for the command bar, doing any overridden actions.
|
||||||
|
// Returns true if the action was executed OR if there are more keys remaining
|
||||||
|
// to process before executing an action (if this is a key sequence event).
|
||||||
|
// Returns false if no action found.
|
||||||
func (h *InfoPane) DoKeyEvent(e KeyEvent) bool {
|
func (h *InfoPane) DoKeyEvent(e KeyEvent) bool {
|
||||||
action, more := InfoBindings.NextEvent(e, nil)
|
action, more := InfoBindings.NextEvent(e, nil)
|
||||||
if action != nil && !more {
|
if action != nil && !more {
|
||||||
|
@ -132,11 +143,25 @@ func (h *InfoPane) DoKeyEvent(e KeyEvent) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !more {
|
if !more {
|
||||||
|
// If no infopane action found, try to find a bufpane action.
|
||||||
|
//
|
||||||
|
// TODO: this is buggy. For example, if the command bar has the following
|
||||||
|
// two bindings:
|
||||||
|
//
|
||||||
|
// "<Ctrl-x><Ctrl-p>": "HistoryUp",
|
||||||
|
// "<Ctrl-x><Ctrl-v>": "Paste",
|
||||||
|
//
|
||||||
|
// the 2nd binding (with a bufpane action) doesn't work, since <Ctrl-x>
|
||||||
|
// has been already consumed by the 1st binding (with an infopane action).
|
||||||
|
//
|
||||||
|
// We should either iterate both InfoBindings and InfoBufBindings keytrees
|
||||||
|
// together, or just use the same keytree for both infopane and bufpane
|
||||||
|
// bindings.
|
||||||
action, more = InfoBufBindings.NextEvent(e, nil)
|
action, more = InfoBufBindings.NextEvent(e, nil)
|
||||||
if action != nil && !more {
|
if action != nil && !more {
|
||||||
done := action(h.BufPane)
|
action(h.BufPane)
|
||||||
InfoBufBindings.ResetEvents()
|
InfoBufBindings.ResetEvents()
|
||||||
return done
|
return true
|
||||||
} else if action == nil && !more {
|
} else if action == nil && !more {
|
||||||
InfoBufBindings.ResetEvents()
|
InfoBufBindings.ResetEvents()
|
||||||
}
|
}
|
||||||
|
@ -155,6 +180,18 @@ func (h *InfoPane) HistoryDown() {
|
||||||
h.DownHistory(h.History[h.PromptType])
|
h.DownHistory(h.History[h.PromptType])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HistorySearchUp fetches the previous history item beginning with the text
|
||||||
|
// in the infobuffer before cursor
|
||||||
|
func (h *InfoPane) HistorySearchUp() {
|
||||||
|
h.SearchUpHistory(h.History[h.PromptType])
|
||||||
|
}
|
||||||
|
|
||||||
|
// HistorySearchDown fetches the next history item beginning with the text
|
||||||
|
// in the infobuffer before cursor
|
||||||
|
func (h *InfoPane) HistorySearchDown() {
|
||||||
|
h.SearchDownHistory(h.History[h.PromptType])
|
||||||
|
}
|
||||||
|
|
||||||
// Autocomplete begins autocompletion
|
// Autocomplete begins autocompletion
|
||||||
func (h *InfoPane) CommandComplete() {
|
func (h *InfoPane) CommandComplete() {
|
||||||
b := h.Buf
|
b := h.Buf
|
||||||
|
@ -200,6 +237,8 @@ func (h *InfoPane) AbortCommand() {
|
||||||
var InfoKeyActions = map[string]InfoKeyAction{
|
var InfoKeyActions = map[string]InfoKeyAction{
|
||||||
"HistoryUp": (*InfoPane).HistoryUp,
|
"HistoryUp": (*InfoPane).HistoryUp,
|
||||||
"HistoryDown": (*InfoPane).HistoryDown,
|
"HistoryDown": (*InfoPane).HistoryDown,
|
||||||
|
"HistorySearchUp": (*InfoPane).HistorySearchUp,
|
||||||
|
"HistorySearchDown": (*InfoPane).HistorySearchDown,
|
||||||
"CommandComplete": (*InfoPane).CommandComplete,
|
"CommandComplete": (*InfoPane).CommandComplete,
|
||||||
"ExecuteCommand": (*InfoPane).ExecuteCommand,
|
"ExecuteCommand": (*InfoPane).ExecuteCommand,
|
||||||
"AbortCommand": (*InfoPane).AbortCommand,
|
"AbortCommand": (*InfoPane).AbortCommand,
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package action
|
package action
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
luar "layeh.com/gopher-luar"
|
||||||
|
|
||||||
"github.com/zyedidia/micro/v2/internal/buffer"
|
"github.com/zyedidia/micro/v2/internal/buffer"
|
||||||
"github.com/zyedidia/micro/v2/internal/config"
|
"github.com/zyedidia/micro/v2/internal/config"
|
||||||
"github.com/zyedidia/micro/v2/internal/display"
|
"github.com/zyedidia/micro/v2/internal/display"
|
||||||
|
ulua "github.com/zyedidia/micro/v2/internal/lua"
|
||||||
"github.com/zyedidia/micro/v2/internal/screen"
|
"github.com/zyedidia/micro/v2/internal/screen"
|
||||||
"github.com/zyedidia/micro/v2/internal/views"
|
"github.com/zyedidia/micro/v2/internal/views"
|
||||||
"github.com/zyedidia/tcell/v2"
|
"github.com/zyedidia/tcell/v2"
|
||||||
|
@ -121,6 +124,12 @@ func (t *TabList) HandleEvent(event tcell.Event) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case tcell.ButtonNone:
|
||||||
|
if t.List[t.Active()].release {
|
||||||
|
// Mouse release received, while already released
|
||||||
|
t.ResetMouse()
|
||||||
|
return
|
||||||
|
}
|
||||||
case tcell.WheelUp:
|
case tcell.WheelUp:
|
||||||
if my == t.Y {
|
if my == t.Y {
|
||||||
t.Scroll(4)
|
t.Scroll(4)
|
||||||
|
@ -144,6 +153,45 @@ func (t *TabList) Display() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TabList) SetActive(a int) {
|
||||||
|
t.TabWindow.SetActive(a)
|
||||||
|
|
||||||
|
for i, p := range t.List {
|
||||||
|
if i == a {
|
||||||
|
if !p.isActive {
|
||||||
|
p.isActive = true
|
||||||
|
|
||||||
|
err := config.RunPluginFn("onSetActive", luar.New(ulua.L, p.CurPane()))
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.isActive = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetMouse resets the mouse release state after the screen was stopped
|
||||||
|
// or the pane changed.
|
||||||
|
// This prevents situations in which mouse releases are received at the wrong place
|
||||||
|
// and the mouse state is still pressed.
|
||||||
|
func (t *TabList) ResetMouse() {
|
||||||
|
for _, tab := range t.List {
|
||||||
|
if !tab.release && tab.resizing != nil {
|
||||||
|
tab.resizing = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tab.release = true
|
||||||
|
|
||||||
|
for _, p := range tab.Panes {
|
||||||
|
if bp, ok := p.(*BufPane); ok {
|
||||||
|
bp.resetMouse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tabs is the global tab list
|
// Tabs is the global tab list
|
||||||
var Tabs *TabList
|
var Tabs *TabList
|
||||||
|
|
||||||
|
@ -161,6 +209,8 @@ func InitTabs(bufs []*buffer.Buffer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
screen.RestartCallback = Tabs.ResetMouse
|
||||||
}
|
}
|
||||||
|
|
||||||
func MainTab() *Tab {
|
func MainTab() *Tab {
|
||||||
|
@ -174,6 +224,9 @@ func MainTab() *Tab {
|
||||||
type Tab struct {
|
type Tab struct {
|
||||||
*views.Node
|
*views.Node
|
||||||
*display.UIWindow
|
*display.UIWindow
|
||||||
|
|
||||||
|
isActive bool
|
||||||
|
|
||||||
Panes []Pane
|
Panes []Pane
|
||||||
active int
|
active int
|
||||||
|
|
||||||
|
@ -211,16 +264,20 @@ func NewTabFromPane(x, y, width, height int, pane Pane) *Tab {
|
||||||
// HandleEvent takes a tcell event and usually dispatches it to the current
|
// HandleEvent takes a tcell event and usually dispatches it to the current
|
||||||
// active pane. However if the event is a resize or a mouse event where the user
|
// active pane. However if the event is a resize or a mouse event where the user
|
||||||
// is interacting with the UI (resizing splits) then the event is consumed here
|
// is interacting with the UI (resizing splits) then the event is consumed here
|
||||||
// If the event is a mouse event in a pane, that pane will become active and get
|
// If the event is a mouse press event in a pane, that pane will become active
|
||||||
// the event
|
// and get the event
|
||||||
func (t *Tab) HandleEvent(event tcell.Event) {
|
func (t *Tab) HandleEvent(event tcell.Event) {
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
case *tcell.EventMouse:
|
case *tcell.EventMouse:
|
||||||
mx, my := e.Position()
|
mx, my := e.Position()
|
||||||
switch e.Buttons() {
|
btn := e.Buttons()
|
||||||
case tcell.Button1:
|
switch {
|
||||||
|
case btn & ^(tcell.WheelUp|tcell.WheelDown|tcell.WheelLeft|tcell.WheelRight) != tcell.ButtonNone:
|
||||||
|
// button press or drag
|
||||||
wasReleased := t.release
|
wasReleased := t.release
|
||||||
t.release = false
|
t.release = false
|
||||||
|
|
||||||
|
if btn == tcell.Button1 {
|
||||||
if t.resizing != nil {
|
if t.resizing != nil {
|
||||||
var size int
|
var size int
|
||||||
if t.resizing.Kind == views.STVert {
|
if t.resizing.Kind == views.STVert {
|
||||||
|
@ -232,13 +289,15 @@ func (t *Tab) HandleEvent(event tcell.Event) {
|
||||||
t.Resize()
|
t.Resize()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if wasReleased {
|
if wasReleased {
|
||||||
t.resizing = t.GetMouseSplitNode(buffer.Loc{mx, my})
|
t.resizing = t.GetMouseSplitNode(buffer.Loc{mx, my})
|
||||||
if t.resizing != nil {
|
if t.resizing != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if wasReleased {
|
||||||
for i, p := range t.Panes {
|
for i, p := range t.Panes {
|
||||||
v := p.GetView()
|
v := p.GetView()
|
||||||
inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
|
inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
|
||||||
|
@ -248,10 +307,15 @@ func (t *Tab) HandleEvent(event tcell.Event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case tcell.ButtonNone:
|
case btn == tcell.ButtonNone:
|
||||||
t.resizing = nil
|
// button release
|
||||||
t.release = true
|
t.release = true
|
||||||
|
if t.resizing != nil {
|
||||||
|
t.resizing = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
|
// wheel move
|
||||||
for _, p := range t.Panes {
|
for _, p := range t.Panes {
|
||||||
v := p.GetView()
|
v := p.GetView()
|
||||||
inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
|
inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
|
||||||
|
|
|
@ -81,6 +81,10 @@ func (t *TermPane) SetID(i uint64) {
|
||||||
t.id = i
|
t.id = i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TermPane) Name() string {
|
||||||
|
return t.Terminal.Name()
|
||||||
|
}
|
||||||
|
|
||||||
func (t *TermPane) SetTab(tab *Tab) {
|
func (t *TermPane) SetTab(tab *Tab) {
|
||||||
t.tab = tab
|
t.tab = tab
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ func (b *Buffer) CycleAutocomplete(forward bool) {
|
||||||
|
|
||||||
// GetWord gets the most recent word separated by any separator
|
// GetWord gets the most recent word separated by any separator
|
||||||
// (whitespace, punctuation, any non alphanumeric character)
|
// (whitespace, punctuation, any non alphanumeric character)
|
||||||
func GetWord(b *Buffer) ([]byte, int) {
|
func (b *Buffer) GetWord() ([]byte, int) {
|
||||||
c := b.GetActiveCursor()
|
c := b.GetActiveCursor()
|
||||||
l := b.LineBytes(c.Y)
|
l := b.LineBytes(c.Y)
|
||||||
l = util.SliceStart(l, c.X)
|
l = util.SliceStart(l, c.X)
|
||||||
|
@ -73,17 +73,17 @@ func GetWord(b *Buffer) ([]byte, int) {
|
||||||
return []byte{}, -1
|
return []byte{}, -1
|
||||||
}
|
}
|
||||||
|
|
||||||
if util.IsNonAlphaNumeric(b.RuneAt(c.Loc.Move(-1, b))) {
|
if util.IsNonWordChar(b.RuneAt(c.Loc.Move(-1, b))) {
|
||||||
return []byte{}, c.X
|
return []byte{}, c.X
|
||||||
}
|
}
|
||||||
|
|
||||||
args := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
|
args := bytes.FieldsFunc(l, util.IsNonWordChar)
|
||||||
input := args[len(args)-1]
|
input := args[len(args)-1]
|
||||||
return input, c.X - util.CharacterCount(input)
|
return input, c.X - util.CharacterCount(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetArg gets the most recent word (separated by ' ' only)
|
// GetArg gets the most recent word (separated by ' ' only)
|
||||||
func GetArg(b *Buffer) (string, int) {
|
func (b *Buffer) GetArg() (string, int) {
|
||||||
c := b.GetActiveCursor()
|
c := b.GetActiveCursor()
|
||||||
l := b.LineBytes(c.Y)
|
l := b.LineBytes(c.Y)
|
||||||
l = util.SliceStart(l, c.X)
|
l = util.SliceStart(l, c.X)
|
||||||
|
@ -104,7 +104,7 @@ func GetArg(b *Buffer) (string, int) {
|
||||||
// FileComplete autocompletes filenames
|
// FileComplete autocompletes filenames
|
||||||
func FileComplete(b *Buffer) ([]string, []string) {
|
func FileComplete(b *Buffer) ([]string, []string) {
|
||||||
c := b.GetActiveCursor()
|
c := b.GetActiveCursor()
|
||||||
input, argstart := GetArg(b)
|
input, argstart := b.GetArg()
|
||||||
|
|
||||||
sep := string(os.PathSeparator)
|
sep := string(os.PathSeparator)
|
||||||
dirs := strings.Split(input, sep)
|
dirs := strings.Split(input, sep)
|
||||||
|
@ -153,7 +153,7 @@ func FileComplete(b *Buffer) ([]string, []string) {
|
||||||
// BufferComplete autocompletes based on previous words in the buffer
|
// BufferComplete autocompletes based on previous words in the buffer
|
||||||
func BufferComplete(b *Buffer) ([]string, []string) {
|
func BufferComplete(b *Buffer) ([]string, []string) {
|
||||||
c := b.GetActiveCursor()
|
c := b.GetActiveCursor()
|
||||||
input, argstart := GetWord(b)
|
input, argstart := b.GetWord()
|
||||||
|
|
||||||
if argstart == -1 {
|
if argstart == -1 {
|
||||||
return []string{}, []string{}
|
return []string{}, []string{}
|
||||||
|
@ -166,7 +166,7 @@ func BufferComplete(b *Buffer) ([]string, []string) {
|
||||||
var suggestions []string
|
var suggestions []string
|
||||||
for i := c.Y; i >= 0; i-- {
|
for i := c.Y; i >= 0; i-- {
|
||||||
l := b.LineBytes(i)
|
l := b.LineBytes(i)
|
||||||
words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
|
words := bytes.FieldsFunc(l, util.IsNonWordChar)
|
||||||
for _, w := range words {
|
for _, w := range words {
|
||||||
if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen {
|
if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen {
|
||||||
strw := string(w)
|
strw := string(w)
|
||||||
|
@ -179,7 +179,7 @@ func BufferComplete(b *Buffer) ([]string, []string) {
|
||||||
}
|
}
|
||||||
for i := c.Y + 1; i < b.LinesNum(); i++ {
|
for i := c.Y + 1; i < b.LinesNum(); i++ {
|
||||||
l := b.LineBytes(i)
|
l := b.LineBytes(i)
|
||||||
words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
|
words := bytes.FieldsFunc(l, util.IsNonWordChar)
|
||||||
for _, w := range words {
|
for _, w := range words {
|
||||||
if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen {
|
if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen {
|
||||||
strw := string(w)
|
strw := string(w)
|
||||||
|
|
|
@ -57,7 +57,7 @@ var (
|
||||||
BTLog = BufType{2, true, true, false}
|
BTLog = BufType{2, true, true, false}
|
||||||
// BTScratch is a buffer that cannot be saved (for scratch work)
|
// BTScratch is a buffer that cannot be saved (for scratch work)
|
||||||
BTScratch = BufType{3, false, true, false}
|
BTScratch = BufType{3, false, true, false}
|
||||||
// BTRaw is is a buffer that shows raw terminal events
|
// BTRaw is a buffer that shows raw terminal events
|
||||||
BTRaw = BufType{4, false, true, false}
|
BTRaw = BufType{4, false, true, false}
|
||||||
// BTInfo is a buffer for inputting information
|
// BTInfo is a buffer for inputting information
|
||||||
BTInfo = BufType{5, false, true, false}
|
BTInfo = BufType{5, false, true, false}
|
||||||
|
@ -430,6 +430,15 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloseOpenBuffers removes all open buffers
|
||||||
|
func CloseOpenBuffers() {
|
||||||
|
for i, buf := range OpenBuffers {
|
||||||
|
buf.Fini()
|
||||||
|
OpenBuffers[i] = nil
|
||||||
|
}
|
||||||
|
OpenBuffers = OpenBuffers[:0]
|
||||||
|
}
|
||||||
|
|
||||||
// Close removes this buffer from the list of open buffers
|
// Close removes this buffer from the list of open buffers
|
||||||
func (b *Buffer) Close() {
|
func (b *Buffer) Close() {
|
||||||
for i, buf := range OpenBuffers {
|
for i, buf := range OpenBuffers {
|
||||||
|
@ -474,7 +483,7 @@ func (b *Buffer) GetName() string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
//SetName changes the name for this buffer
|
// SetName changes the name for this buffer
|
||||||
func (b *Buffer) SetName(s string) {
|
func (b *Buffer) SetName(s string) {
|
||||||
b.name = s
|
b.name = s
|
||||||
}
|
}
|
||||||
|
@ -559,6 +568,13 @@ func (b *Buffer) RelocateCursors() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeselectCursors removes selection from all cursors
|
||||||
|
func (b *Buffer) DeselectCursors() {
|
||||||
|
for _, c := range b.cursors {
|
||||||
|
c.Deselect(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RuneAt returns the rune at a given location in the buffer
|
// RuneAt returns the rune at a given location in the buffer
|
||||||
func (b *Buffer) RuneAt(loc Loc) rune {
|
func (b *Buffer) RuneAt(loc Loc) rune {
|
||||||
line := b.LineBytes(loc.Y)
|
line := b.LineBytes(loc.Y)
|
||||||
|
@ -666,112 +682,78 @@ func calcHash(b *Buffer, out *[md5.Size]byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRules updates the syntax rules and filetype for this buffer
|
func parseDefFromFile(f config.RuntimeFile, header *highlight.Header) *highlight.Def {
|
||||||
// This is called when the colorscheme changes
|
|
||||||
func (b *Buffer) UpdateRules() {
|
|
||||||
if !b.Type.Syntax {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ft := b.Settings["filetype"].(string)
|
|
||||||
if ft == "off" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
syntaxFile := ""
|
|
||||||
foundDef := false
|
|
||||||
var header *highlight.Header
|
|
||||||
// search for the syntax file in the user's custom syntax files
|
|
||||||
for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
|
|
||||||
data, err := f.Data()
|
data, err := f.Data()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
|
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
|
||||||
continue
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if header == nil {
|
||||||
header, err = highlight.MakeHeaderYaml(data)
|
header, err = highlight.MakeHeaderYaml(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage("Error parsing header for syntax file " + f.Name() + ": " + err.Error())
|
screen.TermMessage("Error parsing header for syntax file " + f.Name() + ": " + err.Error())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
file, err := highlight.ParseFile(data)
|
file, err := highlight.ParseFile(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
||||||
continue
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ft == "unknown" || ft == "") && highlight.MatchFiletype(header.FtDetect, b.Path, b.lines[0].data)) || header.FileType == ft {
|
|
||||||
syndef, err := highlight.ParseDef(file, header)
|
syndef, err := highlight.ParseDef(file, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
||||||
continue
|
return nil
|
||||||
}
|
|
||||||
b.SyntaxDef = syndef
|
|
||||||
syntaxFile = f.Name()
|
|
||||||
foundDef = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// search in the default syntax files
|
return syndef
|
||||||
for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) {
|
}
|
||||||
data, err := f.Data()
|
|
||||||
if err != nil {
|
|
||||||
screen.TermMessage("Error loading syntax header file " + f.Name() + ": " + err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
header, err = highlight.MakeHeader(data)
|
// findRealRuntimeSyntaxDef finds a specific syntax definition
|
||||||
if err != nil {
|
// in the user's custom syntax files
|
||||||
screen.TermMessage("Error reading syntax header file", f.Name(), err)
|
func findRealRuntimeSyntaxDef(name string, header *highlight.Header) *highlight.Def {
|
||||||
continue
|
for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
|
||||||
|
if f.Name() == name {
|
||||||
|
syndef := parseDefFromFile(f, header)
|
||||||
|
if syndef != nil {
|
||||||
|
return syndef
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if ft == "unknown" || ft == "" {
|
// findRuntimeSyntaxDef finds a specific syntax definition
|
||||||
if highlight.MatchFiletype(header.FtDetect, b.Path, b.lines[0].data) {
|
// in the built-in syntax files
|
||||||
syntaxFile = f.Name()
|
func findRuntimeSyntaxDef(name string, header *highlight.Header) *highlight.Def {
|
||||||
break
|
|
||||||
}
|
|
||||||
} else if header.FileType == ft {
|
|
||||||
syntaxFile = f.Name()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if syntaxFile != "" && !foundDef {
|
|
||||||
// we found a syntax file using a syntax header file
|
|
||||||
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
|
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
|
||||||
if f.Name() == syntaxFile {
|
if f.Name() == name {
|
||||||
data, err := f.Data()
|
syndef := parseDefFromFile(f, header)
|
||||||
if err != nil {
|
if syndef != nil {
|
||||||
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
|
return syndef
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
file, err := highlight.ParseFile(data)
|
func resolveIncludes(syndef *highlight.Def) {
|
||||||
if err != nil {
|
includes := highlight.GetIncludes(syndef)
|
||||||
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
if len(includes) == 0 {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
syndef, err := highlight.ParseDef(file, header)
|
|
||||||
if err != nil {
|
|
||||||
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
b.SyntaxDef = syndef
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.SyntaxDef != nil && highlight.HasIncludes(b.SyntaxDef) {
|
|
||||||
includes := highlight.GetIncludes(b.SyntaxDef)
|
|
||||||
|
|
||||||
var files []*highlight.File
|
var files []*highlight.File
|
||||||
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
|
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
|
||||||
data, err := f.Data()
|
data, err := f.Data()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
header, err := highlight.MakeHeaderYaml(data)
|
header, err := highlight.MakeHeaderYaml(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
||||||
|
@ -794,15 +776,193 @@ func (b *Buffer) UpdateRules() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
highlight.ResolveIncludes(b.SyntaxDef, files)
|
highlight.ResolveIncludes(syndef, files)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRules updates the syntax rules and filetype for this buffer
|
||||||
|
// This is called when the colorscheme changes
|
||||||
|
func (b *Buffer) UpdateRules() {
|
||||||
|
if !b.Type.Syntax {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ft := b.Settings["filetype"].(string)
|
||||||
|
if ft == "off" {
|
||||||
|
b.ClearMatches()
|
||||||
|
b.SyntaxDef = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.SyntaxDef = nil
|
||||||
|
|
||||||
|
// syntaxFileInfo is an internal helper structure
|
||||||
|
// to store properties of one single syntax file
|
||||||
|
type syntaxFileInfo struct {
|
||||||
|
header *highlight.Header
|
||||||
|
fileName string
|
||||||
|
syntaxDef *highlight.Def
|
||||||
|
}
|
||||||
|
|
||||||
|
fnameMatches := []syntaxFileInfo{}
|
||||||
|
headerMatches := []syntaxFileInfo{}
|
||||||
|
syntaxFile := ""
|
||||||
|
foundDef := false
|
||||||
|
var header *highlight.Header
|
||||||
|
// search for the syntax file in the user's custom syntax files
|
||||||
|
for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
|
||||||
|
if f.Name() == "default" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := f.Data()
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err = highlight.MakeHeaderYaml(data)
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage("Error parsing header for syntax file " + f.Name() + ": " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
matchedFileType := false
|
||||||
|
matchedFileName := false
|
||||||
|
matchedFileHeader := false
|
||||||
|
|
||||||
|
if ft == "unknown" || ft == "" {
|
||||||
|
if header.MatchFileName(b.Path) {
|
||||||
|
matchedFileName = true
|
||||||
|
}
|
||||||
|
if len(fnameMatches) == 0 && header.MatchFileHeader(b.lines[0].data) {
|
||||||
|
matchedFileHeader = true
|
||||||
|
}
|
||||||
|
} else if header.FileType == ft {
|
||||||
|
matchedFileType = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if matchedFileType || matchedFileName || matchedFileHeader {
|
||||||
|
file, err := highlight.ParseFile(data)
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
syndef, err := highlight.ParseDef(file, header)
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if matchedFileType {
|
||||||
|
b.SyntaxDef = syndef
|
||||||
|
syntaxFile = f.Name()
|
||||||
|
foundDef = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if matchedFileName {
|
||||||
|
fnameMatches = append(fnameMatches, syntaxFileInfo{header, f.Name(), syndef})
|
||||||
|
} else if matchedFileHeader {
|
||||||
|
headerMatches = append(headerMatches, syntaxFileInfo{header, f.Name(), syndef})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundDef {
|
||||||
|
// search for the syntax file in the built-in syntax files
|
||||||
|
for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) {
|
||||||
|
data, err := f.Data()
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage("Error loading syntax header file " + f.Name() + ": " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err = highlight.MakeHeader(data)
|
||||||
|
if err != nil {
|
||||||
|
screen.TermMessage("Error reading syntax header file", f.Name(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ft == "unknown" || ft == "" {
|
||||||
|
if header.MatchFileName(b.Path) {
|
||||||
|
fnameMatches = append(fnameMatches, syntaxFileInfo{header, f.Name(), nil})
|
||||||
|
}
|
||||||
|
if len(fnameMatches) == 0 && header.MatchFileHeader(b.lines[0].data) {
|
||||||
|
headerMatches = append(headerMatches, syntaxFileInfo{header, f.Name(), nil})
|
||||||
|
}
|
||||||
|
} else if header.FileType == ft {
|
||||||
|
syntaxFile = f.Name()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if syntaxFile == "" {
|
||||||
|
matches := fnameMatches
|
||||||
|
if len(matches) == 0 {
|
||||||
|
matches = headerMatches
|
||||||
|
}
|
||||||
|
|
||||||
|
length := len(matches)
|
||||||
|
if length > 0 {
|
||||||
|
signatureMatch := false
|
||||||
|
if length > 1 {
|
||||||
|
// multiple matching syntax files found, try to resolve the ambiguity
|
||||||
|
// using signatures
|
||||||
|
detectlimit := util.IntOpt(b.Settings["detectlimit"])
|
||||||
|
lineCount := len(b.lines)
|
||||||
|
limit := lineCount
|
||||||
|
if detectlimit > 0 && lineCount > detectlimit {
|
||||||
|
limit = detectlimit
|
||||||
|
}
|
||||||
|
|
||||||
|
matchLoop:
|
||||||
|
for _, m := range matches {
|
||||||
|
if m.header.HasFileSignature() {
|
||||||
|
for i := 0; i < limit; i++ {
|
||||||
|
if m.header.MatchFileSignature(b.lines[i].data) {
|
||||||
|
syntaxFile = m.fileName
|
||||||
|
if m.syntaxDef != nil {
|
||||||
|
b.SyntaxDef = m.syntaxDef
|
||||||
|
foundDef = true
|
||||||
|
}
|
||||||
|
header = m.header
|
||||||
|
signatureMatch = true
|
||||||
|
break matchLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if length == 1 || !signatureMatch {
|
||||||
|
syntaxFile = matches[0].fileName
|
||||||
|
if matches[0].syntaxDef != nil {
|
||||||
|
b.SyntaxDef = matches[0].syntaxDef
|
||||||
|
foundDef = true
|
||||||
|
}
|
||||||
|
header = matches[0].header
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if syntaxFile != "" && !foundDef {
|
||||||
|
// we found a syntax file using a syntax header file
|
||||||
|
b.SyntaxDef = findRuntimeSyntaxDef(syntaxFile, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.Highlighter == nil || syntaxFile != "" {
|
|
||||||
if b.SyntaxDef != nil {
|
if b.SyntaxDef != nil {
|
||||||
b.Settings["filetype"] = b.SyntaxDef.FileType
|
b.Settings["filetype"] = b.SyntaxDef.FileType
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
b.SyntaxDef = &highlight.EmptyDef
|
// search for the default file in the user's custom syntax files
|
||||||
|
b.SyntaxDef = findRealRuntimeSyntaxDef("default", nil)
|
||||||
|
if b.SyntaxDef == nil {
|
||||||
|
// search for the default file in the built-in syntax files
|
||||||
|
b.SyntaxDef = findRuntimeSyntaxDef("default", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.SyntaxDef != nil {
|
||||||
|
resolveIncludes(b.SyntaxDef)
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.SyntaxDef != nil {
|
if b.SyntaxDef != nil {
|
||||||
|
@ -904,7 +1064,7 @@ func (b *Buffer) MergeCursors() {
|
||||||
b.EventHandler.active = b.curCursor
|
b.EventHandler.active = b.curCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCursors updates all the cursors indicies
|
// UpdateCursors updates all the cursors indices
|
||||||
func (b *Buffer) UpdateCursors() {
|
func (b *Buffer) UpdateCursors() {
|
||||||
b.EventHandler.cursors = b.cursors
|
b.EventHandler.cursors = b.cursors
|
||||||
b.EventHandler.active = b.curCursor
|
b.EventHandler.active = b.curCursor
|
||||||
|
@ -980,34 +1140,14 @@ var BracePairs = [][2]rune{
|
||||||
{'[', ']'},
|
{'[', ']'},
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindMatchingBrace returns the location in the buffer of the matching bracket
|
func (b *Buffer) findMatchingBrace(braceType [2]rune, start Loc, char rune) (Loc, bool) {
|
||||||
// It is given a brace type containing the open and closing character, (for example
|
|
||||||
// '{' and '}') as well as the location to match from
|
|
||||||
// TODO: maybe can be more efficient with utf8 package
|
|
||||||
// returns the location of the matching brace
|
|
||||||
// if the boolean returned is true then the original matching brace is one character left
|
|
||||||
// of the starting location
|
|
||||||
func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool, bool) {
|
|
||||||
curLine := []rune(string(b.LineBytes(start.Y)))
|
|
||||||
startChar := ' '
|
|
||||||
if start.X >= 0 && start.X < len(curLine) {
|
|
||||||
startChar = curLine[start.X]
|
|
||||||
}
|
|
||||||
leftChar := ' '
|
|
||||||
if start.X-1 >= 0 && start.X-1 < len(curLine) {
|
|
||||||
leftChar = curLine[start.X-1]
|
|
||||||
}
|
|
||||||
var i int
|
var i int
|
||||||
if startChar == braceType[0] || leftChar == braceType[0] {
|
if char == braceType[0] {
|
||||||
for y := start.Y; y < b.LinesNum(); y++ {
|
for y := start.Y; y < b.LinesNum(); y++ {
|
||||||
l := []rune(string(b.LineBytes(y)))
|
l := []rune(string(b.LineBytes(y)))
|
||||||
xInit := 0
|
xInit := 0
|
||||||
if y == start.Y {
|
if y == start.Y {
|
||||||
if startChar == braceType[0] {
|
|
||||||
xInit = start.X
|
xInit = start.X
|
||||||
} else {
|
|
||||||
xInit = start.X - 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for x := xInit; x < len(l); x++ {
|
for x := xInit; x < len(l); x++ {
|
||||||
r := l[x]
|
r := l[x]
|
||||||
|
@ -1016,42 +1156,74 @@ func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool, boo
|
||||||
} else if r == braceType[1] {
|
} else if r == braceType[1] {
|
||||||
i--
|
i--
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
if startChar == braceType[0] {
|
return Loc{x, y}, true
|
||||||
return Loc{x, y}, false, true
|
|
||||||
}
|
|
||||||
return Loc{x, y}, true, true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if startChar == braceType[1] || leftChar == braceType[1] {
|
} else if char == braceType[1] {
|
||||||
for y := start.Y; y >= 0; y-- {
|
for y := start.Y; y >= 0; y-- {
|
||||||
l := []rune(string(b.lines[y].data))
|
l := []rune(string(b.lines[y].data))
|
||||||
xInit := len(l) - 1
|
xInit := len(l) - 1
|
||||||
if y == start.Y {
|
if y == start.Y {
|
||||||
if leftChar == braceType[1] {
|
|
||||||
xInit = start.X - 1
|
|
||||||
} else {
|
|
||||||
xInit = start.X
|
xInit = start.X
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for x := xInit; x >= 0; x-- {
|
for x := xInit; x >= 0; x-- {
|
||||||
r := l[x]
|
r := l[x]
|
||||||
if r == braceType[0] {
|
if r == braceType[1] {
|
||||||
|
i++
|
||||||
|
} else if r == braceType[0] {
|
||||||
i--
|
i--
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
if leftChar == braceType[1] {
|
return Loc{x, y}, true
|
||||||
return Loc{x, y}, true, true
|
|
||||||
}
|
|
||||||
return Loc{x, y}, false, true
|
|
||||||
}
|
|
||||||
} else if r == braceType[1] {
|
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return start, true, false
|
}
|
||||||
|
return start, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a brace character (for example '{' or ']') at the given start location,
|
||||||
|
// FindMatchingBrace returns the location of the matching brace for it (for example '}'
|
||||||
|
// or '['). The second returned value is true if there was no matching brace found
|
||||||
|
// for given starting location but it was found for the location one character left
|
||||||
|
// of it. The third returned value is true if the matching brace was found at all.
|
||||||
|
func (b *Buffer) FindMatchingBrace(start Loc) (Loc, bool, bool) {
|
||||||
|
// TODO: maybe can be more efficient with utf8 package
|
||||||
|
curLine := []rune(string(b.LineBytes(start.Y)))
|
||||||
|
|
||||||
|
// first try to find matching brace for the given location (it has higher priority)
|
||||||
|
if start.X >= 0 && start.X < len(curLine) {
|
||||||
|
startChar := curLine[start.X]
|
||||||
|
|
||||||
|
for _, bp := range BracePairs {
|
||||||
|
if startChar == bp[0] || startChar == bp[1] {
|
||||||
|
mb, found := b.findMatchingBrace(bp, start, startChar)
|
||||||
|
if found {
|
||||||
|
return mb, false, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// failed to find matching brace for the given location, so try to find matching
|
||||||
|
// brace for the location one character left of it
|
||||||
|
if start.X-1 >= 0 && start.X-1 < len(curLine) {
|
||||||
|
leftChar := curLine[start.X-1]
|
||||||
|
left := Loc{start.X - 1, start.Y}
|
||||||
|
|
||||||
|
for _, bp := range BracePairs {
|
||||||
|
if leftChar == bp[0] || leftChar == bp[1] {
|
||||||
|
mb, found := b.findMatchingBrace(bp, left, leftChar)
|
||||||
|
if found {
|
||||||
|
return mb, true, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return start, false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retab changes all tabs to spaces or vice versa
|
// Retab changes all tabs to spaces or vice versa
|
||||||
|
@ -1073,7 +1245,11 @@ func (b *Buffer) Retab() {
|
||||||
}
|
}
|
||||||
|
|
||||||
l = bytes.TrimLeft(l, " \t")
|
l = bytes.TrimLeft(l, " \t")
|
||||||
|
|
||||||
|
b.Lock()
|
||||||
b.lines[i].data = append(ws, l...)
|
b.lines[i].data = append(ws, l...)
|
||||||
|
b.Unlock()
|
||||||
|
|
||||||
b.MarkModified(i, i)
|
b.MarkModified(i, i)
|
||||||
dirty = true
|
dirty = true
|
||||||
}
|
}
|
||||||
|
@ -1116,7 +1292,7 @@ func (b *Buffer) Write(bytes []byte) (n int, err error) {
|
||||||
return len(bytes), nil
|
return len(bytes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Buffer) updateDiffSync() {
|
func (b *Buffer) updateDiff(synchronous bool) {
|
||||||
b.diffLock.Lock()
|
b.diffLock.Lock()
|
||||||
defer b.diffLock.Unlock()
|
defer b.diffLock.Unlock()
|
||||||
|
|
||||||
|
@ -1127,7 +1303,16 @@ func (b *Buffer) updateDiffSync() {
|
||||||
}
|
}
|
||||||
|
|
||||||
differ := dmp.New()
|
differ := dmp.New()
|
||||||
baseRunes, bufferRunes, _ := differ.DiffLinesToRunes(string(b.diffBase), string(b.Bytes()))
|
|
||||||
|
if !synchronous {
|
||||||
|
b.Lock()
|
||||||
|
}
|
||||||
|
bytes := b.Bytes()
|
||||||
|
if !synchronous {
|
||||||
|
b.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
baseRunes, bufferRunes, _ := differ.DiffLinesToRunes(string(b.diffBase), string(bytes))
|
||||||
diffs := differ.DiffMainRunes(baseRunes, bufferRunes, false)
|
diffs := differ.DiffMainRunes(baseRunes, bufferRunes, false)
|
||||||
lineN := 0
|
lineN := 0
|
||||||
|
|
||||||
|
@ -1156,13 +1341,9 @@ func (b *Buffer) updateDiffSync() {
|
||||||
|
|
||||||
// UpdateDiff computes the diff between the diff base and the buffer content.
|
// UpdateDiff computes the diff between the diff base and the buffer content.
|
||||||
// The update may be performed synchronously or asynchronously.
|
// The update may be performed synchronously or asynchronously.
|
||||||
// UpdateDiff calls the supplied callback when the update is complete.
|
|
||||||
// The argument passed to the callback is set to true if and only if
|
|
||||||
// the update was performed synchronously.
|
|
||||||
// If an asynchronous update is already pending when UpdateDiff is called,
|
// If an asynchronous update is already pending when UpdateDiff is called,
|
||||||
// UpdateDiff does not schedule another update, in which case the callback
|
// UpdateDiff does not schedule another update.
|
||||||
// is not called.
|
func (b *Buffer) UpdateDiff() {
|
||||||
func (b *Buffer) UpdateDiff(callback func(bool)) {
|
|
||||||
if b.updateDiffTimer != nil {
|
if b.updateDiffTimer != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1173,20 +1354,18 @@ func (b *Buffer) UpdateDiff(callback func(bool)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if lineCount < 1000 {
|
if lineCount < 1000 {
|
||||||
b.updateDiffSync()
|
b.updateDiff(true)
|
||||||
callback(true)
|
|
||||||
} else if lineCount < 30000 {
|
} else if lineCount < 30000 {
|
||||||
b.updateDiffTimer = time.AfterFunc(500*time.Millisecond, func() {
|
b.updateDiffTimer = time.AfterFunc(500*time.Millisecond, func() {
|
||||||
b.updateDiffTimer = nil
|
b.updateDiffTimer = nil
|
||||||
b.updateDiffSync()
|
b.updateDiff(false)
|
||||||
callback(false)
|
screen.Redraw()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Don't compute diffs for very large files
|
// Don't compute diffs for very large files
|
||||||
b.diffLock.Lock()
|
b.diffLock.Lock()
|
||||||
b.diff = make(map[int]DiffStatus)
|
b.diff = make(map[int]DiffStatus)
|
||||||
b.diffLock.Unlock()
|
b.diffLock.Unlock()
|
||||||
callback(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1198,9 +1377,7 @@ func (b *Buffer) SetDiffBase(diffBase []byte) {
|
||||||
} else {
|
} else {
|
||||||
b.diffBaseLineCount = strings.Count(string(diffBase), "\n")
|
b.diffBaseLineCount = strings.Count(string(diffBase), "\n")
|
||||||
}
|
}
|
||||||
b.UpdateDiff(func(synchronous bool) {
|
b.UpdateDiff()
|
||||||
screen.Redraw()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiffStatus returns the diff status for a line in the buffer
|
// DiffStatus returns the diff status for a line in the buffer
|
||||||
|
@ -1211,6 +1388,41 @@ func (b *Buffer) DiffStatus(lineN int) DiffStatus {
|
||||||
return b.diff[lineN]
|
return b.diff[lineN]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindNextDiffLine returns the line number of the next block of diffs.
|
||||||
|
// If `startLine` is already in a block of diffs, lines in that block are skipped.
|
||||||
|
func (b *Buffer) FindNextDiffLine(startLine int, forward bool) (int, error) {
|
||||||
|
if b.diff == nil {
|
||||||
|
return 0, errors.New("no diff data")
|
||||||
|
}
|
||||||
|
startStatus, ok := b.diff[startLine]
|
||||||
|
if !ok {
|
||||||
|
startStatus = DSUnchanged
|
||||||
|
}
|
||||||
|
curLine := startLine
|
||||||
|
for {
|
||||||
|
curStatus, ok := b.diff[curLine]
|
||||||
|
if !ok {
|
||||||
|
curStatus = DSUnchanged
|
||||||
|
}
|
||||||
|
if curLine < 0 || curLine > b.LinesNum() {
|
||||||
|
return 0, errors.New("no next diff hunk")
|
||||||
|
}
|
||||||
|
if curStatus != startStatus {
|
||||||
|
if startStatus != DSUnchanged && curStatus == DSUnchanged {
|
||||||
|
// Skip over the block of unchanged text
|
||||||
|
startStatus = DSUnchanged
|
||||||
|
} else {
|
||||||
|
return curLine, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if forward {
|
||||||
|
curLine++
|
||||||
|
} else {
|
||||||
|
curLine--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SearchMatch returns true if the given location is within a match of the last search.
|
// SearchMatch returns true if the given location is within a match of the last search.
|
||||||
// It is used for search highlighting
|
// It is used for search highlighting
|
||||||
func (b *Buffer) SearchMatch(pos Loc) bool {
|
func (b *Buffer) SearchMatch(pos Loc) bool {
|
||||||
|
|
|
@ -20,6 +20,7 @@ type operation struct {
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ulua.L = lua.NewState()
|
ulua.L = lua.NewState()
|
||||||
|
config.InitRuntimeFiles(false)
|
||||||
config.InitGlobalSettings()
|
config.InitGlobalSettings()
|
||||||
config.GlobalSettings["backup"] = false
|
config.GlobalSettings["backup"] = false
|
||||||
config.GlobalSettings["fastdirty"] = true
|
config.GlobalSettings["fastdirty"] = true
|
||||||
|
|
|
@ -30,6 +30,11 @@ type Cursor struct {
|
||||||
// to know what the original selection was
|
// to know what the original selection was
|
||||||
OrigSelection [2]Loc
|
OrigSelection [2]Loc
|
||||||
|
|
||||||
|
// The line number where a new trailing whitespace has been added
|
||||||
|
// or -1 if there is no new trailing whitespace at this cursor.
|
||||||
|
// This is used for checking if a trailing whitespace should be highlighted
|
||||||
|
NewTrailingWsY int
|
||||||
|
|
||||||
// Which cursor index is this (for multiple cursors)
|
// Which cursor index is this (for multiple cursors)
|
||||||
Num int
|
Num int
|
||||||
}
|
}
|
||||||
|
@ -38,6 +43,8 @@ func NewCursor(b *Buffer, l Loc) *Cursor {
|
||||||
c := &Cursor{
|
c := &Cursor{
|
||||||
buf: b,
|
buf: b,
|
||||||
Loc: l,
|
Loc: l,
|
||||||
|
|
||||||
|
NewTrailingWsY: -1,
|
||||||
}
|
}
|
||||||
c.StoreVisualX()
|
c.StoreVisualX()
|
||||||
return c
|
return c
|
||||||
|
@ -396,13 +403,26 @@ func (c *Cursor) SelectTo(loc Loc) {
|
||||||
|
|
||||||
// WordRight moves the cursor one word to the right
|
// WordRight moves the cursor one word to the right
|
||||||
func (c *Cursor) WordRight() {
|
func (c *Cursor) WordRight() {
|
||||||
for util.IsWhitespace(c.RuneUnder(c.X)) {
|
|
||||||
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
||||||
c.Right()
|
c.Right()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
for util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
c.Right()
|
c.Right()
|
||||||
}
|
}
|
||||||
|
if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) &&
|
||||||
|
util.IsNonWordChar(c.RuneUnder(c.X+1)) {
|
||||||
|
for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Right()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
c.Right()
|
c.Right()
|
||||||
for util.IsWordChar(c.RuneUnder(c.X)) {
|
for util.IsWordChar(c.RuneUnder(c.X)) {
|
||||||
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
||||||
|
@ -414,6 +434,10 @@ func (c *Cursor) WordRight() {
|
||||||
|
|
||||||
// WordLeft moves the cursor one word to the left
|
// WordLeft moves the cursor one word to the left
|
||||||
func (c *Cursor) WordLeft() {
|
func (c *Cursor) WordLeft() {
|
||||||
|
if c.X == 0 {
|
||||||
|
c.Left()
|
||||||
|
return
|
||||||
|
}
|
||||||
c.Left()
|
c.Left()
|
||||||
for util.IsWhitespace(c.RuneUnder(c.X)) {
|
for util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
if c.X == 0 {
|
if c.X == 0 {
|
||||||
|
@ -421,6 +445,17 @@ func (c *Cursor) WordLeft() {
|
||||||
}
|
}
|
||||||
c.Left()
|
c.Left()
|
||||||
}
|
}
|
||||||
|
if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) &&
|
||||||
|
util.IsNonWordChar(c.RuneUnder(c.X-1)) {
|
||||||
|
for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Left()
|
||||||
|
}
|
||||||
|
c.Right()
|
||||||
|
return
|
||||||
|
}
|
||||||
c.Left()
|
c.Left()
|
||||||
for util.IsWordChar(c.RuneUnder(c.X)) {
|
for util.IsWordChar(c.RuneUnder(c.X)) {
|
||||||
if c.X == 0 {
|
if c.X == 0 {
|
||||||
|
@ -431,6 +466,132 @@ func (c *Cursor) WordLeft() {
|
||||||
c.Right()
|
c.Right()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubWordRight moves the cursor one sub-word to the right
|
||||||
|
func (c *Cursor) SubWordRight() {
|
||||||
|
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
||||||
|
c.Right()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
for util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Right()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Right()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
|
||||||
|
for util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Right()
|
||||||
|
}
|
||||||
|
if util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if util.IsUpperLetter(c.RuneUnder(c.X)) &&
|
||||||
|
util.IsUpperLetter(c.RuneUnder(c.X+1)) {
|
||||||
|
for util.IsUpperAlphanumeric(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Right()
|
||||||
|
}
|
||||||
|
if util.IsLowerAlphanumeric(c.RuneUnder(c.X)) {
|
||||||
|
c.Left()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.Right()
|
||||||
|
for util.IsLowerAlphanumeric(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Right()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubWordLeft moves the cursor one sub-word to the left
|
||||||
|
func (c *Cursor) SubWordLeft() {
|
||||||
|
if c.X == 0 {
|
||||||
|
c.Left()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Left()
|
||||||
|
if util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
for util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Left()
|
||||||
|
}
|
||||||
|
c.Right()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Left()
|
||||||
|
}
|
||||||
|
c.Right()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
|
||||||
|
for util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Left()
|
||||||
|
}
|
||||||
|
if util.IsWhitespace(c.RuneUnder(c.X)) {
|
||||||
|
c.Right()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.X == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if util.IsUpperLetter(c.RuneUnder(c.X)) &&
|
||||||
|
util.IsUpperLetter(c.RuneUnder(c.X-1)) {
|
||||||
|
for util.IsUpperAlphanumeric(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Left()
|
||||||
|
}
|
||||||
|
if !util.IsUpperAlphanumeric(c.RuneUnder(c.X)) {
|
||||||
|
c.Right()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for util.IsLowerAlphanumeric(c.RuneUnder(c.X)) {
|
||||||
|
if c.X == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Left()
|
||||||
|
}
|
||||||
|
if !util.IsAlphanumeric(c.RuneUnder(c.X)) {
|
||||||
|
c.Right()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RuneUnder returns the rune under the given x position
|
// RuneUnder returns the rune under the given x position
|
||||||
func (c *Cursor) RuneUnder(x int) rune {
|
func (c *Cursor) RuneUnder(x int) rune {
|
||||||
line := c.buf.LineBytes(c.Y)
|
line := c.buf.LineBytes(c.Y)
|
||||||
|
|
|
@ -106,6 +106,10 @@ func (eh *EventHandler) DoTextEvent(t *TextEvent, useUndo bool) {
|
||||||
c.Relocate()
|
c.Relocate()
|
||||||
c.LastVisualX = c.GetVisualX()
|
c.LastVisualX = c.GetVisualX()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if useUndo {
|
||||||
|
eh.updateTrailingWs(t)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecuteTextEvent runs a text event
|
// ExecuteTextEvent runs a text event
|
||||||
|
@ -237,7 +241,7 @@ func (eh *EventHandler) Execute(t *TextEvent) {
|
||||||
}
|
}
|
||||||
eh.UndoStack.Push(t)
|
eh.UndoStack.Push(t)
|
||||||
|
|
||||||
b, err := config.RunPluginFnBool("onBeforeTextEvent", luar.New(ulua.L, eh.buf), luar.New(ulua.L, t))
|
b, err := config.RunPluginFnBool(nil, "onBeforeTextEvent", luar.New(ulua.L, eh.buf), luar.New(ulua.L, t))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage(err)
|
screen.TermMessage(err)
|
||||||
}
|
}
|
||||||
|
@ -290,6 +294,7 @@ func (eh *EventHandler) UndoOneEvent() {
|
||||||
if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
|
if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
|
||||||
t.C = *eh.cursors[teCursor.Num]
|
t.C = *eh.cursors[teCursor.Num]
|
||||||
eh.cursors[teCursor.Num].Goto(teCursor)
|
eh.cursors[teCursor.Num].Goto(teCursor)
|
||||||
|
eh.cursors[teCursor.Num].NewTrailingWsY = teCursor.NewTrailingWsY
|
||||||
} else {
|
} else {
|
||||||
teCursor.Num = -1
|
teCursor.Num = -1
|
||||||
}
|
}
|
||||||
|
@ -333,6 +338,7 @@ func (eh *EventHandler) RedoOneEvent() {
|
||||||
if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
|
if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
|
||||||
t.C = *eh.cursors[teCursor.Num]
|
t.C = *eh.cursors[teCursor.Num]
|
||||||
eh.cursors[teCursor.Num].Goto(teCursor)
|
eh.cursors[teCursor.Num].Goto(teCursor)
|
||||||
|
eh.cursors[teCursor.Num].NewTrailingWsY = teCursor.NewTrailingWsY
|
||||||
} else {
|
} else {
|
||||||
teCursor.Num = -1
|
teCursor.Num = -1
|
||||||
}
|
}
|
||||||
|
@ -342,3 +348,58 @@ func (eh *EventHandler) RedoOneEvent() {
|
||||||
|
|
||||||
eh.UndoStack.Push(t)
|
eh.UndoStack.Push(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateTrailingWs updates the cursor's trailing whitespace status after a text event
|
||||||
|
func (eh *EventHandler) updateTrailingWs(t *TextEvent) {
|
||||||
|
if len(t.Deltas) != 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text := t.Deltas[0].Text
|
||||||
|
start := t.Deltas[0].Start
|
||||||
|
end := t.Deltas[0].End
|
||||||
|
|
||||||
|
c := eh.cursors[eh.active]
|
||||||
|
isEol := func(loc Loc) bool {
|
||||||
|
return loc.X == util.CharacterCount(eh.buf.LineBytes(loc.Y))
|
||||||
|
}
|
||||||
|
if t.EventType == TextEventInsert && c.Loc == end && isEol(end) {
|
||||||
|
var addedTrailingWs bool
|
||||||
|
addedAfterWs := false
|
||||||
|
addedWsOnly := false
|
||||||
|
if start.Y == end.Y {
|
||||||
|
addedTrailingWs = util.HasTrailingWhitespace(text)
|
||||||
|
addedWsOnly = util.IsBytesWhitespace(text)
|
||||||
|
addedAfterWs = start.X > 0 && util.IsWhitespace(c.buf.RuneAt(Loc{start.X - 1, start.Y}))
|
||||||
|
} else {
|
||||||
|
lastnl := bytes.LastIndex(text, []byte{'\n'})
|
||||||
|
addedTrailingWs = util.HasTrailingWhitespace(text[lastnl+1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
if addedTrailingWs && !(addedAfterWs && addedWsOnly) {
|
||||||
|
c.NewTrailingWsY = c.Y
|
||||||
|
} else if !addedTrailingWs {
|
||||||
|
c.NewTrailingWsY = -1
|
||||||
|
}
|
||||||
|
} else if t.EventType == TextEventRemove && c.Loc == start && isEol(start) {
|
||||||
|
removedAfterWs := util.HasTrailingWhitespace(eh.buf.LineBytes(start.Y))
|
||||||
|
var removedWsOnly bool
|
||||||
|
if start.Y == end.Y {
|
||||||
|
removedWsOnly = util.IsBytesWhitespace(text)
|
||||||
|
} else {
|
||||||
|
firstnl := bytes.Index(text, []byte{'\n'})
|
||||||
|
removedWsOnly = util.IsBytesWhitespace(text[:firstnl])
|
||||||
|
}
|
||||||
|
|
||||||
|
if removedAfterWs && !removedWsOnly {
|
||||||
|
c.NewTrailingWsY = c.Y
|
||||||
|
} else if !removedAfterWs {
|
||||||
|
c.NewTrailingWsY = -1
|
||||||
|
}
|
||||||
|
} else if c.NewTrailingWsY != -1 && start.Y != end.Y && c.Loc.GreaterThan(start) &&
|
||||||
|
((t.EventType == TextEventInsert && c.Y == c.NewTrailingWsY+(end.Y-start.Y)) ||
|
||||||
|
(t.EventType == TextEventRemove && c.Y == c.NewTrailingWsY-(end.Y-start.Y))) {
|
||||||
|
// The cursor still has its new trailingws
|
||||||
|
// but its line number was shifted by insert or remove of lines above
|
||||||
|
c.NewTrailingWsY = c.Y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -48,7 +48,6 @@ type Line struct {
|
||||||
|
|
||||||
state highlight.State
|
state highlight.State
|
||||||
match highlight.LineMatch
|
match highlight.LineMatch
|
||||||
rehighlight bool
|
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
|
|
||||||
// The search states for the line, used for highlighting of search matches,
|
// The search states for the line, used for highlighting of search matches,
|
||||||
|
@ -75,6 +74,7 @@ type LineArray struct {
|
||||||
lines []Line
|
lines []Line
|
||||||
Endings FileFormat
|
Endings FileFormat
|
||||||
initsize uint64
|
initsize uint64
|
||||||
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append efficiently appends lines together
|
// Append efficiently appends lines together
|
||||||
|
@ -116,12 +116,12 @@ func NewLineArray(size uint64, endings FileFormat, reader io.Reader) *LineArray
|
||||||
dlen := len(data)
|
dlen := len(data)
|
||||||
if dlen > 1 && data[dlen-2] == '\r' {
|
if dlen > 1 && data[dlen-2] == '\r' {
|
||||||
data = append(data[:dlen-2], '\n')
|
data = append(data[:dlen-2], '\n')
|
||||||
if endings == FFAuto {
|
if la.Endings == FFAuto {
|
||||||
la.Endings = FFDos
|
la.Endings = FFDos
|
||||||
}
|
}
|
||||||
dlen = len(data)
|
dlen = len(data)
|
||||||
} else if dlen > 0 {
|
} else if dlen > 0 {
|
||||||
if endings == FFAuto {
|
if la.Endings == FFAuto {
|
||||||
la.Endings = FFUnix
|
la.Endings = FFUnix
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,6 @@ func NewLineArray(size uint64, endings FileFormat, reader io.Reader) *LineArray
|
||||||
data: data,
|
data: data,
|
||||||
state: nil,
|
state: nil,
|
||||||
match: nil,
|
match: nil,
|
||||||
rehighlight: false,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Last line was read
|
// Last line was read
|
||||||
|
@ -160,7 +159,6 @@ func NewLineArray(size uint64, endings FileFormat, reader io.Reader) *LineArray
|
||||||
data: data[:dlen-1],
|
data: data[:dlen-1],
|
||||||
state: nil,
|
state: nil,
|
||||||
match: nil,
|
match: nil,
|
||||||
rehighlight: false,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
n++
|
n++
|
||||||
|
@ -193,19 +191,20 @@ func (la *LineArray) newlineBelow(y int) {
|
||||||
data: []byte{' '},
|
data: []byte{' '},
|
||||||
state: nil,
|
state: nil,
|
||||||
match: nil,
|
match: nil,
|
||||||
rehighlight: false,
|
|
||||||
})
|
})
|
||||||
copy(la.lines[y+2:], la.lines[y+1:])
|
copy(la.lines[y+2:], la.lines[y+1:])
|
||||||
la.lines[y+1] = Line{
|
la.lines[y+1] = Line{
|
||||||
data: []byte{},
|
data: []byte{},
|
||||||
state: la.lines[y].state,
|
state: la.lines[y].state,
|
||||||
match: nil,
|
match: nil,
|
||||||
rehighlight: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inserts a byte array at a given location
|
// Inserts a byte array at a given location
|
||||||
func (la *LineArray) insert(pos Loc, value []byte) {
|
func (la *LineArray) insert(pos Loc, value []byte) {
|
||||||
|
la.lock.Lock()
|
||||||
|
defer la.lock.Unlock()
|
||||||
|
|
||||||
x, y := runeToByteIndex(pos.X, la.lines[pos.Y].data), pos.Y
|
x, y := runeToByteIndex(pos.X, la.lines[pos.Y].data), pos.Y
|
||||||
for i := 0; i < len(value); i++ {
|
for i := 0; i < len(value); i++ {
|
||||||
if value[i] == '\n' || (value[i] == '\r' && i < len(value)-1 && value[i+1] == '\n') {
|
if value[i] == '\n' || (value[i] == '\r' && i < len(value)-1 && value[i+1] == '\n') {
|
||||||
|
@ -233,24 +232,26 @@ func (la *LineArray) insertByte(pos Loc, value byte) {
|
||||||
|
|
||||||
// joinLines joins the two lines a and b
|
// joinLines joins the two lines a and b
|
||||||
func (la *LineArray) joinLines(a, b int) {
|
func (la *LineArray) joinLines(a, b int) {
|
||||||
la.insert(Loc{len(la.lines[a].data), a}, la.lines[b].data)
|
la.lines[a].data = append(la.lines[a].data, la.lines[b].data...)
|
||||||
la.deleteLine(b)
|
la.deleteLine(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// split splits a line at a given position
|
// split splits a line at a given position
|
||||||
func (la *LineArray) split(pos Loc) {
|
func (la *LineArray) split(pos Loc) {
|
||||||
la.newlineBelow(pos.Y)
|
la.newlineBelow(pos.Y)
|
||||||
la.insert(Loc{0, pos.Y + 1}, la.lines[pos.Y].data[pos.X:])
|
la.lines[pos.Y+1].data = append(la.lines[pos.Y+1].data, la.lines[pos.Y].data[pos.X:]...)
|
||||||
la.lines[pos.Y+1].state = la.lines[pos.Y].state
|
la.lines[pos.Y+1].state = la.lines[pos.Y].state
|
||||||
la.lines[pos.Y].state = nil
|
la.lines[pos.Y].state = nil
|
||||||
la.lines[pos.Y].match = nil
|
la.lines[pos.Y].match = nil
|
||||||
la.lines[pos.Y+1].match = nil
|
la.lines[pos.Y+1].match = nil
|
||||||
la.lines[pos.Y].rehighlight = true
|
|
||||||
la.deleteToEnd(Loc{pos.X, pos.Y})
|
la.deleteToEnd(Loc{pos.X, pos.Y})
|
||||||
}
|
}
|
||||||
|
|
||||||
// removes from start to end
|
// removes from start to end
|
||||||
func (la *LineArray) remove(start, end Loc) []byte {
|
func (la *LineArray) remove(start, end Loc) []byte {
|
||||||
|
la.lock.Lock()
|
||||||
|
defer la.lock.Unlock()
|
||||||
|
|
||||||
sub := la.Substr(start, end)
|
sub := la.Substr(start, end)
|
||||||
startX := runeToByteIndex(start.X, la.lines[start.Y].data)
|
startX := runeToByteIndex(start.X, la.lines[start.Y].data)
|
||||||
endX := runeToByteIndex(end.X, la.lines[end.Y].data)
|
endX := runeToByteIndex(end.X, la.lines[end.Y].data)
|
||||||
|
@ -327,11 +328,11 @@ func (la *LineArray) End() Loc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LineBytes returns line n as an array of bytes
|
// LineBytes returns line n as an array of bytes
|
||||||
func (la *LineArray) LineBytes(n int) []byte {
|
func (la *LineArray) LineBytes(lineN int) []byte {
|
||||||
if n >= len(la.lines) || n < 0 {
|
if lineN >= len(la.lines) || lineN < 0 {
|
||||||
return []byte{}
|
return []byte{}
|
||||||
}
|
}
|
||||||
return la.lines[n].data
|
return la.lines[lineN].data
|
||||||
}
|
}
|
||||||
|
|
||||||
// State gets the highlight state for the given line number
|
// State gets the highlight state for the given line number
|
||||||
|
@ -362,16 +363,14 @@ func (la *LineArray) Match(lineN int) highlight.LineMatch {
|
||||||
return la.lines[lineN].match
|
return la.lines[lineN].match
|
||||||
}
|
}
|
||||||
|
|
||||||
func (la *LineArray) Rehighlight(lineN int) bool {
|
// Locks the whole LineArray
|
||||||
la.lines[lineN].lock.Lock()
|
func (la *LineArray) Lock() {
|
||||||
defer la.lines[lineN].lock.Unlock()
|
la.lock.Lock()
|
||||||
return la.lines[lineN].rehighlight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (la *LineArray) SetRehighlight(lineN int, on bool) {
|
// Unlocks the whole LineArray
|
||||||
la.lines[lineN].lock.Lock()
|
func (la *LineArray) Unlock() {
|
||||||
defer la.lines[lineN].lock.Unlock()
|
la.lock.Unlock()
|
||||||
la.lines[lineN].rehighlight = on
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchMatch returns true if the location `pos` is within a match
|
// SearchMatch returns true if the location `pos` is within a match
|
||||||
|
|
|
@ -51,27 +51,38 @@ func overwriteFile(name string, enc encoding.Encoding, fn func(io.Writer) error,
|
||||||
// contents to its stdin it might hang because the kernel's pipe size
|
// contents to its stdin it might hang because the kernel's pipe size
|
||||||
// is too small to handle the full file contents all at once
|
// is too small to handle the full file contents all at once
|
||||||
if e := cmd.Start(); e != nil && err == nil {
|
if e := cmd.Start(); e != nil && err == nil {
|
||||||
|
screen.TempStart(screenb)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if writeCloser, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
|
} else if writeCloser, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w := bufio.NewWriter(transform.NewWriter(writeCloser, enc.NewEncoder()))
|
w := bufio.NewWriter(transform.NewWriter(writeCloser, enc.NewEncoder()))
|
||||||
err = fn(w)
|
err = fn(w)
|
||||||
w.Flush()
|
|
||||||
|
|
||||||
if e := writeCloser.Close(); e != nil && err == nil {
|
if err2 := w.Flush(); err2 != nil && err == nil {
|
||||||
err = e
|
err = err2
|
||||||
|
}
|
||||||
|
// Call Sync() on the file to make sure the content is safely on disk.
|
||||||
|
// Does not work with sudo as we don't have direct access to the file.
|
||||||
|
if !withSudo {
|
||||||
|
f := writeCloser.(*os.File)
|
||||||
|
if err2 := f.Sync(); err2 != nil && err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err2 := writeCloser.Close(); err2 != nil && err == nil {
|
||||||
|
err = err2
|
||||||
}
|
}
|
||||||
|
|
||||||
if withSudo {
|
if withSudo {
|
||||||
// wait for dd to finish and restart the screen if we used sudo
|
// wait for dd to finish and restart the screen if we used sudo
|
||||||
err := cmd.Wait()
|
err := cmd.Wait()
|
||||||
|
screen.TempStart(screenb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
screen.TempStart(screenb)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -82,9 +93,14 @@ func (b *Buffer) Save() error {
|
||||||
return b.SaveAs(b.Path)
|
return b.SaveAs(b.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AutoSave saves the buffer to its default path
|
||||||
|
func (b *Buffer) AutoSave() error {
|
||||||
|
return b.saveToFile(b.Path, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
// SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
|
// SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
|
||||||
func (b *Buffer) SaveAs(filename string) error {
|
func (b *Buffer) SaveAs(filename string) error {
|
||||||
return b.saveToFile(filename, false)
|
return b.saveToFile(filename, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Buffer) SaveWithSudo() error {
|
func (b *Buffer) SaveWithSudo() error {
|
||||||
|
@ -92,10 +108,10 @@ func (b *Buffer) SaveWithSudo() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Buffer) SaveAsWithSudo(filename string) error {
|
func (b *Buffer) SaveAsWithSudo(filename string) error {
|
||||||
return b.saveToFile(filename, true)
|
return b.saveToFile(filename, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Buffer) saveToFile(filename string, withSudo bool) error {
|
func (b *Buffer) saveToFile(filename string, withSudo bool, autoSave bool) error {
|
||||||
var err error
|
var err error
|
||||||
if b.Type.Readonly {
|
if b.Type.Readonly {
|
||||||
return errors.New("Cannot save readonly buffer")
|
return errors.New("Cannot save readonly buffer")
|
||||||
|
@ -107,7 +123,7 @@ func (b *Buffer) saveToFile(filename string, withSudo bool) error {
|
||||||
return errors.New("Save with sudo not supported on Windows")
|
return errors.New("Save with sudo not supported on Windows")
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.Settings["rmtrailingws"].(bool) {
|
if !autoSave && b.Settings["rmtrailingws"].(bool) {
|
||||||
for i, l := range b.lines {
|
for i, l := range b.lines {
|
||||||
leftover := util.CharacterCount(bytes.TrimRightFunc(l.data, unicode.IsSpace))
|
leftover := util.CharacterCount(bytes.TrimRightFunc(l.data, unicode.IsSpace))
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ func (b *Buffer) FindNext(s string, start, end, from Loc, down bool, useRegex bo
|
||||||
// ReplaceRegex replaces all occurrences of 'search' with 'replace' in the given area
|
// ReplaceRegex replaces all occurrences of 'search' with 'replace' in the given area
|
||||||
// and returns the number of replacements made and the number of runes
|
// and returns the number of replacements made and the number of runes
|
||||||
// added or removed on the last line of the range
|
// added or removed on the last line of the range
|
||||||
func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []byte) (int, int) {
|
func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []byte, captureGroups bool) (int, int) {
|
||||||
if start.GreaterThan(end) {
|
if start.GreaterThan(end) {
|
||||||
start, end = end, start
|
start, end = end, start
|
||||||
}
|
}
|
||||||
|
@ -172,10 +172,14 @@ func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []b
|
||||||
l = util.SliceStart(l, end.X)
|
l = util.SliceStart(l, end.X)
|
||||||
}
|
}
|
||||||
newText := search.ReplaceAllFunc(l, func(in []byte) []byte {
|
newText := search.ReplaceAllFunc(l, func(in []byte) []byte {
|
||||||
result := []byte{}
|
var result []byte
|
||||||
|
if captureGroups {
|
||||||
for _, submatches := range search.FindAllSubmatchIndex(in, -1) {
|
for _, submatches := range search.FindAllSubmatchIndex(in, -1) {
|
||||||
result = search.Expand(result, replace, in, submatches)
|
result = search.Expand(result, replace, in, submatches)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
result = replace
|
||||||
|
}
|
||||||
found++
|
found++
|
||||||
if i == end.Y {
|
if i == end.Y {
|
||||||
netrunes += util.CharacterCount(result) - util.CharacterCount(in)
|
netrunes += util.CharacterCount(result) - util.CharacterCount(in)
|
||||||
|
|
|
@ -20,7 +20,7 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error {
|
||||||
} else if option == "statusline" {
|
} else if option == "statusline" {
|
||||||
screen.Redraw()
|
screen.Redraw()
|
||||||
} else if option == "filetype" {
|
} else if option == "filetype" {
|
||||||
config.InitRuntimeFiles()
|
config.InitRuntimeFiles(true)
|
||||||
err := config.ReadSettings()
|
err := config.ReadSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
screen.TermMessage(err)
|
screen.TermMessage(err)
|
||||||
|
@ -55,6 +55,25 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error {
|
||||||
buf.HighlightSearch = nativeValue.(bool)
|
buf.HighlightSearch = nativeValue.(bool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for _, pl := range config.Plugins {
|
||||||
|
if option == pl.Name {
|
||||||
|
if nativeValue.(bool) {
|
||||||
|
if !pl.Loaded {
|
||||||
|
pl.Load()
|
||||||
|
}
|
||||||
|
_, err := pl.Call("init")
|
||||||
|
if err != nil && err != config.ErrNoSuchFunction {
|
||||||
|
screen.TermMessage(err)
|
||||||
|
}
|
||||||
|
} else if !nativeValue.(bool) && pl.Loaded {
|
||||||
|
_, err := pl.Call("deinit")
|
||||||
|
if err != nil && err != config.ErrNoSuchFunction {
|
||||||
|
screen.TermMessage(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.OptionCallback != nil {
|
if b.OptionCallback != nil {
|
||||||
|
|
|
@ -52,43 +52,55 @@ func InitColorscheme() error {
|
||||||
Colorscheme = make(map[string]tcell.Style)
|
Colorscheme = make(map[string]tcell.Style)
|
||||||
DefStyle = tcell.StyleDefault
|
DefStyle = tcell.StyleDefault
|
||||||
|
|
||||||
return LoadDefaultColorscheme()
|
c, err := LoadDefaultColorscheme()
|
||||||
|
if err == nil {
|
||||||
|
Colorscheme = c
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadDefaultColorscheme loads the default colorscheme from $(ConfigDir)/colorschemes
|
// LoadDefaultColorscheme loads the default colorscheme from $(ConfigDir)/colorschemes
|
||||||
func LoadDefaultColorscheme() error {
|
func LoadDefaultColorscheme() (map[string]tcell.Style, error) {
|
||||||
return LoadColorscheme(GlobalSettings["colorscheme"].(string))
|
var parsedColorschemes []string
|
||||||
|
return LoadColorscheme(GlobalSettings["colorscheme"].(string), &parsedColorschemes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadColorscheme loads the given colorscheme from a directory
|
// LoadColorscheme loads the given colorscheme from a directory
|
||||||
func LoadColorscheme(colorschemeName string) error {
|
func LoadColorscheme(colorschemeName string, parsedColorschemes *[]string) (map[string]tcell.Style, error) {
|
||||||
|
c := make(map[string]tcell.Style)
|
||||||
file := FindRuntimeFile(RTColorscheme, colorschemeName)
|
file := FindRuntimeFile(RTColorscheme, colorschemeName)
|
||||||
if file == nil {
|
if file == nil {
|
||||||
return errors.New(colorschemeName + " is not a valid colorscheme")
|
return c, errors.New(colorschemeName + " is not a valid colorscheme")
|
||||||
}
|
}
|
||||||
if data, err := file.Data(); err != nil {
|
if data, err := file.Data(); err != nil {
|
||||||
return errors.New("Error loading colorscheme: " + err.Error())
|
return c, errors.New("Error loading colorscheme: " + err.Error())
|
||||||
} else {
|
} else {
|
||||||
Colorscheme, err = ParseColorscheme(string(data))
|
var err error
|
||||||
|
c, err = ParseColorscheme(file.Name(), string(data), parsedColorschemes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return c, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseColorscheme parses the text definition for a colorscheme and returns the corresponding object
|
// ParseColorscheme parses the text definition for a colorscheme and returns the corresponding object
|
||||||
// Colorschemes are made up of color-link statements linking a color group to a list of colors
|
// Colorschemes are made up of color-link statements linking a color group to a list of colors
|
||||||
// For example, color-link keyword (blue,red) makes all keywords have a blue foreground and
|
// For example, color-link keyword (blue,red) makes all keywords have a blue foreground and
|
||||||
// red background
|
// red background
|
||||||
func ParseColorscheme(text string) (map[string]tcell.Style, error) {
|
func ParseColorscheme(name string, text string, parsedColorschemes *[]string) (map[string]tcell.Style, error) {
|
||||||
var err error
|
var err error
|
||||||
parser := regexp.MustCompile(`color-link\s+(\S*)\s+"(.*)"`)
|
colorParser := regexp.MustCompile(`color-link\s+(\S*)\s+"(.*)"`)
|
||||||
|
includeParser := regexp.MustCompile(`include\s+"(.*)"`)
|
||||||
lines := strings.Split(text, "\n")
|
lines := strings.Split(text, "\n")
|
||||||
|
|
||||||
c := make(map[string]tcell.Style)
|
c := make(map[string]tcell.Style)
|
||||||
|
|
||||||
|
if parsedColorschemes != nil {
|
||||||
|
*parsedColorschemes = append(*parsedColorschemes, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
lineLoop:
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
if strings.TrimSpace(line) == "" ||
|
if strings.TrimSpace(line) == "" ||
|
||||||
strings.TrimSpace(line)[0] == '#' {
|
strings.TrimSpace(line)[0] == '#' {
|
||||||
|
@ -96,7 +108,30 @@ func ParseColorscheme(text string) (map[string]tcell.Style, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
matches := parser.FindSubmatch([]byte(line))
|
matches := includeParser.FindSubmatch([]byte(line))
|
||||||
|
if len(matches) == 2 {
|
||||||
|
// support includes only in case parsedColorschemes are given
|
||||||
|
if parsedColorschemes != nil {
|
||||||
|
include := string(matches[1])
|
||||||
|
for _, name := range *parsedColorschemes {
|
||||||
|
// check for circular includes...
|
||||||
|
if name == include {
|
||||||
|
// ...and prevent them
|
||||||
|
continue lineLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
includeScheme, err := LoadColorscheme(include, parsedColorschemes)
|
||||||
|
if err != nil {
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
for k, v := range includeScheme {
|
||||||
|
c[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
matches = colorParser.FindSubmatch([]byte(line))
|
||||||
if len(matches) == 3 {
|
if len(matches) == 3 {
|
||||||
link := string(matches[1])
|
link := string(matches[1])
|
||||||
colors := string(matches[2])
|
colors := string(matches[2])
|
||||||
|
|
|
@ -65,7 +65,7 @@ color-link constant "#AE81FF,#282828"
|
||||||
color-link constant.string "#E6DB74,#282828"
|
color-link constant.string "#E6DB74,#282828"
|
||||||
color-link constant.string.char "#BDE6AD,#282828"`
|
color-link constant.string.char "#BDE6AD,#282828"`
|
||||||
|
|
||||||
c, err := ParseColorscheme(testColorscheme)
|
c, err := ParseColorscheme("testColorscheme", testColorscheme, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
fg, bg, _ := c["comment"].Decompose()
|
fg, bg, _ := c["comment"].Decompose()
|
||||||
|
|
|
@ -28,7 +28,7 @@ func LoadAllPlugins() error {
|
||||||
func RunPluginFn(fn string, args ...lua.LValue) error {
|
func RunPluginFn(fn string, args ...lua.LValue) error {
|
||||||
var reterr error
|
var reterr error
|
||||||
for _, p := range Plugins {
|
for _, p := range Plugins {
|
||||||
if !p.IsEnabled() {
|
if !p.IsLoaded() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, err := p.Call(fn, args...)
|
_, err := p.Call(fn, args...)
|
||||||
|
@ -42,11 +42,11 @@ func RunPluginFn(fn string, args ...lua.LValue) error {
|
||||||
// RunPluginFnBool runs a function in all plugins and returns
|
// RunPluginFnBool runs a function in all plugins and returns
|
||||||
// false if any one of them returned false
|
// false if any one of them returned false
|
||||||
// also returns an error if any of the plugins had an error
|
// also returns an error if any of the plugins had an error
|
||||||
func RunPluginFnBool(fn string, args ...lua.LValue) (bool, error) {
|
func RunPluginFnBool(settings map[string]interface{}, fn string, args ...lua.LValue) (bool, error) {
|
||||||
var reterr error
|
var reterr error
|
||||||
retbool := true
|
retbool := true
|
||||||
for _, p := range Plugins {
|
for _, p := range Plugins {
|
||||||
if !p.IsEnabled() {
|
if !p.IsLoaded() || (settings != nil && settings[p.Name] == false) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val, err := p.Call(fn, args...)
|
val, err := p.Call(fn, args...)
|
||||||
|
@ -74,8 +74,8 @@ type Plugin struct {
|
||||||
Default bool // pre-installed plugin
|
Default bool // pre-installed plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEnabled returns if a plugin is enabled
|
// IsLoaded returns if a plugin is enabled
|
||||||
func (p *Plugin) IsEnabled() bool {
|
func (p *Plugin) IsLoaded() bool {
|
||||||
if v, ok := GlobalSettings[p.Name]; ok {
|
if v, ok := GlobalSettings[p.Name]; ok {
|
||||||
return v.(bool) && p.Loaded
|
return v.(bool) && p.Loaded
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ func (p *Plugin) Load() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.Loaded = true
|
p.Loaded = true
|
||||||
RegisterGlobalOption(p.Name, true)
|
RegisterCommonOption(p.Name, true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ func (p *Plugin) Call(fn string, args ...lua.LValue) (lua.LValue, error) {
|
||||||
func FindPlugin(name string) *Plugin {
|
func FindPlugin(name string) *Plugin {
|
||||||
var pl *Plugin
|
var pl *Plugin
|
||||||
for _, p := range Plugins {
|
for _, p := range Plugins {
|
||||||
if !p.IsEnabled() {
|
if !p.IsLoaded() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if p.Name == name {
|
if p.Name == name {
|
||||||
|
|
|
@ -363,7 +363,7 @@ func GetInstalledVersions(withCore bool) PluginVersions {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range Plugins {
|
for _, p := range Plugins {
|
||||||
if !p.IsEnabled() {
|
if !p.IsLoaded() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
version := GetInstalledPluginVersion(p.Name)
|
version := GetInstalledPluginVersion(p.Name)
|
||||||
|
@ -572,7 +572,7 @@ func (pv PluginVersions) install(out io.Writer) {
|
||||||
// UninstallPlugin deletes the plugin folder of the given plugin
|
// UninstallPlugin deletes the plugin folder of the given plugin
|
||||||
func UninstallPlugin(out io.Writer, name string) {
|
func UninstallPlugin(out io.Writer, name string) {
|
||||||
for _, p := range Plugins {
|
for _, p := range Plugins {
|
||||||
if !p.IsEnabled() {
|
if !p.IsLoaded() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if p.Name == name {
|
if p.Name == name {
|
||||||
|
@ -605,7 +605,7 @@ func UpdatePlugins(out io.Writer, plugins []string) {
|
||||||
// if no plugins are specified, update all installed plugins.
|
// if no plugins are specified, update all installed plugins.
|
||||||
if len(plugins) == 0 {
|
if len(plugins) == 0 {
|
||||||
for _, p := range Plugins {
|
for _, p := range Plugins {
|
||||||
if !p.IsEnabled() || p.Default {
|
if !p.IsLoaded() || p.Default {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
plugins = append(plugins, p.Name)
|
plugins = append(plugins, p.Name)
|
||||||
|
|
|
@ -40,6 +40,10 @@ var allFiles [][]RuntimeFile
|
||||||
var realFiles [][]RuntimeFile
|
var realFiles [][]RuntimeFile
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
initRuntimeVars()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRuntimeVars() {
|
||||||
allFiles = make([][]RuntimeFile, NumTypes)
|
allFiles = make([][]RuntimeFile, NumTypes)
|
||||||
realFiles = make([][]RuntimeFile, NumTypes)
|
realFiles = make([][]RuntimeFile, NumTypes)
|
||||||
}
|
}
|
||||||
|
@ -129,9 +133,17 @@ func AddRuntimeFilesFromAssets(fileType RTFiletype, directory, pattern string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assetLoop:
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if ok, _ := path.Match(pattern, f); ok {
|
if ok, _ := path.Match(pattern, f); ok {
|
||||||
AddRuntimeFile(fileType, assetFile(path.Join(directory, f)))
|
af := assetFile(path.Join(directory, f))
|
||||||
|
for _, rf := range realFiles[fileType] {
|
||||||
|
if af.Name() == rf.Name() {
|
||||||
|
continue assetLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddRuntimeFile(fileType, af)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,19 +170,30 @@ func ListRealRuntimeFiles(fileType RTFiletype) []RuntimeFile {
|
||||||
return realFiles[fileType]
|
return realFiles[fileType]
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitRuntimeFiles initializes all assets file and the config directory
|
// InitRuntimeFiles initializes all assets files and the config directory.
|
||||||
func InitRuntimeFiles() {
|
// If `user` is false, InitRuntimeFiles ignores the config directory and
|
||||||
|
// initializes asset files only.
|
||||||
|
func InitRuntimeFiles(user bool) {
|
||||||
add := func(fileType RTFiletype, dir, pattern string) {
|
add := func(fileType RTFiletype, dir, pattern string) {
|
||||||
|
if user {
|
||||||
AddRuntimeFilesFromDirectory(fileType, filepath.Join(ConfigDir, dir), pattern)
|
AddRuntimeFilesFromDirectory(fileType, filepath.Join(ConfigDir, dir), pattern)
|
||||||
|
}
|
||||||
AddRuntimeFilesFromAssets(fileType, path.Join("runtime", dir), pattern)
|
AddRuntimeFilesFromAssets(fileType, path.Join("runtime", dir), pattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initRuntimeVars()
|
||||||
|
|
||||||
add(RTColorscheme, "colorschemes", "*.micro")
|
add(RTColorscheme, "colorschemes", "*.micro")
|
||||||
add(RTSyntax, "syntax", "*.yaml")
|
add(RTSyntax, "syntax", "*.yaml")
|
||||||
add(RTSyntaxHeader, "syntax", "*.hdr")
|
add(RTSyntaxHeader, "syntax", "*.hdr")
|
||||||
add(RTHelp, "help", "*.md")
|
add(RTHelp, "help", "*.md")
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitPlugins initializes the plugins
|
||||||
|
func InitPlugins() {
|
||||||
|
Plugins = Plugins[:0]
|
||||||
initlua := filepath.Join(ConfigDir, "init.lua")
|
initlua := filepath.Join(ConfigDir, "init.lua")
|
||||||
|
|
||||||
if _, err := os.Stat(initlua); !os.IsNotExist(err) {
|
if _, err := os.Stat(initlua); !os.IsNotExist(err) {
|
||||||
p := new(Plugin)
|
p := new(Plugin)
|
||||||
p.Name = "initlua"
|
p.Name = "initlua"
|
||||||
|
@ -218,7 +241,15 @@ func InitRuntimeFiles() {
|
||||||
|
|
||||||
plugdir = filepath.Join("runtime", "plugins")
|
plugdir = filepath.Join("runtime", "plugins")
|
||||||
if files, err := rt.AssetDir(plugdir); err == nil {
|
if files, err := rt.AssetDir(plugdir); err == nil {
|
||||||
|
outer:
|
||||||
for _, d := range files {
|
for _, d := range files {
|
||||||
|
for _, p := range Plugins {
|
||||||
|
if p.Name == d {
|
||||||
|
log.Println(p.Name, "built-in plugin overridden by user-defined one")
|
||||||
|
continue outer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if srcs, err := rt.AssetDir(filepath.Join(plugdir, d)); err == nil {
|
if srcs, err := rt.AssetDir(filepath.Join(plugdir, d)); err == nil {
|
||||||
p := new(Plugin)
|
p := new(Plugin)
|
||||||
p.Name = d
|
p.Name = d
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
InitRuntimeFiles()
|
InitRuntimeFiles(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddFile(t *testing.T) {
|
func TestAddFile(t *testing.T) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -19,6 +20,116 @@ import (
|
||||||
|
|
||||||
type optionValidator func(string, interface{}) error
|
type optionValidator func(string, interface{}) error
|
||||||
|
|
||||||
|
// a list of settings that need option validators
|
||||||
|
var optionValidators = map[string]optionValidator{
|
||||||
|
"autosave": validateNonNegativeValue,
|
||||||
|
"clipboard": validateChoice,
|
||||||
|
"colorcolumn": validateNonNegativeValue,
|
||||||
|
"colorscheme": validateColorscheme,
|
||||||
|
"detectlimit": validateNonNegativeValue,
|
||||||
|
"encoding": validateEncoding,
|
||||||
|
"fileformat": validateChoice,
|
||||||
|
"matchbracestyle": validateChoice,
|
||||||
|
"multiopen": validateChoice,
|
||||||
|
"reload": validateChoice,
|
||||||
|
"scrollmargin": validateNonNegativeValue,
|
||||||
|
"scrollspeed": validateNonNegativeValue,
|
||||||
|
"tabsize": validatePositiveValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
// a list of settings with pre-defined choices
|
||||||
|
var OptionChoices = map[string][]string{
|
||||||
|
"clipboard": {"internal", "external", "terminal"},
|
||||||
|
"fileformat": {"unix", "dos"},
|
||||||
|
"matchbracestyle": {"underline", "highlight"},
|
||||||
|
"multiopen": {"tab", "hsplit", "vsplit"},
|
||||||
|
"reload": {"prompt", "auto", "disabled"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// a list of settings that can be globally and locally modified and their
|
||||||
|
// default values
|
||||||
|
var defaultCommonSettings = map[string]interface{}{
|
||||||
|
"autoindent": true,
|
||||||
|
"autosu": false,
|
||||||
|
"backup": true,
|
||||||
|
"backupdir": "",
|
||||||
|
"basename": false,
|
||||||
|
"colorcolumn": float64(0),
|
||||||
|
"cursorline": true,
|
||||||
|
"detectlimit": float64(100),
|
||||||
|
"diffgutter": false,
|
||||||
|
"encoding": "utf-8",
|
||||||
|
"eofnewline": true,
|
||||||
|
"fastdirty": false,
|
||||||
|
"fileformat": defaultFileFormat(),
|
||||||
|
"filetype": "unknown",
|
||||||
|
"hlsearch": false,
|
||||||
|
"hltaberrors": false,
|
||||||
|
"hltrailingws": false,
|
||||||
|
"incsearch": true,
|
||||||
|
"ignorecase": true,
|
||||||
|
"indentchar": " ",
|
||||||
|
"keepautoindent": false,
|
||||||
|
"matchbrace": true,
|
||||||
|
"matchbracestyle": "underline",
|
||||||
|
"mkparents": false,
|
||||||
|
"permbackup": false,
|
||||||
|
"readonly": false,
|
||||||
|
"reload": "prompt",
|
||||||
|
"rmtrailingws": false,
|
||||||
|
"ruler": true,
|
||||||
|
"relativeruler": false,
|
||||||
|
"savecursor": false,
|
||||||
|
"saveundo": false,
|
||||||
|
"scrollbar": false,
|
||||||
|
"scrollmargin": float64(3),
|
||||||
|
"scrollspeed": float64(2),
|
||||||
|
"smartpaste": true,
|
||||||
|
"softwrap": false,
|
||||||
|
"splitbottom": true,
|
||||||
|
"splitright": true,
|
||||||
|
"statusformatl": "$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
|
||||||
|
"statusformatr": "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help",
|
||||||
|
"statusline": true,
|
||||||
|
"syntax": true,
|
||||||
|
"tabmovement": false,
|
||||||
|
"tabsize": float64(4),
|
||||||
|
"tabstospaces": false,
|
||||||
|
"useprimary": true,
|
||||||
|
"wordwrap": false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// a list of settings that should only be globally modified and their
|
||||||
|
// default values
|
||||||
|
var DefaultGlobalOnlySettings = map[string]interface{}{
|
||||||
|
"autosave": float64(0),
|
||||||
|
"clipboard": "external",
|
||||||
|
"colorscheme": "default",
|
||||||
|
"divchars": "|-",
|
||||||
|
"divreverse": true,
|
||||||
|
"fakecursor": false,
|
||||||
|
"infobar": true,
|
||||||
|
"keymenu": false,
|
||||||
|
"mouse": true,
|
||||||
|
"multiopen": "tab",
|
||||||
|
"parsecursor": false,
|
||||||
|
"paste": false,
|
||||||
|
"pluginchannels": []string{"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"},
|
||||||
|
"pluginrepos": []string{},
|
||||||
|
"savehistory": true,
|
||||||
|
"scrollbarchar": "|",
|
||||||
|
"sucmd": "sudo",
|
||||||
|
"tabhighlight": false,
|
||||||
|
"tabreverse": true,
|
||||||
|
"xterm": false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// a list of settings that should never be globally modified
|
||||||
|
var LocalSettings = []string{
|
||||||
|
"filetype",
|
||||||
|
"readonly",
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidOption = errors.New("Invalid option")
|
ErrInvalidOption = errors.New("Invalid option")
|
||||||
ErrInvalidValue = errors.New("Invalid value")
|
ErrInvalidValue = errors.New("Invalid value")
|
||||||
|
@ -33,27 +144,18 @@ var (
|
||||||
// ModifiedSettings is a map of settings which should be written to disk
|
// ModifiedSettings is a map of settings which should be written to disk
|
||||||
// because they have been modified by the user in this session
|
// because they have been modified by the user in this session
|
||||||
ModifiedSettings map[string]bool
|
ModifiedSettings map[string]bool
|
||||||
|
|
||||||
|
// VolatileSettings is a map of settings which should not be written to disk
|
||||||
|
// because they have been temporarily set for this session only
|
||||||
|
VolatileSettings map[string]bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ModifiedSettings = make(map[string]bool)
|
ModifiedSettings = make(map[string]bool)
|
||||||
|
VolatileSettings = make(map[string]bool)
|
||||||
parsedSettings = make(map[string]interface{})
|
parsedSettings = make(map[string]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options with validators
|
|
||||||
var optionValidators = map[string]optionValidator{
|
|
||||||
"autosave": validateNonNegativeValue,
|
|
||||||
"clipboard": validateClipboard,
|
|
||||||
"tabsize": validatePositiveValue,
|
|
||||||
"scrollmargin": validateNonNegativeValue,
|
|
||||||
"scrollspeed": validateNonNegativeValue,
|
|
||||||
"colorscheme": validateColorscheme,
|
|
||||||
"colorcolumn": validateNonNegativeValue,
|
|
||||||
"fileformat": validateLineEnding,
|
|
||||||
"encoding": validateEncoding,
|
|
||||||
"multiopen": validateMultiOpen,
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadSettings() error {
|
func ReadSettings() error {
|
||||||
filename := filepath.Join(ConfigDir, "settings.json")
|
filename := filepath.Join(ConfigDir, "settings.json")
|
||||||
if _, e := os.Stat(filename); e == nil {
|
if _, e := os.Stat(filename); e == nil {
|
||||||
|
@ -172,7 +274,8 @@ func WriteSettings(filename string) error {
|
||||||
for k, v := range parsedSettings {
|
for k, v := range parsedSettings {
|
||||||
if !strings.HasPrefix(reflect.TypeOf(v).String(), "map") {
|
if !strings.HasPrefix(reflect.TypeOf(v).String(), "map") {
|
||||||
cur, okcur := GlobalSettings[k]
|
cur, okcur := GlobalSettings[k]
|
||||||
if def, ok := defaults[k]; ok && okcur && reflect.DeepEqual(cur, def) {
|
_, vol := VolatileSettings[k]
|
||||||
|
if def, ok := defaults[k]; ok && okcur && !vol && reflect.DeepEqual(cur, def) {
|
||||||
delete(parsedSettings, k)
|
delete(parsedSettings, k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,18 +320,7 @@ func OverwriteSettings(filename string) error {
|
||||||
|
|
||||||
// RegisterCommonOptionPlug creates a new option (called pl.name). This is meant to be called by plugins to add options.
|
// RegisterCommonOptionPlug creates a new option (called pl.name). This is meant to be called by plugins to add options.
|
||||||
func RegisterCommonOptionPlug(pl string, name string, defaultvalue interface{}) error {
|
func RegisterCommonOptionPlug(pl string, name string, defaultvalue interface{}) error {
|
||||||
name = pl + "." + name
|
return RegisterCommonOption(pl+"."+name, defaultvalue)
|
||||||
if _, ok := GlobalSettings[name]; !ok {
|
|
||||||
defaultCommonSettings[name] = defaultvalue
|
|
||||||
GlobalSettings[name] = defaultvalue
|
|
||||||
err := WriteSettings(filepath.Join(ConfigDir, "settings.json"))
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("Error writing settings.json file: " + err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
defaultCommonSettings[name] = defaultvalue
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterGlobalOptionPlug creates a new global-only option (named pl.name)
|
// RegisterGlobalOptionPlug creates a new global-only option (named pl.name)
|
||||||
|
@ -236,18 +328,21 @@ func RegisterGlobalOptionPlug(pl string, name string, defaultvalue interface{})
|
||||||
return RegisterGlobalOption(pl+"."+name, defaultvalue)
|
return RegisterGlobalOption(pl+"."+name, defaultvalue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterCommonOption creates a new option
|
||||||
|
func RegisterCommonOption(name string, defaultvalue interface{}) error {
|
||||||
|
if _, ok := GlobalSettings[name]; !ok {
|
||||||
|
GlobalSettings[name] = defaultvalue
|
||||||
|
}
|
||||||
|
defaultCommonSettings[name] = defaultvalue
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterGlobalOption creates a new global-only option
|
// RegisterGlobalOption creates a new global-only option
|
||||||
func RegisterGlobalOption(name string, defaultvalue interface{}) error {
|
func RegisterGlobalOption(name string, defaultvalue interface{}) error {
|
||||||
if v, ok := GlobalSettings[name]; !ok {
|
if _, ok := GlobalSettings[name]; !ok {
|
||||||
DefaultGlobalOnlySettings[name] = defaultvalue
|
|
||||||
GlobalSettings[name] = defaultvalue
|
GlobalSettings[name] = defaultvalue
|
||||||
err := WriteSettings(filepath.Join(ConfigDir, "settings.json"))
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("Error writing settings.json file: " + err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DefaultGlobalOnlySettings[name] = v
|
|
||||||
}
|
}
|
||||||
|
DefaultGlobalOnlySettings[name] = defaultvalue
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,50 +351,11 @@ func GetGlobalOption(name string) interface{} {
|
||||||
return GlobalSettings[name]
|
return GlobalSettings[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultCommonSettings = map[string]interface{}{
|
func defaultFileFormat() string {
|
||||||
"autoindent": true,
|
if runtime.GOOS == "windows" {
|
||||||
"autosu": false,
|
return "dos"
|
||||||
"backup": true,
|
}
|
||||||
"backupdir": "",
|
return "unix"
|
||||||
"basename": false,
|
|
||||||
"colorcolumn": float64(0),
|
|
||||||
"cursorline": true,
|
|
||||||
"diffgutter": false,
|
|
||||||
"encoding": "utf-8",
|
|
||||||
"eofnewline": true,
|
|
||||||
"fastdirty": false,
|
|
||||||
"fileformat": "unix",
|
|
||||||
"filetype": "unknown",
|
|
||||||
"hlsearch": false,
|
|
||||||
"incsearch": true,
|
|
||||||
"ignorecase": true,
|
|
||||||
"indentchar": " ",
|
|
||||||
"keepautoindent": false,
|
|
||||||
"matchbrace": true,
|
|
||||||
"mkparents": false,
|
|
||||||
"permbackup": false,
|
|
||||||
"readonly": false,
|
|
||||||
"rmtrailingws": false,
|
|
||||||
"ruler": true,
|
|
||||||
"relativeruler": false,
|
|
||||||
"savecursor": false,
|
|
||||||
"saveundo": false,
|
|
||||||
"scrollbar": false,
|
|
||||||
"scrollmargin": float64(3),
|
|
||||||
"scrollspeed": float64(2),
|
|
||||||
"smartpaste": true,
|
|
||||||
"softwrap": false,
|
|
||||||
"splitbottom": true,
|
|
||||||
"splitright": true,
|
|
||||||
"statusformatl": "$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
|
|
||||||
"statusformatr": "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help",
|
|
||||||
"statusline": true,
|
|
||||||
"syntax": true,
|
|
||||||
"tabmovement": false,
|
|
||||||
"tabsize": float64(4),
|
|
||||||
"tabstospaces": false,
|
|
||||||
"useprimary": true,
|
|
||||||
"wordwrap": false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetInfoBarOffset() int {
|
func GetInfoBarOffset() int {
|
||||||
|
@ -323,36 +379,6 @@ func DefaultCommonSettings() map[string]interface{} {
|
||||||
return commonsettings
|
return commonsettings
|
||||||
}
|
}
|
||||||
|
|
||||||
// a list of settings that should only be globally modified and their
|
|
||||||
// default values
|
|
||||||
var DefaultGlobalOnlySettings = map[string]interface{}{
|
|
||||||
"autosave": float64(0),
|
|
||||||
"clipboard": "external",
|
|
||||||
"colorscheme": "default",
|
|
||||||
"divchars": "|-",
|
|
||||||
"divreverse": true,
|
|
||||||
"fakecursor": false,
|
|
||||||
"infobar": true,
|
|
||||||
"keymenu": false,
|
|
||||||
"mouse": true,
|
|
||||||
"multiopen": "tab",
|
|
||||||
"parsecursor": false,
|
|
||||||
"paste": false,
|
|
||||||
"pluginchannels": []string{"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"},
|
|
||||||
"pluginrepos": []string{},
|
|
||||||
"savehistory": true,
|
|
||||||
"sucmd": "sudo",
|
|
||||||
"tabhighlight": false,
|
|
||||||
"tabreverse": true,
|
|
||||||
"xterm": false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// a list of settings that should never be globally modified
|
|
||||||
var LocalSettings = []string{
|
|
||||||
"filetype",
|
|
||||||
"readonly",
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultGlobalSettings returns the default global settings for micro
|
// DefaultGlobalSettings returns the default global settings for micro
|
||||||
// Note that colorscheme is a global only option
|
// Note that colorscheme is a global only option
|
||||||
func DefaultGlobalSettings() map[string]interface{} {
|
func DefaultGlobalSettings() map[string]interface{} {
|
||||||
|
@ -446,6 +472,26 @@ func validateNonNegativeValue(option string, value interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateChoice(option string, value interface{}) error {
|
||||||
|
if choices, ok := OptionChoices[option]; ok {
|
||||||
|
val, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("Expected string type for " + option)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range choices {
|
||||||
|
if val == v {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
choicesStr := strings.Join(choices, ", ")
|
||||||
|
return errors.New(option + " must be one of: " + choicesStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New("Option has no pre-defined choices")
|
||||||
|
}
|
||||||
|
|
||||||
func validateColorscheme(option string, value interface{}) error {
|
func validateColorscheme(option string, value interface{}) error {
|
||||||
colorscheme, ok := value.(string)
|
colorscheme, ok := value.(string)
|
||||||
|
|
||||||
|
@ -460,53 +506,7 @@ func validateColorscheme(option string, value interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateClipboard(option string, value interface{}) error {
|
|
||||||
val, ok := value.(string)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return errors.New("Expected string type for clipboard")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch val {
|
|
||||||
case "internal", "external", "terminal":
|
|
||||||
default:
|
|
||||||
return errors.New(option + " must be 'internal', 'external', or 'terminal'")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateLineEnding(option string, value interface{}) error {
|
|
||||||
endingType, ok := value.(string)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return errors.New("Expected string type for file format")
|
|
||||||
}
|
|
||||||
|
|
||||||
if endingType != "unix" && endingType != "dos" {
|
|
||||||
return errors.New("File format must be either 'unix' or 'dos'")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateEncoding(option string, value interface{}) error {
|
func validateEncoding(option string, value interface{}) error {
|
||||||
_, err := htmlindex.Get(value.(string))
|
_, err := htmlindex.Get(value.(string))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateMultiOpen(option string, value interface{}) error {
|
|
||||||
val, ok := value.(string)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return errors.New("Expected string type for multiopen")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch val {
|
|
||||||
case "tab", "hsplit", "vsplit":
|
|
||||||
default:
|
|
||||||
return errors.New(option + " must be 'tab', 'hsplit', or 'vsplit'")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -129,6 +129,11 @@ func (w *BufWindow) updateDisplayInfo() {
|
||||||
w.bufHeight--
|
w.bufHeight--
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrollbarWidth := 0
|
||||||
|
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height && w.Width > 0 {
|
||||||
|
scrollbarWidth = 1
|
||||||
|
}
|
||||||
|
|
||||||
w.hasMessage = len(b.Messages) > 0
|
w.hasMessage = len(b.Messages) > 0
|
||||||
|
|
||||||
// We need to know the string length of the largest line number
|
// We need to know the string length of the largest line number
|
||||||
|
@ -146,13 +151,13 @@ func (w *BufWindow) updateDisplayInfo() {
|
||||||
w.gutterOffset += w.maxLineNumLength + 1
|
w.gutterOffset += w.maxLineNumLength + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
prevBufWidth := w.bufWidth
|
if w.gutterOffset > w.Width-scrollbarWidth {
|
||||||
|
w.gutterOffset = w.Width - scrollbarWidth
|
||||||
w.bufWidth = w.Width - w.gutterOffset
|
|
||||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
|
||||||
w.bufWidth--
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prevBufWidth := w.bufWidth
|
||||||
|
w.bufWidth = w.Width - w.gutterOffset - scrollbarWidth
|
||||||
|
|
||||||
if w.bufWidth != prevBufWidth && w.Buf.Settings["softwrap"].(bool) {
|
if w.bufWidth != prevBufWidth && w.Buf.Settings["softwrap"].(bool) {
|
||||||
for _, c := range w.Buf.GetCursors() {
|
for _, c := range w.Buf.GetCursors() {
|
||||||
c.LastVisualX = c.GetVisualX()
|
c.LastVisualX = c.GetVisualX()
|
||||||
|
@ -277,13 +282,17 @@ func (w *BufWindow) drawGutter(vloc *buffer.Loc, bloc *buffer.Loc) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for i := 0; i < 2 && vloc.X < w.gutterOffset; i++ {
|
||||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
|
||||||
vloc.X++
|
vloc.X++
|
||||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
|
}
|
||||||
vloc.X++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *BufWindow) drawDiffGutter(backgroundStyle tcell.Style, softwrapped bool, vloc *buffer.Loc, bloc *buffer.Loc) {
|
func (w *BufWindow) drawDiffGutter(backgroundStyle tcell.Style, softwrapped bool, vloc *buffer.Loc, bloc *buffer.Loc) {
|
||||||
|
if vloc.X >= w.gutterOffset {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
symbol := ' '
|
symbol := ' '
|
||||||
styleName := ""
|
styleName := ""
|
||||||
|
|
||||||
|
@ -319,26 +328,28 @@ func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, vloc
|
||||||
} else {
|
} else {
|
||||||
lineInt = bloc.Y - cursorLine
|
lineInt = bloc.Y - cursorLine
|
||||||
}
|
}
|
||||||
lineNum := strconv.Itoa(util.Abs(lineInt))
|
lineNum := []rune(strconv.Itoa(util.Abs(lineInt)))
|
||||||
|
|
||||||
// Write the spaces before the line number if necessary
|
// Write the spaces before the line number if necessary
|
||||||
for i := 0; i < w.maxLineNumLength-len(lineNum); i++ {
|
for i := 0; i < w.maxLineNumLength-len(lineNum) && vloc.X < w.gutterOffset; i++ {
|
||||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
||||||
vloc.X++
|
vloc.X++
|
||||||
}
|
}
|
||||||
// Write the actual line number
|
// Write the actual line number
|
||||||
for _, ch := range lineNum {
|
for i := 0; i < len(lineNum) && vloc.X < w.gutterOffset; i++ {
|
||||||
if softwrapped {
|
if softwrapped || (w.bufWidth == 0 && w.Buf.Settings["softwrap"] == true) {
|
||||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
||||||
} else {
|
} else {
|
||||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ch, nil, lineNumStyle)
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, lineNum[i], nil, lineNumStyle)
|
||||||
}
|
}
|
||||||
vloc.X++
|
vloc.X++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the extra space
|
// Write the extra space
|
||||||
|
if vloc.X < w.gutterOffset {
|
||||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
||||||
vloc.X++
|
vloc.X++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getStyle returns the highlight style for the given character position
|
// getStyle returns the highlight style for the given character position
|
||||||
|
@ -373,18 +384,7 @@ func (w *BufWindow) displayBuffer() {
|
||||||
|
|
||||||
if b.ModifiedThisFrame {
|
if b.ModifiedThisFrame {
|
||||||
if b.Settings["diffgutter"].(bool) {
|
if b.Settings["diffgutter"].(bool) {
|
||||||
b.UpdateDiff(func(synchronous bool) {
|
b.UpdateDiff()
|
||||||
// If the diff was updated asynchronously, the outer call to
|
|
||||||
// displayBuffer might already be completed and we need to
|
|
||||||
// schedule a redraw in order to display the new diff.
|
|
||||||
// Note that this cannot lead to an infinite recursion
|
|
||||||
// because the modifications were cleared above so there won't
|
|
||||||
// be another call to UpdateDiff when displayBuffer is called
|
|
||||||
// during the redraw.
|
|
||||||
if !synchronous {
|
|
||||||
screen.Redraw()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
b.ModifiedThisFrame = false
|
b.ModifiedThisFrame = false
|
||||||
}
|
}
|
||||||
|
@ -392,26 +392,20 @@ func (w *BufWindow) displayBuffer() {
|
||||||
var matchingBraces []buffer.Loc
|
var matchingBraces []buffer.Loc
|
||||||
// bracePairs is defined in buffer.go
|
// bracePairs is defined in buffer.go
|
||||||
if b.Settings["matchbrace"].(bool) {
|
if b.Settings["matchbrace"].(bool) {
|
||||||
for _, bp := range buffer.BracePairs {
|
|
||||||
for _, c := range b.GetCursors() {
|
for _, c := range b.GetCursors() {
|
||||||
if c.HasSelection() {
|
if c.HasSelection() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
curX := c.X
|
|
||||||
curLoc := c.Loc
|
|
||||||
|
|
||||||
r := c.RuneUnder(curX)
|
mb, left, found := b.FindMatchingBrace(c.Loc)
|
||||||
rl := c.RuneUnder(curX - 1)
|
|
||||||
if r == bp[0] || r == bp[1] || rl == bp[0] || rl == bp[1] {
|
|
||||||
mb, left, found := b.FindMatchingBrace(bp, curLoc)
|
|
||||||
if found {
|
if found {
|
||||||
matchingBraces = append(matchingBraces, mb)
|
matchingBraces = append(matchingBraces, mb)
|
||||||
if !left {
|
if !left {
|
||||||
matchingBraces = append(matchingBraces, curLoc)
|
if b.Settings["matchbracestyle"].(string) != "highlight" {
|
||||||
|
matchingBraces = append(matchingBraces, c.Loc)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
matchingBraces = append(matchingBraces, curLoc.Move(-1, b))
|
matchingBraces = append(matchingBraces, c.Loc.Move(-1, b))
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -482,6 +476,12 @@ func (w *BufWindow) displayBuffer() {
|
||||||
vloc.X = w.gutterOffset
|
vloc.X = w.gutterOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bline := b.LineBytes(bloc.Y)
|
||||||
|
blineLen := util.CharacterCount(bline)
|
||||||
|
|
||||||
|
leadingwsEnd := len(util.GetLeadingWhitespace(bline))
|
||||||
|
trailingwsStart := blineLen - util.CharacterCount(util.GetTrailingWhitespace(bline))
|
||||||
|
|
||||||
line, nColsBeforeStart, bslice, startStyle := w.getStartInfo(w.StartCol, bloc.Y)
|
line, nColsBeforeStart, bslice, startStyle := w.getStartInfo(w.StartCol, bloc.Y)
|
||||||
if startStyle != nil {
|
if startStyle != nil {
|
||||||
curStyle = *startStyle
|
curStyle = *startStyle
|
||||||
|
@ -505,6 +505,37 @@ func (w *BufWindow) displayBuffer() {
|
||||||
// over cursor-line and color-column
|
// over cursor-line and color-column
|
||||||
dontOverrideBackground := origBg != defBg
|
dontOverrideBackground := origBg != defBg
|
||||||
|
|
||||||
|
if b.Settings["hltaberrors"].(bool) {
|
||||||
|
if s, ok := config.Colorscheme["tab-error"]; ok {
|
||||||
|
isTab := (r == '\t') || (r == ' ' && !showcursor)
|
||||||
|
if (b.Settings["tabstospaces"].(bool) && isTab) ||
|
||||||
|
(!b.Settings["tabstospaces"].(bool) && bloc.X < leadingwsEnd && r == ' ' && !isTab) {
|
||||||
|
fg, _, _ := s.Decompose()
|
||||||
|
style = style.Background(fg)
|
||||||
|
dontOverrideBackground = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Settings["hltrailingws"].(bool) {
|
||||||
|
if s, ok := config.Colorscheme["trailingws"]; ok {
|
||||||
|
if bloc.X >= trailingwsStart && bloc.X < blineLen {
|
||||||
|
hl := true
|
||||||
|
for _, c := range cursors {
|
||||||
|
if c.NewTrailingWsY == bloc.Y {
|
||||||
|
hl = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hl {
|
||||||
|
fg, _, _ := s.Decompose()
|
||||||
|
style = style.Background(fg)
|
||||||
|
dontOverrideBackground = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, c := range cursors {
|
for _, c := range cursors {
|
||||||
if c.HasSelection() &&
|
if c.HasSelection() &&
|
||||||
(bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) ||
|
(bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) ||
|
||||||
|
@ -557,10 +588,18 @@ func (w *BufWindow) displayBuffer() {
|
||||||
|
|
||||||
for _, mb := range matchingBraces {
|
for _, mb := range matchingBraces {
|
||||||
if mb.X == bloc.X && mb.Y == bloc.Y {
|
if mb.X == bloc.X && mb.Y == bloc.Y {
|
||||||
|
if b.Settings["matchbracestyle"].(string) == "highlight" {
|
||||||
|
if s, ok := config.Colorscheme["match-brace"]; ok {
|
||||||
|
style = s
|
||||||
|
} else {
|
||||||
|
style = style.Reverse(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
style = style.Underline(true)
|
style = style.Underline(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, combc, style)
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, combc, style)
|
||||||
|
|
||||||
|
@ -609,7 +648,7 @@ func (w *BufWindow) displayBuffer() {
|
||||||
wordwidth := 0
|
wordwidth := 0
|
||||||
|
|
||||||
totalwidth := w.StartCol - nColsBeforeStart
|
totalwidth := w.StartCol - nColsBeforeStart
|
||||||
for len(line) > 0 {
|
for len(line) > 0 && vloc.X < maxWidth {
|
||||||
r, combc, size := util.DecodeCharacter(line)
|
r, combc, size := util.DecodeCharacter(line)
|
||||||
line = line[size:]
|
line = line[size:]
|
||||||
|
|
||||||
|
@ -767,8 +806,14 @@ func (w *BufWindow) displayScrollBar() {
|
||||||
scrollBarStyle = style
|
scrollBarStyle = style
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrollBarChar := config.GetGlobalOption("scrollbarchar").(string)
|
||||||
|
if util.CharacterCountInString(scrollBarChar) != 1 {
|
||||||
|
scrollBarChar = "|"
|
||||||
|
}
|
||||||
|
scrollBarRune := []rune(scrollBarChar)
|
||||||
|
|
||||||
for y := barstart; y < util.Min(barstart+barsize, w.Y+w.bufHeight); y++ {
|
for y := barstart; y < util.Min(barstart+barsize, w.Y+w.bufHeight); y++ {
|
||||||
screen.SetContent(scrollX, y, '|', nil, scrollBarStyle)
|
screen.SetContent(scrollX, y, scrollBarRune[0], nil, scrollBarStyle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,7 +260,9 @@ func (i *InfoWindow) Display() {
|
||||||
done := false
|
done := false
|
||||||
|
|
||||||
statusLineStyle := config.DefStyle.Reverse(true)
|
statusLineStyle := config.DefStyle.Reverse(true)
|
||||||
if style, ok := config.Colorscheme["statusline"]; ok {
|
if style, ok := config.Colorscheme["statusline.suggestions"]; ok {
|
||||||
|
statusLineStyle = style
|
||||||
|
} else if style, ok := config.Colorscheme["statusline"]; ok {
|
||||||
statusLineStyle = style
|
statusLineStyle = style
|
||||||
}
|
}
|
||||||
keymenuOffset := 0
|
keymenuOffset := 0
|
||||||
|
|
|
@ -66,7 +66,7 @@ func SetStatusInfoFnLua(fn string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
statusInfo[fn] = func(b *buffer.Buffer) string {
|
statusInfo[fn] = func(b *buffer.Buffer) string {
|
||||||
if pl == nil || !pl.IsEnabled() {
|
if pl == nil || !pl.IsLoaded() {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
val, err := pl.Call(plFn, luar.New(ulua.L, b))
|
val, err := pl.Call(plFn, luar.New(ulua.L, b))
|
||||||
|
@ -110,7 +110,9 @@ func (s *StatusLine) Display() {
|
||||||
// autocomplete suggestions (for the buffer, not for the infowindow)
|
// autocomplete suggestions (for the buffer, not for the infowindow)
|
||||||
if b.HasSuggestions && len(b.Suggestions) > 1 {
|
if b.HasSuggestions && len(b.Suggestions) > 1 {
|
||||||
statusLineStyle := config.DefStyle.Reverse(true)
|
statusLineStyle := config.DefStyle.Reverse(true)
|
||||||
if style, ok := config.Colorscheme["statusline"]; ok {
|
if style, ok := config.Colorscheme["statusline.suggestions"]; ok {
|
||||||
|
statusLineStyle = style
|
||||||
|
} else if style, ok := config.Colorscheme["statusline"]; ok {
|
||||||
statusLineStyle = style
|
statusLineStyle = style
|
||||||
}
|
}
|
||||||
x := 0
|
x := 0
|
||||||
|
@ -167,9 +169,17 @@ func (s *StatusLine) Display() {
|
||||||
rightText = formatParser.ReplaceAllFunc(rightText, formatter)
|
rightText = formatParser.ReplaceAllFunc(rightText, formatter)
|
||||||
|
|
||||||
statusLineStyle := config.DefStyle.Reverse(true)
|
statusLineStyle := config.DefStyle.Reverse(true)
|
||||||
|
if s.win.IsActive() {
|
||||||
if style, ok := config.Colorscheme["statusline"]; ok {
|
if style, ok := config.Colorscheme["statusline"]; ok {
|
||||||
statusLineStyle = style
|
statusLineStyle = style
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if style, ok := config.Colorscheme["statusline.inactive"]; ok {
|
||||||
|
statusLineStyle = style
|
||||||
|
} else if style, ok := config.Colorscheme["statusline"]; ok {
|
||||||
|
statusLineStyle = style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
leftLen := util.StringWidth(leftText, util.CharacterCount(leftText), 1)
|
leftLen := util.StringWidth(leftText, util.CharacterCount(leftText), 1)
|
||||||
rightLen := util.StringWidth(rightText, util.CharacterCount(rightText), 1)
|
rightLen := util.StringWidth(rightText, util.CharacterCount(rightText), 1)
|
||||||
|
|
|
@ -110,6 +110,8 @@ func (w *TermWindow) Display() {
|
||||||
}
|
}
|
||||||
if w.State.CursorVisible() && w.active {
|
if w.State.CursorVisible() && w.active {
|
||||||
curx, cury := w.State.Cursor()
|
curx, cury := w.State.Cursor()
|
||||||
|
if curx < w.Width && cury < w.Height {
|
||||||
screen.ShowCursor(curx+w.X, cury+w.Y)
|
screen.ShowCursor(curx+w.X, cury+w.Y)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,10 @@ import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/zyedidia/micro/v2/internal/config"
|
"github.com/zyedidia/micro/v2/internal/config"
|
||||||
|
"github.com/zyedidia/micro/v2/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoadHistory attempts to load user history from configDir/buffers/history
|
// LoadHistory attempts to load user history from configDir/buffers/history
|
||||||
|
@ -102,3 +104,51 @@ func (i *InfoBuf) DownHistory(history []string) {
|
||||||
i.Buffer.GetActiveCursor().GotoLoc(i.End())
|
i.Buffer.GetActiveCursor().GotoLoc(i.End())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchUpHistory fetches the previous item in the history
|
||||||
|
// beginning with the text in the infobuffer before cursor
|
||||||
|
func (i *InfoBuf) SearchUpHistory(history []string) {
|
||||||
|
if i.HistoryNum > 0 && i.HasPrompt && !i.HasYN {
|
||||||
|
i.searchHistory(history, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchDownHistory fetches the next item in the history
|
||||||
|
// beginning with the text in the infobuffer before cursor
|
||||||
|
func (i *InfoBuf) SearchDownHistory(history []string) {
|
||||||
|
if i.HistoryNum < len(history)-1 && i.HasPrompt && !i.HasYN {
|
||||||
|
i.searchHistory(history, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *InfoBuf) searchHistory(history []string, down bool) {
|
||||||
|
line := string(i.LineBytes(0))
|
||||||
|
c := i.Buffer.GetActiveCursor()
|
||||||
|
|
||||||
|
if !i.HistorySearch || !strings.HasPrefix(line, i.HistorySearchPrefix) {
|
||||||
|
i.HistorySearch = true
|
||||||
|
i.HistorySearchPrefix = util.SliceStartStr(line, c.X)
|
||||||
|
}
|
||||||
|
|
||||||
|
found := -1
|
||||||
|
if down {
|
||||||
|
for j := i.HistoryNum + 1; j < len(history); j++ {
|
||||||
|
if strings.HasPrefix(history[j], i.HistorySearchPrefix) {
|
||||||
|
found = j
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for j := i.HistoryNum - 1; j >= 0; j-- {
|
||||||
|
if strings.HasPrefix(history[j], i.HistorySearchPrefix) {
|
||||||
|
found = j
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found != -1 {
|
||||||
|
i.HistoryNum = found
|
||||||
|
i.Replace(i.Start(), i.End(), history[found])
|
||||||
|
c.GotoLoc(i.End())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// The InfoBuf displays messages and other info at the bottom of the screen.
|
// The InfoBuf displays messages and other info at the bottom of the screen.
|
||||||
// It is respresented as a buffer and a message with a style.
|
// It is represented as a buffer and a message with a style.
|
||||||
type InfoBuf struct {
|
type InfoBuf struct {
|
||||||
*buffer.Buffer
|
*buffer.Buffer
|
||||||
|
|
||||||
|
@ -25,6 +25,10 @@ type InfoBuf struct {
|
||||||
// It's a map of history type -> history array
|
// It's a map of history type -> history array
|
||||||
History map[string][]string
|
History map[string][]string
|
||||||
HistoryNum int
|
HistoryNum int
|
||||||
|
// HistorySearch indicates whether we are searching for history items
|
||||||
|
// beginning with HistorySearchPrefix
|
||||||
|
HistorySearch bool
|
||||||
|
HistorySearchPrefix string
|
||||||
|
|
||||||
// Is the current message a message from the gutter
|
// Is the current message a message from the gutter
|
||||||
HasGutter bool
|
HasGutter bool
|
||||||
|
@ -102,6 +106,7 @@ func (i *InfoBuf) Prompt(prompt string, msg string, ptype string, eventcb func(s
|
||||||
i.History[ptype] = append(i.History[ptype], "")
|
i.History[ptype] = append(i.History[ptype], "")
|
||||||
}
|
}
|
||||||
i.HistoryNum = len(i.History[ptype]) - 1
|
i.HistoryNum = len(i.History[ptype]) - 1
|
||||||
|
i.HistorySearch = false
|
||||||
|
|
||||||
i.PromptType = ptype
|
i.PromptType = ptype
|
||||||
i.Msg = prompt
|
i.Msg = prompt
|
||||||
|
@ -138,13 +143,12 @@ func (i *InfoBuf) DonePrompt(canceled bool) {
|
||||||
if i.PromptCallback != nil {
|
if i.PromptCallback != nil {
|
||||||
if canceled {
|
if canceled {
|
||||||
i.Replace(i.Start(), i.End(), "")
|
i.Replace(i.Start(), i.End(), "")
|
||||||
i.PromptCallback("", true)
|
|
||||||
h := i.History[i.PromptType]
|
h := i.History[i.PromptType]
|
||||||
i.History[i.PromptType] = h[:len(h)-1]
|
i.History[i.PromptType] = h[:len(h)-1]
|
||||||
|
i.PromptCallback("", true)
|
||||||
} else {
|
} else {
|
||||||
resp := string(i.LineBytes(0))
|
resp := string(i.LineBytes(0))
|
||||||
i.Replace(i.Start(), i.End(), "")
|
i.Replace(i.Start(), i.End(), "")
|
||||||
i.PromptCallback(resp, false)
|
|
||||||
h := i.History[i.PromptType]
|
h := i.History[i.PromptType]
|
||||||
h[len(h)-1] = resp
|
h[len(h)-1] = resp
|
||||||
|
|
||||||
|
@ -155,6 +159,8 @@ func (i *InfoBuf) DonePrompt(canceled bool) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.PromptCallback(resp, false)
|
||||||
}
|
}
|
||||||
// i.PromptCallback = nil
|
// i.PromptCallback = nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
@ -27,7 +26,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var L *lua.LState
|
var L *lua.LState
|
||||||
var Lock sync.Mutex
|
|
||||||
|
|
||||||
// LoadFile loads a lua file
|
// LoadFile loads a lua file
|
||||||
func LoadFile(module string, file string, data []byte) error {
|
func LoadFile(module string, file string, data []byte) error {
|
||||||
|
@ -376,9 +374,9 @@ func importOs() *lua.LTable {
|
||||||
L.SetField(pkg, "Remove", luar.New(L, os.Remove))
|
L.SetField(pkg, "Remove", luar.New(L, os.Remove))
|
||||||
L.SetField(pkg, "RemoveAll", luar.New(L, os.RemoveAll))
|
L.SetField(pkg, "RemoveAll", luar.New(L, os.RemoveAll))
|
||||||
L.SetField(pkg, "Rename", luar.New(L, os.Rename))
|
L.SetField(pkg, "Rename", luar.New(L, os.Rename))
|
||||||
L.SetField(pkg, "SEEK_CUR", luar.New(L, os.SEEK_CUR))
|
L.SetField(pkg, "SEEK_CUR", luar.New(L, io.SeekCurrent))
|
||||||
L.SetField(pkg, "SEEK_END", luar.New(L, os.SEEK_END))
|
L.SetField(pkg, "SEEK_END", luar.New(L, io.SeekEnd))
|
||||||
L.SetField(pkg, "SEEK_SET", luar.New(L, os.SEEK_SET))
|
L.SetField(pkg, "SEEK_SET", luar.New(L, io.SeekStart))
|
||||||
L.SetField(pkg, "SameFile", luar.New(L, os.SameFile))
|
L.SetField(pkg, "SameFile", luar.New(L, os.SameFile))
|
||||||
L.SetField(pkg, "Setenv", luar.New(L, os.Setenv))
|
L.SetField(pkg, "Setenv", luar.New(L, os.Setenv))
|
||||||
L.SetField(pkg, "StartProcess", luar.New(L, os.StartProcess))
|
L.SetField(pkg, "StartProcess", luar.New(L, os.StartProcess))
|
||||||
|
@ -523,21 +521,16 @@ func importErrors() *lua.LTable {
|
||||||
func importTime() *lua.LTable {
|
func importTime() *lua.LTable {
|
||||||
pkg := L.NewTable()
|
pkg := L.NewTable()
|
||||||
|
|
||||||
L.SetField(pkg, "After", luar.New(L, time.After))
|
|
||||||
L.SetField(pkg, "Sleep", luar.New(L, time.Sleep))
|
L.SetField(pkg, "Sleep", luar.New(L, time.Sleep))
|
||||||
L.SetField(pkg, "Tick", luar.New(L, time.Tick))
|
|
||||||
L.SetField(pkg, "Since", luar.New(L, time.Since))
|
L.SetField(pkg, "Since", luar.New(L, time.Since))
|
||||||
L.SetField(pkg, "FixedZone", luar.New(L, time.FixedZone))
|
L.SetField(pkg, "FixedZone", luar.New(L, time.FixedZone))
|
||||||
L.SetField(pkg, "LoadLocation", luar.New(L, time.LoadLocation))
|
L.SetField(pkg, "LoadLocation", luar.New(L, time.LoadLocation))
|
||||||
L.SetField(pkg, "NewTicker", luar.New(L, time.NewTicker))
|
|
||||||
L.SetField(pkg, "Date", luar.New(L, time.Date))
|
L.SetField(pkg, "Date", luar.New(L, time.Date))
|
||||||
L.SetField(pkg, "Now", luar.New(L, time.Now))
|
L.SetField(pkg, "Now", luar.New(L, time.Now))
|
||||||
L.SetField(pkg, "Parse", luar.New(L, time.Parse))
|
L.SetField(pkg, "Parse", luar.New(L, time.Parse))
|
||||||
L.SetField(pkg, "ParseDuration", luar.New(L, time.ParseDuration))
|
L.SetField(pkg, "ParseDuration", luar.New(L, time.ParseDuration))
|
||||||
L.SetField(pkg, "ParseInLocation", luar.New(L, time.ParseInLocation))
|
L.SetField(pkg, "ParseInLocation", luar.New(L, time.ParseInLocation))
|
||||||
L.SetField(pkg, "Unix", luar.New(L, time.Unix))
|
L.SetField(pkg, "Unix", luar.New(L, time.Unix))
|
||||||
L.SetField(pkg, "AfterFunc", luar.New(L, time.AfterFunc))
|
|
||||||
L.SetField(pkg, "NewTimer", luar.New(L, time.NewTimer))
|
|
||||||
L.SetField(pkg, "Nanosecond", luar.New(L, time.Nanosecond))
|
L.SetField(pkg, "Nanosecond", luar.New(L, time.Nanosecond))
|
||||||
L.SetField(pkg, "Microsecond", luar.New(L, time.Microsecond))
|
L.SetField(pkg, "Microsecond", luar.New(L, time.Microsecond))
|
||||||
L.SetField(pkg, "Millisecond", luar.New(L, time.Millisecond))
|
L.SetField(pkg, "Millisecond", luar.New(L, time.Millisecond))
|
||||||
|
@ -545,6 +538,15 @@ func importTime() *lua.LTable {
|
||||||
L.SetField(pkg, "Minute", luar.New(L, time.Minute))
|
L.SetField(pkg, "Minute", luar.New(L, time.Minute))
|
||||||
L.SetField(pkg, "Hour", luar.New(L, time.Hour))
|
L.SetField(pkg, "Hour", luar.New(L, time.Hour))
|
||||||
|
|
||||||
|
// TODO: these raw Go timer APIs don't provide any synchronization
|
||||||
|
// with micro. Stop exposing them to lua once plugins switch to using
|
||||||
|
// the safer micro.After() interface instead. See issue #3209
|
||||||
|
L.SetField(pkg, "After", luar.New(L, time.After))
|
||||||
|
L.SetField(pkg, "AfterFunc", luar.New(L, time.AfterFunc))
|
||||||
|
L.SetField(pkg, "NewTicker", luar.New(L, time.NewTicker))
|
||||||
|
L.SetField(pkg, "NewTimer", luar.New(L, time.NewTimer))
|
||||||
|
L.SetField(pkg, "Tick", luar.New(L, time.Tick))
|
||||||
|
|
||||||
return pkg
|
return pkg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,10 @@ var Screen tcell.Screen
|
||||||
// Events is the channel of tcell events
|
// Events is the channel of tcell events
|
||||||
var Events chan (tcell.Event)
|
var Events chan (tcell.Event)
|
||||||
|
|
||||||
|
// RestartCallback is called when the screen is restarted after it was
|
||||||
|
// temporarily shut down
|
||||||
|
var RestartCallback func()
|
||||||
|
|
||||||
// The lock is necessary since the screen is polled on a separate thread
|
// The lock is necessary since the screen is polled on a separate thread
|
||||||
var lock sync.Mutex
|
var lock sync.Mutex
|
||||||
|
|
||||||
|
@ -134,6 +138,10 @@ func TempStart(screenWasNil bool) {
|
||||||
if !screenWasNil {
|
if !screenWasNil {
|
||||||
Init()
|
Init()
|
||||||
Unlock()
|
Unlock()
|
||||||
|
|
||||||
|
if RestartCallback != nil {
|
||||||
|
RestartCallback()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
|
||||||
|
|
||||||
shellquote "github.com/kballard/go-shellquote"
|
shellquote "github.com/kballard/go-shellquote"
|
||||||
"github.com/zyedidia/micro/v2/internal/screen"
|
"github.com/zyedidia/micro/v2/internal/screen"
|
||||||
|
@ -59,16 +58,11 @@ func RunBackgroundShell(input string) (func() string, error) {
|
||||||
inputCmd := args[0]
|
inputCmd := args[0]
|
||||||
return func() string {
|
return func() string {
|
||||||
output, err := RunCommand(input)
|
output, err := RunCommand(input)
|
||||||
totalLines := strings.Split(output, "\n")
|
|
||||||
|
|
||||||
str := output
|
str := output
|
||||||
if len(totalLines) < 3 {
|
if err != nil {
|
||||||
if err == nil {
|
|
||||||
str = fmt.Sprint(inputCmd, " exited without error")
|
|
||||||
} else {
|
|
||||||
str = fmt.Sprint(inputCmd, " exited with error: ", err, ": ", output)
|
str = fmt.Sprint(inputCmd, " exited with error: ", err, ": ", output)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return str
|
return str
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,8 +78,9 @@ func (t *Terminal) Start(execCmd []string, getOutput bool, wait bool, callback f
|
||||||
t.output = nil
|
t.output = nil
|
||||||
if getOutput {
|
if getOutput {
|
||||||
t.output = bytes.NewBuffer([]byte{})
|
t.output = bytes.NewBuffer([]byte{})
|
||||||
|
cmd.Stdout = t.output
|
||||||
}
|
}
|
||||||
Term, _, err := terminal.Start(&t.State, cmd, t.output)
|
Term, _, err := terminal.Start(&t.State, cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/blang/semver"
|
"github.com/blang/semver"
|
||||||
runewidth "github.com/mattn/go-runewidth"
|
runewidth "github.com/mattn/go-runewidth"
|
||||||
|
@ -217,10 +218,68 @@ func FSize(f *os.File) int64 {
|
||||||
return fi.Size()
|
return fi.Size()
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsWordChar returns whether or not the string is a 'word character'
|
// IsWordChar returns whether or not a rune is a 'word character'
|
||||||
// Word characters are defined as numbers, letters, or '_'
|
// Word characters are defined as numbers, letters or sub-word delimiters
|
||||||
func IsWordChar(r rune) bool {
|
func IsWordChar(r rune) bool {
|
||||||
return unicode.IsLetter(r) || unicode.IsNumber(r) || r == '_'
|
return IsAlphanumeric(r) || IsSubwordDelimiter(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonWordChar returns whether or not a rune is not a 'word character'
|
||||||
|
// Non word characters are defined as all characters not being numbers, letters or sub-word delimiters
|
||||||
|
// See IsWordChar()
|
||||||
|
func IsNonWordChar(r rune) bool {
|
||||||
|
return !IsWordChar(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUpperWordChar returns whether or not a rune is an 'upper word character'
|
||||||
|
// Upper word characters are defined as numbers, upper-case letters or sub-word delimiters
|
||||||
|
func IsUpperWordChar(r rune) bool {
|
||||||
|
return IsUpperAlphanumeric(r) || IsSubwordDelimiter(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLowerWordChar returns whether or not a rune is a 'lower word character'
|
||||||
|
// Lower word characters are defined as numbers, lower-case letters or sub-word delimiters
|
||||||
|
func IsLowerWordChar(r rune) bool {
|
||||||
|
return IsLowerAlphanumeric(r) || IsSubwordDelimiter(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSubwordDelimiter returns whether or not a rune is a 'sub-word delimiter character'
|
||||||
|
// i.e. is considered a part of the word and is used as a delimiter between sub-words of the word.
|
||||||
|
// For now the only sub-word delimiter character is '_'.
|
||||||
|
func IsSubwordDelimiter(r rune) bool {
|
||||||
|
return r == '_'
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlphanumeric returns whether or not a rune is an 'alphanumeric character'
|
||||||
|
// Alphanumeric characters are defined as numbers or letters
|
||||||
|
func IsAlphanumeric(r rune) bool {
|
||||||
|
return unicode.IsLetter(r) || unicode.IsNumber(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUpperAlphanumeric returns whether or not a rune is an 'upper alphanumeric character'
|
||||||
|
// Upper alphanumeric characters are defined as numbers or upper-case letters
|
||||||
|
func IsUpperAlphanumeric(r rune) bool {
|
||||||
|
return IsUpperLetter(r) || unicode.IsNumber(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLowerAlphanumeric returns whether or not a rune is a 'lower alphanumeric character'
|
||||||
|
// Lower alphanumeric characters are defined as numbers or lower-case letters
|
||||||
|
func IsLowerAlphanumeric(r rune) bool {
|
||||||
|
return IsLowerLetter(r) || unicode.IsNumber(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUpperLetter returns whether or not a rune is an 'upper letter character'
|
||||||
|
// Upper letter characters are defined as upper-case letters
|
||||||
|
func IsUpperLetter(r rune) bool {
|
||||||
|
// unicode.IsUpper() returns true for letters only
|
||||||
|
return unicode.IsUpper(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLowerLetter returns whether or not a rune is a 'lower letter character'
|
||||||
|
// Lower letter characters are defined as lower-case letters
|
||||||
|
func IsLowerLetter(r rune) bool {
|
||||||
|
// unicode.IsLower() returns true for letters only
|
||||||
|
return unicode.IsLower(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spaces returns a string with n spaces
|
// Spaces returns a string with n spaces
|
||||||
|
@ -315,7 +374,7 @@ func ReplaceHome(path string) (string, error) {
|
||||||
// This is used for opening files like util.go:10:5 to specify a line and column
|
// This is used for opening files like util.go:10:5 to specify a line and column
|
||||||
// Special cases like Windows Absolute path (C:\myfile.txt:10:5) are handled correctly.
|
// Special cases like Windows Absolute path (C:\myfile.txt:10:5) are handled correctly.
|
||||||
func GetPathAndCursorPosition(path string) (string, []string) {
|
func GetPathAndCursorPosition(path string) (string, []string) {
|
||||||
re := regexp.MustCompile(`([\s\S]+?)(?::(\d+))(?::(\d+))?`)
|
re := regexp.MustCompile(`([\s\S]+?)(?::(\d+))(?::(\d+))?$`)
|
||||||
match := re.FindStringSubmatch(path)
|
match := re.FindStringSubmatch(path)
|
||||||
// no lines/columns were specified in the path, return just the path with no cursor location
|
// no lines/columns were specified in the path, return just the path with no cursor location
|
||||||
if len(match) == 0 {
|
if len(match) == 0 {
|
||||||
|
@ -363,6 +422,28 @@ func GetLeadingWhitespace(b []byte) []byte {
|
||||||
return ws
|
return ws
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTrailingWhitespace returns the trailing whitespace of the given byte array
|
||||||
|
func GetTrailingWhitespace(b []byte) []byte {
|
||||||
|
ws := []byte{}
|
||||||
|
for len(b) > 0 {
|
||||||
|
r, size := utf8.DecodeLastRune(b)
|
||||||
|
if IsWhitespace(r) {
|
||||||
|
ws = append([]byte(string(r)), ws...)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
b = b[:len(b)-size]
|
||||||
|
}
|
||||||
|
return ws
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasTrailingWhitespace returns true if the given byte array ends with a whitespace
|
||||||
|
func HasTrailingWhitespace(b []byte) bool {
|
||||||
|
r, _ := utf8.DecodeLastRune(b)
|
||||||
|
return IsWhitespace(r)
|
||||||
|
}
|
||||||
|
|
||||||
// IntOpt turns a float64 setting to an int
|
// IntOpt turns a float64 setting to an int
|
||||||
func IntOpt(opt interface{}) int {
|
func IntOpt(opt interface{}) int {
|
||||||
return int(opt.(float64))
|
return int(opt.(float64))
|
||||||
|
@ -422,14 +503,9 @@ func Clamp(val, min, max int) int {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNonAlphaNumeric returns if the rune is not a number of letter or underscore.
|
|
||||||
func IsNonAlphaNumeric(c rune) bool {
|
|
||||||
return !unicode.IsLetter(c) && !unicode.IsNumber(c) && c != '_'
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAutocomplete returns whether a character should begin an autocompletion.
|
// IsAutocomplete returns whether a character should begin an autocompletion.
|
||||||
func IsAutocomplete(c rune) bool {
|
func IsAutocomplete(c rune) bool {
|
||||||
return c == '.' || !IsNonAlphaNumeric(c)
|
return c == '.' || IsWordChar(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseSpecial replaces escaped ts with '\t'.
|
// ParseSpecial replaces escaped ts with '\t'.
|
||||||
|
|
|
@ -185,6 +185,10 @@ func (n *Node) hResizeSplit(i int, size int) bool {
|
||||||
|
|
||||||
// ResizeSplit resizes a certain split to a given size
|
// ResizeSplit resizes a certain split to a given size
|
||||||
func (n *Node) ResizeSplit(size int) bool {
|
func (n *Node) ResizeSplit(size int) bool {
|
||||||
|
// TODO: `size < 0` does not work for some reason
|
||||||
|
if size <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if len(n.parent.children) <= 1 {
|
if len(n.parent.children) <= 1 {
|
||||||
// cannot resize a lone node
|
// cannot resize a lone node
|
||||||
return false
|
return false
|
||||||
|
@ -201,7 +205,7 @@ func (n *Node) ResizeSplit(size int) bool {
|
||||||
return n.parent.hResizeSplit(ind, size)
|
return n.parent.hResizeSplit(ind, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize sets this node's size and resizes all children accordlingly
|
// Resize sets this node's size and resizes all children accordingly
|
||||||
func (n *Node) Resize(w, h int) {
|
func (n *Node) Resize(w, h int) {
|
||||||
n.W, n.H = w, h
|
n.W, n.H = w, h
|
||||||
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
package highlight
|
|
||||||
|
|
||||||
import "regexp"
|
|
||||||
|
|
||||||
// MatchFiletype will use the list of syntax definitions provided and the filename and first line of the file
|
|
||||||
// to determine the filetype of the file
|
|
||||||
// It will return the corresponding syntax definition for the filetype
|
|
||||||
func MatchFiletype(ftdetect [2]*regexp.Regexp, filename string, firstLine []byte) bool {
|
|
||||||
if ftdetect[0] != nil && ftdetect[0].MatchString(filename) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if ftdetect[1] != nil {
|
|
||||||
return ftdetect[1].Match(firstLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -67,9 +67,6 @@ func combineLineMatch(src, dst LineMatch) LineMatch {
|
||||||
// A State represents the region at the end of a line
|
// A State represents the region at the end of a line
|
||||||
type State *region
|
type State *region
|
||||||
|
|
||||||
// EmptyDef is an empty definition.
|
|
||||||
var EmptyDef = Def{nil, &rules{}}
|
|
||||||
|
|
||||||
// LineStates is an interface for a buffer-like object which can also store the states and matches for every line
|
// LineStates is an interface for a buffer-like object which can also store the states and matches for every line
|
||||||
type LineStates interface {
|
type LineStates interface {
|
||||||
LineBytes(n int) []byte
|
LineBytes(n int) []byte
|
||||||
|
@ -77,6 +74,8 @@ type LineStates interface {
|
||||||
State(lineN int) State
|
State(lineN int) State
|
||||||
SetState(lineN int, s State)
|
SetState(lineN int, s State)
|
||||||
SetMatch(lineN int, m LineMatch)
|
SetMatch(lineN int, m LineMatch)
|
||||||
|
Lock()
|
||||||
|
Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Highlighter contains the information needed to highlight a string
|
// A Highlighter contains the information needed to highlight a string
|
||||||
|
@ -96,19 +95,7 @@ func NewHighlighter(def *Def) *Highlighter {
|
||||||
// color's group (represented as one byte)
|
// color's group (represented as one byte)
|
||||||
type LineMatch map[int]Group
|
type LineMatch map[int]Group
|
||||||
|
|
||||||
func findIndex(regex *regexp.Regexp, skip *regexp.Regexp, str []byte, canMatchStart, canMatchEnd bool) []int {
|
func findIndex(regex *regexp.Regexp, skip *regexp.Regexp, str []byte) []int {
|
||||||
regexStr := regex.String()
|
|
||||||
if strings.Contains(regexStr, "^") {
|
|
||||||
if !canMatchStart {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if strings.Contains(regexStr, "$") {
|
|
||||||
if !canMatchEnd {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var strbytes []byte
|
var strbytes []byte
|
||||||
if skip != nil {
|
if skip != nil {
|
||||||
strbytes = skip.ReplaceAllFunc(str, func(match []byte) []byte {
|
strbytes = skip.ReplaceAllFunc(str, func(match []byte) []byte {
|
||||||
|
@ -127,18 +114,7 @@ func findIndex(regex *regexp.Regexp, skip *regexp.Regexp, str []byte, canMatchSt
|
||||||
return []int{runePos(match[0], str), runePos(match[1], str)}
|
return []int{runePos(match[0], str), runePos(match[1], str)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func findAllIndex(regex *regexp.Regexp, str []byte, canMatchStart, canMatchEnd bool) [][]int {
|
func findAllIndex(regex *regexp.Regexp, str []byte) [][]int {
|
||||||
regexStr := regex.String()
|
|
||||||
if strings.Contains(regexStr, "^") {
|
|
||||||
if !canMatchStart {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if strings.Contains(regexStr, "$") {
|
|
||||||
if !canMatchEnd {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
matches := regex.FindAllIndex(str, -1)
|
matches := regex.FindAllIndex(str, -1)
|
||||||
for i, m := range matches {
|
for i, m := range matches {
|
||||||
matches[i][0] = runePos(m[0], str)
|
matches[i][0] = runePos(m[0], str)
|
||||||
|
@ -157,40 +133,20 @@ func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loc := findIndex(curRegion.end, curRegion.skip, line, start == 0, canMatchEnd)
|
|
||||||
if loc != nil {
|
|
||||||
if !statesOnly {
|
|
||||||
highlights[start+loc[0]] = curRegion.limitGroup
|
|
||||||
}
|
|
||||||
if curRegion.parent == nil {
|
|
||||||
if !statesOnly {
|
|
||||||
highlights[start+loc[1]] = 0
|
|
||||||
h.highlightRegion(highlights, start, false, lineNum, sliceEnd(line, loc[0]), curRegion, statesOnly)
|
|
||||||
}
|
|
||||||
h.highlightEmptyRegion(highlights, start+loc[1], canMatchEnd, lineNum, sliceStart(line, loc[1]), statesOnly)
|
|
||||||
return highlights
|
|
||||||
}
|
|
||||||
if !statesOnly {
|
|
||||||
highlights[start+loc[1]] = curRegion.parent.group
|
|
||||||
h.highlightRegion(highlights, start, false, lineNum, sliceEnd(line, loc[0]), curRegion, statesOnly)
|
|
||||||
}
|
|
||||||
h.highlightRegion(highlights, start+loc[1], canMatchEnd, lineNum, sliceStart(line, loc[1]), curRegion.parent, statesOnly)
|
|
||||||
return highlights
|
|
||||||
}
|
|
||||||
|
|
||||||
if lineLen == 0 {
|
|
||||||
if canMatchEnd {
|
|
||||||
h.lastRegion = curRegion
|
|
||||||
}
|
|
||||||
|
|
||||||
return highlights
|
|
||||||
}
|
|
||||||
|
|
||||||
firstLoc := []int{lineLen, 0}
|
|
||||||
|
|
||||||
var firstRegion *region
|
var firstRegion *region
|
||||||
|
firstLoc := []int{lineLen, 0}
|
||||||
|
searchNesting := true
|
||||||
|
endLoc := findIndex(curRegion.end, curRegion.skip, line)
|
||||||
|
if endLoc != nil {
|
||||||
|
if start == endLoc[0] {
|
||||||
|
searchNesting = false
|
||||||
|
} else {
|
||||||
|
firstLoc = endLoc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if searchNesting {
|
||||||
for _, r := range curRegion.rules.regions {
|
for _, r := range curRegion.rules.regions {
|
||||||
loc := findIndex(r.start, nil, line, start == 0, canMatchEnd)
|
loc := findIndex(r.start, r.skip, line)
|
||||||
if loc != nil {
|
if loc != nil {
|
||||||
if loc[0] < firstLoc[0] {
|
if loc[0] < firstLoc[0] {
|
||||||
firstLoc = loc
|
firstLoc = loc
|
||||||
|
@ -198,11 +154,12 @@ func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if firstLoc[0] != lineLen {
|
}
|
||||||
|
if firstRegion != nil && firstLoc[0] != lineLen {
|
||||||
if !statesOnly {
|
if !statesOnly {
|
||||||
highlights[start+firstLoc[0]] = firstRegion.limitGroup
|
highlights[start+firstLoc[0]] = firstRegion.limitGroup
|
||||||
}
|
}
|
||||||
h.highlightRegion(highlights, start, false, lineNum, sliceEnd(line, firstLoc[0]), curRegion, statesOnly)
|
h.highlightEmptyRegion(highlights, start+firstLoc[1], canMatchEnd, lineNum, sliceStart(line, firstLoc[1]), statesOnly)
|
||||||
h.highlightRegion(highlights, start+firstLoc[1], canMatchEnd, lineNum, sliceStart(line, firstLoc[1]), firstRegion, statesOnly)
|
h.highlightRegion(highlights, start+firstLoc[1], canMatchEnd, lineNum, sliceStart(line, firstLoc[1]), firstRegion, statesOnly)
|
||||||
return highlights
|
return highlights
|
||||||
}
|
}
|
||||||
|
@ -213,14 +170,20 @@ func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchE
|
||||||
fullHighlights[i] = curRegion.group
|
fullHighlights[i] = curRegion.group
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if searchNesting {
|
||||||
for _, p := range curRegion.rules.patterns {
|
for _, p := range curRegion.rules.patterns {
|
||||||
matches := findAllIndex(p.regex, line, start == 0, canMatchEnd)
|
if curRegion.group == curRegion.limitGroup || p.group == curRegion.limitGroup {
|
||||||
|
matches := findAllIndex(p.regex, line)
|
||||||
for _, m := range matches {
|
for _, m := range matches {
|
||||||
|
if ((endLoc == nil) || (m[0] < endLoc[0])) {
|
||||||
for i := m[0]; i < m[1]; i++ {
|
for i := m[0]; i < m[1]; i++ {
|
||||||
fullHighlights[i] = p.group
|
fullHighlights[i] = p.group
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for i, h := range fullHighlights {
|
for i, h := range fullHighlights {
|
||||||
if i == 0 || h != fullHighlights[i-1] {
|
if i == 0 || h != fullHighlights[i-1] {
|
||||||
highlights[start+i] = h
|
highlights[start+i] = h
|
||||||
|
@ -228,6 +191,25 @@ func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loc := endLoc
|
||||||
|
if loc != nil {
|
||||||
|
if !statesOnly {
|
||||||
|
highlights[start+loc[0]] = curRegion.limitGroup
|
||||||
|
}
|
||||||
|
if curRegion.parent == nil {
|
||||||
|
if !statesOnly {
|
||||||
|
highlights[start+loc[1]] = 0
|
||||||
|
}
|
||||||
|
h.highlightEmptyRegion(highlights, start+loc[1], canMatchEnd, lineNum, sliceStart(line, loc[1]), statesOnly)
|
||||||
|
return highlights
|
||||||
|
}
|
||||||
|
if !statesOnly {
|
||||||
|
highlights[start+loc[1]] = curRegion.parent.group
|
||||||
|
}
|
||||||
|
h.highlightRegion(highlights, start+loc[1], canMatchEnd, lineNum, sliceStart(line, loc[1]), curRegion.parent, statesOnly)
|
||||||
|
return highlights
|
||||||
|
}
|
||||||
|
|
||||||
if canMatchEnd {
|
if canMatchEnd {
|
||||||
h.lastRegion = curRegion
|
h.lastRegion = curRegion
|
||||||
}
|
}
|
||||||
|
@ -244,10 +226,10 @@ func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canM
|
||||||
return highlights
|
return highlights
|
||||||
}
|
}
|
||||||
|
|
||||||
firstLoc := []int{lineLen, 0}
|
|
||||||
var firstRegion *region
|
var firstRegion *region
|
||||||
|
firstLoc := []int{lineLen, 0}
|
||||||
for _, r := range h.Def.rules.regions {
|
for _, r := range h.Def.rules.regions {
|
||||||
loc := findIndex(r.start, nil, line, start == 0, canMatchEnd)
|
loc := findIndex(r.start, r.skip, line)
|
||||||
if loc != nil {
|
if loc != nil {
|
||||||
if loc[0] < firstLoc[0] {
|
if loc[0] < firstLoc[0] {
|
||||||
firstLoc = loc
|
firstLoc = loc
|
||||||
|
@ -255,7 +237,7 @@ func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if firstLoc[0] != lineLen {
|
if firstRegion != nil && firstLoc[0] != lineLen {
|
||||||
if !statesOnly {
|
if !statesOnly {
|
||||||
highlights[start+firstLoc[0]] = firstRegion.limitGroup
|
highlights[start+firstLoc[0]] = firstRegion.limitGroup
|
||||||
}
|
}
|
||||||
|
@ -274,7 +256,7 @@ func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canM
|
||||||
|
|
||||||
fullHighlights := make([]Group, len(line))
|
fullHighlights := make([]Group, len(line))
|
||||||
for _, p := range h.Def.rules.patterns {
|
for _, p := range h.Def.rules.patterns {
|
||||||
matches := findAllIndex(p.regex, line, start == 0, canMatchEnd)
|
matches := findAllIndex(p.regex, line)
|
||||||
for _, m := range matches {
|
for _, m := range matches {
|
||||||
for i := m[0]; i < m[1]; i++ {
|
for i := m[0]; i < m[1]; i++ {
|
||||||
fullHighlights[i] = p.group
|
fullHighlights[i] = p.group
|
||||||
|
@ -320,7 +302,13 @@ func (h *Highlighter) HighlightString(input string) []LineMatch {
|
||||||
|
|
||||||
// HighlightStates correctly sets all states for the buffer
|
// HighlightStates correctly sets all states for the buffer
|
||||||
func (h *Highlighter) HighlightStates(input LineStates) {
|
func (h *Highlighter) HighlightStates(input LineStates) {
|
||||||
for i := 0; i < input.LinesNum(); i++ {
|
for i := 0; ; i++ {
|
||||||
|
input.Lock()
|
||||||
|
if i >= input.LinesNum() {
|
||||||
|
input.Unlock()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
line := input.LineBytes(i)
|
line := input.LineBytes(i)
|
||||||
// highlights := make(LineMatch)
|
// highlights := make(LineMatch)
|
||||||
|
|
||||||
|
@ -333,6 +321,7 @@ func (h *Highlighter) HighlightStates(input LineStates) {
|
||||||
curState := h.lastRegion
|
curState := h.lastRegion
|
||||||
|
|
||||||
input.SetState(i, curState)
|
input.SetState(i, curState)
|
||||||
|
input.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,7 +330,9 @@ func (h *Highlighter) HighlightStates(input LineStates) {
|
||||||
// This assumes that all the states are set correctly
|
// This assumes that all the states are set correctly
|
||||||
func (h *Highlighter) HighlightMatches(input LineStates, startline, endline int) {
|
func (h *Highlighter) HighlightMatches(input LineStates, startline, endline int) {
|
||||||
for i := startline; i <= endline; i++ {
|
for i := startline; i <= endline; i++ {
|
||||||
|
input.Lock()
|
||||||
if i >= input.LinesNum() {
|
if i >= input.LinesNum() {
|
||||||
|
input.Unlock()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,6 +347,7 @@ func (h *Highlighter) HighlightMatches(input LineStates, startline, endline int)
|
||||||
}
|
}
|
||||||
|
|
||||||
input.SetMatch(i, match)
|
input.SetMatch(i, match)
|
||||||
|
input.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,9 +359,19 @@ func (h *Highlighter) ReHighlightStates(input LineStates, startline int) int {
|
||||||
|
|
||||||
h.lastRegion = nil
|
h.lastRegion = nil
|
||||||
if startline > 0 {
|
if startline > 0 {
|
||||||
|
input.Lock()
|
||||||
|
if startline-1 < input.LinesNum() {
|
||||||
h.lastRegion = input.State(startline - 1)
|
h.lastRegion = input.State(startline - 1)
|
||||||
}
|
}
|
||||||
for i := startline; i < input.LinesNum(); i++ {
|
input.Unlock()
|
||||||
|
}
|
||||||
|
for i := startline; ; i++ {
|
||||||
|
input.Lock()
|
||||||
|
if i >= input.LinesNum() {
|
||||||
|
input.Unlock()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
line := input.LineBytes(i)
|
line := input.LineBytes(i)
|
||||||
// highlights := make(LineMatch)
|
// highlights := make(LineMatch)
|
||||||
|
|
||||||
|
@ -383,6 +385,7 @@ func (h *Highlighter) ReHighlightStates(input LineStates, startline int) int {
|
||||||
lastState := input.State(i)
|
lastState := input.State(i)
|
||||||
|
|
||||||
input.SetState(i, curState)
|
input.SetState(i, curState)
|
||||||
|
input.Unlock()
|
||||||
|
|
||||||
if curState == lastState {
|
if curState == lastState {
|
||||||
return i
|
return i
|
||||||
|
@ -394,6 +397,9 @@ func (h *Highlighter) ReHighlightStates(input LineStates, startline int) int {
|
||||||
|
|
||||||
// ReHighlightLine will rehighlight the state and match for a single line
|
// ReHighlightLine will rehighlight the state and match for a single line
|
||||||
func (h *Highlighter) ReHighlightLine(input LineStates, lineN int) {
|
func (h *Highlighter) ReHighlightLine(input LineStates, lineN int) {
|
||||||
|
input.Lock()
|
||||||
|
defer input.Unlock()
|
||||||
|
|
||||||
line := input.LineBytes(lineN)
|
line := input.LineBytes(lineN)
|
||||||
highlights := make(LineMatch)
|
highlights := make(LineMatch)
|
||||||
|
|
||||||
|
|
|
@ -33,26 +33,27 @@ func (g Group) String() string {
|
||||||
// Then it has the rules which define how to highlight the file
|
// Then it has the rules which define how to highlight the file
|
||||||
type Def struct {
|
type Def struct {
|
||||||
*Header
|
*Header
|
||||||
|
|
||||||
rules *rules
|
rules *rules
|
||||||
}
|
}
|
||||||
|
|
||||||
type Header struct {
|
type Header struct {
|
||||||
FileType string
|
FileType string
|
||||||
FtDetect [2]*regexp.Regexp
|
FileNameRegex *regexp.Regexp
|
||||||
|
HeaderRegex *regexp.Regexp
|
||||||
|
SignatureRegex *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
type HeaderYaml struct {
|
type HeaderYaml struct {
|
||||||
FileType string `yaml:"filetype"`
|
FileType string `yaml:"filetype"`
|
||||||
Detect struct {
|
Detect struct {
|
||||||
FNameRgx string `yaml:"filename"`
|
FNameRegexStr string `yaml:"filename"`
|
||||||
HeaderRgx string `yaml:"header"`
|
HeaderRegexStr string `yaml:"header"`
|
||||||
|
SignatureRegexStr string `yaml:"signature"`
|
||||||
} `yaml:"detect"`
|
} `yaml:"detect"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
FileType string
|
FileType string
|
||||||
|
|
||||||
yamlSrc map[interface{}]interface{}
|
yamlSrc map[interface{}]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,20 +98,24 @@ func init() {
|
||||||
// A yaml file might take ~400us to parse while a header file only takes ~20us
|
// A yaml file might take ~400us to parse while a header file only takes ~20us
|
||||||
func MakeHeader(data []byte) (*Header, error) {
|
func MakeHeader(data []byte) (*Header, error) {
|
||||||
lines := bytes.Split(data, []byte{'\n'})
|
lines := bytes.Split(data, []byte{'\n'})
|
||||||
if len(lines) < 3 {
|
if len(lines) < 4 {
|
||||||
return nil, errors.New("Header file has incorrect format")
|
return nil, errors.New("Header file has incorrect format")
|
||||||
}
|
}
|
||||||
header := new(Header)
|
header := new(Header)
|
||||||
var err error
|
var err error
|
||||||
header.FileType = string(lines[0])
|
header.FileType = string(lines[0])
|
||||||
fnameRgx := string(lines[1])
|
fnameRegexStr := string(lines[1])
|
||||||
headerRgx := string(lines[2])
|
headerRegexStr := string(lines[2])
|
||||||
|
signatureRegexStr := string(lines[3])
|
||||||
|
|
||||||
if fnameRgx != "" {
|
if fnameRegexStr != "" {
|
||||||
header.FtDetect[0], err = regexp.Compile(fnameRgx)
|
header.FileNameRegex, err = regexp.Compile(fnameRegexStr)
|
||||||
}
|
}
|
||||||
if headerRgx != "" {
|
if err == nil && headerRegexStr != "" {
|
||||||
header.FtDetect[1], err = regexp.Compile(headerRgx)
|
header.HeaderRegex, err = regexp.Compile(headerRegexStr)
|
||||||
|
}
|
||||||
|
if err == nil && signatureRegexStr != "" {
|
||||||
|
header.SignatureRegex, err = regexp.Compile(signatureRegexStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -132,11 +137,14 @@ func MakeHeaderYaml(data []byte) (*Header, error) {
|
||||||
header := new(Header)
|
header := new(Header)
|
||||||
header.FileType = hdrYaml.FileType
|
header.FileType = hdrYaml.FileType
|
||||||
|
|
||||||
if hdrYaml.Detect.FNameRgx != "" {
|
if hdrYaml.Detect.FNameRegexStr != "" {
|
||||||
header.FtDetect[0], err = regexp.Compile(hdrYaml.Detect.FNameRgx)
|
header.FileNameRegex, err = regexp.Compile(hdrYaml.Detect.FNameRegexStr)
|
||||||
}
|
}
|
||||||
if hdrYaml.Detect.HeaderRgx != "" {
|
if err == nil && hdrYaml.Detect.HeaderRegexStr != "" {
|
||||||
header.FtDetect[1], err = regexp.Compile(hdrYaml.Detect.HeaderRgx)
|
header.HeaderRegex, err = regexp.Compile(hdrYaml.Detect.HeaderRegexStr)
|
||||||
|
}
|
||||||
|
if err == nil && hdrYaml.Detect.SignatureRegexStr != "" {
|
||||||
|
header.SignatureRegex, err = regexp.Compile(hdrYaml.Detect.SignatureRegexStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -146,6 +154,37 @@ func MakeHeaderYaml(data []byte) (*Header, error) {
|
||||||
return header, nil
|
return header, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchFileName will check the given file name with the stored regex
|
||||||
|
func (header *Header) MatchFileName(filename string) bool {
|
||||||
|
if header.FileNameRegex != nil {
|
||||||
|
return header.FileNameRegex.MatchString(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (header *Header) MatchFileHeader(firstLine []byte) bool {
|
||||||
|
if header.HeaderRegex != nil {
|
||||||
|
return header.HeaderRegex.Match(firstLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasFileSignature checks the presence of a stored signature
|
||||||
|
func (header *Header) HasFileSignature() bool {
|
||||||
|
return header.SignatureRegex != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchFileSignature will check the given line with the stored regex
|
||||||
|
func (header *Header) MatchFileSignature(line []byte) bool {
|
||||||
|
if header.SignatureRegex != nil {
|
||||||
|
return header.SignatureRegex.Match(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func ParseFile(input []byte) (f *File, err error) {
|
func ParseFile(input []byte) (f *File, err error) {
|
||||||
// This is just so if we have an error, we can exit cleanly and return the parse error to the user
|
// This is just so if we have an error, we can exit cleanly and return the parse error to the user
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -170,11 +209,19 @@ func ParseFile(input []byte) (f *File, err error) {
|
||||||
if k == "filetype" {
|
if k == "filetype" {
|
||||||
filetype := v.(string)
|
filetype := v.(string)
|
||||||
|
|
||||||
|
if filetype == "" {
|
||||||
|
return nil, errors.New("empty filetype")
|
||||||
|
}
|
||||||
|
|
||||||
f.FileType = filetype
|
f.FileType = filetype
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if f.FileType == "" {
|
||||||
|
return nil, errors.New("missing filetype")
|
||||||
|
}
|
||||||
|
|
||||||
return f, err
|
return f, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,12 +238,12 @@ func ParseDef(f *File, header *Header) (s *Def, err error) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
rules := f.yamlSrc
|
src := f.yamlSrc
|
||||||
|
|
||||||
s = new(Def)
|
s = new(Def)
|
||||||
s.Header = header
|
s.Header = header
|
||||||
|
|
||||||
for k, v := range rules {
|
for k, v := range src {
|
||||||
if k == "rules" {
|
if k == "rules" {
|
||||||
inputRules := v.([]interface{})
|
inputRules := v.([]interface{})
|
||||||
|
|
||||||
|
@ -209,6 +256,11 @@ func ParseDef(f *File, header *Header) (s *Def, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.rules == nil {
|
||||||
|
// allow empty rules
|
||||||
|
s.rules = new(rules)
|
||||||
|
}
|
||||||
|
|
||||||
return s, err
|
return s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,6 +355,10 @@ func parseRules(input []interface{}, curRegion *region) (ru *rules, err error) {
|
||||||
|
|
||||||
switch object := val.(type) {
|
switch object := val.(type) {
|
||||||
case string:
|
case string:
|
||||||
|
if object == "" {
|
||||||
|
return nil, fmt.Errorf("Empty rule %s", k)
|
||||||
|
}
|
||||||
|
|
||||||
if k == "include" {
|
if k == "include" {
|
||||||
ru.includes = append(ru.includes, object)
|
ru.includes = append(ru.includes, object)
|
||||||
} else {
|
} else {
|
||||||
|
@ -356,30 +412,56 @@ func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegio
|
||||||
r.group = groupNum
|
r.group = groupNum
|
||||||
r.parent = prevRegion
|
r.parent = prevRegion
|
||||||
|
|
||||||
r.start, err = regexp.Compile(regionInfo["start"].(string))
|
// start is mandatory
|
||||||
|
if start, ok := regionInfo["start"]; ok {
|
||||||
|
start := start.(string)
|
||||||
|
if start == "" {
|
||||||
|
return nil, fmt.Errorf("Empty start in %s", group)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.start, err = regexp.Compile(start)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("Missing start in %s", group)
|
||||||
|
}
|
||||||
|
|
||||||
r.end, err = regexp.Compile(regionInfo["end"].(string))
|
// end is mandatory
|
||||||
|
if end, ok := regionInfo["end"]; ok {
|
||||||
|
end := end.(string)
|
||||||
|
if end == "" {
|
||||||
|
return nil, fmt.Errorf("Empty end in %s", group)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.end, err = regexp.Compile(end)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("Missing end in %s", group)
|
||||||
|
}
|
||||||
|
|
||||||
// skip is optional
|
// skip is optional
|
||||||
if _, ok := regionInfo["skip"]; ok {
|
if skip, ok := regionInfo["skip"]; ok {
|
||||||
r.skip, err = regexp.Compile(regionInfo["skip"].(string))
|
skip := skip.(string)
|
||||||
|
if skip == "" {
|
||||||
|
return nil, fmt.Errorf("Empty skip in %s", group)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.skip, err = regexp.Compile(skip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// limit-color is optional
|
// limit-color is optional
|
||||||
if _, ok := regionInfo["limit-group"]; ok {
|
if groupStr, ok := regionInfo["limit-group"]; ok {
|
||||||
groupStr := regionInfo["limit-group"].(string)
|
groupStr := groupStr.(string)
|
||||||
|
if groupStr == "" {
|
||||||
|
return nil, fmt.Errorf("Empty limit-group in %s", group)
|
||||||
|
}
|
||||||
|
|
||||||
if _, ok := Groups[groupStr]; !ok {
|
if _, ok := Groups[groupStr]; !ok {
|
||||||
numGroups++
|
numGroups++
|
||||||
Groups[groupStr] = numGroups
|
Groups[groupStr] = numGroups
|
||||||
|
|
|
@ -28,3 +28,6 @@ color-link color-column "#2D2F31"
|
||||||
#No extended types (bool in C, etc.)
|
#No extended types (bool in C, etc.)
|
||||||
#color-link type.extended "default"
|
#color-link type.extended "default"
|
||||||
#Plain brackets
|
#Plain brackets
|
||||||
|
color-link match-brace "#1D1F21,#62B1FE"
|
||||||
|
color-link tab-error "#D75F5F"
|
||||||
|
color-link trailingws "#D75F5F"
|
||||||
|
|
|
@ -26,3 +26,6 @@ color-link color-column "254"
|
||||||
#No extended types (bool in C, &c.) and plain brackets
|
#No extended types (bool in C, &c.) and plain brackets
|
||||||
color-link type.extended "241,231"
|
color-link type.extended "241,231"
|
||||||
color-link symbol.brackets "241,231"
|
color-link symbol.brackets "241,231"
|
||||||
|
color-link match-brace "231,28"
|
||||||
|
color-link tab-error "210"
|
||||||
|
color-link trailingws "210"
|
||||||
|
|
|
@ -42,3 +42,6 @@ color-link gutter-warning "red"
|
||||||
color-link color-column "cyan"
|
color-link color-column "cyan"
|
||||||
color-link underlined.url "underline blue, white"
|
color-link underlined.url "underline blue, white"
|
||||||
color-link divider "blue"
|
color-link divider "blue"
|
||||||
|
color-link match-brace "black,cyan"
|
||||||
|
color-link tab-error "brightred"
|
||||||
|
color-link trailingws "brightred"
|
||||||
|
|
|
@ -38,3 +38,6 @@ color-link color-column "#f26522"
|
||||||
color-link constant.bool "bold #55ffff"
|
color-link constant.bool "bold #55ffff"
|
||||||
color-link constant.bool.true "bold #85ff85"
|
color-link constant.bool.true "bold #85ff85"
|
||||||
color-link constant.bool.false "bold #ff8585"
|
color-link constant.bool.false "bold #ff8585"
|
||||||
|
color-link match-brace "#1e2124,#55ffff"
|
||||||
|
color-link tab-error "#d75f5f"
|
||||||
|
color-link trailingws "#d75f5f"
|
||||||
|
|
|
@ -29,3 +29,6 @@ color-link color-column "#2C2C2C"
|
||||||
color-link type.extended "default"
|
color-link type.extended "default"
|
||||||
#color-link symbol.brackets "default"
|
#color-link symbol.brackets "default"
|
||||||
color-link symbol.tag "#AE81FF,#242424"
|
color-link symbol.tag "#AE81FF,#242424"
|
||||||
|
color-link match-brace "#242424,#7A9EC2"
|
||||||
|
color-link tab-error "#D75F5F"
|
||||||
|
color-link trailingws "#D75F5F"
|
||||||
|
|
|
@ -1,31 +1 @@
|
||||||
color-link default "#F8F8F2,#282828"
|
include "monokai"
|
||||||
color-link comment "#75715E,#282828"
|
|
||||||
color-link identifier "#66D9EF,#282828"
|
|
||||||
color-link constant "#AE81FF,#282828"
|
|
||||||
color-link constant.string "#E6DB74,#282828"
|
|
||||||
color-link constant.string.char "#BDE6AD,#282828"
|
|
||||||
color-link statement "#F92672,#282828"
|
|
||||||
color-link symbol.operator "#F92671,#282828"
|
|
||||||
color-link preproc "#CB4B16,#282828"
|
|
||||||
color-link type "#66D9EF,#282828"
|
|
||||||
color-link special "#A6E22E,#282828"
|
|
||||||
color-link underlined "#D33682,#282828"
|
|
||||||
color-link error "bold #CB4B16,#282828"
|
|
||||||
color-link todo "bold #D33682,#282828"
|
|
||||||
color-link hlsearch "#282828,#E6DB74"
|
|
||||||
color-link statusline "#282828,#F8F8F2"
|
|
||||||
color-link tabbar "#282828,#F8F8F2"
|
|
||||||
color-link indent-char "#505050,#282828"
|
|
||||||
color-link line-number "#AAAAAA,#323232"
|
|
||||||
color-link current-line-number "#AAAAAA,#282828"
|
|
||||||
color-link diff-added "#00AF00"
|
|
||||||
color-link diff-modified "#FFAF00"
|
|
||||||
color-link diff-deleted "#D70000"
|
|
||||||
color-link gutter-error "#CB4B16,#282828"
|
|
||||||
color-link gutter-warning "#E6DB74,#282828"
|
|
||||||
color-link cursor-line "#323232"
|
|
||||||
color-link color-column "#323232"
|
|
||||||
#No extended types; Plain brackets.
|
|
||||||
color-link type.extended "default"
|
|
||||||
#color-link symbol.brackets "default"
|
|
||||||
color-link symbol.tag "#AE81FF,#282828"
|
|
||||||
|
|
|
@ -43,3 +43,7 @@ color-link cursor-line "#44475A,#F8F8F2"
|
||||||
color-link color-column "#44475A"
|
color-link color-column "#44475A"
|
||||||
color-link type.extended "default"
|
color-link type.extended "default"
|
||||||
|
|
||||||
|
color-link match-brace "#282A36,#FF79C6"
|
||||||
|
|
||||||
|
color-link tab-error "#D75F5F"
|
||||||
|
color-link trailingws "#D75F5F"
|
||||||
|
|
|
@ -33,3 +33,6 @@ color-link type "bold #3cc83c,#001e28"
|
||||||
color-link type.keyword "bold #5aaae6,#001e28"
|
color-link type.keyword "bold #5aaae6,#001e28"
|
||||||
color-link type.extended "#ffffff,#001e28"
|
color-link type.extended "#ffffff,#001e28"
|
||||||
color-link underlined "#608b4e,#001e28"
|
color-link underlined "#608b4e,#001e28"
|
||||||
|
color-link match-brace "#001e28,#5aaae6"
|
||||||
|
color-link tab-error "#d75f5f"
|
||||||
|
color-link trailingws "#d75f5f"
|
||||||
|
|
|
@ -33,3 +33,6 @@ color-link type "bold #004080,#f0f0f0"
|
||||||
color-link type.keyword "bold #780050,#f0f0f0"
|
color-link type.keyword "bold #780050,#f0f0f0"
|
||||||
color-link type.extended "#000000,#f0f0f0"
|
color-link type.extended "#000000,#f0f0f0"
|
||||||
color-link underlined "#3f7f5f,#f0f0f0"
|
color-link underlined "#3f7f5f,#f0f0f0"
|
||||||
|
color-link match-brace "#f0f0f0,#780050"
|
||||||
|
color-link tab-error "#ff8787"
|
||||||
|
color-link trailingws "#ff8787"
|
||||||
|
|
|
@ -33,3 +33,6 @@ color-link type "bold #3cc83c,#2d0023"
|
||||||
color-link type.keyword "bold #5aaae6,#2d0023"
|
color-link type.keyword "bold #5aaae6,#2d0023"
|
||||||
color-link type.extended "#ffffff,#2d0023"
|
color-link type.extended "#ffffff,#2d0023"
|
||||||
color-link underlined "#886484,#2d0023"
|
color-link underlined "#886484,#2d0023"
|
||||||
|
color-link match-brace "#2d0023,#5aaae6"
|
||||||
|
color-link tab-error "#d75f5f"
|
||||||
|
color-link trailingws "#d75f5f"
|
||||||
|
|
|
@ -24,3 +24,6 @@ color-link diff-modified "yellow"
|
||||||
color-link diff-deleted "red"
|
color-link diff-deleted "red"
|
||||||
color-link gutter-error ",red"
|
color-link gutter-error ",red"
|
||||||
color-link gutter-warning "red"
|
color-link gutter-warning "red"
|
||||||
|
color-link match-brace "black,cyan"
|
||||||
|
color-link tab-error "brightred"
|
||||||
|
color-link trailingws "brightred"
|
||||||
|
|
|
@ -24,3 +24,6 @@ color-link gutter-warning "#EDB443,#11151C"
|
||||||
color-link cursor-line "#091F2E"
|
color-link cursor-line "#091F2E"
|
||||||
color-link color-column "#11151C"
|
color-link color-column "#11151C"
|
||||||
color-link symbol "#99D1CE,#0C1014"
|
color-link symbol "#99D1CE,#0C1014"
|
||||||
|
color-link match-brace "#0C1014,#D26937"
|
||||||
|
color-link tab-error "#D75F5F"
|
||||||
|
color-link trailingws "#D75F5F"
|
||||||
|
|
|
@ -24,3 +24,6 @@ color-link cursor-line "#3c3836"
|
||||||
color-link color-column "#79740e"
|
color-link color-column "#79740e"
|
||||||
color-link statusline "#ebdbb2,#665c54"
|
color-link statusline "#ebdbb2,#665c54"
|
||||||
color-link tabbar "#ebdbb2,#665c54"
|
color-link tabbar "#ebdbb2,#665c54"
|
||||||
|
color-link match-brace "#282828,#d3869b"
|
||||||
|
color-link tab-error "#d75f5f"
|
||||||
|
color-link trailingws "#d75f5f"
|
||||||
|
|
|
@ -21,3 +21,6 @@ color-link cursor-line "237"
|
||||||
color-link color-column "237"
|
color-link color-column "237"
|
||||||
color-link statusline "223,237"
|
color-link statusline "223,237"
|
||||||
color-link tabbar "223,237"
|
color-link tabbar "223,237"
|
||||||
|
color-link match-brace "235,72"
|
||||||
|
color-link tab-error "167"
|
||||||
|
color-link trailingws "167"
|
||||||
|
|
|
@ -20,6 +20,7 @@ color-link identifier.macro "#FFCB6B,#263238"
|
||||||
color-link indent-char "#505050,#263238"
|
color-link indent-char "#505050,#263238"
|
||||||
color-link line-number "#656866,#283942"
|
color-link line-number "#656866,#283942"
|
||||||
color-link preproc "#C792EA,#263238"
|
color-link preproc "#C792EA,#263238"
|
||||||
|
color-link scrollbar "#80DEEA,#283942"
|
||||||
color-link special "#C792EA,#263238"
|
color-link special "#C792EA,#263238"
|
||||||
color-link statement "#C792EA,#263238"
|
color-link statement "#C792EA,#263238"
|
||||||
color-link statusline "#80DEEA,#3b4d56"
|
color-link statusline "#80DEEA,#3b4d56"
|
||||||
|
@ -30,3 +31,6 @@ color-link tabbar "#80DEEA,#3b4d56"
|
||||||
color-link todo "bold #C792EA,#263238"
|
color-link todo "bold #C792EA,#263238"
|
||||||
color-link type "#FFCB6B,#263238"
|
color-link type "#FFCB6B,#263238"
|
||||||
color-link underlined "underline #EEFFFF,#263238"
|
color-link underlined "underline #EEFFFF,#263238"
|
||||||
|
color-link match-brace "#263238,#C792EA"
|
||||||
|
color-link tab-error "#D75F5F"
|
||||||
|
color-link trailingws "#D75F5F"
|
||||||
|
|
|
@ -23,3 +23,6 @@ color-link gutter-error "#CB4B16"
|
||||||
color-link gutter-warning "#E6DB74"
|
color-link gutter-warning "#E6DB74"
|
||||||
color-link cursor-line "#323232"
|
color-link cursor-line "#323232"
|
||||||
color-link color-column "#323232"
|
color-link color-column "#323232"
|
||||||
|
color-link match-brace "#1D0000,#AE81FF"
|
||||||
|
color-link tab-error "#D75F5F"
|
||||||
|
color-link trailingws "#D75F5F"
|
||||||
|
|
|
@ -29,3 +29,6 @@ color-link color-column "#323232"
|
||||||
color-link type.extended "default"
|
color-link type.extended "default"
|
||||||
#color-link symbol.brackets "default"
|
#color-link symbol.brackets "default"
|
||||||
color-link symbol.tag "#AE81FF,#282828"
|
color-link symbol.tag "#AE81FF,#282828"
|
||||||
|
color-link match-brace "#282828,#AE81FF"
|
||||||
|
color-link tab-error "#D75F5F"
|
||||||
|
color-link trailingws "#D75F5F"
|
||||||
|
|
|
@ -34,3 +34,6 @@ color-link todo "#8B98AB"
|
||||||
color-link type "#66D9EF"
|
color-link type "#66D9EF"
|
||||||
color-link type.keyword "#C678DD"
|
color-link type.keyword "#C678DD"
|
||||||
color-link underlined "#8996A8"
|
color-link underlined "#8996A8"
|
||||||
|
color-link match-brace "#21252C,#C678DD"
|
||||||
|
color-link tab-error "#D75F5F"
|
||||||
|
color-link trailingws "#D75F5F"
|
||||||
|
|
|
@ -28,6 +28,10 @@ color-link tabbar "bold #b1b1b1,#232323"
|
||||||
color-link cursor-line "#353535"
|
color-link cursor-line "#353535"
|
||||||
color-link color-column "#353535"
|
color-link color-column "#353535"
|
||||||
color-link space "underline #e6e1dc,#2b2b2b"
|
color-link space "underline #e6e1dc,#2b2b2b"
|
||||||
|
color-link tab-error "#d75f5f"
|
||||||
|
color-link trailingws "#d75f5f"
|
||||||
|
|
||||||
#the Python syntax definition are wrong. This is not how you should do decorators!
|
#the Python syntax definition are wrong. This is not how you should do decorators!
|
||||||
color-link brightgreen "#edb753,#2b2b2b"
|
color-link brightgreen "#edb753,#2b2b2b"
|
||||||
|
|
||||||
|
color-link match-brace "#2b2b2b,#a5c261"
|
||||||
|
|
|
@ -10,6 +10,7 @@ color-link ignore "default"
|
||||||
color-link error ",brightred"
|
color-link error ",brightred"
|
||||||
color-link todo ",brightyellow"
|
color-link todo ",brightyellow"
|
||||||
color-link hlsearch "black,yellow"
|
color-link hlsearch "black,yellow"
|
||||||
|
color-link statusline "black,white"
|
||||||
color-link indent-char "black"
|
color-link indent-char "black"
|
||||||
color-link line-number "yellow"
|
color-link line-number "yellow"
|
||||||
color-link current-line-number "red"
|
color-link current-line-number "red"
|
||||||
|
@ -27,3 +28,6 @@ color-link type.extended "default"
|
||||||
color-link symbol.brackets "default"
|
color-link symbol.brackets "default"
|
||||||
#Color shebangs the comment color
|
#Color shebangs the comment color
|
||||||
color-link preproc.shebang "comment"
|
color-link preproc.shebang "comment"
|
||||||
|
color-link match-brace ",magenta"
|
||||||
|
color-link tab-error "brightred"
|
||||||
|
color-link trailingws "brightred"
|
||||||
|
|
|
@ -26,3 +26,6 @@ color-link cursor-line "#003541"
|
||||||
color-link color-column "#003541"
|
color-link color-column "#003541"
|
||||||
color-link type.extended "#839496,#002833"
|
color-link type.extended "#839496,#002833"
|
||||||
color-link symbol.brackets "#839496,#002833"
|
color-link symbol.brackets "#839496,#002833"
|
||||||
|
color-link match-brace "#002833,#268BD2"
|
||||||
|
color-link tab-error "#D75F5F"
|
||||||
|
color-link trailingws "#D75F5F"
|
||||||
|
|
|
@ -25,3 +25,6 @@ color-link cursor-line "black"
|
||||||
color-link color-column "black"
|
color-link color-column "black"
|
||||||
color-link type.extended "default"
|
color-link type.extended "default"
|
||||||
color-link symbol.brackets "default"
|
color-link symbol.brackets "default"
|
||||||
|
color-link match-brace ",blue"
|
||||||
|
color-link tab-error "brightred"
|
||||||
|
color-link trailingws "brightred"
|
||||||
|
|
|
@ -24,3 +24,6 @@ color-link gutter-warning "88"
|
||||||
color-link cursor-line "229"
|
color-link cursor-line "229"
|
||||||
#color-link color-column "196"
|
#color-link color-column "196"
|
||||||
color-link current-line-number "246"
|
color-link current-line-number "246"
|
||||||
|
color-line match-brace "230,22"
|
||||||
|
color-link tab-error "210"
|
||||||
|
color-link trailingws "210"
|
||||||
|
|
|
@ -35,3 +35,6 @@ color-link todo "#8B98AB"
|
||||||
color-link type "#F9EE98"
|
color-link type "#F9EE98"
|
||||||
color-link type.keyword "#CDA869"
|
color-link type.keyword "#CDA869"
|
||||||
color-link underlined "#8996A8"
|
color-link underlined "#8996A8"
|
||||||
|
color-link match-brace "#141414,#E0C589"
|
||||||
|
color-link tab-error "#D75F5F"
|
||||||
|
color-link trailingws "#D75F5F"
|
||||||
|
|
|
@ -25,3 +25,6 @@ color-link gutter-warning "174,237"
|
||||||
color-link cursor-line "238"
|
color-link cursor-line "238"
|
||||||
color-link color-column "238"
|
color-link color-column "238"
|
||||||
color-link current-line-number "188,237"
|
color-link current-line-number "188,237"
|
||||||
|
color-link match-brace "237,223"
|
||||||
|
color-link tab-error "167"
|
||||||
|
color-link trailingws "167"
|
||||||
|
|
|
@ -8,7 +8,7 @@ This help page aims to cover two aspects of micro's syntax highlighting engine:
|
||||||
|
|
||||||
## Colorschemes
|
## Colorschemes
|
||||||
|
|
||||||
To change your colorscheme, press Ctrl-e in micro to bring up the command
|
To change your colorscheme, press `Ctrl-e` in micro to bring up the command
|
||||||
prompt, and type:
|
prompt, and type:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -194,6 +194,10 @@ Here is a list of the colorscheme groups that you can use:
|
||||||
* divider (Color of the divider between vertical splits)
|
* divider (Color of the divider between vertical splits)
|
||||||
* message (Color of messages in the bottom line of the screen)
|
* message (Color of messages in the bottom line of the screen)
|
||||||
* error-message (Color of error messages in the bottom line of the screen)
|
* error-message (Color of error messages in the bottom line of the screen)
|
||||||
|
* match-brace (Color of matching brackets when `matchbracestyle` is set to `highlight`)
|
||||||
|
* hlsearch (Color of highlighted search results when `hlsearch` is enabled)
|
||||||
|
* tab-error (Color of tab vs space errors when `hltaberrors` is enabled)
|
||||||
|
* trailingws (Color of trailing whitespaces when `hltrailingws` is enabled)
|
||||||
|
|
||||||
Colorschemes must be placed in the `~/.config/micro/colorschemes` directory to
|
Colorschemes must be placed in the `~/.config/micro/colorschemes` directory to
|
||||||
be used.
|
be used.
|
||||||
|
@ -210,7 +214,7 @@ safe and recommended to use subgroups in your custom syntax files.
|
||||||
For example if `constant.string` is found in your colorscheme, micro will us
|
For example if `constant.string` is found in your colorscheme, micro will us
|
||||||
that for highlighting strings. If it's not found, it will use constant instead.
|
that for highlighting strings. If it's not found, it will use constant instead.
|
||||||
Micro tries to match the largest set of groups it can find in the colorscheme
|
Micro tries to match the largest set of groups it can find in the colorscheme
|
||||||
definitions, so if, for examle `constant.bool.true` is found then micro will
|
definitions, so if, for example `constant.bool.true` is found then micro will
|
||||||
use that. If `constant.bool.true` is not found but `constant.bool` is found
|
use that. If `constant.bool.true` is not found but `constant.bool` is found
|
||||||
micro will use `constant.bool`. If not, it uses `constant`.
|
micro will use `constant.bool`. If not, it uses `constant`.
|
||||||
|
|
||||||
|
@ -236,6 +240,12 @@ Here's a list of subgroups used in micro's built-in syntax files.
|
||||||
|
|
||||||
In the future, plugins may also be able to use color groups for styling.
|
In the future, plugins may also be able to use color groups for styling.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Last but not least it's even possible to use `include` followed by the
|
||||||
|
colorscheme name as string to include a different colorscheme within a new one.
|
||||||
|
Additionally the groups can then be extended or overwritten. The `default.micro`
|
||||||
|
theme can be seen as an example, which links to the chosen default colorscheme.
|
||||||
|
|
||||||
## Syntax files
|
## Syntax files
|
||||||
|
|
||||||
|
@ -267,8 +277,9 @@ detect:
|
||||||
```
|
```
|
||||||
|
|
||||||
Micro will match this regex against a given filename to detect the filetype.
|
Micro will match this regex against a given filename to detect the filetype.
|
||||||
You may also provide an optional `header` regex that will check the first line
|
|
||||||
of the file. For example:
|
In addition to the `filename` regex (or even instead of it) you can provide
|
||||||
|
a `header` regex that will check the first line of the file. For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
detect:
|
detect:
|
||||||
|
@ -276,6 +287,32 @@ detect:
|
||||||
header: "%YAML"
|
header: "%YAML"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This is useful in cases when the given file name is not sufficient to determine
|
||||||
|
the filetype, e.g. with the above example, if a YAML file has no `.yaml`
|
||||||
|
extension but may contain a `%YAML` directive in its first line.
|
||||||
|
|
||||||
|
`filename` takes precedence over `header`, i.e. if there is a syntax file that
|
||||||
|
matches the file with a filetype by the `filename` and another syntax file that
|
||||||
|
matches the same file with another filetype by the `header`, the first filetype
|
||||||
|
will be used.
|
||||||
|
|
||||||
|
Finally, in addition to `filename` and/or `header` (but not instead of them)
|
||||||
|
you may also provide an optional `signature` regex which is useful for resolving
|
||||||
|
ambiguities when there are multiple syntax files matching the same file with
|
||||||
|
different filetypes. If a `signature` regex is given, micro will match a certain
|
||||||
|
amount of first lines in the file (this amount is determined by the `detectlimit`
|
||||||
|
option) against this regex, and if any of the lines match, this syntax file's
|
||||||
|
filetype will be preferred over other matching filetypes.
|
||||||
|
|
||||||
|
For example, to distinguish C++ header files from C and Objective-C header files
|
||||||
|
that have the same `.h` extension:
|
||||||
|
|
||||||
|
```
|
||||||
|
detect:
|
||||||
|
filename: "\\.c(c|pp|xx)$|\\.h(h|pp|xx)?$"
|
||||||
|
signature: "namespace|template|public|protected|private"
|
||||||
|
```
|
||||||
|
|
||||||
### Syntax rules
|
### Syntax rules
|
||||||
|
|
||||||
Next you must provide the syntax highlighting rules. There are two types of
|
Next you must provide the syntax highlighting rules. There are two types of
|
||||||
|
@ -356,15 +393,28 @@ example, the following is possible for html:
|
||||||
- include: "css"
|
- include: "css"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Syntax file headers
|
Note that nested include (i.e. including syntax files that include other syntax
|
||||||
|
files) is not supported yet.
|
||||||
|
|
||||||
Syntax file headers are an optimization and it is likely you do not need to
|
### Default syntax highlighting
|
||||||
worry about them.
|
|
||||||
|
|
||||||
Syntax file headers are files that contain only the filetype and the detection
|
If micro cannot detect the filetype of the file, it falls back to using the
|
||||||
regular expressions for a given syntax file. They have a `.hdr` suffix and are
|
default syntax highlighting for it, which highlights just the bare minimum:
|
||||||
used by default only for the pre-installed syntax files. Header files allow
|
email addresses, URLs etc.
|
||||||
micro to parse the syntax files much faster when checking the filetype of a
|
|
||||||
certain file. Custom syntax files may provide header files in
|
Just like in other cases, you can override the default highlighting by adding
|
||||||
`~/.config/micro/syntax` as well but it is not necessary (only do this if you
|
your own custom `default.yaml` file to `~/.config/micro/syntax`.
|
||||||
have many (100+) custom syntax files and want to improve performance).
|
|
||||||
|
For example, if you work with various config files that use the `#` sign to mark
|
||||||
|
the beginning of a comment, you can use the following custom `default.yaml` to
|
||||||
|
highlight those comments by default:
|
||||||
|
|
||||||
|
```
|
||||||
|
filetype: unknown
|
||||||
|
|
||||||
|
detect:
|
||||||
|
filename: ""
|
||||||
|
|
||||||
|
rules:
|
||||||
|
- comment: "(^|\\s)#.*$"
|
||||||
|
```
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Command bar
|
# Command bar
|
||||||
|
|
||||||
The command bar is opened by pressing Ctrl-e. It is a single-line buffer,
|
The command bar is opened by pressing `Ctrl-e`. It is a single-line buffer,
|
||||||
meaning that all keybindings from a normal buffer are supported (as well
|
meaning that all keybindings from a normal buffer are supported (as well
|
||||||
as mouse and selection).
|
as mouse and selection).
|
||||||
|
|
||||||
|
@ -21,21 +21,26 @@ quotes here but these are not necessary when entering the command in micro.
|
||||||
This command will modify `bindings.json` and overwrite any bindings to
|
This command will modify `bindings.json` and overwrite any bindings to
|
||||||
`key` that already exist.
|
`key` that already exist.
|
||||||
|
|
||||||
* `help 'topic'?`: opens the corresponding help topic. If no topic is provided
|
* `help ['topic']`: opens the corresponding help topic. If no topic is provided
|
||||||
opens the default help screen. Help topics are stored as `.md` files in the
|
opens the default help screen. Help topics are stored as `.md` files in the
|
||||||
`runtime/help` directory of the source tree, which is embedded in the final
|
`runtime/help` directory of the source tree, which is embedded in the final
|
||||||
binary.
|
binary.
|
||||||
|
|
||||||
* `save 'filename'?`: saves the current buffer. If the file is provided it
|
* `save ['filename']`: saves the current buffer. If the file is provided it
|
||||||
will 'save as' the filename.
|
will 'save as' the filename.
|
||||||
|
|
||||||
* `quit`: quits micro.
|
* `quit`: quits micro.
|
||||||
|
|
||||||
* `goto 'line'`: jumps to the given line number. A negative number can be
|
* `goto 'line[:col]'`: goes to the given absolute line (and optional column)
|
||||||
passed to jump inward from the end of the file; for example, -5 jumps
|
number.
|
||||||
to the 5th-last line in the file.
|
A negative number can be passed to go inward from the end of the file.
|
||||||
|
Example: -5 goes to the 5th-last line in the file.
|
||||||
|
|
||||||
* `replace 'search' 'value' 'flags'?`: This will replace `search` with `value`.
|
* `jump 'line[:col]'`: goes to the given relative number from the current
|
||||||
|
line (and optional absolute column) number.
|
||||||
|
Example: -5 jumps 5 lines up in the file, while (+)3 jumps 3 lines down.
|
||||||
|
|
||||||
|
* `replace 'search' 'value' ['flags']`: This will replace `search` with `value`.
|
||||||
The `flags` are optional. Possible flags are:
|
The `flags` are optional. Possible flags are:
|
||||||
* `-a`: Replace all occurrences at once
|
* `-a`: Replace all occurrences at once
|
||||||
* `-l`: Do a literal search instead of a regex search
|
* `-l`: Do a literal search instead of a regex search
|
||||||
|
@ -43,6 +48,12 @@ quotes here but these are not necessary when entering the command in micro.
|
||||||
Note that `search` must be a valid regex (unless `-l` is passed). If one
|
Note that `search` must be a valid regex (unless `-l` is passed). If one
|
||||||
of the arguments does not have any spaces in it, you may omit the quotes.
|
of the arguments does not have any spaces in it, you may omit the quotes.
|
||||||
|
|
||||||
|
In case the search is done non-literal (without `-l`), the 'value'
|
||||||
|
is interpreted as a template:
|
||||||
|
* `$3` or `${3}` substitutes the submatch of the 3rd (capturing group)
|
||||||
|
* `$foo` or `${foo}` substitutes the submatch of the (?P<foo>named group)
|
||||||
|
* You have to write `$$` to substitute a literal dollar.
|
||||||
|
|
||||||
* `replaceall 'search' 'value'`: this will replace all occurrences of `search`
|
* `replaceall 'search' 'value'`: this will replace all occurrences of `search`
|
||||||
with `value` without user confirmation.
|
with `value` without user confirmation.
|
||||||
|
|
||||||
|
@ -60,15 +71,15 @@ quotes here but these are not necessary when entering the command in micro.
|
||||||
* `run 'sh-command'`: runs the given shell command in the background. The
|
* `run 'sh-command'`: runs the given shell command in the background. The
|
||||||
command's output will be displayed in one line when it finishes running.
|
command's output will be displayed in one line when it finishes running.
|
||||||
|
|
||||||
* `vsplit 'filename'`: opens a vertical split with `filename`. If no filename
|
* `vsplit ['filename']`: opens a vertical split with `filename`. If no filename
|
||||||
is provided, a vertical split is opened with an empty buffer.
|
is provided, a vertical split is opened with an empty buffer.
|
||||||
|
|
||||||
* `hsplit 'filename'`: same as `vsplit` but opens a horizontal split instead
|
* `hsplit ['filename']`: same as `vsplit` but opens a horizontal split instead
|
||||||
of a vertical split.
|
of a vertical split.
|
||||||
|
|
||||||
* `tab 'filename'`: opens the given file in a new tab.
|
* `tab ['filename']`: opens the given file in a new tab.
|
||||||
|
|
||||||
* `tabmove '[-+]?n'`: Moves the active tab to another slot. `n` is an integer.
|
* `tabmove '[-+]n'`: Moves the active tab to another slot. `n` is an integer.
|
||||||
If `n` is prefixed with `-` or `+`, then it represents a relative position
|
If `n` is prefixed with `-` or `+`, then it represents a relative position
|
||||||
(e.g. `tabmove +2` moves the tab to the right by `2`). If `n` has no prefix,
|
(e.g. `tabmove +2` moves the tab to the right by `2`). If `n` has no prefix,
|
||||||
it represents an absolute position (e.g. `tabmove 2` moves the tab to slot `2`).
|
it represents an absolute position (e.g. `tabmove 2` moves the tab to slot `2`).
|
||||||
|
@ -89,7 +100,7 @@ quotes here but these are not necessary when entering the command in micro.
|
||||||
|
|
||||||
* `plugin remove 'pl'`: remove a plugin.
|
* `plugin remove 'pl'`: remove a plugin.
|
||||||
|
|
||||||
* `plugin update 'pl'`: update a plugin (if no arguments are provided
|
* `plugin update ['pl']`: update a plugin (if no arguments are provided
|
||||||
updates all plugins).
|
updates all plugins).
|
||||||
|
|
||||||
* `plugin search 'pl'`: search available plugins for a keyword.
|
* `plugin search 'pl'`: search available plugins for a keyword.
|
||||||
|
@ -114,10 +125,10 @@ quotes here but these are not necessary when entering the command in micro.
|
||||||
the terminal and helps you see which bindings aren't possible and why. This
|
the terminal and helps you see which bindings aren't possible and why. This
|
||||||
is most useful for debugging keybindings.
|
is most useful for debugging keybindings.
|
||||||
|
|
||||||
* `showkey`: Show the action(s) bound to a given key. For example
|
* `showkey 'key'`: Show the action(s) bound to a given key. For example
|
||||||
running `> showkey Ctrl-c` will display `Copy`.
|
running `> showkey Ctrl-c` will display `Copy`.
|
||||||
|
|
||||||
* `term exec?`: Open a terminal emulator running the given executable. If no
|
* `term ['exec']`: Open a terminal emulator running the given executable. If no
|
||||||
executable is given, this will open the default shell in the terminal
|
executable is given, this will open the default shell in the terminal
|
||||||
emulator.
|
emulator.
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,13 @@ is limited support among terminal emulators for the terminal clipboard
|
||||||
(which uses the OSC 52 protocol to communicate clipboard contents).
|
(which uses the OSC 52 protocol to communicate clipboard contents).
|
||||||
Here is a list of terminal emulators and their status:
|
Here is a list of terminal emulators and their status:
|
||||||
|
|
||||||
* Kitty: supported, but only writing is enabled by default. To enable
|
* `Kitty`: supported, but only writing is enabled by default. To enable
|
||||||
reading, add `read-primary` and `read-clipboard` to the
|
reading, add `read-primary` and `read-clipboard` to the
|
||||||
`clipboard_control` option.
|
`clipboard_control` option.
|
||||||
|
|
||||||
* iTerm2: only copying (writing to clipboard) is supported. Must be enabled in
|
* `iTerm2`: only copying (writing to clipboard) is supported. Must be enabled in
|
||||||
`Preferences->General-> Selection->Applications in terminal may access clipboard`.
|
`Preferences->General-> Selection->Applications in terminal may access clipboard`.
|
||||||
You can use Command-v to paste.
|
You can use `Command-v` to paste.
|
||||||
|
|
||||||
* `st`: supported.
|
* `st`: supported.
|
||||||
|
|
||||||
|
@ -31,10 +31,14 @@ Here is a list of terminal emulators and their status:
|
||||||
|
|
||||||
* `gnome-terminal`: does not support OSC 52.
|
* `gnome-terminal`: does not support OSC 52.
|
||||||
|
|
||||||
* `alacritty`: supported.
|
* `alacritty`: supported. Since 0.13.0, reading has been disabled by default.
|
||||||
|
To reenable it, set the `terminal.osc52` option to `CopyPaste`.
|
||||||
|
|
||||||
* `foot`: supported.
|
* `foot`: supported.
|
||||||
|
|
||||||
|
* `wezterm`: only copying (writing to clipboard) is supported.
|
||||||
|
|
||||||
|
|
||||||
**Summary:** If you want copy and paste to work over SSH, then you
|
**Summary:** If you want copy and paste to work over SSH, then you
|
||||||
should set `clipboard` to `terminal`, and make sure your terminal
|
should set `clipboard` to `terminal`, and make sure your terminal
|
||||||
supports OSC 52.
|
supports OSC 52.
|
||||||
|
@ -45,12 +49,12 @@ supports OSC 52.
|
||||||
|
|
||||||
The recommended method of pasting is the following:
|
The recommended method of pasting is the following:
|
||||||
|
|
||||||
* If you are not working over SSH, use the micro keybinding (Ctrl-v
|
* If you are not working over SSH, use the micro keybinding (`Ctrl-v`
|
||||||
by default) to perform pastes. If on Linux, install `xclip` or
|
by default) to perform pastes. If on Linux, install `xclip` or
|
||||||
`xsel` beforehand.
|
`xsel` beforehand.
|
||||||
|
|
||||||
* If you are working over SSH, use the terminal keybinding
|
* If you are working over SSH, use the terminal keybinding
|
||||||
(Ctrl-Shift-v or Command-v) to perform pastes. If your terminal
|
(`Ctrl-Shift-v` or `Command-v`) to perform pastes. If your terminal
|
||||||
does not support bracketed paste, when performing a paste first
|
does not support bracketed paste, when performing a paste first
|
||||||
enable the `paste` option, and when finished disable the option.
|
enable the `paste` option, and when finished disable the option.
|
||||||
|
|
||||||
|
@ -59,8 +63,8 @@ The recommended method of pasting is the following:
|
||||||
Micro is an application that runs within the terminal. This means
|
Micro is an application that runs within the terminal. This means
|
||||||
that the terminal sends micro events, such as key events, mouse
|
that the terminal sends micro events, such as key events, mouse
|
||||||
events, resize events, and paste events. Micro's default keybinding
|
events, resize events, and paste events. Micro's default keybinding
|
||||||
for paste is Ctrl-v. This means that when micro receives the key
|
for paste is `Ctrl-v`. This means that when micro receives the key
|
||||||
event saying Ctrl-v has been pressed from the terminal, it will
|
event saying `Ctrl-v` has been pressed from the terminal, it will
|
||||||
attempt to access the system clipboard and effect a paste. The
|
attempt to access the system clipboard and effect a paste. The
|
||||||
system clipboard will be accessed through `pbpaste` on MacOS
|
system clipboard will be accessed through `pbpaste` on MacOS
|
||||||
(installed by default), `xclip` or `xsel` on Linux (these
|
(installed by default), `xclip` or `xsel` on Linux (these
|
||||||
|
@ -73,8 +77,8 @@ For certain keypresses, the terminal will not send an event to
|
||||||
micro and will instead do something itself. In this document,
|
micro and will instead do something itself. In this document,
|
||||||
such keypresses will be called "terminal keybindings." Often
|
such keypresses will be called "terminal keybindings." Often
|
||||||
there will be a terminal keybinding for pasting and copying. On
|
there will be a terminal keybinding for pasting and copying. On
|
||||||
MacOS these are Command-v and Command-c and on Linux Ctrl-Shift-v
|
MacOS these are Command-v and Command-c and on Linux `Ctrl-Shift-v`
|
||||||
and Ctrl-Shift-c. When the terminal keybinding for paste is
|
and `Ctrl-Shift-c`. When the terminal keybinding for paste is
|
||||||
executed, your terminal will access the system clipboard, and send
|
executed, your terminal will access the system clipboard, and send
|
||||||
micro either a paste event or a list of key events (one key for each
|
micro either a paste event or a list of key events (one key for each
|
||||||
character in the paste), depending on whether or not your terminal
|
character in the paste), depending on whether or not your terminal
|
||||||
|
@ -97,7 +101,7 @@ entered.
|
||||||
|
|
||||||
When working over SSH, micro is running on the remote machine and
|
When working over SSH, micro is running on the remote machine and
|
||||||
your terminal is running on your local machine. Therefore if you
|
your terminal is running on your local machine. Therefore if you
|
||||||
would like to paste, using Ctrl-v (micro's keybinding) will not
|
would like to paste, using `Ctrl-v` (micro's keybinding) will not
|
||||||
work because when micro attempts to access the system clipboard,
|
work because when micro attempts to access the system clipboard,
|
||||||
it will access the remote machine's clipboard rather than the local
|
it will access the remote machine's clipboard rather than the local
|
||||||
machine's clipboard. On the other hand, the terminal keybinding
|
machine's clipboard. On the other hand, the terminal keybinding
|
||||||
|
@ -110,12 +114,12 @@ the network as a paste event, which is what you want.
|
||||||
|
|
||||||
The recommended method of copying is the following:
|
The recommended method of copying is the following:
|
||||||
|
|
||||||
* If you are not working over SSH, use the micro keybinding (Ctrl-c by
|
* If you are not working over SSH, use the micro keybinding (`Ctrl-c` by
|
||||||
default) to perform copies. If on Linux, install `xclip` or `xsel`
|
default) to perform copies. If on Linux, install `xclip` or `xsel`
|
||||||
beforehand.
|
beforehand.
|
||||||
|
|
||||||
* If you are working over SSH, use the terminal keybinding
|
* If you are working over SSH, use the terminal keybinding
|
||||||
(Ctrl-Shift-c or Command-c) to perform copies. You must first disable
|
(`Ctrl-Shift-c` or `Command-c`) to perform copies. You must first disable
|
||||||
the `mouse` option to perform a terminal selection, and you may wish
|
the `mouse` option to perform a terminal selection, and you may wish
|
||||||
to disable line numbers and diff indicators (`ruler` and `diffgutter`
|
to disable line numbers and diff indicators (`ruler` and `diffgutter`
|
||||||
options) and close other splits. This method will only be able to copy
|
options) and close other splits. This method will only be able to copy
|
||||||
|
@ -126,14 +130,14 @@ Copying follows a similar discussion to the one above about pasting.
|
||||||
The primary difference is before performing a copy, the application
|
The primary difference is before performing a copy, the application
|
||||||
doing the copy must be told what text needs to be copied.
|
doing the copy must be told what text needs to be copied.
|
||||||
|
|
||||||
Micro has a keybinding (Ctrl-c) for copying and will access the system
|
Micro has a keybinding (`Ctrl-c`) for copying and will access the system
|
||||||
clipboard to perform the copy. The text that micro will copy into is
|
clipboard to perform the copy. The text that micro will copy into is
|
||||||
the text that is currently selected in micro (usually such text is
|
the text that is currently selected in micro (usually such text is
|
||||||
displayed with a white background). When the `mouse` option is enabled,
|
displayed with a white background). When the `mouse` option is enabled,
|
||||||
the mouse can be used to select text, as well as other keybindings,
|
the mouse can be used to select text, as well as other keybindings,
|
||||||
such as ShiftLeft, etc...
|
such as ShiftLeft, etc...
|
||||||
|
|
||||||
The terminal also has a keybinding (Ctrl-Shift-c or Command-c) to perform
|
The terminal also has a keybinding (`Ctrl-Shift-c` or `Command-c`) to perform
|
||||||
a copy, and the text that it copies is the text selected by the terminal's
|
a copy, and the text that it copies is the text selected by the terminal's
|
||||||
selection (*not* micro's selection). To select text with the terminal
|
selection (*not* micro's selection). To select text with the terminal
|
||||||
selection, micro's mouse support must first be disabled by turning the
|
selection, micro's mouse support must first be disabled by turning the
|
||||||
|
|
|
@ -52,9 +52,9 @@ can change it!
|
||||||
| Ctrl-n | Find next instance of current search |
|
| Ctrl-n | Find next instance of current search |
|
||||||
| Ctrl-p | Find previous instance of current search |
|
| Ctrl-p | Find previous instance of current search |
|
||||||
|
|
||||||
Note: Ctrl-n and Ctrl-p should be used from the main buffer, not from inside
|
Note: `Ctrl-n` and `Ctrl-p` should be used from the main buffer, not from inside
|
||||||
the search prompt. After Ctrl-f, press enter to complete the search and then
|
the search prompt. After `Ctrl-f`, press enter to complete the search and then
|
||||||
you can use Ctrl-n and Ctrl-p to cycle through matches.
|
you can use `Ctrl-n` and `Ctrl-p` to cycle through matches.
|
||||||
|
|
||||||
### File Operations
|
### File Operations
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ Micro is an easy to use, intuitive, text editor that takes advantage of the
|
||||||
full capabilities of modern terminals.
|
full capabilities of modern terminals.
|
||||||
|
|
||||||
Micro can be controlled by commands entered on the command bar, or with
|
Micro can be controlled by commands entered on the command bar, or with
|
||||||
keybindings. To open the command bar, press Ctrl-e: the `>` prompt will
|
keybindings. To open the command bar, press `Ctrl-e`: the `>` prompt will
|
||||||
display. From now on, when the documentation shows a command to run (such as
|
display. From now on, when the documentation shows a command to run (such as
|
||||||
`> help`), press Ctrl-e and type the command followed by enter.
|
`> help`), press `Ctrl-e` and type the command followed by enter.
|
||||||
|
|
||||||
For a list of the default keybindings, run `> help defaultkeys`.
|
For a list of the default keybindings, run `> help defaultkeys`.
|
||||||
For more information on keybindings, see `> help keybindings`.
|
For more information on keybindings, see `> help keybindings`.
|
||||||
|
@ -14,7 +14,7 @@ To toggle a short list of important keybindings, press Alt-g.
|
||||||
|
|
||||||
## Quick-start
|
## Quick-start
|
||||||
|
|
||||||
To quit, press Ctrl-q. Save by pressing Ctrl-s. Press Ctrl-e, as previously
|
To quit, press `Ctrl-q`. Save by pressing `Ctrl-s`. Press `Ctrl-e`, as previously
|
||||||
mentioned, to start typing commands. To see which commands are available, at the
|
mentioned, to start typing commands. To see which commands are available, at the
|
||||||
prompt, press tab, or view the help topic with `> help commands`.
|
prompt, press tab, or view the help topic with `> help commands`.
|
||||||
|
|
||||||
|
@ -26,30 +26,30 @@ If the colorscheme doesn't look good, you can change it with
|
||||||
or see more information about colorschemes and syntax highlighting with `> help
|
or see more information about colorschemes and syntax highlighting with `> help
|
||||||
colors`.
|
colors`.
|
||||||
|
|
||||||
Press Ctrl-w to move between splits, and type `> vsplit filename` or
|
Press `Ctrl-w` to move between splits, and type `> vsplit filename` or
|
||||||
`> hsplit filename` to open a new split.
|
`> hsplit filename` to open a new split.
|
||||||
|
|
||||||
## Accessing more help
|
## Accessing more help
|
||||||
|
|
||||||
Micro has a built-in help system which can be accessed with the `> help` command.
|
Micro has a built-in help system which can be accessed with the `> help` command.
|
||||||
|
|
||||||
To view help for the various available topics, press Ctrl-e to access command
|
To view help for the various available topics, press `Ctrl-e` to access command
|
||||||
mode and type in `> help` followed by a topic. Typing just `> help` will open
|
mode and type in `> help` followed by a topic. Typing just `> help` will open
|
||||||
this page.
|
this page.
|
||||||
|
|
||||||
Here are the available help topics:
|
Here are the available help topics:
|
||||||
|
|
||||||
* tutorial: A brief tutorial which gives an overview of all the other help
|
* `tutorial`: A brief tutorial which gives an overview of all the other help
|
||||||
topics
|
topics
|
||||||
* keybindings: Gives a full list of the default keybindings as well as how to
|
* `keybindings`: Gives a full list of the default keybindings as well as how to
|
||||||
rebind them
|
rebind them
|
||||||
* defaultkeys: Gives a more straight-forward list of the hotkey commands and
|
* `defaultkeys`: Gives a more straight-forward list of the hotkey commands and
|
||||||
what they do
|
what they do
|
||||||
* commands: Gives a list of all the commands and what they do
|
* `commands`: Gives a list of all the commands and what they do
|
||||||
* options: Gives a list of all the options you can customize
|
* `options`: Gives a list of all the options you can customize
|
||||||
* plugins: Explains how micro's plugin system works and how to create your own
|
* `plugins`: Explains how micro's plugin system works and how to create your own
|
||||||
plugins
|
plugins
|
||||||
* colors: Explains micro's colorscheme and syntax highlighting engine and how
|
* `colors`: Explains micro's colorscheme and syntax highlighting engine and how
|
||||||
to create your own colorschemes or add new languages to the engine
|
to create your own colorschemes or add new languages to the engine
|
||||||
|
|
||||||
For example, to open the help page on plugins you would run `> help plugins`.
|
For example, to open the help page on plugins you would run `> help plugins`.
|
||||||
|
|
|
@ -16,7 +16,7 @@ Micro will know what to do with it.
|
||||||
|
|
||||||
You can use Ctrl + arrows to move word by word (Alt + arrows for Mac). Alt + left and right
|
You can use Ctrl + arrows to move word by word (Alt + arrows for Mac). Alt + left and right
|
||||||
move the cursor to the start and end of the line (Ctrl + left/right for Mac), and Ctrl + up and down move the
|
move the cursor to the start and end of the line (Ctrl + left/right for Mac), and Ctrl + up and down move the
|
||||||
cursor the start and end of the buffer.
|
cursor to the start and end of the buffer.
|
||||||
|
|
||||||
You can hold shift with all of these movement actions to select while moving.
|
You can hold shift with all of these movement actions to select while moving.
|
||||||
|
|
||||||
|
@ -36,15 +36,15 @@ following in the `bindings.json` file.
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** The syntax `<Modifier><key>` is equivalent to `<Modifier>-<key>`. In
|
**Note:** The syntax `<Modifier><key>` is equivalent to `<Modifier>-<key>`. In
|
||||||
addition, Ctrl-Shift bindings are not supported by terminals, and are the same
|
addition, `Ctrl-Shift` bindings are not supported by terminals, and are the same
|
||||||
as simply Ctrl bindings. This means that `CtrlG`, `Ctrl-G`, and `Ctrl-g` all
|
as simply `Ctrl` bindings. This means that `CtrlG`, `Ctrl-G`, and `Ctrl-g` all
|
||||||
mean the same thing. However, for Alt this is not the case: `AltG` and `Alt-G`
|
mean the same thing. However, for `Alt` this is not the case: `AltG` and `Alt-G`
|
||||||
mean `Alt-Shift-g`, while `Alt-g` does not require the Shift modifier.
|
mean `Alt-Shift-g`, while `Alt-g` does not require the Shift modifier.
|
||||||
|
|
||||||
In addition to editing your `~/.config/micro/bindings.json`, you can run
|
In addition to editing your `~/.config/micro/bindings.json`, you can run
|
||||||
`>bind <keycombo> <action>` For a list of bindable actions, see below.
|
`>bind <keycombo> <action>` For a list of bindable actions, see below.
|
||||||
|
|
||||||
You can also chain commands when rebinding. For example, if you want Alt-s to
|
You can also chain commands when rebinding. For example, if you want `Alt-s` to
|
||||||
save and quit you can bind it like so:
|
save and quit you can bind it like so:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -178,12 +178,18 @@ SelectToStartOfText
|
||||||
SelectToStartOfTextToggle
|
SelectToStartOfTextToggle
|
||||||
WordRight
|
WordRight
|
||||||
WordLeft
|
WordLeft
|
||||||
|
SubWordRight
|
||||||
|
SubWordLeft
|
||||||
SelectWordRight
|
SelectWordRight
|
||||||
SelectWordLeft
|
SelectWordLeft
|
||||||
|
SelectSubWordRight
|
||||||
|
SelectSubWordLeft
|
||||||
MoveLinesUp
|
MoveLinesUp
|
||||||
MoveLinesDown
|
MoveLinesDown
|
||||||
DeleteWordRight
|
DeleteWordRight
|
||||||
DeleteWordLeft
|
DeleteWordLeft
|
||||||
|
DeleteSubWordRight
|
||||||
|
DeleteSubWordLeft
|
||||||
SelectLine
|
SelectLine
|
||||||
SelectToStartOfLine
|
SelectToStartOfLine
|
||||||
SelectToEndOfLine
|
SelectToEndOfLine
|
||||||
|
@ -200,6 +206,8 @@ Find
|
||||||
FindLiteral
|
FindLiteral
|
||||||
FindNext
|
FindNext
|
||||||
FindPrevious
|
FindPrevious
|
||||||
|
DiffPrevious
|
||||||
|
DiffNext
|
||||||
Undo
|
Undo
|
||||||
Redo
|
Redo
|
||||||
Copy
|
Copy
|
||||||
|
@ -407,8 +415,14 @@ mouse actions)
|
||||||
|
|
||||||
```
|
```
|
||||||
MouseLeft
|
MouseLeft
|
||||||
|
MouseLeftDrag
|
||||||
|
MouseLeftRelease
|
||||||
MouseMiddle
|
MouseMiddle
|
||||||
|
MouseMiddleDrag
|
||||||
|
MouseMiddleRelease
|
||||||
MouseRight
|
MouseRight
|
||||||
|
MouseRightDrag
|
||||||
|
MouseRightRelease
|
||||||
MouseWheelUp
|
MouseWheelUp
|
||||||
MouseWheelDown
|
MouseWheelDown
|
||||||
MouseWheelLeft
|
MouseWheelLeft
|
||||||
|
@ -473,6 +487,8 @@ conventions for text editing defaults.
|
||||||
"Alt-F": "FindLiteral",
|
"Alt-F": "FindLiteral",
|
||||||
"Ctrl-n": "FindNext",
|
"Ctrl-n": "FindNext",
|
||||||
"Ctrl-p": "FindPrevious",
|
"Ctrl-p": "FindPrevious",
|
||||||
|
"Alt-[": "DiffPrevious|CursorStart",
|
||||||
|
"Alt-]": "DiffNext|CursorEnd",
|
||||||
"Ctrl-z": "Undo",
|
"Ctrl-z": "Undo",
|
||||||
"Ctrl-y": "Redo",
|
"Ctrl-y": "Redo",
|
||||||
"Ctrl-c": "CopyLine|Copy",
|
"Ctrl-c": "CopyLine|Copy",
|
||||||
|
@ -523,9 +539,12 @@ conventions for text editing defaults.
|
||||||
"MouseWheelUp": "ScrollUp",
|
"MouseWheelUp": "ScrollUp",
|
||||||
"MouseWheelDown": "ScrollDown",
|
"MouseWheelDown": "ScrollDown",
|
||||||
"MouseLeft": "MousePress",
|
"MouseLeft": "MousePress",
|
||||||
|
"MouseLeftDrag": "MouseDrag",
|
||||||
|
"MouseLeftRelease": "MouseRelease",
|
||||||
"MouseMiddle": "PastePrimary",
|
"MouseMiddle": "PastePrimary",
|
||||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||||
|
|
||||||
|
// Multi-cursor bindings
|
||||||
"Alt-n": "SpawnMultiCursor",
|
"Alt-n": "SpawnMultiCursor",
|
||||||
"AltShiftUp": "SpawnMultiCursorUp",
|
"AltShiftUp": "SpawnMultiCursorUp",
|
||||||
"AltShiftDown": "SpawnMultiCursorDown",
|
"AltShiftDown": "SpawnMultiCursorDown",
|
||||||
|
@ -632,6 +651,8 @@ are given below:
|
||||||
"MouseWheelUp": "HistoryUp",
|
"MouseWheelUp": "HistoryUp",
|
||||||
"MouseWheelDown": "HistoryDown",
|
"MouseWheelDown": "HistoryDown",
|
||||||
"MouseLeft": "MousePress",
|
"MouseLeft": "MousePress",
|
||||||
|
"MouseLeftDrag": "MouseDrag",
|
||||||
|
"MouseLeftRelease": "MouseRelease",
|
||||||
"MouseMiddle": "PastePrimary"
|
"MouseMiddle": "PastePrimary"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,13 @@ Here are the available options:
|
||||||
|
|
||||||
default value: `true`
|
default value: `true`
|
||||||
|
|
||||||
|
* `detectlimit`: if this is not set to 0, it will limit the amount of first
|
||||||
|
lines in a file that are matched to determine the filetype.
|
||||||
|
A higher limit means better accuracy of guessing the filetype, but also
|
||||||
|
taking more time.
|
||||||
|
|
||||||
|
default value: `100`
|
||||||
|
|
||||||
* `diffgutter`: display diff indicators before lines.
|
* `diffgutter`: display diff indicators before lines.
|
||||||
|
|
||||||
default value: `false`
|
default value: `false`
|
||||||
|
@ -129,6 +136,12 @@ Here are the available options:
|
||||||
|
|
||||||
default value: `true`
|
default value: `true`
|
||||||
|
|
||||||
|
* `fakecursor`: forces micro to render the cursor using terminal colors rather
|
||||||
|
than the actual terminal cursor. This is useful when the terminal's cursor is
|
||||||
|
slow or otherwise unavailable/undesirable to use.
|
||||||
|
|
||||||
|
default value: `false`
|
||||||
|
|
||||||
* `fastdirty`: this determines what kind of algorithm micro uses to determine
|
* `fastdirty`: this determines what kind of algorithm micro uses to determine
|
||||||
if a buffer is modified or not. When `fastdirty` is on, micro just uses a
|
if a buffer is modified or not. When `fastdirty` is on, micro just uses a
|
||||||
boolean `modified` that is set to `true` as soon as the user makes an edit.
|
boolean `modified` that is set to `true` as soon as the user makes an edit.
|
||||||
|
@ -151,7 +164,7 @@ Here are the available options:
|
||||||
an effect if the file is empty/newly created, because otherwise the fileformat
|
an effect if the file is empty/newly created, because otherwise the fileformat
|
||||||
will be automatically detected from the existing line endings.
|
will be automatically detected from the existing line endings.
|
||||||
|
|
||||||
default value: `unix`
|
default value: `unix` on Unix systems, `dos` on Windows
|
||||||
|
|
||||||
* `filetype`: sets the filetype for the current buffer. Set this option to
|
* `filetype`: sets the filetype for the current buffer. Set this option to
|
||||||
`off` to completely disable filetype detection.
|
`off` to completely disable filetype detection.
|
||||||
|
@ -168,6 +181,19 @@ Here are the available options:
|
||||||
|
|
||||||
default value: `false`
|
default value: `false`
|
||||||
|
|
||||||
|
* `hltaberrors`: highlight tabs when spaces are expected, and spaces when tabs
|
||||||
|
are expected. More precisely: if `tabstospaces` option is on, highlight
|
||||||
|
all tab characters; if `tabstospaces` is off, highlight space characters
|
||||||
|
in the initial indent part of the line.
|
||||||
|
|
||||||
|
default value: `false`
|
||||||
|
|
||||||
|
* `hltrailingws`: highlight trailing whitespaces at ends of lines. Note that
|
||||||
|
it doesn't highlight newly added trailing whitespaces that naturally occur
|
||||||
|
while typing text. It highlights only nasty forgotten trailing whitespaces.
|
||||||
|
|
||||||
|
default value: `false`
|
||||||
|
|
||||||
* `incsearch`: enable incremental search in "Find" prompt (matching as you type).
|
* `incsearch`: enable incremental search in "Find" prompt (matching as you type).
|
||||||
|
|
||||||
default value: `true`
|
default value: `true`
|
||||||
|
@ -200,15 +226,23 @@ Here are the available options:
|
||||||
|
|
||||||
* `keymenu`: display the nano-style key menu at the bottom of the screen. Note
|
* `keymenu`: display the nano-style key menu at the bottom of the screen. Note
|
||||||
that ToggleKeyMenu is bound to `Alt-g` by default and this is displayed in
|
that ToggleKeyMenu is bound to `Alt-g` by default and this is displayed in
|
||||||
the statusline. To disable this, simply by `Alt-g` to `UnbindKey`.
|
the statusline. To disable the key binding, bind `Alt-g` to `None`.
|
||||||
|
|
||||||
default value: `false`
|
default value: `false`
|
||||||
|
|
||||||
* `matchbrace`: underline matching braces for '()', '{}', '[]' when the cursor
|
* `matchbrace`: show matching braces for '()', '{}', '[]' when the cursor
|
||||||
is on a brace character.
|
is on a brace character or next to it.
|
||||||
|
|
||||||
default value: `true`
|
default value: `true`
|
||||||
|
|
||||||
|
* `matchbracestyle`: whether to underline or highlight matching braces when
|
||||||
|
`matchbrace` is enabled. The color of highlight is determined by the `match-brace`
|
||||||
|
field in the current theme. Possible values:
|
||||||
|
* `underline`: underline matching braces.
|
||||||
|
* `highlight`: use `match-brace` style from the current theme.
|
||||||
|
|
||||||
|
default value: `underline`
|
||||||
|
|
||||||
* `mkparents`: if a file is opened on a path that does not exist, the file
|
* `mkparents`: if a file is opened on a path that does not exist, the file
|
||||||
cannot be saved because the parent directories don't exist. This option lets
|
cannot be saved because the parent directories don't exist. This option lets
|
||||||
micro automatically create the parent directories in such a situation.
|
micro automatically create the parent directories in such a situation.
|
||||||
|
@ -234,7 +268,7 @@ Here are the available options:
|
||||||
|
|
||||||
* `paste`: treat characters sent from the terminal in a single chunk as a paste
|
* `paste`: treat characters sent from the terminal in a single chunk as a paste
|
||||||
event rather than a series of manual key presses. If you are pasting using
|
event rather than a series of manual key presses. If you are pasting using
|
||||||
the terminal keybinding (not Ctrl-v, which is micro's default paste
|
the terminal keybinding (not `Ctrl-v`, which is micro's default paste
|
||||||
keybinding) then it is a good idea to enable this option during the paste
|
keybinding) then it is a good idea to enable this option during the paste
|
||||||
and disable once the paste is over. See `> help copypaste` for details about
|
and disable once the paste is over. See `> help copypaste` for details about
|
||||||
copying and pasting in a terminal environment.
|
copying and pasting in a terminal environment.
|
||||||
|
@ -275,8 +309,16 @@ Here are the available options:
|
||||||
|
|
||||||
default value: `false`
|
default value: `false`
|
||||||
|
|
||||||
|
* `reload`: controls the reload behavior of the current buffer in case the file
|
||||||
|
has changed. The available options are `prompt`, `auto` & `disabled`.
|
||||||
|
|
||||||
|
default value: `prompt`
|
||||||
|
|
||||||
* `rmtrailingws`: micro will automatically trim trailing whitespaces at ends of
|
* `rmtrailingws`: micro will automatically trim trailing whitespaces at ends of
|
||||||
lines. Note: This setting overrides `keepautoindent`
|
lines.
|
||||||
|
Note: This setting overrides `keepautoindent` and isn't used at timed `autosave`
|
||||||
|
or forced `autosave` in case the buffer didn't change. A manual save will
|
||||||
|
involve the action regardless if the buffer has been changed or not.
|
||||||
|
|
||||||
default value: `false`
|
default value: `false`
|
||||||
|
|
||||||
|
@ -311,6 +353,10 @@ Here are the available options:
|
||||||
|
|
||||||
default value: `false`
|
default value: `false`
|
||||||
|
|
||||||
|
* `scrollbarchar`: specifies the character used for displaying the scrollbar
|
||||||
|
|
||||||
|
default value: `|`
|
||||||
|
|
||||||
* `scrollmargin`: margin at which the view starts scrolling when the cursor
|
* `scrollmargin`: margin at which the view starts scrolling when the cursor
|
||||||
approaches the edge of the view.
|
approaches the edge of the view.
|
||||||
|
|
||||||
|
@ -398,7 +444,7 @@ Here are the available options:
|
||||||
|
|
||||||
* `useprimary` (only useful on unix): defines whether or not micro will use the
|
* `useprimary` (only useful on unix): defines whether or not micro will use the
|
||||||
primary clipboard to copy selections in the background. This does not affect
|
primary clipboard to copy selections in the background. This does not affect
|
||||||
the normal clipboard using Ctrl-c and Ctrl-v.
|
the normal clipboard using `Ctrl-c` and `Ctrl-v`.
|
||||||
|
|
||||||
default value: `true`
|
default value: `true`
|
||||||
|
|
||||||
|
@ -412,7 +458,7 @@ Here are the available options:
|
||||||
Enabling this option may cause unwanted effects if your terminal in fact
|
Enabling this option may cause unwanted effects if your terminal in fact
|
||||||
does not conform to the `xterm-256color` standard.
|
does not conform to the `xterm-256color` standard.
|
||||||
|
|
||||||
Default value: `false`
|
default value: `false`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -480,6 +526,7 @@ so that you can see what the formatting should look like.
|
||||||
"linter": true,
|
"linter": true,
|
||||||
"literate": true,
|
"literate": true,
|
||||||
"matchbrace": true,
|
"matchbrace": true,
|
||||||
|
"matchbracestyle": "underline",
|
||||||
"mkparents": false,
|
"mkparents": false,
|
||||||
"mouse": true,
|
"mouse": true,
|
||||||
"parsecursor": false,
|
"parsecursor": false,
|
||||||
|
|
|
@ -5,7 +5,8 @@ folders containing Lua files and possibly other source files placed
|
||||||
in `~/.config/micro/plug`. The plugin directory (within `plug`) should
|
in `~/.config/micro/plug`. The plugin directory (within `plug`) should
|
||||||
contain at least one Lua file and a `repo.json` file. The `repo.json` file
|
contain at least one Lua file and a `repo.json` file. The `repo.json` file
|
||||||
provides additional information such as the name of the plugin, the
|
provides additional information such as the name of the plugin, the
|
||||||
plugin's website, dependencies, etc... [Here is an example `repo.json` file](https://github.com/micro-editor/updated-plugins/blob/master/go-plugin/repo.json)
|
plugin's website, dependencies, etc...
|
||||||
|
[Here is an example `repo.json` file](https://github.com/micro-editor/updated-plugins/blob/master/go-plugin/repo.json)
|
||||||
from the go plugin, which has the following file structure:
|
from the go plugin, which has the following file structure:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -56,6 +57,8 @@ which micro defines:
|
||||||
* `onBufPaneOpen(bufpane)`: runs when a bufpane is opened. The input
|
* `onBufPaneOpen(bufpane)`: runs when a bufpane is opened. The input
|
||||||
contains the bufpane object.
|
contains the bufpane object.
|
||||||
|
|
||||||
|
* `onSetActive(bufpane)`: runs when changing the currently active bufpane.
|
||||||
|
|
||||||
* `onAction(bufpane)`: runs when `Action` is triggered by the user, where
|
* `onAction(bufpane)`: runs when `Action` is triggered by the user, where
|
||||||
`Action` is a bindable action (see `> help keybindings`). A bufpane
|
`Action` is a bindable action (see `> help keybindings`). A bufpane
|
||||||
is passed as input and the function should return a boolean defining
|
is passed as input and the function should return a boolean defining
|
||||||
|
@ -65,6 +68,14 @@ which micro defines:
|
||||||
by the user. Returns a boolean which defines whether the action should
|
by the user. Returns a boolean which defines whether the action should
|
||||||
be canceled.
|
be canceled.
|
||||||
|
|
||||||
|
* `onRune(bufpane, rune)`: runs when the composed rune has been inserted
|
||||||
|
|
||||||
|
* `preRune(bufpane, rune)`: runs before the composed rune will be inserted
|
||||||
|
|
||||||
|
* `onAnyEvent()`: runs when literally anything happens. It is useful for
|
||||||
|
detecting various changes of micro's state that cannot be detected
|
||||||
|
using other callbacks.
|
||||||
|
|
||||||
For example a function which is run every time the user saves the buffer
|
For example a function which is run every time the user saves the buffer
|
||||||
would be:
|
would be:
|
||||||
|
|
||||||
|
@ -115,6 +126,11 @@ The packages and functions are listed below (in Go type signatures):
|
||||||
current pane is not a BufPane.
|
current pane is not a BufPane.
|
||||||
|
|
||||||
- `CurTab() *Tab`: returns the current tab.
|
- `CurTab() *Tab`: returns the current tab.
|
||||||
|
|
||||||
|
- `After(t time.Duration, f func())`: run function `f` in the background
|
||||||
|
after time `t` elapses. See https://pkg.go.dev/time#Duration for the
|
||||||
|
usage of `time.Duration`.
|
||||||
|
|
||||||
* `micro/config`
|
* `micro/config`
|
||||||
- `MakeCommand(name string, action func(bp *BufPane, args[]string),
|
- `MakeCommand(name string, action func(bp *BufPane, args[]string),
|
||||||
completer buffer.Completer)`:
|
completer buffer.Completer)`:
|
||||||
|
@ -361,6 +377,7 @@ strings
|
||||||
regexp
|
regexp
|
||||||
errors
|
errors
|
||||||
time
|
time
|
||||||
|
unicode/utf8
|
||||||
archive/zip
|
archive/zip
|
||||||
net/http
|
net/http
|
||||||
```
|
```
|
||||||
|
|
|
@ -50,11 +50,11 @@ function preInsertNewline(bp)
|
||||||
for i = 1, #autoNewlinePairs do
|
for i = 1, #autoNewlinePairs do
|
||||||
if curRune == charAt(autoNewlinePairs[i], 1) then
|
if curRune == charAt(autoNewlinePairs[i], 1) then
|
||||||
if nextRune == charAt(autoNewlinePairs[i], 2) then
|
if nextRune == charAt(autoNewlinePairs[i], 2) then
|
||||||
bp:InsertNewline()
|
|
||||||
bp:InsertTab()
|
|
||||||
bp.Buf:Insert(-bp.Cursor.Loc, "\n" .. ws)
|
bp.Buf:Insert(-bp.Cursor.Loc, "\n" .. ws)
|
||||||
bp:StartOfLine()
|
bp:StartOfLine()
|
||||||
bp:CursorLeft()
|
bp:CursorLeft()
|
||||||
|
bp:InsertNewline()
|
||||||
|
bp:InsertTab()
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ local buffer = import("micro/buffer")
|
||||||
local ft = {}
|
local ft = {}
|
||||||
|
|
||||||
ft["apacheconf"] = "# %s"
|
ft["apacheconf"] = "# %s"
|
||||||
ft["bat"] = ":: %s"
|
ft["batch"] = ":: %s"
|
||||||
ft["c"] = "// %s"
|
ft["c"] = "// %s"
|
||||||
ft["c++"] = "// %s"
|
ft["c++"] = "// %s"
|
||||||
ft["cmake"] = "# %s"
|
ft["cmake"] = "# %s"
|
||||||
|
@ -63,7 +63,7 @@ ft["zsh"] = "# %s"
|
||||||
local last_ft
|
local last_ft
|
||||||
|
|
||||||
function updateCommentType(buf)
|
function updateCommentType(buf)
|
||||||
if buf.Settings["commenttype"] == nil or last_ft ~= buf.Settings["filetype"] then
|
if buf.Settings["commenttype"] == nil or (last_ft ~= buf.Settings["filetype"] and last_ft ~= nil) then
|
||||||
if ft[buf.Settings["filetype"]] ~= nil then
|
if ft[buf.Settings["filetype"]] ~= nil then
|
||||||
buf.Settings["commenttype"] = ft[buf.Settings["filetype"]]
|
buf.Settings["commenttype"] = ft[buf.Settings["filetype"]]
|
||||||
else
|
else
|
||||||
|
|
|
@ -8,7 +8,7 @@ file:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"Alt-g": "comment.comment"
|
"Alt-g": "lua:comment.comment"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ func AssetDir(name string) ([]string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
names := make([]string, len(entries), len(entries))
|
names := make([]string, len(entries))
|
||||||
for i, entry := range entries {
|
for i, entry := range entries {
|
||||||
names[i] = entry.Name()
|
names[i] = entry.Name()
|
||||||
}
|
}
|
||||||
|
|
16
runtime/runtime_test.go
Normal file
16
runtime/runtime_test.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAssetDir(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// Test AssetDir
|
||||||
|
entries, err := AssetDir("syntax")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, entries, "go.yaml")
|
||||||
|
assert.True(t, len(entries) > 5)
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ filetype: powershell
|
||||||
|
|
||||||
detect:
|
detect:
|
||||||
filename: "\\.ps(1|m1|d1)$"
|
filename: "\\.ps(1|m1|d1)$"
|
||||||
#header: ""
|
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
# - comment.block: # Block Comment
|
# - comment.block: # Block Comment
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
Here are micro's syntax files.
|
Here are micro's syntax files.
|
||||||
|
|
||||||
Each yaml file specifies how to detect the filetype based on file extension or headers (first line of the file).
|
Each yaml file specifies how to detect the filetype based on file extension or header (first line of the line).
|
||||||
|
In addition, a signature can be provided to help resolving ambiguities when multiple matching filetypes are detected.
|
||||||
Then there are patterns and regions linked to highlight groups which tell micro how to highlight that filetype.
|
Then there are patterns and regions linked to highlight groups which tell micro how to highlight that filetype.
|
||||||
|
|
||||||
Making your own syntax files is very simple. I recommend you check the file after you are finished with the
|
Making your own syntax files is very simple. I recommend you check the file after you are finished with the
|
||||||
|
@ -21,7 +22,7 @@ syntax files that you would like to convert to the new filetype, you can use the
|
||||||
$ go run syntax_converter.go c.micro > c.yaml
|
$ go run syntax_converter.go c.micro > c.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Most the the syntax files here have been converted using that tool.
|
Most of the syntax files here have been converted using that tool.
|
||||||
|
|
||||||
Note that the tool isn't perfect and though it is unlikely, you may run into some small issues that you will have to fix manually
|
Note that the tool isn't perfect and though it is unlikely, you may run into some small issues that you will have to fix manually
|
||||||
(about 4 files from this directory had issues after being converted).
|
(about 4 files from this directory had issues after being converted).
|
||||||
|
|
|
@ -36,7 +36,6 @@ rules:
|
||||||
- constant.interpolation: \\\([[:graph:]]*\)
|
- constant.interpolation: \\\([[:graph:]]*\)
|
||||||
- constant.unicode: \\u\{[[:xdigit:]]+}
|
- constant.unicode: \\u\{[[:xdigit:]]+}
|
||||||
|
|
||||||
|
|
||||||
# Line Comment
|
# Line Comment
|
||||||
- comment.line: "--.*"
|
- comment.line: "--.*"
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ detect:
|
||||||
rules:
|
rules:
|
||||||
- identifier: "\\b[A-Z_][0-9A-Z_]+\\b"
|
- identifier: "\\b[A-Z_][0-9A-Z_]+\\b"
|
||||||
|
|
||||||
##
|
## Sized (u)int types
|
||||||
- type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr)))_t\\b"
|
- type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr)))_t\\b"
|
||||||
|
|
||||||
## Constants
|
## Constants
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
filetype: batch
|
filetype: batch
|
||||||
|
|
||||||
detect:
|
detect:
|
||||||
filename: "(\\.bat$)"
|
filename: "(\\.bat$|\\.cmd$)"
|
||||||
# header: ""
|
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
# Numbers
|
# Numbers
|
||||||
|
|
|
@ -5,19 +5,22 @@ detect:
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
- identifier: "\\b[A-Z_][0-9A-Z_]+\\b"
|
- identifier: "\\b[A-Z_][0-9A-Z_]+\\b"
|
||||||
- type: "\\b(auto|float|double|char|int|short|long|sizeof|enum|void|static|const|struct|union|typedef|extern|(un)?signed|inline)\\b"
|
- type: "\\b(_Atomic|_BitInt|float|double|_Decimal32|_Decimal64|_Decimal128|_Complex|complex|_Imaginary|imaginary|_Bool|bool|char|int|short|long|enum|void|struct|union|typedef|typeof|typeof_unqual|(un)?signed|inline|_Noreturn)\\b"
|
||||||
- type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr)))_t\\b"
|
- type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr))|char(8|16|32)|wchar)_t\\b"
|
||||||
|
# GCC float/decimal/fixed types
|
||||||
|
- type: "\\b(_Float16|__fp16|_Float32|_Float32x|_Float64|_Float64x|__float80|_Float128|_Float128x|__float128|__ibm128|__int128|_Fract|_Sat|_Accum)\\b"
|
||||||
- type: "\\b[a-z_][0-9a-z_]+(_t|_T)\\b"
|
- type: "\\b[a-z_][0-9a-z_]+(_t|_T)\\b"
|
||||||
- type.extended: "\\b(bool)\\b"
|
- statement: "\\b(auto|volatile|register|restrict|_Alignas|alignas|_Alignof|alignof|static|const|constexpr|extern|_Thread_local|thread_local)\\b"
|
||||||
- statement: "\\b(volatile|register|restrict)\\b"
|
- statement: "\\b(for|if|while|do|else|case|default|switch|_Generic|_Static_assert|static_assert)\\b"
|
||||||
- statement: "\\b(for|if|while|do|else|case|default|switch)\\b"
|
|
||||||
- statement: "\\b(goto|continue|break|return)\\b"
|
- statement: "\\b(goto|continue|break|return)\\b"
|
||||||
- preproc: "^[[:space:]]*#[[:space:]]*(define|pragma|include|(un|ifn?)def|endif|el(if|se)|if|warning|error)"
|
- statement: "\\b(asm|fortran)\\b"
|
||||||
|
- preproc: "^[[:space:]]*#[[:space:]]*(define|embed|pragma|include|(un|ifn?)def|endif|el(if|ifdef|ifndef|se)|if|line|warning|error|__has_include|__has_embed|__has_c_attribute)"
|
||||||
|
- preproc: "^[[:space:]]*_Pragma\\b"
|
||||||
# GCC builtins
|
# GCC builtins
|
||||||
- statement: "__attribute__[[:space:]]*\\(\\([^)]*\\)\\)"
|
- statement: "__attribute__[[:space:]]*\\(\\([^)]*\\)\\)"
|
||||||
- statement: "__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__"
|
- statement: "__(aligned|asm|builtin|extension|hidden|inline|packed|restrict|section|typeof|weak)__"
|
||||||
# Operator Color
|
# Operator Color
|
||||||
- symbol.operator: "([.:;,+*|=!\\%]|<|>|/|-|&)"
|
- symbol.operator: "[-+*/%=<>.:;,~&|^!?]|\\b(offsetof|sizeof)\\b"
|
||||||
- symbol.brackets: "[(){}]|\\[|\\]"
|
- symbol.brackets: "[(){}]|\\[|\\]"
|
||||||
# Integer Constants
|
# Integer Constants
|
||||||
- constant.number: "(\\b([1-9][0-9]*|0[0-7]*|0[Xx][0-9A-Fa-f]+|0[Bb][01]+)([Uu][Ll]?[Ll]?|[Ll][Ll]?[Uu]?)?\\b)"
|
- constant.number: "(\\b([1-9][0-9]*|0[0-7]*|0[Xx][0-9A-Fa-f]+|0[Bb][01]+)([Uu][Ll]?[Ll]?|[Ll][Ll]?[Uu]?)?\\b)"
|
||||||
|
@ -25,7 +28,7 @@ rules:
|
||||||
- constant.number: "(\\b(([0-9]*[.][0-9]+|[0-9]+[.][0-9]*)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)[FfLl]?\\b)"
|
- constant.number: "(\\b(([0-9]*[.][0-9]+|[0-9]+[.][0-9]*)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)[FfLl]?\\b)"
|
||||||
# Hexadecimal Floating Constants
|
# Hexadecimal Floating Constants
|
||||||
- constant.number: "(\\b0[Xx]([0-9A-Za-z]*[.][0-9A-Za-z]+|[0-9A-Za-z]+[.][0-9A-Za-z]*)[Pp][+-]?[0-9]+[FfLl]?\\b)"
|
- constant.number: "(\\b0[Xx]([0-9A-Za-z]*[.][0-9A-Za-z]+|[0-9A-Za-z]+[.][0-9A-Za-z]*)[Pp][+-]?[0-9]+[FfLl]?\\b)"
|
||||||
- constant.number: "NULL"
|
- constant.bool: "(\\b(true|false|NULL|nullptr|TRUE|FALSE)\\b)"
|
||||||
|
|
||||||
- constant.string:
|
- constant.string:
|
||||||
start: "\""
|
start: "\""
|
||||||
|
@ -53,3 +56,4 @@ rules:
|
||||||
end: "\\*/"
|
end: "\\*/"
|
||||||
rules:
|
rules:
|
||||||
- todo: "(TODO|XXX|FIXME):?"
|
- todo: "(TODO|XXX|FIXME):?"
|
||||||
|
|
||||||
|
|
7
runtime/syntax/cake.yaml
Normal file
7
runtime/syntax/cake.yaml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
filetype: cake
|
||||||
|
detect:
|
||||||
|
filename: "\\.cake$"
|
||||||
|
|
||||||
|
rules:
|
||||||
|
- include: "csharp"
|
||||||
|
- preproc: "^[[:space:]]*#(addin|break|l|load|module|r|reference|tool)"
|
|
@ -1,7 +1,8 @@
|
||||||
filetype: c++
|
filetype: c++
|
||||||
|
|
||||||
detect:
|
detect:
|
||||||
filename: "(\\.c(c|pp|xx)$|\\.h(h|pp|xx)$|\\.ii?$|\\.(def)$)"
|
filename: "(\\.c(c|pp|xx)$|\\.h(h|pp|xx)?$|\\.ii?$|\\.(def)$)"
|
||||||
|
signature: "namespace|template|public|protected|private"
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
- identifier: "\\b[A-Z_][0-9A-Z_]*\\b"
|
- identifier: "\\b[A-Z_][0-9A-Z_]*\\b"
|
||||||
|
@ -9,7 +10,7 @@ rules:
|
||||||
- type: "\\b(((s?size)|((u_?)?int(8|16|32|64|ptr))|char(8|16|32))_t|wchar_t)\\b"
|
- type: "\\b(((s?size)|((u_?)?int(8|16|32|64|ptr))|char(8|16|32))_t|wchar_t)\\b"
|
||||||
- type: "\\b[a-z_][0-9a-z_]+(_t|_T)\\b"
|
- type: "\\b[a-z_][0-9a-z_]+(_t|_T)\\b"
|
||||||
- type: "\\b(final|override)\\b"
|
- type: "\\b(final|override)\\b"
|
||||||
- type.keyword: "\\b(auto|volatile|const(expr|eval|init)?|mutable|register|thread_local|static|extern|decltype|explicit|virtual)\\b"
|
- statement: "\\b(auto|volatile|const(expr|eval|init)?|mutable|register|thread_local|static|extern|decltype|explicit|virtual)\\b"
|
||||||
- statement: "\\b(class|namespace|template|typename|this|friend|using|public|protected|private|noexcept)\\b"
|
- statement: "\\b(class|namespace|template|typename|this|friend|using|public|protected|private|noexcept)\\b"
|
||||||
- statement: "\\b(concept|requires)\\b"
|
- statement: "\\b(concept|requires)\\b"
|
||||||
- statement: "\\b(import|export|module)\\b"
|
- statement: "\\b(import|export|module)\\b"
|
||||||
|
@ -34,7 +35,7 @@ rules:
|
||||||
- constant.number: "(\\b(([0-9']*[.][0-9']+|[0-9']+[.][0-9']*)([Ee][+-]?[0-9']+)?|[0-9']+[Ee][+-]?[0-9']+)[FfLl]?\\b)"
|
- constant.number: "(\\b(([0-9']*[.][0-9']+|[0-9']+[.][0-9']*)([Ee][+-]?[0-9']+)?|[0-9']+[Ee][+-]?[0-9']+)[FfLl]?\\b)"
|
||||||
# Hexadecimal Floating-point Literals
|
# Hexadecimal Floating-point Literals
|
||||||
- constant.number: "(\\b0[Xx]([0-9a-zA-Z']*[.][0-9a-zA-Z']+|[0-9a-zA-Z']+[.][0-9a-zA-Z']*)[Pp][+-]?[0-9']+[FfLl]?\\b)"
|
- constant.number: "(\\b0[Xx]([0-9a-zA-Z']*[.][0-9a-zA-Z']+|[0-9a-zA-Z']+[.][0-9a-zA-Z']*)[Pp][+-]?[0-9']+[FfLl]?\\b)"
|
||||||
- constant.bool: "(\\b(true|false|NULL|nullptr)\\b)"
|
- constant.bool: "(\\b(true|false|NULL|nullptr|TRUE|FALSE)\\b)"
|
||||||
|
|
||||||
- constant.string:
|
- constant.string:
|
||||||
start: "\""
|
start: "\""
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue