Compare commits

..

1 commit

Author SHA1 Message Date
Zachary Yedidia
f61dd7a894 Display a tilde before every capital letter for Canute 2023-02-13 14:15:34 -08:00
188 changed files with 1628 additions and 3928 deletions

View file

@ -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,7 +68,6 @@ 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
@ -84,7 +83,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 in [releases](https://github.com/zyedidia/micro/releases). Pre-built binaries are distributed with [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`.
@ -133,7 +132,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 as dnf, AUR, Nix, and package managers Micro is also available through other package managers on Linux such 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. -->
@ -160,8 +159,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.
@ -222,7 +221,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 color mode make sure your `TERM` variable is set If you are using the default Ubuntu terminal, to enable 256 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
@ -241,7 +240,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 or Cygwin (although this may change in the future). Micro also doesn't support NaCl (which is deprecated anyway). Plan9, and Cygwin (although this may change in the future). Micro also doesn't support NaCl (which is deprecated anyway).
## Usage ## Usage

View file

@ -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.2.2 (b0a8486541, 2022-12-01)" inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
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,23 +43,21 @@
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:window-width="1920" inkscape:window-width="1920"
inkscape:window-height="1080" inkscape:window-height="1043"
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="13.204388" inkscape:zoom="2.5161345"
inkscape:cx="71.832181" inkscape:cx="158.97401"
inkscape:cy="63.956011" inkscape:cy="109.69207"
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" inkscape:pagecheckerboard="0" /><g
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
@ -103,7 +101,4 @@
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" /><path style="fill:#2e3192" /></svg>
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: 16 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -2,6 +2,13 @@
<!-- 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"
@ -9,20 +16,13 @@
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="1.2.2 (b0a8486541, 2022-12-01)" inkscape:version="0.91 r13725"
sodipodi:docname="micro-logo-mark.svg" sodipodi:docname="micro-logo-notext.svg"
width="103.2" width="103.2"
height="103.2" height="103.2"><metadata
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 /></cc:Work></rdf:RDF></metadata><defs rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></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,28 +32,22 @@
guidetolerance="10" guidetolerance="10"
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:window-width="1920" inkscape:window-width="733"
inkscape:window-height="1080" inkscape:window-height="480"
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="5.405335" inkscape:zoom="0.28541667"
inkscape:cx="75.573484" inkscape:cx="302"
inkscape:cy="51.153166" inkscape:cy="-4"
inkscape:window-x="0" inkscape:window-x="1699"
inkscape:window-y="0" inkscape:window-y="277"
inkscape:window-maximized="0" inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" inkscape:current-layer="Layer_1" /><path
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" /><path style="fill:#2e3192" /></svg>
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: 6.2 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -2,6 +2,13 @@
<!-- 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"
@ -9,20 +16,13 @@
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="1.2.2 (b0a8486541, 2022-12-01)" inkscape:version="0.91 r13725"
sodipodi:docname="micro-logo.svg" sodipodi:docname="micro-logo.svg"
width="299.89999" width="299.89999"
height="103.2" height="103.2"><metadata
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 /></cc:Work></rdf:RDF></metadata><defs rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></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,24 +32,21 @@
guidetolerance="10" guidetolerance="10"
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:window-width="1920" inkscape:window-width="1237"
inkscape:window-height="1080" inkscape:window-height="867"
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="16.645603" inkscape:zoom="1.1416667"
inkscape:cx="65.092264" inkscape:cx="75.655934"
inkscape:cy="49.051992" inkscape:cy="-4"
inkscape:window-x="0" inkscape:window-x="1097"
inkscape:window-y="0" inkscape:window-y="185"
inkscape:window-maximized="0" inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" inkscape:current-layer="Layer_1" /><g
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"
@ -70,7 +67,4 @@
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" /><path style="fill:#2e3192" /></svg>
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: 12 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View file

@ -2,7 +2,6 @@ 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"
@ -48,18 +47,14 @@ 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.BufPane { ulua.L.SetField(pkg, "CurPane", luar.New(ulua.L, func() action.Pane {
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, "After", luar.New(ulua.L, func(t time.Duration, f func()) { ulua.L.SetField(pkg, "Lock", luar.New(ulua.L, ulua.Lock))
time.AfterFunc(t, func() {
timerChan <- f
})
}))
return pkg return pkg
} }

View file

@ -3,7 +3,6 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
@ -23,6 +22,7 @@ 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,6 +30,9 @@ 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")
@ -42,8 +45,6 @@ var (
sigterm chan os.Signal sigterm chan os.Signal
sighup chan os.Signal sighup chan os.Signal
timerChan chan func()
) )
func InitFlags() { func InitFlags() {
@ -66,7 +67,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 plugins can be managed at the command line with the following commands.\n") fmt.Print("\nMicro's plugin's 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]...")
@ -254,9 +255,7 @@ func main() {
screen.TermMessage(err) screen.TermMessage(err)
} }
config.InitRuntimeFiles(true) config.InitRuntimeFiles()
config.InitPlugins()
err = config.ReadSettings() err = config.ReadSettings()
if err != nil { if err != nil {
screen.TermMessage(err) screen.TermMessage(err)
@ -275,7 +274,6 @@ func main() {
continue continue
} }
config.GlobalSettings[k] = nativeValue config.GlobalSettings[k] = nativeValue
config.VolatileSettings[k] = true
} }
} }
@ -362,11 +360,9 @@ 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, syscall.SIGABRT) signal.Notify(sigterm, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
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 {
@ -417,19 +413,21 @@ 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.AutoSave() b.Save()
} }
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() {
@ -450,39 +448,13 @@ func DoEvent() {
os.Exit(0) os.Exit(0)
} }
if e, ok := event.(*tcell.EventError); ok { ulua.Lock.Lock()
log.Println("tcell event error: ", e.Error()) // if event != nil {
if action.InfoBar.HasPrompt {
if e.Err() == io.EOF { action.InfoBar.HandleEvent(event)
// shutdown due to terminal closing/becoming inaccessible } else {
for _, b := range buffer.OpenBuffers { action.Tabs.HandleEvent(event)
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)
} else {
action.Tabs.HandleEvent(event)
}
}
err := config.RunPluginFn("onAnyEvent")
if err != nil {
screen.TermMessage(err)
} }
// }
ulua.Lock.Unlock()
} }

View file

