Compare commits

...

121 Commits

Author SHA1 Message Date
Zachary Yedidia
5fc8f847a6 Improve command bar keybindings
The command bar now supports better keybindings:

CtrlA, CtrlLeft: start of line
CtrlE, CtrlRight: end of line
CtrlF, AltRight: next word
CtrlB, AltLeft: previous word
CtrlW, AltBackspace: delete previous word
CtrlV: Paste
Arrow keys as usual

These keybindings are not rebindable (maybe support will be added
in the future).
2017-11-24 14:43:26 -05:00
Zachary Yedidia
af6ef4f87f Minor comment improvement 2017-11-24 13:35:11 -05:00
Zachary Yedidia
7f287b62fb Fix autocomplete behavior for empty args
This also adds a modified version of go-shellwords as a dependency
and removes the dependency on the original go-shellwords.
2017-11-23 23:04:32 -05:00
Zachary Yedidia
36d72c4cab Move incomplete colorschemes and improve cd
The default colorschemes should ideally use 256 colors instead
of just 16 colors. The `simple` colorscheme should cover most
16 color use cases. I went through the colorschemes and put the themes
that didn't look good or looked incomplete in an in_progress directory.

This commit also improves the `cd` command behavior when using an
unnamed buffer.
2017-11-23 15:57:17 -05:00
Zachary Yedidia
71ee185b80 Check width before drawing cellview
Fixes #927
2017-11-23 14:44:07 -05:00
Zachary Yedidia
0360a2fcb5 Improve cmdbar parsing and add -l replace flag
The -l flag to the replace command means "literal" and will treat
the search term literally instead of as a regular expression.

The command bar also now supports expanding environment variables
and running expressions through the shell and using the result
in the command.
2017-11-22 13:54:39 -05:00
Zachary Yedidia
2ee7adb196 Support either io/ioutil or ioutil for lua import
Closes #923
2017-11-21 16:24:39 -05:00
Zachary Yedidia
d247db3e9d Implement retab command
Ref #919
2017-11-21 00:51:07 -05:00
Zachary Yedidia
e4c2f5d259 Merge pull request #891 from pranavraja/master
search: Only update lastSearch on ENTER
2017-11-19 15:47:05 -05:00
Zachary Yedidia
cc15df9307 Remove unnecessary authors file 2017-11-19 15:40:21 -05:00
Zachary Yedidia
812b547679 Merge pull request #613 from GeigerCounter/build_tools
Build tools
2017-11-19 15:39:32 -05:00
Zachary Yedidia
1c43bb572a Merge pull request #847 from sotpapathe/octave_support
Initial support for Octave/Matlab syntax highlighting
2017-11-18 16:56:31 -05:00
Zachary Yedidia
f96e9e9c1d Update lua go stdlib access documentation
Ref #912
2017-11-16 14:29:36 -05:00
Zachary Yedidia
7dfeda1ae5 Support .cljs and .cljc as clojure files
Fixes #911
2017-11-14 13:58:28 -05:00
Zachary Yedidia
d6ccaf0e41 Merge pull request #908 from FujiHaruka/patch-1
Update javascript.yaml
2017-11-08 00:28:09 -05:00
Zachary Yedidia
6b6fcc8ba0 Minor documentation update 2017-11-08 00:23:18 -05:00
Fuji Haruka
07bfcc9747 Update javascript.yaml
Add statements `async` and `await`.