@ -35,9 +35,7 @@ func startup(args []string) (tcell.SimulationScreen, error) {
return nil, err return nil, err
} }
config.InitRuntimeFiles(true) config.InitRuntimeFiles()
config.InitPlugins()
err = config.ReadSettings() err = config.ReadSettings()
if err != nil { if err != nil {
return nil, err return nil, err
@ -109,10 +107,7 @@ func handleEvent() {
if e != nil { if e != nil {
screen.Events <- e screen.Events <- e
} }
DoEvent()
for len(screen.DrawChan()) > 0 || len(screen.Events) > 0 {
DoEvent()
}
} }
func injectKey(key tcell.Key, r rune, mod tcell.ModMask) { func injectKey(key tcell.Key, r rune, mod tcell.ModMask) {
@ -154,16 +149,6 @@ 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 {
@ -203,7 +188,14 @@ func TestSimpleEdit(t *testing.T) {
openFile(file) openFile(file)
if findBuffer(file) == nil { var buf *buffer.Buffer
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
} }
@ -242,11 +234,6 @@ 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
@ -310,11 +297,6 @@ 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)

View file

@ -0,0 +1,24 @@
<?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>

View file

@ -1,51 +0,0 @@
<?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>

View file

@ -1,367 +0,0 @@
{
"$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
View file

@ -14,9 +14,10 @@ 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/tcell/v2 v2.0.10 // indirect github.com/zyedidia/pty v1.1.20 // indirect
github.com/zyedidia/terminal v0.0.0-20230315200948-4b3bcf6dddef github.com/zyedidia/tcell/v2 v2.0.10-0.20221007181625-f562052bccb8 // indirect
golang.org/x/text v0.3.8 github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415
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
View file

@ -3,8 +3,6 @@ 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=
@ -38,10 +36,13 @@ 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=
@ -54,46 +55,26 @@ 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/tcell/v2 v2.0.10-0.20230320201625-54f6acdada4a h1:W4TWa++Wk6uRGxZoxr2nPX1TpIEl+Wxv0mTtocG4TYc= 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/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws= github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415/go.mod h1:8leT8G0Cm8NoJHdrrKHyR9MirWoF4YW7pZh06B6H+1E=
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
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=

View file

@ -51,47 +51,53 @@ 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()
h.Cursor = h.Buf.GetActiveCursor() h.Cursor = h.Buf.GetActiveCursor()
h.Cursor.Loc = mouseLoc h.Cursor.Loc = mouseLoc
}
if time.Since(h.lastClickTime)/time.Millisecond < config.DoubleClickThreshold && (mouseLoc.X == h.lastLoc.X && mouseLoc.Y == h.lastLoc.Y) {
if h.doubleClick {
// Triple click
h.lastClickTime = time.Now()
h.tripleClick = true
h.doubleClick = false
h.Cursor.SelectLine()
h.Cursor.CopySelection(clipboard.PrimaryReg)
} else {
// Double click
h.lastClickTime = time.Now()
h.doubleClick = true
h.tripleClick = false
h.Cursor.SelectWord()
h.Cursor.CopySelection(clipboard.PrimaryReg)
} }
} else { if time.Since(h.lastClickTime)/time.Millisecond < config.DoubleClickThreshold && (mouseLoc.X == h.lastLoc.X && mouseLoc.Y == h.lastLoc.Y) {
h.doubleClick = false if h.doubleClick {
h.tripleClick = false // Triple click
h.lastClickTime = time.Now() h.lastClickTime = time.Now()
h.Cursor.OrigSelection[0] = h.Cursor.Loc h.tripleClick = true
h.Cursor.CurSelection[0] = h.Cursor.Loc h.doubleClick = false
h.Cursor.CurSelection[1] = h.Cursor.Loc
h.Cursor.SelectLine()
h.Cursor.CopySelection(clipboard.PrimaryReg)
} else {
// Double click
h.lastClickTime = time.Now()
h.doubleClick = true
h.tripleClick = false
h.Cursor.SelectWord()
h.Cursor.CopySelection(clipboard.PrimaryReg)
}
} else {
h.doubleClick = false
h.tripleClick = false
h.lastClickTime = time.Now()
h.Cursor.OrigSelection[0] = h.Cursor.Loc
h.Cursor.CurSelection[0] = h.Cursor.Loc
h.Cursor.CurSelection[1] = h.Cursor.Loc
}
h.mouseReleased = false
} else if !h.mouseReleased {
if h.tripleClick {
h.Cursor.AddLineToSelection()
} else if h.doubleClick {
h.Cursor.AddWordToSelection()
} else {
h.Cursor.SetSelectionEnd(h.Cursor.Loc)
}
} }
h.Cursor.StoreVisualX() h.Cursor.StoreVisualX()
@ -100,45 +106,6 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
return true 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 {
h.Cursor.AddLineToSelection()
} else if h.doubleClick {
h.Cursor.AddWordToSelection()
} else {
h.Cursor.SelectTo(h.Cursor.Loc)
}
h.Cursor.StoreVisualX()
h.Relocate()
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"]))
@ -209,7 +176,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(false) h.Cursor.Deselect(true)
h.MoveCursorDown(1) h.MoveCursorDown(1)
h.Relocate() h.Relocate()
return true return true
@ -244,7 +211,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.Right() h.Cursor.Loc = h.Cursor.Loc.Move(1, h.Buf)
} 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)
@ -283,22 +250,6 @@ 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() {
@ -375,28 +326,6 @@ 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)
@ -660,28 +589,6 @@ 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() {
@ -805,8 +712,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.IsWordChar(r) { if !util.IsAutocomplete(prev) || !util.IsNonAlphaNumeric(r) {
// don't autocomplete if cursor is within a word // don't autocomplete if cursor is on alpha numeric character (middle of a word)
return false return false
} }
@ -886,26 +793,25 @@ 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 errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission) { if os.IsNotExist(err) {
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(
fmt.Sprintf("The file %s already exists in the directory, would you like to overwrite? Y/n", fileinfo.Name()),
func(yes, canceled bool) {
if yes && !canceled {
noPrompt := h.saveBufToFile(filename, action, callback)
if noPrompt {
h.completeAction(action)
}
}
},
)
} }
InfoBar.YNPrompt(
fmt.Sprintf("the file %s already exists in the directory, would you like to overwrite? Y/n", fileinfo.Name()),
func(yes, canceled bool) {
if yes && !canceled {
noPrompt := h.saveBufToFile(filename, action, callback)
if noPrompt {
h.completeAction(action)
}
}
},
)
} }
}) })
return false return false
@ -1047,9 +953,6 @@ 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)
} }
@ -1124,28 +1027,6 @@ 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()
@ -1370,13 +1251,9 @@ 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 { if h.Cursor.X > 0 && len(util.GetLeadingWhitespace([]byte(strings.TrimLeft(clip, "\r\n")))) == 0 {
leadingPasteWS := string(util.GetLeadingWhitespace([]byte(clip))) leadingWS := util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y))
if leadingPasteWS != " " && strings.Contains(clip, "\n"+leadingPasteWS) { clip = strings.ReplaceAll(clip, "\n", "\n"+string(leadingWS))
leadingWS := string(util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y)))
clip = strings.TrimPrefix(clip, leadingPasteWS)
clip = strings.ReplaceAll(clip, "\n"+leadingPasteWS, "\n"+leadingWS)
}
} }
} }
@ -1394,15 +1271,21 @@ 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 {
matchingBrace, left, found := h.Buf.FindMatchingBrace(h.Cursor.Loc) for _, bp := range buffer.BracePairs {
if found { r := h.Cursor.RuneUnder(h.Cursor.X)
if left { rl := h.Cursor.RuneUnder(h.Cursor.X - 1)
h.Cursor.GotoLoc(matchingBrace) if r == bp[0] || r == bp[1] || rl == bp[0] || rl == bp[1] {
} else { matchingBrace, left, found := h.Buf.FindMatchingBrace(bp, h.Cursor.Loc)
h.Cursor.GotoLoc(matchingBrace.Move(1, h.Buf)) if found {
if left {
h.Cursor.GotoLoc(matchingBrace)
} else {
h.Cursor.GotoLoc(matchingBrace.Move(1, h.Buf))
}
h.Relocate()
return true
}
} }
h.Relocate()
return true
} }
return false return false
} }
@ -1428,7 +1311,7 @@ func (h *BufPane) OpenFile() bool {
return true return true
} }
// JumpLine asks the user to enter a line number to jump to // OpenFile opens a new file in the buffer
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 {
@ -1534,7 +1417,9 @@ 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() h.Buf.UpdateDiff(func(synchronous bool) {
screen.Redraw()
})
InfoBar.Message("Enabled diff gutter") InfoBar.Message("Enabled diff gutter")
} else { } else {
h.Buf.Settings["diffgutter"] = false h.Buf.Settings["diffgutter"] = false
@ -1675,7 +1560,9 @@ func (h *BufPane) QuitAll() bool {
} }
quit := func() { quit := func() {
buffer.CloseOpenBuffers() for _, b := range buffer.OpenBuffers {
b.Close()
}
screen.Screen.Fini() screen.Screen.Fini()
InfoBar.Close() InfoBar.Close()
runtime.Goexit() runtime.Goexit()
@ -1804,7 +1691,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 BufKeyAction: case func(*BufPane) bool:
t(h) t(h)
} }
} }
@ -1853,39 +1740,15 @@ func (h *BufPane) SpawnMultiCursor() bool {
return true return true
} }
// SpawnMultiCursorUpN is not an action // SpawnMultiCursorUp creates additional cursor, at the same X (if possible), one Y less.
func (h *BufPane) SpawnMultiCursorUpN(n int) bool { func (h *BufPane) SpawnMultiCursorUp() bool {
lastC := h.Buf.GetCursor(h.Buf.NumCursors() - 1) if h.Cursor.Y == 0 {
var c *buffer.Cursor return false
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
}
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.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.AddCursor(c)
h.Buf.SetCurCursor(h.Buf.NumCursors() - 1) h.Buf.SetCurCursor(h.Buf.NumCursors() - 1)
h.Buf.MergeCursors() h.Buf.MergeCursors()
@ -1894,14 +1757,20 @@ func (h *BufPane) SpawnMultiCursorUpN(n int) 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 {
return h.SpawnMultiCursorUpN(-1) if h.Cursor.Y+1 == h.Buf.LinesNum() {
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
@ -1938,27 +1807,11 @@ 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()

View file

@ -88,10 +88,6 @@ 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) {
@ -157,6 +153,7 @@ 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
@ -204,20 +201,11 @@ 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
} }
@ -251,24 +239,6 @@ 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) {
@ -294,23 +264,21 @@ func TryBindKey(k, v string, overwrite bool) (bool, error) {
} }
found := false found := false
var ev string for ev := range parsed {
for ev = range parsed {
if e, err := findEvent(ev); err == nil { if e, err := findEvent(ev); err == nil {
if eventsEqual(e, key) { if e == key {
if overwrite {
parsed[ev] = v
}
found = true found = true
break break
} }
} }
} }
if found { if found && !overwrite {
if overwrite { return true, nil
parsed[ev] = v } else if !found {
} else {
return true, nil
}
} else {
parsed[k] = v parsed[k] = v
} }
@ -347,17 +315,13 @@ 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 eventsEqual(e, key) { if 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"])

View file

@ -8,6 +8,7 @@ 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"
@ -16,8 +17,6 @@ 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
@ -45,9 +44,8 @@ func init() {
BufBindings = NewKeyTree() BufBindings = NewKeyTree()
} }
// LuaAction makes an action from a lua function. It returns either a BufKeyAction // LuaAction makes a BufKeyAction from a lua function.
// or a BufMouseAction depending on the event type. func LuaAction(fn string) func(*BufPane) bool {
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
@ -57,42 +55,33 @@ func LuaAction(fn string, k Event) BufAction {
if pl == nil { if pl == nil {
return nil return nil
} }
return func(h *BufPane) bool {
var action BufAction val, err := pl.Call(plFn, luar.New(ulua.L, h))
switch k.(type) { if err != nil {
case KeyEvent, KeySequenceEvent, RawEvent: screen.TermMessage(err)
action = BufKeyAction(func(h *BufPane) bool { }
val, err := pl.Call(plFn, luar.New(ulua.L, h)) if v, ok := val.(lua.LBool); !ok {
if err != nil { return false
screen.TermMessage(err) } else {
} return bool(v)
if v, ok := val.(lua.LBool); !ok { }
return false
} else {
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
} }
// BufMapEvent maps an event to an action // BufMapKey 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
var actionfns []BufAction switch e := k.(type) {
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++ {
@ -113,7 +102,7 @@ func BufMapEvent(k Event, action string) {
action = "" action = ""
} }
var afn BufAction var afn func(*BufPane) bool
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)
@ -124,7 +113,7 @@ func BufMapEvent(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, k) afn = LuaAction(a)
if afn == nil { if afn == nil {
screen.TermMessage("Lua Error:", a, "does not exist") screen.TermMessage("Lua Error:", a, "does not exist")
continue continue
@ -140,16 +129,13 @@ func BufMapEvent(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, te *tcell.EventMouse) bool { bufAction := func(h *BufPane) bool {
cursors := h.Buf.GetCursors() cursors := h.Buf.GetCursors()
success := true success := true
for i, a := range actionfns { for i, a := range actionfns {
@ -161,7 +147,7 @@ func BufMapEvent(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, te) innerSuccess = innerSuccess && h.execAction(a, names[i], j)
} else { } else {
break break
} }
@ -169,21 +155,21 @@ func BufMapEvent(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
} }
switch e := k.(type) { BufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(bufAction))
case KeyEvent, KeySequenceEvent, RawEvent: }
BufBindings.RegisterKeyBinding(e, BufKeyActionGeneral(func(h *BufPane) bool {
return bufAction(h, nil) // BufMapMouse maps a mouse event to an action
})) func bufMapMouse(k MouseEvent, action string) {
case MouseEvent: if f, ok := BufMouseActions[action]; ok {
BufBindings.RegisterMouseBinding(e, BufMouseActionGeneral(bufAction)) BufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f))
} else {
// TODO
// delete(BufMouseBindings, k)
bufMapKey(k, action)
} }
} }
@ -214,15 +200,11 @@ 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 press event // Since tcell doesn't differentiate between a mouse release event
// and a mouse move event with button pressed (nor between a mouse // and a mouse move event with no keys pressed, we need to keep
// release event and a mouse move event with no buttons pressed), // track of whether or not the mouse was pressed (or not released) last event to determine
// we need to keep track of whether or not the mouse was previously // mouse release events
// pressed, to determine mouse release and mouse drag events. mouseReleased bool
// 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
@ -268,7 +250,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.mousePressed = make(map[MouseEvent]bool) h.mouseReleased = true
return h return h
} }
@ -294,11 +276,7 @@ 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
@ -326,9 +304,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 aggregate boolean response // error if there is one and returns the aggregrate boolean response
func (h *BufPane) PluginCB(cb string) bool { func (h *BufPane) PluginCB(cb string) bool {
b, err := config.RunPluginFnBool(h.Buf.Settings, cb, luar.New(ulua.L, h)) b, err := config.RunPluginFnBool(cb, luar.New(ulua.L, h))
if err != nil { if err != nil {
screen.TermMessage(err) screen.TermMessage(err)
} }
@ -337,19 +315,13 @@ 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(h.Buf.Settings, cb, luar.New(ulua.L, h), luar.New(ulua.L, string(r))) b, err := config.RunPluginFnBool(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()
@ -360,7 +332,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.resetMouse() h.mouseReleased = true
// 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
@ -423,40 +395,20 @@ 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() InfoBar.YNPrompt("The file on disk has changed. Reload file? (y,n,esc)", func(yes, canceled bool) {
if canceled {
h.Buf.DisableReload()
}
if !yes || canceled {
h.Buf.UpdateModTime()
} else {
h.Buf.ReOpen()
}
})
if reload == "prompt" {
InfoBar.YNPrompt("The file on disk has changed. Reload file? (y,n,esc)", func(yes, canceled bool) {
if canceled {
h.Buf.DisableReload()
}
if !yes || canceled {
h.Buf.UpdateModTime()
} else {
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) {
@ -480,37 +432,50 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
h.DoRuneInsert(e.Rune()) h.DoRuneInsert(e.Rune())
} }
case *tcell.EventMouse: case *tcell.EventMouse:
if e.Buttons() != tcell.ButtonNone { cancel := false
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()
@ -530,14 +495,6 @@ 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.
@ -549,10 +506,7 @@ 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)
@ -566,7 +520,7 @@ func (h *BufPane) DoKeyEvent(e Event) bool {
return more return more
} }
func (h *BufPane) execAction(action BufAction, name string, cursor int, te *tcell.EventMouse) bool { func (h *BufPane) execAction(action func(*BufPane) bool, name string, cursor int) bool {
if name != "Autocomplete" && name != "CycleAutocompleteBack" { if name != "Autocomplete" && name != "CycleAutocompleteBack" {
h.Buf.HasSuggestions = false h.Buf.HasSuggestions = false
} }
@ -574,13 +528,7 @@ func (h *BufPane) execAction(action BufAction, name string, cursor int, te *tcel
_, 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) {
var success bool success := action(h)
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 {
@ -701,10 +649,6 @@ 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
@ -720,12 +664,8 @@ 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
@ -746,16 +686,10 @@ 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,
@ -774,8 +708,6 @@ 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,
@ -856,8 +788,6 @@ 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,
} }
@ -882,16 +812,10 @@ 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,

View file

@ -41,7 +41,6 @@ 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},
@ -330,30 +329,13 @@ func (h *BufPane) ToggleLogCmd(args []string) {
} }
} }
// ReloadCmd reloads all files (syntax files, colorschemes, plugins...) // ReloadCmd reloads all files (syntax files, colorschemes...)
func (h *BufPane) ReloadCmd(args []string) { func (h *BufPane) ReloadCmd(args []string) {
reloadRuntime(true) ReloadConfig()
} }
// ReloadConfig reloads only the configuration
func ReloadConfig() { func ReloadConfig() {
reloadRuntime(false) config.InitRuntimeFiles()
}
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)
@ -362,36 +344,14 @@ func reloadRuntime(reloadPlugins bool) {
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()
} }
@ -403,24 +363,22 @@ 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.ReOpen() h.Buf.ReOpen()
} else if !canceled { } else if !canceled {
h.ReOpen() h.Buf.ReOpen()
} }
}) })
} else { } else {
h.ReOpen() h.Buf.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.Sprintf("Unable to load help text for %s: %v", page, err)) return errors.New(fmt.Sprint("Unable to load help text", page, "\n", 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)
@ -513,61 +471,61 @@ func (h *BufPane) NewTabCmd(args []string) {
} }
func SetGlobalOptionNative(option string, nativeValue interface{}) error { func SetGlobalOptionNative(option string, nativeValue interface{}) error {
// check for local option first... local := false
for _, s := range config.LocalSettings { for _, s := range config.LocalSettings {
if s == option { if s == option {
MainTab().CurPane().Buf.SetOptionNative(option, nativeValue) local = true
return nil break
} }
} }
// ...if it's not local continue with the globals if !local {
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()
config.InitColorscheme() config.InitColorscheme()
for _, b := range buffer.OpenBuffers { for _, b := range buffer.OpenBuffers {
b.UpdateRules() b.UpdateRules()
} }
} else if option == "infobar" || option == "keymenu" { } else if option == "infobar" || option == "keymenu" {
Tabs.Resize() Tabs.Resize()
} else if option == "mouse" { } else if option == "mouse" {
if !nativeValue.(bool) { if !nativeValue.(bool) {
screen.Screen.DisableMouse() screen.Screen.DisableMouse()
} else {
screen.Screen.EnableMouse()
}
} else if option == "autosave" {
if nativeValue.(float64) > 0 {
config.SetAutoTime(int(nativeValue.(float64)))
config.StartAutoSave()
} else {
config.SetAutoTime(0)
}
} else if option == "paste" {
screen.Screen.SetPaste(nativeValue.(bool))
} else if option == "clipboard" {
m := clipboard.SetMethod(nativeValue.(string))
err := clipboard.Initialize(m)
if err != nil {
return err
}
} else { } else {
screen.Screen.EnableMouse() for _, pl := range config.Plugins {
} if option == pl.Name {
} else if option == "autosave" { if nativeValue.(bool) && !pl.Loaded {
if nativeValue.(float64) > 0 { pl.Load()
config.SetAutoTime(int(nativeValue.(float64))) _, err := pl.Call("init")
config.StartAutoSave() if err != nil && err != config.ErrNoSuchFunction {
} else { screen.TermMessage(err)
config.SetAutoTime(0) }
} } else if !nativeValue.(bool) && pl.Loaded {
} else if option == "paste" { _, err := pl.Call("deinit")
screen.Screen.SetPaste(nativeValue.(bool)) if err != nil && err != config.ErrNoSuchFunction {
} else if option == "clipboard" { screen.TermMessage(err)
m := clipboard.SetMethod(nativeValue.(string)) }
err := clipboard.Initialize(m)
if err != nil {
return err
}
} else {
for _, pl := range config.Plugins {
if option == pl.Name {
if nativeValue.(bool) && !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)
} }
} }
} }
@ -676,11 +634,6 @@ 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 {
@ -688,7 +641,7 @@ func (h *BufPane) ShowKeyCmd(args []string) {
return return
} }
event, err := findEvent(parseKeyArg(args[0])) event, err := findEvent(args[0])
if err != nil { if err != nil {
InfoBar.Error(err) InfoBar.Error(err)
return return
@ -707,7 +660,7 @@ func (h *BufPane) BindCmd(args []string) {
return return
} }
_, err := TryBindKey(parseKeyArg(args[0]), args[1], true) _, err := TryBindKey(args[0], args[1], true)
if err != nil { if err != nil {
InfoBar.Error(err) InfoBar.Error(err)
} }
@ -720,7 +673,7 @@ func (h *BufPane) UnbindCmd(args []string) {
return return
} }
err := UnbindKey(parseKeyArg(args[0])) err := UnbindKey(args[0])
if err != nil { if err != nil {
InfoBar.Error(err) InfoBar.Error(err)
} }
@ -748,65 +701,41 @@ 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) {
line, col, err := h.parseLineCol(args)
if err != nil {
InfoBar.Error(err)
return
}
if line < 0 {
line = h.Buf.LinesNum() + 1 + line
}
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line)))
h.RemoveAllMultiCursors()
h.GotoLoc(buffer.Loc{col, line})
}
// 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 {
InfoBar.Error(err)
return
}
line = h.Buf.GetActiveCursor().Y + 1 + line
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
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 { if len(args) <= 0 {
return 0, 0, errors.New("Not enough arguments") InfoBar.Error("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 { } else {
line, err = strconv.Atoi(args[0]) h.RemoveAllMultiCursors()
if err != nil { if strings.Contains(args[0], ":") {
return 0, 0, err 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 {
InfoBar.Error(err)
return
}
if line < 0 {
line = h.Buf.LinesNum() + 1 + line
}
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line)))
h.GotoLoc(buffer.Loc{col, line})
} else {
line, err := strconv.Atoi(args[0])
if err != nil {
InfoBar.Error(err)
return
}
if line < 0 {
line = h.Buf.LinesNum() + 1 + line
}
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
h.GotoLoc(buffer.Loc{0, line})
} }
} }
return line, col, nil
} }
// SaveCmd saves the buffer optionally with an argument file name // SaveCmd saves the buffer optionally with an argument file name
@ -881,7 +810,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, !noRegex) nreplaced, _ = h.Buf.ReplaceRegex(start, end, regex, replace)
} 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)
@ -911,7 +840,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, !noRegex) _, nrunes := h.Buf.ReplaceRegex(locs[0], locs[1], regex, replace)
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)

View file

@ -41,8 +41,6 @@ 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",
@ -92,13 +90,11 @@ var bufdefaults = map[string]string{
"Esc": "Escape,Deselect,ClearInfo,RemoveAllMultiCursors,UnhighlightSearch", "Esc": "Escape,Deselect,ClearInfo,RemoveAllMultiCursors,UnhighlightSearch",
// Mouse bindings // Mouse bindings
"MouseWheelUp": "ScrollUp", "MouseWheelUp": "ScrollUp",
"MouseWheelDown": "ScrollDown", "MouseWheelDown": "ScrollDown",
"MouseLeft": "MousePress", "MouseLeft": "MousePress",
"MouseLeftDrag": "MouseDrag", "MouseMiddle": "PastePrimary",
"MouseLeftRelease": "MouseRelease", "Ctrl-MouseLeft": "MouseMultiCursor",
"MouseMiddle": "PastePrimary",
"Ctrl-MouseLeft": "MouseMultiCursor",
"Alt-n": "SpawnMultiCursor", "Alt-n": "SpawnMultiCursor",
"AltShiftUp": "SpawnMultiCursorUp", "AltShiftUp": "SpawnMultiCursorUp",
@ -177,10 +173,8 @@ var infodefaults = map[string]string{
"Esc": "AbortCommand", "Esc": "AbortCommand",
// Mouse bindings // Mouse bindings
"MouseWheelUp": "HistoryUp", "MouseWheelUp": "HistoryUp",
"MouseWheelDown": "HistoryDown", "MouseWheelDown": "HistoryDown",
"MouseLeft": "MousePress", "MouseLeft": "MousePress",
"MouseLeftDrag": "MouseDrag", "MouseMiddle": "PastePrimary",
"MouseLeftRelease": "MouseRelease",
"MouseMiddle": "PastePrimary",
} }

View file

@ -1,4 +1,3 @@
//go:build !darwin
// +build !darwin // +build !darwin
package action package action
@ -44,8 +43,6 @@ 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,13 +92,11 @@ var bufdefaults = map[string]string{
"Esc": "Escape,Deselect,ClearInfo,RemoveAllMultiCursors,UnhighlightSearch", "Esc": "Escape,Deselect,ClearInfo,RemoveAllMultiCursors,UnhighlightSearch",
// Mouse bindings // Mouse bindings
"MouseWheelUp": "ScrollUp", "MouseWheelUp": "ScrollUp",
"MouseWheelDown": "ScrollDown", "MouseWheelDown": "ScrollDown",
"MouseLeft": "MousePress", "MouseLeft": "MousePress",
"MouseLeftDrag": "MouseDrag", "MouseMiddle": "PastePrimary",
"MouseLeftRelease": "MouseRelease", "Ctrl-MouseLeft": "MouseMultiCursor",
"MouseMiddle": "PastePrimary",
"Ctrl-MouseLeft": "MouseMultiCursor",
"Alt-n": "SpawnMultiCursor", "Alt-n": "SpawnMultiCursor",
"Alt-m": "SpawnMultiCursorSelect", "Alt-m": "SpawnMultiCursorSelect",
@ -180,10 +175,8 @@ var infodefaults = map[string]string{
"Esc": "AbortCommand", "Esc": "AbortCommand",
// Mouse bindings // Mouse bindings
"MouseWheelUp": "HistoryUp", "MouseWheelUp": "HistoryUp",
"MouseWheelDown": "HistoryDown", "MouseWheelDown": "HistoryDown",
"MouseLeft": "MousePress", "MouseLeft": "MousePress",
"MouseLeftDrag": "MouseDrag", "MouseMiddle": "PastePrimary",
"MouseLeftRelease": "MouseRelease",
"MouseMiddle": "PastePrimary",
} }

View file

@ -100,20 +100,11 @@ 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 {
@ -131,17 +122,9 @@ 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%s", mod, k, state) return fmt.Sprintf("%s%s", mod, k)
} }
} }
return "" return ""

View file

@ -8,7 +8,6 @@ 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
@ -18,7 +17,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 := b.GetArg() input, argstart := buffer.GetArg(b)
var suggestions []string var suggestions []string
for cmd := range commands { for cmd := range commands {
@ -39,7 +38,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 := b.GetArg() input, argstart := buffer.GetArg(b)
var suggestions []string var suggestions []string
@ -78,63 +77,6 @@ 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 {
@ -147,7 +89,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 := b.GetArg() input, argstart := buffer.GetArg(b)
var suggestions []string var suggestions []string
for option := range config.GlobalSettings { for option := range config.GlobalSettings {
@ -174,7 +116,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 := b.GetArg() input, argstart := buffer.GetArg(b)
completeValue := false completeValue := false
args := bytes.Split(l, []byte{' '}) args := bytes.Split(l, []byte{' '})
@ -230,8 +172,13 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
switch inputOpt { switch inputOpt {
case "colorscheme": case "colorscheme":
_, suggestions = colorschemeComplete(input) _, suggestions = colorschemeComplete(input)
case "filetype": case "fileformat":
_, suggestions = filetypeComplete(input) if strings.HasPrefix("unix", 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")
@ -239,13 +186,15 @@ 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")
} }
default: case "clipboard":
if choices, ok := config.OptionChoices[inputOpt]; ok { if strings.HasPrefix("external", input) {
for _, choice := range choices { suggestions = append(suggestions, "external")
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")
} }
} }
} }
@ -261,7 +210,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 := b.GetArg() input, argstart := buffer.GetArg(b)
var suggestions []string var suggestions []string
for _, cmd := range PluginCmds { for _, cmd := range PluginCmds {
@ -283,7 +232,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 := b.GetArg() input, argstart := buffer.GetArg(b)
completeValue := false completeValue := false
args := bytes.Split(l, []byte{' '}) args := bytes.Split(l, []byte{' '})

View file

@ -83,8 +83,6 @@ 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(),
@ -95,14 +93,12 @@ 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 {
y := e.Rune() == 'y' || e.Rune() == 'Y' if (e.Rune() == 'y' || e.Rune() == 'Y') && hasYN {
n := e.Rune() == 'n' || e.Rune() == 'N' h.YNResp = true
if y || n { h.DonePrompt(false)
h.YNResp = y } else if (e.Rune() == 'n' || e.Rune() == 'N') && hasYN {
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 {
@ -112,11 +108,7 @@ 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] { hist[h.HistoryNum] = resp
h.HistoryNum = len(hist) - 1
hist[h.HistoryNum] = resp
h.HistorySearch = false
}
if h.EventCallback != nil { if h.EventCallback != nil {
h.EventCallback(resp) h.EventCallback(resp)
} }
@ -126,10 +118,7 @@ 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 {
@ -143,25 +132,11 @@ 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 {
action(h.BufPane) done := action(h.BufPane)
InfoBufBindings.ResetEvents() InfoBufBindings.ResetEvents()
return true return done
} else if action == nil && !more { } else if action == nil && !more {
InfoBufBindings.ResetEvents() InfoBufBindings.ResetEvents()
} }
@ -180,18 +155,6 @@ 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
@ -235,11 +198,9 @@ func (h *InfoPane) AbortCommand() {
// InfoKeyActions contains the list of all possible key actions the infopane could execute // InfoKeyActions contains the list of all possible key actions the infopane could execute
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, "CommandComplete": (*InfoPane).CommandComplete,
"HistorySearchDown": (*InfoPane).HistorySearchDown, "ExecuteCommand": (*InfoPane).ExecuteCommand,
"CommandComplete": (*InfoPane).CommandComplete, "AbortCommand": (*InfoPane).AbortCommand,
"ExecuteCommand": (*InfoPane).ExecuteCommand,
"AbortCommand": (*InfoPane).AbortCommand,
} }

View file

@ -1,12 +1,9 @@
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"
@ -124,12 +121,6 @@ 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)
@ -153,45 +144,6 @@ 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
@ -209,8 +161,6 @@ func InitTabs(bufs []*buffer.Buffer) {
} }
} }
} }
screen.RestartCallback = Tabs.ResetMouse
} }
func MainTab() *Tab { func MainTab() *Tab {
@ -224,9 +174,6 @@ 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
@ -264,40 +211,34 @@ 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 press event in a pane, that pane will become active // If the event is a mouse event in a pane, that pane will become active and get
// and get the event // 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()
btn := e.Buttons() switch e.Buttons() {
switch { case tcell.Button1:
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 t.resizing != nil {
if btn == tcell.Button1 { var size int
if t.resizing != nil { if t.resizing.Kind == views.STVert {
var size int size = mx - t.resizing.X
if t.resizing.Kind == views.STVert { } else {
size = mx - t.resizing.X size = my - t.resizing.Y + 1
} else {
size = my - t.resizing.Y + 1
}
t.resizing.ResizeSplit(size)
t.Resize()
return
}
if wasReleased {
t.resizing = t.GetMouseSplitNode(buffer.Loc{mx, my})
if t.resizing != nil {
return
}
} }
t.resizing.ResizeSplit(size)
t.Resize()
return
} }
if wasReleased { if wasReleased {
t.resizing = t.GetMouseSplitNode(buffer.Loc{mx, my})
if t.resizing != nil {
return
}
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
@ -307,15 +248,10 @@ func (t *Tab) HandleEvent(event tcell.Event) {
} }
} }
} }
case btn == tcell.ButtonNone: case tcell.ButtonNone:
// button release t.resizing = nil
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

View file

@ -81,10 +81,6 @@ 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
} }

View file

@ -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 (b *Buffer) GetWord() ([]byte, int) { func GetWord(b *Buffer) ([]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 (b *Buffer) GetWord() ([]byte, int) {
return []byte{}, -1 return []byte{}, -1
} }
if util.IsNonWordChar(b.RuneAt(c.Loc.Move(-1, b))) { if util.IsNonAlphaNumeric(b.RuneAt(c.Loc.Move(-1, b))) {
return []byte{}, c.X return []byte{}, c.X
} }
args := bytes.FieldsFunc(l, util.IsNonWordChar) args := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
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 (b *Buffer) GetArg() (string, int) { func GetArg(b *Buffer) (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 (b *Buffer) GetArg() (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 := b.GetArg() input, argstart := GetArg(b)
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 := b.GetWord() input, argstart := GetWord(b)
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.IsNonWordChar) words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
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.IsNonWordChar) words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
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)

View file

@ -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 a buffer that shows raw terminal events // BTRaw is 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,15 +430,6 @@ 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 {
@ -483,7 +474,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
} }
@ -568,13 +559,6 @@ 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)
@ -682,103 +666,6 @@ func calcHash(b *Buffer, out *[md5.Size]byte) error {
return nil return nil
} }
func parseDefFromFile(f config.RuntimeFile, header *highlight.Header) *highlight.Def {
data, err := f.Data()
if err != nil {
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
return nil
}
if header == nil {
header, err = highlight.MakeHeaderYaml(data)
if err != nil {
screen.TermMessage("Error parsing header for syntax file " + f.Name() + ": " + err.Error())
return nil
}
}
file, err := highlight.ParseFile(data)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
return nil
}
syndef, err := highlight.ParseDef(file, header)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
return nil
}
return syndef
}
// findRealRuntimeSyntaxDef finds a specific syntax definition
// in the user's custom syntax files
func findRealRuntimeSyntaxDef(name string, header *highlight.Header) *highlight.Def {
for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
if f.Name() == name {
syndef := parseDefFromFile(f, header)
if syndef != nil {
return syndef
}
}
}
return nil
}
// findRuntimeSyntaxDef finds a specific syntax definition
// in the built-in syntax files
func findRuntimeSyntaxDef(name string, header *highlight.Header) *highlight.Def {
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
if f.Name() == name {
syndef := parseDefFromFile(f, header)
if syndef != nil {
return syndef
}
}
}
return nil
}
func resolveIncludes(syndef *highlight.Def) {
includes := highlight.GetIncludes(syndef)
if len(includes) == 0 {
return
}
var files []*highlight.File
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
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 syntax file " + f.Name() + ": " + err.Error())
continue
}
for _, i := range includes {
if header.FileType == i {
file, err := highlight.ParseFile(data)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
files = append(files, file)
break
}
}
if len(files) >= len(includes) {
break
}
}
highlight.ResolveIncludes(syndef, files)
}
// UpdateRules updates the syntax rules and filetype for this buffer // UpdateRules updates the syntax rules and filetype for this buffer
// This is called when the colorscheme changes // This is called when the colorscheme changes
func (b *Buffer) UpdateRules() { func (b *Buffer) UpdateRules() {
@ -787,32 +674,13 @@ func (b *Buffer) UpdateRules() {
} }
ft := b.Settings["filetype"].(string) ft := b.Settings["filetype"].(string)
if ft == "off" { if ft == "off" {
b.ClearMatches()
b.SyntaxDef = nil
return 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 := "" syntaxFile := ""
foundDef := false foundDef := false
var header *highlight.Header var header *highlight.Header
// search for the syntax file in the user's custom syntax files // search for the syntax file in the user's custom syntax files
for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) { for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
if f.Name() == "default" {
continue
}
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())
@ -822,147 +690,119 @@ func (b *Buffer) UpdateRules() {
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())
}
file, err := highlight.ParseFile(data)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue continue
} }
matchedFileType := false if ((ft == "unknown" || ft == "") && highlight.MatchFiletype(header.FtDetect, b.Path, b.lines[0].data)) || header.FileType == ft {
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) 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 continue
} }
b.SyntaxDef = syndef
if matchedFileType { syntaxFile = f.Name()
b.SyntaxDef = syndef foundDef = true
syntaxFile = f.Name() break
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 in the default syntax files
// search for the syntax file in the built-in syntax files for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) {
for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) { data, err := f.Data()
data, err := f.Data() if err != nil {
if err != nil { screen.TermMessage("Error loading syntax header file " + f.Name() + ": " + err.Error())
screen.TermMessage("Error loading syntax header file " + f.Name() + ": " + err.Error()) continue
continue }
}
header, err = highlight.MakeHeader(data) header, err = highlight.MakeHeader(data)
if err != nil { if err != nil {
screen.TermMessage("Error reading syntax header file", f.Name(), err) screen.TermMessage("Error reading syntax header file", f.Name(), err)
continue continue
} }
if ft == "unknown" || ft == "" { if ft == "unknown" || ft == "" {
if header.MatchFileName(b.Path) { if highlight.MatchFiletype(header.FtDetect, b.Path, b.lines[0].data) {
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() syntaxFile = f.Name()
break break
} }
} } 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 { if syntaxFile != "" && !foundDef {
// we found a syntax file using a syntax header file // we found a syntax file using a syntax header file
b.SyntaxDef = findRuntimeSyntaxDef(syntaxFile, header) for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
} if f.Name() == syntaxFile {
data, err := f.Data()
if err != nil {
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
continue
}
if b.SyntaxDef != nil { file, err := highlight.ParseFile(data)
b.Settings["filetype"] = b.SyntaxDef.FileType if err != nil {
} else { screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
// search for the default file in the user's custom syntax files continue
b.SyntaxDef = findRealRuntimeSyntaxDef("default", nil) }
if b.SyntaxDef == nil {
// search for the default file in the built-in syntax files syndef, err := highlight.ParseDef(file, header)
b.SyntaxDef = findRuntimeSyntaxDef("default", nil) if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
b.SyntaxDef = syndef
break
}
} }
} }
if b.SyntaxDef != nil { if b.SyntaxDef != nil && highlight.HasIncludes(b.SyntaxDef) {
resolveIncludes(b.SyntaxDef) includes := highlight.GetIncludes(b.SyntaxDef)
var files []*highlight.File
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
data, err := f.Data()
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
header, err := highlight.MakeHeaderYaml(data)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
for _, i := range includes {
if header.FileType == i {
file, err := highlight.ParseFile(data)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
files = append(files, file)
break
}
}
if len(files) >= len(includes) {
break
}
}
highlight.ResolveIncludes(b.SyntaxDef, files)
}
if b.Highlighter == nil || syntaxFile != "" {
if b.SyntaxDef != nil {
b.Settings["filetype"] = b.SyntaxDef.FileType
}
} else {
b.SyntaxDef = &highlight.EmptyDef
} }
if b.SyntaxDef != nil { if b.SyntaxDef != nil {
@ -1064,7 +904,7 @@ func (b *Buffer) MergeCursors() {
b.EventHandler.active = b.curCursor b.EventHandler.active = b.curCursor
} }
// UpdateCursors updates all the cursors indices // UpdateCursors updates all the cursors indicies
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
@ -1140,14 +980,34 @@ var BracePairs = [][2]rune{
{'[', ']'}, {'[', ']'},
} }
func (b *Buffer) findMatchingBrace(braceType [2]rune, start Loc, char rune) (Loc, bool) { // FindMatchingBrace returns the location in the buffer of the matching bracket
// 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 char == braceType[0] { if startChar == braceType[0] || leftChar == 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 {
xInit = start.X if startChar == braceType[0] {
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]
@ -1156,74 +1016,42 @@ func (b *Buffer) findMatchingBrace(braceType [2]rune, start Loc, char rune) (Loc
} else if r == braceType[1] { } else if r == braceType[1] {
i-- i--
if i == 0 { if i == 0 {
return Loc{x, y}, true if startChar == braceType[0] {
return Loc{x, y}, false, true
}
return Loc{x, y}, true, true
} }
} }
} }
} }
} else if char == braceType[1] { } else if startChar == braceType[1] || leftChar == 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 {
xInit = start.X if leftChar == braceType[1] {
xInit = start.X - 1
} else {
xInit = start.X
}
} }
for x := xInit; x >= 0; x-- { for x := xInit; x >= 0; x-- {
r := l[x] r := l[x]
if r == braceType[1] { if r == braceType[0] {
i++
} else if r == braceType[0] {
i-- i--
if i == 0 { if i == 0 {
return Loc{x, y}, true if leftChar == braceType[1] {
return Loc{x, y}, true, true
}
return Loc{x, y}, false, true
} }
} else if r == braceType[1] {
i++
} }
} }
} }
} }
return start, false return start, true, 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
@ -1245,11 +1073,7 @@ 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
} }
@ -1292,7 +1116,7 @@ func (b *Buffer) Write(bytes []byte) (n int, err error) {
return len(bytes), nil return len(bytes), nil
} }
func (b *Buffer) updateDiff(synchronous bool) { func (b *Buffer) updateDiffSync() {
b.diffLock.Lock() b.diffLock.Lock()
defer b.diffLock.Unlock() defer b.diffLock.Unlock()
@ -1303,16 +1127,7 @@ func (b *Buffer) updateDiff(synchronous bool) {
} }
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
@ -1341,9 +1156,13 @@ func (b *Buffer) updateDiff(synchronous bool) {
// 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. // UpdateDiff does not schedule another update, in which case the callback
func (b *Buffer) UpdateDiff() { // is not called.
func (b *Buffer) UpdateDiff(callback func(bool)) {
if b.updateDiffTimer != nil { if b.updateDiffTimer != nil {
return return
} }
@ -1354,18 +1173,20 @@ func (b *Buffer) UpdateDiff() {
} }
if lineCount < 1000 { if lineCount < 1000 {
b.updateDiff(true) b.updateDiffSync()
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.updateDiff(false) b.updateDiffSync()
screen.Redraw() callback(false)
}) })
} 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)
} }
} }
@ -1377,7 +1198,9 @@ func (b *Buffer) SetDiffBase(diffBase []byte) {
} else { } else {
b.diffBaseLineCount = strings.Count(string(diffBase), "\n") b.diffBaseLineCount = strings.Count(string(diffBase), "\n")
} }
b.UpdateDiff() b.UpdateDiff(func(synchronous bool) {
screen.Redraw()
})
} }
// DiffStatus returns the diff status for a line in the buffer // DiffStatus returns the diff status for a line in the buffer
@ -1388,41 +1211,6 @@ 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 {

View file

@ -20,7 +20,6 @@ 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

View file

@ -30,11 +30,6 @@ 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
} }
@ -43,8 +38,6 @@ 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
@ -403,26 +396,13 @@ 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() {
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
c.Right()
return
}
for util.IsWhitespace(c.RuneUnder(c.X)) { 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()
return 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)) {
@ -434,10 +414,6 @@ 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 {
@ -445,17 +421,6 @@ 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 {
@ -466,132 +431,6 @@ 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)

View file

@ -106,10 +106,6 @@ 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
@ -241,7 +237,7 @@ func (eh *EventHandler) Execute(t *TextEvent) {
} }
eh.UndoStack.Push(t) eh.UndoStack.Push(t)
b, err := config.RunPluginFnBool(nil, "onBeforeTextEvent", luar.New(ulua.L, eh.buf), luar.New(ulua.L, t)) b, err := config.RunPluginFnBool("onBeforeTextEvent", luar.New(ulua.L, eh.buf), luar.New(ulua.L, t))
if err != nil { if err != nil {
screen.TermMessage(err) screen.TermMessage(err)
} }
@ -294,7 +290,6 @@ 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
} }
@ -338,7 +333,6 @@ 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
} }
@ -348,58 +342,3 @@ 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
}
}