Its status is stage 3 Draft.
https://tc39.github.io/ecmascript-asyncawait/#async-function-definitions
But I think it's usefull to add, because Node.js >= v7.6 support it.
2017-11-06 20:52:28 +09:00
Zachary Yedidia
423f4675d2 Add a scroll bar option
The option is `scrollbar` and is off by default. The scroll bar is
not interactive (you can't click and drag it) but this will likely
be fixed in the future.

Ref #869
2017-11-05 20:07:14 -05:00
Zachary Yedidia
c01ba97215 Add installation script instructions to readme 2017-10-31 16:16:57 -04:00
Zachary Yedidia
288717451f Fix typo in readme 2017-10-23 22:26:45 -04:00
Zachary Yedidia
a1f3499825 Fix issue with multicursor IDs
Fixes #899
2017-10-22 19:51:16 -04:00
Zachary Yedidia
63fa8fec41 Merge 2017-10-22 18:02:18 -04:00
Zachary Yedidia
b9e916999f Don't print error message if history file doesn't exist 2017-10-22 18:00:47 -04:00
Zachary Yedidia
afedad9977 Merge pull request #898 from TedSinger/master
savehistory bugfix
2017-10-22 11:59:43 -04:00
Ted Singer
d82ea2279d If the history file is unreadable or unparseable, Messenger.history remained nil, causing a panic on read.
Now in that case, we temporarily disable saving history and initialize history to empty, instead of nil
2017-10-21 18:59:11 -04:00
Zachary Yedidia
5b5998cf14 Merge 2017-10-21 15:32:34 -04:00
Zachary Yedidia
7b6430af1c Add savehistory option
When savehistory is enabled, micro will save your command history across
sessions. This includes command-mode, shell-mode, open, jump-to-line...
Anything that uses up-arrow for history in the infobar.

This option is on by default.

Closes #874
2017-10-21 15:31:04 -04:00
Zachary Yedidia
a0d475bebf Merge pull request #782 from i-amdroid/master
Added Twilight color scheme
2017-10-21 00:12:45 -04:00
therainingmonkey
31cd4b5795 Update Lua syntax (#893)
* Edited Lua syntax ('hash' is not a comment in Lua).

* Edited Lua syntax - hash (#) is a symbol in Lua (the length operator).
2017-10-21 00:10:46 -04:00
Zachary Yedidia
19ee4b281e Fix comment regex for shell filetype
Fixes #895
2017-10-20 23:57:49 -04:00
Zachary Yedidia
a171795654 Merge pull request #882 from onodera-punpun/ft
Add fish to ftoptions
2017-10-17 00:04:04 -04:00
Zachary Yedidia
98d8bfa879 Merge branch 'master' into ft 2017-10-17 00:03:57 -04:00
Pranav Raja
7bc2d870cd search: Only update lastSearch on ENTER
This has a few effects:

- `lastSearch` doesn't get overriden with partial searches
unnecessarily, which matches the behaviour of vim/emacs etc.

- Selecting a word, then pressing C-c C-f ENTER works better as you can
now use C-n and C-p to jump to more occurrences of what you just
searched for. Without this C-n would jump to what you searched for
*previously*.

- `lastSearch` will now be updated even if the search did not match -
again, this matches the behaviour of vim/emacs.
2017-10-16 17:44:44 +11:00
Zachary Yedidia
678819683a Merge 2017-10-15 15:35:54 -04:00
Zachary Yedidia
08e46f9112 Don't draw statusline if infobar is off and in use
Fixes #873
2017-10-15 15:35:19 -04:00
Zachary Yedidia
e071209add Merge pull request #890 from Jipok/patch-1
Use spaces for nim
2017-10-15 15:32:50 -04:00
Zachary Yedidia
74e79dc8f2 Merge pull request #880 from onodera-punpun/consistent
Alphabetically order options, format *.md files
2017-10-15 15:32:35 -04:00
Zachary Yedidia
955e8ffb08 Merge pull request #883 from onodera-punpun/lint
alphabetically order linters, add shell linter
2017-10-15 15:30:52 -04:00
Zachary Yedidia
b87a74711e Merge pull request #888 from matthewgraybosch/master
Update README.md
2017-10-15 15:30:35 -04:00
Jipok
ade0e9dd39 Use spaces for nim
From manual:
Nim's standard grammar describes an indentation sensitive language. This means that all the control structures are recognized by indentation. Indentation consists only of spaces; tabulators are not allowed.
2017-10-14 20:21:41 +05:00
Matthew Graybosch
f05f0b06ac Update README.md
Added information for OpenBSD. It works great there. 🤘
2017-10-12 13:52:47 -04:00
Camille Scholtz
f2006f592a alphabetically order linters, add shell linter 2017-10-11 17:47:23 +02:00
Camille Scholtz
5e66489836 Add fish to ftoptions 2017-10-11 17:02:37 +02:00
Camille Scholtz
9daa05d696 Use more consisten syntax in md files, format tp 80 collumns, fix some typos 2017-10-11 15:16:53 +02:00
Camille Scholtz
d76704839a alphabetically order options 2017-10-11 14:43:38 +02:00
Camille Scholtz
329669ce79 Make settings capitalization consistent 2017-10-11 14:22:23 +02:00
Zachary Yedidia
5af5140362 Merge pull request #876 from yannicka/setlocal-optionvaluecompletion
Add option value completion on setlocal
2017-10-08 14:52:50 -04:00
Yannick Armand
bf6ce3a17e Add option value completion on setlocal 2017-10-08 18:42:09 +02:00
Zachary Yedidia
e99fd1337e Update readme 2017-10-07 16:27:55 -04:00
Zachary Yedidia
17dac164ea Don't store cmd stdout in string
Storing the stdout confuses isatty causing programs running within
ShellMode to not format properly.

Fixes #862
2017-10-06 21:09:53 -04:00
Zachary Yedidia
b7c99c52d2 Update runtime 2017-10-06 20:43:14 -04:00
Zachary Yedidia
278aa6b050 Add docs for binding esc sequences 2017-10-06 20:42:58 -04:00
Zachary Yedidia
773c54a40d Support binding raw escapes codes 2017-10-06 14:03:35 -04:00
Zachary Yedidia
74589af1fc Revert "Update tcell to use gdamore's fix for idle wakeup"
This reverts commit f01ad3f726.
2017-10-06 13:21:53 -04:00
Zachary Yedidia
f01ad3f726 Update tcell to use gdamore's fix for idle wakeup
Note that you may encounter merge conflicts if you try to update. If you
do, remove the directory `cmd/micro/vendor/github.com/zyedidia/tcell`
and it will be recloned.
2017-10-06 13:03:43 -04:00
Zachary Yedidia
a0f3ec805d Merge 2017-10-06 11:00:31 -04:00
Zachary Yedidia
ea6012922f Add paren highlighting for js and update runtime 2017-10-06 10:59:43 -04:00
Zachary Yedidia
da33b59858 Merge pull request #868 from nicolasbd/patch-1
Support .es files and fix js parenthesis highlighting
2017-10-06 10:58:28 -04:00
Nicolas
9703d4f52f support es files and fix parenthesis highlighting
* This allows `micro` to use javascript syntax highlighting on `.es`, `.es6|7|8` files
* Fix parenthesis highlighting with @is73 regex, see #864
2017-10-06 16:29:49 +02:00
Zachary Yedidia
f3a30412f4 Merge pull request #858 from andreaTP/scalaSyntax
a couple more keywords to scala syntax
2017-10-04 15:15:18 -04:00
Zachary Yedidia
3116b082d8 Fix save and quit prompt 2017-10-04 12:11:20 -04:00
andrea
3e0a1b4517 a couple more keywords to scala syntax 2017-10-04 10:17:50 +01:00
Zachary Yedidia
ac3de065d9 Merge pull request #850 from nitsakh/feat-809
Implementation of Paragraph Feature
2017-10-03 23:49:57 -04:00
Zachary Yedidia
3e63ec74b9 Merge pull request #852 from popey/patch-3
Ensure snap is built with git version/tag info
2017-10-03 10:48:46 -04:00
Zachary Yedidia
c7334eb3b7 Fix sucmd option
Fixes #854
2017-10-03 10:48:07 -04:00
Alan Pope
dfbddd4b86 Ensure snap is built with git version/tag info
Changing from version: master to version: git will prevent the snap being built with the text 'master' as the version, but instead use the latest git tag or version info. This makes it easier to figure out which build is which in the store.
2017-10-03 12:50:47 +01:00
Zachary Yedidia
299416062f Merge 2017-10-02 23:44:58 -04:00
Zachary Yedidia
8b8fffb98d Add nano-style key menu option
Use the `keymenu` option (default `off`) to enable. ToggleKeyMenu is
also bound to `Alt-g` and this info is now displayed in the status line.

Closes #829
2017-10-02 23:44:11 -04:00
Nitish Sakhawalkar
ec221c0bc4 Implementation of Paragraph Feature
Changes to support moving cursor to next and previous paragraph
and updates to corresponding documentation
2017-10-02 19:54:57 -07:00
Zachary Yedidia
d27f8f9802 Merge pull request #846 from sotpapathe/patch_yaml_ftoptions
Added automatic tabs to spaces for yaml and updated readme
2017-10-02 14:05:14 -04:00
sotpapathe
c40c79427a Added initial support for Octave/Matlab syntax highlighting 2017-10-02 14:08:22 +04:00
sotpapathe
8a4f2193d8 Added automatic tabs to spaces for yaml and updated readme 2017-10-02 13:36:28 +04:00
Zachary Yedidia
aa667f6ba9 Merge pull request #845 from paykroyd/nit
grammar nit
2017-10-01 23:13:11 -04:00
Pete Aykroyd
d067de8150 grammar nit 2017-10-01 22:33:03 -04:00
Zachary Yedidia
b3559df543 Merge 2017-10-01 21:56:10 -04:00
Zachary Yedidia
f4e94d6d34 Add sucmd to customize "sudo" command
Fixes #833
2017-10-01 21:55:43 -04:00
Zachary Yedidia
13daa4e715 Merge pull request #842 from marius92mc/add-editorconfig-and-set-indent-size-to-4
Add .editorconfig and set indent_size to 4
2017-10-01 14:37:05 -04:00
marius92mc
75be4f5f61 Add .editorconfig and set indent_size to 4 2017-10-01 20:51:33 +03:00
Zachary Yedidia
46ced988eb Fix some golint warnings 2017-10-01 12:42:23 -04:00
Zachary Yedidia
28acfc6d3f Fix support for user-friendly plugin names
Fixes #840
2017-09-30 17:47:19 -04:00
Zachary Yedidia
660c7d3be5 Merge pull request #838 from yursan9/appstream
Update Appstream
2017-09-29 23:54:28 -04:00
Yurizal Susanto
52617bd5a8 Update Appstream 2017-09-30 10:12:53 +07:00
Zachary Yedidia
9db181037f Merge 2017-09-29 13:46:54 -04:00
Zachary Yedidia
861ea5aabc Update readme 2017-09-29 13:46:51 -04:00
Zachary Yedidia
e4125c0c6a Merge pull request #835 from andreaTP/jsSyntax
few more keywords for js syntax
2017-09-29 13:39:04 -04:00
andrea
ff9a8a1247 few more keywords for js syntax 2017-09-29 16:20:38 +01:00
Zachary Yedidia
ac29e30f54 Update readme 2017-09-28 19:38:24 -04:00
Zachary Yedidia
a02ae3ceed Replace home directory before performing SaveAs
Fixes #820
2017-09-26 22:55:06 -04:00
MrSndmn
54c02f4781 Perl syntax highlighting fix (#818)
* Perl syntax highlighting fix

* Useless escapes removed
2017-09-24 11:59:57 -04:00
Zachary Yedidia
a5e721b107 Set fastdirty on for files larger than 50kb 2017-09-23 21:18:37 -04:00
Zachary Yedidia
12a4dd58f3 Only replace '~' with home if at start of path
Ref #757
2017-09-23 20:56:08 -04:00
Zachary Yedidia
5a7ddb8330 Add autocompletion for option values
Closes #555
2017-09-23 20:47:19 -04:00
Zachary Yedidia
cb75531818 Make mouse option global option
Fixes #816
2017-09-21 17:10:53 -04:00
Zachary Yedidia
6229a0579f Update tcell
The latest commit to tcell should fix behavior for large pastes.

Fixes #815
2017-09-19 13:21:09 -04:00
Zachary Yedidia
fb980bb695 Add option for very accurate dirty flag
Set the `fastdirty` option flag to off if you really want accurate
reporting on whether the buffer is modified. This is more resource
intensive but it can be useful for people who don't mind.

Closes #787
Closes #467
2017-09-17 23:33:18 -04:00
Zachary Yedidia
19dc9d7bbc Fix options and make usage text much more readable
Now micro -h will just show you the important information and if you
want to see each individual option's help text use micro -options.
2017-09-17 22:11:26 -04:00
Zachary Yedidia
1e55b6f6b3 Only register double click on equal mouse location 2017-09-17 18:31:32 -04:00
Zachary Yedidia
0a35bfe2f5 Update readme 2017-09-15 16:25:01 -04:00
Zachary Yedidia
2f587c6d48 Fix moving to end of line on cursor down 2017-09-15 16:09:33 -04:00
Zachary Yedidia
5b426aee86 Update tcell 2017-09-15 14:15:21 -04:00
Zachary Yedidia
f700769b27 Update tcell 2017-09-15 13:33:06 -04:00
Zachary Yedidia
04b672eebe Update tcell 2017-09-15 10:50:56 -04:00
Zachary Yedidia
f7238e8e53 Update tcell 2017-09-14 17:29:25 -04:00
Zachary Yedidia
33cb39d318 Use type.keyword instead of keyword
Some syntax files used keyword from an old version when they should have
been using type.keyword.

Fixes #811
2017-09-13 18:00:47 -04:00
Zachary Yedidia
612658d9c4 Add documentation for new lua functions 2017-09-11 12:23:19 -04:00
Zachary Yedidia
c31613b2c7 Add --config-dir option 2017-09-10 23:20:21 -04:00
Zachary Yedidia
d7419d213a Merge branch 'better-lua' 2017-09-10 22:22:31 -04:00
Zachary Yedidia
67a3f86cc9 Update tcell 2017-09-10 17:21:37 -04:00
Andrey Yurtaev
e7facd74ba Added Twilight color scheme 2017-08-13 14:47:43 +03:00
GeigerCounter
e6797e0303 Packaging scripts passed install test. 2017-03-31 09:26:12 -04:00
GeigerCounter
6041e063e2 Added script for rpm and template rpmspec 2017-03-31 05:22:32 -04:00
GeigerCounter
c3861955e0 Added arm packaging to the scripts 2017-03-29 11:31:58 -04:00
GeigerCounter
4a45e69eb1 Properly include the man page 2017-03-29 08:56:25 -04:00
GeigerCounter
e52d05113e Added AUTHORS file for documentation 2017-03-29 08:42:28 -04:00
GeigerCounter
45992a0e0a Fixed mistake in desktop file 2017-03-29 08:39:50 -04:00
GeigerCounter
1fb405afd3 Tweaked build-deb script 2017-03-29 07:28:14 -04:00
GeigerCounter
e23d4d8fa1 Added scalable logo for packaging. 2017-03-29 05:36:09 -05:00
GeigerCounter
d8aab386f1 Stuff and things and stuff. 2017-03-28 14:05:17 -04:00
GeigerCounter
7d422bfae2 Merge branch 'master' of https://github.com/zyedidia/micro into build_tools 2017-03-28 13:40:26 -04:00
GeigerCounter
7bc870e72f Added a desktop specification. ( micro.desktop ) 2017-03-28 13:39:47 -04:00
GeigerCounter
edee53f6f2 Added rpm build script ( Untested. ) 2017-03-28 13:37:41 -04:00
84 changed files with 3029 additions and 1309 deletions

7
.editorconfig Normal file
View File

@@ -0,0 +1,7 @@
# See http://editorconfig.org
# In Go files we indent with tabs but still
# set indent_size to control the GitHub web viewer.
[*.go]
indent_size=4

View File

@@ -50,7 +50,7 @@ You can also check out the website for Micro at https://micro-editor.github.io.
* Macros
* Common editor things such as undo/redo, line numbers, Unicode support, softwrap...
Although not yet implemented, I hope to add more features such as autocompletion ([#174](https://github.com/zyedidia/micro/issues/174)), and multiple cursors ([#5](https://github.com/zyedidia/micro/issues/5)) in the future.
Although not yet implemented, I hope to add more features such as autocompletion ([#174](https://github.com/zyedidia/micro/issues/174)) or a tree view ([#249](https://github.com/zyedidia/micro/issues/249)) in the future.
# Installation
@@ -69,7 +69,19 @@ and you'll see all the stable releases with the corresponding binaries.
If you'd like to see more information after installing micro, run `micro -version`.
### Package Managers
### Installation script
There is a great script which can install micro for you by downloading the latest prebuilt binary. You can find it at https://getmic.ro (the github repo for it is [here](https://github.com/benweissmann/getmic.ro)).
Then you can easily install micro:
$ curl https://getmic.ro | bash
The script will install the micro binary to the current directory.
See the [Github page](https://github.com/benweissmann/getmic.ro) for more information.
### Package managers
You can install micro using Homebrew on Mac:
@@ -92,14 +104,20 @@ scoop install micro
On Linux, you can install micro through [snap](https://snapcraft.io/docs/core/install)
```
snap install micro --edge --classic
snap install micro --classic
```
On OpenBSD, micro is available in the ports tree. It is also available as a binary package.
```
pkg_add -v micro
```
### Building from source
If your operating system does not have a binary release, but does run Go, you can build from source.
Make sure that you have Go version 1.5 or greater (Go 1.4 will work if your version supports CGO) and that your `GOPATH` env variable is set (I recommand setting it to `~/go` if you don't have one).
Make sure that you have Go version 1.5 or greater (Go 1.4 will work if your version supports CGO) and that your `GOPATH` env variable is set (I recommend setting it to `~/go` if you don't have one).
```
go get -d github.com/zyedidia/micro/cmd/micro
@@ -115,7 +133,7 @@ You can install directly with `go get` (`go get -u github.com/zyedidia/micro/cmd
### MacOS terminal
If you are using MacOS, you should consider using [iTerm2](http://iterm2.com/) instead of the default Mac terminal. The iTerm2 terminal has much better mouse support as well as better handling of key events. The newest versions also support true color.
If you are using MacOS, you should consider using [iTerm2](http://iterm2.com/) instead of the default Mac terminal. The iTerm2 terminal has much better mouse support as well as better handling of key events. For best keybinding behavior, choose `xterm defaults` under `Preferences->Profiles->Keys->Load Preset`. The newest versions also support true color.
### Linux clipboard support

63
assets/logo.svg Normal file
View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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"
id="svg3336"
version="1.1"
inkscape:version="0.91 r13725"
width="128"
height="128"
viewBox="0 0 128 128"
sodipodi:docname="logo.svg">
<metadata
id="metadata3342">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3340" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1355"
inkscape:window-height="717"
id="namedview3338"
showgrid="false"
inkscape:zoom="1.6243169"
inkscape:cx="111.32302"
inkscape:cy="30.538264"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg3336" />
<path
style="fill:#2e3192;fill-opacity:1"
d="m 56.1,127.32358 c -13.68932,-1.70993 -27.156628,-8.3544 -37.112903,-18.31068 -25.0687936,-25.068788 -25.0687936,-65.95701 0,-91.025803 25.068793,-25.0687936 65.957015,-25.0687936 91.025803,0 25.0688,25.068793 25.0688,65.957015 0,91.025803 C 95.87457,123.15123 76.198116,129.83404 56.1,127.32358 Z"
id="path3364"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff"
d="m 40.756452,106.01908 c 1.442831,-1.83426 1.55476,-4.09687 0.414499,-8.37899 -0.678184,-2.546844 -0.684604,-4.05591 -0.03829,-9 1.276867,-9.767604 4.483143,-23.040636 5.565559,-23.039766 0.220979,1.74e-4 0.417725,2.092674 0.437213,4.65 0.04167,5.468298 1.558564,9.06891 4.638769,11.010942 2.551646,1.608774 9.15365,1.329324 12.80399,-0.541974 3.245124,-1.663572 7.649064,-6.112434 9.850956,-9.951438 L 76.188736,67.7 l 0.0054,3.922866 c 0.0042,2.867148 0.36894,4.642788 1.355628,6.59796 1.532058,3.035856 3.323226,4.15755 6.659322,4.17033 5.192928,0.01986 9.07014,-3.668676 10.866768,-10.338036 0.98277,-3.64821 1.064448,-11.21265 0.09235,-8.55312 -3.025218,8.276592 -4.468212,9.893562 -9.238056,10.351884 -2.629152,0.25263 -3.177804,0.08883 -4.921776,-1.469412 -1.609044,-1.437678 -2.016072,-2.308416 -2.258508,-4.8315 -0.262884,-2.73585 0.105942,-4.06497 3.32007,-11.964365 C 88.28388,40.315087 89.33625,35.536248 87,33.2 c -1.559352,-1.559353 -3.62787,-1.522741 -5.691792,0.10074 -2.295762,1.805846 -3.105984,4.070756 -5.14293,14.376662 -2.464164,12.46744 -6.525822,20.297092 -12.62193,24.331306 C 59.052142,74.98085 52.704914,73.6403 50.637191,69.282896 49.19967,66.253544 49.857706,62.552972 53.387813,53.814319 56.613526,45.829186 58.8,38.711369 58.8,36.195564 c 0,-4.161283 -4.366993,-5.665719 -7.364438,-2.537061 -2.183558,2.279144 -3.117251,5.256959 -4.280897,13.653016 -0.547956,3.953665 -1.259292,9.010489 -1.580746,11.237387 -0.321454,2.226896 -2.083918,8.706896 -3.916587,14.400002 -4.33165,13.456074 -6.85029,23.184822 -7.273674,28.096022 -0.325586,3.77675 -0.269352,4.00056 1.319044,5.25 2.187498,1.72068 3.541408,1.64679 5.05375,-0.27585 z"
id="path3362"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

56
assets/packaging/micro.1 Normal file
View File

@@ -0,0 +1,56 @@
.\" micro manual page - micro(1)
.\"
.\" Copyright © 2017 Zachary Yedidia <zyedidia@gmail.com>
.\" Copyright © 2017 Collin Warren <anatoly@somethinghub.com>
.\"
.\" This document is provided under the same licensing as micro.
.\" See \usr\share\doc\micro\LICENSE for more information.
.TH micro 1 "2017-03-28"
.SH NAME
micro \- An intuitive and modern terminal text editor
.
.SH SYNOPSIS
.B micro
.RB []
[
.I "filename \&..."
]
.SH DESCRIPTION
( Copied from the README file. )
Micro is a terminal-based text editor that aims to be easy to use and intuitive, while also taking advantage of the full capabilities
of modern terminals. It comes as one single, batteries-included, static binary with no dependencies.
As the name indicates, micro aims to be somewhat of a successor to the nano editor by being easy to install and use in a pinch, but micro also aims to be
enjoyable to use full time, whether you work in the terminal because you prefer it (like me), or because you need to (over ssh).
.SH OPTIONS
.B \-v --version
Displays the current version of micro and the git commit hash.
.TP
.SH ENVIRONMENT
Micro's behaviour can be changed by setting environment variables, of which
there is currently only one:
.I MICRO_TRUE_COLOR
When MICRO_TRUE_COLOR is set to 1, micro will attempt to treat your terminal as
a true-color terminal and will be able to make full use of the true-color colorschemes
that are included with micro. If MICRO_TRUE_COLOR is not set or is set to 0, then
micro will only make use of 256 color features and will internally map true-color
colorschemes to the nearest colors available. For more information see micro's documentation.
.SH NOTICE
This manpage is intended only to serve as a quick guide to the invocation of
micro and is not intended to replace the full documentation included with micro
which can be accessed from within micro. Micro tells you what key combination to
press to get help in the lower right.
.SH BUGS
A comprehensive list of bugs will not be listed in this manpage. See the Github
page at \fBhttps://github.com/zyedidia/micro/issues\fP for a list of known bugs
and to report any newly encountered bugs you may find. We strive to correct
bugs as swiftly as possible.
.SH COPYRIGHT
Copyright \(co 2017 Zachary Yedidia, Collin Warren, et al.
See /usr/share/doc/micro/LICENSE and /usr/share/doc/micro/AUTHORS for more information.

View File

@@ -0,0 +1,15 @@
[Desktop Entry]
Name=Micro
GenericName=Text Editor
Comment=Edit text files in a terminal
Icon=micro
Type=Application
Categories=terminal;TextEditor;
Keywords=text;editor;syntax;terminal;
Exec=micro %U
StartupNotify=false
Terminal=true
MimeType=text/plain;text/x-chdr;text/x-csrc;text/x-c++hdr;text/x-c++src;text/x-java;text/x-dsrc;text/x-pascal;text/x-perl;text/x-python;application/x-php;application/x-httpd-php3;application/x-httpd-php4;application/x-httpd-php5;application/xml;text/html;text/css;text/x-sql;text/x-diff;

View File

@@ -10,6 +10,7 @@ import (
"github.com/yuin/gopher-lua"
"github.com/zyedidia/clipboard"
"github.com/zyedidia/micro/cmd/micro/shellwords"
"github.com/zyedidia/tcell"
)
@@ -78,7 +79,7 @@ func (v *View) MousePress(usePlugin bool, e *tcell.EventMouse) bool {
v.Cursor.ResetSelection()
v.Relocate()
}
if time.Since(v.lastClickTime)/time.Millisecond < doubleClickThreshold {
if time.Since(v.lastClickTime)/time.Millisecond < doubleClickThreshold && (x == v.lastLoc.X && y == v.lastLoc.Y) {
if v.doubleClick {
// Triple click
v.lastClickTime = time.Now()
@@ -119,6 +120,8 @@ func (v *View) MousePress(usePlugin bool, e *tcell.EventMouse) bool {
}
}
v.lastLoc = Loc{x, y}
if usePlugin {
PostActionCall("MousePress", v, e)
}
@@ -491,6 +494,89 @@ func (v *View) SelectToEndOfLine(usePlugin bool) bool {
return true
}
// ParagraphPrevious moves the cursor to the previous empty line, or beginning of the buffer if there's none
func (v *View) ParagraphPrevious(usePlugin bool) bool {
if usePlugin && !PreActionCall("ParagraphPrevious", v) {
return false
}
var line int
for line = v.Cursor.Y; line > 0; line-- {
if len(v.Buf.lines[line].data) == 0 && line != v.Cursor.Y {
v.Cursor.X = 0
v.Cursor.Y = line
break
}
}
// If no empty line found. move cursor to end of buffer
if line == 0 {
v.Cursor.Loc = v.Buf.Start()
}
if usePlugin {
return PostActionCall("ParagraphPrevious", v)
}
return true
}
// ParagraphNext moves the cursor to the next empty line, or end of the buffer if there's none
func (v *View) ParagraphNext(usePlugin bool) bool {
if usePlugin && !PreActionCall("ParagraphNext", v) {
return false
}
var line int
for line = v.Cursor.Y; line < len(v.Buf.lines); line++ {
if len(v.Buf.lines[line].data) == 0 && line != v.Cursor.Y {
v.Cursor.X = 0
v.Cursor.Y = line
break
}
}
// If no empty line found. move cursor to end of buffer
if line == len(v.Buf.lines) {
v.Cursor.Loc = v.Buf.End()
}
if usePlugin {
return PostActionCall("ParagraphNext", v)
}
return true
}
func (v *View) Retab(usePlugin bool) bool {
if usePlugin && !PreActionCall("Retab", v) {
return false
}
toSpaces := v.Buf.Settings["tabstospaces"].(bool)
tabsize := int(v.Buf.Settings["tabsize"].(float64))
dirty := false
for i := 0; i < v.Buf.NumLines; i++ {
l := v.Buf.Line(i)
ws := GetLeadingWhitespace(l)
if ws != "" {
if toSpaces {
ws = strings.Replace(ws, "\t", Spaces(tabsize), -1)
} else {
ws = strings.Replace(ws, Spaces(tabsize), "\t", -1)
}
}
l = strings.TrimLeft(l, " \t")
v.Buf.lines[i].data = []byte(ws + l)
dirty = true
}
v.Buf.IsModified = dirty
if usePlugin {
return PostActionCall("Retab", v)
}
return true
}
// CursorStart moves the cursor to the start of the buffer
func (v *View) CursorStart(usePlugin bool) bool {
if usePlugin && !PreActionCall("CursorStart", v) {
@@ -912,7 +998,12 @@ func (v *View) SaveAs(usePlugin bool) bool {
filename, canceled := messenger.Prompt("Filename: ", "", "Save", NoCompletion)
if !canceled {
// the filename might or might not be quoted, so unquote first then join the strings.
filename = strings.Join(SplitCommandArgs(filename), " ")
args, err := shellwords.Split(filename)
filename = strings.Join(args, " ")
if err != nil {
messenger.Error("Error parsing arguments: ", err)
return false
}
v.saveToFile(filename)
}
@@ -1549,6 +1640,25 @@ func (v *View) ToggleHelp(usePlugin bool) bool {
return true
}
// ToggleKeyMenu toggles the keymenu option and resizes all tabs
func (v *View) ToggleKeyMenu(usePlugin bool) bool {
if v.mainCursor() {
if usePlugin && !PreActionCall("ToggleBindings", v) {
return false
}
globalSettings["keymenu"] = !globalSettings["keymenu"].(bool)
for _, tab := range tabs {
tab.Resize()
}
if usePlugin {
return PostActionCall("ToggleBindings", v)
}
}
return true
}
// ShellMode opens a terminal to run a shell command
func (v *View) ShellMode(usePlugin bool) bool {
if v.mainCursor() {
@@ -1638,6 +1748,7 @@ func (v *View) Quit(usePlugin bool) bool {
}
screen.Fini()
messenger.SaveHistory()
os.Exit(0)
}
}
@@ -1681,6 +1792,7 @@ func (v *View) QuitAll(usePlugin bool) bool {
}
screen.Fini()
messenger.SaveHistory()
os.Exit(0)
}
}

View File

@@ -4,8 +4,6 @@ import (
"io/ioutil"
"os"
"strings"
"github.com/mitchellh/go-homedir"
)
var pluginCompletions []func(string) []string
@@ -22,13 +20,9 @@ func FileComplete(input string) (string, []string) {
var files []os.FileInfo
var err error
if len(dirs) > 1 {
home, _ := homedir.Dir()
directories := strings.Join(dirs[:len(dirs)-1], sep) + sep
if strings.HasPrefix(directories, "~") {
directories = strings.Replace(directories, "~", home, 1)
}
directories = ReplaceHome(directories)
files, err = ioutil.ReadDir(directories)
} else {
files, err = ioutil.ReadDir(".")
@@ -148,6 +142,62 @@ func OptionComplete(input string) (string, []string) {
return chosen, suggestions
}
func OptionValueComplete(inputOpt, input string) (string, []string) {
inputOpt = strings.TrimSpace(inputOpt)
var suggestions []string
localSettings := DefaultLocalSettings()
var optionVal interface{}
for k, option := range globalSettings {
if k == inputOpt {
optionVal = option
}
}
for k, option := range localSettings {
if k == inputOpt {
optionVal = option
}
}
switch optionVal.(type) {
case bool:
if strings.HasPrefix("on", input) {
suggestions = append(suggestions, "on")
} else if strings.HasPrefix("true", input) {
suggestions = append(suggestions, "true")
}
if strings.HasPrefix("off", input) {
suggestions = append(suggestions, "off")
} else if strings.HasPrefix("false", input) {
suggestions = append(suggestions, "false")
}
case string:
switch inputOpt {
case "colorscheme":
_, suggestions = ColorschemeComplete(input)
case "fileformat":
if strings.HasPrefix("unix", input) {
suggestions = append(suggestions, "unix")
}
if strings.HasPrefix("dos", input) {
suggestions = append(suggestions, "dos")
}
case "sucmd":
if strings.HasPrefix("sudo", input) {
suggestions = append(suggestions, "sudo")
}
if strings.HasPrefix("doas", input) {
suggestions = append(suggestions, "doas")
}
}
}
var chosen string
if len(suggestions) == 1 {
chosen = suggestions[0]
}
return chosen, suggestions
}
// MakeCompletion registers a function from a plugin for autocomplete commands
func MakeCompletion(function string) Completion {
pluginCompletions = append(pluginCompletions, LuaFunctionComplete(function))

View File

@@ -12,6 +12,7 @@ import (
var bindings map[Key][]func(*View, bool) bool
var mouseBindings map[Key][]func(*View, bool, *tcell.EventMouse) bool
var helpBinding string
var kmenuBinding string
var mouseBindingActions = map[string]func(*View, bool, *tcell.EventMouse) bool{
"MousePress": (*View).MousePress,
@@ -41,6 +42,8 @@ var bindingActions = map[string]func(*View, bool) bool{
"DeleteWordLeft": (*View).DeleteWordLeft,
"SelectToStartOfLine": (*View).SelectToStartOfLine,
"SelectToEndOfLine": (*View).SelectToEndOfLine,
"ParagraphPrevious": (*View).ParagraphPrevious,
"ParagraphNext": (*View).ParagraphNext,
"InsertNewline": (*View).InsertNewline,
"InsertSpace": (*View).InsertSpace,
"Backspace": (*View).Backspace,
@@ -78,6 +81,7 @@ var bindingActions = map[string]func(*View, bool) bool{
"StartOfLine": (*View).StartOfLine,
"EndOfLine": (*View).EndOfLine,
"ToggleHelp": (*View).ToggleHelp,
"ToggleKeyMenu": (*View).ToggleKeyMenu,
"ToggleRuler": (*View).ToggleRuler,
"JumpLine": (*View).JumpLine,
"ClearStatus": (*View).ClearStatus,
@@ -256,6 +260,7 @@ type Key struct {
modifiers tcell.ModMask
buttons tcell.ButtonMask
r rune
escape string
}
// InitBindings initializes the keybindings for micro
@@ -312,6 +317,14 @@ modSearch:
case strings.HasPrefix(k, "Shift"):
k = k[5:]
modifiers |= tcell.ModShift
case strings.HasPrefix(k, "\x1b"):
return Key{
keyCode: -1,
modifiers: modifiers,
buttons: -1,
r: 0,
escape: k,
}, true
default:
break modSearch
}
@@ -397,9 +410,15 @@ func BindKey(k, v string) {
if v == "ToggleHelp" {
helpBinding = k
}
if v == "ToggleKeyMenu" {
kmenuBinding = k
}
if helpBinding == k && v != "ToggleHelp" {
helpBinding = ""
}
if kmenuBinding == k && v != "ToggleKeyMenu" {
kmenuBinding = ""
}
actionNames := strings.Split(v, ",")
if actionNames[0] == "UnbindKey" {
@@ -458,6 +477,8 @@ func DefaultBindings() map[string]string {
"CtrlDown": "CursorEnd",
"CtrlShiftUp": "SelectToStart",
"CtrlShiftDown": "SelectToEnd",
"Alt-{": "ParagraphPrevious",
"Alt-}": "ParagraphNext",
"Enter": "InsertNewline",
"CtrlH": "Backspace",
"Backspace": "Backspace",
@@ -490,6 +511,7 @@ func DefaultBindings() map[string]string {
"CtrlPageUp": "PreviousTab",
"CtrlPageDown": "NextTab",
"CtrlG": "ToggleHelp",
"Alt-g": "ToggleKeyMenu",
"CtrlR": "ToggleRuler",
"CtrlL": "JumpLine",
"Delete": "Delete",
@@ -509,7 +531,6 @@ func DefaultBindings() map[string]string {
// "Alt-n": "CursorDown",
// Integration with file managers
"F1": "ToggleHelp",
"F2": "Save",
"F3": "Find",
"F4": "Quit",

View File

@@ -2,6 +2,7 @@ package main
import (
"bytes"
"crypto/md5"
"encoding/gob"
"io"
"io/ioutil"
@@ -15,7 +16,6 @@ import (
"time"
"unicode/utf8"
"github.com/mitchellh/go-homedir"
"github.com/zyedidia/micro/cmd/micro/highlight"
)
@@ -57,6 +57,9 @@ type Buffer struct {
syntaxDef *highlight.Def
highlighter *highlight.Highlighter
// Hash of the original buffer -- empty if fastdirty is on
origHash [16]byte
// Buffer local settings
Settings map[string]interface{}
}
@@ -174,7 +177,7 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
}
if b.Settings["saveundo"].(bool) {
// We should only use last time's eventhandler if the file wasn't by someone else in the meantime
// We should only use last time's eventhandler if the file wasn't modified by someone else in the meantime
if b.ModTime == buffer.ModTime {
b.EventHandler = buffer.EventHandler
b.EventHandler.buf = b
@@ -184,8 +187,13 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
file.Close()
}
if b.Settings["mouse"].(bool) {
screen.EnableMouse()
if !b.Settings["fastdirty"].(bool) {
if size > 50000 {
// If the file is larger than a megabyte fastdirty needs to be on
b.Settings["fastdirty"] = true
} else {
b.origHash = md5.Sum([]byte(b.String()))
}
}
b.cursors = []*Cursor{&b.Cursor}
@@ -341,6 +349,14 @@ func (b *Buffer) MergeCursors() {
}
b.cursors = cursors
for i := range b.cursors {
b.cursors[i].Num = i
}
if b.curCursor >= len(b.cursors) {
b.curCursor = len(b.cursors) - 1
}
}
func (b *Buffer) UpdateCursors() {
@@ -381,7 +397,6 @@ func (b *Buffer) Serialize() error {
// SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
func (b *Buffer) SaveAs(filename string) error {
b.UpdateRules()
dir, _ := homedir.Dir()
if b.Settings["rmtrailingws"].(bool) {
r, _ := regexp.Compile(`[ \t]+$`)
for lineNum, line := range b.Lines(0, b.NumLines) {
@@ -402,9 +417,9 @@ func (b *Buffer) SaveAs(filename string) error {
}
str := b.SaveString(b.Settings["fileformat"] == "dos")
data := []byte(str)
err := ioutil.WriteFile(filename, data, 0644)
err := ioutil.WriteFile(ReplaceHome(filename), data, 0644)
if err == nil {
b.Path = strings.Replace(filename, "~", dir, 1)
b.Path = filename
b.IsModified = false
b.ModTime, _ = GetModTime(filename)
return b.Serialize()
@@ -424,7 +439,7 @@ func (b *Buffer) SaveAsWithSudo(filename string) error {
screen = nil
// Set up everything for the command
cmd := exec.Command("sudo", "tee", filename)
cmd := exec.Command(globalSettings["sucmd"].(string), "tee", filename)
cmd.Stdin = bytes.NewBufferString(b.SaveString(b.Settings["fileformat"] == "dos"))
// This is a trap for Ctrl-C so that it doesn't kill micro
@@ -451,6 +466,13 @@ func (b *Buffer) SaveAsWithSudo(filename string) error {
return err
}
func (b *Buffer) Modified() bool {
if b.Settings["fastdirty"].(bool) {
return b.IsModified
}
return b.origHash != md5.Sum([]byte(b.String()))
}
func (b *Buffer) insert(pos Loc, value []byte) {
b.IsModified = true
b.LineArray.insert(pos, value)

View File

@@ -65,6 +65,10 @@ type CellView struct {
}
func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
if width <= 0 {
return
}
tabsize := int(buf.Settings["tabsize"].(float64))
softwrap := buf.Settings["softwrap"].(bool)
indentrunes := []rune(buf.Settings["indentchar"].(string))

View File

@@ -15,7 +15,7 @@ type Colorscheme map[string]tcell.Style
// The current colorscheme
var colorscheme Colorscheme
// This takes in a syntax group and returns the colorscheme's style for that group
// GetColor takes in a syntax group and returns the colorscheme's style for that group
func GetColor(color string) tcell.Style {
st := defStyle
if color == "" {

View File

@@ -3,7 +3,6 @@ package main
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"os/signal"
@@ -14,7 +13,7 @@ import (
"strings"
humanize "github.com/dustin/go-humanize"
"github.com/mitchellh/go-homedir"
"github.com/zyedidia/micro/cmd/micro/shellwords"
)
// A Command contains a action (a function to call) as well as information about how to autocomplete the command
@@ -57,6 +56,7 @@ func init() {
"Open": Open,
"TabSwitch": TabSwitch,
"MemUsage": MemUsage,
"Retab": Retab,
}
}
@@ -90,8 +90,8 @@ func MakeCommand(name, function string, completions ...Completion) {
// DefaultCommands returns a map containing micro's default commands
func DefaultCommands() map[string]StrCommand {
return map[string]StrCommand{
"set": {"Set", []Completion{OptionCompletion, NoCompletion}},
"setlocal": {"SetLocal", []Completion{OptionCompletion, NoCompletion}},
"set": {"Set", []Completion{OptionCompletion, OptionValueCompletion}},
"setlocal": {"SetLocal", []Completion{OptionCompletion, OptionValueCompletion}},
"show": {"Show", []Completion{OptionCompletion, NoCompletion}},
"bind": {"Bind", []Completion{NoCompletion}},
"run": {"Run", []Completion{NoCompletion}},
@@ -112,6 +112,7 @@ func DefaultCommands() map[string]StrCommand {
"open": {"Open", []Completion{FileCompletion}},
"tabswitch": {"TabSwitch", []Completion{NoCompletion}},
"memusage": {"MemUsage", []Completion{NoCompletion}},
"retab": {"Retab", []Completion{NoCompletion}},
}
}
@@ -198,6 +199,10 @@ func PluginCmd(args []string) {
}
}
func Retab(args []string) {
CurView().Retab(true)
}
// TabSwitch switches to a given tab either by name or by number
func TabSwitch(args []string) {
if len(args) > 0 {
@@ -230,12 +235,19 @@ func TabSwitch(args []string) {
// Cd changes the current working directory
func Cd(args []string) {
if len(args) > 0 {
home, _ := homedir.Dir()
path := strings.Replace(args[0], "~", home, 1)
os.Chdir(path)
path := ReplaceHome(args[0])
err := os.Chdir(path)
if err != nil {
messenger.Error("Error with cd: ", err)
return
}
wd, _ := os.Getwd()
for _, tab := range tabs {
for _, view := range tab.views {
wd, _ := os.Getwd()
if len(view.Buf.name) == 0 {
continue
}
view.Buf.Path, _ = MakeRelative(view.Buf.AbsPath, wd)
if p, _ := filepath.Abs(view.Buf.Path); !strings.Contains(p, wd) {
view.Buf.Path = view.Buf.AbsPath
@@ -274,7 +286,12 @@ func Open(args []string) {
if len(args) > 0 {
filename := args[0]
// the filename might or might not be quoted, so unquote first then join the strings.
filename = strings.Join(SplitCommandArgs(filename), " ")
args, err := shellwords.Split(filename)
if err != nil {
messenger.Error("Error parsing args ", err)
return
}
filename = strings.Join(args, " ")
CurView().Open(filename)
} else {
@@ -325,8 +342,7 @@ func VSplit(args []string) {
CurView().VSplit(NewBufferFromString("", ""))
} else {
filename := args[0]
home, _ := homedir.Dir()
filename = strings.Replace(filename, "~", home, 1)
filename = ReplaceHome(filename)
file, err := os.Open(filename)
fileInfo, _ := os.Stat(filename)
@@ -355,8 +371,7 @@ func HSplit(args []string) {
CurView().HSplit(NewBufferFromString("", ""))
} else {
filename := args[0]
home, _ := homedir.Dir()
filename = strings.Replace(filename, "~", home, 1)
filename = ReplaceHome(filename)
file, err := os.Open(filename)
fileInfo, _ := os.Stat(filename)
@@ -396,8 +411,7 @@ func NewTab(args []string) {
CurView().AddTab(true)
} else {
filename := args[0]
home, _ := homedir.Dir()
filename = strings.Replace(filename, "~", home, 1)
filename = ReplaceHome(filename)
file, err := os.Open(filename)
fileInfo, _ := os.Stat(filename)
@@ -487,7 +501,7 @@ func Bind(args []string) {
// Run runs a shell command in the background
func Run(args []string) {
// Run a shell command in the background (openTerm is false)
HandleShellCommand(JoinCommandArgs(args...), false, true)
HandleShellCommand(shellwords.Join(args...), false, true)
}
// Quit closes the main view
@@ -508,24 +522,35 @@ func Save(args []string) {
// Replace runs search and replace
func Replace(args []string) {
if len(args) < 2 || len(args) > 3 {
if len(args) < 2 || len(args) > 4 {
// We need to find both a search and replace expression
messenger.Error("Invalid replace statement: " + strings.Join(args, " "))
return
}
allAtOnce := false
if len(args) == 3 {
// user added -a flag
if args[2] == "-a" {
allAtOnce = true
} else {
messenger.Error("Invalid replace flag: " + args[2])
return
all := false
noRegex := false
if len(args) > 2 {
for _, arg := range args[2:] {
switch arg {
case "-a":
all = true
case "-l":
noRegex = true
default:
messenger.Error("Invalid flag: " + arg)
return
}
}
}
search := string(args[0])
if noRegex {
search = regexp.QuoteMeta(search)
}
replace := string(args[1])
regex, err := regexp.Compile("(?m)" + search)
@@ -561,7 +586,7 @@ func Replace(args []string) {
view.Buf.MultipleReplace(deltas)
}
if allAtOnce {
if all {
replaceAll()
} else {
for {
@@ -621,15 +646,18 @@ func ReplaceAll(args []string) {
// RunShellCommand executes a shell command and returns the output/error
func RunShellCommand(input string) (string, error) {
inputCmd := SplitCommandArgs(input)[0]
args := SplitCommandArgs(input)[1:]
args, err := shellwords.Split(input)
if err != nil {
return "", err
}
inputCmd := args[0]
cmd := exec.Command(inputCmd, args...)
cmd := exec.Command(inputCmd, args[1:]...)
outputBytes := &bytes.Buffer{}
cmd.Stdout = outputBytes
cmd.Stderr = outputBytes
cmd.Start()
err := cmd.Wait() // wait for command to finish
err = cmd.Wait() // wait for command to finish
outstring := outputBytes.String()
return outstring, err
}
@@ -638,7 +666,11 @@ func RunShellCommand(input string) (string, error) {
// The openTerm argument specifies whether a terminal should be opened (for viewing output
// or interacting with stdin)
func HandleShellCommand(input string, openTerm bool, waitToFinish bool) string {
inputCmd := SplitCommandArgs(input)[0]
args, err := shellwords.Split(input)
if err != nil {
return ""
}
inputCmd := args[0]
if !openTerm {
// Simply run the command in the background and notify the user when it's done
messenger.Message("Running...")
@@ -663,13 +695,13 @@ func HandleShellCommand(input string, openTerm bool, waitToFinish bool) string {
screen.Fini()
screen = nil
args := SplitCommandArgs(input)[1:]
args := args[1:]
// Set up everything for the command
var outputBuf bytes.Buffer
var output string
cmd := exec.Command(inputCmd, args...)
cmd.Stdin = os.Stdin
cmd.Stdout = io.MultiWriter(os.Stdout, &outputBuf)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// This is a trap for Ctrl-C so that it doesn't kill micro
@@ -685,7 +717,6 @@ func HandleShellCommand(input string, openTerm bool, waitToFinish bool) string {
cmd.Start()
err := cmd.Wait()
output := outputBuf.String()
if err != nil {
output = err.Error()
}
@@ -705,7 +736,12 @@ func HandleShellCommand(input string, openTerm bool, waitToFinish bool) string {
// HandleCommand handles input from the user
func HandleCommand(input string) {
args := SplitCommandArgs(input)
args, err := shellwords.Split(input)
if err != nil {
messenger.Error("Error parsing args ", err)
return
}
inputCmd := args[0]
if _, ok := commands[inputCmd]; !ok {

View File

@@ -28,13 +28,15 @@ type Cursor struct {
Num int
}
// Goto puts the cursor at the given cursor's location and gives the current cursor its selection too
// Goto puts the cursor at the given cursor's location and gives
// the current cursor its selection too
func (c *Cursor) Goto(b Cursor) {
c.X, c.Y, c.LastVisualX = b.X, b.Y, b.LastVisualX
c.OrigSelection, c.CurSelection = b.OrigSelection, b.CurSelection
}
// CopySelection copies the user's selection to either "primary" or "clipboard"
// CopySelection copies the user's selection to either "primary"
// or "clipboard"
func (c *Cursor) CopySelection(target string) {
if c.HasSelection() {
if target != "primary" || c.buf.Settings["useprimary"].(bool) {
@@ -151,7 +153,8 @@ func (c *Cursor) SelectWord() {
c.Loc = c.CurSelection[1]
}
// AddWordToSelection adds the word the cursor is currently on to the selection
// AddWordToSelection adds the word the cursor is currently on
// to the selection
func (c *Cursor) AddWordToSelection() {
if c.Loc.GreaterThan(c.OrigSelection[0]) && c.Loc.LessThan(c.OrigSelection[1]) {
c.CurSelection = c.OrigSelection
@@ -183,7 +186,8 @@ func (c *Cursor) AddWordToSelection() {
c.Loc = c.CurSelection[1]
}
// SelectTo selects from the current cursor location to the given location
// SelectTo selects from the current cursor location to the given
// location
func (c *Cursor) SelectTo(loc Loc) {
if loc.GreaterThan(c.OrigSelection[0]) {
c.SetSelectionStart(c.OrigSelection[0])
@@ -258,7 +262,7 @@ func (c *Cursor) UpN(amount int) {
runes := []rune(c.buf.Line(c.Y))
c.X = c.GetCharPosInLine(proposedY, c.LastVisualX)
if c.X > len(runes) {
if c.X > len(runes) || (amount < 0 && proposedY == c.Y) {
c.X = len(runes)
}
@@ -280,7 +284,8 @@ func (c *Cursor) Down() {
c.DownN(1)
}
// Left moves the cursor left one cell (if possible) or to the last line if it is at the beginning
// Left moves the cursor left one cell (if possible) or to
// the previous line if it is at the beginning
func (c *Cursor) Left() {
if c.Loc == c.buf.Start() {
return
@@ -294,7 +299,8 @@ func (c *Cursor) Left() {
c.LastVisualX = c.GetVisualX()
}
// Right moves the cursor right one cell (if possible) or to the next line if it is at the end
// Right moves the cursor right one cell (if possible) or
// to the next line if it is at the end
func (c *Cursor) Right() {
if c.Loc == c.buf.End() {
return
@@ -320,7 +326,9 @@ func (c *Cursor) Start() {
c.LastVisualX = c.GetVisualX()
}
// GetCharPosInLine gets the char position of a visual x y coordinate (this is necessary because tabs are 1 char but 4 visual spaces)
// GetCharPosInLine gets the char position of a visual x y
// coordinate (this is necessary because tabs are 1 char but
// 4 visual spaces)
func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int {
// Get the tab size
tabSize := int(c.buf.Settings["tabsize"].(float64))
@@ -355,8 +363,9 @@ func (c *Cursor) StoreVisualX() {
c.LastVisualX = c.GetVisualX()
}
// Relocate makes sure that the cursor is inside the bounds of the buffer
// If it isn't, it moves it to be within the buffer's lines
// Relocate makes sure that the cursor is inside the bounds
// of the buffer If it isn't, it moves it to be within the
// buffer's lines
func (c *Cursor) Relocate() {
if c.Y < 0 {
c.Y = 0

View File

@@ -2,7 +2,7 @@ package highlight
import "regexp"
// DetectFiletype will use the list of syntax definitions provided and the filename and first line of the file
// 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 {

20
cmd/micro/keymenu.go Normal file
View File

@@ -0,0 +1,20 @@
package main
// DisplayKeyMenu displays the nano-style key menu at the bottom of the screen
func DisplayKeyMenu() {
w, h := screen.Size()
bot := h - 3
display := []string{"^Q Quit, ^S Save, ^O Open, ^G Help, ^E Command Bar, ^K Cut Line", "^F Find, ^Z Undo, ^Y Redo, ^A Select All, ^D Duplicate Line, ^T New Tab"}
for y := 0; y < len(display); y++ {
for x := 0; x < w; x++ {
if x < len(display[y]) {
screen.SetContent(x, bot+y, rune(display[y][x]), nil, defStyle)
} else {
screen.SetContent(x, bot+y, ' ', nil, defStyle)
}
}
}
}

View File

@@ -54,6 +54,7 @@ type Loc struct {
X, Y int
}
// Diff returns the distance between two locations
func Diff(a, b Loc, buf *Buffer) int {
if a.Y == b.Y {
if a.X > b.X {

View File

@@ -28,6 +28,7 @@ func init() {
L.SetGlobal("import", luar.New(L, Import))
}
// LoadFile loads a lua file
func LoadFile(module string, file string, data string) error {
pluginDef := "local P = {};" + module + " = P;setmetatable(" + module + ", {__index = _G});setfenv(1, P);"
@@ -39,40 +40,43 @@ func LoadFile(module string, file string, data string) error {
}
}
// Import allows a lua plugin to import a package
func Import(pkg string) *lua.LTable {
switch pkg {
case "fmt":
return ImportFmt()
return importFmt()
case "io":
return ImportIo()
case "ioutil":
return ImportIoUtil()
return importIo()
case "io/ioutil", "ioutil":
return importIoUtil()
case "net":
return ImportNet()
return importNet()
case "math":
return ImportMath()
return importMath()
case "math/rand":
return importMathRand()
case "os":
return ImportOs()
return importOs()
case "runtime":
return ImportRuntime()
return importRuntime()
case "path":
return ImportPath()
return importPath()
case "filepath":
return ImportFilePath()
return importFilePath()
case "strings":
return ImportStrings()
return importStrings()
case "regexp":
return ImportRegexp()
return importRegexp()
case "errors":
return ImportErrors()
return importErrors()
case "time":
return ImportTime()
return importTime()
default:
return nil
}
}
func ImportFmt() *lua.LTable {
func importFmt() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "tErrorf", luar.New(L, fmt.Errorf))
@@ -98,7 +102,7 @@ func ImportFmt() *lua.LTable {
return pkg
}
func ImportIo() *lua.LTable {
func importIo() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "Copy", luar.New(L, io.Copy))
@@ -122,7 +126,7 @@ func ImportIo() *lua.LTable {
return pkg
}
func ImportIoUtil() *lua.LTable {
func importIoUtil() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "ReadAll", luar.New(L, ioutil.ReadAll))
@@ -133,7 +137,7 @@ func ImportIoUtil() *lua.LTable {
return pkg
}
func ImportNet() *lua.LTable {
func importNet() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "CIDRMask", luar.New(L, net.CIDRMask))
@@ -201,7 +205,7 @@ func ImportNet() *lua.LTable {
return pkg
}
func ImportMath() *lua.LTable {
func importMath() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "Abs", luar.New(L, math.Abs))
@@ -269,7 +273,7 @@ func ImportMath() *lua.LTable {
return pkg
}
func ImportMathRand() *lua.LTable {
func importMathRand() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "ExpFloat64", luar.New(L, rand.ExpFloat64))
@@ -289,7 +293,7 @@ func ImportMathRand() *lua.LTable {
return pkg
}
func ImportOs() *lua.LTable {
func importOs() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "Args", luar.New(L, os.Args))
@@ -380,7 +384,7 @@ func ImportOs() *lua.LTable {
return pkg
}
func ImportRuntime() *lua.LTable {
func importRuntime() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "GC", luar.New(L, runtime.GC))
@@ -392,7 +396,7 @@ func ImportRuntime() *lua.LTable {
return pkg
}
func ImportPath() *lua.LTable {
func importPath() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "Base", luar.New(L, path.Base))
@@ -408,7 +412,7 @@ func ImportPath() *lua.LTable {
return pkg
}
func ImportFilePath() *lua.LTable {
func importFilePath() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "Join", luar.New(L, filepath.Join))
@@ -434,7 +438,7 @@ func ImportFilePath() *lua.LTable {
return pkg
}
func ImportStrings() *lua.LTable {
func importStrings() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "Contains", luar.New(L, strings.Contains))
@@ -484,7 +488,7 @@ func ImportStrings() *lua.LTable {
return pkg
}
func ImportRegexp() *lua.LTable {
func importRegexp() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "Match", luar.New(L, regexp.Match))
@@ -499,7 +503,7 @@ func ImportRegexp() *lua.LTable {
return pkg
}
func ImportErrors() *lua.LTable {
func importErrors() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "New", luar.New(L, errors.New))
@@ -507,7 +511,7 @@ func ImportErrors() *lua.LTable {
return pkg
}
func ImportTime() *lua.LTable {
func importTime() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "After", luar.New(L, time.After))

View File

@@ -3,12 +3,14 @@ package main
import (
"bufio"
"bytes"
"encoding/gob"
"fmt"
"os"
"strconv"
"github.com/mattn/go-runewidth"
"github.com/zyedidia/clipboard"
"github.com/zyedidia/micro/cmd/micro/shellwords"
"github.com/zyedidia/tcell"
)
@@ -231,6 +233,7 @@ const (
OptionCompletion
PluginCmdCompletion
PluginNameCompletion
OptionValueCompletion
)
// Prompt sends the user a message and waits for a response to be typed in
@@ -270,9 +273,16 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
response, canceled = m.response, false
m.history[historyType][len(m.history[historyType])-1] = response
case tcell.KeyTab:
args := SplitCommandArgs(m.response)
currentArgNum := len(args) - 1
currentArg := args[currentArgNum]
args, err := shellwords.Split(m.response)
if err != nil {
break
}
currentArg := ""
currentArgNum := 0
if len(args) > 0 {
currentArgNum = len(args) - 1
currentArg = args[currentArgNum]
}
var completionType Completion
if completionTypes[0] == CommandCompletion && currentArgNum > 0 {
@@ -296,6 +306,10 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
chosen, suggestions = HelpComplete(currentArg)
} else if completionType == OptionCompletion {
chosen, suggestions = OptionComplete(currentArg)
} else if completionType == OptionValueCompletion {
if currentArgNum-1 > 0 {
chosen, suggestions = OptionValueComplete(args[currentArgNum-1], currentArg)
}
} else if completionType == PluginCmdCompletion {
chosen, suggestions = PluginCmdComplete(currentArg)
} else if completionType == PluginNameCompletion {
@@ -309,7 +323,7 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
}
if chosen != "" {
m.response = JoinCommandArgs(append(args[:len(args)-1], chosen)...)
m.response = shellwords.Join(append(args[:len(args)-1], chosen)...)
m.cursorx = Count(m.response)
}
}
@@ -334,62 +348,139 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
return response, canceled
}
func (m *Messenger) UpHistory(history []string) {
if m.historyNum > 0 {
m.historyNum--
m.response = history[m.historyNum]
m.cursorx = Count(m.response)
}
}
func (m *Messenger) DownHistory(history []string) {
if m.historyNum < len(history)-1 {
m.historyNum++
m.response = history[m.historyNum]
m.cursorx = Count(m.response)
}
}
func (m *Messenger) CursorLeft() {
if m.cursorx > 0 {
m.cursorx--
}
}
func (m *Messenger) CursorRight() {
if m.cursorx < Count(m.response) {
m.cursorx++
}
}
func (m *Messenger) Start() {
m.cursorx = 0
}
func (m *Messenger) End() {
m.cursorx = Count(m.response)
}
func (m *Messenger) Backspace() {
if m.cursorx > 0 {
m.response = string([]rune(m.response)[:m.cursorx-1]) + string([]rune(m.response)[m.cursorx:])
m.cursorx--
}
}
func (m *Messenger) Paste() {
clip, _ := clipboard.ReadAll("clipboard")
m.response = Insert(m.response, m.cursorx, clip)
m.cursorx += Count(clip)
}
func (m *Messenger) WordLeft() {
response := []rune(m.response)
m.CursorLeft()
if m.cursorx <= 0 {
return
}
for IsWhitespace(response[m.cursorx]) {
if m.cursorx <= 0 {
return
}
m.CursorLeft()
}
m.CursorLeft()
for IsWordChar(string(response[m.cursorx])) {
if m.cursorx <= 0 {
return
}
m.CursorLeft()
}
m.CursorRight()
}
func (m *Messenger) WordRight() {
response := []rune(m.response)
if m.cursorx >= len(response) {
return
}
for IsWhitespace(response[m.cursorx]) {
m.CursorRight()
if m.cursorx >= len(response) {
m.CursorRight()
return
}
}
m.CursorRight()
if m.cursorx >= len(response) {
return
}
for IsWordChar(string(response[m.cursorx])) {
m.CursorRight()
if m.cursorx >= len(response) {
return
}
}
}
func (m *Messenger) DeleteWordLeft() {
m.WordLeft()
m.response = string([]rune(m.response)[:m.cursorx])
}
// HandleEvent handles an event for the prompter
func (m *Messenger) HandleEvent(event tcell.Event, history []string) {
switch e := event.(type) {
case *tcell.EventKey:
if e.Key() != tcell.KeyRune || e.Modifiers() != 0 {
for key, actions := range bindings {
if e.Key() == key.keyCode {
if e.Key() == tcell.KeyRune {
if e.Rune() != key.r {
continue
}
}
if e.Modifiers() == key.modifiers {
for _, action := range actions {
funcName := FuncName(action)
switch funcName {
case "main.(*View).CursorUp":
if m.historyNum > 0 {
m.historyNum--
m.response = history[m.historyNum]
m.cursorx = Count(m.response)
}
case "main.(*View).CursorDown":
if m.historyNum < len(history)-1 {
m.historyNum++
m.response = history[m.historyNum]
m.cursorx = Count(m.response)
}
case "main.(*View).CursorLeft":
if m.cursorx > 0 {
m.cursorx--
}
case "main.(*View).CursorRight":
if m.cursorx < Count(m.response) {
m.cursorx++
}
case "main.(*View).CursorStart", "main.(*View).StartOfLine":
m.cursorx = 0
case "main.(*View).CursorEnd", "main.(*View).EndOfLine":
m.cursorx = Count(m.response)
case "main.(*View).Backspace":
if m.cursorx > 0 {
m.response = string([]rune(m.response)[:m.cursorx-1]) + string([]rune(m.response)[m.cursorx:])
m.cursorx--
}
case "main.(*View).Paste":
clip, _ := clipboard.ReadAll("clipboard")
m.response = Insert(m.response, m.cursorx, clip)
m.cursorx += Count(clip)
}
}
}
}
}
}
switch e.Key() {
case tcell.KeyCtrlA:
m.Start()
case tcell.KeyCtrlE:
m.End()
case tcell.KeyUp:
m.UpHistory(history)
case tcell.KeyDown:
m.DownHistory(history)
case tcell.KeyLeft:
if e.Modifiers() == tcell.ModCtrl {
m.Start()
} else if e.Modifiers() == tcell.ModAlt || e.Modifiers() == tcell.ModMeta {
m.WordLeft()
} else {
m.CursorLeft()
}
case tcell.KeyRight:
if e.Modifiers() == tcell.ModCtrl {
m.End()
} else if e.Modifiers() == tcell.ModAlt || e.Modifiers() == tcell.ModMeta {
m.WordRight()
} else {
m.CursorRight()
}
case tcell.KeyBackspace2, tcell.KeyBackspace:
if e.Modifiers() == tcell.ModCtrl || e.Modifiers() == tcell.ModAlt || e.Modifiers() == tcell.ModMeta {
m.DeleteWordLeft()
} else {
m.Backspace()
}
case tcell.KeyCtrlW:
m.DeleteWordLeft()
case tcell.KeyCtrlV:
m.Paste()
case tcell.KeyCtrlF:
m.WordRight()
case tcell.KeyCtrlB:
m.WordLeft()
case tcell.KeyRune:
m.response = Insert(m.response, m.cursorx, string(e.Rune()))
m.cursorx++
@@ -480,6 +571,59 @@ func (m *Messenger) Display() {
}
}
// LoadHistory attempts to load user history from configDir/buffers/history
// into the history map
// The savehistory option must be on
func (m *Messenger) LoadHistory() {
if GetGlobalOption("savehistory").(bool) {
file, err := os.Open(configDir + "/buffers/history")
var decodedMap map[string][]string
if err == nil {
decoder := gob.NewDecoder(file)
err = decoder.Decode(&decodedMap)
file.Close()
if err != nil {
m.Error("Error loading history:", err)
return
}
}
if decodedMap != nil {
m.history = decodedMap
} else {
m.history = make(map[string][]string)
}
} else {
m.history = make(map[string][]string)
}
}
// SaveHistory saves the user's command history to configDir/buffers/history
// only if the savehistory option is on
func (m *Messenger) SaveHistory() {
if GetGlobalOption("savehistory").(bool) {
// Don't save history past 100
for k, v := range m.history {
if len(v) > 100 {
m.history[k] = v[0:100]
}
}
file, err := os.Create(configDir + "/buffers/history")
if err == nil {
encoder := gob.NewEncoder(file)
err = encoder.Encode(m.history)
if err != nil {
m.Error("Error saving history:", err)
return
}
file.Close()
}
}
}
// A GutterMessage is a message displayed on the side of the editor
type GutterMessage struct {
lineNum int

View File

@@ -143,6 +143,15 @@ func InitConfigDir() {
}
configDir = xdgHome + "/micro"
if len(*flagConfigDir) > 0 {
if _, err := os.Stat(*flagConfigDir); os.IsNotExist(err) {
TermMessage("Error: " + *flagConfigDir + " does not exist. Defaulting to " + configDir + ".")
} else {
configDir = *flagConfigDir
return
}
}
if _, err := os.Stat(xdgHome); os.IsNotExist(err) {
// If the xdgHome doesn't exist we should create it
err = os.Mkdir(xdgHome, os.ModePerm)
@@ -193,6 +202,10 @@ func InitScreen() {
os.Setenv("TERM", oldTerm)
}
if GetGlobalOption("mouse").(bool) {
screen.EnableMouse()
}
screen.SetStyle(defStyle)
}
@@ -212,6 +225,9 @@ func RedrawAll() {
}
DisplayTabs()
messenger.Display()
if globalSettings["keymenu"].(bool) {
DisplayKeyMenu()
}
screen.Show()
}
@@ -240,13 +256,26 @@ func LoadAll() {
// Passing -version as a flag will have micro print out the version number
var flagVersion = flag.Bool("version", false, "Show the version number and information")
var flagStartPos = flag.String("startpos", "", "LINE,COL to start the cursor at when opening a buffer.")
var flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
var flagOptions = flag.Bool("options", false, "Show all option help")
func main() {
flag.Usage = func() {
fmt.Println("Usage: micro [OPTIONS] [FILE]...")
fmt.Print("Micro's options can be set via command line arguments for quick adjustments. For real configuration, please use the bindings.json file (see 'help options').\n\n")
flag.CommandLine.SetOutput(os.Stdout)
flag.PrintDefaults()
fmt.Println("-config-dir dir")
fmt.Println(" \tSpecify a custom location for the configuration directory")
fmt.Println("-startpos LINE,COL")
fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
fmt.Println("-options")
fmt.Println(" \tShow all option help")
fmt.Println("-version")
fmt.Println(" \tShow the version number and information")
fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the bindings.json\nfile (see 'help options').\n\n")
fmt.Println("-option value")
fmt.Println(" \tSet `option` to `value` for this session")
fmt.Println(" \tFor example: `micro -syntax off file.c`")
fmt.Println("\nUse `micro -options` to see the full list of configuration options")
}
optionFlags := make(map[string]*string)
@@ -265,6 +294,15 @@ func main() {
os.Exit(0)
}
if *flagOptions {
// If -options was passed
for k, v := range DefaultGlobalSettings() {
fmt.Printf("-%s value\n", k)
fmt.Printf(" \tThe %s option. Default value: '%v'\n", k, v)
}
os.Exit(0)
}
// Start the Lua VM for running plugins
L = lua.NewState()
defer L.Close()
@@ -304,7 +342,7 @@ func main() {
// Create a new messenger
// This is used for sending the user messages in the bottom of the editor
messenger = new(Messenger)
messenger.history = make(map[string][]string)
messenger.LoadHistory()
// Now we load the input
buffers := LoadInput()
@@ -438,6 +476,8 @@ func main() {
}
for event != nil {
didAction := false
switch e := event.(type) {
case *tcell.EventResize:
for _, t := range tabs {
@@ -467,24 +507,36 @@ func main() {
}
}
}
} else if e.Buttons() == tcell.WheelUp || e.Buttons() == tcell.WheelDown {
var view *View
x, y := e.Position()
for _, v := range tabs[curTab].views {
if x >= v.x && x < v.x+v.Width && y >= v.y && y < v.y+v.Height {
view = tabs[curTab].views[v.Num]
}
}
view.HandleEvent(e)
didAction = true
}
}
}
// This function checks the mouse event for the possibility of changing the current tab
// If the tab was changed it returns true
if TabbarHandleMouseEvent(event) {
break
}
if !didAction {
// This function checks the mouse event for the possibility of changing the current tab
// If the tab was changed it returns true
if TabbarHandleMouseEvent(event) {
break
}
if searching {
// Since searching is done in real time, we need to redraw every time
// there is a new event in the search bar so we need a special function
// to run instead of the standard HandleEvent.
HandleSearchEvent(event, CurView())
} else {
// Send it to the view
CurView().HandleEvent(event)
if searching {
// Since searching is done in real time, we need to redraw every time
// there is a new event in the search bar so we need a special function
// to run instead of the standard HandleEvent.
HandleSearchEvent(event, CurView())
} else {
// Send it to the view
CurView().HandleEvent(event)
}
}
select {

View File

@@ -146,7 +146,7 @@ func LoadPlugins() {
pluginLuaName := luaPluginName(pluginName)
if err := LoadFile(pluginName, pluginName, string(data)); err != nil {
if err := LoadFile(pluginLuaName, pluginLuaName, string(data)); err != nil {
TermMessage(err)
continue
}

File diff suppressed because one or more lines are too long

18
cmd/micro/scrollbar.go Normal file
View File

@@ -0,0 +1,18 @@
package main
type ScrollBar struct {
view *View
}
func (sb *ScrollBar) Display() {
style := defStyle.Reverse(true)
screen.SetContent(sb.view.x+sb.view.Width-1, sb.view.y+sb.Pos(), ' ', nil, style)
}
func (sb *ScrollBar) Pos() int {
numlines := sb.view.Buf.NumLines
h := sb.view.Height
filepercent := float32(sb.view.Topline) / float32(numlines)
return int(filepercent * float32(h))
}

View File

@@ -64,7 +64,12 @@ func HandleSearchEvent(event tcell.Event, v *View) {
// Exit the search mode
ExitSearch(v)
return
case tcell.KeyCtrlQ, tcell.KeyCtrlC, tcell.KeyEnter:
case tcell.KeyEnter:
// If the user has pressed Enter, they want this to be the lastSearch
lastSearch = messenger.response
EndSearch()
return
case tcell.KeyCtrlQ, tcell.KeyCtrlC:
// Done
EndSearch()
return
@@ -179,9 +184,7 @@ func Search(searchStr string, v *View, down bool) {
found = searchUp(r, v, v.Buf.End(), searchStart)
}
}
if found {
lastSearch = searchStr
} else {
if !found {
v.Cursor.ResetSelection()
}
}

View File

@@ -1,6 +1,7 @@
package main
import (
"crypto/md5"
"encoding/json"
"errors"
"io/ioutil"
@@ -192,37 +193,40 @@ func GetOption(name string) interface{} {
func DefaultGlobalSettings() map[string]interface{} {
return map[string]interface{}{
"autoindent": true,
"keepautoindent": false,
"autosave": false,
"colorcolumn": float64(0),
"colorscheme": "default",
"cursorline": true,
"eofnewline": false,
"rmtrailingws": false,
"fastdirty": true,
"fileformat": "unix",
"ignorecase": false,
"indentchar": " ",
"infobar": true,
"keepautoindent": false,
"keymenu": false,
"mouse": true,
"pluginchannels": []string{"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"},
"pluginrepos": []string{},
"rmtrailingws": false,
"ruler": true,
"savecursor": false,
"savehistory": true,
"saveundo": false,
"scrollspeed": float64(2),
"scrollbar": false,
"scrollmargin": float64(3),
"scrollspeed": float64(2),
"softwrap": false,
"splitRight": true,
"splitBottom": true,
"splitbottom": true,
"splitright": true,
"statusline": true,
"sucmd": "sudo",
"syntax": true,
"tabmovement": false,
"tabsize": float64(4),
"tabstospaces": false,
"termtitle": false,
"pluginchannels": []string{
"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json",
},
"pluginrepos": []string{},
"useprimary": true,
"fileformat": "unix",
"mouse": true,
"useprimary": true,
}
}
@@ -231,31 +235,32 @@ func DefaultGlobalSettings() map[string]interface{} {
func DefaultLocalSettings() map[string]interface{} {
return map[string]interface{}{
"autoindent": true,
"keepautoindent": false,
"autosave": false,
"colorcolumn": float64(0),
"cursorline": true,
"eofnewline": false,
"rmtrailingws": false,
"fastdirty": true,
"fileformat": "unix",
"filetype": "Unknown",
"ignorecase": false,
"indentchar": " ",
"keepautoindent": false,
"rmtrailingws": false,
"ruler": true,
"savecursor": false,
"saveundo": false,
"scrollspeed": float64(2),
"scrollbar": false,
"scrollmargin": float64(3),
"scrollspeed": float64(2),
"softwrap": false,
"splitRight": true,
"splitBottom": true,
"splitbottom": true,
"splitright": true,
"statusline": true,
"syntax": true,
"tabmovement": false,
"tabsize": float64(4),
"tabstospaces": false,
"useprimary": true,
"fileformat": "unix",
"mouse": true,
}
}
@@ -309,16 +314,26 @@ func SetOption(option, value string) error {
}
}
if option == "infobar" {
if option == "infobar" || option == "keymenu" {
for _, tab := range tabs {
tab.Resize()
}
}
if _, ok := CurView().Buf.Settings[option]; ok {
for _, tab := range tabs {
for _, view := range tab.views {
SetLocalOption(option, value, view)
if option == "mouse" {
if !nativeValue.(bool) {
screen.DisableMouse()
} else {
screen.EnableMouse()
}
}
if len(tabs) != 0 {
if _, ok := CurView().Buf.Settings[option]; ok {
for _, tab := range tabs {
for _, view := range tab.views {
SetLocalOption(option, value, view)
}
}
}
}
@@ -358,6 +373,26 @@ func SetLocalOption(option, value string, view *View) error {
return err
}
if option == "fastdirty" {
// If it is being turned off, we have to hash every open buffer
var empty [16]byte
for _, tab := range tabs {
for _, v := range tab.views {
if !nativeValue.(bool) {
if v.Buf.origHash == empty {
data, err := ioutil.ReadFile(v.Buf.AbsPath)
if err != nil {
data = []byte{}
}
v.Buf.origHash = md5.Sum(data)
}
} else {
v.Buf.IsModified = v.Buf.Modified()
}
}
}
}
buf.Settings[option] = nativeValue
if option == "statusline" {
@@ -382,14 +417,6 @@ func SetLocalOption(option, value string, view *View) error {
}
}
if option == "mouse" {
if !nativeValue.(bool) {
screen.DisableMouse()
} else {
screen.EnableMouse()
}
}
return nil
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Yasuhiro Matsumoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,49 @@
This is a modified version of `go-shellwords` for the micro editor.
# go-shellwords
[![Coverage Status](https://coveralls.io/repos/mattn/go-shellwords/badge.png?branch=master)](https://coveralls.io/r/mattn/go-shellwords?branch=master)
[![Build Status](https://travis-ci.org/mattn/go-shellwords.svg?branch=master)](https://travis-ci.org/mattn/go-shellwords)
Parse line as shell words.
## Usage
```go
args, err := shellwords.Parse("./foo --bar=baz")
// args should be ["./foo", "--bar=baz"]
```
```go
os.Setenv("FOO", "bar")
p := shellwords.NewParser()
p.ParseEnv = true
args, err := p.Parse("./foo $FOO")
// args should be ["./foo", "bar"]
```
```go
p := shellwords.NewParser()
p.ParseBacktick = true
args, err := p.Parse("./foo `echo $SHELL`")
// args should be ["./foo", "/bin/bash"]
```
```go
shellwords.ParseBacktick = true
p := shellwords.NewParser()
args, err := p.Parse("./foo `echo $SHELL`")
// args should be ["./foo", "/bin/bash"]
```
# Thanks
This is based on cpan module [Parse::CommandLine](https://metacpan.org/pod/Parse::CommandLine).
# License
under the MIT License: http://mattn.mit-license.org/2017
# Author
Yasuhiro Matsumoto (a.k.a mattn)

View File

@@ -0,0 +1,189 @@
package shellwords
import (
"bytes"
"errors"
"os"
"regexp"
)
var envRe = regexp.MustCompile(`\$({[a-zA-Z0-9_]+}|[a-zA-Z0-9_]+)`)
func isSpace(r rune) bool {
switch r {
case ' ', '\t', '\r', '\n':
return true
}
return false
}
func replaceEnv(s string) string {
return envRe.ReplaceAllStringFunc(s, func(s string) string {
s = s[1:]
if s[0] == '{' {
s = s[1 : len(s)-1]
}
return os.Getenv(s)
})
}
type Parser struct {
Position int
}
func NewParser() *Parser {
return &Parser{0}
}
func (p *Parser) Parse(line string) ([]string, error) {
args := []string{}
buf := ""
var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool
backtick := ""
pos := -1
got := false
loop:
for i, r := range line {
if escaped {
buf += string(r)
escaped = false
continue
}
if r == '\\' {
if singleQuoted {
buf += string(r)
} else {
escaped = true
}
continue
}
if isSpace(r) {
if singleQuoted || doubleQuoted || backQuote || dollarQuote {
buf += string(r)
backtick += string(r)
} else if got {
buf = replaceEnv(buf)
args = append(args, buf)
buf = ""
got = false
}
continue
}
switch r {
case '`':
if !singleQuoted && !doubleQuoted && !dollarQuote {
if backQuote {
out, err := shellRun(backtick)
if err != nil {
return nil, err
}
buf = out
}
backtick = ""
backQuote = !backQuote
continue
backtick = ""
backQuote = !backQuote
}
case ')':
if !singleQuoted && !doubleQuoted && !backQuote {
if dollarQuote {
out, err := shellRun(backtick)
if err != nil {
return nil, err
}
buf = out
}
backtick = ""
dollarQuote = !dollarQuote
continue
backtick = ""
dollarQuote = !dollarQuote
}
case '(':
if !singleQuoted && !doubleQuoted && !backQuote {
if !dollarQuote && len(buf) > 0 && buf == "$" {
dollarQuote = true
buf += "("
continue
} else {
return nil, errors.New("invalid command line string")
}
}
case '"':
if !singleQuoted && !dollarQuote {
doubleQuoted = !doubleQuoted
continue
}
case '\'':
if !doubleQuoted && !dollarQuote {
singleQuoted = !singleQuoted
continue
}
case ';', '&', '|', '<', '>':
if !(escaped || singleQuoted || doubleQuoted || backQuote) {
pos = i
break loop
}
}
got = true
buf += string(r)
if backQuote || dollarQuote {
backtick += string(r)
}
}
buf = replaceEnv(buf)
args = append(args, buf)
if escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote {
return nil, errors.New("invalid command line string")
}
p.Position = pos
return args, nil
}
func Split(line string) ([]string, error) {
return NewParser().Parse(line)
}
func Join(args ...string) string {
var buf bytes.Buffer
for i, w := range args {
if i != 0 {
buf.WriteByte(' ')
}
if w == "" {
buf.WriteString("''")
continue
}
for _, b := range w {
switch b {
case 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u',
'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'_', '-', '.', ',', ':', '/', '@':
buf.WriteString(string(b))
default:
buf.WriteByte('\\')
buf.WriteString(string(b))
}
}
}
return buf.String()
}

View File

@@ -0,0 +1,229 @@
package shellwords
import (
"os"
"reflect"
"testing"
)
var testcases = []struct {
line string
expected []string
}{
{`var --bar=baz`, []string{`var`, `--bar=baz`}},
{`var --bar="baz"`, []string{`var`, `--bar=baz`}},
{`var "--bar=baz"`, []string{`var`, `--bar=baz`}},
{`var "--bar='baz'"`, []string{`var`, `--bar='baz'`}},
{"var --bar=`baz`", []string{`var`, "--bar=`baz`"}},
{`var "--bar=\"baz'"`, []string{`var`, `--bar="baz'`}},
{`var "--bar=\'baz\'"`, []string{`var`, `--bar='baz'`}},
{`var --bar='\'`, []string{`var`, `--bar=\`}},
{`var "--bar baz"`, []string{`var`, `--bar baz`}},
{`var --"bar baz"`, []string{`var`, `--bar baz`}},
{`var --"bar baz"`, []string{`var`, `--bar baz`}},
}
func TestSimple(t *testing.T) {
for _, testcase := range testcases {
args, err := Parse(testcase.line)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(args, testcase.expected) {
t.Fatalf("Expected %#v, but %#v:", testcase.expected, args)
}
}
}
func TestError(t *testing.T) {
_, err := Parse("foo '")
if err == nil {
t.Fatal("Should be an error")
}
_, err = Parse(`foo "`)
if err == nil {
t.Fatal("Should be an error")
}
_, err = Parse("foo `")
if err == nil {
t.Fatal("Should be an error")
}
}
func TestLastSpace(t *testing.T) {
args, err := Parse("foo bar\\ ")
if err != nil {
t.Fatal(err)
}
if len(args) != 2 {
t.Fatal("Should have two elements")
}
if args[0] != "foo" {
t.Fatal("1st element should be `foo`")
}
if args[1] != "bar " {
t.Fatal("1st element should be `bar `")
}
}
func TestBacktick(t *testing.T) {
goversion, err := shellRun("go version")
if err != nil {
t.Fatal(err)
}
parser := NewParser()
parser.ParseBacktick = true
args, err := parser.Parse("echo `go version`")
if err != nil {
t.Fatal(err)
}
expected := []string{"echo", goversion}
if !reflect.DeepEqual(args, expected) {
t.Fatalf("Expected %#v, but %#v:", expected, args)
}
args, err = parser.Parse(`echo $(echo foo)`)
if err != nil {
t.Fatal(err)
}
expected = []string{"echo", "foo"}
if !reflect.DeepEqual(args, expected) {
t.Fatalf("Expected %#v, but %#v:", expected, args)
}
parser.ParseBacktick = false
args, err = parser.Parse(`echo $(echo "foo")`)
expected = []string{"echo", `$(echo "foo")`}
if !reflect.DeepEqual(args, expected) {
t.Fatalf("Expected %#v, but %#v:", expected, args)
}
args, err = parser.Parse("echo $(`echo1)")
if err != nil {
t.Fatal(err)
}
expected = []string{"echo", "$(`echo1)"}
if !reflect.DeepEqual(args, expected) {
t.Fatalf("Expected %#v, but %#v:", expected, args)
}
}
func TestBacktickError(t *testing.T) {
parser := NewParser()
parser.ParseBacktick = true
_, err := parser.Parse("echo `go Version`")
if err == nil {
t.Fatal("Should be an error")
}
expected := "exit status 2:go: unknown subcommand \"Version\"\nRun 'go help' for usage.\n"
if expected != err.Error() {
t.Fatalf("Expected %q, but %q", expected, err.Error())
}
_, err = parser.Parse(`echo $(echo1)`)
if err == nil {
t.Fatal("Should be an error")
}
_, err = parser.Parse(`echo $(echo1`)
if err == nil {
t.Fatal("Should be an error")
}
_, err = parser.Parse(`echo $ (echo1`)
if err == nil {
t.Fatal("Should be an error")
}
_, err = parser.Parse(`echo (echo1`)
if err == nil {
t.Fatal("Should be an error")
}
_, err = parser.Parse(`echo )echo1`)
if err == nil {
t.Fatal("Should be an error")
}
}
func TestEnv(t *testing.T) {
os.Setenv("FOO", "bar")
parser := NewParser()
parser.ParseEnv = true
args, err := parser.Parse("echo $FOO")
if err != nil {
t.Fatal(err)
}
expected := []string{"echo", "bar"}
if !reflect.DeepEqual(args, expected) {
t.Fatalf("Expected %#v, but %#v:", expected, args)
}
}
func TestNoEnv(t *testing.T) {
parser := NewParser()
parser.ParseEnv = true
args, err := parser.Parse("echo $BAR")
if err != nil {
t.Fatal(err)
}
expected := []string{"echo", ""}
if !reflect.DeepEqual(args, expected) {
t.Fatalf("Expected %#v, but %#v:", expected, args)
}
}
func TestDupEnv(t *testing.T) {
os.Setenv("FOO", "bar")
os.Setenv("FOO_BAR", "baz")
parser := NewParser()
parser.ParseEnv = true
args, err := parser.Parse("echo $$FOO$")
if err != nil {
t.Fatal(err)
}
expected := []string{"echo", "$bar$"}
if !reflect.DeepEqual(args, expected) {
t.Fatalf("Expected %#v, but %#v:", expected, args)
}
args, err = parser.Parse("echo $${FOO_BAR}$")
if err != nil {
t.Fatal(err)
}
expected = []string{"echo", "$baz$"}
if !reflect.DeepEqual(args, expected) {
t.Fatalf("Expected %#v, but %#v:", expected, args)
}
}
func TestHaveMore(t *testing.T) {
parser := NewParser()
parser.ParseEnv = true
line := "echo foo; seq 1 10"
args, err := parser.Parse(line)
if err != nil {
t.Fatalf(err.Error())
}
expected := []string{"echo", "foo"}
if !reflect.DeepEqual(args, expected) {
t.Fatalf("Expected %#v, but %#v:", expected, args)
}
if parser.Position == 0 {
t.Fatalf("Commands should be remaining")
}
line = string([]rune(line)[parser.Position+1:])
args, err = parser.Parse(line)
if err != nil {
t.Fatalf(err.Error())
}
expected = []string{"seq", "1", "10"}
if !reflect.DeepEqual(args, expected) {
t.Fatalf("Expected %#v, but %#v:", expected, args)
}
if parser.Position > 0 {
t.Fatalf("Commands should not be remaining")
}
}

View File

@@ -0,0 +1,22 @@
// +build !windows
package shellwords
import (
"errors"
"os"
"os/exec"
"strings"
)
func shellRun(line string) (string, error) {
shell := os.Getenv("SHELL")
b, err := exec.Command(shell, "-c", line).Output()
if err != nil {
if eerr, ok := err.(*exec.ExitError); ok {
b = eerr.Stderr
}
return "", errors.New(err.Error() + ":" + string(b))
}
return strings.TrimSpace(string(b)), nil
}

View File

@@ -0,0 +1,20 @@
package shellwords
import (
"errors"
"os"
"os/exec"
"strings"
)
func shellRun(line string) (string, error) {
shell := os.Getenv("COMSPEC")
b, err := exec.Command(shell, "/c", line).Output()
if err != nil {
if eerr, ok := err.(*exec.ExitError); ok {
b = eerr.Stderr
}
return "", errors.New(err.Error() + ":" + string(b))
}
return strings.TrimSpace(string(b)), nil
}

View File

@@ -14,13 +14,17 @@ type Statusline struct {
// Display draws the statusline to the screen
func (sline *Statusline) Display() {
if messenger.hasPrompt && !GetGlobalOption("infobar").(bool) {
return
}
// We'll draw the line at the lowest line in the view
y := sline.view.Height + sline.view.y
file := sline.view.Buf.GetName()
// If the buffer is dirty (has been modified) write a little '+'
if sline.view.Buf.IsModified {
if sline.view.Buf.Modified() {
file += " +"
}
@@ -39,12 +43,24 @@ func (sline *Statusline) Display() {
file += " " + sline.view.Buf.Settings["fileformat"].(string)
rightText := ""
if len(helpBinding) > 0 {
rightText = helpBinding + " for help "
if sline.view.Type == vtHelp {
rightText = helpBinding + " to close help "
if len(kmenuBinding) > 0 {
if globalSettings["keymenu"].(bool) {
rightText += kmenuBinding + ": hide bindings"
} else {
rightText += kmenuBinding + ": show bindings"
}
}
if len(helpBinding) > 0 {
if len(kmenuBinding) > 0 {
rightText += ", "
}
if sline.view.Type == vtHelp {
rightText += helpBinding + ": close help"
} else {
rightText += helpBinding + ": open help"
}
}
rightText += " "
statusLineStyle := defStyle.Reverse(true)
if style, ok := colorscheme["statusline"]; ok {

View File

@@ -36,6 +36,9 @@ func NewTabFromView(v *View) *Tab {
if globalSettings["infobar"].(bool) {
t.tree.height--
}
if globalSettings["keymenu"].(bool) {
t.tree.height -= 2
}
t.Resize()
@@ -62,6 +65,9 @@ func (t *Tab) Resize() {
if globalSettings["infobar"].(bool) {
t.tree.height--
}
if globalSettings["keymenu"].(bool) {
t.tree.height -= 2
}
t.tree.ResizeSplits()
@@ -91,7 +97,7 @@ func TabbarString() (string, map[int]int) {
}
buf := t.views[t.CurView].Buf
str += buf.GetName()
if buf.IsModified {
if buf.Modified() {
str += " +"
}
if i == curTab {

View File

@@ -1,7 +1,6 @@
package main
import (
"bytes"
"os"
"path/filepath"
"reflect"
@@ -12,6 +11,7 @@ import (
"unicode/utf8"
"github.com/mattn/go-runewidth"
homedir "github.com/mitchellh/go-homedir"
)
// Util.go is a collection of utility functions that are used throughout
@@ -55,6 +55,7 @@ func Max(a, b int) int {
return b
}
// FSize gets the size of a file
func FSize(f *os.File) int64 {
fi, _ := f.Stat()
// get the size
@@ -245,6 +246,7 @@ func lcs(a, b string) string {
return lcs
}
// CommonSubstring gets a common substring among the inputs
func CommonSubstring(arr ...string) string {
commonStr := arr[0]
@@ -273,93 +275,17 @@ func ShortFuncName(i interface{}) string {
return strings.TrimPrefix(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name(), "main.(*View).")
}
// SplitCommandArgs separates multiple command arguments which may be quoted.
// The returned slice contains at least one string
func SplitCommandArgs(input string) []string {
var result []string
var curQuote *bytes.Buffer
curArg := new(bytes.Buffer)
escape := false
finishQuote := func() {
if curQuote == nil {
return
}
str := curQuote.String()
if unquoted, err := strconv.Unquote(str); err == nil {
str = unquoted
}
curArg.WriteString(str)
curQuote = nil
// ReplaceHome takes a path as input and replaces ~ at the start of the path with the user's
// home directory. Does nothing if the path does not start with '~'.
func ReplaceHome(path string) string {
if !strings.HasPrefix(path, "~") {
return path
}
appendResult := func() {
finishQuote()
escape = false
str := curArg.String()
result = append(result, str)
curArg.Reset()
home, err := homedir.Dir()
if err != nil {
messenger.Error("Could not find home directory: ", err)
return path
}
for _, r := range input {
if r == ' ' && curQuote == nil {
appendResult()
} else {
runeHandled := false
appendRuneToBuff := func() {
if curQuote != nil {
curQuote.WriteRune(r)
} else {
curArg.WriteRune(r)
}
runeHandled = true
}
if r == '"' && curQuote == nil {
curQuote = new(bytes.Buffer)
appendRuneToBuff()
} else {
if curQuote != nil && !escape {
if r == '"' {
appendRuneToBuff()
finishQuote()
} else if r == '\\' {
appendRuneToBuff()
escape = true
continue
}
}
}
if !runeHandled {
appendRuneToBuff()
}
}
escape = false
}
appendResult()
return result
}
// JoinCommandArgs joins multiple command arguments and quote the strings if needed.
func JoinCommandArgs(args ...string) string {
buf := new(bytes.Buffer)
first := true
for _, arg := range args {
if first {
first = false
} else {
buf.WriteRune(' ')
}
quoted := strconv.Quote(arg)
if quoted[1:len(quoted)-1] != arg || strings.ContainsRune(arg, ' ') {
buf.WriteString(quoted)
} else {
buf.WriteString(arg)
}
}
return buf.String()
return strings.Replace(path, "~", home, 1)
}

View File

@@ -1,7 +1,6 @@
package main
import (
"reflect"
"testing"
)
@@ -67,56 +66,6 @@ func TestIsWordChar(t *testing.T) {
}
}
func TestJoinAndSplitCommandArgs(t *testing.T) {
tests := []struct {
Query []string
Wanted string
}{
{[]string{`test case`}, `"test case"`},
{[]string{`quote "test"`}, `"quote \"test\""`},
{[]string{`slash\\\ test`}, `"slash\\\\\\ test"`},
{[]string{`path 1`, `path\" 2`}, `"path 1" "path\\\" 2"`},
{[]string{`foo`}, `foo`},
{[]string{`foo\"bar`}, `"foo\\\"bar"`},
{[]string{``}, ``},
{[]string{`"`}, `"\""`},
{[]string{`a`, ``}, `a `},
{[]string{``, ``, ``, ``}, ` `},
{[]string{"\n"}, `"\n"`},
{[]string{"foo\tbar"}, `"foo\tbar"`},
}
for i, test := range tests {
if result := JoinCommandArgs(test.Query...); test.Wanted != result {
t.Errorf("JoinCommandArgs failed at Test %d\nGot: %q", i, result)
}
if result := SplitCommandArgs(test.Wanted); !reflect.DeepEqual(test.Query, result) {
t.Errorf("SplitCommandArgs failed at Test %d\nGot: `%q`", i, result)
}
}
splitTests := []struct {
Query string
Wanted []string
}{
{`"hallo""Welt"`, []string{`halloWelt`}},
{`"hallo" "Welt"`, []string{`hallo`, `Welt`}},
{`\"`, []string{`\"`}},
{`"foo`, []string{`"foo`}},
{`"foo"`, []string{`foo`}},
{`"\"`, []string{`"\"`}},
{`"C:\\"foo.txt`, []string{`C:\foo.txt`}},
{`"\n"new"\n"line`, []string{"\nnew\nline"}},
}
for i, test := range splitTests {
if result := SplitCommandArgs(test.Query); !reflect.DeepEqual(test.Wanted, result) {
t.Errorf("SplitCommandArgs failed at Split-Test %d\nGot: `%q`", i, result)
}
}
}
func TestStringWidth(t *testing.T) {
tabsize := 4
if w := StringWidth("1\t2", tabsize); w != 5 {

View File

@@ -6,10 +6,10 @@ import (
"strings"
"time"
"github.com/mitchellh/go-homedir"
"github.com/zyedidia/tcell"
)
// The ViewType defines what kind of view this is
type ViewType struct {
kind int
readonly bool // The file cannot be edited
@@ -62,7 +62,7 @@ type View struct {
// The buffer
Buf *Buffer
// The statusline
sline Statusline
sline *Statusline
// Since tcell doesn't differentiate between a mouse release event
// and a mouse move event with no keys pressed, we need to keep
@@ -73,6 +73,7 @@ type View struct {
// This stores when the last click was
// This is useful for detecting double and triple clicks
lastClickTime time.Time
lastLoc Loc
// lastCutTime stores when the last ctrl+k was issued.
// It is used for clearing the clipboard to replace it with fresh cut lines.
@@ -91,6 +92,8 @@ type View struct {
cellview *CellView
splitNode *LeafNode
scrollbar *ScrollBar
}
// NewView returns a new fullscreen view
@@ -116,7 +119,11 @@ func NewViewWidthHeight(buf *Buffer, w, h int) *View {
v.messages = make(map[string][]GutterMessage)
v.sline = Statusline{
v.sline = &Statusline{
view: v,
}
v.scrollbar = &ScrollBar{
view: v,
}
@@ -198,7 +205,7 @@ func (v *View) ScrollDown(n int) {
// If there are unsaved changes, the user will be asked if the view can be closed
// causing them to lose the unsaved changes
func (v *View) CanClose() bool {
if v.Type == vtDefault && v.Buf.IsModified {
if v.Type == vtDefault && v.Buf.Modified() {
var choice bool
var canceled bool
if v.Buf.Settings["autosave"].(bool) {
@@ -210,15 +217,12 @@ func (v *View) CanClose() bool {
//if char == 'y' {
if choice {
v.Save(true)
return true
} else {
return true
}
} else {
return false
}
} else {
return true
}
return false
return true
}
// OpenBuffer opens a new buffer in this view.
@@ -243,8 +247,7 @@ func (v *View) OpenBuffer(buf *Buffer) {
// Open opens the given file in the view
func (v *View) Open(filename string) {
home, _ := homedir.Dir()
filename = strings.Replace(filename, "~", home, 1)
filename = ReplaceHome(filename)
file, err := os.Open(filename)
fileInfo, _ := os.Stat(filename)
@@ -285,7 +288,7 @@ func (v *View) ReOpen() {
// HSplit opens a horizontal split with the given buffer
func (v *View) HSplit(buf *Buffer) {
i := 0
if v.Buf.Settings["splitBottom"].(bool) {
if v.Buf.Settings["splitbottom"].(bool) {
i = 1
}
v.splitNode.HSplit(buf, v.Num+i)
@@ -294,7 +297,7 @@ func (v *View) HSplit(buf *Buffer) {
// VSplit opens a vertical split with the given buffer
func (v *View) VSplit(buf *Buffer) {
i := 0
if v.Buf.Settings["splitRight"].(bool) {
if v.Buf.Settings["splitright"].(bool) {
i = 1
}
v.splitNode.VSplit(buf, v.Num+i)
@@ -450,6 +453,7 @@ func (v *View) MoveToMouseClick(x, y int) {
v.Cursor.LastVisualX = v.Cursor.GetVisualX()
}
// Execute actions executes the supplied actions
func (v *View) ExecuteActions(actions []func(*View, bool) bool) bool {
relocate := false
readonlyBindingsList := []string{"Delete", "Insert", "Backspace", "Cut", "Play", "Paste", "Move", "Add", "DuplicateLine", "Macro"}
@@ -479,6 +483,7 @@ func (v *View) ExecuteActions(actions []func(*View, bool) bool) bool {
return relocate
}
// SetCursor sets the view's and buffer's cursor
func (v *View) SetCursor(c *Cursor) bool {
if c == nil {
return false
@@ -498,6 +503,24 @@ func (v *View) HandleEvent(event tcell.Event) {
v.Buf.CheckModTime()
switch e := event.(type) {
case *tcell.EventRaw:
for key, actions := range bindings {
if key.keyCode == -1 {
if e.EscapeCode() == key.escape {
for _, c := range v.Buf.cursors {
ok := v.SetCursor(c)
if !ok {
break
}
relocate = false
relocate = v.ExecuteActions(actions) || relocate
}
v.SetCursor(&v.Buf.Cursor)
v.Buf.MergeCursors()
break
}
}
}
case *tcell.EventKey:
// Check first if input is a key binding, if it is we 'eat' the input and don't insert a rune
isBinding := false
@@ -683,6 +706,7 @@ func (v *View) openHelp(helpPage string) {
}
}
// DisplayView draws the view to the screen
func (v *View) DisplayView() {
if v.Buf.Settings["softwrap"].(bool) && v.leftCol != 0 {
v.leftCol = 0
@@ -990,6 +1014,11 @@ func (v *View) Display() {
screen.HideCursor()
}
_, screenH := screen.Size()
if v.Buf.Settings["scrollbar"].(bool) {
v.scrollbar.Display()
}
if v.Buf.Settings["statusline"].(bool) {
v.sline.Display()
} else if (v.y + v.Height) != screenH-1 {

View File

@@ -1,27 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<component>
<id>com.github.zyedidia.micro</id>
<name>Micro Text Editor</name>
<summary>A modern and intuitive terminal-based text editor</summary>
<url type="homepage">https://micro-editor.github.io</url>
<url type="bugtracker">https://github.com/zyedidia/micro</url>
<metadata_license>MIT</metadata_license>
<categories>
<category>Development</category>
<category>TextEditor</category>
</categories>
<provides>
<binary>micro</binary>
</provides>
<releases>
<release version="1.2.0" date="2017-05-28" />
</releases>
<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>
<id>com.github.zyedidia.micro</id>
<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,30 +0,0 @@
#Flamepoint theme
#By CaptainMcClellan
color-link default ""
color-link comment ""
color-link constant ""
color-link constant.bool ""
color-link constant.bool.true ""
color-link constant.bool.false ""
color-link constant.number ""
color-link constant.specialChar ""
color-link constant.string ""
color-link constant.string.url "underline"
color-link identifier ""
color-link identifier.var ""
color-link preproc ""
color-link special ""
color-link statement ""
color-link symbol ""
color-link symbol.brackets ""
color-link symbol.tag ""
color-link type ""
color-link type.keyword ""
color-link error ""
color-link todo ""
color-link cursor-line ""
color-link statusline ""
color-link tabbar ""
color-link color-column ""
color-link gutter-error ""
color-link gutter-warning ""

View File

@@ -1 +0,0 @@
#Funky Cactus theme in true colour.

View File

@@ -1,30 +0,0 @@
#NES
#A color theme only using NES pallette colours
color-link default ""
color-link comment ""
color-link constant ""
color-link constant.bool ""
color-link constant.bool.true ""
color-link constant.bool.false ""
color-link constant.number ""
color-link constant.specialChar ""
color-link constant.string ""
color-link constant.string.url "underline"
color-link identifier ""
color-link identifier.var ""
color-link preproc ""
color-link special ""
color-link statement ""
color-link symbol ""
color-link symbol.brackets ""
color-link symbol.tag ""
color-link type ""
color-link type.keyword ""
color-link error ""
color-link todo ""
color-link cursor-line ""
color-link statusline ""
color-link tabbar ""
color-link color-column ""
color-link gutter-error ""
color-link gutter-warning ""

View File

@@ -0,0 +1,32 @@
# Twilight color scheme
color-link default "#F8F8F8,#141414"
color-link color-column "#1B1B1B"
color-link comment "#5F5A60"
color-link constant "#CF6A4C"
#color-link constant.number "#CF6A4C"
color-link constant.specialChar "#DDF2A4"
color-link constant.string "#8F9D6A"
color-link current-line-number "#868686,#1B1B1B"
color-link cursor-line "#1B1B1B"
color-link divider "#1E1E1E"
color-link error "#D2A8A1"
color-link gutter-error "#9B859D"
color-link gutter-warning "#9B859D"
color-link identifier "#9B703F"
color-link identifier.class "#DAD085"
color-link identifier.var "#7587A6"
color-link indent-char "#515151"
color-link line-number "#868686"
color-link preproc "#E0C589"
color-link special "#E0C589"
color-link statement "#CDA869"
color-link statusline "#515151,#1E1E1E"
color-link symbol "#AC885B"
color-link symbol.brackets "#F8F8F8"
color-link symbol.operator "#CDA869"
color-link symbol.tag "#AC885B"
color-link tabbar "#F2F0EC,#2D2D2D"
color-link todo "#8B98AB"
color-link type "#F9EE98"
color-link type.keyword "#CDA869"
color-link underlined "#8996A8"

View File

@@ -5,79 +5,85 @@ This help page aims to cover two aspects of micro's syntax highlighting engine:
- How to create colorschemes and use them
- How to create syntax files to add to the list of languages micro can highlight
## Colorschemes
Micro comes with a number of colorschemes by default. Here is the list:
* simple: this is the simplest colorscheme. It uses 16 colors which are
set by your terminal
* simple: this is the simplest colorscheme. It uses 16 colors which are set by
your terminal
* mc: A 16-color theme based on the look and feel of GNU Midnight Commander.
This will look great used in conjunction with Midnight Commander.
* nano: A 16-color theme loosely based on GNU nano's syntax highlighting.
* monokai: this is the monokai colorscheme; you may recognize it as
Sublime Text's default colorscheme. It requires true color to
look perfect, but the 256 color approximation looks very good as well.
It's also the default colorscheme.
* monokai: this is the monokai colorscheme; you may recognize it as Sublime
Text's default colorscheme. It requires true color to look perfect, but the
256 color approximation looks very good as well. It's also the default
colorscheme.
* zenburn: The 'zenburn' colorscheme and works well with 256 color terminals
* solarized: this is the solarized colorscheme.
You should have the solarized color palette in your terminal to use it.
* solarized: this is the solarized colorscheme. You should have the solarized
color palette in your terminal to use it.
* solarized-tc: this is the solarized colorscheme for true color; just
make sure your terminal supports true color before using it and that the
MICRO_TRUECOLOR environment variable is set to 1 before starting micro.
* solarized-tc: this is the solarized colorscheme for true color; just make sure
your terminal supports true color before using it and that the MICRO_TRUECOLOR
environment variable is set to 1 before starting micro.
* atom-dark-tc: this colorscheme is based off of Atom's "dark" colorscheme.
It requires true color to look good.
* atom-dark-tc: this colorscheme is based off of Atom's "dark" colorscheme. It
requires true color to look good.
* cmc-16: A very nice 16-color theme. Written by contributor CaptainMcClellan
(Collin Warren.) Licensed under the same license as the rest of the themes.
* cmc-paper: Basically cmc-16, but on a white background. ( Actually light grey on most
ANSI (16-color) terminals.)
* cmc-paper: Basically cmc-16, but on a white background. (Actually light grey
on most ANSI (16-color) terminals)
* 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.
* 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.
* codeblocks: A colorscheme based on the Code::Blocks IDE's default syntax highlighting.
* codeblocks: A colorscheme based on the Code::Blocks IDE's default syntax
highlighting.
* codeblocks-paper: Same as codeblocks, but on a white background. ( Actually light grey. )
* codeblocks-paper: Same as codeblocks, but on a white background. (Actually
light grey)
* github-tc: A colorscheme based on Github's syntax highlighting. Requires true color to look its best.
* github-tc: A colorscheme based on Github's syntax highlighting. Requires true
color to look its best.
* paper-tc: A nice minimalist theme with a light background, good for editing documents on.
Requires true color to look its best. Not to be confused with `-paper` suffixed themes.
* paper-tc: A nice minimalist theme with a light background, good for editing
documents on. Requires true color to look its best. Not to be confused with
`-paper` suffixed themes.
* geany: Colorscheme based on geany's default highlighting.
* geany-alt-tc: Based on an alternate theme bundled with geany.
* flamepoint-tc: A fire inspired, high intensity true color theme written by CaptainMcClellan.
As with all the other `-tc` suffixed themes, it looks its best on a
* flamepoint-tc: A fire inspired, high intensity true color theme written by
CaptainMcClellan. As with all the other `-tc` suffixed themes, it looks its
best on a
To enable one of these colorschemes just press CtrlE in micro and type `set colorscheme solarized`.
(or whichever one you choose). You can also use `set colorscheme monochrome` if you'd prefer
to have just the terminal's default foreground and background colors.
Note: This provides no syntax highlighting!
To enable one of these colorschemes just press CtrlE in micro and type
`set colorscheme solarized`. (or whichever one you choose). You can also use
`set colorscheme monochrome` if you'd prefer to have just the terminal's default
foreground and background colors. Note: This provides no syntax highlighting!
See `help gimmickcolors` for a list of some true colour themes that are more
just for fun than for serious use. ( Though feel free if you want! )
just for fun than for serious use. (Though feel free if you want!)
---
### Creating a Colorscheme
## Creating a Colorscheme
Micro's colorschemes are also extremely simple to create. The default ones can be found
Micro's colorschemes are also extremely simple to create. The default ones ca
be found
[here](https://github.com/zyedidia/micro/tree/master/runtime/colorschemes).
They are only about 18-30 lines in total.
Basically to create the colorscheme you need to link highlight groups with actual colors.
This is done using the `color-link` command.
Basically to create the colorscheme you need to link highlight groups with
actual colors. This is done using the `color-link` command.
For example, to highlight all comments in green, you would use the command:
@@ -109,20 +115,22 @@ color-link comment "bold red"
There are three different ways to specify the color.
Color terminals usually have 16 colors that are preset by the user. This means that
you cannot depend on those colors always being the same. You can use those colors with
the names `black, red, green, yellow, blue, magenta, cyan, white` and the bright variants
of each one (brightblack, brightred...).
Color terminals usually have 16 colors that are preset by the user. This means
that you cannot depend on those colors always being the same. You can use those
colors with the names `black, red, green, yellow, blue, magenta, cyan, white`
and the bright variants of each one (brightblack, brightred...).
Then you can use the terminals 256 colors by using their numbers 1-256 (numbers 1-16 will
refer to the named colors).
Then you can use the terminals 256 colors by using their numbers 1-256 (numbers
1-16 will refer to the named 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 told to use a true color colorscheme
it will attempt to map the colors to the available 256 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
told to use a true color colorscheme it will attempt to map the colors to the
available 256 colors.
Generally colorschemes which require true color terminals to look good are marked with a `-tc` suffix
and colorschemes which supply a white background are marked with a `-paper` suffix.
Generally colorschemes which require true color terminals to look good are
marked with a `-tc` suffix and colorschemes which supply a white background are
marked with a `-paper` suffix.
---
@@ -140,9 +148,10 @@ Here is a list of the colorscheme groups that you can use:
* underlined
* error
* todo
* statusline ( Color of the statusline)
* tabbar ( Color of the tabbar that lists open files.)
* indent-char ( Color of the character which indicates tabs if the option is enabled)
* statusline (Color of the statusline)
* tabbar (Color of the tabbar that lists open files)
* indent-char (Color of the character which indicates tabs if the option is
enabled)
* line-number
* gutter-error
* gutter-warning
@@ -150,29 +159,30 @@ Here is a list of the colorscheme groups that you can use:
* current-line-number
* color-column
* ignore
* divider ( Color of the divider between vertical splits. )
* divider (Color of the divider between vertical splits)
Colorschemes must be placed in the `~/.config/micro/colorschemes` directory to be used.
Colorschemes must be placed in the `~/.config/micro/colorschemes` directory to
be used.
---
In addition to the main colorscheme groups, there are subgroups that you can
specify by adding `.subgroup` to the group. If you're creating your own
custom syntax files, you can make use of your own subgroups.
specify by adding `.subgroup` to the group. If you're creating your own custom
syntax files, you can make use of your own subgroups.
If micro can't match the subgroup, it'll default to the root group, so
it's safe and recommended to use subgroups in your custom syntax files.
If micro can't match the subgroup, it'll default to the root group, so it's
safe and recommended to use subgroups in your custom syntax files.
For example if `constant.string` is found in your colorscheme, micro will
use 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 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 micro will use `constant.bool`. If not, it uses `constant`.
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.
Micro tries to match the largest set of groups it can find in the colorscheme
definitions, so if, for examle `constant.bool.true` is found then micro will use
that. If `constant.bool.true` is not found but `constant.bool` is found micro
will use `constant.bool`. If not, it uses `constant`.
Here's a list of subgroups used in micro's built-in syntax files.
* comment.bright ( Some filetypes have distinctions between types of comments.)
* comment.bright (Some filetypes have distinctions between types of comments)
* constant.bool
* constant.bool.true
* constant.bool.false
@@ -180,29 +190,32 @@ Here's a list of subgroups used in micro's built-in syntax files.
* constant.specialChar
* constant.string
* constant.string.url
* identifier.class ( Also used for functions. )
* identifier.class (Also used for functions)
* identifier.macro
* identifier.var
* preproc.shebang ( The #! at the beginning of a file that tells the os what script interpreter to use. )
* symbol.brackets ( {}()[] and sometimes <> )
* symbol.operator ( Color operator symbols differently. )
* symbol.tag ( For html tags, among other things.)
* type.keyword ( If you want a special highlight for keywords like `private` )
* preproc.shebang (The #! at the beginning of a file that tells the os what
script interpreter to use)
* symbol.brackets (`{}()[]` and sometimes `<>`)
* symbol.operator (Color operator symbols differently)
* symbol.tag (For html tags, among other things)
* type.keyword (If you want a special highlight for keywords like `private`)
In the future, plugins may also be able to use color groups for styling.
## Syntax files
The syntax files is written in yaml-format and specify how to highlight languages.
The syntax files is written in yaml-format and specify how to highlight
languages.
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
syntax files built int for over 100 languages now. However, there may be
situations where you find Micro's highlighting to be insufficient or not to
your liking. Good news is you can create syntax files (.micro extension), place them in
`~/.config/micro/syntax` and Micro will use those instead.
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
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 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.
### Filetype defintion
### Filetype definition
You must start the syntax file by declaring the filetype:
@@ -219,8 +232,9 @@ detect:
filename: "\\.go$"
```
Micro will match this regex against a given filename to detect the filetype. You may also
provide an optional `header` regex that will check the first line of the file. For example:
Micro will match this regex against a given filename to detect the filetype. You
may also provide an optional `header` regex that will check the first line of
the file. For example:
```
detect:
@@ -230,9 +244,10 @@ detect:
#### Syntax rules
Next you must provide the syntax highlighting rules. There are two types of rules: patterns and regions.
A pattern is matched on a single line and usually a single word as well. A region highlights between two
patterns over multiple lines and may have rules of its own inside the region.
Next you must provide the syntax highlighting rules. There are two types of
rules: patterns and regions. A pattern is matched on a single line and usually a
single word as well. A region highlights between two patterns over multiple
lines and may have rules of its own inside the region.
Here are some example patterns in Go:
@@ -243,7 +258,8 @@ rules:
- preproc: "\\b(package|import|const|var|type|struct|func|go|defer|iota)\\b"
```
The order of patterns does matter as patterns lower in the file will overwrite the ones defined above them.
The order of patterns does matter as patterns lower in the file will overwrite
the ones defined above them.
And here are some example regions for Go:
@@ -269,12 +285,15 @@ And here are some example regions for Go:
- todo: "(TODO|XXX|FIXME):?"
```
Notice how the regions may contain rules inside of them. Any inner rules that are matched are then skipped when searching
for the end of the region. For example, when highlighting `"foo \" bar"`, since `\"` is matched by an inner rule in the
region, it is skipped. Likewise for `"foo \\" bar`, since `\\` is matched by an inner rule, it is skipped, and then the `"`
is found and the string ends at the correct place.
Notice how the regions may contain rules inside of them. Any inner rules that
are matched are then skipped when searching for the end of the region. For
example, when highlighting `"foo \" bar"`, since `\"` is matched by an inner
rule in the region, it is skipped. Likewise for `"foo \\" bar`, since `\\` is
matched by an inner rule, it is skipped, and then the `"` is found and the
string ends at the correct place.
You may also explicitly mark skip regexes if you don't want them to be highlighted. For example:
You may also explicitly mark skip regexes if you don't want them to be
highlighted. For example:
```
- constant.string:
@@ -286,8 +305,8 @@ You may also explicitly mark skip regexes if you don't want them to be highlight
#### Includes
You may also include rules from other syntax files as embedded languages. For example, the following is possible
for html:
You may also include rules from other syntax files as embedded languages. For
example, the following is possible for html:
```
- default:

View File

@@ -5,24 +5,24 @@ Here are the possible commands that you can use.
* `quit`: Quits micro.
* `save filename?`: Saves the current buffer. If the filename is provided it will
'save as' the filename.
* `save filename?`: Saves the current buffer. If the filename is provided it
will 'save as' the filename.
* `replace "search" "value" flags`: This will replace `search` with `value`.
The `flags` are optional.
At this point, there is only one flag: `-a`, which replaces all occurrences
at once.
The `flags` are optional. Possible flags are:
* `-a`: Replace all occurrences at once
* `-l`: Do a literal search instead of a regex search
Note that `search` must be a valid regex. If one of the arguments
does not have any spaces in it, you may omit the quotes.
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.
* `replaceall "search" "value"`: This will replace `search` with `value` without
user confirmation.
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 topic
for a list of options you can set.
* `set option value`: sets the option to value. See the `options` help topic for
a list of options you can set.
* `setlocal option value`: sets the option to value locally (only in the current
buffer).
@@ -30,27 +30,25 @@ Here are the possible commands that you can use.
* `show option`: shows the current value of the given option.
* `eval "expression"`: Evaluates a Lua expression. Note that micro will not
print anything so you should use `messenger:Message(...)` to display a
value.
print anything so you should use `messenger:Message(...)` to display a value.
* `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.
* `bind key action`: creates a keybinding from key to action. See the sections on
keybindings above for more info about what keys and actions are available.
* `bind key action`: creates a keybinding from key to action. See the sections
on keybindings above for more info about what keys and actions are available.
* `vsplit filename`: opens a vertical split with `filename`. If no filename is
provided, a vertical split is opened with an empty buffer.
* `hsplit filename`: same as `vsplit` but opens a horizontal split instead of
a vertical split.
* `hsplit filename`: same as `vsplit` but opens a horizontal split instead of a
vertical split.
* `tab filename`: opens the given file in a new tab.
* `tabswitch tab`: This command will switch to the specified tab.
The `tab` can either be a tab number, or a name of a tab.
* `tabswitch tab`: This command will switch to the specified tab. The `tab` can
either be a tab number, or a name of a tab.
* `log`: opens a log of all messages and debug statements.
* `plugin install plugin_name`: installs the given plugin.
@@ -61,15 +59,15 @@ Here are the possible commands that you can use.
* `plugin update`: updates all installed plugins.
* `plugin search plugin_name`: searches for the given plugin.
Note that you can find a list of all available plugins at
* `plugin search plugin_name`: searches for the given plugin. Note that you can
find a list of all available plugins at
github.com/micro-editor/plugin-channel.
You can also see more information about the plugin manager
in the `Plugin Manager` section of the `plugins` help topic.
You can also see more information about the plugin manager in the
`Plugin Manager` section of the `plugins` help topic.
* `plugin available`: list plugins available for download (this includes
any plugins that may be already installed).
* `plugin available`: list plugins available for download (this includes any
plugins that may be already installed).
* `reload`: reloads all runtime files.
@@ -79,8 +77,19 @@ Here are the possible commands that you can use.
* `open filename`: Open a file in the current buffer.
* `retab`: Replaces all leading tabs with spaces or leading spaces with tabs
depending on the value of `tabstospaces`.
---
The following commands are provided by the default plugins:
* `lint`: Lint the current file for errors.
# Command Parsing
When running a command, you can use extra syntax that micro will expand before
running the command. To use an argument with a space in it, simply put it in
quotes. You can also use environment variables in the command bar and they
will be expanded to their value. Finally, you can put an expression in backticks
and it will be evaluated by the shell beforehand.

View File

@@ -1,13 +1,13 @@
# Default Keys
Below are simple charts of the default hotkeys and their functions.
For more information about binding custom hotkeys or changing
default bindings, please run `> help keybindings`
Below are simple charts of the default hotkeys and their functions. For more
information about binding custom hotkeys or changing default bindings, please
run `> help keybindings`
Please remember that *all* keys here are rebindable!
If you don't like it, you can change it!
Please remember that *all* keys here are rebindable! If you don't like it, you
can change it!
# Power user
### Power user
| Key | Description of function |
|-------- |-------------------------------------------------------------------------------------------------- |
@@ -15,7 +15,7 @@ If you don't like it, you can change it!
| Tab | In command prompt, it will autocomplete if possible. |
| Ctrl+B | Run a shell command (this will close micro while your command executes). |
# Navigation
### Navigation
| Key | Description of function |
|-------------------------- |------------------------------------------------------------------------------------------ |
@@ -25,6 +25,8 @@ If you don't like it, you can change it!
| End or CtrlRightArrow | Move to the end of the current line |
| AltLeftArrow | Move cursor one word left |
| AltRightArrow | Move cursor one word right |
| Alt+{ | Move cursor to previous empty line, or beginning of document |
| Alt+} | Move cursor to next empty line, or end of document |
| PageUp | Move cursor up one page |
| PageDown | Move cursor down one page |
| CtrlHome or CtrlUpArrow | Move cursor to start of document |
@@ -32,7 +34,7 @@ If you don't like it, you can change it!
| Ctrl+L | Jump to a line in the file (prompts with #) |
| Ctrl+W | Cycle between splits in the current tab (use `> vsplit` or `> hsplit` to create a split) |
# Tabs
### Tabs
| Key | Description of function |
|-------- |------------------------- |
@@ -40,7 +42,7 @@ If you don't like it, you can change it!
| Alt+, | Previous tab |
| Alt+. | Next tab |
# Find Operations
### Find Operations
| Key | Description of function |
|-------- |------------------------------------------ |
@@ -48,7 +50,7 @@ If you don't like it, you can change it!
| Ctrl+N | Find next instance of current search |
| Ctrl+P | Find previous instance of current search |
# File Operations
### File Operations
| Key | Description of function |
|-------- |---------------------------------------------------------------- |
@@ -56,7 +58,7 @@ If you don't like it, you can change it!
| Ctrl+O | Open a file (prompts for filename) |
| Ctrl+S | Save current file |
# Text operations
### Text operations
| Key | Description of function |
|--------------------------------- |------------------------------------------ |
@@ -78,14 +80,14 @@ If you don't like it, you can change it!
| AltBackspace or AltCtrl+H | Delete word left |
| Ctrl+A | Select all |
# Macros
### Macros
| Key | Description of function |
|-------- |---------------------------------------------------------------------------------- |
| Ctrl+U | Toggle macro recording (press Ctrl+U to start recording and press again to stop) |
| Ctrl+J | Run latest recorded macro |
# Multiple cursors
### Multiple cursors
| Key | Description of function |
|---------------- |---------------------------------------------------------------------------------------------- |
@@ -95,7 +97,7 @@ If you don't like it, you can change it!
| Alt+X | Skip multiple cursor selection |
| Ctrl-MouseLeft | Place a multiple cursor at any location |
# Other
### Other
| Key | Description of function |
|-------- |----------------------------------------------------------------------------------- |
@@ -103,7 +105,7 @@ If you don't like it, you can change it!
| Ctrl+H | Backspace (old terminals do not support the backspace key and use Ctrl+H instead) |
| Ctrl+R | Toggle the line number ruler |
# Emacs style actions
### Emacs style actions
| Key | Description of function |
|------- |------------------------- |
@@ -112,7 +114,7 @@ If you don't like it, you can change it!
| Alt+A | Move to start of line |
| Alt+E | Move to end of line |
# Function keys.
### Function keys.
Warning! The function keys may not work in all terminals!

View File

@@ -8,7 +8,7 @@ We have included a few colorschemes that are for fun:
Nintendo Entertainment System color palette.
* symbian-tc: Colorscheme based on SymbOS's GUI.
* matrix: Pretend it's 1981 with a colorscheme based on a monochrome
IBM 5151. ( Does not include the ghosting and trailing. )
IBM 5151. (Does not include the ghosting and trailing)
Check the plugin repo periodically for gimmick-color extension packs
and genuine additional themes.
Check the plugin repo periodically for gimmick-color extension packs and genuine
additional themes.

View File

@@ -1,6 +1,7 @@
# Micro help text
Thank you for downloading and using micro.
Micro is a terminal-based text editor that aims to be easy to use and intuitive,
while also taking advantage of the full capabilities of modern terminals.
@@ -8,44 +9,52 @@ If you want to see all the keybindings press CtrlE and type `help keybindings`.
See the next section for more information about documentation and help.
### Quick-start
Press CtrlQ to quit, and CtrlS to save. Press CtrlE to start typing commands
and you can see which commands are available by pressing tab, or by
viewing the help topic `> help commands`. When I write `> ...` I mean press
CtrlE and then type whatever is there.
## Quick-start
Move the cursor around with the mouse or the arrow keys. Type `> help defaultkeys` to
get a quick, easy overview of the default hotkeys and what they do. For more info
on rebinding keys, see type `> help keybindings`
Press CtrlQ to quit, and CtrlS to save. Press CtrlE to start typing commands and
you can see which commands are available by pressing tab, or by viewing the help
topic `> help commands`. When I write `> ...` I mean press Ctrl0E and then type
whatever is there.
If the colorscheme doesn't look good, you can change it with `> set colorscheme ...`.
You can press tab to see the available colorschemes, or see more information with
`> help colors`.
Move the cursor around with the mouse or the arrow keys. Type
`> help defaultkeys` to get a quick, easy overview of the default hotkeys and
what they do. For more info on rebinding keys, see type `> help keybindings`.
Press CtrlW to move between splits, and type `> vsplit filename` or `> hsplit filename`
to open a new split.
If the colorscheme doesn't look good, you can change it with
`> set colorscheme ...`. You can press tab to see the available colorschemes, or
see more information with `> help colors`.
### Accessing more help
Press CtrlW to move between splits, and type `> vsplit filename` or
`> hsplit filename` to open a new split.
## Accessing more help
Micro has a built-in help system much like Vim's (although less extensive).
To use it, press CtrlE to access command mode and type in `help` followed by a topic.
Typing `help` followed by nothing will open this page.
To use it, press CtrlE to access command mode and type in `help` followed by a
topic. Typing `help` followed by nothing will open this page.
Here are the possible help topics that you can read:
* tutorial: A brief tutorial which gives an overview of all the other help topics
* keybindings: Gives a full list of the default keybindings as well as how to rebind them
* defaultkeys: Gives a more straight-forward list of the hotkey commands and what they do.
* tutorial: A brief tutorial which gives an overview of all the other help
topics
* keybindings: Gives a full list of the default keybindings as well as how to
rebind them
* defaultkeys: Gives a more straight-forward list of the hotkey 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
* plugins: Explains how micro's plugin system works and how to create your own plugins
* colors: Explains micro's colorscheme and syntax highlighting engine and how to create your
own colorschemes or add new languages to the engine
* plugins: Explains how micro's plugin system works and how to create your own
plugins
* colors: Explains micro's colorscheme and syntax highlighting engine and how to
create your own colorschemes or add new languages to the engine
For example, to open the help page on plugins you would press CtrlE and type `help plugins`.
For example, to open the help page on plugins you would press CtrlE and type
`help plugins`.
I recommend looking at the `tutorial` help file because it is short for each section and
gives concrete examples of how to use the various configuration options in micro. However,
it does not give the in-depth documentation that the other topics provide.
I recommend looking at the `tutorial` help file because it is short for each
section and gives concrete examples of how to use the various configuration
options in micro. However, it does not give the in-depth documentation that the
other topics provide.

View File

@@ -2,25 +2,28 @@
Micro has a plethora of hotkeys that make it easy and powerful to use and all
hotkeys are fully customizable to your liking.
Custom keybindings are stored internally in micro if changed with the `>bind` command or
you can also be added in the file `~/.config/micro/bindings.json` as discussed below.
For a list of the default keybindings in the json format used by micro, please see
the end of this file. For a more user-friendly list with explanations of what the default
hotkeys are and what they do, please see `>help defaultkeys`
Custom keybindings are stored internally in micro if changed with the `> bind`
command or you can also be added in the file `~/.config/micro/bindings.json` as
discussed below. For a list of the default keybindings in the json format used
by micro, please see the end of this file. For a more user-friendly list with
explanations of what the default hotkeys are and what they do, please see
`>help defaultkeys`
If `~/.config/micro/bindings.json` does not exist, you can simply create it.
Micro will know what to do with it.
You can use the alt keys + arrows to move word by word.
Ctrl left and right move the cursor to the start and end of the line, and
ctrl up and down move the cursor the start and end of the buffer.
You can use the alt keys + arrows to move word by word. Ctrl left and right move
the cursor to the start and end of the line, and ctrl up and down move the
cursor the start and end of the buffer.
You can hold shift with all of these movement actions to select while moving.
# Rebinding keys
The bindings may be rebound using the `~/.config/micro/bindings.json`
file. Each key is bound to an action.
## Rebinding keys
The bindings may be rebound using the `~/.config/micro/bindings.json` file. Each
key is bound to an action.
For example, to bind `Ctrl-y` to undo and `Ctrl-z` to redo, you could put the
following in the `bindings.json` file.
@@ -35,8 +38,8 @@ following in the `bindings.json` file.
In addition to editing your `~/.config/micro/bindings.json`, you can run
`>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 save
and quit you can bind it like so:
You can also chain commands when rebinding. For example, if you want Alt-s to
save and quit you can bind it like so:
```json
{
@@ -44,12 +47,59 @@ and quit you can bind it like so:
}
```
# Unbinding keys
## Binding raw escape sequences
Only read this section if you are interested in binding keys that aren't on the
list of supported keys for binding.
One of the drawbacks of using a terminal-based editor is that the editor must
get all of its information about key events through the terminal. The terminal
sends these events in the form of escape sequences often (but not always)
starting with `0x1b`.
For example, if micro reads `\x1b[1;5D`, on most terminals this will mean the
user pressed CtrlLeft.
For many key chords though, the terminal won't send any escape code or will send
an escape code already in use. For example for `CtrlBackspace`, my terminal
sends `\u007f` (note this doesn't start with `0x1b`), which it also sends for
`Backspace` meaning micro can't bind `CtrlBackspace`.
However, some terminals do allow you to bind keys to send specific escape
sequences you define. Then from micro you can directly bind those escape
sequences to actions. For example, to bind `CtrlBackspace` you can instruct your
terminal to send `\x1bctrlback` and then bind it in `bindings.json`:
```json
{
"\u001bctrlback": "DeleteWordLeft"
}
```
Here are some instructions for sending raw escapes in different terminals
### iTerm2
In iTerm2, you can do this in `Preferences->Profiles->Keys` then click the `+`,
input your keybinding, and for the `Action` select `Send Escape Sequence`. For
the above example your would type `ctrlback` into the box (the `\x1b`) is
automatically sent by iTerm2.
### Linux using loadkeys
You can do this in linux using the loadkeys program.
Coming soon!
## Unbinding keys
It is also possible to disable any of the default key bindings by use of the
`UnbindKey` action in the user's `bindings.json` file.
# Bindable actions and bindable keys
## Bindable actions and bindable keys
The list of default keybindings contains most of the possible actions and keys
which you can use, but not all of them. Here is a full list of both.
@@ -113,6 +163,8 @@ HalfPageUp
HalfPageDown
StartOfLine
EndOfLine
ParagraphPrevious
ParagraphNext
ToggleHelp
ToggleRuler
JumpLine
@@ -142,6 +194,7 @@ UnbindKey
```
You can also bind some mouse actions (these must be bound to mouse buttons)
```
MousePress
MouseMultiCursor
@@ -275,7 +328,8 @@ Escape
Enter
```
You can also bind some mouse buttons (they may be bound to normal actions or mouse actions)
You can also bind some mouse buttons (they may be bound to normal actions or
mouse actions)
```
MouseLeft
@@ -315,6 +369,8 @@ MouseWheelRight
"CtrlDown": "CursorEnd",
"CtrlShiftUp": "SelectToStart",
"CtrlShiftDown": "SelectToEnd",
"Alt-{": "ParagraphPrevious",
"Alt-}": "ParagraphNext",
"Enter": "InsertNewline",
"CtrlH": "Backspace",
"Backspace": "Backspace",
@@ -385,12 +441,13 @@ MouseWheelRight
}
```
# Final notes
Note: On some old terminal emulators and on Windows machines, `CtrlH` should be used
for backspace.
## Final notes
Additionally, alt keys can be bound by using `Alt-key`. For example `Alt-a`
or `Alt-Up`. Micro supports an optional `-` between modifiers like `Alt` and `Ctrl`
so `Alt-a` could be rewritten as `Alta` (case matters for alt bindings). This is
why in the default keybindings you can see `AltShiftLeft` instead of `Alt-ShiftLeft`
(they are equivalent).
Note: On some old terminal emulators and on Windows machines, `CtrlH` should be
used for backspace.
Additionally, alt keys can be bound by using `Alt-key`. For example `Alt-a` or
`Alt-Up`. Micro supports an optional `-` between modifiers like `Alt` and
`Ctrl` so `Alt-a` could be rewritten as `Alta` (case matters for alt bindings).
This is why in the default keybindings you can see `AltShiftLeft` instead of
`Alt-ShiftLeft` (they are equivalent).

View File

@@ -1,4 +1,4 @@
### Options
# Options
Micro stores all of the user configuration in its configuration directory.
@@ -8,11 +8,29 @@ the config directory.
Here are the options that you can set:
* `autoindent`: when creating a new line use the same indentation as the
previous line.
default value: `on`
* `autosave`: micro will save the buffer every 8 seconds automatically. Micro
also will automatically save and quit when you exit without asking. Be
careful when using this feature, because you might accidentally save a file,
overwriting what was there before.
default value: `off`
* `colorcolumn`: if this is not set to 0, it will display a column at the
specified column. This is useful if you want column 80 to be highlighted
special for example.
default value: `0`
* `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`
Note that the default colorschemes (default, solarized, and solarized-tc)
are not located in configDir, because they are embedded in the micro binary.
@@ -20,202 +38,233 @@ Here are the options that you can set:
~/.config/micro/colorschemes/ directory. Micro comes by default with three
colorschemes:
You can read more about micro's colorschemes in the `colors` help topic
(`help colors`).
You can read more about micro's colorschemes in the `colors` help topic
(`help colors`).
* `colorcolumn`: if this is not set to 0, it will display a column at the specified
column. This is useful if you want column 80 to be highlighted special for example.
* `cursorline`: highlight the line that the cursor is on in a different color
(the color is defined by the colorscheme you are using).
default value: `0`
default value: `on`
* `eofnewline`: micro will automatically add a newline to the file.
default value: `false`
* `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
boolean `modified` that is set to `true` as soon as the user makes an edit.
This is fast, but can be inaccurate. If `fastdirty` is off, then micro will
hash the current buffer against a hash of the original file (created when the
buffer was loaded). This is more accurate but obviously more resource
intensive. This option is only for people who really care about having
accurate modified status.
default value: `on`
* `fileformat`: this determines what kind of line endings micro will use for the
file. UNIX line endings are just `\n` (lf) whereas dos line endings are
`\r\n` (crlf). The two possible values for this option are `unix` and `dos`.
The fileformat will be automatically detected and displayed on the statusline
but this option is useful if you would like to change the line endings or if
you are starting a new file.
default value: `unix`
* `filetype`: sets the filetype for the current buffer. This setting is
`local only`.
default value: this will be automatically set depending on the file you have
open
* `ignorecase`: perform case-insensitive searches.
default value: `off`
* `indentchar`: sets the indentation character.
default value: ` `
* `infobar`: enables the line at the bottom of the editor where messages are
printed. This option is `global only`.
default value: `on`
* `keepautoindent`: when using autoindent, whitespace is added for you. This
option determines if when you move to the next line without any insertions
the whitespace that was added should be deleted. By default the autoindent
whitespace is deleted if the line was left empty.
default value: `off`
* `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
the statusline. To disable this, simply by `Alt-g` to `UnbindKey`.
default value: `off`
* `mouse`: whether to enable mouse support. When mouse support is disabled,
usually the terminal will be able to access mouse events which can be useful
if you want to copy from the terminal instead of from micro (if over ssh for
example, because the terminal has access to the local clipboard and micro
does not).
default value: `on`
* `pluginchannels`: contains all the channels micro's plugin manager will search
for plugins in. A channel is simply a list of 'repository' json files which
contain metadata about the given plugin. See the `Plugin Manager` section of
the `plugins` help topic for more information.
default value: `https://github.com/micro-editor/plugin-channel`
* `pluginrepos`: contains all the 'repositories' micro's plugin manager will
search for plugins in. A repository consists of a `repo.json` file which
contains metadata for a single plugin.
default value: ` `
* `rmtrailingws`: micro will automatically trim trailing whitespaces at eol.
default value: `false`
* `ruler`: display line numbers.
default value: `on`
* `savecursor`: remember where the cursor was last time the file was opened and
put it there when you open the file again.
default value: `off`
* `savehistory`: remember command history between closing and re-opening
micro.
default value: `on`
* `saveundo`: when this option is on, undo is saved even after you close a file
so if you close and reopen a file, you can keep undoing.
default value: `off`
* `scrollbar`: display a scroll bar
default value: `off`
* `scrollmargin`: amount of lines you would like to see above and below the
cursor.
default value: `3`
* `scrollspeed`: amount of lines to scroll for one scroll event.
default value: `2`
* `softwrap`: should micro wrap lines that are too long to fit on the screen.
default value: `off`
* `splitbottom`: when a horizontal split is created, should it be created below
the current split?
default value: `on`
* `splitright`: when a vertical split is created, should it be created to the
right of the current split?
default value: `on`
* `statusline`: display the status line at the bottom of the screen.
default value: `on`
* `syntax`: turns syntax on or off.
default value: `on`
* `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
saving with su.
default value: `sudo`
* `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
`tabstospaces` is on.
default value: `off`
* `tabsize`: sets the tab size to `option`
default value: `4`
* `indentchar`: sets the indentation character
default value: ` `
* `infobar`: enables the line at the bottom of the editor where messages are printed.
This option is `global only`.
default value: `on`
* `filetype`: sets the filetype for the current buffer. This setting is `local only`
default value: this will be automatically set depending on the file you have open
* `ignorecase`: perform case-insensitive searches
default value: `off`
* `syntax`: turns syntax on or off
default value: `on`
* `tabstospaces`: use spaces instead of tabs
default value: `off`
* `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 `tabstospaces` is on.
default value: `off`
* `autoindent`: when creating a new line use the same indentation as the
previous line
default value: `on`
* `cursorline`: highlight the line that the cursor is on in a different color
(the color is defined by the colorscheme you are using)
default value: `on`
* `ruler`: display line numbers
default value: `on`
* `statusline`: display the status line at the bottom of the screen
default value: `on`
* `savecursor`: remember where the cursor was last time the file was opened and
put it there when you open the file again
* `termtitle`: defines whether or not your terminal's title will be set by micro
when opened.
default value: `off`
* `saveundo`: when this option is on, undo is saved even after you close a file
so if you close and reopen a file, you can keep undoing
default value: `off`
* `scrollmargin`: amount of lines you would like to see above and below the cursor
default value: `3`
* `scrollspeed`: amount of lines to scroll for one scroll event
default value: `2`
* `softwrap`: should micro wrap lines that are too long to fit on the screen
default value: `off`
* `splitRight`: when a vertical split is created, should it be created to the right of
the current split?
* `useprimary` (only useful on *nix): defines whether or not micro will use the
primary clipboard to copy selections in the background. This does not affect
the normal clipboard using Ctrl-C and Ctrl-V.
default value: `on`
* `splitBottom`: when a horizontal split is created, should it be created below the
current split?
default value: `on`
* `autosave`: micro will save the buffer every 8 seconds automatically.
Micro also will automatically save and quit when you exit without asking.
Be careful when using this feature, because you might accidentally save a file,
overwriting what was there before.
default value: `off`
* `pluginchannels`: contains all the channels micro's plugin manager will search
for plugins in. A channel is simply a list of 'repository' json files which contain
metadata about the given plugin. See the `Plugin Manager` section of the `plugins` help topic
for more information.
default value: `https://github.com/micro-editor/plugin-channel`
* `pluginrepos`: contains all the 'repositories' micro's plugin manager will search for
plugins in. A repository consists of a `repo.json` file which contains metadata for a
single plugin.
default value: ` `
* `useprimary` (only useful on Linux): defines whether or not micro will use the primary clipboard to copy selections
in the background. This does not affect the normal clipboard using Ctrl-C and Ctrl-V.
default value: `on`
* `keepautoindent`: when using autoindent, whitespace is added for you. This option determines if
when you move to the next line without any insertions the whitespace that was added should be deleted.
By default the autoindent whitespace is deleted if the line was left empty.
default value: `off`
* `termtitle`: defines whether or not your terminal's title will be set by micro when opened.
default value: `off`
* `mouse`: whether to enable mouse support. When mouse support is disabled, usually the terminal will be able
to access mouse events which can be useful if you want to copy from the terminal instead of from micro (if
over ssh for example, because the terminal has access to the local clipboard and micro does not).
default value: `on`
---
Default plugin options:
* `autoclose`: Automatically close `{}` `()` `[]` `""` `''`. Provided by the `autoclose` plugin
* `autoclose`: automatically close `{}` `()` `[]` `""` `''`. Provided by the
`autoclose` plugin
default value: `on`
* `linter`: Automatically lint when the file is saved. Provided by the `linter` plugin
* `ftoptions`: by default, micro will set some options based on the filetype. At
the moment, micro will use tabs for makefiles and spaces for python and yaml
files regardless of your settings. If you would like to disable this behavior
turn this option off.
default value: `on`
* `ftoptions`: by default, micro will set some options based on the filetype. At the moment, micro will
use tabs for makefiles and spaces for python files regardless of your settings. If you would like to
disable this behavior turn this option off.
* `linter`: Automatically lint when the file is saved. Provided by the `linter`
plugin.
default value: `on`
* `fileformat`: this determines what kind of line endings micro will use for the file. Unix line endings
are just `\n` (lf) whereas dos line endings are `\r\n` (crlf). The two possible values for this option
are `unix` and `dos`. The fileformat will be automatically detected and displayed on the statusline but
this option is useful if you would like to change the line endings or if you are starting a new file.
default value: `unix`
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
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.
# Global and local settings
You can set these settings either globally or locally. Locally means that the setting
won't be saved to `~/.config/micro/settings.json` and that it will only be set in
the current buffer. Setting an option globally is the default, and will set the option
in all buffers.
## Global and local settings
The `colorscheme` option is global only, and the `filetype` option is local only. To
set an option locally, use `setlocal` instead of `set`.
You can set these settings either globally or locally. Locally means that the
setting won't be saved to `~/.config/micro/settings.json` and that it will only
be set in the current buffer. Setting an option globally is the default, and
will set the option in all buffers.
In the `settings.json` file you can also put set options locally by specifying a glob.
Here is an example which has `tabstospaces` on for all files except Go files, and
`tabsize` 4 for all files except Ruby files:
The `colorscheme` option is global only, and the `filetype` option is local
only. To set an option locally, use `setlocal` instead of `set`.
In the `settings.json` file you can also put set options locally by specifying a
glob. Here is an example which has `tabstospaces` on for all files except Go
files, and `tabsize` 4 for all files except Ruby files:
```json
{
"*.go": {
"tabstospaces": false
},
"*.rb": {
"tabsize": 2
},
"tabstospaces": true,
"tabsize": 4
"*.go": {
"tabstospaces": false
},
"*.rb": {
"tabsize": 2
},
"tabstospaces": true,
"tabsize": 4
}
```
As you can see it is quite easy to set options locally using the `settings.json` file.
As you can see it is quite easy to set options locally using the `settings.json`
file.

View File

@@ -4,10 +4,9 @@ Micro supports creating plugins with a simple Lua system. Every plugin has a
main script which is run at startup which should be placed in
`~/.config/micro/plugins/pluginName/pluginName.lua`.
There are a number of callback functions which you can create in your
plugin to run code at times other than startup. The naming scheme is
`onAction(view)`. For example a function which is run every time the user saves
the buffer would be:
There are a number of callback functions which you can create in your plugin to
run code at times other than startup. The naming scheme is `onAction(view)`. For
example a function which is run every time the user saves the buffer would be:
```lua
function onSave(view)
@@ -17,7 +16,8 @@ end
```
The `view` variable is a reference to the view the action is being executed on.
This is almost always the current view, which you can get with `CurView()` as well.
This is almost always the current view, which you can get with `CurView()` as
well.
All available actions are listed in the keybindings section of the help.
@@ -31,27 +31,28 @@ function onMousePress(view, event)
end
```
These functions should also return a boolean specifying whether the view
should be relocated to the cursor or not after the action is complete.
These functions should also return a boolean specifying whether the view should
be relocated to the cursor or not after the action is complete.
Note that these callbacks occur after the action has been completed. If you
want a callback before the action is executed, use `preAction()`. In this case
the boolean returned specifies whether or not the action should be executed
after the lua code completes.
Note that these callbacks occur after the action has been completed. If you want
a callback before the action is executed, use `preAction()`. In this case the
boolean returned specifies whether or not the action should be executed after
the lua code completes.
Another useful callback to know about which is not a action is
Another useful callback to know about which is not an action is
`onViewOpen(view)` which is called whenever a new view is opened and the new
view is passed in. This is useful for setting local options based on the filetype,
for example turning off `tabstospaces` only for Go files when they are opened.
view is passed in. This is useful for setting local options based on the
filetype, for example turning off `tabstospaces` only for Go files when they are
opened.
---
There are a number of functions and variables that are available to you in
order to access the inner workings of micro. Here is a list (the type signatures
for functions are given using Go's type system):
There are a number of functions and variables that are available to you in order
to access the inner workings of micro. Here is a list (the type signatures for
functions are given using Go's type system):
* `OS`: variable which gives the OS micro is currently running on (this is the same
as Go's GOOS variable, so `darwin`, `windows`, `linux`, `freebsd`...)
* `OS`: variable which gives the OS micro is currently running on (this is the
same as Go's GOOS variable, so `darwin`, `windows`, `linux`, `freebsd`...)
* `configDir`: contains the path to the micro configuration files
@@ -61,29 +62,35 @@ as Go's GOOS variable, so `darwin`, `windows`, `linux`, `freebsd`...)
* `messenger`: lets you send messages to the user or create prompts
* `NewBuffer(text, path string) *Buffer`: creates a new buffer from a given reader with a given path
* `NewBuffer(text, path string) *Buffer`: creates a new buffer from a given
reader with a given path
* `GetLeadingWhitespace() bool`: returns the leading whitespace of the given string
* `GetLeadingWhitespace() bool`: returns the leading whitespace of the given
string
* `IsWordChar(str string) bool`: returns whether or not the string is a 'word character'
* `IsWordChar(str string) bool`: returns whether or not the string is a 'word
character'
* `RuneStr(r rune) string`: returns a string containing the given rune
* `Loc(x, y int) Loc`: returns a new `Loc` struct
* `WorkingDirectory() string`: returns a rooted path name to the current working directory
* `WorkingDirectory() string`: returns a rooted path name to the current working
directory
* `JoinPaths(dir... string) string`: combines multiple directories to a full path
* `JoinPaths(dir... string) string`: combines multiple directories to a full
path
* `DirectoryName(path string)`: returns all but the last element of path ,typically the path's directory
* `DirectoryName(path string)`: returns all but the last element of path,
typically the path's directory
* `GetOption(name string)`: returns the value of the requested option
* `AddOption(name string, value interface{})`: sets the given option with the given
value (`interface{}` means any type in Go)
* `AddOption(name string, value interface{})`: sets the given option with the
given value (`interface{}` means any type in Go)
* `SetOption(option, value string)`: sets the given option to the value. This will
set the option globally, unless it is a local only option.
* `SetOption(option, value string)`: sets the given option to the value. This
will set the option globally, unless it is a local only option.
* `SetLocalOption(option, value string, view *View)`: sets the given option to
the value locally in the given buffer
@@ -91,8 +98,8 @@ as Go's GOOS variable, so `darwin`, `windows`, `linux`, `freebsd`...)
* `BindKey(key, action string)`: binds `key` to `action`
* `MakeCommand(name, function string, completions ...Completion)`:
creates a command with `name` which will call `function` when executed.
Use 0 for completions to get NoCompletion.
creates a command with `name` which will call `function` when executed. Use 0
for completions to get NoCompletion.
* `MakeCompletion(function string)`:
creates a `Completion` to use with `MakeCommand`
@@ -101,42 +108,49 @@ as Go's GOOS variable, so `darwin`, `windows`, `linux`, `freebsd`...)
* `HandleCommand(cmd string)`: runs the given command
* `HandleShellCommand(shellCmd string, interactive bool, waitToClose bool)`: runs the given shell
command. The `interactive` bool specifies whether the command should run in the background. The
`waitToClose` bool only applies if `interactive` is true and means that it should wait before
returning to the editor.
* `HandleShellCommand(shellCmd string, interactive bool, waitToClose bool)`:
runs the given shell command. The `interactive` bool specifies whether the
command should run in the background. The `waitToClose` bool only applies if
`interactive` is true and means that it should wait before returning to the
editor.
* `ToCharPos(loc Loc, buf *Buffer) int`: returns the character position of a given x, y location
* `ToCharPos(loc Loc, buf *Buffer) int`: returns the character position of a
given x, y location
* `Reload`: (Re)load everything
* `ByteOffset(loc Loc, buf *Buffer) int`: exactly like `ToCharPos` except it it counts bytes instead of runes
* `ByteOffset(loc Loc, buf *Buffer) int`: exactly like `ToCharPos` except it it
counts bytes instead of runes
* `JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit string, userargs ...string)`:
Starts running the given process in the background. `onStdout` `onStderr` and `onExit`
are callbacks to lua functions which will be called when the given actions happen
to the background process.
`userargs` are the arguments which will get passed to the callback functions
Starts running the given process in the background. `onStdout` `onStderr` and
`onExit` are callbacks to lua functions which will be called when the given
actions happen to the background process. `userargs` are the arguments which
will get passed to the callback functions
* `JobStart(cmd string, onStdout, onStderr, onExit string, userargs ...string)`:
Starts running the given shell command in the background. Note that the command execute
is first parsed by a shell when using this command. It is executed with `sh -c`.
Starts running the given shell command in the background. Note that the
command execute is first parsed by a shell when using this command. It is
executed with `sh -c`.
* `JobSend(cmd *exec.Cmd, data string)`: send a string into the stdin of the job process
* `JobSend(cmd *exec.Cmd, data string)`: send a string into the stdin of the job
process
* `JobStop(cmd *exec.Cmd)`: kill a job
This may seem like a small list of available functions but some of the objects
returned by the functions have many methods. `CurView()` returns a view object
which has all the actions which you can call. For example `CurView():Save(false)`.
You can see the full list of possible actions in the keybindings help topic.
The boolean on all the actions indicates whether or not the lua callbacks should
be run. I would recommend generally sticking to false when making a plugin to
avoid recursive problems, for example if you call `CurView():Save(true)` in `onSave()`.
Just use `CurView():Save(false)` so that it won't call `onSave()` again.
which has all the actions which you can call. For example
`CurView():Save(false)`. You can see the full list of possible actions in the
keybindings help topic. The boolean on all the actions indicates whether or not
the lua callbacks should be run. I would recommend generally sticking to false
when making a plugin to avoid recursive problems, for example if you call
`CurView():Save(true)` in `onSave()`. Just use `CurView():Save(false)` so that
it won't call `onSave()` again.
Using the view object, you can also access the buffer associated with that view
by using `CurView().Buf`, which lets you access the `FileType`, `Path`, `Name`...
by using `CurView().Buf`, which lets you access the `FileType`, `Path`,
`Name`...
The possible methods which you can call using the `messenger` variable are:
@@ -146,44 +160,105 @@ The possible methods which you can call using the `messenger` variable are:
* `messenger.Prompt(prompt, historyType string, completionType Completion) (string, bool)`
* `messenger.AddLog(msg ...interface{})`
## Note
`golang` function signatures use `.` and lua uses `:` so
#### Note
Go function signatures use `.` and lua uses `:` so
```go
messenger.Message()
```
turns to
```lua
messenger:Message()
```
turns to
```lua
messenger:Message()
```
If you want a standard prompt, just use
```lua
messenger:Prompt(prompt, "", 0)
```
Debug or logging your plugin can be done with below lua example code.
```lua
messenger:AddLog("Message goes here ",pluginVariableToPrintHere)
```
In Micro Editor to see your plugin logging output press `ctrl E` then type `log`
A logging window will open and any logging sent from your plugin will be displayed here.
# Adding help files, syntax files, or colorschemes in your plugin
In Micro to see your plugin logging output press `CtrlE` then type `log`, a
logging window will open and any logging sent from your plugin will be displayed
here.
You can use the `AddRuntimeFile(name, type, path string)` function to add various kinds of
files to your plugin. For example, if you'd like to add a help topic to your plugin
called `test`, you would create a `test.md` file, and call the function:
## Accessing the Go standard library
It is possible for your lua code to access many of the functions in the Go
standard library.
Simply import the package you'd like and then you can use it. For example:
```lua
local ioutil = import("io/ioutil")
local fmt = import("fmt")
local data, err = ioutil.ReadFile("SomeFile.txt")
if err ~= nil then
messenger:Error("Error reading file: SomeFile.txt")
else
-- Data is returned as an array of bytes
-- Using Sprintf will convert it to a string
local str = fmt.Sprintf("%s", data)
-- Do something with the file you just read!
-- ...
end
```
Here are the packages from the Go standard library that you can access.
Nearly all functions from these packages are supported. For an exact
list of which functions are supported you can look through `lua.go`
(which should be easy to understand).
```
fmt
io
io/ioutil
net
math
math/rand
os
runtime
path
filepath
strings
regexp
errors
time
```
For documentation for each of these functions, you can simply look
through the Go standard library documentation.
## Adding help files, syntax files, or colorschemes in your plugin
You can use the `AddRuntimeFile(name, type, path string)` function to add
various kinds of files to your plugin. For example, if you'd like to add a help
topic to your plugin called `test`, you would create a `test.md` file, and call
the function:
```lua
AddRuntimeFile("test", "help", "test.md")
```
Use `AddRuntimeFilesFromDirectory(name, type, dir, pattern)` to add a number of files
to the runtime.
To read the content of a runtime file use `ReadRuntimeFile(fileType, name string)`
or `ListRuntimeFiles(fileType string)` for all runtime files.
Use `AddRuntimeFilesFromDirectory(name, type, dir, pattern)` to add a number of
files to the runtime. To read the content of a runtime file use
`ReadRuntimeFile(fileType, name string)` or `ListRuntimeFiles(fileType string)`
for all runtime files.
# Autocomplete command arguments
## Autocomplete command arguments
See this example to learn how to use `MakeCompletion` and `MakeCommand`
@@ -213,27 +288,32 @@ end
MakeCommand("foo", "example.foo", MakeCompletion("example.complete"))
```
# Default plugins
## Default plugins
For examples of plugins, see the default `autoclose` and `linter` plugins
(stored in the normal micro core repo under `runtime/plugins`) as well as
any plugins that are stored in the official channel [here](https://github.com/micro-editor/plugin-channel).
(stored in the normal micro core repo under `runtime/plugins`) as well as any
plugins that are stored in the official channel
[here](https://github.com/micro-editor/plugin-channel).
# Plugin Manager
Micro also has a built in plugin manager which you can invoke with the `> plugin ...` command.
## Plugin Manager
Micro also has a built in plugin manager which you can invoke with the
`> plugin ...` command.
For the valid commands you can use, see the `command` help topic.
The manager fetches plugins from the channels (which is simply a list of plugin metadata)
which it knows about. By default, micro only knows about the official channel which is located
at github.com/micro-editor/plugin-channel but you can add your own third-party channels using
the `pluginchannels` option and you can directly link third-party plugins to allow installation
through the plugin manager with the `pluginrepos` option.
The manager fetches plugins from the channels (which is simply a list of plugin
metadata) which it knows about. By default, micro only knows about the official
channel which is located at github.com/micro-editor/plugin-channel but you can
add your own third-party channels using the `pluginchannels` option and you can
directly link third-party plugins to allow installation through the plugin
manager with the `pluginrepos` option.
If you'd like to publish a plugin you've made as an official plugin, you should upload your
plugin online (to Github preferably) and add a `repo.json` file. This file will contain the
metadata for your plugin. Here is an example:
If you'd like to publish a plugin you've made as an official plugin, you should
upload your plugin online (to Github preferably) and add a `repo.json` file.
This file will contain the metadata for your plugin. Here is an example:
```json
[{
@@ -252,7 +332,8 @@ metadata for your plugin. Here is an example:
}]
```
Then open a pull request at github.com/micro-editor/plugin-channel adding a link to the
raw `repo.json` that is in your plugin repository.
To make updating the plugin work, the first line of your plugins lua code should contain the version of the plugin. (Like this: `VERSION = "1.0.0"`)
Please make sure to use [semver](http://semver.org/) for versioning.
Then open a pull request at github.com/micro-editor/plugin-channel adding a link
to the raw `repo.json` that is in your plugin repository. To make updating the
plugin work, the first line of your plugins lua code should contain the version
of the plugin. (Like this: `VERSION = "1.0.0"`) Please make sure to use
[semver](http://semver.org/) for versioning.

View File

@@ -1,15 +1,15 @@
# Tutorial
This is a brief intro to micro's configuration system that will give some
simple examples showing how to configure settings, rebind keys,
and use `init.lua` to configure micro to your liking.
This is a brief intro to micro's configuration system that will give some simple
examples showing how to configure settings, rebind keys, and use `init.lua` to
configure micro to your liking.
Hopefully you'll find this useful.
### Plugins
Micro has a plugin manager which can automatically download plugins for you.
To see the plugins 'official' plugins, go to github.com/micro-editor/plugin-channel.
Micro has a plugin manager which can automatically download plugins for you. To
see the plugins 'official' plugins, go to github.com/micro-editor/plugin-channel.
For example, if you'd like to install the snippets plugin (listed in that repo),
just press `CtrlE` to execute a command, and type `plugin install snippets`.
@@ -20,23 +20,23 @@ topic.
### Settings
In micro, your settings are stored in `~/.config/micro/settings.json`, a file
that is created the first time you run micro. It is a json file which holds
all the settings and their values. To change an option, you can either
change the value in the `settings.json` file, or you can type it in directly
while using micro.
that is created the first time you run micro. It is a json file which holds all
the settings and their values. To change an option, you can either change the
value in the `settings.json` file, or you can type it in directly while using
micro.
Simply press CtrlE to go to command mode, and type `set option value` (in the
future, I will use `> set option value` to indicate pressing CtrlE). The
change will take effect immediately and will also be saved to the `settings.json`
file so that the setting will stick even after you close micro.
future, I will use `> set option value` to indicate pressing CtrlE). The change
will take effect immediately and will also be saved to the `settings.json` file
so that the setting will stick even after you close micro.
You can also set options locally which means that the setting will only have
the value you give it in the buffer you set it in. For example, if you have
two splits open, and you type `> setlocal tabsize 2`, the tabsize will only
be 2 in the current buffer. Also micro will not save this local change to the
You can also set options locally which means that the setting will only have the
value you give it in the buffer you set it in. For example, if you have two
splits open, and you type `> setlocal tabsize 2`, the tabsize will only be 2 in
the current buffer. Also micro will not save this local change to the
`settings.json` file. However, you can still set options locally in the
`settings.json` file. For example, if you want the `tabsize` to be 2 only
in Ruby files, and 4 otherwise, you could put the following in `settings.json`:
`settings.json` file. For example, if you want the `tabsize` to be 2 only in
Ruby files, and 4 otherwise, you could put the following in `settings.json`:
```json
{
@@ -54,8 +54,8 @@ If you would like to know more about all the available options, see the
### Keybindings
Keybindings work in much the same way as options. You configure them using
the `~/.config/micro/bindings.json` file.
Keybindings work in much the same way as options. You configure them using the
`~/.config/micro/bindings.json` file.
For example if you would like to bind `CtrlR` to redo you could put the
following in `bindings.json`:
@@ -78,8 +78,8 @@ what actions are available, see the `keybindings` help topic (`> help keybinding
### Configuration with Lua
If you need more power than the json files provide, you can use the `init.lua`
file. Create it in `~/.config/micro`. This file is a lua file that is run
when micro starts and is essentially a one-file plugin.
file. Create it in `~/.config/micro`. This file is a lua file that is run when
micro starts and is essentially a one-file plugin.
I'll show you how to use the `init.lua` file by giving an example of how to
create a binding to `CtrlR` which will execute `go run` on the current file,
@@ -98,8 +98,8 @@ end
BindKey("CtrlR", "init.gorun")
```
Alternatively, you could get rid of the `BindKey` line, and put this line in
the `bindings.json` file:
Alternatively, you could get rid of the `BindKey` line, and put this line in the
`bindings.json` file:
```json
{
@@ -107,5 +107,5 @@ the `bindings.json` file:
}
```
For more information about plugins and the lua system that micro uses, see
the `plugins` help topic (`> help plugins`).
For more information about plugins and the lua system that micro uses, see the
`plugins` help topic (`> help plugins`).

View File

@@ -9,9 +9,15 @@ function onViewOpen(view)
local ft = view.Buf.Settings["filetype"]
if ft == "makefile" or ft == "go" then
if ft == "go" or
ft == "makefile" then
SetOption("tabstospaces", "off")
elseif ft == "python" or ft == "python2" or ft == "python3" then
elseif ft == "fish" or
ft == "python" or
ft == "python2" or
ft == "python3" or
ft == "yaml" or
ft == "nim" then
SetOption("tabstospaces", "on")
end
end

View File

@@ -16,37 +16,40 @@ function runLinter()
if OS == "windows" then
devnull = "NUL"
else
devnull = "/dev/null"
devnull = "/dev/null"
end
if ft == "go" then
lint("gobuild", "go", {"build", "-o", devnull}, "%f:%l: %m")
lint("golint", "golint", {file}, "%f:%l:%d+: %m")
elseif ft == "lua" then
lint("luacheck", "luacheck", {"--no-color", file}, "%f:%l:%d+: %m")
elseif ft == "python" then
lint("pyflakes", "pyflakes", {file}, "%f:%l:.-:? %m")
lint("mypy", "mypy", {file}, "%f:%l: %m")
lint("pylint", "pylint", {"--output-format=parseable", "--reports=no", file}, "%f:%l: %m")
elseif ft == "c" then
if ft == "c" then
lint("gcc", "gcc", {"-fsyntax-only", "-Wall", "-Wextra", file}, "%f:%l:%d+:.+: %m")
elseif ft == "c++" then
lint("gcc", "gcc", {"-fsyntax-only","-std=c++14", "-Wall", "-Wextra", file}, "%f:%l:%d+:.+: %m")
elseif ft == "swift" and OS == "darwin" then
lint("switfc", "xcrun", {"swiftc", file}, "%f:%l:%d+:.+: %m")
elseif ft == "swift" and OS == "linux" then
lint("switfc", "swiftc", {file}, "%f:%l:%d+:.+: %m")
elseif ft == "Objective-C" then
lint("clang", "xcrun", {"clang", "-fsyntax-only", "-Wall", "-Wextra", file}, "%f:%l:%d+:.+: %m")
elseif ft == "c++" then
lint("gcc", "gcc", {"-fsyntax-only","-std=c++14", "-Wall", "-Wextra", file}, "%f:%l:%d+:.+: %m")
elseif ft == "d" then
lint("dmd", "dmd", {"-color=off", "-o-", "-w", "-wi", "-c", file}, "%f%(%l%):.+: %m")
elseif ft == "go" then
lint("gobuild", "go", {"build", "-o", devnull}, "%f:%l: %m")
lint("golint", "golint", {file}, "%f:%l:%d+: %m")
elseif ft == "java" then
lint("javac", "javac", {"-d", dir, file}, "%f:%l: error: %m")
elseif ft == "javascript" then
lint("jshint", "jshint", {file}, "%f: line %l,.+, %m")
elseif ft == "nim" then
lint("nim", "nim", {"check", "--listFullPaths", "--stdout", "--hints:off", file}, "%f.%l, %d+. %m")
elseif string.match(ft, "literate") then
lint("literate", "lit", {"-c", file}, "%f:%l:%m")
elseif ft == "lua" then
lint("luacheck", "luacheck", {"--no-color", file}, "%f:%l:%d+: %m")
elseif ft == "nim" then
lint("nim", "nim", {"check", "--listFullPaths", "--stdout", "--hints:off", file}, "%f.%l, %d+. %m")
elseif ft == "Objective-C" then
lint("clang", "xcrun", {"clang", "-fsyntax-only", "-Wall", "-Wextra", file}, "%f:%l:%d+:.+: %m")
elseif ft == "python" then
lint("pyflakes", "pyflakes", {file}, "%f:%l:.-:? %m")
lint("mypy", "mypy", {file}, "%f:%l: %m")
lint("pylint", "pylint", {"--output-format=parseable", "--reports=no", file}, "%f:%l: %m")
elseif ft == "shell" then
lint("shfmt", "shfmt", {file}, "%f:%l:%d+: %m")
elseif ft == "swift" and OS == "darwin" then
lint("switfc", "xcrun", {"swiftc", file}, "%f:%l:%d+:.+: %m")
elseif ft == "swift" and OS == "linux" then
lint("switfc", "swiftc", {file}, "%f:%l:%d+:.+: %m")
elseif ft == "yaml" then
lint("yaml", "yamllint", {"--format", "parsable", file}, "%f:%l:%d+:.+ %m")
end

View File

@@ -25,7 +25,7 @@ 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
(about 4 files from this directory had issues after being converted).
=======
# Micro syntax highlighting files
These are the syntax highlighting files for micro. To install them, just

View File

@@ -1,7 +1,7 @@
filetype: clojure
detect:
filename: "\\.(clj)$"
filename: "\\.(clj[sc]?)$"
rules:

View File

@@ -5,7 +5,7 @@ detect:
rules:
## Keywords
- keyword: "(?i)^(FROM|MAINTAINER|RUN|CMD|LABEL|EXPOSE|ENV|ADD|COPY|ENTRYPOINT|VOLUME|USER|WORKDIR|ONBUILD|ARG|HEALTHCHECK|STOPSIGNAL|SHELL)[[:space:]]"
- type.keyword: "(?i)^(FROM|MAINTAINER|RUN|CMD|LABEL|EXPOSE|ENV|ADD|COPY|ENTRYPOINT|VOLUME|USER|WORKDIR|ONBUILD|ARG|HEALTHCHECK|STOPSIGNAL|SHELL)[[:space:]]"
## Brackets & parenthesis
- statement: "(\\(|\\)|\\[|\\])"

View File

@@ -12,17 +12,18 @@ rules:
end: "$"
rules: []
# File changes
- keyword: "#[[:space:]](deleted|modified|new file|renamed):[[:space:]].*"
- keyword: "#[[:space:]]deleted:"
- keyword: "#[[:space:]]modified:"
- keyword: "#[[:space:]]new file:"
- keyword: "#[[:space:]]renamed:"
- type.keyword: "#[[:space:]](deleted|modified|new file|renamed):[[:space:]].*"
- type.keyword: "#[[:space:]]deleted:"
- type.keyword: "#[[:space:]]modified:"
- type.keyword: "#[[:space:]]new file:"
- type.keyword: "#[[:space:]]renamed:"
# Untracked filenames
- error: "^# [^/?*:;{}\\\\]+\\.[^/?*:;{}\\\\]+$"
- keyword: "^#[[:space:]]Changes.*[:]"
- keyword: "^#[[:space:]]Your branch and '[^']+"
- keyword: "^#[[:space:]]Your branch and '"
- keyword: "^#[[:space:]]On branch [^ ]+"
- keyword: "^#[[:space:]]On branch"
- type.keyword: "^#[[:space:]]Changes.*[:]"
- type.keyword: "^#[[:space:]]Your branch and '[^']+"
- type.keyword: "^#[[:space:]]Your branch and '"
- type.keyword: "^#[[:space:]]On branch [^ ]+"
- type.keyword: "^#[[:space:]]On branch"
# Recolor hash symbols
- special: "#"

View File

@@ -5,7 +5,7 @@ detect:
rules:
- constant: "\\<(true|false)\\>"
- keyword: "^[[:space:]]*[^=]*="
- type.keyword: "^[[:space:]]*[^=]*="
- constant: "^[[:space:]]*\\[.*\\]$"
- constant: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'"
- comment:

View File

@@ -11,11 +11,9 @@ rules:
- symbol.operator: "[-+/*=<>!~%&|^]|:="
# Types
- special: "[a-zA-Z0-9]*\\("
- symbol: "(,|\\.)"
- type: "\\b(u?int(8|16|32|64)?|float(32|64)|complex(64|128))\\b"
- type: "\\b(uintptr|byte|rune|string|interface|bool|map|chan|error)\\b"
##I'm... not sure, but aren't structs a type?
- type.keyword: "\\b(struct)\\b"
- constant.bool: "\\b(true|false|nil)\\b"

View File

@@ -1,16 +1,23 @@
filetype: javascript
detect:
filename: "\\.js$"
filename: "(\\.js$|\\.es[5678]?$)"
header: "^#!.*/(env +)?node( |$)"
rules:
- constant.number: "\\b[-+]?([1-9][0-9]*|0[0-7]*|0x[0-9a-fA-F]+)([uU][lL]?|[lL][uU]?)?\\b"
- constant.number: "\\b[-+]?([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)([EePp][+-]?[0-9]+)?[fFlL]?"
- constant.number: "\\b[-+]?([0-9]+[EePp][+-]?[0-9]+)[fFlL]?"
- identifier: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[(]"
- symbol.brackets: "(\\{|\\})"
- symbol.brackets: "(\\(|\\))"
- symbol.brackets: "(\\[|\\])"
- symbol.brackets: "(\\{|\\})"
- symbol.brackets: "(\\(|\\))"
- symbol.brackets: "(\\[|\\])"
- statement: "\\b(break|case|catch|continue|default|delete|do|else|finally)\\b"
- statement: "\\b(for|function|get|if|in|instanceof|new|return|set|switch)\\b"
- statement: "\\b(switch|this|throw|try|typeof|var|void|while|with)\\b"
- statement: "\\b(for|function|class|extends|get|if|in|instanceof|new|return|set|switch|async|await)\\b"
- statement: "\\b(switch|this|throw|try|typeof|var|const|let|void|while|with)\\b"
- constant: "\\b(null|undefined|NaN)\\b"
- constant: "\\b(true|false)\\b"
- type: "\\b(Array|Boolean|Date|Enumerator|Error|Function|Math)\\b"

View File

@@ -22,7 +22,7 @@ rules:
- constant: "\\b(false|nil|true)\\b"
- statement: "(\\b(dofile|require|include)|%q|%!|%Q|%r|%x)\\b"
- constant.number: "\\b([0-9]+)\\b"
- symbol: "(\\(|\\)|\\[|\\]|\\{|\\}|\\*\\*|\\*|/|%|\\+|-|\\^|>|>=|<|<=|~=|=|\\.\\.)"
- symbol: "(\\(|\\)|\\[|\\]|\\{|\\}|\\*\\*|\\*|/|%|\\+|-|\\^|>|>=|<|<=|~=|=|\\.\\.|#)"
- constant.string:
start: "\""
@@ -46,11 +46,6 @@ rules:
- special: "\\\\[0-7][0-7][0-7]|\\\\x[0-9a-fA-F][0-9a-fA-F]|\\\\[abefnrs]|(\\\\c|\\\\C-|\\\\M-|\\\\M-\\\\C-)."
- comment:
start: "#"
end: "$"
rules: []
- comment:
start: "\\-\\-"
end: "$"

View File

@@ -0,0 +1,83 @@
# References
# https://github.com/zyedidia/micro/blob/master/runtime/syntax/go.yaml
# https://github.com/vim-scripts/octave.vim--/blob/master/syntax/octave.vim
#
# TODO
# include only needed operators
# ... highlighting
# built-in function highlighting?
# highlight eps/pi/e etc. as functions when followed by ()
# what are skip and error fields in strings?
# multiline comments not working
filetype: octave
detect:
filename: "\\.m$"
rules:
# Statements https://www.gnu.org/software/octave/doc/v4.0.0/Statements.html
- statement: "\\b(function|endfunction|return|end|global|persistent)\\b"
- statement: "\\b(if|elseif|else|endif|switch|case|otherwise|endswitch)\\b"
- statement: "\\b(while|endwhile|do|until|for|endfor|parfor|endparfor|break|continue)\\b"
- statement: "\\b(unwind_protect|unwind_protect_cleanup|end_unwind_protect|try|catch|end_try_catch)\\b"
# Operators
- symbol.operator: "[-+/*=<>!~%&|^]|:="
# Brackets
- symbol.brackets: "(\\{|\\})"
- symbol.brackets: "(\\(|\\))"
- symbol.brackets: "(\\[|\\])"
# Commas
- symbol: ","
# Numbers https://www.gnu.org/software/octave/doc/v4.0.1/Mathematical-Constants.html
- constant.number: "\\b([0-9]+|0x[0-9a-fA-F]*)\\b|'.'"
- constant.number: "\\b(pi|e|I|Inf|NaN|eps|realmax|realmin)\\b|"
# Boolean
- constant.bool: "\\b(true|false)\\b"
# Strings https://www.gnu.org/software/octave/doc/v4.0.1/Strings.html
- constant.string:
start: "\""
end: "\""
skip: "\\\\."
rules:
- constant.specialChar: "%"
- constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]"
- constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})"
- constant.string:
start: "'"
end: "'"
skip: "\\\\."
rules:
- error: "..+"
- constant.specialChar: "%"
- constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]"
- constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})"
# Comments https://www.gnu.org/software/octave/doc/v4.2.1/Comments.html
- comment:
start: "%"
end: "$"
rules:
- todo: "(TODO|XXX|FIXME):?"
- comment:
start: "#"
end: "$"
rules:
- todo: "(TODO|XXX|FIXME):?"
- comment:
start: "%{"
end: "%}"
rules:
- todo: "(TODO|XXX|FIXME):?"
- comment:
start: "#{"
end: "#}"
rules:
- todo: "(TODO|XXX|FIXME):?"

View File

@@ -7,21 +7,33 @@ detect:
rules:
- type: "\\b(accept|alarm|atan2|bin(d|mode)|c(aller|h(dir|mod|op|own|root)|lose(dir)?|onnect|os|rypt)|d(bm(close|open)|efined|elete|ie|o|ump)|e(ach|of|val|x(ec|ists|it|p))|f(cntl|ileno|lock|ork))\\b|\\b(get(c|login|peername|pgrp|ppid|priority|pwnam|(host|net|proto|serv)byname|pwuid|grgid|(host|net)byaddr|protobynumber|servbyport)|([gs]et|end)(pw|gr|host|net|proto|serv)ent|getsock(name|opt)|gmtime|goto|grep|hex|index|int|ioctl|join)\\b|\\b(keys|kill|last|length|link|listen|local(time)?|log|lstat|m|mkdir|msg(ctl|get|snd|rcv)|next|oct|open(dir)?|ord|pack|pipe|pop|printf?|push|q|qq|qx|rand|re(ad(dir|link)?|cv|do|name|quire|set|turn|verse|winddir)|rindex|rmdir|s|scalar|seek(dir)?)\\b|\\b(se(lect|mctl|mget|mop|nd|tpgrp|tpriority|tsockopt)|shift|shm(ctl|get|read|write)|shutdown|sin|sleep|socket(pair)?|sort|spli(ce|t)|sprintf|sqrt|srand|stat|study|substr|symlink|sys(call|read|tem|write)|tell(dir)?|time|tr(y)?|truncate|umask)\\b|\\b(un(def|link|pack|shift)|utime|values|vec|wait(pid)?|wantarray|warn|write)\\b"
- statement: "\\b(continue|else|elsif|do|for|foreach|if|unless|until|while|eq|ne|lt|gt|le|ge|cmp|x|my|sub|use|package|can|isa)\\b"
- special: "\\-\\>"
- symbol: "(,|\\.)"
- identifier:
start: "[$@%]"
end: "((?i) |[^0-9A-Z_]|-)"
start: "[\\$@%]"
end: "\\W"
rules: []
- constant.string: "\".*\"|qq\\|.*\\|"
- constant.string: "\"\\(.*\\)\"|qq?\\|.*\\||qq?\\{.*\\}|qq?\\/.*\\/"
- default: "[sm]/.*/"
- preproc:
start: "(^use| = new)"
end: ";"
rules: []
- comment: "#.*"
- comment:
start: "#"
end: "$"
rules: []
- comment:
start: "^="
end: "^=cut"
rules: []
- identifier.macro:
start: "<< 'STOP'"
end: "STOP"
rules: []

View File

@@ -6,7 +6,7 @@ detect:
rules:
- type: "\\b(boolean|byte|char|double|float|int|long|new|short|this|transient|void)\\b"
- statement: "\\b(match|val|var|break|case|catch|continue|default|do|else|finally|for|if|return|switch|throw|try|while)\\b"
- statement: "\\b(def|object|case|trait|lazy|implicit|abstract|class|extends|final|implements|import|instanceof|interface|native|package|private|protected|public|static|strictfp|super|synchronized|throws|volatile|sealed)\\b"
- statement: "\\b(def|object|case|trait|lazy|implicit|abstract|class|extends|with|final|implements|override|import|instanceof|interface|native|package|private|protected|public|static|strictfp|super|synchronized|throws|volatile|sealed)\\b"
- constant.string:
start: "\""
end: "\""

View File

@@ -36,7 +36,7 @@ rules:
rules: []
- comment:
start: "#"
start: "(^|[[:space:]])#"
end: "$"
rules:
- todo: "(TODO|XXX|FIXME):?"

View File

@@ -9,7 +9,7 @@ rules:
- identifier: "[a-zA-Z][_a-zA-Z0-9]*[[:space:]]*"
- statement: "\\b(assembly|break|continue|do|for|function|if|else|new|return|returns|while)\\b"
- special: "\\b(\\.send|throw)\\b" # make sure they are very visible
- keyword: "\\b(anonymous|constant|indexed|payable|public|private|external|internal)\\b"
- type.keyword: "\\b(anonymous|constant|indexed|payable|public|private|external|internal)\\b"
- constant: "\\b(block(\\.(blockhash|coinbase|difficulty|gaslimit|number|timestamp))?|msg(\\.(data|gas|sender|value))?|now|tx(\\.(gasprice|origin))?)\\b"
- constant: "\\b(keccak256|sha3|sha256|ripemd160|ecrecover|addmod|mulmod|this|super|selfdestruct|\\.balance)\\b"
- constant: "\\b(true|false)\\b"

View File

@@ -1,5 +1,5 @@
name: micro
version: master
version: git
summary: A modern and intuitive terminal-based text editor
description: |
Micro is a terminal-based text editor that aims to be easy to use and

View File

@@ -1,7 +1,5 @@
# Builds two .deb packages, for x86 (i386) and x86_64 (amd64)
# These packages are the bare minimum, which means that they can be installed
# But they do not feature everything yet.
# This does not mean that the editor itself is affected.
# Builds three .deb packages, for x86 (i386) and x86_64 (amd64) and arm (arm)
# These packages include a manpage, an icon, and a desktop file.
function getControl() {
echo Section: editors
@@ -21,42 +19,56 @@ echo " and it supports mouse input"
function installFiles() {
TO="$1/$2/usr/share/doc/micro/"
mkdir -p $TO
mkdir -p "$1/$2/usr/share/man/man1/"
mkdir -p "$1/$2/usr/share/applications/"
mkdir -p "$1/$2/usr/share/icons/"
cp ../LICENSE $TO
cp ../LICENSE-THIRD-PARTY $TO
cp ../README.md $TO
gzip -c ../assets/packaging/micro.1 > $1/$2/usr/share/man/man1/micro.1.gz
cp ../assets/packaging/micro.desktop $1/$2/usr/share/applications/
cp ../assets/logo.svg $1/$2/usr/share/icons/micro.svg
}
version=$1
if [ "$1" == "" ]
then
version=$(go run build-version.go)
then
version=$(go run build-version.go | tr "-" ".")
fi
echo "Building packages for Version '$version'"
echo "Running Cross-Compile"
./cross-compile.sh $version
echo "Compiling."
./compile-linux.sh $version
echo "Beginning package build process"
PKGPATH="../packages/deb"
rm -fr ../packages
rm -fr $PKGPATH
mkdir -p $PKGPATH/amd64/DEBIAN/
mkdir -p $PKGPATH/i386/DEBIAN/
mkdir -p $PKGPATH/arm/DEBIAN/
getControl "amd64" "$version" > $PKGPATH/amd64/DEBIAN/control
tar -xzf "../binaries/micro-$version-linux64.tar.gz" "micro-$version/micro"
mkdir -p $PKGPATH/amd64/usr/local/bin/
mv "micro-$version/micro" "$PKGPATH/amd64/usr/local/bin/"
getControl "i386" "$version" > $PKGPATH/i386/DEBIAN/control
tar -xzf "../binaries/micro-$version-linux32.tar.gz" "micro-$version/micro"
mkdir -p $PKGPATH/i386/usr/local/bin/
mv "micro-$version/micro" "$PKGPATH/i386/usr/local/bin/"
getControl "arm" "$version" > $PKGPATH/arm/DEBIAN/control
tar -xzf "../binaries/micro-$version-linux-arm.tar.gz" "micro-$version/micro"
mkdir -p $PKGPATH/arm/usr/local/bin
mv "micro-$version/micro" "$PKGPATH/arm/usr/local/bin"
rm -rf "micro-$version"
installFiles $PKGPATH "amd64"
installFiles $PKGPATH "i386"
installFiles $PKGPATH "arm"
dpkg -b "$PKGPATH/amd64/" "../packages/micro-$version-amd64.deb"
dpkg -b "$PKGPATH/i386/" "../packages/micro-$version-i386.deb"
dpkg -b "$PKGPATH/arm/" "../packages/micro-$version-arm.deb"

129
tools/build-packages.sh Executable file
View File

@@ -0,0 +1,129 @@
#Builds all packages we support
version=$1
if [ "$1" == "" ]
then
version=$(go run build-version.go | tr "-" ".")
fi
echo "Building packages for Version '$version'"
echo "Compiling."
./compile-linux.sh $version
#Build the debs
function getControl() {
echo Section: editors
echo Package: micro
echo Version: $2
echo Priority: extra
echo Maintainer: \"Zachary Yedidia\" \<zyedidia@gmail.com\>
echo Standards-Version: 3.9.8
echo Homepage: https://micro-editor.github.io/
echo Architecture: $1
echo "Description: A modern and intuitive terminal-based text editor"
echo " This package contains a modern alternative to other terminal-based"
echo " Editors. It is easy to Use, highly customizable via themes and plugins"
echo " and it supports mouse input"
}
function installFiles() {
TO="$1/$2/usr/share/doc/micro/"
mkdir -p $TO
mkdir -p "$1/$2/usr/share/man/man1/"
mkdir -p "$1/$2/usr/share/applications/"
mkdir -p "$1/$2/usr/share/icons/"
cp ../LICENSE $TO
cp ../LICENSE-THIRD-PARTY $TO
cp ../README.md $TO
gzip -c ../assets/packaging/micro.1 > $1/$2/usr/share/man/man1/micro.1.gz
cp ../assets/packaging/micro.desktop $1/$2/usr/share/applications/
cp ../assets/logo.svg $1/$2/usr/share/icons/micro.svg
}
echo "Starting deb build process"
PKGPATH="../packages/deb"
rm -fr $PKGPATH
mkdir -p $PKGPATH/amd64/DEBIAN/
mkdir -p $PKGPATH/i386/DEBIAN/
mkdir -p $PKGPATH/arm/DEBIAN/
getControl "amd64" "$version" > $PKGPATH/amd64/DEBIAN/control
tar -xzf "../binaries/micro-$version-linux64.tar.gz" "micro-$version/micro"
mkdir -p $PKGPATH/amd64/usr/local/bin/
mv "micro-$version/micro" "$PKGPATH/amd64/usr/local/bin/"
getControl "i386" "$version" > $PKGPATH/i386/DEBIAN/control
tar -xzf "../binaries/micro-$version-linux32.tar.gz" "micro-$version/micro"
mkdir -p $PKGPATH/i386/usr/local/bin/
mv "micro-$version/micro" "$PKGPATH/i386/usr/local/bin/"
getControl "arm" "$version" > $PKGPATH/arm/DEBIAN/control
tar -xzf "../binaries/micro-$version-linux-arm.tar.gz" "micro-$version/micro"
mkdir -p $PKGPATH/arm/usr/local/bin
mv "micro-$version/micro" "$PKGPATH/arm/usr/local/bin"
rm -rf "micro-$version"
installFiles $PKGPATH "amd64"
installFiles $PKGPATH "i386"
installFiles $PKGPATH "arm"
echo "Building debs"
dpkg -b "$PKGPATH/amd64/" "../packages/micro-$version-amd64.deb"
dpkg -b "$PKGPATH/i386/" "../packages/micro-$version-i386.deb"
dpkg -b "$PKGPATH/arm/" "../packages/micro-$version-arm.deb"
#Build the RPMS
echo "Starting RPM build process"
PKGPATH="../packages/rpm"
rm -rf $PKGPATH
mkdir -p $PKGPATH
versionsplit=$(echo $version | tr "." "\n")
version=""
i=0
for string in $versionsplit
do
if (("$i" < "2"))
then
version=$(echo $version$string.)
fi
if (("$i" == "2"))
then
version=$(echo $version$string)
fi
if (("$i" == "3"))
then
dev=$(echo $dev$string.)
fi
if (("$i"=="4"))
then
dev=$(echo $dev$string)
fi
let "i+=1"
done
#Generate the spec file from template
cat micro.spec | sed s/"dev.126"/"$dev"/ | sed s/"Version: 1.1.5"/"Version: $version"/ | sed s/"-Version: 1.1.5"/"-Version: $version"/ | sed s/"DATE"/"$(date +%F\ %H:%m)"/ | sed s/"rdieter1@localhost.localdomain"/"$USER@$HOSTNAME"/ | tee > $PKGPATH/micro.spec
cd $PKGPATH
echo "Building the RPM packages"
rpmbuild -bs micro.spec --define "_sourcedir $(pwd)/../../binaries/" --define "_rpmdir $(pwd)/../"
rpmbuild -bb micro.spec --define "_sourcedir $(pwd)/../../binaries/" --define "_rpmdir $(pwd)/../" --target x86_64
rpmbuild -bb micro.spec --define "_sourcedir $(pwd)/../../binaries/" --define "_rpmdir $(pwd)/../" --target i686
rpmbuild -bb micro.spec --define "_sourcedir $(pwd)/../../binaries/" --define "_rpmdir $(pwd)/../" --target armv7l
cd ..
mv x86_64/micro-$version-1.$dev.x86_64.rpm ./
mv i686/micro-$version-1.$dev.i686.rpm ./
mv armv7l/micro-$version-1.$dev.armv7l.rpm ./
echo "Cleaning up."
rm -rf x86_64
rm -rf i686
rm -rf armv7l
rm -rf rpm
rm -rf deb
echo "Your packages should be ready now. Thank you, have a nice day. :)"

59
tools/build-rpm.sh Executable file
View File

@@ -0,0 +1,59 @@
#This script builds four rpm packages
#One for x86 (i386) and x86_64 (amd64) and arm (armv7l)
#and one containing the source tarball
version=$1
if [ "$1" == "" ]
then
version=$(go run build-version.go | tr "-" ".")
fi
echo "Building packages for Version '$version'"
echo "Compiling."
./compile-linux.sh $version
PKGPATH="../packages/rpm"
rm -rf $PKGPATH
mkdir -p $PKGPATH
versionsplit=$(echo $version | tr "." "\n")
version=""
i=0
for string in $versionsplit
do
if (("$i" < "2"))
then
version=$(echo $version$string.)
fi
if (("$i" == "2"))
then
version=$(echo $version$string)
fi
if (("$i" == "3"))
then
dev=$(echo $dev$string.)
fi
if (("$i"=="4"))
then
dev=$(echo $dev$string)
fi
let "i+=1"
done
echo "Starting the packaging process"
#Generate the spec file
cat micro.spec | sed s/"dev.126"/"$dev"/ | sed s/"Version: 1.1.5"/"Version: $version"/ | sed s/"-Version: 1.1.5"/"-Version: $version"/ | sed s/"DATE"/"$(date +%F\ %H:%m)"/ | sed s/"rdieter1@localhost.localdomain"/"$USER@$HOSTNAME"/ | tee > $PKGPATH/micro.spec
cd $PKGPATH
rpmbuild -bs micro.spec --define "_sourcedir $(pwd)/../../binaries/" --define "_rpmdir $(pwd)/../"
rpmbuild -bb micro.spec --define "_sourcedir $(pwd)/../../binaries/" --define "_rpmdir $(pwd)/../" --target x86_64
rpmbuild -bb micro.spec --define "_sourcedir $(pwd)/../../binaries/" --define "_rpmdir $(pwd)/../" --target i686
rpmbuild -bb micro.spec --define "_sourcedir $(pwd)/../../binaries/" --define "_rpmdir $(pwd)/../" --target armv7l
cd ..
mv x86_64/micro-$version-1.$dev.x86_64.rpm ./
mv i686/micro-$version-1.$dev.i686.rpm ./
mv armv7l/micro-$version-1.$dev.armv7l.rpm ./
rm -rf x86_64
rm -rf i686
rm -rf armv7l

34
tools/compile-linux.sh Executable file
View File

@@ -0,0 +1,34 @@
# Source tar
./vendor-src.sh micro-$1-src
cd ..
mkdir -p binaries
mkdir -p micro-$1
mv micro-$1-src.tar.gz binaries
mv micro-$1-src.zip binaries
cp LICENSE micro-$1
cp README.md micro-$1
cp LICENSE-THIRD-PARTY micro-$1
HASH="$(git rev-parse --short HEAD)"
VERSION="$(go run tools/build-version.go)"
DATE="$(go run tools/build-date.go)"
ADDITIONAL_GO_LINKER_FLAGS="$(go run tools/info-plist.go $VERSION)"
echo "Linux 64"
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X main.Version=$1 -X main.CommitHash=$HASH -X 'main.CompileDate=$DATE'" -o micro-$1/micro ./cmd/micro
tar -czf micro-$1-linux64.tar.gz micro-$1
mv micro-$1-linux64.tar.gz binaries
echo "Linux 32"
GOOS=linux GOARCH=386 go build -ldflags "-s -w -X main.Version=$1 -X main.CommitHash=$HASH -X 'main.CompileDate=$DATE'" -o micro-$1/micro ./cmd/micro
tar -czf micro-$1-linux32.tar.gz micro-$1
mv micro-$1-linux32.tar.gz binaries
echo "Linux arm"
GOOS=linux GOARCH=arm go build -ldflags "-s -w -X main.Version=$1 -X main.CommitHash=$HASH -X 'main.CompileDate=$DATE'" -o micro-$1/micro ./cmd/micro
tar -czf micro-$1-linux-arm.tar.gz micro-$1
mv micro-$1-linux-arm.tar.gz binaries
rm -rf micro-$1

71
tools/micro.spec Normal file
View File

@@ -0,0 +1,71 @@
%global dev_rev dev.126
Name: micro
Version: 1.1.5
Release: 1.%{dev_rev}
Summary: A feature-rich terminal text editor
URL: https://micro-editor.github.io
Packager: Zachary Yedidia <zyedidia@gmail.com>
License: MIT
Group: Applications/Editors
Source0: https://somethinghub.com/magicant/micro-binaries/micro-%{version}.%{dev_rev}-src.tar.gz
# disable debuginfo, using prebuilt binaries
%global debug_package %{nil}
## x86_64 section
Source1: https://somethinghub.com/magicant/micro-binaries/micro-%{version}.%{dev_rev}-linux64.tar.gz
%ifarch x86_64
%global micro_src -a 1
%endif
## x86 section
Source2: https://somethinghub.com/magicant/micro-binaries/micro-%{version}.%{dev_rev}-linux32.tar.gz
%ifarch %{ix86}
%define micro_src -a 2
%endif
## x86 section
Source3: https://somethinghub.com/magicant/micro-binaries/micro-%{version}.%{dev_rev}-linux-arm.tar.gz
%ifarch %{arm}
%define micro_src -a 3
%endif
%description
A modern and intuitive terminal-based text editor.
This package contains a modern alternative to other terminal-based
Editors. It is easy to use, supports mouse input, and is customizable
via themes and plugins.
%prep
%setup -q -n %{name} %{?micro_src}
%build
# skipped, using pre-built binaries
%install
install -D -m 755 micro-%{version}.%{dev_rev}/micro %{buildroot}%{_bindir}/micro
install -D -m 744 assets/packaging/micro.1 %{buildroot}%{_mandir}/man1/micro.1
install -D -m 744 assets/packaging/micro.desktop %{buildroot}%{_datadir}/applications/micro.desktop
install -D -m 744 assets/logo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/micro.svg
%files
%doc AUTHORS
%doc LICENSE
%doc LICENSE-THIRD-PARTY
%doc README.md
%{_bindir}/micro
%{_mandir}/man1/micro.1*
%{_datadir}/applications/micro.desktop
%{_datadir}/icons/hicolor/scalable/apps/micro.svg
%changelog
* Thu Mar 30 2017 Zachary Yedidia <zyedidia@gmail.com>
-Version: 1.1.5
-Auto generated on DATE by rdieter1@localhost.localdomain