View file

@ -46,9 +46,10 @@ type searchState struct {
type Line struct { type Line struct {
data []byte data []byte
state highlight.State state highlight.State
match highlight.LineMatch match highlight.LineMatch
lock sync.Mutex rehighlight bool
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,
// separately from the syntax highlighting. // separately from the syntax highlighting.
@ -74,7 +75,6 @@ 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 la.Endings == FFAuto { if endings == FFAuto {
la.Endings = FFDos la.Endings = FFDos
} }
dlen = len(data) dlen = len(data)
} else if dlen > 0 { } else if dlen > 0 {
if la.Endings == FFAuto { if endings == FFAuto {
la.Endings = FFUnix la.Endings = FFUnix
} }
} }
@ -147,18 +147,20 @@ func NewLineArray(size uint64, endings FileFormat, reader io.Reader) *LineArray
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
la.lines = Append(la.lines, Line{ la.lines = Append(la.lines, Line{
data: data, data: data,
state: nil, state: nil,
match: nil, match: nil,
rehighlight: false,
}) })
} }
// Last line was read // Last line was read
break break
} else { } else {
la.lines = Append(la.lines, Line{ la.lines = Append(la.lines, Line{
data: data[:dlen-1], data: data[:dlen-1],
state: nil, state: nil,
match: nil, match: nil,
rehighlight: false,
}) })
} }
n++ n++
@ -188,23 +190,22 @@ func (la *LineArray) Bytes() []byte {
// newlineBelow adds a newline below the given line number // newlineBelow adds a newline below the given line number
func (la *LineArray) newlineBelow(y int) { func (la *LineArray) newlineBelow(y int) {
la.lines = append(la.lines, Line{ la.lines = append(la.lines, Line{
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') {
@ -232,26 +233,24 @@ 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.lines[a].data = append(la.lines[a].data, la.lines[b].data...) la.insert(Loc{len(la.lines[a].data), a}, 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.lines[pos.Y+1].data = append(la.lines[pos.Y+1].data, la.lines[pos.Y].data[pos.X:]...) la.insert(Loc{0, pos.Y + 1}, 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)
@ -328,11 +327,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(lineN int) []byte { func (la *LineArray) LineBytes(n int) []byte {
if lineN >= len(la.lines) || lineN < 0 { if n >= len(la.lines) || n < 0 {
return []byte{} return []byte{}
} }
return la.lines[lineN].data return la.lines[n].data
} }
// State gets the highlight state for the given line number // State gets the highlight state for the given line number
@ -363,14 +362,16 @@ func (la *LineArray) Match(lineN int) highlight.LineMatch {
return la.lines[lineN].match return la.lines[lineN].match
} }
// Locks the whole LineArray func (la *LineArray) Rehighlight(lineN int) bool {
func (la *LineArray) Lock() { la.lines[lineN].lock.Lock()
la.lock.Lock() defer la.lines[lineN].lock.Unlock()
return la.lines[lineN].rehighlight
} }
// Unlocks the whole LineArray func (la *LineArray) SetRehighlight(lineN int, on bool) {
func (la *LineArray) Unlock() { la.lines[lineN].lock.Lock()
la.lock.Unlock() defer la.lines[lineN].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

View file

@ -51,38 +51,27 @@ 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, 0666); err != nil { } else if writeCloser, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); 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 err2 := w.Flush(); err2 != nil && err == nil { if e := writeCloser.Close(); e != nil && err == nil {
err = err2 err = e
}
// 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
@ -93,14 +82,9 @@ 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, false) return b.saveToFile(filename, false)
} }
func (b *Buffer) SaveWithSudo() error { func (b *Buffer) SaveWithSudo() error {
@ -108,10 +92,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, false) return b.saveToFile(filename, true)
} }
func (b *Buffer) saveToFile(filename string, withSudo bool, autoSave bool) error { func (b *Buffer) saveToFile(filename string, withSudo 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")
@ -123,7 +107,7 @@ func (b *Buffer) saveToFile(filename string, withSudo bool, autoSave bool) error
return errors.New("Save with sudo not supported on Windows") return errors.New("Save with sudo not supported on Windows")
} }
if !autoSave && b.Settings["rmtrailingws"].(bool) { if 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))

View file

@ -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, captureGroups bool) (int, int) { func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []byte) (int, int) {
if start.GreaterThan(end) { if start.GreaterThan(end) {
start, end = end, start start, end = end, start
} }
@ -172,13 +172,9 @@ 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 {
var result []byte 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 {

View file

@ -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(true) config.InitRuntimeFiles()
err := config.ReadSettings() err := config.ReadSettings()
if err != nil { if err != nil {
screen.TermMessage(err) screen.TermMessage(err)
@ -55,25 +55,6 @@ 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 {

View file

@ -52,55 +52,43 @@ func InitColorscheme() error {
Colorscheme = make(map[string]tcell.Style) Colorscheme = make(map[string]tcell.Style)
DefStyle = tcell.StyleDefault DefStyle = tcell.StyleDefault
c, err := LoadDefaultColorscheme() return 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() (map[string]tcell.Style, error) { func LoadDefaultColorscheme() error {
var parsedColorschemes []string return LoadColorscheme(GlobalSettings["colorscheme"].(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, parsedColorschemes *[]string) (map[string]tcell.Style, error) { func LoadColorscheme(colorschemeName string) error {
c := make(map[string]tcell.Style)
file := FindRuntimeFile(RTColorscheme, colorschemeName) file := FindRuntimeFile(RTColorscheme, colorschemeName)
if file == nil { if file == nil {
return c, errors.New(colorschemeName + " is not a valid colorscheme") return errors.New(colorschemeName + " is not a valid colorscheme")
} }
if data, err := file.Data(); err != nil { if data, err := file.Data(); err != nil {
return c, errors.New("Error loading colorscheme: " + err.Error()) return errors.New("Error loading colorscheme: " + err.Error())
} else { } else {
var err error Colorscheme, err = ParseColorscheme(string(data))
c, err = ParseColorscheme(file.Name(), string(data), parsedColorschemes)
if err != nil { if err != nil {
return c, err return err
} }
} }
return c, nil return 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(name string, text string, parsedColorschemes *[]string) (map[string]tcell.Style, error) { func ParseColorscheme(text string) (map[string]tcell.Style, error) {
var err error var err error
colorParser := regexp.MustCompile(`color-link\s+(\S*)\s+"(.*)"`) parser := 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] == '#' {
@ -108,30 +96,7 @@ lineLoop:
continue continue
} }
matches := includeParser.FindSubmatch([]byte(line)) matches := parser.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])

View file

@ -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", testColorscheme, nil) c, err := ParseColorscheme(testColorscheme)
assert.Nil(t, err) assert.Nil(t, err)
fg, bg, _ := c["comment"].Decompose() fg, bg, _ := c["comment"].Decompose()

View file

@ -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.IsLoaded() { if !p.IsEnabled() {
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(settings map[string]interface{}, fn string, args ...lua.LValue) (bool, error) { func RunPluginFnBool(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.IsLoaded() || (settings != nil && settings[p.Name] == false) { if !p.IsEnabled() {
continue continue
} }
val, err := p.Call(fn, args...) val, err := p.Call(fn, args...)
@ -71,11 +71,11 @@ type Plugin struct {
Info *PluginInfo // json file containing info Info *PluginInfo // json file containing info
Srcs []RuntimeFile // lua files Srcs []RuntimeFile // lua files
Loaded bool Loaded bool
Default bool // pre-installed plugin Default bool // pre-installed plugin
} }
// IsLoaded returns if a plugin is enabled // IsEnabled returns if a plugin is enabled
func (p *Plugin) IsLoaded() bool { func (p *Plugin) IsEnabled() 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
RegisterCommonOption(p.Name, true) RegisterGlobalOption(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.IsLoaded() { if !p.IsEnabled() {
continue continue
} }
if p.Name == name { if p.Name == name {

View file

@ -363,7 +363,7 @@ func GetInstalledVersions(withCore bool) PluginVersions {
} }
for _, p := range Plugins { for _, p := range Plugins {
if !p.IsLoaded() { if !p.IsEnabled() {
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.IsLoaded() { if !p.IsEnabled() {
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.IsLoaded() || p.Default { if !p.IsEnabled() || p.Default {
continue continue
} }
plugins = append(plugins, p.Name) plugins = append(plugins, p.Name)

View file

@ -40,10 +40,6 @@ 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)
} }
@ -133,17 +129,9 @@ 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 {
af := assetFile(path.Join(directory, f)) AddRuntimeFile(fileType, assetFile(path.Join(directory, f)))
for _, rf := range realFiles[fileType] {
if af.Name() == rf.Name() {
continue assetLoop
}
}
AddRuntimeFile(fileType, af)
} }
} }
} }
@ -170,30 +158,19 @@ func ListRealRuntimeFiles(fileType RTFiletype) []RuntimeFile {
return realFiles[fileType] return realFiles[fileType]
} }
// InitRuntimeFiles initializes all assets files and the config directory. // InitRuntimeFiles initializes all assets file and the config directory
// If `user` is false, InitRuntimeFiles ignores the config directory and func InitRuntimeFiles() {
// 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"
@ -241,15 +218,7 @@ func InitPlugins() {
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

View file

@ -7,7 +7,7 @@ import (
) )
func init() { func init() {
InitRuntimeFiles(false) InitRuntimeFiles()
} }
func TestAddFile(t *testing.T) { func TestAddFile(t *testing.T) {

View file

@ -8,7 +8,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime"
"strconv" "strconv"
"strings" "strings"
@ -20,116 +19,6 @@ 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")
@ -144,18 +33,27 @@ 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 {
@ -274,8 +172,7 @@ 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]
_, vol := VolatileSettings[k] if def, ok := defaults[k]; ok && okcur && reflect.DeepEqual(cur, def) {
if def, ok := defaults[k]; ok && okcur && !vol && reflect.DeepEqual(cur, def) {
delete(parsedSettings, k) delete(parsedSettings, k)
} }
} }
@ -320,7 +217,18 @@ 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 {
return RegisterCommonOption(pl+"."+name, defaultvalue) name = pl + "." + name
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)
@ -328,21 +236,18 @@ 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 _, ok := GlobalSettings[name]; !ok { if v, 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
} }
@ -351,11 +256,50 @@ func GetGlobalOption(name string) interface{} {
return GlobalSettings[name] return GlobalSettings[name]
} }
func defaultFileFormat() string { var defaultCommonSettings = map[string]interface{}{
if runtime.GOOS == "windows" { "autoindent": true,
return "dos" "autosu": false,
} "backup": true,
return "unix" "backupdir": "",
"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 {
@ -379,6 +323,36 @@ 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{} {
@ -472,26 +446,6 @@ 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)
@ -506,7 +460,53 @@ 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
}

View file

@ -2,6 +2,7 @@ package display
import ( import (
"strconv" "strconv"
"unicode"
runewidth "github.com/mattn/go-runewidth" runewidth "github.com/mattn/go-runewidth"
"github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/buffer"
@ -129,11 +130,6 @@ 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
@ -151,12 +147,12 @@ func (w *BufWindow) updateDisplayInfo() {
w.gutterOffset += w.maxLineNumLength + 1 w.gutterOffset += w.maxLineNumLength + 1
} }
if w.gutterOffset > w.Width-scrollbarWidth {
w.gutterOffset = w.Width - scrollbarWidth
}
prevBufWidth := w.bufWidth prevBufWidth := w.bufWidth
w.bufWidth = w.Width - w.gutterOffset - scrollbarWidth
w.bufWidth = w.Width - w.gutterOffset
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
w.bufWidth--
}
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() {
@ -282,17 +278,13 @@ 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 := ""
@ -328,28 +320,26 @@ func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, vloc
} else { } else {
lineInt = bloc.Y - cursorLine lineInt = bloc.Y - cursorLine
} }
lineNum := []rune(strconv.Itoa(util.Abs(lineInt))) lineNum := 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) && vloc.X < w.gutterOffset; i++ { for i := 0; i < w.maxLineNumLength-len(lineNum); 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 i := 0; i < len(lineNum) && vloc.X < w.gutterOffset; i++ { for _, ch := range lineNum {
if softwrapped || (w.bufWidth == 0 && w.Buf.Settings["softwrap"] == true) { if softwrapped {
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, lineNum[i], nil, lineNumStyle) screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ch, 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
@ -384,7 +374,18 @@ func (w *BufWindow) displayBuffer() {
if b.ModifiedThisFrame { if b.ModifiedThisFrame {
if b.Settings["diffgutter"].(bool) { if b.Settings["diffgutter"].(bool) {
b.UpdateDiff() b.UpdateDiff(func(synchronous bool) {
// 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,20 +393,26 @@ 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 _, c := range b.GetCursors() { for _, bp := range buffer.BracePairs {
if c.HasSelection() { for _, c := range b.GetCursors() {
continue if c.HasSelection() {
} continue
}
curX := c.X
curLoc := c.Loc
mb, left, found := b.FindMatchingBrace(c.Loc) r := c.RuneUnder(curX)
if found { rl := c.RuneUnder(curX - 1)
matchingBraces = append(matchingBraces, mb) if r == bp[0] || r == bp[1] || rl == bp[0] || rl == bp[1] {
if !left { mb, left, found := b.FindMatchingBrace(bp, curLoc)
if b.Settings["matchbracestyle"].(string) != "highlight" { if found {
matchingBraces = append(matchingBraces, c.Loc) matchingBraces = append(matchingBraces, mb)
if !left {
matchingBraces = append(matchingBraces, curLoc)
} else {
matchingBraces = append(matchingBraces, curLoc.Move(-1, b))
}
} }
} else {
matchingBraces = append(matchingBraces, c.Loc.Move(-1, b))
} }
} }
} }
@ -476,12 +483,6 @@ 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,37 +506,6 @@ 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]) ||
@ -588,15 +558,7 @@ 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" { style = style.Underline(true)
if s, ok := config.Colorscheme["match-brace"]; ok {
style = s
} else {
style = style.Reverse(true)
}
} else {
style = style.Underline(true)
}
} }
} }
} }
@ -648,7 +610,7 @@ func (w *BufWindow) displayBuffer() {
wordwidth := 0 wordwidth := 0
totalwidth := w.StartCol - nColsBeforeStart totalwidth := w.StartCol - nColsBeforeStart
for len(line) > 0 && vloc.X < maxWidth { for len(line) > 0 {
r, combc, size := util.DecodeCharacter(line) r, combc, size := util.DecodeCharacter(line)
line = line[size:] line = line[size:]
@ -663,8 +625,12 @@ func (w *BufWindow) displayBuffer() {
width = util.Min(ts, maxWidth-vloc.X) width = util.Min(ts, maxWidth-vloc.X)
totalwidth += ts totalwidth += ts
default: default:
width = runewidth.RuneWidth(r) if unicode.IsUpper(r) {
totalwidth += width width = 2
} else {
width = runewidth.RuneWidth(r)
totalwidth += width
}
} }
word = append(word, glyph{r, combc, curStyle, width}) word = append(word, glyph{r, combc, curStyle, width})
@ -697,17 +663,22 @@ func (w *BufWindow) displayBuffer() {
} }
for _, r := range word { for _, r := range word {
draw(r.r, r.combc, r.style, true, true) if unicode.IsUpper(r.r) {
draw('~', nil, r.style, true, true)
draw(r.r, r.combc, r.style, true, false)
} else {
draw(r.r, r.combc, r.style, true, true)
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes // Draw any extra characters either spaces for tabs or @ for incomplete wide runes
if r.width > 1 { if r.width > 1 {
char := ' ' char := ' '
if r.r != '\t' { if r.r != '\t' {
char = '@' char = '@'
} }
for i := 1; i < r.width; i++ { for i := 1; i < r.width; i++ {
draw(char, nil, r.style, true, false) draw(char, nil, r.style, true, false)
}
} }
} }
bloc.X++ bloc.X++
@ -806,14 +777,8 @@ 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, scrollBarRune[0], nil, scrollBarStyle) screen.SetContent(scrollX, y, '|', nil, scrollBarStyle)
} }
} }
} }

View file

@ -260,9 +260,7 @@ func (i *InfoWindow) Display() {
done := false done := false
statusLineStyle := config.DefStyle.Reverse(true) statusLineStyle := config.DefStyle.Reverse(true)
if style, ok := config.Colorscheme["statusline.suggestions"]; ok { if style, ok := config.Colorscheme["statusline"]; ok {
statusLineStyle = style
} else if style, ok := config.Colorscheme["statusline"]; ok {
statusLineStyle = style statusLineStyle = style
} }
keymenuOffset := 0 keymenuOffset := 0

View file

@ -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.IsLoaded() { if pl == nil || !pl.IsEnabled() {
return "" return ""
} }
val, err := pl.Call(plFn, luar.New(ulua.L, b)) val, err := pl.Call(plFn, luar.New(ulua.L, b))
@ -110,9 +110,7 @@ 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.suggestions"]; ok { if style, ok := config.Colorscheme["statusline"]; ok {
statusLineStyle = style
} else if style, ok := config.Colorscheme["statusline"]; ok {
statusLineStyle = style statusLineStyle = style
} }
x := 0 x := 0
@ -169,16 +167,8 @@ 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)

View file

@ -110,8 +110,6 @@ 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)
}
} }
} }

View file

@ -4,10 +4,8 @@ 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
@ -104,51 +102,3 @@ 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())
}
}

View file

@ -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 represented as a buffer and a message with a style. // It is respresented as a buffer and a message with a style.
type InfoBuf struct { type InfoBuf struct {
*buffer.Buffer *buffer.Buffer
@ -25,10 +25,6 @@ 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
@ -106,7 +102,6 @@ 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
@ -143,12 +138,13 @@ 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
@ -159,8 +155,6 @@ func (i *InfoBuf) DonePrompt(canceled bool) {
break break
} }
} }
i.PromptCallback(resp, false)
} }
// i.PromptCallback = nil // i.PromptCallback = nil
} }

View file

@ -17,6 +17,7 @@ import (
"regexp" "regexp"
"runtime" "runtime"
"strings" "strings"
"sync"
"time" "time"
"unicode/utf8" "unicode/utf8"
@ -26,6 +27,7 @@ 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 {
@ -374,9 +376,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, io.SeekCurrent)) L.SetField(pkg, "SEEK_CUR", luar.New(L, os.SEEK_CUR))
L.SetField(pkg, "SEEK_END", luar.New(L, io.SeekEnd)) L.SetField(pkg, "SEEK_END", luar.New(L, os.SEEK_END))
L.SetField(pkg, "SEEK_SET", luar.New(L, io.SeekStart)) L.SetField(pkg, "SEEK_SET", luar.New(L, os.SEEK_SET))
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))
@ -521,16 +523,21 @@ 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))
@ -538,15 +545,6 @@ 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
} }

View file

@ -22,10 +22,6 @@ 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
@ -138,10 +134,6 @@ func TempStart(screenWasNil bool) {
if !screenWasNil { if !screenWasNil {
Init() Init()
Unlock() Unlock()
if RestartCallback != nil {
RestartCallback()
}
} }
} }

View file

@ -8,6 +8,7 @@ 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"
@ -58,10 +59,15 @@ 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 err != nil { if len(totalLines) < 3 {
str = fmt.Sprint(inputCmd, " exited with error: ", err, ": ", output) if err == nil {
str = fmt.Sprint(inputCmd, " exited without error")
} else {
str = fmt.Sprint(inputCmd, " exited with error: ", err, ": ", output)
}
} }
return str return str
}, nil }, nil

View file

@ -78,9 +78,8 @@ 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) Term, _, err := terminal.Start(&t.State, cmd, t.output)
if err != nil { if err != nil {
return err return err
} }

View file

@ -16,7 +16,6 @@ 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"
@ -218,68 +217,10 @@ func FSize(f *os.File) int64 {
return fi.Size() return fi.Size()
} }
// IsWordChar returns whether or not a rune is a 'word character' // IsWordChar returns whether or not the string is a 'word character'
// Word characters are defined as numbers, letters or sub-word delimiters // Word characters are defined as numbers, letters, or '_'
func IsWordChar(r rune) bool { func IsWordChar(r rune) bool {
return IsAlphanumeric(r) || IsSubwordDelimiter(r) return unicode.IsLetter(r) || unicode.IsNumber(r) || 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
@ -374,7 +315,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 {
@ -422,28 +363,6 @@ 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))
@ -503,9 +422,14 @@ 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 == '.' || IsWordChar(c) return c == '.' || !IsNonAlphaNumeric(c)
} }
// ParseSpecial replaces escaped ts with '\t'. // ParseSpecial replaces escaped ts with '\t'.

View file

@ -185,10 +185,6 @@ 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
@ -205,7 +201,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 accordingly // Resize sets this node's size and resizes all children accordlingly
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

18
pkg/highlight/ftdetect.go Normal file
View file

@ -0,0 +1,18 @@
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
}

View file

@ -67,6 +67,9 @@ 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
@ -74,8 +77,6 @@ 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
@ -95,7 +96,19 @@ 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) []int { func findIndex(regex *regexp.Regexp, skip *regexp.Regexp, str []byte, canMatchStart, canMatchEnd bool) []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 {
@ -114,7 +127,18 @@ func findIndex(regex *regexp.Regexp, skip *regexp.Regexp, str []byte) []int {
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) [][]int { func findAllIndex(regex *regexp.Regexp, str []byte, canMatchStart, canMatchEnd bool) [][]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)
@ -133,33 +157,52 @@ func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchE
} }
} }
var firstRegion *region loc := findIndex(curRegion.end, curRegion.skip, line, start == 0, canMatchEnd)
firstLoc := []int{lineLen, 0} if loc != nil {
searchNesting := true if !statesOnly {
endLoc := findIndex(curRegion.end, curRegion.skip, line) highlights[start+loc[0]] = curRegion.limitGroup
if endLoc != nil {
if start == endLoc[0] {
searchNesting = false
} else {
firstLoc = endLoc
} }
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 searchNesting {
for _, r := range curRegion.rules.regions { if lineLen == 0 {
loc := findIndex(r.start, r.skip, line) if canMatchEnd {
if loc != nil { h.lastRegion = curRegion
if loc[0] < firstLoc[0] { }
firstLoc = loc
firstRegion = r return highlights
} }
firstLoc := []int{lineLen, 0}
var firstRegion *region
for _, r := range curRegion.rules.regions {
loc := findIndex(r.start, nil, line, start == 0, canMatchEnd)
if loc != nil {
if loc[0] < firstLoc[0] {
firstLoc = loc
firstRegion = r
} }
} }
} }
if firstRegion != nil && firstLoc[0] != lineLen { if firstLoc[0] != lineLen {
if !statesOnly { if !statesOnly {
highlights[start+firstLoc[0]] = firstRegion.limitGroup highlights[start+firstLoc[0]] = firstRegion.limitGroup
} }
h.highlightEmptyRegion(highlights, start+firstLoc[1], canMatchEnd, lineNum, sliceStart(line, firstLoc[1]), statesOnly) h.highlightRegion(highlights, start, false, lineNum, sliceEnd(line, firstLoc[0]), curRegion, 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
} }
@ -170,17 +213,11 @@ 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 { for _, m := range matches {
matches := findAllIndex(p.regex, line) for i := m[0]; i < m[1]; i++ {
for _, m := range matches { fullHighlights[i] = p.group
if ((endLoc == nil) || (m[0] < endLoc[0])) {
for i := m[0]; i < m[1]; i++ {
fullHighlights[i] = p.group
}
}
}
} }
} }
} }
@ -191,25 +228,6 @@ 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
} }
@ -226,10 +244,10 @@ func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canM
return highlights return highlights
} }
var firstRegion *region
firstLoc := []int{lineLen, 0} firstLoc := []int{lineLen, 0}
var firstRegion *region
for _, r := range h.Def.rules.regions { for _, r := range h.Def.rules.regions {
loc := findIndex(r.start, r.skip, line) loc := findIndex(r.start, nil, line, start == 0, canMatchEnd)
if loc != nil { if loc != nil {
if loc[0] < firstLoc[0] { if loc[0] < firstLoc[0] {
firstLoc = loc firstLoc = loc
@ -237,7 +255,7 @@ func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canM
} }
} }
} }
if firstRegion != nil && firstLoc[0] != lineLen { if firstLoc[0] != lineLen {
if !statesOnly { if !statesOnly {
highlights[start+firstLoc[0]] = firstRegion.limitGroup highlights[start+firstLoc[0]] = firstRegion.limitGroup
} }
@ -256,7 +274,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) matches := findAllIndex(p.regex, line, start == 0, canMatchEnd)
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
@ -302,13 +320,7 @@ 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++ { for i := 0; i < input.LinesNum(); i++ {
input.Lock()
if i >= input.LinesNum() {
input.Unlock()
break
}
line := input.LineBytes(i) line := input.LineBytes(i)
// highlights := make(LineMatch) // highlights := make(LineMatch)
@ -321,7 +333,6 @@ func (h *Highlighter) HighlightStates(input LineStates) {
curState := h.lastRegion curState := h.lastRegion
input.SetState(i, curState) input.SetState(i, curState)
input.Unlock()
} }
} }
@ -330,9 +341,7 @@ 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
} }
@ -347,7 +356,6 @@ func (h *Highlighter) HighlightMatches(input LineStates, startline, endline int)
} }
input.SetMatch(i, match) input.SetMatch(i, match)
input.Unlock()
} }
} }
@ -359,19 +367,9 @@ func (h *Highlighter) ReHighlightStates(input LineStates, startline int) int {
h.lastRegion = nil h.lastRegion = nil
if startline > 0 { if startline > 0 {
input.Lock() h.lastRegion = input.State(startline - 1)
if startline-1 < input.LinesNum() {
h.lastRegion = input.State(startline - 1)
}
input.Unlock()
} }
for i := startline; ; i++ { for i := startline; i < input.LinesNum(); i++ {
input.Lock()
if i >= input.LinesNum() {
input.Unlock()
break
}
line := input.LineBytes(i) line := input.LineBytes(i)
// highlights := make(LineMatch) // highlights := make(LineMatch)
@ -385,7 +383,6 @@ 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
@ -397,9 +394,6 @@ 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)

View file

@ -33,28 +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
FileNameRegex *regexp.Regexp FtDetect [2]*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 {
FNameRegexStr string `yaml:"filename"` FNameRgx string `yaml:"filename"`
HeaderRegexStr string `yaml:"header"` HeaderRgx 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{}
} }
// A Pattern is one simple syntax rule // A Pattern is one simple syntax rule
@ -98,24 +97,20 @@ 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) < 4 { if len(lines) < 3 {
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])
fnameRegexStr := string(lines[1]) fnameRgx := string(lines[1])
headerRegexStr := string(lines[2]) headerRgx := string(lines[2])
signatureRegexStr := string(lines[3])
if fnameRegexStr != "" { if fnameRgx != "" {
header.FileNameRegex, err = regexp.Compile(fnameRegexStr) header.FtDetect[0], err = regexp.Compile(fnameRgx)
} }
if err == nil && headerRegexStr != "" { if headerRgx != "" {
header.HeaderRegex, err = regexp.Compile(headerRegexStr) header.FtDetect[1], err = regexp.Compile(headerRgx)
}
if err == nil && signatureRegexStr != "" {
header.SignatureRegex, err = regexp.Compile(signatureRegexStr)
} }
if err != nil { if err != nil {
@ -137,14 +132,11 @@ func MakeHeaderYaml(data []byte) (*Header, error) {
header := new(Header) header := new(Header)
header.FileType = hdrYaml.FileType header.FileType = hdrYaml.FileType
if hdrYaml.Detect.FNameRegexStr != "" { if hdrYaml.Detect.FNameRgx != "" {
header.FileNameRegex, err = regexp.Compile(hdrYaml.Detect.FNameRegexStr) header.FtDetect[0], err = regexp.Compile(hdrYaml.Detect.FNameRgx)
} }
if err == nil && hdrYaml.Detect.HeaderRegexStr != "" { if hdrYaml.Detect.HeaderRgx != "" {
header.HeaderRegex, err = regexp.Compile(hdrYaml.Detect.HeaderRegexStr) header.FtDetect[1], err = regexp.Compile(hdrYaml.Detect.HeaderRgx)
}
if err == nil && hdrYaml.Detect.SignatureRegexStr != "" {
header.SignatureRegex, err = regexp.Compile(hdrYaml.Detect.SignatureRegexStr)
} }
if err != nil { if err != nil {
@ -154,37 +146,6 @@ 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() {
@ -209,19 +170,11 @@ 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
} }
@ -238,12 +191,12 @@ func ParseDef(f *File, header *Header) (s *Def, err error) {
} }
}() }()
src := f.yamlSrc rules := f.yamlSrc
s = new(Def) s = new(Def)
s.Header = header s.Header = header
for k, v := range src { for k, v := range rules {
if k == "rules" { if k == "rules" {
inputRules := v.([]interface{}) inputRules := v.([]interface{})
@ -256,11 +209,6 @@ 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
} }
@ -355,10 +303,6 @@ 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 {
@ -412,56 +356,30 @@ func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegio
r.group = groupNum r.group = groupNum
r.parent = prevRegion r.parent = prevRegion
// start is mandatory r.start, err = regexp.Compile(regionInfo["start"].(string))
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)
} }
// end is mandatory r.end, err = regexp.Compile(regionInfo["end"].(string))
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 skip, ok := regionInfo["skip"]; ok { if _, ok := regionInfo["skip"]; ok {
skip := skip.(string) r.skip, err = regexp.Compile(regionInfo["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 groupStr, ok := regionInfo["limit-group"]; ok { if _, ok := regionInfo["limit-group"]; ok {
groupStr := groupStr.(string) groupStr := regionInfo["limit-group"].(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

View file

@ -28,6 +28,3 @@ 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"

View file

@ -26,6 +26,3 @@ 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"

View file

@ -42,6 +42,3 @@ 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"

View file

@ -38,6 +38,3 @@ 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"

View file

@ -29,6 +29,3 @@ 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"

View file

@ -1 +1,31 @@
include "monokai" color-link default "#F8F8F2,#282828"
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"

View file

@ -43,7 +43,3 @@ 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"

View file

@ -33,6 +33,3 @@ 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"

View file

@ -33,6 +33,3 @@ 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"

View file

@ -33,6 +33,3 @@ 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"

View file

@ -24,6 +24,3 @@ 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"

View file

@ -24,6 +24,3 @@ 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"

View file

@ -24,6 +24,3 @@ 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"

View file

@ -21,6 +21,3 @@ 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"

View file

@ -20,7 +20,6 @@ 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"
@ -31,6 +30,3 @@ 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"

View file

@ -23,6 +23,3 @@ 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"

View file

@ -29,6 +29,3 @@ 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"

View file

@ -34,6 +34,3 @@ 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"

View file

@ -28,10 +28,6 @@ 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"

View file

@ -10,7 +10,6 @@ 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"
@ -28,6 +27,3 @@ 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"

View file

@ -26,6 +26,3 @@ 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"

View file

@ -25,6 +25,3 @@ 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"

View file

@ -24,6 +24,3 @@ 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"

View file

@ -35,6 +35,3 @@ 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"

View file

@ -25,6 +25,3 @@ 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"

View file

@ -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:
``` ```
@ -93,7 +93,7 @@ and set this variable yourself.
* `solarized-tc`: this is the solarized colorscheme for true color. * `solarized-tc`: this is the solarized colorscheme for true color.
* `atom-dark`: this colorscheme is based off of Atom's "dark" colorscheme. * `atom-dark`: this colorscheme is based off of Atom's "dark" colorscheme.
* `cmc-tc`: A true colour variant of the cmc theme. It requires true color to * `cmc-tc`: A true colour variant of the cmc theme. It requires true color to
look its best. Use cmc-16 if your terminal doesn't support true color. look its best. Use cmc-16 if your terminal doesn't support true color.
* `gruvbox-tc`: The true color version of the gruvbox colorscheme * `gruvbox-tc`: The true color version of the gruvbox colorscheme
* `material-tc`: Colorscheme based off of Google's Material Design palette * `material-tc`: Colorscheme based off of Google's Material Design palette
@ -106,7 +106,7 @@ be found
Custom colorschemes should be placed in the `~/.config/micro/colorschemes` Custom colorschemes should be placed in the `~/.config/micro/colorschemes`
directory. directory.
A number of custom directives are placed in a `.micro` file. Colorschemes are A number of custom directives are placed in a `.micro` file. Colorschemes are
typically only 18-30 lines in total. typically only 18-30 lines in total.
To create the colorscheme you need to link highlight groups with To create the colorscheme you need to link highlight groups with
@ -152,7 +152,7 @@ Then you can use the terminals 256 colors by using their numbers 1-256 (numbers
If the user's terminal supports true color, then you can also specify colors If the user's terminal supports true color, then you can also specify colors
exactly using their hex codes. If the terminal is not true color but micro is exactly using their hex codes. If the terminal is not true color but micro is
told to use a true color colorscheme it will attempt to map the colors to the told to use a true color colorscheme it will attempt to map the colors to the
available 256 colors. available 256 colors.
Generally colorschemes which require true color terminals to look good are Generally colorschemes which require true color terminals to look good are
@ -194,10 +194,6 @@ 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.
@ -214,9 +210,9 @@ 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 example `constant.bool.true` is found then micro will definitions, so if, for examle `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`.
Here's a list of subgroups used in micro's built-in syntax files. Here's a list of subgroups used in micro's built-in syntax files.
@ -224,10 +220,10 @@ Here's a list of subgroups used in micro's built-in syntax files.
* constant.bool * constant.bool
* constant.bool.true * constant.bool.true
* constant.bool.false * constant.bool.false
* constant.number * constant.number
* constant.specialChar * constant.specialChar
* constant.string * constant.string
* constant.string.url * constant.string.url
* identifier.class (Also used for functions) * identifier.class (Also used for functions)
* identifier.macro * identifier.macro
* identifier.var * identifier.var
@ -240,12 +236,6 @@ 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
@ -254,7 +244,7 @@ languages.
Micro's builtin syntax highlighting tries very hard to be sane, sensible and Micro's builtin syntax highlighting tries very hard to be sane, sensible and
provide ample coverage of the meaningful elements of a language. Micro has provide ample coverage of the meaningful elements of a language. Micro has
syntax files built in for over 100 languages now! However, there may be syntax files built in for over 100 languages now! However, there may be
situations where you find Micro's highlighting to be insufficient or not to situations where you find Micro's highlighting to be insufficient or not to
your liking. The good news is that you can create your own syntax files, and your liking. The good news is that you can create your own syntax files, and
place them in `~/.config/micro/syntax` and Micro will use those instead. place them in `~/.config/micro/syntax` and Micro will use those instead.
@ -277,9 +267,8 @@ 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
In addition to the `filename` regex (or even instead of it) you can provide of the file. For example:
a `header` regex that will check the first line of the file. For example:
``` ```
detect: detect:
@ -287,32 +276,6 @@ 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
@ -393,28 +356,15 @@ example, the following is possible for html:
- include: "css" - include: "css"
``` ```
Note that nested include (i.e. including syntax files that include other syntax ## Syntax file headers
files) is not supported yet.
### Default syntax highlighting Syntax file headers are an optimization and it is likely you do not need to
worry about them.
If micro cannot detect the filetype of the file, it falls back to using the Syntax file headers are files that contain only the filetype and the detection
default syntax highlighting for it, which highlights just the bare minimum: regular expressions for a given syntax file. They have a `.hdr` suffix and are
email addresses, URLs etc. used by default only for the pre-installed syntax files. Header files allow
micro to parse the syntax files much faster when checking the filetype of a
Just like in other cases, you can override the default highlighting by adding certain file. Custom syntax files may provide header files in
your own custom `default.yaml` file to `~/.config/micro/syntax`. `~/.config/micro/syntax` as well but it is not necessary (only do this if you
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)#.*$"
```

View file

@ -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,43 +21,32 @@ 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[:col]'`: goes to the given absolute line (and optional column) * `goto 'line'`: jumps to the given line number. A negative number can be
number. passed to jump inward from the end of the file; for example, -5 jumps
A negative number can be passed to go inward from the end of the file. to the 5th-last line in the file.
Example: -5 goes to the 5th-last line in the file.
* `jump 'line[:col]'`: goes to the given relative number from the current * `replace 'search' 'value' 'flags'?`: This will replace `search` with `value`.
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
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.
See `replace` command for more information. See `replace` command for more information.
* `set 'option' 'value'`: sets the option to value. See the `options` help * `set 'option' 'value'`: sets the option to value. See the `options` help
topic for a list of options you can set. This will modify your topic for a list of options you can set. This will modify your
@ -68,18 +57,18 @@ quotes here but these are not necessary when entering the command in micro.
* `show 'option'`: shows the current value of the given option. * `show 'option'`: shows the current value of the given option.
* `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`).
@ -100,7 +89,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.
@ -125,10 +114,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 'key'`: Show the action(s) bound to a given key. For example * `showkey`: 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.

View file

@ -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,14 +31,10 @@ 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. Since 0.13.0, reading has been disabled by default. * `alacritty`: supported.
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.
@ -49,12 +45,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.
@ -63,8 +59,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
@ -77,8 +73,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
@ -90,7 +86,7 @@ sends a list of key events, this can cause issues because micro
will think you manually entered each character and may add closing will think you manually entered each character and may add closing
brackets or automatic indentation, which will mess up the pasted brackets or automatic indentation, which will mess up the pasted
text. To avoid this, you can temporarily enable the `paste` option text. To avoid this, you can temporarily enable the `paste` option
while you perform the paste. When paste option is on, micro will while you perform the paste. When paste option is on, micro will
aggregate lists of multiple key events into larger paste events. aggregate lists of multiple key events into larger paste events.
It is a good idea to disable the `paste` option during normal use It is a good idea to disable the `paste` option during normal use
as occasionally if you are typing quickly, the terminal will send as occasionally if you are typing quickly, the terminal will send
@ -101,7 +97,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
@ -114,12 +110,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
@ -130,14 +126,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

View file

@ -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
@ -129,7 +129,7 @@ you can use `Ctrl-n` and `Ctrl-p` to cycle through matches.
### Function keys. ### Function keys.
Warning! The function keys may not work in all terminals! Warning! The function keys may not work in all terminals!
| Key | Description of function | | Key | Description of function |
|------ |-------------------------- | |------ |-------------------------- |

View file

@ -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,31 +26,31 @@ 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`.

View file

@ -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 to the start and end of the buffer. cursor 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.
@ -30,21 +30,21 @@ following in the `bindings.json` file.
```json ```json
{ {
"Ctrl-y": "Undo", "Ctrl-y": "Undo",
"Ctrl-z": "Redo" "Ctrl-z": "Redo"
} }
``` ```
**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,18 +178,12 @@ 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
@ -206,8 +200,6 @@ Find
FindLiteral FindLiteral
FindNext FindNext
FindPrevious FindPrevious
DiffPrevious
DiffNext
Undo Undo
Redo Redo
Copy Copy
@ -415,14 +407,8 @@ mouse actions)
``` ```
MouseLeft MouseLeft
MouseLeftDrag
MouseLeftRelease
MouseMiddle MouseMiddle
MouseMiddleDrag
MouseMiddleRelease
MouseRight MouseRight
MouseRightDrag
MouseRightRelease
MouseWheelUp MouseWheelUp
MouseWheelDown MouseWheelDown
MouseWheelLeft MouseWheelLeft
@ -475,31 +461,29 @@ conventions for text editing defaults.
"Alt-{": "ParagraphPrevious", "Alt-{": "ParagraphPrevious",
"Alt-}": "ParagraphNext", "Alt-}": "ParagraphNext",
"Enter": "InsertNewline", "Enter": "InsertNewline",
"Ctrl-h": "Backspace", "Ctrl-h": "Backspace",
"Backspace": "Backspace", "Backspace": "Backspace",
"Alt-CtrlH": "DeleteWordLeft", "Alt-CtrlH": "DeleteWordLeft",
"Alt-Backspace": "DeleteWordLeft", "Alt-Backspace": "DeleteWordLeft",
"Tab": "Autocomplete|IndentSelection|InsertTab", "Tab": "Autocomplete|IndentSelection|InsertTab",
"Backtab": "OutdentSelection|OutdentLine", "Backtab": "OutdentSelection|OutdentLine",
"Ctrl-o": "OpenFile", "Ctrl-o": "OpenFile",
"Ctrl-s": "Save", "Ctrl-s": "Save",
"Ctrl-f": "Find", "Ctrl-f": "Find",
"Alt-F": "FindLiteral", "Alt-F": "FindLiteral",
"Ctrl-n": "FindNext", "Ctrl-n": "FindNext",
"Ctrl-p": "FindPrevious", "Ctrl-p": "FindPrevious",
"Alt-[": "DiffPrevious|CursorStart", "Ctrl-z": "Undo",
"Alt-]": "DiffNext|CursorEnd", "Ctrl-y": "Redo",
"Ctrl-z": "Undo", "Ctrl-c": "CopyLine|Copy",
"Ctrl-y": "Redo", "Ctrl-x": "Cut",
"Ctrl-c": "CopyLine|Copy", "Ctrl-k": "CutLine",
"Ctrl-x": "Cut", "Ctrl-d": "DuplicateLine",
"Ctrl-k": "CutLine", "Ctrl-v": "Paste",
"Ctrl-d": "DuplicateLine", "Ctrl-a": "SelectAll",
"Ctrl-v": "Paste", "Ctrl-t": "AddTab",
"Ctrl-a": "SelectAll", "Alt-,": "PreviousTab",
"Ctrl-t": "AddTab", "Alt-.": "NextTab",
"Alt-,": "PreviousTab",
"Alt-.": "NextTab",
"Home": "StartOfText", "Home": "StartOfText",
"End": "EndOfLine", "End": "EndOfLine",
"CtrlHome": "CursorStart", "CtrlHome": "CursorStart",
@ -508,17 +492,17 @@ conventions for text editing defaults.
"PageDown": "CursorPageDown", "PageDown": "CursorPageDown",
"CtrlPageUp": "PreviousTab", "CtrlPageUp": "PreviousTab",
"CtrlPageDown": "NextTab", "CtrlPageDown": "NextTab",
"Ctrl-g": "ToggleHelp", "Ctrl-g": "ToggleHelp",
"Alt-g": "ToggleKeyMenu", "Alt-g": "ToggleKeyMenu",
"Ctrl-r": "ToggleRuler", "Ctrl-r": "ToggleRuler",
"Ctrl-l": "command-edit:goto ", "Ctrl-l": "command-edit:goto ",
"Delete": "Delete", "Delete": "Delete",
"Ctrl-b": "ShellMode", "Ctrl-b": "ShellMode",
"Ctrl-q": "Quit", "Ctrl-q": "Quit",
"Ctrl-e": "CommandMode", "Ctrl-e": "CommandMode",
"Ctrl-w": "NextSplit", "Ctrl-w": "NextSplit",
"Ctrl-u": "ToggleMacro", "Ctrl-u": "ToggleMacro",
"Ctrl-j": "PlayMacro", "Ctrl-j": "PlayMacro",
"Insert": "ToggleOverwriteMode", "Insert": "ToggleOverwriteMode",
// Emacs-style keybindings // Emacs-style keybindings
@ -536,15 +520,12 @@ conventions for text editing defaults.
"Esc": "Escape", "Esc": "Escape",
// Mouse bindings // Mouse bindings
"MouseWheelUp": "ScrollUp", "MouseWheelUp": "ScrollUp",
"MouseWheelDown": "ScrollDown", "MouseWheelDown": "ScrollDown",
"MouseLeft": "MousePress", "MouseLeft": "MousePress",
"MouseLeftDrag": "MouseDrag", "MouseMiddle": "PastePrimary",
"MouseLeftRelease": "MouseRelease", "Ctrl-MouseLeft": "MouseMultiCursor",
"MouseMiddle": "PastePrimary",
"Ctrl-MouseLeft": "MouseMultiCursor",
// Multi-cursor bindings
"Alt-n": "SpawnMultiCursor", "Alt-n": "SpawnMultiCursor",
"AltShiftUp": "SpawnMultiCursorUp", "AltShiftUp": "SpawnMultiCursorUp",
"AltShiftDown": "SpawnMultiCursorDown", "AltShiftDown": "SpawnMultiCursorDown",
@ -648,12 +629,10 @@ are given below:
"Esc": "AbortCommand", "Esc": "AbortCommand",
// Mouse bindings // Mouse bindings
"MouseWheelUp": "HistoryUp", "MouseWheelUp": "HistoryUp",
"MouseWheelDown": "HistoryDown", "MouseWheelDown": "HistoryDown",
"MouseLeft": "MousePress", "MouseLeft": "MousePress",
"MouseLeftDrag": "MouseDrag", "MouseMiddle": "PastePrimary"
"MouseLeftRelease": "MouseRelease",
"MouseMiddle": "PastePrimary"
} }
} }
``` ```

View file

@ -11,10 +11,10 @@ if you have set either of the above environment variables).
Here are the available options: Here are the available options:
* `autoindent`: when creating a new line, use the same indentation as the * `autoindent`: when creating a new line, use the same indentation as the
previous line. previous line.
default value: `true` default value: `true`
* `autosave`: automatically save the buffer every n seconds, where n is the * `autosave`: automatically save the buffer every n seconds, where n is the
value of the autosave option. Also when quitting on a modified buffer, micro value of the autosave option. Also when quitting on a modified buffer, micro
@ -77,39 +77,32 @@ Here are the available options:
specified column. This is useful if you want column 80 to be highlighted specified column. This is useful if you want column 80 to be highlighted
special for example. special for example.
default value: `0` default value: `0`
* `colorscheme`: loads the colorscheme stored in * `colorscheme`: loads the colorscheme stored in
$(configDir)/colorschemes/`option`.micro, This setting is `global only`. $(configDir)/colorschemes/`option`.micro, This setting is `global only`.
default value: `default` default value: `default`
Note that the default colorschemes (default, solarized, and solarized-tc) Note that the default colorschemes (default, solarized, and solarized-tc)
are not located in configDir, because they are embedded in the micro are not located in configDir, because they are embedded in the micro
binary. binary.
The colorscheme can be selected from all the files in the The colorscheme can be selected from all the files in the
~/.config/micro/colorschemes/ directory. Micro comes by default with ~/.config/micro/colorschemes/ directory. Micro comes by default with
three colorschemes: three colorschemes:
You can read more about micro's colorschemes in the `colors` help topic You can read more about micro's colorschemes in the `colors` help topic
(`help colors`). (`help colors`).
* `cursorline`: highlight the line that the cursor is on in a different color * `cursorline`: highlight the line that the cursor is on in a different color
(the color is defined by the colorscheme you are using). (the color is defined by the colorscheme you are using).
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`
* `divchars`: specifies the "divider" characters used for the dividing line * `divchars`: specifies the "divider" characters used for the dividing line
between vertical/horizontal splits. The first character is for vertical between vertical/horizontal splits. The first character is for vertical
@ -134,13 +127,7 @@ Here are the available options:
* `eofnewline`: micro will automatically add a newline to the end of the * `eofnewline`: micro will automatically add a newline to the end of the
file if one does not exist. file if one does not exist.
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
@ -151,7 +138,7 @@ Here are the available options:
intensive. This option will be automatically disabled if the file size intensive. This option will be automatically disabled if the file size
exceeds 50KB. exceeds 50KB.
default value: `false` default value: `false`
* `fileformat`: this determines what kind of line endings micro will use for * `fileformat`: this determines what kind of line endings micro will use for
the file. Unix line endings are just `\n` (linefeed) whereas dos line the file. Unix line endings are just `\n` (linefeed) whereas dos line
@ -164,12 +151,12 @@ 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` on Unix systems, `dos` on Windows default value: `unix`
* `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.
default value: `unknown`. This will be automatically overridden depending default value: `unknown`. This will be automatically overridden depending
on the file you open. on the file you open.
* `hlsearch`: highlight all instances of the searched text after a successful * `hlsearch`: highlight all instances of the searched text after a successful
@ -179,70 +166,49 @@ Here are the available options:
change the `hlsearch` setting. As long as `hlsearch` is set to true, the next change the `hlsearch` setting. As long as `hlsearch` is set to true, the next
search will have the highlighting turned on again. search will have the highlighting turned on again.
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`
* `ignorecase`: perform case-insensitive searches. * `ignorecase`: perform case-insensitive searches.
default value: `true` default value: `true`
* `indentchar`: sets the indentation character. This will not be inserted into * `indentchar`: sets the indentation character. This will not be inserted into
files; it is only a visual indicator that whitespace is present. If set to a files; it is only a visual indicator that whitespace is present. If set to a
printing character, it functions as a subset of the "show invisibles" printing character, it functions as a subset of the "show invisibles"
setting available in many other text editors. The color of this character is setting available in many other text editors. The color of this character is
determined by the `indent-char` field in the current theme rather than the determined by the `indent-char` field in the current theme rather than the
default text color. default text color.
default value: ` ` (space) default value: ` ` (space)
* `infobar`: enables the line at the bottom of the editor where messages are * `infobar`: enables the line at the bottom of the editor where messages are
printed. This option is `global only`. printed. This option is `global only`.
default value: `true` default value: `true`
* `keepautoindent`: when using autoindent, whitespace is added for you. This * `keepautoindent`: when using autoindent, whitespace is added for you. This
option determines if when you move to the next line without any insertions option determines if when you move to the next line without any insertions
the whitespace that was added should be deleted to remove trailing the whitespace that was added should be deleted to remove trailing
whitespace. By default, the autoindent whitespace is deleted if the line whitespace. By default, the autoindent whitespace is deleted if the line
was left empty. was left empty.
default value: `false` default value: `false`
* `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 the key binding, bind `Alt-g` to `None`. the statusline. To disable this, simply by `Alt-g` to `UnbindKey`.
default value: `false` default value: `false`
* `matchbrace`: show matching braces for '()', '{}', '[]' when the cursor * `matchbrace`: underline matching braces for '()', '{}', '[]' when the cursor
is on a brace character or next to it. is on a brace character.
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.
@ -255,7 +221,7 @@ Here are the available options:
example, because the terminal has access to the local clipboard and micro example, because the terminal has access to the local clipboard and micro
does not). does not).
default value: `true` default value: `true`
* `multiopen`: specifies how to layout multiple files opened at startup. * `multiopen`: specifies how to layout multiple files opened at startup.
Most useful as a command-line option, like `-multiopen vsplit`. Possible Most useful as a command-line option, like `-multiopen vsplit`. Possible
@ -264,11 +230,11 @@ Here are the available options:
* `vsplit`: open files side-by-side. * `vsplit`: open files side-by-side.
* `hsplit`: open files stacked top to bottom. * `hsplit`: open files stacked top to bottom.
default value: `tab` default value: `tab`
* `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.
@ -309,34 +275,26 @@ 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. lines. Note: This setting overrides `keepautoindent`
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`
* `ruler`: display line numbers. * `ruler`: display line numbers.
default value: `true` default value: `true`
* `relativeruler`: make line numbers display relatively. If set to true, all * `relativeruler`: make line numbers display relatively. If set to true, all
lines except for the line that the cursor is located will display the distance lines except for the line that the cursor is located will display the distance
from the cursor's line. from the cursor's line.
default value: `false` default value: `false`
* `savecursor`: remember where the cursor was last time the file was opened and * `savecursor`: remember where the cursor was last time the file was opened and
put it there when you open the file again. Information is saved to put it there when you open the file again. Information is saved to
`~/.config/micro/buffers/` `~/.config/micro/buffers/`
default value: `false` default value: `false`
* `savehistory`: remember command history between closing and re-opening * `savehistory`: remember command history between closing and re-opening
micro. Information is saved to `~/.config/micro/buffers/history`. micro. Information is saved to `~/.config/micro/buffers/history`.
@ -347,44 +305,40 @@ Here are the available options:
so if you close and reopen a file, you can keep undoing. Information is so if you close and reopen a file, you can keep undoing. Information is
saved to `~/.config/micro/buffers/`. saved to `~/.config/micro/buffers/`.
default value: `false` default value: `false`
* `scrollbar`: display a scroll bar * `scrollbar`: display a scroll bar
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.
default value: `3` default value: `3`
* `scrollspeed`: amount of lines to scroll for one scroll event. * `scrollspeed`: amount of lines to scroll for one scroll event.
default value: `2` default value: `2`
* `smartpaste`: add leading whitespace when pasting multiple lines. * `smartpaste`: add leading whitespace when pasting multiple lines.
This will attempt to preserve the current indentation level when pasting an This will attempt to preserve the current indentation level when pasting an
unindented block. unindented block.
default value: `true` default value: `true`
* `softwrap`: wrap lines that are too long to fit on the screen. * `softwrap`: wrap lines that are too long to fit on the screen.
default value: `false` default value: `false`
* `splitbottom`: when a horizontal split is created, create it below the * `splitbottom`: when a horizontal split is created, create it below the
current split. current split.
default value: `true` default value: `true`
* `splitright`: when a vertical split is created, create it to the right of the * `splitright`: when a vertical split is created, create it to the right of the
current split. current split.
default value: `true` default value: `true`
* `statusformatl`: format string definition for the left-justified part of the * `statusformatl`: format string definition for the left-justified part of the
statusline. Special directives should be placed inside `$()`. Special statusline. Special directives should be placed inside `$()`. Special
@ -403,36 +357,36 @@ Here are the available options:
* `statusline`: display the status line at the bottom of the screen. * `statusline`: display the status line at the bottom of the screen.
default value: `true` default value: `true`
* `sucmd`: specifies the super user command. On most systems this is "sudo" but * `sucmd`: specifies the super user command. On most systems this is "sudo" but
on BSD it can be "doas." This option can be customized and is only used when on BSD it can be "doas." This option can be customized and is only used when
saving with su. saving with su.
default value: `sudo` default value: `sudo`
* `syntax`: enables syntax highlighting. * `syntax`: enables syntax highlighting.
default value: `true` default value: `true`
* `tabmovement`: navigate spaces at the beginning of lines as if they are tabs * `tabmovement`: navigate spaces at the beginning of lines as if they are tabs
(e.g. move over 4 spaces at once). This option only does anything if (e.g. move over 4 spaces at once). This option only does anything if
`tabstospaces` is on. `tabstospaces` is on.
default value: `false` default value: `false`
* `tabhighlight`: inverts the tab characters' (filename, save indicator, etc) * `tabhighlight`: inverts the tab characters' (filename, save indicator, etc)
colors with respect to the tab bar. colors with respect to the tab bar.
default value: false default value: false
* `tabreverse`: reverses the tab bar colors when active. * `tabreverse`: reverses the tab bar colors when active.
default value: true default value: true
* `tabsize`: the size in spaces that a tab character should be displayed with. * `tabsize`: the size in spaces that a tab character should be displayed with.
default value: `4` default value: `4`
* `tabstospaces`: use spaces instead of tabs. Note: This option will be * `tabstospaces`: use spaces instead of tabs. Note: This option will be
overridden by [the `ftoptions` plugin](https://github.com/zyedidia/micro/blob/master/runtime/plugins/ftoptions/ftoptions.lua) overridden by [the `ftoptions` plugin](https://github.com/zyedidia/micro/blob/master/runtime/plugins/ftoptions/ftoptions.lua)
@ -440,25 +394,25 @@ Here are the available options:
your config. See [issue #2213](https://github.com/zyedidia/micro/issues/2213) your config. See [issue #2213](https://github.com/zyedidia/micro/issues/2213)
for more details. for more details.
default value: `false` default value: `false`
* `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`
* `wordwrap`: wrap long lines by words, i.e. break at spaces. This option * `wordwrap`: wrap long lines by words, i.e. break at spaces. This option
only does anything if `softwrap` is on. only does anything if `softwrap` is on.
default value: `false` default value: `false`
* `xterm`: micro will assume that the terminal it is running in conforms to * `xterm`: micro will assume that the terminal it is running in conforms to
`xterm-256color` regardless of what the `$TERM` variable actually contains. `xterm-256color` regardless of what the `$TERM` variable actually contains.
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`
--- ---
@ -481,7 +435,7 @@ or disable them:
recent Git commit rather than the diff since opening the file. recent Git commit rather than the diff since opening the file.
Any option you set in the editor will be saved to the file Any option you set in the editor will be saved to the file
~/.config/micro/settings.json so, in effect, your configuration file will be ~/.config/micro/settings.json so, in effect, your configuration file will be
created for you. If you'd like to take your configuration with you to another created for you. If you'd like to take your configuration with you to another
machine, simply copy the settings.json to the other machine. machine, simply copy the settings.json to the other machine.
@ -526,7 +480,6 @@ 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,
@ -583,14 +536,14 @@ all files except Go files, and `tabsize` 4 for all files except Ruby files:
```json ```json
{ {
"ft:go": { "ft:go": {
"tabstospaces": false "tabstospaces": false
}, },
"ft:ruby": { "ft:ruby": {
"tabsize": 2 "tabsize": 2
}, },
"tabstospaces": true, "tabstospaces": true,
"tabsize": 4 "tabsize": 4
} }
``` ```
@ -598,13 +551,13 @@ Or similarly you can match with globs:
```json ```json
{ {
"*.go": { "*.go": {
"tabstospaces": false "tabstospaces": false
}, },
"*.rb": { "*.rb": {
"tabsize": 2 "tabsize": 2
}, },
"tabstospaces": true, "tabstospaces": true,
"tabsize": 4 "tabsize": 4
} }
``` ```

View file

@ -5,8 +5,7 @@ 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... 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)
[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:
``` ```
@ -57,8 +56,6 @@ 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
@ -68,14 +65,6 @@ 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:
@ -87,7 +76,7 @@ end
``` ```
The `bp` variable is a reference to the bufpane the action is being executed The `bp` variable is a reference to the bufpane the action is being executed
within. This is almost always the current bufpane. within. This is almost always the current bufpane.
All available actions are listed in the keybindings section of the help. All available actions are listed in the keybindings section of the help.
@ -126,104 +115,99 @@ 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)`:
create a command with the given name, and lua callback function when create a command with the given name, and lua callback function when
the command is run. A completer may also be given to specify how the command is run. A completer may also be given to specify how
autocompletion should work with the custom command. autocompletion should work with the custom command.
- `FileComplete`: autocomplete using files in the current directory - `FileComplete`: autocomplete using files in the current directory
- `HelpComplete`: autocomplete using names of help documents - `HelpComplete`: autocomplete using names of help documents
- `OptionComplete`: autocomplete using names of options - `OptionComplete`: autocomplete using names of options
- `OptionValueComplete`: autocomplete using names of options, and valid - `OptionValueComplete`: autocomplete using names of options, and valid
values afterwards values afterwards
- `NoComplete`: no autocompletion suggestions - `NoComplete`: no autocompletion suggestions
- `TryBindKey(k, v string, overwrite bool) (bool, error)`: bind the key - `TryBindKey(k, v string, overwrite bool) (bool, error)`: bind the key
`k` to the string `v` in the `bindings.json` file. If `overwrite` is `k` to the string `v` in the `bindings.json` file. If `overwrite` is
true, this will overwrite any existing binding to key `k`. Returns true true, this will overwrite any existing binding to key `k`. Returns true
if the binding was made, and a possible error (for example writing to if the binding was made, and a possible error (for example writing to
`bindings.json` can cause an error). `bindings.json` can cause an error).
- `Reload()`: reload configuration files. - `Reload()`: reload configuration files.
- `AddRuntimeFileFromMemory(filetype RTFiletype, filename, data string)`: - `AddRuntimeFileFromMemory(filetype RTFiletype, filename, data string)`:
add a runtime file to the `filetype` runtime filetype, with name add a runtime file to the `filetype` runtime filetype, with name
`filename` and data `data`. `filename` and data `data`.
- `AddRuntimeFilesFromDirectory(plugin string, filetype RTFiletype, - `AddRuntimeFilesFromDirectory(plugin string, filetype RTFiletype,
directory, pattern string)`: directory, pattern string)`:
add runtime files for the given plugin with the given RTFiletype from add runtime files for the given plugin with the given RTFiletype from
a directory within the plugin root. Only adds files that match the a directory within the plugin root. Only adds files that match the
pattern using Go's `filepath.Match` pattern using Go's `filepath.Match`
- `AddRuntimeFile(plugin string, filetype RTFiletype, filepath string)`: - `AddRuntimeFile(plugin string, filetype RTFiletype, filepath string)`:
add a given file inside the plugin root directory as a runtime file add a given file inside the plugin root directory as a runtime file
to the given RTFiletype category. to the given RTFiletype category.
- `ListRuntimeFiles(fileType RTFiletype) []string`: returns a list of - `ListRuntimeFiles(fileType RTFiletype) []string`: returns a list of
names of runtime files of the given type. names of runtime files of the given type.
- `ReadRuntimeFile(fileType RTFiletype, name string) string`: returns the - `ReadRuntimeFile(fileType RTFiletype, name string) string`: returns the
contents of a given runtime file. contents of a given runtime file.
- `NewRTFiletype() int`: creates a new RTFiletype, and returns its value. - `NewRTFiletype() int`: creates a new RTFiletype, and returns its value.
- `RTColorscheme`: runtime files for colorschemes. - `RTColorscheme`: runtime files for colorschemes.
- `RTSyntax`: runtime files for syntax files. - `RTSyntax`: runtime files for syntax files.
- `RTHelp`: runtime files for help documents. - `RTHelp`: runtime files for help documents.
- `RTPlugin`: runtime files for plugin source code. - `RTPlugin`: runtime files for plugin source code.
- `RegisterCommonOption(pl string, name string, defaultvalue interface{})`: - `RegisterCommonOption(pl string, name string, defaultvalue interface{})`:
registers a new option with for the given plugin. The name of the registers a new option with for the given plugin. The name of the
option will be `pl.name`, and will have the given default value. Since option will be `pl.name`, and will have the given default value. Since
this registers a common option, the option will be modifiable on a this registers a common option, the option will be modifiable on a
per-buffer basis, while also having a global value (in the per-buffer basis, while also having a global value (in the
GlobalSettings map). GlobalSettings map).
- `RegisterGlobalOption(pl string, name string, defaultvalue interface{})`: - `RegisterGlobalOption(pl string, name string, defaultvalue interface{})`:
same as `RegisterCommonOption` but the option cannot be modified same as `RegisterCommonOption` but the option cannot be modified
locally to each buffer. locally to each buffer.
- `GetGlobalOption(name string) interface{}`: returns the value of a - `GetGlobalOption(name string) interface{}`: returns the value of a
given plugin in the `GlobalSettings` map. given plugin in the `GlobalSettings` map.
- `SetGlobalOption(option, value string) error`: sets an option to a - `SetGlobalOption(option, value string) error`: sets an option to a
given value. Same as using the `> set` command. This will parse the given value. Same as using the `> set` command. This will parse the
value to the actual value type. value to the actual value type.
- `SetGlobalOptionNative(option string, value interface{}) error`: sets - `SetGlobalOptionNative(option string, value interface{}) error`: sets
an option to a given value, where the type of value is the actual an option to a given value, where the type of value is the actual
type of the value internally. type of the value internally.
* `micro/shell` * `micro/shell`
- `ExecCommand(name string, arg ...string) (string, error)`: runs an - `ExecCommand(name string, arg ...string) (string, error)`: runs an
executable with the given arguments, and pipes the output (stderr executable with the given arguments, and pipes the output (stderr
and stdout) of the executable to an internal buffer, which is and stdout) of the executable to an internal buffer, which is
returned as a string, along with a possible error. returned as a string, along with a possible error.
- `RunCommand(input string) (string, error)`: same as `ExecCommand`, - `RunCommand(input string) (string, error)`: same as `ExecCommand`,
except this uses micro's argument parser to parse the arguments from except this uses micro's argument parser to parse the arguments from
the input. For example `cat 'hello world.txt' file.txt`, will pass the input. For example `cat 'hello world.txt' file.txt`, will pass
two arguments in the `ExecCommand` argument list (quoting arguments two arguments in the `ExecCommand` argument list (quoting arguments
will preserve spaces). will preserve spaces).
- `RunBackgroundShell(input string) (func() string, error)`: returns a - `RunBackgroundShell(input string) (func() string, error)`: returns a
function that will run the given shell command and return its output. function that will run the given shell command and return its output.
- `RunInteractiveShell(input string, wait bool, getOutput bool) - `RunInteractiveShell(input string, wait bool, getOutput bool)
(string, error)`: (string, error)`:
temporarily closes micro and runs the given command in the terminal. temporarily closes micro and runs the given command in the terminal.
If `wait` is true, micro will wait for the user to press enter before If `wait` is true, micro will wait for the user to press enter before
returning to text editing. If `getOutput` is true, micro redirect returning to text editing. If `getOutput` is true, micro redirect
stdout from the command to the returned string. stdout from the command to the returned string.
- `JobStart(cmd string, onStdout, onStderr, - `JobStart(cmd string, onStdout, onStderr,
onExit func(string, []interface{}), userargs ...interface{}) onExit func(string, []interface{}), userargs ...interface{})
*exec.Cmd`: *exec.Cmd`:
Starts a background job by running the shell on the given command Starts a background job by running the shell on the given command
@ -232,16 +216,16 @@ The packages and functions are listed below (in Go type signatures):
be passed to the callbacks, along with the output as the first be passed to the callbacks, along with the output as the first
argument of the callback. argument of the callback.
- `JobSpawn(cmd string, cmdArgs []string, onStdout, onStderr, - `JobSpawn(cmd string, cmdArgs []string, onStdout, onStderr,
onExit func(string, []interface{}), userargs ...interface{}) onExit func(string, []interface{}), userargs ...interface{})
*exec.Cmd`: *exec.Cmd`:
same as `JobStart`, except doesn't run the command through the shell same as `JobStart`, except doesn't run the command through the shell
and instead takes as inputs the list of arguments. and instead takes as inputs the list of arguments.
- `JobStop(cmd *exec.Cmd)`: kills a job. - `JobStop(cmd *exec.Cmd)`: kills a job.
- `JobSend(cmd *exec.Cmd, data string)`: sends some data to a job's stdin. - `JobSend(cmd *exec.Cmd, data string)`: sends some data to a job's stdin.
- `RunTermEmulator(h *BufPane, input string, wait bool, getOutput bool, - `RunTermEmulator(h *BufPane, input string, wait bool, getOutput bool,
callback func(out string, userargs []interface{}), callback func(out string, userargs []interface{}),
userargs []interface{}) error`: userargs []interface{}) error`:
starts a terminal emulator from a given BufPane with the input command. starts a terminal emulator from a given BufPane with the input command.
@ -252,7 +236,7 @@ The packages and functions are listed below (in Go type signatures):
optional user arguments. This function returns an error on systems optional user arguments. This function returns an error on systems
where the terminal emulator is not supported. where the terminal emulator is not supported.
- `TermEmuSupported`: true on systems where the terminal emulator is - `TermEmuSupported`: true on systems where the terminal emulator is
supported and false otherwise. Supported systems: supported and false otherwise. Supported systems:
* Linux * Linux
* MacOS * MacOS
@ -377,7 +361,6 @@ strings
regexp regexp
errors errors
time time
unicode/utf8
archive/zip archive/zip
net/http net/http
``` ```

View file

@ -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

View file

@ -7,7 +7,7 @@ local buffer = import("micro/buffer")
local ft = {} local ft = {}
ft["apacheconf"] = "# %s" ft["apacheconf"] = "# %s"
ft["batch"] = ":: %s" ft["bat"] = ":: %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"] and last_ft ~= nil) then if buf.Settings["commenttype"] == nil or last_ft ~= buf.Settings["filetype"] 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

View file

@ -8,7 +8,7 @@ file:
```json ```json
{ {
"Alt-g": "lua:comment.comment" "Alt-g": "comment.comment"
} }
``` ```

View file

@ -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)) names := make([]string, len(entries), len(entries))
for i, entry := range entries { for i, entry := range entries {
names[i] = entry.Name() names[i] = entry.Name()
} }

View file

@ -1,16 +0,0 @@
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)
}

View file

@ -5,6 +5,7 @@ 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

View file

@ -2,8 +2,7 @@
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 header (first line of the line). Each yaml file specifies how to detect the filetype based on file extension or headers (first line of the file).
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
@ -22,7 +21,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 of the syntax files here have been converted using that tool. Most the 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).

View file

@ -3,7 +3,7 @@ filetype: ada
detect: detect:
filename: "(\\.ads$|\\.adb$|\\.ada$)" filename: "(\\.ads$|\\.adb$|\\.ada$)"
rules: rules:
# Operators # Operators
- symbol.operator: ([.:;,+*|=!?\\%]|<|>|/|-|&) - symbol.operator: ([.:;,+*|=!?\\%]|<|>|/|-|&)
- symbol.brackets: "[(){}]|\\[|\\]" - symbol.brackets: "[(){}]|\\[|\\]"
@ -18,11 +18,11 @@ rules:
# Constant # Constant
- constant.bool: \b(TRUE|FALSE) - constant.bool: \b(TRUE|FALSE)
- constant.number: ([0-9]+) - constant.number: ([0-9]+)
# Storage Types # Storage Types
- type.storage: \b(INTEGER|NATURAL|POSITIVE|FLOAT|CHARACTER|STRING)\b - type.storage: \b(INTEGER|NATURAL|POSITIVE|FLOAT|CHARACTER|STRING)\b
- type.storage: \b(LONG_INTEGER|SHORT_INTEGER|LONG_FLOAT|SHORT_FLOAT)\b - type.storage: \b(LONG_INTEGER|SHORT_INTEGER|LONG_FLOAT|SHORT_FLOAT)\b
#Character #Character
- constant.string.char: \'.\' - constant.string.char: \'.\'
@ -36,8 +36,9 @@ rules:
- constant.interpolation: \\\([[:graph:]]*\) - constant.interpolation: \\\([[:graph:]]*\)
- constant.unicode: \\u\{[[:xdigit:]]+} - constant.unicode: \\u\{[[:xdigit:]]+}
# Line Comment # Line Comment
- comment.line: "--.*" - comment.line: "--.*"
# Todo # Todo
- todo: "(TODO|XXX|FIXME):?" - todo: "(TODO|XXX|FIXME):?"

View file

@ -4,9 +4,9 @@ detect:
filename: "\\.?ino$" filename: "\\.?ino$"
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
@ -33,7 +33,7 @@ rules:
- type: "\\b(boolean|byte|char|float|int|long|word)\\b" - type: "\\b(boolean|byte|char|float|int|long|word)\\b"
## Control Structions ## Control Structions
- statement: "\\b(case|class|default|do|double|else|false|for|if|new|null|private|protected|public|short|signed|static|String|switch|this|throw|try|true|unsigned|void|while)\\b" - statement: "\\b(case|class|default|do|double|else|false|for|if|new|null|private|protected|public|short|signed|static|String|switch|this|throw|try|true|unsigned|void|while)\\b"
- statement: "\\b(goto|continue|break|return)\\b" - statement: "\\b(goto|continue|break|return)\\b"
## Math ## Math
@ -66,7 +66,7 @@ rules:
## Structure ## Structure
- identifier: "\\b(setup|loop)\\b" - identifier: "\\b(setup|loop)\\b"
## ##
- statement: "^[[:space:]]*#[[:space:]]*(define|include(_next)?|(un|ifn?)def|endif|el(if|se)|if|warning|error|pragma)" - statement: "^[[:space:]]*#[[:space:]]*(define|include(_next)?|(un|ifn?)def|endif|el(if|se)|if|warning|error|pragma)"
## GCC builtins ## GCC builtins

View file

@ -28,7 +28,7 @@ rules:
# Paragraph Title # Paragraph Title
- statement: "^\\..*$" - statement: "^\\..*$"
# source # source
- identifier: "^\\[(source,.+|NOTE|TIP|IMPORTANT|WARNING|CAUTION)\\]" - identifier: "^\\[(source,.+|NOTE|TIP|IMPORTANT|WARNING|CAUTION)\\]"
# Other markup # Other markup

View file

@ -1,7 +1,8 @@
filetype: batch filetype: batch
detect: detect:
filename: "(\\.bat$|\\.cmd$)" filename: "(\\.bat$)"
# header: ""
rules: rules:
# Numbers # Numbers

View file

@ -5,22 +5,19 @@ detect:
rules: rules:
- identifier: "\\b[A-Z_][0-9A-Z_]+\\b" - identifier: "\\b[A-Z_][0-9A-Z_]+\\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(auto|float|double|char|int|short|long|sizeof|enum|void|static|const|struct|union|typedef|extern|(un)?signed|inline)\\b"
- type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr))|char(8|16|32)|wchar)_t\\b" - type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr)))_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"
- statement: "\\b(auto|volatile|register|restrict|_Alignas|alignas|_Alignof|alignof|static|const|constexpr|extern|_Thread_local|thread_local)\\b" - type.extended: "\\b(bool)\\b"
- statement: "\\b(for|if|while|do|else|case|default|switch|_Generic|_Static_assert|static_assert)\\b" - statement: "\\b(volatile|register|restrict)\\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"
- statement: "\\b(asm|fortran)\\b" - preproc: "^[[:space:]]*#[[:space:]]*(define|pragma|include|(un|ifn?)def|endif|el(if|se)|if|warning|error)"
- 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|extension|hidden|inline|packed|restrict|section|typeof|weak)__" - statement: "__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__"
# Operator Color # Operator Color
- symbol.operator: "[-+*/%=<>.:;,~&|^!?]|\\b(offsetof|sizeof)\\b" - symbol.operator: "([.:;,+*|=!\\%]|<|>|/|-|&)"
- 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)"
@ -28,7 +25,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.bool: "(\\b(true|false|NULL|nullptr|TRUE|FALSE)\\b)" - constant.number: "NULL"
- constant.string: - constant.string:
start: "\"" start: "\""
@ -56,4 +53,3 @@ rules:
end: "\\*/" end: "\\*/"
rules: rules:
- todo: "(TODO|XXX|FIXME):?" - todo: "(TODO|XXX|FIXME):?"

View file

@ -1,7 +0,0 @@
filetype: cake
detect:
filename: "\\.cake$"
rules:
- include: "csharp"
- preproc: "^[[:space:]]*#(addin|break|l|load|module|r|reference|tool)"

Some files were not shown because too many files have changed in this diff Show more